Skip to contentSkip to conversation

fsfreeze: check ioctls

PATCH New Fernando Luis Vazquez Cao 1 participant 8 messages Jul 21, 2011 – Jul 27, 2011

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

cover fsfreeze: check ioctls

The current fsfreeze API lacks a method to check the current freeze status of a superblock. One might be tempted to think that a new vfs ioctl is all we need, but with the current implemetation of fsfreeze it it is possible to umount a freezed filesystem, which means that the only way to check the freeze status is through the block device. For a moment I thought I could get without the vfs version but then I realized that the block device ioctl would not play well with btrfs.

Because of all of the above I ended up implementing both. Al also suggested [1] a thaw-by-block-device ioctl which I am currently working on.

[1] http://marc.info/?l=linux-kernel&m=125361768208298&w=2

#1 lore raw

PATCH 1/2 fsfreeze: add FIISFROZEN ioctl

The FIISFROZEN ioctl can be use by HA and monitoring software to check the freeze state of a mounted filesystem.

---

fs/compat_ioctl.c +1 -0
diff -urNp linux-3.0-rc7-orig/fs/compat_ioctl.c linux-3.0-rc7/fs/compat_ioctl.c
@@ -883,6 +883,7 @@ COMPATIBLE_IOCTL(FIGETBSZ)
 /* 'X' - originally XFS but some now in the VFS */
 COMPATIBLE_IOCTL(FIFREEZE)
 COMPATIBLE_IOCTL(FITHAW)
+COMPATIBLE_IOCTL(FIISFROZEN)
 COMPATIBLE_IOCTL(KDGETKEYCODE)
 COMPATIBLE_IOCTL(KDSETKEYCODE)
 COMPATIBLE_IOCTL(KDGKBTYPE)
fs/ioctl.c +16 -0
diff -urNp linux-3.0-rc7-orig/fs/ioctl.c linux-3.0-rc7/fs/ioctl.c
@@ -536,6 +536,16 @@ static int ioctl_fsthaw(struct file *fil
 	return thaw_super(sb);
 }
 
+static int ioctl_fs_isfrozen(struct file *filp)
+{
+	struct super_block *sb = filp->f_path.dentry->d_inode->i_sb;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	return isfrozen_super(sb);
+}
+
 /*
  * When you add any new common ioctls to the switches above and below
  * please update compat_sys_ioctl() too.
@@ -585,6 +595,12 @@ int do_vfs_ioctl(struct file *filp, unsi
 		error = ioctl_fsthaw(filp);
 		break;
 
+	case FIISFROZEN:
+		error = ioctl_fs_isfrozen(filp);
+		if (error >= 0)
+			return put_user(error, (int __user *)arg);
+		break;
+
 	case FS_IOC_FIEMAP:
 		return ioctl_fiemap(filp, arg);
 
fs/super.c +5 -0
diff -urNp linux-3.0-rc7-orig/fs/super.c linux-3.0-rc7/fs/super.c
@@ -1057,3 +1057,8 @@ out:
 	return 0;
 }
 EXPORT_SYMBOL(thaw_super);
+
+int isfrozen_super(struct super_block *sb)
+{
+	return sb->s_frozen > SB_UNFROZEN ? 1:0;
+}
include/linux/fs.h +2 -0
diff -urNp linux-3.0-rc7-orig/include/linux/fs.h linux-3.0-rc7/include/linux/fs.h
@@ -324,6 +325,7 @@ struct inodes_stat_t {
 #define FIFREEZE	_IOWR('X', 119, int)	/* Freeze */
 #define FITHAW		_IOWR('X', 120, int)	/* Thaw */
 #define FITRIM		_IOWR('X', 121, struct fstrim_range)	/* Trim */
+#define FIISFROZEN	_IOR('X', 122, int)	/* get sb freeze state */
 
 #define	FS_IOC_GETFLAGS			_IOR('f', 1, long)
 #define	FS_IOC_SETFLAGS			_IOW('f', 2, long)
@@ -1882,6 +1884,7 @@ extern int fd_statfs(int, struct kstatfs
 extern int statfs_by_dentry(struct dentry *, struct kstatfs *);
 extern int freeze_super(struct super_block *super);
 extern int thaw_super(struct super_block *super);
+extern int isfrozen_super(struct super_block *sb);
 
 extern int current_umask(void);
 
#2 lore raw

PATCH 2/2 fsfreeze: add BLKISFROZEN ioctl

In reply to Fernando Luis Vazquez Cao

The FIISFROZEN ioctl can be use by HA and monitoring software to check the freeze state of the filesystem sitting on top of a block device. This will not work for filesystems that can handle multiple devices, i.e. btrfs.

---

block/compat_ioctl.c +7 -0
diff -urNp linux-3.0-rc7-orig/block/compat_ioctl.c linux-3.0-rc7/block/compat_ioctl.c
@@ -757,6 +757,13 @@ long compat_blkdev_ioctl(struct file *fi
 	case BLKTRACETEARDOWN: /* compatible */
 		ret = blk_trace_ioctl(bdev, cmd, compat_ptr(arg));
 		return ret;
+	case BLKISFROZEN:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		ret = isfrozen_bdev(bdev);
+		if (ret >= 0)
+			return compat_put_int(arg, ret);
+                return ret;
 	default:
 		if (disk->fops->compat_ioctl)
 			ret = disk->fops->compat_ioctl(bdev, mode, cmd, arg);
block/ioctl.c +7 -0
diff -urNp linux-3.0-rc7-orig/block/ioctl.c linux-3.0-rc7/block/ioctl.c
@@ -322,6 +322,13 @@ int blkdev_ioctl(struct block_device *bd
 	case BLKTRACETEARDOWN:
 		ret = blk_trace_ioctl(bdev, cmd, (char __user *) arg);
 		break;
+	case BLKISFROZEN:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		ret = isfrozen_bdev(bdev);
+		if (ret >= 0)
+			return put_int(arg, ret);
+		return ret;
 	default:
 		ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
 	}
fs/block_dev.c +19 -0
diff -urNp linux-3.0-rc7-orig/fs/block_dev.c linux-3.0-rc7/fs/block_dev.c
@@ -310,6 +310,25 @@ out:
 }
 EXPORT_SYMBOL(thaw_bdev);
 
+int isfrozen_bdev(struct block_device *bdev)
+{
+	int ret;
+	struct super_block *sb;
+
+	mutex_lock(&bdev->bd_fsfreeze_mutex);
+
+	ret = -EINVAL;
+	sb = get_active_super(bdev);
+	if (!sb)
+		goto out;
+	ret = isfrozen_super(sb);
+	deactivate_super(sb);
+out:
+	mutex_unlock(&bdev->bd_fsfreeze_mutex);
+
+	return ret;
+}
+
 static int blkdev_writepage(struct page *page, struct writeback_control *wbc)
 {
 	return block_write_full_page(page, blkdev_get_block, wbc);
include/linux/fs.h +7 -0
diff -urNp linux-3.0-rc7-orig/include/linux/fs.h linux-3.0-rc7/include/linux/fs.h
@@ -317,6 +317,7 @@ struct inodes_stat_t {
 #define BLKPBSZGET _IO(0x12,123)
 #define BLKDISCARDZEROES _IO(0x12,124)
 #define BLKSECDISCARD _IO(0x12,125)
+#define BLKISFROZEN _IOR(0x12,126, int) /* get file system freeze state */
 
 #define BMAP_IOCTL 1		/* obsolete - kept for compatibility */
 #define FIBMAP	   _IO(0x00,1)	/* bmap access */
@@ -2036,6 +2039,7 @@ extern int sync_blockdev(struct block_de
 extern struct super_block *freeze_bdev(struct block_device *);
 extern void emergency_thaw_all(void);
 extern int thaw_bdev(struct block_device *bdev, struct super_block *sb);
+extern int isfrozen_bdev(struct block_device *bdev);
 extern int fsync_bdev(struct block_device *);
 #else
 static inline void bd_forget(struct inode *inode) {}
@@ -2051,6 +2055,11 @@ static inline int thaw_bdev(struct block
 {
 	return 0;
 }
+
+static int isfrozen_bdev(struct block_device *bdev)
+{
+	return 0;
+}
 #endif
 extern int sync_filesystem(struct super_block *);
 extern const struct file_operations def_blk_fops;
#3 lore raw

cover fsfreeze: new API

In reply to Fernando Luis Vazquez Cao

There are a few with issues with how freeze works in current kernels:

- Unmounting: It is possible to umount a frozen filesystem; the freeze code holds an active reference to the superblock so it does not go away. Since it is not possible to thaw by block device, thawing an unmounted filesystem involves mounting it again and using the current vfs freeze API.

- There is no check API: There is no easy way to know whether a filesystem is frozen or not.

- Foolproofness: Quite often the freeze/thaw process is handled from a daemon. In such cases we have to make really sure that the process does not go away or is killed while the filesystem is frozen; there is no check API so it is not always easy to figure out what is going on. With the advent of virtualization things got even funnier; in some cases not even the root user is aware of the existence of such daemon (usually a guest agent that freezes the guests filesystems before taking a storage snapshot).

I cooked some patches that add the long missing check ioctls and a new freeze API, which should (modulo bugs) the current situation:

- Check ioctls

[1/4] fsfreeze: add vfs ioctl to check freeze state
Adds a vfs ioctl to check the freeze state.

[2/4] fsfreeze: add block device ioctl to check freeze state
Adds a block device ioctl to check the freeze state. This is needed because it possible to umount frozen filesystems.

- New freeze API

[3/4] fsfreeze: add ioctl to create a fd for freeze control
Adds a new ioctl that freezes the indicated filesystem and returns a file descriptor; as long as that file descriptor is held open, the filesystem remains open.

[4/4] fsfreeze: add freeze fd ioctls
Adds new ioctls that operate on the file descriptor described in the previous patch; these can be used to freeze/thaw the filesystem and check the freeze state of the filesystem, thus avoiding the need to get new file descriptors constantly.

#4 lore raw

PATCH 1/4 fsfreeze: add vfs ioctl to check freeze state

The FIISFROZEN ioctl can be use by HA and monitoring software to check the freeze state of a mounted filesystem.

---

fs/compat_ioctl.c +1 -0
diff -urNp linux-3.0-rc7-orig/fs/compat_ioctl.c linux-3.0-rc7/fs/compat_ioctl.c
@@ -883,6 +883,7 @@ COMPATIBLE_IOCTL(FIGETBSZ)
 /* 'X' - originally XFS but some now in the VFS */
 COMPATIBLE_IOCTL(FIFREEZE)
 COMPATIBLE_IOCTL(FITHAW)
+COMPATIBLE_IOCTL(FIISFROZEN)
 COMPATIBLE_IOCTL(KDGETKEYCODE)
 COMPATIBLE_IOCTL(KDSETKEYCODE)
 COMPATIBLE_IOCTL(KDGKBTYPE)
fs/ioctl.c +16 -0
diff -urNp linux-3.0-rc7-orig/fs/ioctl.c linux-3.0-rc7/fs/ioctl.c
@@ -536,6 +536,16 @@ static int ioctl_fsthaw(struct file *fil
 	return thaw_super(sb);
 }
 
+static int ioctl_fs_isfrozen(struct file *filp)
+{
+	struct super_block *sb = filp->f_path.dentry->d_inode->i_sb;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	return isfrozen_super(sb);
+}
+
 /*
  * When you add any new common ioctls to the switches above and below
  * please update compat_sys_ioctl() too.
@@ -585,6 +595,12 @@ int do_vfs_ioctl(struct file *filp, unsi
 		error = ioctl_fsthaw(filp);
 		break;
 
+	case FIISFROZEN:
+		error = ioctl_fs_isfrozen(filp);
+		if (error >= 0)
+			return put_user(error, (int __user *)arg);
+		break;
+
 	case FS_IOC_FIEMAP:
 		return ioctl_fiemap(filp, arg);
 
fs/super.c +5 -0
diff -urNp linux-3.0-rc7-orig/fs/super.c linux-3.0-rc7/fs/super.c
@@ -1057,3 +1057,8 @@ out:
 	return 0;
 }
 EXPORT_SYMBOL(thaw_super);
+
+int isfrozen_super(struct super_block *sb)
+{
+	return sb->s_frozen > SB_UNFROZEN ? 1:0;
+}
include/linux/fs.h +2 -0
diff -urNp linux-3.0-rc7-orig/include/linux/fs.h linux-3.0-rc7/include/linux/fs.h
@@ -324,6 +325,7 @@ struct inodes_stat_t {
 #define FIFREEZE	_IOWR('X', 119, int)	/* Freeze */
 #define FITHAW		_IOWR('X', 120, int)	/* Thaw */
 #define FITRIM		_IOWR('X', 121, struct fstrim_range)	/* Trim */
+#define FIISFROZEN	_IOR('X', 122, int)	/* get sb freeze state */
 
 #define	FS_IOC_GETFLAGS			_IOR('f', 1, long)
 #define	FS_IOC_SETFLAGS			_IOW('f', 2, long)
@@ -1882,6 +1884,7 @@ extern int fd_statfs(int, struct kstatfs
 extern int statfs_by_dentry(struct dentry *, struct kstatfs *);
 extern int freeze_super(struct super_block *super);
 extern int thaw_super(struct super_block *super);
+extern int isfrozen_super(struct super_block *sb);
 
 extern int current_umask(void);
 
#5 lore raw

PATCH 2/4 fsfreeze: add block device ioctl to check freeze state

In reply to Fernando Luis Vazquez Cao

The BLKISFROZEN ioctl can be use by HA and monitoring software to check the freeze state of the filesystem sitting on top of a block device. This will not work for filesystems that can handle multiple devices, i.e. btrfs.

---

block/compat_ioctl.c +7 -0
diff -urNp linux-3.0-rc7-orig/block/compat_ioctl.c linux-3.0-rc7/block/compat_ioctl.c
@@ -757,6 +757,13 @@ long compat_blkdev_ioctl(struct file *fi
 	case BLKTRACETEARDOWN: /* compatible */
 		ret = blk_trace_ioctl(bdev, cmd, compat_ptr(arg));
 		return ret;
+	case BLKISFROZEN:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		ret = isfrozen_bdev(bdev);
+		if (ret >= 0)
+			return compat_put_int(arg, ret);
+                return ret;
 	default:
 		if (disk->fops->compat_ioctl)
 			ret = disk->fops->compat_ioctl(bdev, mode, cmd, arg);
block/ioctl.c +7 -0
diff -urNp linux-3.0-rc7-orig/block/ioctl.c linux-3.0-rc7/block/ioctl.c
@@ -322,6 +322,13 @@ int blkdev_ioctl(struct block_device *bd
 	case BLKTRACETEARDOWN:
 		ret = blk_trace_ioctl(bdev, cmd, (char __user *) arg);
 		break;
+	case BLKISFROZEN:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		ret = isfrozen_bdev(bdev);
+		if (ret >= 0)
+			return put_int(arg, ret);
+		return ret;
 	default:
 		ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
 	}
fs/block_dev.c +19 -0
diff -urNp linux-3.0-rc7-orig/fs/block_dev.c linux-3.0-rc7/fs/block_dev.c
@@ -310,6 +310,25 @@ out:
 }
 EXPORT_SYMBOL(thaw_bdev);
 
+int isfrozen_bdev(struct block_device *bdev)
+{
+	int ret;
+	struct super_block *sb;
+
+	mutex_lock(&bdev->bd_fsfreeze_mutex);
+
+	ret = -EINVAL;
+	sb = get_active_super(bdev);
+	if (!sb)
+		goto out;
+	ret = isfrozen_super(sb);
+	deactivate_super(sb);
+out:
+	mutex_unlock(&bdev->bd_fsfreeze_mutex);
+
+	return ret;
+}
+
 static int blkdev_writepage(struct page *page, struct writeback_control *wbc)
 {
 	return block_write_full_page(page, blkdev_get_block, wbc);
include/linux/fs.h +7 -0
diff -urNp linux-3.0-rc7-orig/include/linux/fs.h linux-3.0-rc7/include/linux/fs.h
@@ -317,6 +317,7 @@ struct inodes_stat_t {
 #define BLKPBSZGET _IO(0x12,123)
 #define BLKDISCARDZEROES _IO(0x12,124)
 #define BLKSECDISCARD _IO(0x12,125)
+#define BLKISFROZEN _IOR(0x12,126, int) /* get file system freeze state */
 
 #define BMAP_IOCTL 1		/* obsolete - kept for compatibility */
 #define FIBMAP	   _IO(0x00,1)	/* bmap access */
@@ -2036,6 +2039,7 @@ extern int sync_blockdev(struct block_de
 extern struct super_block *freeze_bdev(struct block_device *);
 extern void emergency_thaw_all(void);
 extern int thaw_bdev(struct block_device *bdev, struct super_block *sb);
+extern int isfrozen_bdev(struct block_device *bdev);
 extern int fsync_bdev(struct block_device *);
 #else
 static inline void bd_forget(struct inode *inode) {}
@@ -2051,6 +2055,11 @@ static inline int thaw_bdev(struct block
 {
 	return 0;
 }
+
+static int isfrozen_bdev(struct block_device *bdev)
+{
+	return 0;
+}
 #endif
 extern int sync_filesystem(struct super_block *);
 extern const struct file_operations def_blk_fops;
#6 lore raw

PATCH 3/4 fsfreeze: add ioctl to create a fd for freeze control

In reply to Fernando Luis Vazquez Cao

Adds a new ioctl, FIGETFREEZEFD, which freezes the indicated filesystem and returns a file descriptor; as long as that file descriptor is held open, the filesystem remains open.

---

fs/compat_ioctl.c +1 -0
diff -urNp linux-3.0-orig/fs/compat_ioctl.c linux-3.0/fs/compat_ioctl.c
@@ -882,6 +882,7 @@ COMPATIBLE_IOCTL(FIBMAP)
 COMPATIBLE_IOCTL(FIGETBSZ)
 /* 'X' - originally XFS but some now in the VFS */
 COMPATIBLE_IOCTL(FIFREEZE)
+COMPATIBLE_IOCTL(FIGETFREEZEFD)
 COMPATIBLE_IOCTL(FITHAW)
 COMPATIBLE_IOCTL(FIISFROZEN)
 COMPATIBLE_IOCTL(KDGETKEYCODE)
fs/ioctl.c +79 -0

97 lines not on this page · raw message

include/linux/fs.h +1 -0
diff -urNp linux-3.0-orig/include/linux/fs.h linux-3.0/include/linux/fs.h
@@ -326,6 +326,7 @@ struct inodes_stat_t {
 #define FITHAW		_IOWR('X', 120, int)	/* Thaw */
 #define FITRIM		_IOWR('X', 121, struct fstrim_range)	/* Trim */
 #define FIISFROZEN	_IOR('X', 122, int)	/* get sb freeze state */
+#define FIGETFREEZEFD	_IOWR('X', 123, int)
 
 #define	FS_IOC_GETFLAGS			_IOR('f', 1, long)
 #define	FS_IOC_SETFLAGS			_IOW('f', 2, long)
#7 lore raw

PATCH 4/4 fsfreeze: add freeze fd ioctls

In reply to Fernando Luis Vazquez Cao

Adds new ioctls (FS_FREEZE_FD, FS_THAW_FD, FS_ISFROZEN_FD) that operate on the freeze file descriptor; these can be used to freeze/thaw the filesystem and check the freeze state of the filesystem, thus avoiding the need to get new file descriptors constantly.

Additionaly, the argument of the ioctl FIGETFREEZEFD can now be used to indicate whether we want the filesystem frozen at fd creation time (a non-zero value means "freeze", 0 means "leave as is").

---

Documentation/ioctl/ioctl-number.txt +2 -0
diff -urNp linux-3.0-orig/Documentation/ioctl/ioctl-number.txt linux-3.0/Documentation/ioctl/ioctl-number.txt
@@ -301,6 +301,8 @@ Code  Seq#(hex)	Include File		Comments
 					<mailto:[email protected]>
 0xAE	all	linux/kvm.h		Kernel-based Virtual Machine
 					<mailto:[email protected]>
+0xAF	00-03	Filesystem freeze	see fs/ioctl.c
+					<mailto:[email protected]>
 0xB0	all	RATIO devices		in development:
 					<mailto:[email protected]>
 0xB1	00-1F	PPPoX			<mailto:[email protected]>
fs/ioctl.c +53 -5

104 lines not on this page · raw message

include/linux/fs.h +4 -0
diff -urNp linux-3.0-orig/include/linux/fs.h linux-3.0/include/linux/fs.h
@@ -328,6 +328,10 @@ struct inodes_stat_t {
 #define FIISFROZEN	_IOR('X', 122, int)	/* get sb freeze state */
 #define FIGETFREEZEFD	_IOWR('X', 123, int)
 
+#define FS_FREEZE_FD	_IOWR(0xAF, 1, int)
+#define FS_THAW_FD	_IOWR(0xAF, 2, int)
+#define FS_ISFROZEN_FD	_IOR(0xAF, 3, int)
+
 #define	FS_IOC_GETFLAGS			_IOR('f', 1, long)
 #define	FS_IOC_SETFLAGS			_IOW('f', 2, long)
 #define	FS_IOC_GETVERSION		_IOR('v', 1, long)