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:
parent
0de5580152
commit
673b2fe6ff
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user