diff options
Diffstat (limited to 'drivers/scsi/scsi.c')
-rw-r--r-- | drivers/scsi/scsi.c | 144 |
1 files changed, 66 insertions, 78 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 3d38c6d463b8..f3f4926a3e77 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -412,6 +412,63 @@ int scsi_get_vpd_page(struct scsi_device *sdev, u8 page, unsigned char *buf, EXPORT_SYMBOL_GPL(scsi_get_vpd_page); /** + * scsi_get_vpd_buf - Get Vital Product Data from a SCSI device + * @sdev: The device to ask + * @page: Which Vital Product Data to return + * @len: Upon success, the VPD length will be stored in *@len. + * + * Returns %NULL upon failure. + */ +static unsigned char *scsi_get_vpd_buf(struct scsi_device *sdev, u8 page, + int *len) +{ + unsigned char *vpd_buf; + int vpd_len = SCSI_VPD_PG_LEN, result; + +retry_pg: + vpd_buf = kmalloc(vpd_len, GFP_KERNEL); + if (!vpd_buf) + return NULL; + + result = scsi_vpd_inquiry(sdev, vpd_buf, page, vpd_len); + if (result < 0) { + kfree(vpd_buf); + return NULL; + } + if (result > vpd_len) { + vpd_len = result; + kfree(vpd_buf); + goto retry_pg; + } + + *len = result; + + return vpd_buf; +} + +static void scsi_update_vpd_page(struct scsi_device *sdev, u8 page, + unsigned char __rcu **sdev_vpd_buf, + int *sdev_vpd_len) +{ + unsigned char *vpd_buf; + int len; + + vpd_buf = scsi_get_vpd_buf(sdev, page, &len); + if (!vpd_buf) + return; + + mutex_lock(&sdev->inquiry_mutex); + rcu_swap_protected(*sdev_vpd_buf, vpd_buf, + lockdep_is_held(&sdev->inquiry_mutex)); + *sdev_vpd_len = len; + mutex_unlock(&sdev->inquiry_mutex); + + synchronize_rcu(); + + kfree(vpd_buf); +} + +/** * scsi_attach_vpd - Attach Vital Product Data to a SCSI device structure * @sdev: The device to ask * @@ -422,95 +479,26 @@ EXPORT_SYMBOL_GPL(scsi_get_vpd_page); */ 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 __rcu *vpd_buf, *orig_vpd_buf = NULL; + int i, vpd_len; + unsigned char *vpd_buf; if (!scsi_device_supports_vpd(sdev)) 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); + vpd_buf = scsi_get_vpd_buf(sdev, 0, &vpd_len); + if (!vpd_buf) return; - } - if (result > vpd_len) { - vpd_len = result; - kfree(vpd_buf); - goto retry_pg0; - } - for (i = 4; i < result; i++) { + for (i = 4; i < vpd_len; i++) { if (vpd_buf[i] == 0x80) - pg80_supported = 1; + scsi_update_vpd_page(sdev, 0x80, &sdev->vpd_pg80, + &sdev->vpd_pg80_len); if (vpd_buf[i] == 0x83) - pg83_supported = 1; + scsi_update_vpd_page(sdev, 0x83, &sdev->vpd_pg83, + &sdev->vpd_pg83_len); } 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; - } - 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; - } - - 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; - } - 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); - } } /** |