diff options
-rw-r--r-- | Documentation/rbtree.txt | 58 | ||||
-rw-r--r-- | arch/x86/include/asm/cacheflush.h | 44 | ||||
-rw-r--r-- | arch/x86/mm/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/mm/pat.c | 239 | ||||
-rw-r--r-- | arch/x86/mm/pat_internal.h | 46 | ||||
-rw-r--r-- | arch/x86/mm/pat_rbtree.c | 273 | ||||
-rw-r--r-- | include/linux/rbtree.h | 5 | ||||
-rw-r--r-- | lib/rbtree.c | 48 |
8 files changed, 470 insertions, 244 deletions
diff --git a/Documentation/rbtree.txt b/Documentation/rbtree.txt index aae8355d3166..221f38be98f4 100644 --- a/Documentation/rbtree.txt +++ b/Documentation/rbtree.txt | |||
@@ -190,3 +190,61 @@ Example: | |||
190 | for (node = rb_first(&mytree); node; node = rb_next(node)) | 190 | for (node = rb_first(&mytree); node; node = rb_next(node)) |
191 | printk("key=%s\n", rb_entry(node, struct mytype, node)->keystring); | 191 | printk("key=%s\n", rb_entry(node, struct mytype, node)->keystring); |
192 | 192 | ||
193 | Support for Augmented rbtrees | ||
194 | ----------------------------- | ||
195 | |||
196 | Augmented rbtree is an rbtree with "some" additional data stored in each node. | ||
197 | This data can be used to augment some new functionality to rbtree. | ||
198 | Augmented rbtree is an optional feature built on top of basic rbtree | ||
199 | infrastructure. rbtree user who wants this feature will have an augment | ||
200 | callback function in rb_root initialized. | ||
201 | |||
202 | This callback function will be called from rbtree core routines whenever | ||
203 | a node has a change in one or both of its children. It is the responsibility | ||
204 | of the callback function to recalculate the additional data that is in the | ||
205 | rb node using new children information. Note that if this new additional | ||
206 | data affects the parent node's additional data, then callback function has | ||
207 | to handle it and do the recursive updates. | ||
208 | |||
209 | |||
210 | Interval tree is an example of augmented rb tree. Reference - | ||
211 | "Introduction to Algorithms" by Cormen, Leiserson, Rivest and Stein. | ||
212 | More details about interval trees: | ||
213 | |||
214 | Classical rbtree has a single key and it cannot be directly used to store | ||
215 | interval ranges like [lo:hi] and do a quick lookup for any overlap with a new | ||
216 | lo:hi or to find whether there is an exact match for a new lo:hi. | ||
217 | |||
218 | However, rbtree can be augmented to store such interval ranges in a structured | ||
219 | way making it possible to do efficient lookup and exact match. | ||
220 | |||
221 | This "extra information" stored in each node is the maximum hi | ||
222 | (max_hi) value among all the nodes that are its descendents. This | ||
223 | information can be maintained at each node just be looking at the node | ||
224 | and its immediate children. And this will be used in O(log n) lookup | ||
225 | for lowest match (lowest start address among all possible matches) | ||
226 | with something like: | ||
227 | |||
228 | find_lowest_match(lo, hi, node) | ||
229 | { | ||
230 | lowest_match = NULL; | ||
231 | while (node) { | ||
232 | if (max_hi(node->left) > lo) { | ||
233 | // Lowest overlap if any must be on left side | ||
234 | node = node->left; | ||
235 | } else if (overlap(lo, hi, node)) { | ||
236 | lowest_match = node; | ||
237 | break; | ||
238 | } else if (lo > node->lo) { | ||
239 | // Lowest overlap if any must be on right side | ||
240 | node = node->right; | ||
241 | } else { | ||
242 | break; | ||
243 | } | ||
244 | } | ||
245 | return lowest_match; | ||
246 | } | ||
247 | |||
248 | Finding exact match will be to first find lowest match and then to follow | ||
249 | successor nodes looking for exact match, until the start of a node is beyond | ||
250 | the hi value we are looking for. | ||
diff --git a/arch/x86/include/asm/cacheflush.h b/arch/x86/include/asm/cacheflush.h index 634c40a739a6..c70068d05f70 100644 --- a/arch/x86/include/asm/cacheflush.h +++ b/arch/x86/include/asm/cacheflush.h | |||
@@ -44,9 +44,6 @@ static inline void copy_from_user_page(struct vm_area_struct *vma, | |||
44 | memcpy(dst, src, len); | 44 | memcpy(dst, src, len); |
45 | } | 45 | } |
46 | 46 | ||
47 | #define PG_WC PG_arch_1 | ||
48 | PAGEFLAG(WC, WC) | ||
49 | |||
50 | #ifdef CONFIG_X86_PAT | 47 | #ifdef CONFIG_X86_PAT |
51 | /* | 48 | /* |
52 | * X86 PAT uses page flags WC and Uncached together to keep track of | 49 | * X86 PAT uses page flags WC and Uncached together to keep track of |
@@ -55,16 +52,24 @@ PAGEFLAG(WC, WC) | |||
55 | * _PAGE_CACHE_UC_MINUS and fourth state where page's memory type has not | 52 | * _PAGE_CACHE_UC_MINUS and fourth state where page's memory type has not |
56 | * been changed from its default (value of -1 used to denote this). | 53 | * been changed from its default (value of -1 used to denote this). |
57 | * Note we do not support _PAGE_CACHE_UC here. | 54 | * Note we do not support _PAGE_CACHE_UC here. |
58 | * | ||
59 | * Caller must hold memtype_lock for atomicity. | ||
60 | */ | 55 | */ |
56 | |||
57 | #define _PGMT_DEFAULT 0 | ||
58 | #define _PGMT_WC (1UL << PG_arch_1) | ||
59 | #define _PGMT_UC_MINUS (1UL << PG_uncached) | ||
60 | #define _PGMT_WB (1UL << PG_uncached | 1UL << PG_arch_1) | ||
61 | #define _PGMT_MASK (1UL << PG_uncached | 1UL << PG_arch_1) | ||
62 | #define _PGMT_CLEAR_MASK (~_PGMT_MASK) | ||
63 | |||
61 | static inline unsigned long get_page_memtype(struct page *pg) | 64 | static inline unsigned long get_page_memtype(struct page *pg) |
62 | { | 65 | { |
63 | if (!PageUncached(pg) && !PageWC(pg)) | 66 | unsigned long pg_flags = pg->flags & _PGMT_MASK; |
67 | |||
68 | if (pg_flags == _PGMT_DEFAULT) | ||
64 | return -1; | 69 | return -1; |
65 | else if (!PageUncached(pg) && PageWC(pg)) | 70 | else if (pg_flags == _PGMT_WC) |
66 | return _PAGE_CACHE_WC; | 71 | return _PAGE_CACHE_WC; |
67 | else if (PageUncached(pg) && !PageWC(pg)) | 72 | else if (pg_flags == _PGMT_UC_MINUS) |
68 | return _PAGE_CACHE_UC_MINUS; | 73 | return _PAGE_CACHE_UC_MINUS; |
69 | else | 74 | else |
70 | return _PAGE_CACHE_WB; | 75 | return _PAGE_CACHE_WB; |
@@ -72,25 +77,26 @@ static inline unsigned long get_page_memtype(struct page *pg) | |||
72 | 77 | ||
73 | static inline void set_page_memtype(struct page *pg, unsigned long memtype) | 78 | static inline void set_page_memtype(struct page *pg, unsigned long memtype) |
74 | { | 79 | { |
80 | unsigned long memtype_flags = _PGMT_DEFAULT; | ||
81 | unsigned long old_flags; | ||
82 | unsigned long new_flags; | ||
83 | |||
75 | switch (memtype) { | 84 | switch (memtype) { |
76 | case _PAGE_CACHE_WC: | 85 | case _PAGE_CACHE_WC: |
77 | ClearPageUncached(pg); | 86 | memtype_flags = _PGMT_WC; |
78 | SetPageWC(pg); | ||
79 | break; | 87 | break; |
80 | case _PAGE_CACHE_UC_MINUS: | 88 | case _PAGE_CACHE_UC_MINUS: |
81 | SetPageUncached(pg); | 89 | memtype_flags = _PGMT_UC_MINUS; |
82 | ClearPageWC(pg); | ||
83 | break; | 90 | break; |
84 | case _PAGE_CACHE_WB: | 91 | case _PAGE_CACHE_WB: |
85 | SetPageUncached(pg); | 92 | memtype_flags = _PGMT_WB; |
86 | SetPageWC(pg); | ||
87 | break; | ||
88 | default: | ||
89 | case -1: | ||
90 | ClearPageUncached(pg); | ||
91 | ClearPageWC(pg); | ||
92 | break; | 93 | break; |
93 | } | 94 | } |
95 | |||
96 | do { | ||
97 | old_flags = pg->flags; | ||
98 | new_flags = (old_flags & _PGMT_CLEAR_MASK) | memtype_flags; | ||
99 | } while (cmpxchg(&pg->flags, old_flags, new_flags) != old_flags); | ||
94 | } | 100 | } |
95 | #else | 101 | #else |
96 | static inline unsigned long get_page_memtype(struct page *pg) { return -1; } | 102 | static inline unsigned long get_page_memtype(struct page *pg) { return -1; } |
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index 06630d26e56d..a4c768397baa 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile | |||
@@ -6,6 +6,7 @@ nostackp := $(call cc-option, -fno-stack-protector) | |||
6 | CFLAGS_physaddr.o := $(nostackp) | 6 | CFLAGS_physaddr.o := $(nostackp) |
7 | CFLAGS_setup_nx.o := $(nostackp) | 7 | CFLAGS_setup_nx.o := $(nostackp) |
8 | 8 | ||
9 | obj-$(CONFIG_X86_PAT) += pat_rbtree.o | ||
9 | obj-$(CONFIG_SMP) += tlb.o | 10 | obj-$(CONFIG_SMP) += tlb.o |
10 | 11 | ||
11 | obj-$(CONFIG_X86_32) += pgtable_32.o iomap_32.o | 12 | obj-$(CONFIG_X86_32) += pgtable_32.o iomap_32.o |
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c index edc8b95afc1a..bbe5502ee1cb 100644 --- a/arch/x86/mm/pat.c +++ b/arch/x86/mm/pat.c | |||
@@ -30,6 +30,8 @@ | |||
30 | #include <asm/pat.h> | 30 | #include <asm/pat.h> |
31 | #include <asm/io.h> | 31 | #include <asm/io.h> |
32 | 32 | ||
33 | #include "pat_internal.h" | ||
34 | |||
33 | #ifdef CONFIG_X86_PAT | 35 | #ifdef CONFIG_X86_PAT |
34 | int __read_mostly pat_enabled = 1; | 36 | int __read_mostly pat_enabled = 1; |
35 | 37 | ||
@@ -53,19 +55,15 @@ static inline void pat_disable(const char *reason) | |||
53 | #endif | 55 | #endif |
54 | 56 | ||
55 | 57 | ||
56 | static int debug_enable; | 58 | int pat_debug_enable; |
57 | 59 | ||
58 | static int __init pat_debug_setup(char *str) | 60 | static int __init pat_debug_setup(char *str) |
59 | { | 61 | { |
60 | debug_enable = 1; | 62 | pat_debug_enable = 1; |
61 | return 0; | 63 | return 0; |
62 | } | 64 | } |
63 | __setup("debugpat", pat_debug_setup); | 65 | __setup("debugpat", pat_debug_setup); |
64 | 66 | ||
65 | #define dprintk(fmt, arg...) \ | ||
66 | do { if (debug_enable) printk(KERN_INFO fmt, ##arg); } while (0) | ||
67 | |||
68 | |||
69 | static u64 __read_mostly boot_pat_state; | 67 | static u64 __read_mostly boot_pat_state; |
70 | 68 | ||
71 | enum { | 69 | enum { |
@@ -132,84 +130,7 @@ void pat_init(void) | |||
132 | 130 | ||
133 | #undef PAT | 131 | #undef PAT |
134 | 132 | ||
135 | static char *cattr_name(unsigned long flags) | 133 | static DEFINE_SPINLOCK(memtype_lock); /* protects memtype accesses */ |
136 | { | ||
137 | switch (flags & _PAGE_CACHE_MASK) { | ||
138 | case _PAGE_CACHE_UC: return "uncached"; | ||
139 | case _PAGE_CACHE_UC_MINUS: return "uncached-minus"; | ||
140 | case _PAGE_CACHE_WB: return "write-back"; | ||
141 | case _PAGE_CACHE_WC: return "write-combining"; | ||
142 | default: return "broken"; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * The global memtype list keeps track of memory type for specific | ||
148 | * physical memory areas. Conflicting memory types in different | ||
149 | * mappings can cause CPU cache corruption. To avoid this we keep track. | ||
150 | * | ||
151 | * The list is sorted based on starting address and can contain multiple | ||
152 | * entries for each address (this allows reference counting for overlapping | ||
153 | * areas). All the aliases have the same cache attributes of course. | ||
154 | * Zero attributes are represented as holes. | ||
155 | * | ||
156 | * The data structure is a list that is also organized as an rbtree | ||
157 | * sorted on the start address of memtype range. | ||
158 | * | ||
159 | * memtype_lock protects both the linear list and rbtree. | ||
160 | */ | ||
161 | |||
162 | struct memtype { | ||
163 | u64 start; | ||
164 | u64 end; | ||
165 | unsigned long type; | ||
166 | struct list_head nd; | ||
167 | struct rb_node rb; | ||
168 | }; | ||
169 | |||
170 | static struct rb_root memtype_rbroot = RB_ROOT; | ||
171 | static LIST_HEAD(memtype_list); | ||
172 | static DEFINE_SPINLOCK(memtype_lock); /* protects memtype list */ | ||
173 | |||
174 | static struct memtype *memtype_rb_search(struct rb_root *root, u64 start) | ||
175 | { | ||
176 | struct rb_node *node = root->rb_node; | ||
177 | struct memtype *last_lower = NULL; | ||
178 | |||
179 | while (node) { | ||
180 | struct memtype *data = container_of(node, struct memtype, rb); | ||
181 | |||
182 | if (data->start < start) { | ||
183 | last_lower = data; | ||
184 | node = node->rb_right; | ||
185 | } else if (data->start > start) { | ||
186 | node = node->rb_left; | ||
187 | } else | ||
188 | return data; | ||
189 | } | ||
190 | |||
191 | /* Will return NULL if there is no entry with its start <= start */ | ||
192 | return last_lower; | ||
193 | } | ||
194 | |||
195 | static void memtype_rb_insert(struct rb_root *root, struct memtype *data) | ||
196 | { | ||
197 | struct rb_node **new = &(root->rb_node); | ||
198 | struct rb_node *parent = NULL; | ||
199 | |||
200 | while (*new) { | ||
201 | struct memtype *this = container_of(*new, struct memtype, rb); | ||
202 | |||
203 | parent = *new; | ||
204 | if (data->start <= this->start) | ||
205 | new = &((*new)->rb_left); | ||
206 | else if (data->start > this->start) | ||
207 | new = &((*new)->rb_right); | ||
208 | } | ||
209 | |||
210 | rb_link_node(&data->rb, parent, new); | ||
211 | rb_insert_color(&data->rb, root); | ||
212 | } | ||
213 | 134 | ||
214 | /* | 135 | /* |
215 | * Does intersection of PAT memory type and MTRR memory type and returns | 136 | * Does intersection of PAT memory type and MTRR memory type and returns |
@@ -237,33 +158,6 @@ static unsigned long pat_x_mtrr_type(u64 start, u64 end, unsigned long req_type) | |||
237 | return req_type; | 158 | return req_type; |
238 | } | 159 | } |
239 | 160 | ||
240 | static int | ||
241 | chk_conflict(struct memtype *new, struct memtype *entry, unsigned long *type) | ||
242 | { | ||
243 | if (new->type != entry->type) { | ||
244 | if (type) { | ||
245 | new->type = entry->type; | ||
246 | *type = entry->type; | ||
247 | } else | ||
248 | goto conflict; | ||
249 | } | ||
250 | |||
251 | /* check overlaps with more than one entry in the list */ | ||
252 | list_for_each_entry_continue(entry, &memtype_list, nd) { | ||
253 | if (new->end <= entry->start) | ||
254 | break; | ||
255 | else if (new->type != entry->type) | ||
256 | goto conflict; | ||
257 | } | ||
258 | return 0; | ||
259 | |||
260 | conflict: | ||
261 | printk(KERN_INFO "%s:%d conflicting memory types " | ||
262 | "%Lx-%Lx %s<->%s\n", current->comm, current->pid, new->start, | ||
263 | new->end, cattr_name(new->type), cattr_name(entry->type)); | ||
264 | return -EBUSY; | ||
265 | } | ||
266 | |||
267 | static int pat_pagerange_is_ram(unsigned long start, unsigned long end) | 161 | static int pat_pagerange_is_ram(unsigned long start, unsigned long end) |
268 | { | 162 | { |
269 | int ram_page = 0, not_rampage = 0; | 163 | int ram_page = 0, not_rampage = 0; |
@@ -296,8 +190,6 @@ static int pat_pagerange_is_ram(unsigned long start, unsigned long end) | |||
296 | * Here we do two pass: | 190 | * Here we do two pass: |
297 | * - Find the memtype of all the pages in the range, look for any conflicts | 191 | * - Find the memtype of all the pages in the range, look for any conflicts |
298 | * - In case of no conflicts, set the new memtype for pages in the range | 192 | * - In case of no conflicts, set the new memtype for pages in the range |
299 | * | ||
300 | * Caller must hold memtype_lock for atomicity. | ||
301 | */ | 193 | */ |
302 | static int reserve_ram_pages_type(u64 start, u64 end, unsigned long req_type, | 194 | static int reserve_ram_pages_type(u64 start, u64 end, unsigned long req_type, |
303 | unsigned long *new_type) | 195 | unsigned long *new_type) |
@@ -364,9 +256,8 @@ static int free_ram_pages_type(u64 start, u64 end) | |||
364 | int reserve_memtype(u64 start, u64 end, unsigned long req_type, | 256 | int reserve_memtype(u64 start, u64 end, unsigned long req_type, |
365 | unsigned long *new_type) | 257 | unsigned long *new_type) |
366 | { | 258 | { |
367 | struct memtype *new, *entry; | 259 | struct memtype *new; |
368 | unsigned long actual_type; | 260 | unsigned long actual_type; |
369 | struct list_head *where; | ||
370 | int is_range_ram; | 261 | int is_range_ram; |
371 | int err = 0; | 262 | int err = 0; |
372 | 263 | ||
@@ -404,9 +295,7 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, | |||
404 | is_range_ram = pat_pagerange_is_ram(start, end); | 295 | is_range_ram = pat_pagerange_is_ram(start, end); |
405 | if (is_range_ram == 1) { | 296 | if (is_range_ram == 1) { |
406 | 297 | ||
407 | spin_lock(&memtype_lock); | ||
408 | err = reserve_ram_pages_type(start, end, req_type, new_type); | 298 | err = reserve_ram_pages_type(start, end, req_type, new_type); |
409 | spin_unlock(&memtype_lock); | ||
410 | 299 | ||
411 | return err; | 300 | return err; |
412 | } else if (is_range_ram < 0) { | 301 | } else if (is_range_ram < 0) { |
@@ -423,42 +312,7 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, | |||
423 | 312 | ||
424 | spin_lock(&memtype_lock); | 313 | spin_lock(&memtype_lock); |
425 | 314 | ||
426 | /* Search for existing mapping that overlaps the current range */ | 315 | err = rbt_memtype_check_insert(new, new_type); |
427 | where = NULL; | ||
428 | list_for_each_entry(entry, &memtype_list, nd) { | ||
429 | if (end <= entry->start) { | ||
430 | where = entry->nd.prev; | ||
431 | break; | ||
432 | } else if (start <= entry->start) { /* end > entry->start */ | ||
433 | err = chk_conflict(new, entry, new_type); | ||
434 | if (!err) { | ||
435 | dprintk("Overlap at 0x%Lx-0x%Lx\n", | ||
436 | entry->start, entry->end); | ||
437 | where = entry->nd.prev; | ||
438 | } | ||
439 | break; | ||
440 | } else if (start < entry->end) { /* start > entry->start */ | ||
441 | err = chk_conflict(new, entry, new_type); | ||
442 | if (!err) { | ||
443 | dprintk("Overlap at 0x%Lx-0x%Lx\n", | ||
444 | entry->start, entry->end); | ||
445 | |||
446 | /* | ||
447 | * Move to right position in the linked | ||
448 | * list to add this new entry | ||
449 | */ | ||
450 | list_for_each_entry_continue(entry, | ||
451 | &memtype_list, nd) { | ||
452 | if (start <= entry->start) { | ||
453 | where = entry->nd.prev; | ||
454 | break; | ||
455 | } | ||
456 | } | ||
457 | } | ||
458 | break; | ||
459 | } | ||
460 | } | ||
461 | |||
462 | if (err) { | 316 | if (err) { |
463 | printk(KERN_INFO "reserve_memtype failed 0x%Lx-0x%Lx, " | 317 | printk(KERN_INFO "reserve_memtype failed 0x%Lx-0x%Lx, " |
464 | "track %s, req %s\n", | 318 | "track %s, req %s\n", |
@@ -469,13 +323,6 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, | |||
469 | return err; | 323 | return err; |
470 | } | 324 | } |
471 | 325 | ||
472 | if (where) | ||
473 | list_add(&new->nd, where); | ||
474 | else | ||
475 | list_add_tail(&new->nd, &memtype_list); | ||
476 | |||
477 | memtype_rb_insert(&memtype_rbroot, new); | ||
478 | |||
479 | spin_unlock(&memtype_lock); | 326 | spin_unlock(&memtype_lock); |
480 | 327 | ||
481 | dprintk("reserve_memtype added 0x%Lx-0x%Lx, track %s, req %s, ret %s\n", | 328 | dprintk("reserve_memtype added 0x%Lx-0x%Lx, track %s, req %s, ret %s\n", |
@@ -487,7 +334,6 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, | |||
487 | 334 | ||
488 | int free_memtype(u64 start, u64 end) | 335 | int free_memtype(u64 start, u64 end) |
489 | { | 336 | { |
490 | struct memtype *entry, *saved_entry; | ||
491 | int err = -EINVAL; | 337 | int err = -EINVAL; |
492 | int is_range_ram; | 338 | int is_range_ram; |
493 | 339 | ||
@@ -501,9 +347,7 @@ int free_memtype(u64 start, u64 end) | |||
501 | is_range_ram = pat_pagerange_is_ram(start, end); | 347 | is_range_ram = pat_pagerange_is_ram(start, end); |
502 | if (is_range_ram == 1) { | 348 | if (is_range_ram == 1) { |
503 | 349 | ||
504 | spin_lock(&memtype_lock); | ||
505 | err = free_ram_pages_type(start, end); | 350 | err = free_ram_pages_type(start, end); |
506 | spin_unlock(&memtype_lock); | ||
507 | 351 | ||
508 | return err; | 352 | return err; |
509 | } else if (is_range_ram < 0) { | 353 | } else if (is_range_ram < 0) { |
@@ -511,46 +355,7 @@ int free_memtype(u64 start, u64 end) | |||
511 | } | 355 | } |
512 | 356 | ||
513 | spin_lock(&memtype_lock); | 357 | spin_lock(&memtype_lock); |
514 | 358 | err = rbt_memtype_erase(start, end); | |
515 | entry = memtype_rb_search(&memtype_rbroot, start); | ||
516 | if (unlikely(entry == NULL)) | ||
517 | goto unlock_ret; | ||
518 | |||
519 | /* | ||
520 | * Saved entry points to an entry with start same or less than what | ||
521 | * we searched for. Now go through the list in both directions to look | ||
522 | * for the entry that matches with both start and end, with list stored | ||
523 | * in sorted start address | ||
524 | */ | ||
525 | saved_entry = entry; | ||
526 | list_for_each_entry_from(entry, &memtype_list, nd) { | ||
527 | if (entry->start == start && entry->end == end) { | ||
528 | rb_erase(&entry->rb, &memtype_rbroot); | ||
529 | list_del(&entry->nd); | ||
530 | kfree(entry); | ||
531 | err = 0; | ||
532 | break; | ||
533 | } else if (entry->start > start) { | ||
534 | break; | ||
535 | } | ||
536 | } | ||
537 | |||
538 | if (!err) | ||
539 | goto unlock_ret; | ||
540 | |||
541 | entry = saved_entry; | ||
542 | list_for_each_entry_reverse(entry, &memtype_list, nd) { | ||
543 | if (entry->start == start && entry->end == end) { | ||
544 | rb_erase(&entry->rb, &memtype_rbroot); | ||
545 | list_del(&entry->nd); | ||
546 | kfree(entry); | ||
547 | err = 0; | ||
548 | break; | ||
549 | } else if (entry->start < start) { | ||
550 | break; | ||
551 | } | ||
552 | } | ||
553 | unlock_ret: | ||
554 | spin_unlock(&memtype_lock); | 359 | spin_unlock(&memtype_lock); |
555 | 360 | ||
556 | if (err) { | 361 | if (err) { |
@@ -583,10 +388,8 @@ static unsigned long lookup_memtype(u64 paddr) | |||
583 | 388 | ||
584 | if (pat_pagerange_is_ram(paddr, paddr + PAGE_SIZE)) { | 389 | if (pat_pagerange_is_ram(paddr, paddr + PAGE_SIZE)) { |
585 | struct page *page; | 390 | struct page *page; |
586 | spin_lock(&memtype_lock); | ||
587 | page = pfn_to_page(paddr >> PAGE_SHIFT); | 391 | page = pfn_to_page(paddr >> PAGE_SHIFT); |
588 | rettype = get_page_memtype(page); | 392 | rettype = get_page_memtype(page); |
589 | spin_unlock(&memtype_lock); | ||
590 | /* | 393 | /* |
591 | * -1 from get_page_memtype() implies RAM page is in its | 394 | * -1 from get_page_memtype() implies RAM page is in its |
592 | * default state and not reserved, and hence of type WB | 395 | * default state and not reserved, and hence of type WB |
@@ -599,7 +402,7 @@ static unsigned long lookup_memtype(u64 paddr) | |||
599 | 402 | ||
600 | spin_lock(&memtype_lock); | 403 | spin_lock(&memtype_lock); |
601 | 404 | ||
602 | entry = memtype_rb_search(&memtype_rbroot, paddr); | 405 | entry = rbt_memtype_lookup(paddr); |
603 | if (entry != NULL) | 406 | if (entry != NULL) |
604 | rettype = entry->type; | 407 | rettype = entry->type; |
605 | else | 408 | else |
@@ -936,29 +739,25 @@ EXPORT_SYMBOL_GPL(pgprot_writecombine); | |||
936 | 739 | ||
937 | #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_X86_PAT) | 740 | #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_X86_PAT) |
938 | 741 | ||
939 | /* get Nth element of the linked list */ | ||
940 | static struct memtype *memtype_get_idx(loff_t pos) | 742 | static struct memtype *memtype_get_idx(loff_t pos) |
941 | { | 743 | { |
942 | struct memtype *list_node, *print_entry; | 744 | struct memtype *print_entry; |
943 | int i = 1; | 745 | int ret; |
944 | 746 | ||
945 | print_entry = kmalloc(sizeof(struct memtype), GFP_KERNEL); | 747 | print_entry = kzalloc(sizeof(struct memtype), GFP_KERNEL); |
946 | if (!print_entry) | 748 | if (!print_entry) |
947 | return NULL; | 749 | return NULL; |
948 | 750 | ||
949 | spin_lock(&memtype_lock); | 751 | spin_lock(&memtype_lock); |
950 | list_for_each_entry(list_node, &memtype_list, nd) { | 752 | ret = rbt_memtype_copy_nth_element(print_entry, pos); |
951 | if (pos == i) { | ||
952 | *print_entry = *list_node; | ||
953 | spin_unlock(&memtype_lock); | ||
954 | return print_entry; | ||
955 | } | ||
956 | ++i; | ||
957 | } | ||
958 | spin_unlock(&memtype_lock); | 753 | spin_unlock(&memtype_lock); |
959 | kfree(print_entry); | ||
960 | 754 | ||
961 | return NULL; | 755 | if (!ret) { |
756 | return print_entry; | ||
757 | } else { | ||
758 | kfree(print_entry); | ||
759 | return NULL; | ||
760 | } | ||
962 | } | 761 | } |
963 | 762 | ||
964 | static void *memtype_seq_start(struct seq_file *seq, loff_t *pos) | 763 | static void *memtype_seq_start(struct seq_file *seq, loff_t *pos) |
diff --git a/arch/x86/mm/pat_internal.h b/arch/x86/mm/pat_internal.h new file mode 100644 index 000000000000..4f39eefa3e61 --- /dev/null +++ b/arch/x86/mm/pat_internal.h | |||
@@ -0,0 +1,46 @@ | |||
1 | #ifndef __PAT_INTERNAL_H_ | ||
2 | #define __PAT_INTERNAL_H_ | ||
3 | |||
4 | extern int pat_debug_enable; | ||
5 | |||
6 | #define dprintk(fmt, arg...) \ | ||
7 | do { if (pat_debug_enable) printk(KERN_INFO fmt, ##arg); } while (0) | ||
8 | |||
9 | struct memtype { | ||
10 | u64 start; | ||
11 | u64 end; | ||
12 | u64 subtree_max_end; | ||
13 | unsigned long type; | ||
14 | struct rb_node rb; | ||
15 | }; | ||
16 | |||
17 | static inline char *cattr_name(unsigned long flags) | ||
18 | { | ||
19 | switch (flags & _PAGE_CACHE_MASK) { | ||
20 | case _PAGE_CACHE_UC: return "uncached"; | ||
21 | case _PAGE_CACHE_UC_MINUS: return "uncached-minus"; | ||
22 | case _PAGE_CACHE_WB: return "write-back"; | ||
23 | case _PAGE_CACHE_WC: return "write-combining"; | ||
24 | default: return "broken"; | ||
25 | } | ||
26 | } | ||
27 | |||
28 | #ifdef CONFIG_X86_PAT | ||
29 | extern int rbt_memtype_check_insert(struct memtype *new, | ||
30 | unsigned long *new_type); | ||
31 | extern int rbt_memtype_erase(u64 start, u64 end); | ||
32 | extern struct memtype *rbt_memtype_lookup(u64 addr); | ||
33 | extern int rbt_memtype_copy_nth_element(struct memtype *out, loff_t pos); | ||
34 | #else | ||
35 | static inline int rbt_memtype_check_insert(struct memtype *new, | ||
36 | unsigned long *new_type) | ||
37 | { return 0; } | ||
38 | static inline int rbt_memtype_erase(u64 start, u64 end) | ||
39 | { return 0; } | ||
40 | static inline struct memtype *rbt_memtype_lookup(u64 addr) | ||
41 | { return NULL; } | ||
42 | static inline int rbt_memtype_copy_nth_element(struct memtype *out, loff_t pos) | ||
43 | { return 0; } | ||
44 | #endif | ||
45 | |||
46 | #endif /* __PAT_INTERNAL_H_ */ | ||
diff --git a/arch/x86/mm/pat_rbtree.c b/arch/x86/mm/pat_rbtree.c new file mode 100644 index 000000000000..07de4cb8cc30 --- /dev/null +++ b/arch/x86/mm/pat_rbtree.c | |||
@@ -0,0 +1,273 @@ | |||
1 | /* | ||
2 | * Handle caching attributes in page tables (PAT) | ||
3 | * | ||
4 | * Authors: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> | ||
5 | * Suresh B Siddha <suresh.b.siddha@intel.com> | ||
6 | * | ||
7 | * Interval tree (augmented rbtree) used to store the PAT memory type | ||
8 | * reservations. | ||
9 | */ | ||
10 | |||
11 | #include <linux/seq_file.h> | ||
12 | #include <linux/debugfs.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/rbtree.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/gfp.h> | ||
18 | |||
19 | #include <asm/pgtable.h> | ||
20 | #include <asm/pat.h> | ||
21 | |||
22 | #include "pat_internal.h" | ||
23 | |||
24 | /* | ||
25 | * The memtype tree keeps track of memory type for specific | ||
26 | * physical memory areas. Without proper tracking, conflicting memory | ||
27 | * types in different mappings can cause CPU cache corruption. | ||
28 | * | ||
29 | * The tree is an interval tree (augmented rbtree) with tree ordered | ||
30 | * on starting address. Tree can contain multiple entries for | ||
31 | * different regions which overlap. All the aliases have the same | ||
32 | * cache attributes of course. | ||
33 | * | ||
34 | * memtype_lock protects the rbtree. | ||
35 | */ | ||
36 | |||
37 | static void memtype_rb_augment_cb(struct rb_node *node); | ||
38 | static struct rb_root memtype_rbroot = RB_AUGMENT_ROOT(&memtype_rb_augment_cb); | ||
39 | |||
40 | static int is_node_overlap(struct memtype *node, u64 start, u64 end) | ||
41 | { | ||
42 | if (node->start >= end || node->end <= start) | ||
43 | return 0; | ||
44 | |||
45 | return 1; | ||
46 | } | ||
47 | |||
48 | static u64 get_subtree_max_end(struct rb_node *node) | ||
49 | { | ||
50 | u64 ret = 0; | ||
51 | if (node) { | ||
52 | struct memtype *data = container_of(node, struct memtype, rb); | ||
53 | ret = data->subtree_max_end; | ||
54 | } | ||
55 | return ret; | ||
56 | } | ||
57 | |||
58 | /* Update 'subtree_max_end' for a node, based on node and its children */ | ||
59 | static void update_node_max_end(struct rb_node *node) | ||
60 | { | ||
61 | struct memtype *data; | ||
62 | u64 max_end, child_max_end; | ||
63 | |||
64 | if (!node) | ||
65 | return; | ||
66 | |||
67 | data = container_of(node, struct memtype, rb); | ||
68 | max_end = data->end; | ||
69 | |||
70 | child_max_end = get_subtree_max_end(node->rb_right); | ||
71 | if (child_max_end > max_end) | ||
72 | max_end = child_max_end; | ||
73 | |||
74 | child_max_end = get_subtree_max_end(node->rb_left); | ||
75 | if (child_max_end > max_end) | ||
76 | max_end = child_max_end; | ||
77 | |||
78 | data->subtree_max_end = max_end; | ||
79 | } | ||
80 | |||
81 | /* Update 'subtree_max_end' for a node and all its ancestors */ | ||
82 | static void update_path_max_end(struct rb_node *node) | ||
83 | { | ||
84 | u64 old_max_end, new_max_end; | ||
85 | |||
86 | while (node) { | ||
87 | struct memtype *data = container_of(node, struct memtype, rb); | ||
88 | |||
89 | old_max_end = data->subtree_max_end; | ||
90 | update_node_max_end(node); | ||
91 | new_max_end = data->subtree_max_end; | ||
92 | |||
93 | if (new_max_end == old_max_end) | ||
94 | break; | ||
95 | |||
96 | node = rb_parent(node); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | /* Find the first (lowest start addr) overlapping range from rb tree */ | ||
101 | static struct memtype *memtype_rb_lowest_match(struct rb_root *root, | ||
102 | u64 start, u64 end) | ||
103 | { | ||
104 | struct rb_node *node = root->rb_node; | ||
105 | struct memtype *last_lower = NULL; | ||
106 | |||
107 | while (node) { | ||
108 | struct memtype *data = container_of(node, struct memtype, rb); | ||
109 | |||
110 | if (get_subtree_max_end(node->rb_left) > start) { | ||
111 | /* Lowest overlap if any must be on left side */ | ||
112 | node = node->rb_left; | ||
113 | } else if (is_node_overlap(data, start, end)) { | ||
114 | last_lower = data; | ||
115 | break; | ||
116 | } else if (start >= data->start) { | ||
117 | /* Lowest overlap if any must be on right side */ | ||
118 | node = node->rb_right; | ||
119 | } else { | ||
120 | break; | ||
121 | } | ||
122 | } | ||
123 | return last_lower; /* Returns NULL if there is no overlap */ | ||
124 | } | ||
125 | |||
126 | static struct memtype *memtype_rb_exact_match(struct rb_root *root, | ||
127 | u64 start, u64 end) | ||
128 | { | ||
129 | struct memtype *match; | ||
130 | |||
131 | match = memtype_rb_lowest_match(root, start, end); | ||
132 | while (match != NULL && match->start < end) { | ||
133 | struct rb_node *node; | ||
134 | |||
135 | if (match->start == start && match->end == end) | ||
136 | return match; | ||
137 | |||
138 | node = rb_next(&match->rb); | ||
139 | if (node) | ||
140 | match = container_of(node, struct memtype, rb); | ||
141 | else | ||
142 | match = NULL; | ||
143 | } | ||
144 | |||
145 | return NULL; /* Returns NULL if there is no exact match */ | ||
146 | } | ||
147 | |||
148 | static int memtype_rb_check_conflict(struct rb_root *root, | ||
149 | u64 start, u64 end, | ||
150 | unsigned long reqtype, unsigned long *newtype) | ||
151 | { | ||
152 | struct rb_node *node; | ||
153 | struct memtype *match; | ||
154 | int found_type = reqtype; | ||
155 | |||
156 | match = memtype_rb_lowest_match(&memtype_rbroot, start, end); | ||
157 | if (match == NULL) | ||
158 | goto success; | ||
159 | |||
160 | if (match->type != found_type && newtype == NULL) | ||
161 | goto failure; | ||
162 | |||
163 | dprintk("Overlap at 0x%Lx-0x%Lx\n", match->start, match->end); | ||
164 | found_type = match->type; | ||
165 | |||
166 | node = rb_next(&match->rb); | ||
167 | while (node) { | ||
168 | match = container_of(node, struct memtype, rb); | ||
169 | |||
170 | if (match->start >= end) /* Checked all possible matches */ | ||
171 | goto success; | ||
172 | |||
173 | if (is_node_overlap(match, start, end) && | ||
174 | match->type != found_type) { | ||
175 | goto failure; | ||
176 | } | ||
177 | |||
178 | node = rb_next(&match->rb); | ||
179 | } | ||
180 | success: | ||
181 | if (newtype) | ||
182 | *newtype = found_type; | ||
183 | |||
184 | return 0; | ||
185 | |||
186 | failure: | ||
187 | printk(KERN_INFO "%s:%d conflicting memory types " | ||
188 | "%Lx-%Lx %s<->%s\n", current->comm, current->pid, start, | ||
189 | end, cattr_name(found_type), cattr_name(match->type)); | ||
190 | return -EBUSY; | ||
191 | } | ||
192 | |||
193 | static void memtype_rb_augment_cb(struct rb_node *node) | ||
194 | { | ||
195 | if (node) | ||
196 | update_path_max_end(node); | ||
197 | } | ||
198 | |||
199 | static void memtype_rb_insert(struct rb_root *root, struct memtype *newdata) | ||
200 | { | ||
201 | struct rb_node **node = &(root->rb_node); | ||
202 | struct rb_node *parent = NULL; | ||
203 | |||
204 | while (*node) { | ||
205 | struct memtype *data = container_of(*node, struct memtype, rb); | ||
206 | |||
207 | parent = *node; | ||
208 | if (newdata->start <= data->start) | ||
209 | node = &((*node)->rb_left); | ||
210 | else if (newdata->start > data->start) | ||
211 | node = &((*node)->rb_right); | ||
212 | } | ||
213 | |||
214 | rb_link_node(&newdata->rb, parent, node); | ||
215 | rb_insert_color(&newdata->rb, root); | ||
216 | } | ||
217 | |||
218 | int rbt_memtype_check_insert(struct memtype *new, unsigned long *ret_type) | ||
219 | { | ||
220 | int err = 0; | ||
221 | |||
222 | err = memtype_rb_check_conflict(&memtype_rbroot, new->start, new->end, | ||
223 | new->type, ret_type); | ||
224 | |||
225 | if (!err) { | ||
226 | if (ret_type) | ||
227 | new->type = *ret_type; | ||
228 | |||
229 | memtype_rb_insert(&memtype_rbroot, new); | ||
230 | } | ||
231 | return err; | ||
232 | } | ||
233 | |||
234 | int rbt_memtype_erase(u64 start, u64 end) | ||
235 | { | ||
236 | struct memtype *data; | ||
237 | |||
238 | data = memtype_rb_exact_match(&memtype_rbroot, start, end); | ||
239 | if (!data) | ||
240 | return -EINVAL; | ||
241 | |||
242 | rb_erase(&data->rb, &memtype_rbroot); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | struct memtype *rbt_memtype_lookup(u64 addr) | ||
247 | { | ||
248 | struct memtype *data; | ||
249 | data = memtype_rb_lowest_match(&memtype_rbroot, addr, addr + PAGE_SIZE); | ||
250 | return data; | ||
251 | } | ||
252 | |||
253 | #if defined(CONFIG_DEBUG_FS) | ||
254 | int rbt_memtype_copy_nth_element(struct memtype *out, loff_t pos) | ||
255 | { | ||
256 | struct rb_node *node; | ||
257 | int i = 1; | ||
258 | |||
259 | node = rb_first(&memtype_rbroot); | ||
260 | while (node && pos != i) { | ||
261 | node = rb_next(node); | ||
262 | i++; | ||
263 | } | ||
264 | |||
265 | if (node) { /* pos == i */ | ||
266 | struct memtype *this = container_of(node, struct memtype, rb); | ||
267 | *out = *this; | ||
268 | return 0; | ||
269 | } else { | ||
270 | return 1; | ||
271 | } | ||
272 | } | ||
273 | #endif | ||
diff --git a/include/linux/rbtree.h b/include/linux/rbtree.h index 5210a5c60877..fe1872e5b37e 100644 --- a/include/linux/rbtree.h +++ b/include/linux/rbtree.h | |||
@@ -110,6 +110,7 @@ struct rb_node | |||
110 | struct rb_root | 110 | struct rb_root |
111 | { | 111 | { |
112 | struct rb_node *rb_node; | 112 | struct rb_node *rb_node; |
113 | void (*augment_cb)(struct rb_node *node); | ||
113 | }; | 114 | }; |
114 | 115 | ||
115 | 116 | ||
@@ -129,7 +130,9 @@ static inline void rb_set_color(struct rb_node *rb, int color) | |||
129 | rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; | 130 | rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; |
130 | } | 131 | } |
131 | 132 | ||
132 | #define RB_ROOT (struct rb_root) { NULL, } | 133 | #define RB_ROOT (struct rb_root) { NULL, NULL, } |
134 | #define RB_AUGMENT_ROOT(x) (struct rb_root) { NULL, x} | ||
135 | |||
133 | #define rb_entry(ptr, type, member) container_of(ptr, type, member) | 136 | #define rb_entry(ptr, type, member) container_of(ptr, type, member) |
134 | 137 | ||
135 | #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) | 138 | #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) |
diff --git a/lib/rbtree.c b/lib/rbtree.c index e2aa3be29858..15e10b1afdd2 100644 --- a/lib/rbtree.c +++ b/lib/rbtree.c | |||
@@ -44,6 +44,11 @@ static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) | |||
44 | else | 44 | else |
45 | root->rb_node = right; | 45 | root->rb_node = right; |
46 | rb_set_parent(node, right); | 46 | rb_set_parent(node, right); |
47 | |||
48 | if (root->augment_cb) { | ||
49 | root->augment_cb(node); | ||
50 | root->augment_cb(right); | ||
51 | } | ||
47 | } | 52 | } |
48 | 53 | ||
49 | static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) | 54 | static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) |
@@ -67,12 +72,20 @@ static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) | |||
67 | else | 72 | else |
68 | root->rb_node = left; | 73 | root->rb_node = left; |
69 | rb_set_parent(node, left); | 74 | rb_set_parent(node, left); |
75 | |||
76 | if (root->augment_cb) { | ||
77 | root->augment_cb(node); | ||
78 | root->augment_cb(left); | ||
79 | } | ||
70 | } | 80 | } |
71 | 81 | ||
72 | void rb_insert_color(struct rb_node *node, struct rb_root *root) | 82 | void rb_insert_color(struct rb_node *node, struct rb_root *root) |
73 | { | 83 | { |
74 | struct rb_node *parent, *gparent; | 84 | struct rb_node *parent, *gparent; |
75 | 85 | ||
86 | if (root->augment_cb) | ||
87 | root->augment_cb(node); | ||
88 | |||
76 | while ((parent = rb_parent(node)) && rb_is_red(parent)) | 89 | while ((parent = rb_parent(node)) && rb_is_red(parent)) |
77 | { | 90 | { |
78 | gparent = rb_parent(parent); | 91 | gparent = rb_parent(parent); |
@@ -227,12 +240,15 @@ void rb_erase(struct rb_node *node, struct rb_root *root) | |||
227 | else | 240 | else |
228 | { | 241 | { |
229 | struct rb_node *old = node, *left; | 242 | struct rb_node *old = node, *left; |
243 | int old_parent_cb = 0; | ||
244 | int successor_parent_cb = 0; | ||
230 | 245 | ||
231 | node = node->rb_right; | 246 | node = node->rb_right; |
232 | while ((left = node->rb_left) != NULL) | 247 | while ((left = node->rb_left) != NULL) |
233 | node = left; | 248 | node = left; |
234 | 249 | ||
235 | if (rb_parent(old)) { | 250 | if (rb_parent(old)) { |
251 | old_parent_cb = 1; | ||
236 | if (rb_parent(old)->rb_left == old) | 252 | if (rb_parent(old)->rb_left == old) |
237 | rb_parent(old)->rb_left = node; | 253 | rb_parent(old)->rb_left = node; |
238 | else | 254 | else |
@@ -247,8 +263,10 @@ void rb_erase(struct rb_node *node, struct rb_root *root) | |||
247 | if (parent == old) { | 263 | if (parent == old) { |
248 | parent = node; | 264 | parent = node; |
249 | } else { | 265 | } else { |
266 | successor_parent_cb = 1; | ||
250 | if (child) | 267 | if (child) |
251 | rb_set_parent(child, parent); | 268 | rb_set_parent(child, parent); |
269 | |||
252 | parent->rb_left = child; | 270 | parent->rb_left = child; |
253 | 271 | ||
254 | node->rb_right = old->rb_right; | 272 | node->rb_right = old->rb_right; |
@@ -259,6 +277,24 @@ void rb_erase(struct rb_node *node, struct rb_root *root) | |||
259 | node->rb_left = old->rb_left; | 277 | node->rb_left = old->rb_left; |
260 | rb_set_parent(old->rb_left, node); | 278 | rb_set_parent(old->rb_left, node); |
261 | 279 | ||
280 | if (root->augment_cb) { | ||
281 | /* | ||
282 | * Here, three different nodes can have new children. | ||
283 | * The parent of the successor node that was selected | ||
284 | * to replace the node to be erased. | ||
285 | * The node that is getting erased and is now replaced | ||
286 | * by its successor. | ||
287 | * The parent of the node getting erased-replaced. | ||
288 | */ | ||
289 | if (successor_parent_cb) | ||
290 | root->augment_cb(parent); | ||
291 | |||
292 | root->augment_cb(node); | ||
293 | |||
294 | if (old_parent_cb) | ||
295 | root->augment_cb(rb_parent(old)); | ||
296 | } | ||
297 | |||
262 | goto color; | 298 | goto color; |
263 | } | 299 | } |
264 | 300 | ||
@@ -267,15 +303,19 @@ void rb_erase(struct rb_node *node, struct rb_root *root) | |||
267 | 303 | ||
268 | if (child) | 304 | if (child) |
269 | rb_set_parent(child, parent); | 305 | rb_set_parent(child, parent); |
270 | if (parent) | 306 | |
271 | { | 307 | if (parent) { |
272 | if (parent->rb_left == node) | 308 | if (parent->rb_left == node) |
273 | parent->rb_left = child; | 309 | parent->rb_left = child; |
274 | else | 310 | else |
275 | parent->rb_right = child; | 311 | parent->rb_right = child; |
276 | } | 312 | |
277 | else | 313 | if (root->augment_cb) |
314 | root->augment_cb(parent); | ||
315 | |||
316 | } else { | ||
278 | root->rb_node = child; | 317 | root->rb_node = child; |
318 | } | ||
279 | 319 | ||
280 | color: | 320 | color: |
281 | if (color == RB_BLACK) | 321 | if (color == RB_BLACK) |