Commit 673b2fe6 authored by Damien Le Moal's avatar Damien Le Moal Committed by Martin K. Petersen
Browse files

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: default avatarDamien Le Moal <dlemoal@kernel.org>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Co-developed-by: default avatarNiklas Cassel <niklas.cassel@wdc.com>
Signed-off-by: default avatarNiklas Cassel <niklas.cassel@wdc.com>
Link: https://lore.kernel.org/r/20230511011356.227789-17-nks@flawful.org


Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 0de55801
Loading
Loading
Loading
Loading
+128 −22
Original line number Diff line number Diff line
@@ -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);

		buf[2] |= (d_sense << 2);	/* descriptor format sense data */
	unsigned int n;

	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,14 +2389,25 @@ 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)) {
	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) {
	case RW_RECOVERY_MPAGE:
@@ -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;