truncate patches
Subsystems (14)
cover truncate patches
Al would you merge these truncate patches? Are there any objections to them?
ext2 has passed xfstests and an hour of fsx-linux testing, fat and tmpfs pass fsx-linux testing. I will submit the other filesystems conversions to fs maintainers as these get merged.
Thanks,
Nick
PATCH 1/6 fs: introduce new truncate sequence
Introduce a new truncate calling sequence into fs/mm subsystems. Rather than setattr > vmtruncate > truncate, have filesystems call their truncate sequence from ->setattr if filesystem specific operations are required. vmtruncate is deprecated, and truncate_pagecache and inode_newsize_ok helpers introduced previously should be used.
simple_setattr is introduced for simple in-ram filesystems to implement the new truncate sequence. Eventually all filesystems should be converted to implement a setattr, and the default code in notify_change should go away.
simple_setsize is also introduced to perform just the ATTR_SIZE portion of simple_setattr (ie. changing i_size and trimming pagecache).
To implement the new truncate sequence:
- filesystem specific manipulations (eg freeing blocks) must be done in the setattr method rather than ->truncate.
- vmtruncate can not be used by core code to trim blocks past i_size in the event of write failure after allocation, so this must be performed in the fs code.
- convert usage of helpers block_write_begin, nobh_write_begin, cont_write_begin, and *blockdev_direct_IO* to use _newtrunc postfixed variants. These avoid calling vmtruncate to trim blocks (see previous).
- inode_setattr should not be used. generic_setattr is a new function to be used to copy simple attributes into the generic inode.
- make use of the better opportunity to handle errors with the new sequence.
Big problem with the previous calling sequence: the filesystem is not called until i_size has already changed. This means it is not allowed to fail the call, and also it does not know what the previous i_size was. Also, generic code calling vmtruncate to truncate allocated blocks in case of error had no good way to return a meaningful error (or, for example, atomically handle block deallocation).
--- Documentation/filesystems/vfs.txt | 7 +- fs/attr.c | 50 ++++++++++++--- fs/buffer.c | 123 ++++++++++++++++++++++++++++++-------- fs/direct-io.c | 61 ++++++++++++------ fs/libfs.c | 83 +++++++++++++++++++++++++ include/linux/buffer_head.h | 9 ++ include/linux/fs.h | 27 ++++++++ mm/truncate.c | 10 +-- 8 files changed, 307 insertions(+), 63 deletions(-)
Documentation/filesystems/vfs.txt +6 -1
@@ -401,11 +401,16 @@ otherwise noted. started might not be in the page cache at the end of the walk). - truncate: called by the VFS to change the size of a file. The + truncate: Deprecated. This will not be called if ->setsize is defined. + Called by the VFS to change the size of a file. The i_size field of the inode is set to the desired size by the VFS before this method is called. This method is called by the truncate(2) system call and related functionality. + Note: ->truncate and vmtruncate are deprecated. Do not add new + instances/calls of these. Filesystems should be converted to do their + truncate sequence via ->setattr(). + permission: called by the VFS to check for access rights on a POSIX-like filesystem.
fs/attr.c +40 -10
77 lines not on this page · raw message
fs/buffer.c +98 -25
183 lines not on this page · raw message
fs/direct-io.c +40 -21
88 lines not on this page · raw message
fs/libfs.c +76 -0
88 lines not on this page · raw message
include/linux/fs.h +26 -1
@@ -2250,6 +2250,10 @@ static inline int xip_truncate_page(stru#endif #ifdef CONFIG_BLOCK +ssize_t __blockdev_direct_IO_newtrunc (int rw,struct kiocb *iocb,struct inode *inode, +struct block_device *bdev,const struct iovec *iov,loff_t offset, +unsigned long nr_segs,get_block_t get_block,dio_iodone_t end_io, +int lock_type); ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, loff_t offset, unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io,
@@ -2263,6 +2267,24 @@ enum { DIO_SKIP_HOLES = 0x02, }; +static inline ssize_t blockdev_direct_IO_newtrunc (int rw,struct kiocb *iocb, +struct inode *inode,struct block_device *bdev,const struct iovec *iov, +loff_t offset,unsigned long nr_segs,get_block_t get_block, +dio_iodone_t end_io) +{ +return __blockdev_direct_IO_newtrunc (rw, iocb, inode, bdev, iov, offset, + nr_segs, get_block, end_io, +DIO_LOCKING |DIO_SKIP_HOLES ); +} + +static inline ssize_t blockdev_direct_IO_no_locking_newtrunc (int rw,struct kiocb *iocb, +struct inode *inode,struct block_device *bdev,const struct iovec *iov, +loff_t offset,unsigned long nr_segs,get_block_t get_block, +dio_iodone_t end_io) +{ +return __blockdev_direct_IO_newtrunc (rw, iocb, inode, bdev, iov, offset, + nr_segs, get_block, end_io,0 ); +} static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, loff_t offset, unsigned long nr_segs, get_block_t get_block,
@@ -2335,12 +2357,14 @@ extern int dcache_dir_open(struct inodeextern int dcache_dir_close (struct inode *,struct file *);extern loff_t dcache_dir_lseek (struct file *,loff_t ,int );extern int dcache_readdir (struct file *,void *,filldir_t ); +extern int simple_setattr (struct dentry *dentry,struct iattr *attr);extern int simple_getattr (struct vfsmount *,struct dentry *,struct kstat *);extern int simple_statfs (struct dentry *,struct kstatfs *);extern int simple_link (struct dentry *,struct inode *,struct dentry *);extern int simple_unlink (struct inode *,struct dentry *);extern int simple_rmdir (struct inode *,struct dentry *);extern int simple_rename (struct inode *,struct dentry *,struct inode *,struct dentry *); +extern int simple_setsize (struct inode *inode,loff_t newsize);extern int simple_sync_file (struct file *,struct dentry *,int );extern int simple_empty (struct dentry *);extern int simple_readpage (struct file *file,struct page *page);
@@ -2377,7 +2401,8 @@ extern int buffer_migrate_page(struct adextern int inode_change_ok (const struct inode *,struct iattr *);extern int inode_newsize_ok (const struct inode *,loff_t offset); -extern int __must_check inode_setattr (struct inode *,struct iattr *); +extern int __must_check inode_setattr (struct inode *,const struct iattr *); +extern void generic_setattr (struct inode *inode,const struct iattr *attr);extern void file_update_time (struct file *file);
mm/truncate.c +5 -5
@@ -548,18 +548,18 @@ EXPORT_SYMBOL(truncate_pagecache);* NOTE! We have to be ready to update the memory sharing * between the file and the memory map for a potential last * incomplete page. Ugly, but necessary. +* +* This function is deprecated and simple_setsize or truncate_pagecache +* should be used instead. */ int vmtruncate (struct inode *inode,loff_t offset) { -loff_t oldsize;int error; - error =inode_newsize_ok (inode, offset); + error =simple_setsize (inode, offset);if (error)return error; - oldsize = inode->i_size; -i_size_write (inode, offset); -truncate_pagecache (inode, oldsize, offset); +if (inode->i_op->truncate) inode->i_op->truncate (inode);
include/linux/buffer_head.h +9 -0
@@ -203,6 +203,9 @@ int block_write_full_page_endio(struct pint block_read_full_page (struct page *,get_block_t *);int block_is_partially_uptodate (struct page *page,read_descriptor_t *desc,unsigned long from); +int block_write_begin_newtrunc (struct file *,struct address_space *, +loff_t ,unsigned ,unsigned , +struct page **,void **,get_block_t *);int block_write_begin (struct file *,struct address_space *,loff_t ,unsigned ,unsigned ,struct page **,void **,get_block_t *);
@@ -214,6 +217,9 @@ int generic_write_end(struct file *, str struct page *, void *);void page_zero_new_buffers (struct page *page,unsigned from,unsigned to);int block_prepare_write (struct page *,unsigned ,unsigned ,get_block_t *); +int cont_write_begin_newtrunc (struct file *,struct address_space *,loff_t , +unsigned ,unsigned ,struct page **,void **, +get_block_t *,loff_t *);int cont_write_begin (struct file *,struct address_space *,loff_t ,unsigned ,unsigned ,struct page **,void **,get_block_t *,loff_t *);
@@ -225,6 +231,9 @@ void block_sync_page(struct page *);sector_t generic_block_bmap (struct address_space *,sector_t ,get_block_t *);int block_truncate_page (struct address_space *,loff_t ,get_block_t *);int file_fsync (struct file *,struct dentry *,int ); +int nobh_write_begin_newtrunc (struct file *,struct address_space *, +loff_t ,unsigned ,unsigned , +struct page **,void **,get_block_t *);int nobh_write_begin (struct file *,struct address_space *,loff_t ,unsigned ,unsigned ,struct page **,void **,get_block_t *);
PATCH 2/6 kill spurious reference to vmtruncate
In reply to [email protected]Lots of filesystems calls vmtruncate despite not implementing the old ->truncate method. Switch them to use simple_setsize and add some comments about the truncate code where it seems fitting.
--- fs/adfs/inode.c | 3 ++- fs/ecryptfs/inode.c | 15 +++++++++++++-- fs/gfs2/aops.c | 8 +++++++- fs/gfs2/ops_inode.c | 5 ++++- fs/jffs2/fs.c | 4 ++-- fs/ocfs2/file.c | 8 ++++++-- fs/smbfs/inode.c | 2 +- fs/ubifs/file.c | 13 ++++++++----- fs/ubifs/ubifs.h | 2 +- fs/ufs/truncate.c | 10 ++++------ 10 files changed, 48 insertions(+), 22 deletions(-)
fs/adfs/inode.c +2 -1
@@ -322,8 +322,9 @@ adfs_notify_change(struct dentry *dentryif (error) goto out; +/* XXX: this is missing some actual on-disk truncation.. */ if (ia_valid &ATTR_SIZE ) - error =vmtruncate (inode, attr->ia_size); + error =simple_setsize (inode, attr->ia_size);if (error) goto out;
fs/ecryptfs/inode.c +2 -2
@@ -805,7 +805,7 @@ static int truncate_upper(struct dentry - (ia->ia_size & ~PAGE_CACHE_MASK));if (!(crypt_stat->flags &ECRYPTFS_ENCRYPTED )) { - rc =vmtruncate (inode, ia->ia_size); + rc =simple_setsize (inode, ia->ia_size);if (rc) goto out; lower_ia->ia_size = ia->ia_size;
@@ -830,7 +830,7 @@ static int truncate_upper(struct dentry goto out; } } -vmtruncate (inode, ia->ia_size); +simple_setsize (inode, ia->ia_size); rc =ecryptfs_write_inode_size_to_metadata (inode);if (rc) { printk(KERN_ERR "Problem with "
fs/gfs2/aops.c +7 -1
@@ -700,8 +700,14 @@ out:return 0 ;page_cache_release (page); + +/* +* XXX(hch): the call below should probably be replaced with +* a call to the gfs2-specific truncate blocks helper to actually +* release disk blocks.. +*/ if (pos + len > ip->i_inode.i_size) -vmtruncate (&ip->i_inode, ip->i_inode.i_size); +simple_setsize (&ip->i_inode, ip->i_inode.i_size); out_endtrans:gfs2_trans_end (sdp); out_trans_fail:
fs/gfs2/ops_inode.c +4 -1
@@ -1071,6 +1071,9 @@ int gfs2_permission(struct inode *inode,return error; } +/* +* XXX: should be changed to have proper ordering by opencoding simple_setsize +*/ static int setattr_size (struct inode *inode,struct iattr *attr) {struct gfs2_inode *ip =GFS2_I (inode);
@@ -1081,7 +1084,7 @@ static int setattr_size(struct inode *in error =gfs2_trans_begin (sdp,0 , sdp->sd_jdesc->jd_blocks);if (error)return error; - error =vmtruncate (inode, attr->ia_size); + error =simple_setsize (inode, attr->ia_size);gfs2_trans_end (sdp);if (error)return error;
fs/jffs2/fs.c +2 -2
@@ -169,13 +169,13 @@ int jffs2_do_setattr (struct inode *inodmutex_unlock (&f->sem);jffs2_complete_reservation (c); -/* We have to do the vmtruncate() without f->sem held, since +/* We have to do the simple_setsize() without f->sem held, since some pages may be locked and waiting for it in readpage(). We are protected from a simultaneous write() extending i_size back past iattr->ia_size, because do_truncate() holds the generic inode semaphore. */ if (ivalid &ATTR_SIZE && inode->i_size > iattr->ia_size) { -vmtruncate (inode, iattr->ia_size); +simple_setsize (inode, iattr->ia_size); inode->i_blocks = (inode->i_size +511 ) >>9 ; }
fs/ocfs2/file.c +6 -2
@@ -1053,7 +1053,7 @@ int ocfs2_setattr(struct dentry *dentry, }/* -* This will intentionally not wind up calling vmtruncate(), +* This will intentionally not wind up calling simple_setsize(), * since all the work for a size change has been done above. * Otherwise, we could get into problems with truncate as * ip_alloc_sem is used there to protect against i_size
@@ -2119,9 +2119,13 @@ relock:* direct write may have instantiated a few * blocks outside i_size. Trim these off again. * Don't need i_size_read because we hold i_mutex. +* +* XXX(hch): this looks buggy because ocfs2 did not +* actually implement ->truncate. Take a look at +* the new truncate sequence and update this accordingly */ if (*ppos + count > inode->i_size) -vmtruncate (inode, inode->i_size); +simple_setsize (inode, inode->i_size); ret = written; goto out_dio; }
fs/smbfs/inode.c +1 -1
@@ -714,7 +714,7 @@ smb_notify_change(struct dentry *dentry, error = server->ops->truncate (inode, attr->ia_size);if (error) goto out; - error =vmtruncate (inode, attr->ia_size); + error =simple_setsize (inode, attr->ia_size);if (error) goto out; refresh =1 ;
fs/ubifs/file.c +8 -5
@@ -967,12 +967,15 @@ static int do_writepage(struct page *pag* the page locked, and it locks @ui_mutex. However, write-back does take inode * @i_mutex, which means other VFS operations may be run on this inode at the * same time. And the problematic one is truncation to smaller size, from where -* we have to call 'vmtruncate()', which first changes @inode->i_size, then +* we have to call 'simple_setsize()', which first changes @inode->i_size, then * drops the truncated pages. And while dropping the pages, it takes the page -* lock. This means that 'do_truncation()' cannot call 'vmtruncate()' with +* lock. This means that 'do_truncation()' cannot call 'simple_setsize()' with * @ui_mutex locked, because it would deadlock with 'ubifs_writepage()'. This * means that @inode->i_size is changed while @ui_mutex is unlocked. * +* XXX: with the new truncate the above is not true anymore, the simple_setsize +* calls can be replaced with the individual components. +* * But in 'ubifs_writepage()' we have to guarantee that we do not write beyond * inode size. How do we do this if @inode->i_size may became smaller while we * are in the middle of 'ubifs_writepage()'? The UBIFS solution is the
@@ -1125,7 +1128,7 @@ static int do_truncation(struct ubifs_in budgeted =0 ; } - err =vmtruncate (inode, new_size); + err =simple_setsize (inode, new_size);if (err) goto out_budg;
@@ -1214,7 +1217,7 @@ static int do_setattr(struct ubifs_infoif (attr->ia_valid &ATTR_SIZE ) {dbg_gen ("size %lld -> %lld" , inode->i_size, new_size); - err =vmtruncate (inode, new_size); + err =simple_setsize (inode, new_size);if (err) goto out; }
@@ -1223,7 +1226,7 @@ static int do_setattr(struct ubifs_infoif (attr->ia_valid &ATTR_SIZE ) {/* Truncation changes inode [mc]time */ inode->i_mtime = inode->i_ctime =ubifs_current_time (inode); -/* 'vmtruncate()' changed @i_size, update @ui_size */ +/* 'simple_setsize()' changed @i_size, update @ui_size */ ui->ui_size = inode->i_size; }
fs/ubifs/ubifs.h +1 -1
@@ -379,7 +379,7 @@ struct ubifs_gced_idx_leb {* The @ui_size is a "shadow" variable for @inode->i_size and UBIFS uses * @ui_size instead of @inode->i_size. The reason for this is that UBIFS cannot * make sure @inode->i_size is always changed under @ui_mutex, because it -* cannot call 'vmtruncate()' with @ui_mutex locked, because it would deadlock +* cannot call 'simple_setsize()' with @ui_mutex locked, because it would deadlock * with 'ubifs_writepage()' (see file.c). All the other inode fields are * changed under @ui_mutex, so they do not need "shadow" fields. Note, one * could consider to rework locking and base it on "shadow" fields.
fs/ufs/truncate.c +4 -6
@@ -501,12 +501,10 @@ out:return err; } -/* -* We don't define our `inode->i_op->truncate', and call it here, -* because of: -* - there is no way to know old size -* - there is no way inform user about error, if it happens in `truncate' +* TODO: +* - truncate case should use proper ordering instead of using +* simple_setsize */ int ufs_setattr (struct dentry *dentry,struct iattr *attr) {
@@ -530,7 +528,7 @@ int ufs_setattr(struct dentry *dentry, sif (ia_valid &ATTR_SIZE && attr->ia_size != inode->i_size) {loff_t old_i_size = inode->i_size; - error =vmtruncate (inode, attr->ia_size); + error =simple_setsize (inode, attr->ia_size);if (error)return error; error =ufs_truncate (inode, old_i_size);
PATCH 3/6 fs: convert simple fs to new truncate
In reply to [email protected]Convert simple filesystems: ramfs, configfs, sysfs, block_dev to new truncate sequence.
--- fs/block_dev.c | 9 +++++---- fs/configfs/inode.c | 10 +++++++--- fs/ramfs/file-mmu.c | 1 + fs/ramfs/file-nommu.c | 7 ++++--- fs/sysfs/inode.c | 6 +----- 5 files changed, 18 insertions(+), 15 deletions(-)
fs/configfs/inode.c +7 -3
@@ -78,9 +78,13 @@ int configfs_setattr(struct dentry * denif (error)return error; - error =inode_setattr (inode, iattr); -if (error) -return error; +if (iattr->ia_valid &ATTR_SIZE ) { + error =simple_setsize (inode, iattr->ia_size); +if (error) +return error; + } + +generic_setattr (inode, iattr);if (!sd_iattr) {/* setting attributes for the first time, allocate now */
fs/ramfs/file-mmu.c +1 -0
@@ -50,5 +50,6 @@ const struct file_operations ramfs_file_ };const struct inode_operations ramfs_file_inode_operations = { + .setattr = simple_setattr, .getattr = simple_getattr, };
fs/ramfs/file-nommu.c +4 -3
@@ -146,7 +146,7 @@ static int ramfs_nommu_resize(struct inoreturn ret; } - ret =vmtruncate (inode, newsize); + ret =simple_setsize (inode, newsize);return ret; }
@@ -169,7 +169,8 @@ static int ramfs_nommu_setattr(struct de/* pick out size-changing events */ if (ia->ia_valid &ATTR_SIZE ) { -loff_t size =i_size_read (inode); +loff_t size = inode->i_size; +if (ia->ia_size != size) { ret =ramfs_nommu_resize (inode, ia->ia_size, size);if (ret <0 || ia->ia_valid ==ATTR_SIZE )
@@ -182,7 +183,7 @@ static int ramfs_nommu_setattr(struct de } } - ret =inode_setattr (inode, ia); +generic_setattr (inode, ia); out: ia->ia_valid = old_ia_valid;return ret;
fs/sysfs/inode.c +4 -4
@@ -117,13 +117,13 @@ int sysfs_setattr(struct dentry *dentry,if (error) goto out; - iattr->ia_valid &= ~ATTR_SIZE ;/* ignore size changes */ - - error =inode_setattr (inode, iattr); + error =sysfs_sd_setattr (sd, iattr);if (error) goto out; - error =sysfs_sd_setattr (sd, iattr); +/* this ignores size changes */ +generic_setattr (inode, iattr); + out:mutex_unlock (&sysfs_mutex);return error;
fs/block_dev.c +5 -4
@@ -172,8 +172,9 @@ blkdev_direct_IO(int rw, struct kiocb *istruct file *file = iocb->ki_filp;struct inode *inode = file->f_mapping->host; -return blockdev_direct_IO_no_locking (rw, iocb, inode,I_BDEV (inode), - iov, offset, nr_segs, blkdev_get_blocks,NULL ); +return blockdev_direct_IO_no_locking_newtrunc (rw, iocb, inode, +I_BDEV (inode), iov, offset, nr_segs, + blkdev_get_blocks,NULL ); }int __sync_blockdev (struct block_device *bdev,int wait)
@@ -309,8 +310,8 @@ static int blkdev_write_begin(struct fil struct page **pagep, void **fsdata) { *pagep =NULL ; -return block_write_begin (file, mapping, pos, len, flags, pagep, fsdata, - blkdev_get_block); +return block_write_begin_newtrunc (file, mapping, pos, len, flags, + pagep, fsdata, blkdev_get_block); } static int blkdev_write_end(struct file *file, struct address_space *mapping,
Al Viro
maintainerRe: [patch 3/6] [PATCH] fs: convert simple fs to new truncate
Quoted 10 lines · On Thu, May 27, 2010 at 01:05:35AM +1000, [email protected] wrote:
===================================================================
fs/configfs/inode.c +0
-3@@ -78,9 +78,13 @@ int configfs_setattr(struct dentry * denif (error)return error; - error =inode_setattr (inode, iattr); -if (error) -return error;
fs/configfs/inode.c int configfs_setattr(struct dentry * den · line 90
Quoted 10 lines
fs/configfs/inode.c +7
-0+if (iattr->ia_valid &ATTR_SIZE ) { + error =simple_setsize (inode, iattr->ia_size); +if (error) +return error; + } + +generic_setattr (inode, iattr);if (!sd_iattr) {/* setting attributes for the first time, allocate now */
That should use simple_setattr() instead of generic_setattr(); look at the check just before this chunk...
Quoted 14 lines
+++
linux-2.6/fs/block_dev.cfs/configfs/inode.c +3
-2@@ -172,8 +172,9 @@ blkdev_direct_IO(int rw, struct kiocb *istruct file *file = iocb->ki_filp;struct inode *inode = file->f_mapping->host; -return blockdev_direct_IO_no_locking (rw, iocb, inode,I_BDEV (inode), - iov, offset, nr_segs, blkdev_get_blocks,NULL ); +return blockdev_direct_IO_no_locking_newtrunc (rw, iocb, inode, +I_BDEV (inode), iov, offset, nr_segs, + blkdev_get_blocks,NULL ); }int __sync_blockdev (struct block_device *bdev,int wait)
fs/configfs/inode.c static int blkdev_write_begin(struct fil · line 315
Quoted 8 lines
fs/configfs/inode.c +2
-2@@ -309,8 +310,8 @@ static int blkdev_write_begin(struct fil struct page **pagep, void **fsdata) { *pagep =NULL ; -return block_write_begin (file, mapping, pos, len, flags, pagep, fsdata, - blkdev_get_block); +return block_write_begin_newtrunc (file, mapping, pos, len, flags, + pagep, fsdata, blkdev_get_block); }
And that should be folded back into the patch #1.
Re: [patch 3/6] [PATCH] fs: convert simple fs to new truncate
Quoted 19 lines · Al Viro
Quoted 18 lines · On Thu, May 27, 2010 at 01:05:35AM +1000, [email protected] wrote:
===================================================================
fs/configfs/inode.c +7
-3@@ -78,9 +78,13 @@ int configfs_setattr(struct dentry * denif (error)return error; - error =inode_setattr (inode, iattr); -if (error) -return error; +if (iattr->ia_valid &ATTR_SIZE ) { + error =simple_setsize (inode, iattr->ia_size); +if (error) +return error; + } + +generic_setattr (inode, iattr);
Quoted 5 lines
fs/configfs/inode.c int configfs_setattr(struct dentry * den · line 90
Quoted 2 lines
fs/configfs/inode.c +0
-0if (!sd_iattr) {/* setting attributes for the first time, allocate now */ That should use
simple_setattr()instead ofgeneric_setattr(); look at the check just before this chunk...
Thanks, agree. I wonder if we shouldn't move the setattr part over to after the sd_iattr is allocated? Couldn't a failure result in inconsistent "on disk" vs inode attributes?
I'm guilty of making undocumented similar change in sysfs too, so I'll resend this patch with that reverted as well, and send patch to change sequence independently.
Quoted 12 lines
Quoted 12 lines
+++
linux-2.6/fs/block_dev.cfs/configfs/inode.c +3
-2@@ -172,8 +172,9 @@ blkdev_direct_IO(int rw, struct kiocb *istruct file *file = iocb->ki_filp;struct inode *inode = file->f_mapping->host; -return blockdev_direct_IO_no_locking (rw, iocb, inode,I_BDEV (inode), - iov, offset, nr_segs, blkdev_get_blocks,NULL ); +return blockdev_direct_IO_no_locking_newtrunc (rw, iocb, inode, +I_BDEV (inode), iov, offset, nr_segs, + blkdev_get_blocks,NULL ); }
Quoted 12 lines
fs/configfs/inode.c static int blkdev_write_begin(struct fil · line 315
Quoted 10 lines
fs/configfs/inode.c +2
-2int __sync_blockdev (struct block_device *bdev,int wait)@@ -309,8 +310,8 @@ static int blkdev_write_begin(struct fil struct page **pagep, void **fsdata) { *pagep =NULL ; -return block_write_begin (file, mapping, pos, len, flags, pagep, fsdata, - blkdev_get_block); +return block_write_begin_newtrunc (file, mapping, pos, len, flags, + pagep, fsdata, blkdev_get_block); }And that should be folded back into the patch #1.
I thought this hunk should remain part of the simple fs conversions, just to leave the add core functions versus use new functions in different parts.
How's this?
--
From: Nick Piggin <[email protected]>
Subject: [PATCH] fs: convert simple fs to new truncate
Convert simple filesystems: ramfs, configfs, sysfs, block_dev to new truncate sequence.
--- fs/block_dev.c | 9 +++++---- fs/configfs/inode.c | 10 +++++++--- fs/ramfs/file-mmu.c | 1 + fs/ramfs/file-nommu.c | 7 ++++--- fs/sysfs/inode.c | 6 +----- 5 files changed, 18 insertions(+), 15 deletions(-)
fs/configfs/inode.c +2 -7
@@ -72,16 +72,11 @@ int configfs_setattr(struct dentry * denif (!sd)return -EINVAL ; - sd_iattr = sd->s_iattr; - - error =inode_change_ok (inode, iattr); -if (error) -return error; - - error =inode_setattr (inode, iattr); + error =simple_setattr (dentry, iattr);if (error)return error; + sd_iattr = sd->s_iattr;if (!sd_iattr) {/* setting attributes for the first time, allocate now */ sd_iattr =kzalloc (sizeof (struct iattr ),GFP_KERNEL );
fs/ramfs/file-mmu.c +1 -0
@@ -50,5 +50,6 @@ const struct file_operations ramfs_file_ };const struct inode_operations ramfs_file_inode_operations = { + .setattr = simple_setattr, .getattr = simple_getattr, };
fs/ramfs/file-nommu.c +4 -3
@@ -146,7 +146,7 @@ static int ramfs_nommu_resize(struct inoreturn ret; } - ret =vmtruncate (inode, newsize); + ret =simple_setsize (inode, newsize);return ret; }
@@ -169,7 +169,8 @@ static int ramfs_nommu_setattr(struct de/* pick out size-changing events */ if (ia->ia_valid &ATTR_SIZE ) { -loff_t size =i_size_read (inode); +loff_t size = inode->i_size; +if (ia->ia_size != size) { ret =ramfs_nommu_resize (inode, ia->ia_size, size);if (ret <0 || ia->ia_valid ==ATTR_SIZE )
@@ -182,7 +183,7 @@ static int ramfs_nommu_setattr(struct de } } - ret =inode_setattr (inode, ia); +generic_setattr (inode, ia); out: ia->ia_valid = old_ia_valid;return ret;
fs/sysfs/inode.c +3 -5
@@ -117,13 +117,11 @@ int sysfs_setattr(struct dentry *dentry,if (error) goto out; - iattr->ia_valid &= ~ATTR_SIZE ;/* ignore size changes */ - - error =inode_setattr (inode, iattr); -if (error) - goto out; +/* this ignores size changes */ +generic_setattr (inode, iattr); error =sysfs_sd_setattr (sd, iattr); + out:mutex_unlock (&sysfs_mutex);return error;
fs/block_dev.c +5 -4
@@ -172,8 +172,9 @@ blkdev_direct_IO(int rw, struct kiocb *istruct file *file = iocb->ki_filp;struct inode *inode = file->f_mapping->host; -return blockdev_direct_IO_no_locking (rw, iocb, inode,I_BDEV (inode), - iov, offset, nr_segs, blkdev_get_blocks,NULL ); +return blockdev_direct_IO_no_locking_newtrunc (rw, iocb, inode, +I_BDEV (inode), iov, offset, nr_segs, + blkdev_get_blocks,NULL ); }int __sync_blockdev (struct block_device *bdev,int wait)
@@ -309,8 +310,8 @@ static int blkdev_write_begin(struct fil struct page **pagep, void **fsdata) { *pagep =NULL ; -return block_write_begin (file, mapping, pos, len, flags, pagep, fsdata, - blkdev_get_block); +return block_write_begin_newtrunc (file, mapping, pos, len, flags, + pagep, fsdata, blkdev_get_block); } static int blkdev_write_end(struct file *file, struct address_space *mapping,
Re: [patch 3/6] [PATCH] fs: convert simple fs to new truncate
Quoted 1 line · Nick Piggin
How's this?
Applied.
PATCH 4/6 tmpfs: convert to use the new truncate convention
In reply to [email protected]--- mm/shmem.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-)
mm/shmem.c +22 -21
@@ -727,10 +727,11 @@ done2:if (inode->i_mapping->nrpages && (info->flags &SHMEM_PAGEIN )) {/* * Call truncate_inode_pages again: racing shmem_unuse_inode -* may have swizzled a page in from swap since vmtruncate or -* generic_delete_inode did it, before we lowered next_index. -* Also, though shmem_getpage checks i_size before adding to -* cache, no recheck after: so fix the narrow window there too. +* may have swizzled a page in from swap since +* truncate_pagecache or generic_delete_inode did it, before we +* lowered next_index. Also, though shmem_getpage checks +* i_size before adding to cache, no recheck after: so fix the +* narrow window there too. * * Recalling truncate_inode_pages_range and unmap_mapping_range * every time for punch_hole (which never got a chance to clear
@@ -760,19 +761,16 @@ done2: } } -static void shmem_truncate (struct inode *inode) -{ -shmem_truncate_range (inode, inode->i_size, (loff_t)-1 ); -} -static int shmem_notify_change (struct dentry *dentry,struct iattr *attr) {struct inode *inode = dentry->d_inode; -struct page *page =NULL ;int error;if (S_ISREG (inode->i_mode) && (attr->ia_valid &ATTR_SIZE )) { -if (attr->ia_size < inode->i_size) { +loff_t newsize = attr->ia_size; +struct page *page =NULL ; + +if (newsize < inode->i_size) {/* * If truncating down to a partial page, then * if that page is already allocated, hold it
@@ -780,9 +778,9 @@ static int shmem_notify_change(struct de* truncate_partial_page cannnot miss it were * it assigned to swap. */ -if (attr->ia_size & (PAGE_CACHE_SIZE -1 )) { +if (newsize & (PAGE_CACHE_SIZE -1 )) { (void )shmem_getpage (inode, - attr->ia_size>>PAGE_CACHE_SHIFT , + newsize >>PAGE_CACHE_SHIFT , &page,SGP_READ ,NULL );if (page)unlock_page (page);
@@ -794,24 +792,29 @@ static int shmem_notify_change(struct de* if it's being fully truncated to zero-length: the * nrpages check is efficient enough in that case. */ -if (attr->ia_size) { +if (newsize) {struct shmem_inode_info *info =SHMEM_I (inode);spin_lock (&info->lock); info->flags &= ~SHMEM_PAGEIN ;spin_unlock (&info->lock); } } + + error =simple_setsize (inode, newsize); +if (page) +page_cache_release (page); +if (error) +return error; +shmem_truncate_range (inode, newsize, (loff_t)-1 ); } error =inode_change_ok (inode, attr);if (!error) - error =inode_setattr (inode, attr); +generic_setattr (inode, attr);#ifdef CONFIG_TMPFS_POSIX_ACL if (!error && (attr->ia_valid &ATTR_MODE )) error =generic_acl_chmod (inode);#endif -if (page) -page_cache_release (page);return error; }
@@ -819,11 +822,11 @@ static void shmem_delete_inode(struct in {struct shmem_inode_info *info =SHMEM_I (inode); -if (inode->i_op->truncate == shmem_truncate) { +if (inode->i_mapping->a_ops == &shmem_aops) {truncate_inode_pages (inode->i_mapping,0 );shmem_unacct_size (info->flags, inode->i_size); inode->i_size =0 ; -shmem_truncate (inode); +shmem_truncate_range (inode,0 , (loff_t)-1 );if (!list_empty (&info->swaplist)) {mutex_lock (&shmem_swaplist_mutex);list_del_init (&info->swaplist);
@@ -2022,7 +2025,6 @@ static const struct inode_operations shm };static const struct inode_operations shmem_symlink_inode_operations = { - .truncate = shmem_truncate, .readlink = generic_readlink, .follow_link = shmem_follow_link, .put_link = shmem_put_link,
@@ -2440,7 +2442,6 @@ static const struct file_operations shme };static const struct inode_operations shmem_inode_operations = { - .truncate = shmem_truncate, .setattr = shmem_notify_change, .truncate_range = shmem_truncate_range, #ifdef CONFIG_TMPFS_POSIX_ACL
PATCH 5/6 ext2: convert to use the new truncate convention.
In reply to [email protected]I also have commented a possible bug in existing ext2 code, marked with XXX.
--- fs/ext2/ext2.h | 1 fs/ext2/file.c | 1 fs/ext2/inode.c | 153 +++++++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 119 insertions(+), 36 deletions(-)
fs/ext2/ext2.h +0 -1
@@ -122,7 +122,6 @@ extern int ext2_write_inode (struct inodextern void ext2_delete_inode (struct inode *);extern int ext2_sync_inode (struct inode *);extern int ext2_get_block (struct inode *,sector_t ,struct buffer_head *,int ); -extern void ext2_truncate (struct inode *);extern int ext2_setattr (struct dentry *,struct iattr *);extern void ext2_set_inode_flags (struct inode *inode);extern void ext2_get_inode_flags (struct ext2_inode_info *);
fs/ext2/file.c +0 -1
@@ -95,7 +95,6 @@ const struct file_operations ext2_xip_fi#endif const struct inode_operations ext2_file_inode_operations = { - .truncate = ext2_truncate, #ifdef CONFIG_EXT2_FS_XATTR .setxattr = generic_setxattr, .getxattr = generic_getxattr,
fs/ext2/inode.c +119 -34
232 lines not on this page · raw message
PATCH 6/6 fat: convert to use the new truncate convention.
In reply to [email protected]--- fs/fat/fat.h | 3 ++- fs/fat/file.c | 34 ++++++++++++++++++++++++++-------- fs/fat/inode.c | 35 +++++++++++++++++++++++++++++------ 3 files changed, 57 insertions(+), 15 deletions(-)
fs/fat/fat.h +2 -1
@@ -306,7 +306,8 @@ extern long fat_generic_ioctl(struct filextern const struct file_operations fat_file_operations;extern const struct inode_operations fat_file_inode_operations;extern int fat_setattr (struct dentry * dentry,struct iattr * attr); -extern void fat_truncate (struct inode *inode); +extern int fat_setsize (struct inode *inode,loff_t offset); +extern void fat_truncate_blocks (struct inode *inode,loff_t offset);extern int fat_getattr (struct vfsmount *mnt,struct dentry *dentry,struct kstat *stat); extern int fat_file_fsync(struct file *file, struct dentry *dentry,
fs/fat/file.c +26 -8
@@ -283,7 +283,7 @@ static int fat_free(struct inode *inode,return fat_free_clusters (inode, free_start); } -void fat_truncate (struct inode *inode) +void fat_truncate_blocks (struct inode *inode,loff_t offset) {struct msdos_sb_info *sbi =MSDOS_SB (inode->i_sb);const unsigned int cluster_size = sbi->cluster_size;
@@ -293,10 +293,10 @@ void fat_truncate(struct inode *inode)* This protects against truncating a file bigger than it was then * trying to write into the hole. */ -if (MSDOS_I (inode)->mmu_private > inode->i_size) -MSDOS_I (inode)->mmu_private = inode->i_size; +if (MSDOS_I (inode)->mmu_private > offset) +MSDOS_I (inode)->mmu_private = offset; - nr_clusters = (inode->i_size + (cluster_size -1 )) >> sbi->cluster_bits; + nr_clusters = (offset + (cluster_size -1 )) >> sbi->cluster_bits;fat_free (inode, nr_clusters);fat_flush_inodes (inode->i_sb, inode,NULL );
@@ -364,6 +364,18 @@ static int fat_allow_set_time(struct msdreturn 0 ; } +int fat_setsize (struct inode *inode,loff_t offset) +{ +int error; + + error =simple_setsize (inode, offset); +if (error) +return error; +fat_truncate_blocks (inode, offset); + +return error; +} +#define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)/* valid file mode bits */ #define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO)
@@ -378,7 +390,8 @@ int fat_setattr(struct dentry *dentry, s/* * Expand the file. Since inode_setattr() updates ->i_size * before calling the ->truncate(), but FAT needs to fill the -* hole before it. +* hole before it. XXX: this is no longer true with new truncate +* sequence. */ if (attr->ia_valid &ATTR_SIZE ) {if (attr->ia_size > inode->i_size) {
@@ -427,15 +440,20 @@ int fat_setattr(struct dentry *dentry, s attr->ia_valid &= ~ATTR_MODE ; } -if (attr->ia_valid) - error =inode_setattr (inode, attr); +if (attr->ia_valid &ATTR_SIZE ) { + error =fat_setsize (inode, attr->ia_size); +if (error) + goto out; + } + +generic_setattr (inode, attr); +mark_inode_dirty (inode); out:return error; }EXPORT_SYMBOL_GPL (fat_setattr);const struct inode_operations fat_file_inode_operations = { - .truncate = fat_truncate, .setattr = fat_setattr, .getattr = fat_getattr, };
fs/fat/inode.c +29 -6
@@ -142,14 +142,29 @@ static int fat_readpages(struct file *fireturn mpage_readpages (mapping, pages, nr_pages, fat_get_block); } +static void fat_write_failed (struct address_space *mapping,loff_t to) +{ +struct inode *inode = mapping->host; + +if (to > inode->i_size) { +truncate_pagecache (inode, to, inode->i_size); +fat_truncate_blocks (inode, inode->i_size); + } +} +static int fat_write_begin (struct file *file,struct address_space *mapping,loff_t pos,unsigned len,unsigned flags,struct page **pagep,void **fsdata) { +int err; + *pagep =NULL ; -return cont_write_begin (file, mapping, pos, len, flags, pagep, fsdata, - fat_get_block, + err =cont_write_begin_newtrunc (file, mapping, pos, len, flags, + pagep, fsdata, fat_get_block, &MSDOS_I (mapping->host)->mmu_private); +if (err <0 ) +fat_write_failed (mapping, pos + len); +return err; } static int fat_write_end(struct file *file, struct address_space *mapping,
@@ -159,6 +174,8 @@ static int fat_write_end(struct file *fistruct inode *inode = mapping->host;int err; err =generic_write_end (file, mapping, pos, len, copied, pagep, fsdata); +if (err < len) +fat_write_failed (mapping, pos + len);if (!(err <0 ) && !(MSDOS_I (inode)->i_attrs &ATTR_ARCH )) { inode->i_mtime = inode->i_ctime =CURRENT_TIME_SEC ;MSDOS_I (inode)->i_attrs |=ATTR_ARCH ;
@@ -172,7 +189,9 @@ static ssize_t fat_direct_IO(int rw, str loff_t offset, unsigned long nr_segs) {struct file *file = iocb->ki_filp; -struct inode *inode = file->f_mapping->host; +struct address_space *mapping = file->f_mapping; +struct inode *inode = mapping->host; +ssize_t ret;if (rw ==WRITE ) {/*
@@ -193,8 +212,12 @@ static ssize_t fat_direct_IO(int rw, str* FAT need to use the DIO_LOCKING for avoiding the race * condition of fat_get_block() and ->truncate(). */ -return blockdev_direct_IO (rw, iocb, inode, inode->i_sb->s_bdev, iov, - offset, nr_segs, fat_get_block,NULL ); + ret =blockdev_direct_IO_newtrunc (rw, iocb, inode, inode->i_sb->s_bdev, + iov, offset, nr_segs, fat_get_block,NULL ); +if (ret <0 && (rw &WRITE )) +fat_write_failed (mapping, offset +iov_length (iov, nr_segs)); + +return ret; }static sector_t _fat_bmap (struct address_space *mapping,sector_t block)
@@ -429,7 +452,7 @@ static void fat_delete_inode(struct inod {truncate_inode_pages (&inode->i_data,0 ); inode->i_size =0 ; -fat_truncate (inode); +fat_truncate_blocks (inode,0 );clear_inode (inode); }