summaryrefslogtreecommitdiffstats
path: root/drivers/ata/libata-core.c
diff options
context:
space:
mode:
authorDamien Le Moal <dlemoal@kernel.org>2023-05-11 03:13:50 +0200
committerMartin K. Petersen <martin.petersen@oracle.com>2023-05-22 17:05:20 -0400
commitdf60f9c64576d6d05b59ec5c34addcd61ef1efb0 (patch)
tree6f024e6cd8ea07169e39a68ade6c953842389271 /drivers/ata/libata-core.c
parent673b2fe6ff1da29d9e70bd484903964772dcae3d (diff)
downloadlinux-stable-df60f9c64576d6d05b59ec5c34addcd61ef1efb0.tar.gz
linux-stable-df60f9c64576d6d05b59ec5c34addcd61ef1efb0.tar.bz2
linux-stable-df60f9c64576d6d05b59ec5c34addcd61ef1efb0.zip
scsi: ata: libata: Add ATA feature control sub-page translation
Add support for the ATA feature control sub-page of the control mode page to enable/disable the command duration limits feature using the cdl_ctrl field of the ATA feature control sub-page. Both mode sense and mode select translation are supported. For mode sense, the ata device flag ATA_DFLAG_CDL_ENABLED is used to cache the status of the command duration limits feature. Enabling this feature is done using a SET FEATURES command with a cdl action set to 1 when the page cdl_ctrl field value is 0x2 (T2A and T2B pages supported). If this field is 0, CDL is disabled using the SET FEATURES command with a cdl action set to 0. Since a device CDL and NCQ priority features should not be used simultaneously, ata_mselect_control_ata_feature() returns an error when attempting to enable CDL with the device priority feature enabled. Conversely, the function ata_ncq_prio_enable_store() used to enable the use of the device NCQ priority feature through sysfs is modified to return an error if the device CDL feature is enabled. Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Hannes Reinecke <hare@suse.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-18-nks@flawful.org Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r--drivers/ata/libata-core.c40
1 files changed, 38 insertions, 2 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 83fe037f63b9..cd7aaf202397 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2371,13 +2371,15 @@ static void ata_dev_config_cdl(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
unsigned int err_mask;
+ bool cdl_enabled;
u64 val;
if (ata_id_major_version(dev->id) < 12)
goto not_supported;
if (!ata_log_supported(dev, ATA_LOG_IDENTIFY_DEVICE) ||
- !ata_identify_page_supported(dev, ATA_LOG_SUPPORTED_CAPABILITIES))
+ !ata_identify_page_supported(dev, ATA_LOG_SUPPORTED_CAPABILITIES) ||
+ !ata_identify_page_supported(dev, ATA_LOG_CURRENT_SETTINGS))
goto not_supported;
err_mask = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE,
@@ -2397,6 +2399,40 @@ static void ata_dev_config_cdl(struct ata_device *dev)
"Command duration guideline is not supported\n");
/*
+ * If CDL is marked as enabled, make sure the feature is enabled too.
+ * Conversely, if CDL is disabled, make sure the feature is turned off.
+ */
+ err_mask = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE,
+ ATA_LOG_CURRENT_SETTINGS,
+ ap->sector_buf, 1);
+ if (err_mask)
+ goto not_supported;
+
+ val = get_unaligned_le64(&ap->sector_buf[8]);
+ cdl_enabled = val & BIT_ULL(63) && val & BIT_ULL(21);
+ if (dev->flags & ATA_DFLAG_CDL_ENABLED) {
+ if (!cdl_enabled) {
+ /* Enable CDL on the device */
+ err_mask = ata_dev_set_feature(dev, SETFEATURES_CDL, 1);
+ if (err_mask) {
+ ata_dev_err(dev,
+ "Enable CDL feature failed\n");
+ goto not_supported;
+ }
+ }
+ } else {
+ if (cdl_enabled) {
+ /* Disable CDL on the device */
+ err_mask = ata_dev_set_feature(dev, SETFEATURES_CDL, 0);
+ if (err_mask) {
+ ata_dev_err(dev,
+ "Disable CDL feature failed\n");
+ goto not_supported;
+ }
+ }
+ }
+
+ /*
* Command duration limits is supported: cache the CDL log page 18h
* (command duration descriptors).
*/
@@ -2412,7 +2448,7 @@ static void ata_dev_config_cdl(struct ata_device *dev)
return;
not_supported:
- dev->flags &= ~ATA_DFLAG_CDL;
+ dev->flags &= ~(ATA_DFLAG_CDL | ATA_DFLAG_CDL_ENABLED);
}
static int ata_dev_config_lba(struct ata_device *dev)