Fixups to ATA ACPI hotplug
authorMatthew Garrett <mjg59@srcf.ucam.org>
Mon, 19 May 2008 16:29:34 +0000 (17:29 +0100)
committerJeff Garzik <jgarzik@redhat.com>
Mon, 19 May 2008 21:55:18 +0000 (17:55 -0400)
The libata-acpi.c code currently accepts hotplug messages from both the
port and the device. This does not match the behaviour of the bay
driver, and may result in confusion when two hotplug requests are
received for the same device. This patch limits the hotplug notification
to removable ACPI devices, which in turn allows it to use the _STA
method to determine whether the device has been removed or inserted.
On removal, devices are marked as detached. On insertion, a hotplug scan
is started. This should avoid lockups caused by the ata layer attempting
to scan devices which have been removed. The uevent sending is moved
outside the spinlock in order to avoid a warning generated by it firing
when interrupts are disabled.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/ata/libata-acpi.c

index 70b77e0899a84848d5da89b736505aaee977f2b2..865a552c91e6e0464d14c88067f54302cdbfade7 100644 (file)
@@ -118,8 +118,8 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap)
                ap->pflags |= ATA_PFLAG_INIT_GTM_VALID;
 }
 
-static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev,
-                                   u32 event)
+static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device 
+                                   *dev, u32 event)
 {
        char event_string[12];
        char *envp[] = { event_string, NULL };
@@ -127,39 +127,67 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev,
        struct kobject *kobj = NULL;
        int wait = 0;
        unsigned long flags;
-
+       acpi_handle handle, tmphandle;
+       unsigned long sta;
+       acpi_status status;
+       
        if (!ap)
                ap = dev->link->ap;
        ehi = &ap->link.eh_info;
 
        spin_lock_irqsave(ap->lock, flags);
 
+       if (dev)
+               handle = dev->acpi_handle;
+       else
+               handle = ap->acpi_handle;
+
+       status = acpi_get_handle(handle, "_EJ0", &tmphandle);
+       if (ACPI_FAILURE(status)) {
+               /* This device is not ejectable */
+               spin_unlock_irqrestore(ap->lock, flags);
+               return;
+       }
+
+       status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+       if (ACPI_FAILURE(status)) {
+               printk ("Unable to determine bay status\n");
+               spin_unlock_irqrestore(ap->lock, flags);
+               return;
+       }
+
        switch (event) {
        case ACPI_NOTIFY_BUS_CHECK:
        case ACPI_NOTIFY_DEVICE_CHECK:
                ata_ehi_push_desc(ehi, "ACPI event");
-               ata_ehi_hotplugged(ehi);
-               ata_port_freeze(ap);
-               break;
-
-       case ACPI_NOTIFY_EJECT_REQUEST:
-               ata_ehi_push_desc(ehi, "ACPI event");
-               if (dev)
-                       dev->flags |= ATA_DFLAG_DETACH;
-               else {
-                       struct ata_link *tlink;
-                       struct ata_device *tdev;
-
-                       ata_port_for_each_link(tlink, ap)
-                               ata_link_for_each_dev(tdev, tlink)
-                                       tdev->flags |= ATA_DFLAG_DETACH;
+               if (!sta) {
+                /* Device has been unplugged */
+                       if (dev)
+                               dev->flags |= ATA_DFLAG_DETACH;
+                       else {
+                               struct ata_link *tlink;
+                               struct ata_device *tdev;
+                               
+                               ata_port_for_each_link(tlink, ap) {
+                                       ata_link_for_each_dev(tdev, tlink) {
+                                               tdev->flags |= 
+                                                       ATA_DFLAG_DETACH;
+                                       }
+                               }
+                       }
+                       ata_port_schedule_eh(ap);
+                       wait = 1;
+               } else {
+                       ata_ehi_hotplugged(ehi);
+                       ata_port_freeze(ap);
                }
-
-               ata_port_schedule_eh(ap);
-               wait = 1;
-               break;
        }
 
+       spin_unlock_irqrestore(ap->lock, flags);
+
+       if (wait)
+               ata_port_wait_eh(ap);
+
        if (dev) {
                if (dev->sdev)
                        kobj = &dev->sdev->sdev_gendev.kobj;
@@ -170,11 +198,6 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev,
                sprintf(event_string, "BAY_EVENT=%d", event);
                kobject_uevent_env(kobj, KOBJ_CHANGE, envp);
        }
-
-       spin_unlock_irqrestore(ap->lock, flags);
-
-       if (wait)
-               ata_port_wait_eh(ap);
 }
 
 static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data)