aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-06-24 05:22:53 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-06-24 05:22:53 -0400
commit21a31013f774c726bd199526cd673acc6432b21d (patch)
treeba9659d45f91fda6f574b9488c1a273bd6c105c4 /drivers/pci
parentd66ecb7220a70ec3f6c0e38e4af28fb8b25d31c6 (diff)
ACPI / dock / PCI: Synchronous handling of dock events for PCI devices
The interactions between the ACPI dock driver and the ACPI-based PCI hotplug (acpiphp) are currently problematic because of ordering issues during hot-remove operations. First of all, the current ACPI glue code expects that physical devices will always be deleted before deleting the companion ACPI device objects. Otherwise, acpi_unbind_one() will fail with a warning message printed to the kernel log, for example: [ 185.026073] usb usb5: Oops, 'acpi_handle' corrupt [ 185.035150] pci 0000:1b:00.0: Oops, 'acpi_handle' corrupt [ 185.035515] pci 0000:18:02.0: Oops, 'acpi_handle' corrupt [ 180.013656] port1: Oops, 'acpi_handle' corrupt This means, in particular, that struct pci_dev objects have to be deleted before the struct acpi_device objects they are "glued" with. Now, the following happens the during the undocking of an ACPI-based dock station: 1) hotplug_dock_devices() invokes registered hotplug callbacks to destroy physical devices associated with the ACPI device objects depending on the dock station. It calls dd->ops->handler() for each of those device objects. 2) For PCI devices dd->ops->handler() points to handle_hotplug_event_func() that queues up a separate work item to execute _handle_hotplug_event_func() for the given device and returns immediately. That work item will be executed later. 3) hotplug_dock_devices() calls dock_remove_acpi_device() for each device depending on the dock station. This runs acpi_bus_trim() for each of them, which causes the underlying ACPI device object to be destroyed, but the work items queued up by handle_hotplug_event_func() haven't been started yet. 4) _handle_hotplug_event_func() queued up in step 2) are executed and cause the above failure to happen, because the PCI devices they handle do not have the companion ACPI device objects any more (those objects have been deleted in step 3). The possible breakage doesn't end here, though, because hotplug_dock_devices() may return before at least some of the _handle_hotplug_event_func() work items spawned by it have a chance to complete and then undock() will cause _DCK to be evaluated and that will cause the devices handled by the _handle_hotplug_event_func() to go away possibly while they are being accessed. This means that dd->ops->handler() for PCI devices should not point to handle_hotplug_event_func(). Instead, it should point to a function that will do the work of _handle_hotplug_event_func() synchronously. For this reason, introduce such a function, hotplug_event_func(), and modity acpiphp_dock_ops to point to it as the handler. Unfortunately, however, this is not sufficient, because if the dock code were not changed further, hotplug_event_func() would now deadlock with hotplug_dock_devices() that called it, since it would run unregister_hotplug_dock_device() which in turn would attempt to acquire the dock station's hp_lock mutex already acquired by hotplug_dock_devices(). To resolve that deadlock use the observation that unregister_hotplug_dock_device() won't need to acquire hp_lock if PCI bridges the devices on the dock station depend on are prevented from being removed prematurely while the first loop in hotplug_dock_devices() is in progress. To make that possible, introduce a mechanism by which the callers of register_hotplug_dock_device() can provide "init" and "release" routines that will be executed, respectively, during the addition and removal of the physical device object associated with the given ACPI device handle. Make acpiphp use two new functions, acpiphp_dock_init() and acpiphp_dock_release(), that call get_bridge() and put_bridge(), respectively, on the acpiphp bridge holding the given device, for this purpose. In addition to that, remove the dock station's list of "hotplug devices" and make the dock code always walk the whole list of "dependent devices" instead in such a way that the loops in hotplug_dock_devices() and dock_event() (replacing the loops over "hotplug devices") will take references to the list entries that register_hotplug_dock_device() has been called for. That prevents the "release" routines associated with those entries from being called while the given entry is being processed and for PCI devices this means that their bridges won't be removed (by a concurrent thread) while hotplug_event_func() handling them is being executed. This change is based on two earlier patches from Jiang Liu. References: https://bugzilla.kernel.org/show_bug.cgi?id=59501 Reported-and-tested-by: Alexander E. Patrakov <patrakov@gmail.com> Tracked-down-by: Jiang Liu <jiang.liu@huawei.com> Tested-by: Illya Klymov <xanf@xanf.me> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Yinghai Lu <yinghai@kernel.org> Cc: 3.9+ <stable@vger.kernel.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c46
1 files changed, 32 insertions, 14 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 050b6f9abfd1..59df8575a48c 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -61,6 +61,7 @@ static DEFINE_MUTEX(bridge_mutex);
61static void handle_hotplug_event_bridge (acpi_handle, u32, void *); 61static void handle_hotplug_event_bridge (acpi_handle, u32, void *);
62static void acpiphp_sanitize_bus(struct pci_bus *bus); 62static void acpiphp_sanitize_bus(struct pci_bus *bus);
63static void acpiphp_set_hpp_values(struct pci_bus *bus); 63static void acpiphp_set_hpp_values(struct pci_bus *bus);
64static void hotplug_event_func(acpi_handle handle, u32 type, void *context);
64static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context); 65static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context);
65static void free_bridge(struct kref *kref); 66static void free_bridge(struct kref *kref);
66 67
@@ -147,7 +148,7 @@ static int post_dock_fixups(struct notifier_block *nb, unsigned long val,
147 148
148 149
149static const struct acpi_dock_ops acpiphp_dock_ops = { 150static const struct acpi_dock_ops acpiphp_dock_ops = {
150 .handler = handle_hotplug_event_func, 151 .handler = hotplug_event_func,
151}; 152};
152 153
153/* Check whether the PCI device is managed by native PCIe hotplug driver */ 154/* Check whether the PCI device is managed by native PCIe hotplug driver */
@@ -179,6 +180,20 @@ static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
179 return true; 180 return true;
180} 181}
181 182
183static void acpiphp_dock_init(void *data)
184{
185 struct acpiphp_func *func = data;
186
187 get_bridge(func->slot->bridge);
188}
189
190static void acpiphp_dock_release(void *data)
191{
192 struct acpiphp_func *func = data;
193
194 put_bridge(func->slot->bridge);
195}
196
182/* callback routine to register each ACPI PCI slot object */ 197/* callback routine to register each ACPI PCI slot object */
183static acpi_status 198static acpi_status
184register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) 199register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
@@ -298,7 +313,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
298 */ 313 */
299 newfunc->flags &= ~FUNC_HAS_EJ0; 314 newfunc->flags &= ~FUNC_HAS_EJ0;
300 if (register_hotplug_dock_device(handle, 315 if (register_hotplug_dock_device(handle,
301 &acpiphp_dock_ops, newfunc)) 316 &acpiphp_dock_ops, newfunc,
317 acpiphp_dock_init, acpiphp_dock_release))
302 dbg("failed to register dock device\n"); 318 dbg("failed to register dock device\n");
303 319
304 /* we need to be notified when dock events happen 320 /* we need to be notified when dock events happen
@@ -1068,22 +1084,12 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type,
1068 alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_bridge); 1084 alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_bridge);
1069} 1085}
1070 1086
1071static void _handle_hotplug_event_func(struct work_struct *work) 1087static void hotplug_event_func(acpi_handle handle, u32 type, void *context)
1072{ 1088{
1073 struct acpiphp_func *func; 1089 struct acpiphp_func *func = context;
1074 char objname[64]; 1090 char objname[64];
1075 struct acpi_buffer buffer = { .length = sizeof(objname), 1091 struct acpi_buffer buffer = { .length = sizeof(objname),
1076 .pointer = objname }; 1092 .pointer = objname };
1077 struct acpi_hp_work *hp_work;
1078 acpi_handle handle;
1079 u32 type;
1080
1081 hp_work = container_of(work, struct acpi_hp_work, work);
1082 handle = hp_work->handle;
1083 type = hp_work->type;
1084 func = (struct acpiphp_func *)hp_work->context;
1085
1086 acpi_scan_lock_acquire();
1087 1093
1088 acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); 1094 acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
1089 1095
@@ -1116,6 +1122,18 @@ static void _handle_hotplug_event_func(struct work_struct *work)
1116 warn("notify_handler: unknown event type 0x%x for %s\n", type, objname); 1122 warn("notify_handler: unknown event type 0x%x for %s\n", type, objname);
1117 break; 1123 break;
1118 } 1124 }
1125}
1126
1127static void _handle_hotplug_event_func(struct work_struct *work)
1128{
1129 struct acpi_hp_work *hp_work;
1130 struct acpiphp_func *func;
1131
1132 hp_work = container_of(work, struct acpi_hp_work, work);
1133 func = hp_work->context;
1134 acpi_scan_lock_acquire();
1135
1136 hotplug_event_func(hp_work->handle, hp_work->type, func);
1119 1137
1120 acpi_scan_lock_release(); 1138 acpi_scan_lock_release();
1121 kfree(hp_work); /* allocated in handle_hotplug_event_func */ 1139 kfree(hp_work); /* allocated in handle_hotplug_event_func */