Commit 9983bed3 authored by Hannes Reinecke's avatar Hannes Reinecke Committed by Martin K. Petersen

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: 's avatarHannes Reinecke <hare@suse.de>
Reviewed-by: 's avatarEwan Milne <emilne@redhat.com>
Reviewed-by: 's avatarJohannes Thumshirn <jthumshirn@suse.de>
Reviewed-by: 's avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: 's avatarMartin K. Petersen <martin.petersen@oracle.com>
parent e2d817db
......@@ -3154,3 +3154,143 @@ void sdev_enable_disk_events(struct scsi_device *sdev)
atomic_dec(&sdev->disk_events_disable_depth);
}
EXPORT_SYMBOL(sdev_enable_disk_events);
/**
* scsi_vpd_lun_id - return a unique device identification
* @sdev: SCSI device
* @id: buffer for the identification
* @id_len: length of the buffer
*
* Copies a unique device identification into @id based
* on the information in the VPD page 0x83 of the device.
* The string will be formatted as a SCSI name string.
*
* Returns the length of the identification or error on failure.
* If the identifier is longer than the supplied buffer the actual
* identifier length is returned and the buffer is not zero-padded.
*/
int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len)
{
u8 cur_id_type = 0xff;
u8 cur_id_size = 0;
unsigned char *d, *cur_id_str;
unsigned char __rcu *vpd_pg83;
int id_size = -EINVAL;
rcu_read_lock();
vpd_pg83 = rcu_dereference(sdev->vpd_pg83);
if (!vpd_pg83) {
rcu_read_unlock();
return -ENXIO;
}
/*
* Look for the correct descriptor.
* Order of preference for lun descriptor:
* - SCSI name string
* - NAA IEEE Registered Extended
* - EUI-64 based 16-byte
* - EUI-64 based 12-byte
* - NAA IEEE Registered
* - NAA IEEE Extended
* as longer descriptors reduce the likelyhood
* of identification clashes.
*/
/* The id string must be at least 20 bytes + terminating NULL byte */
if (id_len < 21) {
rcu_read_unlock();
return -EINVAL;
}
memset(id, 0, id_len);
d = vpd_pg83 + 4;
while (d < vpd_pg83 + sdev->vpd_pg83_len) {
/* Skip designators not referring to the LUN */
if ((d[1] & 0x30) != 0x00)
goto next_desig;
switch (d[1] & 0xf) {
case 0x2:
/* EUI-64 */
if (cur_id_size > d[3])
break;
/* Prefer NAA IEEE Registered Extended */
if (cur_id_type == 0x3 &&
cur_id_size == d[3])
break;
cur_id_size = d[3];
cur_id_str = d + 4;
cur_id_type = d[1] & 0xf;
switch (cur_id_size) {
case 8:
id_size = snprintf(id, id_len,
"eui.%8phN",
cur_id_str);
break;
case 12:
id_size = snprintf(id, id_len,
"eui.%12phN",
cur_id_str);
break;
case 16:
id_size = snprintf(id, id_len,
"eui.%16phN",
cur_id_str);
break;
default:
cur_id_size = 0;
break;
}
break;
case 0x3:
/* NAA */
if (cur_id_size > d[3])
break;
cur_id_size = d[3];
cur_id_str = d + 4;
cur_id_type = d[1] & 0xf;
switch (cur_id_size) {
case 8:
id_size = snprintf(id, id_len,
"naa.%8phN",
cur_id_str);
break;
case 16:
id_size = snprintf(id, id_len,
"naa.%16phN",
cur_id_str);
break;
default:
cur_id_size = 0;
break;
}
break;
case 0x8:
/* SCSI name string */
if (cur_id_size + 4 > d[3])
break;
/* Prefer others for truncated descriptor */
if (cur_id_size && d[3] > id_len)
break;
cur_id_size = id_size = d[3];
cur_id_str = d + 4;
cur_id_type = d[1] & 0xf;
if (cur_id_size >= id_len)
cur_id_size = id_len - 1;
memcpy(id, cur_id_str, cur_id_size);
/* Decrease priority for truncated descriptor */
if (cur_id_size != id_size)
cur_id_size = 6;
break;
default:
break;
}
next_desig:
d += d[3] + 4;
}
rcu_read_unlock();
return id_size;
}
EXPORT_SYMBOL(scsi_vpd_lun_id);
......@@ -415,6 +415,7 @@ static inline int scsi_execute_req(struct scsi_device *sdev,
}
extern void sdev_disable_disk_events(struct scsi_device *sdev);
extern void sdev_enable_disk_events(struct scsi_device *sdev);
extern int scsi_vpd_lun_id(struct scsi_device *, char *, size_t);
#ifdef CONFIG_PM
extern int scsi_autopm_get_device(struct scsi_device *);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment