Skip to content

Commit 656f096

Browse files
Wer-Wolfij-intel
authored andcommitted
platform/x86: wmi: Rework WCxx/WExx ACPI method handling
The handling of the WExx ACPI methods used for enabling and disabling WMI events has multiple flaws: - the ACPI methods are called even when the WMI device has not been marked as expensive. - WExx ACPI methods might be called for inappropriate WMI devices. - the error code AE_NOT_FOUND is treated as success. The handling of the WCxx ACPI methods used for enabling and disabling WMI data blocks is also flawed: - WMI data blocks are enabled and disabled for every single "query" operation. This is racy and inefficient. Unify the handling of both ACPI methods by introducing a common helper function for enabling and disabling WMI devices. Also enable/disable WMI data blocks during probe/remove and shutdown to match the handling of WMI events. Legacy GUID-based functions still have to enable/disable the WMI device manually and thus still suffer from a potential race condition. Since those functions are deprecated and suffer from various other flaws this issue is purposefully not fixed. Signed-off-by: Armin Wolf <W_Armin@gmx.de> Link: https://lore.kernel.org/r/20250216193251.866125-7-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
1 parent b6b5669 commit 656f096

File tree

1 file changed

+53
-60
lines changed
  • drivers/platform/x86

1 file changed

+53
-60
lines changed

drivers/platform/x86/wmi.c

Lines changed: 53 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -123,24 +123,6 @@ static const void *find_guid_context(struct wmi_block *wblock,
123123
return NULL;
124124
}
125125

126-
static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable)
127-
{
128-
struct guid_block *block;
129-
char method[5];
130-
acpi_status status;
131-
acpi_handle handle;
132-
133-
block = &wblock->gblock;
134-
handle = wblock->acpi_device->handle;
135-
136-
snprintf(method, 5, "WE%02X", block->notify_id);
137-
status = acpi_execute_simple_method(handle, method, enable);
138-
if (status == AE_NOT_FOUND)
139-
return AE_OK;
140-
141-
return status;
142-
}
143-
144126
#define WMI_ACPI_METHOD_NAME_SIZE 5
145127

146128
static inline void get_acpi_method_name(const struct wmi_block *wblock,
@@ -184,6 +166,44 @@ static int wmidev_match_guid(struct device *dev, const void *data)
184166

185167
static const struct bus_type wmi_bus_type;
186168

169+
static const struct device_type wmi_type_event;
170+
171+
static const struct device_type wmi_type_method;
172+
173+
static int wmi_device_enable(struct wmi_device *wdev, bool enable)
174+
{
175+
struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev);
176+
char method[WMI_ACPI_METHOD_NAME_SIZE];
177+
acpi_handle handle;
178+
acpi_status status;
179+
180+
if (!(wblock->gblock.flags & ACPI_WMI_EXPENSIVE))
181+
return 0;
182+
183+
if (wblock->dev.dev.type == &wmi_type_method)
184+
return 0;
185+
186+
if (wblock->dev.dev.type == &wmi_type_event)
187+
snprintf(method, sizeof(method), "WE%02X", wblock->gblock.notify_id);
188+
else
189+
get_acpi_method_name(wblock, 'C', method);
190+
191+
/*
192+
* Not all WMI devices marked as expensive actually implement the
193+
* necessary ACPI method. Ignore this missing ACPI method to match
194+
* the behaviour of the Windows driver.
195+
*/
196+
status = acpi_get_handle(wblock->acpi_device->handle, method, &handle);
197+
if (ACPI_FAILURE(status))
198+
return 0;
199+
200+
status = acpi_execute_simple_method(handle, NULL, enable);
201+
if (ACPI_FAILURE(status))
202+
return -EIO;
203+
204+
return 0;
205+
}
206+
187207
static struct wmi_device *wmi_find_device_by_guid(const char *guid_string)
188208
{
189209
struct device *dev;
@@ -337,10 +357,8 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
337357
{
338358
struct guid_block *block;
339359
acpi_handle handle;
340-
acpi_status status, wc_status = AE_ERROR;
341360
struct acpi_object_list input;
342361
union acpi_object wq_params[1];
343-
char wc_method[WMI_ACPI_METHOD_NAME_SIZE];
344362
char method[WMI_ACPI_METHOD_NAME_SIZE];
345363

346364
if (!out)
@@ -364,40 +382,9 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
364382
if (instance == 0 && test_bit(WMI_READ_TAKES_NO_ARGS, &wblock->flags))
365383
input.count = 0;
366384

367-
/*
368-
* If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
369-
* enable collection.
370-
*/
371-
if (block->flags & ACPI_WMI_EXPENSIVE) {
372-
get_acpi_method_name(wblock, 'C', wc_method);
373-
374-
/*
375-
* Some GUIDs break the specification by declaring themselves
376-
* expensive, but have no corresponding WCxx method. So we
377-
* should not fail if this happens.
378-
*/
379-
wc_status = acpi_execute_simple_method(handle, wc_method, 1);
380-
}
381-
382385
get_acpi_method_name(wblock, 'Q', method);
383-
status = acpi_evaluate_object(handle, method, &input, out);
384386

385-
/*
386-
* If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
387-
* the WQxx method failed - we should disable collection anyway.
388-
*/
389-
if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
390-
/*
391-
* Ignore whether this WCxx call succeeds or not since
392-
* the previously executed WQxx method call might have
393-
* succeeded, and returning the failing status code
394-
* of this call would throw away the result of the WQxx
395-
* call, potentially leaking memory.
396-
*/
397-
acpi_execute_simple_method(handle, wc_method, 0);
398-
}
399-
400-
return status;
387+
return acpi_evaluate_object(handle, method, &input, out);
401388
}
402389

403390
/**
@@ -421,9 +408,15 @@ acpi_status wmi_query_block(const char *guid_string, u8 instance,
421408
if (IS_ERR(wdev))
422409
return AE_ERROR;
423410

411+
if (wmi_device_enable(wdev, true) < 0)
412+
dev_warn(&wdev->dev, "Failed to enable device\n");
413+
424414
wblock = container_of(wdev, struct wmi_block, dev);
425415
status = __query_block(wblock, instance, out);
426416

417+
if (wmi_device_enable(wdev, false) < 0)
418+
dev_warn(&wdev->dev, "Failed to disable device\n");
419+
427420
wmi_device_put(wdev);
428421

429422
return status;
@@ -551,7 +544,7 @@ acpi_status wmi_install_notify_handler(const char *guid,
551544
wblock->handler = handler;
552545
wblock->handler_data = data;
553546

554-
if (ACPI_FAILURE(wmi_method_enable(wblock, true)))
547+
if (wmi_device_enable(wdev, true) < 0)
555548
dev_warn(&wblock->dev.dev, "Failed to enable device\n");
556549

557550
status = AE_OK;
@@ -588,7 +581,7 @@ acpi_status wmi_remove_notify_handler(const char *guid)
588581
if (!wblock->handler) {
589582
status = AE_NULL_ENTRY;
590583
} else {
591-
if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
584+
if (wmi_device_enable(wdev, false) < 0)
592585
dev_warn(&wblock->dev.dev, "Failed to disable device\n");
593586

594587
wblock->handler = NULL;
@@ -823,10 +816,10 @@ static int wmi_dev_match(struct device *dev, const struct device_driver *driver)
823816

824817
static void wmi_dev_disable(void *data)
825818
{
826-
struct wmi_block *wblock = data;
819+
struct device *dev = data;
827820

828-
if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
829-
dev_warn(&wblock->dev.dev, "Failed to disable device\n");
821+
if (wmi_device_enable(to_wmi_device(dev), false) < 0)
822+
dev_warn(dev, "Failed to disable device\n");
830823
}
831824

832825
static int wmi_dev_probe(struct device *dev)
@@ -852,14 +845,14 @@ static int wmi_dev_probe(struct device *dev)
852845
return -ENODEV;
853846
}
854847

855-
if (ACPI_FAILURE(wmi_method_enable(wblock, true)))
848+
if (wmi_device_enable(to_wmi_device(dev), true) < 0)
856849
dev_warn(dev, "failed to enable device -- probing anyway\n");
857850

858851
/*
859852
* We have to make sure that all devres-managed resources are released first because
860853
* some might still want to access the underlying WMI device.
861854
*/
862-
ret = devm_add_action_or_reset(dev, wmi_dev_disable, wblock);
855+
ret = devm_add_action_or_reset(dev, wmi_dev_disable, dev);
863856
if (ret < 0)
864857
return ret;
865858

@@ -915,7 +908,7 @@ static void wmi_dev_shutdown(struct device *dev)
915908
* We still need to disable the WMI device here since devres-managed resources
916909
* like wmi_dev_disable() will not be release during shutdown.
917910
*/
918-
if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
911+
if (wmi_device_enable(to_wmi_device(dev), false) < 0)
919912
dev_warn(dev, "Failed to disable device\n");
920913
}
921914
}

0 commit comments

Comments
 (0)