[linux-cifs-client] [PATCH 5/5] cifs: cancel oplock release callbacks during reconnect

Jeff Layton jlayton at redhat.com
Tue Aug 18 12:07:02 MDT 2009


cifs_oplock_thread has a check for pTcon->needs_reconnect and will skip
the CIFSSMBLock call if it's set.

Problem: what if the tcon has this set and then gets reconnected before
the call goes out on the wire? The oplock release isn't needed and could
be a bad thing at that point if the filehandle was reclaimed.

Cancel oplock release calls during a reconnect event.

Signed-off-by: Jeff Layton <jlayton at redhat.com>
---
 fs/cifs/cifsfs.c   |    2 +-
 fs/cifs/cifsglob.h |    1 +
 fs/cifs/connect.c  |    9 +++++++++
 fs/cifs/misc.c     |    1 +
 4 files changed, 12 insertions(+), 1 deletions(-)

diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 647c5bc..92e06c1 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -1024,7 +1024,7 @@ static int cifs_oplock_thread(void *dummyarg)
 			 * to server still is disconnected since oplock
 			 * already released by the server in that case
 			 */
-			if (!tcon->need_reconnect) {
+			if (!oplock->cancel) {
 				rc = CIFSSMBLock(0, tcon, oplock->netfid,
 						0 /* len */ , 0 /* offset */, 0,
 						0, LOCKING_ANDX_OPLOCK_RELEASE,
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 363dbcf..676c107 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -448,6 +448,7 @@ struct oplock_q_entry {
 	struct inode *pinode;
 	struct cifsTconInfo *tcon;
 	__u16 netfid;
+	bool cancel;
 };
 
 /* for pending dnotify requests */
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index f49304d..b7b67e9 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -121,6 +121,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
 	struct cifsSesInfo *ses;
 	struct cifsTconInfo *tcon;
 	struct mid_q_entry *mid_entry;
+	struct oplock_q_entry *oplock;
 
 	spin_lock(&GlobalMid_Lock);
 	if (server->tcpStatus == CifsExiting) {
@@ -137,6 +138,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
 
 	/* before reconnecting the tcp session, mark the smb session (uid)
 		and the tid bad so they are not used until reconnected */
+	spin_lock(&cifs_oplock_lock);
 	read_lock(&cifs_tcp_ses_lock);
 	list_for_each(tmp, &server->smb_ses_list) {
 		ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
@@ -145,9 +147,16 @@ cifs_reconnect(struct TCP_Server_Info *server)
 		list_for_each(tmp2, &ses->tcon_list) {
 			tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list);
 			tcon->need_reconnect = true;
+			list_for_each_entry(oplock, &cifs_oplock_list, qhead) {
+				if (oplock->tcon == tcon)
+					oplock->cancel = true;
+			}
 		}
+		
 	}
 	read_unlock(&cifs_tcp_ses_lock);
+	spin_unlock(&cifs_oplock_lock);
+
 	/* do not want to be sending data on a socket we are freeing */
 	mutex_lock(&server->srv_mutex);
 	if (server->ssocket) {
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 3bf3a52..4a2d297 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -612,6 +612,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv,
 				oplock->tcon = tcon;
 				oplock->pinode = inode;
 				oplock->netfid = netfile->netfid;
+				oplock->cancel = false;
 				spin_lock(&cifs_oplock_lock);
 				list_add_tail(&oplock->qhead,
 					      &cifs_oplock_list);
-- 
1.6.0.6



More information about the linux-cifs-client mailing list