diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-05-30 04:03:33 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-05-30 04:03:36 -0400 |
commit | 4657fb8a98a4e02981a574492bbe470c147b6657 (patch) | |
tree | 9363e9452ce5b798cad43b83b0276bda6a7568c9 | |
parent | 54ad64129cc166b9eec7151f3f9fc83589e33555 (diff) |
[S390] tape: fix race with stack local wait_queue_head_t.
A wait_event call with a stack local wait_queue_head_t structure that is
used to do the wake up for the wait_event is inherently racy. After the
wait_event finished the wake_up call might not have completed yet.
Replace the stack local wait_queue_head_t in tape_do_io and
tape_do_io_interruptible with a per device wait queue.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | drivers/s390/char/tape.h | 3 | ||||
-rw-r--r-- | drivers/s390/char/tape_core.c | 16 |
2 files changed, 10 insertions, 9 deletions
diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index dddf8d62c153..d0d565a05dfe 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h | |||
@@ -231,6 +231,9 @@ struct tape_device { | |||
231 | /* Request queue. */ | 231 | /* Request queue. */ |
232 | struct list_head req_queue; | 232 | struct list_head req_queue; |
233 | 233 | ||
234 | /* Request wait queue. */ | ||
235 | wait_queue_head_t wait_queue; | ||
236 | |||
234 | /* Each tape device has (currently) two minor numbers. */ | 237 | /* Each tape device has (currently) two minor numbers. */ |
235 | int first_minor; | 238 | int first_minor; |
236 | 239 | ||
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 76e44eb7c47f..c20e3c548343 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c | |||
@@ -449,6 +449,7 @@ tape_alloc_device(void) | |||
449 | INIT_LIST_HEAD(&device->req_queue); | 449 | INIT_LIST_HEAD(&device->req_queue); |
450 | INIT_LIST_HEAD(&device->node); | 450 | INIT_LIST_HEAD(&device->node); |
451 | init_waitqueue_head(&device->state_change_wq); | 451 | init_waitqueue_head(&device->state_change_wq); |
452 | init_waitqueue_head(&device->wait_queue); | ||
452 | device->tape_state = TS_INIT; | 453 | device->tape_state = TS_INIT; |
453 | device->medium_state = MS_UNKNOWN; | 454 | device->medium_state = MS_UNKNOWN; |
454 | *device->modeset_byte = 0; | 455 | *device->modeset_byte = 0; |
@@ -954,21 +955,19 @@ __tape_wake_up(struct tape_request *request, void *data) | |||
954 | int | 955 | int |
955 | tape_do_io(struct tape_device *device, struct tape_request *request) | 956 | tape_do_io(struct tape_device *device, struct tape_request *request) |
956 | { | 957 | { |
957 | wait_queue_head_t wq; | ||
958 | int rc; | 958 | int rc; |
959 | 959 | ||
960 | init_waitqueue_head(&wq); | ||
961 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | 960 | spin_lock_irq(get_ccwdev_lock(device->cdev)); |
962 | /* Setup callback */ | 961 | /* Setup callback */ |
963 | request->callback = __tape_wake_up; | 962 | request->callback = __tape_wake_up; |
964 | request->callback_data = &wq; | 963 | request->callback_data = &device->wait_queue; |
965 | /* Add request to request queue and try to start it. */ | 964 | /* Add request to request queue and try to start it. */ |
966 | rc = __tape_start_request(device, request); | 965 | rc = __tape_start_request(device, request); |
967 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | 966 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); |
968 | if (rc) | 967 | if (rc) |
969 | return rc; | 968 | return rc; |
970 | /* Request added to the queue. Wait for its completion. */ | 969 | /* Request added to the queue. Wait for its completion. */ |
971 | wait_event(wq, (request->callback == NULL)); | 970 | wait_event(device->wait_queue, (request->callback == NULL)); |
972 | /* Get rc from request */ | 971 | /* Get rc from request */ |
973 | return request->rc; | 972 | return request->rc; |
974 | } | 973 | } |
@@ -989,20 +988,19 @@ int | |||
989 | tape_do_io_interruptible(struct tape_device *device, | 988 | tape_do_io_interruptible(struct tape_device *device, |
990 | struct tape_request *request) | 989 | struct tape_request *request) |
991 | { | 990 | { |
992 | wait_queue_head_t wq; | ||
993 | int rc; | 991 | int rc; |
994 | 992 | ||
995 | init_waitqueue_head(&wq); | ||
996 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | 993 | spin_lock_irq(get_ccwdev_lock(device->cdev)); |
997 | /* Setup callback */ | 994 | /* Setup callback */ |
998 | request->callback = __tape_wake_up_interruptible; | 995 | request->callback = __tape_wake_up_interruptible; |
999 | request->callback_data = &wq; | 996 | request->callback_data = &device->wait_queue; |
1000 | rc = __tape_start_request(device, request); | 997 | rc = __tape_start_request(device, request); |
1001 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | 998 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); |
1002 | if (rc) | 999 | if (rc) |
1003 | return rc; | 1000 | return rc; |
1004 | /* Request added to the queue. Wait for its completion. */ | 1001 | /* Request added to the queue. Wait for its completion. */ |
1005 | rc = wait_event_interruptible(wq, (request->callback == NULL)); | 1002 | rc = wait_event_interruptible(device->wait_queue, |
1003 | (request->callback == NULL)); | ||
1006 | if (rc != -ERESTARTSYS) | 1004 | if (rc != -ERESTARTSYS) |
1007 | /* Request finished normally. */ | 1005 | /* Request finished normally. */ |
1008 | return request->rc; | 1006 | return request->rc; |
@@ -1015,7 +1013,7 @@ tape_do_io_interruptible(struct tape_device *device, | |||
1015 | /* Wait for the interrupt that acknowledges the halt. */ | 1013 | /* Wait for the interrupt that acknowledges the halt. */ |
1016 | do { | 1014 | do { |
1017 | rc = wait_event_interruptible( | 1015 | rc = wait_event_interruptible( |
1018 | wq, | 1016 | device->wait_queue, |
1019 | (request->callback == NULL) | 1017 | (request->callback == NULL) |
1020 | ); | 1018 | ); |
1021 | } while (rc == -ERESTARTSYS); | 1019 | } while (rc == -ERESTARTSYS); |