mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-09 23:00:21 +00:00
libata-acpi: improve dock event handling
Improve ACPI hotplug handling such that dock event is handled properly. * Register handlers for dock events. * Directly detach device on EJECT_REQUEST instead of signaling hotplug event. This prevents libata from accessing severed controller and/or device. * While at it, use named constants for ACPI events and move uevent signaling inside host lock. Original patch and testing by Holger Macht. Signed-off-by: Tejun Heo <htejun@gmail.com> Cc: Holger Macht <hmacht@suse.de> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
parent
a978b30af3
commit
233f112042
@ -118,45 +118,77 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap)
|
|||||||
ap->pflags |= ATA_PFLAG_INIT_GTM_VALID;
|
ap->pflags |= ATA_PFLAG_INIT_GTM_VALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ata_acpi_handle_hotplug(struct ata_port *ap, struct kobject *kobj,
|
static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev,
|
||||||
u32 event)
|
u32 event)
|
||||||
{
|
{
|
||||||
char event_string[12];
|
char event_string[12];
|
||||||
char *envp[] = { event_string, NULL };
|
char *envp[] = { event_string, NULL };
|
||||||
struct ata_eh_info *ehi = &ap->link.eh_info;
|
struct ata_eh_info *ehi;
|
||||||
|
struct kobject *kobj = NULL;
|
||||||
if (event == 0 || event == 1) {
|
int wait = 0;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (!ap)
|
||||||
|
ap = dev->link->ap;
|
||||||
|
ehi = &ap->link.eh_info;
|
||||||
|
|
||||||
spin_lock_irqsave(ap->lock, flags);
|
spin_lock_irqsave(ap->lock, flags);
|
||||||
ata_ehi_clear_desc(ehi);
|
|
||||||
|
switch (event) {
|
||||||
|
case ACPI_NOTIFY_BUS_CHECK:
|
||||||
|
case ACPI_NOTIFY_DEVICE_CHECK:
|
||||||
ata_ehi_push_desc(ehi, "ACPI event");
|
ata_ehi_push_desc(ehi, "ACPI event");
|
||||||
ata_ehi_hotplugged(ehi);
|
ata_ehi_hotplugged(ehi);
|
||||||
ata_port_freeze(ap);
|
ata_port_freeze(ap);
|
||||||
spin_unlock_irqrestore(ap->lock, flags);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ata_port_schedule_eh(ap);
|
||||||
|
wait = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev) {
|
||||||
|
if (dev->sdev)
|
||||||
|
kobj = &dev->sdev->sdev_gendev.kobj;
|
||||||
|
} else
|
||||||
|
kobj = &ap->dev->kobj;
|
||||||
|
|
||||||
if (kobj) {
|
if (kobj) {
|
||||||
sprintf(event_string, "BAY_EVENT=%d", event);
|
sprintf(event_string, "BAY_EVENT=%d", event);
|
||||||
kobject_uevent_env(kobj, KOBJ_CHANGE, envp);
|
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)
|
static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data)
|
||||||
{
|
{
|
||||||
struct ata_device *dev = data;
|
struct ata_device *dev = data;
|
||||||
struct kobject *kobj = NULL;
|
|
||||||
|
|
||||||
if (dev->sdev)
|
ata_acpi_handle_hotplug(NULL, dev, event);
|
||||||
kobj = &dev->sdev->sdev_gendev.kobj;
|
|
||||||
|
|
||||||
ata_acpi_handle_hotplug(dev->link->ap, kobj, event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data)
|
static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data)
|
||||||
{
|
{
|
||||||
struct ata_port *ap = data;
|
struct ata_port *ap = data;
|
||||||
|
|
||||||
ata_acpi_handle_hotplug(ap, &ap->dev->kobj, event);
|
ata_acpi_handle_hotplug(ap, NULL, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -191,20 +223,30 @@ void ata_acpi_associate(struct ata_host *host)
|
|||||||
else
|
else
|
||||||
ata_acpi_associate_ide_port(ap);
|
ata_acpi_associate_ide_port(ap);
|
||||||
|
|
||||||
if (ap->acpi_handle)
|
if (ap->acpi_handle) {
|
||||||
acpi_install_notify_handler (ap->acpi_handle,
|
acpi_install_notify_handler(ap->acpi_handle,
|
||||||
ACPI_SYSTEM_NOTIFY,
|
ACPI_SYSTEM_NOTIFY,
|
||||||
ata_acpi_ap_notify,
|
ata_acpi_ap_notify, ap);
|
||||||
ap);
|
#if defined(CONFIG_ACPI_DOCK) || defined(CONFIG_ACPI_DOCK_MODULE)
|
||||||
|
/* we might be on a docking station */
|
||||||
|
register_hotplug_dock_device(ap->acpi_handle,
|
||||||
|
ata_acpi_ap_notify, ap);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
for (j = 0; j < ata_link_max_devices(&ap->link); j++) {
|
for (j = 0; j < ata_link_max_devices(&ap->link); j++) {
|
||||||
struct ata_device *dev = &ap->link.device[j];
|
struct ata_device *dev = &ap->link.device[j];
|
||||||
|
|
||||||
if (dev->acpi_handle)
|
if (dev->acpi_handle) {
|
||||||
acpi_install_notify_handler (dev->acpi_handle,
|
acpi_install_notify_handler(dev->acpi_handle,
|
||||||
ACPI_SYSTEM_NOTIFY,
|
ACPI_SYSTEM_NOTIFY,
|
||||||
ata_acpi_dev_notify,
|
ata_acpi_dev_notify, dev);
|
||||||
dev);
|
#if defined(CONFIG_ACPI_DOCK) || defined(CONFIG_ACPI_DOCK_MODULE)
|
||||||
|
/* we might be on a docking station */
|
||||||
|
register_hotplug_dock_device(dev->acpi_handle,
|
||||||
|
ata_acpi_dev_notify, dev);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user