diff options
author | Kirill A. Shutemov <kirill.shutemov@linux.intel.com> | 2014-04-07 18:37:19 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-07 19:35:53 -0400 |
commit | f1820361f83d556a7f0a9f629100f3825e594328 (patch) | |
tree | 1fc18989613d72ef115b355864786dd934b4d317 /mm/filemap.c | |
parent | 8c6e50b0290c4c708a3e6462729e1e9151a9a7df (diff) |
mm: implement ->map_pages for page cache
filemap_map_pages() is generic implementation of ->map_pages() for
filesystems who uses page cache.
It should be safe to use filemap_map_pages() for ->map_pages() if
filesystem use filemap_fault() for ->fault().
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Rik van Riel <riel@redhat.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Matthew Wilcox <matthew.r.wilcox@intel.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Dave Chinner <david@fromorbit.com>
Cc: Ning Qu <quning@gmail.com>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/filemap.c')
-rw-r--r-- | mm/filemap.c | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/mm/filemap.c b/mm/filemap.c index 21781f1fe52b..3f9b5fbb623f 100644 --- a/mm/filemap.c +++ b/mm/filemap.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */ | 33 | #include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */ |
34 | #include <linux/memcontrol.h> | 34 | #include <linux/memcontrol.h> |
35 | #include <linux/cleancache.h> | 35 | #include <linux/cleancache.h> |
36 | #include <linux/rmap.h> | ||
36 | #include "internal.h" | 37 | #include "internal.h" |
37 | 38 | ||
38 | #define CREATE_TRACE_POINTS | 39 | #define CREATE_TRACE_POINTS |
@@ -2064,6 +2065,78 @@ page_not_uptodate: | |||
2064 | } | 2065 | } |
2065 | EXPORT_SYMBOL(filemap_fault); | 2066 | EXPORT_SYMBOL(filemap_fault); |
2066 | 2067 | ||
2068 | void filemap_map_pages(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
2069 | { | ||
2070 | struct radix_tree_iter iter; | ||
2071 | void **slot; | ||
2072 | struct file *file = vma->vm_file; | ||
2073 | struct address_space *mapping = file->f_mapping; | ||
2074 | loff_t size; | ||
2075 | struct page *page; | ||
2076 | unsigned long address = (unsigned long) vmf->virtual_address; | ||
2077 | unsigned long addr; | ||
2078 | pte_t *pte; | ||
2079 | |||
2080 | rcu_read_lock(); | ||
2081 | radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, vmf->pgoff) { | ||
2082 | if (iter.index > vmf->max_pgoff) | ||
2083 | break; | ||
2084 | repeat: | ||
2085 | page = radix_tree_deref_slot(slot); | ||
2086 | if (unlikely(!page)) | ||
2087 | goto next; | ||
2088 | if (radix_tree_exception(page)) { | ||
2089 | if (radix_tree_deref_retry(page)) | ||
2090 | break; | ||
2091 | else | ||
2092 | goto next; | ||
2093 | } | ||
2094 | |||
2095 | if (!page_cache_get_speculative(page)) | ||
2096 | goto repeat; | ||
2097 | |||
2098 | /* Has the page moved? */ | ||
2099 | if (unlikely(page != *slot)) { | ||
2100 | page_cache_release(page); | ||
2101 | goto repeat; | ||
2102 | } | ||
2103 | |||
2104 | if (!PageUptodate(page) || | ||
2105 | PageReadahead(page) || | ||
2106 | PageHWPoison(page)) | ||
2107 | goto skip; | ||
2108 | if (!trylock_page(page)) | ||
2109 | goto skip; | ||
2110 | |||
2111 | if (page->mapping != mapping || !PageUptodate(page)) | ||
2112 | goto unlock; | ||
2113 | |||
2114 | size = i_size_read(mapping->host) + PAGE_CACHE_SIZE - 1; | ||
2115 | if (page->index >= size >> PAGE_CACHE_SHIFT) | ||
2116 | goto unlock; | ||
2117 | |||
2118 | pte = vmf->pte + page->index - vmf->pgoff; | ||
2119 | if (!pte_none(*pte)) | ||
2120 | goto unlock; | ||
2121 | |||
2122 | if (file->f_ra.mmap_miss > 0) | ||
2123 | file->f_ra.mmap_miss--; | ||
2124 | addr = address + (page->index - vmf->pgoff) * PAGE_SIZE; | ||
2125 | do_set_pte(vma, addr, page, pte, false, false); | ||
2126 | unlock_page(page); | ||
2127 | goto next; | ||
2128 | unlock: | ||
2129 | unlock_page(page); | ||
2130 | skip: | ||
2131 | page_cache_release(page); | ||
2132 | next: | ||
2133 | if (iter.index == vmf->max_pgoff) | ||
2134 | break; | ||
2135 | } | ||
2136 | rcu_read_unlock(); | ||
2137 | } | ||
2138 | EXPORT_SYMBOL(filemap_map_pages); | ||
2139 | |||
2067 | int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) | 2140 | int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) |
2068 | { | 2141 | { |
2069 | struct page *page = vmf->page; | 2142 | struct page *page = vmf->page; |
@@ -2093,6 +2166,7 @@ EXPORT_SYMBOL(filemap_page_mkwrite); | |||
2093 | 2166 | ||
2094 | const struct vm_operations_struct generic_file_vm_ops = { | 2167 | const struct vm_operations_struct generic_file_vm_ops = { |
2095 | .fault = filemap_fault, | 2168 | .fault = filemap_fault, |
2169 | .map_pages = filemap_map_pages, | ||
2096 | .page_mkwrite = filemap_page_mkwrite, | 2170 | .page_mkwrite = filemap_page_mkwrite, |
2097 | .remap_pages = generic_file_remap_pages, | 2171 | .remap_pages = generic_file_remap_pages, |
2098 | }; | 2172 | }; |