Skip to content

Commit

Permalink
[S390] dasd: fix race between tasklet and dasd_sleep_on
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Stefan Weinhuber authored and Martin Schwidefsky committed May 12, 2010
1 parent cea0d76 commit 1c1e093
Showing 1 changed file with 10 additions and 7 deletions.
17 changes: 10 additions & 7 deletions drivers/s390/block/dasd.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
*/
#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
*/
Expand Down Expand Up @@ -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)
{
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)
Expand All @@ -1482,10 +1488,7 @@ static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)

device = cqr->startdev;
spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = ((cqr->status == DASD_CQR_DONE ||
cqr->status == DASD_CQR_NEED_ERP ||
cqr->status == DASD_CQR_TERMINATED) &&
list_empty(&cqr->devlist));
rc = (cqr->callback_data == DASD_SLEEPON_END_TAG);
spin_unlock_irq(get_ccwdev_lock(device->cdev));
return rc;
}
Expand Down Expand Up @@ -1573,7 +1576,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
wait_event(generic_waitq, !(device->stopped));

cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &generic_waitq;
cqr->callback_data = DASD_SLEEPON_START_TAG;
dasd_add_request_tail(cqr);
if (interruptible) {
rc = wait_event_interruptible(
Expand Down Expand Up @@ -1652,7 +1655,7 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr)
}

cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &generic_waitq;
cqr->callback_data = DASD_SLEEPON_START_TAG;
cqr->status = DASD_CQR_QUEUED;
list_add(&cqr->devlist, &device->ccw_queue);

Expand Down

0 comments on commit 1c1e093

Please sign in to comment.