libata: support host-aware and host-managed ZAC devices

Byte 69 bits 0:1 in the IDENTIFY DEVICE data indicate a
host-aware ZAC device.
Host-managed ZAC devices have their own individual signature,
and to not set the bits in the IDENTIFY DEVICE data.
And whenever we detect a ZAC-compatible device we should
be displaying the zoned block characteristics VPD page.

Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
Hannes Reinecke 2016-04-25 12:45:56 +02:00 committed by Tejun Heo
parent 856c466393
commit 6d1003ae8d
4 changed files with 138 additions and 2 deletions

View File

@ -2227,6 +2227,99 @@ static void ata_dev_config_sense_reporting(struct ata_device *dev)
} }
} }
static void ata_dev_config_zac(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
unsigned int err_mask;
u8 *identify_buf = ap->sector_buf;
int log_index = ATA_LOG_SATA_ID_DEV_DATA * 2, i, found = 0;
u16 log_pages;
dev->zac_zones_optimal_open = U32_MAX;
dev->zac_zones_optimal_nonseq = U32_MAX;
dev->zac_zones_max_open = U32_MAX;
/*
* Always set the 'ZAC' flag for Host-managed devices.
*/
if (dev->class == ATA_DEV_ZAC)
dev->flags |= ATA_DFLAG_ZAC;
else if (ata_id_zoned_cap(dev->id) == 0x01)
/*
* Check for host-aware devices.
*/
dev->flags |= ATA_DFLAG_ZAC;
if (!(dev->flags & ATA_DFLAG_ZAC))
return;
/*
* Read Log Directory to figure out if IDENTIFY DEVICE log
* is supported.
*/
err_mask = ata_read_log_page(dev, ATA_LOG_DIRECTORY,
0, ap->sector_buf, 1);
if (err_mask) {
ata_dev_info(dev,
"failed to get Log Directory Emask 0x%x\n",
err_mask);
return;
}
log_pages = get_unaligned_le16(&ap->sector_buf[log_index]);
if (log_pages == 0) {
ata_dev_warn(dev,
"ATA Identify Device Log not supported\n");
return;
}
/*
* Read IDENTIFY DEVICE data log, page 0, to figure out
* if page 9 is supported.
*/
err_mask = ata_read_log_page(dev, ATA_LOG_SATA_ID_DEV_DATA, 0,
identify_buf, 1);
if (err_mask) {
ata_dev_info(dev,
"failed to get Device Identify Log Emask 0x%x\n",
err_mask);
return;
}
log_pages = identify_buf[8];
for (i = 0; i < log_pages; i++) {
if (identify_buf[9 + i] == ATA_LOG_ZONED_INFORMATION) {
found++;
break;
}
}
if (!found) {
ata_dev_warn(dev,
"ATA Zoned Information Log not supported\n");
return;
}
/*
* Read IDENTIFY DEVICE data log, page 9 (Zoned-device information)
*/
err_mask = ata_read_log_page(dev, ATA_LOG_SATA_ID_DEV_DATA,
ATA_LOG_ZONED_INFORMATION,
identify_buf, 1);
if (!err_mask) {
u64 zoned_cap, opt_open, opt_nonseq, max_open;
zoned_cap = get_unaligned_le64(&identify_buf[8]);
if ((zoned_cap >> 63))
dev->zac_zoned_cap = (zoned_cap & 1);
opt_open = get_unaligned_le64(&identify_buf[24]);
if ((opt_open >> 63))
dev->zac_zones_optimal_open = (u32)opt_open;
opt_nonseq = get_unaligned_le64(&identify_buf[32]);
if ((opt_nonseq >> 63))
dev->zac_zones_optimal_nonseq = (u32)opt_nonseq;
max_open = get_unaligned_le64(&identify_buf[40]);
if ((max_open >> 63))
dev->zac_zones_max_open = (u32)max_open;
}
}
/** /**
* ata_dev_configure - Configure the specified ATA/ATAPI device * ata_dev_configure - Configure the specified ATA/ATAPI device
* @dev: Target device to configure * @dev: Target device to configure
@ -2450,6 +2543,7 @@ int ata_dev_configure(struct ata_device *dev)
} }
} }
ata_dev_config_sense_reporting(dev); ata_dev_config_sense_reporting(dev);
ata_dev_config_zac(dev);
dev->cdb_len = 16; dev->cdb_len = 16;
} }

View File

@ -2144,6 +2144,7 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf)
*/ */
static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf) static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf)
{ {
int num_pages;
const u8 pages[] = { const u8 pages[] = {
0x00, /* page 0x00, this page */ 0x00, /* page 0x00, this page */
0x80, /* page 0x80, unit serial no page */ 0x80, /* page 0x80, unit serial no page */
@ -2152,10 +2153,14 @@ static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf)
0xb0, /* page 0xb0, block limits page */ 0xb0, /* page 0xb0, block limits page */
0xb1, /* page 0xb1, block device characteristics page */ 0xb1, /* page 0xb1, block device characteristics page */
0xb2, /* page 0xb2, thin provisioning page */ 0xb2, /* page 0xb2, thin provisioning page */
0xb6, /* page 0xb6, zoned block device characteristics */
}; };
rbuf[3] = sizeof(pages); /* number of supported VPD pages */ num_pages = sizeof(pages);
memcpy(rbuf + 4, pages, sizeof(pages)); if (!(args->dev->flags & ATA_DFLAG_ZAC))
num_pages--;
rbuf[3] = num_pages; /* number of supported VPD pages */
memcpy(rbuf + 4, pages, num_pages);
return 0; return 0;
} }
@ -2343,6 +2348,26 @@ static unsigned int ata_scsiop_inq_b2(struct ata_scsi_args *args, u8 *rbuf)
return 0; return 0;
} }
static unsigned int ata_scsiop_inq_b6(struct ata_scsi_args *args, u8 *rbuf)
{
/*
* zbc-r05 SCSI Zoned Block device characteristics VPD page
*/
rbuf[1] = 0xb6;
rbuf[3] = 0x3C;
/*
* URSWRZ bit is only meaningful for host-managed ZAC drives
*/
if (args->dev->zac_zoned_cap & 1)
rbuf[4] |= 1;
put_unaligned_be32(args->dev->zac_zones_optimal_open, &rbuf[8]);
put_unaligned_be32(args->dev->zac_zones_optimal_nonseq, &rbuf[12]);
put_unaligned_be32(args->dev->zac_zones_max_open, &rbuf[16]);
return 0;
}
/** /**
* ata_scsiop_noop - Command handler that simply returns success. * ata_scsiop_noop - Command handler that simply returns success.
* @args: device IDENTIFY data / SCSI command of interest. * @args: device IDENTIFY data / SCSI command of interest.
@ -2661,6 +2686,9 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
rbuf[14] |= 0x40; /* LBPRZ */ rbuf[14] |= 0x40; /* LBPRZ */
} }
} }
if (ata_id_zoned_cap(args->id) ||
args->dev->class == ATA_DEV_ZAC)
rbuf[12] = (1 << 4); /* RC_BASIS */
} }
return 0; return 0;
} }
@ -4046,6 +4074,12 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
case 0xb2: case 0xb2:
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b2); ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b2);
break; break;
case 0xb6:
if (dev->flags & ATA_DFLAG_ZAC) {
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b6);
break;
}
/* Fallthrough */
default: default:
ata_scsi_invalid_field(dev, cmd, 2); ata_scsi_invalid_field(dev, cmd, 2);
break; break;

View File

@ -338,6 +338,7 @@ enum {
ATA_LOG_NCQ_SEND_RECV = 0x13, ATA_LOG_NCQ_SEND_RECV = 0x13,
ATA_LOG_SATA_ID_DEV_DATA = 0x30, ATA_LOG_SATA_ID_DEV_DATA = 0x30,
ATA_LOG_SATA_SETTINGS = 0x08, ATA_LOG_SATA_SETTINGS = 0x08,
ATA_LOG_ZONED_INFORMATION = 0x09,
ATA_LOG_DEVSLP_OFFSET = 0x30, ATA_LOG_DEVSLP_OFFSET = 0x30,
ATA_LOG_DEVSLP_SIZE = 0x08, ATA_LOG_DEVSLP_SIZE = 0x08,
ATA_LOG_DEVSLP_MDAT = 0x00, ATA_LOG_DEVSLP_MDAT = 0x00,

View File

@ -181,6 +181,7 @@ enum {
ATA_DFLAG_DEVSLP = (1 << 27), /* device supports Device Sleep */ ATA_DFLAG_DEVSLP = (1 << 27), /* device supports Device Sleep */
ATA_DFLAG_ACPI_DISABLED = (1 << 28), /* ACPI for the device is disabled */ ATA_DFLAG_ACPI_DISABLED = (1 << 28), /* ACPI for the device is disabled */
ATA_DFLAG_D_SENSE = (1 << 29), /* Descriptor sense requested */ ATA_DFLAG_D_SENSE = (1 << 29), /* Descriptor sense requested */
ATA_DFLAG_ZAC = (1 << 30), /* ZAC device */
ATA_DEV_UNKNOWN = 0, /* unknown device */ ATA_DEV_UNKNOWN = 0, /* unknown device */
ATA_DEV_ATA = 1, /* ATA device */ ATA_DEV_ATA = 1, /* ATA device */
@ -731,6 +732,12 @@ struct ata_device {
u8 ncq_send_recv_cmds[ATA_LOG_NCQ_SEND_RECV_SIZE]; u8 ncq_send_recv_cmds[ATA_LOG_NCQ_SEND_RECV_SIZE];
u8 ncq_non_data_cmds[ATA_LOG_NCQ_NON_DATA_SIZE]; u8 ncq_non_data_cmds[ATA_LOG_NCQ_NON_DATA_SIZE];
/* ZAC zone configuration */
u32 zac_zoned_cap;
u32 zac_zones_optimal_open;
u32 zac_zones_optimal_nonseq;
u32 zac_zones_max_open;
/* error history */ /* error history */
int spdn_cnt; int spdn_cnt;
/* ering is CLEAR_END, read comment above CLEAR_END */ /* ering is CLEAR_END, read comment above CLEAR_END */