diff options
author | Horst Hummel <horst.hummel@de.ibm.com> | 2006-02-01 06:06:37 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-02-01 11:53:24 -0500 |
commit | c2ba444d1d871d3f6cd3bc5e7d8e19c48c8c02a4 (patch) | |
tree | 668b3251195ee98521d1ef6a3f55f4d6f45e3126 | |
parent | 57467195d1581e354998d5cc35dfd7a12d6e0a24 (diff) |
[PATCH] s390: dasd wait for clear i/o interrupt
The sleep_on function clears a running cqr without waiting for the related
interrupt. This can lead to a panic at the time the interrupt is processed
because the related memory might already be freed. Wait for clear-interrupt
and de-queue cqr prior to return.
Signed-off-by: Horst Hummel <horst.hummel@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | drivers/s390/block/dasd.c | 45 |
1 files changed, 33 insertions, 12 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 953097c23d62..abdf1ee633e7 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c | |||
@@ -674,11 +674,8 @@ dasd_term_IO(struct dasd_ccw_req * cqr) | |||
674 | rc = ccw_device_clear(device->cdev, (long) cqr); | 674 | rc = ccw_device_clear(device->cdev, (long) cqr); |
675 | switch (rc) { | 675 | switch (rc) { |
676 | case 0: /* termination successful */ | 676 | case 0: /* termination successful */ |
677 | if (cqr->retries > 0) { | 677 | cqr->retries--; |
678 | cqr->retries--; | 678 | cqr->status = DASD_CQR_CLEAR; |
679 | cqr->status = DASD_CQR_CLEAR; | ||
680 | } else | ||
681 | cqr->status = DASD_CQR_FAILED; | ||
682 | cqr->stopclk = get_clock(); | 679 | cqr->stopclk = get_clock(); |
683 | DBF_DEV_EVENT(DBF_DEBUG, device, | 680 | DBF_DEV_EVENT(DBF_DEBUG, device, |
684 | "terminate cqr %p successful", | 681 | "terminate cqr %p successful", |
@@ -1307,7 +1304,7 @@ dasd_tasklet(struct dasd_device * device) | |||
1307 | /* Now call the callback function of requests with final status */ | 1304 | /* Now call the callback function of requests with final status */ |
1308 | list_for_each_safe(l, n, &final_queue) { | 1305 | list_for_each_safe(l, n, &final_queue) { |
1309 | cqr = list_entry(l, struct dasd_ccw_req, list); | 1306 | cqr = list_entry(l, struct dasd_ccw_req, list); |
1310 | list_del(&cqr->list); | 1307 | list_del_init(&cqr->list); |
1311 | if (cqr->callback != NULL) | 1308 | if (cqr->callback != NULL) |
1312 | (cqr->callback)(cqr, cqr->callback_data); | 1309 | (cqr->callback)(cqr, cqr->callback_data); |
1313 | } | 1310 | } |
@@ -1392,7 +1389,9 @@ _wait_for_wakeup(struct dasd_ccw_req *cqr) | |||
1392 | 1389 | ||
1393 | device = cqr->device; | 1390 | device = cqr->device; |
1394 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | 1391 | spin_lock_irq(get_ccwdev_lock(device->cdev)); |
1395 | rc = cqr->status == DASD_CQR_DONE || cqr->status == DASD_CQR_FAILED; | 1392 | rc = ((cqr->status == DASD_CQR_DONE || |
1393 | cqr->status == DASD_CQR_FAILED) && | ||
1394 | list_empty(&cqr->list)); | ||
1396 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | 1395 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); |
1397 | return rc; | 1396 | return rc; |
1398 | } | 1397 | } |
@@ -1456,15 +1455,37 @@ dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr) | |||
1456 | while (!finished) { | 1455 | while (!finished) { |
1457 | rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr)); | 1456 | rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr)); |
1458 | if (rc != -ERESTARTSYS) { | 1457 | if (rc != -ERESTARTSYS) { |
1459 | /* Request status is either done or failed. */ | 1458 | /* Request is final (done or failed) */ |
1460 | rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; | 1459 | rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; |
1461 | break; | 1460 | break; |
1462 | } | 1461 | } |
1463 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | 1462 | spin_lock_irq(get_ccwdev_lock(device->cdev)); |
1464 | if (cqr->status == DASD_CQR_IN_IO && | 1463 | switch (cqr->status) { |
1465 | device->discipline->term_IO(cqr) == 0) { | 1464 | case DASD_CQR_IN_IO: |
1466 | list_del(&cqr->list); | 1465 | /* terminate runnig cqr */ |
1466 | if (device->discipline->term_IO) { | ||
1467 | cqr->retries = -1; | ||
1468 | device->discipline->term_IO(cqr); | ||
1469 | /*nished = | ||
1470 | * wait (non-interruptible) for final status | ||
1471 | * because signal ist still pending | ||
1472 | */ | ||
1473 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
1474 | wait_event(wait_q, _wait_for_wakeup(cqr)); | ||
1475 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | ||
1476 | rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; | ||
1477 | finished = 1; | ||
1478 | } | ||
1479 | break; | ||
1480 | case DASD_CQR_QUEUED: | ||
1481 | /* request */ | ||
1482 | list_del_init(&cqr->list); | ||
1483 | rc = -EIO; | ||
1467 | finished = 1; | 1484 | finished = 1; |
1485 | break; | ||
1486 | default: | ||
1487 | /* cqr with 'non-interruptable' status - just wait */ | ||
1488 | break; | ||
1468 | } | 1489 | } |
1469 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | 1490 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); |
1470 | } | 1491 | } |