]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Fix regressions in scsi_internal_device_block
authorMike Christie <michaelc@cs.wisc.edu>
Wed, 6 Oct 2010 08:10:59 +0000 (03:10 -0500)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 21 Mar 2011 19:43:38 +0000 (12:43 -0700)
commit 986fe6c7f50974e871b8ab5a800f5310ea25b361 upstream.

Deleting a SCSI device on a blocked fc_remote_port (before
fast_io_fail_tmo fires) results in a hanging thread:

  STACK:
  0 schedule+1108 [0x5cac48]
  1 schedule_timeout+528 [0x5cb7fc]
  2 wait_for_common+266 [0x5ca6be]
  3 blk_execute_rq+160 [0x354054]
  4 scsi_execute+324 [0x3b7ef4]
  5 scsi_execute_req+162 [0x3b80ca]
  6 sd_sync_cache+138 [0x3cf662]
  7 sd_shutdown+138 [0x3cf91a]
  8 sd_remove+112 [0x3cfe4c]
  9 __device_release_driver+124 [0x3a08b8]
10 device_release_driver+60 [0x3a0a5c]
11 bus_remove_device+266 [0x39fa76]
12 device_del+340 [0x39d818]
13 __scsi_remove_device+204 [0x3bcc48]
14 scsi_remove_device+66 [0x3bcc8e]
15 sysfs_schedule_callback_work+50 [0x260d66]
16 worker_thread+622 [0x162326]
17 kthread+160 [0x1680b0]
18 kernel_thread_starter+6 [0x10aaea]

During the delete, the SCSI device is in moved to SDEV_CANCEL.  When
the FC transport class later calls scsi_target_unblock, this has no
effect, since scsi_internal_device_unblock ignores SCSI devics in this
state.

It looks like all these are regressions caused by:
5c10e63c943b4c67561ddc6bf61e01d4141f881f
[SCSI] limit state transitions in scsi_internal_device_unblock

Fix by rejecting offline and cancel in the state transition.

Signed-off-by: Christof Schmitt <christof.schmitt@de.ibm.com>
[jejb: Original patch by Christof Schmitt, modified by Mike Christie]
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/scsi/scsi_lib.c

index 56977097de9f84a5e10e560f787c51ad35861729..514ef91906b54535de47b6cce865d30dce4fabd2 100644 (file)
@@ -2436,7 +2436,8 @@ scsi_internal_device_unblock(struct scsi_device *sdev)
                sdev->sdev_state = SDEV_RUNNING;
        else if (sdev->sdev_state == SDEV_CREATED_BLOCK)
                sdev->sdev_state = SDEV_CREATED;
-       else
+       else if (sdev->sdev_state != SDEV_CANCEL &&
+                sdev->sdev_state != SDEV_OFFLINE)
                return -EINVAL;
 
        spin_lock_irqsave(q->queue_lock, flags);