diff mbox series

[v3,2/6] compiler.h: Introduce TYPEOF_UNQUAL() macro

Message ID 20241208204708.3742696-3-ubizjak@gmail.com (mailing list archive)
State Not Applicable
Headers show
Series Enable strict percpu address space checks | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Uros Bizjak Dec. 8, 2024, 8:45 p.m. UTC
Define TYPEOF_UNQUAL() to use __typeof_unqual__() as typeof operator
when available, to return unqualified type of the expression.

Current version of sparse doesn't know anything about __typeof_unqual__()
operator. Avoid the usage of __typeof_unqual__() when sparse checking
is active to prevent sparse errors with unknowing keyword.

Signed-off-by: Uros Bizjak <ubizjak@gmail.com>
Acked-by: Nadav Amit <nadav.amit@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Dennis Zhou <dennis@kernel.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Christoph Lameter <cl@linux.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Peter Zijlstra <peterz@infradead.org
---
 include/linux/compiler.h | 13 +++++++++++++
 init/Kconfig             |  3 +++
 2 files changed, 16 insertions(+)

Comments

Peter Zijlstra Dec. 9, 2024, 11:30 a.m. UTC | #1
On Sun, Dec 08, 2024 at 09:45:17PM +0100, Uros Bizjak wrote:
> Define TYPEOF_UNQUAL() to use __typeof_unqual__() as typeof operator
> when available, to return unqualified type of the expression.
> 
> Current version of sparse doesn't know anything about __typeof_unqual__()
> operator. Avoid the usage of __typeof_unqual__() when sparse checking
> is active to prevent sparse errors with unknowing keyword.

Ooooh, new toys.

I suppose __unqual_scalar_typeof() wants to be using this when
available?
Uros Bizjak Dec. 9, 2024, 1:01 p.m. UTC | #2
On Mon, Dec 9, 2024 at 12:30 PM Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Sun, Dec 08, 2024 at 09:45:17PM +0100, Uros Bizjak wrote:
> > Define TYPEOF_UNQUAL() to use __typeof_unqual__() as typeof operator
> > when available, to return unqualified type of the expression.
> >
> > Current version of sparse doesn't know anything about __typeof_unqual__()
> > operator. Avoid the usage of __typeof_unqual__() when sparse checking
> > is active to prevent sparse errors with unknowing keyword.
>
> Ooooh, new toys.
>
> I suppose __unqual_scalar_typeof() wants to be using this when
> available?

Yes, the attached patch compiles and boots OK.

Uros.
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index 5d6544545658..87a9ce3ebd13 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -486,15 +486,19 @@ struct ftrace_likely_data {
  * __unqual_scalar_typeof(x) - Declare an unqualified scalar type, leaving
  *			       non-scalar types unchanged.
  */
+
+#if defined(CONFIG_CC_HAS_TYPEOF_UNQUAL) && !defined(__CHECKER__)
+# define __unqual_scalar_typeof(x) __typeof_unqual__(x)
+#else
 /*
  * Prefer C11 _Generic for better compile-times and simpler code. Note: 'char'
  * is not type-compatible with 'signed char', and we define a separate case.
  */
-#define __scalar_type_to_expr_cases(type)				\
+ #define __scalar_type_to_expr_cases(type)				\
 		unsigned type:	(unsigned type)0,			\
 		signed type:	(signed type)0
 
-#define __unqual_scalar_typeof(x) typeof(				\
+ #define __unqual_scalar_typeof(x) typeof(				\
 		_Generic((x),						\
 			 char:	(char)0,				\
 			 __scalar_type_to_expr_cases(char),		\
@@ -503,6 +507,7 @@ struct ftrace_likely_data {
 			 __scalar_type_to_expr_cases(long),		\
 			 __scalar_type_to_expr_cases(long long),	\
 			 default: (x)))
+#endif
 
 /* Is this type a native word size -- useful for atomic operations */
 #define __native_word(t) \
Uros Bizjak Dec. 10, 2024, 4:37 p.m. UTC | #3
On Mon, Dec 9, 2024 at 12:30 PM Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Sun, Dec 08, 2024 at 09:45:17PM +0100, Uros Bizjak wrote:
> > Define TYPEOF_UNQUAL() to use __typeof_unqual__() as typeof operator
> > when available, to return unqualified type of the expression.
> >
> > Current version of sparse doesn't know anything about __typeof_unqual__()
> > operator. Avoid the usage of __typeof_unqual__() when sparse checking
> > is active to prevent sparse errors with unknowing keyword.
>
> Ooooh, new toys.
>
> I suppose __unqual_scalar_typeof() wants to be using this when
> available?

Not only that, the new toy enables clang to check kernel's address
spaces in a generic way using address_space attribute.

Please find attached a follow-up patch that enables __percpu checks
for all targets, supported by clang. Clang is a little bit pickier
than gcc about named address space declarations (it warns for use of
duplicated address space attribute), so the patch in addition to the
obvious

+#  define __percpu_qual        __attribute__((address_space(3)))

also fixes a couple of macros that could result in a duplicated
address space attribute.

The patch, applied as a follow-up to the series, survives allyesconfig
compilation with clang-19 and produces a bootable kernel. The patch
was tested only for x86_64 target, for other targets a couple of
trivial fixes would be necessary (a cast or a substitution of typeof()
with TYPEOF_UNQUAL()).

AFAICS, the same approach using clang's address_space attribute can be
implemented to also check other address spaces: __user, __iommu  and
__rcu.

Uros.
diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h
index 02aeca21479a..4109d828a564 100644
--- a/include/asm-generic/percpu.h
+++ b/include/asm-generic/percpu.h
@@ -16,7 +16,12 @@
  * space qualifier).
  */
 #ifndef __percpu_qual
-# define __percpu_qual
+# if __has_attribute(address_space) && \
+     defined(CONFIG_CC_HAS_TYPEOF_UNQUAL) && !defined(__CHECKER__)
+#  define __percpu_qual		__attribute__((address_space(3)))
+# else
+#  define __percpu_qual
+# endif
 #endif
 
 #ifdef CONFIG_SMP
diff --git a/include/linux/device.h b/include/linux/device.h
index 667cb6db9019..1d6a55d5250a 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -431,9 +431,9 @@ static inline int __devm_add_action_or_reset(struct device *dev, void (*action)(
  * RETURNS:
  * Pointer to allocated memory on success, NULL on failure.
  */
-#define devm_alloc_percpu(dev, type)      \
-	((typeof(type) __percpu *)__devm_alloc_percpu((dev), sizeof(type), \
-						      __alignof__(type)))
+#define devm_alloc_percpu(dev, type)				    \
+	((TYPEOF_UNQUAL(type) __percpu *)__devm_alloc_percpu((dev), \
+				sizeof(type), __alignof__(type)))
 
 void __percpu *__devm_alloc_percpu(struct device *dev, size_t size,
 				   size_t align);
diff --git a/include/linux/percpu.h b/include/linux/percpu.h
index 52b5ea663b9f..c3bf040aba66 100644
--- a/include/linux/percpu.h
+++ b/include/linux/percpu.h
@@ -148,13 +148,13 @@ extern void __percpu *pcpu_alloc_noprof(size_t size, size_t align, bool reserved
 	alloc_hooks(pcpu_alloc_noprof(_size, _align, true, GFP_KERNEL))
 
 #define alloc_percpu_gfp(type, gfp)					\
-	(typeof(type) __percpu *)__alloc_percpu_gfp(sizeof(type),	\
+	(TYPEOF_UNQUAL(type) __percpu *)__alloc_percpu_gfp(sizeof(type), \
 						__alignof__(type), gfp)
 #define alloc_percpu(type)						\
-	(typeof(type) __percpu *)__alloc_percpu(sizeof(type),		\
+	(TYPEOF_UNQUAL(type) __percpu *)__alloc_percpu(sizeof(type),	\
 						__alignof__(type))
 #define alloc_percpu_noprof(type)					\
-	((typeof(type) __percpu *)pcpu_alloc_noprof(sizeof(type),	\
+	((TYPEOF_UNQUAL(type) __percpu *)pcpu_alloc_noprof(sizeof(type), \
 					__alignof__(type), false, GFP_KERNEL))
 
 extern void free_percpu(void __percpu *__pdata);
diff mbox series

Patch

diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 469a64dd6495..ec0429d7a153 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -321,6 +321,19 @@  static inline void *offset_to_ptr(const int *off)
  */
 #define prevent_tail_call_optimization()	mb()
 
+/*
+ * Define TYPEOF_UNQUAL() to use __typeof_unqual__() as typeof
+ * operator when available, to return unqualified type of the exp.
+ *
+ * XXX: Remove test for __CHECKER__ once
+ * sparse learns about __typeof_unqual__.
+ */
+#if defined(CONFIG_CC_HAS_TYPEOF_UNQUAL) && !defined(__CHECKER__)
+# define TYPEOF_UNQUAL(exp) __typeof_unqual__(exp)
+#else
+# define TYPEOF_UNQUAL(exp) __typeof__(exp)
+#endif
+
 #include <asm/rwonce.h>
 
 #endif /* __LINUX_COMPILER_H */
diff --git a/init/Kconfig b/init/Kconfig
index a20e6efd3f0f..c1f9eb3d5f2e 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -894,6 +894,9 @@  config ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
 config CC_HAS_INT128
 	def_bool !$(cc-option,$(m64-flag) -D__SIZEOF_INT128__=0) && 64BIT
 
+config CC_HAS_TYPEOF_UNQUAL
+	def_bool $(success,echo 'int foo (int a) { __typeof_unqual__(a) b = a; return b; }' | $(CC) -x c - -S -o /dev/null)
+
 config CC_IMPLICIT_FALLTHROUGH
 	string
 	default "-Wimplicit-fallthrough=5" if CC_IS_GCC && $(cc-option,-Wimplicit-fallthrough=5)