From 4f5299ac4e3a03d5c596c00d726fa932c600609d Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 2 Jan 2009 10:42:21 -0600 Subject: [PATCH] [SCSI] scsi_lib: don't decrement busy counters when inserting commands A bug was introduced by commit b60af5b0adf0da24c673598c8d3fb4d4189a15ce Author: Alan Stern Date: Mon Nov 3 15:56:47 2008 -0500 [SCSI] simplify scsi_io_completion() because the simplification uses scsi_queue_insert(). The problem with this function is that it expects to be called from the completion path while the command is still outstanding, so it decrements the device and host busy counts to do the requeue. The problem is that scsi_io_completion() is a path executed well after these counts have *already* been decremented, leading to a double decrement if the command goes down any error path leading to ACTION_DELAYED_RETRY. The fix is to allow a private function __scsi_queue_insert() with a flag to say whether the busy counters should be decremented. This is made static to scsi_lib.c to discourage other use. Reported-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_lib.c | 61 ++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 8c73bb4e0a2..911514c1fea 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -91,26 +91,19 @@ static void scsi_unprep_request(struct request *req) scsi_put_command(cmd); } -/* - * Function: scsi_queue_insert() - * - * Purpose: Insert a command in the midlevel queue. - * - * Arguments: cmd - command that we are adding to queue. - * reason - why we are inserting command to queue. - * - * Lock status: Assumed that lock is not held upon entry. - * - * Returns: Nothing. - * - * Notes: We do this for one of two cases. Either the host is busy - * and it cannot accept any more commands for the time being, - * or the device returned QUEUE_FULL and can accept no more - * commands. - * Notes: This could be called either from an interrupt context or a - * normal process context. +/** + * __scsi_queue_insert - private queue insertion + * @cmd: The SCSI command being requeued + * @reason: The reason for the requeue + * @unbusy: Whether the queue should be unbusied + * + * This is a private queue insertion. The public interface + * scsi_queue_insert() always assumes the queue should be unbusied + * because it's always called before the completion. This function is + * for a requeue after completion, which should only occur in this + * file. */ -int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) +static int __scsi_queue_insert(struct scsi_cmnd *cmd, int reason, int unbusy) { struct Scsi_Host *host = cmd->device->host; struct scsi_device *device = cmd->device; @@ -150,7 +143,8 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) * Decrement the counters, since these commands are no longer * active on the host/device. */ - scsi_device_unbusy(device); + if (unbusy) + scsi_device_unbusy(device); /* * Requeue this command. It will go before all other commands @@ -172,6 +166,29 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) return 0; } +/* + * Function: scsi_queue_insert() + * + * Purpose: Insert a command in the midlevel queue. + * + * Arguments: cmd - command that we are adding to queue. + * reason - why we are inserting command to queue. + * + * Lock status: Assumed that lock is not held upon entry. + * + * Returns: Nothing. + * + * Notes: We do this for one of two cases. Either the host is busy + * and it cannot accept any more commands for the time being, + * or the device returned QUEUE_FULL and can accept no more + * commands. + * Notes: This could be called either from an interrupt context or a + * normal process context. + */ +int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) +{ + return __scsi_queue_insert(cmd, reason, 1); +} /** * scsi_execute - insert request and wait for the result * @sdev: scsi device @@ -1075,11 +1092,11 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) break; case ACTION_RETRY: /* Retry the same command immediately */ - scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY); + __scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY, 0); break; case ACTION_DELAYED_RETRY: /* Retry the same command after a delay */ - scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); + __scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY, 0); break; } } -- 2.41.1