diff options
author | Christoph Hellwig <hch@lst.de> | 2014-01-22 09:29:29 -0500 |
---|---|---|
committer | Christoph Hellwig <hch@lst.de> | 2014-07-25 07:43:43 -0400 |
commit | 74665016086615bbaa3fa6f83af410a0a4e029ee (patch) | |
tree | 9f9d8a692bec91dce69fd303eeedf16c35b41ac6 | |
parent | 7ae65c0f9646c29432b69580b80e08632e6cd813 (diff) |
scsi: convert host_busy to atomic_t
Avoid taking the host-wide host_lock to check the per-host queue limit.
Instead we do an atomic_inc_return early on to grab our slot in the queue,
and if necessary decrement it after finishing all checks.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Webb Scales <webbnh@hp.com>
Acked-by: Jens Axboe <axboe@kernel.dk>
Tested-by: Bart Van Assche <bvanassche@acm.org>
Tested-by: Robert Elliott <elliott@hp.com>
-rw-r--r-- | drivers/scsi/advansys.c | 4 | ||||
-rw-r--r-- | drivers/scsi/libiscsi.c | 4 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 5 | ||||
-rw-r--r-- | drivers/scsi/qlogicpti.c | 2 | ||||
-rw-r--r-- | drivers/scsi/scsi.c | 2 | ||||
-rw-r--r-- | drivers/scsi/scsi_error.c | 7 | ||||
-rw-r--r-- | drivers/scsi/scsi_lib.c | 74 | ||||
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 9 | ||||
-rw-r--r-- | include/scsi/scsi_host.h | 10 |
9 files changed, 69 insertions, 48 deletions
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index e716d0aef194..43761c1c46f0 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c | |||
@@ -2512,7 +2512,7 @@ static void asc_prt_scsi_host(struct Scsi_Host *s) | |||
2512 | 2512 | ||
2513 | printk("Scsi_Host at addr 0x%p, device %s\n", s, dev_name(boardp->dev)); | 2513 | printk("Scsi_Host at addr 0x%p, device %s\n", s, dev_name(boardp->dev)); |
2514 | printk(" host_busy %u, host_no %d,\n", | 2514 | printk(" host_busy %u, host_no %d,\n", |
2515 | s->host_busy, s->host_no); | 2515 | atomic_read(&s->host_busy), s->host_no); |
2516 | 2516 | ||
2517 | printk(" base 0x%lx, io_port 0x%lx, irq %d,\n", | 2517 | printk(" base 0x%lx, io_port 0x%lx, irq %d,\n", |
2518 | (ulong)s->base, (ulong)s->io_port, boardp->irq); | 2518 | (ulong)s->base, (ulong)s->io_port, boardp->irq); |
@@ -3346,7 +3346,7 @@ static void asc_prt_driver_conf(struct seq_file *m, struct Scsi_Host *shost) | |||
3346 | 3346 | ||
3347 | seq_printf(m, | 3347 | seq_printf(m, |
3348 | " host_busy %u, max_id %u, max_lun %llu, max_channel %u\n", | 3348 | " host_busy %u, max_id %u, max_lun %llu, max_channel %u\n", |
3349 | shost->host_busy, shost->max_id, | 3349 | atomic_read(&shost->host_busy), shost->max_id, |
3350 | shost->max_lun, shost->max_channel); | 3350 | shost->max_lun, shost->max_channel); |
3351 | 3351 | ||
3352 | seq_printf(m, | 3352 | seq_printf(m, |
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index f2db82beb646..f9f3a1224dfa 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c | |||
@@ -2971,7 +2971,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) | |||
2971 | */ | 2971 | */ |
2972 | for (;;) { | 2972 | for (;;) { |
2973 | spin_lock_irqsave(session->host->host_lock, flags); | 2973 | spin_lock_irqsave(session->host->host_lock, flags); |
2974 | if (!session->host->host_busy) { /* OK for ERL == 0 */ | 2974 | if (!atomic_read(&session->host->host_busy)) { /* OK for ERL == 0 */ |
2975 | spin_unlock_irqrestore(session->host->host_lock, flags); | 2975 | spin_unlock_irqrestore(session->host->host_lock, flags); |
2976 | break; | 2976 | break; |
2977 | } | 2977 | } |
@@ -2979,7 +2979,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) | |||
2979 | msleep_interruptible(500); | 2979 | msleep_interruptible(500); |
2980 | iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): " | 2980 | iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): " |
2981 | "host_busy %d host_failed %d\n", | 2981 | "host_busy %d host_failed %d\n", |
2982 | session->host->host_busy, | 2982 | atomic_read(&session->host->host_busy), |
2983 | session->host->host_failed); | 2983 | session->host->host_failed); |
2984 | /* | 2984 | /* |
2985 | * force eh_abort() to unblock | 2985 | * force eh_abort() to unblock |
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 7d02a19419a7..24e477d2ea70 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c | |||
@@ -813,7 +813,7 @@ retry: | |||
813 | spin_unlock_irq(shost->host_lock); | 813 | spin_unlock_irq(shost->host_lock); |
814 | 814 | ||
815 | SAS_DPRINTK("Enter %s busy: %d failed: %d\n", | 815 | SAS_DPRINTK("Enter %s busy: %d failed: %d\n", |
816 | __func__, shost->host_busy, shost->host_failed); | 816 | __func__, atomic_read(&shost->host_busy), shost->host_failed); |
817 | /* | 817 | /* |
818 | * Deal with commands that still have SAS tasks (i.e. they didn't | 818 | * Deal with commands that still have SAS tasks (i.e. they didn't |
819 | * complete via the normal sas_task completion mechanism), | 819 | * complete via the normal sas_task completion mechanism), |
@@ -858,7 +858,8 @@ out: | |||
858 | goto retry; | 858 | goto retry; |
859 | 859 | ||
860 | SAS_DPRINTK("--- Exit %s: busy: %d failed: %d tries: %d\n", | 860 | SAS_DPRINTK("--- Exit %s: busy: %d failed: %d tries: %d\n", |
861 | __func__, shost->host_busy, shost->host_failed, tries); | 861 | __func__, atomic_read(&shost->host_busy), |
862 | shost->host_failed, tries); | ||
862 | } | 863 | } |
863 | 864 | ||
864 | enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) | 865 | enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) |
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 6d48d30bed05..740ae495aa77 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c | |||
@@ -959,7 +959,7 @@ static inline void update_can_queue(struct Scsi_Host *host, u_int in_ptr, u_int | |||
959 | /* Temporary workaround until bug is found and fixed (one bug has been found | 959 | /* Temporary workaround until bug is found and fixed (one bug has been found |
960 | already, but fixing it makes things even worse) -jj */ | 960 | already, but fixing it makes things even worse) -jj */ |
961 | int num_free = QLOGICPTI_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr) - 64; | 961 | int num_free = QLOGICPTI_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr) - 64; |
962 | host->can_queue = host->host_busy + num_free; | 962 | host->can_queue = atomic_read(&host->host_busy) + num_free; |
963 | host->sg_tablesize = QLOGICPTI_MAX_SG(num_free); | 963 | host->sg_tablesize = QLOGICPTI_MAX_SG(num_free); |
964 | } | 964 | } |
965 | 965 | ||
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 6200a2615436..21fb97b01dd6 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c | |||
@@ -600,7 +600,7 @@ void scsi_log_completion(struct scsi_cmnd *cmd, int disposition) | |||
600 | if (level > 3) | 600 | if (level > 3) |
601 | scmd_printk(KERN_INFO, cmd, | 601 | scmd_printk(KERN_INFO, cmd, |
602 | "scsi host busy %d failed %d\n", | 602 | "scsi host busy %d failed %d\n", |
603 | cmd->device->host->host_busy, | 603 | atomic_read(&cmd->device->host->host_busy), |
604 | cmd->device->host->host_failed); | 604 | cmd->device->host->host_failed); |
605 | } | 605 | } |
606 | } | 606 | } |
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index e4a532463f9a..5db8454474ee 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -59,7 +59,7 @@ static int scsi_try_to_abort_cmd(struct scsi_host_template *, | |||
59 | /* called with shost->host_lock held */ | 59 | /* called with shost->host_lock held */ |
60 | void scsi_eh_wakeup(struct Scsi_Host *shost) | 60 | void scsi_eh_wakeup(struct Scsi_Host *shost) |
61 | { | 61 | { |
62 | if (shost->host_busy == shost->host_failed) { | 62 | if (atomic_read(&shost->host_busy) == shost->host_failed) { |
63 | trace_scsi_eh_wakeup(shost); | 63 | trace_scsi_eh_wakeup(shost); |
64 | wake_up_process(shost->ehandler); | 64 | wake_up_process(shost->ehandler); |
65 | SCSI_LOG_ERROR_RECOVERY(5, shost_printk(KERN_INFO, shost, | 65 | SCSI_LOG_ERROR_RECOVERY(5, shost_printk(KERN_INFO, shost, |
@@ -2164,7 +2164,7 @@ int scsi_error_handler(void *data) | |||
2164 | while (!kthread_should_stop()) { | 2164 | while (!kthread_should_stop()) { |
2165 | set_current_state(TASK_INTERRUPTIBLE); | 2165 | set_current_state(TASK_INTERRUPTIBLE); |
2166 | if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) || | 2166 | if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) || |
2167 | shost->host_failed != shost->host_busy) { | 2167 | shost->host_failed != atomic_read(&shost->host_busy)) { |
2168 | SCSI_LOG_ERROR_RECOVERY(1, | 2168 | SCSI_LOG_ERROR_RECOVERY(1, |
2169 | shost_printk(KERN_INFO, shost, | 2169 | shost_printk(KERN_INFO, shost, |
2170 | "scsi_eh_%d: sleeping\n", | 2170 | "scsi_eh_%d: sleeping\n", |
@@ -2178,7 +2178,8 @@ int scsi_error_handler(void *data) | |||
2178 | shost_printk(KERN_INFO, shost, | 2178 | shost_printk(KERN_INFO, shost, |
2179 | "scsi_eh_%d: waking up %d/%d/%d\n", | 2179 | "scsi_eh_%d: waking up %d/%d/%d\n", |
2180 | shost->host_no, shost->host_eh_scheduled, | 2180 | shost->host_no, shost->host_eh_scheduled, |
2181 | shost->host_failed, shost->host_busy)); | 2181 | shost->host_failed, |
2182 | atomic_read(&shost->host_busy))); | ||
2182 | 2183 | ||
2183 | /* | 2184 | /* |
2184 | * We have a host that is failing for some reason. Figure out | 2185 | * We have a host that is failing for some reason. Figure out |
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 0580711c2c57..d0bd7e0ab7a8 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c | |||
@@ -292,14 +292,17 @@ void scsi_device_unbusy(struct scsi_device *sdev) | |||
292 | struct scsi_target *starget = scsi_target(sdev); | 292 | struct scsi_target *starget = scsi_target(sdev); |
293 | unsigned long flags; | 293 | unsigned long flags; |
294 | 294 | ||
295 | spin_lock_irqsave(shost->host_lock, flags); | 295 | atomic_dec(&shost->host_busy); |
296 | shost->host_busy--; | ||
297 | atomic_dec(&starget->target_busy); | 296 | atomic_dec(&starget->target_busy); |
297 | |||
298 | if (unlikely(scsi_host_in_recovery(shost) && | 298 | if (unlikely(scsi_host_in_recovery(shost) && |
299 | (shost->host_failed || shost->host_eh_scheduled))) | 299 | (shost->host_failed || shost->host_eh_scheduled))) { |
300 | spin_lock_irqsave(shost->host_lock, flags); | ||
300 | scsi_eh_wakeup(shost); | 301 | scsi_eh_wakeup(shost); |
301 | spin_unlock(shost->host_lock); | 302 | spin_unlock_irqrestore(shost->host_lock, flags); |
302 | spin_lock(sdev->request_queue->queue_lock); | 303 | } |
304 | |||
305 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | ||
303 | sdev->device_busy--; | 306 | sdev->device_busy--; |
304 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | 307 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); |
305 | } | 308 | } |
@@ -367,7 +370,8 @@ static inline int scsi_target_is_busy(struct scsi_target *starget) | |||
367 | 370 | ||
368 | static inline int scsi_host_is_busy(struct Scsi_Host *shost) | 371 | static inline int scsi_host_is_busy(struct Scsi_Host *shost) |
369 | { | 372 | { |
370 | if ((shost->can_queue > 0 && shost->host_busy >= shost->can_queue) || | 373 | if ((shost->can_queue > 0 && |
374 | atomic_read(&shost->host_busy) >= shost->can_queue) || | ||
371 | shost->host_blocked || shost->host_self_blocked) | 375 | shost->host_blocked || shost->host_self_blocked) |
372 | return 1; | 376 | return 1; |
373 | 377 | ||
@@ -1334,38 +1338,54 @@ static inline int scsi_host_queue_ready(struct request_queue *q, | |||
1334 | struct Scsi_Host *shost, | 1338 | struct Scsi_Host *shost, |
1335 | struct scsi_device *sdev) | 1339 | struct scsi_device *sdev) |
1336 | { | 1340 | { |
1337 | int ret = 0; | 1341 | unsigned int busy; |
1338 | |||
1339 | spin_lock_irq(shost->host_lock); | ||
1340 | 1342 | ||
1341 | if (scsi_host_in_recovery(shost)) | 1343 | if (scsi_host_in_recovery(shost)) |
1342 | goto out; | 1344 | return 0; |
1343 | if (shost->host_busy == 0 && shost->host_blocked) { | 1345 | |
1346 | busy = atomic_inc_return(&shost->host_busy) - 1; | ||
1347 | if (shost->host_blocked) { | ||
1348 | if (busy) | ||
1349 | goto starved; | ||
1350 | |||
1344 | /* | 1351 | /* |
1345 | * unblock after host_blocked iterates to zero | 1352 | * unblock after host_blocked iterates to zero |
1346 | */ | 1353 | */ |
1347 | if (--shost->host_blocked != 0) | 1354 | spin_lock_irq(shost->host_lock); |
1348 | goto out; | 1355 | if (--shost->host_blocked != 0) { |
1356 | spin_unlock_irq(shost->host_lock); | ||
1357 | goto out_dec; | ||
1358 | } | ||
1359 | spin_unlock_irq(shost->host_lock); | ||
1349 | 1360 | ||
1350 | SCSI_LOG_MLQUEUE(3, | 1361 | SCSI_LOG_MLQUEUE(3, |
1351 | shost_printk(KERN_INFO, shost, | 1362 | shost_printk(KERN_INFO, shost, |
1352 | "unblocking host at zero depth\n")); | 1363 | "unblocking host at zero depth\n")); |
1353 | } | 1364 | } |
1354 | if (scsi_host_is_busy(shost)) { | 1365 | |
1355 | if (list_empty(&sdev->starved_entry)) | 1366 | if (shost->can_queue > 0 && busy >= shost->can_queue) |
1356 | list_add_tail(&sdev->starved_entry, &shost->starved_list); | 1367 | goto starved; |
1357 | goto out; | 1368 | if (shost->host_self_blocked) |
1358 | } | 1369 | goto starved; |
1359 | 1370 | ||
1360 | /* We're OK to process the command, so we can't be starved */ | 1371 | /* We're OK to process the command, so we can't be starved */ |
1361 | if (!list_empty(&sdev->starved_entry)) | 1372 | if (!list_empty(&sdev->starved_entry)) { |
1362 | list_del_init(&sdev->starved_entry); | 1373 | spin_lock_irq(shost->host_lock); |
1374 | if (!list_empty(&sdev->starved_entry)) | ||
1375 | list_del_init(&sdev->starved_entry); | ||
1376 | spin_unlock_irq(shost->host_lock); | ||
1377 | } | ||
1363 | 1378 | ||
1364 | shost->host_busy++; | 1379 | return 1; |
1365 | ret = 1; | 1380 | |
1366 | out: | 1381 | starved: |
1382 | spin_lock_irq(shost->host_lock); | ||
1383 | if (list_empty(&sdev->starved_entry)) | ||
1384 | list_add_tail(&sdev->starved_entry, &shost->starved_list); | ||
1367 | spin_unlock_irq(shost->host_lock); | 1385 | spin_unlock_irq(shost->host_lock); |
1368 | return ret; | 1386 | out_dec: |
1387 | atomic_dec(&shost->host_busy); | ||
1388 | return 0; | ||
1369 | } | 1389 | } |
1370 | 1390 | ||
1371 | /* | 1391 | /* |
@@ -1429,12 +1449,8 @@ static void scsi_kill_request(struct request *req, struct request_queue *q) | |||
1429 | * with the locks as normal issue path does. | 1449 | * with the locks as normal issue path does. |
1430 | */ | 1450 | */ |
1431 | sdev->device_busy++; | 1451 | sdev->device_busy++; |
1432 | spin_unlock(sdev->request_queue->queue_lock); | 1452 | atomic_inc(&shost->host_busy); |
1433 | spin_lock(shost->host_lock); | ||
1434 | shost->host_busy++; | ||
1435 | atomic_inc(&starget->target_busy); | 1453 | atomic_inc(&starget->target_busy); |
1436 | spin_unlock(shost->host_lock); | ||
1437 | spin_lock(sdev->request_queue->queue_lock); | ||
1438 | 1454 | ||
1439 | blk_complete_request(req); | 1455 | blk_complete_request(req); |
1440 | } | 1456 | } |
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 5f36788705ba..de57b8bca7be 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
@@ -334,7 +334,6 @@ store_shost_eh_deadline(struct device *dev, struct device_attribute *attr, | |||
334 | static DEVICE_ATTR(eh_deadline, S_IRUGO | S_IWUSR, show_shost_eh_deadline, store_shost_eh_deadline); | 334 | static DEVICE_ATTR(eh_deadline, S_IRUGO | S_IWUSR, show_shost_eh_deadline, store_shost_eh_deadline); |
335 | 335 | ||
336 | shost_rd_attr(unique_id, "%u\n"); | 336 | shost_rd_attr(unique_id, "%u\n"); |
337 | shost_rd_attr(host_busy, "%hu\n"); | ||
338 | shost_rd_attr(cmd_per_lun, "%hd\n"); | 337 | shost_rd_attr(cmd_per_lun, "%hd\n"); |
339 | shost_rd_attr(can_queue, "%hd\n"); | 338 | shost_rd_attr(can_queue, "%hd\n"); |
340 | shost_rd_attr(sg_tablesize, "%hu\n"); | 339 | shost_rd_attr(sg_tablesize, "%hu\n"); |
@@ -344,6 +343,14 @@ shost_rd_attr(prot_capabilities, "%u\n"); | |||
344 | shost_rd_attr(prot_guard_type, "%hd\n"); | 343 | shost_rd_attr(prot_guard_type, "%hd\n"); |
345 | shost_rd_attr2(proc_name, hostt->proc_name, "%s\n"); | 344 | shost_rd_attr2(proc_name, hostt->proc_name, "%s\n"); |
346 | 345 | ||
346 | static ssize_t | ||
347 | show_host_busy(struct device *dev, struct device_attribute *attr, char *buf) | ||
348 | { | ||
349 | struct Scsi_Host *shost = class_to_shost(dev); | ||
350 | return snprintf(buf, 20, "%d\n", atomic_read(&shost->host_busy)); | ||
351 | } | ||
352 | static DEVICE_ATTR(host_busy, S_IRUGO, show_host_busy, NULL); | ||
353 | |||
347 | static struct attribute *scsi_sysfs_shost_attrs[] = { | 354 | static struct attribute *scsi_sysfs_shost_attrs[] = { |
348 | &dev_attr_unique_id.attr, | 355 | &dev_attr_unique_id.attr, |
349 | &dev_attr_host_busy.attr, | 356 | &dev_attr_host_busy.attr, |
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index b2bc5198b7fb..51f7911b1cbb 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h | |||
@@ -582,13 +582,9 @@ struct Scsi_Host { | |||
582 | */ | 582 | */ |
583 | struct blk_queue_tag *bqt; | 583 | struct blk_queue_tag *bqt; |
584 | 584 | ||
585 | /* | 585 | atomic_t host_busy; /* commands actually active on low-level */ |
586 | * The following two fields are protected with host_lock; | 586 | unsigned int host_failed; /* commands that failed. |
587 | * however, eh routines can safely access during eh processing | 587 | protected by host_lock */ |
588 | * without acquiring the lock. | ||
589 | */ | ||
590 | unsigned int host_busy; /* commands actually active on low-level */ | ||
591 | unsigned int host_failed; /* commands that failed. */ | ||
592 | unsigned int host_eh_scheduled; /* EH scheduled without command */ | 588 | unsigned int host_eh_scheduled; /* EH scheduled without command */ |
593 | 589 | ||
594 | unsigned int host_no; /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */ | 590 | unsigned int host_no; /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */ |