diff mbox

[RFC/PATCH,3/3] ext4: add EXT4_IOC_GOINGDOWN ioctl

Message ID 20170202225924.19301-4-tytso@mit.edu
State Superseded, archived
Headers show

Commit Message

Theodore Ts'o Feb. 2, 2017, 10:59 p.m. UTC
This ioctl is modeled after the xfs's XFS_IOC_GOINGDOWN ioctl.  (In
fact, it uses the same code points.)

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h  | 10 ++++++++++
 fs/ext4/ioctl.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/super.c |  2 +-
 3 files changed, 53 insertions(+), 1 deletion(-)

Comments

Darrick Wong Feb. 3, 2017, 12:02 a.m. UTC | #1
On Thu, Feb 02, 2017 at 05:59:24PM -0500, Theodore Ts'o wrote:
> This ioctl is modeled after the xfs's XFS_IOC_GOINGDOWN ioctl.  (In
> fact, it uses the same code points.)
> 
> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> ---
>  fs/ext4/ext4.h  | 10 ++++++++++
>  fs/ext4/ioctl.c | 42 ++++++++++++++++++++++++++++++++++++++++++
>  fs/ext4/super.c |  2 +-
>  3 files changed, 53 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 94064b7ce16c..eae92f1a52e1 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -679,6 +679,16 @@ struct fsxattr {
>  #define EXT4_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
>  #define EXT4_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
>  
> +#define EXT4_IOC_GOiNGDOWN _IOR ('X', 125, __u32)

Lowercase 'i'?

ISTR Dave asking if we could rename it IOC_SHUTDOWN way back when f2fs
was trying to implement it.

> +
> +/*
> + * Flags for going down operation
> + */
> +#define EXT4_GOING_FLAGS_DEFAULT		0x0	/* going down */
> +#define EXT4_GOING_FLAGS_LOGFLUSH		0x1	/* flush log but not data */
> +#define EXT4_GOING_FLAGS_NOLOGFLUSH		0x2	/* don't flush log nor data */
> +
> +
>  #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
>  /*
>   * ioctl commands in 32 bit emulation
> diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
> index d534399cf607..6d5443cf4a47 100644
> --- a/fs/ext4/ioctl.c
> +++ b/fs/ext4/ioctl.c
> @@ -442,6 +442,45 @@ static inline unsigned long ext4_xflags_to_iflags(__u32 xflags)
>  	return iflags;
>  }
>  
> +int ext4_goingdown(struct super_block *sb, unsigned long arg)
> +{
> +	struct ext4_sb_info *sbi = EXT4_SB(sb);
> +	__u32 flags;
> +
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +
> +	if (get_user(flags, (__u32 __user *)arg))
> +		return -EFAULT;
> +
> +	if (flags > EXT4_GOING_FLAGS_NOLOGFLUSH)
> +		return -EINVAL;
> +
> +	if (ext4_forced_shutdown(sbi))
> +		return 0;
> +
> +	switch (flags) {
> +	case EXT4_GOING_FLAGS_DEFAULT:
> +		freeze_bdev(sb->s_bdev);
> +		set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
> +		thaw_bdev(sb->s_bdev, sb);
> +		break;
> +	case EXT4_GOING_FLAGS_LOGFLUSH:
> +		if (sbi->s_journal && !is_journal_aborted(sbi->s_journal))
> +		    (void) ext4_force_commit(sb);
> +		/* fall through */
> +	case EXT4_GOING_FLAGS_NOLOGFLUSH:
> +		if (sbi->s_journal && !is_journal_aborted(sbi->s_journal))
> +			(void) jbd2_journal_destroy(sbi->s_journal);
> +		sbi->s_journal = NULL;
> +		set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	return 0;

/me wonders if mount -o errors=shutdown follows from this? :)

(Something a little less drastic than errors=panic that stops everything
in its tracks.)

--D

> +}
> +
>  long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  {
>  	struct inode *inode = file_inode(filp);
> @@ -893,6 +932,8 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  
>  		return 0;
>  	}
> +	case EXT4_IOC_GOiNGDOWN:
> +		return ext4_goingdown(sb, arg);
>  	default:
>  		return -ENOTTY;
>  	}
> @@ -959,6 +1000,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  	case EXT4_IOC_SET_ENCRYPTION_POLICY:
>  	case EXT4_IOC_GET_ENCRYPTION_PWSALT:
>  	case EXT4_IOC_GET_ENCRYPTION_POLICY:
> +	case EXT4_IOC_GOiNGDOWN:
>  		break;
>  	default:
>  		return -ENOIOCTLCMD;
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 829e4a7b59e4..8db181c4dad6 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -4797,7 +4797,7 @@ static int ext4_freeze(struct super_block *sb)
>   */
>  static int ext4_unfreeze(struct super_block *sb)
>  {
> -	if (sb->s_flags & MS_RDONLY)
> +	if ((sb->s_flags & MS_RDONLY) || ext4_forced_shutdown(EXT4_SB(sb)))
>  		return 0;
>  
>  	if (EXT4_SB(sb)->s_journal) {
> -- 
> 2.11.0.rc0.7.gbe5a750
>
Theodore Ts'o Feb. 3, 2017, 2:22 a.m. UTC | #2
On Thu, Feb 02, 2017 at 04:02:04PM -0800, Darrick J. Wong wrote:
> >  
> > +#define EXT4_IOC_GOiNGDOWN _IOR ('X', 125, __u32)
> 
> Lowercase 'i'?

Yeah, oops.  Already fixed.

> ISTR Dave asking if we could rename it IOC_SHUTDOWN way back when f2fs
> was trying to implement it.

Yeah, I decided to evade the whole discussion about which names should
land in include/uapi/fs.h.  (e.g., xxx_GOING_FLAGS_LOGFLUSH vs
xxx_GOING_FLAGS_METAFLUsh, etc.)

Once we have names that everyone is happy with, it'll be simple enough
to do the rename the identifiers.

> 
> /me wonders if mount -o errors=shutdown follows from this? :)
> 
> (Something a little less drastic than errors=panic that stops everything
> in its tracks.)

Yeah, I was thinking about that.  We have errors=readonly already,
which is actually not _that_ different from errors=shutdown.  (I
noticed for example that xfs doesn't have a shutdown check in
xfs_vm_readpage and and xfs_vm_readpages.)

One of the things we probably should do is make clear what are the
semantics with FS_IOC_SHUTDOWN.  For example, should readpages()
return an error on a shutdown file system, or not?

And as I've observed already, there are a number of tests in xfstsests
that are enabled when the fs supports shutdown that are very specific
to how delayed allocation and writes are handled.  How much this needs
to be fundamental to the shutdown API?  A lot of this depends on which
applications would actually be using shutdown, and whether they care
about what happens with a write followed by truncate followed
shutdown.

						- Ted
diff mbox

Patch

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 94064b7ce16c..eae92f1a52e1 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -679,6 +679,16 @@  struct fsxattr {
 #define EXT4_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
 #define EXT4_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
 
+#define EXT4_IOC_GOiNGDOWN _IOR ('X', 125, __u32)
+
+/*
+ * Flags for going down operation
+ */
+#define EXT4_GOING_FLAGS_DEFAULT		0x0	/* going down */
+#define EXT4_GOING_FLAGS_LOGFLUSH		0x1	/* flush log but not data */
+#define EXT4_GOING_FLAGS_NOLOGFLUSH		0x2	/* don't flush log nor data */
+
+
 #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
 /*
  * ioctl commands in 32 bit emulation
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index d534399cf607..6d5443cf4a47 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -442,6 +442,45 @@  static inline unsigned long ext4_xflags_to_iflags(__u32 xflags)
 	return iflags;
 }
 
+int ext4_goingdown(struct super_block *sb, unsigned long arg)
+{
+	struct ext4_sb_info *sbi = EXT4_SB(sb);
+	__u32 flags;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (get_user(flags, (__u32 __user *)arg))
+		return -EFAULT;
+
+	if (flags > EXT4_GOING_FLAGS_NOLOGFLUSH)
+		return -EINVAL;
+
+	if (ext4_forced_shutdown(sbi))
+		return 0;
+
+	switch (flags) {
+	case EXT4_GOING_FLAGS_DEFAULT:
+		freeze_bdev(sb->s_bdev);
+		set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
+		thaw_bdev(sb->s_bdev, sb);
+		break;
+	case EXT4_GOING_FLAGS_LOGFLUSH:
+		if (sbi->s_journal && !is_journal_aborted(sbi->s_journal))
+		    (void) ext4_force_commit(sb);
+		/* fall through */
+	case EXT4_GOING_FLAGS_NOLOGFLUSH:
+		if (sbi->s_journal && !is_journal_aborted(sbi->s_journal))
+			(void) jbd2_journal_destroy(sbi->s_journal);
+		sbi->s_journal = NULL;
+		set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct inode *inode = file_inode(filp);
@@ -893,6 +932,8 @@  long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
 		return 0;
 	}
+	case EXT4_IOC_GOiNGDOWN:
+		return ext4_goingdown(sb, arg);
 	default:
 		return -ENOTTY;
 	}
@@ -959,6 +1000,7 @@  long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case EXT4_IOC_SET_ENCRYPTION_POLICY:
 	case EXT4_IOC_GET_ENCRYPTION_PWSALT:
 	case EXT4_IOC_GET_ENCRYPTION_POLICY:
+	case EXT4_IOC_GOiNGDOWN:
 		break;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 829e4a7b59e4..8db181c4dad6 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -4797,7 +4797,7 @@  static int ext4_freeze(struct super_block *sb)
  */
 static int ext4_unfreeze(struct super_block *sb)
 {
-	if (sb->s_flags & MS_RDONLY)
+	if ((sb->s_flags & MS_RDONLY) || ext4_forced_shutdown(EXT4_SB(sb)))
 		return 0;
 
 	if (EXT4_SB(sb)->s_journal) {