diff options
Diffstat (limited to 'drivers/s390/block/dasd.c')
-rw-r--r-- | drivers/s390/block/dasd.c | 109 |
1 files changed, 99 insertions, 10 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index f1b7fdc58a5f..4195cc05efeb 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -246,7 +246,7 @@ static struct dentry *dasd_debugfs_setup(const char *name, static int dasd_state_known_to_basic(struct dasd_device *device) { struct dasd_block *block = device->block; - int rc; + int rc = 0; /* Allocate and register gendisk structure. */ if (block) { @@ -273,7 +273,8 @@ static int dasd_state_known_to_basic(struct dasd_device *device) DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created"); device->state = DASD_STATE_BASIC; - return 0; + + return rc; } /* @@ -282,6 +283,7 @@ static int dasd_state_known_to_basic(struct dasd_device *device) static int dasd_state_basic_to_known(struct dasd_device *device) { int rc; + if (device->block) { dasd_profile_exit(&device->block->profile); if (device->block->debugfs_dentry) @@ -332,8 +334,10 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) if (block->base->discipline->do_analysis != NULL) rc = block->base->discipline->do_analysis(block); if (rc) { - if (rc != -EAGAIN) + if (rc != -EAGAIN) { device->state = DASD_STATE_UNFMT; + goto out; + } return rc; } dasd_setup_queue(block); @@ -341,11 +345,16 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) block->blocks << block->s2b_shift); device->state = DASD_STATE_READY; rc = dasd_scan_partitions(block); - if (rc) + if (rc) { device->state = DASD_STATE_BASIC; + return rc; + } } else { device->state = DASD_STATE_READY; } +out: + if (device->discipline->basic_to_ready) + rc = device->discipline->basic_to_ready(device); return rc; } @@ -368,6 +377,11 @@ static int dasd_state_ready_to_basic(struct dasd_device *device) { int rc; + if (device->discipline->ready_to_basic) { + rc = device->discipline->ready_to_basic(device); + if (rc) + return rc; + } device->state = DASD_STATE_BASIC; if (device->block) { struct dasd_block *block = device->block; @@ -402,16 +416,10 @@ static int dasd_state_unfmt_to_basic(struct dasd_device *device) static int dasd_state_ready_to_online(struct dasd_device * device) { - int rc; struct gendisk *disk; struct disk_part_iter piter; struct hd_struct *part; - if (device->discipline->ready_to_online) { - rc = device->discipline->ready_to_online(device); - if (rc) - return rc; - } device->state = DASD_STATE_ONLINE; if (device->block) { dasd_schedule_block_bh(device->block); @@ -444,6 +452,7 @@ static int dasd_state_online_to_ready(struct dasd_device *device) if (rc) return rc; } + device->state = DASD_STATE_READY; if (device->block && !(device->features & DASD_FEATURE_USERAW)) { disk = device->block->bdev->bd_disk; @@ -2223,6 +2232,77 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) return rc; } +static inline int _wait_for_wakeup_queue(struct list_head *ccw_queue) +{ + struct dasd_ccw_req *cqr; + + list_for_each_entry(cqr, ccw_queue, blocklist) { + if (cqr->callback_data != DASD_SLEEPON_END_TAG) + return 0; + } + + return 1; +} + +static int _dasd_sleep_on_queue(struct list_head *ccw_queue, int interruptible) +{ + struct dasd_device *device; + int rc; + struct dasd_ccw_req *cqr, *n; + +retry: + list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { + device = cqr->startdev; + if (cqr->status != DASD_CQR_FILLED) /*could be failed*/ + continue; + + if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags) && + !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = -EPERM; + continue; + } + /*Non-temporary stop condition will trigger fail fast*/ + if (device->stopped & ~DASD_STOPPED_PENDING && + test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && + !dasd_eer_enabled(device)) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = -EAGAIN; + continue; + } + + /*Don't try to start requests if device is stopped*/ + if (interruptible) { + rc = wait_event_interruptible( + generic_waitq, !device->stopped); + if (rc == -ERESTARTSYS) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = rc; + continue; + } + } else + wait_event(generic_waitq, !(device->stopped)); + + if (!cqr->callback) + cqr->callback = dasd_wakeup_cb; + cqr->callback_data = DASD_SLEEPON_START_TAG; + dasd_add_request_tail(cqr); + } + + wait_event(generic_waitq, _wait_for_wakeup_queue(ccw_queue)); + + rc = 0; + list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { + if (__dasd_sleep_on_erp(cqr)) + rc = 1; + } + if (rc) + goto retry; + + + return 0; +} + /* * Queue a request to the tail of the device ccw_queue and wait for * it's completion. @@ -2233,6 +2313,15 @@ int dasd_sleep_on(struct dasd_ccw_req *cqr) } /* + * Start requests from a ccw_queue and wait for their completion. + */ +int dasd_sleep_on_queue(struct list_head *ccw_queue) +{ + return _dasd_sleep_on_queue(ccw_queue, 0); +} +EXPORT_SYMBOL(dasd_sleep_on_queue); + +/* * Queue a request to the tail of the device ccw_queue and wait * interruptible for it's completion. */ |