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