Add a flag 'vpd_invalid' to the SCSI device to indicate that the VPD data needs to be refreshed. This is required if either a manual rescan is triggered or if the sense code INQUIRY DATA HAS CHANGED has been received. Signed-off-by: Hannes Reinecke <hare@xxxxxxx> --- drivers/scsi/scsi.c | 91 ++++++++++++++++++++++++++++++++++------------ drivers/scsi/scsi_error.c | 1 + drivers/scsi/scsi_scan.c | 7 +++- drivers/scsi/scsi_sysfs.c | 6 ++- drivers/scsi/ses.c | 2 +- include/scsi/scsi_device.h | 2 + 6 files changed, 82 insertions(+), 27 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 2669cb8..971b099 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -1056,10 +1056,11 @@ void scsi_attach_vpd(struct scsi_device *sdev) int vpd_len = 255; int pg80_supported = 0; int pg83_supported = 0; - unsigned char *vpd_buf; + unsigned char *vpd_buf, *tmp_pg; if (sdev->skip_vpd_pages) return; + retry_pg0: vpd_buf = kmalloc(vpd_len, GFP_KERNEL); if (!vpd_buf) @@ -1087,45 +1088,89 @@ retry_pg0: } kfree(vpd_buf); - if (pg80_supported) { retry_pg80: + if (pg80_supported) { vpd_buf = kmalloc(vpd_len, GFP_KERNEL); if (!vpd_buf) - return; - - result = scsi_vpd_inquiry(sdev, vpd_buf, 0x80, vpd_len); + result = -ENOMEM; + else + result = scsi_vpd_inquiry(sdev, vpd_buf, + 0x80, vpd_len); if (result < 0) { kfree(vpd_buf); + spin_lock(&sdev->reconfig_lock); + tmp_pg = sdev->vpd_pg80; + sdev->vpd_pg80 = NULL; + sdev->vpd_pg80_len = result; + kfree(tmp_pg); + spin_unlock(&sdev->reconfig_lock); + /* + * An unexpected error occurred, + * do not clear vpd_invalid flag + */ return; + } else { + if (result > vpd_len) { + vpd_len = result; + kfree(vpd_buf); + goto retry_pg80; + } + spin_lock(&sdev->reconfig_lock); + sdev->vpd_pg80 = vpd_buf; + sdev->vpd_pg80_len = result; + spin_unlock(&sdev->reconfig_lock); } - if (result > vpd_len) { - vpd_len = result; - kfree(vpd_buf); - goto retry_pg80; - } - sdev->vpd_pg80_len = result; - sdev->vpd_pg80 = vpd_buf; + } else { + spin_lock(&sdev->reconfig_lock); + tmp_pg = sdev->vpd_pg80; + sdev->vpd_pg80 = NULL; + sdev->vpd_pg80_len = -ENOENT; + kfree(tmp_pg); + spin_unlock(&sdev->reconfig_lock); } - if (pg83_supported) { retry_pg83: + if (pg83_supported) { vpd_buf = kmalloc(vpd_len, GFP_KERNEL); if (!vpd_buf) - return; - - result = scsi_vpd_inquiry(sdev, vpd_buf, 0x83, vpd_len); + result = -ENOMEM; + else + result = scsi_vpd_inquiry(sdev, vpd_buf, + 0x83, vpd_len); if (result < 0) { kfree(vpd_buf); + spin_lock(&sdev->reconfig_lock); + tmp_pg = sdev->vpd_pg83; + sdev->vpd_pg83 = NULL; + sdev->vpd_pg83_len = result; + kfree(tmp_pg); + spin_unlock(&sdev->reconfig_lock); + /* + * An unexpected error occurred, + * do not clear vpd_invalid flag + */ return; + } else { + if (result > vpd_len) { + vpd_len = result; + kfree(vpd_buf); + goto retry_pg83; + } + spin_lock(&sdev->reconfig_lock); + sdev->vpd_pg83 = vpd_buf; + sdev->vpd_pg83_len = result; + spin_unlock(&sdev->reconfig_lock); } - if (result > vpd_len) { - vpd_len = result; - kfree(vpd_buf); - goto retry_pg83; - } - sdev->vpd_pg83_len = result; - sdev->vpd_pg83 = vpd_buf; + } else { + spin_lock(&sdev->reconfig_lock); + tmp_pg = sdev->vpd_pg83; + sdev->vpd_pg83 = NULL; + sdev->vpd_pg83_len = -ENOENT; + kfree(tmp_pg); + spin_unlock(&sdev->reconfig_lock); } + + sdev->vpd_invalid = 0; } /** diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 78b004d..b1468c7 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -393,6 +393,7 @@ static void scsi_report_sense(struct scsi_device *sdev, if (sshdr->sense_key == UNIT_ATTENTION) { if (sshdr->asc == 0x3f && sshdr->ascq == 0x03) { + sdev->vpd_invalid = 1; evt_type = SDEV_EVT_INQUIRY_CHANGE_REPORTED; sdev_printk(KERN_WARNING, sdev, "Inquiry data has changed"); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 154bb5e..d177929 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -252,6 +252,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, INIT_LIST_HEAD(&sdev->starved_entry); INIT_LIST_HEAD(&sdev->event_list); spin_lock_init(&sdev->list_lock); + spin_lock_init(&sdev->reconfig_lock); INIT_WORK(&sdev->event_work, scsi_evt_thread); INIT_WORK(&sdev->requeue_work, scsi_requeue_run_queue); @@ -1558,7 +1559,11 @@ EXPORT_SYMBOL(scsi_add_device); void scsi_rescan_device(struct device *dev) { struct scsi_driver *drv; - + struct scsi_device *sdev = to_scsi_device(dev); + + sdev->vpd_invalid = 1; + scsi_attach_vpd(sdev); + if (!dev->driver) return; diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 8c916d0..315d1d3 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -764,8 +764,10 @@ show_vpd_##_page(struct file *filp, struct kobject *kobj, \ { \ struct device *dev = container_of(kobj, struct device, kobj); \ struct scsi_device *sdev = to_scsi_device(dev); \ - if (!sdev->vpd_##_page) \ - return -EINVAL; \ + if (sdev->vpd_invalid) \ + scsi_attach_vpd(sdev); \ + if (sdev->vpd_##_page##_len < 0) \ + return sdev->vpd_##_page##_len; \ return memory_read_from_buffer(buf, count, &off, \ sdev->vpd_##_page, \ sdev->vpd_##_page##_len); \ diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 80bfece..773766a 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -456,7 +456,7 @@ static void ses_match_to_enclosure(struct enclosure_device *edev, ses_enclosure_data_process(edev, to_scsi_device(edev->edev.parent), 0); - if (!sdev->vpd_pg83_len) + if (sdev->vpd_pg83_len < 4) return; desc = sdev->vpd_pg83 + 4; diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 2ea8212..9fa5771 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -117,6 +117,7 @@ struct scsi_device { unsigned char *vpd_pg83; unsigned char vpd_pg80_len; unsigned char *vpd_pg80; + spinlock_t reconfig_lock; unsigned char current_tag; /* current tag */ struct scsi_target *sdev_target; /* used only for single_lun */ @@ -171,6 +172,7 @@ struct scsi_device { unsigned is_visible:1; /* is the device visible in sysfs */ unsigned wce_default_on:1; /* Cache is ON by default */ unsigned no_dif:1; /* T10 PI (DIF) should be disabled */ + unsigned vpd_invalid:1; /* VPD data needs to be refreshed */ atomic_t disk_events_disable_depth; /* disable depth for disk events */ -- 1.7.12.4 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html