diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-29 11:01:17 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-29 11:01:17 -0700 |
commit | b026188e8214ce87790730a56f3017d0bd222751 (patch) | |
tree | b4d1e482b1f77360768a5c8e565bfd311ed74232 /drivers | |
parent | 1903ac54f8536b11478e4f01c339e10b538f59e0 (diff) | |
parent | 94bb063312d872d9269deb2e5c0c7c6d5b0318e1 (diff) | |
download | blackbird-op-linux-b026188e8214ce87790730a56f3017d0bd222751.tar.gz blackbird-op-linux-b026188e8214ce87790730a56f3017d0bd222751.zip |
Merge branch 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6
* 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6: (28 commits)
[S390] rework of channel measurement facility.
[S390] appldata enhancements.
[S390] Add vmpanic parameter.
[S390] add PAV support to the dasd driver.
[S390] remove export of sys_call_table
[S390] remove unused macros from binfmt_elf32.c
[S390] fix duplicate export of overflow{ug}id
[S390] cio chpid offline.
[S390] avenrun export in appdata_base.c
Convert s390_collect_crw_info() in s390mach.c from being started
[S390] dasd eer data format.
[S390] preempt_count initialization.
[S390] head.S code moving.
[S390] dasd whitespace and other cosmetics.
[S390] virtual cpu accounting vs. machine checks.
[S390] add __cpuinit to appldata cpu hotplug notifier.
[S390] dasd_eckd_dump_sense bug.
[S390] missing check in dasd_eer_open.
[S390] modular 3270 driver.
[S390] console_unblank woes.
...
Diffstat (limited to 'drivers')
27 files changed, 1071 insertions, 724 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index cfb1fff3787c..bafcd2f20ae2 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -95,7 +95,7 @@ dasd_alloc_device(void) spin_lock_init(&device->mem_lock); spin_lock_init(&device->request_queue_lock); atomic_set (&device->tasklet_scheduled, 0); - tasklet_init(&device->tasklet, + tasklet_init(&device->tasklet, (void (*)(unsigned long)) dasd_tasklet, (unsigned long) device); INIT_LIST_HEAD(&device->ccw_queue); @@ -128,7 +128,7 @@ dasd_state_new_to_known(struct dasd_device *device) int rc; /* - * As long as the device is not in state DASD_STATE_NEW we want to + * As long as the device is not in state DASD_STATE_NEW we want to * keep the reference count > 0. */ dasd_get_device(device); @@ -336,7 +336,7 @@ dasd_decrease_state(struct dasd_device *device) if (device->state == DASD_STATE_ONLINE && device->target <= DASD_STATE_READY) dasd_state_online_to_ready(device); - + if (device->state == DASD_STATE_READY && device->target <= DASD_STATE_BASIC) dasd_state_ready_to_basic(device); @@ -348,7 +348,7 @@ dasd_decrease_state(struct dasd_device *device) if (device->state == DASD_STATE_BASIC && device->target <= DASD_STATE_KNOWN) dasd_state_basic_to_known(device); - + if (device->state == DASD_STATE_KNOWN && device->target <= DASD_STATE_NEW) dasd_state_known_to_new(device); @@ -994,7 +994,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, ((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr); /* Find out the appropriate era_action. */ - if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) + if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) era = dasd_era_fatal; else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && irb->scsw.cstat == 0 && @@ -1004,7 +1004,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, era = dasd_era_fatal; /* don't recover this request */ else if (irb->esw.esw0.erw.cons) era = device->discipline->examine_error(cqr, irb); - else + else era = dasd_era_recover; DBF_DEV_EVENT(DBF_DEBUG, device, "era_code %d", era); @@ -1287,7 +1287,7 @@ __dasd_start_head(struct dasd_device * device) } /* - * Remove requests from the ccw queue. + * Remove requests from the ccw queue. */ static void dasd_flush_ccw_queue(struct dasd_device * device, int all) @@ -1450,23 +1450,23 @@ dasd_sleep_on(struct dasd_ccw_req * cqr) wait_queue_head_t wait_q; struct dasd_device *device; int rc; - + device = cqr->device; spin_lock_irq(get_ccwdev_lock(device->cdev)); - + init_waitqueue_head (&wait_q); cqr->callback = dasd_wakeup_cb; cqr->callback_data = (void *) &wait_q; cqr->status = DASD_CQR_QUEUED; list_add_tail(&cqr->list, &device->ccw_queue); - + /* let the bh start the request to keep them in order */ dasd_schedule_bh(device); - + spin_unlock_irq(get_ccwdev_lock(device->cdev)); wait_event(wait_q, _wait_for_wakeup(cqr)); - + /* Request status is either done or failed. */ rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; return rc; @@ -1568,7 +1568,7 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) wait_queue_head_t wait_q; struct dasd_device *device; int rc; - + device = cqr->device; spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = _dasd_term_running_cqr(device); @@ -1576,20 +1576,20 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr) spin_unlock_irq(get_ccwdev_lock(device->cdev)); return rc; } - + init_waitqueue_head (&wait_q); cqr->callback = dasd_wakeup_cb; cqr->callback_data = (void *) &wait_q; cqr->status = DASD_CQR_QUEUED; list_add(&cqr->list, &device->ccw_queue); - + /* let the bh start the request to keep them in order */ dasd_schedule_bh(device); - + spin_unlock_irq(get_ccwdev_lock(device->cdev)); wait_event(wait_q, _wait_for_wakeup(cqr)); - + /* Request status is either done or failed. */ rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; return rc; @@ -1725,7 +1725,7 @@ dasd_flush_request_queue(struct dasd_device * device) if (!device->request_queue) return; - + spin_lock_irq(&device->request_queue_lock); while (!list_empty(&device->request_queue->queue_head)) { req = elv_next_request(device->request_queue); @@ -1855,15 +1855,34 @@ dasd_generic_probe (struct ccw_device *cdev, { int ret; + ret = ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); + if (ret) { + printk(KERN_WARNING + "dasd_generic_probe: could not set ccw-device options " + "for %s\n", cdev->dev.bus_id); + return ret; + } ret = dasd_add_sysfs_files(cdev); if (ret) { printk(KERN_WARNING "dasd_generic_probe: could not add sysfs entries " "for %s\n", cdev->dev.bus_id); - } else { - cdev->handler = &dasd_int_handler; + return ret; } + cdev->handler = &dasd_int_handler; + /* + * Automatically online either all dasd devices (dasd_autodetect) + * or all devices specified with dasd= parameters during + * initial probe. + */ + if ((dasd_get_feature(cdev, DASD_FEATURE_INITIAL_ONLINE) > 0 ) || + (dasd_autodetect && dasd_busid_known(cdev->dev.bus_id) != 0)) + ret = ccw_device_set_online(cdev); + if (ret) + printk(KERN_WARNING + "dasd_generic_probe: could not initially online " + "ccw-device %s\n", cdev->dev.bus_id); return ret; } @@ -1911,6 +1930,8 @@ dasd_generic_set_online (struct ccw_device *cdev, struct dasd_device *device; int rc; + /* first online clears initial online feature flag */ + dasd_set_feature(cdev, DASD_FEATURE_INITIAL_ONLINE, 0); device = dasd_create_device(cdev); if (IS_ERR(device)) return PTR_ERR(device); @@ -2065,31 +2086,6 @@ dasd_generic_notify(struct ccw_device *cdev, int event) return ret; } -/* - * Automatically online either all dasd devices (dasd_autodetect) or - * all devices specified with dasd= parameters. - */ -static int -__dasd_auto_online(struct device *dev, void *data) -{ - struct ccw_device *cdev; - - cdev = to_ccwdev(dev); - if (dasd_autodetect || dasd_busid_known(cdev->dev.bus_id) == 0) - ccw_device_set_online(cdev); - return 0; -} - -void -dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver) -{ - struct device_driver *drv; - - drv = get_driver(&dasd_discipline_driver->driver); - driver_for_each_device(drv, NULL, NULL, __dasd_auto_online); - put_driver(drv); -} - static int __init dasd_init(void) @@ -2170,23 +2166,4 @@ 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_auto_online); -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: 1 - * tab-width: 8 - * End: - */ diff --git a/drivers/s390/block/dasd_3370_erp.c b/drivers/s390/block/dasd_3370_erp.c index 1d11c2a9525d..1ddab8991d92 100644 --- a/drivers/s390/block/dasd_3370_erp.c +++ b/drivers/s390/block/dasd_3370_erp.c @@ -1,4 +1,4 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_3370_erp.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> @@ -12,10 +12,10 @@ /* - * DASD_3370_ERP_EXAMINE + * DASD_3370_ERP_EXAMINE * * DESCRIPTION - * Checks only for fatal/no/recover error. + * Checks only for fatal/no/recover error. * A detailed examination of the sense data is done later outside * the interrupt handler. * @@ -23,7 +23,7 @@ * 'Chapter 7. 3370 Sense Data'. * * RETURN VALUES - * dasd_era_none no error + * dasd_era_none no error * dasd_era_fatal for all fatal (unrecoverable errors) * dasd_era_recover for all others. */ @@ -82,22 +82,3 @@ dasd_3370_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) return dasd_era_recover; } /* END dasd_3370_erp_examine */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: 1 - * tab-width: 8 - * End: - */ diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 2ed51562319e..669805d4402d 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -1,6 +1,6 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_3990_erp.c - * Author(s)......: Horst Hummel <Horst.Hummel@de.ibm.com> + * Author(s)......: Horst Hummel <Horst.Hummel@de.ibm.com> * Holger Smolinski <Holger.Smolinski@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001 @@ -25,23 +25,23 @@ struct DCTL_data { } __attribute__ ((packed)); /* - ***************************************************************************** + ***************************************************************************** * SECTION ERP EXAMINATION - ***************************************************************************** + ***************************************************************************** */ /* - * DASD_3990_ERP_EXAMINE_24 + * DASD_3990_ERP_EXAMINE_24 * * DESCRIPTION - * Checks only for fatal (unrecoverable) error. + * Checks only for fatal (unrecoverable) error. * A detailed examination of the sense data is done later outside * the interrupt handler. * * Each bit configuration leading to an action code 2 (Exit with * programming error or unusual condition indication) * are handled as fatal error´s. - * + * * All other configurations are handled as recoverable errors. * * RETURN VALUES @@ -93,15 +93,15 @@ dasd_3990_erp_examine_24(struct dasd_ccw_req * cqr, char *sense) } /* END dasd_3990_erp_examine_24 */ /* - * DASD_3990_ERP_EXAMINE_32 + * DASD_3990_ERP_EXAMINE_32 * * DESCRIPTION - * Checks only for fatal/no/recoverable error. + * Checks only for fatal/no/recoverable error. * A detailed examination of the sense data is done later outside * the interrupt handler. * * RETURN VALUES - * dasd_era_none no error + * dasd_era_none no error * dasd_era_fatal for all fatal (unrecoverable errors) * dasd_era_recover for recoverable others. */ @@ -128,10 +128,10 @@ dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense) } /* end dasd_3990_erp_examine_32 */ /* - * DASD_3990_ERP_EXAMINE + * DASD_3990_ERP_EXAMINE * * DESCRIPTION - * Checks only for fatal/no/recover error. + * Checks only for fatal/no/recover error. * A detailed examination of the sense data is done later outside * the interrupt handler. * @@ -139,7 +139,7 @@ dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense) * 'Chapter 7. Error Recovery Procedures'. * * RETURN VALUES - * dasd_era_none no error + * dasd_era_none no error * dasd_era_fatal for all fatal (unrecoverable errors) * dasd_era_recover for all others. */ @@ -178,18 +178,18 @@ dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) } /* END dasd_3990_erp_examine */ /* - ***************************************************************************** + ***************************************************************************** * SECTION ERP HANDLING - ***************************************************************************** + ***************************************************************************** */ /* - ***************************************************************************** + ***************************************************************************** * 24 and 32 byte sense ERP functions - ***************************************************************************** + ***************************************************************************** */ /* - * DASD_3990_ERP_CLEANUP + * DASD_3990_ERP_CLEANUP * * DESCRIPTION * Removes the already build but not necessary ERP request and sets @@ -197,10 +197,10 @@ dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) * * PARAMETER * erp request to be blocked - * final_status either DASD_CQR_DONE or DASD_CQR_FAILED + * final_status either DASD_CQR_DONE or DASD_CQR_FAILED * * RETURN VALUES - * cqr original cqr + * cqr original cqr */ static struct dasd_ccw_req * dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) @@ -214,7 +214,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) } /* end dasd_3990_erp_cleanup */ /* - * DASD_3990_ERP_BLOCK_QUEUE + * DASD_3990_ERP_BLOCK_QUEUE * * DESCRIPTION * Block the given device request queue to prevent from further @@ -237,7 +237,7 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) } /* - * DASD_3990_ERP_INT_REQ + * DASD_3990_ERP_INT_REQ * * DESCRIPTION * Handles 'Intervention Required' error. @@ -277,7 +277,7 @@ dasd_3990_erp_int_req(struct dasd_ccw_req * erp) } /* end dasd_3990_erp_int_req */ /* - * DASD_3990_ERP_ALTERNATE_PATH + * DASD_3990_ERP_ALTERNATE_PATH * * DESCRIPTION * Repeat the operation on a different channel path. @@ -330,15 +330,15 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) * DASD_3990_ERP_DCTL * * DESCRIPTION - * Setup cqr to do the Diagnostic Control (DCTL) command with an + * Setup cqr to do the Diagnostic Control (DCTL) command with an * Inhibit Write subcommand (0x20) and the given modifier. * * PARAMETER * erp pointer to the current (failed) ERP * modifier subcommand modifier - * + * * RETURN VALUES - * dctl_cqr pointer to NEW dctl_cqr + * dctl_cqr pointer to NEW dctl_cqr * */ static struct dasd_ccw_req * @@ -386,7 +386,7 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) } /* end dasd_3990_erp_DCTL */ /* - * DASD_3990_ERP_ACTION_1 + * DASD_3990_ERP_ACTION_1 * * DESCRIPTION * Setup ERP to do the ERP action 1 (see Reference manual). @@ -415,7 +415,7 @@ dasd_3990_erp_action_1(struct dasd_ccw_req * erp) } /* end dasd_3990_erp_action_1 */ /* - * DASD_3990_ERP_ACTION_4 + * DASD_3990_ERP_ACTION_4 * * DESCRIPTION * Setup ERP to do the ERP action 4 (see Reference manual). @@ -453,11 +453,11 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) if (sense[25] == 0x1D) { /* state change pending */ - DEV_MESSAGE(KERN_INFO, device, + DEV_MESSAGE(KERN_INFO, device, "waiting for state change pending " "interrupt, %d retries left", erp->retries); - + dasd_3990_erp_block_queue(erp, 30*HZ); } else if (sense[25] == 0x1E) { /* busy */ @@ -469,9 +469,9 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) } else { /* no state change pending - retry */ - DEV_MESSAGE (KERN_INFO, device, + DEV_MESSAGE (KERN_INFO, device, "redriving request immediately, " - "%d retries left", + "%d retries left", erp->retries); erp->status = DASD_CQR_QUEUED; } @@ -482,13 +482,13 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) } /* end dasd_3990_erp_action_4 */ /* - ***************************************************************************** + ***************************************************************************** * 24 byte sense ERP functions (only) - ***************************************************************************** + ***************************************************************************** */ /* - * DASD_3990_ERP_ACTION_5 + * DASD_3990_ERP_ACTION_5 * * DESCRIPTION * Setup ERP to do the ERP action 5 (see Reference manual). @@ -523,7 +523,7 @@ dasd_3990_erp_action_5(struct dasd_ccw_req * erp) * * PARAMETER * sense current sense data - * + * * RETURN VALUES * void */ @@ -1150,9 +1150,9 @@ dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense) * PARAMETER * erp current erp_head * sense current sense data - * + * * RETURN VALUES - * erp 'new' erp_head - pointer to new ERP + * erp 'new' erp_head - pointer to new ERP */ static struct dasd_ccw_req * dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense) @@ -1185,7 +1185,7 @@ dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense) } /* end dasd_3990_erp_com_rej */ /* - * DASD_3990_ERP_BUS_OUT + * DASD_3990_ERP_BUS_OUT * * DESCRIPTION * Handles 24 byte 'Bus Out Parity Check' error. @@ -1483,7 +1483,7 @@ dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense) * * PARAMETER * erp already added default ERP - * + * * RETURN VALUES * erp new erp_head - pointer to new ERP */ @@ -1527,11 +1527,11 @@ dasd_3990_erp_file_prot(struct dasd_ccw_req * erp) } /* end dasd_3990_erp_file_prot */ /* - * DASD_3990_ERP_INSPECT_24 + * DASD_3990_ERP_INSPECT_24 * * DESCRIPTION * Does a detailed inspection of the 24 byte sense data - * and sets up a related error recovery action. + * and sets up a related error recovery action. * * PARAMETER * sense sense data of the actual error @@ -1602,13 +1602,13 @@ dasd_3990_erp_inspect_24(struct dasd_ccw_req * erp, char *sense) } /* END dasd_3990_erp_inspect_24 */ /* - ***************************************************************************** + ***************************************************************************** * 32 byte sense ERP functions (only) - ***************************************************************************** + ***************************************************************************** */ /* - * DASD_3990_ERPACTION_10_32 + * DASD_3990_ERPACTION_10_32 * * DESCRIPTION * Handles 32 byte 'Action 10' of Single Program Action Codes. @@ -1616,7 +1616,7 @@ dasd_3990_erp_inspect_24(struct dasd_ccw_req * erp, char *sense) * * PARAMETER * erp current erp_head - * sense current sense data + * sense current sense data * RETURN VALUES * erp modified erp_head */ @@ -1640,18 +1640,18 @@ dasd_3990_erp_action_10_32(struct dasd_ccw_req * erp, char *sense) * * DESCRIPTION * Handles 32 byte 'Action 1B' of Single Program Action Codes. - * A write operation could not be finished because of an unexpected + * A write operation could not be finished because of an unexpected * condition. - * The already created 'default erp' is used to get the link to - * the erp chain, but it can not be used for this recovery + * The already created 'default erp' is used to get the link to + * the erp chain, but it can not be used for this recovery * action because it contains no DE/LO data space. * * PARAMETER * default_erp already added default erp. - * sense current sense data + * sense current sense data * * RETURN VALUES - * erp new erp or + * erp new erp or * default_erp in case of imprecise ending or error */ static struct dasd_ccw_req * @@ -1789,16 +1789,16 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) * DASD_3990_UPDATE_1B * * DESCRIPTION - * Handles the update to the 32 byte 'Action 1B' of Single Program + * Handles the update to the 32 byte 'Action 1B' of Single Program * Action Codes in case the first action was not successful. * The already created 'previous_erp' is the currently not successful - * ERP. + * ERP. * * PARAMETER * previous_erp already created previous erp. - * sense current sense data + * sense current sense data * RETURN VALUES - * erp modified erp + * erp modified erp */ static struct dasd_ccw_req * dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) @@ -1897,7 +1897,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) } /* end dasd_3990_update_1B */ /* - * DASD_3990_ERP_COMPOUND_RETRY + * DASD_3990_ERP_COMPOUND_RETRY * * DESCRIPTION * Handles the compound ERP action retry code. @@ -1943,7 +1943,7 @@ dasd_3990_erp_compound_retry(struct dasd_ccw_req * erp, char *sense) } /* end dasd_3990_erp_compound_retry */ /* - * DASD_3990_ERP_COMPOUND_PATH + * DASD_3990_ERP_COMPOUND_PATH * * DESCRIPTION * Handles the compound ERP action for retry on alternate @@ -1965,7 +1965,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense) dasd_3990_erp_alternate_path(erp); if (erp->status == DASD_CQR_FAILED) { - /* reset the lpm and the status to be able to + /* reset the lpm and the status to be able to * try further actions. */ erp->lpm = 0; @@ -1980,7 +1980,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense) } /* end dasd_3990_erp_compound_path */ /* - * DASD_3990_ERP_COMPOUND_CODE + * DASD_3990_ERP_COMPOUND_CODE * * DESCRIPTION * Handles the compound ERP action for retry code. @@ -2001,18 +2001,18 @@ dasd_3990_erp_compound_code(struct dasd_ccw_req * erp, char *sense) switch (sense[28]) { case 0x17: - /* issue a Diagnostic Control command with an + /* issue a Diagnostic Control command with an * Inhibit Write subcommand and controler modifier */ erp = dasd_3990_erp_DCTL(erp, 0x20); break; - + case 0x25: /* wait for 5 seconds and retry again */ erp->retries = 1; - + dasd_3990_erp_block_queue (erp, 5*HZ); break; - + default: /* should not happen - continue */ break; @@ -2026,7 +2026,7 @@ dasd_3990_erp_compound_code(struct dasd_ccw_req * erp, char *sense) } /* end dasd_3990_erp_compound_code */ /* - * DASD_3990_ERP_COMPOUND_CONFIG + * DASD_3990_ERP_COMPOUND_CONFIG * * DESCRIPTION * Handles the compound ERP action for configruation @@ -2063,10 +2063,10 @@ dasd_3990_erp_compound_config(struct dasd_ccw_req * erp, char *sense) } /* end dasd_3990_erp_compound_config */ /* - * DASD_3990_ERP_COMPOUND + * DASD_3990_ERP_COMPOUND * * DESCRIPTION - * Does the further compound program action if + * Does the further compound program action if * compound retry was not successful. * * PARAMETER @@ -2110,11 +2110,11 @@ dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense) } /* end dasd_3990_erp_compound */ /* - * DASD_3990_ERP_INSPECT_32 + * DASD_3990_ERP_INSPECT_32 * * DESCRIPTION * Does a detailed inspection of the 32 byte sense data - * and sets up a related error recovery action. + * and sets up a related error recovery action. * * PARAMETER * sense sense data of the actual error @@ -2228,9 +2228,9 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) } /* end dasd_3990_erp_inspect_32 */ /* - ***************************************************************************** + ***************************************************************************** * main ERP control fuctions (24 and 32 byte sense) - ***************************************************************************** + ***************************************************************************** */ /* @@ -2243,7 +2243,7 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) * PARAMETER * erp pointer to the currently created default ERP * RETURN VALUES - * erp_new contens was possibly modified + * erp_new contens was possibly modified */ static struct dasd_ccw_req * dasd_3990_erp_inspect(struct dasd_ccw_req * erp) @@ -2272,14 +2272,14 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp) /* * DASD_3990_ERP_ADD_ERP - * + * * DESCRIPTION * This funtion adds an additional request block (ERP) to the head of * the given cqr (or erp). * This erp is initialized as an default erp (retry TIC) * * PARAMETER - * cqr head of the current ERP-chain (or single cqr if + * cqr head of the current ERP-chain (or single cqr if * first error) * RETURN VALUES * erp pointer to new ERP-chain head @@ -2332,15 +2332,15 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) } /* - * DASD_3990_ERP_ADDITIONAL_ERP - * + * DASD_3990_ERP_ADDITIONAL_ERP + * * DESCRIPTION * An additional ERP is needed to handle the current error. * Add ERP to the head of the ERP-chain containing the ERP processing * determined based on the sense data. * * PARAMETER - * cqr head of the current ERP-chain (or single cqr if + * cqr head of the current ERP-chain (or single cqr if * first error) * * RETURN VALUES @@ -2376,7 +2376,7 @@ dasd_3990_erp_additional_erp(struct dasd_ccw_req * cqr) * 24 byte sense byte 25 and 27 is set as well. * * PARAMETER - * cqr1 first cqr, which will be compared with the + * cqr1 first cqr, which will be compared with the * cqr2 second cqr. * * RETURN VALUES @@ -2415,7 +2415,7 @@ dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1, struct dasd_ccw_req *cqr2) * cqr failed cqr (either original cqr or already an erp) * * RETURN VALUES - * erp erp-pointer to the already defined error + * erp erp-pointer to the already defined error * recovery procedure OR * NULL if a 'new' error occurred. */ @@ -2451,10 +2451,10 @@ dasd_3990_erp_in_erp(struct dasd_ccw_req *cqr) * DASD_3990_ERP_FURTHER_ERP (24 & 32 byte sense) * * DESCRIPTION - * No retry is left for the current ERP. Check what has to be done + * No retry is left for the current ERP. Check what has to be done * with the ERP. * - do further defined ERP action or - * - wait for interrupt or + * - wait for interrupt or * - exit with permanent error * * PARAMETER @@ -2485,7 +2485,7 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) if (!(sense[2] & DASD_SENSE_BIT_0)) { - /* issue a Diagnostic Control command with an + /* issue a Diagnostic Control command with an * Inhibit Write subcommand */ switch (sense[25]) { @@ -2535,14 +2535,14 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) } /* end dasd_3990_erp_further_erp */ /* - * DASD_3990_ERP_HANDLE_MATCH_ERP + * DASD_3990_ERP_HANDLE_MATCH_ERP * * DESCRIPTION * An error occurred again and an ERP has been detected which is already - * used to handle this error (e.g. retries). + * used to handle this error (e.g. retries). * All prior ERP's are asumed to be successful and therefore removed * from queue. - * If retry counter of matching erp is already 0, it is checked if further + * If retry counter of matching erp is already 0, it is checked if further * action is needed (besides retry) or if the ERP has failed. * * PARAMETER @@ -2631,7 +2631,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, * erp erp-pointer to the head of the ERP action chain. * This means: * - either a ptr to an additional ERP cqr or - * - the original given cqr (which's status might + * - the original given cqr (which's status might * be modified) */ struct dasd_ccw_req * @@ -2723,22 +2723,3 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) return erp; } /* end dasd_3990_erp_action */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: 1 - * tab-width: 8 - * End: - */ diff --git a/drivers/s390/block/dasd_9336_erp.c b/drivers/s390/block/dasd_9336_erp.c index dc861446d056..6e082688475a 100644 --- a/drivers/s390/block/dasd_9336_erp.c +++ b/drivers/s390/block/dasd_9336_erp.c @@ -1,4 +1,4 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_9336_erp.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> @@ -12,10 +12,10 @@ /* - * DASD_9336_ERP_EXAMINE + * DASD_9336_ERP_EXAMINE * * DESCRIPTION - * Checks only for fatal/no/recover error. + * Checks only for fatal/no/recover error. * A detailed examination of the sense data is done later outside * the interrupt handler. * @@ -23,7 +23,7 @@ * 'Chapter 7. 9336 Sense Data'. * * RETURN VALUES - * dasd_era_none no error + * dasd_era_none no error * dasd_era_fatal for all fatal (unrecoverable errors) * dasd_era_recover for all others. */ @@ -39,22 +39,3 @@ dasd_9336_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) return dasd_era_recover; } /* END dasd_9336_erp_examine */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: 1 - * tab-width: 8 - * End: - */ diff --git a/drivers/s390/block/dasd_9343_erp.c b/drivers/s390/block/dasd_9343_erp.c index 4a5b79569aaa..ddecb9808ed4 100644 --- a/drivers/s390/block/dasd_9343_erp.c +++ b/drivers/s390/block/dasd_9343_erp.c @@ -1,4 +1,4 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_9345_erp.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 216bc4fba199..9e9ae7179602 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -27,7 +27,7 @@ #include "dasd_int.h" kmem_cache_t *dasd_page_cache; -EXPORT_SYMBOL(dasd_page_cache); +EXPORT_SYMBOL_GPL(dasd_page_cache); /* * dasd_devmap_t is used to store the features and the relation @@ -49,6 +49,20 @@ struct dasd_devmap { }; /* + * dasd_servermap is used to store the server_id of all storage servers + * accessed by DASD device driver. + */ +struct dasd_servermap { + struct list_head list; + struct server_id { + char vendor[4]; + char serial[15]; + } sid; +}; + +static struct list_head dasd_serverlist; + +/* * Parameter parsing functions for dasd= parameter. The syntax is: * <devno> : (0x)?[0-9a-fA-F]+ * <busid> : [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+ @@ -64,6 +78,8 @@ struct dasd_devmap { int dasd_probeonly = 0; /* is true, when probeonly mode is active */ int dasd_autodetect = 0; /* is true, when autodetection is active */ +int dasd_nopav = 0; /* is true, when PAV is disabled */ +EXPORT_SYMBOL_GPL(dasd_nopav); /* * char *dasd[] is intended to hold the ranges supplied by the dasd= statement @@ -123,7 +139,7 @@ static inline int dasd_busid(char **str, int *id0, int *id1, int *devno) { int val, old_style; - + /* check for leading '0x' */ old_style = 0; if ((*str)[0] == '0' && (*str)[1] == 'x') { @@ -179,7 +195,7 @@ dasd_feature_list(char *str, char **endp) features = 0; while (1) { - for (len = 0; + for (len = 0; str[len] && str[len] != ':' && str[len] != ')'; len++); if (len == 2 && !strncmp(str, "ro", 2)) features |= DASD_FEATURE_READONLY; @@ -228,19 +244,24 @@ dasd_parse_keyword( char *parsestring ) { length = strlen(parsestring); residual_str = parsestring + length; } - if (strncmp ("autodetect", parsestring, length) == 0) { + if (strncmp("autodetect", parsestring, length) == 0) { dasd_autodetect = 1; MESSAGE (KERN_INFO, "%s", "turning to autodetection mode"); return residual_str; } - if (strncmp ("probeonly", parsestring, length) == 0) { + if (strncmp("probeonly", parsestring, length) == 0) { dasd_probeonly = 1; MESSAGE(KERN_INFO, "%s", "turning to probeonly mode"); return residual_str; } - if (strncmp ("fixedbuffers", parsestring, length) == 0) { + if (strncmp("nopav", parsestring, length) == 0) { + dasd_nopav = 1; + MESSAGE(KERN_INFO, "%s", "disable PAV mode"); + return residual_str; + } + if (strncmp("fixedbuffers", parsestring, length) == 0) { if (dasd_page_cache) return residual_str; dasd_page_cache = @@ -294,6 +315,8 @@ dasd_parse_range( char *parsestring ) { features = dasd_feature_list(str, &str); if (features < 0) return ERR_PTR(-EINVAL); + /* each device in dasd= parameter should be set initially online */ + features |= DASD_FEATURE_INITIAL_ONLINE; while (from <= to) { sprintf(bus_id, "%01x.%01x.%04x", from_id0, from_id1, from++); @@ -359,7 +382,7 @@ dasd_parse(void) * Add a devmap for the device specified by busid. It is possible that * the devmap already exists (dasd= parameter). The order of the devices * added through this function will define the kdevs for the individual - * devices. + * devices. */ static struct dasd_devmap * dasd_add_busid(char *bus_id, int features) @@ -368,7 +391,7 @@ dasd_add_busid(char *bus_id, int features) int hash; new = (struct dasd_devmap *) - kmalloc(sizeof(struct dasd_devmap), GFP_KERNEL); + kzalloc(sizeof(struct dasd_devmap), GFP_KERNEL); if (!new) return ERR_PTR(-ENOMEM); spin_lock(&dasd_devmap_lock); @@ -630,7 +653,8 @@ dasd_ro_show(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -dasd_ro_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +dasd_ro_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct dasd_devmap *devmap; int ro_flag; @@ -658,7 +682,7 @@ static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store); * use_diag controls whether the driver should use diag rather than ssch * to talk to the device */ -static ssize_t +static ssize_t dasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dasd_devmap *devmap; @@ -673,7 +697,8 @@ dasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -dasd_use_diag_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +dasd_use_diag_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct dasd_devmap *devmap; ssize_t rc; @@ -697,11 +722,11 @@ dasd_use_diag_store(struct device *dev, struct device_attribute *attr, const cha return rc; } -static -DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store); +static DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store); static ssize_t -dasd_discipline_show(struct device *dev, struct device_attribute *attr, char *buf) +dasd_discipline_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct dasd_devmap *devmap; char *dname; @@ -834,6 +859,38 @@ static struct attribute_group dasd_attr_group = { .attrs = dasd_attrs, }; +/* + * Check if the related storage server is already contained in the + * dasd_serverlist. If server is not contained, create new entry. + * Return 0 if server was already in serverlist, + * 1 if the server was added successfully + * <0 in case of error. + */ +static int +dasd_add_server(struct dasd_uid *uid) +{ + struct dasd_servermap *new, *tmp; + + /* check if server is already contained */ + list_for_each_entry(tmp, &dasd_serverlist, list) + // normale cmp? + if (strncmp(tmp->sid.vendor, uid->vendor, + sizeof(tmp->sid.vendor)) == 0 + && strncmp(tmp->sid.serial, uid->serial, + sizeof(tmp->sid.serial)) == 0) + return 0; + + new = (struct dasd_servermap *) + kzalloc(sizeof(struct dasd_servermap), GFP_KERNEL); + if (!new) + return -ENOMEM; + + strncpy(new->sid.vendor, uid->vendor, sizeof(new->sid.vendor)); + strncpy(new->sid.serial, uid->serial, sizeof(new->sid.serial)); + list_add(&new->list, &dasd_serverlist); + return 1; +} + /* * Return copy of the device unique identifier. @@ -854,21 +911,26 @@ dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid) /* * Register the given device unique identifier into devmap struct. + * Return 0 if server was already in serverlist, + * 1 if the server was added successful + * <0 in case of error. */ int dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid) { struct dasd_devmap *devmap; + int rc; devmap = dasd_find_busid(cdev->dev.bus_id); if (IS_ERR(devmap)) return PTR_ERR(devmap); spin_lock(&dasd_devmap_lock); devmap->uid = *uid; + rc = dasd_add_server(uid); spin_unlock(&dasd_devmap_lock); - return 0; + return rc; } -EXPORT_SYMBOL(dasd_set_uid); +EXPORT_SYMBOL_GPL(dasd_set_uid); /* * Return value of the specified feature. @@ -880,7 +942,7 @@ dasd_get_feature(struct ccw_device *cdev, int feature) devmap = dasd_find_busid(cdev->dev.bus_id); if (IS_ERR(devmap)) - return (int) PTR_ERR(devmap); + return PTR_ERR(devmap); return ((devmap->features & feature) != 0); } @@ -896,7 +958,7 @@ dasd_set_feature(struct ccw_device *cdev, int feature, int flag) devmap = dasd_find_busid(cdev->dev.bus_id); if (IS_ERR(devmap)) - return (int) PTR_ERR(devmap); + return PTR_ERR(devmap); spin_lock(&dasd_devmap_lock); if (flag) @@ -932,8 +994,10 @@ dasd_devmap_init(void) dasd_max_devindex = 0; for (i = 0; i < 256; i++) INIT_LIST_HEAD(&dasd_hashlists[i]); - return 0; + /* Initialize servermap structure. */ + INIT_LIST_HEAD(&dasd_serverlist); + return 0; } void diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 3f9d704d2657..4002f6c1c1b3 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -1,4 +1,4 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_diag.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Based on.......: linux/drivers/s390/block/mdisk.c @@ -336,7 +336,7 @@ dasd_diag_check_device(struct dasd_device *device) private = (struct dasd_diag_private *) device->private; if (private == NULL) { - private = kmalloc(sizeof(struct dasd_diag_private),GFP_KERNEL); + private = kzalloc(sizeof(struct dasd_diag_private),GFP_KERNEL); if (private == NULL) { DEV_MESSAGE(KERN_WARNING, device, "%s", "memory allocation failed for private data"); @@ -527,7 +527,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) datasize, device); if (IS_ERR(cqr)) return cqr; - + dreq = (struct dasd_diag_req *) cqr->data; dreq->block_count = count; dbio = dreq->bio; diff --git a/drivers/s390/block/dasd_diag.h b/drivers/s390/block/dasd_diag.h index 38a4e55f8953..b8c78267ff3e 100644 --- a/drivers/s390/block/dasd_diag.h +++ b/drivers/s390/block/dasd_diag.h @@ -1,4 +1,4 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_diag.h * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Based on.......: linux/drivers/s390/block/mdisk.h diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 7d5a6cee4bd8..0dfab30e8089 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1,7 +1,7 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_eckd.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> - * Horst Hummel <Horst.Hummel@de.ibm.com> + * Horst Hummel <Horst.Hummel@de.ibm.com> * Carsten Otte <Cotte@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> @@ -24,6 +24,7 @@ #include <asm/io.h> #include <asm/todclk.h> #include <asm/uaccess.h> +#include <asm/cio.h> #include <asm/ccwdev.h> #include "dasd_int.h" @@ -89,17 +90,22 @@ dasd_eckd_probe (struct ccw_device *cdev) { int ret; - ret = dasd_generic_probe (cdev, &dasd_eckd_discipline); - if (ret) + /* set ECKD specific ccw-device options */ + ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE); + if (ret) { + printk(KERN_WARNING + "dasd_eckd_probe: could not set ccw-device options " + "for %s\n", cdev->dev.bus_id); return ret; - ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | CCWDEV_ALLOW_FORCE); - return 0; + } + ret = dasd_generic_probe(cdev, &dasd_eckd_discipline); + return ret; } static int dasd_eckd_set_online(struct ccw_device *cdev) { - return dasd_generic_set_online (cdev, &dasd_eckd_discipline); + return dasd_generic_set_online(cdev, &dasd_eckd_discipline); } static struct ccw_driver dasd_eckd_driver = { @@ -210,14 +216,14 @@ check_XRC (struct ccw1 *de_ccw, /* switch on System Time Stamp - needed for XRC Support */ if (private->rdc_data.facilities.XRC_supported) { - + data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ - + data->ep_sys_time = get_clock (); - + de_ccw->count = sizeof (struct DE_eckd_data); - de_ccw->flags |= CCW_FLAG_SLI; + de_ccw->flags |= CCW_FLAG_SLI; } return; @@ -296,8 +302,8 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, /* check for sequential prestage - enhance cylinder range */ if (data->attributes.operation == DASD_SEQ_PRESTAGE || data->attributes.operation == DASD_SEQ_ACCESS) { - - if (end.cyl + private->attrib.nr_cyl < geo.cyl) + + if (end.cyl + private->attrib.nr_cyl < geo.cyl) end.cyl += private->attrib.nr_cyl; else end.cyl = (geo.cyl - 1); @@ -317,7 +323,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk, struct dasd_eckd_private *private; int sector; int dn, d; - + private = (struct dasd_eckd_private *) device->private; DBF_DEV_EVENT(DBF_INFO, device, @@ -541,6 +547,86 @@ dasd_eckd_read_conf(struct dasd_device *device) } /* + * Build CP for Perform Subsystem Function - SSC. + */ +struct dasd_ccw_req * +dasd_eckd_build_psf_ssc(struct dasd_device *device) +{ + struct dasd_ccw_req *cqr; + struct dasd_psf_ssc_data *psf_ssc_data; + struct ccw1 *ccw; + + cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ , + sizeof(struct dasd_psf_ssc_data), + device); + + if (IS_ERR(cqr)) { + DEV_MESSAGE(KERN_WARNING, device, "%s", + "Could not allocate PSF-SSC request"); + return cqr; + } + psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data; + psf_ssc_data->order = PSF_ORDER_SSC; + psf_ssc_data->suborder = 0x08; + + ccw = cqr->cpaddr; + ccw->cmd_code = DASD_ECKD_CCW_PSF; + ccw->cda = (__u32)(addr_t)psf_ssc_data; + ccw->count = 66; + + cqr->device = device; + cqr->expires = 10*HZ; + cqr->buildclk = get_clock(); + cqr->status = DASD_CQR_FILLED; + return cqr; +} + +/* + * Perform Subsystem Function. + * It is necessary to trigger CIO for channel revalidation since this + * call might change behaviour of DASD devices. + */ +static int +dasd_eckd_psf_ssc(struct dasd_device *device) +{ + struct dasd_ccw_req *cqr; + int rc; + + cqr = dasd_eckd_build_psf_ssc(device); + if (IS_ERR(cqr)) + return PTR_ERR(cqr); + + rc = dasd_sleep_on(cqr); + if (!rc) + /* trigger CIO to reprobe devices */ + css_schedule_reprobe(); + dasd_sfree_request(cqr, cqr->device); + return rc; +} + +/* + * Valide storage server of current device. + */ +static int +dasd_eckd_validate_server(struct dasd_device *device) +{ + int rc; + + /* Currently PAV is the only reason to 'validate' server on LPAR */ + if (dasd_nopav || MACHINE_IS_VM) + return 0; + + rc = dasd_eckd_psf_ssc(device); + if (rc) + /* may be requested feature is not available on server, + * therefore just report error and go ahead */ + DEV_MESSAGE(KERN_INFO, device, + "Perform Subsystem Function returned rc=%d", rc); + /* RE-Read Configuration Data */ + return dasd_eckd_read_conf(device); +} + +/* * Check device characteristics. * If the device is accessible using ECKD discipline, the device is enabled. */ @@ -554,7 +640,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device) private = (struct dasd_eckd_private *) device->private; if (private == NULL) { - private = kmalloc(sizeof(struct dasd_eckd_private), + private = kzalloc(sizeof(struct dasd_eckd_private), GFP_KERNEL | GFP_DMA); if (private == NULL) { DEV_MESSAGE(KERN_WARNING, device, "%s", @@ -562,7 +648,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device) "data"); return -ENOMEM; } - memset(private, 0, sizeof(struct dasd_eckd_private)); device->private = (void *) private; } /* Invalidate status of initial analysis. */ @@ -571,16 +656,29 @@ dasd_eckd_check_characteristics(struct dasd_device *device) private->attrib.operation = DASD_NORMAL_CACHE; private->attrib.nr_cyl = 0; + /* Read Configuration Data */ + rc = dasd_eckd_read_conf(device); + if (rc) + return rc; + + /* Generate device unique id and register in devmap */ + rc = dasd_eckd_generate_uid(device, &uid); + if (rc) + return rc; + rc = dasd_set_uid(device->cdev, &uid); + if (rc == 1) /* new server found */ + rc = dasd_eckd_validate_server(device); + if (rc) + return rc; + /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); memset(rdc_data, 0, sizeof(rdc_data)); rc = read_dev_chars(device->cdev, &rdc_data, 64); - if (rc) { + if (rc) DEV_MESSAGE(KERN_WARNING, device, - "Read device characteristics returned error %d", - rc); - return rc; - } + "Read device characteristics returned " + "rc=%d", rc); DEV_MESSAGE(KERN_INFO, device, "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d", @@ -591,19 +689,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device) private->rdc_data.no_cyl, private->rdc_data.trk_per_cyl, private->rdc_data.sec_per_trk); - - /* Read Configuration Data */ - rc = dasd_eckd_read_conf (device); - if (rc) - return rc; - - /* Generate device unique id and register in devmap */ - rc = dasd_eckd_generate_uid(device, &uid); - if (rc) - return rc; - - rc = dasd_set_uid(device->cdev, &uid); - return rc; } @@ -773,7 +858,7 @@ dasd_eckd_end_analysis(struct dasd_device *device) ((private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl * blk_per_trk * (device->bp_block >> 9)) >> 1), - ((blk_per_trk * device->bp_block) >> 10), + ((blk_per_trk * device->bp_block) >> 10), private->uses_cdl ? "compatible disk layout" : "linux disk layout"); @@ -970,7 +1055,7 @@ dasd_eckd_format_device(struct dasd_device * device, if (i < 3) { ect->kl = 4; ect->dl = sizes_trk0[i] - 4; - } + } } if ((fdata->intensity & 0x08) && fdata->start_unit == 1) { @@ -1270,7 +1355,7 @@ dasd_eckd_fill_info(struct dasd_device * device, /* * Release device ioctl. - * Buils a channel programm to releases a prior reserved + * Buils a channel programm to releases a prior reserved * (see dasd_eckd_reserve) device. */ static int @@ -1310,8 +1395,8 @@ dasd_eckd_release(struct dasd_device *device) /* * Reserve device ioctl. * Options are set to 'synchronous wait for interrupt' and - * 'timeout the request'. This leads to a terminate IO if - * the interrupt is outstanding for a certain time. + * 'timeout the request'. This leads to a terminate IO if + * the interrupt is outstanding for a certain time. */ static int dasd_eckd_reserve(struct dasd_device *device) @@ -1349,7 +1434,7 @@ dasd_eckd_reserve(struct dasd_device *device) /* * Steal lock ioctl - unconditional reserve device. - * Buils a channel programm to break a device's reservation. + * Buils a channel programm to break a device's reservation. * (unconditional reserve) */ static int @@ -1522,6 +1607,40 @@ dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp) } /* + * Dump the range of CCWs into 'page' buffer + * and return number of printed chars. + */ +static inline int +dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page) +{ + int len, count; + char *datap; + + len = 0; + while (from <= to) { + len += sprintf(page + len, KERN_ERR PRINTK_HEADER + " CCW %p: %08X %08X DAT:", + from, ((int *) from)[0], ((int *) from)[1]); + + /* get pointer to data (consider IDALs) */ + if (from->flags & CCW_FLAG_IDA) + datap = (char *) *((addr_t *) (addr_t) from->cda); + else + datap = (char *) ((addr_t) from->cda); + + /* dump data (max 32 bytes) */ + for (count = 0; count < from->count && count < 32; count++) { + if (count % 8 == 0) len += sprintf(page + len, " "); + if (count % 4 == 0) len += sprintf(page + len, " "); + len += sprintf(page + len, "%02x", datap[count]); + } + len += sprintf(page + len, "\n"); + from++; + } + return len; +} + +/* * Print sense data and related channel program. * Parts are printed because printk buffer is only 1024 bytes. */ @@ -1530,8 +1649,8 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, struct irb *irb) { char *page; - struct ccw1 *act, *end, *last; - int len, sl, sct, count; + struct ccw1 *first, *last, *fail, *from, *to; + int len, sl, sct; page = (char *) get_zeroed_page(GFP_ATOMIC); if (page == NULL) { @@ -1539,7 +1658,8 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, "No memory to dump sense data"); return; } - len = sprintf(page, KERN_ERR PRINTK_HEADER + /* dump the sense data */ + len = sprintf(page, KERN_ERR PRINTK_HEADER " I/O status report for device %s:\n", device->cdev->dev.bus_id); len += sprintf(page + len, KERN_ERR PRINTK_HEADER @@ -1564,87 +1684,55 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, if (irb->ecw[27] & DASD_SENSE_BIT_0) { /* 24 Byte Sense Data */ - len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " 24 Byte: %x MSG %x, " - "%s MSGb to SYSOP\n", - irb->ecw[7] >> 4, irb->ecw[7] & 0x0f, - irb->ecw[1] & 0x10 ? "" : "no"); + sprintf(page + len, KERN_ERR PRINTK_HEADER + " 24 Byte: %x MSG %x, " + "%s MSGb to SYSOP\n", + irb->ecw[7] >> 4, irb->ecw[7] & 0x0f, + irb->ecw[1] & 0x10 ? "" : "no"); } else { /* 32 Byte Sense Data */ - len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " 32 Byte: Format: %x " - "Exception class %x\n", - irb->ecw[6] & 0x0f, irb->ecw[22] >> 4); + sprintf(page + len, KERN_ERR PRINTK_HEADER + " 32 Byte: Format: %x " + "Exception class %x\n", + irb->ecw[6] & 0x0f, irb->ecw[22] >> 4); } } else { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " SORRY - NO VALID SENSE AVAILABLE\n"); + sprintf(page + len, KERN_ERR PRINTK_HEADER + " SORRY - NO VALID SENSE AVAILABLE\n"); } - MESSAGE_LOG(KERN_ERR, "%s", - page + sizeof(KERN_ERR PRINTK_HEADER)); - - /* dump the Channel Program */ - /* print first CCWs (maximum 8) */ - act = req->cpaddr; - for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); - end = min(act + 8, last); - len = sprintf(page, KERN_ERR PRINTK_HEADER + printk("%s", page); + + /* dump the Channel Program (max 140 Bytes per line) */ + /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */ + first = req->cpaddr; + for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++); + to = min(first + 6, last); + len = sprintf(page, KERN_ERR PRINTK_HEADER " Related CP in req: %p\n", req); - while (act <= end) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " CCW %p: %08X %08X DAT:", - act, ((int *) act)[0], ((int *) act)[1]); - for (count = 0; count < 32 && count < act->count; - count += sizeof(int)) - len += sprintf(page + len, " %08X", - ((int *) (addr_t) act->cda) - [(count>>2)]); - len += sprintf(page + len, "\n"); - act++; - } - MESSAGE_LOG(KERN_ERR, "%s", - page + sizeof(KERN_ERR PRINTK_HEADER)); + dasd_eckd_dump_ccw_range(first, to, page + len); + printk("%s", page); - /* print failing CCW area */ + /* print failing CCW area (maximum 4) */ + /* scsw->cda is either valid or zero */ len = 0; - if (act < ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) { - act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2; - len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); - } - end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last); - while (act <= end) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " CCW %p: %08X %08X DAT:", - act, ((int *) act)[0], ((int *) act)[1]); - for (count = 0; count < 32 && count < act->count; - count += sizeof(int)) - len += sprintf(page + len, " %08X", - ((int *) (addr_t) act->cda) - [(count>>2)]); - len += sprintf(page + len, "\n"); - act++; + from = ++to; + fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */ + if (from < fail - 2) { + from = fail - 2; /* there is a gap - print header */ + len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n"); } + to = min(fail + 1, last); + len += dasd_eckd_dump_ccw_range(from, to, page + len); - /* print last CCWs */ - if (act < last - 2) { - act = last - 2; + /* print last CCWs (maximum 2) */ + from = max(from, ++to); + if (from < last - 1) { + from = last - 1; /* there is a gap - print header */ len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); } - while (act <= last) { - len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " CCW %p: %08X %08X DAT:", - act, ((int *) act)[0], ((int *) act)[1]); - for (count = 0; count < 32 && count < act->count; - count += sizeof(int)) - len += sprintf(page + len, " %08X", - ((int *) (addr_t) act->cda) - [(count>>2)]); - len += sprintf(page + len, "\n"); - act++; - } + len += dasd_eckd_dump_ccw_range(from, last, page + len); if (len > 0) - MESSAGE_LOG(KERN_ERR, "%s", - page + sizeof(KERN_ERR PRINTK_HEADER)); + printk("%s", page); free_page((unsigned long) page); } @@ -1685,14 +1773,8 @@ static struct dasd_discipline dasd_eckd_discipline = { static int __init dasd_eckd_init(void) { - int ret; - ASCEBC(dasd_eckd_discipline.ebcname, 4); - - ret = ccw_driver_register(&dasd_eckd_driver); - if (!ret) - dasd_generic_auto_online(&dasd_eckd_driver); - return ret; + return ccw_driver_register(&dasd_eckd_driver); } static void __exit @@ -1703,22 +1785,3 @@ dasd_eckd_cleanup(void) module_init(dasd_eckd_init); module_exit(dasd_eckd_cleanup); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: 1 - * tab-width: 8 - * End: - */ diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index d5734e976e1c..712ff1650134 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -1,7 +1,7 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_eckd.h * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> - * Horst Hummel <Horst.Hummel@de.ibm.com> + * Horst Hummel <Horst.Hummel@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * @@ -41,9 +41,10 @@ #define DASD_ECKD_CCW_RESERVE 0xB4 /* - *Perform Subsystem Function / Sub-Orders + * Perform Subsystem Function / Sub-Orders */ -#define PSF_ORDER_PRSSD 0x18 +#define PSF_ORDER_PRSSD 0x18 +#define PSF_ORDER_SSC 0x1D /***************************************************************************** * SECTION: Type Definitions @@ -155,7 +156,7 @@ struct dasd_eckd_characteristics { unsigned char reserved2:4; unsigned char reserved3:8; unsigned char defect_wr:1; - unsigned char XRC_supported:1; + unsigned char XRC_supported:1; unsigned char reserved4:1; unsigned char striping:1; unsigned char reserved5:4; @@ -343,7 +344,7 @@ struct dasd_eckd_path { }; /* - * Perform Subsystem Function - Prepare for Read Subsystem Data + * Perform Subsystem Function - Prepare for Read Subsystem Data */ struct dasd_psf_prssd_data { unsigned char order; @@ -353,4 +354,15 @@ struct dasd_psf_prssd_data { unsigned char varies[9]; } __attribute__ ((packed)); +/* + * Perform Subsystem Function - Set Subsystem Characteristics + */ +struct dasd_psf_ssc_data { + unsigned char order; + unsigned char flags; + unsigned char cu_type[4]; + unsigned char suborder; + unsigned char reserved[59]; +} __attribute__((packed)); + #endif /* DASD_ECKD_H */ diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index 2d8af709947f..da65f1b032f5 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -276,7 +276,7 @@ struct dasd_eer_header { __u64 tv_sec; __u64 tv_usec; char busid[DASD_EER_BUSID_SIZE]; -}; +} __attribute__ ((packed)); /* * The following function can be used for those triggers that have @@ -521,6 +521,8 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) unsigned long flags; eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL); + if (!eerb) + return -ENOMEM; eerb->buffer_page_count = eer_pages; if (eerb->buffer_page_count < 1 || eerb->buffer_page_count > INT_MAX / PAGE_SIZE) { diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index b842377cb0c6..4108d96f6a5a 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -90,7 +90,7 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr) /* just retry - there is nothing to save ... I got no sense data.... */ if (cqr->retries > 0) { - DEV_MESSAGE (KERN_DEBUG, device, + DEV_MESSAGE (KERN_DEBUG, device, "default ERP called (%i retries left)", cqr->retries); cqr->lpm = LPM_ANYPATH; @@ -155,7 +155,7 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr) /* * Print the hex dump of the memory used by a request. This includes - * all error recovery ccws that have been chained in from of the + * all error recovery ccws that have been chained in from of the * real request. */ static inline void @@ -227,12 +227,12 @@ dasd_log_ccw(struct dasd_ccw_req * cqr, int caller, __u32 cpa) /* * Log bytes arround failed CCW but only if we did * not log the whole CP of the CCW is outside the - * logged CP. + * logged CP. */ if (cplength > 40 || ((addr_t) cpa < (addr_t) lcqr->cpaddr && (addr_t) cpa > (addr_t) (lcqr->cpaddr + cplength + 4))) { - + DEV_MESSAGE(KERN_ERR, device, "Failed CCW (%p) (area):", (void *) (long) cpa); diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 91145698f8e9..bb7755b9b19d 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -1,4 +1,4 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_fba.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> @@ -56,19 +56,13 @@ static struct ccw_driver dasd_fba_driver; /* see below */ static int dasd_fba_probe(struct ccw_device *cdev) { - int ret; - - ret = dasd_generic_probe (cdev, &dasd_fba_discipline); - if (ret) - return ret; - ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); - return 0; + return dasd_generic_probe(cdev, &dasd_fba_discipline); } static int dasd_fba_set_online(struct ccw_device *cdev) { - return dasd_generic_set_online (cdev, &dasd_fba_discipline); + return dasd_generic_set_online(cdev, &dasd_fba_discipline); } static struct ccw_driver dasd_fba_driver = { @@ -125,13 +119,13 @@ static int dasd_fba_check_characteristics(struct dasd_device *device) { struct dasd_fba_private *private; - struct ccw_device *cdev = device->cdev; + struct ccw_device *cdev = device->cdev; void *rdc_data; int rc; private = (struct dasd_fba_private *) device->private; if (private == NULL) { - private = kmalloc(sizeof(struct dasd_fba_private), GFP_KERNEL); + private = kzalloc(sizeof(struct dasd_fba_private), GFP_KERNEL); if (private == NULL) { DEV_MESSAGE(KERN_WARNING, device, "%s", "memory allocation failed for private " @@ -204,7 +198,7 @@ dasd_fba_examine_error(struct dasd_ccw_req * cqr, struct irb * irb) if (irb->scsw.cstat == 0x00 && irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) return dasd_era_none; - + cdev = device->cdev; switch (cdev->id.dev_type) { case 0x3370: @@ -539,7 +533,7 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, * 8192 bytes (=2 pages). For 64 bit one dasd_mchunkt_t structure has * 24 bytes, the struct dasd_ccw_req has 136 bytes and each block can use * up to 16 bytes (8 for the ccw and 8 for the idal pointer). In - * addition we have one define extent ccw + 16 bytes of data and a + * addition we have one define extent ccw + 16 bytes of data and a * locate record ccw for each block (stupid devices!) + 16 bytes of data. * That makes: * (8192 - 24 - 136 - 8 - 16) / 40 = 200.2 blocks at maximum. @@ -569,16 +563,8 @@ static struct dasd_discipline dasd_fba_discipline = { static int __init dasd_fba_init(void) { - int ret; - ASCEBC(dasd_fba_discipline.ebcname, 4); - - ret = ccw_driver_register(&dasd_fba_driver); - if (ret) - return ret; - - dasd_generic_auto_online(&dasd_fba_driver); - return 0; + return ccw_driver_register(&dasd_fba_driver); } static void __exit @@ -589,22 +575,3 @@ dasd_fba_cleanup(void) module_init(dasd_fba_init); module_exit(dasd_fba_cleanup); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: 1 - * tab-width: 8 - * End: - */ diff --git a/drivers/s390/block/dasd_fba.h b/drivers/s390/block/dasd_fba.h index da1fa91fc01d..14c910baa5fe 100644 --- a/drivers/s390/block/dasd_fba.h +++ b/drivers/s390/block/dasd_fba.h @@ -1,4 +1,4 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_fba.h * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index d4b13e300a76..03a83efc34c4 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -1,7 +1,7 @@ -/* +/* * File...........: linux/drivers/s390/block/dasd_int.h * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> - * Horst Hummel <Horst.Hummel@de.ibm.com> + * Horst Hummel <Horst.Hummel@de.ibm.com> * Martin Schwidefsky <schwidefsky@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 @@ -186,7 +186,7 @@ struct dasd_ccw_req { void *callback_data; }; -/* +/* * dasd_ccw_req -> status can be: */ #define DASD_CQR_FILLED 0x00 /* request is ready to be processed */ @@ -248,7 +248,7 @@ struct dasd_discipline { /* * Error recovery functions. examine_error() returns a value that * indicates what to do for an error condition. If examine_error() - * returns 'dasd_era_recover' erp_action() is called to create a + * returns 'dasd_era_recover' erp_action() is called to create a * special error recovery ccw. erp_postaction() is called after * an error recovery ccw has finished its execution. dump_sense * is called for every error condition to print the sense data @@ -302,11 +302,11 @@ struct dasd_device { spinlock_t request_queue_lock; struct block_device *bdev; unsigned int devindex; - unsigned long blocks; /* size of volume in blocks */ - unsigned int bp_block; /* bytes per block */ - unsigned int s2b_shift; /* log2 (bp_block/512) */ - unsigned long flags; /* per device flags */ - unsigned short features; /* copy of devmap-features (read-only!) */ + unsigned long blocks; /* size of volume in blocks */ + unsigned int bp_block; /* bytes per block */ + unsigned int s2b_shift; /* log2 (bp_block/512) */ + unsigned long flags; /* per device flags */ + unsigned short features; /* copy of devmap-features (read-only!) */ /* extended error reporting stuff (eer) */ struct dasd_ccw_req *eer_cqr; @@ -513,12 +513,12 @@ void dasd_generic_remove (struct ccw_device *cdev); int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *); int dasd_generic_set_offline (struct ccw_device *cdev); int dasd_generic_notify(struct ccw_device *, int); -void dasd_generic_auto_online (struct ccw_driver *); /* externals in dasd_devmap.c */ extern int dasd_max_devindex; extern int dasd_probeonly; extern int dasd_autodetect; +extern int dasd_nopav; int dasd_devmap_init(void); void dasd_devmap_exit(void); @@ -606,22 +606,3 @@ static inline int dasd_eer_enabled(struct dasd_device *device) #endif /* __KERNEL__ */ #endif /* DASD_H */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: 1 - * tab-width: 8 - * End: - */ diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index b8c80d28df41..302bcd0f28be 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -90,10 +90,10 @@ static int dasd_ioctl_quiesce(struct dasd_device *device) { unsigned long flags; - + if (!capable (CAP_SYS_ADMIN)) return -EACCES; - + DEV_MESSAGE (KERN_DEBUG, device, "%s", "Quiesce IO on device"); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); @@ -110,13 +110,13 @@ static int dasd_ioctl_resume(struct dasd_device *device) { unsigned long flags; - - if (!capable (CAP_SYS_ADMIN)) + + if (!capable (CAP_SYS_ADMIN)) return -EACCES; DEV_MESSAGE (KERN_DEBUG, device, "%s", "resume IO on device"); - + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); device->stopped &= ~DASD_STOPPED_QUIESCE; spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); @@ -287,7 +287,7 @@ dasd_ioctl_information(struct dasd_device *device, dasd_info->open_count = atomic_read(&device->open_count); if (!device->bdev) dasd_info->open_count++; - + /* * check if device is really formatted * LDL / CDL was returned by 'fill_info' diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index eecb2afad5c2..3c1314b7391b 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -50,6 +50,9 @@ struct raw3270 { unsigned char *ascebc; /* ascii -> ebcdic table */ struct class_device *clttydev; /* 3270-class tty device ptr */ struct class_device *cltubdev; /* 3270-class tub device ptr */ + + struct raw3270_request init_request; + unsigned char init_data[256]; }; /* raw3270->flags */ @@ -484,8 +487,6 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */ } __attribute__ ((packed)) aua; } __attribute__ ((packed)); -static unsigned char raw3270_init_data[256]; -static struct raw3270_request raw3270_init_request; static struct diag210 raw3270_init_diag210; static DECLARE_MUTEX(raw3270_init_sem); @@ -644,17 +645,17 @@ __raw3270_size_device(struct raw3270 *rp) * required (3270 device switched to 'stand-by') and command * rejects (old devices that can't do 'read partition'). */ - memset(&raw3270_init_request, 0, sizeof(raw3270_init_request)); - memset(raw3270_init_data, 0, sizeof(raw3270_init_data)); - /* Store 'read partition' data stream to raw3270_init_data */ - memcpy(raw3270_init_data, wbuf, sizeof(wbuf)); - INIT_LIST_HEAD(&raw3270_init_request.list); - raw3270_init_request.ccw.cmd_code = TC_WRITESF; - raw3270_init_request.ccw.flags = CCW_FLAG_SLI; - raw3270_init_request.ccw.count = sizeof(wbuf); - raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data); - - rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request); + memset(&rp->init_request, 0, sizeof(rp->init_request)); + memset(&rp->init_data, 0, 256); + /* Store 'read partition' data stream to init_data */ + memcpy(&rp->init_data, wbuf, sizeof(wbuf)); + INIT_LIST_HEAD(&rp->init_request.list); + rp->init_request.ccw.cmd_code = TC_WRITESF; + rp->init_request.ccw.flags = CCW_FLAG_SLI; + rp->init_request.ccw.count = sizeof(wbuf); + rp->init_request.ccw.cda = (__u32) __pa(&rp->init_data); + + rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); if (rc) /* Check error cases: -ERESTARTSYS, -EIO and -EOPNOTSUPP */ return rc; @@ -679,18 +680,18 @@ __raw3270_size_device(struct raw3270 *rp) * The device accepted the 'read partition' command. Now * set up a read ccw and issue it. */ - raw3270_init_request.ccw.cmd_code = TC_READMOD; - raw3270_init_request.ccw.flags = CCW_FLAG_SLI; - raw3270_init_request.ccw.count = sizeof(raw3270_init_data); - raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data); - rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request); + rp->init_request.ccw.cmd_code = TC_READMOD; + rp->init_request.ccw.flags = CCW_FLAG_SLI; + rp->init_request.ccw.count = sizeof(rp->init_data); + rp->init_request.ccw.cda = (__u32) __pa(rp->init_data); + rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); if (rc) return rc; /* Got a Query Reply */ - count = sizeof(raw3270_init_data) - raw3270_init_request.rescnt; - uap = (struct raw3270_ua *) (raw3270_init_data + 1); + count = sizeof(rp->init_data) - rp->init_request.rescnt; + uap = (struct raw3270_ua *) (rp->init_data + 1); /* Paranoia check. */ - if (raw3270_init_data[0] != 0x88 || uap->uab.qcode != 0x81) + if (rp->init_data[0] != 0x88 || uap->uab.qcode != 0x81) return -EOPNOTSUPP; /* Copy rows/columns of default Usable Area */ rp->rows = uap->uab.h; @@ -749,18 +750,18 @@ raw3270_reset_device(struct raw3270 *rp) int rc; down(&raw3270_init_sem); - memset(&raw3270_init_request, 0, sizeof(raw3270_init_request)); - memset(raw3270_init_data, 0, sizeof(raw3270_init_data)); - /* Store reset data stream to raw3270_init_data/raw3270_init_request */ - raw3270_init_data[0] = TW_KR; - INIT_LIST_HEAD(&raw3270_init_request.list); - raw3270_init_request.ccw.cmd_code = TC_EWRITEA; - raw3270_init_request.ccw.flags = CCW_FLAG_SLI; - raw3270_init_request.ccw.count = 1; - raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data); + memset(&rp->init_request, 0, sizeof(rp->init_request)); + memset(&rp->init_data, 0, sizeof(rp->init_data)); + /* Store reset data stream to init_data/init_request */ + rp->init_data[0] = TW_KR; + INIT_LIST_HEAD(&rp->init_request.list); + rp->init_request.ccw.cmd_code = TC_EWRITEA; + rp->init_request.ccw.flags = CCW_FLAG_SLI; + rp->init_request.ccw.count = 1; + rp->init_request.ccw.cda = (__u32) __pa(rp->init_data); rp->view = &raw3270_init_view; raw3270_init_view.dev = rp; - rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request); + rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); raw3270_init_view.dev = 0; rp->view = 0; up(&raw3270_init_sem); @@ -854,7 +855,7 @@ raw3270_setup_console(struct ccw_device *cdev) char *ascebc; int rc; - rp = (struct raw3270 *) alloc_bootmem(sizeof(struct raw3270)); + rp = (struct raw3270 *) alloc_bootmem_low(sizeof(struct raw3270)); ascebc = (char *) alloc_bootmem(256); rc = raw3270_setup_device(cdev, rp, ascebc); if (rc) @@ -895,7 +896,7 @@ raw3270_create_device(struct ccw_device *cdev) char *ascebc; int rc; - rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL); + rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA); if (!rp) return ERR_PTR(-ENOMEM); ascebc = kmalloc(256, GFP_KERNEL); diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index 0960bef7b199..15b895496a45 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -224,39 +224,6 @@ is_blacklisted (int ssid, int devno) } #ifdef CONFIG_PROC_FS -static int -__s390_redo_validation(struct subchannel_id schid, void *data) -{ - int ret; - struct subchannel *sch; - - sch = get_subchannel_by_schid(schid); - if (sch) { - /* Already known. */ - put_device(&sch->dev); - return 0; - } - ret = css_probe_device(schid); - if (ret == -ENXIO) - return ret; /* We're through. */ - if (ret == -ENOMEM) - /* Stop validation for now. Bad, but no need for a panic. */ - return ret; - return 0; -} - -/* - * Function: s390_redo_validation - * Look for no longer blacklisted devices - * FIXME: there must be a better way to do this */ -static inline void -s390_redo_validation (void) -{ - CIO_TRACE_EVENT (0, "redoval"); - - for_each_subchannel(__s390_redo_validation, NULL); -} - /* * Function: blacklist_parse_proc_parameters * parse the stuff which is piped to /proc/cio_ignore @@ -281,7 +248,7 @@ blacklist_parse_proc_parameters (char *buf) return; } - s390_redo_validation (); + css_schedule_reprobe(); } /* Iterator struct for all devices. */ diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index bdfee7fbaa2e..c7319a07ba35 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -404,21 +404,24 @@ ccwgroup_driver_register (struct ccwgroup_driver *cdriver) } static int -__ccwgroup_driver_unregister_device(struct device *dev, void *data) +__ccwgroup_match_all(struct device *dev, void *data) { - __ccwgroup_remove_symlinks(to_ccwgroupdev(dev)); - device_unregister(dev); - put_device(dev); - return 0; + return 1; } void ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver) { + struct device *dev; + /* We don't want ccwgroup devices to live longer than their driver. */ get_driver(&cdriver->driver); - driver_for_each_device(&cdriver->driver, NULL, NULL, - __ccwgroup_driver_unregister_device); + while ((dev = driver_find_device(&cdriver->driver, NULL, NULL, + __ccwgroup_match_all))) { + __ccwgroup_remove_symlinks(to_ccwgroupdev(dev)); + device_unregister(dev); + put_device(dev); + } put_driver(&cdriver->driver); driver_unregister(&cdriver->driver); } diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 72187e54dcac..b00f3ed051a0 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -244,8 +244,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) && (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) && - (sch->schib.pmcw.lpum == mask) && - (sch->vpm == 0)) { + (sch->schib.pmcw.lpum == mask)) { int cc; cc = cio_clear(sch); @@ -918,12 +917,13 @@ chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count) chp = to_channelpath(container_of(kobj, struct device, kobj)); css = to_css(chp->dev.parent); - size = sizeof(struct cmg_chars); + size = sizeof(struct cmg_entry); /* Only allow single reads. */ if (off || count < size) return 0; chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id); + count = size; return count; } diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 07ef3f640f4a..1c3e8e9012b0 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -3,9 +3,10 @@ * * Linux on zSeries Channel Measurement Facility support * - * Copyright 2000,2003 IBM Corporation + * Copyright 2000,2006 IBM Corporation * - * Author: Arnd Bergmann <arndb@de.ibm.com> + * Authors: Arnd Bergmann <arndb@de.ibm.com> + * Cornelia Huck <cornelia.huck@de.ibm.com> * * original idea from Natarajan Krishnaswami <nkrishna@us.ibm.com> * @@ -96,9 +97,9 @@ module_param(format, bool, 0444); /** * struct cmb_operations - functions to use depending on cmb_format * - * all these functions operate on a struct cmf_device. There is only - * one instance of struct cmb_operations because all cmf_device - * objects are guaranteed to be of the same type. + * Most of these functions operate on a struct ccw_device. There is only + * one instance of struct cmb_operations because the format of the measurement + * data is guaranteed to be the same for every ccw_device. * * @alloc: allocate memory for a channel measurement block, * either with the help of a special pool or with kmalloc @@ -107,6 +108,7 @@ module_param(format, bool, 0444); * @readall: read a measurement block in a common format * @reset: clear the data in the associated measurement block and * reset its time stamp + * @align: align an allocated block so that the hardware can use it */ struct cmb_operations { int (*alloc) (struct ccw_device*); @@ -115,11 +117,19 @@ struct cmb_operations { u64 (*read) (struct ccw_device*, int); int (*readall)(struct ccw_device*, struct cmbdata *); void (*reset) (struct ccw_device*); + void * (*align) (void *); struct attribute_group *attr_group; }; static struct cmb_operations *cmbops; +struct cmb_data { + void *hw_block; /* Pointer to block updated by hardware */ + void *last_block; /* Last changed block copied from hardware block */ + int size; /* Size of hw_block and last_block */ + unsigned long long last_update; /* when last_block was updated */ +}; + /* our user interface is designed in terms of nanoseconds, * while the hardware measures total times in its own * unit.*/ @@ -226,63 +236,229 @@ struct set_schib_struct { unsigned long address; wait_queue_head_t wait; int ret; + struct kref kref; }; +static void cmf_set_schib_release(struct kref *kref) +{ + struct set_schib_struct *set_data; + + set_data = container_of(kref, struct set_schib_struct, kref); + kfree(set_data); +} + +#define CMF_PENDING 1 + static int set_schib_wait(struct ccw_device *cdev, u32 mme, int mbfc, unsigned long address) { - struct set_schib_struct s = { - .mme = mme, - .mbfc = mbfc, - .address = address, - .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s.wait), - }; + struct set_schib_struct *set_data; + int ret; spin_lock_irq(cdev->ccwlock); - s.ret = set_schib(cdev, mme, mbfc, address); - if (s.ret != -EBUSY) { - goto out_nowait; + if (!cdev->private->cmb) { + ret = -ENODEV; + goto out; } + set_data = kzalloc(sizeof(struct set_schib_struct), GFP_ATOMIC); + if (!set_data) { + ret = -ENOMEM; + goto out; + } + init_waitqueue_head(&set_data->wait); + kref_init(&set_data->kref); + set_data->mme = mme; + set_data->mbfc = mbfc; + set_data->address = address; + + ret = set_schib(cdev, mme, mbfc, address); + if (ret != -EBUSY) + goto out_put; if (cdev->private->state != DEV_STATE_ONLINE) { - s.ret = -EBUSY; /* if the device is not online, don't even try again */ - goto out_nowait; + ret = -EBUSY; + goto out_put; } + cdev->private->state = DEV_STATE_CMFCHANGE; - cdev->private->cmb_wait = &s; - s.ret = 1; + set_data->ret = CMF_PENDING; + cdev->private->cmb_wait = set_data; spin_unlock_irq(cdev->ccwlock); - if (wait_event_interruptible(s.wait, s.ret != 1)) { + if (wait_event_interruptible(set_data->wait, + set_data->ret != CMF_PENDING)) { spin_lock_irq(cdev->ccwlock); - if (s.ret == 1) { - s.ret = -ERESTARTSYS; - cdev->private->cmb_wait = 0; + if (set_data->ret == CMF_PENDING) { + set_data->ret = -ERESTARTSYS; if (cdev->private->state == DEV_STATE_CMFCHANGE) cdev->private->state = DEV_STATE_ONLINE; } spin_unlock_irq(cdev->ccwlock); } - return s.ret; - -out_nowait: + spin_lock_irq(cdev->ccwlock); + cdev->private->cmb_wait = NULL; + ret = set_data->ret; +out_put: + kref_put(&set_data->kref, cmf_set_schib_release); +out: spin_unlock_irq(cdev->ccwlock); - return s.ret; + return ret; } void retry_set_schib(struct ccw_device *cdev) { - struct set_schib_struct *s; + struct set_schib_struct *set_data; + + set_data = cdev->private->cmb_wait; + if (!set_data) { + WARN_ON(1); + return; + } + kref_get(&set_data->kref); + set_data->ret = set_schib(cdev, set_data->mme, set_data->mbfc, + set_data->address); + wake_up(&set_data->wait); + kref_put(&set_data->kref, cmf_set_schib_release); +} + +static int cmf_copy_block(struct ccw_device *cdev) +{ + struct subchannel *sch; + void *reference_buf; + void *hw_block; + struct cmb_data *cmb_data; + + sch = to_subchannel(cdev->dev.parent); + + if (stsch(sch->schid, &sch->schib)) + return -ENODEV; + + if (sch->schib.scsw.fctl & SCSW_FCTL_START_FUNC) { + /* Don't copy if a start function is in progress. */ + if ((!sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED) && + (sch->schib.scsw.actl & + (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) && + (!sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) + return -EBUSY; + } + cmb_data = cdev->private->cmb; + hw_block = cmbops->align(cmb_data->hw_block); + if (!memcmp(cmb_data->last_block, hw_block, cmb_data->size)) + /* No need to copy. */ + return 0; + reference_buf = kzalloc(cmb_data->size, GFP_ATOMIC); + if (!reference_buf) + return -ENOMEM; + /* Ensure consistency of block copied from hardware. */ + do { + memcpy(cmb_data->last_block, hw_block, cmb_data->size); + memcpy(reference_buf, hw_block, cmb_data->size); + } while (memcmp(cmb_data->last_block, reference_buf, cmb_data->size)); + cmb_data->last_update = get_clock(); + kfree(reference_buf); + return 0; +} + +struct copy_block_struct { + wait_queue_head_t wait; + int ret; + struct kref kref; +}; + +static void cmf_copy_block_release(struct kref *kref) +{ + struct copy_block_struct *copy_block; + + copy_block = container_of(kref, struct copy_block_struct, kref); + kfree(copy_block); +} + +static int cmf_cmb_copy_wait(struct ccw_device *cdev) +{ + struct copy_block_struct *copy_block; + int ret; + unsigned long flags; + + spin_lock_irqsave(cdev->ccwlock, flags); + if (!cdev->private->cmb) { + ret = -ENODEV; + goto out; + } + copy_block = kzalloc(sizeof(struct copy_block_struct), GFP_ATOMIC); + if (!copy_block) { + ret = -ENOMEM; + goto out; + } + init_waitqueue_head(©_block->wait); + kref_init(©_block->kref); + + ret = cmf_copy_block(cdev); + if (ret != -EBUSY) + goto out_put; + + if (cdev->private->state != DEV_STATE_ONLINE) { + ret = -EBUSY; + goto out_put; + } + + cdev->private->state = DEV_STATE_CMFUPDATE; + copy_block->ret = CMF_PENDING; + cdev->private->cmb_wait = copy_block; + + spin_unlock_irqrestore(cdev->ccwlock, flags); + if (wait_event_interruptible(copy_block->wait, + copy_block->ret != CMF_PENDING)) { + spin_lock_irqsave(cdev->ccwlock, flags); + if (copy_block->ret == CMF_PENDING) { + copy_block->ret = -ERESTARTSYS; + if (cdev->private->state == DEV_STATE_CMFUPDATE) + cdev->private->state = DEV_STATE_ONLINE; + } + spin_unlock_irqrestore(cdev->ccwlock, flags); + } + spin_lock_irqsave(cdev->ccwlock, flags); + cdev->private->cmb_wait = NULL; + ret = copy_block->ret; +out_put: + kref_put(©_block->kref, cmf_copy_block_release); +out: + spin_unlock_irqrestore(cdev->ccwlock, flags); + return ret; +} + +void cmf_retry_copy_block(struct ccw_device *cdev) +{ + struct copy_block_struct *copy_block; - s = cdev->private->cmb_wait; - cdev->private->cmb_wait = 0; - if (!s) { + copy_block = cdev->private->cmb_wait; + if (!copy_block) { WARN_ON(1); return; } - s->ret = set_schib(cdev, s->mme, s->mbfc, s->address); - wake_up(&s->wait); + kref_get(©_block->kref); + copy_block->ret = cmf_copy_block(cdev); + wake_up(©_block->wait); + kref_put(©_block->kref, cmf_copy_block_release); +} + +static void cmf_generic_reset(struct ccw_device *cdev) +{ + struct cmb_data *cmb_data; + + spin_lock_irq(cdev->ccwlock); + cmb_data = cdev->private->cmb; + if (cmb_data) { + memset(cmb_data->last_block, 0, cmb_data->size); + /* + * Need to reset hw block as well to make the hardware start + * from 0 again. + */ + memset(cmbops->align(cmb_data->hw_block), 0, cmb_data->size); + cmb_data->last_update = 0; + } + cdev->private->cmb_start_time = get_clock(); + spin_unlock_irq(cdev->ccwlock); } /** @@ -343,8 +519,8 @@ struct cmb { /* insert a single device into the cmb_area list * called with cmb_area.lock held from alloc_cmb */ -static inline int -alloc_cmb_single (struct ccw_device *cdev) +static inline int alloc_cmb_single (struct ccw_device *cdev, + struct cmb_data *cmb_data) { struct cmb *cmb; struct ccw_device_private *node; @@ -358,10 +534,12 @@ alloc_cmb_single (struct ccw_device *cdev) /* find first unused cmb in cmb_area.mem. * this is a little tricky: cmb_area.list - * remains sorted by ->cmb pointers */ + * remains sorted by ->cmb->hw_data pointers */ cmb = cmb_area.mem; list_for_each_entry(node, &cmb_area.list, cmb_list) { - if ((struct cmb*)node->cmb > cmb) + struct cmb_data *data; + data = node->cmb; + if ((struct cmb*)data->hw_block > cmb) break; cmb++; } @@ -372,7 +550,8 @@ alloc_cmb_single (struct ccw_device *cdev) /* insert new cmb */ list_add_tail(&cdev->private->cmb_list, &node->cmb_list); - cdev->private->cmb = cmb; + cmb_data->hw_block = cmb; + cdev->private->cmb = cmb_data; ret = 0; out: spin_unlock_irq(cdev->ccwlock); @@ -385,7 +564,19 @@ alloc_cmb (struct ccw_device *cdev) int ret; struct cmb *mem; ssize_t size; + struct cmb_data *cmb_data; + + /* Allocate private cmb_data. */ + cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL); + if (!cmb_data) + return -ENOMEM; + cmb_data->last_block = kzalloc(sizeof(struct cmb), GFP_KERNEL); + if (!cmb_data->last_block) { + kfree(cmb_data); + return -ENOMEM; + } + cmb_data->size = sizeof(struct cmb); spin_lock(&cmb_area.lock); if (!cmb_area.mem) { @@ -414,29 +605,36 @@ alloc_cmb (struct ccw_device *cdev) } /* do the actual allocation */ - ret = alloc_cmb_single(cdev); + ret = alloc_cmb_single(cdev, cmb_data); out: spin_unlock(&cmb_area.lock); - + if (ret) { + kfree(cmb_data->last_block); + kfree(cmb_data); + } return ret; } -static void -free_cmb(struct ccw_device *cdev) +static void free_cmb(struct ccw_device *cdev) { struct ccw_device_private *priv; - - priv = cdev->private; + struct cmb_data *cmb_data; spin_lock(&cmb_area.lock); spin_lock_irq(cdev->ccwlock); + priv = cdev->private; + if (list_empty(&priv->cmb_list)) { /* already freed */ goto out; } + cmb_data = priv->cmb; priv->cmb = NULL; + if (cmb_data) + kfree(cmb_data->last_block); + kfree(cmb_data); list_del_init(&priv->cmb_list); if (list_empty(&cmb_area.list)) { @@ -451,83 +649,97 @@ out: spin_unlock(&cmb_area.lock); } -static int -set_cmb(struct ccw_device *cdev, u32 mme) +static int set_cmb(struct ccw_device *cdev, u32 mme) { u16 offset; + struct cmb_data *cmb_data; + unsigned long flags; - if (!cdev->private->cmb) + spin_lock_irqsave(cdev->ccwlock, flags); + if (!cdev->private->cmb) { + spin_unlock_irqrestore(cdev->ccwlock, flags); return -EINVAL; - - offset = mme ? (struct cmb *)cdev->private->cmb - cmb_area.mem : 0; + } + cmb_data = cdev->private->cmb; + offset = mme ? (struct cmb *)cmb_data->hw_block - cmb_area.mem : 0; + spin_unlock_irqrestore(cdev->ccwlock, flags); return set_schib_wait(cdev, mme, 0, offset); } -static u64 -read_cmb (struct ccw_device *cdev, int index) +static u64 read_cmb (struct ccw_device *cdev, int index) { - /* yes, we have to put it on the stack - * because the cmb must only be accessed - * atomically, e.g. with mvc */ - struct cmb cmb; - unsigned long flags; + struct cmb *cmb; u32 val; + int ret; + unsigned long flags; + + ret = cmf_cmb_copy_wait(cdev); + if (ret < 0) + return 0; spin_lock_irqsave(cdev->ccwlock, flags); if (!cdev->private->cmb) { - spin_unlock_irqrestore(cdev->ccwlock, flags); - return 0; + ret = 0; + goto out; } - - cmb = *(struct cmb*)cdev->private->cmb; - spin_unlock_irqrestore(cdev->ccwlock, flags); + cmb = ((struct cmb_data *)cdev->private->cmb)->last_block; switch (index) { case cmb_ssch_rsch_count: - return cmb.ssch_rsch_count; + ret = cmb->ssch_rsch_count; + goto out; case cmb_sample_count: - return cmb.sample_count; + ret = cmb->sample_count; + goto out; case cmb_device_connect_time: - val = cmb.device_connect_time; + val = cmb->device_connect_time; break; case cmb_function_pending_time: - val = cmb.function_pending_time; + val = cmb->function_pending_time; break; case cmb_device_disconnect_time: - val = cmb.device_disconnect_time; + val = cmb->device_disconnect_time; break; case cmb_control_unit_queuing_time: - val = cmb.control_unit_queuing_time; + val = cmb->control_unit_queuing_time; break; case cmb_device_active_only_time: - val = cmb.device_active_only_time; + val = cmb->device_active_only_time; break; default: - return 0; + ret = 0; + goto out; } - return time_to_avg_nsec(val, cmb.sample_count); + ret = time_to_avg_nsec(val, cmb->sample_count); +out: + spin_unlock_irqrestore(cdev->ccwlock, flags); + return ret; } -static int -readall_cmb (struct ccw_device *cdev, struct cmbdata *data) +static int readall_cmb (struct ccw_device *cdev, struct cmbdata *data) { - /* yes, we have to put it on the stack - * because the cmb must only be accessed - * atomically, e.g. with mvc */ - struct cmb cmb; - unsigned long flags; + struct cmb *cmb; + struct cmb_data *cmb_data; u64 time; + unsigned long flags; + int ret; + ret = cmf_cmb_copy_wait(cdev); + if (ret < 0) + return ret; spin_lock_irqsave(cdev->ccwlock, flags); - if (!cdev->private->cmb) { - spin_unlock_irqrestore(cdev->ccwlock, flags); - return -ENODEV; + cmb_data = cdev->private->cmb; + if (!cmb_data) { + ret = -ENODEV; + goto out; } - - cmb = *(struct cmb*)cdev->private->cmb; - time = get_clock() - cdev->private->cmb_start_time; - spin_unlock_irqrestore(cdev->ccwlock, flags); + if (cmb_data->last_update == 0) { + ret = -EAGAIN; + goto out; + } + cmb = cmb_data->last_block; + time = cmb_data->last_update - cdev->private->cmb_start_time; memset(data, 0, sizeof(struct cmbdata)); @@ -538,31 +750,32 @@ readall_cmb (struct ccw_device *cdev, struct cmbdata *data) data->elapsed_time = (time * 1000) >> 12; /* copy data to new structure */ - data->ssch_rsch_count = cmb.ssch_rsch_count; - data->sample_count = cmb.sample_count; + data->ssch_rsch_count = cmb->ssch_rsch_count; + data->sample_count = cmb->sample_count; /* time fields are converted to nanoseconds while copying */ - data->device_connect_time = time_to_nsec(cmb.device_connect_time); - data->function_pending_time = time_to_nsec(cmb.function_pending_time); - data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time); + data->device_connect_time = time_to_nsec(cmb->device_connect_time); + data->function_pending_time = time_to_nsec(cmb->function_pending_time); + data->device_disconnect_time = + time_to_nsec(cmb->device_disconnect_time); data->control_unit_queuing_time - = time_to_nsec(cmb.control_unit_queuing_time); + = time_to_nsec(cmb->control_unit_queuing_time); data->device_active_only_time - = time_to_nsec(cmb.device_active_only_time); + = time_to_nsec(cmb->device_active_only_time); + ret = 0; +out: + spin_unlock_irqrestore(cdev->ccwlock, flags); + return ret; +} - return 0; +static void reset_cmb(struct ccw_device *cdev) +{ + cmf_generic_reset(cdev); } -static void -reset_cmb(struct ccw_device *cdev) +static void * align_cmb(void *area) { - struct cmb *cmb; - spin_lock_irq(cdev->ccwlock); - cmb = cdev->private->cmb; - if (cmb) - memset (cmb, 0, sizeof (*cmb)); - cdev->private->cmb_start_time = get_clock(); - spin_unlock_irq(cdev->ccwlock); + return area; } static struct attribute_group cmf_attr_group; @@ -574,6 +787,7 @@ static struct cmb_operations cmbops_basic = { .read = read_cmb, .readall = readall_cmb, .reset = reset_cmb, + .align = align_cmb, .attr_group = &cmf_attr_group, }; @@ -610,22 +824,34 @@ static inline struct cmbe* cmbe_align(struct cmbe *c) return (struct cmbe*)addr; } -static int -alloc_cmbe (struct ccw_device *cdev) +static int alloc_cmbe (struct ccw_device *cdev) { struct cmbe *cmbe; - cmbe = kmalloc (sizeof (*cmbe) * 2, GFP_KERNEL); + struct cmb_data *cmb_data; + int ret; + + cmbe = kzalloc (sizeof (*cmbe) * 2, GFP_KERNEL); if (!cmbe) return -ENOMEM; - + cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL); + if (!cmb_data) { + ret = -ENOMEM; + goto out_free; + } + cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL); + if (!cmb_data->last_block) { + ret = -ENOMEM; + goto out_free; + } + cmb_data->size = sizeof(struct cmbe); spin_lock_irq(cdev->ccwlock); if (cdev->private->cmb) { - kfree(cmbe); spin_unlock_irq(cdev->ccwlock); - return -EBUSY; + ret = -EBUSY; + goto out_free; } - - cdev->private->cmb = cmbe; + cmb_data->hw_block = cmbe; + cdev->private->cmb = cmb_data; spin_unlock_irq(cdev->ccwlock); /* activate global measurement if this is the first channel */ @@ -636,14 +862,24 @@ alloc_cmbe (struct ccw_device *cdev) spin_unlock(&cmb_area.lock); return 0; +out_free: + if (cmb_data) + kfree(cmb_data->last_block); + kfree(cmb_data); + kfree(cmbe); + return ret; } -static void -free_cmbe (struct ccw_device *cdev) +static void free_cmbe (struct ccw_device *cdev) { + struct cmb_data *cmb_data; + spin_lock_irq(cdev->ccwlock); - kfree(cdev->private->cmb); + cmb_data = cdev->private->cmb; cdev->private->cmb = NULL; + if (cmb_data) + kfree(cmb_data->last_block); + kfree(cmb_data); spin_unlock_irq(cdev->ccwlock); /* deactivate global measurement if this is the last channel */ @@ -654,89 +890,105 @@ free_cmbe (struct ccw_device *cdev) spin_unlock(&cmb_area.lock); } -static int -set_cmbe(struct ccw_device *cdev, u32 mme) +static int set_cmbe(struct ccw_device *cdev, u32 mme) { unsigned long mba; + struct cmb_data *cmb_data; + unsigned long flags; - if (!cdev->private->cmb) + spin_lock_irqsave(cdev->ccwlock, flags); + if (!cdev->private->cmb) { + spin_unlock_irqrestore(cdev->ccwlock, flags); return -EINVAL; - mba = mme ? (unsigned long) cmbe_align(cdev->private->cmb) : 0; + } + cmb_data = cdev->private->cmb; + mba = mme ? (unsigned long) cmbe_align(cmb_data->hw_block) : 0; + spin_unlock_irqrestore(cdev->ccwlock, flags); return set_schib_wait(cdev, mme, 1, mba); } -u64 -read_cmbe (struct ccw_device *cdev, int index) +static u64 read_cmbe (struct ccw_device *cdev, int index) { - /* yes, we have to put it on the stack - * because the cmb must only be accessed - * atomically, e.g. with mvc */ - struct cmbe cmb; - unsigned long flags; + struct cmbe *cmb; + struct cmb_data *cmb_data; u32 val; + int ret; + unsigned long flags; - spin_lock_irqsave(cdev->ccwlock, flags); - if (!cdev->private->cmb) { - spin_unlock_irqrestore(cdev->ccwlock, flags); + ret = cmf_cmb_copy_wait(cdev); + if (ret < 0) return 0; - } - cmb = *cmbe_align(cdev->private->cmb); - spin_unlock_irqrestore(cdev->ccwlock, flags); + spin_lock_irqsave(cdev->ccwlock, flags); + cmb_data = cdev->private->cmb; + if (!cmb_data) { + ret = 0; + goto out; + } + cmb = cmb_data->last_block; switch (index) { case cmb_ssch_rsch_count: - return cmb.ssch_rsch_count; + ret = cmb->ssch_rsch_count; + goto out; case cmb_sample_count: - return cmb.sample_count; + ret = cmb->sample_count; + goto out; case cmb_device_connect_time: - val = cmb.device_connect_time; + val = cmb->device_connect_time; break; case cmb_function_pending_time: - val = cmb.function_pending_time; + val = cmb->function_pending_time; break; case cmb_device_disconnect_time: - val = cmb.device_disconnect_time; + val = cmb->device_disconnect_time; break; case cmb_control_unit_queuing_time: - val = cmb.control_unit_queuing_time; + val = cmb->control_unit_queuing_time; break; case cmb_device_active_only_time: - val = cmb.device_active_only_time; + val = cmb->device_active_only_time; break; case cmb_device_busy_time: - val = cmb.device_busy_time; + val = cmb->device_busy_time; break; case cmb_initial_command_response_time: - val = cmb.initial_command_response_time; + val = cmb->initial_command_response_time; break; default: - return 0; + ret = 0; + goto out; } - return time_to_avg_nsec(val, cmb.sample_count); + ret = time_to_avg_nsec(val, cmb->sample_count); +out: + spin_unlock_irqrestore(cdev->ccwlock, flags); + return ret; } -static int -readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) +static int readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) { - /* yes, we have to put it on the stack - * because the cmb must only be accessed - * atomically, e.g. with mvc */ - struct cmbe cmb; - unsigned long flags; + struct cmbe *cmb; + struct cmb_data *cmb_data; u64 time; + unsigned long flags; + int ret; + ret = cmf_cmb_copy_wait(cdev); + if (ret < 0) + return ret; spin_lock_irqsave(cdev->ccwlock, flags); - if (!cdev->private->cmb) { - spin_unlock_irqrestore(cdev->ccwlock, flags); - return -ENODEV; + cmb_data = cdev->private->cmb; + if (!cmb_data) { + ret = -ENODEV; + goto out; } - - cmb = *cmbe_align(cdev->private->cmb); - time = get_clock() - cdev->private->cmb_start_time; - spin_unlock_irqrestore(cdev->ccwlock, flags); + if (cmb_data->last_update == 0) { + ret = -EAGAIN; + goto out; + } + time = cmb_data->last_update - cdev->private->cmb_start_time; memset (data, 0, sizeof(struct cmbdata)); @@ -746,35 +998,38 @@ readall_cmbe (struct ccw_device *cdev, struct cmbdata *data) /* conver to nanoseconds */ data->elapsed_time = (time * 1000) >> 12; + cmb = cmb_data->last_block; /* copy data to new structure */ - data->ssch_rsch_count = cmb.ssch_rsch_count; - data->sample_count = cmb.sample_count; + data->ssch_rsch_count = cmb->ssch_rsch_count; + data->sample_count = cmb->sample_count; /* time fields are converted to nanoseconds while copying */ - data->device_connect_time = time_to_nsec(cmb.device_connect_time); - data->function_pending_time = time_to_nsec(cmb.function_pending_time); - data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time); + data->device_connect_time = time_to_nsec(cmb->device_connect_time); + data->function_pending_time = time_to_nsec(cmb->function_pending_time); + data->device_disconnect_time = + time_to_nsec(cmb->device_disconnect_time); data->control_unit_queuing_time - = time_to_nsec(cmb.control_unit_queuing_time); + = time_to_nsec(cmb->control_unit_queuing_time); data->device_active_only_time - = time_to_nsec(cmb.device_active_only_time); - data->device_busy_time = time_to_nsec(cmb.device_busy_time); + = time_to_nsec(cmb->device_active_only_time); + data->device_busy_time = time_to_nsec(cmb->device_busy_time); data->initial_command_response_time - = time_to_nsec(cmb.initial_command_response_time); + = time_to_nsec(cmb->initial_command_response_time); - return 0; + ret = 0; +out: + spin_unlock_irqrestore(cdev->ccwlock, flags); + return ret; } -static void -reset_cmbe(struct ccw_device *cdev) +static void reset_cmbe(struct ccw_device *cdev) { - struct cmbe *cmb; - spin_lock_irq(cdev->ccwlock); - cmb = cmbe_align(cdev->private->cmb); - if (cmb) - memset (cmb, 0, sizeof (*cmb)); - cdev->private->cmb_start_time = get_clock(); - spin_unlock_irq(cdev->ccwlock); + cmf_generic_reset(cdev); +} + +static void * align_cmbe(void *area) +{ + return cmbe_align(area); } static struct attribute_group cmf_attr_group_ext; @@ -786,6 +1041,7 @@ static struct cmb_operations cmbops_extended = { .read = read_cmbe, .readall = readall_cmbe, .reset = reset_cmbe, + .align = align_cmbe, .attr_group = &cmf_attr_group_ext, }; @@ -803,14 +1059,19 @@ cmb_show_avg_sample_interval(struct device *dev, struct device_attribute *attr, struct ccw_device *cdev; long interval; unsigned long count; + struct cmb_data *cmb_data; cdev = to_ccwdev(dev); - interval = get_clock() - cdev->private->cmb_start_time; count = cmf_read(cdev, cmb_sample_count); - if (count) + spin_lock_irq(cdev->ccwlock); + cmb_data = cdev->private->cmb; + if (count) { + interval = cmb_data->last_update - + cdev->private->cmb_start_time; interval /= count; - else + } else interval = -1; + spin_unlock_irq(cdev->ccwlock); return sprintf(buf, "%ld\n", interval); } @@ -823,7 +1084,10 @@ cmb_show_avg_utilization(struct device *dev, struct device_attribute *attr, char int ret; ret = cmf_readall(to_ccwdev(dev), &data); - if (ret) + if (ret == -EAGAIN || ret == -ENODEV) + /* No data (yet/currently) available to use for calculation. */ + return sprintf(buf, "n/a\n"); + else if (ret) return ret; utilization = data.device_connect_time + @@ -982,6 +1246,13 @@ cmf_readall(struct ccw_device *cdev, struct cmbdata *data) return cmbops->readall(cdev, data); } +/* Reenable cmf when a disconnected device becomes available again. */ +int cmf_reenable(struct ccw_device *cdev) +{ + cmbops->reset(cdev); + return cmbops->set(cdev, 2); +} + static int __init init_cmf(void) { diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 74ea8aac4b7d..1d3be80797f8 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -19,9 +19,11 @@ #include "cio_debug.h" #include "ioasm.h" #include "chsc.h" +#include "device.h" int need_rescan = 0; int css_init_done = 0; +static int need_reprobe = 0; static int max_ssid = 0; struct channel_subsystem *css[__MAX_CSSID + 1]; @@ -339,6 +341,67 @@ typedef void (*workfunc)(void *); DECLARE_WORK(slow_path_work, (workfunc)css_trigger_slow_path, NULL); struct workqueue_struct *slow_path_wq; +/* Reprobe subchannel if unregistered. */ +static int reprobe_subchannel(struct subchannel_id schid, void *data) +{ + struct subchannel *sch; + int ret; + + CIO_DEBUG(KERN_INFO, 6, "cio: reprobe 0.%x.%04x\n", + schid.ssid, schid.sch_no); + if (need_reprobe) + return -EAGAIN; + + sch = get_subchannel_by_schid(schid); + if (sch) { + /* Already known. */ + put_device(&sch->dev); + return 0; + } + + ret = css_probe_device(schid); + switch (ret) { + case 0: + break; + case -ENXIO: + case -ENOMEM: + /* These should abort looping */ + break; + default: + ret = 0; + } + + return ret; +} + +/* Work function used to reprobe all unregistered subchannels. */ +static void reprobe_all(void *data) +{ + int ret; + + CIO_MSG_EVENT(2, "reprobe start\n"); + + need_reprobe = 0; + /* Make sure initial subchannel scan is done. */ + wait_event(ccw_device_init_wq, + atomic_read(&ccw_device_init_count) == 0); + ret = for_each_subchannel(reprobe_subchannel, NULL); + + CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret, + need_reprobe); +} + +DECLARE_WORK(css_reprobe_work, reprobe_all, NULL); + +/* Schedule reprobing of all unregistered subchannels. */ +void css_schedule_reprobe(void) +{ + need_reprobe = 1; + queue_work(ccw_device_work, &css_reprobe_work); +} + +EXPORT_SYMBOL_GPL(css_schedule_reprobe); + /* * Rescan for new devices. FIXME: This is slow. * This function is called when we have lost CRWs due to overflows and we have diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 8e3053c2a451..eafde43e8410 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -133,8 +133,8 @@ struct css_driver io_subchannel_driver = { struct workqueue_struct *ccw_device_work; struct workqueue_struct *ccw_device_notify_work; -static wait_queue_head_t ccw_device_init_wq; -static atomic_t ccw_device_init_count; +wait_queue_head_t ccw_device_init_wq; +atomic_t ccw_device_init_count; static int __init init_ccw_bus_type (void) diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 11587ebb7289..00be9a5b4acd 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -1,6 +1,10 @@ #ifndef S390_DEVICE_H #define S390_DEVICE_H +#include <asm/ccwdev.h> +#include <asm/atomic.h> +#include <linux/wait.h> + /* * states of the device statemachine */ @@ -23,6 +27,7 @@ enum dev_state { DEV_STATE_DISCONNECTED, DEV_STATE_DISCONNECTED_SENSE_ID, DEV_STATE_CMFCHANGE, + DEV_STATE_CMFUPDATE, /* last element! */ NR_DEV_STATES }; @@ -67,6 +72,8 @@ dev_fsm_final_state(struct ccw_device *cdev) extern struct workqueue_struct *ccw_device_work; extern struct workqueue_struct *ccw_device_notify_work; +extern wait_queue_head_t ccw_device_init_wq; +extern atomic_t ccw_device_init_count; void io_subchannel_recog_done(struct ccw_device *cdev); @@ -112,5 +119,8 @@ int ccw_device_stlck(struct ccw_device *); void ccw_device_set_timeout(struct ccw_device *, int); extern struct subchannel_id ccw_device_get_subchannel_id(struct ccw_device *); +/* Channel measurement facility related */ void retry_set_schib(struct ccw_device *cdev); +void cmf_retry_copy_block(struct ccw_device *); +int cmf_reenable(struct ccw_device *); #endif diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 49ec562d7f60..7d0dd72635eb 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -336,8 +336,11 @@ ccw_device_oper_notify(void *data) if (!ret) /* Driver doesn't want device back. */ ccw_device_do_unreg_rereg((void *)cdev); - else + else { + /* Reenable channel measurements, if needed. */ + cmf_reenable(cdev); wake_up(&cdev->private->wait_q); + } } /* @@ -861,6 +864,8 @@ ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event) irb = (struct irb *) __LC_IRB; /* Accumulate status. We don't do basic sense. */ ccw_device_accumulate_irb(cdev, irb); + /* Remember to clear irb to avoid residuals. */ + memset(&cdev->private->irb, 0, sizeof(struct irb)); /* Try to start delayed device verification. */ ccw_device_online_verify(cdev, 0); /* Note: Don't call handler for cio initiated clear! */ @@ -1093,6 +1098,13 @@ ccw_device_change_cmfstate(struct ccw_device *cdev, enum dev_event dev_event) dev_fsm_event(cdev, dev_event); } +static void ccw_device_update_cmfblock(struct ccw_device *cdev, + enum dev_event dev_event) +{ + cmf_retry_copy_block(cdev); + cdev->private->state = DEV_STATE_ONLINE; + dev_fsm_event(cdev, dev_event); +} static void ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event) @@ -1247,6 +1259,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { [DEV_EVENT_TIMEOUT] = ccw_device_change_cmfstate, [DEV_EVENT_VERIFY] = ccw_device_change_cmfstate, }, + [DEV_STATE_CMFUPDATE] = { + [DEV_EVENT_NOTOPER] = ccw_device_update_cmfblock, + [DEV_EVENT_INTERRUPT] = ccw_device_update_cmfblock, + [DEV_EVENT_TIMEOUT] = ccw_device_update_cmfblock, + [DEV_EVENT_VERIFY] = ccw_device_update_cmfblock, + }, }; /* diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 795abb5a65ba..b266ad8e14ff 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -78,7 +78,8 @@ ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, return -ENODEV; if (cdev->private->state == DEV_STATE_NOT_OPER) return -ENODEV; - if (cdev->private->state == DEV_STATE_VERIFY) { + if (cdev->private->state == DEV_STATE_VERIFY || + cdev->private->state == DEV_STATE_CLEAR_VERIFY) { /* Remember to fake irb when finished. */ if (!cdev->private->flags.fake_irb) { cdev->private->flags.fake_irb = 1; @@ -270,7 +271,8 @@ ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb) * We didn't get channel end / device end. Check if path * verification has been started; we can retry after it has * finished. We also retry unit checks except for command reject - * or intervention required. + * or intervention required. Also check for long busy + * conditions. */ if (cdev->private->flags.doverify || cdev->private->state == DEV_STATE_VERIFY) @@ -279,6 +281,10 @@ ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb) !(irb->ecw[0] & (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ))) cdev->private->intparm = -EAGAIN; + else if ((irb->scsw.dstat & DEV_STAT_ATTENTION) && + (irb->scsw.dstat & DEV_STAT_DEV_END) && + (irb->scsw.dstat & DEV_STAT_UNIT_EXCEP)) + cdev->private->intparm = -EAGAIN; else cdev->private->intparm = -EIO; diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index f99e55308b32..8dc75002acbe 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -14,6 +14,7 @@ #include <linux/errno.h> #include <linux/workqueue.h> #include <linux/time.h> +#include <linux/kthread.h> #include <asm/lowcore.h> @@ -56,8 +57,6 @@ s390_collect_crw_info(void *param) unsigned int chain; sem = (struct semaphore *)param; - /* Set a nice name. */ - daemonize("kmcheck"); repeat: down_interruptible(sem); slow = 0; @@ -516,7 +515,7 @@ arch_initcall(machine_check_init); static int __init machine_check_crw_init (void) { - kernel_thread(s390_collect_crw_info, &m_sem, CLONE_FS|CLONE_FILES); + kthread_run(s390_collect_crw_info, &m_sem, "kmcheck"); ctl_set_bit(14, 28); /* enable channel report MCH */ return 0; } |