diff options
| -rw-r--r-- | Documentation/cachetlb.txt | 24 | ||||
| -rw-r--r-- | arch/arm/include/asm/cacheflush.h | 10 | ||||
| -rw-r--r-- | arch/parisc/include/asm/cacheflush.h | 12 | ||||
| -rw-r--r-- | arch/sh/include/asm/cacheflush.h | 8 | ||||
| -rw-r--r-- | fs/xfs/linux-2.6/xfs_buf.c | 30 | ||||
| -rw-r--r-- | include/linux/highmem.h | 6 |
6 files changed, 89 insertions, 1 deletions
diff --git a/Documentation/cachetlb.txt b/Documentation/cachetlb.txt index da42ab414c48..b231414bb8bc 100644 --- a/Documentation/cachetlb.txt +++ b/Documentation/cachetlb.txt | |||
| @@ -377,3 +377,27 @@ maps this page at its virtual address. | |||
| 377 | All the functionality of flush_icache_page can be implemented in | 377 | All the functionality of flush_icache_page can be implemented in |
| 378 | flush_dcache_page and update_mmu_cache. In 2.7 the hope is to | 378 | flush_dcache_page and update_mmu_cache. In 2.7 the hope is to |
| 379 | remove this interface completely. | 379 | remove this interface completely. |
| 380 | |||
| 381 | The final category of APIs is for I/O to deliberately aliased address | ||
| 382 | ranges inside the kernel. Such aliases are set up by use of the | ||
| 383 | vmap/vmalloc API. Since kernel I/O goes via physical pages, the I/O | ||
| 384 | subsystem assumes that the user mapping and kernel offset mapping are | ||
| 385 | the only aliases. This isn't true for vmap aliases, so anything in | ||
| 386 | the kernel trying to do I/O to vmap areas must manually manage | ||
| 387 | coherency. It must do this by flushing the vmap range before doing | ||
| 388 | I/O and invalidating it after the I/O returns. | ||
| 389 | |||
| 390 | void flush_kernel_vmap_range(void *vaddr, int size) | ||
| 391 | flushes the kernel cache for a given virtual address range in | ||
| 392 | the vmap area. This is to make sure that any data the kernel | ||
| 393 | modified in the vmap range is made visible to the physical | ||
| 394 | page. The design is to make this area safe to perform I/O on. | ||
| 395 | Note that this API does *not* also flush the offset map alias | ||
| 396 | of the area. | ||
| 397 | |||
| 398 | void invalidate_kernel_vmap_range(void *vaddr, int size) invalidates | ||
| 399 | the cache for a given virtual address range in the vmap area | ||
| 400 | which prevents the processor from making the cache stale by | ||
| 401 | speculatively reading data while the I/O was occurring to the | ||
| 402 | physical pages. This is only necessary for data reads into the | ||
| 403 | vmap area. | ||
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index 8113bb5fb66e..5fe4a2ad7fa3 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h | |||
| @@ -447,6 +447,16 @@ static inline void __flush_icache_all(void) | |||
| 447 | : "r" (0)); | 447 | : "r" (0)); |
| 448 | #endif | 448 | #endif |
| 449 | } | 449 | } |
| 450 | static inline void flush_kernel_vmap_range(void *addr, int size) | ||
| 451 | { | ||
| 452 | if ((cache_is_vivt() || cache_is_vipt_aliasing())) | ||
| 453 | __cpuc_flush_dcache_area(addr, (size_t)size); | ||
| 454 | } | ||
| 455 | static inline void invalidate_kernel_vmap_range(void *addr, int size) | ||
| 456 | { | ||
| 457 | if ((cache_is_vivt() || cache_is_vipt_aliasing())) | ||
| 458 | __cpuc_flush_dcache_area(addr, (size_t)size); | ||
| 459 | } | ||
| 450 | 460 | ||
| 451 | #define ARCH_HAS_FLUSH_ANON_PAGE | 461 | #define ARCH_HAS_FLUSH_ANON_PAGE |
| 452 | static inline void flush_anon_page(struct vm_area_struct *vma, | 462 | static inline void flush_anon_page(struct vm_area_struct *vma, |
diff --git a/arch/parisc/include/asm/cacheflush.h b/arch/parisc/include/asm/cacheflush.h index 7a73b615c23d..477277739da5 100644 --- a/arch/parisc/include/asm/cacheflush.h +++ b/arch/parisc/include/asm/cacheflush.h | |||
| @@ -38,6 +38,18 @@ void flush_cache_mm(struct mm_struct *mm); | |||
| 38 | 38 | ||
| 39 | #define flush_kernel_dcache_range(start,size) \ | 39 | #define flush_kernel_dcache_range(start,size) \ |
| 40 | flush_kernel_dcache_range_asm((start), (start)+(size)); | 40 | flush_kernel_dcache_range_asm((start), (start)+(size)); |
| 41 | /* vmap range flushes and invalidates. Architecturally, we don't need | ||
| 42 | * the invalidate, because the CPU should refuse to speculate once an | ||
| 43 | * area has been flushed, so invalidate is left empty */ | ||
| 44 | static inline void flush_kernel_vmap_range(void *vaddr, int size) | ||
| 45 | { | ||
| 46 | unsigned long start = (unsigned long)vaddr; | ||
| 47 | |||
| 48 | flush_kernel_dcache_range_asm(start, start + size); | ||
| 49 | } | ||
| 50 | static inline void invalidate_kernel_vmap_range(void *vaddr, int size) | ||
| 51 | { | ||
| 52 | } | ||
| 41 | 53 | ||
| 42 | #define flush_cache_vmap(start, end) flush_cache_all() | 54 | #define flush_cache_vmap(start, end) flush_cache_all() |
| 43 | #define flush_cache_vunmap(start, end) flush_cache_all() | 55 | #define flush_cache_vunmap(start, end) flush_cache_all() |
diff --git a/arch/sh/include/asm/cacheflush.h b/arch/sh/include/asm/cacheflush.h index dda96eb3e7c0..da3ebec921a7 100644 --- a/arch/sh/include/asm/cacheflush.h +++ b/arch/sh/include/asm/cacheflush.h | |||
| @@ -63,6 +63,14 @@ static inline void flush_anon_page(struct vm_area_struct *vma, | |||
| 63 | if (boot_cpu_data.dcache.n_aliases && PageAnon(page)) | 63 | if (boot_cpu_data.dcache.n_aliases && PageAnon(page)) |
| 64 | __flush_anon_page(page, vmaddr); | 64 | __flush_anon_page(page, vmaddr); |
| 65 | } | 65 | } |
| 66 | static inline void flush_kernel_vmap_range(void *addr, int size) | ||
| 67 | { | ||
| 68 | __flush_wback_region(addr, size); | ||
| 69 | } | ||
| 70 | static inline void invalidate_kernel_vmap_range(void *addr, int size) | ||
| 71 | { | ||
| 72 | __flush_invalidate_region(addr, size); | ||
| 73 | } | ||
| 66 | 74 | ||
| 67 | #define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE | 75 | #define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE |
| 68 | static inline void flush_kernel_dcache_page(struct page *page) | 76 | static inline void flush_kernel_dcache_page(struct page *page) |
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 77b8be81c769..6f3ebb634b8b 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c | |||
| @@ -76,6 +76,27 @@ struct workqueue_struct *xfsconvertd_workqueue; | |||
| 76 | #define xfs_buf_deallocate(bp) \ | 76 | #define xfs_buf_deallocate(bp) \ |
| 77 | kmem_zone_free(xfs_buf_zone, (bp)); | 77 | kmem_zone_free(xfs_buf_zone, (bp)); |
| 78 | 78 | ||
| 79 | static inline int | ||
| 80 | xfs_buf_is_vmapped( | ||
| 81 | struct xfs_buf *bp) | ||
| 82 | { | ||
| 83 | /* | ||
| 84 | * Return true if the buffer is vmapped. | ||
| 85 | * | ||
| 86 | * The XBF_MAPPED flag is set if the buffer should be mapped, but the | ||
| 87 | * code is clever enough to know it doesn't have to map a single page, | ||
| 88 | * so the check has to be both for XBF_MAPPED and bp->b_page_count > 1. | ||
| 89 | */ | ||
| 90 | return (bp->b_flags & XBF_MAPPED) && bp->b_page_count > 1; | ||
| 91 | } | ||
| 92 | |||
| 93 | static inline int | ||
| 94 | xfs_buf_vmap_len( | ||
| 95 | struct xfs_buf *bp) | ||
| 96 | { | ||
| 97 | return (bp->b_page_count * PAGE_SIZE) - bp->b_offset; | ||
| 98 | } | ||
| 99 | |||
| 79 | /* | 100 | /* |
| 80 | * Page Region interfaces. | 101 | * Page Region interfaces. |
| 81 | * | 102 | * |
| @@ -314,7 +335,7 @@ xfs_buf_free( | |||
| 314 | if (bp->b_flags & (_XBF_PAGE_CACHE|_XBF_PAGES)) { | 335 | if (bp->b_flags & (_XBF_PAGE_CACHE|_XBF_PAGES)) { |
| 315 | uint i; | 336 | uint i; |
| 316 | 337 | ||
| 317 | if ((bp->b_flags & XBF_MAPPED) && (bp->b_page_count > 1)) | 338 | if (xfs_buf_is_vmapped(bp)) |
| 318 | free_address(bp->b_addr - bp->b_offset); | 339 | free_address(bp->b_addr - bp->b_offset); |
| 319 | 340 | ||
| 320 | for (i = 0; i < bp->b_page_count; i++) { | 341 | for (i = 0; i < bp->b_page_count; i++) { |
| @@ -1107,6 +1128,9 @@ xfs_buf_bio_end_io( | |||
| 1107 | 1128 | ||
| 1108 | xfs_buf_ioerror(bp, -error); | 1129 | xfs_buf_ioerror(bp, -error); |
| 1109 | 1130 | ||
| 1131 | if (!error && xfs_buf_is_vmapped(bp) && (bp->b_flags & XBF_READ)) | ||
| 1132 | invalidate_kernel_vmap_range(bp->b_addr, xfs_buf_vmap_len(bp)); | ||
| 1133 | |||
| 1110 | do { | 1134 | do { |
| 1111 | struct page *page = bvec->bv_page; | 1135 | struct page *page = bvec->bv_page; |
| 1112 | 1136 | ||
| @@ -1216,6 +1240,10 @@ next_chunk: | |||
| 1216 | 1240 | ||
| 1217 | submit_io: | 1241 | submit_io: |
| 1218 | if (likely(bio->bi_size)) { | 1242 | if (likely(bio->bi_size)) { |
| 1243 | if (xfs_buf_is_vmapped(bp)) { | ||
| 1244 | flush_kernel_vmap_range(bp->b_addr, | ||
| 1245 | xfs_buf_vmap_len(bp)); | ||
| 1246 | } | ||
| 1219 | submit_bio(rw, bio); | 1247 | submit_bio(rw, bio); |
| 1220 | if (size) | 1248 | if (size) |
| 1221 | goto next_chunk; | 1249 | goto next_chunk; |
diff --git a/include/linux/highmem.h b/include/linux/highmem.h index ab2cc20e21a5..74152c08ad07 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h | |||
| @@ -17,6 +17,12 @@ static inline void flush_anon_page(struct vm_area_struct *vma, struct page *page | |||
| 17 | static inline void flush_kernel_dcache_page(struct page *page) | 17 | static inline void flush_kernel_dcache_page(struct page *page) |
| 18 | { | 18 | { |
| 19 | } | 19 | } |
| 20 | static inline void flush_kernel_vmap_range(void *vaddr, int size) | ||
| 21 | { | ||
| 22 | } | ||
| 23 | static inline void invalidate_kernel_vmap_range(void *vaddr, int size) | ||
| 24 | { | ||
| 25 | } | ||
| 20 | #endif | 26 | #endif |
| 21 | 27 | ||
| 22 | #include <asm/kmap_types.h> | 28 | #include <asm/kmap_types.h> |
