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

libata: disable LPM for some WD SATA-I devices

For some reason, some early WD drives spin up and down drives
erratically when the link is put into slumber mode which can reduce
the life expectancy of the device significantly. Unfortunately, we
don't have full list of devices and given the nature of the issue it'd
be better to err on the side of false positives than the other way
around. Let's disable LPM on all WD devices which match one of the
known problematic model prefixes and are SATA-I.

As horkage list doesn't support matching SATA capabilities, this is
implemented as two horkages - WD_BROKEN_LPM and NOLPM. The former is
set for the known prefixes and sets the latter if the matched device
is SATA-I.

Note that this isn't optimal as this disables all LPM operations and
partial link power state reportedly works fine on these; however, the
way LPM is implemented in libata makes it difficult to precisely map
libata LPM setting to specific link power state. Well, these devices
are already fairly outdated. Let's just disable whole LPM for now.

Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-and-tested-by: Nikos Barkas <levelwol@gmail.com>
Reported-and-tested-by: Ioannis Barkas <risc4all@yahoo.com>
References: https://bugzilla.kernel.org/show_bug.cgi?id=57211
Cc: stable@vger.kernel.org

Tejun Heo ecd75ad5 6e1af697

+44 -3
+27
drivers/ata/libata-core.c
··· 2222 2222 if (rc) 2223 2223 return rc; 2224 2224 2225 + /* some WD SATA-1 drives have issues with LPM, turn on NOLPM for them */ 2226 + if ((dev->horkage & ATA_HORKAGE_WD_BROKEN_LPM) && 2227 + (id[ATA_ID_SATA_CAPABILITY] & 0xe) == 0x2) 2228 + dev->horkage |= ATA_HORKAGE_NOLPM; 2229 + 2230 + if (dev->horkage & ATA_HORKAGE_NOLPM) { 2231 + ata_dev_warn(dev, "LPM support broken, forcing max_power\n"); 2232 + dev->link->ap->target_lpm_policy = ATA_LPM_MAX_POWER; 2233 + } 2234 + 2225 2235 /* let ACPI work its magic */ 2226 2236 rc = ata_acpi_on_devcfg(dev); 2227 2237 if (rc) ··· 4225 4215 /* devices that don't properly handle queued TRIM commands */ 4226 4216 { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, 4227 4217 { "Crucial_CT???M500SSD1", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, 4218 + 4219 + /* 4220 + * Some WD SATA-I drives spin up and down erratically when the link 4221 + * is put into the slumber mode. We don't have full list of the 4222 + * affected devices. Disable LPM if the device matches one of the 4223 + * known prefixes and is SATA-1. As a side effect LPM partial is 4224 + * lost too. 4225 + * 4226 + * https://bugzilla.kernel.org/show_bug.cgi?id=57211 4227 + */ 4228 + { "WDC WD800JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, 4229 + { "WDC WD1200JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, 4230 + { "WDC WD1600JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, 4231 + { "WDC WD2000JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, 4232 + { "WDC WD2500JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, 4233 + { "WDC WD3000JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, 4234 + { "WDC WD3200JD-*", NULL, ATA_HORKAGE_WD_BROKEN_LPM }, 4228 4235 4229 4236 /* End Marker */ 4230 4237 { }
+15 -3
drivers/ata/libata-scsi.c
··· 111 111 [ATA_LPM_MIN_POWER] = "min_power", 112 112 }; 113 113 114 - static ssize_t ata_scsi_lpm_store(struct device *dev, 114 + static ssize_t ata_scsi_lpm_store(struct device *device, 115 115 struct device_attribute *attr, 116 116 const char *buf, size_t count) 117 117 { 118 - struct Scsi_Host *shost = class_to_shost(dev); 118 + struct Scsi_Host *shost = class_to_shost(device); 119 119 struct ata_port *ap = ata_shost_to_port(shost); 120 + struct ata_link *link; 121 + struct ata_device *dev; 120 122 enum ata_lpm_policy policy; 121 123 unsigned long flags; 122 124 ··· 134 132 return -EINVAL; 135 133 136 134 spin_lock_irqsave(ap->lock, flags); 135 + 136 + ata_for_each_link(link, ap, EDGE) { 137 + ata_for_each_dev(dev, &ap->link, ENABLED) { 138 + if (dev->horkage & ATA_HORKAGE_NOLPM) { 139 + count = -EOPNOTSUPP; 140 + goto out_unlock; 141 + } 142 + } 143 + } 144 + 137 145 ap->target_lpm_policy = policy; 138 146 ata_port_schedule_eh(ap); 147 + out_unlock: 139 148 spin_unlock_irqrestore(ap->lock, flags); 140 - 141 149 return count; 142 150 } 143 151
+2
include/linux/libata.h
··· 419 419 ATA_HORKAGE_MAX_SEC_LBA48 = (1 << 17), /* Set max sects to 65535 */ 420 420 ATA_HORKAGE_ATAPI_DMADIR = (1 << 18), /* device requires dmadir */ 421 421 ATA_HORKAGE_NO_NCQ_TRIM = (1 << 19), /* don't use queued TRIM */ 422 + ATA_HORKAGE_NOLPM = (1 << 20), /* don't use LPM */ 423 + ATA_HORKAGE_WD_BROKEN_LPM = (1 << 21), /* some WDs have broken LPM */ 422 424 423 425 /* DMA mask for user DMA control: User visible values; DO NOT 424 426 renumber */