[linux-cifs-client] [PATCH 3/4] cifs: take reference to inode for oplock breaks

Jeff Layton jlayton at redhat.com
Mon Aug 17 06:16:07 MDT 2009


When an oplock break comes in, cifs needs to do things like writeback
the inode. It doesn't hold a reference to that inode in this case
however. Get an active reference to the inode when an oplock break
comes in. If we don't get a reference, we still need to create an oplock
queue entry so that the oplock release call gets done, but we'll want to
skip writeback in that case.

Signed-off-by: Jeff Layton <jlayton at redhat.com>
---
 fs/cifs/cifsfs.c    |   33 +++++++++++++++------------------
 fs/cifs/misc.c      |    9 +++++----
 fs/cifs/transport.c |    1 +
 3 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index ab4b373..4c724d5 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -977,6 +977,7 @@ static int cifs_oplock_thread(void *dummyarg)
 {
 	struct oplock_q_entry *oplock_item;
 	struct cifsTconInfo *tcon;
+	struct cifsInodeInfo *cifsi;
 	struct inode *inode;
 	__u16  netfid;
 	int rc, waitrc = 0;
@@ -1004,33 +1005,29 @@ static int cifs_oplock_thread(void *dummyarg)
 			list_del(&oplock_item->qhead);
 			kmem_cache_free(cifs_oplock_cachep, oplock_item);
 			mutex_unlock(&cifs_oplock_mutex);
-			/* can not grab inode sem here since it would
-				deadlock when oplock received on delete
-				since vfs_unlink holds the i_mutex across
-				the call */
-			/* mutex_lock(&inode->i_mutex);*/
-			if (S_ISREG(inode->i_mode)) {
+
+			if (inode && S_ISREG(inode->i_mode)) {
+				cifsi = CIFS_I(inode);
 #ifdef CONFIG_CIFS_EXPERIMENTAL
-				if (CIFS_I(inode)->clientCanCacheAll == 0)
+				if (cifsi->clientCanCacheAll == 0)
 					break_lease(inode, FMODE_READ);
-				else if (CIFS_I(inode)->clientCanCacheRead == 0)
+				else if (cifsi->clientCanCacheRead == 0)
 					break_lease(inode, FMODE_WRITE);
 #endif
 				rc = filemap_fdatawrite(inode->i_mapping);
-				if (CIFS_I(inode)->clientCanCacheRead == 0) {
+				if (cifsi->clientCanCacheRead == 0) {
 					waitrc = filemap_fdatawait(
 							      inode->i_mapping);
+					if (rc == 0)
+						rc = waitrc;
 					invalidate_remote_inode(inode);
 				}
-				if (rc == 0)
-					rc = waitrc;
-			} else
-				rc = 0;
-			/* mutex_unlock(&inode->i_mutex);*/
-			if (rc)
-				CIFS_I(inode)->write_behind_rc = rc;
-			cFYI(1, ("Oplock flush inode %p rc %d",
-				inode, rc));
+				if (rc)
+					cifsi->write_behind_rc = rc;
+				cFYI(1, ("Oplock flush inode %p rc %d",
+					 inode, rc));
+			}
+			iput(inode);
 
 				/* releasing stale oplock after recent reconnect
 				of smb session using a now incorrect file
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index e079a91..5bdd81c 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -500,6 +500,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
 	struct cifsTconInfo *tcon;
 	struct cifsInodeInfo *pCifsInode;
 	struct cifsFileInfo *netfile;
+	struct inode *inode;
 
 	cFYI(1, ("Checking for oplock break or dnotify response"));
 	if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
@@ -569,16 +570,16 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
 				if (pSMB->Fid != netfile->netfid)
 					continue;
 
-				write_unlock(&GlobalSMBSeslock);
-				read_unlock(&cifs_tcp_ses_lock);
 				cFYI(1, ("file id match, oplock break"));
 				pCifsInode = CIFS_I(netfile->pInode);
 				pCifsInode->clientCanCacheAll = false;
 				if (pSMB->OplockLevel == 0)
 					pCifsInode->clientCanCacheRead = false;
 				pCifsInode->oplockPending = true;
-				AllocOplockQEntry(netfile->pInode,
-						  netfile->netfid, tcon);
+				inode = igrab(netfile->pInode);
+				write_unlock(&GlobalSMBSeslock);
+				read_unlock(&cifs_tcp_ses_lock);
+				AllocOplockQEntry(inode, netfile->netfid, tcon);
 				cFYI(1, ("about to wake up oplock thread"));
 				if (oplockThread)
 					wake_up_process(oplockThread);
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 59f0e95..c52d27c 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -133,6 +133,7 @@ void DeleteTconOplockQEntries(struct cifsTconInfo *tcon)
 	mutex_lock(&cifs_oplock_mutex);
 	list_for_each_entry_safe(entry, next, &cifs_oplock_list, qhead) {
 		if (entry->tcon && entry->tcon == tcon) {
+			iput(entry->inode);
 			list_del(&entry->qhead);
 			kmem_cache_free(cifs_oplock_cachep, entry);
 		}
-- 
1.6.0.6



More information about the linux-cifs-client mailing list