[S390] dasd: fix race between tasklet and dasd_sleep_on
The various dasd_sleep_on functions use a global wait queue when waiting for a cqr. The wait condition checks the status and devlist fields of the cqr to determine if it is safe to continue. This evaluation may return true, although the tasklet has not finished processing of the cqr and the callback function has not been called yet. When the callback is finally called, the data in the cqr may already be invalid. The sleep_on wait condition needs a safe way to determine if the tasklet has finished processing. Use the callback_data field of the cqr to store a token, which is set by the callback function itself. Cc: <stable@kernel.org> Signed-off-by: Stefan Weinhuber <wein@de.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
cea0d767c2
commit
1c1e093cbf
1 changed files with 10 additions and 7 deletions
|
@ -37,6 +37,9 @@
|
||||||
*/
|
*/
|
||||||
#define DASD_CHANQ_MAX_SIZE 4
|
#define DASD_CHANQ_MAX_SIZE 4
|
||||||
|
|
||||||
|
#define DASD_SLEEPON_START_TAG (void *) 1
|
||||||
|
#define DASD_SLEEPON_END_TAG (void *) 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SECTION: exported variables of dasd.c
|
* SECTION: exported variables of dasd.c
|
||||||
*/
|
*/
|
||||||
|
@ -1472,7 +1475,10 @@ void dasd_add_request_tail(struct dasd_ccw_req *cqr)
|
||||||
*/
|
*/
|
||||||
static void dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
|
static void dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
|
||||||
{
|
{
|
||||||
wake_up((wait_queue_head_t *) data);
|
spin_lock_irq(get_ccwdev_lock(cqr->startdev->cdev));
|
||||||
|
cqr->callback_data = DASD_SLEEPON_END_TAG;
|
||||||
|
spin_unlock_irq(get_ccwdev_lock(cqr->startdev->cdev));
|
||||||
|
wake_up(&generic_waitq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
|
static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
|
||||||
|
@ -1482,10 +1488,7 @@ static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
|
||||||
|
|
||||||
device = cqr->startdev;
|
device = cqr->startdev;
|
||||||
spin_lock_irq(get_ccwdev_lock(device->cdev));
|
spin_lock_irq(get_ccwdev_lock(device->cdev));
|
||||||
rc = ((cqr->status == DASD_CQR_DONE ||
|
rc = (cqr->callback_data == DASD_SLEEPON_END_TAG);
|
||||||
cqr->status == DASD_CQR_NEED_ERP ||
|
|
||||||
cqr->status == DASD_CQR_TERMINATED) &&
|
|
||||||
list_empty(&cqr->devlist));
|
|
||||||
spin_unlock_irq(get_ccwdev_lock(device->cdev));
|
spin_unlock_irq(get_ccwdev_lock(device->cdev));
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1573,7 +1576,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
|
||||||
wait_event(generic_waitq, !(device->stopped));
|
wait_event(generic_waitq, !(device->stopped));
|
||||||
|
|
||||||
cqr->callback = dasd_wakeup_cb;
|
cqr->callback = dasd_wakeup_cb;
|
||||||
cqr->callback_data = (void *) &generic_waitq;
|
cqr->callback_data = DASD_SLEEPON_START_TAG;
|
||||||
dasd_add_request_tail(cqr);
|
dasd_add_request_tail(cqr);
|
||||||
if (interruptible) {
|
if (interruptible) {
|
||||||
rc = wait_event_interruptible(
|
rc = wait_event_interruptible(
|
||||||
|
@ -1652,7 +1655,7 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr)
|
||||||
}
|
}
|
||||||
|
|
||||||
cqr->callback = dasd_wakeup_cb;
|
cqr->callback = dasd_wakeup_cb;
|
||||||
cqr->callback_data = (void *) &generic_waitq;
|
cqr->callback_data = DASD_SLEEPON_START_TAG;
|
||||||
cqr->status = DASD_CQR_QUEUED;
|
cqr->status = DASD_CQR_QUEUED;
|
||||||
list_add(&cqr->devlist, &device->ccw_queue);
|
list_add(&cqr->devlist, &device->ccw_queue);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue