diff options
71 files changed, 462 insertions, 164 deletions
| diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 9b6ed7c9f34f..ca7e25292542 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -412,7 +412,7 @@ prototypes:  	int (*open) (struct inode *, struct file *);  	int (*flush) (struct file *);  	int (*release) (struct inode *, struct file *); -	int (*fsync) (struct file *, int datasync); +	int (*fsync) (struct file *, loff_t start, loff_t end, int datasync);  	int (*aio_fsync) (struct kiocb *, int datasync);  	int (*fasync) (int, struct file *, int);  	int (*lock) (struct file *, int, struct file_lock *); @@ -438,9 +438,7 @@ prototypes:  locking rules:  	All may block except for ->setlease. -	No VFS locks held on entry except for ->fsync and ->setlease. - -->fsync() has i_mutex on inode. +	No VFS locks held on entry except for ->setlease.  ->setlease has the file_list_lock held and must not sleep. diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index 6b96773e27cb..7f8861d341ea 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -421,3 +421,10 @@ data and there is a virtual hole at the end of the file.  So if the provided  offset is less than i_size and SEEK_DATA is specified, return the same offset.  If the above is true for the offset and you are given SEEK_HOLE, return the end  of the file.  If the offset is i_size or greater return -ENXIO in either case. + +[mandatory] +	If you have your own ->fsync() you must make sure to call +filemap_write_and_wait_range() so that all dirty pages are synced out properly. +You must also keep in mind that ->fsync() is not called with i_mutex held +anymore, so if you require i_mutex locking you must make sure to take it and +release it yourself. diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 6bf85b78cfea..eff6617c9a0f 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -777,7 +777,7 @@ struct file_operations {  	int (*open) (struct inode *, struct file *);  	int (*flush) (struct file *);  	int (*release) (struct inode *, struct file *); -	int (*fsync) (struct file *, int datasync); +	int (*fsync) (struct file *, loff_t, loff_t, int datasync);  	int (*aio_fsync) (struct kiocb *, int datasync);  	int (*fasync) (int, struct file *, int);  	int (*lock) (struct file *, int, struct file_lock *); diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 3c7c3f82d842..fb59c46e9e9e 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -1850,9 +1850,16 @@ out:  	return ret;  } -static int spufs_mfc_fsync(struct file *file, int datasync) +static int spufs_mfc_fsync(struct file *file, loff_t start, loff_t end, int datasync)  { -	return spufs_mfc_flush(file, NULL); +	struct inode *inode = file->f_path.dentry->d_inode; +	int err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (!err) { +		mutex_lock(&inode->i_mutex); +		err = spufs_mfc_flush(file, NULL); +		mutex_unlock(&inode->i_mutex); +	} +	return err;  }  static int spufs_mfc_fasync(int fd, struct file *file, int on) diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index 5a06787e5be3..d0c57c2e2909 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c @@ -309,9 +309,14 @@ static int ps3flash_flush(struct file *file, fl_owner_t id)  	return ps3flash_writeback(ps3flash_dev);  } -static int ps3flash_fsync(struct file *file, int datasync) +static int ps3flash_fsync(struct file *file, loff_t start, loff_t end, int datasync)  { -	return ps3flash_writeback(ps3flash_dev); +	struct inode *inode = file->f_path.dentry->d_inode; +	int err; +	mutex_lock(&inode->i_mutex); +	err = ps3flash_writeback(ps3flash_dev); +	mutex_unlock(&inode->i_mutex); +	return err;  }  static irqreturn_t ps3flash_interrupt(int irq, void *data) diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 191f3bb3c41a..3320a50ba4f0 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -189,12 +189,16 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)  	return new_offset;  } -static int vol_cdev_fsync(struct file *file, int datasync) +static int vol_cdev_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct ubi_volume_desc *desc = file->private_data;  	struct ubi_device *ubi = desc->vol->ubi; - -	return ubi_sync(ubi->ubi_num); +	struct inode *inode = file->f_path.dentry->d_inode; +	int err; +	mutex_lock(&inode->i_mutex); +	err = ubi_sync(ubi->ubi_num); +	mutex_unlock(&inode->i_mutex); +	return err;  } diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c index c0f0ac7c1cdb..f3c6060c96b8 100644 --- a/drivers/staging/pohmelfs/inode.c +++ b/drivers/staging/pohmelfs/inode.c @@ -887,11 +887,16 @@ static struct inode *pohmelfs_alloc_inode(struct super_block *sb)  /*   * We want fsync() to work on POHMELFS.   */ -static int pohmelfs_fsync(struct file *file, int datasync) +static int pohmelfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct inode *inode = file->f_mapping->host; - -	return sync_inode_metadata(inode, 1); +	int err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (!err) { +		mutex_lock(&inode->i_mutex); +		err = sync_inode_metadata(inode, 1); +		mutex_unlock(&inode->i_mutex); +	} +	return err;  }  ssize_t pohmelfs_write(struct file *file, const char __user *buf, diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 271ef94668e7..978e6a101bf2 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -795,12 +795,14 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)  }  static int -printer_fsync(struct file *fd, int datasync) +printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)  {  	struct printer_dev	*dev = fd->private_data; +	struct inode *inode = fd->f_path.dentry->d_inode;  	unsigned long		flags;  	int			tx_list_empty; +	mutex_lock(&inode->i_mutex);  	spin_lock_irqsave(&dev->lock, flags);  	tx_list_empty = (likely(list_empty(&dev->tx_reqs)));  	spin_unlock_irqrestore(&dev->lock, flags); @@ -810,6 +812,7 @@ printer_fsync(struct file *fd, int datasync)  		wait_event_interruptible(dev->tx_flush_wait,  				(likely(list_empty(&dev->tx_reqs_active))));  	} +	mutex_unlock(&inode->i_mutex);  	return 0;  } diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 804000183c5e..32814e8800e0 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -66,19 +66,26 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma,  	return 0;  } -int fb_deferred_io_fsync(struct file *file, int datasync) +int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct fb_info *info = file->private_data; +	struct inode *inode = file->f_path.dentry->d_inode; +	int err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (err) +		return err;  	/* Skip if deferred io is compiled-in but disabled on this fbdev */  	if (!info->fbdefio)  		return 0; +	mutex_lock(&inode->i_mutex);  	/* Kill off the delayed work */  	cancel_delayed_work_sync(&info->deferred_work);  	/* Run it immediately */ -	return schedule_delayed_work(&info->deferred_work, 0); +	err = schedule_delayed_work(&info->deferred_work, 0); +	mutex_unlock(&inode->i_mutex); +	return err;  }  EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index 4014160903a9..46ce357ca1ab 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -70,7 +70,8 @@ ssize_t v9fs_file_readn(struct file *, char *, char __user *, u32, u64);  ssize_t v9fs_fid_readn(struct p9_fid *, char *, char __user *, u32, u64);  void v9fs_blank_wstat(struct p9_wstat *wstat);  int v9fs_vfs_setattr_dotl(struct dentry *, struct iattr *); -int v9fs_file_fsync_dotl(struct file *filp, int datasync); +int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end, +			 int datasync);  ssize_t v9fs_file_write_internal(struct inode *, struct p9_fid *,  				 const char __user *, size_t, loff_t *, int);  int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode); diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index ffed55817f0c..3c173fcc2c5a 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -519,32 +519,50 @@ out:  } -static int v9fs_file_fsync(struct file *filp, int datasync) +static int v9fs_file_fsync(struct file *filp, loff_t start, loff_t end, +			   int datasync)  {  	struct p9_fid *fid; +	struct inode *inode = filp->f_mapping->host;  	struct p9_wstat wstat;  	int retval; +	retval = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (retval) +		return retval; + +	mutex_lock(&inode->i_mutex);  	P9_DPRINTK(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);  	fid = filp->private_data;  	v9fs_blank_wstat(&wstat);  	retval = p9_client_wstat(fid, &wstat); +	mutex_unlock(&inode->i_mutex); +  	return retval;  } -int v9fs_file_fsync_dotl(struct file *filp, int datasync) +int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end, +			 int datasync)  {  	struct p9_fid *fid; +	struct inode *inode = filp->f_mapping->host;  	int retval; +	retval = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (retval) +		return retval; + +	mutex_lock(&inode->i_mutex);  	P9_DPRINTK(P9_DEBUG_VFS, "v9fs_file_fsync_dotl: filp %p datasync %x\n",  			filp, datasync);  	fid = filp->private_data;  	retval = p9_client_fsync(fid, datasync); +	mutex_unlock(&inode->i_mutex); +  	return retval;  } diff --git a/fs/affs/affs.h b/fs/affs/affs.h index 0e95f73a7023..c2b9c79eb64e 100644 --- a/fs/affs/affs.h +++ b/fs/affs/affs.h @@ -182,7 +182,7 @@ extern int			 affs_add_entry(struct inode *dir, struct inode *inode, struct dent  void		affs_free_prealloc(struct inode *inode);  extern void	affs_truncate(struct inode *); -int		affs_file_fsync(struct file *, int); +int		affs_file_fsync(struct file *, loff_t, loff_t, int);  /* dir.c */ diff --git a/fs/affs/file.c b/fs/affs/file.c index acf321b70fcd..2f4c935cb327 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -923,14 +923,20 @@ affs_truncate(struct inode *inode)  	affs_free_prealloc(inode);  } -int affs_file_fsync(struct file *filp, int datasync) +int affs_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)  {  	struct inode *inode = filp->f_mapping->host;  	int ret, err; +	err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (err) +		return err; + +	mutex_lock(&inode->i_mutex);  	ret = write_inode_now(inode, 0);  	err = sync_blockdev(inode->i_sb->s_bdev);  	if (!ret)  		ret = err; +	mutex_unlock(&inode->i_mutex);  	return ret;  } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index f396d337b817..d2b0888126d4 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -750,7 +750,7 @@ extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *);  extern ssize_t afs_file_write(struct kiocb *, const struct iovec *,  			      unsigned long, loff_t);  extern int afs_writeback_all(struct afs_vnode *); -extern int afs_fsync(struct file *, int); +extern int afs_fsync(struct file *, loff_t, loff_t, int);  /*****************************************************************************/ diff --git a/fs/afs/write.c b/fs/afs/write.c index b806285ff853..9aa52d93c73c 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -681,9 +681,10 @@ int afs_writeback_all(struct afs_vnode *vnode)   * - the return status from this call provides a reliable indication of   *   whether any write errors occurred for this process.   */ -int afs_fsync(struct file *file, int datasync) +int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct dentry *dentry = file->f_path.dentry; +	struct inode *inode = file->f_mapping->host;  	struct afs_writeback *wb, *xwb;  	struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode);  	int ret; @@ -692,12 +693,19 @@ int afs_fsync(struct file *file, int datasync)  	       vnode->fid.vid, vnode->fid.vnode, dentry->d_name.name,  	       datasync); +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; +	mutex_lock(&inode->i_mutex); +  	/* use a writeback record as a marker in the queue - when this reaches  	 * the front of the queue, all the outstanding writes are either  	 * completed or rejected */  	wb = kzalloc(sizeof(*wb), GFP_KERNEL); -	if (!wb) -		return -ENOMEM; +	if (!wb) { +		ret = -ENOMEM; +		goto out; +	}  	wb->vnode = vnode;  	wb->first = 0;  	wb->last = -1; @@ -720,7 +728,7 @@ int afs_fsync(struct file *file, int datasync)  	if (ret < 0) {  		afs_put_writeback(wb);  		_leave(" = %d [wb]", ret); -		return ret; +		goto out;  	}  	/* wait for the preceding writes to actually complete */ @@ -729,6 +737,8 @@ int afs_fsync(struct file *file, int datasync)  				       vnode->writebacks.next == &wb->link);  	afs_put_writeback(wb);  	_leave(" = %d", ret); +out: +	mutex_unlock(&inode->i_mutex);  	return ret;  } diff --git a/fs/bad_inode.c b/fs/bad_inode.c index f024d8aaddef..9205cf25f1c6 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -87,7 +87,8 @@ static int bad_file_release(struct inode *inode, struct file *filp)  	return -EIO;  } -static int bad_file_fsync(struct file *file, int datasync) +static int bad_file_fsync(struct file *file, loff_t start, loff_t end, +			  int datasync)  {  	return -EIO;  } diff --git a/fs/block_dev.c b/fs/block_dev.c index 966617a422d9..9fb0b15331d3 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -378,7 +378,7 @@ out:  	return retval;  } -int blkdev_fsync(struct file *filp, int datasync) +int blkdev_fsync(struct file *filp, loff_t start, loff_t end, int datasync)  {  	struct inode *bd_inode = filp->f_mapping->host;  	struct block_device *bdev = I_BDEV(bd_inode); @@ -389,14 +389,10 @@ int blkdev_fsync(struct file *filp, int datasync)  	 * i_mutex and doing so causes performance issues with concurrent  	 * O_SYNC writers to a block device.  	 */ -	mutex_unlock(&bd_inode->i_mutex); -  	error = blkdev_issue_flush(bdev, GFP_KERNEL, NULL);  	if (error == -EOPNOTSUPP)  		error = 0; -	mutex_lock(&bd_inode->i_mutex); -  	return error;  }  EXPORT_SYMBOL(blkdev_fsync); diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index f1ff62bff1b3..82be74efbb26 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2605,7 +2605,7 @@ int btrfs_defrag_file(struct inode *inode, struct file *file,  int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,  			   struct inode *inode);  int btrfs_run_defrag_inodes(struct btrfs_fs_info *fs_info); -int btrfs_sync_file(struct file *file, int datasync); +int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync);  int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,  			    int skip_pinned);  extern const struct file_operations btrfs_file_operations; diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index bd4d061c6e4d..59cbdb120ad0 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1452,7 +1452,7 @@ int btrfs_release_file(struct inode *inode, struct file *filp)   * important optimization for directories because holding the mutex prevents   * new operations on the dir while we write to disk.   */ -int btrfs_sync_file(struct file *file, int datasync) +int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct dentry *dentry = file->f_path.dentry;  	struct inode *inode = dentry->d_inode; @@ -1462,9 +1462,13 @@ int btrfs_sync_file(struct file *file, int datasync)  	trace_btrfs_sync_file(file, datasync); +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; +	mutex_lock(&inode->i_mutex); +  	/* we wait first, since the writeback may change the inode */  	root->log_batch++; -	/* the VFS called filemap_fdatawrite for us */  	btrfs_wait_ordered_range(inode, 0, (u64)-1);  	root->log_batch++; @@ -1472,8 +1476,10 @@ int btrfs_sync_file(struct file *file, int datasync)  	 * check the transaction that last modified this inode  	 * and see if its already been committed  	 */ -	if (!BTRFS_I(inode)->last_trans) +	if (!BTRFS_I(inode)->last_trans) { +		mutex_unlock(&inode->i_mutex);  		goto out; +	}  	/*  	 * if the last transaction that changed this file was before @@ -1484,6 +1490,7 @@ int btrfs_sync_file(struct file *file, int datasync)  	if (BTRFS_I(inode)->last_trans <=  	    root->fs_info->last_trans_committed) {  		BTRFS_I(inode)->last_trans = 0; +		mutex_unlock(&inode->i_mutex);  		goto out;  	} @@ -1496,12 +1503,15 @@ int btrfs_sync_file(struct file *file, int datasync)  	trans = btrfs_start_transaction(root, 0);  	if (IS_ERR(trans)) {  		ret = PTR_ERR(trans); +		mutex_unlock(&inode->i_mutex);  		goto out;  	}  	ret = btrfs_log_dentry_safe(trans, root, dentry); -	if (ret < 0) +	if (ret < 0) { +		mutex_unlock(&inode->i_mutex);  		goto out; +	}  	/* we've logged all the items and now have a consistent  	 * version of the file in the log.  It is possible that @@ -1513,7 +1523,7 @@ int btrfs_sync_file(struct file *file, int datasync)  	 * file again, but that will end up using the synchronization  	 * inside btrfs_sync_log to keep things safe.  	 */ -	mutex_unlock(&dentry->d_inode->i_mutex); +	mutex_unlock(&inode->i_mutex);  	if (ret != BTRFS_NO_LOG_SYNC) {  		if (ret > 0) { @@ -1528,7 +1538,6 @@ int btrfs_sync_file(struct file *file, int datasync)  	} else {  		ret = btrfs_end_transaction(trans, root);  	} -	mutex_lock(&dentry->d_inode->i_mutex);  out:  	return ret > 0 ? -EIO : ret;  } diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index f605753c8fe9..8d74ad7ba556 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1811,7 +1811,7 @@ out:  	spin_unlock(&ci->i_unsafe_lock);  } -int ceph_fsync(struct file *file, int datasync) +int ceph_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct inode *inode = file->f_mapping->host;  	struct ceph_inode_info *ci = ceph_inode(inode); @@ -1822,9 +1822,10 @@ int ceph_fsync(struct file *file, int datasync)  	dout("fsync %p%s\n", inode, datasync ? " datasync" : "");  	sync_write_wait(inode); -	ret = filemap_write_and_wait(inode->i_mapping); +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);  	if (ret < 0)  		return ret; +	mutex_lock(&inode->i_mutex);  	dirty = try_flush_caps(inode, NULL, &flush_tid);  	dout("fsync dirty caps are %s\n", ceph_cap_string(dirty)); @@ -1841,6 +1842,7 @@ int ceph_fsync(struct file *file, int datasync)  	}  	dout("fsync %p%s done\n", inode, datasync ? " datasync" : ""); +	mutex_unlock(&inode->i_mutex);  	return ret;  } diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 0972b457a03f..1065ac779840 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -1118,7 +1118,8 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size,   * an fsync() on a dir will wait for any uncommitted directory   * operations to commit.   */ -static int ceph_dir_fsync(struct file *file, int datasync) +static int ceph_dir_fsync(struct file *file, loff_t start, loff_t end, +			  int datasync)  {  	struct inode *inode = file->f_path.dentry->d_inode;  	struct ceph_inode_info *ci = ceph_inode(inode); @@ -1128,6 +1129,11 @@ static int ceph_dir_fsync(struct file *file, int datasync)  	int ret = 0;  	dout("dir_fsync %p\n", inode); +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; +	mutex_lock(&inode->i_mutex); +  	spin_lock(&ci->i_unsafe_lock);  	if (list_empty(head))  		goto out; @@ -1161,6 +1167,8 @@ static int ceph_dir_fsync(struct file *file, int datasync)  	} while (req->r_tid < last_tid);  out:  	spin_unlock(&ci->i_unsafe_lock); +	mutex_unlock(&inode->i_mutex); +  	return ret;  } diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 56c41ef47cad..30446b144e3d 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -728,7 +728,8 @@ extern void ceph_put_cap(struct ceph_mds_client *mdsc,  extern void ceph_queue_caps_release(struct inode *inode);  extern int ceph_write_inode(struct inode *inode, struct writeback_control *wbc); -extern int ceph_fsync(struct file *file, int datasync); +extern int ceph_fsync(struct file *file, loff_t start, loff_t end, +		      int datasync);  extern void ceph_kick_flushing_caps(struct ceph_mds_client *mdsc,  				    struct ceph_mds_session *session);  extern struct ceph_cap *ceph_get_cap_for_mds(struct ceph_inode_info *ci, diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 036ca83e5f46..fbd050c8d52a 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -91,8 +91,8 @@ extern ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,  extern ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,  				  unsigned long nr_segs, loff_t pos);  extern int cifs_lock(struct file *, int, struct file_lock *); -extern int cifs_fsync(struct file *, int); -extern int cifs_strict_fsync(struct file *, int); +extern int cifs_fsync(struct file *, loff_t, loff_t, int); +extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int);  extern int cifs_flush(struct file *, fl_owner_t id);  extern int cifs_file_mmap(struct file * , struct vm_area_struct *);  extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index bb71471a4d9d..cef584451113 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1401,7 +1401,8 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,  	return rc;  } -int cifs_strict_fsync(struct file *file, int datasync) +int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, +		      int datasync)  {  	int xid;  	int rc = 0; @@ -1410,6 +1411,11 @@ int cifs_strict_fsync(struct file *file, int datasync)  	struct inode *inode = file->f_path.dentry->d_inode;  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +	rc = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (rc) +		return rc; +	mutex_lock(&inode->i_mutex); +  	xid = GetXid();  	cFYI(1, "Sync file - name: %s datasync: 0x%x", @@ -1428,16 +1434,23 @@ int cifs_strict_fsync(struct file *file, int datasync)  		rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);  	FreeXid(xid); +	mutex_unlock(&inode->i_mutex);  	return rc;  } -int cifs_fsync(struct file *file, int datasync) +int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	int xid;  	int rc = 0;  	struct cifs_tcon *tcon;  	struct cifsFileInfo *smbfile = file->private_data;  	struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); +	struct inode *inode = file->f_mapping->host; + +	rc = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (rc) +		return rc; +	mutex_lock(&inode->i_mutex);  	xid = GetXid(); @@ -1449,6 +1462,7 @@ int cifs_fsync(struct file *file, int datasync)  		rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);  	FreeXid(xid); +	mutex_unlock(&inode->i_mutex);  	return rc;  } diff --git a/fs/coda/coda_int.h b/fs/coda/coda_int.h index 6b443ff43a19..b7143cf783ac 100644 --- a/fs/coda/coda_int.h +++ b/fs/coda/coda_int.h @@ -11,7 +11,7 @@ extern int coda_fake_statfs;  void coda_destroy_inodecache(void);  int coda_init_inodecache(void); -int coda_fsync(struct file *coda_file, int datasync); +int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync);  void coda_sysctl_init(void);  void coda_sysctl_clean(void); diff --git a/fs/coda/file.c b/fs/coda/file.c index 0433057be330..8edd404e6419 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -199,7 +199,7 @@ int coda_release(struct inode *coda_inode, struct file *coda_file)  	return 0;  } -int coda_fsync(struct file *coda_file, int datasync) +int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync)  {  	struct file *host_file;  	struct inode *coda_inode = coda_file->f_path.dentry->d_inode; @@ -210,6 +210,11 @@ int coda_fsync(struct file *coda_file, int datasync)  	      S_ISLNK(coda_inode->i_mode)))  		return -EINVAL; +	err = filemap_write_and_wait_range(coda_inode->i_mapping, start, end); +	if (err) +		return err; +	mutex_lock(&coda_inode->i_mutex); +  	cfi = CODA_FTOC(coda_file);  	BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);  	host_file = cfi->cfi_container; @@ -217,6 +222,7 @@ int coda_fsync(struct file *coda_file, int datasync)  	err = vfs_fsync(host_file, datasync);  	if (!err && !datasync)  		err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode)); +	mutex_unlock(&coda_inode->i_mutex);  	return err;  } diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 4ec9eb00a241..c6ac98cf9baa 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -270,14 +270,15 @@ static int ecryptfs_release(struct inode *inode, struct file *file)  }  static int -ecryptfs_fsync(struct file *file, int datasync) +ecryptfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	int rc = 0; -	rc = generic_file_fsync(file, datasync); +	rc = generic_file_fsync(file, start, end, datasync);  	if (rc)  		goto out; -	rc = vfs_fsync(ecryptfs_file_to_lower(file), datasync); +	rc = vfs_fsync_range(ecryptfs_file_to_lower(file), start, end, +			     datasync);  out:  	return rc;  } diff --git a/fs/exofs/file.c b/fs/exofs/file.c index 45ca323d8363..491c6c078e7f 100644 --- a/fs/exofs/file.c +++ b/fs/exofs/file.c @@ -42,11 +42,19 @@ static int exofs_release_file(struct inode *inode, struct file *filp)   *   Note, in exofs all metadata is written as part of inode, regardless.   *   The writeout is synchronous   */ -static int exofs_file_fsync(struct file *filp, int datasync) +static int exofs_file_fsync(struct file *filp, loff_t start, loff_t end, +			    int datasync)  { +	struct inode *inode = filp->f_mapping->host;  	int ret; +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; + +	mutex_lock(&inode->i_mutex);  	ret = sync_inode_metadata(filp->f_mapping->host, 1); +	mutex_unlock(&inode->i_mutex);  	return ret;  } diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 645be9e7ee47..af9fc89b1b2d 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -150,7 +150,8 @@ extern void ext2_write_super (struct super_block *);  extern const struct file_operations ext2_dir_operations;  /* file.c */ -extern int ext2_fsync(struct file *file, int datasync); +extern int ext2_fsync(struct file *file, loff_t start, loff_t end, +		      int datasync);  extern const struct inode_operations ext2_file_inode_operations;  extern const struct file_operations ext2_file_operations;  extern const struct file_operations ext2_xip_file_operations; diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 49eec9456c5b..82e06321de35 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -40,13 +40,13 @@ static int ext2_release_file (struct inode * inode, struct file * filp)  	return 0;  } -int ext2_fsync(struct file *file, int datasync) +int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	int ret;  	struct super_block *sb = file->f_mapping->host->i_sb;  	struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; -	ret = generic_file_fsync(file, datasync); +	ret = generic_file_fsync(file, start, end, datasync);  	if (ret == -EIO || test_and_clear_bit(AS_EIO, &mapping->flags)) {  		/* We don't really know where the IO error happened... */  		ext2_error(sb, __func__, diff --git a/fs/ext3/fsync.c b/fs/ext3/fsync.c index 09b13bb34c94..0bcf63adb80a 100644 --- a/fs/ext3/fsync.c +++ b/fs/ext3/fsync.c @@ -43,7 +43,7 @@   * inode to disk.   */ -int ext3_sync_file(struct file *file, int datasync) +int ext3_sync_file(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct inode *inode = file->f_mapping->host;  	struct ext3_inode_info *ei = EXT3_I(inode); @@ -54,6 +54,17 @@ int ext3_sync_file(struct file *file, int datasync)  	if (inode->i_sb->s_flags & MS_RDONLY)  		return 0; +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; + +	/* +	 * Taking the mutex here just to keep consistent with how fsync was +	 * called previously, however it looks like we don't need to take +	 * i_mutex at all. +	 */ +	mutex_lock(&inode->i_mutex); +  	J_ASSERT(ext3_journal_current_handle() == NULL);  	/* @@ -70,8 +81,10 @@ int ext3_sync_file(struct file *file, int datasync)  	 *  (they were dirtied by commit).  But that's OK - the blocks are  	 *  safe in-journal, which is all fsync() needs to ensure.  	 */ -	if (ext3_should_journal_data(inode)) +	if (ext3_should_journal_data(inode)) { +		mutex_unlock(&inode->i_mutex);  		return ext3_force_commit(inode->i_sb); +	}  	if (datasync)  		commit_tid = atomic_read(&ei->i_datasync_tid); @@ -91,5 +104,6 @@ int ext3_sync_file(struct file *file, int datasync)  	 */  	if (needs_barrier)  		blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); +	mutex_unlock(&inode->i_mutex);  	return ret;  } diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 1921392cd708..fa44df879711 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1758,7 +1758,7 @@ extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,  extern void ext4_htree_free_dir_info(struct dir_private_info *p);  /* fsync.c */ -extern int ext4_sync_file(struct file *, int); +extern int ext4_sync_file(struct file *, loff_t, loff_t, int);  extern int ext4_flush_completed_IO(struct inode *);  /* hash.c */ diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c index ce66d2fe826c..da3bed3e0c29 100644 --- a/fs/ext4/fsync.c +++ b/fs/ext4/fsync.c @@ -151,6 +151,32 @@ static int ext4_sync_parent(struct inode *inode)  	return ret;  } +/** + * __sync_file - generic_file_fsync without the locking and filemap_write + * @inode:	inode to sync + * @datasync:	only sync essential metadata if true + * + * This is just generic_file_fsync without the locking.  This is needed for + * nojournal mode to make sure this inodes data/metadata makes it to disk + * properly.  The i_mutex should be held already. + */ +static int __sync_inode(struct inode *inode, int datasync) +{ +	int err; +	int ret; + +	ret = sync_mapping_buffers(inode->i_mapping); +	if (!(inode->i_state & I_DIRTY)) +		return ret; +	if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) +		return ret; + +	err = sync_inode_metadata(inode, 1); +	if (ret == 0) +		ret = err; +	return ret; +} +  /*   * akpm: A new design for ext4_sync_file().   * @@ -165,7 +191,7 @@ static int ext4_sync_parent(struct inode *inode)   * i_mutex lock is held when entering and exiting this function   */ -int ext4_sync_file(struct file *file, int datasync) +int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct inode *inode = file->f_mapping->host;  	struct ext4_inode_info *ei = EXT4_I(inode); @@ -178,15 +204,20 @@ int ext4_sync_file(struct file *file, int datasync)  	trace_ext4_sync_file_enter(file, datasync); +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; +	mutex_lock(&inode->i_mutex); +  	if (inode->i_sb->s_flags & MS_RDONLY) -		return 0; +		goto out;  	ret = ext4_flush_completed_IO(inode);  	if (ret < 0)  		goto out;  	if (!journal) { -		ret = generic_file_fsync(file, datasync); +		ret = __sync_inode(inode, datasync);  		if (!ret && !list_empty(&inode->i_dentry))  			ret = ext4_sync_parent(inode);  		goto out; @@ -220,6 +251,7 @@ int ext4_sync_file(struct file *file, int datasync)  	if (needs_barrier)  		blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);   out: +	mutex_unlock(&inode->i_mutex);  	trace_ext4_sync_file_exit(inode, ret);  	return ret;  } diff --git a/fs/fat/fat.h b/fs/fat/fat.h index a975b4147e91..a5d3853822e0 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -310,7 +310,8 @@ extern int fat_setattr(struct dentry * dentry, struct iattr * attr);  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, int datasync); +extern int fat_file_fsync(struct file *file, loff_t start, loff_t end, +			  int datasync);  /* fat/inode.c */  extern void fat_attach(struct inode *inode, loff_t i_pos); diff --git a/fs/fat/file.c b/fs/fat/file.c index e1587c54d3c1..c118acf16e43 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -149,12 +149,12 @@ static int fat_file_release(struct inode *inode, struct file *filp)  	return 0;  } -int fat_file_fsync(struct file *filp, int datasync) +int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)  {  	struct inode *inode = filp->f_mapping->host;  	int res, err; -	res = generic_file_fsync(filp, datasync); +	res = generic_file_fsync(filp, start, end, datasync);  	err = sync_mapping_buffers(MSDOS_SB(inode->i_sb)->fat_inode->i_mapping);  	return res ? res : err; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 02063dde2728..9f63e493a9b6 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1176,9 +1176,10 @@ static int fuse_dir_release(struct inode *inode, struct file *file)  	return 0;  } -static int fuse_dir_fsync(struct file *file, int datasync) +static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end, +			  int datasync)  { -	return fuse_fsync_common(file, datasync, 1); +	return fuse_fsync_common(file, start, end, datasync, 1);  }  static bool update_mtime(unsigned ivalid) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 73b89df20851..7bb685cdd00c 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -400,7 +400,8 @@ static void fuse_sync_writes(struct inode *inode)  	fuse_release_nowrite(inode);  } -int fuse_fsync_common(struct file *file, int datasync, int isdir) +int fuse_fsync_common(struct file *file, loff_t start, loff_t end, +		      int datasync, int isdir)  {  	struct inode *inode = file->f_mapping->host;  	struct fuse_conn *fc = get_fuse_conn(inode); @@ -412,9 +413,15 @@ int fuse_fsync_common(struct file *file, int datasync, int isdir)  	if (is_bad_inode(inode))  		return -EIO; +	err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (err) +		return err; +  	if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))  		return 0; +	mutex_lock(&inode->i_mutex); +  	/*  	 * Start writeback against all dirty pages of the inode, then  	 * wait for all outstanding writes, before sending the FSYNC @@ -422,13 +429,15 @@ int fuse_fsync_common(struct file *file, int datasync, int isdir)  	 */  	err = write_inode_now(inode, 0);  	if (err) -		return err; +		goto out;  	fuse_sync_writes(inode);  	req = fuse_get_req(fc); -	if (IS_ERR(req)) -		return PTR_ERR(req); +	if (IS_ERR(req)) { +		err = PTR_ERR(req); +		goto out; +	}  	memset(&inarg, 0, sizeof(inarg));  	inarg.fh = ff->fh; @@ -448,12 +457,15 @@ int fuse_fsync_common(struct file *file, int datasync, int isdir)  			fc->no_fsync = 1;  		err = 0;  	} +out: +	mutex_unlock(&inode->i_mutex);  	return err;  } -static int fuse_fsync(struct file *file, int datasync) +static int fuse_fsync(struct file *file, loff_t start, loff_t end, +		      int datasync)  { -	return fuse_fsync_common(file, datasync, 0); +	return fuse_fsync_common(file, start, end, datasync, 0);  }  void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos, diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index b788becada76..c6aa2d4b8517 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -589,7 +589,8 @@ void fuse_release_common(struct file *file, int opcode);  /**   * Send FSYNC or FSYNCDIR request   */ -int fuse_fsync_common(struct file *file, int datasync, int isdir); +int fuse_fsync_common(struct file *file, loff_t start, loff_t end, +		      int datasync, int isdir);  /**   * Notify poll wakeup diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 89c39e53760d..f82cb5e1cb6b 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -544,7 +544,9 @@ static int gfs2_close(struct inode *inode, struct file *file)  /**   * gfs2_fsync - sync the dirty data for a file (across the cluster) - * @file: the file that points to the dentry (we ignore this) + * @file: the file that points to the dentry + * @start: the start position in the file to sync + * @end: the end position in the file to sync   * @datasync: set if we can ignore timestamp changes   *   * The VFS will flush data for us. We only need to worry @@ -553,23 +555,32 @@ static int gfs2_close(struct inode *inode, struct file *file)   * Returns: errno   */ -static int gfs2_fsync(struct file *file, int datasync) +static int gfs2_fsync(struct file *file, loff_t start, loff_t end, +		      int datasync)  {  	struct inode *inode = file->f_mapping->host;  	int sync_state = inode->i_state & (I_DIRTY_SYNC|I_DIRTY_DATASYNC);  	struct gfs2_inode *ip = GFS2_I(inode);  	int ret; +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; +	mutex_lock(&inode->i_mutex); +  	if (datasync)  		sync_state &= ~I_DIRTY_SYNC;  	if (sync_state) {  		ret = sync_inode_metadata(inode, 1); -		if (ret) +		if (ret) { +			mutex_unlock(&inode->i_mutex);  			return ret; +		}  		gfs2_ail_flush(ip->i_gl);  	} +	mutex_unlock(&inode->i_mutex);  	return 0;  } diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 5e7c3f309617..96a1b625fc74 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -627,12 +627,18 @@ int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr)  	return 0;  } -static int hfs_file_fsync(struct file *filp, int datasync) +static int hfs_file_fsync(struct file *filp, loff_t start, loff_t end, +			  int datasync)  {  	struct inode *inode = filp->f_mapping->host;  	struct super_block * sb;  	int ret, err; +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; +	mutex_lock(&inode->i_mutex); +  	/* sync the inode to buffers */  	ret = write_inode_now(inode, 0); @@ -649,6 +655,7 @@ static int hfs_file_fsync(struct file *filp, int datasync)  	err = sync_blockdev(sb->s_bdev);  	if (!ret)  		ret = err; +	mutex_unlock(&inode->i_mutex);  	return ret;  } diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index d6857523336d..38184e360932 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -392,7 +392,8 @@ int hfsplus_cat_read_inode(struct inode *, struct hfs_find_data *);  int hfsplus_cat_write_inode(struct inode *);  struct inode *hfsplus_new_inode(struct super_block *, int);  void hfsplus_delete_inode(struct inode *); -int hfsplus_file_fsync(struct file *file, int datasync); +int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end, +		       int datasync);  /* ioctl.c */  long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 5b1cb98741cc..30486e01d003 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -308,13 +308,19 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr)  	return 0;  } -int hfsplus_file_fsync(struct file *file, int datasync) +int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end, +		       int datasync)  {  	struct inode *inode = file->f_mapping->host;  	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);  	struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);  	int error = 0, error2; +	error = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (error) +		return error; +	mutex_lock(&inode->i_mutex); +  	/*  	 * Sync inode metadata into the catalog and extent trees.  	 */ @@ -342,6 +348,8 @@ int hfsplus_file_fsync(struct file *file, int datasync)  	if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags))  		blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); +	mutex_unlock(&inode->i_mutex); +  	return error;  } diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 6e449c599b9d..0d22afdd4611 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -362,9 +362,20 @@ retry:  	return 0;  } -int hostfs_fsync(struct file *file, int datasync) +int hostfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)  { -	return fsync_file(HOSTFS_I(file->f_mapping->host)->fd, datasync); +	struct inode *inode = file->f_mapping->host; +	int ret; + +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; + +	mutex_lock(&inode->i_mutex); +	ret = fsync_file(HOSTFS_I(inode)->fd, datasync); +	mutex_unlock(&inode->i_mutex); + +	return ret;  }  static const struct file_operations hostfs_file_fops = { diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index 89c500ee5213..89d2a5803ae3 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -18,9 +18,14 @@ static int hpfs_file_release(struct inode *inode, struct file *file)  	return 0;  } -int hpfs_file_fsync(struct file *file, int datasync) +int hpfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct inode *inode = file->f_mapping->host; +	int ret; + +	ret = filemap_write_and_wait_range(file->f_mapping, start, end); +	if (ret) +		return ret;  	return sync_blockdev(inode->i_sb->s_bdev);  } diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index dd552f862c8f..331b5e234ef3 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -258,7 +258,7 @@ void hpfs_set_ea(struct inode *, struct fnode *, const char *,  /* file.c */ -int hpfs_file_fsync(struct file *, int); +int hpfs_file_fsync(struct file *, loff_t, loff_t, int);  extern const struct file_operations hpfs_file_ops;  extern const struct inode_operations hpfs_file_iops;  extern const struct address_space_operations hpfs_aops; diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c index 85c098a499f3..8635be5ffd97 100644 --- a/fs/hppfs/hppfs.c +++ b/fs/hppfs/hppfs.c @@ -573,9 +573,10 @@ static int hppfs_readdir(struct file *file, void *ent, filldir_t filldir)  	return err;  } -static int hppfs_fsync(struct file *file, int datasync) +static int hppfs_fsync(struct file *file, loff_t start, loff_t end, +		       int datasync)  { -	return 0; +	return filemap_write_and_wait_range(file->f_mapping, start, end);  }  static const struct file_operations hppfs_dir_fops = { diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 1c0a08d711aa..3989f7e09f7f 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -27,13 +27,20 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,  			struct page **pagep, void **fsdata);  static int jffs2_readpage (struct file *filp, struct page *pg); -int jffs2_fsync(struct file *filp, int datasync) +int jffs2_fsync(struct file *filp, loff_t start, loff_t end, int datasync)  {  	struct inode *inode = filp->f_mapping->host;  	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +	int ret; + +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; +	mutex_lock(&inode->i_mutex);  	/* Trigger GC to flush any pending writes for this inode */  	jffs2_flush_wbuf_gc(c, inode->i_ino); +	mutex_unlock(&inode->i_mutex);  	return 0;  } diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 65c6c43ca482..9c252835e8e5 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -158,7 +158,7 @@ extern const struct inode_operations jffs2_dir_inode_operations;  extern const struct file_operations jffs2_file_operations;  extern const struct inode_operations jffs2_file_inode_operations;  extern const struct address_space_operations jffs2_file_address_operations; -int jffs2_fsync(struct file *, int); +int jffs2_fsync(struct file *, loff_t, loff_t, int);  int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);  /* ioctl.c */ diff --git a/fs/jfs/file.c b/fs/jfs/file.c index 9f32315acef1..7527855b5cc6 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -28,19 +28,26 @@  #include "jfs_acl.h"  #include "jfs_debug.h" -int jfs_fsync(struct file *file, int datasync) +int jfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct inode *inode = file->f_mapping->host;  	int rc = 0; +	rc = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (rc) +		return rc; + +	mutex_lock(&inode->i_mutex);  	if (!(inode->i_state & I_DIRTY) ||  	    (datasync && !(inode->i_state & I_DIRTY_DATASYNC))) {  		/* Make sure committed changes hit the disk */  		jfs_flush_journal(JFS_SBI(inode->i_sb)->log, 1); +		mutex_unlock(&inode->i_mutex);  		return rc;  	}  	rc |= jfs_commit_inode(inode, 1); +	mutex_unlock(&inode->i_mutex);  	return rc ? -EIO : 0;  } diff --git a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h index ec2fb8b945fc..9271cfe4a149 100644 --- a/fs/jfs/jfs_inode.h +++ b/fs/jfs/jfs_inode.h @@ -21,7 +21,7 @@  struct fid;  extern struct inode *ialloc(struct inode *, umode_t); -extern int jfs_fsync(struct file *, int); +extern int jfs_fsync(struct file *, loff_t, loff_t, int);  extern long jfs_ioctl(struct file *, unsigned int, unsigned long);  extern long jfs_compat_ioctl(struct file *, unsigned int, unsigned long);  extern struct inode *jfs_iget(struct super_block *, unsigned long); diff --git a/fs/libfs.c b/fs/libfs.c index bd50b11f92da..8f2271a5df53 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -905,21 +905,29 @@ EXPORT_SYMBOL_GPL(generic_fh_to_parent);   * filesystems which track all non-inode metadata in the buffers list   * hanging off the address_space structure.   */ -int generic_file_fsync(struct file *file, int datasync) +int generic_file_fsync(struct file *file, loff_t start, loff_t end, +		       int datasync)  {  	struct inode *inode = file->f_mapping->host;  	int err;  	int ret; +	err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (err) +		return err; + +	mutex_lock(&inode->i_mutex);  	ret = sync_mapping_buffers(inode->i_mapping);  	if (!(inode->i_state & I_DIRTY)) -		return ret; +		goto out;  	if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) -		return ret; +		goto out;  	err = sync_inode_metadata(inode, 1);  	if (ret == 0)  		ret = err; +out: +	mutex_unlock(&inode->i_mutex);  	return ret;  }  EXPORT_SYMBOL(generic_file_fsync); @@ -956,7 +964,7 @@ EXPORT_SYMBOL(generic_check_addressable);  /*   * No-op implementation of ->fsync for in-memory filesystems.   */ -int noop_fsync(struct file *file, int datasync) +int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	return 0;  } diff --git a/fs/logfs/file.c b/fs/logfs/file.c index c2ad7028def4..b548c87a86f1 100644 --- a/fs/logfs/file.c +++ b/fs/logfs/file.c @@ -219,11 +219,20 @@ long logfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  	}  } -int logfs_fsync(struct file *file, int datasync) +int logfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct super_block *sb = file->f_mapping->host->i_sb; +	struct inode *inode = file->f_mapping->host; +	int ret; + +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; +	mutex_lock(&inode->i_mutex);  	logfs_write_anchor(sb); +	mutex_unlock(&inode->i_mutex); +  	return 0;  } diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index 57afd4a6fabb..f22d108bfa5d 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -506,7 +506,7 @@ extern const struct file_operations logfs_reg_fops;  extern const struct address_space_operations logfs_reg_aops;  int logfs_readpage(struct file *file, struct page *page);  long logfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -int logfs_fsync(struct file *file, int datasync); +int logfs_fsync(struct file *file, loff_t start, loff_t end, int datasync);  /* gc.c */  u32 get_best_cand(struct super_block *sb, struct candidate_list *list, u32 *ec); diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 0ed65e0c3dfe..64a326418aa2 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -20,9 +20,9 @@  #include "ncp_fs.h" -static int ncp_fsync(struct file *file, int datasync) +static int ncp_fsync(struct file *file, loff_t start, loff_t end, int datasync)  { -	return 0; +	return filemap_write_and_wait_range(file->f_mapping, start, end);  }  /* diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 8a45e6d1f6a4..57f578e2560a 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -56,7 +56,7 @@ static int nfs_link(struct dentry *, struct inode *, struct dentry *);  static int nfs_mknod(struct inode *, struct dentry *, int, dev_t);  static int nfs_rename(struct inode *, struct dentry *,  		      struct inode *, struct dentry *); -static int nfs_fsync_dir(struct file *, int); +static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);  static loff_t nfs_llseek_dir(struct file *, loff_t, int);  static void nfs_readdir_clear_array(struct page*); @@ -945,15 +945,19 @@ out:   * All directory operations under NFS are synchronous, so fsync()   * is a dummy operation.   */ -static int nfs_fsync_dir(struct file *filp, int datasync) +static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end, +			 int datasync)  {  	struct dentry *dentry = filp->f_path.dentry; +	struct inode *inode = dentry->d_inode;  	dfprintk(FILE, "NFS: fsync dir(%s/%s) datasync %d\n",  			dentry->d_parent->d_name.name, dentry->d_name.name,  			datasync); +	mutex_lock(&inode->i_mutex);  	nfs_inc_stats(dentry->d_inode, NFSIOS_VFSFSYNC); +	mutex_unlock(&inode->i_mutex);  	return 0;  } diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 2c1705b6acd7..28b8c3f3cda3 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -55,7 +55,7 @@ static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,  static ssize_t nfs_file_write(struct kiocb *, const struct iovec *iov,  				unsigned long nr_segs, loff_t pos);  static int  nfs_file_flush(struct file *, fl_owner_t id); -static int  nfs_file_fsync(struct file *, int datasync); +static int  nfs_file_fsync(struct file *, loff_t, loff_t, int datasync);  static int nfs_check_flags(int flags);  static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl);  static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl); @@ -308,7 +308,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)   * fall back to doing a synchronous write.   */  static int -nfs_file_fsync(struct file *file, int datasync) +nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct dentry *dentry = file->f_path.dentry;  	struct nfs_open_context *ctx = nfs_file_open_context(file); @@ -316,11 +316,15 @@ nfs_file_fsync(struct file *file, int datasync)  	int have_error, status;  	int ret = 0; -  	dprintk("NFS: fsync file(%s/%s) datasync %d\n",  			dentry->d_parent->d_name.name, dentry->d_name.name,  			datasync); +	ret = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (ret) +		return ret; +	mutex_lock(&inode->i_mutex); +  	nfs_inc_stats(inode, NFSIOS_VFSFSYNC);  	have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);  	status = nfs_commit_inode(inode, FLUSH_SYNC); @@ -332,6 +336,7 @@ nfs_file_fsync(struct file *file, int datasync)  	if (!ret && !datasync)  		/* application has asked for meta-data sync */  		ret = pnfs_layoutcommit_inode(inode, true); +	mutex_unlock(&inode->i_mutex);  	return ret;  } diff --git a/fs/nilfs2/file.c b/fs/nilfs2/file.c index d7eeca62febd..26601529dc17 100644 --- a/fs/nilfs2/file.c +++ b/fs/nilfs2/file.c @@ -27,7 +27,7 @@  #include "nilfs.h"  #include "segment.h" -int nilfs_sync_file(struct file *file, int datasync) +int nilfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)  {  	/*  	 * Called from fsync() system call @@ -40,8 +40,15 @@ int nilfs_sync_file(struct file *file, int datasync)  	struct inode *inode = file->f_mapping->host;  	int err; -	if (!nilfs_inode_dirty(inode)) +	err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (err) +		return err; +	mutex_lock(&inode->i_mutex); + +	if (!nilfs_inode_dirty(inode)) { +		mutex_unlock(&inode->i_mutex);  		return 0; +	}  	if (datasync)  		err = nilfs_construct_dsync_segment(inode->i_sb, inode, 0, @@ -49,6 +56,7 @@ int nilfs_sync_file(struct file *file, int datasync)  	else  		err = nilfs_construct_segment(inode->i_sb); +	mutex_unlock(&inode->i_mutex);  	return err;  } diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 6fb7511c0328..255d5e1c03b7 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -235,7 +235,7 @@ extern void nilfs_set_link(struct inode *, struct nilfs_dir_entry *,  			   struct page *, struct inode *);  /* file.c */ -extern int nilfs_sync_file(struct file *, int); +extern int nilfs_sync_file(struct file *, loff_t, loff_t, int);  /* ioctl.c */  long nilfs_ioctl(struct file *, unsigned int, unsigned long); diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 0f48e7c5d9e1..99e36107ff60 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -1527,13 +1527,20 @@ static int ntfs_dir_open(struct inode *vi, struct file *filp)   * this problem for now.  We do write the $BITMAP attribute if it is present   * which is the important one for a directory so things are not too bad.   */ -static int ntfs_dir_fsync(struct file *filp, int datasync) +static int ntfs_dir_fsync(struct file *filp, loff_t start, loff_t end, +			  int datasync)  {  	struct inode *bmp_vi, *vi = filp->f_mapping->host;  	int err, ret;  	ntfs_attr na;  	ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); + +	err = filemap_write_and_wait_range(vi->i_mapping, start, end); +	if (err) +		return err; +	mutex_lock(&vi->i_mutex); +  	BUG_ON(!S_ISDIR(vi->i_mode));  	/* If the bitmap attribute inode is in memory sync it, too. */  	na.mft_no = vi->i_ino; @@ -1555,6 +1562,7 @@ static int ntfs_dir_fsync(struct file *filp, int datasync)  	else  		ntfs_warning(vi->i_sb, "Failed to f%ssync inode 0x%lx.  Error "  				"%u.", datasync ? "data" : "", vi->i_ino, -ret); +	mutex_unlock(&vi->i_mutex);  	return ret;  } diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index b59f5ac26bef..c587e2d27183 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -2152,12 +2152,19 @@ static ssize_t ntfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,   * with this inode but since we have no simple way of getting to them we ignore   * this problem for now.   */ -static int ntfs_file_fsync(struct file *filp, int datasync) +static int ntfs_file_fsync(struct file *filp, loff_t start, loff_t end, +			   int datasync)  {  	struct inode *vi = filp->f_mapping->host;  	int err, ret = 0;  	ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); + +	err = filemap_write_and_wait_range(vi->i_mapping, start, end); +	if (err) +		return err; +	mutex_lock(&vi->i_mutex); +  	BUG_ON(S_ISDIR(vi->i_mode));  	if (!datasync || !NInoNonResident(NTFS_I(vi)))  		ret = __ntfs_write_inode(vi, 1); @@ -2175,6 +2182,7 @@ static int ntfs_file_fsync(struct file *filp, int datasync)  	else  		ntfs_warning(vi->i_sb, "Failed to f%ssync inode 0x%lx.  Error "  				"%u.", datasync ? "data" : "", vi->i_ino, -ret); +	mutex_unlock(&vi->i_mutex);  	return ret;  } diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 22d604601957..0fc2bd34039d 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -171,7 +171,8 @@ static int ocfs2_dir_release(struct inode *inode, struct file *file)  	return 0;  } -static int ocfs2_sync_file(struct file *file, int datasync) +static int ocfs2_sync_file(struct file *file, loff_t start, loff_t end, +			   int datasync)  {  	int err = 0;  	journal_t *journal; @@ -184,6 +185,16 @@ static int ocfs2_sync_file(struct file *file, int datasync)  			      file->f_path.dentry->d_name.name,  			      (unsigned long long)datasync); +	err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (err) +		return err; + +	/* +	 * Probably don't need the i_mutex at all in here, just putting it here +	 * to be consistent with how fsync used to be called, someone more +	 * familiar with the fs could possibly remove it. +	 */ +	mutex_lock(&inode->i_mutex);  	if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) {  		/*  		 * We still have to flush drive's caches to get data to the @@ -200,6 +211,7 @@ static int ocfs2_sync_file(struct file *file, int datasync)  bail:  	if (err)  		mlog_errno(err); +	mutex_unlock(&inode->i_mutex);  	return (err < 0) ? -EIO : 0;  } diff --git a/fs/reiserfs/dir.c b/fs/reiserfs/dir.c index 198dabf1b2bb..133e9355dc6f 100644 --- a/fs/reiserfs/dir.c +++ b/fs/reiserfs/dir.c @@ -14,7 +14,8 @@  extern const struct reiserfs_key MIN_KEY;  static int reiserfs_readdir(struct file *, void *, filldir_t); -static int reiserfs_dir_fsync(struct file *filp, int datasync); +static int reiserfs_dir_fsync(struct file *filp, loff_t start, loff_t end, +			      int datasync);  const struct file_operations reiserfs_dir_operations = {  	.llseek = generic_file_llseek, @@ -27,13 +28,21 @@ const struct file_operations reiserfs_dir_operations = {  #endif  }; -static int reiserfs_dir_fsync(struct file *filp, int datasync) +static int reiserfs_dir_fsync(struct file *filp, loff_t start, loff_t end, +			      int datasync)  {  	struct inode *inode = filp->f_mapping->host;  	int err; + +	err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (err) +		return err; + +	mutex_lock(&inode->i_mutex);  	reiserfs_write_lock(inode->i_sb);  	err = reiserfs_commit_for_inode(inode);  	reiserfs_write_unlock(inode->i_sb); +	mutex_unlock(&inode->i_mutex);  	if (err < 0)  		return err;  	return 0; diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index bbf31003d308..c7156dc39ce7 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -140,12 +140,18 @@ static void reiserfs_vfs_truncate_file(struct inode *inode)   * be removed...   */ -static int reiserfs_sync_file(struct file *filp, int datasync) +static int reiserfs_sync_file(struct file *filp, loff_t start, loff_t end, +			      int datasync)  {  	struct inode *inode = filp->f_mapping->host;  	int err;  	int barrier_done; +	err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (err) +		return err; + +	mutex_lock(&inode->i_mutex);  	BUG_ON(!S_ISREG(inode->i_mode));  	err = sync_mapping_buffers(inode->i_mapping);  	reiserfs_write_lock(inode->i_sb); @@ -153,6 +159,7 @@ static int reiserfs_sync_file(struct file *filp, int datasync)  	reiserfs_write_unlock(inode->i_sb);  	if (barrier_done != 1 && reiserfs_barrier_flush(inode->i_sb))  		blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); +	mutex_unlock(&inode->i_mutex);  	if (barrier_done < 0)  		return barrier_done;  	return (err < 0) ? -EIO : 0; diff --git a/fs/sync.c b/fs/sync.c index c38ec163da6c..c98a7477edfd 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -165,28 +165,9 @@ SYSCALL_DEFINE1(syncfs, int, fd)   */  int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync)  { -	struct address_space *mapping = file->f_mapping; -	int err, ret; - -	if (!file->f_op || !file->f_op->fsync) { -		ret = -EINVAL; -		goto out; -	} - -	ret = filemap_write_and_wait_range(mapping, start, end); - -	/* -	 * We need to protect against concurrent writers, which could cause -	 * livelocks in fsync_buffers_list(). -	 */ -	mutex_lock(&mapping->host->i_mutex); -	err = file->f_op->fsync(file, datasync); -	if (!ret) -		ret = err; -	mutex_unlock(&mapping->host->i_mutex); - -out: -	return ret; +	if (!file->f_op || !file->f_op->fsync) +		return -EINVAL; +	return file->f_op->fsync(file, start, end, datasync);  }  EXPORT_SYMBOL(vfs_fsync_range); diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 5e7fccfc4b29..89ef9a2f7837 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1304,7 +1304,7 @@ static void *ubifs_follow_link(struct dentry *dentry, struct nameidata *nd)  	return NULL;  } -int ubifs_fsync(struct file *file, int datasync) +int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct inode *inode = file->f_mapping->host;  	struct ubifs_info *c = inode->i_sb->s_fs_info; @@ -1319,14 +1319,16 @@ int ubifs_fsync(struct file *file, int datasync)  		 */  		return 0; -	/* -	 * VFS has already synchronized dirty pages for this inode. Synchronize -	 * the inode unless this is a 'datasync()' call. -	 */ +	err = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (err) +		return err; +	mutex_lock(&inode->i_mutex); + +	/* Synchronize the inode unless this is a 'datasync()' call. */  	if (!datasync || (inode->i_state & I_DIRTY_DATASYNC)) {  		err = inode->i_sb->s_op->write_inode(inode, NULL);  		if (err) -			return err; +			goto out;  	}  	/* @@ -1334,10 +1336,9 @@ int ubifs_fsync(struct file *file, int datasync)  	 * them.  	 */  	err = ubifs_sync_wbufs_by_inode(c, inode); -	if (err) -		return err; - -	return 0; +out: +	mutex_unlock(&inode->i_mutex); +	return err;  }  /** diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index f79983d6f860..4cd648501fa4 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -1720,7 +1720,7 @@ const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c);  int ubifs_calc_dark(const struct ubifs_info *c, int spc);  /* file.c */ -int ubifs_fsync(struct file *file, int datasync); +int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync);  int ubifs_setattr(struct dentry *dentry, struct iattr *attr);  /* dir.c */ diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 7f782af286bf..fbbf657df0cd 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -127,6 +127,8 @@ xfs_iozero(  STATIC int  xfs_file_fsync(  	struct file		*file, +	loff_t			start, +	loff_t			end,  	int			datasync)  {  	struct inode		*inode = file->f_mapping->host; @@ -138,6 +140,10 @@ xfs_file_fsync(  	trace_xfs_file_fsync(ip); +	error = filemap_write_and_wait_range(inode->i_mapping, start, end); +	if (error) +		return error; +  	if (XFS_FORCED_SHUTDOWN(mp))  		return -XFS_ERROR(EIO); @@ -875,18 +881,11 @@ xfs_file_aio_write(  	/* Handle various SYNC-type writes */  	if ((file->f_flags & O_DSYNC) || IS_SYNC(inode)) {  		loff_t end = pos + ret - 1; -		int error, error2;  		xfs_rw_iunlock(ip, iolock); -		error = filemap_write_and_wait_range(mapping, pos, end); +		ret = -xfs_file_fsync(file, pos, end, +				      (file->f_flags & __O_SYNC) ? 0 : 1);  		xfs_rw_ilock(ip, iolock); - -		error2 = -xfs_file_fsync(file, -					 (file->f_flags & __O_SYNC) ? 0 : 1); -		if (error) -			ret = error; -		else if (error2) -			ret = error2;  	}  out_unlock: diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 5e06acf95d0f..0c473fd79acb 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -877,7 +877,7 @@ extern int ext3_htree_store_dirent(struct file *dir_file, __u32 hash,  extern void ext3_htree_free_dir_info(struct dir_private_info *p);  /* fsync.c */ -extern int ext3_sync_file(struct file *, int); +extern int ext3_sync_file(struct file *, loff_t, loff_t, int);  /* hash.c */  extern int ext3fs_dirhash(const char *name, int len, struct diff --git a/include/linux/fb.h b/include/linux/fb.h index 6a8274877171..1d6836c498dd 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -1043,7 +1043,8 @@ extern void fb_deferred_io_open(struct fb_info *info,  				struct inode *inode,  				struct file *file);  extern void fb_deferred_io_cleanup(struct fb_info *info); -extern int fb_deferred_io_fsync(struct file *file, int datasync); +extern int fb_deferred_io_fsync(struct file *file, loff_t start, +				loff_t end, int datasync);  static inline bool fb_be_math(struct fb_info *info)  { diff --git a/include/linux/fs.h b/include/linux/fs.h index 4a61f98823a6..9cd2075c4a39 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1572,7 +1572,7 @@ struct file_operations {  	int (*open) (struct inode *, struct file *);  	int (*flush) (struct file *, fl_owner_t id);  	int (*release) (struct inode *, struct file *); -	int (*fsync) (struct file *, int datasync); +	int (*fsync) (struct file *, loff_t, loff_t, int datasync);  	int (*aio_fsync) (struct kiocb *, int datasync);  	int (*fasync) (int, struct file *, int);  	int (*lock) (struct file *, int, struct file_lock *); @@ -2360,7 +2360,8 @@ extern int generic_segment_checks(const struct iovec *iov,  /* fs/block_dev.c */  extern ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov,  				unsigned long nr_segs, loff_t pos); -extern int blkdev_fsync(struct file *filp, int datasync); +extern int blkdev_fsync(struct file *filp, loff_t start, loff_t end, +			int datasync);  /* fs/splice.c */  extern ssize_t generic_file_splice_read(struct file *, loff_t *, @@ -2490,7 +2491,7 @@ 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 noop_fsync(struct file *, int); +extern int noop_fsync(struct file *, loff_t, loff_t, int);  extern int simple_empty(struct dentry *);  extern int simple_readpage(struct file *file, struct page *page);  extern int simple_write_begin(struct file *file, struct address_space *mapping, @@ -2515,7 +2516,7 @@ extern ssize_t simple_read_from_buffer(void __user *to, size_t count,  extern ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos,  		const void __user *from, size_t count); -extern int generic_file_fsync(struct file *, int); +extern int generic_file_fsync(struct file *, loff_t, loff_t, int);  extern int generic_check_addressable(unsigned, u64); diff --git a/ipc/shm.c b/ipc/shm.c index ab3385a21b27..27884adb1a90 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -277,13 +277,13 @@ static int shm_release(struct inode *ino, struct file *file)  	return 0;  } -static int shm_fsync(struct file *file, int datasync) +static int shm_fsync(struct file *file, loff_t start, loff_t end, int datasync)  {  	struct shm_file_data *sfd = shm_file_data(file);  	if (!sfd->file->f_op->fsync)  		return -EINVAL; -	return sfd->file->f_op->fsync(sfd->file, datasync); +	return sfd->file->f_op->fsync(sfd->file, start, end, datasync);  }  static unsigned long shm_get_unmapped_area(struct file *file, |