X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Flockd%2Fclntproc.c;h=5fc0a3b0f8704ee8c4e0154078ab0b0d402ea51d;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=319e28fde008ebfbbaef9df21939c82699fcfc93;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 319e28fde..5fc0a3b0f 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -27,6 +28,7 @@ static int nlmclnt_unlock(struct nlm_rqst *, struct file_lock *); static void nlmclnt_unlock_callback(struct rpc_task *); static void nlmclnt_cancel_callback(struct rpc_task *); static int nlm_stat_to_errno(u32 stat); +static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host); /* * Cookie counter for NLM requests @@ -41,11 +43,83 @@ static inline void nlmclnt_next_cookie(struct nlm_cookie *c) nlm_cookie++; } +static struct nlm_lockowner *nlm_get_lockowner(struct nlm_lockowner *lockowner) +{ + atomic_inc(&lockowner->count); + return lockowner; +} + +static void nlm_put_lockowner(struct nlm_lockowner *lockowner) +{ + if (!atomic_dec_and_lock(&lockowner->count, &lockowner->host->h_lock)) + return; + list_del(&lockowner->list); + spin_unlock(&lockowner->host->h_lock); + nlm_release_host(lockowner->host); + kfree(lockowner); +} + +static inline int nlm_pidbusy(struct nlm_host *host, uint32_t pid) +{ + struct nlm_lockowner *lockowner; + list_for_each_entry(lockowner, &host->h_lockowners, list) { + if (lockowner->pid == pid) + return -EBUSY; + } + return 0; +} + +static inline uint32_t __nlm_alloc_pid(struct nlm_host *host) +{ + uint32_t res; + do { + res = host->h_pidcount++; + } while (nlm_pidbusy(host, res) < 0); + return res; +} + +static struct nlm_lockowner *__nlm_find_lockowner(struct nlm_host *host, fl_owner_t owner) +{ + struct nlm_lockowner *lockowner; + list_for_each_entry(lockowner, &host->h_lockowners, list) { + if (lockowner->owner != owner) + continue; + return nlm_get_lockowner(lockowner); + } + return NULL; +} + +static struct nlm_lockowner *nlm_find_lockowner(struct nlm_host *host, fl_owner_t owner) +{ + struct nlm_lockowner *res, *new = NULL; + + spin_lock(&host->h_lock); + res = __nlm_find_lockowner(host, owner); + if (res == NULL) { + spin_unlock(&host->h_lock); + new = (struct nlm_lockowner *)kmalloc(sizeof(*new), GFP_KERNEL); + spin_lock(&host->h_lock); + res = __nlm_find_lockowner(host, owner); + if (res == NULL && new != NULL) { + res = new; + atomic_set(&new->count, 1); + new->owner = owner; + new->pid = __nlm_alloc_pid(host); + new->host = nlm_get_host(host); + list_add(&new->list, &host->h_lockowners); + new = NULL; + } + } + spin_unlock(&host->h_lock); + if (new != NULL) + kfree(new); + return res; +} + /* * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls */ -static inline void -nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) +static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) { struct nlm_args *argp = &req->a_args; struct nlm_lock *lock = &argp->lock; @@ -60,6 +134,14 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) locks_copy_lock(&lock->fl, fl); } +static void nlmclnt_release_lockargs(struct nlm_rqst *req) +{ + struct file_lock *fl = &req->a_args.lock.fl; + + if (fl->fl_ops && fl->fl_ops->fl_release_private) + fl->fl_ops->fl_release_private(fl); +} + /* * Initialize arguments for GRANTED call. The nlm_rqst structure * has been cleared already. @@ -77,8 +159,10 @@ nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) if (lock->oh.len > NLMCLNT_OHSIZE) { void *data = kmalloc(lock->oh.len, GFP_KERNEL); - if (!data) + if (!data) { + nlmclnt_freegrantargs(call); return 0; + } call->a_args.lock.oh.data = (u8 *) data; } @@ -89,12 +173,15 @@ nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock) void nlmclnt_freegrantargs(struct nlm_rqst *call) { + struct file_lock *fl = &call->a_args.lock.fl; /* * Check whether we allocated memory for the owner. */ if (call->a_args.lock.oh.data != (u8 *) call->a_owner) { kfree(call->a_args.lock.oh.data); } + if (fl->fl_ops && fl->fl_ops->fl_release_private) + fl->fl_ops->fl_release_private(fl); } /* @@ -165,6 +252,8 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) } call->a_host = host; + nlmclnt_locks_init_private(fl, host); + /* Set up the argument struct */ nlmclnt_setlockargs(call, fl); @@ -179,9 +268,6 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) else status = -EINVAL; - if (status < 0 && (call->a_flags & RPC_TASK_ASYNC)) - kfree(call); - out_restore: spin_lock_irqsave(¤t->sighand->siglock, flags); current->blocked = oldset; @@ -193,6 +279,7 @@ done: nlm_release_host(host); return status; } +EXPORT_SYMBOL(nlmclnt_proc); /* * Allocate an NLM RPC call struct @@ -382,7 +469,9 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) { int status; - if ((status = nlmclnt_call(req, NLMPROC_TEST)) < 0) + status = nlmclnt_call(req, NLMPROC_TEST); + nlmclnt_release_lockargs(req); + if (status < 0) return status; status = req->a_res.status; @@ -391,10 +480,9 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) } if (status == NLM_LCK_DENIED) { /* * Report the conflicting lock back to the application. - * FIXME: Is it OK to report the pid back as well? */ locks_copy_lock(fl, &req->a_res.lock.fl); - /* fl->fl_pid = 0; */ + fl->fl_pid = 0; } else { return nlm_stat_to_errno(req->a_res.status); } @@ -402,18 +490,30 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) return 0; } -static -void nlmclnt_insert_lock_callback(struct file_lock *fl) +static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl) { - nlm_get_host(fl->fl_u.nfs_fl.host); + memcpy(&new->fl_u.nfs_fl, &fl->fl_u.nfs_fl, sizeof(new->fl_u.nfs_fl)); + nlm_get_lockowner(new->fl_u.nfs_fl.owner); } -static -void nlmclnt_remove_lock_callback(struct file_lock *fl) + +static void nlmclnt_locks_release_private(struct file_lock *fl) { - if (fl->fl_u.nfs_fl.host) { - nlm_release_host(fl->fl_u.nfs_fl.host); - fl->fl_u.nfs_fl.host = NULL; - } + nlm_put_lockowner(fl->fl_u.nfs_fl.owner); + fl->fl_ops = NULL; +} + +static struct file_lock_operations nlmclnt_lock_ops = { + .fl_copy_lock = nlmclnt_locks_copy_lock, + .fl_release_private = nlmclnt_locks_release_private, +}; + +static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host) +{ + BUG_ON(fl->fl_ops != NULL); + fl->fl_u.nfs_fl.state = 0; + fl->fl_u.nfs_fl.flags = 0; + fl->fl_u.nfs_fl.owner = nlm_find_lockowner(host, fl->fl_owner); + fl->fl_ops = &nlmclnt_lock_ops; } /* @@ -446,7 +546,8 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) if (!host->h_monitored && nsm_monitor(host) < 0) { printk(KERN_NOTICE "lockd: failed to monitor %s\n", host->h_name); - return -ENOLCK; + status = -ENOLCK; + goto out; } do { @@ -456,18 +557,21 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) status = nlmclnt_block(host, fl, &resp->status); } if (status < 0) - return status; + goto out; } while (resp->status == NLM_LCK_BLOCKED && req->a_args.block); 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_u.nfs_fl.host = host; - fl->fl_insert = nlmclnt_insert_lock_callback; - fl->fl_remove = nlmclnt_remove_lock_callback; + 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__); } - - return nlm_stat_to_errno(resp->status); + status = nlm_stat_to_errno(resp->status); +out: + nlmclnt_release_lockargs(req); + return status; } /* @@ -527,13 +631,24 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED; if (req->a_flags & RPC_TASK_ASYNC) { - return nlmclnt_async_call(req, NLMPROC_UNLOCK, + 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); + } + return status; } - if ((status = nlmclnt_call(req, NLMPROC_UNLOCK)) < 0) + status = nlmclnt_call(req, NLMPROC_UNLOCK); + nlmclnt_release_lockargs(req); + if (status < 0) return status; + posix_lock_file(fl->fl_file, fl); if (resp->status == NLM_LCK_GRANTED) return 0; @@ -564,9 +679,9 @@ 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); kfree(req); return; retry_rebind: @@ -605,8 +720,10 @@ nlmclnt_cancel(struct nlm_host *host, struct file_lock *fl) status = nlmclnt_async_call(req, NLMPROC_CANCEL, nlmclnt_cancel_callback); - if (status < 0) + if (status < 0) { + nlmclnt_release_lockargs(req); kfree(req); + } spin_lock_irqsave(¤t->sighand->siglock, flags); current->blocked = oldset; @@ -648,6 +765,7 @@ nlmclnt_cancel_callback(struct rpc_task *task) die: nlm_release_host(req->a_host); + nlmclnt_release_lockargs(req); kfree(req); return;