[PATCH][SMB3 client] fix oplock breaks when using multichannel

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



If a mount to a server is using multichannel, an oplock break arriving
on a secondary channel won't find the open file (since it won't find the
tcon for it), and this will cause each oplock break on secondary channels
to time out, slowing performance drastically.

Fix smb2_is_valid_oplock_break so that if it is a secondary channel and
an oplock break was not found, check for tcons (and the files hanging
off the tcons) on the primary channel.

Fixes xfstest generic/013 to ksmbd

Cc: <stable@xxxxxxxxxxxxxxx>


-- 
Thanks,

Steve
From 0186c0d4c418251f066cc8c8d80f16783f9ded00 Mon Sep 17 00:00:00 2001
From: Steve French <stfrench@xxxxxxxxxxxxx>
Date: Fri, 28 Oct 2022 01:13:16 -0500
Subject: [PATCH] smb3: fix oplock break when using multichannel

If a mount to a server is using multichannel, an oplock break arriving
on a secondary channel won't find the open file (since it won't find the
tcon for it).

Fix smb2_is_valid_oplock_break so that if it is a secondary channel and
an oplock break was not found, check for tcons (and the files hanging
off the tcons) on the primary chanel.

Fixes xfstest generic/013 to ksmbd

Cc: <stable@xxxxxxxxxxxxxxx>
Signed-off-by: Steve French <stfrench@xxxxxxxxxxxxx>
---
 fs/cifs/smb2misc.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)

diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index a38720477966..ccd294e6c9d9 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -689,13 +689,56 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
 			return false;
 	}
 
-	cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel);
+	cifs_dbg(FYI, "oplock level 0x%x fid 0x%llx\n",
+		 rsp->OplockLevel, rsp->PersistentFid);
 
 	/* look up tcon based on tid & uid */
 	spin_lock(&cifs_tcp_ses_lock);
 	list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
 		list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+			spin_lock(&tcon->open_file_lock);
+			list_for_each_entry(cfile, &tcon->openFileList, tlist) {
+				if (rsp->PersistentFid !=
+				    cfile->fid.persistent_fid ||
+				    rsp->VolatileFid !=
+				    cfile->fid.volatile_fid)
+					continue;
+
+				cifs_dbg(FYI, "file id match, oplock break\n");
+				cifs_stats_inc(
+				    &tcon->stats.cifs_stats.num_oplock_brks);
+				cinode = CIFS_I(d_inode(cfile->dentry));
+				spin_lock(&cfile->file_info_lock);
+				if (!CIFS_CACHE_WRITE(cinode) &&
+				    rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE)
+					cfile->oplock_break_cancelled = true;
+				else
+					cfile->oplock_break_cancelled = false;
 
+				set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK,
+					&cinode->flags);
+
+				cfile->oplock_epoch = 0;
+				cfile->oplock_level = rsp->OplockLevel;
+
+				spin_unlock(&cfile->file_info_lock);
+
+				cifs_queue_oplock_break(cfile);
+
+				spin_unlock(&tcon->open_file_lock);
+				spin_unlock(&cifs_tcp_ses_lock);
+				return true;
+			}
+			spin_unlock(&tcon->open_file_lock);
+		}
+	}
+
+	/* if file not found and secondary, check primary channel */
+	if (server->primary_server == NULL)
+		goto valid_oplock_not_found;
+
+	list_for_each_entry(ses, &server->primary_server->smb_ses_list, smb_ses_list) {
+		list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
 			spin_lock(&tcon->open_file_lock);
 			list_for_each_entry(cfile, &tcon->openFileList, tlist) {
 				if (rsp->PersistentFid !=
@@ -732,6 +775,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
 			spin_unlock(&tcon->open_file_lock);
 		}
 	}
+
+valid_oplock_not_found:
 	spin_unlock(&cifs_tcp_ses_lock);
 	cifs_dbg(FYI, "No file id matched, oplock break ignored\n");
 	trace_smb3_oplock_not_found(0 /* no xid */, rsp->PersistentFid,
-- 
2.34.1


[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux