fs/cifs/file.c | 2 fs/lockd/clntlock.c | 4 fs/lockd/clntproc.c | 182 ++++++++++++++++++++++++++++++++++++-------- fs/lockd/host.c | 18 ++-- fs/lockd/svc4proc.c | 1 fs/lockd/svclock.c | 13 ++- fs/lockd/svcproc.c | 1 fs/locks.c | 130 +++++++++++++++++++------------ fs/nfs/nfs4state.c | 5 + fs/nfsd/nfs4state.c | 13 +-- include/linux/fs.h | 20 +++- include/linux/lockd/lockd.h | 19 ++++ include/linux/nfs_fs_i.h | 4 13 files changed, 310 insertions(+), 102 deletions(-) diff -u --recursive --new-file --show-c-function linux-2.6.7/fs/cifs/file.c linux-2.6.7-07-cleanup_posix/fs/cifs/file.c --- linux-2.6.7/fs/cifs/file.c 2004-06-29 17:27:24.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/fs/cifs/file.c 2004-06-30 14:18:02.000000000 -0400 @@ -569,6 +569,8 @@ cifs_lock(struct file *file, int cmd, st 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; } diff -u --recursive --new-file --show-c-function linux-2.6.7/fs/lockd/clntlock.c linux-2.6.7-07-cleanup_posix/fs/lockd/clntlock.c --- linux-2.6.7/fs/lockd/clntlock.c 2004-06-29 17:28:14.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/fs/lockd/clntlock.c 2004-06-30 14:17:40.000000000 -0400 @@ -146,7 +146,7 @@ void nlmclnt_mark_reclaim(struct nlm_hos inode = fl->fl_file->f_dentry->d_inode; if (inode->i_sb->s_magic != NFS_SUPER_MAGIC) continue; - if (fl->fl_u.nfs_fl.host != host) + if (fl->fl_u.nfs_fl.owner->host != host) continue; if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) continue; @@ -215,7 +215,7 @@ restart: inode = fl->fl_file->f_dentry->d_inode; if (inode->i_sb->s_magic != NFS_SUPER_MAGIC) continue; - if (fl->fl_u.nfs_fl.host != host) + if (fl->fl_u.nfs_fl.owner->host != host) continue; if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_RECLAIM)) continue; diff -u --recursive --new-file --show-c-function linux-2.6.7/fs/lockd/clntproc.c linux-2.6.7-07-cleanup_posix/fs/lockd/clntproc.c --- linux-2.6.7/fs/lockd/clntproc.c 2004-06-29 17:27:26.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/fs/lockd/clntproc.c 2004-06-30 14:18:02.000000000 -0400 @@ -27,6 +27,7 @@ static int nlmclnt_unlock(struct nlm_rqs 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 +42,83 @@ static inline void nlmclnt_next_cookie(s 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 +133,14 @@ nlmclnt_setlockargs(struct nlm_rqst *req 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 +158,10 @@ nlmclnt_setgrantargs(struct nlm_rqst *ca 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 +172,15 @@ nlmclnt_setgrantargs(struct nlm_rqst *ca 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 +251,8 @@ nlmclnt_proc(struct inode *inode, int cm } call->a_host = host; + nlmclnt_locks_init_private(fl, host); + /* Set up the argument struct */ nlmclnt_setlockargs(call, fl); @@ -179,9 +267,6 @@ nlmclnt_proc(struct inode *inode, int cm 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; @@ -382,7 +467,9 @@ nlmclnt_test(struct nlm_rqst *req, struc { 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 +478,9 @@ nlmclnt_test(struct nlm_rqst *req, struc } 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 +488,36 @@ nlmclnt_test(struct nlm_rqst *req, struc 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 void nlmclnt_steal_locks(struct file_lock *fl, fl_owner_t owner) +{ + locks_remove_posix(fl->fl_file, owner); +} + +static struct file_lock_operations nlmclnt_lock_ops = { + .fl_copy_lock = nlmclnt_locks_copy_lock, + .fl_release_private = nlmclnt_locks_release_private, + .fl_steal_locks = nlmclnt_steal_locks, +}; + +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 +550,8 @@ nlmclnt_lock(struct nlm_rqst *req, struc 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 +561,21 @@ nlmclnt_lock(struct nlm_rqst *req, struc 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; - } - - return nlm_stat_to_errno(resp->status); + 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: + nlmclnt_release_lockargs(req); + return status; } /* @@ -527,13 +635,24 @@ nlmclnt_unlock(struct nlm_rqst *req, str 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 +683,9 @@ nlmclnt_unlock_callback(struct rpc_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 +724,10 @@ nlmclnt_cancel(struct nlm_host *host, st 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 +769,7 @@ nlmclnt_cancel_callback(struct rpc_task die: nlm_release_host(req->a_host); + nlmclnt_release_lockargs(req); kfree(req); return; diff -u --recursive --new-file --show-c-function linux-2.6.7/fs/lockd/host.c linux-2.6.7-07-cleanup_posix/fs/lockd/host.c --- linux-2.6.7/fs/lockd/host.c 2004-06-29 17:27:41.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/fs/lockd/host.c 2004-06-30 14:17:40.000000000 -0400 @@ -119,13 +119,15 @@ nlm_lookup_host(int server, struct socka init_MUTEX(&host->h_sema); host->h_nextrebind = jiffies + NLM_HOST_REBIND; host->h_expires = jiffies + NLM_HOST_EXPIRE; - host->h_count = 1; + atomic_set(&host->h_count, 1); init_waitqueue_head(&host->h_gracewait); host->h_state = 0; /* pseudo NSM state */ host->h_nsmstate = 0; /* real NSM state */ host->h_server = server; host->h_next = nlm_hosts[hash]; nlm_hosts[hash] = host; + INIT_LIST_HEAD(&host->h_lockowners); + spin_lock_init(&host->h_lock); if (++nrhosts > NLM_HOST_MAX) next_gc = 0; @@ -235,7 +237,7 @@ struct nlm_host * nlm_get_host(struct nl { if (host) { dprintk("lockd: get host %s\n", host->h_name); - host->h_count ++; + atomic_inc(&host->h_count); host->h_expires = jiffies + NLM_HOST_EXPIRE; } return host; @@ -246,9 +248,10 @@ struct nlm_host * nlm_get_host(struct nl */ void nlm_release_host(struct nlm_host *host) { - if (host && host->h_count) { + if (host != NULL) { dprintk("lockd: release host %s\n", host->h_name); - host->h_count --; + atomic_dec(&host->h_count); + BUG_ON(atomic_read(&host->h_count) < 0); } } @@ -283,7 +286,7 @@ nlm_shutdown_hosts(void) for (i = 0; i < NLM_HOST_NRHASH; i++) { for (host = nlm_hosts[i]; host; host = host->h_next) { dprintk(" %s (cnt %d use %d exp %ld)\n", - host->h_name, host->h_count, + host->h_name, atomic_read(&host->h_count), host->h_inuse, host->h_expires); } } @@ -314,10 +317,10 @@ nlm_gc_hosts(void) for (i = 0; i < NLM_HOST_NRHASH; i++) { q = &nlm_hosts[i]; while ((host = *q) != NULL) { - if (host->h_count || host->h_inuse + if (atomic_read(&host->h_count) || host->h_inuse || time_before(jiffies, host->h_expires)) { dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n", - host->h_name, host->h_count, + host->h_name, atomic_read(&host->h_count), host->h_inuse, host->h_expires); q = &host->h_next; continue; @@ -336,6 +339,7 @@ nlm_gc_hosts(void) rpc_destroy_client(host->h_rpcclnt); } } + BUG_ON(!list_empty(&host->h_lockowners)); kfree(host); nrhosts--; } diff -u --recursive --new-file --show-c-function linux-2.6.7/fs/lockd/svc4proc.c linux-2.6.7-07-cleanup_posix/fs/lockd/svc4proc.c --- linux-2.6.7/fs/lockd/svc4proc.c 2004-06-29 17:27:37.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/fs/lockd/svc4proc.c 2004-06-30 14:17:36.000000000 -0400 @@ -55,6 +55,7 @@ nlm4svc_retrieve_args(struct svc_rqst *r /* Set up the missing parts of the file_lock structure */ lock->fl.fl_file = &file->f_file; lock->fl.fl_owner = (fl_owner_t) host; + lock->fl.fl_lmops = &nlmsvc_lock_operations; } return 0; diff -u --recursive --new-file --show-c-function linux-2.6.7/fs/lockd/svclock.c linux-2.6.7-07-cleanup_posix/fs/lockd/svclock.c --- linux-2.6.7/fs/lockd/svclock.c 2004-06-29 17:27:34.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/fs/lockd/svclock.c 2004-06-30 14:17:45.000000000 -0400 @@ -42,7 +42,6 @@ static void nlmsvc_insert_block(struct nlm_block *block, unsigned long); static int nlmsvc_remove_block(struct nlm_block *block); static void nlmsvc_grant_callback(struct rpc_task *task); -static void nlmsvc_notify_blocked(struct file_lock *); /* * The list of blocked locks to retry @@ -193,7 +192,7 @@ nlmsvc_create_block(struct svc_rqst *rqs goto failed_free; /* Set notifier function for VFS, and init args */ - block->b_call.a_args.lock.fl.fl_notify = nlmsvc_notify_blocked; + block->b_call.a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations; block->b_call.a_args.cookie = *cookie; /* see above */ dprintk("lockd: created block %p...\n", block); @@ -479,6 +478,16 @@ nlmsvc_notify_blocked(struct file_lock * printk(KERN_WARNING "lockd: notification for unknown block!\n"); } +static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2) +{ + return fl1->fl_owner == fl2->fl_owner && fl1->fl_pid == fl2->fl_pid; +} + +struct lock_manager_operations nlmsvc_lock_operations = { + .fl_compare_owner = nlmsvc_same_owner, + .fl_notify = nlmsvc_notify_blocked, +}; + /* * Try to claim a lock that was previously blocked. * diff -u --recursive --new-file --show-c-function linux-2.6.7/fs/lockd/svcproc.c linux-2.6.7-07-cleanup_posix/fs/lockd/svcproc.c --- linux-2.6.7/fs/lockd/svcproc.c 2004-06-29 17:27:48.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/fs/lockd/svcproc.c 2004-06-30 14:17:36.000000000 -0400 @@ -84,6 +84,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rq /* Set up the missing parts of the file_lock structure */ lock->fl.fl_file = &file->f_file; lock->fl.fl_owner = (fl_owner_t) host; + lock->fl.fl_lmops = &nlmsvc_lock_operations; } return 0; diff -u --recursive --new-file --show-c-function linux-2.6.7/fs/locks.c linux-2.6.7-07-cleanup_posix/fs/locks.c --- linux-2.6.7/fs/locks.c 2004-06-29 17:28:14.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/fs/locks.c 2004-06-30 14:18:02.000000000 -0400 @@ -167,6 +167,12 @@ static inline void locks_free_lock(struc if (!list_empty(&fl->fl_link)) panic("Attempting to free lock on active lock list"); + if (fl->fl_ops && fl->fl_ops->fl_release_private) { + fl->fl_ops->fl_release_private(fl); + fl->fl_ops = NULL; + } + fl->fl_lmops = NULL; + kmem_cache_free(filelock_cache, fl); } @@ -183,9 +189,8 @@ void locks_init_lock(struct file_lock *f fl->fl_flags = 0; fl->fl_type = 0; fl->fl_start = fl->fl_end = 0; - fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; } EXPORT_SYMBOL(locks_init_lock); @@ -217,10 +222,10 @@ void locks_copy_lock(struct file_lock *n new->fl_type = fl->fl_type; new->fl_start = fl->fl_start; new->fl_end = fl->fl_end; - new->fl_notify = fl->fl_notify; - new->fl_insert = fl->fl_insert; - new->fl_remove = fl->fl_remove; - new->fl_u = fl->fl_u; + new->fl_ops = fl->fl_ops; + new->fl_lmops = fl->fl_lmops; + if (fl->fl_ops && fl->fl_ops->fl_copy_lock) + fl->fl_ops->fl_copy_lock(new, fl); } EXPORT_SYMBOL(locks_copy_lock); @@ -321,9 +326,8 @@ static int flock_to_posix_lock(struct fi fl->fl_pid = current->tgid; fl->fl_file = filp; fl->fl_flags = FL_POSIX; - fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; return assign_type(fl, l->l_type); } @@ -361,9 +365,8 @@ static int flock64_to_posix_lock(struct fl->fl_pid = current->tgid; fl->fl_file = filp; fl->fl_flags = FL_POSIX; - fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; switch (l->l_type) { case F_RDLCK: @@ -397,9 +400,8 @@ static int lease_alloc(struct file *filp } fl->fl_start = 0; fl->fl_end = OFFSET_MAX; - fl->fl_notify = NULL; - fl->fl_insert = NULL; - fl->fl_remove = NULL; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; *flp = fl; return 0; @@ -414,14 +416,15 @@ static inline int locks_overlap(struct f } /* - * Check whether two locks have the same owner. The apparently superfluous - * check for fl_pid enables us to distinguish between locks set by lockd. + * Check whether two locks have the same owner. */ static inline int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2) { - return (fl1->fl_owner == fl2->fl_owner) && - (fl1->fl_pid == fl2->fl_pid); + if (fl1->fl_lmops && fl1->fl_lmops->fl_compare_owner) + return fl2->fl_lmops == fl1->fl_lmops && + fl1->fl_lmops->fl_compare_owner(fl1, fl2); + return fl1->fl_owner == fl2->fl_owner; } /* Remove waiter from blocker's block list. @@ -472,8 +475,8 @@ static void locks_wake_up_blocks(struct struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_block); __locks_delete_block(waiter); - if (waiter->fl_notify) - waiter->fl_notify(waiter); + if (waiter->fl_lmops && waiter->fl_lmops->fl_notify) + waiter->fl_lmops->fl_notify(waiter); else wake_up(&waiter->fl_wait); } @@ -490,8 +493,8 @@ static void locks_insert_lock(struct fil fl->fl_next = *pos; *pos = fl; - if (fl->fl_insert) - fl->fl_insert(fl); + if (fl->fl_ops && fl->fl_ops->fl_insert) + fl->fl_ops->fl_insert(fl); } /* @@ -514,8 +517,8 @@ static void locks_delete_lock(struct fil fl->fl_fasync = NULL; } - if (fl->fl_remove) - fl->fl_remove(fl); + if (fl->fl_ops && fl->fl_ops->fl_remove) + fl->fl_ops->fl_remove(fl); locks_wake_up_blocks(fl); locks_free_lock(fl); @@ -631,24 +634,15 @@ int posix_locks_deadlock(struct file_loc struct file_lock *block_fl) { struct list_head *tmp; - fl_owner_t caller_owner, blocked_owner; - unsigned int caller_pid, blocked_pid; - - caller_owner = caller_fl->fl_owner; - caller_pid = caller_fl->fl_pid; - blocked_owner = block_fl->fl_owner; - blocked_pid = block_fl->fl_pid; next_task: - if (caller_owner == blocked_owner && caller_pid == blocked_pid) + if (posix_same_owner(caller_fl, block_fl)) return 1; list_for_each(tmp, &blocked_list) { struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); - if ((fl->fl_owner == blocked_owner) - && (fl->fl_pid == blocked_pid)) { + if (posix_same_owner(fl, block_fl)) { fl = fl->fl_next; - blocked_owner = fl->fl_owner; - blocked_pid = fl->fl_pid; + block_fl = fl; goto next_task; } } @@ -912,6 +906,34 @@ int posix_lock_file(struct file *filp, s } /** + * 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 * @@ -988,6 +1010,8 @@ int locks_mandatory_area(int read_write, break; } + if (fl.fl_ops && fl.fl_ops->fl_release_private) + fl.fl_ops->fl_release_private(&fl); return error; } @@ -1422,7 +1446,6 @@ int fcntl_getlk(struct file *filp, struc error = -EFAULT; if (!copy_to_user(l, &flock, sizeof(flock))) error = 0; - out: return error; } @@ -1489,8 +1512,7 @@ int fcntl_setlk(struct file *filp, unsig 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 (;;) { @@ -1624,8 +1646,7 @@ int fcntl_setlk64(struct file *filp, uns 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 (;;) { @@ -1672,10 +1693,12 @@ void locks_remove_posix(struct file *fil lock.fl_owner = owner; lock.fl_pid = current->tgid; lock.fl_file = filp; + lock.fl_ops = NULL; + lock.fl_lmops = NULL; 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 @@ -1684,13 +1707,16 @@ void locks_remove_posix(struct file *fil lock_kernel(); while (*before != NULL) { struct file_lock *fl = *before; - if (IS_POSIX(fl) && (fl->fl_owner == owner)) { + if (IS_POSIX(fl) && posix_same_owner(fl, &lock)) { locks_delete_lock(before); continue; } before = &fl->fl_next; } unlock_kernel(); +out: + if (lock.fl_ops && lock.fl_ops->fl_release_private) + lock.fl_ops->fl_release_private(&lock); } EXPORT_SYMBOL(locks_remove_posix); @@ -1985,12 +2011,18 @@ EXPORT_SYMBOL(lock_may_write); static inline void __steal_locks(struct file *file, fl_owner_t from) { struct inode *inode = file->f_dentry->d_inode; - struct file_lock *fl = inode->i_flock; + struct file_lock *fl; - while (fl) { - if (fl->fl_file == file && fl->fl_owner == from) +restart: + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (fl->fl_file == file && fl->fl_owner == from) { + if (fl->fl_ops && fl->fl_ops->fl_steal_locks) { + fl->fl_ops->fl_steal_locks(fl, from); + /* Some filesystems may just drop the lock */ + goto restart; + } fl->fl_owner = current->files; - fl = fl->fl_next; + } } } diff -u --recursive --new-file --show-c-function linux-2.6.7/fs/nfs/nfs4state.c linux-2.6.7-07-cleanup_posix/fs/nfs/nfs4state.c --- linux-2.6.7/fs/nfs/nfs4state.c 2004-06-29 17:27:26.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/fs/nfs/nfs4state.c 2004-06-30 14:18:02.000000000 -0400 @@ -602,6 +602,10 @@ nfs4_notify_setlk(struct inode *inode, s { struct nfs4_state *state = lsp->ls_parent; + /* 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__); if (!list_empty(&lsp->ls_locks)) return; write_lock(&state->state_lock); @@ -625,6 +629,7 @@ nfs4_notify_unlck(struct inode *inode, s struct nfs4_state *state = lsp->ls_parent; struct file_lock *fl; + posix_lock_file(request->fl_file, request); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & FL_POSIX)) continue; diff -u --recursive --new-file --show-c-function linux-2.6.7/fs/nfsd/nfs4state.c linux-2.6.7-07-cleanup_posix/fs/nfsd/nfs4state.c --- linux-2.6.7/fs/nfsd/nfs4state.c 2004-06-29 17:27:53.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/fs/nfsd/nfs4state.c 2004-06-30 14:17:32.000000000 -0400 @@ -2180,6 +2180,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struc goto out; } + locks_init_lock(&file_lock); switch (lock->lk_type) { case NFS4_READ_LT: case NFS4_READW_LT: @@ -2197,9 +2198,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struc file_lock.fl_pid = lockownerid_hashval(lock->lk_stateowner->so_id); file_lock.fl_file = filp; file_lock.fl_flags = FL_POSIX; - file_lock.fl_notify = NULL; - file_lock.fl_insert = NULL; - file_lock.fl_remove = NULL; file_lock.fl_start = lock->lk_offset; if ((lock->lk_length == ~(u64)0) || @@ -2215,6 +2213,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struc */ status = posix_lock_file(filp, &file_lock); + if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private) + file_lock.fl_ops->fl_release_private(&file_lock); dprintk("NFSD: nfsd4_lock: posix_test_lock passed. posix_lock_file status %d\n",status); switch (-status) { case 0: /* success! */ @@ -2296,6 +2296,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, stru } inode = current_fh->fh_dentry->d_inode; + locks_init_lock(&file_lock); switch (lockt->lt_type) { case NFS4_READ_LT: case NFS4_READW_LT: @@ -2381,14 +2382,12 @@ nfsd4_locku(struct svc_rqst *rqstp, stru filp = &stp->st_vfs_file; BUG_ON(!filp); + locks_init_lock(&file_lock); file_lock.fl_type = F_UNLCK; file_lock.fl_owner = (fl_owner_t) locku->lu_stateowner; file_lock.fl_pid = lockownerid_hashval(locku->lu_stateowner->so_id); file_lock.fl_file = filp; file_lock.fl_flags = FL_POSIX; - file_lock.fl_notify = NULL; - file_lock.fl_insert = NULL; - file_lock.fl_remove = NULL; file_lock.fl_start = locku->lu_offset; if ((locku->lu_length == ~(u64)0) || LOFF_OVERFLOW(locku->lu_offset, locku->lu_length)) @@ -2401,6 +2400,8 @@ nfsd4_locku(struct svc_rqst *rqstp, stru * Try to unlock the file in the VFS. */ status = posix_lock_file(filp, &file_lock); + if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private) + file_lock.fl_ops->fl_release_private(&file_lock); if (status) { printk("NFSD: nfs4_locku: posix_lock_file failed!\n"); goto out_nfserr; diff -u --recursive --new-file --show-c-function linux-2.6.7/include/linux/fs.h linux-2.6.7-07-cleanup_posix/include/linux/fs.h --- linux-2.6.7/include/linux/fs.h 2004-06-29 17:27:33.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/include/linux/fs.h 2004-06-30 14:18:02.000000000 -0400 @@ -622,6 +622,19 @@ extern void close_private_file(struct fi */ typedef struct files_struct *fl_owner_t; +struct file_lock_operations { + void (*fl_insert)(struct file_lock *); /* lock insertion callback */ + void (*fl_remove)(struct file_lock *); /* lock removal callback */ + void (*fl_copy_lock)(struct file_lock *, struct file_lock *); + void (*fl_release_private)(struct file_lock *); + void (*fl_steal_locks)(struct file_lock *, fl_owner_t); +}; + +struct lock_manager_operations { + int (*fl_compare_owner)(struct file_lock *, struct file_lock *); + void (*fl_notify)(struct file_lock *); /* unblock callback */ +}; + /* that will die - we need it for nfs_lock_info */ #include @@ -638,13 +651,11 @@ struct file_lock { loff_t fl_start; loff_t fl_end; - void (*fl_notify)(struct file_lock *); /* unblock callback */ - void (*fl_insert)(struct file_lock *); /* lock insertion callback */ - void (*fl_remove)(struct file_lock *); /* lock removal callback */ - struct fasync_struct * fl_fasync; /* for lease break notifications */ unsigned long fl_break_time; /* for nonblocking lease breaks */ + struct file_lock_operations *fl_ops; /* Callbacks for filesystems */ + struct lock_manager_operations *fl_lmops; /* Callbacks for lockmanagers */ union { struct nfs_lock_info nfs_fl; } fl_u; @@ -683,6 +694,7 @@ extern void locks_remove_posix(struct fi 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 *); diff -u --recursive --new-file --show-c-function linux-2.6.7/include/linux/lockd/lockd.h linux-2.6.7-07-cleanup_posix/include/linux/lockd/lockd.h --- linux-2.6.7/include/linux/lockd/lockd.h 2004-06-29 17:27:27.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/include/linux/lockd/lockd.h 2004-06-30 14:17:40.000000000 -0400 @@ -52,10 +52,25 @@ struct nlm_host { wait_queue_head_t h_gracewait; /* wait while reclaiming */ u32 h_state; /* pseudo-state counter */ u32 h_nsmstate; /* true remote NSM state */ - unsigned int h_count; /* reference count */ + u32 h_pidcount; /* Pseudopids */ + atomic_t h_count; /* reference count */ struct semaphore h_sema; /* mutex for pmap binding */ unsigned long h_nextrebind; /* next portmap call */ unsigned long h_expires; /* eligible for GC */ + struct list_head h_lockowners; /* Lockowners for the client */ + spinlock_t h_lock; +}; + +/* + * Map an fl_owner_t into a unique 32-bit "pid" + */ +struct nlm_lockowner { + struct list_head list; + atomic_t count; + + struct nlm_host *host; + fl_owner_t owner; + uint32_t pid; }; /* @@ -205,6 +220,8 @@ nlm_compare_locks(struct file_lock *fl1, &&(fl1->fl_type == fl2->fl_type || fl2->fl_type == F_UNLCK); } +extern struct lock_manager_operations nlmsvc_lock_operations; + #endif /* __KERNEL__ */ #endif /* LINUX_LOCKD_LOCKD_H */ diff -u --recursive --new-file --show-c-function linux-2.6.7/include/linux/nfs_fs_i.h linux-2.6.7-07-cleanup_posix/include/linux/nfs_fs_i.h --- linux-2.6.7/include/linux/nfs_fs_i.h 2004-06-29 17:27:34.000000000 -0400 +++ linux-2.6.7-07-cleanup_posix/include/linux/nfs_fs_i.h 2004-06-30 14:17:40.000000000 -0400 @@ -5,13 +5,15 @@ #include #include +struct nlm_lockowner; + /* * NFS lock info */ struct nfs_lock_info { u32 state; u32 flags; - struct nlm_host *host; + struct nlm_lockowner *owner; }; /*