Commit 9983bed3907c379d1d30b7509bb0a871ed655f9d

Authored by Hannes Reinecke
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 *);