aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/hotplug
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-02-13 08:36:47 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-02-13 08:36:47 -0500
commit3757b94802fb65d8f696597a74053cf21738da0b (patch)
tree78a3a00dbd82fab5e30a46699f83a3e492604e86 /drivers/pci/hotplug
parent64fd7401c5e4cf7c64452ecd9b700a55a5ebea50 (diff)
ACPI / hotplug: Fix concurrency issues and memory leaks
This changeset is aimed at fixing a few different but related problems in the ACPI hotplug infrastructure. First of all, since notify handlers may be run in parallel with acpi_bus_scan(), acpi_bus_trim() and acpi_bus_hot_remove_device() and some of them are installed for ACPI handles that have no struct acpi_device objects attached (i.e. before those objects are created), those notify handlers have to take acpi_scan_lock to prevent races from taking place (e.g. a struct acpi_device is found to be present for the given ACPI handle, but right after that it is removed by acpi_bus_trim() running in parallel to the given notify handler). Moreover, since some of them call acpi_bus_scan() and acpi_bus_trim(), this leads to the conclusion that acpi_scan_lock should be acquired by the callers of these two funtions rather by these functions themselves. For these reasons, make all notify handlers that can handle device addition and eject events take acpi_scan_lock and remove the acpi_scan_lock locking from acpi_bus_scan() and acpi_bus_trim(). Accordingly, update all of their users to make sure that they are always called under acpi_scan_lock. Furthermore, since eject operations are carried out asynchronously with respect to the notify events that trigger them, with the help of acpi_bus_hot_remove_device(), even if notify handlers take the ACPI scan lock, it still is possible that, for example, acpi_bus_trim() will run between acpi_bus_hot_remove_device() and the notify handler that scheduled its execution and that acpi_bus_trim() will remove the device node passed to acpi_bus_hot_remove_device() for ejection. In that case, the struct acpi_device object obtained by acpi_bus_hot_remove_device() will be invalid and not-so-funny things will ensue. To protect agaist that, make the users of acpi_bus_hot_remove_device() run get_device() on ACPI device node objects that are about to be passed to it and make acpi_bus_hot_remove_device() run put_device() on them and check if their ACPI handles are not NULL (make acpi_device_unregister() clear the device nodes' ACPI handles for that check to work). Finally, observe that acpi_os_hotplug_execute() actually can fail, in which case its caller ought to free memory allocated for the context object to prevent leaks from happening. It also needs to run put_device() on the device node that it ran get_device() on previously in that case. Modify the code accordingly. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Yinghai Lu <yinghai@kernel.org>
Diffstat (limited to 'drivers/pci/hotplug')
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c6
-rw-r--r--drivers/pci/hotplug/sgi_hotplug.c5
2 files changed, 10 insertions, 1 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index d1a6f4a25da8..a951c22921d1 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -1218,6 +1218,8 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)
1218 handle = hp_work->handle; 1218 handle = hp_work->handle;
1219 type = hp_work->type; 1219 type = hp_work->type;
1220 1220
1221 acpi_scan_lock_acquire();
1222
1221 if (acpi_bus_get_device(handle, &device)) { 1223 if (acpi_bus_get_device(handle, &device)) {
1222 /* This bridge must have just been physically inserted */ 1224 /* This bridge must have just been physically inserted */
1223 handle_bridge_insertion(handle, type); 1225 handle_bridge_insertion(handle, type);
@@ -1295,6 +1297,7 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)
1295 } 1297 }
1296 1298
1297out: 1299out:
1300 acpi_scan_lock_release();
1298 kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ 1301 kfree(hp_work); /* allocated in handle_hotplug_event_bridge */
1299} 1302}
1300 1303
@@ -1341,6 +1344,8 @@ static void _handle_hotplug_event_func(struct work_struct *work)
1341 1344
1342 func = (struct acpiphp_func *)context; 1345 func = (struct acpiphp_func *)context;
1343 1346
1347 acpi_scan_lock_acquire();
1348
1344 switch (type) { 1349 switch (type) {
1345 case ACPI_NOTIFY_BUS_CHECK: 1350 case ACPI_NOTIFY_BUS_CHECK:
1346 /* bus re-enumerate */ 1351 /* bus re-enumerate */
@@ -1371,6 +1376,7 @@ static void _handle_hotplug_event_func(struct work_struct *work)
1371 break; 1376 break;
1372 } 1377 }
1373 1378
1379 acpi_scan_lock_release();
1374 kfree(hp_work); /* allocated in handle_hotplug_event_func */ 1380 kfree(hp_work); /* allocated in handle_hotplug_event_func */
1375} 1381}
1376 1382
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index ae606b3e991e..574421bc2fa6 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -425,6 +425,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
425 pdevice = NULL; 425 pdevice = NULL;
426 } 426 }
427 427
428 acpi_scan_lock_acquire();
428 /* 429 /*
429 * Walk the rootbus node's immediate children looking for 430 * Walk the rootbus node's immediate children looking for
430 * the slot's device node(s). There can be more than 431 * the slot's device node(s). There can be more than
@@ -458,6 +459,7 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
458 } 459 }
459 } 460 }
460 } 461 }
462 acpi_scan_lock_release();
461 } 463 }
462 464
463 /* Call the driver for the new device */ 465 /* Call the driver for the new device */
@@ -508,6 +510,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
508 /* Get the rootbus node pointer */ 510 /* Get the rootbus node pointer */
509 phandle = PCI_CONTROLLER(slot->pci_bus)->acpi_handle; 511 phandle = PCI_CONTROLLER(slot->pci_bus)->acpi_handle;
510 512
513 acpi_scan_lock_acquire();
511 /* 514 /*
512 * Walk the rootbus node's immediate children looking for 515 * Walk the rootbus node's immediate children looking for
513 * the slot's device node(s). There can be more than 516 * the slot's device node(s). There can be more than
@@ -538,7 +541,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
538 acpi_bus_trim(device); 541 acpi_bus_trim(device);
539 } 542 }
540 } 543 }
541 544 acpi_scan_lock_release();
542 } 545 }
543 546
544 /* Free the SN resources assigned to the Linux device.*/ 547 /* Free the SN resources assigned to the Linux device.*/