diff mbox series

[net-next,v6,2/5] bitops: add non-atomic bitops for pointers

Message ID 20210702111825.491065-3-memxor@gmail.com (mailing list archive)
State Accepted
Delegated to: BPF
Headers show
Series Generic XDP improvements | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for net-next
netdev/subject_prefix success Link
netdev/cc_maintainers warning 8 maintainers not CCed: yhs@fb.com akpm@linux-foundation.org kpsingh@kernel.org andriy.shevchenko@linux.intel.com geert+renesas@glider.be songliubraving@fb.com richard.weiyang@linux.alibaba.com yury.norov@gmail.com
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 18923 this patch: 18923
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 75 lines checked
netdev/build_allmodconfig_warn success Errors and warnings before: 18364 this patch: 18364
netdev/header_inline success Link

Commit Message

Kumar Kartikeya Dwivedi July 2, 2021, 11:18 a.m. UTC
cpumap needs to set, clear, and test the lowest bit in skb pointer in
various places. To make these checks less noisy, add pointer friendly
bitop macros that also do some typechecking to sanitize the argument.

These wrap the non-atomic bitops __set_bit, __clear_bit, and test_bit
but for pointer arguments. Pointer's address has to be passed in and it
is treated as an unsigned long *, since width and representation of
pointer and unsigned long match on targets Linux supports. They are
prefixed with double underscore to indicate lack of atomicity.

Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 include/linux/bitops.h    | 50 +++++++++++++++++++++++++++++++++++++++
 include/linux/typecheck.h |  9 +++++++
 2 files changed, 59 insertions(+)

Comments

David Laight July 2, 2021, 2:47 p.m. UTC | #1
From: Kumar Kartikeya Dwivedi
> Sent: 02 July 2021 12:18
> 
> cpumap needs to set, clear, and test the lowest bit in skb pointer in
> various places. To make these checks less noisy, add pointer friendly
> bitop macros that also do some typechecking to sanitize the argument.

Would this work?
#define BIT_OP(val, op) ((typeof (val))((unsigned long)(val) op))

Should let you do:
	ptr = BIT_OP(ptr, | 1);
	ptr = BIT_OP(ptr, & ~1);
	if (BIT_OPT(ptr, & 1))
		...

See https://godbolt.org/z/E57aGK4js

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Kumar Kartikeya Dwivedi July 2, 2021, 4:11 p.m. UTC | #2
On Fri, Jul 02, 2021 at 08:17:47PM IST, David Laight wrote:
> From: Kumar Kartikeya Dwivedi
> > Sent: 02 July 2021 12:18
> >
> > cpumap needs to set, clear, and test the lowest bit in skb pointer in
> > various places. To make these checks less noisy, add pointer friendly
> > bitop macros that also do some typechecking to sanitize the argument.
>
> Would this work?
> #define BIT_OP(val, op) ((typeof (val))((unsigned long)(val) op))
>
> Should let you do:
> 	ptr = BIT_OP(ptr, | 1);
> 	ptr = BIT_OP(ptr, & ~1);
> 	if (BIT_OPT(ptr, & 1))
> 		...
>
> See https://godbolt.org/z/E57aGK4js
>

This certainly works, but my preference for keeping it this way was reusing the
existing infrastructure (which also has KASAN/KCSAN instrumentation) and avoids
UB while shifting. Ofcourse for this particular case, anything works, but if
putting this in bitops, I thought we should keep it as simple wrappers over
__set_bit/__clear_bit/test_bit.

Also, I compared codegen for both, and it looks pretty much the same to me...

See https://godbolt.org/z/s9cjEnYKj

Let me know if I'm missing something.

> 	David
>
> -
> Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
> Registration No: 1397386 (Wales)

--
Kartikeya
diff mbox series

Patch

diff --git a/include/linux/bitops.h b/include/linux/bitops.h
index 26bf15e6cd35..5e62e2383b7f 100644
--- a/include/linux/bitops.h
+++ b/include/linux/bitops.h
@@ -4,6 +4,7 @@ 
 
 #include <asm/types.h>
 #include <linux/bits.h>
+#include <linux/typecheck.h>
 
 #include <uapi/linux/kernel.h>
 
@@ -253,6 +254,55 @@  static __always_inline void __assign_bit(long nr, volatile unsigned long *addr,
 		__clear_bit(nr, addr);
 }
 
+/**
+ * __ptr_set_bit - Set bit in a pointer's value
+ * @nr: the bit to set
+ * @addr: the address of the pointer variable
+ *
+ * Example:
+ *	void *p = foo();
+ *	__ptr_set_bit(bit, &p);
+ */
+#define __ptr_set_bit(nr, addr)                         \
+	({                                              \
+		typecheck_pointer(*(addr));             \
+		__set_bit(nr, (unsigned long *)(addr)); \
+	})
+
+/**
+ * __ptr_clear_bit - Clear bit in a pointer's value
+ * @nr: the bit to clear
+ * @addr: the address of the pointer variable
+ *
+ * Example:
+ *	void *p = foo();
+ *	__ptr_clear_bit(bit, &p);
+ */
+#define __ptr_clear_bit(nr, addr)                         \
+	({                                                \
+		typecheck_pointer(*(addr));               \
+		__clear_bit(nr, (unsigned long *)(addr)); \
+	})
+
+/**
+ * __ptr_test_bit - Test bit in a pointer's value
+ * @nr: the bit to test
+ * @addr: the address of the pointer variable
+ *
+ * Example:
+ *	void *p = foo();
+ *	if (__ptr_test_bit(bit, &p)) {
+ *	        ...
+ *	} else {
+ *		...
+ *	}
+ */
+#define __ptr_test_bit(nr, addr)                       \
+	({                                             \
+		typecheck_pointer(*(addr));            \
+		test_bit(nr, (unsigned long *)(addr)); \
+	})
+
 #ifdef __KERNEL__
 
 #ifndef set_mask_bits
diff --git a/include/linux/typecheck.h b/include/linux/typecheck.h
index 20d310331eb5..46b15e2aaefb 100644
--- a/include/linux/typecheck.h
+++ b/include/linux/typecheck.h
@@ -22,4 +22,13 @@ 
 	(void)__tmp; \
 })
 
+/*
+ * Check at compile time that something is a pointer type.
+ */
+#define typecheck_pointer(x) \
+({	typeof(x) __dummy; \
+	(void)sizeof(*__dummy); \
+	1; \
+})
+
 #endif		/* TYPECHECK_H_INCLUDED */