diff options
Diffstat (limited to 'fs/sync.c')
| -rw-r--r-- | fs/sync.c | 184 | 
1 files changed, 82 insertions, 102 deletions
diff --git a/fs/sync.c b/fs/sync.c index ba76b9623e7..b28d1dd10e8 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -6,14 +6,14 @@  #include <linux/file.h>  #include <linux/fs.h>  #include <linux/slab.h> -#include <linux/module.h> +#include <linux/export.h> +#include <linux/namei.h>  #include <linux/sched.h>  #include <linux/writeback.h>  #include <linux/syscalls.h>  #include <linux/linkage.h>  #include <linux/pagemap.h>  #include <linux/quotaops.h> -#include <linux/buffer_head.h>  #include <linux/backing-dev.h>  #include "internal.h" @@ -29,20 +29,10 @@   */  static int __sync_filesystem(struct super_block *sb, int wait)  { -	/* -	 * This should be safe, as we require bdi backing to actually -	 * write out data in the first place -	 */ -	if (!sb->s_bdi || sb->s_bdi == &noop_backing_dev_info) -		return 0; - -	if (sb->s_qcop && sb->s_qcop->quota_sync) -		sb->s_qcop->quota_sync(sb, -1, wait); -  	if (wait)  		sync_inodes_sb(sb);  	else -		writeback_inodes_sb(sb); +		writeback_inodes_sb(sb, WB_REASON_SYNC);  	if (sb->s_op->sync_fs)  		sb->s_op->sync_fs(sb, wait); @@ -77,29 +67,48 @@ int sync_filesystem(struct super_block *sb)  }  EXPORT_SYMBOL_GPL(sync_filesystem); -static void sync_one_sb(struct super_block *sb, void *arg) +static void sync_inodes_one_sb(struct super_block *sb, void *arg) +{ +	if (!(sb->s_flags & MS_RDONLY)) +		sync_inodes_sb(sb); +} + +static void sync_fs_one_sb(struct super_block *sb, void *arg)  { -	if (!(sb->s_flags & MS_RDONLY) && sb->s_bdi) -		__sync_filesystem(sb, *(int *)arg); +	if (!(sb->s_flags & MS_RDONLY) && sb->s_op->sync_fs) +		sb->s_op->sync_fs(sb, *(int *)arg);  } -/* - * Sync all the data for all the filesystems (called by sys_sync() and - * emergency sync) - */ -static void sync_filesystems(int wait) + +static void fdatawrite_one_bdev(struct block_device *bdev, void *arg)  { -	iterate_supers(sync_one_sb, &wait); +	filemap_fdatawrite(bdev->bd_inode->i_mapping); +} + +static void fdatawait_one_bdev(struct block_device *bdev, void *arg) +{ +	filemap_fdatawait(bdev->bd_inode->i_mapping);  }  /* - * sync everything.  Start out by waking pdflush, because that writes back - * all queues in parallel. + * Sync everything. We start by waking flusher threads so that most of + * writeback runs on all devices in parallel. Then we sync all inodes reliably + * which effectively also waits for all flusher threads to finish doing + * writeback. At this point all data is on disk so metadata should be stable + * and we tell filesystems to sync their metadata via ->sync_fs() calls. + * Finally, we writeout all block devices because some filesystems (e.g. ext2) + * just write metadata (such as inodes or bitmaps) to block device page cache + * and do not sync it on their own in ->sync_fs().   */  SYSCALL_DEFINE0(sync)  { -	wakeup_flusher_threads(0); -	sync_filesystems(0); -	sync_filesystems(1); +	int nowait = 0, wait = 1; + +	wakeup_flusher_threads(0, WB_REASON_SYNC); +	iterate_supers(sync_inodes_one_sb, NULL); +	iterate_supers(sync_fs_one_sb, &nowait); +	iterate_supers(sync_fs_one_sb, &wait); +	iterate_bdevs(fdatawrite_one_bdev, NULL); +	iterate_bdevs(fdatawait_one_bdev, NULL);  	if (unlikely(laptop_mode))  		laptop_sync_completion();  	return 0; @@ -107,12 +116,18 @@ SYSCALL_DEFINE0(sync)  static void do_sync_work(struct work_struct *work)  { +	int nowait = 0; +  	/*  	 * Sync twice to reduce the possibility we skipped some inodes / pages  	 * because they were temporarily locked  	 */ -	sync_filesystems(0); -	sync_filesystems(0); +	iterate_supers(sync_inodes_one_sb, &nowait); +	iterate_supers(sync_fs_one_sb, &nowait); +	iterate_bdevs(fdatawrite_one_bdev, NULL); +	iterate_supers(sync_inodes_one_sb, &nowait); +	iterate_supers(sync_fs_one_sb, &nowait); +	iterate_bdevs(fdatawrite_one_bdev, NULL);  	printk("Emergency Sync complete\n");  	kfree(work);  } @@ -128,6 +143,27 @@ void emergency_sync(void)  	}  } +/* + * sync a single super + */ +SYSCALL_DEFINE1(syncfs, int, fd) +{ +	struct fd f = fdget(fd); +	struct super_block *sb; +	int ret; + +	if (!f.file) +		return -EBADF; +	sb = f.file->f_dentry->d_sb; + +	down_read(&sb->s_umount); +	ret = sync_filesystem(sb); +	up_read(&sb->s_umount); + +	fdput(f); +	return ret; +} +  /**   * vfs_fsync_range - helper to sync a range of data & metadata to disk   * @file:		file to sync @@ -141,28 +177,9 @@ void emergency_sync(void)   */  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->fsync) +		return -EINVAL; +	return file->f_op->fsync(file, start, end, datasync);  }  EXPORT_SYMBOL(vfs_fsync_range); @@ -182,13 +199,12 @@ EXPORT_SYMBOL(vfs_fsync);  static int do_fsync(unsigned int fd, int datasync)  { -	struct file *file; +	struct fd f = fdget(fd);  	int ret = -EBADF; -	file = fget(fd); -	if (file) { -		ret = vfs_fsync(file, datasync); -		fput(file); +	if (f.file) { +		ret = vfs_fsync(f.file, datasync); +		fdput(f);  	}  	return ret;  } @@ -203,23 +219,6 @@ SYSCALL_DEFINE1(fdatasync, unsigned int, fd)  	return do_fsync(fd, 1);  } -/** - * generic_write_sync - perform syncing after a write if file / inode is sync - * @file:	file to which the write happened - * @pos:	offset where the write started - * @count:	length of the write - * - * This is just a simple wrapper about our general syncing function. - */ -int generic_write_sync(struct file *file, loff_t pos, loff_t count) -{ -	if (!(file->f_flags & O_DSYNC) && !IS_SYNC(file->f_mapping->host)) -		return 0; -	return vfs_fsync_range(file, pos, pos + count - 1, -			       (file->f_flags & __O_SYNC) ? 0 : 1); -} -EXPORT_SYMBOL(generic_write_sync); -  /*   * sys_sync_file_range() permits finely controlled syncing over a segment of   * a file in the range offset .. (offset+nbytes-1) inclusive.  If nbytes is @@ -267,14 +266,13 @@ EXPORT_SYMBOL(generic_write_sync);   * already-instantiated disk blocks, there are no guarantees here that the data   * will be available after a crash.   */ -SYSCALL_DEFINE(sync_file_range)(int fd, loff_t offset, loff_t nbytes, -				unsigned int flags) +SYSCALL_DEFINE4(sync_file_range, int, fd, loff_t, offset, loff_t, nbytes, +				unsigned int, flags)  {  	int ret; -	struct file *file; +	struct fd f;  	struct address_space *mapping;  	loff_t endbyte;			/* inclusive */ -	int fput_needed;  	umode_t i_mode;  	ret = -EINVAL; @@ -313,17 +311,17 @@ SYSCALL_DEFINE(sync_file_range)(int fd, loff_t offset, loff_t nbytes,  		endbyte--;		/* inclusive */  	ret = -EBADF; -	file = fget_light(fd, &fput_needed); -	if (!file) +	f = fdget(fd); +	if (!f.file)  		goto out; -	i_mode = file->f_path.dentry->d_inode->i_mode; +	i_mode = file_inode(f.file)->i_mode;  	ret = -ESPIPE;  	if (!S_ISREG(i_mode) && !S_ISBLK(i_mode) && !S_ISDIR(i_mode) &&  			!S_ISLNK(i_mode))  		goto out_put; -	mapping = file->f_mapping; +	mapping = f.file->f_mapping;  	if (!mapping) {  		ret = -EINVAL;  		goto out_put; @@ -346,33 +344,15 @@ SYSCALL_DEFINE(sync_file_range)(int fd, loff_t offset, loff_t nbytes,  		ret = filemap_fdatawait_range(mapping, offset, endbyte);  out_put: -	fput_light(file, fput_needed); +	fdput(f);  out:  	return ret;  } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_sync_file_range(long fd, loff_t offset, loff_t nbytes, -				    long flags) -{ -	return SYSC_sync_file_range((int) fd, offset, nbytes, -				    (unsigned int) flags); -} -SYSCALL_ALIAS(sys_sync_file_range, SyS_sync_file_range); -#endif  /* It would be nice if people remember that not all the world's an i386     when they introduce new system calls */ -SYSCALL_DEFINE(sync_file_range2)(int fd, unsigned int flags, -				 loff_t offset, loff_t nbytes) +SYSCALL_DEFINE4(sync_file_range2, int, fd, unsigned int, flags, +				 loff_t, offset, loff_t, nbytes)  {  	return sys_sync_file_range(fd, offset, nbytes, flags);  } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_sync_file_range2(long fd, long flags, -				     loff_t offset, loff_t nbytes) -{ -	return SYSC_sync_file_range2((int) fd, (unsigned int) flags, -				     offset, nbytes); -} -SYSCALL_ALIAS(sys_sync_file_range2, SyS_sync_file_range2); -#endif  | 
