Re: [PATCH 1/5] platform/x86: wmi: Pass event data directly to legacy notify handlers

Ilpo Järvinen
2024-08-27T08:32:53Z

[-- Attachment #1: Type: text/plain, Size: 16595 bytes --]

On Thu, 22 Aug 2024, Armin Wolf wrote:

> The current legacy WMI handlers are susceptible to picking up wrong
> WMI event data on systems where different WMI devices share some
> notification IDs.
>
> Prevent this by letting the WMI driver core taking care of retrieving
> the event data. This also simplifies the legacy WMI handlers and their
> implementation inside the WMI driver core.
>
> Signed-off-by: Armin Wolf <W_Armin@gmx.de>
> ---
> drivers/hwmon/hp-wmi-sensors.c | 17 ++--------
> drivers/platform/x86/acer-wmi.c | 16 +--------
> drivers/platform/x86/asus-wmi.c | 19 ++---------
> drivers/platform/x86/dell/dell-wmi-aio.c | 13 +------
> drivers/platform/x86/hp/hp-wmi.c | 16 +--------
> drivers/platform/x86/huawei-wmi.c | 14 +-------
> drivers/platform/x86/lg-laptop.c | 13 +------
> drivers/platform/x86/msi-wmi.c | 20 ++---------
> drivers/platform/x86/toshiba-wmi.c | 15 +--------
> drivers/platform/x86/wmi.c | 43 ++++++++++--------------
> include/linux/acpi.h | 2 +-
> 11 files changed, 34 insertions(+), 154 deletions(-)
>
> diff --git a/drivers/hwmon/hp-wmi-sensors.c b/drivers/hwmon/hp-wmi-sensors.c
> index b5325d0e72b9..6892518d537c 100644
> --- a/drivers/hwmon/hp-wmi-sensors.c
> +++ b/drivers/hwmon/hp-wmi-sensors.c
> @@ -1597,15 +1597,13 @@ static void hp_wmi_devm_notify_remove(void *ignored)
> }
>
> /* hp_wmi_notify - WMI event notification handler */
> -static void hp_wmi_notify(u32 value, void *context)
> +static void hp_wmi_notify(union acpi_object *wobj, void *context)
> {
> struct hp_wmi_info *temp_info[HP_WMI_MAX_INSTANCES] = {};
> - struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
> struct hp_wmi_sensors *state = context;
> struct device *dev = &state->wdev->dev;
> struct hp_wmi_event event = {};
> struct hp_wmi_info *fan_info;
> - union acpi_object *wobj;
> acpi_status err;
> int event_type;
> u8 count;
> @@ -1632,16 +1630,10 @@ static void hp_wmi_notify(u32 value, void *context)
>
> mutex_lock(&state->lock);
>
> - err = wmi_get_event_data(value, &out);
> - if (ACPI_FAILURE(err))
> - goto out_unlock;
> -
> - wobj = out.pointer;
> -
> err = populate_event_from_wobj(dev, &event, wobj);
> if (err) {
> dev_warn(dev, "Bad event data (ACPI type %d)\n", wobj->type);
> - goto out_free_wobj;
> + goto out_free;
> }
>
> event_type = classify_event(event.name, event.category);
> @@ -1666,13 +1658,10 @@ static void hp_wmi_notify(u32 value, void *context)
> break;
> }
>
> -out_free_wobj:
> - kfree(wobj);
> -
> +out_free:
> devm_kfree(dev, event.name);
> devm_kfree(dev, event.description);

Totally unrelated to your patch, using devm_*() for the members of an
on-stack struct looks very very odd. :-/

Your change looks good and removes lots of code duplication. :-)

Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

--
i.

>
> -out_unlock:
> mutex_unlock(&state->lock);
> }
>
> diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
> index 349169d050c5..7169b84ccdb6 100644
> --- a/drivers/platform/x86/acer-wmi.c
> +++ b/drivers/platform/x86/acer-wmi.c
> @@ -2223,39 +2223,25 @@ static void acer_rfkill_exit(void)
> }
> }
>
> -static void acer_wmi_notify(u32 value, void *context)
> +static void acer_wmi_notify(union acpi_object *obj, void *context)
> {
> - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
> - union acpi_object *obj;
> struct event_return_value return_value;
> - acpi_status status;
> u16 device_state;
> const struct key_entry *key;
> u32 scancode;
>
> - status = wmi_get_event_data(value, &response);
> - if (status != AE_OK) {
> - pr_warn("bad event status 0x%x\n", status);
> - return;
> - }
> -
> - obj = (union acpi_object *)response.pointer;
> -
> if (!obj)
> return;
> if (obj->type != ACPI_TYPE_BUFFER) {
> pr_warn("Unknown response received %d\n", obj->type);
> - kfree(obj);
> return;
> }
> if (obj->buffer.length != 8) {
> pr_warn("Unknown buffer length %d\n", obj->buffer.length);
> - kfree(obj);
> return;
> }
>
> return_value = *((struct event_return_value *)obj->buffer.pointer);
> - kfree(obj);
>
> switch (return_value.function) {
> case WMID_HOTKEY_EVENT:
> diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> index 9c6b3937ac71..1eb6b39df604 100644
> --- a/drivers/platform/x86/asus-wmi.c
> +++ b/drivers/platform/x86/asus-wmi.c
> @@ -4187,28 +4187,15 @@ static void asus_wmi_fnlock_update(struct asus_wmi *asus)
>
> /* WMI events *****************************************************************/
>
> -static int asus_wmi_get_event_code(u32 value)
> +static int asus_wmi_get_event_code(union acpi_object *obj)
> {
> - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
> - union acpi_object *obj;
> - acpi_status status;
> int code;
>
> - status = wmi_get_event_data(value, &response);
> - if (ACPI_FAILURE(status)) {
> - pr_warn("Failed to get WMI notify code: %s\n",
> - acpi_format_exception(status));
> - return -EIO;
> - }
> -
> - obj = (union acpi_object *)response.pointer;
> -
> if (obj && obj->type == ACPI_TYPE_INTEGER)
> code = (int)(obj->integer.value & WMI_EVENT_MASK);
> else
> code = -EIO;
>
> - kfree(obj);
> return code;
> }
>
> @@ -4274,10 +4261,10 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
> pr_info("Unknown key code 0x%x\n", code);
> }
>
> -static void asus_wmi_notify(u32 value, void *context)
> +static void asus_wmi_notify(union acpi_object *obj, void *context)
> {
> struct asus_wmi *asus = context;
> - int code = asus_wmi_get_event_code(value);
> + int code = asus_wmi_get_event_code(obj);
>
> if (code < 0) {
> pr_warn("Failed to get notify code: %d\n", code);
> diff --git a/drivers/platform/x86/dell/dell-wmi-aio.c b/drivers/platform/x86/dell/dell-wmi-aio.c
> index c7b7f1e403fb..54096495719b 100644
> --- a/drivers/platform/x86/dell/dell-wmi-aio.c
> +++ b/drivers/platform/x86/dell/dell-wmi-aio.c
> @@ -70,20 +70,10 @@ static bool dell_wmi_aio_event_check(u8 *buffer, int length)
> return false;
> }
>
> -static void dell_wmi_aio_notify(u32 value, void *context)
> +static void dell_wmi_aio_notify(union acpi_object *obj, void *context)
> {
> - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
> - union acpi_object *obj;
> struct dell_wmi_event *event;
> - acpi_status status;
>
> - status = wmi_get_event_data(value, &response);
> - if (status != AE_OK) {
> - pr_info("bad event status 0x%x\n", status);
> - return;
> - }
> -
> - obj = (union acpi_object *)response.pointer;
> if (obj) {
> unsigned int scancode = 0;
>
> @@ -114,7 +104,6 @@ static void dell_wmi_aio_notify(u32 value, void *context)
> break;
> }
> }
> - kfree(obj);
> }
>
> static int __init dell_wmi_aio_input_setup(void)
> diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
> index 876e0a97cee1..8c05e0dd2a21 100644
> --- a/drivers/platform/x86/hp/hp-wmi.c
> +++ b/drivers/platform/x86/hp/hp-wmi.c
> @@ -834,28 +834,16 @@ static struct attribute *hp_wmi_attrs[] = {
> };
> ATTRIBUTE_GROUPS(hp_wmi);
>
> -static void hp_wmi_notify(u32 value, void *context)
> +static void hp_wmi_notify(union acpi_object *obj, void *context)
> {
> - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
> u32 event_id, event_data;
> - union acpi_object *obj;
> - acpi_status status;
> u32 *location;
> int key_code;
>
> - status = wmi_get_event_data(value, &response);
> - if (status != AE_OK) {
> - pr_info("bad event status 0x%x\n", status);
> - return;
> - }
> -
> - obj = (union acpi_object *)response.pointer;
> -
> if (!obj)
> return;
> if (obj->type != ACPI_TYPE_BUFFER) {
> pr_info("Unknown response received %d\n", obj->type);
> - kfree(obj);
> return;
> }
>
> @@ -872,10 +860,8 @@ static void hp_wmi_notify(u32 value, void *context)
> event_data = *(location + 2);
> } else {
> pr_info("Unknown buffer length %d\n", obj->buffer.length);
> - kfree(obj);
> return;
> }
> - kfree(obj);
>
> switch (event_id) {
> case HPWMI_DOCK_EVENT:
> diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c
> index 09d476dd832e..d81fd5df4a00 100644
> --- a/drivers/platform/x86/huawei-wmi.c
> +++ b/drivers/platform/x86/huawei-wmi.c
> @@ -734,26 +734,14 @@ static void huawei_wmi_process_key(struct input_dev *idev, int code)
> sparse_keymap_report_entry(idev, key, 1, true);
> }
>
> -static void huawei_wmi_input_notify(u32 value, void *context)
> +static void huawei_wmi_input_notify(union acpi_object *obj, void *context)
> {
> struct input_dev *idev = (struct input_dev *)context;
> - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
> - union acpi_object *obj;
> - acpi_status status;
>
> - status = wmi_get_event_data(value, &response);
> - if (ACPI_FAILURE(status)) {
> - dev_err(&idev->dev, "Unable to get event data\n");
> - return;
> - }
> -
> - obj = (union acpi_object *)response.pointer;
> if (obj && obj->type == ACPI_TYPE_INTEGER)
> huawei_wmi_process_key(idev, obj->integer.value);
> else
> dev_err(&idev->dev, "Bad response type\n");
> -
> - kfree(response.pointer);
> }
>
> static int huawei_wmi_input_setup(struct device *dev, const char *guid)
> diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c
> index 9c7857842caf..4d57cf803473 100644
> --- a/drivers/platform/x86/lg-laptop.c
> +++ b/drivers/platform/x86/lg-laptop.c
> @@ -182,21 +182,11 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u
> return (union acpi_object *)buffer.pointer;
> }
>
> -static void wmi_notify(u32 value, void *context)
> +static void wmi_notify(union acpi_object *obj, void *context)
> {
> - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
> - union acpi_object *obj;
> - acpi_status status;
> long data = (long)context;
>
> pr_debug("event guid %li\n", data);
> - status = wmi_get_event_data(value, &response);
> - if (ACPI_FAILURE(status)) {
> - pr_err("Bad event status 0x%x\n", status);
> - return;
> - }
> -
> - obj = (union acpi_object *)response.pointer;
> if (!obj)
> return;
>
> @@ -218,7 +208,6 @@ static void wmi_notify(u32 value, void *context)
>
> pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type,
> obj->integer.value);
> - kfree(response.pointer);
> }
>
> static void wmi_input_setup(void)
> diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c
> index fd318cdfe313..4a7ac85c4db4 100644
> --- a/drivers/platform/x86/msi-wmi.c
> +++ b/drivers/platform/x86/msi-wmi.c
> @@ -170,20 +170,9 @@ static const struct backlight_ops msi_backlight_ops = {
> .update_status = bl_set_status,
> };
>
> -static void msi_wmi_notify(u32 value, void *context)
> +static void msi_wmi_notify(union acpi_object *obj, void *context)
> {
> - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
> struct key_entry *key;
> - union acpi_object *obj;
> - acpi_status status;
> -
> - status = wmi_get_event_data(value, &response);
> - if (status != AE_OK) {
> - pr_info("bad event status 0x%x\n", status);
> - return;
> - }
> -
> - obj = (union acpi_object *)response.pointer;
>
> if (obj && obj->type == ACPI_TYPE_INTEGER) {
> int eventcode = obj->integer.value;
> @@ -192,7 +181,7 @@ static void msi_wmi_notify(u32 value, void *context)
> eventcode);
> if (!key) {
> pr_info("Unknown key pressed - %x\n", eventcode);
> - goto msi_wmi_notify_exit;
> + return;
> }
>
> if (event_wmi->quirk_last_pressed) {
> @@ -204,7 +193,7 @@ static void msi_wmi_notify(u32 value, void *context)
> pr_debug("Suppressed key event 0x%X - "
> "Last press was %lld us ago\n",
> key->code, ktime_to_us(diff));
> - goto msi_wmi_notify_exit;
> + return;
> }
> last_pressed = cur;
> }
> @@ -221,9 +210,6 @@ static void msi_wmi_notify(u32 value, void *context)
> }
> } else
> pr_info("Unknown event received\n");
> -
> -msi_wmi_notify_exit:
> - kfree(response.pointer);
> }
>
> static int __init msi_wmi_backlight_setup(void)
> diff --git a/drivers/platform/x86/toshiba-wmi.c b/drivers/platform/x86/toshiba-wmi.c
> index 77c35529ab6f..12c46455e8dc 100644
> --- a/drivers/platform/x86/toshiba-wmi.c
> +++ b/drivers/platform/x86/toshiba-wmi.c
> @@ -32,26 +32,13 @@ static const struct key_entry toshiba_wmi_keymap[] __initconst = {
> { KE_END, 0 }
> };
>
> -static void toshiba_wmi_notify(u32 value, void *context)
> +static void toshiba_wmi_notify(union acpi_object *obj, void *context)
> {
> - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
> - union acpi_object *obj;
> - acpi_status status;
> -
> - status = wmi_get_event_data(value, &response);
> - if (ACPI_FAILURE(status)) {
> - pr_err("Bad event status 0x%x\n", status);
> - return;
> - }
> -
> - obj = (union acpi_object *)response.pointer;
> if (!obj)
> return;
>
> /* TODO: Add proper checks once we have data */
> pr_debug("Unknown event received, obj type %x\n", obj->type);
> -
> - kfree(response.pointer);
> }
>
> static const struct dmi_system_id toshiba_wmi_dmi_table[] __initconst = {
> diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
> index 1d0b2d6040d1..6ab181dd94ab 100644
> --- a/drivers/platform/x86/wmi.c
> +++ b/drivers/platform/x86/wmi.c
> @@ -1227,40 +1227,33 @@ static int wmi_notify_device(struct device *dev, void *data)
> if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event))
> return 0;
>
> + /* The ACPI WMI specification says that _WED should be
> + * evaluated every time an notification is received, even
> + * if no consumers are present.
> + *
> + * Some firmware implementations actually depend on this
> + * by using a queue for events which will fill up if the
> + * WMI driver core stops evaluating _WED due to missing
> + * WMI event consumers.
> + */
> + ret = wmi_get_notify_data(wblock, &obj);
> + if (ret < 0)
> + return -EIO;
> +
> down_read(&wblock->notify_lock);
> /* The WMI driver notify handler conflicts with the legacy WMI handler.
> * Because of this the WMI driver notify handler takes precedence.
> */
> if (wblock->dev.dev.driver && wblock->driver_ready) {
> - ret = wmi_get_notify_data(wblock, &obj);
> - if (ret >= 0) {
> - wmi_notify_driver(wblock, obj);
> - kfree(obj);
> - }
> + wmi_notify_driver(wblock, obj);
> } else {
> - if (wblock->handler) {
> - wblock->handler(*event, wblock->handler_data);
> - } else {
> - /* The ACPI WMI specification says that _WED should be
> - * evaluated every time an notification is received, even
> - * if no consumers are present.
> - *
> - * Some firmware implementations actually depend on this
> - * by using a queue for events which will fill up if the
> - * WMI driver core stops evaluating _WED due to missing
> - * WMI event consumers.
> - *
> - * Because of this we need this seemingly useless call to
> - * wmi_get_notify_data() which in turn evaluates _WED.
> - */
> - ret = wmi_get_notify_data(wblock, &obj);
> - if (ret >= 0)
> - kfree(obj);
> - }
> -
> + if (wblock->handler)
> + wblock->handler(obj, wblock->handler_data);
> }
> up_read(&wblock->notify_lock);
>
> + kfree(obj);
> +
> acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0);
>
> return -EBUSY;
> diff --git a/include/linux/acpi.h b/include/linux/acpi.h
> index 0687a442fec7..eed105b1fbfb 100644
> --- a/include/linux/acpi.h
> +++ b/include/linux/acpi.h
> @@ -386,7 +386,7 @@ extern bool acpi_is_pnp_device(struct acpi_device *);
>
> #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
>
> -typedef void (*wmi_notify_handler) (u32 value, void *context);
> +typedef void (*wmi_notify_handler) (union acpi_object *data, void *context);
>
> int wmi_instance_count(const char *guid);
>
> --
> 2.39.2
>