| From 491eafce1a51c457701351a4bf40733799745314 Mon Sep 17 00:00:00 2001 |
| From: Steve French <stfrench@microsoft.com> |
| Date: Thu, 23 Mar 2023 16:20:02 -0500 |
| Subject: smb3: fix unusable share after force unmount failure |
| |
| From: Steve French <stfrench@microsoft.com> |
| |
| commit 491eafce1a51c457701351a4bf40733799745314 upstream. |
| |
| If user does forced unmount ("umount -f") while files are still open |
| on the share (as was seen in a Kubernetes example running on SMB3.1.1 |
| mount) then we were marking the share as "TID_EXITING" in umount_begin() |
| which caused all subsequent operations (except write) to fail ... but |
| unfortunately when umount_begin() is called we do not know yet that |
| there are open files or active references on the share that would prevent |
| unmount from succeeding. Kubernetes had example when they were doing |
| umount -f when files were open which caused the share to become |
| unusable until the files were closed (and the umount retried). |
| |
| Fix this so that TID_EXITING is not set until we are about to send |
| the tree disconnect (not at the beginning of forced umounts in |
| umount_begin) so that if "umount -f" fails (due to open files or |
| references) the mount is still usable. |
| |
| Cc: stable@vger.kernel.org |
| Reviewed-by: Shyam Prasad N <sprasad@microsoft.com> |
| Reviewed-by: Paulo Alcantara (SUSE) <pc@manguebit.com> |
| Signed-off-by: Steve French <stfrench@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/cifs/cifsfs.c | 9 ++++++--- |
| fs/cifs/cifssmb.c | 6 ++---- |
| fs/cifs/connect.c | 1 + |
| fs/cifs/smb2pdu.c | 8 ++------ |
| 4 files changed, 11 insertions(+), 13 deletions(-) |
| |
| --- a/fs/cifs/cifsfs.c |
| +++ b/fs/cifs/cifsfs.c |
| @@ -730,13 +730,16 @@ static void cifs_umount_begin(struct sup |
| spin_lock(&tcon->tc_lock); |
| if ((tcon->tc_count > 1) || (tcon->status == TID_EXITING)) { |
| /* we have other mounts to same share or we have |
| - already tried to force umount this and woken up |
| + already tried to umount this and woken up |
| all waiting network requests, nothing to do */ |
| spin_unlock(&tcon->tc_lock); |
| spin_unlock(&cifs_tcp_ses_lock); |
| return; |
| - } else if (tcon->tc_count == 1) |
| - tcon->status = TID_EXITING; |
| + } |
| + /* |
| + * can not set tcon->status to TID_EXITING yet since we don't know if umount -f will |
| + * fail later (e.g. due to open files). TID_EXITING will be set just before tdis req sent |
| + */ |
| spin_unlock(&tcon->tc_lock); |
| spin_unlock(&cifs_tcp_ses_lock); |
| |
| --- a/fs/cifs/cifssmb.c |
| +++ b/fs/cifs/cifssmb.c |
| @@ -85,13 +85,11 @@ cifs_reconnect_tcon(struct cifs_tcon *tc |
| |
| /* |
| * only tree disconnect, open, and write, (and ulogoff which does not |
| - * have tcon) are allowed as we start force umount |
| + * have tcon) are allowed as we start umount |
| */ |
| spin_lock(&tcon->tc_lock); |
| if (tcon->status == TID_EXITING) { |
| - if (smb_command != SMB_COM_WRITE_ANDX && |
| - smb_command != SMB_COM_OPEN_ANDX && |
| - smb_command != SMB_COM_TREE_DISCONNECT) { |
| + if (smb_command != SMB_COM_TREE_DISCONNECT) { |
| spin_unlock(&tcon->tc_lock); |
| cifs_dbg(FYI, "can not send cmd %d while umounting\n", |
| smb_command); |
| --- a/fs/cifs/connect.c |
| +++ b/fs/cifs/connect.c |
| @@ -2365,6 +2365,7 @@ cifs_put_tcon(struct cifs_tcon *tcon) |
| WARN_ON(tcon->tc_count < 0); |
| |
| list_del_init(&tcon->tcon_list); |
| + tcon->status = TID_EXITING; |
| spin_unlock(&tcon->tc_lock); |
| spin_unlock(&cifs_tcp_ses_lock); |
| |
| --- a/fs/cifs/smb2pdu.c |
| +++ b/fs/cifs/smb2pdu.c |
| @@ -225,13 +225,9 @@ smb2_reconnect(__le16 smb2_command, stru |
| spin_lock(&tcon->tc_lock); |
| if (tcon->status == TID_EXITING) { |
| /* |
| - * only tree disconnect, open, and write, |
| - * (and ulogoff which does not have tcon) |
| - * are allowed as we start force umount. |
| + * only tree disconnect allowed when disconnecting ... |
| */ |
| - if ((smb2_command != SMB2_WRITE) && |
| - (smb2_command != SMB2_CREATE) && |
| - (smb2_command != SMB2_TREE_DISCONNECT)) { |
| + if (smb2_command != SMB2_TREE_DISCONNECT) { |
| spin_unlock(&tcon->tc_lock); |
| cifs_dbg(FYI, "can not send cmd %d while umounting\n", |
| smb2_command); |