platform/x86: asus-wmi: Serialize WMI method evaluations and clean up rfkill locks - #12
platform/x86: asus-wmi: Serialize WMI method evaluations and clean up rfkill locks#12scardracs wants to merge 2 commits into
Conversation
|
Don't link as closes like this. merging this PR should not close that issue, until the patchset makes it to mainline |
| static int asus_wmi_evaluate_method3(u32 method_id, | ||
| u32 arg0, u32 arg1, u32 arg2, u32 *retval) | ||
| /* Serializes all evaluations of ASUS_WMI_MGMT_GUID methods */ | ||
| static DEFINE_MUTEX(asus_wmi_eval_lock); |
There was a problem hiding this comment.
Why is this mutex static? We have drvdata in asus-wmi
There was a problem hiding this comment.
AFAIK asus_wmi_evaluate_method() is exported and used by standalone modules like hid-asus.ko and asus-armoury.ko, which don't have access to drvdata. Also, ASUS_WMI_MGMT_GUID is a global firmware interface, so we need one global mutex to serialize access across modules.
| .arg2 = arg2, | ||
| }; | ||
| struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; | ||
| struct acpi_buffer input = { (acpi_size) sizeof(*args), args }; |
There was a problem hiding this comment.
In asus_wmi_evaluate_method_raw(), args is passed in as a pointer (struct bios_args *args) rather than a local stack struct
| return 0; | ||
| } | ||
|
|
||
| int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) |
There was a problem hiding this comment.
Changes to rfkill mixed with asus_wmi_evaluate_method. Not good. One patch should do as little as possible.
There was a problem hiding this comment.
This goes for both comments: do you prefer a 4th patch or to remove it completely?
EDIT: I made it a 4th commit
| return -EIO; | ||
|
|
||
| *obj_ret = output.pointer; | ||
| return 0; |
There was a problem hiding this comment.
This thing returns either 0 or an error. It could be worth to return the ERR_PTR(err) or output.pointer, getting rid of the *obj altogether.
In his own patch where you convert all usages.
There was a problem hiding this comment.
This one where tricky for me to answer so I asked gemini directly.
Returning ERR_PTR is an option, but with __free(kfree) used for RAII cleanup on obj, kfree() will crash if it receives an ERR_PTR on error paths (while it safely no-ops on NULL). Keeping the int return with *obj_ret = NULL on error keeps __free(kfree) leak-proof without needing custom error-pointer cleanup macros. Would you still prefer an ERR_PTR approach with a custom free handler?
| guard(mutex)(&asus_wmi_eval_lock); | ||
|
|
||
| kfree(obj); | ||
| pr_debug("%s called (0x%08x) with args: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", |
There was a problem hiding this comment.
Keeping it inside the lock ensures the debug log reflects the exact serialization order entering the BIOS. Do you see any specific issue with having pr_debug here?
| .arg3 = arg3, | ||
| .arg4 = arg4, | ||
| }; | ||
| struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; |
There was a problem hiding this comment.
Same as above: big changes together with rfkill.
| return -ENODEV; | ||
| } | ||
|
|
||
| return 0; |
There was a problem hiding this comment.
same as before: ERR_PTR() while you are at it. This thing, as the last one returns either 0 or an error, but (maybe) changes a pointer: let it return the pointer/pointer error.
| union acpi_object *obj __free(kfree) = NULL; | ||
| int err; | ||
|
|
||
| if (ACPI_FAILURE(status)) |
There was a problem hiding this comment.
to me it doesn't look like ACPI_FAILURE is contained within asus_wmi_evaluate_method_raw (?)
There was a problem hiding this comment.
It is handled right inside asus_wmi_evaluate_method_raw():
status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id, &input, &output);
if (ACPI_FAILURE(status))
return -EIO;
So any ACPI evaluation failure is caught immediately and returns -EIO.
| struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| acpi_status status; | ||
| union acpi_object *obj; | ||
| union acpi_object *obj __free(kfree) = NULL; |
There was a problem hiding this comment.
Not very good to see, the alternative is not that good either (result of the reworked method) because obj could end up storing an error. Maybe a macro?
There was a problem hiding this comment.
For this one I asked gemini too because I did not have a good enough answer
Checking if (obj && obj->type == ACPI_TYPE_INTEGER) is standard ACPICA idiom across drivers/platform/x86/. Since _raw returns a negative error on failure and guarantees *obj_ret = NULL, obj never contains an error pointer. Would a custom macro be preferred here over the standard ACPI type check?
| } | ||
|
|
||
| if (retval != 0) { | ||
| if (retval > 1) { |
There was a problem hiding this comment.
Accepting a failure, reported properly as a failure, with a success is not acceptable.
The warning is correct here, at most you can specialize the error handling to print a custom message and/or use pr_warn_once.
Same goes for the above one.
Regardless: multi subsystem patches will follow time of both. Be ready to wait a long time.
There was a problem hiding this comment.
On ASUS ACPI WMI, calling DEVS with ASUS_WMI_METHODID_NOTIF returns 1 for active/toggled state and 0 for disabled/off state. Both 0 and 1 are valid success states returned by the BIOS. Treating 1 as a failure (retval != 0) can cause spurious Failed to notify asus-wmi (retval): 0x1 warnings when pressing the fan/Aura key on ROG/TUF laptops. Only values > 1 (like ASUS_WMI_UNSUPPORTED_METHOD) are actual failures. Does that make sense, or did you have a different behavior in mind?
| __func__, method_id, arg0, -EIO); | ||
| return -EIO; | ||
| __func__, method_id, arg0, err); | ||
| return err; |
There was a problem hiding this comment.
You are changing the logic here. Either you change the logic or you refactor: not both in the same patch.
There was a problem hiding this comment.
asus_wmi_evaluate_method_raw() returns -EIO on ACPI_FAILURE, so err evaluates to -EIO exactly as before. The logic is identical, it just avoids hardcoding -EIO in multiple places. Would you prefer keeping the explicit -EIO literal in the debug print instead?
| return 0; | ||
| } | ||
|
|
||
| static int asus_wmi_evaluate_method3(u32 method_id, |
There was a problem hiding this comment.
Because asus_wmi_evaluate_method3() now calls asus_wmi_evaluate_method5() (passing 0 for the unused args) to eliminate duplicate WMI call boilerplate. In C, it has to be defined below asus_wmi_evaluate_method5() to avoid needing a forward declaration.
Good point, thank. I'll change it to |
55216ad to
4431a5d
Compare
|
@NeroReflex I've made the rebase and now it is in line with OGC's master |
Very well. Now what's missing? Changing commits? |
4431a5d to
a19e8a5
Compare
af0e02d to
743e77c
Compare
Concurrent evaluations of ASUS WMI management methods (from ACPI notify, HID, userspace daemons, and debugfs) enter the BIOS ACPI/SMM interface simultaneously, triggering re-entrant SMIs or EC mailbox buffer corruption. Fix this at the root by introducing a centralized evaluation helper (asus_wmi_evaluate_method_locked()) protected by a global mutex (asus_wmi_eval_lock) using guard(mutex). Route all evaluations of ASUS_WMI_MGMT_GUID (method3, method5, method_buf, and show_call) through this helper. A static mutex is required because asus_wmi_evaluate_method() is an exported symbol used by external modules (such as hid-asus and asus-armoury) that lack access to struct asus_wmi drvdata, and the underlying ASUS ACPI/EC management method is a single physical platform resource. Link: OpenGamingCollective/asusctl#328 Cc: stable@vger.kernel.org Signed-off-by: Marco Scardovi <scardracs@disroot.org>
…icate rfkill ops With all WMI method evaluations serialized globally by asus_wmi_eval_lock in asus_wmi_evaluate_method_locked(), the per-device wmi_lock in struct asus_wmi is completely redundant. Remove wmi_lock from struct asus_wmi, its initialization in asus_wmi_rfkill_init(), and its manual locking in asus_rfkill_hotplug(). Consequently, asus_rfkill_wlan_set() becomes a simple pass-through to asus_rfkill_set(), rendering asus_rfkill_wlan_ops identical to asus_rfkill_ops. Drop asus_rfkill_wlan_set() and asus_rfkill_wlan_ops, allocating WLAN rfkill devices with &asus_rfkill_ops directly. Signed-off-by: Marco Scardovi <scardracs@disroot.org>
a19e8a5 to
2a4ae2d
Compare
Summary of Changes
This series addresses firmware re-entrancy issues and EC mailbox buffer corruption caused by concurrent evaluations of ASUS WMI management methods, guarantees mutual exclusion across all callers (including external modules), and cleans up legacy redundant locking primitives in
asus-wmi.Following maintainer review feedback, this series has been streamlined:
drivers/platform/x86/asus-wmi.c(the HID patch has been decoupled to avoid cross-subsystem dependencies).asus_wmi_evaluate_method_locked()helper withguard(mutex).Commits in this Series
platform/x86: asus-wmi: Serialize WMI method evaluations with a mutex
asus_wmi_evaluate_method_locked()) protected by a global mutex (asus_wmi_eval_lock) usingguard(mutex).ASUS_WMI_MGMT_GUID(method3,method5,method_buf, andshow_call) through this helper.asus_wmi_evaluate_method()is an exported symbol invoked by external modules (e.g.hid-asus,asus-armoury) without access tostruct asus_wmi *drvdata, and the underlying ACPI/EC management method is a single shared hardware interface.platform/x86: asus-wmi: Remove redundant per-device wmi_lock and duplicate rfkill ops
asus_wmi_eval_lock, the per-devicewmi_lockinstruct asus_wmiis completely redundant.wmi_lockfromstruct asus_wmi,asus_rfkill_hotplug(), andasus_wmi_rfkill_init().asus_rfkill_wlan_set()andasus_rfkill_wlan_ops, allocating WLAN rfkill devices with&asus_rfkill_opsdirectly.