Commit 8fea2343 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] s390: dasd driver.

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

dasd device driver changes:
 - After a state change interrupt restart all running i/o on queue
   and reset device timer.
 - Improve some debug messages.
 - Lower timeout of reserve/release/steal_lock to 2 seconds.
 - Fix BIODASDPSRD ioctl.
 - Replace ro_flag, use_diag_flag and disconnect_error_flag words by bits.
 - Use BLKPG_DEL_PARTITION ioctl instead of a call to delete_partition because
   delete_partition is not an exported function. Since dasd_destroy_partitions
   can't do blkdev_get because dasd_open would fail, keep the block device
   open as long as partitions exist. This in turn requires a different
   approach to the open vs. offline race.
parent d2ca1196
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
* *
* $Revision: 1.133 $ * $Revision: 1.136 $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -224,7 +224,8 @@ dasd_state_basic_to_ready(struct dasd_device * device) ...@@ -224,7 +224,8 @@ dasd_state_basic_to_ready(struct dasd_device * device)
return rc; return rc;
dasd_setup_queue(device); dasd_setup_queue(device);
device->state = DASD_STATE_READY; device->state = DASD_STATE_READY;
dasd_scan_partitions(device); if (dasd_scan_partitions(device) != 0)
device->state = DASD_STATE_BASIC;
return 0; return 0;
} }
...@@ -687,6 +688,9 @@ dasd_term_IO(struct dasd_ccw_req * cqr) ...@@ -687,6 +688,9 @@ dasd_term_IO(struct dasd_ccw_req * cqr)
rc = ccw_device_clear(device->cdev, (long) cqr); rc = ccw_device_clear(device->cdev, (long) cqr);
switch (rc) { switch (rc) {
case 0: /* termination successful */ case 0: /* termination successful */
if (cqr->retries > 0)
cqr->status = DASD_CQR_QUEUED;
else
cqr->status = DASD_CQR_FAILED; cqr->status = DASD_CQR_FAILED;
cqr->stopclk = get_clock(); cqr->stopclk = get_clock();
break; break;
...@@ -779,7 +783,7 @@ dasd_timeout_device(unsigned long ptr) ...@@ -779,7 +783,7 @@ dasd_timeout_device(unsigned long ptr)
device = (struct dasd_device *) ptr; device = (struct dasd_device *) ptr;
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
/* re-activate first request in queue */ /* re-activate request queue */
device->stopped &= ~DASD_STOPPED_PENDING; device->stopped &= ~DASD_STOPPED_PENDING;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
dasd_schedule_bh(device); dasd_schedule_bh(device);
...@@ -827,12 +831,25 @@ do_state_change_pending(void *data) ...@@ -827,12 +831,25 @@ do_state_change_pending(void *data)
struct dasd_device *device; struct dasd_device *device;
} *p; } *p;
struct dasd_device *device; struct dasd_device *device;
struct dasd_ccw_req *cqr;
struct list_head *l, *n;
unsigned long flags;
p = data; p = data;
device = p->device; device = p->device;
DBF_EVENT(DBF_NOTICE, "State change Interrupt for bus_id %s", DBF_EVENT(DBF_NOTICE, "State change Interrupt for bus_id %s",
device->cdev->dev.bus_id); device->cdev->dev.bus_id);
device->stopped &= ~DASD_STOPPED_PENDING; device->stopped &= ~DASD_STOPPED_PENDING;
/* restart all 'running' IO on queue */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
list_for_each_safe(l, n, &device->ccw_queue) {
cqr = list_entry(l, struct dasd_ccw_req, list);
if (cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_QUEUED;
}
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
dasd_set_timer (device, 0);
dasd_schedule_bh(device); dasd_schedule_bh(device);
dasd_put_device(device); dasd_put_device(device);
kfree(p); kfree(p);
...@@ -847,7 +864,8 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) ...@@ -847,7 +864,8 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
cqr = (struct dasd_ccw_req *) intparm; cqr = (struct dasd_ccw_req *) intparm;
if (cqr->status != DASD_CQR_IN_IO) { if (cqr->status != DASD_CQR_IN_IO) {
MESSAGE(KERN_DEBUG, MESSAGE(KERN_DEBUG,
"invalid status: bus_id %s, status %02x", "invalid status in handle_killed_request: "
"bus_id %s, status %02x",
cdev->dev.bus_id, cqr->status); cdev->dev.bus_id, cqr->status);
return; return;
} }
...@@ -1142,7 +1160,8 @@ __dasd_process_blk_queue(struct dasd_device * device) ...@@ -1142,7 +1160,8 @@ __dasd_process_blk_queue(struct dasd_device * device)
elv_next_request(queue) && elv_next_request(queue) &&
nr_queued < DASD_CHANQ_MAX_SIZE) { nr_queued < DASD_CHANQ_MAX_SIZE) {
req = elv_next_request(queue); req = elv_next_request(queue);
if (device->ro_flag && rq_data_dir(req) == WRITE) { if (test_bit(DASD_FLAG_RO, &device->flags) &&
rq_data_dir(req) == WRITE) {
DBF_EVENT(DBF_ERR, DBF_EVENT(DBF_ERR,
"(%s) Rejecting write request %p", "(%s) Rejecting write request %p",
device->cdev->dev.bus_id, device->cdev->dev.bus_id,
...@@ -1186,13 +1205,11 @@ static inline void ...@@ -1186,13 +1205,11 @@ static inline void
__dasd_check_expire(struct dasd_device * device) __dasd_check_expire(struct dasd_device * device)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
unsigned long long now;
if (list_empty(&device->ccw_queue)) if (list_empty(&device->ccw_queue))
return; return;
cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);
if (cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) { if (cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) {
now = get_clock();
if (time_after_eq(jiffies, cqr->expires + cqr->starttime)) { if (time_after_eq(jiffies, cqr->expires + cqr->starttime)) {
if (device->discipline->term_IO(cqr) != 0) if (device->discipline->term_IO(cqr) != 0)
/* Hmpf, try again in 1/100 sec */ /* Hmpf, try again in 1/100 sec */
...@@ -1517,7 +1534,8 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) ...@@ -1517,7 +1534,8 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
* terminated if it is currently in i/o. * terminated if it is currently in i/o.
* Returns 1 if the request has been terminated. * Returns 1 if the request has been terminated.
*/ */
int dasd_cancel_req(struct dasd_ccw_req *cqr) int
dasd_cancel_req(struct dasd_ccw_req *cqr)
{ {
struct dasd_device *device = cqr->device; struct dasd_device *device = cqr->device;
unsigned long flags; unsigned long flags;
...@@ -1655,18 +1673,13 @@ dasd_open(struct inode *inp, struct file *filp) ...@@ -1655,18 +1673,13 @@ dasd_open(struct inode *inp, struct file *filp)
{ {
struct gendisk *disk = inp->i_bdev->bd_disk; struct gendisk *disk = inp->i_bdev->bd_disk;
struct dasd_device *device = disk->private_data; struct dasd_device *device = disk->private_data;
int old_count, rc; int rc;
/* atomic_inc(&device->open_count);
* We use a negative value in open_count to indicate that if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
* the device must not be used. rc = -ENODEV;
*/ goto unlock;
do { }
old_count = atomic_read(&device->open_count);
if (old_count < 0)
return -ENODEV;
} while (atomic_compare_and_swap(old_count, old_count + 1,
&device->open_count));
if (!try_module_get(device->discipline->owner)) { if (!try_module_get(device->discipline->owner)) {
rc = -EINVAL; rc = -EINVAL;
...@@ -1681,7 +1694,6 @@ dasd_open(struct inode *inp, struct file *filp) ...@@ -1681,7 +1694,6 @@ dasd_open(struct inode *inp, struct file *filp)
goto out; goto out;
} }
rc = -ENODEV;
if (device->state < DASD_STATE_BASIC) { if (device->state < DASD_STATE_BASIC) {
DBF_DEV_EVENT(DBF_ERR, device, " %s", DBF_DEV_EVENT(DBF_ERR, device, " %s",
" Cannot open unrecognized device"); " Cannot open unrecognized device");
...@@ -1704,12 +1716,6 @@ dasd_release(struct inode *inp, struct file *filp) ...@@ -1704,12 +1716,6 @@ dasd_release(struct inode *inp, struct file *filp)
struct gendisk *disk = inp->i_bdev->bd_disk; struct gendisk *disk = inp->i_bdev->bd_disk;
struct dasd_device *device = disk->private_data; struct dasd_device *device = disk->private_data;
if (device->state < DASD_STATE_BASIC) {
DBF_DEV_EVENT(DBF_ERR, device, " %s",
" Cannot release unrecognized device");
return -EINVAL;
}
atomic_dec(&device->open_count); atomic_dec(&device->open_count);
module_put(device->discipline->owner); module_put(device->discipline->owner);
return 0; return 0;
...@@ -1773,17 +1779,21 @@ dasd_generic_remove (struct ccw_device *cdev) ...@@ -1773,17 +1779,21 @@ dasd_generic_remove (struct ccw_device *cdev)
dasd_remove_sysfs_files(cdev); dasd_remove_sysfs_files(cdev);
device = dasd_device_from_cdev(cdev); device = dasd_device_from_cdev(cdev);
if (!IS_ERR(device)) { if (IS_ERR(device))
return;
if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) {
/* Already doing offline processing */
dasd_put_device(device);
return;
}
/* /*
* This device is removed unconditionally. Set open_count * This device is removed unconditionally. Set offline
* to -1 to prevent dasd_open from opening it while it is * flag to prevent dasd_open from opening it while it is
* no quite down yet. * no quite down yet.
*/ */
atomic_set(&device->open_count,-1);
dasd_set_target_state(device, DASD_STATE_NEW); dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */ /* dasd_delete_device destroys the device reference. */
dasd_delete_device(device); dasd_delete_device(device);
}
} }
/* activate a device. This is called from dasd_{eckd,fba}_probe() when either /* activate a device. This is called from dasd_{eckd,fba}_probe() when either
...@@ -1801,7 +1811,7 @@ dasd_generic_set_online (struct ccw_device *cdev, ...@@ -1801,7 +1811,7 @@ dasd_generic_set_online (struct ccw_device *cdev,
if (IS_ERR(device)) if (IS_ERR(device))
return PTR_ERR(device); return PTR_ERR(device);
if (device->use_diag_flag) { if (test_bit(DASD_FLAG_USE_DIAG, &device->flags)) {
if (!dasd_diag_discipline_pointer) { if (!dasd_diag_discipline_pointer) {
printk (KERN_WARNING printk (KERN_WARNING
"dasd_generic couldn't online device %s " "dasd_generic couldn't online device %s "
...@@ -1849,18 +1859,28 @@ int ...@@ -1849,18 +1859,28 @@ int
dasd_generic_set_offline (struct ccw_device *cdev) dasd_generic_set_offline (struct ccw_device *cdev)
{ {
struct dasd_device *device; struct dasd_device *device;
int max_count;
device = dasd_device_from_cdev(cdev); device = dasd_device_from_cdev(cdev);
if (IS_ERR(device))
return PTR_ERR(device);
if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) {
/* Already doing offline processing */
dasd_put_device(device);
return 0;
}
/* /*
* We must make sure that this device is currently not in use * We must make sure that this device is currently not in use.
* (current open_count == 0 ). We set open_count to -1 to indicate * The open_count is increased for every opener, that includes
* that from now on set_offline is in progress and the device must * the blkdev_get in dasd_scan_partitions. We are only interested
* not be used otherwise. * in the other openers.
*/ */
if (atomic_compare_and_swap(0, -1, &device->open_count)) { max_count = device->bdev ? 1 : 0;
if (atomic_read(&device->open_count) > max_count) {
printk (KERN_WARNING "Can't offline dasd device with open" printk (KERN_WARNING "Can't offline dasd device with open"
" count = %i.\n", " count = %i.\n",
atomic_read(&device->open_count)); atomic_read(&device->open_count));
clear_bit(DASD_FLAG_OFFLINE, &device->flags);
dasd_put_device(device); dasd_put_device(device);
return -EBUSY; return -EBUSY;
} }
...@@ -1890,7 +1910,7 @@ dasd_generic_notify(struct ccw_device *cdev, int event) ...@@ -1890,7 +1910,7 @@ dasd_generic_notify(struct ccw_device *cdev, int event)
if (device->state < DASD_STATE_BASIC) if (device->state < DASD_STATE_BASIC)
break; break;
/* Device is active. We want to keep it. */ /* Device is active. We want to keep it. */
if (device->disconnect_error_flag) { if (test_bit(DASD_FLAG_DSC_ERROR, &device->flags)) {
list_for_each_entry(cqr, &device->ccw_queue, list) list_for_each_entry(cqr, &device->ccw_queue, list)
if (cqr->status == DASD_CQR_IN_IO) if (cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_FAILED; cqr->status = DASD_CQR_FAILED;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
* *
* $Revision: 1.27 $ * $Revision: 1.28 $
*/ */
#include <linux/timer.h> #include <linux/timer.h>
...@@ -229,7 +229,7 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) ...@@ -229,7 +229,7 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
struct dasd_device *device = erp->device; struct dasd_device *device = erp->device;
DEV_MESSAGE(KERN_INFO, device, DEV_MESSAGE(KERN_INFO, device,
"blocking request queue for %is", expires); "blocking request queue for %is", expires/HZ);
device->stopped |= DASD_STOPPED_PENDING; device->stopped |= DASD_STOPPED_PENDING;
erp->status = DASD_CQR_QUEUED; erp->status = DASD_CQR_QUEUED;
...@@ -2623,7 +2623,7 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) ...@@ -2623,7 +2623,7 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
#ifdef ERP_DEBUG #ifdef ERP_DEBUG
/* print current erp_chain */ /* print current erp_chain */
DEV_MESSAGE(KERN_DEBUG, device, "%s", DEV_MESSAGE(KERN_ERR, device, "%s",
"ERP chain at BEGINNING of ERP-ACTION"); "ERP chain at BEGINNING of ERP-ACTION");
{ {
struct dasd_ccw_req *temp_erp = NULL; struct dasd_ccw_req *temp_erp = NULL;
...@@ -2631,9 +2631,10 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) ...@@ -2631,9 +2631,10 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
for (temp_erp = cqr; for (temp_erp = cqr;
temp_erp != NULL; temp_erp = temp_erp->refers) { temp_erp != NULL; temp_erp = temp_erp->refers) {
DEV_MESSAGE(KERN_DEBUG, device, DEV_MESSAGE(KERN_ERR, device,
" erp %p refers to %p", " erp %p (%02x) refers to %p",
temp_erp, temp_erp->refers); temp_erp, temp_erp->status,
temp_erp->refers);
} }
} }
#endif /* ERP_DEBUG */ #endif /* ERP_DEBUG */
...@@ -2675,15 +2676,16 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) ...@@ -2675,15 +2676,16 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
#ifdef ERP_DEBUG #ifdef ERP_DEBUG
/* print current erp_chain */ /* print current erp_chain */
DEV_MESSAGE(KERN_DEBUG, device, "%s", "ERP chain at END of ERP-ACTION"); DEV_MESSAGE(KERN_ERR, device, "%s", "ERP chain at END of ERP-ACTION");
{ {
struct dasd_ccw_req *temp_erp = NULL; struct dasd_ccw_req *temp_erp = NULL;
for (temp_erp = erp; for (temp_erp = erp;
temp_erp != NULL; temp_erp = temp_erp->refers) { temp_erp != NULL; temp_erp = temp_erp->refers) {
DEV_MESSAGE(KERN_DEBUG, device, DEV_MESSAGE(KERN_ERR, device,
" erp %p refers to %p", " erp %p (%02x) refers to %p",
temp_erp, temp_erp->refers); temp_erp, temp_erp->status,
temp_erp->refers);
} }
} }
#endif /* ERP_DEBUG */ #endif /* ERP_DEBUG */
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* functions may not be called from interrupt context. In particular * functions may not be called from interrupt context. In particular
* dasd_get_device is a no-no from interrupt context. * dasd_get_device is a no-no from interrupt context.
* *
* $Revision: 1.26 $ * $Revision: 1.27 $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -466,10 +466,14 @@ dasd_create_device(struct ccw_device *cdev) ...@@ -466,10 +466,14 @@ dasd_create_device(struct ccw_device *cdev)
if (!devmap->device) { if (!devmap->device) {
devmap->device = device; devmap->device = device;
device->devindex = devmap->devindex; device->devindex = devmap->devindex;
device->ro_flag = if (devmap->features & DASD_FEATURE_READONLY)
(devmap->features & DASD_FEATURE_READONLY) != 0; set_bit(DASD_FLAG_RO, &device->flags);
device->use_diag_flag = else
(devmap->features & DASD_FEATURE_USEDIAG) != 0; clear_bit(DASD_FLAG_RO, &device->flags);
if (devmap->features & DASD_FEATURE_USEDIAG)
set_bit(DASD_FLAG_USE_DIAG, &device->flags);
else
clear_bit(DASD_FLAG_USE_DIAG, &device->flags);
get_device(&cdev->dev); get_device(&cdev->dev);
device->cdev = cdev; device->cdev = cdev;
rc = 0; rc = 0;
...@@ -596,7 +600,10 @@ dasd_ro_store(struct device *dev, const char *buf, size_t count) ...@@ -596,7 +600,10 @@ dasd_ro_store(struct device *dev, const char *buf, size_t count)
if (devmap->device) { if (devmap->device) {
if (devmap->device->gdp) if (devmap->device->gdp)
set_disk_ro(devmap->device->gdp, ro_flag); set_disk_ro(devmap->device->gdp, ro_flag);
devmap->device->ro_flag = ro_flag; if (ro_flag)
set_bit(DASD_FLAG_RO, &devmap->device->flags);
else
clear_bit(DASD_FLAG_RO, &devmap->device->flags);
} }
spin_unlock(&dasd_devmap_lock); spin_unlock(&dasd_devmap_lock);
return count; return count;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
* *
* $Revision: 1.51 $ * $Revision: 1.53 $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -1131,7 +1131,7 @@ dasd_eckd_release(struct block_device *bdev, int no, long args) ...@@ -1131,7 +1131,7 @@ dasd_eckd_release(struct block_device *bdev, int no, long args)
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
cqr->device = device; cqr->device = device;
cqr->retries = 0; cqr->retries = 0;
cqr->expires = 10 * HZ; cqr->expires = 2 * HZ;
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
...@@ -1174,7 +1174,7 @@ dasd_eckd_reserve(struct block_device *bdev, int no, long args) ...@@ -1174,7 +1174,7 @@ dasd_eckd_reserve(struct block_device *bdev, int no, long args)
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
cqr->device = device; cqr->device = device;
cqr->retries = 0; cqr->retries = 0;
cqr->expires = 10 * HZ; cqr->expires = 2 * HZ;
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
...@@ -1216,7 +1216,7 @@ dasd_eckd_steal_lock(struct block_device *bdev, int no, long args) ...@@ -1216,7 +1216,7 @@ dasd_eckd_steal_lock(struct block_device *bdev, int no, long args)
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data; cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
cqr->device = device; cqr->device = device;
cqr->retries = 0; cqr->retries = 0;
cqr->expires = 10 * HZ; cqr->expires = 2 * HZ;
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
...@@ -1274,6 +1274,7 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args) ...@@ -1274,6 +1274,7 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args)
stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1); stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
memset(stats, 0, sizeof (struct dasd_rssd_perf_stats_t)); memset(stats, 0, sizeof (struct dasd_rssd_perf_stats_t));
ccw++;
ccw->cmd_code = DASD_ECKD_CCW_RSSD; ccw->cmd_code = DASD_ECKD_CCW_RSSD;
ccw->count = sizeof (struct dasd_rssd_perf_stats_t); ccw->count = sizeof (struct dasd_rssd_perf_stats_t);
ccw->cda = (__u32)(addr_t) stats; ccw->cda = (__u32)(addr_t) stats;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* gendisk related functions for the dasd driver. * gendisk related functions for the dasd driver.
* *
* $Revision: 1.44 $ * $Revision: 1.46 $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -71,7 +71,7 @@ dasd_gendisk_alloc(struct dasd_device *device) ...@@ -71,7 +71,7 @@ dasd_gendisk_alloc(struct dasd_device *device)
sprintf(gdp->devfs_name, "dasd/%s", device->cdev->dev.bus_id); sprintf(gdp->devfs_name, "dasd/%s", device->cdev->dev.bus_id);
if (device->ro_flag) if (test_bit(DASD_FLAG_RO, &device->flags))
set_disk_ro(gdp, 1); set_disk_ro(gdp, 1);
gdp->private_data = device; gdp->private_data = device;
gdp->queue = device->request_queue; gdp->queue = device->request_queue;
...@@ -96,22 +96,33 @@ dasd_gendisk_free(struct dasd_device *device) ...@@ -96,22 +96,33 @@ dasd_gendisk_free(struct dasd_device *device)
/* /*
* Trigger a partition detection. * Trigger a partition detection.
*/ */
void int
dasd_scan_partitions(struct dasd_device * device) dasd_scan_partitions(struct dasd_device * device)
{ {
struct block_device *bdev; struct block_device *bdev;
/* Make the disk known. */ /* Make the disk known. */
set_capacity(device->gdp, device->blocks << device->s2b_shift); set_capacity(device->gdp, device->blocks << device->s2b_shift);
/* See fs/partition/check.c:register_disk,rescan_partitions */
bdev = bdget_disk(device->gdp, 0); bdev = bdget_disk(device->gdp, 0);
if (bdev) { if (!bdev || blkdev_get(bdev, FMODE_READ, 1) < 0)
if (blkdev_get(bdev, FMODE_READ, 1) >= 0) { return -ENODEV;
/* Can't call rescan_partitions directly. Use ioctl. */ /*
* See fs/partition/check.c:register_disk,rescan_partitions
* Can't call rescan_partitions directly. Use ioctl.
*/
ioctl_by_bdev(bdev, BLKRRPART, 0); ioctl_by_bdev(bdev, BLKRRPART, 0);
blkdev_put(bdev); /*
} * Since the matching blkdev_put call to the blkdev_get in
} * this function is not called before dasd_destroy_partitions
* the offline open_count limit needs to be increased from
* 0 to 1. This is done by setting device->bdev (see
* dasd_generic_set_offline). As long as the partition
* detection is running no offline should be allowed. That
* is why the assignment to device->bdev is done AFTER
* the BLKRRPART ioctl.
*/
device->bdev = bdev;
return 0;
} }
/* /*
...@@ -121,13 +132,32 @@ dasd_scan_partitions(struct dasd_device * device) ...@@ -121,13 +132,32 @@ dasd_scan_partitions(struct dasd_device * device)
void void
dasd_destroy_partitions(struct dasd_device * device) dasd_destroy_partitions(struct dasd_device * device)
{ {
int p; /* The two structs have 168/176 byte on 31/64 bit. */
struct blkpg_partition bpart;
struct blkpg_ioctl_arg barg;
struct block_device *bdev;
/*
* Get the bdev pointer from the device structure and clear
* device->bdev to lower the offline open_count limit again.
*/
bdev = device->bdev;
device->bdev = 0;
/*
* See fs/partition/check.c:delete_partition
* Can't call delete_partitions directly. Use ioctl.
* The ioctl also does locking and invalidation.
*/
memset(&bpart, sizeof(struct blkpg_partition), 0);
memset(&barg, sizeof(struct blkpg_ioctl_arg), 0);
barg.data = &bpart;
for (bpart.pno = device->gdp->minors - 1; bpart.pno > 0; bpart.pno--)
ioctl_by_bdev(bdev, BLKPG_DEL_PARTITION, (unsigned long) &barg);
for (p = device->gdp->minors - 1; p > 0; p--) {
invalidate_partition(device->gdp, p);
delete_partition(device->gdp, p);
}
invalidate_partition(device->gdp, 0); invalidate_partition(device->gdp, 0);
/* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */
blkdev_put(bdev);
set_capacity(device->gdp, 0); set_capacity(device->gdp, 0);
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
* *
* $Revision: 1.55 $ * $Revision: 1.56 $
*/ */
#ifndef DASD_INT_H #ifndef DASD_INT_H
...@@ -268,14 +268,12 @@ struct dasd_device { ...@@ -268,14 +268,12 @@ struct dasd_device {
struct gendisk *gdp; struct gendisk *gdp;
request_queue_t *request_queue; request_queue_t *request_queue;
spinlock_t request_queue_lock; spinlock_t request_queue_lock;
struct block_device *bdev;
unsigned int devindex; unsigned int devindex;
unsigned long blocks; /* size of volume in blocks */ unsigned long blocks; /* size of volume in blocks */
unsigned int bp_block; /* bytes per block */ unsigned int bp_block; /* bytes per block */
unsigned int s2b_shift; /* log2 (bp_block/512) */ unsigned int s2b_shift; /* log2 (bp_block/512) */
int ro_flag; /* read-only flag */ unsigned long flags; /* per device flags */
int use_diag_flag; /* diag allowed flag */
int disconnect_error_flag; /* return -EIO when disconnected */
/* Device discipline stuff. */ /* Device discipline stuff. */
struct dasd_discipline *discipline; struct dasd_discipline *discipline;
...@@ -318,6 +316,11 @@ struct dasd_device { ...@@ -318,6 +316,11 @@ struct dasd_device {
#define DASD_STOPPED_DC_WAIT 8 /* disconnected, wait */ #define DASD_STOPPED_DC_WAIT 8 /* disconnected, wait */
#define DASD_STOPPED_DC_EIO 16 /* disconnected, return -EIO */ #define DASD_STOPPED_DC_EIO 16 /* disconnected, return -EIO */
/* per device flags */
#define DASD_FLAG_RO 0 /* device is read-only */
#define DASD_FLAG_USE_DIAG 1 /* use diag disciplnie */
#define DASD_FLAG_DSC_ERROR 2 /* return -EIO when disconnected */
#define DASD_FLAG_OFFLINE 3 /* device is in offline processing */
void dasd_put_device_wake(struct dasd_device *); void dasd_put_device_wake(struct dasd_device *);
...@@ -498,7 +501,7 @@ int dasd_gendisk_init(void); ...@@ -498,7 +501,7 @@ int dasd_gendisk_init(void);
void dasd_gendisk_exit(void); void dasd_gendisk_exit(void);
int dasd_gendisk_alloc(struct dasd_device *); int dasd_gendisk_alloc(struct dasd_device *);
void dasd_gendisk_free(struct dasd_device *); void dasd_gendisk_free(struct dasd_device *);
void dasd_scan_partitions(struct dasd_device *); int dasd_scan_partitions(struct dasd_device *);
void dasd_destroy_partitions(struct dasd_device *); void dasd_destroy_partitions(struct dasd_device *);
/* externals in dasd_ioctl.c */ /* externals in dasd_ioctl.c */
......
...@@ -303,7 +303,7 @@ dasd_ioctl_format(struct block_device *bdev, int no, long args) ...@@ -303,7 +303,7 @@ dasd_ioctl_format(struct block_device *bdev, int no, long args)
if (device == NULL) if (device == NULL)
return -ENODEV; return -ENODEV;
if (device->ro_flag) if (test_bit(DASD_FLAG_RO, &device->flags))
return -EROFS; return -EROFS;
if (copy_from_user(&fdata, (void *) args, if (copy_from_user(&fdata, (void *) args,
sizeof (struct format_data_t))) sizeof (struct format_data_t)))
...@@ -415,8 +415,8 @@ dasd_ioctl_information(struct block_device *bdev, int no, long args) ...@@ -415,8 +415,8 @@ dasd_ioctl_information(struct block_device *bdev, int no, long args)
(dasd_check_blocksize(device->bp_block))) (dasd_check_blocksize(device->bp_block)))
dasd_info->format = DASD_FORMAT_NONE; dasd_info->format = DASD_FORMAT_NONE;
dasd_info->features |= device->ro_flag ? DASD_FEATURE_READONLY dasd_info->features |= test_bit(DASD_FLAG_RO, &device->flags) ?
: DASD_FEATURE_DEFAULT; DASD_FEATURE_READONLY : DASD_FEATURE_DEFAULT;
if (device->discipline) if (device->discipline)
memcpy(dasd_info->type, device->discipline->name, 4); memcpy(dasd_info->type, device->discipline->name, 4);
...@@ -472,7 +472,10 @@ dasd_ioctl_set_ro(struct block_device *bdev, int no, long args) ...@@ -472,7 +472,10 @@ dasd_ioctl_set_ro(struct block_device *bdev, int no, long args)
if (device == NULL) if (device == NULL)
return -ENODEV; return -ENODEV;
set_disk_ro(bdev->bd_disk, intval); set_disk_ro(bdev->bd_disk, intval);
device->ro_flag = intval; if (intval)
set_bit(DASD_FLAG_RO, &device->flags);
else
clear_bit(DASD_FLAG_RO, &device->flags);
return 0; return 0;
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* /proc interface for the dasd driver. * /proc interface for the dasd driver.
* *
* $Revision: 1.26 $ * $Revision: 1.27 $
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -77,7 +77,7 @@ dasd_devices_show(struct seq_file *m, void *v) ...@@ -77,7 +77,7 @@ dasd_devices_show(struct seq_file *m, void *v)
else else
seq_printf(m, " is ????????"); seq_printf(m, " is ????????");
/* Print devices features. */ /* Print devices features. */
substr = device->ro_flag ? "(ro)" : " "; substr = test_bit(DASD_FLAG_RO, &device->flags) ? "(ro)" : " ";
seq_printf(m, "%4s: ", substr); seq_printf(m, "%4s: ", substr);
/* Print device status information. */ /* Print device status information. */
switch ((device != NULL) ? device->state : -1) { switch ((device != NULL) ? device->state : -1) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment