From 3af74a3c07d93e34fce3d6c6dcd73676d8381d21 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 6 Feb 2014 12:23:07 -0800 Subject: [SCSI] libsas: introduce scmd_dbg() to quiet false positive "timeout" messages libsas sometimes short circuits timeouts to force commands into error recovery. It is misleading to log that the command timed-out in sas_scsi_timed_out() when in fact it was just queued for error handling. It's also redundant in the case of a true timeout as libata eh will detect and report timeouts via it's AC_ERR_TIMEOUT facility. Given that some environments consider "timeout" errors to be indicative of impending device failure demote the sas_scsi_timed_out() timeout message to be disabled by default. This parallels ata_scsi_timed_out(). [jejb: checkpatch fix] Reported-by: Xun Ni Tested-by: Nelson Cheng Acked-by: Lukasz Dorau Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- include/scsi/scsi_device.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include/scsi/scsi_device.h') diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index d65fbec2533d..622e100561d2 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -235,12 +235,24 @@ struct scsi_dh_data { #define sdev_printk(prefix, sdev, fmt, a...) \ dev_printk(prefix, &(sdev)->sdev_gendev, fmt, ##a) +#define sdev_dbg(sdev, fmt, a...) \ + dev_dbg(&(sdev)->sdev_gendev, fmt, ##a) + #define scmd_printk(prefix, scmd, fmt, a...) \ (scmd)->request->rq_disk ? \ sdev_printk(prefix, (scmd)->device, "[%s] " fmt, \ (scmd)->request->rq_disk->disk_name, ##a) : \ sdev_printk(prefix, (scmd)->device, fmt, ##a) +#define scmd_dbg(scmd, fmt, a...) \ + do { \ + if ((scmd)->request->rq_disk) \ + sdev_dbg((scmd)->device, "[%s] " fmt, \ + (scmd)->request->rq_disk->disk_name, ##a);\ + else \ + sdev_dbg((scmd)->device, fmt, ##a); \ + } while (0) + enum scsi_target_state { STARGET_CREATED = 1, STARGET_RUNNING, -- cgit v1.2.3 From e63ed0d7a98014fdfc2cfeb3f6dada313dcabb59 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Tue, 21 Jan 2014 07:00:50 -0800 Subject: [SCSI] fix our current target reap infrastructure This patch eliminates the reap_ref and replaces it with a proper kref. On last put of this kref, the target is removed from visibility in sysfs. The final call to scsi_target_reap() for the device is done from __scsi_remove_device() and only if the device was made visible. This ensures that the target disappears as soon as the last device is gone rather than waiting until final release of the device (which is often too long). Reviewed-by: Alan Stern Tested-by: Sarah Sharp Cc: stable@vger.kernel.org # delay backport by 2 months for field testing Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 99 ++++++++++++++++++++++++++++------------------ drivers/scsi/scsi_sysfs.c | 20 +++++++--- include/scsi/scsi_device.h | 3 +- 3 files changed, 75 insertions(+), 47 deletions(-) (limited to 'include/scsi/scsi_device.h') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 307a81137607..5fad646ee6e5 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -370,6 +370,31 @@ static struct scsi_target *__scsi_find_target(struct device *parent, return found_starget; } +/** + * scsi_target_reap_ref_release - remove target from visibility + * @kref: the reap_ref in the target being released + * + * Called on last put of reap_ref, which is the indication that no device + * under this target is visible anymore, so render the target invisible in + * sysfs. Note: we have to be in user context here because the target reaps + * should be done in places where the scsi device visibility is being removed. + */ +static void scsi_target_reap_ref_release(struct kref *kref) +{ + struct scsi_target *starget + = container_of(kref, struct scsi_target, reap_ref); + + transport_remove_device(&starget->dev); + device_del(&starget->dev); + starget->state = STARGET_DEL; + scsi_target_destroy(starget); +} + +static void scsi_target_reap_ref_put(struct scsi_target *starget) +{ + kref_put(&starget->reap_ref, scsi_target_reap_ref_release); +} + /** * scsi_alloc_target - allocate a new or find an existing target * @parent: parent of the target (need not be a scsi host) @@ -392,7 +417,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, + shost->transportt->target_size; struct scsi_target *starget; struct scsi_target *found_target; - int error; + int error, ref_got; starget = kzalloc(size, GFP_KERNEL); if (!starget) { @@ -401,7 +426,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, } dev = &starget->dev; device_initialize(dev); - starget->reap_ref = 1; + kref_init(&starget->reap_ref); dev->parent = get_device(parent); dev_set_name(dev, "target%d:%d:%d", shost->host_no, channel, id); dev->bus = &scsi_bus_type; @@ -441,29 +466,36 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, return starget; found: - found_target->reap_ref++; + /* + * release routine already fired if kref is zero, so if we can still + * take the reference, the target must be alive. If we can't, it must + * be dying and we need to wait for a new target + */ + ref_got = kref_get_unless_zero(&found_target->reap_ref); + spin_unlock_irqrestore(shost->host_lock, flags); - if (found_target->state != STARGET_DEL) { + if (ref_got) { put_device(dev); return found_target; } - /* Unfortunately, we found a dying target; need to - * wait until it's dead before we can get a new one */ + /* + * Unfortunately, we found a dying target; need to wait until it's + * dead before we can get a new one. There is an anomaly here. We + * *should* call scsi_target_reap() to balance the kref_get() of the + * reap_ref above. However, since the target being released, it's + * already invisible and the reap_ref is irrelevant. If we call + * scsi_target_reap() we might spuriously do another device_del() on + * an already invisible target. + */ put_device(&found_target->dev); - flush_scheduled_work(); + /* + * length of time is irrelevant here, we just want to yield the CPU + * for a tick to avoid busy waiting for the target to die. + */ + msleep(1); goto retry; } -static void scsi_target_reap_usercontext(struct work_struct *work) -{ - struct scsi_target *starget = - container_of(work, struct scsi_target, ew.work); - - transport_remove_device(&starget->dev); - device_del(&starget->dev); - scsi_target_destroy(starget); -} - /** * scsi_target_reap - check to see if target is in use and destroy if not * @starget: target to be checked @@ -474,28 +506,11 @@ static void scsi_target_reap_usercontext(struct work_struct *work) */ void scsi_target_reap(struct scsi_target *starget) { - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); - unsigned long flags; - enum scsi_target_state state; - int empty = 0; - - spin_lock_irqsave(shost->host_lock, flags); - state = starget->state; - if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { - empty = 1; - starget->state = STARGET_DEL; - } - spin_unlock_irqrestore(shost->host_lock, flags); - - if (!empty) - return; - - BUG_ON(state == STARGET_DEL); - if (state == STARGET_CREATED) + BUG_ON(starget->state == STARGET_DEL); + if (starget->state == STARGET_CREATED) scsi_target_destroy(starget); else - execute_in_process_context(scsi_target_reap_usercontext, - &starget->ew); + scsi_target_reap_ref_put(starget); } /** @@ -1532,6 +1547,10 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, } mutex_unlock(&shost->scan_mutex); scsi_autopm_put_target(starget); + /* + * paired with scsi_alloc_target(). Target will be destroyed unless + * scsi_probe_and_add_lun made an underlying device visible + */ scsi_target_reap(starget); put_device(&starget->dev); @@ -1612,8 +1631,10 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, out_reap: scsi_autopm_put_target(starget); - /* now determine if the target has any children at all - * and if not, nuke it */ + /* + * paired with scsi_alloc_target(): determine if the target has + * any children at all and if not, nuke it + */ scsi_target_reap(starget); put_device(&starget->dev); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 9117d0bf408e..665acbf83693 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -383,17 +383,14 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) { struct scsi_device *sdev; struct device *parent; - struct scsi_target *starget; struct list_head *this, *tmp; unsigned long flags; sdev = container_of(work, struct scsi_device, ew.work); parent = sdev->sdev_gendev.parent; - starget = to_scsi_target(parent); spin_lock_irqsave(sdev->host->host_lock, flags); - starget->reap_ref++; list_del(&sdev->siblings); list_del(&sdev->same_target_siblings); list_del(&sdev->starved_entry); @@ -413,8 +410,6 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) /* NULL queue means the device can't be used */ sdev->request_queue = NULL; - scsi_target_reap(scsi_target(sdev)); - kfree(sdev->inquiry); kfree(sdev); @@ -1071,6 +1066,13 @@ void __scsi_remove_device(struct scsi_device *sdev) sdev->host->hostt->slave_destroy(sdev); transport_destroy_device(dev); + /* + * Paired with the kref_get() in scsi_sysfs_initialize(). We have + * remoed sysfs visibility from the device, so make the target + * invisible if this was the last device underneath it. + */ + scsi_target_reap(scsi_target(sdev)); + put_device(dev); } @@ -1133,7 +1135,7 @@ void scsi_remove_target(struct device *dev) continue; if (starget->dev.parent == dev || &starget->dev == dev) { /* assuming new targets arrive at the end */ - starget->reap_ref++; + kref_get(&starget->reap_ref); spin_unlock_irqrestore(shost->host_lock, flags); if (last) scsi_target_reap(last); @@ -1217,6 +1219,12 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) list_add_tail(&sdev->same_target_siblings, &starget->devices); list_add_tail(&sdev->siblings, &shost->__devices); spin_unlock_irqrestore(shost->host_lock, flags); + /* + * device can now only be removed via __scsi_remove_device() so hold + * the target. Target will be held in CREATED state until something + * beneath it becomes visible (in which case it moves to RUNNING) + */ + kref_get(&starget->reap_ref); } int scsi_is_sdev_device(const struct device *dev) diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 622e100561d2..ccabdc1c27ca 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -269,7 +269,7 @@ struct scsi_target { struct list_head siblings; struct list_head devices; struct device dev; - unsigned int reap_ref; /* protected by the host lock */ + struct kref reap_ref; /* last put renders target invisible */ unsigned int channel; unsigned int id; /* target id ... replace * scsi_device.id eventually */ @@ -296,7 +296,6 @@ struct scsi_target { #define SCSI_DEFAULT_TARGET_BLOCKED 3 char scsi_level; - struct execute_work ew; enum scsi_target_state state; void *hostdata; /* available to low-level driver */ unsigned long starget_data[0]; /* for the transport */ -- cgit v1.2.3 From b3ae8780b42918111387240762f470d5c1e269d6 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Sat, 15 Mar 2014 09:51:49 +0100 Subject: [SCSI] Add EVPD page 0x83 and 0x80 to sysfs EVPD page 0x83 is used to uniquely identify the device. So instead of having each and every program issue a separate SG_IO call to retrieve this information it does make far more sense to display it in sysfs. Some older devices (most notably tapes) will only report reliable information in page 0x80 (Unit Serial Number). So export this in the sysfs attribute 'vpd_pg80'. [jejb: checkpatch fix] [hare: attach after transport configure] [fengguang.wu@intel.com: spotted problems with the original now fixed] Signed-off-by: Hannes Reinecke Signed-off-by: James Bottomley --- drivers/scsi/scsi.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/scsi_scan.c | 3 ++ drivers/scsi/scsi_sysfs.c | 34 +++++++++++++++++- include/scsi/scsi_device.h | 7 ++++ 4 files changed, 130 insertions(+), 1 deletion(-) (limited to 'include/scsi/scsi_device.h') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index b2526ad7b9a1..1d98ac960887 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -1029,6 +1029,93 @@ int scsi_get_vpd_page(struct scsi_device *sdev, u8 page, unsigned char *buf, } EXPORT_SYMBOL_GPL(scsi_get_vpd_page); +/** + * scsi_attach_vpd - Attach Vital Product Data to a SCSI device structure + * @sdev: The device to ask + * + * Attach the 'Device Identification' VPD page (0x83) and the + * 'Unit Serial Number' VPD page (0x80) to a SCSI device + * structure. This information can be used to identify the device + * uniquely. + */ +void scsi_attach_vpd(struct scsi_device *sdev) +{ + int result, i; + int vpd_len = SCSI_VPD_PG_LEN; + int pg80_supported = 0; + int pg83_supported = 0; + unsigned char *vpd_buf; + + if (sdev->skip_vpd_pages) + return; +retry_pg0: + vpd_buf = kmalloc(vpd_len, GFP_KERNEL); + if (!vpd_buf) + return; + + /* Ask for all the pages supported by this device */ + result = scsi_vpd_inquiry(sdev, vpd_buf, 0, vpd_len); + if (result < 0) { + kfree(vpd_buf); + return; + } + if (result > vpd_len) { + vpd_len = result; + kfree(vpd_buf); + goto retry_pg0; + } + + for (i = 4; i < result; i++) { + if (vpd_buf[i] == 0x80) + pg80_supported = 1; + if (vpd_buf[i] == 0x83) + pg83_supported = 1; + } + kfree(vpd_buf); + vpd_len = SCSI_VPD_PG_LEN; + + if (pg80_supported) { +retry_pg80: + vpd_buf = kmalloc(vpd_len, GFP_KERNEL); + if (!vpd_buf) + return; + + result = scsi_vpd_inquiry(sdev, vpd_buf, 0x80, vpd_len); + if (result < 0) { + kfree(vpd_buf); + return; + } + if (result > vpd_len) { + vpd_len = result; + kfree(vpd_buf); + goto retry_pg80; + } + sdev->vpd_pg80_len = result; + sdev->vpd_pg80 = vpd_buf; + vpd_len = SCSI_VPD_PG_LEN; + } + + if (pg83_supported) { +retry_pg83: + vpd_buf = kmalloc(vpd_len, GFP_KERNEL); + if (!vpd_buf) + return; + + result = scsi_vpd_inquiry(sdev, vpd_buf, 0x83, vpd_len); + if (result < 0) { + kfree(vpd_buf); + return; + } + if (result > vpd_len) { + vpd_len = result; + kfree(vpd_buf); + goto retry_pg83; + } + sdev->vpd_pg83_len = result; + sdev->vpd_pg83 = vpd_buf; + } +} + /** * scsi_report_opcode - Find out if a given command opcode is supported * @sdev: scsi device to query diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 4109530e92a0..27f96d5b7680 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -970,6 +970,9 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, } } + if (sdev->scsi_level >= SCSI_3) + scsi_attach_vpd(sdev); + sdev->max_queue_depth = sdev->queue_depth; /* diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 85098222a9e8..1392474c3499 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -412,6 +412,8 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) /* NULL queue means the device can't be used */ sdev->request_queue = NULL; + kfree(sdev->vpd_pg83); + kfree(sdev->vpd_pg80); kfree(sdev->inquiry); kfree(sdev); @@ -751,8 +753,32 @@ store_queue_type_field(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(queue_type, S_IRUGO | S_IWUSR, show_queue_type_field, store_queue_type_field); +#define sdev_vpd_pg_attr(_page) \ +static ssize_t \ +show_vpd_##_page(struct file *filp, struct kobject *kobj, \ + struct bin_attribute *bin_attr, \ + char *buf, loff_t off, size_t count) \ +{ \ + struct device *dev = container_of(kobj, struct device, kobj); \ + struct scsi_device *sdev = to_scsi_device(dev); \ + if (!sdev->vpd_##_page) \ + return -EINVAL; \ + return memory_read_from_buffer(buf, count, &off, \ + sdev->vpd_##_page, \ + sdev->vpd_##_page##_len); \ +} \ +static struct bin_attribute dev_attr_vpd_##_page = { \ + .attr = {.name = __stringify(vpd_##_page), .mode = S_IRUGO }, \ + .size = 0, \ + .read = show_vpd_##_page, \ +}; + +sdev_vpd_pg_attr(pg83); +sdev_vpd_pg_attr(pg80); + static ssize_t -show_iostat_counterbits(struct device *dev, struct device_attribute *attr, char *buf) +show_iostat_counterbits(struct device *dev, struct device_attribute *attr, + char *buf) { return snprintf(buf, 20, "%d\n", (int)sizeof(atomic_t) * 8); } @@ -936,8 +962,14 @@ static struct attribute *scsi_sdev_attrs[] = { NULL }; +static struct bin_attribute *scsi_sdev_bin_attrs[] = { + &dev_attr_vpd_pg83, + &dev_attr_vpd_pg80, + NULL +}; static struct attribute_group scsi_sdev_attr_group = { .attrs = scsi_sdev_attrs, + .bin_attrs = scsi_sdev_bin_attrs, .is_visible = scsi_sdev_attr_is_visible, }; diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index ccabdc1c27ca..4e845b80efd3 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -113,6 +113,12 @@ struct scsi_device { const char * vendor; /* [back_compat] point into 'inquiry' ... */ const char * model; /* ... after scan; point to static string */ const char * rev; /* ... "nullnullnullnull" before scan */ + +#define SCSI_VPD_PG_LEN 255 + int vpd_pg83_len; + unsigned char *vpd_pg83; + int vpd_pg80_len; + unsigned char *vpd_pg80; unsigned char current_tag; /* current tag */ struct scsi_target *sdev_target; /* used only for single_lun */ @@ -320,6 +326,7 @@ extern int scsi_add_device(struct Scsi_Host *host, uint channel, extern int scsi_register_device_handler(struct scsi_device_handler *scsi_dh); extern void scsi_remove_device(struct scsi_device *); extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh); +void scsi_attach_vpd(struct scsi_device *sdev); extern int scsi_device_get(struct scsi_device *); extern void scsi_device_put(struct scsi_device *); -- cgit v1.2.3