diff options
author | Christoph Hellwig <hch@lst.de> | 2005-10-31 12:49:52 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.(none)> | 2005-11-06 13:49:36 -0500 |
commit | 7dfdc9a52b4219fba8240750e36de5db860ddd5f (patch) | |
tree | b7be4c6a706aad1ce4e07ac22dea6c2afb050868 | |
parent | 262eef663b579f9b495c7392ac7d2d3f34ecc9fe (diff) |
[SCSI] use a completion in scsi_send_eh_cmnd
scsi_send_eh_cmnd currently uses a semaphore and an overload of eh_timer
to either get a completion for a command for a timeout.
Switch to using a completion and wait_for_completion_timeout to simply
the code and not having to deal with the races ourselves.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
-rw-r--r-- | drivers/scsi/scsi_error.c | 107 | ||||
-rw-r--r-- | drivers/scsi/scsi_priv.h | 1 | ||||
-rw-r--r-- | include/scsi/scsi_host.h | 5 |
3 files changed, 32 insertions, 81 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 5a30485d5038..18c5d2523014 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -417,43 +417,15 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd) | |||
417 | } | 417 | } |
418 | 418 | ||
419 | /** | 419 | /** |
420 | * scsi_eh_times_out - timeout function for error handling. | ||
421 | * @scmd: Cmd that is timing out. | ||
422 | * | ||
423 | * Notes: | ||
424 | * During error handling, the kernel thread will be sleeping waiting | ||
425 | * for some action to complete on the device. our only job is to | ||
426 | * record that it timed out, and to wake up the thread. | ||
427 | **/ | ||
428 | static void scsi_eh_times_out(struct scsi_cmnd *scmd) | ||
429 | { | ||
430 | scmd->eh_eflags |= SCSI_EH_REC_TIMEOUT; | ||
431 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd:%p\n", __FUNCTION__, | ||
432 | scmd)); | ||
433 | |||
434 | up(scmd->device->host->eh_action); | ||
435 | } | ||
436 | |||
437 | /** | ||
438 | * scsi_eh_done - Completion function for error handling. | 420 | * scsi_eh_done - Completion function for error handling. |
439 | * @scmd: Cmd that is done. | 421 | * @scmd: Cmd that is done. |
440 | **/ | 422 | **/ |
441 | static void scsi_eh_done(struct scsi_cmnd *scmd) | 423 | static void scsi_eh_done(struct scsi_cmnd *scmd) |
442 | { | 424 | { |
443 | /* | 425 | SCSI_LOG_ERROR_RECOVERY(3, |
444 | * if the timeout handler is already running, then just set the | 426 | printk("%s scmd: %p result: %x\n", |
445 | * flag which says we finished late, and return. we have no | 427 | __FUNCTION__, scmd, scmd->result)); |
446 | * way of stopping the timeout handler from running, so we must | 428 | complete(scmd->device->host->eh_action); |
447 | * always defer to it. | ||
448 | */ | ||
449 | if (del_timer(&scmd->eh_timeout)) { | ||
450 | scmd->request->rq_status = RQ_SCSI_DONE; | ||
451 | |||
452 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s scmd: %p result: %x\n", | ||
453 | __FUNCTION__, scmd, scmd->result)); | ||
454 | |||
455 | up(scmd->device->host->eh_action); | ||
456 | } | ||
457 | } | 429 | } |
458 | 430 | ||
459 | /** | 431 | /** |
@@ -461,10 +433,6 @@ static void scsi_eh_done(struct scsi_cmnd *scmd) | |||
461 | * @scmd: SCSI Cmd to send. | 433 | * @scmd: SCSI Cmd to send. |
462 | * @timeout: Timeout for cmd. | 434 | * @timeout: Timeout for cmd. |
463 | * | 435 | * |
464 | * Notes: | ||
465 | * The initialization of the structures is quite a bit different in | ||
466 | * this case, and furthermore, there is a different completion handler | ||
467 | * vs scsi_dispatch_cmd. | ||
468 | * Return value: | 436 | * Return value: |
469 | * SUCCESS or FAILED or NEEDS_RETRY | 437 | * SUCCESS or FAILED or NEEDS_RETRY |
470 | **/ | 438 | **/ |
@@ -472,24 +440,16 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout) | |||
472 | { | 440 | { |
473 | struct scsi_device *sdev = scmd->device; | 441 | struct scsi_device *sdev = scmd->device; |
474 | struct Scsi_Host *shost = sdev->host; | 442 | struct Scsi_Host *shost = sdev->host; |
475 | DECLARE_MUTEX_LOCKED(sem); | 443 | DECLARE_COMPLETION(done); |
444 | unsigned long timeleft; | ||
476 | unsigned long flags; | 445 | unsigned long flags; |
477 | int rtn = SUCCESS; | 446 | int rtn; |
478 | 447 | ||
479 | /* | ||
480 | * we will use a queued command if possible, otherwise we will | ||
481 | * emulate the queuing and calling of completion function ourselves. | ||
482 | */ | ||
483 | if (sdev->scsi_level <= SCSI_2) | 448 | if (sdev->scsi_level <= SCSI_2) |
484 | scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) | | 449 | scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) | |
485 | (sdev->lun << 5 & 0xe0); | 450 | (sdev->lun << 5 & 0xe0); |
486 | 451 | ||
487 | scsi_add_timer(scmd, timeout, scsi_eh_times_out); | 452 | shost->eh_action = &done; |
488 | |||
489 | /* | ||
490 | * set up the semaphore so we wait for the command to complete. | ||
491 | */ | ||
492 | shost->eh_action = &sem; | ||
493 | scmd->request->rq_status = RQ_SCSI_BUSY; | 453 | scmd->request->rq_status = RQ_SCSI_BUSY; |
494 | 454 | ||
495 | spin_lock_irqsave(shost->host_lock, flags); | 455 | spin_lock_irqsave(shost->host_lock, flags); |
@@ -497,47 +457,29 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout) | |||
497 | shost->hostt->queuecommand(scmd, scsi_eh_done); | 457 | shost->hostt->queuecommand(scmd, scsi_eh_done); |
498 | spin_unlock_irqrestore(shost->host_lock, flags); | 458 | spin_unlock_irqrestore(shost->host_lock, flags); |
499 | 459 | ||
500 | down(&sem); | 460 | timeleft = wait_for_completion_timeout(&done, timeout); |
501 | scsi_log_completion(scmd, SUCCESS); | ||
502 | 461 | ||
462 | scmd->request->rq_status = RQ_SCSI_DONE; | ||
503 | shost->eh_action = NULL; | 463 | shost->eh_action = NULL; |
504 | 464 | ||
505 | /* | 465 | scsi_log_completion(scmd, SUCCESS); |
506 | * see if timeout. if so, tell the host to forget about it. | ||
507 | * in other words, we don't want a callback any more. | ||
508 | */ | ||
509 | if (scmd->eh_eflags & SCSI_EH_REC_TIMEOUT) { | ||
510 | scmd->eh_eflags &= ~SCSI_EH_REC_TIMEOUT; | ||
511 | |||
512 | /* | ||
513 | * as far as the low level driver is | ||
514 | * concerned, this command is still active, so | ||
515 | * we must give the low level driver a chance | ||
516 | * to abort it. (db) | ||
517 | * | ||
518 | * FIXME(eric) - we are not tracking whether we could | ||
519 | * abort a timed out command or not. not sure how | ||
520 | * we should treat them differently anyways. | ||
521 | */ | ||
522 | if (shost->hostt->eh_abort_handler) | ||
523 | shost->hostt->eh_abort_handler(scmd); | ||
524 | |||
525 | scmd->request->rq_status = RQ_SCSI_DONE; | ||
526 | rtn = FAILED; | ||
527 | } | ||
528 | 466 | ||
529 | SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd: %p, rtn:%x\n", | 467 | SCSI_LOG_ERROR_RECOVERY(3, |
530 | __FUNCTION__, scmd, rtn)); | 468 | printk("%s: scmd: %p, timeleft: %ld\n", |
469 | __FUNCTION__, scmd, timeleft)); | ||
531 | 470 | ||
532 | /* | 471 | /* |
533 | * now examine the actual status codes to see whether the command | 472 | * If there is time left scsi_eh_done got called, and we will |
534 | * actually did complete normally. | 473 | * examine the actual status codes to see whether the command |
474 | * actually did complete normally, else tell the host to forget | ||
475 | * about this command. | ||
535 | */ | 476 | */ |
536 | if (rtn == SUCCESS) { | 477 | if (timeleft) { |
537 | rtn = scsi_eh_completed_normally(scmd); | 478 | rtn = scsi_eh_completed_normally(scmd); |
538 | SCSI_LOG_ERROR_RECOVERY(3, | 479 | SCSI_LOG_ERROR_RECOVERY(3, |
539 | printk("%s: scsi_eh_completed_normally %x\n", | 480 | printk("%s: scsi_eh_completed_normally %x\n", |
540 | __FUNCTION__, rtn)); | 481 | __FUNCTION__, rtn)); |
482 | |||
541 | switch (rtn) { | 483 | switch (rtn) { |
542 | case SUCCESS: | 484 | case SUCCESS: |
543 | case NEEDS_RETRY: | 485 | case NEEDS_RETRY: |
@@ -547,6 +489,15 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout) | |||
547 | rtn = FAILED; | 489 | rtn = FAILED; |
548 | break; | 490 | break; |
549 | } | 491 | } |
492 | } else { | ||
493 | /* | ||
494 | * FIXME(eric) - we are not tracking whether we could | ||
495 | * abort a timed out command or not. not sure how | ||
496 | * we should treat them differently anyways. | ||
497 | */ | ||
498 | if (shost->hostt->eh_abort_handler) | ||
499 | shost->hostt->eh_abort_handler(scmd); | ||
500 | rtn = FAILED; | ||
550 | } | 501 | } |
551 | 502 | ||
552 | return rtn; | 503 | return rtn; |
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index d05f778d31a8..d632d9e1493c 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h | |||
@@ -22,7 +22,6 @@ struct Scsi_Host; | |||
22 | * Scsi Error Handler Flags | 22 | * Scsi Error Handler Flags |
23 | */ | 23 | */ |
24 | #define SCSI_EH_CANCEL_CMD 0x0001 /* Cancel this cmd */ | 24 | #define SCSI_EH_CANCEL_CMD 0x0001 /* Cancel this cmd */ |
25 | #define SCSI_EH_REC_TIMEOUT 0x0002 /* EH retry timed out */ | ||
26 | 25 | ||
27 | #define SCSI_SENSE_VALID(scmd) \ | 26 | #define SCSI_SENSE_VALID(scmd) \ |
28 | (((scmd)->sense_buffer[0] & 0x70) == 0x70) | 27 | (((scmd)->sense_buffer[0] & 0x70) == 0x70) |
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 9984d3fbb1f0..6cbb1982ed03 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <linux/workqueue.h> | 7 | #include <linux/workqueue.h> |
8 | 8 | ||
9 | struct block_device; | 9 | struct block_device; |
10 | struct completion; | ||
10 | struct module; | 11 | struct module; |
11 | struct scsi_cmnd; | 12 | struct scsi_cmnd; |
12 | struct scsi_device; | 13 | struct scsi_device; |
@@ -467,8 +468,8 @@ struct Scsi_Host { | |||
467 | 468 | ||
468 | struct list_head eh_cmd_q; | 469 | struct list_head eh_cmd_q; |
469 | struct task_struct * ehandler; /* Error recovery thread. */ | 470 | struct task_struct * ehandler; /* Error recovery thread. */ |
470 | struct semaphore * eh_action; /* Wait for specific actions on the | 471 | struct completion * eh_action; /* Wait for specific actions on the |
471 | host. */ | 472 | host. */ |
472 | wait_queue_head_t host_wait; | 473 | wait_queue_head_t host_wait; |
473 | struct scsi_host_template *hostt; | 474 | struct scsi_host_template *hostt; |
474 | struct scsi_transport_template *transportt; | 475 | struct scsi_transport_template *transportt; |