Commit 1eb09e62 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ata fixes from Damien Le Moal:

 - Fix the incorrect return type of ata_mselect_control_ata_feature()

 - Several fixes for the control of the Command Duration Limits feature
   to avoid unnecessary enable and disable actions. Avoiding the
   unnecessary enable action also avoids unwanted resets of the CDL
   statistics log page as that is implied for any enable action.

 - Fix the translation for sensing the control mode page to correctly
   return the last enable or disable action performed, as defined in
   SAT-6. This correct mode sense information is used to fix the
   behavior of the scsi layer to avoid unnecessary mode select command
   issuing.

* tag 'ata-6.15-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/libata/linux:
  scsi: Improve CDL control
  ata: libata-scsi: Improve CDL control
  ata: libata-scsi: Fix ata_msense_control_ata_feature()
  ata: libata-scsi: Fix ata_mselect_control_ata_feature() return type
parents eb98f304 14a3cc75
Loading
Loading
Loading
Loading
+17 −8
Original line number Diff line number Diff line
@@ -2453,8 +2453,8 @@ static unsigned int ata_msense_control_ata_feature(struct ata_device *dev,
	 */
	put_unaligned_be16(ATA_FEATURE_SUB_MPAGE_LEN - 4, &buf[2]);

	if (dev->flags & ATA_DFLAG_CDL)
		buf[4] = 0x02; /* Support T2A and T2B pages */
	if (dev->flags & ATA_DFLAG_CDL_ENABLED)
		buf[4] = 0x02; /* T2A and T2B pages enabled */
	else
		buf[4] = 0;

@@ -3886,12 +3886,11 @@ static int ata_mselect_control_spg0(struct ata_queued_cmd *qc,
}

/*
 * Translate MODE SELECT control mode page, sub-pages f2h (ATA feature mode
 * Translate MODE SELECT control mode page, sub-page f2h (ATA feature mode
 * page) into a SET FEATURES command.
 */
static unsigned int ata_mselect_control_ata_feature(struct ata_queued_cmd *qc,
						    const u8 *buf, int len,
						    u16 *fp)
static int ata_mselect_control_ata_feature(struct ata_queued_cmd *qc,
					   const u8 *buf, int len, u16 *fp)
{
	struct ata_device *dev = qc->dev;
	struct ata_taskfile *tf = &qc->tf;
@@ -3909,17 +3908,27 @@ static unsigned int ata_mselect_control_ata_feature(struct ata_queued_cmd *qc,
	/* Check cdl_ctrl */
	switch (buf[0] & 0x03) {
	case 0:
		/* Disable CDL */
		/* Disable CDL if it is enabled */
		if (!(dev->flags & ATA_DFLAG_CDL_ENABLED))
			return 0;
		ata_dev_dbg(dev, "Disabling CDL\n");
		cdl_action = 0;
		dev->flags &= ~ATA_DFLAG_CDL_ENABLED;
		break;
	case 0x02:
		/* Enable CDL T2A/T2B: NCQ priority must be disabled */
		/*
		 * Enable CDL if not already enabled. Since this is mutually
		 * exclusive with NCQ priority, allow this only if NCQ priority
		 * is disabled.
		 */
		if (dev->flags & ATA_DFLAG_CDL_ENABLED)
			return 0;
		if (dev->flags & ATA_DFLAG_NCQ_PRIO_ENABLED) {
			ata_dev_err(dev,
				"NCQ priority must be disabled to enable CDL\n");
			return -EINVAL;
		}
		ata_dev_dbg(dev, "Enabling CDL\n");
		cdl_action = 1;
		dev->flags |= ATA_DFLAG_CDL_ENABLED;
		break;
+24 −12
Original line number Diff line number Diff line
@@ -707,26 +707,23 @@ void scsi_cdl_check(struct scsi_device *sdev)
 */
int scsi_cdl_enable(struct scsi_device *sdev, bool enable)
{
	struct scsi_mode_data data;
	struct scsi_sense_hdr sshdr;
	struct scsi_vpd *vpd;
	bool is_ata = false;
	char buf[64];
	bool is_ata;
	int ret;

	if (!sdev->cdl_supported)
		return -EOPNOTSUPP;

	rcu_read_lock();
	vpd = rcu_dereference(sdev->vpd_pg89);
	if (vpd)
		is_ata = true;
	is_ata = rcu_dereference(sdev->vpd_pg89);
	rcu_read_unlock();

	/*
	 * For ATA devices, CDL needs to be enabled with a SET FEATURES command.
	 */
	if (is_ata) {
		struct scsi_mode_data data;
		struct scsi_sense_hdr sshdr;
		char *buf_data;
		int len;

@@ -735,16 +732,30 @@ int scsi_cdl_enable(struct scsi_device *sdev, bool enable)
		if (ret)
			return -EINVAL;

		/* Enable CDL using the ATA feature page */
		/* Enable or disable CDL using the ATA feature page */
		len = min_t(size_t, sizeof(buf),
			    data.length - data.header_length -
			    data.block_descriptor_length);
		buf_data = buf + data.header_length +
			data.block_descriptor_length;
		if (enable)
			buf_data[4] = 0x02;
		else
			buf_data[4] = 0;

		/*
		 * If we want to enable CDL and CDL is already enabled on the
		 * device, do nothing. This avoids needlessly resetting the CDL
		 * statistics on the device as that is implied by the CDL enable
		 * action. Similar to this, there is no need to do anything if
		 * we want to disable CDL and CDL is already disabled.
		 */
		if (enable) {
			if ((buf_data[4] & 0x03) == 0x02)
				goto out;
			buf_data[4] &= ~0x03;
			buf_data[4] |= 0x02;
		} else {
			if ((buf_data[4] & 0x03) == 0x00)
				goto out;
			buf_data[4] &= ~0x03;
		}

		ret = scsi_mode_select(sdev, 1, 0, buf_data, len, 5 * HZ, 3,
				       &data, &sshdr);
@@ -756,6 +767,7 @@ int scsi_cdl_enable(struct scsi_device *sdev, bool enable)
		}
	}

out:
	sdev->cdl_enable = enable;

	return 0;