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

Jeff Layton <[email protected]>
Newsgroups gmane.linux.file-systems.cifs
Message-ID <[email protected]>
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 <[email protected]>
---
 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.