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). Cc: Christoph Hellwig <hch@lst.de> Acked-by: Jan Kara <jack@suse.cz> Signed-off-by: Nick Piggin <npiggin@suse.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
@@ -2257,6 +2257,10 @@ typedef void (dio_submit_t)(int rw, struct bio *bio, struct inode *inode,
|
||||
loff_t file_offset);
|
||||
void dio_end_io(struct bio *bio, int error);
|
||||
|
||||
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,
|
||||
dio_submit_t submit_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,
|
||||
@@ -2270,6 +2274,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, NULL,
|
||||
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, NULL, 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,
|
||||
@@ -2342,12 +2364,14 @@ extern int dcache_dir_open(struct inode *, struct file *);
|
||||
extern 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 *, struct iattr *);
|
||||
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 *, loff_t);
|
||||
extern int noop_fsync(struct file *, int);
|
||||
extern int simple_empty(struct dentry *);
|
||||
extern int simple_readpage(struct file *file, struct page *page);
|
||||
@@ -2384,7 +2408,8 @@ extern int buffer_migrate_page(struct address_space *,
|
||||
|
||||
extern 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);
|
||||
|
||||
|
Reference in New Issue
Block a user