From patchwork Mon Aug 17 12:16:07 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [3/4] cifs: take reference to inode for oplock breaks Date: Mon, 17 Aug 2009 02:16:07 -0000 From: Jeff Layton X-Patchwork-Id: 31515 Message-Id: <1250511368-9812-4-git-send-email-jlayton@redhat.com> To: smfrench@gmail.com Cc: linux-cifs-client@lists.samba.org 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 --- 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); }