diff options
-rw-r--r-- | drivers/scsi/scsi.c | 87 | ||||
-rw-r--r-- | drivers/scsi/scsi_scan.c | 3 | ||||
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 34 | ||||
-rw-r--r-- | include/scsi/scsi_device.h | 7 |
4 files changed, 130 insertions, 1 deletions
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 | |||
@@ -1030,6 +1030,93 @@ int scsi_get_vpd_page(struct scsi_device *sdev, u8 page, unsigned char *buf, | |||
1030 | EXPORT_SYMBOL_GPL(scsi_get_vpd_page); | 1030 | EXPORT_SYMBOL_GPL(scsi_get_vpd_page); |
1031 | 1031 | ||
1032 | /** | 1032 | /** |
1033 | * scsi_attach_vpd - Attach Vital Product Data to a SCSI device structure | ||
1034 | * @sdev: The device to ask | ||
1035 | * | ||
1036 | * Attach the 'Device Identification' VPD page (0x83) and the | ||
1037 | * 'Unit Serial Number' VPD page (0x80) to a SCSI device | ||
1038 | * structure. This information can be used to identify the device | ||
1039 | * uniquely. | ||
1040 | */ | ||
1041 | void scsi_attach_vpd(struct scsi_device *sdev) | ||
1042 | { | ||
1043 | int result, i; | ||
1044 | int vpd_len = SCSI_VPD_PG_LEN; | ||
1045 | int pg80_supported = 0; | ||
1046 | int pg83_supported = 0; | ||
1047 | unsigned char *vpd_buf; | ||
1048 | |||
1049 | if (sdev->skip_vpd_pages) | ||
1050 | return; | ||
1051 | retry_pg0: | ||
1052 | vpd_buf = kmalloc(vpd_len, GFP_KERNEL); | ||
1053 | if (!vpd_buf) | ||
1054 | return; | ||
1055 | |||
1056 | /* Ask for all the pages supported by this device */ | ||
1057 | result = scsi_vpd_inquiry(sdev, vpd_buf, 0, vpd_len); | ||
1058 | if (result < 0) { | ||
1059 | kfree(vpd_buf); | ||
1060 | return; | ||
1061 | } | ||
1062 | if (result > vpd_len) { | ||
1063 | vpd_len = result; | ||
1064 | kfree(vpd_buf); | ||
1065 | goto retry_pg0; | ||
1066 | } | ||
1067 | |||
1068 | for (i = 4; i < result; i++) { | ||
1069 | if (vpd_buf[i] == 0x80) | ||
1070 | pg80_supported = 1; | ||
1071 | if (vpd_buf[i] == 0x83) | ||
1072 | pg83_supported = 1; | ||
1073 | } | ||
1074 | kfree(vpd_buf); | ||
1075 | vpd_len = SCSI_VPD_PG_LEN; | ||
1076 | |||
1077 | if (pg80_supported) { | ||
1078 | retry_pg80: | ||
1079 | vpd_buf = kmalloc(vpd_len, GFP_KERNEL); | ||
1080 | if (!vpd_buf) | ||
1081 | return; | ||
1082 | |||
1083 | result = scsi_vpd_inquiry(sdev, vpd_buf, 0x80, vpd_len); | ||
1084 | if (result < 0) { | ||
1085 | kfree(vpd_buf); | ||
1086 | return; | ||
1087 | } | ||
1088 | if (result > vpd_len) { | ||
1089 | vpd_len = result; | ||
1090 | kfree(vpd_buf); | ||
1091 | goto retry_pg80; | ||
1092 | } | ||
1093 | sdev->vpd_pg80_len = result; | ||
1094 | sdev->vpd_pg80 = vpd_buf; | ||
1095 | vpd_len = SCSI_VPD_PG_LEN; | ||
1096 | } | ||
1097 | |||
1098 | if (pg83_supported) { | ||
1099 | retry_pg83: | ||
1100 | vpd_buf = kmalloc(vpd_len, GFP_KERNEL); | ||
1101 | if (!vpd_buf) | ||
1102 | return; | ||
1103 | |||
1104 | result = scsi_vpd_inquiry(sdev, vpd_buf, 0x83, vpd_len); | ||
1105 | if (result < 0) { | ||
1106 | kfree(vpd_buf); | ||
1107 | return; | ||
1108 | } | ||
1109 | if (result > vpd_len) { | ||
1110 | vpd_len = result; | ||
1111 | kfree(vpd_buf); | ||
1112 | goto retry_pg83; | ||
1113 | } | ||
1114 | sdev->vpd_pg83_len = result; | ||
1115 | sdev->vpd_pg83 = vpd_buf; | ||
1116 | } | ||
1117 | } | ||
1118 | |||
1119 | /** | ||
1033 | * scsi_report_opcode - Find out if a given command opcode is supported | 1120 | * scsi_report_opcode - Find out if a given command opcode is supported |
1034 | * @sdev: scsi device to query | 1121 | * @sdev: scsi device to query |
1035 | * @buffer: scratch buffer (must be at least 20 bytes long) | 1122 | * @buffer: scratch buffer (must be at least 20 bytes long) |
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, | |||
970 | } | 970 | } |
971 | } | 971 | } |
972 | 972 | ||
973 | if (sdev->scsi_level >= SCSI_3) | ||
974 | scsi_attach_vpd(sdev); | ||
975 | |||
973 | sdev->max_queue_depth = sdev->queue_depth; | 976 | sdev->max_queue_depth = sdev->queue_depth; |
974 | 977 | ||
975 | /* | 978 | /* |
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) | |||
412 | /* NULL queue means the device can't be used */ | 412 | /* NULL queue means the device can't be used */ |
413 | sdev->request_queue = NULL; | 413 | sdev->request_queue = NULL; |
414 | 414 | ||
415 | kfree(sdev->vpd_pg83); | ||
416 | kfree(sdev->vpd_pg80); | ||
415 | kfree(sdev->inquiry); | 417 | kfree(sdev->inquiry); |
416 | kfree(sdev); | 418 | kfree(sdev); |
417 | 419 | ||
@@ -751,8 +753,32 @@ store_queue_type_field(struct device *dev, struct device_attribute *attr, | |||
751 | static DEVICE_ATTR(queue_type, S_IRUGO | S_IWUSR, show_queue_type_field, | 753 | static DEVICE_ATTR(queue_type, S_IRUGO | S_IWUSR, show_queue_type_field, |
752 | store_queue_type_field); | 754 | store_queue_type_field); |
753 | 755 | ||
756 | #define sdev_vpd_pg_attr(_page) \ | ||
757 | static ssize_t \ | ||
758 | show_vpd_##_page(struct file *filp, struct kobject *kobj, \ | ||
759 | struct bin_attribute *bin_attr, \ | ||
760 | char *buf, loff_t off, size_t count) \ | ||
761 | { \ | ||
762 | struct device *dev = container_of(kobj, struct device, kobj); \ | ||
763 | struct scsi_device *sdev = to_scsi_device(dev); \ | ||
764 | if (!sdev->vpd_##_page) \ | ||
765 | return -EINVAL; \ | ||
766 | return memory_read_from_buffer(buf, count, &off, \ | ||
767 | sdev->vpd_##_page, \ | ||
768 | sdev->vpd_##_page##_len); \ | ||
769 | } \ | ||
770 | static struct bin_attribute dev_attr_vpd_##_page = { \ | ||
771 | .attr = {.name = __stringify(vpd_##_page), .mode = S_IRUGO }, \ | ||
772 | .size = 0, \ | ||
773 | .read = show_vpd_##_page, \ | ||
774 | }; | ||
775 | |||
776 | sdev_vpd_pg_attr(pg83); | ||
777 | sdev_vpd_pg_attr(pg80); | ||
778 | |||
754 | static ssize_t | 779 | static ssize_t |
755 | show_iostat_counterbits(struct device *dev, struct device_attribute *attr, char *buf) | 780 | show_iostat_counterbits(struct device *dev, struct device_attribute *attr, |
781 | char *buf) | ||
756 | { | 782 | { |
757 | return snprintf(buf, 20, "%d\n", (int)sizeof(atomic_t) * 8); | 783 | return snprintf(buf, 20, "%d\n", (int)sizeof(atomic_t) * 8); |
758 | } | 784 | } |
@@ -936,8 +962,14 @@ static struct attribute *scsi_sdev_attrs[] = { | |||
936 | NULL | 962 | NULL |
937 | }; | 963 | }; |
938 | 964 | ||
965 | static struct bin_attribute *scsi_sdev_bin_attrs[] = { | ||
966 | &dev_attr_vpd_pg83, | ||
967 | &dev_attr_vpd_pg80, | ||
968 | NULL | ||
969 | }; | ||
939 | static struct attribute_group scsi_sdev_attr_group = { | 970 | static struct attribute_group scsi_sdev_attr_group = { |
940 | .attrs = scsi_sdev_attrs, | 971 | .attrs = scsi_sdev_attrs, |
972 | .bin_attrs = scsi_sdev_bin_attrs, | ||
941 | .is_visible = scsi_sdev_attr_is_visible, | 973 | .is_visible = scsi_sdev_attr_is_visible, |
942 | }; | 974 | }; |
943 | 975 | ||
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 { | |||
113 | const char * vendor; /* [back_compat] point into 'inquiry' ... */ | 113 | const char * vendor; /* [back_compat] point into 'inquiry' ... */ |
114 | const char * model; /* ... after scan; point to static string */ | 114 | const char * model; /* ... after scan; point to static string */ |
115 | const char * rev; /* ... "nullnullnullnull" before scan */ | 115 | const char * rev; /* ... "nullnullnullnull" before scan */ |
116 | |||
117 | #define SCSI_VPD_PG_LEN 255 | ||
118 | int vpd_pg83_len; | ||
119 | unsigned char *vpd_pg83; | ||
120 | int vpd_pg80_len; | ||
121 | unsigned char *vpd_pg80; | ||
116 | unsigned char current_tag; /* current tag */ | 122 | unsigned char current_tag; /* current tag */ |
117 | struct scsi_target *sdev_target; /* used only for single_lun */ | 123 | struct scsi_target *sdev_target; /* used only for single_lun */ |
118 | 124 | ||
@@ -320,6 +326,7 @@ extern int scsi_add_device(struct Scsi_Host *host, uint channel, | |||
320 | extern int scsi_register_device_handler(struct scsi_device_handler *scsi_dh); | 326 | extern int scsi_register_device_handler(struct scsi_device_handler *scsi_dh); |
321 | extern void scsi_remove_device(struct scsi_device *); | 327 | extern void scsi_remove_device(struct scsi_device *); |
322 | extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh); | 328 | extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh); |
329 | void scsi_attach_vpd(struct scsi_device *sdev); | ||
323 | 330 | ||
324 | extern int scsi_device_get(struct scsi_device *); | 331 | extern int scsi_device_get(struct scsi_device *); |
325 | extern void scsi_device_put(struct scsi_device *); | 332 | extern void scsi_device_put(struct scsi_device *); |