aboutsummaryrefslogtreecommitdiffstats
path: root/mm/truncate.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/truncate.c')
-rw-r--r--mm/truncate.c64
1 files changed, 64 insertions, 0 deletions
diff --git a/mm/truncate.c b/mm/truncate.c
index ccc3ecf7cb98..5900afca0fa9 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -465,3 +465,67 @@ int invalidate_inode_pages2(struct address_space *mapping)
465 return invalidate_inode_pages2_range(mapping, 0, -1); 465 return invalidate_inode_pages2_range(mapping, 0, -1);
466} 466}
467EXPORT_SYMBOL_GPL(invalidate_inode_pages2); 467EXPORT_SYMBOL_GPL(invalidate_inode_pages2);
468
469/**
470 * truncate_pagecache - unmap and remove pagecache that has been truncated
471 * @inode: inode
472 * @old: old file offset
473 * @new: new file offset
474 *
475 * inode's new i_size must already be written before truncate_pagecache
476 * is called.
477 *
478 * This function should typically be called before the filesystem
479 * releases resources associated with the freed range (eg. deallocates
480 * blocks). This way, pagecache will always stay logically coherent
481 * with on-disk format, and the filesystem would not have to deal with
482 * situations such as writepage being called for a page that has already
483 * had its underlying blocks deallocated.
484 */
485void truncate_pagecache(struct inode *inode, loff_t old, loff_t new)
486{
487 if (new < old) {
488 struct address_space *mapping = inode->i_mapping;
489
490 /*
491 * unmap_mapping_range is called twice, first simply for
492 * efficiency so that truncate_inode_pages does fewer
493 * single-page unmaps. However after this first call, and
494 * before truncate_inode_pages finishes, it is possible for
495 * private pages to be COWed, which remain after
496 * truncate_inode_pages finishes, hence the second
497 * unmap_mapping_range call must be made for correctness.
498 */
499 unmap_mapping_range(mapping, new + PAGE_SIZE - 1, 0, 1);
500 truncate_inode_pages(mapping, new);
501 unmap_mapping_range(mapping, new + PAGE_SIZE - 1, 0, 1);
502 }
503}
504EXPORT_SYMBOL(truncate_pagecache);
505
506/**
507 * vmtruncate - unmap mappings "freed" by truncate() syscall
508 * @inode: inode of the file used
509 * @offset: file offset to start truncating
510 *
511 * NOTE! We have to be ready to update the memory sharing
512 * between the file and the memory map for a potential last
513 * incomplete page. Ugly, but necessary.
514 */
515int vmtruncate(struct inode *inode, loff_t offset)
516{
517 loff_t oldsize;
518 int error;
519
520 error = inode_newsize_ok(inode, offset);
521 if (error)
522 return error;
523 oldsize = inode->i_size;
524 i_size_write(inode, offset);
525 truncate_pagecache(inode, oldsize, offset);
526 if (inode->i_op->truncate)
527 inode->i_op->truncate(inode);
528
529 return error;
530}
531EXPORT_SYMBOL(vmtruncate);