diff options
Diffstat (limited to 'lib/xarray.c')
-rw-r--r-- | lib/xarray.c | 232 |
1 files changed, 230 insertions, 2 deletions
diff --git a/lib/xarray.c b/lib/xarray.c index 19cfcbc69a68..aa86c47e532f 100644 --- a/lib/xarray.c +++ b/lib/xarray.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * Author: Matthew Wilcox <willy@infradead.org> | 5 | * Author: Matthew Wilcox <willy@infradead.org> |
6 | */ | 6 | */ |
7 | 7 | ||
8 | #include <linux/bitmap.h> | ||
8 | #include <linux/export.h> | 9 | #include <linux/export.h> |
9 | #include <linux/xarray.h> | 10 | #include <linux/xarray.h> |
10 | 11 | ||
@@ -24,6 +25,48 @@ | |||
24 | * @entry refers to something stored in a slot in the xarray | 25 | * @entry refers to something stored in a slot in the xarray |
25 | */ | 26 | */ |
26 | 27 | ||
28 | static inline void xa_mark_set(struct xarray *xa, xa_mark_t mark) | ||
29 | { | ||
30 | if (!(xa->xa_flags & XA_FLAGS_MARK(mark))) | ||
31 | xa->xa_flags |= XA_FLAGS_MARK(mark); | ||
32 | } | ||
33 | |||
34 | static inline void xa_mark_clear(struct xarray *xa, xa_mark_t mark) | ||
35 | { | ||
36 | if (xa->xa_flags & XA_FLAGS_MARK(mark)) | ||
37 | xa->xa_flags &= ~(XA_FLAGS_MARK(mark)); | ||
38 | } | ||
39 | |||
40 | static inline unsigned long *node_marks(struct xa_node *node, xa_mark_t mark) | ||
41 | { | ||
42 | return node->marks[(__force unsigned)mark]; | ||
43 | } | ||
44 | |||
45 | static inline bool node_get_mark(struct xa_node *node, | ||
46 | unsigned int offset, xa_mark_t mark) | ||
47 | { | ||
48 | return test_bit(offset, node_marks(node, mark)); | ||
49 | } | ||
50 | |||
51 | /* returns true if the bit was set */ | ||
52 | static inline bool node_set_mark(struct xa_node *node, unsigned int offset, | ||
53 | xa_mark_t mark) | ||
54 | { | ||
55 | return __test_and_set_bit(offset, node_marks(node, mark)); | ||
56 | } | ||
57 | |||
58 | /* returns true if the bit was set */ | ||
59 | static inline bool node_clear_mark(struct xa_node *node, unsigned int offset, | ||
60 | xa_mark_t mark) | ||
61 | { | ||
62 | return __test_and_clear_bit(offset, node_marks(node, mark)); | ||
63 | } | ||
64 | |||
65 | static inline bool node_any_mark(struct xa_node *node, xa_mark_t mark) | ||
66 | { | ||
67 | return !bitmap_empty(node_marks(node, mark), XA_CHUNK_SIZE); | ||
68 | } | ||
69 | |||
27 | /* extracts the offset within this node from the index */ | 70 | /* extracts the offset within this node from the index */ |
28 | static unsigned int get_offset(unsigned long index, struct xa_node *node) | 71 | static unsigned int get_offset(unsigned long index, struct xa_node *node) |
29 | { | 72 | { |
@@ -119,6 +162,85 @@ void *xas_load(struct xa_state *xas) | |||
119 | EXPORT_SYMBOL_GPL(xas_load); | 162 | EXPORT_SYMBOL_GPL(xas_load); |
120 | 163 | ||
121 | /** | 164 | /** |
165 | * xas_get_mark() - Returns the state of this mark. | ||
166 | * @xas: XArray operation state. | ||
167 | * @mark: Mark number. | ||
168 | * | ||
169 | * Return: true if the mark is set, false if the mark is clear or @xas | ||
170 | * is in an error state. | ||
171 | */ | ||
172 | bool xas_get_mark(const struct xa_state *xas, xa_mark_t mark) | ||
173 | { | ||
174 | if (xas_invalid(xas)) | ||
175 | return false; | ||
176 | if (!xas->xa_node) | ||
177 | return xa_marked(xas->xa, mark); | ||
178 | return node_get_mark(xas->xa_node, xas->xa_offset, mark); | ||
179 | } | ||
180 | EXPORT_SYMBOL_GPL(xas_get_mark); | ||
181 | |||
182 | /** | ||
183 | * xas_set_mark() - Sets the mark on this entry and its parents. | ||
184 | * @xas: XArray operation state. | ||
185 | * @mark: Mark number. | ||
186 | * | ||
187 | * Sets the specified mark on this entry, and walks up the tree setting it | ||
188 | * on all the ancestor entries. Does nothing if @xas has not been walked to | ||
189 | * an entry, or is in an error state. | ||
190 | */ | ||
191 | void xas_set_mark(const struct xa_state *xas, xa_mark_t mark) | ||
192 | { | ||
193 | struct xa_node *node = xas->xa_node; | ||
194 | unsigned int offset = xas->xa_offset; | ||
195 | |||
196 | if (xas_invalid(xas)) | ||
197 | return; | ||
198 | |||
199 | while (node) { | ||
200 | if (node_set_mark(node, offset, mark)) | ||
201 | return; | ||
202 | offset = node->offset; | ||
203 | node = xa_parent_locked(xas->xa, node); | ||
204 | } | ||
205 | |||
206 | if (!xa_marked(xas->xa, mark)) | ||
207 | xa_mark_set(xas->xa, mark); | ||
208 | } | ||
209 | EXPORT_SYMBOL_GPL(xas_set_mark); | ||
210 | |||
211 | /** | ||
212 | * xas_clear_mark() - Clears the mark on this entry and its parents. | ||
213 | * @xas: XArray operation state. | ||
214 | * @mark: Mark number. | ||
215 | * | ||
216 | * Clears the specified mark on this entry, and walks back to the head | ||
217 | * attempting to clear it on all the ancestor entries. Does nothing if | ||
218 | * @xas has not been walked to an entry, or is in an error state. | ||
219 | */ | ||
220 | void xas_clear_mark(const struct xa_state *xas, xa_mark_t mark) | ||
221 | { | ||
222 | struct xa_node *node = xas->xa_node; | ||
223 | unsigned int offset = xas->xa_offset; | ||
224 | |||
225 | if (xas_invalid(xas)) | ||
226 | return; | ||
227 | |||
228 | while (node) { | ||
229 | if (!node_clear_mark(node, offset, mark)) | ||
230 | return; | ||
231 | if (node_any_mark(node, mark)) | ||
232 | return; | ||
233 | |||
234 | offset = node->offset; | ||
235 | node = xa_parent_locked(xas->xa, node); | ||
236 | } | ||
237 | |||
238 | if (xa_marked(xas->xa, mark)) | ||
239 | xa_mark_clear(xas->xa, mark); | ||
240 | } | ||
241 | EXPORT_SYMBOL_GPL(xas_clear_mark); | ||
242 | |||
243 | /** | ||
122 | * xa_init_flags() - Initialise an empty XArray with flags. | 244 | * xa_init_flags() - Initialise an empty XArray with flags. |
123 | * @xa: XArray. | 245 | * @xa: XArray. |
124 | * @flags: XA_FLAG values. | 246 | * @flags: XA_FLAG values. |
@@ -160,6 +282,112 @@ void *xa_load(struct xarray *xa, unsigned long index) | |||
160 | } | 282 | } |
161 | EXPORT_SYMBOL(xa_load); | 283 | EXPORT_SYMBOL(xa_load); |
162 | 284 | ||
285 | /** | ||
286 | * __xa_set_mark() - Set this mark on this entry while locked. | ||
287 | * @xa: XArray. | ||
288 | * @index: Index of entry. | ||
289 | * @mark: Mark number. | ||
290 | * | ||
291 | * Attempting to set a mark on a NULL entry does not succeed. | ||
292 | * | ||
293 | * Context: Any context. Expects xa_lock to be held on entry. | ||
294 | */ | ||
295 | void __xa_set_mark(struct xarray *xa, unsigned long index, xa_mark_t mark) | ||
296 | { | ||
297 | XA_STATE(xas, xa, index); | ||
298 | void *entry = xas_load(&xas); | ||
299 | |||
300 | if (entry) | ||
301 | xas_set_mark(&xas, mark); | ||
302 | } | ||
303 | EXPORT_SYMBOL_GPL(__xa_set_mark); | ||
304 | |||
305 | /** | ||
306 | * __xa_clear_mark() - Clear this mark on this entry while locked. | ||
307 | * @xa: XArray. | ||
308 | * @index: Index of entry. | ||
309 | * @mark: Mark number. | ||
310 | * | ||
311 | * Context: Any context. Expects xa_lock to be held on entry. | ||
312 | */ | ||
313 | void __xa_clear_mark(struct xarray *xa, unsigned long index, xa_mark_t mark) | ||
314 | { | ||
315 | XA_STATE(xas, xa, index); | ||
316 | void *entry = xas_load(&xas); | ||
317 | |||
318 | if (entry) | ||
319 | xas_clear_mark(&xas, mark); | ||
320 | } | ||
321 | EXPORT_SYMBOL_GPL(__xa_clear_mark); | ||
322 | |||
323 | /** | ||
324 | * xa_get_mark() - Inquire whether this mark is set on this entry. | ||
325 | * @xa: XArray. | ||
326 | * @index: Index of entry. | ||
327 | * @mark: Mark number. | ||
328 | * | ||
329 | * This function uses the RCU read lock, so the result may be out of date | ||
330 | * by the time it returns. If you need the result to be stable, use a lock. | ||
331 | * | ||
332 | * Context: Any context. Takes and releases the RCU lock. | ||
333 | * Return: True if the entry at @index has this mark set, false if it doesn't. | ||
334 | */ | ||
335 | bool xa_get_mark(struct xarray *xa, unsigned long index, xa_mark_t mark) | ||
336 | { | ||
337 | XA_STATE(xas, xa, index); | ||
338 | void *entry; | ||
339 | |||
340 | rcu_read_lock(); | ||
341 | entry = xas_start(&xas); | ||
342 | while (xas_get_mark(&xas, mark)) { | ||
343 | if (!xa_is_node(entry)) | ||
344 | goto found; | ||
345 | entry = xas_descend(&xas, xa_to_node(entry)); | ||
346 | } | ||
347 | rcu_read_unlock(); | ||
348 | return false; | ||
349 | found: | ||
350 | rcu_read_unlock(); | ||
351 | return true; | ||
352 | } | ||
353 | EXPORT_SYMBOL(xa_get_mark); | ||
354 | |||
355 | /** | ||
356 | * xa_set_mark() - Set this mark on this entry. | ||
357 | * @xa: XArray. | ||
358 | * @index: Index of entry. | ||
359 | * @mark: Mark number. | ||
360 | * | ||
361 | * Attempting to set a mark on a NULL entry does not succeed. | ||
362 | * | ||
363 | * Context: Process context. Takes and releases the xa_lock. | ||
364 | */ | ||
365 | void xa_set_mark(struct xarray *xa, unsigned long index, xa_mark_t mark) | ||
366 | { | ||
367 | xa_lock(xa); | ||
368 | __xa_set_mark(xa, index, mark); | ||
369 | xa_unlock(xa); | ||
370 | } | ||
371 | EXPORT_SYMBOL(xa_set_mark); | ||
372 | |||
373 | /** | ||
374 | * xa_clear_mark() - Clear this mark on this entry. | ||
375 | * @xa: XArray. | ||
376 | * @index: Index of entry. | ||
377 | * @mark: Mark number. | ||
378 | * | ||
379 | * Clearing a mark always succeeds. | ||
380 | * | ||
381 | * Context: Process context. Takes and releases the xa_lock. | ||
382 | */ | ||
383 | void xa_clear_mark(struct xarray *xa, unsigned long index, xa_mark_t mark) | ||
384 | { | ||
385 | xa_lock(xa); | ||
386 | __xa_clear_mark(xa, index, mark); | ||
387 | xa_unlock(xa); | ||
388 | } | ||
389 | EXPORT_SYMBOL(xa_clear_mark); | ||
390 | |||
163 | #ifdef XA_DEBUG | 391 | #ifdef XA_DEBUG |
164 | void xa_dump_node(const struct xa_node *node) | 392 | void xa_dump_node(const struct xa_node *node) |
165 | { | 393 | { |
@@ -230,8 +458,8 @@ void xa_dump(const struct xarray *xa) | |||
230 | unsigned int shift = 0; | 458 | unsigned int shift = 0; |
231 | 459 | ||
232 | pr_info("xarray: %px head %px flags %x marks %d %d %d\n", xa, entry, | 460 | pr_info("xarray: %px head %px flags %x marks %d %d %d\n", xa, entry, |
233 | xa->xa_flags, radix_tree_tagged(xa, 0), | 461 | xa->xa_flags, xa_marked(xa, XA_MARK_0), |
234 | radix_tree_tagged(xa, 1), radix_tree_tagged(xa, 2)); | 462 | xa_marked(xa, XA_MARK_1), xa_marked(xa, XA_MARK_2)); |
235 | if (xa_is_node(entry)) | 463 | if (xa_is_node(entry)) |
236 | shift = xa_to_node(entry)->shift + XA_CHUNK_SHIFT; | 464 | shift = xa_to_node(entry)->shift + XA_CHUNK_SHIFT; |
237 | xa_dump_entry(entry, 0, shift); | 465 | xa_dump_entry(entry, 0, shift); |