aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext4/file.c')
-rw-r--r--fs/ext4/file.c82
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
196static 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
208static int ext4_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 196static 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
269static int ext4_dax_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) 265static 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 */
289static 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
275static const struct vm_operations_struct ext4_dax_vm_ops = { 309static 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
285static const struct vm_operations_struct ext4_file_vm_ops = { 319static 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};