~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/lib/generic-radix-tree.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 
  2 #include <linux/atomic.h>
  3 #include <linux/export.h>
  4 #include <linux/generic-radix-tree.h>
  5 #include <linux/gfp.h>
  6 #include <linux/kmemleak.h>
  7 
  8 #define GENRADIX_ARY            (GENRADIX_NODE_SIZE / sizeof(struct genradix_node *))
  9 #define GENRADIX_ARY_SHIFT      ilog2(GENRADIX_ARY)
 10 
 11 struct genradix_node {
 12         union {
 13                 /* Interior node: */
 14                 struct genradix_node    *children[GENRADIX_ARY];
 15 
 16                 /* Leaf: */
 17                 u8                      data[GENRADIX_NODE_SIZE];
 18         };
 19 };
 20 
 21 static inline int genradix_depth_shift(unsigned depth)
 22 {
 23         return GENRADIX_NODE_SHIFT + GENRADIX_ARY_SHIFT * depth;
 24 }
 25 
 26 /*
 27  * Returns size (of data, in bytes) that a tree of a given depth holds:
 28  */
 29 static inline size_t genradix_depth_size(unsigned depth)
 30 {
 31         return 1UL << genradix_depth_shift(depth);
 32 }
 33 
 34 /* depth that's needed for a genradix that can address up to ULONG_MAX: */
 35 #define GENRADIX_MAX_DEPTH      \
 36         DIV_ROUND_UP(BITS_PER_LONG - GENRADIX_NODE_SHIFT, GENRADIX_ARY_SHIFT)
 37 
 38 #define GENRADIX_DEPTH_MASK                             \
 39         ((unsigned long) (roundup_pow_of_two(GENRADIX_MAX_DEPTH + 1) - 1))
 40 
 41 static inline unsigned genradix_root_to_depth(struct genradix_root *r)
 42 {
 43         return (unsigned long) r & GENRADIX_DEPTH_MASK;
 44 }
 45 
 46 static inline struct genradix_node *genradix_root_to_node(struct genradix_root *r)
 47 {
 48         return (void *) ((unsigned long) r & ~GENRADIX_DEPTH_MASK);
 49 }
 50 
 51 /*
 52  * Returns pointer to the specified byte @offset within @radix, or NULL if not
 53  * allocated
 54  */
 55 void *__genradix_ptr(struct __genradix *radix, size_t offset)
 56 {
 57         struct genradix_root *r = READ_ONCE(radix->root);
 58         struct genradix_node *n = genradix_root_to_node(r);
 59         unsigned level          = genradix_root_to_depth(r);
 60 
 61         if (ilog2(offset) >= genradix_depth_shift(level))
 62                 return NULL;
 63 
 64         while (1) {
 65                 if (!n)
 66                         return NULL;
 67                 if (!level)
 68                         break;
 69 
 70                 level--;
 71 
 72                 n = n->children[offset >> genradix_depth_shift(level)];
 73                 offset &= genradix_depth_size(level) - 1;
 74         }
 75 
 76         return &n->data[offset];
 77 }
 78 EXPORT_SYMBOL(__genradix_ptr);
 79 
 80 static inline struct genradix_node *genradix_alloc_node(gfp_t gfp_mask)
 81 {
 82         return kzalloc(GENRADIX_NODE_SIZE, gfp_mask);
 83 }
 84 
 85 static inline void genradix_free_node(struct genradix_node *node)
 86 {
 87         kfree(node);
 88 }
 89 
 90 /*
 91  * Returns pointer to the specified byte @offset within @radix, allocating it if
 92  * necessary - newly allocated slots are always zeroed out:
 93  */
 94 void *__genradix_ptr_alloc(struct __genradix *radix, size_t offset,
 95                            gfp_t gfp_mask)
 96 {
 97         struct genradix_root *v = READ_ONCE(radix->root);
 98         struct genradix_node *n, *new_node = NULL;
 99         unsigned level;
100 
101         /* Increase tree depth if necessary: */
102         while (1) {
103                 struct genradix_root *r = v, *new_root;
104 
105                 n       = genradix_root_to_node(r);
106                 level   = genradix_root_to_depth(r);
107 
108                 if (n && ilog2(offset) < genradix_depth_shift(level))
109                         break;
110 
111                 if (!new_node) {
112                         new_node = genradix_alloc_node(gfp_mask);
113                         if (!new_node)
114                                 return NULL;
115                 }
116 
117                 new_node->children[0] = n;
118                 new_root = ((struct genradix_root *)
119                             ((unsigned long) new_node | (n ? level + 1 : 0)));
120 
121                 if ((v = cmpxchg_release(&radix->root, r, new_root)) == r) {
122                         v = new_root;
123                         new_node = NULL;
124                 } else {
125                         new_node->children[0] = NULL;
126                 }
127         }
128 
129         while (level--) {
130                 struct genradix_node **p =
131                         &n->children[offset >> genradix_depth_shift(level)];
132                 offset &= genradix_depth_size(level) - 1;
133 
134                 n = READ_ONCE(*p);
135                 if (!n) {
136                         if (!new_node) {
137                                 new_node = genradix_alloc_node(gfp_mask);
138                                 if (!new_node)
139                                         return NULL;
140                         }
141 
142                         if (!(n = cmpxchg_release(p, NULL, new_node)))
143                                 swap(n, new_node);
144                 }
145         }
146 
147         if (new_node)
148                 genradix_free_node(new_node);
149 
150         return &n->data[offset];
151 }
152 EXPORT_SYMBOL(__genradix_ptr_alloc);
153 
154 void *__genradix_iter_peek(struct genradix_iter *iter,
155                            struct __genradix *radix,
156                            size_t objs_per_page)
157 {
158         struct genradix_root *r;
159         struct genradix_node *n;
160         unsigned level, i;
161 
162         if (iter->offset == SIZE_MAX)
163                 return NULL;
164 
165 restart:
166         r = READ_ONCE(radix->root);
167         if (!r)
168                 return NULL;
169 
170         n       = genradix_root_to_node(r);
171         level   = genradix_root_to_depth(r);
172 
173         if (ilog2(iter->offset) >= genradix_depth_shift(level))
174                 return NULL;
175 
176         while (level) {
177                 level--;
178 
179                 i = (iter->offset >> genradix_depth_shift(level)) &
180                         (GENRADIX_ARY - 1);
181 
182                 while (!n->children[i]) {
183                         size_t objs_per_ptr = genradix_depth_size(level);
184 
185                         if (iter->offset + objs_per_ptr < iter->offset) {
186                                 iter->offset    = SIZE_MAX;
187                                 iter->pos       = SIZE_MAX;
188                                 return NULL;
189                         }
190 
191                         i++;
192                         iter->offset = round_down(iter->offset + objs_per_ptr,
193                                                   objs_per_ptr);
194                         iter->pos = (iter->offset >> GENRADIX_NODE_SHIFT) *
195                                 objs_per_page;
196                         if (i == GENRADIX_ARY)
197                                 goto restart;
198                 }
199 
200                 n = n->children[i];
201         }
202 
203         return &n->data[iter->offset & (GENRADIX_NODE_SIZE - 1)];
204 }
205 EXPORT_SYMBOL(__genradix_iter_peek);
206 
207 void *__genradix_iter_peek_prev(struct genradix_iter *iter,
208                                 struct __genradix *radix,
209                                 size_t objs_per_page,
210                                 size_t obj_size_plus_page_remainder)
211 {
212         struct genradix_root *r;
213         struct genradix_node *n;
214         unsigned level, i;
215 
216         if (iter->offset == SIZE_MAX)
217                 return NULL;
218 
219 restart:
220         r = READ_ONCE(radix->root);
221         if (!r)
222                 return NULL;
223 
224         n       = genradix_root_to_node(r);
225         level   = genradix_root_to_depth(r);
226 
227         if (ilog2(iter->offset) >= genradix_depth_shift(level)) {
228                 iter->offset = genradix_depth_size(level);
229                 iter->pos = (iter->offset >> GENRADIX_NODE_SHIFT) * objs_per_page;
230 
231                 iter->offset -= obj_size_plus_page_remainder;
232                 iter->pos--;
233         }
234 
235         while (level) {
236                 level--;
237 
238                 i = (iter->offset >> genradix_depth_shift(level)) &
239                         (GENRADIX_ARY - 1);
240 
241                 while (!n->children[i]) {
242                         size_t objs_per_ptr = genradix_depth_size(level);
243 
244                         iter->offset = round_down(iter->offset, objs_per_ptr);
245                         iter->pos = (iter->offset >> GENRADIX_NODE_SHIFT) * objs_per_page;
246 
247                         if (!iter->offset)
248                                 return NULL;
249 
250                         iter->offset -= obj_size_plus_page_remainder;
251                         iter->pos--;
252 
253                         if (!i)
254                                 goto restart;
255                         --i;
256                 }
257 
258                 n = n->children[i];
259         }
260 
261         return &n->data[iter->offset & (GENRADIX_NODE_SIZE - 1)];
262 }
263 EXPORT_SYMBOL(__genradix_iter_peek_prev);
264 
265 static void genradix_free_recurse(struct genradix_node *n, unsigned level)
266 {
267         if (level) {
268                 unsigned i;
269 
270                 for (i = 0; i < GENRADIX_ARY; i++)
271                         if (n->children[i])
272                                 genradix_free_recurse(n->children[i], level - 1);
273         }
274 
275         genradix_free_node(n);
276 }
277 
278 int __genradix_prealloc(struct __genradix *radix, size_t size,
279                         gfp_t gfp_mask)
280 {
281         size_t offset;
282 
283         for (offset = 0; offset < size; offset += GENRADIX_NODE_SIZE)
284                 if (!__genradix_ptr_alloc(radix, offset, gfp_mask))
285                         return -ENOMEM;
286 
287         return 0;
288 }
289 EXPORT_SYMBOL(__genradix_prealloc);
290 
291 void __genradix_free(struct __genradix *radix)
292 {
293         struct genradix_root *r = xchg(&radix->root, NULL);
294 
295         genradix_free_recurse(genradix_root_to_node(r),
296                               genradix_root_to_depth(r));
297 }
298 EXPORT_SYMBOL(__genradix_free);
299 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php