Building the Linux kernel with Clang and LLVM
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] percpu, local_lock: drop btf_type_tag where no compiler records it
@ 2026-11-10  1:22 Vineet Gupta
  2026-11-10  1:22 ` [RFC PATCH 1/2] percpu: do not put btf_type_tag on per-cpu variable declarations Vineet Gupta
  2026-11-10  1:22 ` [RFC PATCH 2/2] local_lock: do not put btf_type_tag on the lock guard type Vineet Gupta
  0 siblings, 2 replies; 6+ messages in thread
From: Vineet Gupta @ 2026-11-10  1:22 UTC (permalink / raw)
  To: dennis, tj, cl, peterz, mingo, will, boqun
  Cc: longman, linux-mm, linux-kernel, bpf, ast, andrii, daniel, acme,
	yonghong.song, eddyz87, martin.lau, nathan, ndesaulniers, morbo,
	justinstitt, llvm, Vineet Gupta

Hi,

Currently PAHOLE_HAS_BTF_TAG is gated on CC_IS_CLANG and the goal is
to allow CC_IS_GCC. There are GCC fixes (PR128479, PR125991) in the
making [1] but despite all of them an x86 defconfig still emits ~40k
instances of spew like:

  arch/x86/include/asm/processor.h:232:36: warning: ignoring attributes
  applied to 'struct cpuinfo_x86' after definition [-Wattributes]

where one is enough to break a -Werror build.

Making GCC stop warning here was proposed as PR128480 and rejected:
the suggested answer was -Wno-attributes, which the kernel would not
accept just to suppress this noise.

And even if warnings were to be ignored, the build would still fail
with 33 hard errors from a separate GCC bug, PR128477,

  include/linux/percpu-defs.h:216:45: error: initialization from
  pointer to non-enclosed address space

These come from the same tag on the same declarations, so this series
removes them too.

The root cause is where the tag is applied. BTF_TYPE_TAG(percpu)
records that a pointer points to per-cpu memory, but two places apply
it where the thing qualified is an aggregate, which no compiler can
tag (GCC or clang): every variant of an aggregate shares one DIE,
so there is nowhere to put it.

  - __PCPU_ATTRS(), so DECLARE_PER_CPU() and DEFINE_PER_CPU() put the tag
    on the per-cpu variable's own type;
  - the type argument of DEFINE_LOCK_GUARD_1() in local_lock.h, which
    cleanup.h turns into "typedef _type lock_<name>_t".

Nothing reads a tag in either position. The verifier matches "percpu" in
btf_ctx_access() and in the member walk in btf_struct_walk(), and both
check btf_type_is_ptr() first, so it only ever sees the tag on a pointer.

The aggregate case is the only one that warns. Where the per-cpu variable
is a pointer there is no diagnostic, but the record is wrong: GCC puts
the tag on the pointee, so

  static DEFINE_PER_CPU(struct bpf_hrtimer *, hrtimer_running);

comes out saying hrtimer_running is an ordinary pointer to per-cpu
memory, when in truth it lives in per-cpu memory and points at ordinary
memory. clang records no tag there at all.

The following two patches address this:

 - 1/2 provides a new variant of __percpu, __percpu_var which elides
   the btf tag. Switches __PCPU_ATTRS to use it.
 - 2/2: Applies __percpu_var in local_lock code.

Binary impact, x86 defconfig built with clang: patch 1 drops 280 BTF types
(140 TYPE_TAG and the 140 pointers carrying them, none referenced) and 3360
bytes of .BTF, with .text unchanged. Patch 2 changes nothing at all.

[1] https://inbox.sourceware.org/gcc-patches/20261109213600.772524-1-vineet.gupta@linux.dev/

Vineet Gupta (2):
  percpu: do not put btf_type_tag on per-cpu variable declarations
  local_lock: do not put btf_type_tag on the lock guard type

 include/linux/compiler_types.h | 8 ++++++++
 include/linux/local_lock.h     | 8 ++++----
 include/linux/percpu-defs.h    | 2 +-
 3 files changed, 13 insertions(+), 5 deletions(-)

-- 
2.53.0-Meta


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC PATCH 1/2] percpu: do not put btf_type_tag on per-cpu variable declarations
  2026-11-10  1:22 [RFC PATCH 0/2] percpu, local_lock: drop btf_type_tag where no compiler records it Vineet Gupta
@ 2026-11-10  1:22 ` Vineet Gupta
  2026-11-13 19:58   ` Chris Down
  2026-11-10  1:22 ` [RFC PATCH 2/2] local_lock: do not put btf_type_tag on the lock guard type Vineet Gupta
  1 sibling, 1 reply; 6+ messages in thread
From: Vineet Gupta @ 2026-11-10  1:22 UTC (permalink / raw)
  To: dennis, tj, cl, peterz, mingo, will, boqun
  Cc: longman, linux-mm, linux-kernel, bpf, ast, andrii, daniel, acme,
	yonghong.song, eddyz87, martin.lau, nathan, ndesaulniers, morbo,
	justinstitt, llvm, Vineet Gupta

__percpu carries BTF_TYPE_TAG(percpu), which records that a pointer
points to per-cpu memory. __PCPU_ATTRS() applies __percpu to the per-cpu
variable itself, so DECLARE_PER_CPU() and DEFINE_PER_CPU() put the tag in
declaration specifier position on the variable's own type.

arch/x86/include/asm/processor.h has

	DECLARE_PER_CPU_READ_MOSTLY(struct cpuinfo_x86, cpu_info);

which an x86 defconfig preprocesses to

	extern __seg_gs __attribute__((btf_type_tag("percpu")))
	__attribute__((section(".data..percpu" "..read_mostly")))
	__typeof__(struct cpuinfo_x86) cpu_info;

and GCC warns:

	processor.h:232:36: warning: ignoring attributes applied to
	'struct cpuinfo_x86' after definition [-Wattributes]

which -Werror turns into a build failure.

The message is confusing: this is a declaration, not a definition. But
the compiler is right to object to a type tag on the aggregate, and the
issue is fundamental: every variant of an aggregate shares one DIE, so
there is nowhere to put it. clang drops it silently and GCC warns.

The tag is meant to record that a pointer points to per-cpu memory, and
cpu_info is not a pointer.

And this gets worse: for a pointer-typed per-cpu variable GCC attaches
the tag to the pointee, so

	static DEFINE_PER_CPU(struct bpf_hrtimer *, hrtimer_running);

comes out as PTR -> TYPE_TAG 'percpu' -> struct bpf_hrtimer. The truth
is the other way round: hrtimer_running lives in per-cpu memory and
points at ordinary memory, while that record says it is an ordinary
pointer to per-cpu memory. clang emits no tag there at all.

Add __percpu_var, which skips the btf tag (__percpu under sparse) and
use it in __PCPU_ATTRS(). The cpu_info declaration above becomes

	extern __seg_gs
	__attribute__((section(".data..percpu" "..read_mostly")))
	__typeof__(struct cpuinfo_x86) cpu_info;

The pointer spelling, T __percpu *p, is unchanged and keeps its tag.

Before this patch the tag also qualified the variable's type, so &var
had type "T __percpu *", and a pointer does have a DIE of its own:
pahole emitted PTR -> TYPE_TAG 'percpu' -> T for each one.
this_cpu_ptr(&var) is that shape, and that is where the records came
from.

Those are the records an x86 defconfig built with clang loses: 280 BTF
types, 140 TYPE_TAG and the 140 pointers carrying them, with .BTF
shrinking from 6831660 to 6828300 bytes. They are the derived pointers
-- clang never recorded the declarations themselves. None of them were
reachable: a record points at a type only if something is declared with
it, and nothing is declared with these. The 794 places that do declare a
"T __percpu *" keep theirs -- the tags reached by a STRUCT, UNION,
ARRAY, FUNC_PROTO or another PTR number 117, 6, 3, 39 and 5 before and
after. .text is byte-identical.

Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
 include/linux/compiler_types.h | 8 ++++++++
 include/linux/percpu-defs.h    | 2 +-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index c5921f139007..0a18b3f10675 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -50,6 +50,7 @@
 # define __user		__attribute__((noderef, address_space(__user)))
 # define __iomem	__attribute__((noderef, address_space(__iomem)))
 # define __percpu	__attribute__((noderef, address_space(__percpu)))
+# define __percpu_var	__percpu
 # define __rcu		__attribute__((noderef, address_space(__rcu)))
 static inline void __chk_user_ptr(const volatile void __user *ptr) { }
 static inline void __chk_io_ptr(const volatile void __iomem *ptr) { }
@@ -68,7 +69,14 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { }
 #  define __user	BTF_TYPE_TAG(user)
 # endif
 # define __iomem
+/*
+ * btf_type_tag records "points to per-cpu memory", so it belongs on the
+ * pointer spelling only. Declaring the per-cpu variable itself puts the tag
+ * in declaration specifier position, where clang drops it and GCC attaches it
+ * to the wrong type, so __percpu_var leaves it out.
+ */
 # define __percpu	__percpu_qual BTF_TYPE_TAG(percpu)
+# define __percpu_var	__percpu_qual
 # define __rcu		BTF_TYPE_TAG(rcu)
 
 # define __chk_user_ptr(x)	(void)0
diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h
index dbe3267a0a13..f53fc430aaad 100644
--- a/include/linux/percpu-defs.h
+++ b/include/linux/percpu-defs.h
@@ -45,7 +45,7 @@
  * that section.
  */
 #define __PCPU_ATTRS(sec)						\
-	__percpu __attribute__((section(PER_CPU_BASE_SECTION sec)))	\
+	__percpu_var __attribute__((section(PER_CPU_BASE_SECTION sec)))	\
 	PER_CPU_ATTRIBUTES
 
 #define __PCPU_DUMMY_ATTRS						\
-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH 2/2] local_lock: do not put btf_type_tag on the lock guard type
  2026-11-10  1:22 [RFC PATCH 0/2] percpu, local_lock: drop btf_type_tag where no compiler records it Vineet Gupta
  2026-11-10  1:22 ` [RFC PATCH 1/2] percpu: do not put btf_type_tag on per-cpu variable declarations Vineet Gupta
@ 2026-11-10  1:22 ` Vineet Gupta
  2026-11-13 19:58   ` Chris Down
  1 sibling, 1 reply; 6+ messages in thread
From: Vineet Gupta @ 2026-11-10  1:22 UTC (permalink / raw)
  To: dennis, tj, cl, peterz, mingo, will, boqun
  Cc: longman, linux-mm, linux-kernel, bpf, ast, andrii, daniel, acme,
	yonghong.song, eddyz87, martin.lau, nathan, ndesaulniers, morbo,
	justinstitt, llvm, Vineet Gupta

local_lock.h applies __percpu the same way the per-cpu declarations do,
and hits the same problem as the previous patch:

	DEFINE_LOCK_GUARD_1(local_lock, local_lock_t __percpu,
			    local_lock(_T->lock),
			    local_unlock(_T->lock))

	local_lock.h:86:1: warning: ignoring attributes applied to
	'struct local_lock' after definition [-Wattributes]

which -Werror turns into a build failure, four times over: the same
pattern is repeated for local_lock_irq, local_lock_irqsave and
local_lock_nested_bh.

local_lock_t is a typedef of struct local_lock, and DEFINE_LOCK_GUARD_1()
hands its second argument to cleanup.h, which forms a typedef of it and a
guard struct holding a pointer to it. An x86 defconfig preprocesses the
line above to

	typedef local_lock_t __seg_gs
	__attribute__((btf_type_tag("percpu"))) lock_local_lock_t;

	typedef struct { local_lock_t __seg_gs
	__attribute__((btf_type_tag("percpu"))) *lock; } class_local_lock_t;

Both put the tag on local_lock_t, an aggregate: the first on a typedef of
it, the second on the pointee of the guard's member. Every variant of an
aggregate shares one DIE, so there is nowhere to record it, exactly as for
DECLARE_PER_CPU() of a struct. clang writes the annotation into DWARF
without complaint, but pahole does not translate it from that position,
so with today's toolchains it reaches BTF from neither compiler.

Use __percpu_var here too, which leaves

	typedef local_lock_t __seg_gs lock_local_lock_t;
	typedef struct { local_lock_t __seg_gs *lock; } class_local_lock_t;

Sparse keeps the __percpu address space, and the __releases() casts to
"local_lock_t __percpu **" further down are unchanged, so the pointer
spelling the tag exists for keeps it.

Unlike the per-cpu declarations this changes no BTF at all. A tag in that
position was never recorded, so there is nothing to remove, and the one
percpu TYPE_TAG that does reach a local_lock type comes from those casts,
which keep it. An x86 defconfig built with clang is identical with and
without this patch: 152551 BTF types and a 6828300 byte .BTF either way,
and .text is unchanged. The patch removes a GCC diagnostic and no more.

Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
 include/linux/local_lock.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/include/linux/local_lock.h b/include/linux/local_lock.h
index b8830148a859..9d3d62e46007 100644
--- a/include/linux/local_lock.h
+++ b/include/linux/local_lock.h
@@ -83,13 +83,13 @@
 #define local_trylock_irqsave(lock, flags)			\
 	__local_trylock_irqsave(__this_cpu_local_lock(lock), flags)
 
-DEFINE_LOCK_GUARD_1(local_lock, local_lock_t __percpu,
+DEFINE_LOCK_GUARD_1(local_lock, local_lock_t __percpu_var,
 		    local_lock(_T->lock),
 		    local_unlock(_T->lock))
-DEFINE_LOCK_GUARD_1(local_lock_irq, local_lock_t __percpu,
+DEFINE_LOCK_GUARD_1(local_lock_irq, local_lock_t __percpu_var,
 		    local_lock_irq(_T->lock),
 		    local_unlock_irq(_T->lock))
-DEFINE_LOCK_GUARD_1(local_lock_irqsave, local_lock_t __percpu,
+DEFINE_LOCK_GUARD_1(local_lock_irqsave, local_lock_t __percpu_var,
 		    local_lock_irqsave(_T->lock, _T->flags),
 		    local_unlock_irqrestore(_T->lock, _T->flags),
 		    unsigned long flags)
@@ -100,7 +100,7 @@ DEFINE_LOCK_GUARD_1(local_lock_irqsave, local_lock_t __percpu,
 #define local_unlock_nested_bh(_lock)				\
 	__local_unlock_nested_bh(__this_cpu_local_lock(_lock))
 
-DEFINE_LOCK_GUARD_1(local_lock_nested_bh, local_lock_t __percpu,
+DEFINE_LOCK_GUARD_1(local_lock_nested_bh, local_lock_t __percpu_var,
 		    local_lock_nested_bh(_T->lock),
 		    local_unlock_nested_bh(_T->lock))
 
-- 
2.53.0-Meta


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH 1/2] percpu: do not put btf_type_tag on per-cpu variable declarations
  2026-11-10  1:22 ` [RFC PATCH 1/2] percpu: do not put btf_type_tag on per-cpu variable declarations Vineet Gupta
@ 2026-11-13 19:58   ` Chris Down
  2026-11-13 22:36     ` Vineet Gupta
  0 siblings, 1 reply; 6+ messages in thread
From: Chris Down @ 2026-11-13 19:58 UTC (permalink / raw)
  To: Vineet Gupta
  Cc: dennis, tj, cl, peterz, mingo, will, boqun, longman, linux-mm,
	linux-kernel, bpf, ast, andrii, daniel, acme, yonghong.song,
	eddyz87, martin.lau, nathan, ndesaulniers, morbo, justinstitt,
	llvm

Hi Vineet,

Vineet Gupta <vineet.gupta@linux.dev> writes:
>The message is confusing: this is a declaration, not a definition. But
>the compiler is right to object to a type tag on the aggregate, and the
>issue is fundamental: every variant of an aggregate shares one DIE, so
>there is nowhere to put it. clang drops it silently and GCC warns.

Not a big deal, but the definition in GCC's message is the definition of
struct cpuinfo_x86, not of cpu_info. It's saying the attribute is being
applied to the struct type after the struct was already defined, which is
accurate. So maybe just drop that first sentence?

I've tested this with clang 23 and GCC 16. With clang, the same 280 types
and 3360 bytes of BTF go away. Likewise, with GCC, the per-cpu
declarations no longer warn, and the non-enclosed address space errors
are gone.

Once the commit message nits are fixed, please feel free to add:

Reviewed-by: Chris Down <chris@chrisdown.name>
Tested-by: Chris Down <chris@chrisdown.name>

Thanks!

Chris

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH 2/2] local_lock: do not put btf_type_tag on the lock guard type
  2026-11-10  1:22 ` [RFC PATCH 2/2] local_lock: do not put btf_type_tag on the lock guard type Vineet Gupta
@ 2026-11-13 19:58   ` Chris Down
  0 siblings, 0 replies; 6+ messages in thread
From: Chris Down @ 2026-11-13 19:58 UTC (permalink / raw)
  To: Vineet Gupta
  Cc: dennis, tj, cl, peterz, mingo, will, boqun, longman, linux-mm,
	linux-kernel, bpf, ast, andrii, daniel, acme, yonghong.song,
	eddyz87, martin.lau, nathan, ndesaulniers, morbo, justinstitt,
	llvm

Hi Vineet,

Vineet Gupta <vineet.gupta@linux.dev> writes:
>	typedef local_lock_t __seg_gs
>	__attribute__((btf_type_tag("percpu"))) lock_local_lock_t;
>
>	typedef struct { local_lock_t __seg_gs
>	__attribute__((btf_type_tag("percpu"))) *lock; } class_local_lock_t;
>
>Both put the tag on local_lock_t, an aggregate: the first on a typedef of
>it, the second on the pointee of the guard's member.

The typedef is the aggregate case, and it's the only one of the two that
GCC warns about. But the second one is just the "T __percpu *p" spelling
that 1/2 keeps the tag for. _T->lock really is a pointer to per-cpu memory,
since local_lock() passes it to this_cpu_ptr(). So the tag is correct
there. This patch drops it from the guard's lock member and the
constructor's argument as well, not only from the typedef. I think it
should keep the tag on those two and only drop it from the typedef.

>Every variant of an
>aggregate shares one DIE, so there is nowhere to record it, exactly as for
>DECLARE_PER_CPU() of a struct. clang writes the annotation into DWARF
>without complaint, but pahole does not translate it from that position,
>so with today's toolchains it reaches BTF from neither compiler.

That's true of the typedef, but not of the member. With clang 23 and
pahole 1.32 the lock member comes out as PTR -> TYPE_TAG 'percpu' ->
local_lock_t, the same as any other per-cpu pointer.

For the typedef, clang 23 puts the annotation on the typedef's own DIE, as
you say, so there is somewhere to record it. It's only pahole that doesn't
pick it up yet. You also mentioned GCC patches for type tags on typedefs
in the pahole thread [0]. Once those land, does this warning go away?

>Unlike the per-cpu declarations this changes no BTF at all. A tag in that
>position was never recorded, so there is nothing to remove, and the one
>percpu TYPE_TAG that does reach a local_lock type comes from those casts,
>which keep it. An x86 defconfig built with clang is identical with and
>without this patch: 152551 BTF types and a 6828300 byte .BTF either way,
>and .text is unchanged. The patch removes a GCC diagnostic and no more.

The defconfig BTF only stays the same because nothing in a defconfig uses
these guards. lib/test_context-analysis.c is the only user in the tree.
With CONFIG_CONTEXT_ANALYSIS_TEST=y, class_local_lock_t,
class_local_lock_irq_t, class_local_lock_irqsave_t and
class_local_lock_nested_bh_t all lose the percpu tag on lock with this
patch applied.

Nothing in BPF looks at those types today, so this doesn't break anything.
But it does remove correct annotations to get rid of a warning that is
only ever caused by the typedef itself. When the guards were added, the
type was only ever used as _type *. The typedef came later with
3931d4b98039 ("cleanup: Basic compatibility with context analysis"). If
GCC isn't going to accept the tag on a typedef, could this be limited to
that typedef in cleanup.h instead, so the guard's pointer keeps its tag?
As far as I can see, lock_##_name##_t is only ever used through a
pointer, in DECLARE_LOCK_GUARD_1_ATTRS().

[0] https://lore.kernel.org/r/252c53f3-3f06-40f4-9cd3-795eb298687c@linux.dev

Thanks!

Chris

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH 1/2] percpu: do not put btf_type_tag on per-cpu variable declarations
  2026-11-13 19:58   ` Chris Down
@ 2026-11-13 22:36     ` Vineet Gupta
  0 siblings, 0 replies; 6+ messages in thread
From: Vineet Gupta @ 2026-11-13 22:36 UTC (permalink / raw)
  To: Chris Down
  Cc: dennis, tj, cl, peterz, mingo, will, boqun, longman, linux-mm,
	linux-kernel, bpf, ast, andrii, daniel, acme, yonghong.song,
	eddyz87, martin.lau, nathan, ndesaulniers, morbo, justinstitt,
	llvm

On 11/13/26 11:58 AM, Chris Down wrote:
> Hi Vineet,
>
> Vineet Gupta <vineet.gupta@linux.dev> writes:
>> The message is confusing: this is a declaration, not a definition. But
>> the compiler is right to object to a type tag on the aggregate, and the
>> issue is fundamental: every variant of an aggregate shares one DIE, so
>> there is nowhere to put it. clang drops it silently and GCC warns.
> Not a big deal, but the definition in GCC's message is the definition of
> struct cpuinfo_x86, not of cpu_info. It's saying the attribute is being
> applied to the struct type after the struct was already defined, which is
> accurate. So maybe just drop that first sentence?

Oops, thanks for catching that. I misread which definition it meant. 
Dropping the first sentence in v2.

> I've tested this with clang 23 and GCC 16. With clang, the same 280 types
> and 3360 bytes of BTF go away. Likewise, with GCC, the per-cpu
> declarations no longer warn, and the non-enclosed address space errors
> are gone.

Thanks for verifying, with both the compilers.

> Once the commit message nits are fixed, please feel free to add:
>
> Reviewed-by: Chris Down <chris@chrisdown.name>
> Tested-by: Chris Down <chris@chrisdown.name>

Awesome, I'll add these in v2

Thx,
-Vineet

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-13 22:36 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-10  1:22 [RFC PATCH 0/2] percpu, local_lock: drop btf_type_tag where no compiler records it Vineet Gupta
2026-11-10  1:22 ` [RFC PATCH 1/2] percpu: do not put btf_type_tag on per-cpu variable declarations Vineet Gupta
2026-11-13 19:58   ` Chris Down
2026-11-13 22:36     ` Vineet Gupta
2026-11-10  1:22 ` [RFC PATCH 2/2] local_lock: do not put btf_type_tag on the lock guard type Vineet Gupta
2026-11-13 19:58   ` Chris Down

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