aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Axboe <jaxboe@fusionio.com>2011-05-17 05:04:44 -0400
committerJens Axboe <jaxboe@fusionio.com>2011-05-17 05:04:44 -0400
commit9937a5e2f32892db0dbeefc2b3bc74b3ae3ea9c7 (patch)
tree0448e96b503deb71dd8a1228da94a9fc22a57d48
parent70087dc38cc77ca8f46059564c00338777734762 (diff)
scsi: remove performance regression due to async queue run
Commit c21e6beb removed our queue request_fn re-enter protection, and defaulted to always running the queues from kblockd to be safe. This was a known potential slow down, but should be safe. Unfortunately this is causing big performance regressions for some, so we need to improve this logic. Looking into the details of the re-enter, the real issue is on requeue of requests. Requeue of requests upon seeing a BUSY condition from the device ends up re-running the queue, causing traces like this: scsi_request_fn() scsi_dispatch_cmd() scsi_queue_insert() __scsi_queue_insert() scsi_run_queue() scsi_request_fn() ... potentially causing the issue we want to avoid. So special case the requeue re-run of the queue, but improve it to offload the entire run of local queue and starved queue from a single workqueue callback. This is a lot better than potentially kicking off a workqueue run for each device seen. This also fixes the issue of the local device going into recursion, since the above mentioned commit never moved that queue run out of line. Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
-rw-r--r--drivers/scsi/scsi_lib.c20
-rw-r--r--drivers/scsi/scsi_scan.c2
-rw-r--r--include/scsi/scsi_device.h1
3 files changed, 19 insertions, 4 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index e9901b8f8443..01e4e51c4b68 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -74,8 +74,6 @@ struct kmem_cache *scsi_sdb_cache;
74 */ 74 */
75#define SCSI_QUEUE_DELAY 3 75#define SCSI_QUEUE_DELAY 3
76 76
77static void scsi_run_queue(struct request_queue *q);
78
79/* 77/*
80 * Function: scsi_unprep_request() 78 * Function: scsi_unprep_request()
81 * 79 *
@@ -161,7 +159,7 @@ static int __scsi_queue_insert(struct scsi_cmnd *cmd, int reason, int unbusy)
161 blk_requeue_request(q, cmd->request); 159 blk_requeue_request(q, cmd->request);
162 spin_unlock_irqrestore(q->queue_lock, flags); 160 spin_unlock_irqrestore(q->queue_lock, flags);
163 161
164 scsi_run_queue(q); 162 kblockd_schedule_work(q, &device->requeue_work);
165 163
166 return 0; 164 return 0;
167} 165}
@@ -433,7 +431,11 @@ static void scsi_run_queue(struct request_queue *q)
433 continue; 431 continue;
434 } 432 }
435 433
436 blk_run_queue_async(sdev->request_queue); 434 spin_unlock(shost->host_lock);
435 spin_lock(sdev->request_queue->queue_lock);
436 __blk_run_queue(sdev->request_queue);
437 spin_unlock(sdev->request_queue->queue_lock);
438 spin_lock(shost->host_lock);
437 } 439 }
438 /* put any unprocessed entries back */ 440 /* put any unprocessed entries back */
439 list_splice(&starved_list, &shost->starved_list); 441 list_splice(&starved_list, &shost->starved_list);
@@ -442,6 +444,16 @@ static void scsi_run_queue(struct request_queue *q)
442 blk_run_queue(q); 444 blk_run_queue(q);
443} 445}
444 446
447void scsi_requeue_run_queue(struct work_struct *work)
448{
449 struct scsi_device *sdev;
450 struct request_queue *q;
451
452 sdev = container_of(work, struct scsi_device, requeue_work);
453 q = sdev->request_queue;
454 scsi_run_queue(q);
455}
456
445/* 457/*
446 * Function: scsi_requeue_command() 458 * Function: scsi_requeue_command()
447 * 459 *
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 087821fac8fe..58584dc0724a 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -242,6 +242,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
242 int display_failure_msg = 1, ret; 242 int display_failure_msg = 1, ret;
243 struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 243 struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
244 extern void scsi_evt_thread(struct work_struct *work); 244 extern void scsi_evt_thread(struct work_struct *work);
245 extern void scsi_requeue_run_queue(struct work_struct *work);
245 246
246 sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size, 247 sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size,
247 GFP_ATOMIC); 248 GFP_ATOMIC);
@@ -264,6 +265,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
264 INIT_LIST_HEAD(&sdev->event_list); 265 INIT_LIST_HEAD(&sdev->event_list);
265 spin_lock_init(&sdev->list_lock); 266 spin_lock_init(&sdev->list_lock);
266 INIT_WORK(&sdev->event_work, scsi_evt_thread); 267 INIT_WORK(&sdev->event_work, scsi_evt_thread);
268 INIT_WORK(&sdev->requeue_work, scsi_requeue_run_queue);
267 269
268 sdev->sdev_gendev.parent = get_device(&starget->dev); 270 sdev->sdev_gendev.parent = get_device(&starget->dev);
269 sdev->sdev_target = starget; 271 sdev->sdev_target = starget;
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 2d3ec5094685..dd82e02ddde3 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -169,6 +169,7 @@ struct scsi_device {
169 sdev_dev; 169 sdev_dev;
170 170
171 struct execute_work ew; /* used to get process context on put */ 171 struct execute_work ew; /* used to get process context on put */
172 struct work_struct requeue_work;
172 173
173 struct scsi_dh_data *scsi_dh_data; 174 struct scsi_dh_data *scsi_dh_data;
174 enum scsi_device_state sdev_state; 175 enum scsi_device_state sdev_state;