diff options
Diffstat (limited to 'fs/ext4/file.c')
-rw-r--r-- | fs/ext4/file.c | 66 |
1 files changed, 57 insertions, 9 deletions
diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 113837e7ba98..0d24ebcd7c9e 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c | |||
@@ -209,15 +209,18 @@ static int ext4_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | |||
209 | { | 209 | { |
210 | int result; | 210 | int result; |
211 | handle_t *handle = NULL; | 211 | handle_t *handle = NULL; |
212 | struct super_block *sb = file_inode(vma->vm_file)->i_sb; | 212 | struct inode *inode = file_inode(vma->vm_file); |
213 | struct super_block *sb = inode->i_sb; | ||
213 | bool write = vmf->flags & FAULT_FLAG_WRITE; | 214 | bool write = vmf->flags & FAULT_FLAG_WRITE; |
214 | 215 | ||
215 | if (write) { | 216 | if (write) { |
216 | sb_start_pagefault(sb); | 217 | sb_start_pagefault(sb); |
217 | file_update_time(vma->vm_file); | 218 | file_update_time(vma->vm_file); |
219 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
218 | handle = ext4_journal_start_sb(sb, EXT4_HT_WRITE_PAGE, | 220 | handle = ext4_journal_start_sb(sb, EXT4_HT_WRITE_PAGE, |
219 | EXT4_DATA_TRANS_BLOCKS(sb)); | 221 | EXT4_DATA_TRANS_BLOCKS(sb)); |
220 | } | 222 | } else |
223 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
221 | 224 | ||
222 | if (IS_ERR(handle)) | 225 | if (IS_ERR(handle)) |
223 | result = VM_FAULT_SIGBUS; | 226 | result = VM_FAULT_SIGBUS; |
@@ -228,8 +231,10 @@ static int ext4_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | |||
228 | if (write) { | 231 | if (write) { |
229 | if (!IS_ERR(handle)) | 232 | if (!IS_ERR(handle)) |
230 | ext4_journal_stop(handle); | 233 | ext4_journal_stop(handle); |
234 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
231 | sb_end_pagefault(sb); | 235 | sb_end_pagefault(sb); |
232 | } | 236 | } else |
237 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
233 | 238 | ||
234 | return result; | 239 | return result; |
235 | } | 240 | } |
@@ -246,10 +251,12 @@ static int ext4_dax_pmd_fault(struct vm_area_struct *vma, unsigned long addr, | |||
246 | if (write) { | 251 | if (write) { |
247 | sb_start_pagefault(sb); | 252 | sb_start_pagefault(sb); |
248 | file_update_time(vma->vm_file); | 253 | file_update_time(vma->vm_file); |
254 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
249 | handle = ext4_journal_start_sb(sb, EXT4_HT_WRITE_PAGE, | 255 | handle = ext4_journal_start_sb(sb, EXT4_HT_WRITE_PAGE, |
250 | ext4_chunk_trans_blocks(inode, | 256 | ext4_chunk_trans_blocks(inode, |
251 | PMD_SIZE / PAGE_SIZE)); | 257 | PMD_SIZE / PAGE_SIZE)); |
252 | } | 258 | } else |
259 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
253 | 260 | ||
254 | if (IS_ERR(handle)) | 261 | if (IS_ERR(handle)) |
255 | result = VM_FAULT_SIGBUS; | 262 | result = VM_FAULT_SIGBUS; |
@@ -260,30 +267,71 @@ static int ext4_dax_pmd_fault(struct vm_area_struct *vma, unsigned long addr, | |||
260 | if (write) { | 267 | if (write) { |
261 | if (!IS_ERR(handle)) | 268 | if (!IS_ERR(handle)) |
262 | ext4_journal_stop(handle); | 269 | ext4_journal_stop(handle); |
270 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
263 | sb_end_pagefault(sb); | 271 | sb_end_pagefault(sb); |
264 | } | 272 | } else |
273 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
265 | 274 | ||
266 | return result; | 275 | return result; |
267 | } | 276 | } |
268 | 277 | ||
269 | static int ext4_dax_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) | 278 | static int ext4_dax_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) |
270 | { | 279 | { |
271 | return dax_mkwrite(vma, vmf, ext4_get_block_dax, | 280 | int err; |
272 | ext4_end_io_unwritten); | 281 | struct inode *inode = file_inode(vma->vm_file); |
282 | |||
283 | sb_start_pagefault(inode->i_sb); | ||
284 | file_update_time(vma->vm_file); | ||
285 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
286 | err = __dax_mkwrite(vma, vmf, ext4_get_block_dax, | ||
287 | ext4_end_io_unwritten); | ||
288 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
289 | sb_end_pagefault(inode->i_sb); | ||
290 | |||
291 | return err; | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * Handle write fault for VM_MIXEDMAP mappings. Similarly to ext4_dax_mkwrite() | ||
296 | * handler we check for races agaist truncate. Note that since we cycle through | ||
297 | * i_mmap_sem, we are sure that also any hole punching that began before we | ||
298 | * were called is finished by now and so if it included part of the file we | ||
299 | * are working on, our pte will get unmapped and the check for pte_same() in | ||
300 | * wp_pfn_shared() fails. Thus fault gets retried and things work out as | ||
301 | * desired. | ||
302 | */ | ||
303 | static int ext4_dax_pfn_mkwrite(struct vm_area_struct *vma, | ||
304 | struct vm_fault *vmf) | ||
305 | { | ||
306 | struct inode *inode = file_inode(vma->vm_file); | ||
307 | struct super_block *sb = inode->i_sb; | ||
308 | int ret = VM_FAULT_NOPAGE; | ||
309 | loff_t size; | ||
310 | |||
311 | sb_start_pagefault(sb); | ||
312 | file_update_time(vma->vm_file); | ||
313 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
314 | size = (i_size_read(inode) + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
315 | if (vmf->pgoff >= size) | ||
316 | ret = VM_FAULT_SIGBUS; | ||
317 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
318 | sb_end_pagefault(sb); | ||
319 | |||
320 | return ret; | ||
273 | } | 321 | } |
274 | 322 | ||
275 | static const struct vm_operations_struct ext4_dax_vm_ops = { | 323 | static const struct vm_operations_struct ext4_dax_vm_ops = { |
276 | .fault = ext4_dax_fault, | 324 | .fault = ext4_dax_fault, |
277 | .pmd_fault = ext4_dax_pmd_fault, | 325 | .pmd_fault = ext4_dax_pmd_fault, |
278 | .page_mkwrite = ext4_dax_mkwrite, | 326 | .page_mkwrite = ext4_dax_mkwrite, |
279 | .pfn_mkwrite = dax_pfn_mkwrite, | 327 | .pfn_mkwrite = ext4_dax_pfn_mkwrite, |
280 | }; | 328 | }; |
281 | #else | 329 | #else |
282 | #define ext4_dax_vm_ops ext4_file_vm_ops | 330 | #define ext4_dax_vm_ops ext4_file_vm_ops |
283 | #endif | 331 | #endif |
284 | 332 | ||
285 | static const struct vm_operations_struct ext4_file_vm_ops = { | 333 | static const struct vm_operations_struct ext4_file_vm_ops = { |
286 | .fault = filemap_fault, | 334 | .fault = ext4_filemap_fault, |
287 | .map_pages = filemap_map_pages, | 335 | .map_pages = filemap_map_pages, |
288 | .page_mkwrite = ext4_page_mkwrite, | 336 | .page_mkwrite = ext4_page_mkwrite, |
289 | }; | 337 | }; |