Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH] Input: charlieplex_keypad - check gpiod_direction_output() return value
From: sashiko-bot @ 2026-07-23  2:38 UTC (permalink / raw)
  To: kr494167; +Cc: linux-input, dmitry.torokhov
In-Reply-To: <20260723022943.9337-1-kr494167@gmail.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] The return value of `gpiod_direction_input()` is ignored, which can leave the line configured as an output driving HIGH upon hardware failure, breaking the matrix scanning logic.
--

--- Patch [1]: [PATCH] Input: charlieplex_keypad - check gpiod_direction_output() return value ---
commit dcc381a1c321f8eedf04b92519f6edd1db4050eb
Author: Surendra Singh Chouhan <kr494167@gmail.com>

Input: charlieplex_keypad - check gpiod_direction_output() return value

This commit updates charlieplex_keypad_scan_line() to capture and return
the error code if setting the active output line fails. This prevents
the keypad from continuing to sleep and read inputs from an improperly
configured GPIO.

> diff --git a/drivers/input/keyboard/charlieplex_keypad.c b/drivers/input/keyboard/charlieplex_keypad.c
> --- a/drivers/input/keyboard/charlieplex_keypad.c
> +++ b/drivers/input/keyboard/charlieplex_keypad.c
> @@ -77,7 +77,9 @@ static int charlieplex_keypad_scan_line(struct charlieplex_keypad *keypad,
>  	int err;
>  
>  	/* Activate only one line as output at a time. */
> -	gpiod_direction_output(line_gpios->desc[oline], 1);
> +	err = gpiod_direction_output(line_gpios->desc[oline], 1);
> +	if (err)
> +		return err;

[Severity: High]
This is a pre-existing issue, but since this patch explicitly adds error
checking for gpiod_direction_output(), should we also check the return value
when restoring the line to an input state?

A few lines below in charlieplex_keypad_scan_line(), the same pattern exists
when restoring the line direction:

	err = gpiod_get_array_value_cansleep(line_gpios->ndescs, ...);

	gpiod_direction_input(line_gpios->desc[oline]);

	if (err)
		return err;

If gpiod_direction_input() fails here due to a temporary hardware or bus error
(like on an i2c or spi GPIO expander), does this leave the line configured
as an output driving high? 

On subsequent scans, this could cause multiple lines to act as outputs
simultaneously and break the matrix scanning logic entirely.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260723022943.9337-1-kr494167@gmail.com?part=1

^ permalink raw reply

* [PATCH] Input: charlieplex_keypad - check gpiod_direction_output() return value
From: kr494167 @ 2026-07-23  2:29 UTC (permalink / raw)
  To: hvilleneuve, dmitry.torokhov
  Cc: andriy.shevchenko, linux-input, linux-kernel,
	Surendra Singh Chouhan

From: Surendra Singh Chouhan <kr494167@gmail.com>

charlieplex_keypad_scan_line() currently ignores the return value of
gpiod_direction_output() when setting the active output line for scanning.

If setting the GPIO direction fails (e.g. on I2C/SPI GPIO expanders or
hardware errors), the function continues to sleep and read input values
from an improperly configured GPIO line.

Fix this by capturing the return value of gpiod_direction_output() and
returning the error code immediately if it fails.

Fixes: 2ca45e57ea02 ("Input: charlieplex_keypad - add GPIO charlieplex keypad")
Signed-off-by: Surendra Singh Chouhan <kr494167@gmail.com>
---
 drivers/input/keyboard/charlieplex_keypad.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/input/keyboard/charlieplex_keypad.c b/drivers/input/keyboard/charlieplex_keypad.c
index d222b622c820..d46a2298e6da 100644
--- a/drivers/input/keyboard/charlieplex_keypad.c
+++ b/drivers/input/keyboard/charlieplex_keypad.c
@@ -77,7 +77,9 @@ static int charlieplex_keypad_scan_line(struct charlieplex_keypad *keypad,
 	int err;
 
 	/* Activate only one line as output at a time. */
-	gpiod_direction_output(line_gpios->desc[oline], 1);
+	err = gpiod_direction_output(line_gpios->desc[oline], 1);
+	if (err)
+		return err;
 
 	if (keypad->settling_time_us)
 		fsleep(keypad->settling_time_us);
-- 
2.55.0


^ permalink raw reply related

* Re: [PATCH bpf-next v1 7/8] resolve_btfids: Discover kfuncs from BTF ID sets
From: Ihor Solodrai @ 2026-07-23  0:57 UTC (permalink / raw)
  To: bot+bpf-ci, ast, andrii, daniel, eddyz87, memxor
  Cc: bentiss, jikos, emil, jolsa, bpf, linux-input, kernel-team,
	martin.lau, yonghong.song, clm
In-Reply-To: <d3fcadd81b59d526182928e15d0d4df59af70c4a4cbe8779d83043d1a1866632@mail.kernel.org>

On 7/22/26 5:32 PM, bot+bpf-ci@kernel.org wrote:
>> collect_kfuncs() currently uses bpf_kfunc decl tags to identify the
>> list of kfuncs. The decl tags are generated by pahole, which makes
>> current implementation implicitly rely on those tags being generated.
>>
>> The authoritative source, used by the the BPF verifier for kfunc
>> registration, of functions being BPF kfuncs are
>> BTF_KFUNCS_START()/END() declarations. These are BTF_ID_SET8 under the
>> hood. Currently resolve_btfids reads kfunc flags from these sets, and
>> populates them with BTF IDs.
>>
>> Implement kfunc discovery from BTF_ID_SET8 symbols in resolve_btfids,
>> removing the dependency on pahole's emmission of decl tags.
>>
>> Walk BTF_ID_KIND_SET8 sets, and use the address-to-symbol index to
>> look up set entry's BTF_ID symbol name (before .BTF_ids is patched),
>> recording the paired flags directly. This makes find_kfunc_flags()
>> helper unnecessary, so it's removed.
>>
>> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> 
> This isn't a bug, but there are a couple of small typos in the
> changelog: "used by the the BPF verifier" has a duplicated "the", and
> "pahole's emmission of decl tags" should be "emission".

I guess typos don't count as proof-of-human-typing, because AI can be
instructed to make typos :) Will fix.

> 
> 
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
> 
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/29967476366


^ permalink raw reply

* Re: [PATCH bpf-next v1 6/8] HID: bpf: Make syscall kfunc flags match the struct_ops set
From: Ihor Solodrai @ 2026-07-23  0:52 UTC (permalink / raw)
  To: sashiko-reviews
  Cc: bpf, dmitry.torokhov, linux-input, Benjamin Tissoires,
	Jiri Kosina
In-Reply-To: <20260722234938.DF9D41F000E9@smtp.kernel.org>

On 7/22/26 4:49 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> 
> Pre-existing issues:
> - [High] TOCTOU NULL pointer dereference in `dispatch_hid_bpf_device_event` during dynamic HID-BPF attachment.
> --
> 
> --- Patch [6]: [PATCH bpf-next v1 6/8] HID: bpf: Make syscall kfunc flags match the struct_ops set ---
> Note: The format of this review is altered to provide a free-form summary
> due to text recitation restrictions. No direct patch diff quoting is used.
> 
> commit 460380780bad5817b193c7927dc05e04ce9f5480
> Author: Ihor Solodrai <ihor.solodrai@linux.dev>
> Subject: HID: bpf: Make syscall kfunc flags match the struct_ops set
> 
> This commit updates the kfunc flags for hid_bpf_syscall_kfunc_ids to match
> the hid_bpf_kfunc_ids set by adding the KF_SLEEPABLE flag. This ensures
> per-kfunc flag consistency across BTF ID sets at build time.
> 
> [Severity: High]
> This isn't a regression introduced by this patch, but while reviewing the

Not relevant to the change.

However HID maintainers may be interested, adding them to cc.


> HID-BPF event dispatching code, a potential time-of-check to time-of-use
> issue was noticed in drivers/hid/bpf/hid_bpf_dispatch.c.
> 
> Could this sequence lead to a null pointer dereference when a HID-BPF
> program is concurrently attached while a device is flooded with input
> reports?
> 
> In dispatch_hid_bpf_device_event(), the first lockless read of the device
> data happens during context initialization:
> 
>     .data = hdev->bpf.device_data,
> 
> Shortly after, a second lockless read is used as a guard:
> 
>     if (!hdev->bpf.device_data)
>         return data;
> 
> If __hid_bpf_allocate_data() is concurrently executed during a program
> attachment, it updates these pointers without memory barriers or taking the
> driver_input_lock:
> 
>     *data = alloc_data;
>     *size = alloc_size;
> 
> If the allocation completes exactly between the two reads in
> dispatch_hid_bpf_device_event(), the first read could evaluate to null
> while the second read sees the newly allocated pointer and evaluates to
> true. This would allow the code to bypass the guard and execute
> memset(ctx_kern.data, 0, hdev->bpf.allocated_data) with a null pointer.
> 
> Would it be beneficial to use READ_ONCE() or proper synchronization here to
> ensure consistent evaluation of the device data pointer?
> 


^ permalink raw reply

* Re: [PATCH bpf-next v1 3/8] resolve_btfids: Keep collected kfuncs in a rbtree
From: Ihor Solodrai @ 2026-07-23  0:51 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: linux-input, bpf, dmitry.torokhov
In-Reply-To: <20260722235024.C478C1F000E9@smtp.kernel.org>

On 7/22/26 4:50 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] Silent dismissal of inconsistent kfunc flags during deduplication.
> --
> 
> --- Patch [3]: [PATCH bpf-next v1 3/8] resolve_btfids: Keep collected kfuncs in a rbtree ---
> commit d91e5d0dd32a660902e686ef89cb30a196dda160
> Author: Ihor Solodrai <ihor.solodrai@linux.dev>
> 
> resolve_btfids: Keep collected kfuncs in a rbtree
> 
> This commit changes the storage of collected kfuncs from a dynamically grown
> array to an rbtree keyed by BTF ID. This allows for efficient deduplication
> when kfuncs are declared in multiple sets.
> 
>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
>> index 3198198b03a78..de4986e1bc3d1 100644
>> --- a/tools/bpf/resolve_btfids/main.c
>> +++ b/tools/bpf/resolve_btfids/main.c
> [ ... ]
>> @@ -979,14 +978,48 @@ static int push_decl_tag_id(struct btf2btf_context *ctx, u32 decl_tag_id)
>>  
>>  static int push_kfunc(struct btf2btf_context *ctx, struct kfunc *kfunc)
>>  {
>> -	if (ensure_mem(&ctx->kfuncs, &ctx->max_kfuncs, ctx->nr_kfuncs + 1))
>> +	struct rb_node **p = &ctx->kfuncs.rb_node;
>> +	struct rb_node *parent = NULL;
>> +	struct kfunc *k;
>> +
>> +	/* Dedup by BTF ID: collecting the same kfunc twice is a no-op. */
>> +	while (*p) {
>> +		parent = *p;
>> +		k = rb_entry(parent, struct kfunc, rb_node);
>> +
>> +		if (kfunc->btf_id < k->btf_id)
>> +			p = &(*p)->rb_left;
>> +		else if (kfunc->btf_id > k->btf_id)
>> +			p = &(*p)->rb_right;
>> +		else
>> +			return 0;
> 
> [Severity: Medium]
> Does this silent return effectively ignore conflicting flags from subsequent
> declarations?

This is deliberate here. Flags are checked for in the patch #8

> 
> If the same kfunc is mistakenly declared in multiple BTF sets with different
> flags (such as KF_ACQUIRE vs none), returning 0 here without checking if
> k->flags == kfunc->flags will silently discard the incoming flags.
> 
> Could this cause critical security flags like KF_ACQUIRE or KF_TRUSTED_ARGS
> to be unexpectedly dropped depending on the processing order?
> 
>> +	}
>> +
>> +	k = zalloc(sizeof(*k));
>> +	if (!k)
>>  		return -ENOMEM;
> 


^ permalink raw reply

* Re: [PATCH bpf-next v1 1/8] resolve_btfids: Implement generic ensure_mem() to grow arrays
From: Ihor Solodrai @ 2026-07-23  0:50 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: linux-input, bpf, dmitry.torokhov
In-Reply-To: <20260722234842.68B271F000E9@smtp.kernel.org>

On 7/22/26 4:48 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] Strict aliasing violation via `void **` casting leading to potential Use-After-Free.
> --
> 
> --- Patch [1]: [PATCH bpf-next v1 1/8] resolve_btfids: Implement generic ensure_mem() to grow arrays ---
> commit b5970d9bf6b1262c4ad812706df67f4a14ee5411
> Author: Ihor Solodrai <ihor.solodrai@linux.dev>
> 
> resolve_btfids: Implement generic ensure_mem() to grow arrays
> 
> This commit factors out array growth logic from push_*() helpers into a
> generic ensure_mem() macro and function. It uses a void ** cast to update
> the array pointer upon reallocation.
> 
>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
>> index f8a91fa7584f4..183466ddc5f97 100644
>> --- a/tools/bpf/resolve_btfids/main.c
>> +++ b/tools/bpf/resolve_btfids/main.c
>> @@ -201,6 +201,35 @@ static int eprintf(int level, int var, const char *fmt, ...)
>>  #define pr_info(fmt, ...) \
>>  	eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
>>  
>> +/*
>> + * Grow *data so it can hold at least cnt elements of elem_sz bytes each.
>> + * *cap is the capacity in elements and is updated on growth.
>> + */
>> +static int __ensure_mem(void **data, u32 *cap, u32 cnt, size_t elem_sz)
>> +{
>> +	u32 new_cap, old_cap = *cap;
>> +	void *arr;
>> +
>> +	if (cnt <= old_cap)
>> +		return 0;
>> +
>> +	new_cap = max(old_cap + 256, old_cap * 2);
>> +	if (new_cap < cnt)
>> +		new_cap = cnt;
>> +
>> +	arr = realloc(*data, elem_sz * new_cap);
>> +	if (!arr)
>> +		return -ENOMEM;
>> +
>> +	*data = arr;
> 
> [Severity: Medium]
> Will this assignment violate strict aliasing rules? Because void * and u32 *
> are incompatible types, modifying the pointer through a void ** pointer
> violates C strict aliasing.
> 
> While core kernel code is compiled with -fno-strict-aliasing, programs under
> tools/ are compiled with standard optimizations including -fstrict-aliasing.
> If __ensure_mem() is inlined, could the compiler assume that this assignment
> does not modify the original typed pointer?

ensure_mem() is modeled on libbpf's own libbpf_ensure_mem(), which is
called exactly this way throughout libbpf with typed pointers cast to
(void **), built under the same tools/ flags.

The pattern is long-established and hasn't caused issues in practice.
Callers re-read the struct member after the call. Wontfix.

> 
>> +	*cap = new_cap;
>> +
>> +	return 0;
>> +}
>> +
>> +#define ensure_mem(arr_ptr, cap_ptr, cnt) \
>> +	__ensure_mem((void **)(arr_ptr), (cap_ptr), (cnt), sizeof(**(arr_ptr)))
> 
> [...]
> 


^ permalink raw reply

* Re: [PATCH bpf-next v1 5/8] resolve_btfids: Fix the _impl lookup for module BTF
From: bot+bpf-ci @ 2026-07-23  0:46 UTC (permalink / raw)
  To: ihor.solodrai, ast, andrii, daniel, eddyz87, memxor
  Cc: bentiss, jikos, emil, jolsa, bpf, linux-input, kernel-team, ast,
	andrii, daniel, martin.lau, eddyz87, yonghong.song, clm,
	ihor.solodrai
In-Reply-To: <20260722233518.778854-6-ihor.solodrai@linux.dev>

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

> resolve_btfids: Fix the _impl lookup for module BTF
>
> process_kfunc_with_implicit_args() skips generating <name>_impl
> function when one already exists for backwards compatibility.
>
> It uses btf__find_by_name_kind(), which searches the base BTF before
> the split BTF. When resolve_btfids processes a module, a same-named
> _impl in vmlinux would be found and the module's own counterpart would
> not be created.
>
> Fix by using btf__find_by_name_kind_own() for the lookup.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>

The btf__find_by_name_kind() call being changed here was introduced
along with process_kfunc_with_implicit_args() itself.  Should this
carry a Fixes: tag?

  Fixes: 9d199965990c ("resolve_btfids: Support for KF_IMPLICIT_ARGS")


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/29967476366

^ permalink raw reply

* Re: [PATCH bpf-next v1 4/8] libbpf: Export btf__find_by_name_kind_own()
From: Ihor Solodrai @ 2026-07-23  0:45 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: linux-input, bpf, dmitry.torokhov
In-Reply-To: <20260722234358.4AA721F000E9@smtp.kernel.org>

On 7/22/26 4:43 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] The newly exposed public libbpf API `btf__find_by_name_kind_own()` lacks the `libbpf_err()` wrapper on its error return path.
> --
> 
> --- Patch [4]: [PATCH bpf-next v1 4/8] libbpf: Export btf__find_by_name_kind_own() ---
> commit 60b04eac1753190ac945126aaca0759082942bc0
> Author: Ihor Solodrai <ihor.solodrai@linux.dev>
> 
> libbpf: Export btf__find_by_name_kind_own()
>     
> btf__find_by_name_kind() searches the base BTF before the split BTF,
> so in case of a name collision between base and split it always
> returns a base type. Tools that process split BTF may need to restrict
> a lookup to the split's own types.
>     
> The internal helper btf__find_by_name_kind_own() already does exactly
> that. Make it a public API.
> 
>> diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
>> index 1a31f2da947f8..587172c0de089 100644
>> --- a/tools/lib/bpf/btf.h
>> +++ b/tools/lib/bpf/btf.h
>> @@ -172,6 +172,8 @@ LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
>>  				   const char *type_name);
>>  LIBBPF_API __s32 btf__find_by_name_kind(const struct btf *btf,
>>  					const char *type_name, __u32 kind);
>> +LIBBPF_API __s32 btf__find_by_name_kind_own(const struct btf *btf,
>> +					    const char *type_name, __u32 kind);
> 
> [Severity: Medium]
> Since btf__find_by_name_kind_own() is now a public API, shouldn't its return
> path in tools/lib/bpf/btf.c be updated to use libbpf_err()?
> 
> Currently, it returns the value from the internal helper directly:
> 
> tools/lib/bpf/btf.c:btf__find_by_name_kind_own() {
> 	return btf_find_by_name_kind(btf, btf->start_id, type_name, kind);
> }
> 
> The libbpf public API error handling contract requires all public API functions
> to set errno using libbpf_err() directly on their return statements. While the
> internal helper btf_find_by_name_kind() currently sets errno indirectly, relying
> on it could cause issues if the internal helper is later fixed to return raw
> error codes.
> 
> Should this be updated to return libbpf_err(btf_find_by_name_kind(...))?

Ok clanker, this is embarrassing. Read the code around the diff before posting.

btf__find_by_name_kind_own() mirrors btf__find_by_name_kind(), and 
both return btf_find_by_name_kind() directly.

And btf_find_by_name_kind() helper already sets errno via libbpf_err() on its
error paths.

> 
>>  LIBBPF_API __u32 btf__type_cnt(const struct btf *btf);
>>  LIBBPF_API const struct btf *btf__base_btf(const struct btf *btf);
> 


^ permalink raw reply

* Re: [PATCH bpf-next v1 7/8] resolve_btfids: Discover kfuncs from BTF ID sets
From: bot+bpf-ci @ 2026-07-23  0:32 UTC (permalink / raw)
  To: ihor.solodrai, ast, andrii, daniel, eddyz87, memxor
  Cc: bentiss, jikos, emil, jolsa, bpf, linux-input, kernel-team, ast,
	andrii, daniel, martin.lau, eddyz87, yonghong.song, clm,
	ihor.solodrai
In-Reply-To: <20260722233518.778854-8-ihor.solodrai@linux.dev>

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

> collect_kfuncs() currently uses bpf_kfunc decl tags to identify the
> list of kfuncs. The decl tags are generated by pahole, which makes
> current implementation implicitly rely on those tags being generated.
>
> The authoritative source, used by the the BPF verifier for kfunc
> registration, of functions being BPF kfuncs are
> BTF_KFUNCS_START()/END() declarations. These are BTF_ID_SET8 under the
> hood. Currently resolve_btfids reads kfunc flags from these sets, and
> populates them with BTF IDs.
>
> Implement kfunc discovery from BTF_ID_SET8 symbols in resolve_btfids,
> removing the dependency on pahole's emmission of decl tags.
>
> Walk BTF_ID_KIND_SET8 sets, and use the address-to-symbol index to
> look up set entry's BTF_ID symbol name (before .BTF_ids is patched),
> recording the paired flags directly. This makes find_kfunc_flags()
> helper unnecessary, so it's removed.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>

This isn't a bug, but there are a couple of small typos in the
changelog: "used by the the BPF verifier" has a duplicated "the", and
"pahole's emmission of decl tags" should be "emission".


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/29967476366

^ permalink raw reply

* Re: [PATCH bpf-next v1 8/8] resolve_btfids: Enforce consistent kfunc flags across BTF ID sets
From: bot+bpf-ci @ 2026-07-23  0:32 UTC (permalink / raw)
  To: ihor.solodrai, ast, andrii, daniel, eddyz87, memxor
  Cc: bentiss, jikos, emil, jolsa, bpf, linux-input, kernel-team, ast,
	andrii, daniel, martin.lau, eddyz87, yonghong.song, clm,
	ihor.solodrai
In-Reply-To: <20260722233518.778854-9-ihor.solodrai@linux.dev>

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

> Enforce the kfunc flag consistency in resolve_btifds by hard failing
> on error and blocking kernel (or module) build.

This isn't a bug, but should 'resolve_btifds' here be 'resolve_btfids'
to match the tool name?

> [1] https://lore.kernel.org/bpf/9b2196dd-443b-4632-ae11-030cdbdc59b4@linux.dev/
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/29967476366

^ permalink raw reply

* Re: [PATCH bpf-next v1 3/8] resolve_btfids: Keep collected kfuncs in a rbtree
From: sashiko-bot @ 2026-07-22 23:50 UTC (permalink / raw)
  To: Ihor Solodrai; +Cc: linux-input, bpf, dmitry.torokhov
In-Reply-To: <20260722233518.778854-4-ihor.solodrai@linux.dev>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Silent dismissal of inconsistent kfunc flags during deduplication.
--

--- Patch [3]: [PATCH bpf-next v1 3/8] resolve_btfids: Keep collected kfuncs in a rbtree ---
commit d91e5d0dd32a660902e686ef89cb30a196dda160
Author: Ihor Solodrai <ihor.solodrai@linux.dev>

resolve_btfids: Keep collected kfuncs in a rbtree

This commit changes the storage of collected kfuncs from a dynamically grown
array to an rbtree keyed by BTF ID. This allows for efficient deduplication
when kfuncs are declared in multiple sets.

> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index 3198198b03a78..de4986e1bc3d1 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
[ ... ]
> @@ -979,14 +978,48 @@ static int push_decl_tag_id(struct btf2btf_context *ctx, u32 decl_tag_id)
>  
>  static int push_kfunc(struct btf2btf_context *ctx, struct kfunc *kfunc)
>  {
> -	if (ensure_mem(&ctx->kfuncs, &ctx->max_kfuncs, ctx->nr_kfuncs + 1))
> +	struct rb_node **p = &ctx->kfuncs.rb_node;
> +	struct rb_node *parent = NULL;
> +	struct kfunc *k;
> +
> +	/* Dedup by BTF ID: collecting the same kfunc twice is a no-op. */
> +	while (*p) {
> +		parent = *p;
> +		k = rb_entry(parent, struct kfunc, rb_node);
> +
> +		if (kfunc->btf_id < k->btf_id)
> +			p = &(*p)->rb_left;
> +		else if (kfunc->btf_id > k->btf_id)
> +			p = &(*p)->rb_right;
> +		else
> +			return 0;

[Severity: Medium]
Does this silent return effectively ignore conflicting flags from subsequent
declarations?

If the same kfunc is mistakenly declared in multiple BTF sets with different
flags (such as KF_ACQUIRE vs none), returning 0 here without checking if
k->flags == kfunc->flags will silently discard the incoming flags.

Could this cause critical security flags like KF_ACQUIRE or KF_TRUSTED_ARGS
to be unexpectedly dropped depending on the processing order?

> +	}
> +
> +	k = zalloc(sizeof(*k));
> +	if (!k)
>  		return -ENOMEM;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260722233518.778854-1-ihor.solodrai@linux.dev?part=3

^ permalink raw reply

* Re: [PATCH bpf-next v1 6/8] HID: bpf: Make syscall kfunc flags match the struct_ops set
From: sashiko-bot @ 2026-07-22 23:49 UTC (permalink / raw)
  To: Ihor Solodrai; +Cc: bpf, dmitry.torokhov, linux-input
In-Reply-To: <20260722233518.778854-7-ihor.solodrai@linux.dev>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] TOCTOU NULL pointer dereference in `dispatch_hid_bpf_device_event` during dynamic HID-BPF attachment.
--

--- Patch [6]: [PATCH bpf-next v1 6/8] HID: bpf: Make syscall kfunc flags match the struct_ops set ---
Note: The format of this review is altered to provide a free-form summary
due to text recitation restrictions. No direct patch diff quoting is used.

commit 460380780bad5817b193c7927dc05e04ce9f5480
Author: Ihor Solodrai <ihor.solodrai@linux.dev>
Subject: HID: bpf: Make syscall kfunc flags match the struct_ops set

This commit updates the kfunc flags for hid_bpf_syscall_kfunc_ids to match
the hid_bpf_kfunc_ids set by adding the KF_SLEEPABLE flag. This ensures
per-kfunc flag consistency across BTF ID sets at build time.

[Severity: High]
This isn't a regression introduced by this patch, but while reviewing the
HID-BPF event dispatching code, a potential time-of-check to time-of-use
issue was noticed in drivers/hid/bpf/hid_bpf_dispatch.c.

Could this sequence lead to a null pointer dereference when a HID-BPF
program is concurrently attached while a device is flooded with input
reports?

In dispatch_hid_bpf_device_event(), the first lockless read of the device
data happens during context initialization:

    .data = hdev->bpf.device_data,

Shortly after, a second lockless read is used as a guard:

    if (!hdev->bpf.device_data)
        return data;

If __hid_bpf_allocate_data() is concurrently executed during a program
attachment, it updates these pointers without memory barriers or taking the
driver_input_lock:

    *data = alloc_data;
    *size = alloc_size;

If the allocation completes exactly between the two reads in
dispatch_hid_bpf_device_event(), the first read could evaluate to null
while the second read sees the newly allocated pointer and evaluates to
true. This would allow the code to bypass the guard and execute
memset(ctx_kern.data, 0, hdev->bpf.allocated_data) with a null pointer.

Would it be beneficial to use READ_ONCE() or proper synchronization here to
ensure consistent evaluation of the device data pointer?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260722233518.778854-1-ihor.solodrai@linux.dev?part=6

^ permalink raw reply

* Re: [PATCH bpf-next v1 1/8] resolve_btfids: Implement generic ensure_mem() to grow arrays
From: sashiko-bot @ 2026-07-22 23:48 UTC (permalink / raw)
  To: Ihor Solodrai; +Cc: linux-input, bpf, dmitry.torokhov
In-Reply-To: <20260722233518.778854-2-ihor.solodrai@linux.dev>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Strict aliasing violation via `void **` casting leading to potential Use-After-Free.
--

--- Patch [1]: [PATCH bpf-next v1 1/8] resolve_btfids: Implement generic ensure_mem() to grow arrays ---
commit b5970d9bf6b1262c4ad812706df67f4a14ee5411
Author: Ihor Solodrai <ihor.solodrai@linux.dev>

resolve_btfids: Implement generic ensure_mem() to grow arrays

This commit factors out array growth logic from push_*() helpers into a
generic ensure_mem() macro and function. It uses a void ** cast to update
the array pointer upon reallocation.

> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index f8a91fa7584f4..183466ddc5f97 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
> @@ -201,6 +201,35 @@ static int eprintf(int level, int var, const char *fmt, ...)
>  #define pr_info(fmt, ...) \
>  	eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
>  
> +/*
> + * Grow *data so it can hold at least cnt elements of elem_sz bytes each.
> + * *cap is the capacity in elements and is updated on growth.
> + */
> +static int __ensure_mem(void **data, u32 *cap, u32 cnt, size_t elem_sz)
> +{
> +	u32 new_cap, old_cap = *cap;
> +	void *arr;
> +
> +	if (cnt <= old_cap)
> +		return 0;
> +
> +	new_cap = max(old_cap + 256, old_cap * 2);
> +	if (new_cap < cnt)
> +		new_cap = cnt;
> +
> +	arr = realloc(*data, elem_sz * new_cap);
> +	if (!arr)
> +		return -ENOMEM;
> +
> +	*data = arr;

[Severity: Medium]
Will this assignment violate strict aliasing rules? Because void * and u32 *
are incompatible types, modifying the pointer through a void ** pointer
violates C strict aliasing.

While core kernel code is compiled with -fno-strict-aliasing, programs under
tools/ are compiled with standard optimizations including -fstrict-aliasing.
If __ensure_mem() is inlined, could the compiler assume that this assignment
does not modify the original typed pointer?

> +	*cap = new_cap;
> +
> +	return 0;
> +}
> +
> +#define ensure_mem(arr_ptr, cap_ptr, cnt) \
> +	__ensure_mem((void **)(arr_ptr), (cap_ptr), (cnt), sizeof(**(arr_ptr)))

[Severity: Medium]
Does casting a typed double pointer (like &ctx->decl_tags which is u32 **) to
void ** here set up the strict aliasing violation mentioned above?

[ ... ]

> @@ -890,17 +919,8 @@ static const struct btf_type *btf_type_skip_qualifiers(const struct btf *btf, s3
>  
>  static int push_decl_tag_id(struct btf2btf_context *ctx, u32 decl_tag_id)
>  {
> -	u32 *arr = ctx->decl_tags;
> -	u32 cap = ctx->max_decl_tags;
> -
> -	if (ctx->nr_decl_tags + 1 > cap) {
> -		cap = max(cap + 256, cap * 2);
> -		arr = realloc(arr, sizeof(u32) * cap);
> -		if (!arr)
> -			return -ENOMEM;
> -		ctx->max_decl_tags = cap;
> -		ctx->decl_tags = arr;
> -	}
> +	if (ensure_mem(&ctx->decl_tags, &ctx->max_decl_tags, ctx->nr_decl_tags + 1))
> +		return -ENOMEM;
>  
>  	ctx->decl_tags[ctx->nr_decl_tags++] = decl_tag_id;

[Severity: Medium]
Could this assignment result in a use-after-free? If the compiler's strict
aliasing optimization assumes ctx->decl_tags was not modified by ensure_mem(),
it might use a cached pointer that was freed by realloc() during the array
assignment.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260722233518.778854-1-ihor.solodrai@linux.dev?part=1

^ permalink raw reply

* Re: [PATCH bpf-next v1 4/8] libbpf: Export btf__find_by_name_kind_own()
From: sashiko-bot @ 2026-07-22 23:43 UTC (permalink / raw)
  To: Ihor Solodrai; +Cc: linux-input, bpf, dmitry.torokhov
In-Reply-To: <20260722233518.778854-5-ihor.solodrai@linux.dev>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The newly exposed public libbpf API `btf__find_by_name_kind_own()` lacks the `libbpf_err()` wrapper on its error return path.
--

--- Patch [4]: [PATCH bpf-next v1 4/8] libbpf: Export btf__find_by_name_kind_own() ---
commit 60b04eac1753190ac945126aaca0759082942bc0
Author: Ihor Solodrai <ihor.solodrai@linux.dev>

libbpf: Export btf__find_by_name_kind_own()
    
btf__find_by_name_kind() searches the base BTF before the split BTF,
so in case of a name collision between base and split it always
returns a base type. Tools that process split BTF may need to restrict
a lookup to the split's own types.
    
The internal helper btf__find_by_name_kind_own() already does exactly
that. Make it a public API.

> diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
> index 1a31f2da947f8..587172c0de089 100644
> --- a/tools/lib/bpf/btf.h
> +++ b/tools/lib/bpf/btf.h
> @@ -172,6 +172,8 @@ LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
>  				   const char *type_name);
>  LIBBPF_API __s32 btf__find_by_name_kind(const struct btf *btf,
>  					const char *type_name, __u32 kind);
> +LIBBPF_API __s32 btf__find_by_name_kind_own(const struct btf *btf,
> +					    const char *type_name, __u32 kind);

[Severity: Medium]
Since btf__find_by_name_kind_own() is now a public API, shouldn't its return
path in tools/lib/bpf/btf.c be updated to use libbpf_err()?

Currently, it returns the value from the internal helper directly:

tools/lib/bpf/btf.c:btf__find_by_name_kind_own() {
	return btf_find_by_name_kind(btf, btf->start_id, type_name, kind);
}

The libbpf public API error handling contract requires all public API functions
to set errno using libbpf_err() directly on their return statements. While the
internal helper btf_find_by_name_kind() currently sets errno indirectly, relying
on it could cause issues if the internal helper is later fixed to return raw
error codes.

Should this be updated to return libbpf_err(btf_find_by_name_kind(...))?

>  LIBBPF_API __u32 btf__type_cnt(const struct btf *btf);
>  LIBBPF_API const struct btf *btf__base_btf(const struct btf *btf);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260722233518.778854-1-ihor.solodrai@linux.dev?part=4

^ permalink raw reply

* [PATCH bpf-next v1 8/8] resolve_btfids: Enforce consistent kfunc flags across BTF ID sets
From: Ihor Solodrai @ 2026-07-22 23:35 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Benjamin Tissoires, Jiri Kosina, Emil Tsalapatis, Jiri Olsa, bpf,
	linux-input, kernel-team
In-Reply-To: <20260722233518.778854-1-ihor.solodrai@linux.dev>

A kfunc may be listed in several BTF ID sets, which is expected
because different kfuncs are available to BPF programs depending on
their type.

However kfunc flags across different BTF ID sets must be consistent [1].
The flags should be considered a part of the kfunc declaration,
because they influence its BTF representation and verifier handling.

Enforce the kfunc flag consistency in resolve_btifds by hard failing
on error and blocking kernel (or module) build.

[1] https://lore.kernel.org/bpf/9b2196dd-443b-4632-ae11-030cdbdc59b4@linux.dev/

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 tools/bpf/resolve_btfids/main.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index 338d0c0a8e58..85488935909d 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -982,17 +982,26 @@ static int push_kfunc(struct btf2btf_context *ctx, struct kfunc *kfunc)
 	struct rb_node *parent = NULL;
 	struct kfunc *k;
 
-	/* Dedup by BTF ID: collecting the same kfunc twice is a no-op. */
+	/*
+	 * Dedup by BTF ID: collecting the same kfunc twice is a no-op,
+	 * UNLESS the kfunc flags are inconsistent, in which case we
+	 * fail hard because it indicates a bug in a kfunc set declaration.
+	 */
 	while (*p) {
 		parent = *p;
 		k = rb_entry(parent, struct kfunc, rb_node);
 
-		if (kfunc->btf_id < k->btf_id)
+		if (kfunc->btf_id < k->btf_id) {
 			p = &(*p)->rb_left;
-		else if (kfunc->btf_id > k->btf_id)
+		} else if (kfunc->btf_id > k->btf_id) {
 			p = &(*p)->rb_right;
-		else
+		} else if (k->flags == kfunc->flags) {
 			return 0;
+		} else {
+			pr_err("ERROR: resolve_btfids: kfunc %s has inconsistent flags across BTF ID sets: 0x%x != 0x%x\n",
+			       kfunc->name, k->flags, kfunc->flags);
+			return -EINVAL;
+		}
 	}
 
 	k = zalloc(sizeof(*k));
-- 
2.55.0


^ permalink raw reply related

* [PATCH bpf-next v1 7/8] resolve_btfids: Discover kfuncs from BTF ID sets
From: Ihor Solodrai @ 2026-07-22 23:35 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Benjamin Tissoires, Jiri Kosina, Emil Tsalapatis, Jiri Olsa, bpf,
	linux-input, kernel-team
In-Reply-To: <20260722233518.778854-1-ihor.solodrai@linux.dev>

collect_kfuncs() currently uses bpf_kfunc decl tags to identify the
list of kfuncs. The decl tags are generated by pahole, which makes
current implementation implicitly rely on those tags being generated.

The authoritative source, used by the the BPF verifier for kfunc
registration, of functions being BPF kfuncs are
BTF_KFUNCS_START()/END() declarations. These are BTF_ID_SET8 under the
hood. Currently resolve_btfids reads kfunc flags from these sets, and
populates them with BTF IDs.

Implement kfunc discovery from BTF_ID_SET8 symbols in resolve_btfids,
removing the dependency on pahole's emmission of decl tags.

Walk BTF_ID_KIND_SET8 sets, and use the address-to-symbol index to
look up set entry's BTF_ID symbol name (before .BTF_ids is patched),
recording the paired flags directly. This makes find_kfunc_flags()
helper unnecessary, so it's removed.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 tools/bpf/resolve_btfids/main.c | 89 +++++++++++----------------------
 1 file changed, 29 insertions(+), 60 deletions(-)

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index ab3ab3045592..338d0c0a8e58 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -1039,19 +1039,18 @@ static int collect_decl_tags(struct btf2btf_context *ctx)
 	return 0;
 }
 
-/*
- * To find kfunc flags, scan BTF_SET8_KFUNCS entries and use the entry
- * address to recover the corresponding BTF_ID symbol name.
- * Return 0 (no flags!) if not found.
- */
-static u32 find_kfunc_flags(struct object *obj, struct btf_id *kfunc_id)
+static int collect_kfuncs(struct object *obj, struct btf2btf_context *ctx)
 {
 	Elf_Data *idlist = obj->efile.idlist;
-	struct btf_id *set_id;
+	struct btf *btf = ctx->btf;
 	struct rb_node *next;
 
+	if (!idlist || !idlist->d_buf)
+		return 0;
+
 	for (next = rb_first(&obj->sets); next; next = rb_next(next)) {
 		struct btf_id_set8 *set8;
+		struct btf_id *set_id;
 		u64 set_addr;
 
 		set_id = rb_entry(next, struct btf_id, rb_node);
@@ -1066,64 +1065,34 @@ static u32 find_kfunc_flags(struct object *obj, struct btf_id *kfunc_id)
 		for (u32 i = 0; i < set_id->cnt; i++) {
 			size_t off = (char *)&set8->pairs[i] - (char *)set8;
 			const char *name = find_name_by_addr(obj, set_addr + off);
+			struct kfunc kfunc;
+			s32 func_id;
+			int err;
 
-			if (!name || strcmp(name, kfunc_id->name) != 0)
+			if (!name) {
+				pr_err("WARN: resolve_btfids: no BTF ID symbol for %s entry %u\n",
+				       set_id->name, i);
+				warnings++;
 				continue;
+			}
 
-			pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
-				 kfunc_id->name, set_id->name);
-
-			return set8->pairs[i].flags;
-		}
-	}
-
-	return 0;
-}
-
-static int collect_kfuncs(struct object *obj, struct btf2btf_context *ctx)
-{
-	const char *tag_name, *func_name;
-	struct btf *btf = ctx->btf;
-	const struct btf_type *t;
-	u32 flags, func_id;
-	struct kfunc kfunc;
-	struct btf_id *id;
-	int err;
-
-	if (ctx->nr_decl_tags == 0)
-		return 0;
-
-	for (u32 i = 0; i < ctx->nr_decl_tags; i++) {
-		t = btf__type_by_id(btf, ctx->decl_tags[i]);
-		if (btf_kflag(t) || btf_decl_tag(t)->component_idx != -1)
-			continue;
-
-		tag_name = btf__name_by_offset(btf, t->name_off);
-		if (strcmp(tag_name, "bpf_kfunc") != 0)
-			continue;
-
-		func_id = t->type;
-		t = btf__type_by_id(btf, func_id);
-		if (!btf_is_func(t))
-			continue;
-
-		func_name = btf__name_by_offset(btf, t->name_off);
-		if (!func_name)
-			continue;
-
-		id = btf_id__find(&obj->funcs, func_name);
-		if (!id || id->kind != BTF_ID_KIND_SYM)
-			continue;
-
-		flags = find_kfunc_flags(obj, id);
+			func_id = btf__find_by_name_kind_own(btf, name, BTF_KIND_FUNC);
+			if (func_id < 0) {
+				pr_err("WARN: resolve_btfids: no BTF func for kfunc %s in %s\n",
+				       name, set_id->name);
+				warnings++;
+				continue;
+			}
 
-		kfunc.name = id->name;
-		kfunc.btf_id = func_id;
-		kfunc.flags = flags;
+			pr_debug("found kfunc %s in %s\n", name, set_id->name);
 
-		err = push_kfunc(ctx, &kfunc);
-		if (err)
-			return err;
+			kfunc.name = name;
+			kfunc.btf_id = func_id;
+			kfunc.flags = set8->pairs[i].flags;
+			err = push_kfunc(ctx, &kfunc);
+			if (err)
+				return err;
+		}
 	}
 
 	return 0;
-- 
2.55.0


^ permalink raw reply related

* [PATCH bpf-next v1 6/8] HID: bpf: Make syscall kfunc flags match the struct_ops set
From: Ihor Solodrai @ 2026-07-22 23:35 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Benjamin Tissoires, Jiri Kosina, Emil Tsalapatis, Jiri Olsa, bpf,
	linux-input, kernel-team
In-Reply-To: <20260722233518.778854-1-ihor.solodrai@linux.dev>

Update kfunc flags for hid_bpf_syscall_kfunc_ids set to exactly match
hid_bpf_kfunc_ids set by adding KF_SLEEPABLE flag.

The syscall set omitted the flag because syscall programs are always
sleepable (the verifier rejects a non-sleepable syscall program).

However the upcoming resolve_btfids change enforces per-kfunc flag
consistency across BTF ID sets at build time, which is why this change
is necessary.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 drivers/hid/bpf/hid_bpf_dispatch.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index 536f6d01fd14..44671dbdeca8 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -590,11 +590,11 @@ static const struct btf_kfunc_id_set hid_bpf_kfunc_set = {
 
 /* for syscall HID-BPF */
 BTF_KFUNCS_START(hid_bpf_syscall_kfunc_ids)
-BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL)
-BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE)
-BTF_ID_FLAGS(func, hid_bpf_hw_request)
-BTF_ID_FLAGS(func, hid_bpf_hw_output_report)
-BTF_ID_FLAGS(func, hid_bpf_input_report)
+BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, hid_bpf_hw_request, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, hid_bpf_hw_output_report, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, hid_bpf_input_report, KF_SLEEPABLE)
 BTF_KFUNCS_END(hid_bpf_syscall_kfunc_ids)
 
 static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {
-- 
2.55.0


^ permalink raw reply related

* [PATCH bpf-next v1 5/8] resolve_btfids: Fix the _impl lookup for module BTF
From: Ihor Solodrai @ 2026-07-22 23:35 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Benjamin Tissoires, Jiri Kosina, Emil Tsalapatis, Jiri Olsa, bpf,
	linux-input, kernel-team
In-Reply-To: <20260722233518.778854-1-ihor.solodrai@linux.dev>

process_kfunc_with_implicit_args() skips generating <name>_impl
function when one already exists for backwards compatibility.

It uses btf__find_by_name_kind(), which searches the base BTF before
the split BTF. When resolve_btfids processes a module, a same-named
_impl in vmlinux would be found and the module's own counterpart would
not be created.

Fix by using btf__find_by_name_kind_own() for the lookup.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 tools/bpf/resolve_btfids/main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index de4986e1bc3d..ab3ab3045592 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -1232,7 +1232,7 @@ static int process_kfunc_with_implicit_args(struct btf2btf_context *ctx, struct
 		return -E2BIG;
 	}
 
-	if (btf__find_by_name_kind(btf, tmp_name, BTF_KIND_FUNC) > 0) {
+	if (btf__find_by_name_kind_own(btf, tmp_name, BTF_KIND_FUNC) > 0) {
 		pr_debug("resolve_btfids: function %s already exists in BTF\n", tmp_name);
 		goto add_new_proto;
 	}
-- 
2.55.0


^ permalink raw reply related

* [PATCH bpf-next v1 4/8] libbpf: Export btf__find_by_name_kind_own()
From: Ihor Solodrai @ 2026-07-22 23:35 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Benjamin Tissoires, Jiri Kosina, Emil Tsalapatis, Jiri Olsa, bpf,
	linux-input, kernel-team
In-Reply-To: <20260722233518.778854-1-ihor.solodrai@linux.dev>

btf__find_by_name_kind() searches the base BTF before the split BTF,
so in case of a name collision between base and split it always
returns a base type. Tools that process split BTF may need to restrict
a lookup to the split's own types.

The internal helper btf__find_by_name_kind_own() already does exactly
that. Make it a public API.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 tools/lib/bpf/btf.h             | 2 ++
 tools/lib/bpf/libbpf.map        | 1 +
 tools/lib/bpf/libbpf_internal.h | 2 --
 3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
index 1a31f2da947f..587172c0de08 100644
--- a/tools/lib/bpf/btf.h
+++ b/tools/lib/bpf/btf.h
@@ -172,6 +172,8 @@ LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
 				   const char *type_name);
 LIBBPF_API __s32 btf__find_by_name_kind(const struct btf *btf,
 					const char *type_name, __u32 kind);
+LIBBPF_API __s32 btf__find_by_name_kind_own(const struct btf *btf,
+					    const char *type_name, __u32 kind);
 LIBBPF_API __u32 btf__type_cnt(const struct btf *btf);
 LIBBPF_API const struct btf *btf__base_btf(const struct btf *btf);
 LIBBPF_API const struct btf_type *btf__type_by_id(const struct btf *btf,
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index b731df19ae69..08ab2ea881fb 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -460,5 +460,6 @@ LIBBPF_1.8.0 {
 	global:
 		bpf_program__attach_tracing_multi;
 		bpf_program__clone;
+		btf__find_by_name_kind_own;
 		btf__new_empty_opts;
 } LIBBPF_1.7.0;
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index d5b7db703b3f..7a74abb904f8 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -596,8 +596,6 @@ typedef int (*type_id_visit_fn)(__u32 *type_id, void *ctx);
 typedef int (*str_off_visit_fn)(__u32 *str_off, void *ctx);
 int btf_ext_visit_type_ids(struct btf_ext *btf_ext, type_id_visit_fn visit, void *ctx);
 int btf_ext_visit_str_offs(struct btf_ext *btf_ext, str_off_visit_fn visit, void *ctx);
-__s32 btf__find_by_name_kind_own(const struct btf *btf, const char *type_name,
-				 __u32 kind);
 
 /* handle direct returned errors */
 static inline int libbpf_err(int ret)
-- 
2.55.0


^ permalink raw reply related

* [PATCH bpf-next v1 3/8] resolve_btfids: Keep collected kfuncs in a rbtree
From: Ihor Solodrai @ 2026-07-22 23:35 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Benjamin Tissoires, Jiri Kosina, Emil Tsalapatis, Jiri Olsa, bpf,
	linux-input, kernel-team
In-Reply-To: <20260722233518.778854-1-ihor.solodrai@linux.dev>

Store collected kfuncs in a rbtree keyed by BTF ID instead of a
dynamically grown array. This allows for efficient deduplication for
kfuncs declared in multiple sets, which is needed for subsequent
patches [1].

[1] https://lore.kernel.org/bpf/CAEf4BzaLzX3mXvQzxv+gbmZOh84XvYofLjMSWFYghNjS-ohEZg@mail.gmail.com/

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 tools/bpf/resolve_btfids/main.c | 50 +++++++++++++++++++++++++++------
 1 file changed, 42 insertions(+), 8 deletions(-)

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index 3198198b03a7..de4986e1bc3d 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -165,6 +165,7 @@ struct object {
 #define KF_IMPL_SUFFIX "_impl"
 
 struct kfunc {
+	struct rb_node rb_node;
 	const char *name;
 	u32 btf_id;
 	u32 flags;
@@ -175,9 +176,7 @@ struct btf2btf_context {
 	u32 *decl_tags;
 	u32 nr_decl_tags;
 	u32 max_decl_tags;
-	struct kfunc *kfuncs;
-	u32 nr_kfuncs;
-	u32 max_kfuncs;
+	struct rb_root kfuncs;
 };
 
 static int verbose;
@@ -979,14 +978,48 @@ static int push_decl_tag_id(struct btf2btf_context *ctx, u32 decl_tag_id)
 
 static int push_kfunc(struct btf2btf_context *ctx, struct kfunc *kfunc)
 {
-	if (ensure_mem(&ctx->kfuncs, &ctx->max_kfuncs, ctx->nr_kfuncs + 1))
+	struct rb_node **p = &ctx->kfuncs.rb_node;
+	struct rb_node *parent = NULL;
+	struct kfunc *k;
+
+	/* Dedup by BTF ID: collecting the same kfunc twice is a no-op. */
+	while (*p) {
+		parent = *p;
+		k = rb_entry(parent, struct kfunc, rb_node);
+
+		if (kfunc->btf_id < k->btf_id)
+			p = &(*p)->rb_left;
+		else if (kfunc->btf_id > k->btf_id)
+			p = &(*p)->rb_right;
+		else
+			return 0;
+	}
+
+	k = zalloc(sizeof(*k));
+	if (!k)
 		return -ENOMEM;
 
-	ctx->kfuncs[ctx->nr_kfuncs++] = *kfunc;
+	*k = *kfunc;
+	rb_link_node(&k->rb_node, parent, p);
+	rb_insert_color(&k->rb_node, &ctx->kfuncs);
 
 	return 0;
 }
 
+static void free_kfuncs(struct rb_root *root)
+{
+	struct rb_node *next;
+	struct kfunc *kfunc;
+
+	next = rb_first(root);
+	while (next) {
+		kfunc = rb_entry(next, struct kfunc, rb_node);
+		next = rb_next(&kfunc->rb_node);
+		rb_erase(&kfunc->rb_node, root);
+		free(kfunc);
+	}
+}
+
 static int collect_decl_tags(struct btf2btf_context *ctx)
 {
 	const u32 type_cnt = btf__type_cnt(ctx->btf);
@@ -1272,14 +1305,15 @@ static int process_kfunc_with_implicit_args(struct btf2btf_context *ctx, struct
 static int btf2btf(struct object *obj)
 {
 	struct btf2btf_context ctx = {};
+	struct rb_node *next;
 	int err;
 
 	err = build_btf2btf_context(obj, &ctx);
 	if (err)
 		goto out;
 
-	for (u32 i = 0; i < ctx.nr_kfuncs; i++) {
-		struct kfunc *kfunc = &ctx.kfuncs[i];
+	for (next = rb_first(&ctx.kfuncs); next; next = rb_next(next)) {
+		struct kfunc *kfunc = rb_entry(next, struct kfunc, rb_node);
 
 		if (!(kfunc->flags & KF_IMPLICIT_ARGS))
 			continue;
@@ -1292,7 +1326,7 @@ static int btf2btf(struct object *obj)
 	err = 0;
 out:
 	free(ctx.decl_tags);
-	free(ctx.kfuncs);
+	free_kfuncs(&ctx.kfuncs);
 
 	return err;
 }
-- 
2.55.0


^ permalink raw reply related

* [PATCH bpf-next v1 0/8] resolve_btfids: Discover kfuncs from BTF ID sets
From: Ihor Solodrai @ 2026-07-22 23:35 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Benjamin Tissoires, Jiri Kosina, Emil Tsalapatis, Jiri Olsa, bpf,
	linux-input, kernel-team

This series develops resolve_btfids in preparation for bringing
kernel-specific BTF transformations in tree, which will reduce kbuild
dependency on pahole's features.

resolve_btfids currently identifies kfuncs by reading the "bpf_kfunc"
decl tags pahole emits into vmlinux BTF. The series switches the
source of truth to the BTF ID sets registered with
BTF_KFUNCS_START()/END() in the kernel, which is the mechanism BPF
verifier uses.

The series is based on patches #5 and #6 from the original
"resolve_btfids: Implement BTF tags emission for kfuncs" series [1],
and includes a few significant additions. Particularly the build time
enforcement of kfunc flags consistency [2].

The series consists of:
  - patches #1-#3 implement supporting infrastructure for the proper
    kfunc discovery and deduplication
  - patches #4-#5 expose btf__find_by_name_kind_own() and use it to
    fix a latent bug in _impl func lookup
  - patch #6 fixes inconsistent kfunc flags for HID kfuncs
  - patch #7 implements kfunc discovery from BTF ID sets
  - patch #8 adds enforcement of kfunc flags consistency

The resolve_btfids selftests patches have landed earlier [3].

[1] https://lore.kernel.org/bpf/20260601221805.821394-1-ihor.solodrai@linux.dev/
[2] https://lore.kernel.org/bpf/9b2196dd-443b-4632-ae11-030cdbdc59b4@linux.dev/
[3] https://lore.kernel.org/bpf/20260617210619.1562858-1-ihor.solodrai@linux.dev/

---

Changes compared to [1]:
  - Drop the idempotency (ensure_*) machinery: assume valid input BTF (Andrii)
  - Use rbtree to store the kfuncs (Andrii)
  - Enforce full KF_* flag consistency as a build error (Eduard)
  - Various cleanups and nits (Andrii, Emil, Jiri)

---

Ihor Solodrai (8):
  resolve_btfids: Implement generic ensure_mem() to grow arrays
  resolve_btfids: Index BTF ID symbols by address
  resolve_btfids: Keep collected kfuncs in a rbtree
  libbpf: Export btf__find_by_name_kind_own()
  resolve_btfids: Fix the _impl lookup for module BTF
  HID: bpf: Make syscall kfunc flags match the struct_ops set
  resolve_btfids: Discover kfuncs from BTF ID sets
  resolve_btfids: Enforce consistent kfunc flags across BTF ID sets

 drivers/hid/bpf/hid_bpf_dispatch.c |  10 +-
 tools/bpf/resolve_btfids/main.c    | 275 ++++++++++++++++++-----------
 tools/lib/bpf/btf.h                |   2 +
 tools/lib/bpf/libbpf.map           |   1 +
 tools/lib/bpf/libbpf_internal.h    |   2 -
 5 files changed, 181 insertions(+), 109 deletions(-)

-- 
2.55.0


^ permalink raw reply

* [PATCH bpf-next v1 2/8] resolve_btfids: Index BTF ID symbols by address
From: Ihor Solodrai @ 2026-07-22 23:35 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Benjamin Tissoires, Jiri Kosina, Emil Tsalapatis, Jiri Olsa, bpf,
	linux-input, kernel-team
In-Reply-To: <20260722233518.778854-1-ihor.solodrai@linux.dev>

Keep an address-sorted index of parsed .BTF_ids symbols so that the
original BTF_ID symbol name can be recovered from an entry address.

Use the index in find_kfunc_flags() to scan BTF_SET8_KFUNCS entries
directly and match each entry back to the requested kfunc.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
---
 tools/bpf/resolve_btfids/main.c | 96 ++++++++++++++++++++++++---------
 1 file changed, 72 insertions(+), 24 deletions(-)

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index 183466ddc5f9..3198198b03a7 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -119,6 +119,11 @@ struct btf_id {
 	Elf64_Addr	 addr[ADDR_CNT];
 };
 
+struct addr_sym {
+	Elf64_Addr	 addr;
+	const char	*name;
+};
+
 struct object {
 	const char *path;
 	const char *btf_path;
@@ -150,6 +155,10 @@ struct object {
 	int nr_structs;
 	int nr_unions;
 	int nr_typedefs;
+
+	struct addr_sym *addr_syms;
+	u32 addr_syms_cnt;
+	u32 addr_syms_cap;
 };
 
 #define KF_IMPLICIT_ARGS (1 << 16)
@@ -509,6 +518,40 @@ static int elf_collect(struct object *obj)
 	return 0;
 }
 
+static int push_addr_sym(struct object *obj, Elf64_Addr addr, const char *name)
+{
+	if (ensure_mem(&obj->addr_syms, &obj->addr_syms_cap, obj->addr_syms_cnt + 1))
+		return -ENOMEM;
+
+	obj->addr_syms[obj->addr_syms_cnt++] = (struct addr_sym){
+		.addr = addr,
+		.name = name,
+	};
+
+	return 0;
+}
+
+static int cmp_addr_sym(const void *a, const void *b)
+{
+	Elf64_Addr aa = ((const struct addr_sym *)a)->addr;
+	Elf64_Addr ab = ((const struct addr_sym *)b)->addr;
+
+	return (aa > ab) - (aa < ab);
+}
+
+static const char *find_name_by_addr(struct object *obj, Elf64_Addr addr)
+{
+	struct addr_sym key = { .addr = addr };
+	struct addr_sym *res;
+
+	if (!obj->addr_syms_cnt)
+		return NULL;
+
+	res = bsearch(&key, obj->addr_syms, obj->addr_syms_cnt,
+		      sizeof(*obj->addr_syms), cmp_addr_sym);
+	return res ? res->name : NULL;
+}
+
 static int symbols_collect(struct object *obj)
 {
 	Elf_Scn *scn = NULL;
@@ -602,8 +645,15 @@ static int symbols_collect(struct object *obj)
 			return -1;
 		}
 		id->addr[id->addr_cnt++] = sym.st_value;
+
+		if (push_addr_sym(obj, sym.st_value, id->name))
+			return -1;
 	}
 
+	if (obj->addr_syms_cnt)
+		qsort(obj->addr_syms, obj->addr_syms_cnt,
+		      sizeof(*obj->addr_syms), cmp_addr_sym);
+
 	return 0;
 }
 
@@ -957,43 +1007,40 @@ static int collect_decl_tags(struct btf2btf_context *ctx)
 }
 
 /*
- * To find the kfunc flags having its struct btf_id (with ELF addresses)
- * we need to find the address that is in range of a set8.
- * If a set8 is found, then the flags are located at addr + 4 bytes.
+ * To find kfunc flags, scan BTF_SET8_KFUNCS entries and use the entry
+ * address to recover the corresponding BTF_ID symbol name.
  * Return 0 (no flags!) if not found.
  */
 static u32 find_kfunc_flags(struct object *obj, struct btf_id *kfunc_id)
 {
-	const u32 *elf_data_ptr = obj->efile.idlist->d_buf;
-	u64 set_lower_addr, set_upper_addr, addr;
+	Elf_Data *idlist = obj->efile.idlist;
 	struct btf_id *set_id;
 	struct rb_node *next;
-	u32 flags;
-	u64 idx;
 
 	for (next = rb_first(&obj->sets); next; next = rb_next(next)) {
+		struct btf_id_set8 *set8;
+		u64 set_addr;
+
 		set_id = rb_entry(next, struct btf_id, rb_node);
 		if (set_id->kind != BTF_ID_KIND_SET8 || set_id->addr_cnt != 1)
 			continue;
 
-		set_lower_addr = set_id->addr[0];
-		set_upper_addr = set_lower_addr + set_id->cnt * sizeof(u64);
+		set_addr = set_id->addr[0];
+		set8 = idlist->d_buf + (set_addr - obj->efile.idlist_addr);
+		if (!(set8->flags & BTF_SET8_KFUNCS))
+			continue;
 
-		for (u32 i = 0; i < kfunc_id->addr_cnt; i++) {
-			addr = kfunc_id->addr[i];
-			/*
-			 * Lower bound is exclusive to skip the 8-byte header of the set.
-			 * Upper bound is inclusive to capture the last entry at offset 8*cnt.
-			 */
-			if (set_lower_addr < addr && addr <= set_upper_addr) {
-				pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
-					 kfunc_id->name, set_id->name);
-				idx = addr - obj->efile.idlist_addr;
-				idx = idx / sizeof(u32) + 1;
-				flags = elf_data_ptr[idx];
-
-				return flags;
-			}
+		for (u32 i = 0; i < set_id->cnt; i++) {
+			size_t off = (char *)&set8->pairs[i] - (char *)set8;
+			const char *name = find_name_by_addr(obj, set_addr + off);
+
+			if (!name || strcmp(name, kfunc_id->name) != 0)
+				continue;
+
+			pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
+				 kfunc_id->name, set_id->name);
+
+			return set8->pairs[i].flags;
 		}
 	}
 
@@ -1586,6 +1633,7 @@ int main(int argc, const char **argv)
 	btf_id__free_all(&obj.typedefs);
 	btf_id__free_all(&obj.funcs);
 	btf_id__free_all(&obj.sets);
+	free(obj.addr_syms);
 	if (obj.efile.elf) {
 		elf_end(obj.efile.elf);
 		close(obj.efile.fd);
-- 
2.55.0


^ permalink raw reply related

* [PATCH bpf-next v1 1/8] resolve_btfids: Implement generic ensure_mem() to grow arrays
From: Ihor Solodrai @ 2026-07-22 23:35 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Benjamin Tissoires, Jiri Kosina, Emil Tsalapatis, Jiri Olsa, bpf,
	linux-input, kernel-team
In-Reply-To: <20260722233518.778854-1-ihor.solodrai@linux.dev>

push_*() helpers in resolve_btfids contain copy-pasted array growth
logic.  Factor it out into ensure_mem() - a simplified variant of
libbpf_ensure_mem(), and use it in the helpers.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 tools/bpf/resolve_btfids/main.c | 55 ++++++++++++++++++++-------------
 1 file changed, 33 insertions(+), 22 deletions(-)

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index f8a91fa7584f..183466ddc5f9 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -201,6 +201,35 @@ static int eprintf(int level, int var, const char *fmt, ...)
 #define pr_info(fmt, ...) \
 	eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
 
+/*
+ * Grow *data so it can hold at least cnt elements of elem_sz bytes each.
+ * *cap is the capacity in elements and is updated on growth.
+ */
+static int __ensure_mem(void **data, u32 *cap, u32 cnt, size_t elem_sz)
+{
+	u32 new_cap, old_cap = *cap;
+	void *arr;
+
+	if (cnt <= old_cap)
+		return 0;
+
+	new_cap = max(old_cap + 256, old_cap * 2);
+	if (new_cap < cnt)
+		new_cap = cnt;
+
+	arr = realloc(*data, elem_sz * new_cap);
+	if (!arr)
+		return -ENOMEM;
+
+	*data = arr;
+	*cap = new_cap;
+
+	return 0;
+}
+
+#define ensure_mem(arr_ptr, cap_ptr, cnt) \
+	__ensure_mem((void **)(arr_ptr), (cap_ptr), (cnt), sizeof(**(arr_ptr)))
+
 static bool is_btf_id(const char *name)
 {
 	return name && !strncmp(name, BTF_ID_PREFIX, sizeof(BTF_ID_PREFIX) - 1);
@@ -890,17 +919,8 @@ static const struct btf_type *btf_type_skip_qualifiers(const struct btf *btf, s3
 
 static int push_decl_tag_id(struct btf2btf_context *ctx, u32 decl_tag_id)
 {
-	u32 *arr = ctx->decl_tags;
-	u32 cap = ctx->max_decl_tags;
-
-	if (ctx->nr_decl_tags + 1 > cap) {
-		cap = max(cap + 256, cap * 2);
-		arr = realloc(arr, sizeof(u32) * cap);
-		if (!arr)
-			return -ENOMEM;
-		ctx->max_decl_tags = cap;
-		ctx->decl_tags = arr;
-	}
+	if (ensure_mem(&ctx->decl_tags, &ctx->max_decl_tags, ctx->nr_decl_tags + 1))
+		return -ENOMEM;
 
 	ctx->decl_tags[ctx->nr_decl_tags++] = decl_tag_id;
 
@@ -909,17 +929,8 @@ static int push_decl_tag_id(struct btf2btf_context *ctx, u32 decl_tag_id)
 
 static int push_kfunc(struct btf2btf_context *ctx, struct kfunc *kfunc)
 {
-	struct kfunc *arr = ctx->kfuncs;
-	u32 cap = ctx->max_kfuncs;
-
-	if (ctx->nr_kfuncs + 1 > cap) {
-		cap = max(cap + 256, cap * 2);
-		arr = realloc(arr, sizeof(struct kfunc) * cap);
-		if (!arr)
-			return -ENOMEM;
-		ctx->max_kfuncs = cap;
-		ctx->kfuncs = arr;
-	}
+	if (ensure_mem(&ctx->kfuncs, &ctx->max_kfuncs, ctx->nr_kfuncs + 1))
+		return -ENOMEM;
 
 	ctx->kfuncs[ctx->nr_kfuncs++] = *kfunc;
 
-- 
2.55.0


^ permalink raw reply related

* [PATCH v14 2/2] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
From: Dave Carey @ 2026-07-22 20:16 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: hdegoede, W_Armin, ilpo.jarvinen, jikos, linux-input,
	linux-kernel, Dave Carey, Hans de Goede
In-Reply-To: <20260722201659.874469-1-carvsdriver@gmail.com>

The Lenovo Yoga Book 9 14IAH10 ships with a detachable Bluetooth keyboard
that magnetically attaches to the bottom (secondary) screen in one of two
positions.  The Embedded Controller tracks the attachment state in a 2-bit
field called BKBD and signals changes via WMI event GUID
806BD2A2-177B-481D-BFB5-3BA0BB4A2285 (notify ID 0xEB on the WM10 ACPI
device, _UID "GMZN").

The device contains embedded BMOF data (WQDD, 20705 bytes) documenting
both WMI interfaces used by this driver:

  LENOVO_BTKBD_EVENT (event GUID): WmiDataId(1) uint32 Status.
  The ACPI _WED(0xEB) method returns EC.BKBD directly as an integer,
  so the notify callback receives BKBD without a separate query.

  LENOVO_FEATURE_STATUS_DATA (block GUID, WQAF method): returns an
  8-byte buffer {uint32 IDs=0x00060000, uint32 Status=BKBD}.
  Used for the initial state read on probe and after resume.

BKBD encoding:
  0 = keyboard detached
  1 = keyboard docked on top half of bottom screen
  2 = keyboard docked on bottom half of bottom screen
  3 = reserved (not observed in practice)

This driver registers two WMI drivers sharing a module-level
BLOCKING_NOTIFIER_HEAD:

  - The event driver (LENOVO_BTKBD_EVENT) uses .notify_new() to receive
    a pre-parsed wmi_buffer and fires the notifier chain with the BKBD
    value extracted from the buffer.

  - The block driver (LENOVO_FEATURE_STATUS_DATA) owns the input_dev in
    its per-device private struct.  At probe time it registers a
    notifier_block on the chain and reads the initial BKBD state via
    wmidev_query_block().  The WMI buffer is parsed as
    struct lenovo_feature_status { __le32 id; __le32 status; }, and the
    ID field is verified before the status is used.

  - SW_TABLET_MODE=1 is reported when the keyboard is detached;
    SW_TABLET_MODE=0 when docked in either position (keyboard present).

  - The raw BKBD value is exposed via read-only sysfs attribute
    "keyboard_position".

  - BKBD state is re-read via wmidev_query_block() on resume from
    suspend or hibernation.

Tested on: Lenovo Yoga Book 9 14IAH10 (model 83KJ), kernel 7.0.

Acked-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Dave Carey <carvsdriver@gmail.com>
---
v14:
- Restore #include <linux/compiler_attributes.h> and <linux/spinlock.h>
  accidentally dropped in v13 (Ilpo Järvinen)
- Restore spinlock_t lock in struct yb9_kbdock_data
- Restore spin_lock_init(), spin_lock/spin_unlock around input_report_switch
  + input_sync in yb9_kbdock_report()
- Restore pointer-out yb9_kbdock_query(d, u32 *bkbd) and remove
  zero-initializer from struct wmi_buffer out
- Restore int return from yb9_kbdock_sync() with error propagation
- Restore error propagation in yb9_kbdock_resume() and yb9_kbdock_block_probe()

v13:
- Resend of v12: patch was corrupted in transit.  git-send-email chose
  quoted-printable encoding (triggered by non-ASCII in the changelog)
  which encoded '=' as '=3D', corrupting the RST table separators in
  the ABI doc and breaking git-am.  No content changes from v12.

v12:
- Add #include <linux/sysfs.h> (for sysfs_emit; Ilpo Järvinen)
- Add #include <linux/types.h> (for __le32 and __packed; Ilpo Järvinen)

v11 (addressing Armin Wolf's v10 review):
- ABI doc: correct Date to "June 2027" and KernelVersion to "7.3"

v10 (addressing Armin Wolf's v9 review):
- Update ABI doc: Date: June 2026, KernelVersion: 6.16
- Add #include <linux/compiler_attributes.h> and <linux/types.h>
- yb9_kbdock_query(): change return to pointer-out u32 *bkbd
- struct yb9_kbdock_data: add spinlock_t lock
- yb9_kbdock_report(): hold lock across input_report_switch + input_sync
- yb9_kbdock_sync(): return int, propagate query error to callers
- yb9_kbdock_resume(), yb9_kbdock_block_probe(): propagate sync error
- struct wmi_buffer out: remove zero-initializer
- yb9_kbdock_notify_new(): drop manual length check; use
  .min_event_size = sizeof(__le32) in struct wmi_driver instead

 .../testing/sysfs-driver-lenovo-yb9-kbdock    |  19 +
 MAINTAINERS                                   |   7 +
 drivers/platform/x86/lenovo/Kconfig           |  14 +
 drivers/platform/x86/lenovo/Makefile          |   1 +
 drivers/platform/x86/lenovo/yb9-kbdock.c      | 324 ++++++++++++++++++
 5 files changed, 365 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
 create mode 100644 drivers/platform/x86/lenovo/yb9-kbdock.c

diff --git a/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
new file mode 100644
index 0000000..ebe713a
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
@@ -0,0 +1,19 @@
+What:		/sys/bus/wmi/drivers/lenovo-yb9-kbdock/<guid>/keyboard_position
+Date:		June 2027
+KernelVersion:	7.3
+Contact:	Dave Carey <carvsdriver@gmail.com>
+Description:
+		Read-only attribute reporting the current keyboard dock position
+		as reported by the Embedded Controller on the Lenovo Yoga Book 9
+		14IAH10.
+
+		Possible values:
+
+		==  =============================================================
+		0   keyboard is not docked to any screen (detached)
+		1   keyboard docked on the top half of the bottom screen
+		2   keyboard docked on the bottom half of the bottom screen
+		==  =============================================================
+
+		SW_TABLET_MODE input events are also emitted: 0 when the keyboard
+		is docked (either position), 1 when detached.
diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e1..00e8275 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14479,6 +14479,13 @@ L:	platform-driver-x86@vger.kernel.org
 S:	Maintained
 F:	drivers/platform/x86/lenovo/wmi-hotkey-utilities.c
 
+LENOVO YOGA BOOK 9 KEYBOARD DOCK DRIVER
+M:	Dave Carey <carvsdriver@gmail.com>
+L:	platform-driver-x86@vger.kernel.org
+S:	Maintained
+F:	Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
+F:	drivers/platform/x86/lenovo/yb9-kbdock.c
+
 LETSKETCH HID TABLET DRIVER
 M:	Hans de Goede <hansg@kernel.org>
 L:	linux-input@vger.kernel.org
diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig
index 9c48487..938b361 100644
--- a/drivers/platform/x86/lenovo/Kconfig
+++ b/drivers/platform/x86/lenovo/Kconfig
@@ -43,6 +43,20 @@ config LENOVO_WMI_CAMERA
 	  To compile this driver as a module, choose M here: the module
 	  will be called lenovo-wmi-camera.
 
+config LENOVO_YB9_KBDOCK
+	tristate "Lenovo Yoga Book 9 keyboard dock detection"
+	depends on ACPI_WMI
+	depends on DMI
+	depends on INPUT
+	help
+	  Say Y here to enable keyboard dock detection on the Lenovo Yoga Book 9
+	  14IAH10.  The detachable Bluetooth keyboard magnetically attaches to
+	  either screen; this driver reports SW_TABLET_MODE input events based
+	  on the attachment state and exposes the raw position in sysfs.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called lenovo-yb9-kbdock.
+
 config LENOVO_YMC
 	tristate "Lenovo Yoga Tablet Mode Control"
 	depends on ACPI_WMI
diff --git a/drivers/platform/x86/lenovo/Makefile b/drivers/platform/x86/lenovo/Makefile
index 7b2128e..2842d7d 100644
--- a/drivers/platform/x86/lenovo/Makefile
+++ b/drivers/platform/x86/lenovo/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_THINKPAD_LMI)	+= think-lmi.o
 obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
 
 lenovo-target-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES)	+= wmi-hotkey-utilities.o
+lenovo-target-$(CONFIG_LENOVO_YB9_KBDOCK)	+= yb9-kbdock.o
 lenovo-target-$(CONFIG_LENOVO_YMC)	+= ymc.o
 lenovo-target-$(CONFIG_YOGABOOK)	+= yogabook.o
 lenovo-target-$(CONFIG_YT2_1380)	+= yoga-tab2-pro-1380-fastcharger.o
diff --git a/drivers/platform/x86/lenovo/yb9-kbdock.c b/drivers/platform/x86/lenovo/yb9-kbdock.c
new file mode 100644
index 0000000..40a2566
--- /dev/null
+++ b/drivers/platform/x86/lenovo/yb9-kbdock.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Lenovo Yoga Book 9 keyboard-dock detection
+ *
+ * The Yoga Book 9 ships with a detachable Bluetooth keyboard that magnetically
+ * attaches to the bottom screen in one of two positions.  The EC tracks
+ * attachment state in a 2-bit field called BKBD and signals changes via WMI
+ * event 0xEB on the WM10 ACPI device (_UID "GMZN").
+ *
+ * BKBD values:
+ *   0 = keyboard detached
+ *   1 = keyboard docked on the top half of the bottom screen
+ *   2 = keyboard docked on the bottom half of the bottom screen
+ *   3 = reserved / not observed
+ *
+ * Two WMI interfaces are used (documented in embedded BMOF, WQDD, 20705 bytes):
+ *
+ *   LENOVO_BTKBD_EVENT (event GUID, 806BD2A2-...)
+ *     WmiDataId(1) uint32 Status — _WED(0xEB) returns EC.BKBD directly.
+ *     The notify callback receives BKBD as an integer; no separate query needed.
+ *
+ *   LENOVO_FEATURE_STATUS_DATA (block GUID, E7F300FA-...)
+ *     WmiDataId(1) uint32 IDs   = 0x00060000 (feature selector)
+ *     WmiDataId(2) uint32 Status = BKBD value
+ *     Used on probe and resume to read initial state.
+ *
+ * The event driver (LENOVO_BTKBD_EVENT) fires a notifier chain on each WMI
+ * event.  The block driver (LENOVO_FEATURE_STATUS_DATA) owns the input_dev
+ * and registers a notifier_block to receive those events, eliminating the
+ * need for shared global state or a mutex.
+ *
+ * SW_TABLET_MODE=1 is reported when the keyboard is detached;
+ * SW_TABLET_MODE=0 when docked in either position (keyboard present).
+ * The raw BKBD value is exposed via the sysfs attribute "keyboard_position".
+ *
+ * Copyright (C) 2026 Dave Carey <carvsdriver@gmail.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#include <linux/compiler_attributes.h>
+#include <linux/dev_printk.h>
+#include <linux/dmi.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/wmi.h>
+
+#define YB9_KBDOCK_EVENT_GUID	"806BD2A2-177B-481D-BFB5-3BA0BB4A2285"
+#define YB9_KBDOCK_QUERY_GUID	"E7F300FA-21CD-4003-ADAC-2696135982E6"
+
+/* BKBD encoding */
+#define BKBD_DETACHED		0
+
+/* LENOVO_FEATURE_STATUS_DATA feature selector */
+#define YB9_FEATURE_STATUS_ID	0x00060000u
+
+/*
+ * LENOVO_FEATURE_STATUS_DATA: 8-byte buffer {uint32 IDs, uint32 Status}.
+ * IDs is always 0x00060000; Status holds the BKBD value (0–3).
+ */
+struct lenovo_feature_status {
+	__le32 id;
+	__le32 status;
+} __packed;
+
+/* ------------------------------------------------------------------
+ * Notifier chain — event driver fires it, block driver listens
+ * ------------------------------------------------------------------ */
+
+static BLOCKING_NOTIFIER_HEAD(yb9_kbdock_chain_head);
+
+static void devm_yb9_kbdock_unregister_notifier(void *data)
+{
+	struct notifier_block *nb = data;
+
+	blocking_notifier_chain_unregister(&yb9_kbdock_chain_head, nb);
+}
+
+static int devm_yb9_kbdock_register_notifier(struct device *dev,
+					      struct notifier_block *nb)
+{
+	int ret;
+
+	ret = blocking_notifier_chain_register(&yb9_kbdock_chain_head, nb);
+	if (ret < 0)
+		return ret;
+
+	return devm_add_action_or_reset(dev, devm_yb9_kbdock_unregister_notifier, nb);
+}
+
+/* ------------------------------------------------------------------
+ * Block WMI driver — LENOVO_FEATURE_STATUS_DATA
+ * (owns input_dev, sysfs, PM resume)
+ * ------------------------------------------------------------------ */
+
+struct yb9_kbdock_data {
+	struct wmi_device	*wdev;
+	struct input_dev	*input_dev;
+	struct notifier_block	 nb;
+	spinlock_t		 lock;	/* protects input_report_switch + input_sync */
+};
+
+static int yb9_kbdock_query(struct yb9_kbdock_data *d, u32 *bkbd)
+{
+	struct wmi_buffer out;
+	int ret;
+
+	ret = wmidev_query_block(d->wdev, 0, &out,
+				 sizeof(struct lenovo_feature_status));
+	if (ret)
+		return ret;
+
+	struct lenovo_feature_status *fs __free(kfree) = out.data;
+
+	if (le32_to_cpu(fs->id) != YB9_FEATURE_STATUS_ID)
+		return -EIO;
+
+	*bkbd = le32_to_cpu(fs->status);
+	return 0;
+}
+
+static void yb9_kbdock_report(struct yb9_kbdock_data *d, u32 bkbd)
+{
+	int tablet = (bkbd == BKBD_DETACHED) ? 1 : 0;
+
+	spin_lock(&d->lock);
+	input_report_switch(d->input_dev, SW_TABLET_MODE, tablet);
+	input_sync(d->input_dev);
+	spin_unlock(&d->lock);
+	dev_dbg(&d->wdev->dev, "BKBD=%u SW_TABLET_MODE=%d\n", bkbd, tablet);
+}
+
+static int yb9_kbdock_sync(struct yb9_kbdock_data *d)
+{
+	u32 bkbd;
+	int ret;
+
+	ret = yb9_kbdock_query(d, &bkbd);
+	if (ret)
+		return ret;
+
+	yb9_kbdock_report(d, bkbd);
+	return 0;
+}
+
+static int yb9_kbdock_nb_call(struct notifier_block *nb,
+			       unsigned long bkbd, void *unused)
+{
+	struct yb9_kbdock_data *d =
+		container_of(nb, struct yb9_kbdock_data, nb);
+
+	yb9_kbdock_report(d, bkbd);
+	return NOTIFY_DONE;
+}
+
+static ssize_t keyboard_position_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct yb9_kbdock_data *d = dev_get_drvdata(dev);
+	u32 bkbd;
+	int ret;
+
+	ret = yb9_kbdock_query(d, &bkbd);
+	if (ret)
+		return ret;
+	return sysfs_emit(buf, "%u\n", bkbd);
+}
+static DEVICE_ATTR_RO(keyboard_position);
+
+static const struct attribute * const yb9_kbdock_attrs[] = {
+	&dev_attr_keyboard_position.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(yb9_kbdock);
+
+static int yb9_kbdock_resume(struct device *dev)
+{
+	struct yb9_kbdock_data *d = dev_get_drvdata(dev);
+
+	return yb9_kbdock_sync(d);
+}
+static DEFINE_SIMPLE_DEV_PM_OPS(yb9_kbdock_pm_ops, NULL, yb9_kbdock_resume);
+
+static int yb9_kbdock_block_probe(struct wmi_device *wdev, const void *ctx)
+{
+	struct yb9_kbdock_data *d;
+	struct input_dev *input_dev;
+	int ret;
+
+	d = devm_kzalloc(&wdev->dev, sizeof(*d), GFP_KERNEL);
+	if (!d)
+		return -ENOMEM;
+
+	d->wdev = wdev;
+	spin_lock_init(&d->lock);
+
+	input_dev = devm_input_allocate_device(&wdev->dev);
+	if (!input_dev)
+		return -ENOMEM;
+
+	input_dev->name		= "Lenovo Yoga Book 9 keyboard dock switch";
+	input_dev->phys		= YB9_KBDOCK_QUERY_GUID "/input0";
+	input_dev->id.bustype	= BUS_HOST;
+	input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
+
+	ret = input_register_device(input_dev);
+	if (ret)
+		return ret;
+
+	d->input_dev		= input_dev;
+	d->nb.notifier_call	= yb9_kbdock_nb_call;
+
+	ret = devm_yb9_kbdock_register_notifier(&wdev->dev, &d->nb);
+	if (ret)
+		return ret;
+
+	dev_set_drvdata(&wdev->dev, d);
+	return yb9_kbdock_sync(d);
+}
+
+static const struct wmi_device_id yb9_kbdock_block_id_table[] = {
+	{ .guid_string = YB9_KBDOCK_QUERY_GUID },
+	{ }
+};
+
+static struct wmi_driver yb9_kbdock_block_driver = {
+	.driver = {
+		.name		= "lenovo-yb9-kbdock",
+		.dev_groups	= yb9_kbdock_groups,
+		.pm		= pm_sleep_ptr(&yb9_kbdock_pm_ops),
+	},
+	.id_table	= yb9_kbdock_block_id_table,
+	.no_singleton	= true,
+	.probe		= yb9_kbdock_block_probe,
+};
+
+/* ------------------------------------------------------------------
+ * Event WMI driver — LENOVO_BTKBD_EVENT
+ * (fires the notifier chain on each WMI event)
+ * ------------------------------------------------------------------ */
+
+static void yb9_kbdock_notify_new(struct wmi_device *wdev,
+				  const struct wmi_buffer *data)
+{
+	/*
+	 * _WED(0xEB) returns EC.BKBD directly as a 32-bit integer
+	 * (LENOVO_BTKBD_EVENT WmiDataId(1) uint32 Status).
+	 */
+	if (!data || data->length < sizeof(__le32))
+		return;
+
+	u32 bkbd = le32_to_cpu(*(const __le32 *)data->data);
+
+	blocking_notifier_call_chain(&yb9_kbdock_chain_head, bkbd, NULL);
+}
+
+static const struct wmi_device_id yb9_kbdock_event_id_table[] = {
+	{ .guid_string = YB9_KBDOCK_EVENT_GUID },
+	{ }
+};
+MODULE_DEVICE_TABLE(wmi, yb9_kbdock_event_id_table);
+
+static struct wmi_driver yb9_kbdock_event_driver = {
+	.driver = {
+		.name = "lenovo-yb9-kbdock-event",
+	},
+	.id_table	= yb9_kbdock_event_id_table,
+	.no_singleton	= true,
+	.notify_new	= yb9_kbdock_notify_new,
+};
+
+/* ------------------------------------------------------------------
+ * Module init / exit
+ * ------------------------------------------------------------------ */
+
+static const struct dmi_system_id yb9_kbdock_dmi_table[] __initconst = {
+	{
+		/* Lenovo Yoga Book 9 14IAH10 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR,   "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
+		},
+	},
+	{ }
+};
+
+static int __init yb9_kbdock_init(void)
+{
+	int ret;
+
+	if (!dmi_check_system(yb9_kbdock_dmi_table))
+		return -ENODEV;
+
+	ret = wmi_driver_register(&yb9_kbdock_event_driver);
+	if (ret)
+		return ret;
+
+	ret = wmi_driver_register(&yb9_kbdock_block_driver);
+	if (ret) {
+		wmi_driver_unregister(&yb9_kbdock_event_driver);
+		return ret;
+	}
+
+	return 0;
+}
+module_init(yb9_kbdock_init);
+
+static void __exit yb9_kbdock_exit(void)
+{
+	wmi_driver_unregister(&yb9_kbdock_block_driver);
+	wmi_driver_unregister(&yb9_kbdock_event_driver);
+}
+module_exit(yb9_kbdock_exit);
+
+MODULE_AUTHOR("Dave Carey <carvsdriver@gmail.com>");
+MODULE_DESCRIPTION("Lenovo Yoga Book 9 keyboard dock detection");
+MODULE_LICENSE("GPL");
-- 
2.55.0


^ permalink raw reply related

* [PATCH v14 1/2] platform/x86/lenovo: lenovo-ymc: Suppress probe on Yoga Book 9 14IAH10
From: Dave Carey @ 2026-07-22 20:16 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: hdegoede, W_Armin, ilpo.jarvinen, jikos, linux-input,
	linux-kernel, Dave Carey
In-Reply-To: <20260722201659.874469-1-carvsdriver@gmail.com>

The Yoga Book 9 14IAH10 (DMI product name "83KJ") has a dedicated
yb9-kbdock WMI driver that registers an input device reporting
SW_TABLET_MODE to track the detachable Bluetooth keyboard.

lenovo-ymc also loads on this machine and creates an input node with the
SW_TABLET_MODE capability bit set.  For input switches, the presence of
the capability bit has semantic meaning: userspace (e.g. GNOME) reads
the switch state at startup from every node advertising the capability
and does not expect more than one such node.

Add a DMI match for the Yoga Book 9 14IAH10 to probe() so that
lenovo-ymc returns -ENODEV on this hardware, leaving yb9-kbdock as the
sole SW_TABLET_MODE source.  The ymc_ec_trigger EC write, the only
other action taken in response to a YMC event, is guarded by a separate
DMI table that excludes this machine; no other functionality is affected.

Signed-off-by: Dave Carey <carvsdriver@gmail.com>
---
 drivers/platform/x86/lenovo/ymc.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/platform/x86/lenovo/ymc.c b/drivers/platform/x86/lenovo/ymc.c
index 470d53e3c9d2..a13c89dcb285 100644
--- a/drivers/platform/x86/lenovo/ymc.c
+++ b/drivers/platform/x86/lenovo/ymc.c
@@ -24,6 +24,22 @@ static bool force;
 module_param(force, bool, 0444);
 MODULE_PARM_DESC(force, "Force loading on boards without a convertible DMI chassis-type");
 
+static const struct dmi_system_id lenovo_ymc_nosupport_dmi_table[] = {
+	{
+		/*
+		 * Yoga Book 9 14IAH10: SW_TABLET_MODE is reported by the
+		 * yb9-kbdock driver.  Suppress lenovo-ymc on this machine to
+		 * avoid userspace seeing two input nodes that both advertise
+		 * the SW_TABLET_MODE capability.
+		 */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
+		},
+	},
+	{ }
+};
+
 static const struct dmi_system_id allowed_chasis_types_dmi_table[] = {
 	{
 		.matches = {
@@ -101,6 +117,9 @@ static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
 	struct input_dev *input_dev;
 	int err;
 
+	if (dmi_check_system(lenovo_ymc_nosupport_dmi_table))
+		return -ENODEV;
+
 	if (!dmi_check_system(allowed_chasis_types_dmi_table)) {
 		if (force)
 			dev_info(&wdev->dev, "Force loading Lenovo YMC support\n");
-- 
2.55.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox