Merge remote-tracking branch 'lsk/v3.10/topic/gator' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / drivers / ata / libata-scsi.c
index 4abdbdff6943454552fa3bf509a8bb4a70dc67bb..9933b4db7caf41cbb5c3b8b82518925b67643dd8 100644 (file)
@@ -112,12 +112,14 @@ static const char *ata_lpm_policy_names[] = {
        [ATA_LPM_MIN_POWER]     = "min_power",
 };
 
-static ssize_t ata_scsi_lpm_store(struct device *dev,
+static ssize_t ata_scsi_lpm_store(struct device *device,
                                  struct device_attribute *attr,
                                  const char *buf, size_t count)
 {
-       struct Scsi_Host *shost = class_to_shost(dev);
+       struct Scsi_Host *shost = class_to_shost(device);
        struct ata_port *ap = ata_shost_to_port(shost);
+       struct ata_link *link;
+       struct ata_device *dev;
        enum ata_lpm_policy policy;
        unsigned long flags;
 
@@ -133,10 +135,20 @@ static ssize_t ata_scsi_lpm_store(struct device *dev,
                return -EINVAL;
 
        spin_lock_irqsave(ap->lock, flags);
+
+       ata_for_each_link(link, ap, EDGE) {
+               ata_for_each_dev(dev, &ap->link, ENABLED) {
+                       if (dev->horkage & ATA_HORKAGE_NOLPM) {
+                               count = -EOPNOTSUPP;
+                               goto out_unlock;
+                       }
+               }
+       }
+
        ap->target_lpm_policy = policy;
        ata_port_schedule_eh(ap);
+out_unlock:
        spin_unlock_irqrestore(ap->lock, flags);
-
        return count;
 }
 
@@ -3864,6 +3876,27 @@ void ata_scsi_hotplug(struct work_struct *work)
                return;
        }
 
+       /*
+        * XXX - UGLY HACK
+        *
+        * The block layer suspend/resume path is fundamentally broken due
+        * to freezable kthreads and workqueue and may deadlock if a block
+        * device gets removed while resume is in progress.  I don't know
+        * what the solution is short of removing freezable kthreads and
+        * workqueues altogether.
+        *
+        * The following is an ugly hack to avoid kicking off device
+        * removal while freezer is active.  This is a joke but does avoid
+        * this particular deadlock scenario.
+        *
+        * https://bugzilla.kernel.org/show_bug.cgi?id=62801
+        * http://marc.info/?l=linux-kernel&m=138695698516487
+        */
+#ifdef CONFIG_FREEZER
+       while (pm_freezing)
+               msleep(10);
+#endif
+
        DPRINTK("ENTER\n");
        mutex_lock(&ap->scsi_scan_mutex);