diff mbox series

cifs: add shutdown support

Message ID CAH2r5mvKfohJ=NkkCM7AxqRYq2+D8kRMTxP3VdG=W0s72Cdh0A@mail.gmail.com (mailing list archive)
State New, archived
Headers show
Series cifs: add shutdown support | expand

Commit Message

Steve French April 29, 2021, 5:33 a.m. UTC
Various filesystem support the shutdown ioctl which is used by various
xfstests. The shutdown ioctl sets a flag on the superblock which
prevents open, unlink, symlink, hardlink, rmdir, create etc.
on the file system until unmount and remounted. The two flags supported
in this patch are:

  FSOP_GOING_FLAGS_LOGFLUSH and FSOP_GOING_FLAGS_NOLOGFLUSH

which require very little other than blocking new operations (since
we do not cache writes to metadata on the client with cifs.ko).
FSOP_GOING_FLAGS_DEFAULT is not supported yet, but could be added in
the future but would need to call syncfs or equivalent to write out
pending data on the mount.  See
https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html for a
description of the flags

With this patch various xfstests now work including tests 043 through
046 for example.

Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/cifs_fs_sb.h |  1 +
 fs/cifs/cifs_ioctl.h | 16 +++++++++++++
 fs/cifs/dir.c        | 10 +++++++++
 fs/cifs/file.c       |  6 +++++
 fs/cifs/inode.c      | 25 +++++++++++++++++++--
 fs/cifs/ioctl.c      | 53 ++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/link.c       |  7 ++++++
 fs/cifs/xattr.c      |  4 ++++
 8 files changed, 120 insertions(+), 2 deletions(-)

Comments

Aurélien Aptel April 29, 2021, 9:29 a.m. UTC | #1
Do we need to add code to clear the flag on remount or is it implicitely
cleared by not copying it?

Steve French <smfrench@gmail.com> writes:
>  #define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from
> special ACE */
>  #define CIFS_MOUNT_RO_CACHE 0x20000000  /* assumes share will not change */
>  #define CIFS_MOUNT_RW_CACHE 0x40000000  /* assumes only client accessing */
> +#define SMB3_MOUNT_SHUTDOWN 0x80000000

While I totally understand wanting to remove the "cifs" name, those
flags are specific to the kernel & filesystem and have nothing to do
with the wire protocol so in this case I would rather use CIFS_ prefix
because SMB3_ is misleading and all the other flags are already using CIFS_.

One day we should do s/CIFS/SMBFS/i on the whole tree where CIFS refers
to kernel stuff (not protocol) and rename the module smbfs. But that's a
story for another day I guess.

>
>  struct cifs_sb_info {
>   struct rb_root tlink_tree;
> diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h
> index 153d5c842a9b..a744022d2a71 100644
> --- a/fs/cifs/cifs_ioctl.h
> +++ b/fs/cifs/cifs_ioctl.h
> @@ -78,3 +78,19 @@ struct smb3_notify {
>  #define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)
>  #define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
>  #define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)
> +#define SMB3_IOC_SHUTDOWN _IOR ('X', 125, __u32)

Same

> +
> +/*
> + * Flags for going down operation
> + */
> +#define SMB3_GOING_FLAGS_DEFAULT                0x0     /* going down */
> +#define SMB3_GOING_FLAGS_LOGFLUSH               0x1     /* flush log
> but not data */
> +#define SMB3_GOING_FLAGS_NOLOGFLUSH             0x2     /* don't

Same

> flush log nor data */
> +
> +static inline bool smb3_forced_shutdown(struct cifs_sb_info *sbi)

Same

> +	cifs_dbg(VFS, "shut down requested (%d)", flags); /* BB FIXME */
> +/*	trace_smb3_shutdown(sb, flags);*/

What is there to fix? It's doing like ext4 so it's fine no?

> +
> +	/*
> +	 * see:
> +	 *   https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html
> +	 * for more information and description of original intent of the flags
> +	 */
> +	switch (flags) {
> +	/*
> +	 * We could add support later for default flag which requires:
> +	 *     "Flush all dirty data and metadata to disk"
> +	 * would need to call syncfs or equivalent to flush page cache for
> +	 * the mount and then issue fsync to server (if nostrictsync not set)
> +	 */
> +	case SMB3_GOING_FLAGS_DEFAULT:
> +		cifs_dbg(VFS, "default flags\n");

Should this be removed, less verbose or more info should be printed?

Cheers,
Steve French April 29, 2021, 6:39 p.m. UTC | #2
You are correct - I have to add the code to clear the bit on remount

I have added your other changes and will send updated patch after lunch

root@smfrench-ThinkPad-P52:~# mount | grep cifs
//localhost/test on /mnt1 type cifs
(rw,relatime,vers=3.1.1,cache=strict,username=smfrench,uid=0,noforceuid,gid=0,noforcegid,addr=127.0.0.1,file_mode=0755,dir_mode=0755,soft,nounix,serverino,mapposix,noperm,rsize=4194304,wsize=4194304,bsize=1048576,echo_interval=60,actimeo=1)
root@smfrench-ThinkPad-P52:~# touch /mnt1/file
root@smfrench-ThinkPad-P52:~# ~smfrench/xfstests-dev/src/godown /mnt1/
root@smfrench-ThinkPad-P52:~# touch /mnt1/file
touch: cannot touch '/mnt1/file': Input/output error
root@smfrench-ThinkPad-P52:~# mount -o remount /mnt1
root@smfrench-ThinkPad-P52:~# touch /mnt1/file
touch: cannot touch '/mnt1/file': Input/output error

On Thu, Apr 29, 2021 at 4:29 AM Aurélien Aptel <aaptel@suse.com> wrote:
>
>
> Do we need to add code to clear the flag on remount or is it implicitely
> cleared by not copying it?
>
> Steve French <smfrench@gmail.com> writes:
> >  #define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from
> > special ACE */
> >  #define CIFS_MOUNT_RO_CACHE 0x20000000  /* assumes share will not change */
> >  #define CIFS_MOUNT_RW_CACHE 0x40000000  /* assumes only client accessing */
> > +#define SMB3_MOUNT_SHUTDOWN 0x80000000
>
> While I totally understand wanting to remove the "cifs" name, those
> flags are specific to the kernel & filesystem and have nothing to do
> with the wire protocol so in this case I would rather use CIFS_ prefix
> because SMB3_ is misleading and all the other flags are already using CIFS_.
>
> One day we should do s/CIFS/SMBFS/i on the whole tree where CIFS refers
> to kernel stuff (not protocol) and rename the module smbfs. But that's a
> story for another day I guess.
>
> >
> >  struct cifs_sb_info {
> >   struct rb_root tlink_tree;
> > diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h
> > index 153d5c842a9b..a744022d2a71 100644
> > --- a/fs/cifs/cifs_ioctl.h
> > +++ b/fs/cifs/cifs_ioctl.h
> > @@ -78,3 +78,19 @@ struct smb3_notify {
> >  #define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)
> >  #define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
> >  #define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)
> > +#define SMB3_IOC_SHUTDOWN _IOR ('X', 125, __u32)
>
> Same
>
> > +
> > +/*
> > + * Flags for going down operation
> > + */
> > +#define SMB3_GOING_FLAGS_DEFAULT                0x0     /* going down */
> > +#define SMB3_GOING_FLAGS_LOGFLUSH               0x1     /* flush log
> > but not data */
> > +#define SMB3_GOING_FLAGS_NOLOGFLUSH             0x2     /* don't
>
> Same
>
> > flush log nor data */
> > +
> > +static inline bool smb3_forced_shutdown(struct cifs_sb_info *sbi)
>
> Same
>
> > +     cifs_dbg(VFS, "shut down requested (%d)", flags); /* BB FIXME */
> > +/*   trace_smb3_shutdown(sb, flags);*/
>
> What is there to fix? It's doing like ext4 so it's fine no?
>
> > +
> > +     /*
> > +      * see:
> > +      *   https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html
> > +      * for more information and description of original intent of the flags
> > +      */
> > +     switch (flags) {
> > +     /*
> > +      * We could add support later for default flag which requires:
> > +      *     "Flush all dirty data and metadata to disk"
> > +      * would need to call syncfs or equivalent to flush page cache for
> > +      * the mount and then issue fsync to server (if nostrictsync not set)
> > +      */
> > +     case SMB3_GOING_FLAGS_DEFAULT:
> > +             cifs_dbg(VFS, "default flags\n");
>
> Should this be removed, less verbose or more info should be printed?
>
> Cheers,
> --
> Aurélien Aptel / SUSE Labs Samba Team
> GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
>
Steve French April 30, 2021, 12:07 a.m. UTC | #3
Updated patch attached.   Fixed the remount issue (remount should
clear the shutdown flag). See below simple demonstration:

root@smfrench-ThinkPad-P52:~# mount | grep cifs
//localhost/test on /mnt1 type cifs
(rw,relatime,vers=3.1.1,cache=strict,username=smfrench,uid=0,noforceuid,gid=0,noforcegid,addr=127.0.0.1,file_mode=0755,dir_mode=0755,soft,nounix,serverino,mapposix,noperm,rsize=4194304,wsize=4194304,bsize=1048576,echo_interval=60,actimeo=1)
root@smfrench-ThinkPad-P52:~# touch /mnt1/file
root@smfrench-ThinkPad-P52:~# ~smfrench/xfstests-dev/src/godown /mnt1/
root@smfrench-ThinkPad-P52:~# touch /mnt1/file
touch: cannot touch '/mnt1/file': Input/output error
root@smfrench-ThinkPad-P52:~# mount -t cifs //localhost/test /mnt1 -o remount
root@smfrench-ThinkPad-P52:~# touch /mnt1/file

On Thu, Apr 29, 2021 at 1:39 PM Steve French <smfrench@gmail.com> wrote:
>
> You are correct - I have to add the code to clear the bit on remount
>
> I have added your other changes and will send updated patch after lunch
>
> root@smfrench-ThinkPad-P52:~# mount | grep cifs
> //localhost/test on /mnt1 type cifs
> (rw,relatime,vers=3.1.1,cache=strict,username=smfrench,uid=0,noforceuid,gid=0,noforcegid,addr=127.0.0.1,file_mode=0755,dir_mode=0755,soft,nounix,serverino,mapposix,noperm,rsize=4194304,wsize=4194304,bsize=1048576,echo_interval=60,actimeo=1)
> root@smfrench-ThinkPad-P52:~# touch /mnt1/file
> root@smfrench-ThinkPad-P52:~# ~smfrench/xfstests-dev/src/godown /mnt1/
> root@smfrench-ThinkPad-P52:~# touch /mnt1/file
> touch: cannot touch '/mnt1/file': Input/output error
> root@smfrench-ThinkPad-P52:~# mount -o remount /mnt1
> root@smfrench-ThinkPad-P52:~# touch /mnt1/file
> touch: cannot touch '/mnt1/file': Input/output error
>
> On Thu, Apr 29, 2021 at 4:29 AM Aurélien Aptel <aaptel@suse.com> wrote:
> >
> >
> > Do we need to add code to clear the flag on remount or is it implicitely
> > cleared by not copying it?
> >
> > Steve French <smfrench@gmail.com> writes:
> > >  #define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from
> > > special ACE */
> > >  #define CIFS_MOUNT_RO_CACHE 0x20000000  /* assumes share will not change */
> > >  #define CIFS_MOUNT_RW_CACHE 0x40000000  /* assumes only client accessing */
> > > +#define SMB3_MOUNT_SHUTDOWN 0x80000000
> >
> > While I totally understand wanting to remove the "cifs" name, those
> > flags are specific to the kernel & filesystem and have nothing to do
> > with the wire protocol so in this case I would rather use CIFS_ prefix
> > because SMB3_ is misleading and all the other flags are already using CIFS_.
> >
> > One day we should do s/CIFS/SMBFS/i on the whole tree where CIFS refers
> > to kernel stuff (not protocol) and rename the module smbfs. But that's a
> > story for another day I guess.
> >
> > >
> > >  struct cifs_sb_info {
> > >   struct rb_root tlink_tree;
> > > diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h
> > > index 153d5c842a9b..a744022d2a71 100644
> > > --- a/fs/cifs/cifs_ioctl.h
> > > +++ b/fs/cifs/cifs_ioctl.h
> > > @@ -78,3 +78,19 @@ struct smb3_notify {
> > >  #define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)
> > >  #define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
> > >  #define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)
> > > +#define SMB3_IOC_SHUTDOWN _IOR ('X', 125, __u32)
> >
> > Same
> >
> > > +
> > > +/*
> > > + * Flags for going down operation
> > > + */
> > > +#define SMB3_GOING_FLAGS_DEFAULT                0x0     /* going down */
> > > +#define SMB3_GOING_FLAGS_LOGFLUSH               0x1     /* flush log
> > > but not data */
> > > +#define SMB3_GOING_FLAGS_NOLOGFLUSH             0x2     /* don't
> >
> > Same
> >
> > > flush log nor data */
> > > +
> > > +static inline bool smb3_forced_shutdown(struct cifs_sb_info *sbi)
> >
> > Same
> >
> > > +     cifs_dbg(VFS, "shut down requested (%d)", flags); /* BB FIXME */
> > > +/*   trace_smb3_shutdown(sb, flags);*/
> >
> > What is there to fix? It's doing like ext4 so it's fine no?
> >
> > > +
> > > +     /*
> > > +      * see:
> > > +      *   https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html
> > > +      * for more information and description of original intent of the flags
> > > +      */
> > > +     switch (flags) {
> > > +     /*
> > > +      * We could add support later for default flag which requires:
> > > +      *     "Flush all dirty data and metadata to disk"
> > > +      * would need to call syncfs or equivalent to flush page cache for
> > > +      * the mount and then issue fsync to server (if nostrictsync not set)
> > > +      */
> > > +     case SMB3_GOING_FLAGS_DEFAULT:
> > > +             cifs_dbg(VFS, "default flags\n");
> >
> > Should this be removed, less verbose or more info should be printed?
> >
> > Cheers,
> > --
> > Aurélien Aptel / SUSE Labs Samba Team
> > GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> > SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> > GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
> >
>
>
> --
> Thanks,
>
> Steve
Aurélien Aptel April 30, 2021, 4:41 p.m. UTC | #4
Hi,

Steve French <smfrench@gmail.com> writes:
> Updated patch attached.   Fixed the remount issue (remount should
> clear the shutdown flag). See below simple demonstration:

I think it looks OK

Reviewed-by: Aurelien Aptel <aaptel@suse.com>

Cheers,
diff mbox series

Patch

From e6e3c8dd9861fc977969d71703e776b51cf7fd4a Mon Sep 17 00:00:00 2001
From: Steve French <stfrench@microsoft.com>
Date: Thu, 29 Apr 2021 00:18:43 -0500
Subject: [PATCH] cifs: add shutdown support

Various filesystem support the shutdown ioctl which is used by various
xfstests. The shutdown ioctl sets a flag on the superblock which
prevents open, unlink, symlink, hardlink, rmdir, create etc.
on the file system until unmount and remounted. The two flags supported
in this patch are:

  FSOP_GOING_FLAGS_LOGFLUSH and FSOP_GOING_FLAGS_NOLOGFLUSH

which require very little other than blocking new operations (since
we do not cache writes to metadata on the client with cifs.ko).
FSOP_GOING_FLAGS_DEFAULT is not supported yet, but could be added in
the future but would need to call syncfs or equivalent to write out
pending data on the mount.

With this patch various xfstests now work including tests 043 through
046 for example.

Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/cifs_fs_sb.h |  1 +
 fs/cifs/cifs_ioctl.h | 16 +++++++++++++
 fs/cifs/dir.c        | 10 +++++++++
 fs/cifs/file.c       |  6 +++++
 fs/cifs/inode.c      | 25 +++++++++++++++++++--
 fs/cifs/ioctl.c      | 53 ++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/link.c       |  7 ++++++
 fs/cifs/xattr.c      |  4 ++++
 8 files changed, 120 insertions(+), 2 deletions(-)

diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
index 2a5325a7ae49..05de08143fcd 100644
--- a/fs/cifs/cifs_fs_sb.h
+++ b/fs/cifs/cifs_fs_sb.h
@@ -55,6 +55,7 @@ 
 #define CIFS_MOUNT_MODE_FROM_SID 0x10000000 /* retrieve mode from special ACE */
 #define CIFS_MOUNT_RO_CACHE	0x20000000  /* assumes share will not change */
 #define CIFS_MOUNT_RW_CACHE	0x40000000  /* assumes only client accessing */
+#define SMB3_MOUNT_SHUTDOWN	0x80000000
 
 struct cifs_sb_info {
 	struct rb_root tlink_tree;
diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h
index 153d5c842a9b..a744022d2a71 100644
--- a/fs/cifs/cifs_ioctl.h
+++ b/fs/cifs/cifs_ioctl.h
@@ -78,3 +78,19 @@  struct smb3_notify {
 #define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)
 #define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
 #define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)
+#define SMB3_IOC_SHUTDOWN _IOR ('X', 125, __u32)
+
+/*
+ * Flags for going down operation
+ */
+#define SMB3_GOING_FLAGS_DEFAULT                0x0     /* going down */
+#define SMB3_GOING_FLAGS_LOGFLUSH               0x1     /* flush log but not data */
+#define SMB3_GOING_FLAGS_NOLOGFLUSH             0x2     /* don't flush log nor data */
+
+static inline bool smb3_forced_shutdown(struct cifs_sb_info *sbi)
+{
+	if (SMB3_MOUNT_SHUTDOWN & sbi->mnt_cifs_flags)
+		return true;
+	else
+		return false;
+}
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 03afad8b24af..bc06a2dc6057 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -34,6 +34,7 @@ 
 #include "cifs_fs_sb.h"
 #include "cifs_unicode.h"
 #include "fs_context.h"
+#include "cifs_ioctl.h"
 
 static void
 renew_parental_timestamps(struct dentry *direntry)
@@ -429,6 +430,9 @@  cifs_atomic_open(struct inode *inode, struct dentry *direntry,
 	__u32 oplock;
 	struct cifsFileInfo *file_info;
 
+	if (unlikely(smb3_forced_shutdown(CIFS_SB(inode->i_sb))))
+		return -EIO;
+
 	/*
 	 * Posix open is only called (at lookup time) for file create now. For
 	 * opens (rather than creates), because we do not know if it is a file
@@ -545,6 +549,9 @@  int cifs_create(struct user_namespace *mnt_userns, struct inode *inode,
 	cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
 		 inode, direntry, direntry);
 
+	if (unlikely(smb3_forced_shutdown(CIFS_SB(inode->i_sb))))
+		return -EIO;
+
 	tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb));
 	rc = PTR_ERR(tlink);
 	if (IS_ERR(tlink))
@@ -582,6 +589,9 @@  int cifs_mknod(struct user_namespace *mnt_userns, struct inode *inode,
 		return -EINVAL;
 
 	cifs_sb = CIFS_SB(inode->i_sb);
+	if (unlikely(smb3_forced_shutdown(cifs_sb)))
+		return -EIO;
+
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink))
 		return PTR_ERR(tlink);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 5058252eeae6..bddeca06da08 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -45,6 +45,7 @@ 
 #include "fscache.h"
 #include "smbdirect.h"
 #include "fs_context.h"
+#include "cifs_ioctl.h"
 
 static inline int cifs_convert_flags(unsigned int flags)
 {
@@ -542,6 +543,11 @@  int cifs_open(struct inode *inode, struct file *file)
 	xid = get_xid();
 
 	cifs_sb = CIFS_SB(inode->i_sb);
+	if (unlikely(smb3_forced_shutdown(cifs_sb))) {
+		free_xid(xid);
+		return -EIO;
+	}
+
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink)) {
 		free_xid(xid);
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index baa9ffb4c446..35020686c66a 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -26,7 +26,6 @@ 
 #include <linux/sched/signal.h>
 #include <linux/wait_bit.h>
 #include <linux/fiemap.h>
-
 #include <asm/div64.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
@@ -38,7 +37,7 @@ 
 #include "cifs_unicode.h"
 #include "fscache.h"
 #include "fs_context.h"
-
+#include "cifs_ioctl.h"
 
 static void cifs_set_ops(struct inode *inode)
 {
@@ -1623,6 +1622,9 @@  int cifs_unlink(struct inode *dir, struct dentry *dentry)
 
 	cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry);
 
+	if (unlikely(smb3_forced_shutdown(cifs_sb)))
+		return -EIO;
+
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink))
 		return PTR_ERR(tlink);
@@ -1876,6 +1878,8 @@  int cifs_mkdir(struct user_namespace *mnt_userns, struct inode *inode,
 		 mode, inode);
 
 	cifs_sb = CIFS_SB(inode->i_sb);
+	if (unlikely(smb3_forced_shutdown(cifs_sb)))
+		return -EIO;
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink))
 		return PTR_ERR(tlink);
@@ -1958,6 +1962,11 @@  int cifs_rmdir(struct inode *inode, struct dentry *direntry)
 	}
 
 	cifs_sb = CIFS_SB(inode->i_sb);
+	if (unlikely(smb3_forced_shutdown(cifs_sb))) {
+		rc = -EIO;
+		goto rmdir_exit;
+	}
+
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink)) {
 		rc = PTR_ERR(tlink);
@@ -2092,6 +2101,9 @@  cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
 		return -EINVAL;
 
 	cifs_sb = CIFS_SB(source_dir->i_sb);
+	if (unlikely(smb3_forced_shutdown(cifs_sb)))
+		return -EIO;
+
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink))
 		return PTR_ERR(tlink);
@@ -2409,6 +2421,9 @@  int cifs_getattr(struct user_namespace *mnt_userns, const struct path *path,
 	struct inode *inode = d_inode(dentry);
 	int rc;
 
+	if (unlikely(smb3_forced_shutdown(CIFS_SB(inode->i_sb))))
+		return -EIO;
+
 	/*
 	 * We need to be sure that all dirty pages are written and the server
 	 * has actual ctime, mtime and file length.
@@ -2481,6 +2496,9 @@  int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
 	struct cifsFileInfo *cfile;
 	int rc;
 
+	if (unlikely(smb3_forced_shutdown(cifs_sb)))
+		return -EIO;
+
 	/*
 	 * We need to be sure that all dirty pages are written as they
 	 * might fill holes on the server.
@@ -2967,6 +2985,9 @@  cifs_setattr(struct user_namespace *mnt_userns, struct dentry *direntry,
 	struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb);
 	int rc, retries = 0;
 
+	if (unlikely(smb3_forced_shutdown(cifs_sb)))
+		return -EIO;
+
 	do {
 		if (pTcon->unix_ext)
 			rc = cifs_setattr_unix(direntry, attrs);
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 08d99fec593e..b34fa6a51a7b 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -164,6 +164,56 @@  static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int smb3_shutdown(struct super_block *sb, unsigned long arg)
+{
+	struct cifs_sb_info *sbi = CIFS_SB(sb);
+	__u32 flags;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (get_user(flags, (__u32 __user *)arg))
+		return -EFAULT;
+
+	if (flags > SMB3_GOING_FLAGS_NOLOGFLUSH)
+		return -EINVAL;
+
+	if (smb3_forced_shutdown(sbi))
+		return 0;
+
+	cifs_dbg(VFS, "shut down requested (%d)", flags); /* BB FIXME */
+/*	trace_smb3_shutdown(sb, flags);*/
+
+	/*
+	 * see:
+	 *   https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html
+	 * for more information and description of original intent of the flags
+	 */
+	switch (flags) {
+	/*
+	 * We could add support later for default flag which requires:
+	 *     "Flush all dirty data and metadata to disk"
+	 * would need to call syncfs or equivalent to flush page cache for
+	 * the mount and then issue fsync to server (if nostrictsync not set)
+	 */
+	case SMB3_GOING_FLAGS_DEFAULT:
+		cifs_dbg(VFS, "default flags\n");
+		return -EINVAL;
+	/*
+	 * FLAGS_LOGFLUSH is easy since it asks to write out metadata (not
+	 * data) but metadata writes are not cached on the client, so can treat
+	 * it similarly to NOLOGFLUSH
+	 */
+	case SMB3_GOING_FLAGS_LOGFLUSH:
+	case SMB3_GOING_FLAGS_NOLOGFLUSH:
+		sbi->mnt_cifs_flags |= SMB3_MOUNT_SHUTDOWN;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 {
 	struct inode *inode = file_inode(filep);
@@ -325,6 +375,9 @@  long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 				rc = -EOPNOTSUPP;
 			cifs_put_tlink(tlink);
 			break;
+		case SMB3_IOC_SHUTDOWN:
+			rc = smb3_shutdown(inode->i_sb, arg);
+			break;
 		default:
 			cifs_dbg(FYI, "unsupported ioctl\n");
 			break;
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index 616e1bc0cc0a..ef2e9d2c155d 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -30,6 +30,7 @@ 
 #include "cifs_fs_sb.h"
 #include "cifs_unicode.h"
 #include "smb2proto.h"
+#include "cifs_ioctl.h"
 
 /*
  * M-F Symlink Functions - Begin
@@ -518,6 +519,9 @@  cifs_hardlink(struct dentry *old_file, struct inode *inode,
 	struct TCP_Server_Info *server;
 	struct cifsInodeInfo *cifsInode;
 
+	if (unlikely(smb3_forced_shutdown(cifs_sb)))
+		return -EIO;
+
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink))
 		return PTR_ERR(tlink);
@@ -682,6 +686,9 @@  cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode,
 	void *page = alloc_dentry_path();
 	struct inode *newinode = NULL;
 
+	if (unlikely(smb3_forced_shutdown(cifs_sb)))
+		return -EIO;
+
 	xid = get_xid();
 
 	tlink = cifs_sb_tlink(cifs_sb);
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
index e351b945135b..c13f93e0e81b 100644
--- a/fs/cifs/xattr.c
+++ b/fs/cifs/xattr.c
@@ -30,6 +30,7 @@ 
 #include "cifs_debug.h"
 #include "cifs_fs_sb.h"
 #include "cifs_unicode.h"
+#include "cifs_ioctl.h"
 
 #define MAX_EA_VALUE_SIZE CIFSMaxBufSize
 #define CIFS_XATTR_CIFS_ACL "system.cifs_acl" /* DACL only */
@@ -421,6 +422,9 @@  ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
 	const char *full_path;
 	void *page;
 
+	if (unlikely(smb3_forced_shutdown(cifs_sb)))
+		return -EIO;
+
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
 		return -EOPNOTSUPP;
 
-- 
2.27.0