Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

tmpfs: take control of its truncate_range

2.6.35's new truncate convention gave tmpfs the opportunity to control
its file truncation, no longer enforced from outside by vmtruncate().
We shall want to build upon that, to handle pagecache and swap together.

Slightly redefine the ->truncate_range interface: let it now be called
between the unmap_mapping_range()s, with the filesystem responsible for
doing the truncate_inode_pages_range() from it - just as the filesystem
is nowadays responsible for doing that from its ->setattr.

Let's rename shmem_notify_change() to shmem_setattr(). Instead of
calling the generic truncate_setsize(), bring that code in so we can
call shmem_truncate_range() - which will later be updated to perform its
own variant of truncate_inode_pages_range().

Remove the punch_hole unmap_mapping_range() from shmem_truncate_range():
now that the COW's unmap_mapping_range() comes after ->truncate_range,
there is no need to call it a third time.

Export shmem_truncate_range() and add it to the list in shmem_fs.h, so
that i915_gem_object_truncate() can call it explicitly in future; get
this patch in first, then update drm/i915 once this is available (until
then, i915 will just be doing the truncate_inode_pages() twice).

Though introduced five years ago, no other filesystem is implementing
->truncate_range, and its only other user is madvise(,,MADV_REMOVE): we
expect to convert it to fallocate(,FALLOC_FL_PUNCH_HOLE,,) shortly,
whereupon ->truncate_range can be removed from inode_operations -
shmem_truncate_range() will help i915 across that transition too.

Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Hugh Dickins and committed by
Linus Torvalds
94c1e62d 072441e2

+32 -24
+1
include/linux/shmem_fs.h
··· 61 61 loff_t size, unsigned long flags); 62 62 extern int shmem_zero_setup(struct vm_area_struct *); 63 63 extern int shmem_lock(struct file *file, int lock, struct user_struct *user); 64 + extern void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end); 64 65 extern int shmem_unuse(swp_entry_t entry, struct page *page); 65 66 extern void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff, 66 67 struct page **pagep, swp_entry_t *ent);
+29 -22
mm/shmem.c
··· 539 539 } while (next); 540 540 } 541 541 542 - static void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end) 542 + void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end) 543 543 { 544 544 struct shmem_inode_info *info = SHMEM_I(inode); 545 545 unsigned long idx; ··· 561 561 spinlock_t *needs_lock; 562 562 spinlock_t *punch_lock; 563 563 unsigned long upper_limit; 564 + 565 + truncate_inode_pages_range(inode->i_mapping, start, end); 564 566 565 567 inode->i_ctime = inode->i_mtime = CURRENT_TIME; 566 568 idx = (start + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; ··· 740 738 * lowered next_index. Also, though shmem_getpage checks 741 739 * i_size before adding to cache, no recheck after: so fix the 742 740 * narrow window there too. 743 - * 744 - * Recalling truncate_inode_pages_range and unmap_mapping_range 745 - * every time for punch_hole (which never got a chance to clear 746 - * SHMEM_PAGEIN at the start of vmtruncate_range) is expensive, 747 - * yet hardly ever necessary: try to optimize them out later. 748 741 */ 749 742 truncate_inode_pages_range(inode->i_mapping, start, end); 750 - if (punch_hole) 751 - unmap_mapping_range(inode->i_mapping, start, 752 - end - start, 1); 753 743 } 754 744 755 745 spin_lock(&info->lock); ··· 760 766 shmem_free_pages(pages_to_free.next); 761 767 } 762 768 } 769 + EXPORT_SYMBOL_GPL(shmem_truncate_range); 763 770 764 - static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) 771 + static int shmem_setattr(struct dentry *dentry, struct iattr *attr) 765 772 { 766 773 struct inode *inode = dentry->d_inode; 767 - loff_t newsize = attr->ia_size; 768 774 int error; 769 775 770 776 error = inode_change_ok(inode, attr); 771 777 if (error) 772 778 return error; 773 779 774 - if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE) 775 - && newsize != inode->i_size) { 780 + if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) { 781 + loff_t oldsize = inode->i_size; 782 + loff_t newsize = attr->ia_size; 776 783 struct page *page = NULL; 777 784 778 - if (newsize < inode->i_size) { 785 + if (newsize < oldsize) { 779 786 /* 780 787 * If truncating down to a partial page, then 781 788 * if that page is already allocated, hold it ··· 805 810 spin_unlock(&info->lock); 806 811 } 807 812 } 808 - 809 - /* XXX(truncate): truncate_setsize should be called last */ 810 - truncate_setsize(inode, newsize); 813 + if (newsize != oldsize) { 814 + i_size_write(inode, newsize); 815 + inode->i_ctime = inode->i_mtime = CURRENT_TIME; 816 + } 817 + if (newsize < oldsize) { 818 + loff_t holebegin = round_up(newsize, PAGE_SIZE); 819 + unmap_mapping_range(inode->i_mapping, holebegin, 0, 1); 820 + shmem_truncate_range(inode, newsize, (loff_t)-1); 821 + /* unmap again to remove racily COWed private pages */ 822 + unmap_mapping_range(inode->i_mapping, holebegin, 0, 1); 823 + } 811 824 if (page) 812 825 page_cache_release(page); 813 - shmem_truncate_range(inode, newsize, (loff_t)-1); 814 826 } 815 827 816 828 setattr_copy(inode, attr); ··· 834 832 struct shmem_xattr *xattr, *nxattr; 835 833 836 834 if (inode->i_mapping->a_ops == &shmem_aops) { 837 - truncate_inode_pages(inode->i_mapping, 0); 838 835 shmem_unacct_size(info->flags, inode->i_size); 839 836 inode->i_size = 0; 840 837 shmem_truncate_range(inode, 0, (loff_t)-1); ··· 2707 2706 }; 2708 2707 2709 2708 static const struct inode_operations shmem_inode_operations = { 2710 - .setattr = shmem_notify_change, 2709 + .setattr = shmem_setattr, 2711 2710 .truncate_range = shmem_truncate_range, 2712 2711 #ifdef CONFIG_TMPFS_XATTR 2713 2712 .setxattr = shmem_setxattr, ··· 2740 2739 .removexattr = shmem_removexattr, 2741 2740 #endif 2742 2741 #ifdef CONFIG_TMPFS_POSIX_ACL 2743 - .setattr = shmem_notify_change, 2742 + .setattr = shmem_setattr, 2744 2743 .check_acl = generic_check_acl, 2745 2744 #endif 2746 2745 }; ··· 2753 2752 .removexattr = shmem_removexattr, 2754 2753 #endif 2755 2754 #ifdef CONFIG_TMPFS_POSIX_ACL 2756 - .setattr = shmem_notify_change, 2755 + .setattr = shmem_setattr, 2757 2756 .check_acl = generic_check_acl, 2758 2757 #endif 2759 2758 }; ··· 2908 2907 { 2909 2908 return 0; 2910 2909 } 2910 + 2911 + void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end) 2912 + { 2913 + truncate_inode_pages_range(inode->i_mapping, start, end); 2914 + } 2915 + EXPORT_SYMBOL_GPL(shmem_truncate_range); 2911 2916 2912 2917 #ifdef CONFIG_CGROUP_MEM_RES_CTLR 2913 2918 /**
+2 -2
mm/truncate.c
··· 619 619 mutex_lock(&inode->i_mutex); 620 620 down_write(&inode->i_alloc_sem); 621 621 unmap_mapping_range(mapping, offset, (end - offset), 1); 622 - truncate_inode_pages_range(mapping, offset, end); 623 - unmap_mapping_range(mapping, offset, (end - offset), 1); 624 622 inode->i_op->truncate_range(inode, offset, end); 623 + /* unmap again to remove racily COWed private pages */ 624 + unmap_mapping_range(mapping, offset, (end - offset), 1); 625 625 up_write(&inode->i_alloc_sem); 626 626 mutex_unlock(&inode->i_mutex); 627 627