Commit 2d56d3c4 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'server-cluster-locking-api' of git://linux-nfs.org/~bfields/linux

* 'server-cluster-locking-api' of git://linux-nfs.org/~bfields/linux:
  gfs2: nfs lock support for gfs2
  lockd: add code to handle deferred lock requests
  lockd: always preallocate block in nlmsvc_lock()
  lockd: handle test_lock deferrals
  lockd: pass cookie in nlmsvc_testlock
  lockd: handle fl_grant callbacks
  lockd: save lock state on deferral
  locks: add fl_grant callback for asynchronous lock return
  nfsd4: Convert NFSv4 to new lock interface
  locks: add lock cancel command
  locks: allow {vfs,posix}_lock_file to return conflicting lock
  locks: factor out generic/filesystem switch from setlock code
  locks: factor out generic/filesystem switch from test_lock
  locks: give posix_test_lock same interface as ->lock
  locks: make ->lock release private data before returning in GETLK case
  locks: create posix-to-flock helper functions
  locks: trivial removal of unnecessary parentheses
parents 0f9008ef 586759f0
...@@ -738,8 +738,7 @@ static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl) ...@@ -738,8 +738,7 @@ static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
if (cmd == F_GETLK) { if (cmd == F_GETLK) {
if (fc->no_lock) { if (fc->no_lock) {
if (!posix_test_lock(file, fl, fl)) posix_test_lock(file, fl);
fl->fl_type = F_UNLCK;
err = 0; err = 0;
} else } else
err = fuse_getlk(file, fl); err = fuse_getlk(file, fl);
......
...@@ -25,6 +25,15 @@ struct plock_op { ...@@ -25,6 +25,15 @@ struct plock_op {
struct gdlm_plock_info info; struct gdlm_plock_info info;
}; };
struct plock_xop {
struct plock_op xop;
void *callback;
void *fl;
void *file;
struct file_lock flc;
};
static inline void set_version(struct gdlm_plock_info *info) static inline void set_version(struct gdlm_plock_info *info)
{ {
info->version[0] = GDLM_PLOCK_VERSION_MAJOR; info->version[0] = GDLM_PLOCK_VERSION_MAJOR;
...@@ -64,12 +73,14 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name, ...@@ -64,12 +73,14 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name,
{ {
struct gdlm_ls *ls = lockspace; struct gdlm_ls *ls = lockspace;
struct plock_op *op; struct plock_op *op;
struct plock_xop *xop;
int rv; int rv;
op = kzalloc(sizeof(*op), GFP_KERNEL); xop = kzalloc(sizeof(*xop), GFP_KERNEL);
if (!op) if (!xop)
return -ENOMEM; return -ENOMEM;
op = &xop->xop;
op->info.optype = GDLM_PLOCK_OP_LOCK; op->info.optype = GDLM_PLOCK_OP_LOCK;
op->info.pid = fl->fl_pid; op->info.pid = fl->fl_pid;
op->info.ex = (fl->fl_type == F_WRLCK); op->info.ex = (fl->fl_type == F_WRLCK);
...@@ -79,9 +90,21 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name, ...@@ -79,9 +90,21 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name,
op->info.start = fl->fl_start; op->info.start = fl->fl_start;
op->info.end = fl->fl_end; op->info.end = fl->fl_end;
op->info.owner = (__u64)(long) fl->fl_owner; op->info.owner = (__u64)(long) fl->fl_owner;
if (fl->fl_lmops && fl->fl_lmops->fl_grant) {
xop->callback = fl->fl_lmops->fl_grant;
locks_init_lock(&xop->flc);
locks_copy_lock(&xop->flc, fl);
xop->fl = fl;
xop->file = file;
} else
xop->callback = NULL;
send_op(op); send_op(op);
if (xop->callback == NULL)
wait_event(recv_wq, (op->done != 0)); wait_event(recv_wq, (op->done != 0));
else
return -EINPROGRESS;
spin_lock(&ops_lock); spin_lock(&ops_lock);
if (!list_empty(&op->list)) { if (!list_empty(&op->list)) {
...@@ -99,7 +122,63 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name, ...@@ -99,7 +122,63 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name,
(unsigned long long)name->ln_number); (unsigned long long)name->ln_number);
} }
kfree(op); kfree(xop);
return rv;
}
/* Returns failure iff a succesful lock operation should be canceled */
static int gdlm_plock_callback(struct plock_op *op)
{
struct file *file;
struct file_lock *fl;
struct file_lock *flc;
int (*notify)(void *, void *, int) = NULL;
struct plock_xop *xop = (struct plock_xop *)op;
int rv = 0;
spin_lock(&ops_lock);
if (!list_empty(&op->list)) {
printk(KERN_INFO "plock op on list\n");
list_del(&op->list);
}
spin_unlock(&ops_lock);
/* check if the following 2 are still valid or make a copy */
file = xop->file;
flc = &xop->flc;
fl = xop->fl;
notify = xop->callback;
if (op->info.rv) {
notify(flc, NULL, op->info.rv);
goto out;
}
/* got fs lock; bookkeep locally as well: */
flc->fl_flags &= ~FL_SLEEP;
if (posix_lock_file(file, flc, NULL)) {
/*
* This can only happen in the case of kmalloc() failure.
* The filesystem's own lock is the authoritative lock,
* so a failure to get the lock locally is not a disaster.
* As long as GFS cannot reliably cancel locks (especially
* in a low-memory situation), we're better off ignoring
* this failure than trying to recover.
*/
log_error("gdlm_plock: vfs lock error file %p fl %p",
file, fl);
}
rv = notify(flc, NULL, 0);
if (rv) {
/* XXX: We need to cancel the fs lock here: */
printk("gfs2 lock granted after lock request failed;"
" dangling lock!\n");
goto out;
}
out:
kfree(xop);
return rv; return rv;
} }
...@@ -138,6 +217,9 @@ int gdlm_punlock(void *lockspace, struct lm_lockname *name, ...@@ -138,6 +217,9 @@ int gdlm_punlock(void *lockspace, struct lm_lockname *name,
rv = op->info.rv; rv = op->info.rv;
if (rv == -ENOENT)
rv = 0;
kfree(op); kfree(op);
return rv; return rv;
} }
...@@ -161,6 +243,7 @@ int gdlm_plock_get(void *lockspace, struct lm_lockname *name, ...@@ -161,6 +243,7 @@ int gdlm_plock_get(void *lockspace, struct lm_lockname *name,
op->info.start = fl->fl_start; op->info.start = fl->fl_start;
op->info.end = fl->fl_end; op->info.end = fl->fl_end;
send_op(op); send_op(op);
wait_event(recv_wq, (op->done != 0)); wait_event(recv_wq, (op->done != 0));
...@@ -173,9 +256,10 @@ int gdlm_plock_get(void *lockspace, struct lm_lockname *name, ...@@ -173,9 +256,10 @@ int gdlm_plock_get(void *lockspace, struct lm_lockname *name,
rv = op->info.rv; rv = op->info.rv;
if (rv == 0)
fl->fl_type = F_UNLCK; fl->fl_type = F_UNLCK;
else if (rv > 0) { if (rv == -ENOENT)
rv = 0;
else if (rv == 0 && op->info.pid != fl->fl_pid) {
fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK; fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK;
fl->fl_pid = op->info.pid; fl->fl_pid = op->info.pid;
fl->fl_start = op->info.start; fl->fl_start = op->info.start;
...@@ -243,9 +327,14 @@ static ssize_t dev_write(struct file *file, const char __user *u, size_t count, ...@@ -243,9 +327,14 @@ static ssize_t dev_write(struct file *file, const char __user *u, size_t count,
} }
spin_unlock(&ops_lock); spin_unlock(&ops_lock);
if (found) if (found) {
wake_up(&recv_wq); struct plock_xop *xop;
xop = (struct plock_xop *)op;
if (xop->callback)
count = gdlm_plock_callback(op);
else else
wake_up(&recv_wq);
} else
printk(KERN_INFO "gdlm dev_write no op %x %llx\n", info.fsid, printk(KERN_INFO "gdlm dev_write no op %x %llx\n", info.fsid,
(unsigned long long)info.number); (unsigned long long)info.number);
return count; return count;
......
...@@ -164,13 +164,7 @@ static void nolock_unhold_lvb(void *lock, char *lvb) ...@@ -164,13 +164,7 @@ static void nolock_unhold_lvb(void *lock, char *lvb)
static int nolock_plock_get(void *lockspace, struct lm_lockname *name, static int nolock_plock_get(void *lockspace, struct lm_lockname *name,
struct file *file, struct file_lock *fl) struct file *file, struct file_lock *fl)
{ {
struct file_lock tmp; posix_test_lock(file, fl);
int ret;
ret = posix_test_lock(file, fl, &tmp);
fl->fl_type = F_UNLCK;
if (ret)
memcpy(fl, &tmp, sizeof(struct file_lock));
return 0; return 0;
} }
......
...@@ -513,18 +513,18 @@ static int gfs2_lock(struct file *file, int cmd, struct file_lock *fl) ...@@ -513,18 +513,18 @@ static int gfs2_lock(struct file *file, int cmd, struct file_lock *fl)
if (sdp->sd_args.ar_localflocks) { if (sdp->sd_args.ar_localflocks) {
if (IS_GETLK(cmd)) { if (IS_GETLK(cmd)) {
struct file_lock tmp; posix_test_lock(file, fl);
int ret;
ret = posix_test_lock(file, fl, &tmp);
fl->fl_type = F_UNLCK;
if (ret)
memcpy(fl, &tmp, sizeof(struct file_lock));
return 0; return 0;
} else { } else {
return posix_lock_file_wait(file, fl); return posix_lock_file_wait(file, fl);
} }
} }
if (cmd == F_CANCELLK) {
/* Hack: */
cmd = F_SETLK;
fl->fl_type = F_UNLCK;
}
if (IS_GETLK(cmd)) if (IS_GETLK(cmd))
return gfs2_lm_plock_get(sdp, &name, file, fl); return gfs2_lm_plock_get(sdp, &name, file, fl);
else if (fl->fl_type == F_UNLCK) else if (fl->fl_type == F_UNLCK)
......
...@@ -99,7 +99,9 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -99,7 +99,9 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
/* Now check for conflicting locks */ /* Now check for conflicting locks */
resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock); resp->status = nlmsvc_testlock(rqstp, file, &argp->lock, &resp->lock, &resp->cookie);
if (resp->status == nlm_drop_reply)
return rpc_drop_reply;
dprintk("lockd: TEST4 status %d\n", ntohl(resp->status)); dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));
nlm_release_host(host); nlm_release_host(host);
...@@ -143,6 +145,8 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -143,6 +145,8 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
/* Now try to lock the file */ /* Now try to lock the file */
resp->status = nlmsvc_lock(rqstp, file, &argp->lock, resp->status = nlmsvc_lock(rqstp, file, &argp->lock,
argp->block, &argp->cookie); argp->block, &argp->cookie);
if (resp->status == nlm_drop_reply)
return rpc_drop_reply;
dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
nlm_release_host(host); nlm_release_host(host);
......
...@@ -210,6 +210,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, ...@@ -210,6 +210,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file,
block->b_daemon = rqstp->rq_server; block->b_daemon = rqstp->rq_server;
block->b_host = host; block->b_host = host;
block->b_file = file; block->b_file = file;
block->b_fl = NULL;
file->f_count++; file->f_count++;
/* Add to file's list of blocks */ /* Add to file's list of blocks */
...@@ -261,6 +262,7 @@ static void nlmsvc_free_block(struct kref *kref) ...@@ -261,6 +262,7 @@ static void nlmsvc_free_block(struct kref *kref)
nlmsvc_freegrantargs(block->b_call); nlmsvc_freegrantargs(block->b_call);
nlm_release_call(block->b_call); nlm_release_call(block->b_call);
nlm_release_file(block->b_file); nlm_release_file(block->b_file);
kfree(block->b_fl);
kfree(block); kfree(block);
} }
...@@ -330,6 +332,31 @@ static void nlmsvc_freegrantargs(struct nlm_rqst *call) ...@@ -330,6 +332,31 @@ static void nlmsvc_freegrantargs(struct nlm_rqst *call)
kfree(call->a_args.lock.oh.data); kfree(call->a_args.lock.oh.data);
} }
/*
* Deferred lock request handling for non-blocking lock
*/
static u32
nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block)
{
u32 status = nlm_lck_denied_nolocks;
block->b_flags |= B_QUEUED;
nlmsvc_insert_block(block, NLM_TIMEOUT);
block->b_cache_req = &rqstp->rq_chandle;
if (rqstp->rq_chandle.defer) {
block->b_deferred_req =
rqstp->rq_chandle.defer(block->b_cache_req);
if (block->b_deferred_req != NULL)
status = nlm_drop_reply;
}
dprintk("lockd: nlmsvc_defer_lock_rqst block %p flags %d status %d\n",
block, block->b_flags, status);
return status;
}
/* /*
* Attempt to establish a lock, and if it can't be granted, block it * Attempt to establish a lock, and if it can't be granted, block it
* if required. * if required.
...@@ -338,7 +365,7 @@ __be32 ...@@ -338,7 +365,7 @@ __be32
nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
struct nlm_lock *lock, int wait, struct nlm_cookie *cookie) struct nlm_lock *lock, int wait, struct nlm_cookie *cookie)
{ {
struct nlm_block *block, *newblock = NULL; struct nlm_block *block = NULL;
int error; int error;
__be32 ret; __be32 ret;
...@@ -351,29 +378,58 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, ...@@ -351,29 +378,58 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
wait); wait);
lock->fl.fl_flags &= ~FL_SLEEP;
again:
/* Lock file against concurrent access */ /* Lock file against concurrent access */
mutex_lock(&file->f_mutex); mutex_lock(&file->f_mutex);
/* Get existing block (in case client is busy-waiting) */ /* Get existing block (in case client is busy-waiting)
* or create new block
*/
block = nlmsvc_lookup_block(file, lock); block = nlmsvc_lookup_block(file, lock);
if (block == NULL) { if (block == NULL) {
if (newblock != NULL) block = nlmsvc_create_block(rqstp, file, lock, cookie);
lock = &newblock->b_call->a_args.lock; ret = nlm_lck_denied_nolocks;
} else if (block == NULL)
goto out;
lock = &block->b_call->a_args.lock; lock = &block->b_call->a_args.lock;
} else
error = posix_lock_file(file->f_file, &lock->fl);
lock->fl.fl_flags &= ~FL_SLEEP; lock->fl.fl_flags &= ~FL_SLEEP;
dprintk("lockd: posix_lock_file returned %d\n", error); if (block->b_flags & B_QUEUED) {
dprintk("lockd: nlmsvc_lock deferred block %p flags %d\n",
block, block->b_flags);
if (block->b_granted) {
nlmsvc_unlink_block(block);
ret = nlm_granted;
goto out;
}
if (block->b_flags & B_TIMED_OUT) {
nlmsvc_unlink_block(block);
ret = nlm_lck_denied;
goto out;
}
ret = nlm_drop_reply;
goto out;
}
if (!wait)
lock->fl.fl_flags &= ~FL_SLEEP;
error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
lock->fl.fl_flags &= ~FL_SLEEP;
dprintk("lockd: vfs_lock_file returned %d\n", error);
switch(error) { switch(error) {
case 0: case 0:
ret = nlm_granted; ret = nlm_granted;
goto out; goto out;
case -EAGAIN: case -EAGAIN:
ret = nlm_lck_denied;
break;
case -EINPROGRESS:
if (wait)
break; break;
/* Filesystem lock operation is in progress
Add it to the queue waiting for callback */
ret = nlmsvc_defer_lock_rqst(rqstp, block);
goto out;
case -EDEADLK: case -EDEADLK:
ret = nlm_deadlock; ret = nlm_deadlock;
goto out; goto out;
...@@ -387,26 +443,11 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, ...@@ -387,26 +443,11 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
goto out; goto out;
ret = nlm_lck_blocked; ret = nlm_lck_blocked;
if (block != NULL)
goto out;
/* If we don't have a block, create and initialize it. Then
* retry because we may have slept in kmalloc. */
/* We have to release f_mutex as nlmsvc_create_block may try to
* to claim it while doing host garbage collection */
if (newblock == NULL) {
mutex_unlock(&file->f_mutex);
dprintk("lockd: blocking on this lock (allocating).\n");
if (!(newblock = nlmsvc_create_block(rqstp, file, lock, cookie)))
return nlm_lck_denied_nolocks;
goto again;
}
/* Append to list of blocked */ /* Append to list of blocked */
nlmsvc_insert_block(newblock, NLM_NEVER); nlmsvc_insert_block(block, NLM_NEVER);
out: out:
mutex_unlock(&file->f_mutex); mutex_unlock(&file->f_mutex);
nlmsvc_release_block(newblock);
nlmsvc_release_block(block); nlmsvc_release_block(block);
dprintk("lockd: nlmsvc_lock returned %u\n", ret); dprintk("lockd: nlmsvc_lock returned %u\n", ret);
return ret; return ret;
...@@ -416,9 +457,14 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, ...@@ -416,9 +457,14 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
* Test for presence of a conflicting lock. * Test for presence of a conflicting lock.
*/ */
__be32 __be32
nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
struct nlm_lock *conflock) struct nlm_lock *lock, struct nlm_lock *conflock,
struct nlm_cookie *cookie)
{ {
struct nlm_block *block = NULL;
int error;
__be32 ret;
dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n", dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
file->f_file->f_path.dentry->d_inode->i_sb->s_id, file->f_file->f_path.dentry->d_inode->i_sb->s_id,
file->f_file->f_path.dentry->d_inode->i_ino, file->f_file->f_path.dentry->d_inode->i_ino,
...@@ -426,19 +472,70 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, ...@@ -426,19 +472,70 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
(long long)lock->fl.fl_start, (long long)lock->fl.fl_start,
(long long)lock->fl.fl_end); (long long)lock->fl.fl_end);
if (posix_test_lock(file->f_file, &lock->fl, &conflock->fl)) { /* Get existing block (in case client is busy-waiting) */
block = nlmsvc_lookup_block(file, lock);
if (block == NULL) {
struct file_lock *conf = kzalloc(sizeof(*conf), GFP_KERNEL);
if (conf == NULL)
return nlm_granted;
block = nlmsvc_create_block(rqstp, file, lock, cookie);
if (block == NULL) {
kfree(conf);
return nlm_granted;
}
block->b_fl = conf;
}
if (block->b_flags & B_QUEUED) {
dprintk("lockd: nlmsvc_testlock deferred block %p flags %d fl %p\n",
block, block->b_flags, block->b_fl);
if (block->b_flags & B_TIMED_OUT) {
nlmsvc_unlink_block(block);
return nlm_lck_denied;
}
if (block->b_flags & B_GOT_CALLBACK) {
if (block->b_fl != NULL
&& block->b_fl->fl_type != F_UNLCK) {
lock->fl = *block->b_fl;
goto conf_lock;
}
else {
nlmsvc_unlink_block(block);
return nlm_granted;
}
}
return nlm_drop_reply;
}
error = vfs_test_lock(file->f_file, &lock->fl);
if (error == -EINPROGRESS)
return nlmsvc_defer_lock_rqst(rqstp, block);
if (error) {
ret = nlm_lck_denied_nolocks;
goto out;
}
if (lock->fl.fl_type == F_UNLCK) {
ret = nlm_granted;
goto out;
}
conf_lock:
dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n", dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
conflock->fl.fl_type, lock->fl.fl_type, (long long)lock->fl.fl_start,
(long long)conflock->fl.fl_start, (long long)lock->fl.fl_end);
(long long)conflock->fl.fl_end);
conflock->caller = "somehost"; /* FIXME */ conflock->caller = "somehost"; /* FIXME */
conflock->len = strlen(conflock->caller); conflock->len = strlen(conflock->caller);
conflock->oh.len = 0; /* don't return OH info */ conflock->oh.len = 0; /* don't return OH info */
conflock->svid = conflock->fl.fl_pid; conflock->svid = lock->fl.fl_pid;
return nlm_lck_denied; conflock->fl.fl_type = lock->fl.fl_type;
} conflock->fl.fl_start = lock->fl.fl_start;
conflock->fl.fl_end = lock->fl.fl_end;
return nlm_granted; ret = nlm_lck_denied;
out:
if (block)
nlmsvc_release_block(block);
return ret;
} }
/* /*
...@@ -464,7 +561,7 @@ nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock) ...@@ -464,7 +561,7 @@ nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
nlmsvc_cancel_blocked(file, lock); nlmsvc_cancel_blocked(file, lock);
lock->fl.fl_type = F_UNLCK; lock->fl.fl_type = F_UNLCK;
error = posix_lock_file(file->f_file, &lock->fl); error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
return (error < 0)? nlm_lck_denied_nolocks : nlm_granted; return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
} }
...@@ -493,12 +590,71 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) ...@@ -493,12 +590,71 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
block = nlmsvc_lookup_block(file, lock); block = nlmsvc_lookup_block(file, lock);
mutex_unlock(&file->f_mutex); mutex_unlock(&file->f_mutex);
if (block != NULL) { if (block != NULL) {
vfs_cancel_lock(block->b_file->f_file,
&block->b_call->a_args.lock.fl);
status = nlmsvc_unlink_block(block); status = nlmsvc_unlink_block(block);
nlmsvc_release_block(block); nlmsvc_release_block(block);
} }
return status ? nlm_lck_denied : nlm_granted; return status ? nlm_lck_denied : nlm_granted;
} }
/*
* This is a callback from the filesystem for VFS file lock requests.
* It will be used if fl_grant is defined and the filesystem can not
* respond to the request immediately.
* For GETLK request it will copy the reply to the nlm_block.
* For SETLK or SETLKW request it will get the local posix lock.
* In all cases it will move the block to the head of nlm_blocked q where
* nlmsvc_retry_blocked() can send back a reply for SETLKW or revisit the
* deferred rpc for GETLK and SETLK.
*/
static void
nlmsvc_update_deferred_block(struct nlm_block *block, struct file_lock *conf,
int result)
{
block->b_flags |= B_GOT_CALLBACK;
if (result == 0)
block->b_granted = 1;
else
block->b_flags |= B_TIMED_OUT;
if (conf) {
if (block->b_fl)
locks_copy_lock(block->b_fl, conf);
}
}
static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
int result)
{
struct nlm_block *block;
int rc = -ENOENT;
lock_kernel();
list_for_each_entry(block, &nlm_blocked, b_list) {
if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
dprintk("lockd: nlmsvc_notify_blocked block %p flags %d\n",
block, block->b_flags);
if (block->b_flags & B_QUEUED) {
if (block->b_flags & B_TIMED_OUT) {
rc = -ENOLCK;
break;
}
nlmsvc_update_deferred_block(block, conf, result);
} else if (result == 0)
block->b_granted = 1;
nlmsvc_insert_block(block, 0);
svc_wake_up(block->b_daemon);
rc = 0;
break;
}
}
unlock_kernel();
if (rc == -ENOENT)
printk(KERN_WARNING "lockd: grant for unknown block\n");
return rc;
}
/* /*
* Unblock a blocked lock request. This is a callback invoked from the * Unblock a blocked lock request. This is a callback invoked from the
* VFS layer when a lock on which we blocked is removed. * VFS layer when a lock on which we blocked is removed.
...@@ -531,6 +687,7 @@ static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2) ...@@ -531,6 +687,7 @@ static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2)
struct lock_manager_operations nlmsvc_lock_operations = { struct lock_manager_operations nlmsvc_lock_operations = {
.fl_compare_owner = nlmsvc_same_owner, .fl_compare_owner = nlmsvc_same_owner,
.fl_notify = nlmsvc_notify_blocked, .fl_notify = nlmsvc_notify_blocked,
.fl_grant = nlmsvc_grant_deferred,
}; };
/* /*
...@@ -553,6 +710,8 @@ nlmsvc_grant_blocked(struct nlm_block *block) ...@@ -553,6 +710,8 @@ nlmsvc_grant_blocked(struct nlm_block *block)
dprintk("lockd: grant blocked lock %p\n", block); dprintk("lockd: grant blocked lock %p\n", block);
kref_get(&block->b_count);
/* Unlink block request from list */ /* Unlink block request from list */
nlmsvc_unlink_block(block); nlmsvc_unlink_block(block);
...@@ -566,20 +725,23 @@ nlmsvc_grant_blocked(struct nlm_block *block) ...@@ -566,20 +725,23 @@ nlmsvc_grant_blocked(struct nlm_block *block)
/* Try the lock operation again */ /* Try the lock operation again */
lock->fl.fl_flags |= FL_SLEEP; lock->fl.fl_flags |= FL_SLEEP;
error = posix_lock_file(file->f_file, &lock->fl); error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
lock->fl.fl_flags &= ~FL_SLEEP; lock->fl.fl_flags &= ~FL_SLEEP;
switch (error) { switch (error) {
case 0: case 0:
break; break;
case -EAGAIN: case -EAGAIN:
dprintk("lockd: lock still blocked\n"); case -EINPROGRESS:
dprintk("lockd: lock still blocked error %d\n", error);
nlmsvc_insert_block(block, NLM_NEVER); nlmsvc_insert_block(block, NLM_NEVER);
nlmsvc_release_block(block);
return; return;
default: default:
printk(KERN_WARNING "lockd: unexpected error %d in %s!\n", printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
-error, __FUNCTION__); -error, __FUNCTION__);
nlmsvc_insert_block(block, 10 * HZ); nlmsvc_insert_block(block, 10 * HZ);
nlmsvc_release_block(block);
return; return;
} }
...@@ -592,7 +754,6 @@ nlmsvc_grant_blocked(struct nlm_block *block) ...@@ -592,7 +754,6 @@ nlmsvc_grant_blocked(struct nlm_block *block)
nlmsvc_insert_block(block, 30 * HZ); nlmsvc_insert_block(block, 30 * HZ);
/* Call the client */ /* Call the client */
kref_get(&block->b_count);
nlm_async_call(block->b_call, NLMPROC_GRANTED_MSG, &nlmsvc_grant_ops); nlm_async_call(block->b_call, NLMPROC_GRANTED_MSG, &nlmsvc_grant_ops);
} }
...@@ -665,6 +826,23 @@ nlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status) ...@@ -665,6 +826,23 @@ nlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status)
nlmsvc_release_block(block); nlmsvc_release_block(block);
} }
/* Helper function to handle retry of a deferred block.
* If it is a blocking lock, call grant_blocked.
* For a non-blocking lock or test lock, revisit the request.
*/
static void
retry_deferred_block(struct nlm_block *block)
{
if (!(block->b_flags & B_GOT_CALLBACK))
block->b_flags |= B_TIMED_OUT;
nlmsvc_insert_block(block, NLM_TIMEOUT);
dprintk("revisit block %p flags %d\n", block, block->b_flags);
if (block->b_deferred_req) {
block->b_deferred_req->revisit(block->b_deferred_req, 0);
block->b_deferred_req = NULL;
}
}
/* /*
* Retry all blocked locks that have been notified. This is where lockd * Retry all blocked locks that have been notified. This is where lockd
* picks up locks that can be granted, or grant notifications that must * picks up locks that can be granted, or grant notifications that must
...@@ -688,9 +866,12 @@ nlmsvc_retry_blocked(void) ...@@ -688,9 +866,12 @@ nlmsvc_retry_blocked(void)
dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n", dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
block, block->b_when); block, block->b_when);
kref_get(&block->b_count); if (block->b_flags & B_QUEUED) {
dprintk("nlmsvc_retry_blocked delete block (%p, granted=%d, flags=%d)\n",
block, block->b_granted, block->b_flags);
retry_deferred_block(block);
} else
nlmsvc_grant_blocked(block); nlmsvc_grant_blocked(block);
nlmsvc_release_block(block);
} }
return timeout; return timeout;
......
...@@ -33,6 +33,7 @@ cast_to_nlm(__be32 status, u32 vers) ...@@ -33,6 +33,7 @@ cast_to_nlm(__be32 status, u32 vers)
case nlm_lck_denied_nolocks: case nlm_lck_denied_nolocks:
case nlm_lck_blocked: case nlm_lck_blocked:
case nlm_lck_denied_grace_period: case nlm_lck_denied_grace_period:
case nlm_drop_reply:
break; break;
case nlm4_deadlock: case nlm4_deadlock:
status = nlm_lck_denied; status = nlm_lck_denied;
...@@ -127,7 +128,9 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -127,7 +128,9 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
/* Now check for conflicting locks */ /* Now check for conflicting locks */
resp->status = cast_status(nlmsvc_testlock(file, &argp->lock, &resp->lock)); resp->status = cast_status(nlmsvc_testlock(rqstp, file, &argp->lock, &resp->lock, &resp->cookie));
if (resp->status == nlm_drop_reply)
return rpc_drop_reply;
dprintk("lockd: TEST status %d vers %d\n", dprintk("lockd: TEST status %d vers %d\n",
ntohl(resp->status), rqstp->rq_vers); ntohl(resp->status), rqstp->rq_vers);
...@@ -172,6 +175,8 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -172,6 +175,8 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
/* Now try to lock the file */ /* Now try to lock the file */
resp->status = cast_status(nlmsvc_lock(rqstp, file, &argp->lock, resp->status = cast_status(nlmsvc_lock(rqstp, file, &argp->lock,
argp->block, &argp->cookie)); argp->block, &argp->cookie));
if (resp->status == nlm_drop_reply)
return rpc_drop_reply;
dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
nlm_release_host(host); nlm_release_host(host);
......
...@@ -182,7 +182,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, ...@@ -182,7 +182,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
lock.fl_type = F_UNLCK; lock.fl_type = F_UNLCK;
lock.fl_start = 0; lock.fl_start = 0;
lock.fl_end = OFFSET_MAX; lock.fl_end = OFFSET_MAX;
if (posix_lock_file(file->f_file, &lock) < 0) { if (vfs_lock_file(file->f_file, F_SETLK, &lock, NULL) < 0) {
printk("lockd: unlock failure in %s:%d\n", printk("lockd: unlock failure in %s:%d\n",
__FILE__, __LINE__); __FILE__, __LINE__);
return 1; return 1;
......
...@@ -665,11 +665,11 @@ static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *w ...@@ -665,11 +665,11 @@ static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *w
} }
int int
posix_test_lock(struct file *filp, struct file_lock *fl, posix_test_lock(struct file *filp, struct file_lock *fl)
struct file_lock *conflock)
{ {
struct file_lock *cfl; struct file_lock *cfl;
fl->fl_type = F_UNLCK;
lock_kernel(); lock_kernel();
for (cfl = filp->f_path.dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) { for (cfl = filp->f_path.dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
if (!IS_POSIX(cfl)) if (!IS_POSIX(cfl))
...@@ -678,7 +678,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl, ...@@ -678,7 +678,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl,
break; break;
} }
if (cfl) { if (cfl) {
__locks_copy_lock(conflock, cfl); __locks_copy_lock(fl, cfl);
unlock_kernel(); unlock_kernel();
return 1; return 1;
} }
...@@ -800,7 +800,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) ...@@ -800,7 +800,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
return error; return error;
} }
static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request, struct file_lock *conflock) static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock)
{ {
struct file_lock *fl; struct file_lock *fl;
struct file_lock *new_fl = NULL; struct file_lock *new_fl = NULL;
...@@ -1006,6 +1006,7 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request ...@@ -1006,6 +1006,7 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request
* posix_lock_file - Apply a POSIX-style lock to a file * posix_lock_file - Apply a POSIX-style lock to a file
* @filp: The file to apply the lock to * @filp: The file to apply the lock to
* @fl: The lock to be applied * @fl: The lock to be applied
* @conflock: Place to return a copy of the conflicting lock, if found.
* *
* Add a POSIX style lock to a file. * Add a POSIX style lock to a file.
* We merge adjacent & overlapping locks whenever possible. * We merge adjacent & overlapping locks whenever possible.
...@@ -1015,26 +1016,12 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request ...@@ -1015,26 +1016,12 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request
* whether or not a lock was successfully freed by testing the return * whether or not a lock was successfully freed by testing the return
* value for -ENOENT. * value for -ENOENT.
*/ */
int posix_lock_file(struct file *filp, struct file_lock *fl) int posix_lock_file(struct file *filp, struct file_lock *fl,
{
return __posix_lock_file_conf(filp->f_path.dentry->d_inode, fl, NULL);
}
EXPORT_SYMBOL(posix_lock_file);
/**
* posix_lock_file_conf - Apply a POSIX-style lock to a file
* @filp: The file to apply the lock to
* @fl: The lock to be applied
* @conflock: Place to return a copy of the conflicting lock, if found.
*
* Except for the conflock parameter, acts just like posix_lock_file.
*/
int posix_lock_file_conf(struct file *filp, struct file_lock *fl,
struct file_lock *conflock) struct file_lock *conflock)
{ {
return __posix_lock_file_conf(filp->f_path.dentry->d_inode, fl, conflock); return __posix_lock_file(filp->f_path.dentry->d_inode, fl, conflock);
} }
EXPORT_SYMBOL(posix_lock_file_conf); EXPORT_SYMBOL(posix_lock_file);
/** /**
* posix_lock_file_wait - Apply a POSIX-style lock to a file * posix_lock_file_wait - Apply a POSIX-style lock to a file
...@@ -1050,7 +1037,7 @@ int posix_lock_file_wait(struct file *filp, struct file_lock *fl) ...@@ -1050,7 +1037,7 @@ int posix_lock_file_wait(struct file *filp, struct file_lock *fl)
int error; int error;
might_sleep (); might_sleep ();
for (;;) { for (;;) {
error = posix_lock_file(filp, fl); error = posix_lock_file(filp, fl, NULL);
if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP)) if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP))
break; break;
error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); error = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
...@@ -1122,7 +1109,7 @@ int locks_mandatory_area(int read_write, struct inode *inode, ...@@ -1122,7 +1109,7 @@ int locks_mandatory_area(int read_write, struct inode *inode,
fl.fl_end = offset + count - 1; fl.fl_end = offset + count - 1;
for (;;) { for (;;) {
error = __posix_lock_file_conf(inode, &fl, NULL); error = __posix_lock_file(inode, &fl, NULL);
if (error != -EAGAIN) if (error != -EAGAIN)
break; break;
if (!(fl.fl_flags & FL_SLEEP)) if (!(fl.fl_flags & FL_SLEEP))
...@@ -1610,12 +1597,62 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) ...@@ -1610,12 +1597,62 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
return error; return error;
} }
/**
* vfs_test_lock - test file byte range lock
* @filp: The file to test lock for
* @fl: The lock to test
* @conf: Place to return a copy of the conflicting lock, if found
*
* Returns -ERRNO on failure. Indicates presence of conflicting lock by
* setting conf->fl_type to something other than F_UNLCK.
*/
int vfs_test_lock(struct file *filp, struct file_lock *fl)
{
if (filp->f_op && filp->f_op->lock)
return filp->f_op->lock(filp, F_GETLK, fl);
posix_test_lock(filp, fl);
return 0;
}
EXPORT_SYMBOL_GPL(vfs_test_lock);
static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl)
{
flock->l_pid = fl->fl_pid;
#if BITS_PER_LONG == 32
/*
* Make sure we can represent the posix lock via
* legacy 32bit flock.
*/
if (fl->fl_start > OFFT_OFFSET_MAX)
return -EOVERFLOW;
if (fl->fl_end != OFFSET_MAX && fl->fl_end > OFFT_OFFSET_MAX)
return -EOVERFLOW;
#endif
flock->l_start = fl->fl_start;
flock->l_len = fl->fl_end == OFFSET_MAX ? 0 :
fl->fl_end - fl->fl_start + 1;
flock->l_whence = 0;
return 0;
}
#if BITS_PER_LONG == 32
static void posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl)
{
flock->l_pid = fl->fl_pid;
flock->l_start = fl->fl_start;
flock->l_len = fl->fl_end == OFFSET_MAX ? 0 :
fl->fl_end - fl->fl_start + 1;
flock->l_whence = 0;
flock->l_type = fl->fl_type;
}
#endif
/* Report the first existing lock that would conflict with l. /* Report the first existing lock that would conflict with l.
* This implements the F_GETLK command of fcntl(). * This implements the F_GETLK command of fcntl().
*/ */
int fcntl_getlk(struct file *filp, struct flock __user *l) int fcntl_getlk(struct file *filp, struct flock __user *l)
{ {
struct file_lock *fl, cfl, file_lock; struct file_lock file_lock;
struct flock flock; struct flock flock;
int error; int error;
...@@ -1630,38 +1667,15 @@ int fcntl_getlk(struct file *filp, struct flock __user *l) ...@@ -1630,38 +1667,15 @@ int fcntl_getlk(struct file *filp, struct flock __user *l)
if (error) if (error)
goto out; goto out;
if (filp->f_op && filp->f_op->lock) { error = vfs_test_lock(filp, &file_lock);
error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error)
if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private)
file_lock.fl_ops->fl_release_private(&file_lock);
if (error < 0)
goto out; goto out;
else
fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
} else {
fl = (posix_test_lock(filp, &file_lock, &cfl) ? &cfl : NULL);
}
flock.l_type = F_UNLCK; flock.l_type = file_lock.fl_type;
if (fl != NULL) { if (file_lock.fl_type != F_UNLCK) {
flock.l_pid = fl->fl_pid; error = posix_lock_to_flock(&flock, &file_lock);
#if BITS_PER_LONG == 32 if (error)
/*
* Make sure we can represent the posix lock via
* legacy 32bit flock.
*/
error = -EOVERFLOW;
if (fl->fl_start > OFFT_OFFSET_MAX)
goto out;
if ((fl->fl_end != OFFSET_MAX)
&& (fl->fl_end > OFFT_OFFSET_MAX))
goto out; goto out;
#endif
flock.l_start = fl->fl_start;
flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
fl->fl_end - fl->fl_start + 1;
flock.l_whence = 0;
flock.l_type = fl->fl_type;
} }
error = -EFAULT; error = -EFAULT;
if (!copy_to_user(l, &flock, sizeof(flock))) if (!copy_to_user(l, &flock, sizeof(flock)))
...@@ -1670,6 +1684,48 @@ int fcntl_getlk(struct file *filp, struct flock __user *l) ...@@ -1670,6 +1684,48 @@ int fcntl_getlk(struct file *filp, struct flock __user *l)
return error; return error;
} }
/**
* vfs_lock_file - file byte range lock
* @filp: The file to apply the lock to
* @cmd: type of locking operation (F_SETLK, F_GETLK, etc.)
* @fl: The lock to be applied
* @conf: Place to return a copy of the conflicting lock, if found.
*
* A caller that doesn't care about the conflicting lock may pass NULL
* as the final argument.
*
* If the filesystem defines a private ->lock() method, then @conf will
* be left unchanged; so a caller that cares should initialize it to
* some acceptable default.
*
* To avoid blocking kernel daemons, such as lockd, that need to acquire POSIX
* locks, the ->lock() interface may return asynchronously, before the lock has
* been granted or denied by the underlying filesystem, if (and only if)
* fl_grant is set. Callers expecting ->lock() to return asynchronously
* will only use F_SETLK, not F_SETLKW; they will set FL_SLEEP if (and only if)
* the request is for a blocking lock. When ->lock() does return asynchronously,
* it must return -EINPROGRESS, and call ->fl_grant() when the lock
* request completes.
* If the request is for non-blocking lock the file system should return
* -EINPROGRESS then try to get the lock and call the callback routine with
* the result. If the request timed out the callback routine will return a
* nonzero return code and the file system should release the lock. The file
* system is also responsible to keep a corresponding posix lock when it
* grants a lock so the VFS can find out which locks are locally held and do
* the correct lock cleanup when required.
* The underlying filesystem must not drop the kernel lock or call
* ->fl_grant() before returning to the caller with a -EINPROGRESS
* return code.
*/
int vfs_lock_file(struct file *filp, unsigned int cmd, struct file_lock *fl, struct file_lock *conf)
{
if (filp->f_op && filp->f_op->lock)
return filp->f_op->lock(filp, cmd, fl);
else
return posix_lock_file(filp, fl, conf);
}
EXPORT_SYMBOL_GPL(vfs_lock_file);
/* Apply the lock described by l to an open file descriptor. /* Apply the lock described by l to an open file descriptor.
* This implements both the F_SETLK and F_SETLKW commands of fcntl(). * This implements both the F_SETLK and F_SETLKW commands of fcntl().
*/ */
...@@ -1732,12 +1788,9 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd, ...@@ -1732,12 +1788,9 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
if (error) if (error)
goto out; goto out;
if (filp->f_op && filp->f_op->lock != NULL)
error = filp->f_op->lock(filp, cmd, file_lock);
else {
for (;;) { for (;;) {
error = posix_lock_file(filp, file_lock); error = vfs_lock_file(filp, cmd, file_lock, NULL);
if ((error != -EAGAIN) || (cmd == F_SETLK)) if (error != -EAGAIN || cmd == F_SETLK)
break; break;
error = wait_event_interruptible(file_lock->fl_wait, error = wait_event_interruptible(file_lock->fl_wait,
!file_lock->fl_next); !file_lock->fl_next);
...@@ -1747,7 +1800,6 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd, ...@@ -1747,7 +1800,6 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
locks_delete_block(file_lock); locks_delete_block(file_lock);
break; break;
} }
}
/* /*
* Attempt to detect a close/fcntl race and recover by * Attempt to detect a close/fcntl race and recover by
...@@ -1769,7 +1821,7 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd, ...@@ -1769,7 +1821,7 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
*/ */
int fcntl_getlk64(struct file *filp, struct flock64 __user *l) int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
{ {
struct file_lock *fl, cfl, file_lock; struct file_lock file_lock;
struct flock64 flock; struct flock64 flock;
int error; int error;
...@@ -1784,27 +1836,14 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l) ...@@ -1784,27 +1836,14 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
if (error) if (error)
goto out; goto out;
if (filp->f_op && filp->f_op->lock) { error = vfs_test_lock(filp, &file_lock);
error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error)
if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private)
file_lock.fl_ops->fl_release_private(&file_lock);
if (error < 0)
goto out; goto out;
else
fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
} else {
fl = (posix_test_lock(filp, &file_lock, &cfl) ? &cfl : NULL);
}
flock.l_type = F_UNLCK; flock.l_type = file_lock.fl_type;
if (fl != NULL) { if (file_lock.fl_type != F_UNLCK)
flock.l_pid = fl->fl_pid; posix_lock_to_flock64(&flock, &file_lock);
flock.l_start = fl->fl_start;
flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
fl->fl_end - fl->fl_start + 1;
flock.l_whence = 0;
flock.l_type = fl->fl_type;
}
error = -EFAULT; error = -EFAULT;
if (!copy_to_user(l, &flock, sizeof(flock))) if (!copy_to_user(l, &flock, sizeof(flock)))
error = 0; error = 0;
...@@ -1875,12 +1914,9 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd, ...@@ -1875,12 +1914,9 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
if (error) if (error)
goto out; goto out;
if (filp->f_op && filp->f_op->lock != NULL)
error = filp->f_op->lock(filp, cmd, file_lock);
else {
for (;;) { for (;;) {
error = posix_lock_file(filp, file_lock); error = vfs_lock_file(filp, cmd, file_lock, NULL);
if ((error != -EAGAIN) || (cmd == F_SETLK64)) if (error != -EAGAIN || cmd == F_SETLK64)
break; break;
error = wait_event_interruptible(file_lock->fl_wait, error = wait_event_interruptible(file_lock->fl_wait,
!file_lock->fl_next); !file_lock->fl_next);
...@@ -1890,7 +1926,6 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd, ...@@ -1890,7 +1926,6 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
locks_delete_block(file_lock); locks_delete_block(file_lock);
break; break;
} }
}
/* /*
* Attempt to detect a close/fcntl race and recover by * Attempt to detect a close/fcntl race and recover by
...@@ -1934,10 +1969,7 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner) ...@@ -1934,10 +1969,7 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner)
lock.fl_ops = NULL; lock.fl_ops = NULL;
lock.fl_lmops = NULL; lock.fl_lmops = NULL;
if (filp->f_op && filp->f_op->lock != NULL) vfs_lock_file(filp, F_SETLK, &lock, NULL);
filp->f_op->lock(filp, F_SETLK, &lock);
else
posix_lock_file(filp, &lock);
if (lock.fl_ops && lock.fl_ops->fl_release_private) if (lock.fl_ops && lock.fl_ops->fl_release_private)
lock.fl_ops->fl_release_private(&lock); lock.fl_ops->fl_release_private(&lock);
...@@ -2014,6 +2046,22 @@ posix_unblock_lock(struct file *filp, struct file_lock *waiter) ...@@ -2014,6 +2046,22 @@ posix_unblock_lock(struct file *filp, struct file_lock *waiter)
EXPORT_SYMBOL(posix_unblock_lock); EXPORT_SYMBOL(posix_unblock_lock);
/**
* vfs_cancel_lock - file byte range unblock lock
* @filp: The file to apply the unblock to
* @fl: The lock to be unblocked
*
* Used by lock managers to cancel blocked requests
*/
int vfs_cancel_lock(struct file *filp, struct file_lock *fl)
{
if (filp->f_op && filp->f_op->lock)
return filp->f_op->lock(filp, F_CANCELLK, fl);
return 0;
}
EXPORT_SYMBOL_GPL(vfs_cancel_lock);
static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx) static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx)
{ {
struct inode *inode = NULL; struct inode *inode = NULL;
......
...@@ -391,17 +391,12 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -391,17 +391,12 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
{ {
struct file_lock cfl;
struct inode *inode = filp->f_mapping->host; struct inode *inode = filp->f_mapping->host;
int status = 0; int status = 0;
lock_kernel(); lock_kernel();
/* Try local locking first */ /* Try local locking first */
if (posix_test_lock(filp, fl, &cfl)) { if (posix_test_lock(filp, fl)) {
fl->fl_start = cfl.fl_start;
fl->fl_end = cfl.fl_end;
fl->fl_type = cfl.fl_type;
fl->fl_pid = cfl.fl_pid;
goto out; goto out;
} }
......
...@@ -3017,6 +3017,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock ...@@ -3017,6 +3017,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
case -NFS4ERR_DENIED: case -NFS4ERR_DENIED:
status = 0; status = 0;
} }
request->fl_ops->fl_release_private(request);
out: out:
up_read(&clp->cl_sem); up_read(&clp->cl_sem);
return status; return status;
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#include <linux/nfsd/xdr4.h> #include <linux/nfsd/xdr4.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/lockd/bind.h>
#define NFSDDBG_FACILITY NFSDDBG_PROC #define NFSDDBG_FACILITY NFSDDBG_PROC
...@@ -2657,6 +2658,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2657,6 +2658,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct file_lock conflock; struct file_lock conflock;
__be32 status = 0; __be32 status = 0;
unsigned int strhashval; unsigned int strhashval;
unsigned int cmd;
int err; int err;
dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n",
...@@ -2739,10 +2741,12 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2739,10 +2741,12 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
case NFS4_READ_LT: case NFS4_READ_LT:
case NFS4_READW_LT: case NFS4_READW_LT:
file_lock.fl_type = F_RDLCK; file_lock.fl_type = F_RDLCK;
cmd = F_SETLK;
break; break;
case NFS4_WRITE_LT: case NFS4_WRITE_LT:
case NFS4_WRITEW_LT: case NFS4_WRITEW_LT:
file_lock.fl_type = F_WRLCK; file_lock.fl_type = F_WRLCK;
cmd = F_SETLK;
break; break;
default: default:
status = nfserr_inval; status = nfserr_inval;
...@@ -2769,9 +2773,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2769,9 +2773,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
/* XXX?: Just to divert the locks_release_private at the start of /* XXX?: Just to divert the locks_release_private at the start of
* locks_copy_lock: */ * locks_copy_lock: */
conflock.fl_ops = NULL; locks_init_lock(&conflock);
conflock.fl_lmops = NULL; err = vfs_lock_file(filp, cmd, &file_lock, &conflock);
err = posix_lock_file_conf(filp, &file_lock, &conflock);
switch (-err) { switch (-err) {
case 0: /* success! */ case 0: /* success! */
update_stateid(&lock_stp->st_stateid); update_stateid(&lock_stp->st_stateid);
...@@ -2788,7 +2791,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2788,7 +2791,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_deadlock; status = nfserr_deadlock;
break; break;
default: default:
dprintk("NFSD: nfsd4_lock: posix_lock_file_conf() failed! status %d\n",err); dprintk("NFSD: nfsd4_lock: vfs_lock_file() failed! status %d\n",err);
status = nfserr_resource; status = nfserr_resource;
break; break;
} }
...@@ -2813,7 +2816,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2813,7 +2816,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct inode *inode; struct inode *inode;
struct file file; struct file file;
struct file_lock file_lock; struct file_lock file_lock;
struct file_lock conflock; int error;
__be32 status; __be32 status;
if (nfs4_in_grace()) if (nfs4_in_grace())
...@@ -2869,18 +2872,23 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2869,18 +2872,23 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfs4_transform_lock_offset(&file_lock); nfs4_transform_lock_offset(&file_lock);
/* posix_test_lock uses the struct file _only_ to resolve the inode. /* vfs_test_lock uses the struct file _only_ to resolve the inode.
* since LOCKT doesn't require an OPEN, and therefore a struct * since LOCKT doesn't require an OPEN, and therefore a struct
* file may not exist, pass posix_test_lock a struct file with * file may not exist, pass vfs_test_lock a struct file with
* only the dentry:inode set. * only the dentry:inode set.
*/ */
memset(&file, 0, sizeof (struct file)); memset(&file, 0, sizeof (struct file));
file.f_path.dentry = cstate->current_fh.fh_dentry; file.f_path.dentry = cstate->current_fh.fh_dentry;
status = nfs_ok; status = nfs_ok;
if (posix_test_lock(&file, &file_lock, &conflock)) { error = vfs_test_lock(&file, &file_lock);
if (error) {
status = nfserrno(error);
goto out;
}
if (file_lock.fl_type != F_UNLCK) {
status = nfserr_denied; status = nfserr_denied;
nfs4_set_lock_denied(&conflock, &lockt->lt_denied); nfs4_set_lock_denied(&file_lock, &lockt->lt_denied);
} }
out: out:
nfs4_unlock_state(); nfs4_unlock_state();
...@@ -2933,9 +2941,9 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2933,9 +2941,9 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
/* /*
* Try to unlock the file in the VFS. * Try to unlock the file in the VFS.
*/ */
err = posix_lock_file(filp, &file_lock); err = vfs_lock_file(filp, F_SETLK, &file_lock, NULL);
if (err) { if (err) {
dprintk("NFSD: nfs4_locku: posix_lock_file failed!\n"); dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n");
goto out_nfserr; goto out_nfserr;
} }
/* /*
......
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
#include <asm/fcntl.h> #include <asm/fcntl.h>
/* Cancel a blocking posix lock; internal use only until we expose an
* asynchronous lock api to userspace: */
#define F_CANCELLK (F_LINUX_SPECIFIC_BASE+5)
#define F_SETLEASE (F_LINUX_SPECIFIC_BASE+0) #define F_SETLEASE (F_LINUX_SPECIFIC_BASE+0)
#define F_GETLEASE (F_LINUX_SPECIFIC_BASE+1) #define F_GETLEASE (F_LINUX_SPECIFIC_BASE+1)
......
...@@ -786,6 +786,7 @@ struct file_lock_operations { ...@@ -786,6 +786,7 @@ struct file_lock_operations {
struct lock_manager_operations { struct lock_manager_operations {
int (*fl_compare_owner)(struct file_lock *, struct file_lock *); int (*fl_compare_owner)(struct file_lock *, struct file_lock *);
void (*fl_notify)(struct file_lock *); /* unblock callback */ void (*fl_notify)(struct file_lock *); /* unblock callback */
int (*fl_grant)(struct file_lock *, struct file_lock *, int);
void (*fl_copy_lock)(struct file_lock *, struct file_lock *); void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
void (*fl_release_private)(struct file_lock *); void (*fl_release_private)(struct file_lock *);
void (*fl_break)(struct file_lock *); void (*fl_break)(struct file_lock *);
...@@ -857,11 +858,13 @@ extern void locks_init_lock(struct file_lock *); ...@@ -857,11 +858,13 @@ extern void locks_init_lock(struct file_lock *);
extern void locks_copy_lock(struct file_lock *, struct file_lock *); extern void locks_copy_lock(struct file_lock *, struct file_lock *);
extern void locks_remove_posix(struct file *, fl_owner_t); extern void locks_remove_posix(struct file *, fl_owner_t);
extern void locks_remove_flock(struct file *); extern void locks_remove_flock(struct file *);
extern int posix_test_lock(struct file *, struct file_lock *, struct file_lock *); extern int posix_test_lock(struct file *, struct file_lock *);
extern int posix_lock_file_conf(struct file *, struct file_lock *, struct file_lock *); extern int posix_lock_file(struct file *, struct file_lock *, 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 int posix_lock_file_wait(struct file *, struct file_lock *);
extern int posix_unblock_lock(struct file *, struct file_lock *); extern int posix_unblock_lock(struct file *, struct file_lock *);
extern int vfs_test_lock(struct file *, struct file_lock *);
extern int vfs_lock_file(struct file *, unsigned int, struct file_lock *, struct file_lock *);
extern int vfs_cancel_lock(struct file *filp, struct file_lock *fl);
extern int flock_lock_file_wait(struct file *filp, struct file_lock *fl); extern int flock_lock_file_wait(struct file *filp, struct file_lock *fl);
extern int __break_lease(struct inode *inode, unsigned int flags); extern int __break_lease(struct inode *inode, unsigned int flags);
extern void lease_get_mtime(struct inode *, struct timespec *time); extern void lease_get_mtime(struct inode *, struct timespec *time);
......
...@@ -119,6 +119,9 @@ struct nlm_file { ...@@ -119,6 +119,9 @@ struct nlm_file {
* couldn't be granted because of a conflicting lock). * couldn't be granted because of a conflicting lock).
*/ */
#define NLM_NEVER (~(unsigned long) 0) #define NLM_NEVER (~(unsigned long) 0)
/* timeout on non-blocking call: */
#define NLM_TIMEOUT (7 * HZ)
struct nlm_block { struct nlm_block {
struct kref b_count; /* Reference count */ struct kref b_count; /* Reference count */
struct list_head b_list; /* linked list of all blocks */ struct list_head b_list; /* linked list of all blocks */
...@@ -130,6 +133,13 @@ struct nlm_block { ...@@ -130,6 +133,13 @@ struct nlm_block {
unsigned int b_id; /* block id */ unsigned int b_id; /* block id */
unsigned char b_granted; /* VFS granted lock */ unsigned char b_granted; /* VFS granted lock */
struct nlm_file * b_file; /* file in question */ struct nlm_file * b_file; /* file in question */
struct cache_req * b_cache_req; /* deferred request handling */
struct file_lock * b_fl; /* set for GETLK */
struct cache_deferred_req * b_deferred_req;
unsigned int b_flags; /* block flags */
#define B_QUEUED 1 /* lock queued */
#define B_GOT_CALLBACK 2 /* got lock or conflicting lock */
#define B_TIMED_OUT 4 /* filesystem too slow to respond */
}; };
/* /*
...@@ -185,8 +195,8 @@ typedef int (*nlm_host_match_fn_t)(struct nlm_host *cur, struct nlm_host *ref) ...@@ -185,8 +195,8 @@ typedef int (*nlm_host_match_fn_t)(struct nlm_host *cur, struct nlm_host *ref)
__be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
struct nlm_lock *, int, struct nlm_cookie *); struct nlm_lock *, int, struct nlm_cookie *);
__be32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); __be32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *);
__be32 nlmsvc_testlock(struct nlm_file *, struct nlm_lock *, __be32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *,
struct nlm_lock *); struct nlm_lock *, struct nlm_lock *, struct nlm_cookie *);
__be32 nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *); __be32 nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *);
unsigned long nlmsvc_retry_blocked(void); unsigned long nlmsvc_retry_blocked(void);
void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *,
......
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