aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/processor_driver.c
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/acpi/processor_driver.c
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/acpi/processor_driver.c')
-rw-r--r--drivers/acpi/processor_driver.c24
1 files changed, 17 insertions, 7 deletions
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index c5d2fd85dbe0..cbf1f122666b 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -683,8 +683,11 @@ static void acpi_processor_hotplug_notify(acpi_handle handle,
683 struct acpi_device *device = NULL; 683 struct acpi_device *device = NULL;
684 struct acpi_eject_event *ej_event = NULL; 684 struct acpi_eject_event *ej_event = NULL;
685 u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ 685 u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
686 acpi_status status;
686 int result; 687 int result;
687 688
689 acpi_scan_lock_acquire();
690
688 switch (event) { 691 switch (event) {
689 case ACPI_NOTIFY_BUS_CHECK: 692 case ACPI_NOTIFY_BUS_CHECK:
690 case ACPI_NOTIFY_DEVICE_CHECK: 693 case ACPI_NOTIFY_DEVICE_CHECK:
@@ -733,25 +736,32 @@ static void acpi_processor_hotplug_notify(acpi_handle handle,
733 break; 736 break;
734 } 737 }
735 738
739 get_device(&device->dev);
736 ej_event->device = device; 740 ej_event->device = device;
737 ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; 741 ej_event->event = ACPI_NOTIFY_EJECT_REQUEST;
738 acpi_os_hotplug_execute(acpi_bus_hot_remove_device, 742 /* The eject is carried out asynchronously. */
739 (void *)ej_event); 743 status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device,
740 744 ej_event);
741 /* eject is performed asynchronously */ 745 if (ACPI_FAILURE(status)) {
742 return; 746 put_device(&device->dev);
747 kfree(ej_event);
748 break;
749 }
750 goto out;
743 751
744 default: 752 default:
745 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 753 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
746 "Unsupported event [0x%x]\n", event)); 754 "Unsupported event [0x%x]\n", event));
747 755
748 /* non-hotplug event; possibly handled by other handler */ 756 /* non-hotplug event; possibly handled by other handler */
749 return; 757 goto out;
750 } 758 }
751 759
752 /* Inform firmware that the hotplug operation has completed */ 760 /* Inform firmware that the hotplug operation has completed */
753 (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL); 761 (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL);
754 return; 762
763 out:
764 acpi_scan_lock_release();
755} 765}
756 766
757static acpi_status is_processor_device(acpi_handle handle) 767static acpi_status is_processor_device(acpi_handle handle)