diff options
Diffstat (limited to 'lib/xarray.c')
-rw-r--r-- | lib/xarray.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/lib/xarray.c b/lib/xarray.c index 862f4c64c754..19cfcbc69a68 100644 --- a/lib/xarray.c +++ b/lib/xarray.c | |||
@@ -24,6 +24,100 @@ | |||
24 | * @entry refers to something stored in a slot in the xarray | 24 | * @entry refers to something stored in a slot in the xarray |
25 | */ | 25 | */ |
26 | 26 | ||
27 | /* extracts the offset within this node from the index */ | ||
28 | static unsigned int get_offset(unsigned long index, struct xa_node *node) | ||
29 | { | ||
30 | return (index >> node->shift) & XA_CHUNK_MASK; | ||
31 | } | ||
32 | |||
33 | /* move the index either forwards (find) or backwards (sibling slot) */ | ||
34 | static void xas_move_index(struct xa_state *xas, unsigned long offset) | ||
35 | { | ||
36 | unsigned int shift = xas->xa_node->shift; | ||
37 | xas->xa_index &= ~XA_CHUNK_MASK << shift; | ||
38 | xas->xa_index += offset << shift; | ||
39 | } | ||
40 | |||
41 | static void *set_bounds(struct xa_state *xas) | ||
42 | { | ||
43 | xas->xa_node = XAS_BOUNDS; | ||
44 | return NULL; | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * Starts a walk. If the @xas is already valid, we assume that it's on | ||
49 | * the right path and just return where we've got to. If we're in an | ||
50 | * error state, return NULL. If the index is outside the current scope | ||
51 | * of the xarray, return NULL without changing @xas->xa_node. Otherwise | ||
52 | * set @xas->xa_node to NULL and return the current head of the array. | ||
53 | */ | ||
54 | static void *xas_start(struct xa_state *xas) | ||
55 | { | ||
56 | void *entry; | ||
57 | |||
58 | if (xas_valid(xas)) | ||
59 | return xas_reload(xas); | ||
60 | if (xas_error(xas)) | ||
61 | return NULL; | ||
62 | |||
63 | entry = xa_head(xas->xa); | ||
64 | if (!xa_is_node(entry)) { | ||
65 | if (xas->xa_index) | ||
66 | return set_bounds(xas); | ||
67 | } else { | ||
68 | if ((xas->xa_index >> xa_to_node(entry)->shift) > XA_CHUNK_MASK) | ||
69 | return set_bounds(xas); | ||
70 | } | ||
71 | |||
72 | xas->xa_node = NULL; | ||
73 | return entry; | ||
74 | } | ||
75 | |||
76 | static void *xas_descend(struct xa_state *xas, struct xa_node *node) | ||
77 | { | ||
78 | unsigned int offset = get_offset(xas->xa_index, node); | ||
79 | void *entry = xa_entry(xas->xa, node, offset); | ||
80 | |||
81 | xas->xa_node = node; | ||
82 | if (xa_is_sibling(entry)) { | ||
83 | offset = xa_to_sibling(entry); | ||
84 | entry = xa_entry(xas->xa, node, offset); | ||
85 | } | ||
86 | |||
87 | xas->xa_offset = offset; | ||
88 | return entry; | ||
89 | } | ||
90 | |||
91 | /** | ||
92 | * xas_load() - Load an entry from the XArray (advanced). | ||
93 | * @xas: XArray operation state. | ||
94 | * | ||
95 | * Usually walks the @xas to the appropriate state to load the entry | ||
96 | * stored at xa_index. However, it will do nothing and return %NULL if | ||
97 | * @xas is in an error state. xas_load() will never expand the tree. | ||
98 | * | ||
99 | * If the xa_state is set up to operate on a multi-index entry, xas_load() | ||
100 | * may return %NULL or an internal entry, even if there are entries | ||
101 | * present within the range specified by @xas. | ||
102 | * | ||
103 | * Context: Any context. The caller should hold the xa_lock or the RCU lock. | ||
104 | * Return: Usually an entry in the XArray, but see description for exceptions. | ||
105 | */ | ||
106 | void *xas_load(struct xa_state *xas) | ||
107 | { | ||
108 | void *entry = xas_start(xas); | ||
109 | |||
110 | while (xa_is_node(entry)) { | ||
111 | struct xa_node *node = xa_to_node(entry); | ||
112 | |||
113 | if (xas->xa_shift > node->shift) | ||
114 | break; | ||
115 | entry = xas_descend(xas, node); | ||
116 | } | ||
117 | return entry; | ||
118 | } | ||
119 | EXPORT_SYMBOL_GPL(xas_load); | ||
120 | |||
27 | /** | 121 | /** |
28 | * xa_init_flags() - Initialise an empty XArray with flags. | 122 | * xa_init_flags() - Initialise an empty XArray with flags. |
29 | * @xa: XArray. | 123 | * @xa: XArray. |
@@ -42,3 +136,104 @@ void xa_init_flags(struct xarray *xa, gfp_t flags) | |||
42 | xa->xa_head = NULL; | 136 | xa->xa_head = NULL; |
43 | } | 137 | } |
44 | EXPORT_SYMBOL(xa_init_flags); | 138 | EXPORT_SYMBOL(xa_init_flags); |
139 | |||
140 | /** | ||
141 | * xa_load() - Load an entry from an XArray. | ||
142 | * @xa: XArray. | ||
143 | * @index: index into array. | ||
144 | * | ||
145 | * Context: Any context. Takes and releases the RCU lock. | ||
146 | * Return: The entry at @index in @xa. | ||
147 | */ | ||
148 | void *xa_load(struct xarray *xa, unsigned long index) | ||
149 | { | ||
150 | XA_STATE(xas, xa, index); | ||
151 | void *entry; | ||
152 | |||
153 | rcu_read_lock(); | ||
154 | do { | ||
155 | entry = xas_load(&xas); | ||
156 | } while (xas_retry(&xas, entry)); | ||
157 | rcu_read_unlock(); | ||
158 | |||
159 | return entry; | ||
160 | } | ||
161 | EXPORT_SYMBOL(xa_load); | ||
162 | |||
163 | #ifdef XA_DEBUG | ||
164 | void xa_dump_node(const struct xa_node *node) | ||
165 | { | ||
166 | unsigned i, j; | ||
167 | |||
168 | if (!node) | ||
169 | return; | ||
170 | if ((unsigned long)node & 3) { | ||
171 | pr_cont("node %px\n", node); | ||
172 | return; | ||
173 | } | ||
174 | |||
175 | pr_cont("node %px %s %d parent %px shift %d count %d values %d " | ||
176 | "array %px list %px %px marks", | ||
177 | node, node->parent ? "offset" : "max", node->offset, | ||
178 | node->parent, node->shift, node->count, node->nr_values, | ||
179 | node->array, node->private_list.prev, node->private_list.next); | ||
180 | for (i = 0; i < XA_MAX_MARKS; i++) | ||
181 | for (j = 0; j < XA_MARK_LONGS; j++) | ||
182 | pr_cont(" %lx", node->marks[i][j]); | ||
183 | pr_cont("\n"); | ||
184 | } | ||
185 | |||
186 | void xa_dump_index(unsigned long index, unsigned int shift) | ||
187 | { | ||
188 | if (!shift) | ||
189 | pr_info("%lu: ", index); | ||
190 | else if (shift >= BITS_PER_LONG) | ||
191 | pr_info("0-%lu: ", ~0UL); | ||
192 | else | ||
193 | pr_info("%lu-%lu: ", index, index | ((1UL << shift) - 1)); | ||
194 | } | ||
195 | |||
196 | void xa_dump_entry(const void *entry, unsigned long index, unsigned long shift) | ||
197 | { | ||
198 | if (!entry) | ||
199 | return; | ||
200 | |||
201 | xa_dump_index(index, shift); | ||
202 | |||
203 | if (xa_is_node(entry)) { | ||
204 | if (shift == 0) { | ||
205 | pr_cont("%px\n", entry); | ||
206 | } else { | ||
207 | unsigned long i; | ||
208 | struct xa_node *node = xa_to_node(entry); | ||
209 | xa_dump_node(node); | ||
210 | for (i = 0; i < XA_CHUNK_SIZE; i++) | ||
211 | xa_dump_entry(node->slots[i], | ||
212 | index + (i << node->shift), node->shift); | ||
213 | } | ||
214 | } else if (xa_is_value(entry)) | ||
215 | pr_cont("value %ld (0x%lx) [%px]\n", xa_to_value(entry), | ||
216 | xa_to_value(entry), entry); | ||
217 | else if (!xa_is_internal(entry)) | ||
218 | pr_cont("%px\n", entry); | ||
219 | else if (xa_is_retry(entry)) | ||
220 | pr_cont("retry (%ld)\n", xa_to_internal(entry)); | ||
221 | else if (xa_is_sibling(entry)) | ||
222 | pr_cont("sibling (slot %ld)\n", xa_to_sibling(entry)); | ||
223 | else | ||
224 | pr_cont("UNKNOWN ENTRY (%px)\n", entry); | ||
225 | } | ||
226 | |||
227 | void xa_dump(const struct xarray *xa) | ||
228 | { | ||
229 | void *entry = xa->xa_head; | ||
230 | unsigned int shift = 0; | ||
231 | |||
232 | pr_info("xarray: %px head %px flags %x marks %d %d %d\n", xa, entry, | ||
233 | xa->xa_flags, radix_tree_tagged(xa, 0), | ||
234 | radix_tree_tagged(xa, 1), radix_tree_tagged(xa, 2)); | ||
235 | if (xa_is_node(entry)) | ||
236 | shift = xa_to_node(entry)->shift + XA_CHUNK_SHIFT; | ||
237 | xa_dump_entry(entry, 0, shift); | ||
238 | } | ||
239 | #endif | ||