diff options
| author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2011-03-03 11:56:07 -0500 |
|---|---|---|
| committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2011-03-03 11:56:14 -0500 |
| commit | 0c2bd9b24e73287aa4ee87844c847205e0da8a9b (patch) | |
| tree | 4e56fae8cb7f8dcbc022771436e573c5c12b94fb | |
| parent | b652277b09d3d030cb074cc6a98ba80b34244c03 (diff) | |
[S390] tape: deadlock on system work queue
The 34xx and 3590 tape driver uses the system work queue to defer work
from the interrupt function to process context, e.g. a medium sense
after an unsolicited interrupt. The tape commands started by the work
handler need to be asynchronous, otherwise a deadlock on the system
work queue can occur.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
| -rw-r--r-- | drivers/s390/char/tape.h | 8 | ||||
| -rw-r--r-- | drivers/s390/char/tape_34xx.c | 59 | ||||
| -rw-r--r-- | drivers/s390/char/tape_3590.c | 83 |
3 files changed, 116 insertions, 34 deletions
diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index 7a242f073632..267b54e8ff5a 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h | |||
| @@ -280,6 +280,14 @@ tape_do_io_free(struct tape_device *device, struct tape_request *request) | |||
| 280 | return rc; | 280 | return rc; |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | static inline void | ||
| 284 | tape_do_io_async_free(struct tape_device *device, struct tape_request *request) | ||
| 285 | { | ||
| 286 | request->callback = (void *) tape_free_request; | ||
| 287 | request->callback_data = NULL; | ||
| 288 | tape_do_io_async(device, request); | ||
| 289 | } | ||
| 290 | |||
| 283 | extern int tape_oper_handler(int irq, int status); | 291 | extern int tape_oper_handler(int irq, int status); |
| 284 | extern void tape_noper_handler(int irq, int status); | 292 | extern void tape_noper_handler(int irq, int status); |
| 285 | extern int tape_open(struct tape_device *); | 293 | extern int tape_open(struct tape_device *); |
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index c17f35b6136a..c26511171ffe 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c | |||
| @@ -53,23 +53,11 @@ static void tape_34xx_delete_sbid_from(struct tape_device *, int); | |||
| 53 | * Medium sense for 34xx tapes. There is no 'real' medium sense call. | 53 | * Medium sense for 34xx tapes. There is no 'real' medium sense call. |
| 54 | * So we just do a normal sense. | 54 | * So we just do a normal sense. |
| 55 | */ | 55 | */ |
| 56 | static int | 56 | static void __tape_34xx_medium_sense(struct tape_request *request) |
| 57 | tape_34xx_medium_sense(struct tape_device *device) | ||
| 58 | { | 57 | { |
| 59 | struct tape_request *request; | 58 | struct tape_device *device = request->device; |
| 60 | unsigned char *sense; | 59 | unsigned char *sense; |
| 61 | int rc; | ||
| 62 | |||
| 63 | request = tape_alloc_request(1, 32); | ||
| 64 | if (IS_ERR(request)) { | ||
| 65 | DBF_EXCEPTION(6, "MSEN fail\n"); | ||
| 66 | return PTR_ERR(request); | ||
| 67 | } | ||
| 68 | |||
| 69 | request->op = TO_MSEN; | ||
| 70 | tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); | ||
| 71 | 60 | ||
| 72 | rc = tape_do_io_interruptible(device, request); | ||
| 73 | if (request->rc == 0) { | 61 | if (request->rc == 0) { |
| 74 | sense = request->cpdata; | 62 | sense = request->cpdata; |
| 75 | 63 | ||
| @@ -88,15 +76,47 @@ tape_34xx_medium_sense(struct tape_device *device) | |||
| 88 | device->tape_generic_status |= GMT_WR_PROT(~0); | 76 | device->tape_generic_status |= GMT_WR_PROT(~0); |
| 89 | else | 77 | else |
| 90 | device->tape_generic_status &= ~GMT_WR_PROT(~0); | 78 | device->tape_generic_status &= ~GMT_WR_PROT(~0); |
| 91 | } else { | 79 | } else |
| 92 | DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n", | 80 | DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n", |
| 93 | request->rc); | 81 | request->rc); |
| 94 | } | ||
| 95 | tape_free_request(request); | 82 | tape_free_request(request); |
| 83 | } | ||
| 84 | |||
| 85 | static int tape_34xx_medium_sense(struct tape_device *device) | ||
| 86 | { | ||
| 87 | struct tape_request *request; | ||
| 88 | int rc; | ||
| 89 | |||
| 90 | request = tape_alloc_request(1, 32); | ||
| 91 | if (IS_ERR(request)) { | ||
| 92 | DBF_EXCEPTION(6, "MSEN fail\n"); | ||
| 93 | return PTR_ERR(request); | ||
| 94 | } | ||
| 96 | 95 | ||
| 96 | request->op = TO_MSEN; | ||
| 97 | tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); | ||
| 98 | rc = tape_do_io_interruptible(device, request); | ||
| 99 | __tape_34xx_medium_sense(request); | ||
| 97 | return rc; | 100 | return rc; |
| 98 | } | 101 | } |
| 99 | 102 | ||
| 103 | static void tape_34xx_medium_sense_async(struct tape_device *device) | ||
| 104 | { | ||
| 105 | struct tape_request *request; | ||
| 106 | |||
| 107 | request = tape_alloc_request(1, 32); | ||
| 108 | if (IS_ERR(request)) { | ||
| 109 | DBF_EXCEPTION(6, "MSEN fail\n"); | ||
| 110 | return; | ||
| 111 | } | ||
| 112 | |||
| 113 | request->op = TO_MSEN; | ||
| 114 | tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); | ||
| 115 | request->callback = (void *) __tape_34xx_medium_sense; | ||
| 116 | request->callback_data = NULL; | ||
| 117 | tape_do_io_async(device, request); | ||
| 118 | } | ||
| 119 | |||
| 100 | struct tape_34xx_work { | 120 | struct tape_34xx_work { |
| 101 | struct tape_device *device; | 121 | struct tape_device *device; |
| 102 | enum tape_op op; | 122 | enum tape_op op; |
| @@ -109,6 +129,9 @@ struct tape_34xx_work { | |||
| 109 | * is inserted but cannot call tape_do_io* from an interrupt context. | 129 | * is inserted but cannot call tape_do_io* from an interrupt context. |
| 110 | * Maybe that's useful for other actions we want to start from the | 130 | * Maybe that's useful for other actions we want to start from the |
| 111 | * interrupt handler. | 131 | * interrupt handler. |
| 132 | * Note: the work handler is called by the system work queue. The tape | ||
| 133 | * commands started by the handler need to be asynchrounous, otherwise | ||
| 134 | * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq). | ||
| 112 | */ | 135 | */ |
| 113 | static void | 136 | static void |
| 114 | tape_34xx_work_handler(struct work_struct *work) | 137 | tape_34xx_work_handler(struct work_struct *work) |
| @@ -119,7 +142,7 @@ tape_34xx_work_handler(struct work_struct *work) | |||
| 119 | 142 | ||
| 120 | switch(p->op) { | 143 | switch(p->op) { |
| 121 | case TO_MSEN: | 144 | case TO_MSEN: |
| 122 | tape_34xx_medium_sense(device); | 145 | tape_34xx_medium_sense_async(device); |
| 123 | break; | 146 | break; |
| 124 | default: | 147 | default: |
| 125 | DBF_EVENT(3, "T34XX: internal error: unknown work\n"); | 148 | DBF_EVENT(3, "T34XX: internal error: unknown work\n"); |
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index fbe361fcd2c0..de2e99e0a71b 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c | |||
| @@ -329,17 +329,17 @@ out: | |||
| 329 | /* | 329 | /* |
| 330 | * Enable encryption | 330 | * Enable encryption |
| 331 | */ | 331 | */ |
| 332 | static int tape_3592_enable_crypt(struct tape_device *device) | 332 | static struct tape_request *__tape_3592_enable_crypt(struct tape_device *device) |
| 333 | { | 333 | { |
| 334 | struct tape_request *request; | 334 | struct tape_request *request; |
| 335 | char *data; | 335 | char *data; |
| 336 | 336 | ||
| 337 | DBF_EVENT(6, "tape_3592_enable_crypt\n"); | 337 | DBF_EVENT(6, "tape_3592_enable_crypt\n"); |
| 338 | if (!crypt_supported(device)) | 338 | if (!crypt_supported(device)) |
| 339 | return -ENOSYS; | 339 | return ERR_PTR(-ENOSYS); |
| 340 | request = tape_alloc_request(2, 72); | 340 | request = tape_alloc_request(2, 72); |
| 341 | if (IS_ERR(request)) | 341 | if (IS_ERR(request)) |
| 342 | return PTR_ERR(request); | 342 | return request; |
| 343 | data = request->cpdata; | 343 | data = request->cpdata; |
| 344 | memset(data,0,72); | 344 | memset(data,0,72); |
| 345 | 345 | ||
| @@ -354,23 +354,42 @@ static int tape_3592_enable_crypt(struct tape_device *device) | |||
| 354 | request->op = TO_CRYPT_ON; | 354 | request->op = TO_CRYPT_ON; |
| 355 | tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data); | 355 | tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data); |
| 356 | tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36); | 356 | tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36); |
| 357 | return request; | ||
| 358 | } | ||
| 359 | |||
| 360 | static int tape_3592_enable_crypt(struct tape_device *device) | ||
| 361 | { | ||
| 362 | struct tape_request *request; | ||
| 363 | |||
| 364 | request = __tape_3592_enable_crypt(device); | ||
| 365 | if (IS_ERR(request)) | ||
| 366 | return PTR_ERR(request); | ||
| 357 | return tape_do_io_free(device, request); | 367 | return tape_do_io_free(device, request); |
| 358 | } | 368 | } |
| 359 | 369 | ||
| 370 | static void tape_3592_enable_crypt_async(struct tape_device *device) | ||
| 371 | { | ||
| 372 | struct tape_request *request; | ||
| 373 | |||
| 374 | request = __tape_3592_enable_crypt(device); | ||
| 375 | if (!IS_ERR(request)) | ||
| 376 | tape_do_io_async_free(device, request); | ||
| 377 | } | ||
| 378 | |||
| 360 | /* | 379 | /* |
| 361 | * Disable encryption | 380 | * Disable encryption |
