diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-18 12:28:04 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-18 12:28:04 -0400 |
| commit | c4fd308ed62f292518363ea9c6c2adb3c2d95f9d (patch) | |
| tree | d6b4e36159e502a43a91ade86379703442204fc5 | |
| parent | 96fbeb973a7e17594a429537201611ca0b395622 (diff) | |
| parent | 1f9cc3cb6a27521edfe0a21abf97d2bb11c4d237 (diff) | |
Merge branch 'x86-pat-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'x86-pat-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
x86, pat: Update the page flags for memtype atomically instead of using memtype_lock
x86, pat: In rbt_memtype_check_insert(), update new->type only if valid
x86, pat: Migrate to rbtree only backend for pat memtype management
x86, pat: Preparatory changes in pat.c for bigger rbtree change
rbtree: Add support for augmented rbtrees
| -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" | ||
