cifs: convert oplock breaks to use slow_work facility (try #4)
This is the fourth respin of the patch to convert oplock breaks to use the slow_work facility. A customer of ours was testing a backport of one of the earlier patchsets, and hit a "Busy inodes after umount..." problem. An oplock break job had raced with a umount, and the superblock got torn down and its memory reused. When the oplock break job tried to dereference the inode->i_sb, the kernel oopsed. This patchset has the oplock break job hold an inode and vfsmount reference until the oplock break completes. With this, there should be no need to take a tcon reference (the vfsmount implicitly holds one already). Currently, when an oplock break comes in there's a chance that the oplock break job won't occur if the allocation of the oplock_q_entry fails. There are also some rather nasty races in the allocation and handling these structs. Rather than allocating oplock queue entries when an oplock break comes in, add a few extra fields to the cifsFileInfo struct. Get rid of the dedicated cifs_oplock_thread as well and queue the oplock break job to the slow_work thread pool. This approach also has the advantage that the oplock break jobs can potentially run in parallel rather than be serialized like they are today. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
This commit is contained in:
committed by
Steve French
parent
48541bd3dd
commit
3bc303c254
@@ -32,7 +32,6 @@
|
||||
|
||||
extern mempool_t *cifs_sm_req_poolp;
|
||||
extern mempool_t *cifs_req_poolp;
|
||||
extern struct task_struct *oplockThread;
|
||||
|
||||
/* The xid serves as a useful identifier for each incoming vfs request,
|
||||
in a similar way to the mid which is useful to track each sent smb,
|
||||
@@ -500,6 +499,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
|
||||
struct cifsTconInfo *tcon;
|
||||
struct cifsInodeInfo *pCifsInode;
|
||||
struct cifsFileInfo *netfile;
|
||||
int rc;
|
||||
|
||||
cFYI(1, ("Checking for oplock break or dnotify response"));
|
||||
if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
|
||||
@@ -569,19 +569,30 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
|
||||
if (pSMB->Fid != netfile->netfid)
|
||||
continue;
|
||||
|
||||
read_unlock(&GlobalSMBSeslock);
|
||||
read_unlock(&cifs_tcp_ses_lock);
|
||||
/*
|
||||
* don't do anything if file is about to be
|
||||
* closed anyway.
|
||||
*/
|
||||
if (netfile->closePend) {
|
||||
read_unlock(&GlobalSMBSeslock);
|
||||
read_unlock(&cifs_tcp_ses_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
cFYI(1, ("file id match, oplock break"));
|
||||
pCifsInode = CIFS_I(netfile->pInode);
|
||||
pCifsInode->clientCanCacheAll = false;
|
||||
if (pSMB->OplockLevel == 0)
|
||||
pCifsInode->clientCanCacheRead = false;
|
||||
AllocOplockQEntry(netfile->pInode,
|
||||
netfile->netfid, tcon);
|
||||
cFYI(1, ("about to wake up oplock thread"));
|
||||
if (oplockThread)
|
||||
wake_up_process(oplockThread);
|
||||
|
||||
rc = slow_work_enqueue(&netfile->oplock_break);
|
||||
if (rc) {
|
||||
cERROR(1, ("failed to enqueue oplock "
|
||||
"break: %d\n", rc));
|
||||
} else {
|
||||
netfile->oplock_break_cancelled = false;
|
||||
}
|
||||
read_unlock(&GlobalSMBSeslock);
|
||||
read_unlock(&cifs_tcp_ses_lock);
|
||||
return true;
|
||||
}
|
||||
read_unlock(&GlobalSMBSeslock);
|
||||
|
Reference in New Issue
Block a user