summaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86
diff options
context:
space:
mode:
authorDarren Hart (VMware) <dvhart@infradead.org>2017-06-06 10:13:49 -0700
committerDarren Hart (VMware) <dvhart@infradead.org>2017-06-06 10:15:19 -0700
commit6ee50aaa9a20c7c11c964c249440857ab58ece36 (patch)
tree95b2fe7faedc95aaa75a2971edabff4397402d28 /drivers/platform/x86
parentd4fc91adfde11c41295d1cf001bdbec5d6879016 (diff)
downloadlinux-stable-6ee50aaa9a20c7c11c964c249440857ab58ece36.tar.gz
linux-stable-6ee50aaa9a20c7c11c964c249440857ab58ece36.tar.bz2
linux-stable-6ee50aaa9a20c7c11c964c249440857ab58ece36.zip
platform/x86: wmi: Instantiate all devices before adding them
At some point, we will want sub-drivers to get references to other devices on the same WMI bus. This change is needed to avoid races. This ends up simplifying the setup code and fixing some leaks, too. This is based on the original work of Andy Lutomirski <luto@kernel.org>, but includes several modifications, many in response to review from Michał Kępień <kernel@kempniu.pl>: https://www.spinics.net/lists/platform-driver-x86/msg08201.html Signed-off-by: Darren Hart (VMware) <dvhart@infradead.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Mario Limonciello <mario_limonciello@dell.com> Cc: Pali Rohár <pali.rohar@gmail.com> Cc: linux-kernel@vger.kernel.org Cc: platform-driver-x86@vger.kernel.org Cc: linux-acpi@vger.kernel.org Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/platform/x86')
-rw-r--r--drivers/platform/x86/wmi.c49
1 files changed, 29 insertions, 20 deletions
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 651693a5e0ea..fbce8765e222 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -796,7 +796,7 @@ static struct device_type wmi_type_data = {
.release = wmi_dev_release,
};
-static int wmi_create_device(struct device *wmi_bus_dev,
+static void wmi_create_device(struct device *wmi_bus_dev,
const struct guid_block *gblock,
struct wmi_block *wblock,
struct acpi_device *device)
@@ -852,7 +852,7 @@ static int wmi_create_device(struct device *wmi_bus_dev,
}
- return device_register(&wblock->dev.dev);
+ device_initialize(&wblock->dev.dev);
}
static void wmi_free_devices(struct acpi_device *device)
@@ -863,10 +863,7 @@ static void wmi_free_devices(struct acpi_device *device)
list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
if (wblock->acpi_device == device) {
list_del(&wblock->list);
- if (wblock->dev.dev.bus)
- device_unregister(&wblock->dev.dev);
- else
- kfree(wblock);
+ device_unregister(&wblock->dev.dev);
}
}
}
@@ -899,11 +896,11 @@ static bool guid_already_parsed(struct acpi_device *device,
static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
{
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
- union acpi_object *obj;
const struct guid_block *gblock;
- struct wmi_block *wblock;
+ struct wmi_block *wblock, *next;
+ union acpi_object *obj;
acpi_status status;
- int retval;
+ int retval = 0;
u32 i, total;
status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out);
@@ -936,19 +933,15 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
continue;
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
- if (!wblock)
- return -ENOMEM;
+ if (!wblock) {
+ retval = -ENOMEM;
+ break;
+ }
wblock->acpi_device = device;
wblock->gblock = gblock[i];
- retval = wmi_create_device(wmi_bus_dev, &gblock[i],
- wblock, device);
- if (retval) {
- put_device(&wblock->dev.dev);
- wmi_free_devices(device);
- goto out_free_pointer;
- }
+ wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device);
list_add_tail(&wblock->list, &wmi_block_list);
@@ -958,11 +951,27 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
}
}
- retval = 0;
+ /*
+ * Now that all of the devices are created, add them to the
+ * device tree and probe subdrivers.
+ */
+ list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
+ if (wblock->acpi_device != device)
+ continue;
+
+ retval = device_add(&wblock->dev.dev);
+ if (retval) {
+ dev_err(wmi_bus_dev, "failed to register %pULL\n",
+ wblock->gblock.guid);
+ if (debug_event)
+ wmi_method_enable(wblock, 0);
+ list_del(&wblock->list);
+ put_device(&wblock->dev.dev);
+ }
+ }
out_free_pointer:
kfree(out.pointer);
-
return retval;
}