Commit | Line | Data |
---|---|---|
00131368 MD |
1 | /* |
2 | * urcu-rbtree.c | |
3 | * | |
4 | * Userspace RCU library - Red-Black Tree | |
5 | * | |
6 | * Copyright (c) 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
7 | * | |
8 | * This library is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License as published by the Free Software Foundation; either | |
11 | * version 2.1 of the License, or (at your option) any later version. | |
12 | * | |
13 | * This library is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * Lesser General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU Lesser General Public | |
19 | * License along with this library; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
21 | * | |
22 | * Implementation of RCU-adapted data structures and operations based on the RB | |
23 | * tree algorithms found in chapter 12 of: | |
24 | * | |
25 | * Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and | |
26 | * Clifford Stein. Introduction to Algorithms, Third Edition. The MIT | |
27 | * Press, September 2009. | |
28 | */ | |
29 | ||
30 | #define _BSD_SOURCE | |
31 | #define _LGPL_SOURCE | |
32 | ||
33 | #include <stdio.h> | |
34 | #include <pthread.h> | |
f6e888c5 | 35 | #include <assert.h> |
00131368 MD |
36 | |
37 | #include <urcu-rbtree.h> | |
38 | #include <urcu-pointer.h> | |
39 | ||
ba16c81f MD |
40 | #define DEBUG |
41 | ||
a4f1873b MD |
42 | #ifdef DEBUG |
43 | #define dbg_printf(args...) printf(args) | |
44 | #else | |
45 | #define dbg_printf(args...) | |
46 | #endif | |
47 | ||
00131368 MD |
48 | /* |
49 | * TODO | |
50 | * Deal with memory allocation errors. | |
51 | * Can be ensured by reserving a pool of memory entries before doing the | |
52 | * insertion, which will have to be function of number of | |
53 | * transplantations/rotations required for the operation. | |
54 | */ | |
55 | ||
8c91bac8 MD |
56 | /* Sentinel (bottom nodes). |
57 | * Don't care about p, left, right, pos and key values */ | |
db5b1669 | 58 | struct rcu_rbtree_node rcu_rbtree_nil = { |
00131368 MD |
59 | .color = COLOR_BLACK, |
60 | }; | |
61 | ||
62 | /* | |
63 | * Iterative rbtree search. | |
64 | */ | |
65 | struct rcu_rbtree_node* rcu_rbtree_search(struct rcu_rbtree_node *x, | |
66 | void *k, rcu_rbtree_comp comp) | |
67 | { | |
68 | x = rcu_dereference(x); | |
69 | ||
a23deda7 | 70 | while (x != &rcu_rbtree_nil && k != x->key) { |
00131368 MD |
71 | if (k < x->key) |
72 | x = rcu_dereference(x->left); | |
73 | else | |
74 | x = rcu_dereference(x->right); | |
75 | } | |
76 | return x; | |
77 | } | |
78 | ||
79 | struct rcu_rbtree_node *rcu_rbtree_min(struct rcu_rbtree_node *x, | |
80 | rcu_rbtree_comp comp) | |
81 | { | |
82 | struct rcu_rbtree_node *xl; | |
83 | ||
84 | x = rcu_dereference(x); | |
85 | ||
a23deda7 | 86 | while ((xl = rcu_dereference(x->left)) != &rcu_rbtree_nil) |
00131368 MD |
87 | x = xl; |
88 | return x; | |
89 | } | |
90 | ||
91 | struct rcu_rbtree_node *rcu_rbtree_max(struct rcu_rbtree_node *x, | |
92 | rcu_rbtree_comp comp) | |
93 | { | |
94 | struct rcu_rbtree_node *xr; | |
95 | ||
96 | x = rcu_dereference(x); | |
97 | ||
a23deda7 | 98 | while ((xr = rcu_dereference(x->right)) != &rcu_rbtree_nil) |
00131368 MD |
99 | x = xr; |
100 | return x; | |
101 | } | |
102 | ||
7ddb62c5 MD |
103 | /* |
104 | * FIXME: | |
105 | * Updates should wait for a grace period between update of the | |
106 | * redirect pointer and update of the parent child pointer. This is to make sure | |
107 | * that no reference to the old entry exist. | |
108 | */ | |
109 | ||
00131368 MD |
110 | /* |
111 | * next and prev need to have mutex held to ensure that parent pointer is | |
112 | * coherent. | |
113 | */ | |
114 | struct rcu_rbtree_node *rcu_rbtree_next(struct rcu_rbtree_node *x, | |
115 | rcu_rbtree_comp comp) | |
116 | { | |
8c91bac8 | 117 | struct rcu_rbtree_node *xr, *y, *yr; |
00131368 MD |
118 | |
119 | x = rcu_dereference(x); | |
120 | ||
a23deda7 | 121 | if ((xr = rcu_dereference(x->right)) != &rcu_rbtree_nil) |
00131368 MD |
122 | return rcu_rbtree_min(xr, comp); |
123 | y = rcu_dereference(x->p); | |
8c91bac8 | 124 | while (y != &rcu_rbtree_nil && x->pos == IS_RIGHT) { |
00131368 MD |
125 | x = y; |
126 | y = rcu_dereference(y->p); | |
127 | } | |
128 | return y; | |
129 | } | |
130 | ||
131 | struct rcu_rbtree_node *rcu_rbtree_prev(struct rcu_rbtree_node *x, | |
132 | rcu_rbtree_comp comp) | |
133 | { | |
8c91bac8 | 134 | struct rcu_rbtree_node *xl, *y, *yl; |
00131368 MD |
135 | |
136 | x = rcu_dereference(x); | |
137 | ||
a23deda7 | 138 | if ((xl = rcu_dereference(x->left)) != &rcu_rbtree_nil) |
9364f0c3 | 139 | return rcu_rbtree_min(xl, comp); |
00131368 | 140 | y = rcu_dereference(x->p); |
8c91bac8 | 141 | while (y != &rcu_rbtree_nil && x->pos == IS_LEFT) { |
00131368 MD |
142 | x = y; |
143 | y = rcu_dereference(y->p); | |
144 | } | |
145 | return y; | |
146 | } | |
147 | ||
148 | /* | |
149 | * We have to ensure these assumptions are correct for prev/next | |
150 | * traversal: | |
151 | * | |
152 | * with x being a right child, the assumption that: | |
153 | * x->p->right == x | |
154 | * or if x is a left child, the assumption that: | |
155 | * x->p->left == x | |
156 | * | |
157 | * This explains why we have to allocate a vc copy of the node for left_rotate, | |
158 | * right_rotate and transplant operations. | |
159 | * | |
160 | * We always ensure that the right/left child and correct parent is set in the | |
161 | * node copies *before* we reparent the children and make the upper-level point | |
162 | * to the copy. | |
163 | */ | |
164 | ||
165 | /* RCU: copy x and y, atomically point to new versions. GC old. */ | |
166 | /* Should be eventually followed by a smp_wmc() */ | |
8c91bac8 MD |
167 | /* Returns the new x. Previous x->right references are changed to yc. |
168 | * Previous y->left->right is changed to bc. */ | |
00131368 MD |
169 | static struct rcu_rbtree_node *left_rotate(struct rcu_rbtree_node **root, |
170 | struct rcu_rbtree_node *x, | |
171 | rcu_rbtree_alloc rballoc, | |
172 | rcu_rbtree_free rbfree) | |
173 | { | |
8c91bac8 | 174 | struct rcu_rbtree_node *xc, *y, *yc, *b, *bc; |
00131368 | 175 | |
a4f1873b MD |
176 | dbg_printf("left rotate %p\n", x->key); |
177 | ||
00131368 MD |
178 | y = x->right; |
179 | ||
21cddea3 MD |
180 | if (x != &rcu_rbtree_nil) { |
181 | xc = rballoc(); | |
182 | *xc = *x; | |
8c91bac8 | 183 | xc->pos = IS_LEFT; |
a23deda7 MD |
184 | } |
185 | ||
186 | if (y != &rcu_rbtree_nil) { | |
187 | yc = rballoc(); | |
188 | *yc = *y; | |
8c91bac8 MD |
189 | yc->pos = x->pos; |
190 | b = y->left; | |
191 | } else { | |
192 | b = &rcu_rbtree_nil; | |
193 | } | |
194 | ||
195 | if (b != &rcu_rbtree_nil) { | |
196 | /* Beta position changes from left to right. */ | |
197 | bc = rballoc(); | |
198 | *bc = *b; | |
199 | assert(b->pos == IS_LEFT); | |
200 | bc->pos = IS_RIGHT; | |
a4f1873b MD |
201 | } else |
202 | bc = &rcu_rbtree_nil; | |
a23deda7 MD |
203 | |
204 | /* Modify children and parents in the node copies */ | |
205 | if (x != &rcu_rbtree_nil) { | |
8c91bac8 | 206 | xc->right = bc; |
21cddea3 MD |
207 | xc->p = yc; |
208 | } else | |
209 | xc = &rcu_rbtree_nil; | |
210 | ||
211 | if (y != &rcu_rbtree_nil) { | |
21cddea3 MD |
212 | yc->left = xc; |
213 | yc->p = x->p; | |
214 | } else | |
215 | yc = &rcu_rbtree_nil; | |
00131368 MD |
216 | |
217 | /* | |
218 | * Order stores to node copies (children/parents) before stores that | |
219 | * will make the copies visible to the rest of the tree. | |
220 | */ | |
221 | smp_wmb(); | |
222 | ||
223 | /* Make parents point to the copies */ | |
db5b1669 | 224 | if (x->p == &rcu_rbtree_nil) |
00131368 | 225 | _STORE_SHARED(*root, yc); |
8c91bac8 | 226 | else if (x->pos == IS_LEFT) |
00131368 MD |
227 | _STORE_SHARED(x->p->left, yc); |
228 | else | |
229 | _STORE_SHARED(x->p->right, yc); | |
230 | ||
231 | /* Assign children parents to copies */ | |
21cddea3 | 232 | if (x != &rcu_rbtree_nil) { |
8c91bac8 | 233 | _STORE_SHARED(xc->left->p, xc); /* alpha stays left */ |
21cddea3 MD |
234 | defer_rcu(rbfree, x); |
235 | } | |
236 | if (y != &rcu_rbtree_nil) { | |
8c91bac8 | 237 | _STORE_SHARED(yc->right->p, yc);/* gamma stays right */ |
21cddea3 MD |
238 | defer_rcu(rbfree, y); |
239 | /* yc->left is xc, its parent is already set in node copy */ | |
240 | } | |
8c91bac8 MD |
241 | if (b != &rcu_rbtree_nil) { |
242 | _STORE_SHARED(bc->right->p, bc); | |
243 | _STORE_SHARED(bc->left->p, bc); | |
244 | defer_rcu(rbfree, b); | |
245 | } | |
00131368 MD |
246 | return xc; |
247 | } | |
248 | ||
249 | #if 0 /* orig */ | |
250 | static void left_rotate(struct rcu_rbtree_node **root, | |
251 | struct rcu_rbtree_node *x, | |
252 | rcu_rbtree_alloc rballoc) | |
253 | { | |
254 | struct rcu_rbtree_node *y; | |
255 | ||
256 | y = x->right; | |
257 | x->right = y->left; | |
db5b1669 | 258 | if (y->left != &rcu_rbtree_nil) |
00131368 MD |
259 | y->left->p = x; |
260 | y->p = x->p; | |
db5b1669 | 261 | if (x->p == &rcu_rbtree_nil) |
00131368 MD |
262 | *root = y; |
263 | else if (x == x->p->left) | |
264 | x->p->left = y; | |
265 | else | |
266 | x->p->right = y; | |
267 | y->left = x; | |
268 | x->p = y; | |
269 | } | |
270 | #endif //0 | |
271 | ||
272 | /* RCU: copy x and y, atomically point to new versions. GC old. */ | |
273 | /* Should be eventually followed by a smp_wmc() */ | |
8c91bac8 MD |
274 | /* Returns the new y. Previous y->left references are changed to xc. |
275 | * Previous y->left->right is changed to bc */ | |
00131368 | 276 | static struct rcu_rbtree_node *right_rotate(struct rcu_rbtree_node **root, |
8c91bac8 | 277 | struct rcu_rbtree_node *y, |
00131368 MD |
278 | rcu_rbtree_alloc rballoc, |
279 | rcu_rbtree_free rbfree) | |
280 | { | |
8c91bac8 | 281 | struct rcu_rbtree_node *x, *xc, *yc, *b, *bc;; |
00131368 | 282 | |
a4f1873b MD |
283 | dbg_printf("right rotate %p\n", y->key); |
284 | ||
8c91bac8 | 285 | x = y->left; |
00131368 | 286 | |
21cddea3 MD |
287 | if (x != &rcu_rbtree_nil) { |
288 | xc = rballoc(); | |
289 | *xc = *x; | |
8c91bac8 MD |
290 | xc->pos = y->pos; |
291 | b = x->right; | |
292 | } else { | |
293 | b = &rcu_rbtree_nil; | |
a23deda7 MD |
294 | } |
295 | ||
296 | if (y != &rcu_rbtree_nil) { | |
297 | yc = rballoc(); | |
298 | *yc = *y; | |
8c91bac8 MD |
299 | yc->pos = IS_RIGHT; |
300 | } | |
301 | ||
302 | if (b != &rcu_rbtree_nil) { | |
303 | /* Beta position changes from right to left. */ | |
304 | bc = rballoc(); | |
305 | *bc = *b; | |
306 | assert(b->pos == IS_RIGHT); | |
307 | bc->pos = IS_LEFT; | |
a4f1873b MD |
308 | } else |
309 | bc = &rcu_rbtree_nil; | |
a23deda7 MD |
310 | |
311 | /* Modify children and parents in the node copies */ | |
312 | if (x != &rcu_rbtree_nil) { | |
8c91bac8 MD |
313 | xc->right = yc; |
314 | xc->p = y->p; | |
21cddea3 MD |
315 | } else |
316 | xc = &rcu_rbtree_nil; | |
317 | ||
318 | if (y != &rcu_rbtree_nil) { | |
8c91bac8 MD |
319 | yc->left = bc; |
320 | yc->p = xc; | |
21cddea3 MD |
321 | } else |
322 | yc = &rcu_rbtree_nil; | |
00131368 MD |
323 | |
324 | /* | |
325 | * Order stores to node copies (children/parents) before stores that | |
326 | * will make the copies visible to the rest of the tree. | |
327 | */ | |
328 | smp_wmb(); | |
329 | ||
330 | /* Make parents point to the copies */ | |
8c91bac8 MD |
331 | if (y->p == &rcu_rbtree_nil) |
332 | _STORE_SHARED(*root, xc); | |
333 | else if (y->pos == IS_RIGHT) | |
334 | _STORE_SHARED(y->p->right, xc); | |
00131368 | 335 | else |
8c91bac8 | 336 | _STORE_SHARED(y->p->left, xc); |
00131368 MD |
337 | |
338 | /* Assign children parents to copies */ | |
21cddea3 | 339 | if (x != &rcu_rbtree_nil) { |
8c91bac8 MD |
340 | _STORE_SHARED(xc->left->p, xc); /* alpha stays left */ |
341 | /* xc->right is yc, its parent is already set in node copy */ | |
21cddea3 MD |
342 | defer_rcu(rbfree, x); |
343 | } | |
344 | if (y != &rcu_rbtree_nil) { | |
8c91bac8 | 345 | _STORE_SHARED(yc->right->p, yc);/* gamma stays right */ |
21cddea3 | 346 | defer_rcu(rbfree, y); |
21cddea3 | 347 | } |
8c91bac8 MD |
348 | if (b != &rcu_rbtree_nil) { |
349 | _STORE_SHARED(bc->right->p, bc); | |
350 | _STORE_SHARED(bc->left->p, bc); | |
351 | defer_rcu(rbfree, b); | |
352 | } | |
353 | return yc; | |
00131368 MD |
354 | } |
355 | ||
00131368 MD |
356 | static void rcu_rbtree_insert_fixup(struct rcu_rbtree_node **root, |
357 | struct rcu_rbtree_node *z, | |
358 | rcu_rbtree_alloc rballoc, | |
359 | rcu_rbtree_free rbfree) | |
360 | { | |
361 | struct rcu_rbtree_node *y; | |
362 | ||
a4f1873b MD |
363 | dbg_printf("insert fixup %p\n", z->key); |
364 | ||
00131368 MD |
365 | while (z->p->color == COLOR_RED) { |
366 | if (z->p == z->p->p->left) { | |
367 | y = z->p->p->right; | |
368 | if (y->color == COLOR_RED) { | |
369 | z->p->color = COLOR_BLACK; | |
370 | y->color = COLOR_BLACK; | |
371 | z->p->p->color = COLOR_RED; | |
372 | z = z->p->p; | |
373 | } else { | |
374 | if (z == z->p->right) { | |
375 | z = z->p; | |
376 | z = left_rotate(root, z, | |
377 | rballoc, rbfree); | |
378 | } | |
379 | z->p->color = COLOR_BLACK; | |
380 | z->p->p->color = COLOR_RED; | |
a23deda7 | 381 | right_rotate(root, z->p->p, rballoc, rbfree); |
00131368 MD |
382 | } |
383 | } else { | |
384 | y = z->p->p->left; | |
385 | if (y->color == COLOR_RED) { | |
386 | z->p->color = COLOR_BLACK; | |
387 | y->color = COLOR_BLACK; | |
388 | z->p->p->color = COLOR_RED; | |
389 | z = z->p->p; | |
390 | } else { | |
391 | if (z == z->p->left) { | |
392 | z = z->p; | |
393 | z = right_rotate(root, z, | |
394 | rballoc, rbfree); | |
395 | } | |
396 | z->p->color = COLOR_BLACK; | |
397 | z->p->p->color = COLOR_RED; | |
a23deda7 | 398 | left_rotate(root, z->p->p, rballoc, rbfree); |
00131368 MD |
399 | } |
400 | } | |
401 | } | |
402 | (*root)->color = COLOR_BLACK; | |
403 | } | |
404 | ||
405 | /* | |
406 | * rcu_rbtree_insert - Insert a node in the RCU rbtree | |
407 | * | |
408 | * Returns 0 on success, or < 0 on error. | |
409 | */ | |
410 | int rcu_rbtree_insert(struct rcu_rbtree_node **root, | |
411 | struct rcu_rbtree_node *z, | |
412 | rcu_rbtree_comp comp, | |
413 | rcu_rbtree_alloc rballoc, | |
414 | rcu_rbtree_free rbfree) | |
415 | { | |
416 | struct rcu_rbtree_node *x, *y; | |
417 | ||
a4f1873b MD |
418 | dbg_printf("insert %p\n", z->key); |
419 | ||
db5b1669 | 420 | y = &rcu_rbtree_nil; |
00131368 MD |
421 | x = *root; |
422 | ||
db5b1669 | 423 | while (x != &rcu_rbtree_nil) { |
00131368 MD |
424 | y = x; |
425 | if (comp(z->key, x->key) < 0) | |
426 | x = x->left; | |
427 | else | |
428 | x = x->right; | |
429 | } | |
430 | ||
431 | z->p = y; | |
db5b1669 MD |
432 | z->left = &rcu_rbtree_nil; |
433 | z->right = &rcu_rbtree_nil; | |
00131368 | 434 | z->color = COLOR_RED; |
8c91bac8 MD |
435 | |
436 | if (y == &rcu_rbtree_nil) | |
437 | z->pos = IS_RIGHT; /* arbitrary for root node */ | |
438 | else if (comp(z->key, y->key) < 0) | |
439 | z->pos = IS_LEFT; | |
440 | else | |
441 | z->pos = IS_RIGHT; | |
00131368 MD |
442 | |
443 | /* | |
444 | * Order stores to z (children/parents) before stores that will make it | |
445 | * visible to the rest of the tree. | |
446 | */ | |
447 | smp_wmb(); | |
448 | ||
db5b1669 | 449 | if (y == &rcu_rbtree_nil) |
00131368 MD |
450 | _STORE_SHARED(*root, z); |
451 | else if (comp(z->key, y->key) < 0) | |
452 | _STORE_SHARED(y->left, z); | |
453 | else | |
454 | _STORE_SHARED(y->right, z); | |
455 | rcu_rbtree_insert_fixup(root, z, rballoc, rbfree); | |
456 | /* | |
457 | * Make sure to commit all _STORE_SHARED() for non-coherent caches. | |
458 | */ | |
459 | smp_wmc(); | |
460 | ||
461 | return 0; | |
462 | } | |
463 | ||
464 | /* | |
465 | * Transplant v into u position. | |
466 | * Returns new copy of v. | |
467 | */ | |
468 | static struct rcu_rbtree_node * | |
469 | rcu_rbtree_transplant(struct rcu_rbtree_node **root, | |
470 | struct rcu_rbtree_node *u, | |
471 | struct rcu_rbtree_node *v, | |
472 | rcu_rbtree_alloc rballoc, | |
473 | rcu_rbtree_free rbfree) | |
474 | { | |
475 | struct rcu_rbtree_node *vc; | |
476 | ||
a4f1873b MD |
477 | dbg_printf("transplant %p\n", v->key); |
478 | ||
21cddea3 MD |
479 | if (v != &rcu_rbtree_nil) { |
480 | vc = rballoc(); | |
481 | *vc = *v; | |
00131368 | 482 | |
21cddea3 MD |
483 | /* Change vc parent pointer */ |
484 | vc->p = u->p; | |
8c91bac8 | 485 | vc->pos = u->pos; |
00131368 | 486 | |
21cddea3 MD |
487 | /* |
488 | * Order stores to node copies (children/parents) before stores | |
489 | * that will make the copies visible to the rest of the tree. | |
490 | */ | |
491 | smp_wmb(); | |
492 | } else { | |
493 | vc = &rcu_rbtree_nil; | |
494 | } | |
00131368 MD |
495 | |
496 | /* Assign upper-level pointer to vc, replacing u. */ | |
db5b1669 | 497 | if (u->p == &rcu_rbtree_nil) |
00131368 MD |
498 | _STORE_SHARED(*root, vc); |
499 | else if (u == u->p->left) | |
500 | _STORE_SHARED(u->p->left, vc); | |
501 | else | |
502 | _STORE_SHARED(u->p->right, vc); | |
503 | ||
21cddea3 MD |
504 | if (v != &rcu_rbtree_nil) { |
505 | /* | |
506 | * The children pointers in vc are the same as v. We can | |
507 | * therefore reparent v's children to vc safely. | |
508 | */ | |
8c91bac8 MD |
509 | if (vc->right != &rcu_rbtree_nil) |
510 | _STORE_SHARED(vc->right->p, vc); | |
511 | if (vc->left != &rcu_rbtree_nil) | |
512 | _STORE_SHARED(vc->left->p, vc); | |
00131368 | 513 | |
21cddea3 MD |
514 | defer_rcu(rbfree, v); |
515 | } | |
00131368 MD |
516 | return vc; |
517 | } | |
518 | ||
519 | static void rcu_rbtree_remove_fixup(struct rcu_rbtree_node **root, | |
520 | struct rcu_rbtree_node *x, | |
521 | rcu_rbtree_alloc rballoc, | |
522 | rcu_rbtree_free rbfree) | |
523 | { | |
a4f1873b MD |
524 | dbg_printf("remove fixup %p\n", x->key); |
525 | ||
00131368 MD |
526 | while (x != *root && x->color == COLOR_BLACK) { |
527 | if (x == x->p->left) { | |
f6e888c5 | 528 | struct rcu_rbtree_node *w, *t; |
00131368 MD |
529 | |
530 | w = x->p->right; | |
ba16c81f MD |
531 | assert(w != &rcu_rbtree_nil); |
532 | ||
00131368 MD |
533 | if (w->color == COLOR_RED) { |
534 | w->color == COLOR_BLACK; | |
535 | x->p->color = COLOR_RED; | |
f6e888c5 MD |
536 | t = left_rotate(root, x->p, rballoc, rbfree); |
537 | assert(x->p->left == t->left); | |
00131368 MD |
538 | /* x is a left node, not copied by rotation */ |
539 | w = x->p->right; | |
540 | } | |
541 | if (w->left->color == COLOR_BLACK | |
542 | && w->right->color == COLOR_BLACK) { | |
543 | w->color = COLOR_RED; | |
544 | x = x->p; | |
545 | } else { | |
546 | if (w->right->color == COLOR_BLACK) { | |
547 | w->left->color = COLOR_BLACK; | |
548 | w->color = COLOR_RED; | |
549 | right_rotate(root, w, rballoc, rbfree); | |
550 | w = x->p->right; | |
551 | } | |
552 | w->color = x->p->color; | |
553 | x->p->color = COLOR_BLACK; | |
554 | w->right->color = COLOR_BLACK; | |
555 | left_rotate(root, x->p, rballoc, rbfree); | |
556 | x = *root; | |
557 | } | |
558 | } else { | |
8c91bac8 | 559 | struct rcu_rbtree_node *w, *t; |
00131368 MD |
560 | |
561 | w = x->p->left; | |
ba16c81f MD |
562 | assert(w != &rcu_rbtree_nil); |
563 | ||
00131368 MD |
564 | if (w->color == COLOR_RED) { |
565 | w->color == COLOR_BLACK; | |
566 | x->p->color = COLOR_RED; | |
8c91bac8 MD |
567 | t = right_rotate(root, x->p, rballoc, rbfree); |
568 | assert(x->p->right == t->right); | |
00131368 MD |
569 | /* x is a right node, not copied by rotation */ |
570 | w = x->p->left; | |
571 | } | |
572 | if (w->right->color == COLOR_BLACK | |
573 | && w->left->color == COLOR_BLACK) { | |
574 | w->color = COLOR_RED; | |
575 | x = x->p; | |
576 | } else { | |
577 | if (w->left->color == COLOR_BLACK) { | |
578 | w->right->color = COLOR_BLACK; | |
579 | w->color = COLOR_RED; | |
580 | left_rotate(root, w, rballoc, rbfree); | |
581 | w = x->p->left; | |
582 | } | |
583 | w->color = x->p->color; | |
584 | x->p->color = COLOR_BLACK; | |
585 | w->left->color = COLOR_BLACK; | |
586 | right_rotate(root, x->p, rballoc, rbfree); | |
587 | x = *root; | |
588 | } | |
589 | } | |
590 | } | |
591 | x->color = COLOR_BLACK; | |
592 | } | |
593 | ||
8c91bac8 MD |
594 | /* |
595 | * Returns the new copy of y->right. | |
596 | * Delete z. All non-copied children left/right positions are unchanged. */ | |
00131368 MD |
597 | static struct rcu_rbtree_node * |
598 | rcu_rbtree_remove_nonil(struct rcu_rbtree_node **root, | |
599 | struct rcu_rbtree_node *z, | |
600 | struct rcu_rbtree_node *y, | |
601 | rcu_rbtree_comp comp, | |
602 | rcu_rbtree_alloc rballoc, | |
603 | rcu_rbtree_free rbfree) | |
604 | { | |
605 | struct rcu_rbtree_node *x, *xc, *yc; | |
606 | ||
a4f1873b MD |
607 | dbg_printf("remove nonil %p\n", z->key); |
608 | ||
00131368 MD |
609 | x = y->right; |
610 | ||
a23deda7 MD |
611 | if (x != &rcu_rbtree_nil) { |
612 | xc = rballoc(); | |
613 | *xc = *x; | |
614 | } else | |
615 | xc = &rcu_rbtree_nil; | |
616 | ||
00131368 | 617 | yc = rballoc(); |
00131368 MD |
618 | *yc = *y; |
619 | ||
620 | /* Update parent and children pointers within copies */ | |
621 | if (y->p == z) | |
622 | xc->p = yc; | |
623 | else { | |
624 | /* Transplant y->right (xc) into y, within copy */ | |
625 | xc->p = y->p; | |
626 | /* But also change the right pointer of yc */ | |
627 | yc->right = z->right; | |
628 | } | |
629 | /* Transplant y into z, within copy */ | |
630 | yc->p = z->p; | |
631 | ||
632 | yc->left = z->left; | |
633 | yc->color = z->color; | |
634 | ||
635 | /* | |
636 | * Order stores to node copies (children/parents) before stores that | |
637 | * will make the copies visible to the rest of the tree. | |
638 | */ | |
639 | smp_wmb(); | |
640 | ||
641 | /* Update external pointers */ | |
642 | ||
643 | if (y->p != z) { | |
644 | /* Transplant y->right into y, external parent links */ | |
645 | ||
646 | /* Assign upper-level pointer to xc, replacing y. */ | |
db5b1669 | 647 | if (y->p == &rcu_rbtree_nil) |
00131368 MD |
648 | _STORE_SHARED(*root, xc); |
649 | else if (y == y->p->left) | |
650 | _STORE_SHARED(y->p->left, xc); | |
651 | else | |
652 | _STORE_SHARED(y->p->right, xc); | |
653 | } | |
654 | ||
655 | /* Transplant y into z, update external parent links */ | |
db5b1669 | 656 | if (z->p == &rcu_rbtree_nil) |
00131368 MD |
657 | _STORE_SHARED(*root, yc); |
658 | else if (z == z->p->left) | |
659 | _STORE_SHARED(z->p->left, yc); | |
660 | else | |
661 | _STORE_SHARED(z->p->right, yc); | |
662 | ||
a23deda7 MD |
663 | if (x != &rcu_rbtree_nil) { |
664 | /* Reparent xc's children to xc. */ | |
665 | _STORE_SHARED(xc->right->p, xc); | |
8c91bac8 MD |
666 | assert(yc->right == &rcu_rbtree_nil |
667 | || xc->right->pos == IS_RIGHT); | |
a23deda7 | 668 | _STORE_SHARED(xc->left->p, xc); |
8c91bac8 MD |
669 | assert(yc->right == &rcu_rbtree_nil |
670 | || xc->left->pos == IS_LEFT); | |
a23deda7 MD |
671 | defer_rcu(rbfree, x); |
672 | } | |
673 | ||
00131368 MD |
674 | /* Reparent yc's children to yc */ |
675 | _STORE_SHARED(yc->right->p, yc); | |
8c91bac8 | 676 | assert(yc->right == &rcu_rbtree_nil || yc->right->pos == IS_RIGHT); |
00131368 | 677 | _STORE_SHARED(yc->left->p, yc); |
8c91bac8 | 678 | assert(yc->right == &rcu_rbtree_nil || yc->left->pos == IS_LEFT); |
00131368 MD |
679 | defer_rcu(rbfree, y); |
680 | ||
681 | return xc; | |
682 | } | |
683 | ||
684 | int rcu_rbtree_remove(struct rcu_rbtree_node **root, | |
685 | struct rcu_rbtree_node *z, | |
686 | rcu_rbtree_comp comp, | |
687 | rcu_rbtree_alloc rballoc, | |
688 | rcu_rbtree_free rbfree) | |
689 | { | |
690 | struct rcu_rbtree_node *x, *y; | |
691 | unsigned int y_original_color; | |
692 | ||
a4f1873b MD |
693 | dbg_printf("remove %p\n", z->key); |
694 | ||
00131368 MD |
695 | y = z; |
696 | y_original_color = y->color; | |
697 | ||
db5b1669 | 698 | if (z->left == &rcu_rbtree_nil) { |
00131368 | 699 | x = rcu_rbtree_transplant(root, z, z->right, rballoc, rbfree); |
db5b1669 | 700 | } else if (z->right == &rcu_rbtree_nil) { |
00131368 MD |
701 | x = rcu_rbtree_transplant(root, z, z->left, rballoc, rbfree); |
702 | } else { | |
703 | y = rcu_rbtree_min(z->right, comp); | |
704 | y_original_color = y->color; | |
705 | x = rcu_rbtree_remove_nonil(root, z, y, comp, rballoc, rbfree); | |
706 | } | |
707 | if (y_original_color == COLOR_BLACK) | |
708 | rcu_rbtree_remove_fixup(root, x, rballoc, rbfree); | |
709 | /* | |
710 | * Commit all _STORE_SHARED(). | |
711 | */ | |
712 | smp_wmc(); | |
713 | ||
714 | return 0; | |
715 | } |