Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ata: libata-sata: Add link_power_management_supported sysfs attribute

A port link power management (LPM) policy can be controlled using the
link_power_management_policy sysfs host attribute. However, this
attribute exists also for hosts that do not support LPM and in such
case, attempting to change the LPM policy for the host (port) will fail
with -EOPNOTSUPP.

Introduce the new sysfs link_power_management_supported host attribute
to indicate to the user if a the port and the devices connected to the
port for the host support LPM, which implies that the
link_power_management_policy attribute can be used.

Since checking that a port and its devices support LPM is common between
the new ata_scsi_lpm_supported_show() function and the existing
ata_scsi_lpm_store() function, the new helper ata_scsi_lpm_supported()
is introduced.

Fixes: 413e800cadbf ("ata: libata-sata: Disallow changing LPM state if not supported")
Reported-by: Borah, Chaitanya Kumar <chaitanya.kumar.borah@intel.com>
Reported-by: kernel test robot <oliver.sang@intel.com>
Closes: https://lore.kernel.org/oe-lkp/202507251014.a5becc3b-lkp@intel.com
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>

+44 -12
+1
drivers/ata/ata_piix.c
··· 1089 1089 }; 1090 1090 1091 1091 static struct attribute *piix_sidpr_shost_attrs[] = { 1092 + &dev_attr_link_power_management_supported.attr, 1092 1093 &dev_attr_link_power_management_policy.attr, 1093 1094 NULL 1094 1095 };
+1
drivers/ata/libahci.c
··· 111 111 static DEVICE_ATTR(em_message_supported, S_IRUGO, ahci_show_em_supported, NULL); 112 112 113 113 static struct attribute *ahci_shost_attrs[] = { 114 + &dev_attr_link_power_management_supported.attr, 114 115 &dev_attr_link_power_management_policy.attr, 115 116 &dev_attr_em_message_type.attr, 116 117 &dev_attr_em_message.attr,
+41 -12
drivers/ata/libata-sata.c
··· 900 900 [ATA_LPM_MIN_POWER] = "min_power", 901 901 }; 902 902 903 + /* 904 + * Check if a port supports link power management. 905 + * Must be called with the port locked. 906 + */ 907 + static bool ata_scsi_lpm_supported(struct ata_port *ap) 908 + { 909 + struct ata_link *link; 910 + struct ata_device *dev; 911 + 912 + if (ap->flags & ATA_FLAG_NO_LPM) 913 + return false; 914 + 915 + ata_for_each_link(link, ap, EDGE) { 916 + ata_for_each_dev(dev, &ap->link, ENABLED) { 917 + if (dev->quirks & ATA_QUIRK_NOLPM) 918 + return false; 919 + } 920 + } 921 + 922 + return true; 923 + } 924 + 925 + static ssize_t ata_scsi_lpm_supported_show(struct device *dev, 926 + struct device_attribute *attr, char *buf) 927 + { 928 + struct Scsi_Host *shost = class_to_shost(dev); 929 + struct ata_port *ap = ata_shost_to_port(shost); 930 + unsigned long flags; 931 + bool supported; 932 + 933 + spin_lock_irqsave(ap->lock, flags); 934 + supported = ata_scsi_lpm_supported(ap); 935 + spin_unlock_irqrestore(ap->lock, flags); 936 + 937 + return sysfs_emit(buf, "%d\n", supported); 938 + } 939 + DEVICE_ATTR(link_power_management_supported, S_IRUGO, 940 + ata_scsi_lpm_supported_show, NULL); 941 + EXPORT_SYMBOL_GPL(dev_attr_link_power_management_supported); 942 + 903 943 static ssize_t ata_scsi_lpm_store(struct device *device, 904 944 struct device_attribute *attr, 905 945 const char *buf, size_t count) 906 946 { 907 947 struct Scsi_Host *shost = class_to_shost(device); 908 948 struct ata_port *ap = ata_shost_to_port(shost); 909 - struct ata_link *link; 910 - struct ata_device *dev; 911 949 enum ata_lpm_policy policy; 912 950 unsigned long flags; 913 951 ··· 962 924 963 925 spin_lock_irqsave(ap->lock, flags); 964 926 965 - if (ap->flags & ATA_FLAG_NO_LPM) { 927 + if (!ata_scsi_lpm_supported(ap)) { 966 928 count = -EOPNOTSUPP; 967 929 goto out_unlock; 968 - } 969 - 970 - ata_for_each_link(link, ap, EDGE) { 971 - ata_for_each_dev(dev, &ap->link, ENABLED) { 972 - if (dev->quirks & ATA_QUIRK_NOLPM) { 973 - count = -EOPNOTSUPP; 974 - goto out_unlock; 975 - } 976 - } 977 930 } 978 931 979 932 ap->target_lpm_policy = policy;
+1
include/linux/libata.h
··· 545 545 546 546 extern struct device_attribute dev_attr_unload_heads; 547 547 #ifdef CONFIG_SATA_HOST 548 + extern struct device_attribute dev_attr_link_power_management_supported; 548 549 extern struct device_attribute dev_attr_link_power_management_policy; 549 550 extern struct device_attribute dev_attr_ncq_prio_supported; 550 551 extern struct device_attribute dev_attr_ncq_prio_enable;