Commit 880b9577 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

fs: distinguish between user initiated freeze and kernel initiated freeze



Userspace can freeze a filesystem using the FIFREEZE ioctl or by
suspending the block device; this state persists until userspace thaws
the filesystem with the FITHAW ioctl or resuming the block device.
Since commit 18e9e510 ("Introduce freeze_super and thaw_super for
the fsfreeze ioctl") we only allow the first freeze command to succeed.

The kernel may decide that it is necessary to freeze a filesystem for
its own internal purposes, such as suspends in progress, filesystem fsck
activities, or quiescing a device prior to removal.  Userspace thaw
commands must never break a kernel freeze, and kernel thaw commands
shouldn't undo userspace's freeze command.

Introduce a couple of freeze holder flags and wire it into the
sb_writers state.  One kernel and one userspace freeze are allowed to
coexist at the same time; the filesystem will not thaw until both are
lifted.

I wonder if the f2fs/gfs2 code should be using a kernel freeze here, but
for now we'll use FREEZE_HOLDER_USERSPACE to preserve existing
behaviors.

Cc: mcgrof@kernel.org
Cc: jack@suse.cz
Cc: hch@infradead.org
Cc: ruansy.fnst@fujitsu.com
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarJan Kara <jack@suse.cz>
parent fdf0eaf1
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -260,9 +260,11 @@ filesystem. The following members are defined:
		void (*evict_inode) (struct inode *);
		void (*put_super) (struct super_block *);
		int (*sync_fs)(struct super_block *sb, int wait);
		int (*freeze_super) (struct super_block *);
		int (*freeze_super) (struct super_block *sb,
					enum freeze_holder who);
		int (*freeze_fs) (struct super_block *);
		int (*thaw_super) (struct super_block *);
		int (*thaw_super) (struct super_block *sb,
					enum freeze_wholder who);
		int (*unfreeze_fs) (struct super_block *);
		int (*statfs) (struct dentry *, struct kstatfs *);
		int (*remount_fs) (struct super_block *, int *, char *);
+4 −4
Original line number Diff line number Diff line
@@ -248,9 +248,9 @@ int freeze_bdev(struct block_device *bdev)
	if (!sb)
		goto sync;
	if (sb->s_op->freeze_super)
		error = sb->s_op->freeze_super(sb);
		error = sb->s_op->freeze_super(sb, FREEZE_HOLDER_USERSPACE);
	else
		error = freeze_super(sb);
		error = freeze_super(sb, FREEZE_HOLDER_USERSPACE);
	deactivate_super(sb);

	if (error) {
@@ -291,9 +291,9 @@ int thaw_bdev(struct block_device *bdev)
		goto out;

	if (sb->s_op->thaw_super)
		error = sb->s_op->thaw_super(sb);
		error = sb->s_op->thaw_super(sb, FREEZE_HOLDER_USERSPACE);
	else
		error = thaw_super(sb);
		error = thaw_super(sb, FREEZE_HOLDER_USERSPACE);
	if (error)
		bdev->bd_fsfreeze_count++;
	else
+5 −3
Original line number Diff line number Diff line
@@ -2181,12 +2181,14 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count)
	if (err)
		return err;

	err = freeze_super(sbi->sb);
	err = freeze_super(sbi->sb, FREEZE_HOLDER_USERSPACE);
	if (err)
		return err;

	if (f2fs_readonly(sbi->sb)) {
		thaw_super(sbi->sb);
		err = thaw_super(sbi->sb, FREEZE_HOLDER_USERSPACE);
		if (err)
			return err;
		return -EROFS;
	}

@@ -2240,6 +2242,6 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count)
out_err:
	f2fs_up_write(&sbi->cp_global_sem);
	f2fs_up_write(&sbi->gc_lock);
	thaw_super(sbi->sb);
	thaw_super(sbi->sb, FREEZE_HOLDER_USERSPACE);
	return err;
}
+7 −5
Original line number Diff line number Diff line
@@ -689,7 +689,7 @@ static int gfs2_freeze_locally(struct gfs2_sbd *sdp)
	struct super_block *sb = sdp->sd_vfs;
	int error;

	error = freeze_super(sb);
	error = freeze_super(sb, FREEZE_HOLDER_USERSPACE);
	if (error)
		return error;

@@ -697,7 +697,9 @@ static int gfs2_freeze_locally(struct gfs2_sbd *sdp)
		gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_FREEZE |
			       GFS2_LFC_FREEZE_GO_SYNC);
		if (gfs2_withdrawn(sdp)) {
			thaw_super(sb);
			error = thaw_super(sb, FREEZE_HOLDER_USERSPACE);
			if (error)
				return error;
			return -EIO;
		}
	}
@@ -712,7 +714,7 @@ static int gfs2_do_thaw(struct gfs2_sbd *sdp)
	error = gfs2_freeze_lock_shared(sdp);
	if (error)
		goto fail;
	error = thaw_super(sb);
	error = thaw_super(sb, FREEZE_HOLDER_USERSPACE);
	if (!error)
		return 0;

@@ -761,7 +763,7 @@ void gfs2_freeze_func(struct work_struct *work)
 *
 */

static int gfs2_freeze_super(struct super_block *sb)
static int gfs2_freeze_super(struct super_block *sb, enum freeze_holder who)
{
	struct gfs2_sbd *sdp = sb->s_fs_info;
	int error;
@@ -816,7 +818,7 @@ static int gfs2_freeze_super(struct super_block *sb)
 *
 */

static int gfs2_thaw_super(struct super_block *sb)
static int gfs2_thaw_super(struct super_block *sb, enum freeze_holder who)
{
	struct gfs2_sbd *sdp = sb->s_fs_info;
	int error;
+2 −2
Original line number Diff line number Diff line
@@ -168,10 +168,10 @@ static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len)

	switch (n) {
	case 0:
		error = thaw_super(sdp->sd_vfs);
		error = thaw_super(sdp->sd_vfs, FREEZE_HOLDER_USERSPACE);
		break;
	case 1:
		error = freeze_super(sdp->sd_vfs);
		error = freeze_super(sdp->sd_vfs, FREEZE_HOLDER_USERSPACE);
		break;
	default:
		return -EINVAL;
Loading