forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									e2d817db32
								
							
						
					
					
						commit
						9983bed390
					
				
					 2 changed files with 141 additions and 0 deletions
				
			
		| 
						 | 
					@ -3154,3 +3154,143 @@ void sdev_enable_disk_events(struct scsi_device *sdev)
 | 
				
			||||||
	atomic_dec(&sdev->disk_events_disable_depth);
 | 
						atomic_dec(&sdev->disk_events_disable_depth);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(sdev_enable_disk_events);
 | 
					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_disable_disk_events(struct scsi_device *sdev);
 | 
				
			||||||
extern void sdev_enable_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
 | 
					#ifdef CONFIG_PM
 | 
				
			||||||
extern int scsi_autopm_get_device(struct scsi_device *);
 | 
					extern int scsi_autopm_get_device(struct scsi_device *);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue