Commit 6640d8f3 authored by Trond Myklebust's avatar Trond Myklebust Committed by Linus Torvalds

[PATCH] Fix posix file locking (7/9)

VFS,CIFS,NLM,NFSv4: make filesystems directly responsible for calling
   posix_lock_file() if they need it. This fixes an NFS race whereby
   in case of a server reboot, the recovery thread could re-establish
   a lock that had just been freed.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent d0f8f3f9
......@@ -569,6 +569,8 @@ cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
netfid, length,
pfLock->fl_start, numUnlock, numLock, lockType,
wait_flag);
if (rc == 0 && (pfLock->fl_flags & FL_POSIX))
posix_lock_file(file, pfLock);
FreeXid(xid);
return rc;
}
......
......@@ -561,6 +561,10 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
if (resp->status == NLM_LCK_GRANTED) {
fl->fl_u.nfs_fl.state = host->h_state;
fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
fl->fl_flags |= FL_SLEEP;
if (posix_lock_file_wait(fl->fl_file, fl) < 0)
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
__FUNCTION__);
}
status = nlm_stat_to_errno(resp->status);
out:
......@@ -627,6 +631,9 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
if (req->a_flags & RPC_TASK_ASYNC) {
status = nlmclnt_async_call(req, NLMPROC_UNLOCK,
nlmclnt_unlock_callback);
/* Hrmf... Do the unlock early since locks_remove_posix()
* really expects us to free the lock synchronously */
posix_lock_file(fl->fl_file, fl);
if (status < 0) {
nlmclnt_release_lockargs(req);
kfree(req);
......@@ -639,6 +646,7 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
if (status < 0)
return status;
posix_lock_file(fl->fl_file, fl);
if (resp->status == NLM_LCK_GRANTED)
return 0;
......@@ -669,7 +677,6 @@ nlmclnt_unlock_callback(struct rpc_task *task)
}
if (status != NLM_LCK_GRANTED)
printk(KERN_WARNING "lockd: unexpected unlock status: %d\n", status);
die:
nlm_release_host(req->a_host);
nlmclnt_release_lockargs(req);
......
......@@ -906,6 +906,34 @@ int posix_lock_file(struct file *filp, struct file_lock *fl)
return __posix_lock_file(filp->f_dentry->d_inode, fl);
}
/**
* posix_lock_file_wait - Apply a POSIX-style lock to a file
* @filp: The file to apply the lock to
* @fl: The lock to be applied
*
* Add a POSIX style lock to a file.
* We merge adjacent & overlapping locks whenever possible.
* POSIX locks are sorted by owner task, then by starting address
*/
int posix_lock_file_wait(struct file *filp, struct file_lock *fl)
{
int error;
might_sleep ();
for (;;) {
error = __posix_lock_file(filp->f_dentry->d_inode, fl);
if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP))
break;
error = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
if (!error)
continue;
locks_delete_block(fl);
break;
}
return error;
}
EXPORT_SYMBOL(posix_lock_file_wait);
/**
* locks_mandatory_locked - Check for an active lock
* @inode: the file to check
......@@ -1483,8 +1511,7 @@ int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock __user *l)
if (filp->f_op && filp->f_op->lock != NULL) {
error = filp->f_op->lock(filp, cmd, file_lock);
if (error < 0)
goto out;
goto out;
}
for (;;) {
......@@ -1618,8 +1645,7 @@ int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l)
if (filp->f_op && filp->f_op->lock != NULL) {
error = filp->f_op->lock(filp, cmd, file_lock);
if (error < 0)
goto out;
goto out;
}
for (;;) {
......@@ -1671,7 +1697,7 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner)
if (filp->f_op && filp->f_op->lock != NULL) {
filp->f_op->lock(filp, F_SETLK, &lock);
/* Ignore any error -- we must remove the locks anyway */
goto out;
}
/* Can't use posix_lock_file here; we need to remove it no matter
......@@ -1687,6 +1713,7 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner)
before = &fl->fl_next;
}
unlock_kernel();
out:
if (lock.fl_ops && lock.fl_ops->fl_release_private)
lock.fl_ops->fl_release_private(&lock);
}
......
......@@ -1856,6 +1856,8 @@ nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
nfs4_put_lock_state(lsp);
out:
up(&state->lock_sema);
if (status == 0)
posix_lock_file(request->fl_file, request);
return nfs4_map_errors(status);
}
......@@ -1932,6 +1934,12 @@ nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
nfs4_put_lock_state(lsp);
out:
up(&state->lock_sema);
if (status == 0) {
/* Note: we always want to sleep here! */
request->fl_flags |= FL_SLEEP;
if (posix_lock_file_wait(request->fl_file, request) < 0)
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
}
return nfs4_map_errors(status);
}
......
......@@ -690,6 +690,7 @@ extern void locks_remove_posix(struct file *, fl_owner_t);
extern void locks_remove_flock(struct file *);
extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
extern int posix_lock_file(struct file *, struct file_lock *);
extern int posix_lock_file_wait(struct file *, struct file_lock *);
extern void posix_block_lock(struct file_lock *, struct file_lock *);
extern void posix_unblock_lock(struct file *, struct file_lock *);
extern int posix_locks_deadlock(struct file_lock *, struct file_lock *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment