diff options
Diffstat (limited to 'drivers/s390/block/dasd.c')
-rw-r--r-- | drivers/s390/block/dasd.c | 122 |
1 files changed, 108 insertions, 14 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index ef4c687e7c01..08c88fcd8963 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c | |||
@@ -7,7 +7,6 @@ | |||
7 | * Bugreports.to..: <Linux390@de.ibm.com> | 7 | * Bugreports.to..: <Linux390@de.ibm.com> |
8 | * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 | 8 | * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 |
9 | * | 9 | * |
10 | * $Revision: 1.172 $ | ||
11 | */ | 10 | */ |
12 | 11 | ||
13 | #include <linux/config.h> | 12 | #include <linux/config.h> |
@@ -19,6 +18,7 @@ | |||
19 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
20 | #include <linux/buffer_head.h> | 19 | #include <linux/buffer_head.h> |
21 | #include <linux/hdreg.h> | 20 | #include <linux/hdreg.h> |
21 | #include <linux/notifier.h> | ||
22 | 22 | ||
23 | #include <asm/ccwdev.h> | 23 | #include <asm/ccwdev.h> |
24 | #include <asm/ebcdic.h> | 24 | #include <asm/ebcdic.h> |
@@ -58,6 +58,7 @@ static void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *); | |||
58 | static void dasd_flush_ccw_queue(struct dasd_device *, int); | 58 | static void dasd_flush_ccw_queue(struct dasd_device *, int); |
59 | static void dasd_tasklet(struct dasd_device *); | 59 | static void dasd_tasklet(struct dasd_device *); |
60 | static void do_kick_device(void *data); | 60 | static void do_kick_device(void *data); |
61 | static void dasd_disable_eer(struct dasd_device *device); | ||
61 | 62 | ||
62 | /* | 63 | /* |
63 | * SECTION: Operations on the device structure. | 64 | * SECTION: Operations on the device structure. |
@@ -152,6 +153,8 @@ dasd_state_new_to_known(struct dasd_device *device) | |||
152 | static inline void | 153 | static inline void |
153 | dasd_state_known_to_new(struct dasd_device * device) | 154 | dasd_state_known_to_new(struct dasd_device * device) |
154 | { | 155 | { |
156 | /* disable extended error reporting for this device */ | ||
157 | dasd_disable_eer(device); | ||
155 | /* Forget the discipline information. */ | 158 | /* Forget the discipline information. */ |
156 | device->discipline = NULL; | 159 | device->discipline = NULL; |
157 | device->state = DASD_STATE_NEW; | 160 | device->state = DASD_STATE_NEW; |
@@ -675,11 +678,8 @@ dasd_term_IO(struct dasd_ccw_req * cqr) | |||
675 | rc = ccw_device_clear(device->cdev, (long) cqr); | 678 | rc = ccw_device_clear(device->cdev, (long) cqr); |
676 | switch (rc) { | 679 | switch (rc) { |
677 | case 0: /* termination successful */ | 680 | case 0: /* termination successful */ |
678 | if (cqr->retries > 0) { | 681 | cqr->retries--; |
679 | cqr->retries--; | 682 | cqr->status = DASD_CQR_CLEAR; |
680 | cqr->status = DASD_CQR_CLEAR; | ||
681 | } else | ||
682 | cqr->status = DASD_CQR_FAILED; | ||
683 | cqr->stopclk = get_clock(); | 683 | cqr->stopclk = get_clock(); |
684 | DBF_DEV_EVENT(DBF_DEBUG, device, | 684 | DBF_DEV_EVENT(DBF_DEBUG, device, |
685 | "terminate cqr %p successful", | 685 | "terminate cqr %p successful", |
@@ -871,6 +871,9 @@ dasd_handle_state_change_pending(struct dasd_device *device) | |||
871 | struct dasd_ccw_req *cqr; | 871 | struct dasd_ccw_req *cqr; |
872 | struct list_head *l, *n; | 872 | struct list_head *l, *n; |
873 | 873 | ||
874 | /* first of all call extended error reporting */ | ||
875 | dasd_write_eer_trigger(DASD_EER_STATECHANGE, device, NULL); | ||
876 | |||
874 | device->stopped &= ~DASD_STOPPED_PENDING; | 877 | device->stopped &= ~DASD_STOPPED_PENDING; |
875 | 878 | ||
876 | /* restart all 'running' IO on queue */ | 879 | /* restart all 'running' IO on queue */ |
@@ -1090,6 +1093,19 @@ restart: | |||
1090 | } | 1093 | } |
1091 | goto restart; | 1094 | goto restart; |
1092 | } | 1095 | } |
1096 | |||
1097 | /* first of all call extended error reporting */ | ||
1098 | if (device->eer && cqr->status == DASD_CQR_FAILED) { | ||
1099 | dasd_write_eer_trigger(DASD_EER_FATALERROR, | ||
1100 | device, cqr); | ||
1101 | |||
1102 | /* restart request */ | ||
1103 | cqr->status = DASD_CQR_QUEUED; | ||
1104 | cqr->retries = 255; | ||
1105 | device->stopped |= DASD_STOPPED_QUIESCE; | ||
1106 | goto restart; | ||
1107 | } | ||
1108 | |||
1093 | /* Process finished ERP request. */ | 1109 | /* Process finished ERP request. */ |
1094 | if (cqr->refers) { | 1110 | if (cqr->refers) { |
1095 | __dasd_process_erp(device, cqr); | 1111 | __dasd_process_erp(device, cqr); |
@@ -1227,7 +1243,8 @@ __dasd_start_head(struct dasd_device * device) | |||
1227 | cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); | 1243 | cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); |
1228 | /* check FAILFAST */ | 1244 | /* check FAILFAST */ |
1229 | if (device->stopped & ~DASD_STOPPED_PENDING && | 1245 | if (device->stopped & ~DASD_STOPPED_PENDING && |
1230 | test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags)) { | 1246 | test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && |
1247 | (!device->eer)) { | ||
1231 | cqr->status = DASD_CQR_FAILED; | 1248 | cqr->status = DASD_CQR_FAILED; |
1232 | dasd_schedule_bh(device); | 1249 | dasd_schedule_bh(device); |
1233 | } | 1250 | } |
@@ -1308,7 +1325,7 @@ dasd_tasklet(struct dasd_device * device) | |||
1308 | /* Now call the callback function of requests with final status */ | 1325 | /* Now call the callback function of requests with final status */ |
1309 | list_for_each_safe(l, n, &final_queue) { | 1326 | list_for_each_safe(l, n, &final_queue) { |
1310 | cqr = list_entry(l, struct dasd_ccw_req, list); | 1327 | cqr = list_entry(l, struct dasd_ccw_req, list); |
1311 | list_del(&cqr->list); | 1328 | list_del_init(&cqr->list); |
1312 | if (cqr->callback != NULL) | 1329 | if (cqr->callback != NULL) |
1313 | (cqr->callback)(cqr, cqr->callback_data); | 1330 | (cqr->callback)(cqr, cqr->callback_data); |
1314 | } | 1331 | } |
@@ -1393,7 +1410,9 @@ _wait_for_wakeup(struct dasd_ccw_req *cqr) | |||
1393 | 1410 | ||
1394 | device = cqr->device; | 1411 | device = cqr->device; |
1395 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | 1412 | spin_lock_irq(get_ccwdev_lock(device->cdev)); |
1396 | rc = cqr->status == DASD_CQR_DONE || cqr->status == DASD_CQR_FAILED; | 1413 | rc = ((cqr->status == DASD_CQR_DONE || |
1414 | cqr->status == DASD_CQR_FAILED) && | ||
1415 | list_empty(&cqr->list)); | ||
1397 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | 1416 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); |
1398 | return rc; | 1417 | return rc; |
1399 | } | 1418 | } |
@@ -1457,15 +1476,37 @@ dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr) | |||
1457 | while (!finished) { | 1476 | while (!finished) { |
1458 | rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr)); | 1477 | rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr)); |
1459 | if (rc != -ERESTARTSYS) { | 1478 | if (rc != -ERESTARTSYS) { |
1460 | /* Request status is either done or failed. */ | 1479 | /* Request is final (done or failed) */ |
1461 | rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; | 1480 | rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; |
1462 | break; | 1481 | break; |
1463 | } | 1482 | } |
1464 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | 1483 | spin_lock_irq(get_ccwdev_lock(device->cdev)); |
1465 | if (cqr->status == DASD_CQR_IN_IO && | 1484 | switch (cqr->status) { |
1466 | device->discipline->term_IO(cqr) == 0) { | 1485 | case DASD_CQR_IN_IO: |
1467 | list_del(&cqr->list); | 1486 | /* terminate runnig cqr */ |
1487 | if (device->discipline->term_IO) { | ||
1488 | cqr->retries = -1; | ||
1489 | device->discipline->term_IO(cqr); | ||
1490 | /*nished = | ||
1491 | * wait (non-interruptible) for final status | ||
1492 | * because signal ist still pending | ||
1493 | */ | ||
1494 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
1495 | wait_event(wait_q, _wait_for_wakeup(cqr)); | ||
1496 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | ||
1497 | rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; | ||
1498 | finished = 1; | ||
1499 | } | ||
1500 | break; | ||
1501 | case DASD_CQR_QUEUED: | ||
1502 | /* request */ | ||
1503 | list_del_init(&cqr->list); | ||
1504 | rc = -EIO; | ||
1468 | finished = 1; | 1505 | finished = 1; |
1506 | break; | ||
1507 | default: | ||
1508 | /* cqr with 'non-interruptable' status - just wait */ | ||
1509 | break; | ||
1469 | } | 1510 | } |
1470 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | 1511 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); |
1471 | } | 1512 | } |
@@ -1945,6 +1986,9 @@ dasd_generic_notify(struct ccw_device *cdev, int event) | |||
1945 | switch (event) { | 1986 | switch (event) { |
1946 | case CIO_GONE: | 1987 | case CIO_GONE: |
1947 | case CIO_NO_PATH: | 1988 | case CIO_NO_PATH: |
1989 | /* first of all call extended error reporting */ | ||
1990 | dasd_write_eer_trigger(DASD_EER_NOPATH, device, NULL); | ||
1991 | |||
1948 | if (device->state < DASD_STATE_BASIC) | 1992 | if (device->state < DASD_STATE_BASIC) |
1949 | break; | 1993 | break; |
1950 | /* Device is active. We want to keep it. */ | 1994 | /* Device is active. We want to keep it. */ |
@@ -2002,6 +2046,51 @@ dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver) | |||
2002 | put_driver(drv); | 2046 | put_driver(drv); |
2003 | } | 2047 | } |
2004 | 2048 | ||
2049 | /* | ||
2050 | * notifications for extended error reports | ||
2051 | */ | ||
2052 | static struct notifier_block *dasd_eer_chain; | ||
2053 | |||
2054 | int | ||
2055 | dasd_register_eer_notifier(struct notifier_block *nb) | ||
2056 | { | ||
2057 | return notifier_chain_register(&dasd_eer_chain, nb); | ||
2058 | } | ||
2059 | |||
2060 | int | ||
2061 | dasd_unregister_eer_notifier(struct notifier_block *nb) | ||
2062 | { | ||
2063 | return notifier_chain_unregister(&dasd_eer_chain, nb); | ||
2064 | } | ||
2065 | |||
2066 | /* | ||
2067 | * Notify the registered error reporting module of a problem | ||
2068 | */ | ||
2069 | void | ||
2070 | dasd_write_eer_trigger(unsigned int id, struct dasd_device *device, | ||
2071 | struct dasd_ccw_req *cqr) | ||
2072 | { | ||
2073 | if (device->eer) { | ||
2074 | struct dasd_eer_trigger temp; | ||
2075 | temp.id = id; | ||
2076 | temp.device = device; | ||
2077 | temp.cqr = cqr; | ||
2078 | notifier_call_chain(&dasd_eer_chain, DASD_EER_TRIGGER, | ||
2079 | (void *)&temp); | ||
2080 | } | ||
2081 | } | ||
2082 | |||
2083 | /* | ||
2084 | * Tell the registered error reporting module to disable error reporting for | ||
2085 | * a given device and to cleanup any private data structures on that device. | ||
2086 | */ | ||
2087 | static void | ||
2088 | dasd_disable_eer(struct dasd_device *device) | ||
2089 | { | ||
2090 | notifier_call_chain(&dasd_eer_chain, DASD_EER_DISABLE, (void *)device); | ||
2091 | } | ||
2092 | |||
2093 | |||
2005 | static int __init | 2094 | static int __init |
2006 | dasd_init(void) | 2095 | dasd_init(void) |
2007 | { | 2096 | { |
@@ -2083,6 +2172,11 @@ EXPORT_SYMBOL_GPL(dasd_generic_set_online); | |||
2083 | EXPORT_SYMBOL_GPL(dasd_generic_set_offline); | 2172 | EXPORT_SYMBOL_GPL(dasd_generic_set_offline); |
2084 | EXPORT_SYMBOL_GPL(dasd_generic_auto_online); | 2173 | EXPORT_SYMBOL_GPL(dasd_generic_auto_online); |
2085 | 2174 | ||
2175 | EXPORT_SYMBOL(dasd_register_eer_notifier); | ||
2176 | EXPORT_SYMBOL(dasd_unregister_eer_notifier); | ||
2177 | EXPORT_SYMBOL(dasd_write_eer_trigger); | ||
2178 | |||
2179 | |||
2086 | /* | 2180 | /* |
2087 | * Overrides for Emacs so that we follow Linus's tabbing style. | 2181 | * Overrides for Emacs so that we follow Linus's tabbing style. |
2088 | * Emacs will notice this stuff at the end of the file and automatically | 2182 | * Emacs will notice this stuff at the end of the file and automatically |