[SCSI] sd: Identify DIF protection type and application tag ownership

If a disk is formatted with protection information (Inquiry bit
PROTECT=1) it is required to support Read Capacity(16).  Force use of
the 16-bit command in this case and extract the P_TYPE field which
indicates whether the disk is formatted using DIF Type 1, 2 or 3.

The ATO (App Tag Own) bit in the Control Mode Page indicates whether
the storage device or the initiator own the contents of the
DIF application tag.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
Martin K. Petersen 2008-07-17 04:28:34 -04:00 committed by James Bottomley
parent 511e44f42e
commit e0597d7001
2 changed files with 141 additions and 4 deletions

View File

@ -233,6 +233,24 @@ sd_show_allow_restart(struct device *dev, struct device_attribute *attr,
return snprintf(buf, 40, "%d\n", sdkp->device->allow_restart); return snprintf(buf, 40, "%d\n", sdkp->device->allow_restart);
} }
static ssize_t
sd_show_protection_type(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct scsi_disk *sdkp = to_scsi_disk(dev);
return snprintf(buf, 20, "%u\n", sdkp->protection_type);
}
static ssize_t
sd_show_app_tag_own(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct scsi_disk *sdkp = to_scsi_disk(dev);
return snprintf(buf, 20, "%u\n", sdkp->ATO);
}
static struct device_attribute sd_disk_attrs[] = { static struct device_attribute sd_disk_attrs[] = {
__ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type, __ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type,
sd_store_cache_type), sd_store_cache_type),
@ -241,6 +259,8 @@ static struct device_attribute sd_disk_attrs[] = {
sd_store_allow_restart), sd_store_allow_restart),
__ATTR(manage_start_stop, S_IRUGO|S_IWUSR, sd_show_manage_start_stop, __ATTR(manage_start_stop, S_IRUGO|S_IWUSR, sd_show_manage_start_stop,
sd_store_manage_start_stop), sd_store_manage_start_stop),
__ATTR(protection_type, S_IRUGO, sd_show_protection_type, NULL),
__ATTR(app_tag_own, S_IRUGO, sd_show_app_tag_own, NULL),
__ATTR_NULL, __ATTR_NULL,
}; };
@ -1164,6 +1184,49 @@ sd_spinup_disk(struct scsi_disk *sdkp)
} }
} }
/*
* Determine whether disk supports Data Integrity Field.
*/
void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer)
{
struct scsi_device *sdp = sdkp->device;
u8 type;
if (scsi_device_protection(sdp) == 0 || (buffer[12] & 1) == 0)
type = 0;
else
type = ((buffer[12] >> 1) & 7) + 1; /* P_TYPE 0 = Type 1 */
switch (type) {
case SD_DIF_TYPE0_PROTECTION:
sdkp->protection_type = 0;
break;
case SD_DIF_TYPE1_PROTECTION:
case SD_DIF_TYPE3_PROTECTION:
sdkp->protection_type = type;
break;
case SD_DIF_TYPE2_PROTECTION:
sd_printk(KERN_ERR, sdkp, "formatted with DIF Type 2 " \
"protection which is currently unsupported. " \
"Disabling disk!\n");
goto disable;
default:
sd_printk(KERN_ERR, sdkp, "formatted with unknown " \
"protection type %d. Disabling disk!\n", type);
goto disable;
}
return;
disable:
sdkp->protection_type = 0;
sdkp->capacity = 0;
}
/* /*
* read disk capacity * read disk capacity
*/ */
@ -1173,7 +1236,8 @@ sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer)
unsigned char cmd[16]; unsigned char cmd[16];
int the_result, retries; int the_result, retries;
int sector_size = 0; int sector_size = 0;
int longrc = 0; /* Force READ CAPACITY(16) when PROTECT=1 */
int longrc = scsi_device_protection(sdkp->device) ? 1 : 0;
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
int sense_valid = 0; int sense_valid = 0;
struct scsi_device *sdp = sdkp->device; struct scsi_device *sdp = sdkp->device;
@ -1185,8 +1249,8 @@ repeat:
memset((void *) cmd, 0, 16); memset((void *) cmd, 0, 16);
cmd[0] = SERVICE_ACTION_IN; cmd[0] = SERVICE_ACTION_IN;
cmd[1] = SAI_READ_CAPACITY_16; cmd[1] = SAI_READ_CAPACITY_16;
cmd[13] = 12; cmd[13] = 13;
memset((void *) buffer, 0, 12); memset((void *) buffer, 0, 13);
} else { } else {
cmd[0] = READ_CAPACITY; cmd[0] = READ_CAPACITY;
memset((void *) &cmd[1], 0, 9); memset((void *) &cmd[1], 0, 9);
@ -1194,7 +1258,7 @@ repeat:
} }
the_result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE, the_result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE,
buffer, longrc ? 12 : 8, &sshdr, buffer, longrc ? 13 : 8, &sshdr,
SD_TIMEOUT, SD_MAX_RETRIES); SD_TIMEOUT, SD_MAX_RETRIES);
if (media_not_present(sdkp, &sshdr)) if (media_not_present(sdkp, &sshdr))
@ -1269,6 +1333,8 @@ repeat:
sector_size = (buffer[8] << 24) | sector_size = (buffer[8] << 24) |
(buffer[9] << 16) | (buffer[10] << 8) | buffer[11]; (buffer[9] << 16) | (buffer[10] << 8) | buffer[11];
sd_read_protection_type(sdkp, buffer);
} }
/* Some devices return the total number of sectors, not the /* Some devices return the total number of sectors, not the
@ -1530,6 +1596,52 @@ defaults:
sdkp->DPOFUA = 0; sdkp->DPOFUA = 0;
} }
/*
* The ATO bit indicates whether the DIF application tag is available
* for use by the operating system.
*/
void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
{
int res, offset;
struct scsi_device *sdp = sdkp->device;
struct scsi_mode_data data;
struct scsi_sense_hdr sshdr;
if (sdp->type != TYPE_DISK)
return;
if (sdkp->protection_type == 0)
return;
res = scsi_mode_sense(sdp, 1, 0x0a, buffer, 36, SD_TIMEOUT,
SD_MAX_RETRIES, &data, &sshdr);
if (!scsi_status_is_good(res) || !data.header_length ||
data.length < 6) {
sd_printk(KERN_WARNING, sdkp,
"getting Control mode page failed, assume no ATO\n");
if (scsi_sense_valid(&sshdr))
sd_print_sense_hdr(sdkp, &sshdr);
return;
}
offset = data.header_length + data.block_descriptor_length;
if ((buffer[offset] & 0x3f) != 0x0a) {
sd_printk(KERN_ERR, sdkp, "ATO Got wrong page\n");
return;
}
if ((buffer[offset + 5] & 0x80) == 0)
return;
sdkp->ATO = 1;
return;
}
/** /**
* sd_revalidate_disk - called the first time a new disk is seen, * sd_revalidate_disk - called the first time a new disk is seen,
* performs disk spin up, read_capacity, etc. * performs disk spin up, read_capacity, etc.
@ -1566,6 +1678,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
sdkp->write_prot = 0; sdkp->write_prot = 0;
sdkp->WCE = 0; sdkp->WCE = 0;
sdkp->RCD = 0; sdkp->RCD = 0;
sdkp->ATO = 0;
sd_spinup_disk(sdkp); sd_spinup_disk(sdkp);
@ -1577,6 +1690,7 @@ static int sd_revalidate_disk(struct gendisk *disk)
sd_read_capacity(sdkp, buffer); sd_read_capacity(sdkp, buffer);
sd_read_write_protect_flag(sdkp, buffer); sd_read_write_protect_flag(sdkp, buffer);
sd_read_cache_type(sdkp, buffer); sd_read_cache_type(sdkp, buffer);
sd_read_app_tag_own(sdkp, buffer);
} }
/* /*

View File

@ -41,7 +41,9 @@ struct scsi_disk {
u32 index; u32 index;
u8 media_present; u8 media_present;
u8 write_prot; u8 write_prot;
u8 protection_type;/* Data Integrity Field */
unsigned previous_state : 1; unsigned previous_state : 1;
unsigned ATO : 1; /* state of disk ATO bit */
unsigned WCE : 1; /* state of disk WCE bit */ unsigned WCE : 1; /* state of disk WCE bit */
unsigned RCD : 1; /* state of disk RCD bit, unused */ unsigned RCD : 1; /* state of disk RCD bit, unused */
unsigned DPOFUA : 1; /* state of disk DPOFUA bit */ unsigned DPOFUA : 1; /* state of disk DPOFUA bit */
@ -59,4 +61,25 @@ static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
(sdsk)->disk->disk_name, ##a) : \ (sdsk)->disk->disk_name, ##a) : \
sdev_printk(prefix, (sdsk)->device, fmt, ##a) sdev_printk(prefix, (sdsk)->device, fmt, ##a)
/*
* A DIF-capable target device can be formatted with different
* protection schemes. Currently 0 through 3 are defined:
*
* Type 0 is regular (unprotected) I/O
*
* Type 1 defines the contents of the guard and reference tags
*
* Type 2 defines the contents of the guard and reference tags and
* uses 32-byte commands to seed the latter
*
* Type 3 defines the contents of the guard tag only
*/
enum sd_dif_target_protection_types {
SD_DIF_TYPE0_PROTECTION = 0x0,
SD_DIF_TYPE1_PROTECTION = 0x1,
SD_DIF_TYPE2_PROTECTION = 0x2,
SD_DIF_TYPE3_PROTECTION = 0x3,
};
#endif /* _SCSI_DISK_H */ #endif /* _SCSI_DISK_H */