Commit 9983bed3907c379d1d30b7509bb0a871ed655f9d
Committed by
Martin K. Petersen
1 parent
e2d817db32
scsi: Add scsi_vpd_lun_id()
Add a function scsi_vpd_lun_id() to return a unique device identifcation based on the designation descriptors of VPD page 0x83. As devices might implement several descriptors the order of preference is: - NAA IEE Registered Extended - EUI-64 based 16-byte - EUI-64 based 12-byte - NAA IEEE Registered - NAA IEEE Extended A SCSI name string descriptor is preferred to all of them if the identification is longer than 16 bytes. The returned unique device identification will be formatted as a SCSI Name string to avoid clashes between different designator types. [mkp: Fixed up kernel doc comment from Johannes] Signed-off-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Ewan Milne <emilne@redhat.com> Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Showing 2 changed files with 141 additions and 0 deletions Side-by-side Diff
drivers/scsi/scsi_lib.c
... | ... | @@ -3154,4 +3154,144 @@ |
3154 | 3154 | atomic_dec(&sdev->disk_events_disable_depth); |
3155 | 3155 | } |
3156 | 3156 | EXPORT_SYMBOL(sdev_enable_disk_events); |
3157 | + | |
3158 | +/** | |
3159 | + * scsi_vpd_lun_id - return a unique device identification | |
3160 | + * @sdev: SCSI device | |
3161 | + * @id: buffer for the identification | |
3162 | + * @id_len: length of the buffer | |
3163 | + * | |
3164 | + * Copies a unique device identification into @id based | |
3165 | + * on the information in the VPD page 0x83 of the device. | |
3166 | + * The string will be formatted as a SCSI name string. | |
3167 | + * | |
3168 | + * Returns the length of the identification or error on failure. | |
3169 | + * If the identifier is longer than the supplied buffer the actual | |
3170 | + * identifier length is returned and the buffer is not zero-padded. | |
3171 | + */ | |
3172 | +int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len) | |
3173 | +{ | |
3174 | + u8 cur_id_type = 0xff; | |
3175 | + u8 cur_id_size = 0; | |
3176 | + unsigned char *d, *cur_id_str; | |
3177 | + unsigned char __rcu *vpd_pg83; | |
3178 | + int id_size = -EINVAL; | |
3179 | + | |
3180 | + rcu_read_lock(); | |
3181 | + vpd_pg83 = rcu_dereference(sdev->vpd_pg83); | |
3182 | + if (!vpd_pg83) { | |
3183 | + rcu_read_unlock(); | |
3184 | + return -ENXIO; | |
3185 | + } | |
3186 | + | |
3187 | + /* | |
3188 | + * Look for the correct descriptor. | |
3189 | + * Order of preference for lun descriptor: | |
3190 | + * - SCSI name string | |
3191 | + * - NAA IEEE Registered Extended | |
3192 | + * - EUI-64 based 16-byte | |
3193 | + * - EUI-64 based 12-byte | |
3194 | + * - NAA IEEE Registered | |
3195 | + * - NAA IEEE Extended | |
3196 | + * as longer descriptors reduce the likelyhood | |
3197 | + * of identification clashes. | |
3198 | + */ | |
3199 | + | |
3200 | + /* The id string must be at least 20 bytes + terminating NULL byte */ | |
3201 | + if (id_len < 21) { | |
3202 | + rcu_read_unlock(); | |
3203 | + return -EINVAL; | |
3204 | + } | |
3205 | + | |
3206 | + memset(id, 0, id_len); | |
3207 | + d = vpd_pg83 + 4; | |
3208 | + while (d < vpd_pg83 + sdev->vpd_pg83_len) { | |
3209 | + /* Skip designators not referring to the LUN */ | |
3210 | + if ((d[1] & 0x30) != 0x00) | |
3211 | + goto next_desig; | |
3212 | + | |
3213 | + switch (d[1] & 0xf) { | |
3214 | + case 0x2: | |
3215 | + /* EUI-64 */ | |
3216 | + if (cur_id_size > d[3]) | |
3217 | + break; | |
3218 | + /* Prefer NAA IEEE Registered Extended */ | |
3219 | + if (cur_id_type == 0x3 && | |
3220 | + cur_id_size == d[3]) | |
3221 | + break; | |
3222 | + cur_id_size = d[3]; | |
3223 | + cur_id_str = d + 4; | |
3224 | + cur_id_type = d[1] & 0xf; | |
3225 | + switch (cur_id_size) { | |
3226 | + case 8: | |
3227 | + id_size = snprintf(id, id_len, | |
3228 | + "eui.%8phN", | |
3229 | + cur_id_str); | |
3230 | + break; | |
3231 | + case 12: | |
3232 | + id_size = snprintf(id, id_len, | |
3233 | + "eui.%12phN", | |
3234 | + cur_id_str); | |
3235 | + break; | |
3236 | + case 16: | |
3237 | + id_size = snprintf(id, id_len, | |
3238 | + "eui.%16phN", | |
3239 | + cur_id_str); | |
3240 | + break; | |
3241 | + default: | |
3242 | + cur_id_size = 0; | |
3243 | + break; | |
3244 | + } | |
3245 | + break; | |
3246 | + case 0x3: | |
3247 | + /* NAA */ | |
3248 | + if (cur_id_size > d[3]) | |
3249 | + break; | |
3250 | + cur_id_size = d[3]; | |
3251 | + cur_id_str = d + 4; | |
3252 | + cur_id_type = d[1] & 0xf; | |
3253 | + switch (cur_id_size) { | |
3254 | + case 8: | |
3255 | + id_size = snprintf(id, id_len, | |
3256 | + "naa.%8phN", | |
3257 | + cur_id_str); | |
3258 | + break; | |
3259 | + case 16: | |
3260 | + id_size = snprintf(id, id_len, | |
3261 | + "naa.%16phN", | |
3262 | + cur_id_str); | |
3263 | + break; | |
3264 | + default: | |
3265 | + cur_id_size = 0; | |
3266 | + break; | |
3267 | + } | |
3268 | + break; | |
3269 | + case 0x8: | |
3270 | + /* SCSI name string */ | |
3271 | + if (cur_id_size + 4 > d[3]) | |
3272 | + break; | |
3273 | + /* Prefer others for truncated descriptor */ | |
3274 | + if (cur_id_size && d[3] > id_len) | |
3275 | + break; | |
3276 | + cur_id_size = id_size = d[3]; | |
3277 | + cur_id_str = d + 4; | |
3278 | + cur_id_type = d[1] & 0xf; | |
3279 | + if (cur_id_size >= id_len) | |
3280 | + cur_id_size = id_len - 1; | |
3281 | + memcpy(id, cur_id_str, cur_id_size); | |
3282 | + /* Decrease priority for truncated descriptor */ | |
3283 | + if (cur_id_size != id_size) | |
3284 | + cur_id_size = 6; | |
3285 | + break; | |
3286 | + default: | |
3287 | + break; | |
3288 | + } | |
3289 | +next_desig: | |
3290 | + d += d[3] + 4; | |
3291 | + } | |
3292 | + rcu_read_unlock(); | |
3293 | + | |
3294 | + return id_size; | |
3295 | +} | |
3296 | +EXPORT_SYMBOL(scsi_vpd_lun_id); |
include/scsi/scsi_device.h
... | ... | @@ -415,6 +415,7 @@ |
415 | 415 | } |
416 | 416 | extern void sdev_disable_disk_events(struct scsi_device *sdev); |
417 | 417 | extern void sdev_enable_disk_events(struct scsi_device *sdev); |
418 | +extern int scsi_vpd_lun_id(struct scsi_device *, char *, size_t); | |
418 | 419 | |
419 | 420 | #ifdef CONFIG_PM |
420 | 421 | extern int scsi_autopm_get_device(struct scsi_device *); |