1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * A tagged pointer implementation
4  */
5 #ifndef __EROFS_FS_TAGPTR_H
6 #define __EROFS_FS_TAGPTR_H
7 
8 #include <linux/types.h>
9 #include <linux/build_bug.h>
10 
11 /*
12  * the name of tagged pointer types are tagptr{1, 2, 3...}_t
13  * avoid directly using the internal structs __tagptr{1, 2, 3...}
14  */
15 #define __MAKE_TAGPTR(n) \
16 typedef struct __tagptr##n {	\
17 	uintptr_t v;	\
18 } tagptr##n##_t;
19 
20 __MAKE_TAGPTR(1)
21 __MAKE_TAGPTR(2)
22 __MAKE_TAGPTR(3)
23 __MAKE_TAGPTR(4)
24 
25 #undef __MAKE_TAGPTR
26 
27 extern void __compiletime_error("bad tagptr tags")
28 	__bad_tagptr_tags(void);
29 
30 extern void __compiletime_error("bad tagptr type")
31 	__bad_tagptr_type(void);
32 
33 /* fix the broken usage of "#define tagptr2_t tagptr3_t" by users */
34 #define __tagptr_mask_1(ptr, n)	\
35 	__builtin_types_compatible_p(typeof(ptr), struct __tagptr##n) ? \
36 		(1UL << (n)) - 1 :
37 
38 #define __tagptr_mask(ptr)	(\
39 	__tagptr_mask_1(ptr, 1) ( \
40 	__tagptr_mask_1(ptr, 2) ( \
41 	__tagptr_mask_1(ptr, 3) ( \
42 	__tagptr_mask_1(ptr, 4) ( \
43 	__bad_tagptr_type(), 0)))))
44 
45 /* generate a tagged pointer from a raw value */
46 #define tagptr_init(type, val) \
47 	((typeof(type)){ .v = (uintptr_t)(val) })
48 
49 /*
50  * directly cast a tagged pointer to the native pointer type, which
51  * could be used for backward compatibility of existing code.
52  */
53 #define tagptr_cast_ptr(tptr) ((void *)(tptr).v)
54 
55 /* encode tagged pointers */
56 #define tagptr_fold(type, ptr, _tags) ({ \
57 	const typeof(_tags) tags = (_tags); \
58 	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(type))) \
59 		__bad_tagptr_tags(); \
60 tagptr_init(type, (uintptr_t)(ptr) | tags); })
61 
62 /* decode tagged pointers */
63 #define tagptr_unfold_ptr(tptr) \
64 	((void *)((tptr).v & ~__tagptr_mask(tptr)))
65 
66 #define tagptr_unfold_tags(tptr) \
67 	((tptr).v & __tagptr_mask(tptr))
68 
69 /* operations for the tagger pointer */
70 #define tagptr_eq(_tptr1, _tptr2) ({ \
71 	typeof(_tptr1) tptr1 = (_tptr1); \
72 	typeof(_tptr2) tptr2 = (_tptr2); \
73 	(void)(&tptr1 == &tptr2); \
74 (tptr1).v == (tptr2).v; })
75 
76 /* lock-free CAS operation */
77 #define tagptr_cmpxchg(_ptptr, _o, _n) ({ \
78 	typeof(_ptptr) ptptr = (_ptptr); \
79 	typeof(_o) o = (_o); \
80 	typeof(_n) n = (_n); \
81 	(void)(&o == &n); \
82 	(void)(&o == ptptr); \
83 tagptr_init(o, cmpxchg(&ptptr->v, o.v, n.v)); })
84 
85 /* wrap WRITE_ONCE if atomic update is needed */
86 #define tagptr_replace_tags(_ptptr, tags) ({ \
87 	typeof(_ptptr) ptptr = (_ptptr); \
88 	*ptptr = tagptr_fold(*ptptr, tagptr_unfold_ptr(*ptptr), tags); \
89 *ptptr; })
90 
91 #define tagptr_set_tags(_ptptr, _tags) ({ \
92 	typeof(_ptptr) ptptr = (_ptptr); \
93 	const typeof(_tags) tags = (_tags); \
94 	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(*ptptr))) \
95 		__bad_tagptr_tags(); \
96 	ptptr->v |= tags; \
97 *ptptr; })
98 
99 #define tagptr_clear_tags(_ptptr, _tags) ({ \
100 	typeof(_ptptr) ptptr = (_ptptr); \
101 	const typeof(_tags) tags = (_tags); \
102 	if (__builtin_constant_p(tags) && (tags & ~__tagptr_mask(*ptptr))) \
103 		__bad_tagptr_tags(); \
104 	ptptr->v &= ~tags; \
105 *ptptr; })
106 
107 #endif	/* __EROFS_FS_TAGPTR_H */
108