* [PATCH] scsi-misc-2.5 software enqueue when can_queue is reached
@ 2003-03-04 0:12 Patrick Mansfield
0 siblings, 0 replies; only message in thread
From: Patrick Mansfield @ 2003-03-04 0:12 UTC (permalink / raw)
To: James Bottomley, linux-scsi
James,
I've made changes to the original patch based on the comments received.
Can you push this through or comment? Thanks.
Patch is based on scsi-misc-2.5.
This patch puts scsi_cmnds on to a pending list if the can_queue limit
is reached, or host_blocked is set.
diff -Nru a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
--- a/drivers/scsi/hosts.c Mon Mar 3 14:44:53 2003
+++ b/drivers/scsi/hosts.c Mon Mar 3 14:44:53 2003
@@ -383,6 +383,7 @@
scsi_assign_lock(shost, &shost->default_lock);
INIT_LIST_HEAD(&shost->my_devices);
INIT_LIST_HEAD(&shost->eh_cmd_q);
+ INIT_LIST_HEAD(&shost->pending_cmd);
init_waitqueue_head(&shost->host_wait);
shost->dma_channel = 0xff;
@@ -613,19 +614,72 @@
spin_unlock_irqrestore(shost->host_lock, flags);
}
+void scsi_host_send_pending(struct Scsi_Host *shost)
+{
+ struct scsi_cmnd *scmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ while (!shost->host_self_blocked &&
+ !list_empty(&shost->pending_cmd)) {
+ /*
+ * list_for_each_safe is not safe here, since after the
+ * unlock someone else can remove the scmd after the one
+ * we are pulling off the list.
+ */
+ scmd = list_entry(shost->pending_cmd.next, struct scsi_cmnd,
+ pending_cmd);
+ list_del_init(&scmd->pending_cmd);
+ SCSI_LOG_MLQUEUE(3,
+ printk("%s: removed from pending cmd: 0x%lx\n",
+ __FUNCTION__, scmd->serial_number));
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ if (scsi_dispatch_cmd(scmd, 0))
+ /*
+ * Device hit host_blocked, or (race case)
+ * can_queue limit was reached.
+ */
+ goto unlocked;
+ spin_lock_irqsave(shost->host_lock, flags);
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+unlocked:
+}
+
void scsi_host_busy_dec_and_test(struct Scsi_Host *shost, Scsi_Device *sdev)
{
unsigned long flags;
+ struct scsi_cmnd *scmd;
spin_lock_irqsave(shost->host_lock, flags);
+ /*
+ * If broken adapters are ever fixed or go away (at least qlogicfc
+ * and qlogicisp):
+ *
+ * WARN_ON(shost->host_busy > shost->can_queue);
+ */
shost->host_busy--;
sdev->device_busy--;
- if (shost->in_recovery && shost->host_failed &&
- (shost->host_busy == shost->host_failed))
- {
- up(shost->eh_wait);
- SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler"
- " thread\n"));
- }
- spin_unlock_irqrestore(shost->host_lock, flags);
+ if (shost->in_recovery) {
+ if (shost->host_failed && (shost->host_busy == shost->host_failed)) {
+ up(shost->eh_wait);
+ SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler"
+ " thread\n"));
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ } else if (!list_empty(&shost->pending_cmd) &&
+ !shost->host_blocked && !shost->host_self_blocked) {
+ /*
+ * Send one pending command.
+ */
+ scmd = list_entry(shost->pending_cmd.next, struct scsi_cmnd,
+ pending_cmd);
+ list_del_init(&scmd->pending_cmd);
+ SCSI_LOG_MLQUEUE(3,
+ printk("%s: removed from pending cmd: 0x%lx\n",
+ __FUNCTION__, scmd->serial_number));
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ scsi_dispatch_cmd(scmd, 0);
+ } else
+ spin_unlock_irqrestore(shost->host_lock, flags);
}
diff -Nru a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
--- a/drivers/scsi/hosts.h Mon Mar 3 14:44:53 2003
+++ b/drivers/scsi/hosts.h Mon Mar 3 14:44:53 2003
@@ -380,6 +380,7 @@
struct scsi_host_cmd_pool *cmd_pool;
spinlock_t free_list_lock;
struct list_head free_list; /* backup store of cmd structs */
+ struct list_head pending_cmd; /* head of list */
spinlock_t default_lock;
spinlock_t *host_lock;
@@ -471,12 +472,6 @@
unsigned reverse_ordering:1;
/*
- * Indicates that one or more devices on this host were starved, and
- * when the device becomes less busy that we need to feed them.
- */
- unsigned some_device_starved:1;
-
- /*
* Host has rejected a command because it was busy.
*/
unsigned int host_blocked;
@@ -580,12 +575,9 @@
extern struct Scsi_Host *scsi_host_hn_get(unsigned short);
extern void scsi_host_put(struct Scsi_Host *);
extern void scsi_host_init(void);
-
-/*
- * host_busy inc/dec/test functions
- */
extern void scsi_host_busy_inc(struct Scsi_Host *, Scsi_Device *);
extern void scsi_host_busy_dec_and_test(struct Scsi_Host *, Scsi_Device *);
+extern void scsi_host_send_pending (struct Scsi_Host *shost);
/**
* scsi_find_device - find a device given the host
diff -Nru a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
--- a/drivers/scsi/scsi.c Mon Mar 3 14:44:53 2003
+++ b/drivers/scsi/scsi.c Mon Mar 3 14:44:54 2003
@@ -303,6 +303,7 @@
cmd->owner = SCSI_OWNER_NOBODY;
init_timer(&cmd->eh_timeout);
INIT_LIST_HEAD(&cmd->list);
+ INIT_LIST_HEAD(&cmd->pending_cmd);
spin_lock_irqsave(&dev->list_lock, flags);
list_add_tail(&cmd->list, &dev->cmd_list);
spin_unlock_irqrestore(&dev->list_lock, flags);
@@ -423,15 +424,17 @@
}
/*
- * Function: scsi_dispatch_command
+ * Function: scsi_dispatch_cmd
*
* Purpose: Dispatch a command to the low-level driver.
*
* Arguments: SCpnt - command block we are dispatching.
+ * resend - command is being resent, host_busy was not
+ * decremented (blah), and we have a slot available for SCpnt.
*
* Notes:
*/
-int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt)
+int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt, int resend)
{
#ifdef DEBUG_DELAY
unsigned long clock;
@@ -494,7 +497,7 @@
* We will use a queued command if possible, otherwise we will emulate the
* queuing and calling of completion function ourselves.
*/
- SCSI_LOG_MLQUEUE(3, printk("scsi_dispatch_cmnd (host = %d, channel = %d, target = %d, "
+ SCSI_LOG_MLQUEUE(4, printk("scsi_dispatch_cmnd (host = %d, channel = %d, target = %d, "
"command = %p, buffer = %p, \nbufflen = %d, done = %p)\n",
SCpnt->device->host->host_no, SCpnt->device->channel, SCpnt->device->id, SCpnt->cmnd,
SCpnt->buffer, SCpnt->bufflen, SCpnt->done));
@@ -502,7 +505,7 @@
SCpnt->state = SCSI_STATE_QUEUED;
SCpnt->owner = SCSI_OWNER_LOWLEVEL;
if (host->can_queue) {
- SCSI_LOG_MLQUEUE(3, printk("queuecommand : routine at %p\n",
+ SCSI_LOG_MLQUEUE(4, printk("queuecommand : routine at %p\n",
host->hostt->queuecommand));
/*
* Before we queue this command, check if the command
@@ -510,12 +513,33 @@
*/
if (CDB_SIZE(SCpnt) <= SCpnt->device->host->max_cmd_len) {
spin_lock_irqsave(host->host_lock, flags);
- rtn = host->hostt->queuecommand(SCpnt, scsi_done);
- spin_unlock_irqrestore(host->host_lock, flags);
- if (rtn != 0) {
- scsi_queue_insert(SCpnt, rtn == SCSI_MLQUEUE_DEVICE_BUSY ? rtn : SCSI_MLQUEUE_HOST_BUSY);
+ if (!resend && ((host->host_busy > host->can_queue) ||
+ host->host_blocked)) {
+ /*
+ * queue a pending command
+ */
SCSI_LOG_MLQUEUE(3,
- printk("queuecommand : request rejected\n"));
+ printk("%s: add to pending cmd:"
+ " 0x%lx\n", __FUNCTION__,
+ SCpnt->serial_number));
+ scsi_delete_timer(SCpnt);
+ list_add_tail(&SCpnt->pending_cmd,
+ &host->pending_cmd);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ rtn = 1;
+ } else {
+ if (!resend)
+ host->host_busy++;
+ rtn = host->hostt->queuecommand(SCpnt,
+ scsi_done);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ if (rtn != 0) {
+ scsi_queue_insert(SCpnt,
+ rtn == SCSI_MLQUEUE_DEVICE_BUSY
+ ? rtn : SCSI_MLQUEUE_HOST_BUSY);
+ SCSI_LOG_MLQUEUE(3,
+ printk("queuecommand : request rejected\n"));
+ }
}
} else {
SCSI_LOG_MLQUEUE(3,
@@ -531,6 +555,8 @@
SCSI_LOG_MLQUEUE(3, printk("command() : routine at %p\n", host->hostt->command));
spin_lock_irqsave(host->host_lock, flags);
+ if (!resend)
+ host->host_busy++;
temp = host->hostt->command(SCpnt);
SCpnt->result = temp;
#ifdef DEBUG_DELAY
@@ -547,7 +573,7 @@
scsi_done(SCpnt);
spin_unlock_irqrestore(host->host_lock, flags);
}
- SCSI_LOG_MLQUEUE(3, printk("leaving scsi_dispatch_cmnd()\n"));
+ SCSI_LOG_MLQUEUE(4, printk("leaving scsi_dispatch_cmnd()\n"));
return rtn;
}
@@ -824,7 +850,7 @@
*/
memset((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer);
- return scsi_dispatch_cmd(SCpnt);
+ return scsi_dispatch_cmd(SCpnt, 1);
}
/*
@@ -845,23 +871,12 @@
ASSERT_LOCK(host->host_lock, 0);
- /*
- * We need to protect the decrement, as otherwise a race condition
- * would exist. Fiddling with SCpnt isn't a problem as the
- * design only allows a single SCpnt to be active in only
- * one execution context, but the device and host structures are
- * shared.
- */
- scsi_host_busy_dec_and_test(host, device);
-
- /*
- * Clear the flags which say that the device/host is no longer
- * capable of accepting new commands. These are set in scsi_queue.c
- * for both the queue full condition on a device, and for a
- * host full condition on the host.
- */
- host->host_blocked = 0;
device->device_blocked = 0;
+ scsi_host_busy_dec_and_test(host, device);
+ if (host->host_blocked) {
+ host->host_blocked = 0;
+ scsi_host_send_pending(host);
+ }
/*
* If we have valid sense information, then some kind of recovery
diff -Nru a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
--- a/drivers/scsi/scsi.h Mon Mar 3 14:44:53 2003
+++ b/drivers/scsi/scsi.h Mon Mar 3 14:44:53 2003
@@ -444,7 +444,7 @@
/*
* Prototypes for functions in scsi.c
*/
-extern int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt);
+extern int scsi_dispatch_cmd(Scsi_Cmnd * SCpnt, int resend);
extern int scsi_setup_command_freelist(struct Scsi_Host *shost);
extern void scsi_destroy_command_freelist(struct Scsi_Host *shost);
extern struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, int flags);
@@ -634,8 +634,6 @@
* because we did a bus reset. */
unsigned ten:1; /* support ten byte read / write */
unsigned remap:1; /* support remapping */
- unsigned starved:1; /* unable to process commands because
- host busy */
// unsigned sync:1; /* Sync transfer state, managed by host */
// unsigned wide:1; /* WIDE transfer state, managed by host */
@@ -727,7 +725,9 @@
struct scsi_cmnd *reset_chain;
struct list_head list; /* scsi_cmnd participates in queue lists */
-
+ struct list_head pending_cmd; /* for use with host->pending_cmd, this
+ * could be shared with eh_entry.
+ */
struct list_head eh_entry; /* entry for the host eh_cmd_q */
int eh_state; /* Used for state tracking in error handlr */
int eh_eflags; /* Used by error handlr */
@@ -854,6 +854,7 @@
#define SCSI_MLQUEUE_HOST_BUSY 0x1055
#define SCSI_MLQUEUE_DEVICE_BUSY 0x1056
#define SCSI_MLQUEUE_EH_RETRY 0x1057
+#define SCSI_MLQUEUE_PENDING 0x1058
/*
* old style reset request from external source
diff -Nru a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
--- a/drivers/scsi/scsi_error.c Mon Mar 3 14:44:53 2003
+++ b/drivers/scsi/scsi_error.c Mon Mar 3 14:44:53 2003
@@ -1467,16 +1467,8 @@
* requests are started.
*/
spin_lock_irqsave(shost->host_lock, flags);
- list_for_each_entry(sdev, &shost->my_devices, siblings) {
- if ((shost->can_queue > 0 &&
- (shost->host_busy >= shost->can_queue))
- || (shost->host_blocked)
- || (shost->host_self_blocked)) {
- break;
- }
-
+ list_for_each_entry(sdev, &shost->my_devices, siblings)
__blk_run_queue(sdev->request_queue);
- }
spin_unlock_irqrestore(shost->host_lock, flags);
}
@@ -1497,6 +1489,34 @@
}
/**
+ * scsi_eh_flush_pending_cmds - finish pending commands or requeue them.
+ * @pending: list_head of queued pending commands.
+ *
+ **/
+static void scsi_eh_flush_pending_cmds(struct list_head *pending)
+{
+ struct list_head *lh, *lh_sf;
+ struct scsi_cmnd *scmd;
+
+ list_for_each_safe(lh, lh_sf, pending) {
+ scmd = list_entry(lh, struct scsi_cmnd, pending_cmd);
+ list_del_init(lh);
+ if (scmd->device->online) {
+ SCSI_LOG_ERROR_RECOVERY(3,
+ printk("%s: flush pending cmd: %p\n",
+ current->comm, scmd));
+ scsi_queue_insert(scmd, SCSI_MLQUEUE_PENDING);
+ } else {
+ scmd->result |= (DRIVER_TIMEOUT << 24);
+ SCSI_LOG_ERROR_RECOVERY(3,
+ printk("%s: finish pending cmd: %p\n",
+ current->comm, scmd));
+ scsi_finish_command(scmd);
+ }
+ }
+}
+
+/**
* scsi_eh_flush_done_q - finish processed commands or retry them.
* @done_q: list_head of processed commands.
*
@@ -1557,6 +1577,7 @@
unsigned long flags;
LIST_HEAD(eh_work_q);
LIST_HEAD(eh_done_q);
+ LIST_HEAD(pending);
spin_lock_irqsave(shost->host_lock, flags);
list_splice_init(&shost->eh_cmd_q, &eh_work_q);
@@ -1568,6 +1589,16 @@
if (!scsi_eh_abort_cmds(&eh_work_q, &eh_done_q))
scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);
+ /*
+ * Finish or requeue all outstanding commands, first the pending
+ * (so they are sent after any error recovered commands) and then
+ * any error recovered commands.
+ */
+ spin_lock_irqsave(shost->host_lock, flags);
+ list_splice_init(&shost->pending_cmd, &pending);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ scsi_eh_flush_pending_cmds(&pending);
+
scsi_eh_flush_done_q(&eh_done_q);
}
@@ -1659,7 +1690,9 @@
* restart, we restart any I/O to any other devices on the bus
* which are still online.
*/
+
scsi_restart_operations(shost);
+
}
diff -Nru a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
--- a/drivers/scsi/scsi_lib.c Mon Mar 3 14:44:53 2003
+++ b/drivers/scsi/scsi_lib.c Mon Mar 3 14:44:53 2003
@@ -92,9 +92,11 @@
{
struct Scsi_Host *host = cmd->device->host;
struct scsi_device *device = cmd->device;
+ unsigned long flags;
SCSI_LOG_MLQUEUE(1,
- printk("Inserting command %p into mlqueue\n", cmd));
+ printk("Inserting command %p into mlqueue reason 0x%x\n",
+ cmd, reason));
/*
* We are inserting the command into the ml queue. First, we
@@ -127,11 +129,20 @@
cmd->owner = SCSI_OWNER_MIDLEVEL;
cmd->bh_next = NULL;
- /*
- * Decrement the counters, since these commands are no longer
- * active on the host/device.
- */
- scsi_host_busy_dec_and_test(host, device);
+ if (reason == SCSI_MLQUEUE_PENDING) {
+ /*
+ * Pending commands have not actually made it to the host,
+ * so do not decrement host_busy.
+ */
+ spin_lock_irqsave(host->host_lock, flags);
+ device->device_busy--;
+ spin_unlock_irqrestore(host->host_lock, flags);
+ } else
+ /*
+ * Decrement the counters, since these commands are no longer
+ * active on the host/device.
+ */
+ scsi_host_busy_dec_and_test(host, device);
/*
* Insert this command at the head of the queue for it's device.
@@ -358,7 +369,6 @@
struct scsi_device *sdev, *sdev2;
struct Scsi_Host *shost;
unsigned long flags;
- int all_clear;
ASSERT_LOCK(q->queue_lock, 0);
@@ -400,10 +410,7 @@
* with special case code, then spin off separate versions and
* use function pointers to pick the right one.
*/
- if (sdev->single_lun && blk_queue_empty(q) && sdev->device_busy ==0 &&
- !shost->host_blocked && !shost->host_self_blocked &&
- !((shost->can_queue > 0) && (shost->host_busy >=
- shost->can_queue))) {
+ if (sdev->single_lun && blk_queue_empty(q) && sdev->device_busy == 0) {
list_for_each_entry(sdev2, &sdev->same_target_siblings,
same_target_siblings) {
if (!sdev2->device_blocked &&
@@ -414,31 +421,6 @@
}
}
- /*
- * Now see whether there are other devices on the bus which
- * might be starved. If so, hit the request function. If we
- * don't find any, then it is safe to reset the flag. If we
- * find any device that it is starved, it isn't safe to reset the
- * flag as the queue function releases the lock and thus some
- * other device might have become starved along the way.
- */
- all_clear = 1;
- if (shost->some_device_starved) {
- list_for_each_entry(sdev, &shost->my_devices, siblings) {
- if (shost->can_queue > 0 &&
- shost->host_busy >= shost->can_queue)
- break;
- if (shost->host_blocked || shost->host_self_blocked)
- break;
- if (sdev->device_blocked || !sdev->starved)
- continue;
- __blk_run_queue(sdev->request_queue);
- all_clear = 0;
- }
-
- if (sdev == NULL && all_clear)
- shost->some_device_starved = 0;
- }
spin_unlock_irqrestore(q->queue_lock, flags);
}
@@ -1094,6 +1076,7 @@
SCSI_LOG_MLQUEUE(3,
printk("scsi%d unblocking host at zero depth\n",
shost->host_no));
+ WARN_ON(!list_empty(&shost->pending_cmd));
} else {
blk_plug_device(q);
break;
@@ -1117,23 +1100,18 @@
*/
if (sdev->device_blocked)
break;
- if ((shost->can_queue > 0 && shost->host_busy >= shost->can_queue) ||
- shost->host_blocked || shost->host_self_blocked) {
+
+ if (shost->host_self_blocked)
+ break;
+
+ if ((sdev->device_busy > 0) && shost->host_blocked)
/*
- * If we are unable to process any commands at all for
- * this device, then we consider it to be starved.
- * What this means is that there are no outstanding
- * commands for this device and hence we need a
- * little help getting it started again
- * once the host isn't quite so busy.
+ * On unblock during IO completion, we send
+ * pending commands, make sure we always have at
+ * least one command pending to keep this queue
+ * running again.
*/
- if (sdev->device_busy == 0) {
- sdev->starved = 1;
- shost->some_device_starved = 1;
- }
break;
- } else
- sdev->starved = 0;
/*
* If we couldn't find a request that could be queued, then we
@@ -1170,11 +1148,6 @@
if (!(blk_queue_tagged(q) && (blk_queue_start_tag(q, req) == 0)))
blkdev_dequeue_request(req);
- /*
- * Now bump the usage count for both the host and the
- * device.
- */
- shost->host_busy++;
sdev->device_busy++;
spin_unlock_irq(q->queue_lock);
@@ -1187,7 +1160,7 @@
/*
* Dispatch the command to the low-level driver.
*/
- scsi_dispatch_cmd(cmd);
+ scsi_dispatch_cmd(cmd, 0);
/*
* Now we need to grab the lock again. We are about to mess
-- Patrick Mansfield
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2003-03-04 0:12 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-03-04 0:12 [PATCH] scsi-misc-2.5 software enqueue when can_queue is reached Patrick Mansfield
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox