--- zzzz-none-000/linux-3.10.107/drivers/s390/block/dasd.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/s390/block/dasd.c 2021-02-04 17:41:59.000000000 +0000 @@ -38,15 +38,16 @@ */ #define DASD_CHANQ_MAX_SIZE 4 -#define DASD_SLEEPON_START_TAG (void *) 1 -#define DASD_SLEEPON_END_TAG (void *) 2 +#define DASD_DIAG_MOD "dasd_diag_mod" /* * SECTION: exported variables of dasd.c */ debug_info_t *dasd_debug_area; +EXPORT_SYMBOL(dasd_debug_area); static struct dentry *dasd_debugfs_root_entry; struct dasd_discipline *dasd_diag_discipline_pointer; +EXPORT_SYMBOL(dasd_diag_discipline_pointer); void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *); MODULE_AUTHOR("Holger Smolinski "); @@ -167,6 +168,7 @@ return block; } +EXPORT_SYMBOL_GPL(dasd_alloc_block); /* * Free memory of a device structure. @@ -175,6 +177,7 @@ { kfree(block); } +EXPORT_SYMBOL_GPL(dasd_free_block); /* * Make a new device known to the system. @@ -284,10 +287,15 @@ { int rc; + if (device->discipline->basic_to_known) { + rc = device->discipline->basic_to_known(device); + if (rc) + return rc; + } + if (device->block) { dasd_profile_exit(&device->block->profile); - if (device->block->debugfs_dentry) - debugfs_remove(device->block->debugfs_dentry); + debugfs_remove(device->block->debugfs_dentry); dasd_gendisk_free(device->block); dasd_block_clear_timer(device->block); } @@ -296,9 +304,7 @@ return rc; dasd_device_clear_timer(device); dasd_profile_exit(&device->profile); - if (device->debugfs_dentry) - debugfs_remove(device->debugfs_dentry); - + debugfs_remove(device->debugfs_dentry); DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device); if (device->debug_area != NULL) { debug_unregister(device->debug_area); @@ -377,11 +383,6 @@ { 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; @@ -580,8 +581,10 @@ { dasd_get_device(device); /* queue call to dasd_kick_device to the kernel event daemon. */ - schedule_work(&device->kick_work); + if (!schedule_work(&device->kick_work)) + dasd_put_device(device); } +EXPORT_SYMBOL(dasd_kick_device); /* * dasd_reload_device will schedule a call do do_reload_device to the kernel @@ -599,7 +602,8 @@ { dasd_get_device(device); /* queue call to dasd_reload_device to the kernel event daemon. */ - schedule_work(&device->reload_device); + if (!schedule_work(&device->reload_device)) + dasd_put_device(device); } EXPORT_SYMBOL(dasd_reload_device); @@ -619,7 +623,8 @@ { dasd_get_device(device); /* queue call to dasd_restore_device to the kernel event daemon. */ - schedule_work(&device->restore_device); + if (!schedule_work(&device->restore_device)) + dasd_put_device(device); } /* @@ -642,6 +647,7 @@ mutex_unlock(&device->state_mutex); dasd_put_device(device); } +EXPORT_SYMBOL(dasd_set_target_state); /* * Enable devices with device numbers in [from..to]. @@ -664,6 +670,7 @@ if (device->discipline->kick_validate) device->discipline->kick_validate(device); } +EXPORT_SYMBOL(dasd_enable_device); /* * SECTION: device operation (interrupt handler, start i/o, term i/o ...) @@ -672,8 +679,9 @@ unsigned int dasd_global_profile_level = DASD_PROFILE_OFF; #ifdef CONFIG_DASD_PROFILE -struct dasd_profile_info dasd_global_profile_data; -static struct dentry *dasd_global_profile_dentry; +struct dasd_profile dasd_global_profile = { + .lock = __SPIN_LOCK_UNLOCKED(dasd_global_profile.lock), +}; static struct dentry *dasd_debugfs_global_entry; /* @@ -694,17 +702,20 @@ if (++counter >= 31) break; - if (dasd_global_profile_level) { - dasd_global_profile_data.dasd_io_nr_req[counter]++; + spin_lock(&dasd_global_profile.lock); + if (dasd_global_profile.data) { + dasd_global_profile.data->dasd_io_nr_req[counter]++; if (rq_data_dir(req) == READ) - dasd_global_profile_data.dasd_read_nr_req[counter]++; + dasd_global_profile.data->dasd_read_nr_req[counter]++; } + spin_unlock(&dasd_global_profile.lock); spin_lock(&block->profile.lock); - if (block->profile.data) + if (block->profile.data) { block->profile.data->dasd_io_nr_req[counter]++; if (rq_data_dir(req) == READ) block->profile.data->dasd_read_nr_req[counter]++; + } spin_unlock(&block->profile.lock); /* @@ -822,8 +833,9 @@ dasd_profile_counter(irqtime / sectors, irqtimeps_ind); dasd_profile_counter(endtime, endtime_ind); - if (dasd_global_profile_level) { - dasd_profile_end_add_data(&dasd_global_profile_data, + spin_lock(&dasd_global_profile.lock); + if (dasd_global_profile.data) { + dasd_profile_end_add_data(dasd_global_profile.data, cqr->startdev != block->base, cqr->cpmode == 1, rq_data_dir(req) == READ, @@ -832,6 +844,7 @@ irqtime_ind, irqtimeps_ind, endtime_ind); } + spin_unlock(&dasd_global_profile.lock); spin_lock(&block->profile.lock); if (block->profile.data) @@ -873,12 +886,6 @@ spin_unlock_bh(&profile->lock); } -void dasd_global_profile_reset(void) -{ - memset(&dasd_global_profile_data, 0, sizeof(dasd_global_profile_data)); - getnstimeofday(&dasd_global_profile_data.starttod); -} - int dasd_profile_on(struct dasd_profile *profile) { struct dasd_profile_info *data; @@ -946,12 +953,20 @@ dasd_profile_reset(prof); } else if (strncmp(str, "on", 2) == 0) { rc = dasd_profile_on(prof); - if (!rc) - rc = user_len; + if (rc) + goto out; + rc = user_len; + if (prof == &dasd_global_profile) { + dasd_profile_reset(prof); + dasd_global_profile_level = DASD_PROFILE_GLOBAL_ONLY; + } } else if (strncmp(str, "off", 3) == 0) { + if (prof == &dasd_global_profile) + dasd_global_profile_level = DASD_PROFILE_OFF; dasd_profile_off(prof); } else rc = -EINVAL; +out: vfree(buffer); return rc; } @@ -974,37 +989,37 @@ seq_printf(m, "total_sectors %u\n", data->dasd_io_sects); seq_printf(m, "total_pav %u\n", data->dasd_io_alias); seq_printf(m, "total_hpf %u\n", data->dasd_io_tpm); - seq_printf(m, "histogram_sectors "); + seq_puts(m, "histogram_sectors "); dasd_stats_array(m, data->dasd_io_secs); - seq_printf(m, "histogram_io_times "); + seq_puts(m, "histogram_io_times "); dasd_stats_array(m, data->dasd_io_times); - seq_printf(m, "histogram_io_times_weighted "); + seq_puts(m, "histogram_io_times_weighted "); dasd_stats_array(m, data->dasd_io_timps); - seq_printf(m, "histogram_time_build_to_ssch "); + seq_puts(m, "histogram_time_build_to_ssch "); dasd_stats_array(m, data->dasd_io_time1); - seq_printf(m, "histogram_time_ssch_to_irq "); + seq_puts(m, "histogram_time_ssch_to_irq "); dasd_stats_array(m, data->dasd_io_time2); - seq_printf(m, "histogram_time_ssch_to_irq_weighted "); + seq_puts(m, "histogram_time_ssch_to_irq_weighted "); dasd_stats_array(m, data->dasd_io_time2ps); - seq_printf(m, "histogram_time_irq_to_end "); + seq_puts(m, "histogram_time_irq_to_end "); dasd_stats_array(m, data->dasd_io_time3); - seq_printf(m, "histogram_ccw_queue_length "); + seq_puts(m, "histogram_ccw_queue_length "); dasd_stats_array(m, data->dasd_io_nr_req); seq_printf(m, "total_read_requests %u\n", data->dasd_read_reqs); seq_printf(m, "total_read_sectors %u\n", data->dasd_read_sects); seq_printf(m, "total_read_pav %u\n", data->dasd_read_alias); seq_printf(m, "total_read_hpf %u\n", data->dasd_read_tpm); - seq_printf(m, "histogram_read_sectors "); + seq_puts(m, "histogram_read_sectors "); dasd_stats_array(m, data->dasd_read_secs); - seq_printf(m, "histogram_read_times "); + seq_puts(m, "histogram_read_times "); dasd_stats_array(m, data->dasd_read_times); - seq_printf(m, "histogram_read_time_build_to_ssch "); + seq_puts(m, "histogram_read_time_build_to_ssch "); dasd_stats_array(m, data->dasd_read_time1); - seq_printf(m, "histogram_read_time_ssch_to_irq "); + seq_puts(m, "histogram_read_time_ssch_to_irq "); dasd_stats_array(m, data->dasd_read_time2); - seq_printf(m, "histogram_read_time_irq_to_end "); + seq_puts(m, "histogram_read_time_irq_to_end "); dasd_stats_array(m, data->dasd_read_time3); - seq_printf(m, "histogram_read_ccw_queue_length "); + seq_puts(m, "histogram_read_ccw_queue_length "); dasd_stats_array(m, data->dasd_read_nr_req); } @@ -1018,7 +1033,7 @@ data = profile->data; if (!data) { spin_unlock_bh(&profile->lock); - seq_printf(m, "disabled\n"); + seq_puts(m, "disabled\n"); return 0; } dasd_stats_seq_print(m, data); @@ -1041,57 +1056,6 @@ .write = dasd_stats_write, }; -static ssize_t dasd_stats_global_write(struct file *file, - const char __user *user_buf, - size_t user_len, loff_t *pos) -{ - char *buffer, *str; - ssize_t rc; - - if (user_len > 65536) - user_len = 65536; - buffer = dasd_get_user_string(user_buf, user_len); - if (IS_ERR(buffer)) - return PTR_ERR(buffer); - str = skip_spaces(buffer); - rc = user_len; - if (strncmp(str, "reset", 5) == 0) { - dasd_global_profile_reset(); - } else if (strncmp(str, "on", 2) == 0) { - dasd_global_profile_reset(); - dasd_global_profile_level = DASD_PROFILE_GLOBAL_ONLY; - } else if (strncmp(str, "off", 3) == 0) { - dasd_global_profile_level = DASD_PROFILE_OFF; - } else - rc = -EINVAL; - vfree(buffer); - return rc; -} - -static int dasd_stats_global_show(struct seq_file *m, void *v) -{ - if (!dasd_global_profile_level) { - seq_printf(m, "disabled\n"); - return 0; - } - dasd_stats_seq_print(m, &dasd_global_profile_data); - return 0; -} - -static int dasd_stats_global_open(struct inode *inode, struct file *file) -{ - return single_open(file, dasd_stats_global_show, NULL); -} - -static const struct file_operations dasd_stats_global_fops = { - .owner = THIS_MODULE, - .open = dasd_stats_global_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = dasd_stats_global_write, -}; - static void dasd_profile_init(struct dasd_profile *profile, struct dentry *base_dentry) { @@ -1113,33 +1077,23 @@ static void dasd_profile_exit(struct dasd_profile *profile) { dasd_profile_off(profile); - if (profile->dentry) { - debugfs_remove(profile->dentry); - profile->dentry = NULL; - } + debugfs_remove(profile->dentry); + profile->dentry = NULL; } static void dasd_statistics_removeroot(void) { dasd_global_profile_level = DASD_PROFILE_OFF; - if (dasd_global_profile_dentry) { - debugfs_remove(dasd_global_profile_dentry); - dasd_global_profile_dentry = NULL; - } - if (dasd_debugfs_global_entry) - debugfs_remove(dasd_debugfs_global_entry); - if (dasd_debugfs_root_entry) - debugfs_remove(dasd_debugfs_root_entry); + dasd_profile_exit(&dasd_global_profile); + debugfs_remove(dasd_debugfs_global_entry); + debugfs_remove(dasd_debugfs_root_entry); } static void dasd_statistics_createroot(void) { - umode_t mode; struct dentry *pde; dasd_debugfs_root_entry = NULL; - dasd_debugfs_global_entry = NULL; - dasd_global_profile_dentry = NULL; pde = debugfs_create_dir("dasd", NULL); if (!pde || IS_ERR(pde)) goto error; @@ -1148,13 +1102,7 @@ if (!pde || IS_ERR(pde)) goto error; dasd_debugfs_global_entry = pde; - - mode = (S_IRUSR | S_IWUSR | S_IFREG); - pde = debugfs_create_file("statistics", mode, dasd_debugfs_global_entry, - NULL, &dasd_stats_global_fops); - if (!pde || IS_ERR(pde)) - goto error; - dasd_global_profile_dentry = pde; + dasd_profile_init(&dasd_global_profile, dasd_debugfs_global_entry); return; error: @@ -1180,7 +1128,7 @@ int dasd_stats_generic_show(struct seq_file *m, void *v) { - seq_printf(m, "Statistics are not activated in this kernel\n"); + seq_puts(m, "Statistics are not activated in this kernel\n"); return 0; } @@ -1245,6 +1193,7 @@ dasd_get_device(device); return cqr; } +EXPORT_SYMBOL(dasd_kmalloc_request); struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength, int datasize, @@ -1284,6 +1233,7 @@ dasd_get_device(device); return cqr; } +EXPORT_SYMBOL(dasd_smalloc_request); /* * Free memory of a channel program. This function needs to free all the @@ -1292,7 +1242,6 @@ */ void dasd_kfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device) { -#ifdef CONFIG_64BIT struct ccw1 *ccw; /* Clear any idals used for the request. */ @@ -1300,12 +1249,12 @@ do { clear_normalized_cda(ccw); } while (ccw++->flags & (CCW_FLAG_CC | CCW_FLAG_DC)); -#endif kfree(cqr->cpaddr); kfree(cqr->data); kfree(cqr); dasd_put_device(device); } +EXPORT_SYMBOL(dasd_kfree_request); void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device) { @@ -1316,6 +1265,7 @@ spin_unlock_irqrestore(&device->mem_lock, flags); dasd_put_device(device); } +EXPORT_SYMBOL(dasd_sfree_request); /* * Check discipline magic in cqr. @@ -1376,6 +1326,20 @@ "I/O error, retry"); break; case -EINVAL: + /* + * device not valid so no I/O could be running + * handle CQR as termination successful + */ + cqr->status = DASD_CQR_CLEARED; + cqr->stopclk = get_tod_clock(); + cqr->starttime = 0; + /* no retries for invalid devices */ + cqr->retries = -1; + DBF_DEV_EVENT(DBF_ERR, device, "%s", + "EINVAL, handle as terminated"); + /* fake rc to success */ + rc = 0; + break; case -EBUSY: DBF_DEV_EVENT(DBF_ERR, device, "%s", "device busy, retry later"); @@ -1393,6 +1357,7 @@ dasd_schedule_device_bh(device); return rc; } +EXPORT_SYMBOL(dasd_term_IO); /* * Start the i/o. This start_IO can fail if the channel is really busy. @@ -1511,6 +1476,7 @@ cqr->intrc = rc; return rc; } +EXPORT_SYMBOL(dasd_start_IO); /* * Timeout function for dasd devices. This is used for different purposes @@ -1543,6 +1509,7 @@ else mod_timer(&device->timer, jiffies + expires); } +EXPORT_SYMBOL(dasd_device_set_timer); /* * Clear timeout for a device. @@ -1551,6 +1518,7 @@ { del_timer(&device->timer); } +EXPORT_SYMBOL(dasd_device_clear_timer); static void dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm) @@ -1603,6 +1571,7 @@ if (device->block) dasd_schedule_block_bh(device->block); } +EXPORT_SYMBOL_GPL(dasd_generic_handle_state_change); /* * Interrupt handler for "normal" ssch-io based dasd devices. @@ -1662,6 +1631,14 @@ device->discipline->check_for_device_change(device, cqr, irb); dasd_put_device(device); } + + /* check for for attention message */ + if (scsw_dstat(&irb->scsw) & DEV_STAT_ATTENTION) { + device = dasd_device_from_cdev_locked(cdev); + device->discipline->check_attention(device, irb->esw.esw1.lpum); + dasd_put_device(device); + } + if (!cqr) return; @@ -1732,6 +1709,7 @@ dasd_device_clear_timer(device); dasd_schedule_device_bh(device); } +EXPORT_SYMBOL(dasd_int_handler); enum uc_todo dasd_generic_uc_handler(struct ccw_device *cdev, struct irb *irb) { @@ -1795,11 +1773,11 @@ list_for_each_safe(l, n, &device->ccw_queue) { cqr = list_entry(l, struct dasd_ccw_req, devlist); - /* Stop list processing at the first non-final request. */ + /* Skip any non-final request. */ if (cqr->status == DASD_CQR_QUEUED || cqr->status == DASD_CQR_IN_IO || cqr->status == DASD_CQR_CLEAR_PENDING) - break; + continue; if (cqr->status == DASD_CQR_ERROR) { __dasd_device_recovery(device, cqr); } @@ -1893,6 +1871,33 @@ } /* + * return 1 when device is not eligible for IO + */ +static int __dasd_device_is_unusable(struct dasd_device *device, + struct dasd_ccw_req *cqr) +{ + int mask = ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM); + + if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) { + /* dasd is being set offline. */ + return 1; + } + if (device->stopped) { + if (device->stopped & mask) { + /* stopped and CQR will not change that. */ + return 1; + } + if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) { + /* CQR is not able to change device to + * operational. */ + return 1; + } + /* CQR required to get device operational. */ + } + return 0; +} + +/* * Take a look at the first request on the ccw queue and check * if it needs to be started. */ @@ -1906,13 +1911,8 @@ cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); if (cqr->status != DASD_CQR_QUEUED) return; - /* when device is stopped, return request to previous layer - * exception: only the disconnect or unresumed bits are set and the - * cqr is a path verification request - */ - if (device->stopped && - !(!(device->stopped & ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM)) - && test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))) { + /* if device is not usable return request to upper layer */ + if (__dasd_device_is_unusable(device, cqr)) { cqr->intrc = -EAGAIN; cqr->status = DASD_CQR_CLEARED; dasd_schedule_device_bh(device); @@ -2005,6 +2005,7 @@ __dasd_device_process_final_queue(device, &flush_queue); return rc; } +EXPORT_SYMBOL_GPL(dasd_flush_device_queue); /* * Acquire the device lock and process queues for the device. @@ -2044,6 +2045,7 @@ dasd_get_device(device); tasklet_hi_schedule(&device->tasklet); } +EXPORT_SYMBOL(dasd_schedule_device_bh); void dasd_device_set_stop_bits(struct dasd_device *device, int bits) { @@ -2076,6 +2078,7 @@ dasd_schedule_device_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } +EXPORT_SYMBOL(dasd_add_request_head); /* * Queue a request to the tail of the device ccw_queue. @@ -2094,6 +2097,7 @@ dasd_schedule_device_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } +EXPORT_SYMBOL(dasd_add_request_tail); /* * Wakeup helper for the 'sleep_on' functions. @@ -2191,21 +2195,25 @@ test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && (!dasd_eer_enabled(device))) { cqr->status = DASD_CQR_FAILED; - cqr->intrc = -EAGAIN; + cqr->intrc = -ENOLINK; 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; - maincqr->intrc = rc; - continue; - } - } else - wait_event(generic_waitq, !(device->stopped)); - + /* + * Don't try to start requests if device is stopped + * except path verification requests + */ + if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) { + if (interruptible) { + rc = wait_event_interruptible( + generic_waitq, !(device->stopped)); + if (rc == -ERESTARTSYS) { + cqr->status = DASD_CQR_FAILED; + maincqr->intrc = rc; + continue; + } + } else + wait_event(generic_waitq, !(device->stopped)); + } if (!cqr->callback) cqr->callback = dasd_wakeup_cb; @@ -2255,8 +2263,8 @@ 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; + int rc; retry: list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { @@ -2301,12 +2309,21 @@ rc = 0; list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { + /* + * for alias devices simplify error recovery and + * return to upper layer + * do not skip ERP requests + */ + if (cqr->startdev != cqr->basedev && !cqr->refers && + (cqr->status == DASD_CQR_TERMINATED || + cqr->status == DASD_CQR_NEED_ERP)) + return -EAGAIN; + + /* normal recovery for basedev IO */ if (__dasd_sleep_on_erp(cqr)) - rc = 1; + /* handle erp first */ + goto retry; } - if (rc) - goto retry; - return 0; } @@ -2319,6 +2336,7 @@ { return _dasd_sleep_on(cqr, 0); } +EXPORT_SYMBOL(dasd_sleep_on); /* * Start requests from a ccw_queue and wait for their completion. @@ -2337,6 +2355,7 @@ { return _dasd_sleep_on(cqr, 1); } +EXPORT_SYMBOL(dasd_sleep_on_interruptible); /* * Whoa nelly now it gets really hairy. For some functions (e.g. steal lock @@ -2403,15 +2422,21 @@ rc = cqr->intrc; else rc = -EIO; + + /* kick tasklets */ + dasd_schedule_device_bh(device); + if (device->block) + dasd_schedule_block_bh(device->block); + return rc; } +EXPORT_SYMBOL(dasd_sleep_on_immediatly); /* * Cancels a request that was started with dasd_sleep_on_req. * This is useful to timeout requests. The request will be * terminated if it is currently in i/o. - * Returns 1 if the request has been terminated. - * 0 if there was no need to terminate the request (not started yet) + * Returns 0 if request termination was successful * negative error code if termination failed * Cancellation of a request is an asynchronous operation! The calling * function has to wait until the request is properly returned via callback. @@ -2428,6 +2453,8 @@ case DASD_CQR_QUEUED: /* request was not started - just set to cleared */ cqr->status = DASD_CQR_CLEARED; + if (cqr->callback_data == DASD_SLEEPON_START_TAG) + cqr->callback_data = DASD_SLEEPON_END_TAG; break; case DASD_CQR_IN_IO: /* request in IO - terminate IO and release again */ @@ -2447,7 +2474,7 @@ dasd_schedule_device_bh(device); return rc; } - +EXPORT_SYMBOL(dasd_cancel_req); /* * SECTION: Operations of the dasd_block layer. @@ -2481,6 +2508,7 @@ else mod_timer(&block->timer, jiffies + expires); } +EXPORT_SYMBOL(dasd_block_set_timer); /* * Clear timeout for a dasd_block. @@ -2489,6 +2517,7 @@ { del_timer(&block->timer); } +EXPORT_SYMBOL(dasd_block_clear_timer); /* * Process finished error recovery ccw. @@ -2534,6 +2563,11 @@ __blk_end_request_all(req, -EIO); return; } + + /* if device ist stopped do not fetch new requests */ + if (basedev->stopped) + return; + /* Now we try to fetch requests from the request queue */ while ((req = blk_peek_request(queue))) { if (basedev->features & DASD_FEATURE_READONLY && @@ -2545,6 +2579,16 @@ __blk_end_request_all(req, -EIO); continue; } + if (test_bit(DASD_FLAG_ABORTALL, &basedev->flags) && + (basedev->features & DASD_FEATURE_FAILFAST || + blk_noretry_request(req))) { + DBF_DEV_EVENT(DBF_ERR, basedev, + "Rejecting failfast request %p", + req); + blk_start_request(req); + __blk_end_request_all(req, -ETIMEDOUT); + continue; + } cqr = basedev->discipline->build_cp(basedev, block, req); if (IS_ERR(cqr)) { if (PTR_ERR(cqr) == -EBUSY) @@ -2583,8 +2627,10 @@ */ cqr->callback_data = (void *) req; cqr->status = DASD_CQR_FILLED; + req->completion_data = cqr; blk_start_request(req); list_add_tail(&cqr->blocklist, &block->ccw_queue); + INIT_LIST_HEAD(&cqr->devlist); dasd_profile_start(block, cqr, req); } } @@ -2598,8 +2644,17 @@ req = (struct request *) cqr->callback_data; dasd_profile_end(cqr->block, cqr, req); status = cqr->block->base->discipline->free_cp(cqr, req); - if (status <= 0) - error = status ? status : -EIO; + if (status < 0) + error = status; + else if (status == 0) { + if (cqr->intrc == -EPERM) + error = -EBADE; + else if (cqr->intrc == -ENOLINK || + cqr->intrc == -ETIMEDOUT) + error = cqr->intrc; + else + error = -EIO; + } __blk_end_request_all(req, error); } @@ -2700,6 +2755,7 @@ test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && (!dasd_eer_enabled(block->base))) { cqr->status = DASD_CQR_FAILED; + cqr->intrc = -ENOLINK; dasd_schedule_block_bh(block); continue; } @@ -2848,6 +2904,7 @@ dasd_get_device(block->base); tasklet_hi_schedule(&block->tasklet); } +EXPORT_SYMBOL(dasd_schedule_block_bh); /* @@ -2872,12 +2929,86 @@ } /* + * Block timeout callback, called from the block layer + * + * request_queue lock is held on entry. + * + * Return values: + * BLK_EH_RESET_TIMER if the request should be left running + * BLK_EH_NOT_HANDLED if the request is handled or terminated + * by the driver. + */ +enum blk_eh_timer_return dasd_times_out(struct request *req) +{ + struct dasd_ccw_req *cqr = req->completion_data; + struct dasd_block *block = req->q->queuedata; + struct dasd_device *device; + int rc = 0; + + if (!cqr) + return BLK_EH_NOT_HANDLED; + + device = cqr->startdev ? cqr->startdev : block->base; + if (!device->blk_timeout) + return BLK_EH_RESET_TIMER; + DBF_DEV_EVENT(DBF_WARNING, device, + " dasd_times_out cqr %p status %x", + cqr, cqr->status); + + spin_lock(&block->queue_lock); + spin_lock(get_ccwdev_lock(device->cdev)); + cqr->retries = -1; + cqr->intrc = -ETIMEDOUT; + if (cqr->status >= DASD_CQR_QUEUED) { + spin_unlock(get_ccwdev_lock(device->cdev)); + rc = dasd_cancel_req(cqr); + } else if (cqr->status == DASD_CQR_FILLED || + cqr->status == DASD_CQR_NEED_ERP) { + cqr->status = DASD_CQR_TERMINATED; + spin_unlock(get_ccwdev_lock(device->cdev)); + } else if (cqr->status == DASD_CQR_IN_ERP) { + struct dasd_ccw_req *searchcqr, *nextcqr, *tmpcqr; + + list_for_each_entry_safe(searchcqr, nextcqr, + &block->ccw_queue, blocklist) { + tmpcqr = searchcqr; + while (tmpcqr->refers) + tmpcqr = tmpcqr->refers; + if (tmpcqr != cqr) + continue; + /* searchcqr is an ERP request for cqr */ + searchcqr->retries = -1; + searchcqr->intrc = -ETIMEDOUT; + if (searchcqr->status >= DASD_CQR_QUEUED) { + spin_unlock(get_ccwdev_lock(device->cdev)); + rc = dasd_cancel_req(searchcqr); + spin_lock(get_ccwdev_lock(device->cdev)); + } else if ((searchcqr->status == DASD_CQR_FILLED) || + (searchcqr->status == DASD_CQR_NEED_ERP)) { + searchcqr->status = DASD_CQR_TERMINATED; + rc = 0; + } else if (searchcqr->status == DASD_CQR_IN_ERP) { + /* + * Shouldn't happen; most recent ERP + * request is at the front of queue + */ + continue; + } + break; + } + spin_unlock(get_ccwdev_lock(device->cdev)); + } + dasd_schedule_block_bh(block); + spin_unlock(&block->queue_lock); + + return rc ? BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED; +} + +/* * Allocate and initialize request queue and default I/O scheduler. */ static int dasd_alloc_queue(struct dasd_block *block) { - int rc; - block->request_queue = blk_init_queue(do_dasd_request, &block->request_queue_lock); if (block->request_queue == NULL) @@ -2885,14 +3016,7 @@ block->request_queue->queuedata = block; - elevator_exit(block->request_queue->elevator); - block->request_queue->elevator = NULL; - mutex_lock(&block->request_queue->sysfs_lock); - rc = elevator_init(block->request_queue, "deadline"); - if (rc) - blk_cleanup_queue(block->request_queue); - mutex_unlock(&block->request_queue->sysfs_lock); - return rc; + return 0; } /* @@ -2914,6 +3038,8 @@ } else { max = block->base->discipline->max_blocks << block->s2b_shift; } + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, block->request_queue); + block->request_queue->limits.max_dev_sectors = max; blk_queue_logical_block_size(block->request_queue, block->bp_block); blk_queue_max_hw_sectors(block->request_queue, max); @@ -3110,8 +3236,8 @@ ret = ccw_device_set_online(cdev); if (ret) - pr_warning("%s: Setting the DASD online failed with rc=%d\n", - dev_name(&cdev->dev), ret); + pr_warn("%s: Setting the DASD online failed with rc=%d\n", + dev_name(&cdev->dev), ret); } /* @@ -3142,6 +3268,7 @@ async_schedule(dasd_generic_auto_online, cdev); return 0; } +EXPORT_SYMBOL_GPL(dasd_generic_probe); /* * This will one day be called from a global not_oper handler. @@ -3184,6 +3311,7 @@ dasd_remove_sysfs_files(cdev); } +EXPORT_SYMBOL_GPL(dasd_generic_remove); /* * Activate a device. This is called from dasd_{eckd,fba}_probe() when either @@ -3206,9 +3334,23 @@ discipline = base_discipline; if (device->features & DASD_FEATURE_USEDIAG) { if (!dasd_diag_discipline_pointer) { - pr_warning("%s Setting the DASD online failed because " - "of missing DIAG discipline\n", - dev_name(&cdev->dev)); + /* Try to load the required module. */ + rc = request_module(DASD_DIAG_MOD); + if (rc) { + pr_warn("%s Setting the DASD online failed " + "because the required module %s " + "could not be loaded (rc=%d)\n", + dev_name(&cdev->dev), DASD_DIAG_MOD, + rc); + dasd_delete_device(device); + return -ENODEV; + } + } + /* Module init could have failed, so check again here after + * request_module(). */ + if (!dasd_diag_discipline_pointer) { + pr_warn("%s Setting the DASD online failed because of missing DIAG discipline\n", + dev_name(&cdev->dev)); dasd_delete_device(device); return -ENODEV; } @@ -3229,9 +3371,8 @@ /* check_device will allocate block device if necessary */ rc = discipline->check_device(device); if (rc) { - pr_warning("%s Setting the DASD online with discipline %s " - "failed with rc=%i\n", - dev_name(&cdev->dev), discipline->name, rc); + pr_warn("%s Setting the DASD online with discipline %s failed with rc=%i\n", + dev_name(&cdev->dev), discipline->name, rc); module_put(discipline->owner); module_put(base_discipline->owner); dasd_delete_device(device); @@ -3240,8 +3381,8 @@ dasd_set_target_state(device, DASD_STATE_ONLINE); if (device->state <= DASD_STATE_KNOWN) { - pr_warning("%s Setting the DASD online failed because of a " - "missing discipline\n", dev_name(&cdev->dev)); + pr_warn("%s Setting the DASD online failed because of a missing discipline\n", + dev_name(&cdev->dev)); rc = -ENODEV; dasd_set_target_state(device, DASD_STATE_NEW); if (device->block) @@ -3256,6 +3397,7 @@ dasd_put_device(device); return rc; } +EXPORT_SYMBOL_GPL(dasd_generic_set_online); int dasd_generic_set_offline(struct ccw_device *cdev) { @@ -3279,13 +3421,11 @@ open_count = atomic_read(&device->block->open_count); if (open_count > max_count) { if (open_count > 0) - pr_warning("%s: The DASD cannot be set offline " - "with open count %i\n", - dev_name(&cdev->dev), open_count); + pr_warn("%s: The DASD cannot be set offline with open count %i\n", + dev_name(&cdev->dev), open_count); else - pr_warning("%s: The DASD cannot be set offline " - "while it is in use\n", - dev_name(&cdev->dev)); + pr_warn("%s: The DASD cannot be set offline while it is in use\n", + dev_name(&cdev->dev)); clear_bit(DASD_FLAG_OFFLINE, &device->flags); dasd_put_device(device); return -EBUSY; @@ -3294,7 +3434,7 @@ if (test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { /* - * safe offline allready running + * safe offline already running * could only be called by normal offline so safe_offline flag * needs to be removed to run normal offline and kill all I/O */ @@ -3359,6 +3499,7 @@ dasd_put_device(device); return rc; } +EXPORT_SYMBOL_GPL(dasd_generic_set_offline); int dasd_generic_last_path_gone(struct dasd_device *device) { @@ -3400,6 +3541,10 @@ dasd_schedule_device_bh(device); if (device->block) dasd_schedule_block_bh(device->block); + + if (!device->stopped) + wake_up(&generic_waitq); + return 1; } EXPORT_SYMBOL_GPL(dasd_generic_path_operational); @@ -3431,6 +3576,7 @@ dasd_put_device(device); return ret; } +EXPORT_SYMBOL_GPL(dasd_generic_notify); void dasd_generic_path_event(struct ccw_device *cdev, int *path_event) { @@ -3780,39 +3926,3 @@ module_init(dasd_init); module_exit(dasd_exit); - -EXPORT_SYMBOL(dasd_debug_area); -EXPORT_SYMBOL(dasd_diag_discipline_pointer); - -EXPORT_SYMBOL(dasd_add_request_head); -EXPORT_SYMBOL(dasd_add_request_tail); -EXPORT_SYMBOL(dasd_cancel_req); -EXPORT_SYMBOL(dasd_device_clear_timer); -EXPORT_SYMBOL(dasd_block_clear_timer); -EXPORT_SYMBOL(dasd_enable_device); -EXPORT_SYMBOL(dasd_int_handler); -EXPORT_SYMBOL(dasd_kfree_request); -EXPORT_SYMBOL(dasd_kick_device); -EXPORT_SYMBOL(dasd_kmalloc_request); -EXPORT_SYMBOL(dasd_schedule_device_bh); -EXPORT_SYMBOL(dasd_schedule_block_bh); -EXPORT_SYMBOL(dasd_set_target_state); -EXPORT_SYMBOL(dasd_device_set_timer); -EXPORT_SYMBOL(dasd_block_set_timer); -EXPORT_SYMBOL(dasd_sfree_request); -EXPORT_SYMBOL(dasd_sleep_on); -EXPORT_SYMBOL(dasd_sleep_on_immediatly); -EXPORT_SYMBOL(dasd_sleep_on_interruptible); -EXPORT_SYMBOL(dasd_smalloc_request); -EXPORT_SYMBOL(dasd_start_IO); -EXPORT_SYMBOL(dasd_term_IO); - -EXPORT_SYMBOL_GPL(dasd_generic_probe); -EXPORT_SYMBOL_GPL(dasd_generic_remove); -EXPORT_SYMBOL_GPL(dasd_generic_notify); -EXPORT_SYMBOL_GPL(dasd_generic_set_online); -EXPORT_SYMBOL_GPL(dasd_generic_set_offline); -EXPORT_SYMBOL_GPL(dasd_generic_handle_state_change); -EXPORT_SYMBOL_GPL(dasd_flush_device_queue); -EXPORT_SYMBOL_GPL(dasd_alloc_block); -EXPORT_SYMBOL_GPL(dasd_free_block);