diff options
Diffstat (limited to 'fs/ext4/file.c')
-rw-r--r-- | fs/ext4/file.c | 82 |
1 files changed, 58 insertions, 24 deletions
diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 113837e7ba98..749b222e6498 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c | |||
@@ -193,43 +193,35 @@ out: | |||
193 | } | 193 | } |
194 | 194 | ||
195 | #ifdef CONFIG_FS_DAX | 195 | #ifdef CONFIG_FS_DAX |
196 | static void ext4_end_io_unwritten(struct buffer_head *bh, int uptodate) | ||
197 | { | ||
198 | struct inode *inode = bh->b_assoc_map->host; | ||
199 | /* XXX: breaks on 32-bit > 16TB. Is that even supported? */ | ||
200 | loff_t offset = (loff_t)(uintptr_t)bh->b_private << inode->i_blkbits; | ||
201 | int err; | ||
202 | if (!uptodate) | ||
203 | return; | ||
204 | WARN_ON(!buffer_unwritten(bh)); | ||
205 | err = ext4_convert_unwritten_extents(NULL, inode, offset, bh->b_size); | ||
206 | } | ||
207 | |||
208 | static int ext4_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | 196 | static int ext4_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf) |
209 | { | 197 | { |
210 | int result; | 198 | int result; |
211 | handle_t *handle = NULL; | 199 | handle_t *handle = NULL; |
212 | struct super_block *sb = file_inode(vma->vm_file)->i_sb; | 200 | struct inode *inode = file_inode(vma->vm_file); |
201 | struct super_block *sb = inode->i_sb; | ||
213 | bool write = vmf->flags & FAULT_FLAG_WRITE; | 202 | bool write = vmf->flags & FAULT_FLAG_WRITE; |
214 | 203 | ||
215 | if (write) { | 204 | if (write) { |
216 | sb_start_pagefault(sb); | 205 | sb_start_pagefault(sb); |
217 | file_update_time(vma->vm_file); | 206 | file_update_time(vma->vm_file); |
207 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
218 | handle = ext4_journal_start_sb(sb, EXT4_HT_WRITE_PAGE, | 208 | handle = ext4_journal_start_sb(sb, EXT4_HT_WRITE_PAGE, |
219 | EXT4_DATA_TRANS_BLOCKS(sb)); | 209 | EXT4_DATA_TRANS_BLOCKS(sb)); |
220 | } | 210 | } else |
211 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
221 | 212 | ||
222 | if (IS_ERR(handle)) | 213 | if (IS_ERR(handle)) |
223 | result = VM_FAULT_SIGBUS; | 214 | result = VM_FAULT_SIGBUS; |
224 | else | 215 | else |
225 | result = __dax_fault(vma, vmf, ext4_get_block_dax, | 216 | result = __dax_fault(vma, vmf, ext4_dax_mmap_get_block, NULL); |
226 | ext4_end_io_unwritten); | ||
227 | 217 | ||
228 | if (write) { | 218 | if (write) { |
229 | if (!IS_ERR(handle)) | 219 | if (!IS_ERR(handle)) |
230 | ext4_journal_stop(handle); | 220 | ext4_journal_stop(handle); |
221 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
231 | sb_end_pagefault(sb); | 222 | sb_end_pagefault(sb); |
232 | } | 223 | } else |
224 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
233 | 225 | ||
234 | return result; | 226 | return result; |
235 | } | 227 | } |
@@ -246,44 +238,86 @@ static int ext4_dax_pmd_fault(struct vm_area_struct *vma, unsigned long addr, | |||
246 | if (write) { | 238 | if (write) { |
247 | sb_start_pagefault(sb); | 239 | sb_start_pagefault(sb); |
248 | file_update_time(vma->vm_file); | 240 | file_update_time(vma->vm_file); |
241 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
249 | handle = ext4_journal_start_sb(sb, EXT4_HT_WRITE_PAGE, | 242 | handle = ext4_journal_start_sb(sb, EXT4_HT_WRITE_PAGE, |
250 | ext4_chunk_trans_blocks(inode, | 243 | ext4_chunk_trans_blocks(inode, |
251 | PMD_SIZE / PAGE_SIZE)); | 244 | PMD_SIZE / PAGE_SIZE)); |
252 | } | 245 | } else |
246 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
253 | 247 | ||
254 | if (IS_ERR(handle)) | 248 | if (IS_ERR(handle)) |
255 | result = VM_FAULT_SIGBUS; | 249 | result = VM_FAULT_SIGBUS; |
256 | else | 250 | else |
257 | result = __dax_pmd_fault(vma, addr, pmd, flags, | 251 | result = __dax_pmd_fault(vma, addr, pmd, flags, |
258 | ext4_get_block_dax, ext4_end_io_unwritten); | 252 | ext4_dax_mmap_get_block, NULL); |
259 | 253 | ||
260 | if (write) { | 254 | if (write) { |
261 | if (!IS_ERR(handle)) | 255 | if (!IS_ERR(handle)) |
262 | ext4_journal_stop(handle); | 256 | ext4_journal_stop(handle); |
257 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
263 | sb_end_pagefault(sb); | 258 | sb_end_pagefault(sb); |
264 | } | 259 | } else |
260 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
265 | 261 | ||
266 | return result; | 262 | return result; |
267 | } | 263 | } |
268 | 264 | ||
269 | static int ext4_dax_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) | 265 | static int ext4_dax_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) |
270 | { | 266 | { |
271 | return dax_mkwrite(vma, vmf, ext4_get_block_dax, | 267 | int err; |
272 | ext4_end_io_unwritten); | 268 | struct inode *inode = file_inode(vma->vm_file); |
269 | |||
270 | sb_start_pagefault(inode->i_sb); | ||
271 | file_update_time(vma->vm_file); | ||
272 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
273 | err = __dax_mkwrite(vma, vmf, ext4_dax_mmap_get_block, NULL); | ||
274 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
275 | sb_end_pagefault(inode->i_sb); | ||
276 | |||
277 | return err; | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * Handle write fault for VM_MIXEDMAP mappings. Similarly to ext4_dax_mkwrite() | ||
282 | * handler we check for races agaist truncate. Note that since we cycle through | ||
283 | * i_mmap_sem, we are sure that also any hole punching that began before we | ||
284 | * were called is finished by now and so if it included part of the file we | ||
285 | * are working on, our pte will get unmapped and the check for pte_same() in | ||
286 | * wp_pfn_shared() fails. Thus fault gets retried and things work out as | ||
287 | * desired. | ||
288 | */ | ||
289 | static int ext4_dax_pfn_mkwrite(struct vm_area_struct *vma, | ||
290 | struct vm_fault *vmf) | ||
291 | { | ||
292 | struct inode *inode = file_inode(vma->vm_file); | ||
293 | struct super_block *sb = inode->i_sb; | ||
294 | int ret = VM_FAULT_NOPAGE; | ||
295 | loff_t size; | ||
296 | |||
297 | sb_start_pagefault(sb); | ||
298 | file_update_time(vma->vm_file); | ||
299 | down_read(&EXT4_I(inode)->i_mmap_sem); | ||
300 | size = (i_size_read(inode) + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
301 | if (vmf->pgoff >= size) | ||
302 | ret = VM_FAULT_SIGBUS; | ||
303 | up_read(&EXT4_I(inode)->i_mmap_sem); | ||
304 | sb_end_pagefault(sb); | ||
305 | |||
306 | return ret; | ||
273 | } | 307 | } |
274 | 308 | ||
275 | static const struct vm_operations_struct ext4_dax_vm_ops = { | 309 | static const struct vm_operations_struct ext4_dax_vm_ops = { |
276 | .fault = ext4_dax_fault, | 310 | .fault = ext4_dax_fault, |
277 | .pmd_fault = ext4_dax_pmd_fault, | 311 | .pmd_fault = ext4_dax_pmd_fault, |
278 | .page_mkwrite = ext4_dax_mkwrite, | 312 | .page_mkwrite = ext4_dax_mkwrite, |
279 | .pfn_mkwrite = dax_pfn_mkwrite, | 313 | .pfn_mkwrite = ext4_dax_pfn_mkwrite, |
280 | }; | 314 | }; |
281 | #else | 315 | #else |
282 | #define ext4_dax_vm_ops ext4_file_vm_ops | 316 | #define ext4_dax_vm_ops ext4_file_vm_ops |
283 | #endif | 317 | #endif |
284 | 318 | ||
285 | static const struct vm_operations_struct ext4_file_vm_ops = { | 319 | static const struct vm_operations_struct ext4_file_vm_ops = { |
286 | .fault = filemap_fault, | 320 | .fault = ext4_filemap_fault, |
287 | .map_pages = filemap_map_pages, | 321 | .map_pages = filemap_map_pages, |
288 | .page_mkwrite = ext4_page_mkwrite, | 322 | .page_mkwrite = ext4_page_mkwrite, |
289 | }; | 323 | }; |