diff options
Diffstat (limited to 'drivers/scsi/scsi.c')
-rw-r--r-- | drivers/scsi/scsi.c | 138 |
1 files changed, 59 insertions, 79 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 1bf274e3b2b6..a7e4fba724b7 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -412,105 +412,85 @@ 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 + * scsi_get_vpd_buf - Get Vital Product Data from a SCSI device * @sdev: The device to ask + * @page: Which Vital Product Data to return * - * 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. + * Returns %NULL upon failure. */ -void scsi_attach_vpd(struct scsi_device *sdev) +static struct scsi_vpd *scsi_get_vpd_buf(struct scsi_device *sdev, u8 page) { - int result, i; - int vpd_len = SCSI_VPD_PG_LEN; - int pg80_supported = 0; - int pg83_supported = 0; - unsigned char __rcu *vpd_buf, *orig_vpd_buf = NULL; - - if (!scsi_device_supports_vpd(sdev)) - return; + struct scsi_vpd *vpd_buf; + int vpd_len = SCSI_VPD_PG_LEN, result; -retry_pg0: - vpd_buf = kmalloc(vpd_len, GFP_KERNEL); +retry_pg: + vpd_buf = kmalloc(sizeof(*vpd_buf) + vpd_len, GFP_KERNEL); if (!vpd_buf) - return; + return NULL; - /* Ask for all the pages supported by this device */ - result = scsi_vpd_inquiry(sdev, vpd_buf, 0, vpd_len); + result = scsi_vpd_inquiry(sdev, vpd_buf->data, page, vpd_len); if (result < 0) { kfree(vpd_buf); - return; + return NULL; } if (result > vpd_len) { vpd_len = result; kfree(vpd_buf); - goto retry_pg0; + goto retry_pg; } - 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; + vpd_buf->len = result; - if (pg80_supported) { -retry_pg80: - vpd_buf = kmalloc(vpd_len, GFP_KERNEL); - if (!vpd_buf) - return; + return vpd_buf; +} - 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; - } - mutex_lock(&sdev->inquiry_mutex); - orig_vpd_buf = sdev->vpd_pg80; - sdev->vpd_pg80_len = result; - rcu_assign_pointer(sdev->vpd_pg80, vpd_buf); - mutex_unlock(&sdev->inquiry_mutex); - synchronize_rcu(); - if (orig_vpd_buf) { - kfree(orig_vpd_buf); - orig_vpd_buf = NULL; - } - vpd_len = SCSI_VPD_PG_LEN; - } +static void scsi_update_vpd_page(struct scsi_device *sdev, u8 page, + struct scsi_vpd __rcu **sdev_vpd_buf) +{ + struct scsi_vpd *vpd_buf; - if (pg83_supported) { -retry_pg83: - vpd_buf = kmalloc(vpd_len, GFP_KERNEL); - if (!vpd_buf) - return; + vpd_buf = scsi_get_vpd_buf(sdev, page); + 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; - } - mutex_lock(&sdev->inquiry_mutex); - orig_vpd_buf = sdev->vpd_pg83; - sdev->vpd_pg83_len = result; - rcu_assign_pointer(sdev->vpd_pg83, vpd_buf); - mutex_unlock(&sdev->inquiry_mutex); - synchronize_rcu(); - if (orig_vpd_buf) - kfree(orig_vpd_buf); + mutex_lock(&sdev->inquiry_mutex); + rcu_swap_protected(*sdev_vpd_buf, vpd_buf, + lockdep_is_held(&sdev->inquiry_mutex)); + mutex_unlock(&sdev->inquiry_mutex); + + if (vpd_buf) + kfree_rcu(vpd_buf, rcu); +} + +/** + * 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 i; + struct scsi_vpd *vpd_buf; + + if (!scsi_device_supports_vpd(sdev)) + return; + + /* Ask for all the pages supported by this device */ + vpd_buf = scsi_get_vpd_buf(sdev, 0); + if (!vpd_buf) + return; + + for (i = 4; i < vpd_buf->len; i++) { + if (vpd_buf->data[i] == 0x80) + scsi_update_vpd_page(sdev, 0x80, &sdev->vpd_pg80); + if (vpd_buf->data[i] == 0x83) + scsi_update_vpd_page(sdev, 0x83, &sdev->vpd_pg83); } + kfree(vpd_buf); } /** |