libata-acpi: add back ACPI based hotplug functionality
authorAaron Lu <aaron.lu@intel.com>
Thu, 20 Jun 2013 01:38:34 +0000 (09:38 +0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 24 Jun 2013 22:51:33 +0000 (00:51 +0200)
Commit 30dcf76acc69 "libata: migrate ACPI code over to new bindings"
mistakenly dropped the code to register hotplug notificaion handler
for ATA port/devices, causing regression for people using ATA bay,
as kernel bug #59871 shows.

Fix this by adding back the hotplug notification handler registration
code.  Since this code has to be run once and notification needs to
be installed on every ATA port/devices handle no matter if there is
actual device attached, we can't do this in binding time for ATA
device ACPI handle, as the binding only occurs when a SCSI device is
created, i.e. there is device attached.  So introduce the
ata_acpi_hotplug_init() function to loop scan all ATA ACPI handles
and if it is available, install the notificaion handler for it during
ATA init time.

With the ATA ACPI handle binding to SCSI device tree, it is possible
now that when the SCSI hotplug work removes the SCSI device, the ACPI
unbind function will find that the corresponding ACPI device has
already been deleted by dock driver, causing a scaring message like:
[  128.263966] scsi 4:0:0:0: Oops, 'acpi_handle' corrupt
Fix this by waiting for SCSI hotplug task finish in our notificaion
handler, so that the removal of ACPI device done in ACPI unbind
function triggered by the removal of SCSI device is run earlier when
ACPI device is still available.

[rjw: Rebased]
References: https://bugzilla.kernel.org/show_bug.cgi?id=59871
Reported-bisected-and-tested-by: Dirk Griesbach <spamthis@freenet.de>
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Acked-by: Tejun Heo <tj@kernel.org>
Cc: 3.6+ <stable@vger.kernel.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/ata/libata-acpi.c
drivers/ata/libata-core.c
drivers/ata/libata.h

index 87f2f395d79a1f9a9cd3dbc81675f0d71ac6f2cd..cf4e7020adacde5e69881a21adb0c578d31d7a3e 100644 (file)
@@ -156,8 +156,10 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev,
 
        spin_unlock_irqrestore(ap->lock, flags);
 
-       if (wait)
+       if (wait) {
                ata_port_wait_eh(ap);
+               flush_work(&ap->hotplug_task.work);
+       }
 }
 
 static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data)
@@ -214,6 +216,39 @@ static const struct acpi_dock_ops ata_acpi_ap_dock_ops = {
        .uevent = ata_acpi_ap_uevent,
 };
 
+void ata_acpi_hotplug_init(struct ata_host *host)
+{
+       int i;
+
+       for (i = 0; i < host->n_ports; i++) {
+               struct ata_port *ap = host->ports[i];
+               acpi_handle handle;
+               struct ata_device *dev;
+
+               if (!ap)
+                       continue;
+
+               handle = ata_ap_acpi_handle(ap);
+               if (handle) {
+                       /* we might be on a docking station */
+                       register_hotplug_dock_device(handle,
+                                                    &ata_acpi_ap_dock_ops, ap,
+                                                    NULL, NULL);
+               }
+
+               ata_for_each_dev(dev, &ap->link, ALL) {
+                       handle = ata_dev_acpi_handle(dev);
+                       if (!handle)
+                               continue;
+
+                       /* we might be on a docking station */
+                       register_hotplug_dock_device(handle,
+                                                    &ata_acpi_dev_dock_ops,
+                                                    dev, NULL, NULL);
+               }
+       }
+}
+
 /**
  * ata_acpi_dissociate - dissociate ATA host from ACPI objects
  * @host: target ATA host
index f2184276539d885d167c2048a305847b51e58417..adf002a3c584b3d2bd7fb27357df040a381f3b82 100644 (file)
@@ -6148,6 +6148,8 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
        if (rc)
                goto err_tadd;
 
+       ata_acpi_hotplug_init(host);
+
        /* set cable, sata_spd_limit and report */
        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap = host->ports[i];
index c949dd311b2ecd2e6d65e294409d24c4afea6eb7..577d902bc4deaa12d69d1acdc8aa304732163227 100644 (file)
@@ -122,6 +122,7 @@ extern int ata_acpi_register(void);
 extern void ata_acpi_unregister(void);
 extern void ata_acpi_bind(struct ata_device *dev);
 extern void ata_acpi_unbind(struct ata_device *dev);
+extern void ata_acpi_hotplug_init(struct ata_host *host);
 #else
 static inline void ata_acpi_dissociate(struct ata_host *host) { }
 static inline int ata_acpi_on_suspend(struct ata_port *ap) { return 0; }
@@ -134,6 +135,7 @@ static inline int ata_acpi_register(void) { return 0; }
 static inline void ata_acpi_unregister(void) { }
 static inline void ata_acpi_bind(struct ata_device *dev) { }
 static inline void ata_acpi_unbind(struct ata_device *dev) { }
+static inline void ata_acpi_hotplug_init(struct ata_host *host) {}
 #endif
 
 /* libata-scsi.c */