@@ -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
@@ -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);
@@ -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);
}
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@redhat.com> --- fs/cifs/cifsfs.c | 33 +++++++++++++++------------------ fs/cifs/misc.c | 9 +++++---- fs/cifs/transport.c | 1 + 3 files changed, 21 insertions(+), 22 deletions(-)