]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Nick Piggin <npiggin@suse.de> |
2 | Subject: mm: page_mkwrite kABI compat 2 | |
3 | Patch-upstream: never | |
4 | ||
5 | Introduce a new vma flag, VM_PAGE_MKWRITE2, which signals to the mm that the | |
6 | driver is using page_mkwrite2. Have all in-tree users set this flag because | |
7 | they use page_mkwrite2. Introduce some fallback paths in the page fault | |
8 | page_mkwrite callers which calls the old page_mkwrite in case this flag is not | |
9 | set. In this way, ABI compatibility should be retained for out of tree modules. | |
10 | ||
11 | We also now do some extra checking in the fallback case to catch page | |
12 | invalidations and just retry the fault rather than erroneously causing a | |
13 | SIGBUS, which was one of the failure cases of the old page_mkwrite ABI (it | |
14 | still has other problems, but this was one of the worst). | |
15 | ||
16 | Signed-off-by: Nick Piggin <npiggin@suse.de> | |
17 | --- | |
18 | drivers/video/fb_defio.c | 2 - | |
19 | fs/ext4/file.c | 2 - | |
20 | fs/fuse/file.c | 1 | |
21 | fs/gfs2/ops_file.c | 1 | |
22 | fs/nfs/file.c | 2 - | |
23 | fs/ocfs2/mmap.c | 2 - | |
24 | fs/ubifs/file.c | 1 | |
25 | fs/xfs/linux-2.6/xfs_file.c | 2 - | |
26 | include/linux/mm.h | 1 | |
27 | mm/memory.c | 77 ++++++++++++++++++++++++++++++++------------ | |
28 | 10 files changed, 66 insertions(+), 25 deletions(-) | |
29 | ||
30 | Index: linux-2.6.27/drivers/video/fb_defio.c | |
31 | =================================================================== | |
32 | --- linux-2.6.27.orig/drivers/video/fb_defio.c | |
33 | +++ linux-2.6.27/drivers/video/fb_defio.c | |
34 | @@ -129,7 +129,7 @@ static const struct address_space_operat | |
35 | static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma) | |
36 | { | |
37 | vma->vm_ops = &fb_deferred_io_vm_ops; | |
38 | - vma->vm_flags |= ( VM_IO | VM_RESERVED | VM_DONTEXPAND ); | |
39 | + vma->vm_flags |= ( VM_IO | VM_RESERVED | VM_DONTEXPAND | VM_PAGE_MKWRITE2 ); | |
40 | vma->vm_private_data = info; | |
41 | return 0; | |
42 | } | |
43 | Index: linux-2.6.27/fs/ext4/file.c | |
44 | =================================================================== | |
45 | --- linux-2.6.27.orig/fs/ext4/file.c | |
46 | +++ linux-2.6.27/fs/ext4/file.c | |
47 | @@ -136,7 +136,7 @@ static int ext4_file_mmap(struct file *f | |
48 | return -ENOEXEC; | |
49 | file_accessed(file); | |
50 | vma->vm_ops = &ext4_file_vm_ops; | |
51 | - vma->vm_flags |= VM_CAN_NONLINEAR; | |
52 | + vma->vm_flags |= VM_CAN_NONLINEAR | VM_PAGE_MKWRITE2; | |
53 | return 0; | |
54 | } | |
55 | ||
56 | Index: linux-2.6.27/fs/fuse/file.c | |
57 | =================================================================== | |
58 | --- linux-2.6.27.orig/fs/fuse/file.c | |
59 | +++ linux-2.6.27/fs/fuse/file.c | |
60 | @@ -1255,6 +1255,7 @@ static int fuse_file_mmap(struct file *f | |
61 | spin_unlock(&fc->lock); | |
62 | } | |
63 | file_accessed(file); | |
64 | + vma->vm_flags |= VM_PAGE_MKWRITE2; | |
65 | vma->vm_ops = &fuse_file_vm_ops; | |
66 | return 0; | |
67 | } | |
68 | Index: linux-2.6.27/fs/gfs2/ops_file.c | |
69 | =================================================================== | |
70 | --- linux-2.6.27.orig/fs/gfs2/ops_file.c | |
71 | +++ linux-2.6.27/fs/gfs2/ops_file.c | |
72 | @@ -446,6 +446,7 @@ static int gfs2_mmap(struct file *file, | |
73 | return error; | |
74 | } | |
75 | ||
76 | + vma->vm_flags |= VM_PAGE_MKWRITE2; | |
77 | vma->vm_ops = &gfs2_vm_ops; | |
78 | ||
79 | gfs2_glock_dq_uninit(&i_gh); | |
80 | Index: linux-2.6.27/fs/nfs/file.c | |
81 | =================================================================== | |
82 | --- linux-2.6.27.orig/fs/nfs/file.c | |
83 | +++ linux-2.6.27/fs/nfs/file.c | |
84 | @@ -304,7 +304,7 @@ nfs_file_mmap(struct file * file, struct | |
85 | status = nfs_revalidate_mapping(inode, file->f_mapping); | |
86 | if (!status) { | |
87 | vma->vm_ops = &nfs_file_vm_ops; | |
88 | - vma->vm_flags |= VM_CAN_NONLINEAR; | |
89 | + vma->vm_flags |= VM_CAN_NONLINEAR | VM_PAGE_MKWRITE2; | |
90 | file_accessed(file); | |
91 | } | |
92 | return status; | |
93 | Index: linux-2.6.27/fs/ocfs2/mmap.c | |
94 | =================================================================== | |
95 | --- linux-2.6.27.orig/fs/ocfs2/mmap.c | |
96 | +++ linux-2.6.27/fs/ocfs2/mmap.c | |
97 | @@ -216,7 +216,7 @@ int ocfs2_mmap(struct file *file, struct | |
98 | ocfs2_inode_unlock(file->f_dentry->d_inode, lock_level); | |
99 | out: | |
100 | vma->vm_ops = &ocfs2_file_vm_ops; | |
101 | - vma->vm_flags |= VM_CAN_NONLINEAR; | |
102 | + vma->vm_flags |= VM_CAN_NONLINEAR | VM_PAGE_MKWRITE2; | |
103 | return 0; | |
104 | } | |
105 | ||
106 | Index: linux-2.6.27/fs/ubifs/file.c | |
107 | =================================================================== | |
108 | --- linux-2.6.27.orig/fs/ubifs/file.c | |
109 | +++ linux-2.6.27/fs/ubifs/file.c | |
110 | @@ -1246,6 +1246,7 @@ static int ubifs_file_mmap(struct file * | |
111 | if (err) | |
112 | return err; | |
113 | vma->vm_ops = &ubifs_file_vm_ops; | |
114 | + vma->vm_flags |= VM_PAGE_MKWRITE2; | |
115 | return 0; | |
116 | } | |
117 | ||
118 | Index: linux-2.6.27/fs/xfs/linux-2.6/xfs_file.c | |
119 | =================================================================== | |
120 | --- linux-2.6.27.orig/fs/xfs/linux-2.6/xfs_file.c | |
121 | +++ linux-2.6.27/fs/xfs/linux-2.6/xfs_file.c | |
122 | @@ -370,7 +370,7 @@ xfs_file_mmap( | |
123 | struct vm_area_struct *vma) | |
124 | { | |
125 | vma->vm_ops = &xfs_file_vm_ops; | |
126 | - vma->vm_flags |= VM_CAN_NONLINEAR; | |
127 | + vma->vm_flags |= VM_CAN_NONLINEAR | VM_PAGE_MKWRITE2; | |
128 | ||
129 | file_accessed(filp); | |
130 | return 0; | |
131 | Index: linux-2.6.27/include/linux/mm.h | |
132 | =================================================================== | |
133 | --- linux-2.6.27.orig/include/linux/mm.h | |
134 | +++ linux-2.6.27/include/linux/mm.h | |
135 | @@ -113,6 +113,7 @@ extern unsigned int kobjsize(const void | |
136 | #define VM_CAN_NONLINEAR 0x08000000 /* Has ->fault & does nonlinear pages */ | |
137 | #define VM_MIXEDMAP 0x10000000 /* Can contain "struct page" and pure PFN pages */ | |
138 | #define VM_SAO 0x20000000 /* Strong Access Ordering (powerpc) */ | |
139 | +#define VM_PAGE_MKWRITE2 0x80000000 /* Uses page_mkwrite2 rather than page_mkwrite */ | |
140 | ||
141 | #ifndef VM_STACK_DEFAULT_FLAGS /* arch can override this */ | |
142 | #define VM_STACK_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS | |
143 | Index: linux-2.6.27/mm/memory.c | |
144 | =================================================================== | |
145 | --- linux-2.6.27.orig/mm/memory.c | |
146 | +++ linux-2.6.27/mm/memory.c | |
147 | @@ -1800,7 +1800,7 @@ static int do_wp_page(struct mm_struct * | |
148 | * read-only shared pages can get COWed by | |
149 | * get_user_pages(.write=1, .force=1). | |
150 | */ | |
151 | - if (vma->vm_ops && vma->vm_ops->_pmkw.page_mkwrite2) { | |
152 | + if (vma->vm_ops && vma->vm_ops->_pmkw.page_mkwrite) { | |
153 | struct vm_fault vmf; | |
154 | int tmp; | |
155 | ||
156 | @@ -1821,21 +1821,42 @@ static int do_wp_page(struct mm_struct * | |
157 | page_cache_get(old_page); | |
158 | pte_unmap_unlock(page_table, ptl); | |
159 | ||
160 | - tmp = vma->vm_ops->_pmkw.page_mkwrite2(vma, &vmf); | |
161 | - if (unlikely(tmp & | |
162 | - (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) { | |
163 | - ret = tmp; | |
164 | - goto unwritable_page; | |
165 | - } | |
166 | - if (unlikely(!(tmp & VM_FAULT_LOCKED))) { | |
167 | + if (likely(vma->vm_flags & VM_PAGE_MKWRITE2)) { | |
168 | + tmp = vma->vm_ops->_pmkw.page_mkwrite2(vma, &vmf); | |
169 | + if (unlikely(tmp & | |
170 | + (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) { | |
171 | + ret = tmp; | |
172 | + goto unwritable_page; | |
173 | + } | |
174 | + if (unlikely(!(tmp & VM_FAULT_LOCKED))) { | |
175 | + lock_page(old_page); | |
176 | + if (!old_page->mapping) { | |
177 | + ret = 0; /* retry the fault */ | |
178 | + unlock_page(old_page); | |
179 | + goto unwritable_page; | |
180 | + } | |
181 | + } else | |
182 | + VM_BUG_ON(!PageLocked(old_page)); | |
183 | + } else { | |
184 | + tmp = vma->vm_ops->_pmkw.page_mkwrite(vma, old_page); | |
185 | lock_page(old_page); | |
186 | if (!old_page->mapping) { | |
187 | + /* | |
188 | + * page_mkwrite API is broken, it returns error even | |
189 | + * if the page was invalidated. So if it was, then | |
190 | + * clear that error here so we don't get a SIGBUS. | |
191 | + */ | |
192 | ret = 0; /* retry the fault */ | |
193 | unlock_page(old_page); | |
194 | goto unwritable_page; | |
195 | } | |
196 | - } else | |
197 | - VM_BUG_ON(!PageLocked(old_page)); | |
198 | + if (tmp) { | |
199 | + /* can't distinguish OOM from SIGBUS, but oh well */ | |
200 | + ret = VM_FAULT_SIGBUS; | |
201 | + unlock_page(old_page); | |
202 | + goto unwritable_page; | |
203 | + } | |
204 | + } | |
205 | ||
206 | /* | |
207 | * Since we dropped the lock we need to revalidate | |
208 | @@ -2517,26 +2538,42 @@ static int __do_fault(struct mm_struct * | |
209 | * address space wants to know that the page is about | |
210 | * to become writable | |
211 | */ | |
212 | - if (vma->vm_ops->_pmkw.page_mkwrite2) { | |
213 | + if (vma->vm_ops->_pmkw.page_mkwrite) { | |
214 | int tmp; | |
215 | ||
216 | unlock_page(page); | |
217 | vmf.flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE; | |
218 | - tmp = vma->vm_ops->_pmkw.page_mkwrite2(vma, &vmf); | |
219 | - if (unlikely(tmp & | |
220 | - (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) { | |
221 | - ret = tmp; | |
222 | - goto unwritable_page; | |
223 | - } | |
224 | - if (unlikely(!(tmp & VM_FAULT_LOCKED))) { | |
225 | + | |
226 | + if (likely(vma->vm_flags & VM_PAGE_MKWRITE2)) { | |
227 | + tmp = vma->vm_ops->_pmkw.page_mkwrite2(vma, &vmf); | |
228 | + if (unlikely(tmp & | |
229 | + (VM_FAULT_ERROR | VM_FAULT_NOPAGE))) { | |
230 | + ret = tmp; | |
231 | + goto unwritable_page; | |
232 | + } | |
233 | + if (unlikely(!(tmp & VM_FAULT_LOCKED))) { | |
234 | + lock_page(page); | |
235 | + if (!page->mapping) { | |
236 | + ret = 0; /* retry the fault */ | |
237 | + unlock_page(page); | |
238 | + goto unwritable_page; | |
239 | + } | |
240 | + } else | |
241 | + VM_BUG_ON(!PageLocked(page)); | |
242 | + } else { | |
243 | + tmp = vma->vm_ops->_pmkw.page_mkwrite(vma, page); | |
244 | lock_page(page); | |
245 | if (!page->mapping) { | |
246 | ret = 0; /* retry the fault */ | |
247 | unlock_page(page); | |
248 | goto unwritable_page; | |
249 | } | |
250 | - } else | |
251 | - VM_BUG_ON(!PageLocked(page)); | |
252 | + if (tmp) { | |
253 | + ret = VM_FAULT_SIGBUS; | |
254 | + unlock_page(page); | |
255 | + goto unwritable_page; | |
256 | + } | |
257 | + } | |
258 | page_mkwrite = 1; | |
259 | } | |
260 | } |