diff options
-rw-r--r-- | arch/x86/include/asm/cacheflush.h | 54 | ||||
-rw-r--r-- | arch/x86/mm/pat.c | 91 |
2 files changed, 102 insertions, 43 deletions
diff --git a/arch/x86/include/asm/cacheflush.h b/arch/x86/include/asm/cacheflush.h index e55dfc1ad453..b54f6afe7ec4 100644 --- a/arch/x86/include/asm/cacheflush.h +++ b/arch/x86/include/asm/cacheflush.h | |||
@@ -43,8 +43,58 @@ static inline void copy_from_user_page(struct vm_area_struct *vma, | |||
43 | memcpy(dst, src, len); | 43 | memcpy(dst, src, len); |
44 | } | 44 | } |
45 | 45 | ||
46 | #define PG_non_WB PG_arch_1 | 46 | #define PG_WC PG_arch_1 |
47 | PAGEFLAG(NonWB, non_WB) | 47 | PAGEFLAG(WC, WC) |
48 | |||
49 | #ifdef CONFIG_X86_PAT | ||
50 | /* | ||
51 | * X86 PAT uses page flags WC and Uncached together to keep track of | ||
52 | * memory type of pages that have backing page struct. X86 PAT supports 3 | ||
53 | * different memory types, _PAGE_CACHE_WB, _PAGE_CACHE_WC and | ||
54 | * _PAGE_CACHE_UC_MINUS and fourth state where page's memory type has not | ||
55 | * been changed from its default (value of -1 used to denote this). | ||
56 | * Note we do not support _PAGE_CACHE_UC here. | ||
57 | * | ||
58 | * Caller must hold memtype_lock for atomicity. | ||
59 | */ | ||
60 | static inline unsigned long get_page_memtype(struct page *pg) | ||
61 | { | ||
62 | if (!PageUncached(pg) && !PageWC(pg)) | ||
63 | return -1; | ||
64 | else if (!PageUncached(pg) && PageWC(pg)) | ||
65 | return _PAGE_CACHE_WC; | ||
66 | else if (PageUncached(pg) && !PageWC(pg)) | ||
67 | return _PAGE_CACHE_UC_MINUS; | ||
68 | else | ||
69 | return _PAGE_CACHE_WB; | ||
70 | } | ||
71 | |||
72 | static inline void set_page_memtype(struct page *pg, unsigned long memtype) | ||
73 | { | ||
74 | switch (memtype) { | ||
75 | case _PAGE_CACHE_WC: | ||
76 | ClearPageUncached(pg); | ||
77 | SetPageWC(pg); | ||
78 | break; | ||
79 | case _PAGE_CACHE_UC_MINUS: | ||
80 | SetPageUncached(pg); | ||
81 | ClearPageWC(pg); | ||
82 | break; | ||
83 | case _PAGE_CACHE_WB: | ||
84 | SetPageUncached(pg); | ||
85 | SetPageWC(pg); | ||
86 | break; | ||
87 | default: | ||
88 | case -1: | ||
89 | ClearPageUncached(pg); | ||
90 | ClearPageWC(pg); | ||
91 | break; | ||
92 | } | ||
93 | } | ||
94 | #else | ||
95 | static inline unsigned long get_page_memtype(struct page *pg) { return -1; } | ||
96 | static inline void set_page_memtype(struct page *pg, unsigned long memtype) { } | ||
97 | #endif | ||
48 | 98 | ||
49 | /* | 99 | /* |
50 | * The set_memory_* API can be used to change various attributes of a virtual | 100 | * The set_memory_* API can be used to change various attributes of a virtual |
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c index c90f2420f56c..1a9d0f07593f 100644 --- a/arch/x86/mm/pat.c +++ b/arch/x86/mm/pat.c | |||
@@ -288,63 +288,61 @@ static int pat_pagerange_is_ram(unsigned long start, unsigned long end) | |||
288 | } | 288 | } |
289 | 289 | ||
290 | /* | 290 | /* |
291 | * For RAM pages, mark the pages as non WB memory type using | 291 | * For RAM pages, we use page flags to mark the pages with appropriate type. |
292 | * PageNonWB (PG_arch_1). We allow only one set_memory_uc() or | 292 | * Here we do two pass: |
293 | * set_memory_wc() on a RAM page at a time before marking it as WB again. | 293 | * - Find the memtype of all the pages in the range, look for any conflicts |
294 | * This is ok, because only one driver will be owning the page and | 294 | * - In case of no conflicts, set the new memtype for pages in the range |
295 | * doing set_memory_*() calls. | ||
296 | * | 295 | * |
297 | * For now, we use PageNonWB to track that the RAM page is being mapped | 296 | * Caller must hold memtype_lock for atomicity. |
298 | * as non WB. In future, we will have to use one more flag | ||
299 | * (or some other mechanism in page_struct) to distinguish between | ||
300 | * UC and WC mapping. | ||
301 | */ | 297 | */ |
302 | static int reserve_ram_pages_type(u64 start, u64 end, unsigned long req_type, | 298 | static int reserve_ram_pages_type(u64 start, u64 end, unsigned long req_type, |
303 | unsigned long *new_type) | 299 | unsigned long *new_type) |
304 | { | 300 | { |
305 | struct page *page; | 301 | struct page *page; |
306 | u64 pfn, end_pfn; | 302 | u64 pfn; |
303 | |||
304 | if (req_type == _PAGE_CACHE_UC) { | ||
305 | /* We do not support strong UC */ | ||
306 | WARN_ON_ONCE(1); | ||
307 | req_type = _PAGE_CACHE_UC_MINUS; | ||
308 | } | ||
307 | 309 | ||
308 | for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) { | 310 | for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) { |
309 | page = pfn_to_page(pfn); | 311 | unsigned long type; |
310 | if (page_mapped(page) || PageNonWB(page)) | ||
311 | goto out; | ||
312 | 312 | ||
313 | SetPageNonWB(page); | 313 | page = pfn_to_page(pfn); |
314 | type = get_page_memtype(page); | ||
315 | if (type != -1) { | ||
316 | printk(KERN_INFO "reserve_ram_pages_type failed " | ||
317 | "0x%Lx-0x%Lx, track 0x%lx, req 0x%lx\n", | ||
318 | start, end, type, req_type); | ||
319 | if (new_type) | ||
320 | *new_type = type; | ||
321 | |||
322 | return -EBUSY; | ||
323 | } | ||
314 | } | 324 | } |
315 | return 0; | ||
316 | 325 | ||
317 | out: | 326 | if (new_type) |
318 | end_pfn = pfn; | 327 | *new_type = req_type; |
319 | for (pfn = (start >> PAGE_SHIFT); pfn < end_pfn; ++pfn) { | 328 | |
329 | for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) { | ||
320 | page = pfn_to_page(pfn); | 330 | page = pfn_to_page(pfn); |
321 | ClearPageNonWB(page); | 331 | set_page_memtype(page, req_type); |
322 | } | 332 | } |
323 | 333 | return 0; | |
324 | return -EINVAL; | ||
325 | } | 334 | } |
326 | 335 | ||
327 | static int free_ram_pages_type(u64 start, u64 end) | 336 | static int free_ram_pages_type(u64 start, u64 end) |
328 | { | 337 | { |
329 | struct page *page; | 338 | struct page *page; |
330 | u64 pfn, end_pfn; | 339 | u64 pfn; |
331 | 340 | ||
332 | for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) { | 341 | for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) { |
333 | page = pfn_to_page(pfn); | 342 | page = pfn_to_page(pfn); |
334 | if (page_mapped(page) || !PageNonWB(page)) | 343 | set_page_memtype(page, -1); |
335 | goto out; | ||
336 | |||
337 | ClearPageNonWB(page); | ||
338 | } | 344 | } |
339 | return 0; | 345 | return 0; |
340 | |||
341 | out: | ||
342 | end_pfn = pfn; | ||
343 | for (pfn = (start >> PAGE_SHIFT); pfn < end_pfn; ++pfn) { | ||
344 | page = pfn_to_page(pfn); | ||
345 | SetPageNonWB(page); | ||
346 | } | ||
347 | return -EINVAL; | ||
348 | } | 346 | } |
349 | 347 | ||
350 | /* | 348 | /* |
@@ -405,11 +403,16 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, | |||
405 | *new_type = actual_type; | 403 | *new_type = actual_type; |
406 | 404 | ||
407 | is_range_ram = pat_pagerange_is_ram(start, end); | 405 | is_range_ram = pat_pagerange_is_ram(start, end); |
408 | if (is_range_ram == 1) | 406 | if (is_range_ram == 1) { |
409 | return reserve_ram_pages_type(start, end, req_type, | 407 | |
410 | new_type); | 408 | spin_lock(&memtype_lock); |
411 | else if (is_range_ram < 0) | 409 | err = reserve_ram_pages_type(start, end, req_type, new_type); |
410 | spin_unlock(&memtype_lock); | ||
411 | |||
412 | return err; | ||
413 | } else if (is_range_ram < 0) { | ||
412 | return -EINVAL; | 414 | return -EINVAL; |
415 | } | ||
413 | 416 | ||
414 | new = kmalloc(sizeof(struct memtype), GFP_KERNEL); | 417 | new = kmalloc(sizeof(struct memtype), GFP_KERNEL); |
415 | if (!new) | 418 | if (!new) |
@@ -505,10 +508,16 @@ int free_memtype(u64 start, u64 end) | |||
505 | return 0; | 508 | return 0; |
506 | 509 | ||
507 | is_range_ram = pat_pagerange_is_ram(start, end); | 510 | is_range_ram = pat_pagerange_is_ram(start, end); |
508 | if (is_range_ram == 1) | 511 | if (is_range_ram == 1) { |
509 | return free_ram_pages_type(start, end); | 512 | |
510 | else if (is_range_ram < 0) | 513 | spin_lock(&memtype_lock); |
514 | err = free_ram_pages_type(start, end); | ||
515 | spin_unlock(&memtype_lock); | ||
516 | |||
517 | return err; | ||
518 | } else if (is_range_ram < 0) { | ||
511 | return -EINVAL; | 519 | return -EINVAL; |
520 | } | ||
512 | 521 | ||
513 | spin_lock(&memtype_lock); | 522 | spin_lock(&memtype_lock); |
514 | 523 | ||