scsi: ata: libata-scsi: Add support for CDL pages mode sense

Modify ata_scsiop_mode_sense() and ata_msense_control() to support mode
sense access to the T2A and T2B sub-pages of the control mode page.
ata_msense_control() is modified to support sub-pages. The T2A sub-page is
generated using the read descriptors of the command duration limits log
page 18h. The T2B sub-page is generated using the write descriptors of the
same log page. With the addition of these sub-pages, getting all sub-pages
of the control mode page is also supported by increasing the value of
ATA_SCSI_RBUF_SIZE from 576B up to 2048B to ensure that all sub-pages fit
in the fill buffer.

Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Co-developed-by: Niklas Cassel <niklas.cassel@wdc.com>
Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
Link: https://lore.kernel.org/r/20230511011356.227789-17-nks@flawful.org
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Damien Le Moal 2023-05-11 03:13:49 +02:00 committed by Martin K. Petersen
parent 0de5580152
commit 673b2fe6ff
1 changed files with 127 additions and 21 deletions

View File

@ -37,7 +37,7 @@
#include "libata.h"
#include "libata-transport.h"
#define ATA_SCSI_RBUF_SIZE 576
#define ATA_SCSI_RBUF_SIZE 2048
static DEFINE_SPINLOCK(ata_scsi_rbuf_lock);
static u8 ata_scsi_rbuf[ATA_SCSI_RBUF_SIZE];
@ -55,6 +55,9 @@ static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap,
#define CONTROL_MPAGE_LEN 12
#define ALL_MPAGES 0x3f
#define ALL_SUB_MPAGES 0xff
#define CDL_T2A_SUB_MPAGE 0x07
#define CDL_T2B_SUB_MPAGE 0x08
#define CDL_T2_SUB_MPAGE_LEN 232
static const u8 def_rw_recovery_mpage[RW_RECOVERY_MPAGE_LEN] = {
RW_RECOVERY_MPAGE,
@ -2196,10 +2199,98 @@ static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable)
return sizeof(def_cache_mpage);
}
/*
* Simulate MODE SENSE control mode page, sub-page 0.
*/
static unsigned int ata_msense_control_spg0(struct ata_device *dev, u8 *buf,
bool changeable)
{
modecpy(buf, def_control_mpage,
sizeof(def_control_mpage), changeable);
if (changeable) {
/* ata_mselect_control() */
buf[2] |= (1 << 2);
} else {
bool d_sense = (dev->flags & ATA_DFLAG_D_SENSE);
/* descriptor format sense data */
buf[2] |= (d_sense << 2);
}
return sizeof(def_control_mpage);
}
/*
* Translate an ATA duration limit in microseconds to a SCSI duration limit
* using the t2cdlunits 0xa (10ms). Since the SCSI duration limits are 2-bytes
* only, take care of overflows.
*/
static inline u16 ata_xlat_cdl_limit(u8 *buf)
{
u32 limit = get_unaligned_le32(buf);
return min_t(u32, limit / 10000, 65535);
}
/*
* Simulate MODE SENSE control mode page, sub-pages 07h and 08h
* (command duration limits T2A and T2B mode pages).
*/
static unsigned int ata_msense_control_spgt2(struct ata_device *dev, u8 *buf,
u8 spg)
{
u8 *b, *cdl = dev->cdl, *desc;
u32 policy;
int i;
/*
* Fill the subpage. The first four bytes of the T2A/T2B mode pages
* are a header. The PAGE LENGTH field is the size of the page
* excluding the header.
*/
buf[0] = CONTROL_MPAGE;
buf[1] = spg;
put_unaligned_be16(CDL_T2_SUB_MPAGE_LEN - 4, &buf[2]);
if (spg == CDL_T2A_SUB_MPAGE) {
/*
* Read descriptors map to the T2A page:
* set perf_vs_duration_guidleine.
*/
buf[7] = (cdl[0] & 0x03) << 4;
desc = cdl + 64;
} else {
/* Write descriptors map to the T2B page */
desc = cdl + 288;
}
/* Fill the T2 page descriptors */
b = &buf[8];
policy = get_unaligned_le32(&cdl[0]);
for (i = 0; i < 7; i++, b += 32, desc += 32) {
/* t2cdlunits: fixed to 10ms */
b[0] = 0x0a;
/* Max inactive time and its policy */
put_unaligned_be16(ata_xlat_cdl_limit(&desc[8]), &b[2]);
b[6] = ((policy >> 8) & 0x0f) << 4;
/* Max active time and its policy */
put_unaligned_be16(ata_xlat_cdl_limit(&desc[4]), &b[4]);
b[6] |= (policy >> 4) & 0x0f;
/* Command duration guideline and its policy */
put_unaligned_be16(ata_xlat_cdl_limit(&desc[16]), &b[10]);
b[14] = policy & 0x0f;
}
return CDL_T2_SUB_MPAGE_LEN;
}
/**
* ata_msense_control - Simulate MODE SENSE control mode page
* @dev: ATA device of interest
* @buf: output buffer
* @spg: sub-page code
* @changeable: whether changeable parameters are requested
*
* Generate a generic MODE SENSE control mode page.
@ -2208,17 +2299,24 @@ static unsigned int ata_msense_caching(u16 *id, u8 *buf, bool changeable)
* None.
*/
static unsigned int ata_msense_control(struct ata_device *dev, u8 *buf,
bool changeable)
u8 spg, bool changeable)
{
modecpy(buf, def_control_mpage, sizeof(def_control_mpage), changeable);
if (changeable) {
buf[2] |= (1 << 2); /* ata_mselect_control() */
} else {
bool d_sense = (dev->flags & ATA_DFLAG_D_SENSE);
unsigned int n;
buf[2] |= (d_sense << 2); /* descriptor format sense data */
switch (spg) {
case 0:
return ata_msense_control_spg0(dev, buf, changeable);
case CDL_T2A_SUB_MPAGE:
case CDL_T2B_SUB_MPAGE:
return ata_msense_control_spgt2(dev, buf, spg);
case ALL_SUB_MPAGES:
n = ata_msense_control_spg0(dev, buf, changeable);
n += ata_msense_control_spgt2(dev, buf + n, CDL_T2A_SUB_MPAGE);
n += ata_msense_control_spgt2(dev, buf + n, CDL_T2A_SUB_MPAGE);
return n;
default:
return 0;
}
return sizeof(def_control_mpage);
}
/**
@ -2291,13 +2389,24 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
pg = scsicmd[2] & 0x3f;
spg = scsicmd[3];
/*
* No mode subpages supported (yet) but asking for _all_
* subpages may be valid
* Supported subpages: all subpages and sub-pages 07h and 08h of
* the control page.
*/
if (spg && (spg != ALL_SUB_MPAGES)) {
fp = 3;
goto invalid_fld;
if (spg) {
switch (spg) {
case ALL_SUB_MPAGES:
break;
case CDL_T2A_SUB_MPAGE:
case CDL_T2B_SUB_MPAGE:
if (dev->flags & ATA_DFLAG_CDL && pg == CONTROL_MPAGE)
break;
fallthrough;
default:
fp = 3;
goto invalid_fld;
}
}
switch(pg) {
@ -2310,13 +2419,13 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
break;
case CONTROL_MPAGE:
p += ata_msense_control(args->dev, p, page_control == 1);
p += ata_msense_control(args->dev, p, spg, page_control == 1);
break;
case ALL_MPAGES:
p += ata_msense_rw_recovery(p, page_control == 1);
p += ata_msense_caching(args->id, p, page_control == 1);
p += ata_msense_control(args->dev, p, page_control == 1);
p += ata_msense_control(args->dev, p, spg, page_control == 1);
break;
default: /* invalid page code */
@ -2335,10 +2444,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
memcpy(rbuf + 4, sat_blk_desc, sizeof(sat_blk_desc));
}
} else {
unsigned int output_len = p - rbuf - 2;
rbuf[0] = output_len >> 8;
rbuf[1] = output_len;
put_unaligned_be16(p - rbuf - 2, &rbuf[0]);
rbuf[3] |= dpofua;
if (ebd) {
rbuf[7] = sizeof(sat_blk_desc);
@ -3637,7 +3743,7 @@ static int ata_mselect_control(struct ata_queued_cmd *qc,
/*
* Check that read-only bits are not modified.
*/
ata_msense_control(dev, mpage, false);
ata_msense_control_spg0(dev, mpage, false);
for (i = 0; i < CONTROL_MPAGE_LEN - 2; i++) {
if (i == 0)
continue;