diff -uNrp linux-2.6.18.i386.orig/fs/fuse/file.c linux-2.6.18.i386/fs/fuse/file.c --- linux-2.6.18.i386.orig/fs/fuse/file.c 2007-06-12 12:05:31.000000000 -0400 +++ linux-2.6.18.i386/fs/fuse/file.c 2007-06-12 12:06:35.000000000 -0400 @@ -743,8 +743,7 @@ static int fuse_file_lock(struct file *f if (cmd == F_GETLK) { if (fc->no_lock) { - if (!posix_test_lock(file, fl, fl)) - fl->fl_type = F_UNLCK; + posix_test_lock(file, fl, NULL); err = 0; } else err = fuse_getlk(file, fl); diff -uNrp linux-2.6.18.i386.orig/fs/gfs2/locking/dlm/plock.c linux-2.6.18.i386/fs/gfs2/locking/dlm/plock.c --- linux-2.6.18.i386.orig/fs/gfs2/locking/dlm/plock.c 2007-06-12 12:05:30.000000000 -0400 +++ linux-2.6.18.i386/fs/gfs2/locking/dlm/plock.c 2007-06-12 12:06:35.000000000 -0400 @@ -24,6 +24,14 @@ struct plock_op { struct gdlm_plock_info info; }; +struct plock_xop { + struct plock_op xop; + void *callback; + void *fl; + void *file; +}; + + static inline void set_version(struct gdlm_plock_info *info) { info->version[0] = GDLM_PLOCK_VERSION_MAJOR; @@ -63,12 +71,14 @@ int gdlm_plock(void *lockspace, struct l { struct gdlm_ls *ls = lockspace; struct plock_op *op; + struct plock_xop *xop; int rv; - op = kzalloc(sizeof(*op), GFP_KERNEL); - if (!op) + xop = kzalloc(sizeof(*xop), GFP_KERNEL); + if (!xop) return -ENOMEM; + op = &xop->xop; op->info.optype = GDLM_PLOCK_OP_LOCK; op->info.pid = fl->fl_pid; op->info.ex = (fl->fl_type == F_WRLCK); @@ -78,9 +88,20 @@ int gdlm_plock(void *lockspace, struct l op->info.start = fl->fl_start; op->info.end = fl->fl_end; op->info.owner = (__u64)(long) fl->fl_owner; + if (fl->fl_lmops && fl->fl_lmops->fl_grant) { + xop->callback = fl->fl_lmops->fl_grant; + /* might need to make a copy */ + xop->fl = fl; + xop->file = file; + } else + xop->callback = NULL; send_op(op); - wait_event(recv_wq, (op->done != 0)); + + if (xop->callback == NULL) + wait_event(recv_wq, (op->done != 0)); + else + return -EINPROGRESS; spin_lock(&ops_lock); if (!list_empty(&op->list)) { @@ -98,7 +119,60 @@ int gdlm_plock(void *lockspace, struct l (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; + 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; + fl = xop->fl; + notify = xop->callback; + + if (op->info.rv) { + notify(fl, NULL, op->info.rv); + goto out; + } + + /* got fs lock; bookkeep locally as well: */ + if (posix_lock_file(file, fl)) { + /* + * 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(fl, 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; } @@ -137,6 +211,9 @@ int gdlm_punlock(void *lockspace, struct rv = op->info.rv; + if (rv == -ENOENT) + rv = 0; + kfree(op); return rv; } @@ -160,6 +237,7 @@ int gdlm_plock_get(void *lockspace, stru op->info.start = fl->fl_start; op->info.end = fl->fl_end; + send_op(op); wait_event(recv_wq, (op->done != 0)); @@ -172,9 +250,10 @@ int gdlm_plock_get(void *lockspace, stru rv = op->info.rv; - if (rv == 0) - fl->fl_type = F_UNLCK; - else if (rv > 0) { + fl->fl_type = F_UNLCK; + 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_pid = op->info.pid; fl->fl_start = op->info.start; @@ -242,9 +321,14 @@ static ssize_t dev_write(struct file *fi } spin_unlock(&ops_lock); - if (found) - wake_up(&recv_wq); - else + if (found) { + struct plock_xop *xop; + xop = (struct plock_xop *)op; + if (xop->callback) + count = gdlm_plock_callback(op); + else + wake_up(&recv_wq); + } else printk(KERN_INFO "gdlm dev_write no op %x %llx\n", info.fsid, (unsigned long long)info.number); return count; diff -uNrp linux-2.6.18.i386.orig/fs/gfs2/locking/nolock/main.c linux-2.6.18.i386/fs/gfs2/locking/nolock/main.c --- linux-2.6.18.i386.orig/fs/gfs2/locking/nolock/main.c 2007-06-12 12:05:30.000000000 -0400 +++ linux-2.6.18.i386/fs/gfs2/locking/nolock/main.c 2007-06-12 12:06:35.000000000 -0400 @@ -164,13 +164,7 @@ static void nolock_unhold_lvb(void *lock static int nolock_plock_get(void *lockspace, struct lm_lockname *name, struct file *file, struct file_lock *fl) { - struct file_lock tmp; - int ret; - - ret = posix_test_lock(file, fl, &tmp); - fl->fl_type = F_UNLCK; - if (ret) - memcpy(fl, &tmp, sizeof(struct file_lock)); + posix_test_lock(file, fl, NULL); return 0; } diff -uNrp linux-2.6.18.i386.orig/fs/gfs2/ops_file.c linux-2.6.18.i386/fs/gfs2/ops_file.c --- linux-2.6.18.i386.orig/fs/gfs2/ops_file.c 2007-06-12 12:05:30.000000000 -0400 +++ linux-2.6.18.i386/fs/gfs2/ops_file.c 2007-06-12 12:06:35.000000000 -0400 @@ -514,18 +514,18 @@ static int gfs2_lock(struct file *file, if (sdp->sd_args.ar_localflocks) { if (IS_GETLK(cmd)) { - struct file_lock tmp; - int ret; - ret = posix_test_lock(file, fl, &tmp); - fl->fl_type = F_UNLCK; - if (ret) - memcpy(fl, &tmp, sizeof(struct file_lock)); + posix_test_lock(file, fl, NULL); return 0; } else { return posix_lock_file_wait(file, fl); } } + if (cmd == F_CANCELLK) { + /* Hack: */ + cmd = F_SETLK; + fl->fl_type = F_UNLCK; + } if (IS_GETLK(cmd)) return gfs2_lm_plock_get(sdp, &name, file, fl); else if (fl->fl_type == F_UNLCK) diff -uNrp linux-2.6.18.i386.orig/fs/lockd/svc4proc.c linux-2.6.18.i386/fs/lockd/svc4proc.c --- linux-2.6.18.i386.orig/fs/lockd/svc4proc.c 2007-06-12 12:05:31.000000000 -0400 +++ linux-2.6.18.i386/fs/lockd/svc4proc.c 2007-06-12 12:06:35.000000000 -0400 @@ -96,10 +96,12 @@ nlm4svc_proc_test(struct svc_rqst *rqstp /* Obtain client and file */ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;; /* 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)); nlm_release_host(host); @@ -126,7 +128,7 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp /* Obtain client and file */ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; #if 0 /* If supplied state doesn't match current state, we assume it's @@ -143,6 +145,8 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp /* Now try to lock the file */ resp->status = nlmsvc_lock(rqstp, file, &argp->lock, argp->block, &argp->cookie); + if (resp->status == nlm_drop_reply) + return rpc_drop_reply; dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); nlm_release_host(host); @@ -169,7 +173,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqs /* Obtain client and file */ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Try to cancel request. */ resp->status = nlmsvc_cancel_blocked(file, &argp->lock); @@ -202,7 +206,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqs /* Obtain client and file */ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Now try to remove the lock */ resp->status = nlmsvc_unlock(file, &argp->lock); @@ -337,7 +341,7 @@ nlm4svc_proc_share(struct svc_rqst *rqst /* Obtain client and file */ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Now try to create the share */ resp->status = nlmsvc_share_file(host, file, argp); @@ -370,7 +374,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rq /* Obtain client and file */ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Now try to lock the file */ resp->status = nlmsvc_unshare_file(host, file, argp); diff -uNrp linux-2.6.18.i386.orig/fs/lockd/svclock.c linux-2.6.18.i386/fs/lockd/svclock.c --- linux-2.6.18.i386.orig/fs/lockd/svclock.c 2007-06-12 12:05:31.000000000 -0400 +++ linux-2.6.18.i386/fs/lockd/svclock.c 2007-06-12 12:06:35.000000000 -0400 @@ -172,7 +172,7 @@ nlmsvc_find_block(struct nlm_cookie *coo */ static inline struct nlm_block * nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, - struct nlm_lock *lock, struct nlm_cookie *cookie) + struct nlm_lock *lock, struct nlm_cookie *cookie, int conf) { struct nlm_block *block; struct nlm_host *host; @@ -203,6 +203,11 @@ nlmsvc_create_block(struct svc_rqst *rqs dprintk("lockd: created block %p...\n", block); + if (conf) { + block->b_fl = kzalloc(sizeof(struct file_lock), GFP_KERNEL); + if (!block->b_fl) + goto failed_free; + } /* Create and initialize the block */ block->b_daemon = rqstp->rq_server; block->b_host = host; @@ -265,6 +270,7 @@ static void nlmsvc_free_block(struct kre nlmsvc_freegrantargs(block->b_call); nlm_release_call(block->b_call); nlm_release_file(block->b_file); + kfree(block->b_fl); kfree(block); } @@ -350,6 +356,31 @@ static void nlmsvc_freegrantargs(struct } /* + * 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 * if required. */ @@ -357,7 +388,7 @@ u32 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_lock *lock, int wait, struct nlm_cookie *cookie) { - struct nlm_block *block, *newblock = NULL; + struct nlm_block *block = NULL; int error; u32 ret; @@ -370,29 +401,58 @@ nlmsvc_lock(struct svc_rqst *rqstp, stru wait); - lock->fl.fl_flags &= ~FL_SLEEP; -again: /* Lock file against concurrent access */ down(&file->f_sema); - /* 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); if (block == NULL) { - if (newblock != NULL) - lock = &newblock->b_call->a_args.lock; - } else + block = nlmsvc_create_block(rqstp, file, lock, cookie, 0); + ret = nlm_lck_denied_nolocks; + if (block == NULL) + goto out; lock = &block->b_call->a_args.lock; + } else + lock->fl.fl_flags &= ~FL_SLEEP; - error = posix_lock_file(file->f_file, &lock->fl); - lock->fl.fl_flags &= ~FL_SLEEP; + 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_TOO_LATE) { + nlmsvc_unlink_block(block); + ret = nlm_lck_denied; + goto out; + } + ret = nlm_drop_reply; + goto out; + } - dprintk("lockd: posix_lock_file returned %d\n", error); + 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) { case 0: ret = nlm_granted; goto out; case -EAGAIN: + ret = nlm_lck_denied; break; + case -EINPROGRESS: + if (wait) + 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: ret = nlm_deadlock; goto out; @@ -406,26 +466,11 @@ again: goto out; 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_sema as nlmsvc_create_block may try to - * to claim it while doing host garbage collection */ - if (newblock == NULL) { - up(&file->f_sema); - 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 */ - nlmsvc_insert_block(newblock, NLM_NEVER); + nlmsvc_insert_block(block, NLM_NEVER); out: up(&file->f_sema); - nlmsvc_release_block(newblock); nlmsvc_release_block(block); dprintk("lockd: nlmsvc_lock returned %u\n", ret); return ret; @@ -435,9 +480,14 @@ out: * Test for presence of a conflicting lock. */ u32 -nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, - struct nlm_lock *conflock) +nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, + 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", file->f_file->f_dentry->d_inode->i_sb->s_id, file->f_file->f_dentry->d_inode->i_ino, @@ -445,18 +495,63 @@ nlmsvc_testlock(struct nlm_file *file, s (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); - if (posix_test_lock(file->f_file, &lock->fl, &conflock->fl)) { - dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n", - conflock->fl.fl_type, - (long long)conflock->fl.fl_start, - (long long)conflock->fl.fl_end); - conflock->caller = "somehost"; /* FIXME */ - conflock->oh.len = 0; /* don't return OH info */ - conflock->svid = conflock->fl.fl_pid; - return nlm_lck_denied; + /* Get existing block (in case client is busy-waiting) */ + block = nlmsvc_lookup_block(file, lock); + + if (block == NULL) { + block = nlmsvc_create_block(rqstp, file, lock, cookie, 1); + if (block == NULL) + return nlm_granted; + } + 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_TOO_LATE) { + 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; } - return nlm_granted; +conf_lock: + dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n", + lock->fl.fl_type, (long long)lock->fl.fl_start, + (long long)lock->fl.fl_end); + conflock->caller = "somehost"; /* FIXME */ + conflock->len = strlen(conflock->caller); + conflock->oh.len = 0; /* don't return OH info */ + conflock->svid = lock->fl.fl_pid; + conflock->fl.fl_type = lock->fl.fl_type; + conflock->fl.fl_start = lock->fl.fl_start; + conflock->fl.fl_end = lock->fl.fl_end; + ret = nlm_lck_denied; +out: + if (block) + nlmsvc_release_block(block); + return ret; } /* @@ -482,7 +577,7 @@ nlmsvc_unlock(struct nlm_file *file, str nlmsvc_cancel_blocked(file, lock); 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; } @@ -518,6 +613,63 @@ nlmsvc_cancel_blocked(struct nlm_file *f } /* + * 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_TOO_LATE; + 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(); + for (block = nlm_blocked; block; block = block->b_next) { + 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_TOO_LATE) { + 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 * VFS layer when a lock on which we blocked is removed. * @@ -549,6 +701,7 @@ static int nlmsvc_same_owner(struct file struct lock_manager_operations nlmsvc_lock_operations = { .fl_compare_owner = nlmsvc_same_owner, .fl_notify = nlmsvc_notify_blocked, + .fl_grant = nlmsvc_grant_deferred, }; /* @@ -571,6 +724,8 @@ nlmsvc_grant_blocked(struct nlm_block *b dprintk("lockd: grant blocked lock %p\n", block); + kref_get(&block->b_count); + /* Unlink block request from list */ nlmsvc_unlink_block(block); @@ -584,20 +739,23 @@ nlmsvc_grant_blocked(struct nlm_block *b /* Try the lock operation again */ 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; switch (error) { case 0: break; 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_release_block(block); return; default: printk(KERN_WARNING "lockd: unexpected error %d in %s!\n", -error, __FUNCTION__); nlmsvc_insert_block(block, 10 * HZ); + nlmsvc_release_block(block); return; } @@ -686,6 +844,23 @@ nlmsvc_grant_reply(struct svc_rqst *rqst 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_TOO_LATE; + 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 * picks up locks that can be granted, or grant notifications that must @@ -706,9 +881,12 @@ nlmsvc_retry_blocked(void) break; dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n", block, block->b_when); - kref_get(&block->b_count); - nlmsvc_grant_blocked(block); - nlmsvc_release_block(block); + 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); } if ((block = nlm_blocked) && block->b_when != NLM_NEVER) diff -uNrp linux-2.6.18.i386.orig/fs/lockd/svcproc.c linux-2.6.18.i386/fs/lockd/svcproc.c --- linux-2.6.18.i386.orig/fs/lockd/svcproc.c 2007-06-12 12:05:31.000000000 -0400 +++ linux-2.6.18.i386/fs/lockd/svcproc.c 2007-06-12 12:06:35.000000000 -0400 @@ -33,6 +33,7 @@ cast_to_nlm(u32 status, u32 vers) case nlm_lck_denied_nolocks: case nlm_lck_blocked: case nlm_lck_denied_grace_period: + case nlm_drop_reply: break; case nlm4_deadlock: status = nlm_lck_denied; @@ -59,7 +60,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rq struct nlm_host *host = NULL; struct nlm_file *file = NULL; struct nlm_lock *lock = &argp->lock; - u32 error; + u32 error = 0; /* nfsd callbacks must have been installed for this procedure */ if (!nlmsvc_ops) @@ -88,6 +89,8 @@ nlmsvc_retrieve_args(struct svc_rqst *rq no_locks: if (host) nlm_release_host(host); + if (error) + return error; return nlm_lck_denied_nolocks; } @@ -122,10 +125,12 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* 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", ntohl(resp->status), rqstp->rq_vers); @@ -153,7 +158,7 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; #if 0 /* If supplied state doesn't match current state, we assume it's @@ -170,6 +175,8 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, /* Now try to lock the file */ resp->status = cast_status(nlmsvc_lock(rqstp, file, &argp->lock, argp->block, &argp->cookie)); + if (resp->status == nlm_drop_reply) + return rpc_drop_reply; dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); nlm_release_host(host); @@ -196,7 +203,7 @@ nlmsvc_proc_cancel(struct svc_rqst *rqst /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Try to cancel request. */ resp->status = cast_status(nlmsvc_cancel_blocked(file, &argp->lock)); @@ -229,7 +236,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqst /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Now try to remove the lock */ resp->status = cast_status(nlmsvc_unlock(file, &argp->lock)); @@ -366,7 +373,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Now try to create the share */ resp->status = cast_status(nlmsvc_share_file(host, file, argp)); @@ -399,7 +406,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqs /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) - return rpc_success; + return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Now try to unshare the file */ resp->status = cast_status(nlmsvc_unshare_file(host, file, argp)); diff -uNrp linux-2.6.18.i386.orig/fs/lockd/svcsubs.c linux-2.6.18.i386/fs/lockd/svcsubs.c --- linux-2.6.18.i386.orig/fs/lockd/svcsubs.c 2007-06-12 12:05:31.000000000 -0400 +++ linux-2.6.18.i386/fs/lockd/svcsubs.c 2007-06-12 12:06:35.000000000 -0400 @@ -135,12 +135,6 @@ out_unlock: out_free: kfree(file); -#ifdef CONFIG_LOCKD_V4 - if (nfserr == 1) - nfserr = nlm4_stale_fh; - else -#endif - nfserr = nlm_lck_denied; goto out_unlock; } @@ -201,7 +195,7 @@ again: lock.fl_type = F_UNLCK; lock.fl_start = 0; 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", __FILE__, __LINE__); return 1; diff -uNrp linux-2.6.18.i386.orig/fs/locks.c linux-2.6.18.i386/fs/locks.c --- linux-2.6.18.i386.orig/fs/locks.c 2007-06-12 12:05:31.000000000 -0400 +++ linux-2.6.18.i386/fs/locks.c 2007-06-12 12:06:35.000000000 -0400 @@ -671,6 +671,7 @@ posix_test_lock(struct file *filp, struc { struct file_lock *cfl; + fl->fl_type = F_UNLCK; lock_kernel(); for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) { if (!IS_POSIX(cfl)) @@ -679,7 +680,10 @@ posix_test_lock(struct file *filp, struc break; } if (cfl) { - __locks_copy_lock(conflock, cfl); + if (conflock) + __locks_copy_lock(conflock, cfl); + else + __locks_copy_lock(fl, cfl); unlock_kernel(); return 1; } @@ -1611,12 +1615,63 @@ asmlinkage long sys_flock(unsigned int f 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) +{ + fl->fl_type = F_UNLCK; + if (filp->f_op && filp->f_op->lock) + return filp->f_op->lock(filp, F_GETLK, fl); + posix_test_lock(filp, fl, NULL); + 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. * This implements the F_GETLK command of fcntl(). */ 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; int error; @@ -1631,38 +1686,15 @@ int fcntl_getlk(struct file *filp, struc if (error) goto out; - if (filp->f_op && filp->f_op->lock) { - error = filp->f_op->lock(filp, F_GETLK, &file_lock); - 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; - else - fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); - } else { - fl = (posix_test_lock(filp, &file_lock, &cfl) ? &cfl : NULL); - } + error = vfs_test_lock(filp, &file_lock); + if (error) + goto out; - flock.l_type = F_UNLCK; - if (fl != NULL) { - flock.l_pid = fl->fl_pid; -#if BITS_PER_LONG == 32 - /* - * 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)) + flock.l_type = file_lock.fl_type; + if (file_lock.fl_type != F_UNLCK) { + error = posix_lock_to_flock(&flock, &file_lock); + if (error) 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; if (!copy_to_user(l, &flock, sizeof(flock))) @@ -1671,6 +1703,41 @@ out: 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. + * + * 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_conf(filp, fl, conf); +} +EXPORT_SYMBOL_GPL(vfs_lock_file); + /* Apply the lock described by l to an open file descriptor. * This implements both the F_SETLK and F_SETLKW commands of fcntl(). */ @@ -1733,21 +1800,17 @@ again: if (error) goto out; - if (filp->f_op && filp->f_op->lock != NULL) - error = filp->f_op->lock(filp, cmd, file_lock); - else { - for (;;) { - error = posix_lock_file(filp, file_lock); - if ((error != -EAGAIN) || (cmd == F_SETLK)) - break; - error = wait_event_interruptible(file_lock->fl_wait, - !file_lock->fl_next); - if (!error) - continue; - - locks_delete_block(file_lock); + for (;;) { + error = vfs_lock_file(filp, cmd, file_lock, NULL); + if (error != -EAGAIN || cmd == F_SETLK) break; - } + error = wait_event_interruptible(file_lock->fl_wait, + !file_lock->fl_next); + if (!error) + continue; + + locks_delete_block(file_lock); + break; } /* @@ -1770,7 +1833,7 @@ out: */ 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; int error; @@ -1785,27 +1848,14 @@ int fcntl_getlk64(struct file *filp, str if (error) goto out; - if (filp->f_op && filp->f_op->lock) { - error = filp->f_op->lock(filp, F_GETLK, &file_lock); - 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; - 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; - if (fl != NULL) { - 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; - } + error = vfs_test_lock(filp, &file_lock); + if (error) + goto out; + + flock.l_type = file_lock.fl_type; + if (file_lock.fl_type != F_UNLCK) + posix_lock_to_flock64(&flock, &file_lock); + error = -EFAULT; if (!copy_to_user(l, &flock, sizeof(flock))) error = 0; @@ -1876,22 +1926,18 @@ again: if (error) goto out; - if (filp->f_op && filp->f_op->lock != NULL) - error = filp->f_op->lock(filp, cmd, file_lock); - else { - for (;;) { - error = posix_lock_file(filp, file_lock); - if ((error != -EAGAIN) || (cmd == F_SETLK64)) - break; - error = wait_event_interruptible(file_lock->fl_wait, - !file_lock->fl_next); - if (!error) - continue; - - locks_delete_block(file_lock); + for (;;) { + error = vfs_lock_file(filp, cmd, file_lock, NULL); + if (error != -EAGAIN || cmd == F_SETLK64) break; - } - } + error = wait_event_interruptible(file_lock->fl_wait, + !file_lock->fl_next); + if (!error) + continue; + + locks_delete_block(file_lock); + break; + } /* * Attempt to detect a close/fcntl race and recover by @@ -1935,10 +1981,7 @@ void locks_remove_posix(struct file *fil 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); - else - posix_lock_file(filp, &lock); + vfs_lock_file(filp, F_SETLK, &lock, NULL); if (lock.fl_ops && lock.fl_ops->fl_release_private) lock.fl_ops->fl_release_private(&lock); @@ -2015,6 +2058,23 @@ posix_unblock_lock(struct file *filp, st 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); + else + return posix_unblock_lock(filp, fl); +} + +EXPORT_SYMBOL_GPL(vfs_cancel_lock); + static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx) { struct inode *inode = NULL; diff -uNrp linux-2.6.18.i386.orig/fs/nfs/file.c linux-2.6.18.i386/fs/nfs/file.c --- linux-2.6.18.i386.orig/fs/nfs/file.c 2007-06-12 12:05:30.000000000 -0400 +++ linux-2.6.18.i386/fs/nfs/file.c 2007-06-12 12:06:35.000000000 -0400 @@ -415,17 +415,12 @@ out_swapfile: static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) { - struct file_lock cfl; struct inode *inode = filp->f_mapping->host; int status = 0; lock_kernel(); /* Try local locking first */ - if (posix_test_lock(filp, fl, &cfl)) { - fl->fl_start = cfl.fl_start; - fl->fl_end = cfl.fl_end; - fl->fl_type = cfl.fl_type; - fl->fl_pid = cfl.fl_pid; + if (posix_test_lock(filp, fl, NULL)) { goto out; } diff -uNrp linux-2.6.18.i386.orig/fs/nfs/nfs4proc.c linux-2.6.18.i386/fs/nfs/nfs4proc.c --- linux-2.6.18.i386.orig/fs/nfs/nfs4proc.c 2007-06-12 12:05:30.000000000 -0400 +++ linux-2.6.18.i386/fs/nfs/nfs4proc.c 2007-06-12 12:06:35.000000000 -0400 @@ -3135,6 +3135,8 @@ static int _nfs4_proc_getlk(struct nfs4_ status = 0; } out: + if (request->fl_ops) + request->fl_ops->fl_release_private(request); up_read(&clp->cl_sem); return status; } diff -uNrp linux-2.6.18.i386.orig/fs/nfsd/lockd.c linux-2.6.18.i386/fs/nfsd/lockd.c --- linux-2.6.18.i386.orig/fs/nfsd/lockd.c 2007-06-12 12:05:30.000000000 -0400 +++ linux-2.6.18.i386/fs/nfsd/lockd.c 2007-06-12 12:06:35.000000000 -0400 @@ -39,18 +39,20 @@ nlm_fopen(struct svc_rqst *rqstp, struct fh_put(&fh); rqstp->rq_client = NULL; exp_readunlock(); - /* nlm and nfsd don't share error codes. - * we invent: 0 = no error - * 1 = stale file handle - * 2 = other error - */ + /* We return nlm error codes as nlm doesn't know + * about nfsd, but nfsd does know about nlm.. + */ switch (nfserr) { case nfs_ok: return 0; + case nfserr_dropit: + return nlm_drop_reply; +#ifdef CONFIG_LOCKD_V4 case nfserr_stale: - return 1; + return nlm4_stale_fh; +#endif default: - return 2; + return nlm_lck_denied; } } diff -uNrp linux-2.6.18.i386.orig/fs/nfsd/nfs4state.c linux-2.6.18.i386/fs/nfsd/nfs4state.c --- linux-2.6.18.i386.orig/fs/nfsd/nfs4state.c 2007-06-12 12:05:30.000000000 -0400 +++ linux-2.6.18.i386/fs/nfsd/nfs4state.c 2007-06-12 12:06:35.000000000 -0400 @@ -50,6 +50,7 @@ #include #include #include +#include #define NFSDDBG_FACILITY NFSDDBG_PROC @@ -2650,6 +2651,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struc struct file_lock conflock; int status = 0; unsigned int strhashval; + unsigned int cmd; dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", (long long) lock->lk_offset, @@ -2730,10 +2732,12 @@ nfsd4_lock(struct svc_rqst *rqstp, struc case NFS4_READ_LT: case NFS4_READW_LT: file_lock.fl_type = F_RDLCK; + cmd = F_SETLK; break; case NFS4_WRITE_LT: case NFS4_WRITEW_LT: file_lock.fl_type = F_WRLCK; + cmd = F_SETLK; break; default: status = nfserr_inval; @@ -2760,10 +2764,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struc /* XXX?: Just to divert the locks_release_private at the start of * locks_copy_lock: */ - conflock.fl_ops = NULL; - conflock.fl_lmops = NULL; - status = posix_lock_file_conf(filp, &file_lock, &conflock); - dprintk("NFSD: nfsd4_lock: posix_lock_file_conf status %d\n",status); + locks_init_lock(&conflock); + status = vfs_lock_file(filp, cmd, &file_lock, &conflock); switch (-status) { case 0: /* success! */ update_stateid(&lock_stp->st_stateid); @@ -2779,7 +2781,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struc status = nfserr_deadlock; break; default: - dprintk("NFSD: nfsd4_lock: posix_lock_file_conf() failed! status %d\n",status); + dprintk("NFSD: nfsd4_lock: vfs_lock_file() failed! status %d\n",status); status = nfserr_resource; break; } @@ -2803,7 +2805,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, stru struct inode *inode; struct file file; struct file_lock file_lock; - struct file_lock conflock; + int error; int status; if (nfs4_in_grace()) @@ -2859,18 +2861,23 @@ nfsd4_lockt(struct svc_rqst *rqstp, stru 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 - * 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. */ memset(&file, 0, sizeof (struct file)); file.f_dentry = current_fh->fh_dentry; 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; - nfs4_set_lock_denied(&conflock, &lockt->lt_denied); + nfs4_set_lock_denied(&file_lock, &lockt->lt_denied); } out: nfs4_unlock_state(); @@ -2921,9 +2928,9 @@ nfsd4_locku(struct svc_rqst *rqstp, stru /* * Try to unlock the file in the VFS. */ - status = posix_lock_file(filp, &file_lock); + status = vfs_lock_file(filp, F_SETLK, &file_lock, NULL); if (status) { - dprintk("NFSD: nfs4_locku: posix_lock_file failed!\n"); + dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n"); goto out_nfserr; } /* diff -uNrp linux-2.6.18.i386.orig/include/linux/fcntl.h linux-2.6.18.i386/include/linux/fcntl.h --- linux-2.6.18.i386.orig/include/linux/fcntl.h 2007-06-12 12:05:32.000000000 -0400 +++ linux-2.6.18.i386/include/linux/fcntl.h 2007-06-12 12:06:35.000000000 -0400 @@ -3,6 +3,10 @@ #include +/* 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_GETLEASE (F_LINUX_SPECIFIC_BASE+1) diff -uNrp linux-2.6.18.i386.orig/include/linux/fs.h linux-2.6.18.i386/include/linux/fs.h --- linux-2.6.18.i386.orig/include/linux/fs.h 2007-06-12 12:05:32.000000000 -0400 +++ linux-2.6.18.i386/include/linux/fs.h 2007-06-12 12:06:35.000000000 -0400 @@ -774,6 +774,9 @@ struct lock_manager_operations { void (*fl_break)(struct file_lock *); int (*fl_mylease)(struct file_lock *, struct file_lock *); int (*fl_change)(struct file_lock **, int); +#ifndef __GENKSYMS__ + int (*fl_grant)(struct file_lock *, struct file_lock *, int); +#endif }; /* that will die - we need it for nfs_lock_info */ @@ -840,6 +843,9 @@ extern int posix_lock_file_conf(struct f extern int posix_lock_file(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 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 __break_lease(struct inode *inode, unsigned int flags); extern void lease_get_mtime(struct inode *, struct timespec *time); diff -uNrp linux-2.6.18.i386.orig/include/linux/lockd/bind.h linux-2.6.18.i386/include/linux/lockd/bind.h --- linux-2.6.18.i386.orig/include/linux/lockd/bind.h 2007-06-12 12:05:32.000000000 -0400 +++ linux-2.6.18.i386/include/linux/lockd/bind.h 2007-06-12 12:06:35.000000000 -0400 @@ -10,6 +10,11 @@ #define LINUX_LOCKD_BIND_H #include +/* need xdr-encoded error codes too, so... */ +#include +#ifdef CONFIG_LOCKD_V4 +#include +#endif /* Dummy declarations */ struct svc_rqst; diff -uNrp linux-2.6.18.i386.orig/include/linux/lockd/lockd.h linux-2.6.18.i386/include/linux/lockd/lockd.h --- linux-2.6.18.i386.orig/include/linux/lockd/lockd.h 2007-06-12 12:05:32.000000000 -0400 +++ linux-2.6.18.i386/include/linux/lockd/lockd.h 2007-06-12 12:06:35.000000000 -0400 @@ -112,6 +112,9 @@ struct nlm_file { * couldn't be granted because of a conflicting lock). */ #define NLM_NEVER (~(unsigned long) 0) +/* timeout on non-blocking call: */ +#define NLM_TIMEOUT (7 * HZ) + struct nlm_block { struct kref b_count; /* Reference count */ struct nlm_block * b_next; /* linked list (all blocks) */ @@ -124,6 +127,13 @@ struct nlm_block { unsigned char b_queued; /* re-queued */ unsigned char b_granted; /* VFS granted lock */ 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_TOO_LATE 4 /* too late for non-blocking lock */ }; /* @@ -178,8 +188,8 @@ extern struct nlm_host *nlm_find_client( u32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, struct nlm_lock *, int, struct nlm_cookie *); u32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); -u32 nlmsvc_testlock(struct nlm_file *, struct nlm_lock *, - struct nlm_lock *); +u32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *, + struct nlm_lock *, struct nlm_lock *, struct nlm_cookie *); u32 nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *); unsigned long nlmsvc_retry_blocked(void); void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, diff -uNrp linux-2.6.18.i386.orig/include/linux/lockd/xdr.h linux-2.6.18.i386/include/linux/lockd/xdr.h --- linux-2.6.18.i386.orig/include/linux/lockd/xdr.h 2007-06-12 12:05:32.000000000 -0400 +++ linux-2.6.18.i386/include/linux/lockd/xdr.h 2007-06-12 12:06:35.000000000 -0400 @@ -22,6 +22,8 @@ #define nlm_lck_blocked __constant_htonl(NLM_LCK_BLOCKED) #define nlm_lck_denied_grace_period __constant_htonl(NLM_LCK_DENIED_GRACE_PERIOD) +#define nlm_drop_reply __constant_htonl(30000) + /* Lock info passed via NLM */ struct nlm_lock { char * caller; diff -uNrp linux-2.6.18.i386.orig/include/linux/sunrpc/msg_prot.h linux-2.6.18.i386/include/linux/sunrpc/msg_prot.h --- linux-2.6.18.i386.orig/include/linux/sunrpc/msg_prot.h 2007-06-12 12:05:32.000000000 -0400 +++ linux-2.6.18.i386/include/linux/sunrpc/msg_prot.h 2007-06-12 12:06:35.000000000 -0400 @@ -53,7 +53,9 @@ enum rpc_accept_stat { RPC_PROG_MISMATCH = 2, RPC_PROC_UNAVAIL = 3, RPC_GARBAGE_ARGS = 4, - RPC_SYSTEM_ERR = 5 + RPC_SYSTEM_ERR = 5, + /* internal use only */ + RPC_DROP_REPLY = 60000 }; enum rpc_reject_stat { diff -uNrp linux-2.6.18.i386.orig/include/linux/sunrpc/xdr.h linux-2.6.18.i386/include/linux/sunrpc/xdr.h --- linux-2.6.18.i386.orig/include/linux/sunrpc/xdr.h 2007-06-12 12:05:32.000000000 -0400 +++ linux-2.6.18.i386/include/linux/sunrpc/xdr.h 2007-06-12 12:06:35.000000000 -0400 @@ -74,6 +74,7 @@ struct xdr_buf { #define rpc_proc_unavail __constant_htonl(RPC_PROC_UNAVAIL) #define rpc_garbage_args __constant_htonl(RPC_GARBAGE_ARGS) #define rpc_system_err __constant_htonl(RPC_SYSTEM_ERR) +#define rpc_drop_reply __constant_htonl(RPC_DROP_REPLY) #define rpc_auth_ok __constant_htonl(RPC_AUTH_OK) #define rpc_autherr_badcred __constant_htonl(RPC_AUTH_BADCRED) diff -uNrp linux-2.6.18.i386.orig/net/sunrpc/svc.c linux-2.6.18.i386/net/sunrpc/svc.c --- linux-2.6.18.i386.orig/net/sunrpc/svc.c 2007-06-12 12:05:43.000000000 -0400 +++ linux-2.6.18.i386/net/sunrpc/svc.c 2007-06-12 12:06:35.000000000 -0400 @@ -389,6 +389,11 @@ svc_process(struct svc_serv *serv, struc *statp = procp->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); /* Encode reply */ + if (*statp == rpc_drop_reply) { + if (procp->pc_release) + procp->pc_release(rqstp, NULL, rqstp->rq_resp); + goto dropit; + } if (*statp == rpc_success && (xdr = procp->pc_encode) && !xdr(rqstp, resv->iov_base+resv->iov_len, rqstp->rq_resp)) { dprintk("svc: failed to encode reply\n"); diff -uNrp linux-2.6.18.locks-e-mailed/fs/gfs2/locking/dlm/plock.c linux-2.6.18.locks6/fs/gfs2/locking/dlm/plock.c --- linux-2.6.18.locks-e-mailed/fs/gfs2/locking/dlm/plock.c 2007-05-29 14:28:02.000000000 -0400 +++ linux-2.6.18.locks6/fs/gfs2/locking/dlm/plock.c 2007-05-29 13:17:14.000000000 -0400 @@ -29,6 +29,7 @@ struct plock_xop { void *callback; void *fl; void *file; + struct file_lock flc; }; @@ -90,7 +91,8 @@ int gdlm_plock(void *lockspace, struct l op->info.owner = (__u64)(long) fl->fl_owner; if (fl->fl_lmops && fl->fl_lmops->fl_grant) { xop->callback = fl->fl_lmops->fl_grant; - /* might need to make a copy */ + locks_init_lock(&xop->flc); + locks_copy_lock(&xop->flc, fl); xop->fl = fl; xop->file = file; } else @@ -128,6 +130,7 @@ static int gdlm_plock_callback(struct pl { 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; @@ -141,16 +144,18 @@ static int gdlm_plock_callback(struct pl /* 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(fl, NULL, op->info.rv); + notify(flc, NULL, op->info.rv); goto out; } /* got fs lock; bookkeep locally as well: */ - if (posix_lock_file(file, fl)) { + flc->fl_flags &= ~FL_SLEEP; + if (posix_lock_file(file, flc)) { /* * This can only happen in the case of kmalloc() failure. * The filesystem's own lock is the authoritative lock, @@ -163,7 +168,7 @@ static int gdlm_plock_callback(struct pl file, fl); } - rv = notify(fl, NULL, 0); + rv = notify(flc, NULL, 0); if (rv) { /* XXX: We need to cancel the fs lock here: */ printk("gfs2 lock granted after lock request failed;" diff -uNrp linux-2.6.18.locks-e-mailed/fs/lockd/svclock.c linux-2.6.18.locks6/fs/lockd/svclock.c --- linux-2.6.18.locks-e-mailed/fs/lockd/svclock.c 2007-05-29 14:28:02.000000000 -0400 +++ linux-2.6.18.locks6/fs/lockd/svclock.c 2007-05-29 13:17:14.000000000 -0400 @@ -606,6 +606,7 @@ nlmsvc_cancel_blocked(struct nlm_file *f block = nlmsvc_lookup_block(file, lock); up(&file->f_sema); if (block != NULL) { + vfs_cancel_lock(block->b_file->f_file, &block->b_call->a_args.lock.fl); status = nlmsvc_unlink_block(block); nlmsvc_release_block(block); } diff -uNrp linux-2.6.18.locks-e-mailed/fs/locks.c linux-2.6.18.locks6/fs/locks.c --- linux-2.6.18.locks-e-mailed/fs/locks.c 2007-05-29 14:28:02.000000000 -0400 +++ linux-2.6.18.locks6/fs/locks.c 2007-05-29 13:17:14.000000000 -0400 @@ -671,7 +671,6 @@ posix_test_lock(struct file *filp, struc { struct file_lock *cfl; - fl->fl_type = F_UNLCK; lock_kernel(); for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) { if (!IS_POSIX(cfl)) @@ -686,7 +685,8 @@ posix_test_lock(struct file *filp, struc __locks_copy_lock(fl, cfl); unlock_kernel(); return 1; - } + } else + fl->fl_type = F_UNLCK; unlock_kernel(); return 0; } @@ -1651,6 +1651,7 @@ static int posix_lock_to_flock(struct fl 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; return 0; } @@ -2069,8 +2070,7 @@ int vfs_cancel_lock(struct file *filp, s { if (filp->f_op && filp->f_op->lock) return filp->f_op->lock(filp, F_CANCELLK, fl); - else - return posix_unblock_lock(filp, fl); + return 0; } EXPORT_SYMBOL_GPL(vfs_cancel_lock); diff -uNpr linux-2.6.18.i386.orig/fs/gfs2/locking/dlm/plock.c linux-2.6.18.i386/fs/gfs2/locking/dlm/plock.c --- linux-2.6.18.i386.orig/fs/gfs2/locking/dlm/plock.c 2007-06-12 15:00:54.000000000 -0400 +++ linux-2.6.18.i386/fs/gfs2/locking/dlm/plock.c 2007-06-12 15:01:12.000000000 -0400 @@ -89,7 +89,7 @@ int gdlm_plock(void *lockspace, struct l op->info.start = fl->fl_start; op->info.end = fl->fl_end; op->info.owner = (__u64)(long) fl->fl_owner; - if (fl->fl_lmops && fl->fl_lmops->fl_grant) { + if ((fl->fl_flags & FL_GRANT) && 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); diff -uNpr linux-2.6.18.i386.orig/fs/lockd/svc4proc.c linux-2.6.18.i386/fs/lockd/svc4proc.c --- linux-2.6.18.i386.orig/fs/lockd/svc4proc.c 2007-06-12 15:00:52.000000000 -0400 +++ linux-2.6.18.i386/fs/lockd/svc4proc.c 2007-06-12 15:01:12.000000000 -0400 @@ -53,6 +53,8 @@ nlm4svc_retrieve_args(struct svc_rqst *r lock->fl.fl_file = file->f_file; lock->fl.fl_owner = (fl_owner_t) host; lock->fl.fl_lmops = &nlmsvc_lock_operations; + /* kABI toggle to let the caller know that f_lmops has the grant callback. */ + lock->fl.fl_flags |= FL_GRANT; } return 0; diff -uNpr linux-2.6.18.i386.orig/fs/lockd/svclock.c linux-2.6.18.i386/fs/lockd/svclock.c --- linux-2.6.18.i386.orig/fs/lockd/svclock.c 2007-06-12 15:00:54.000000000 -0400 +++ linux-2.6.18.i386/fs/lockd/svclock.c 2007-06-12 15:01:12.000000000 -0400 @@ -196,8 +196,9 @@ nlmsvc_create_block(struct svc_rqst *rqs if (!nlmsvc_setgrantargs(call, lock)) goto failed_free; - /* Set notifier function for VFS, and init args */ - call->a_args.lock.fl.fl_flags |= FL_SLEEP; + /* Set notifier function for VFS, kABI toggle to let the caller + know that f_lmops has the grant callback, and init args */ + call->a_args.lock.fl.fl_flags |= FL_SLEEP | FL_GRANT; call->a_args.lock.fl.fl_lmops = &nlmsvc_lock_operations; call->a_args.cookie = *cookie; /* see above */ diff -uNpr linux-2.6.18.i386.orig/fs/lockd/svcproc.c linux-2.6.18.i386/fs/lockd/svcproc.c --- linux-2.6.18.i386.orig/fs/lockd/svcproc.c 2007-06-12 15:00:52.000000000 -0400 +++ linux-2.6.18.i386/fs/lockd/svcproc.c 2007-06-12 15:01:12.000000000 -0400 @@ -82,6 +82,8 @@ nlmsvc_retrieve_args(struct svc_rqst *rq lock->fl.fl_file = file->f_file; lock->fl.fl_owner = (fl_owner_t) host; lock->fl.fl_lmops = &nlmsvc_lock_operations; + /* kABI toggle to let the caller know that f_lmops has the grant callback. */ + lock->fl.fl_flags |= FL_GRANT; } return 0; diff -uNpr linux-2.6.18.i386.orig/fs/locks.c linux-2.6.18.i386/fs/locks.c --- linux-2.6.18.i386.orig/fs/locks.c 2007-06-12 15:00:54.000000000 -0400 +++ linux-2.6.18.i386/fs/locks.c 2007-06-12 15:03:08.000000000 -0400 @@ -161,6 +161,8 @@ static void locks_release_private(struct if (fl->fl_lmops->fl_release_private) fl->fl_lmops->fl_release_private(fl); fl->fl_lmops = NULL; + fl->fl_flags &= ~FL_GRANT; /* Clear kABI toggle which lets the caller + know if fl_lmops has the grant callback.*/ } } @@ -221,6 +223,8 @@ static void locks_copy_private(struct fi if (fl->fl_lmops->fl_copy_lock) fl->fl_lmops->fl_copy_lock(new, fl); new->fl_lmops = fl->fl_lmops; + new->fl_flags |= (fl->fl_flags & FL_GRANT); /* Toggle kABI switch to let the caller + know that fl_lmops has the grant callback.*/ } } @@ -232,7 +236,8 @@ static void __locks_copy_lock(struct fil new->fl_owner = fl->fl_owner; new->fl_pid = fl->fl_pid; new->fl_file = NULL; - new->fl_flags = fl->fl_flags; + new->fl_flags = fl->fl_flags & ~FL_GRANT; /* Clear the kABI toggle switch since + the fl_lmops is cleared. */ new->fl_type = fl->fl_type; new->fl_start = fl->fl_start; new->fl_end = fl->fl_end; diff -uNpr linux-2.6.18.i386.orig/include/linux/fs.h linux-2.6.18.i386/include/linux/fs.h --- linux-2.6.18.i386.orig/include/linux/fs.h 2007-06-12 15:00:52.000000000 -0400 +++ linux-2.6.18.i386/include/linux/fs.h 2007-06-12 15:01:12.000000000 -0400 @@ -744,6 +744,8 @@ extern spinlock_t files_lock; #define FL_POSIX 1 #define FL_FLOCK 2 +#define FL_GRANT 4 /* kABI toggle to let the caller know + that fl_lmops has the grant callback */ #define FL_ACCESS 8 /* not trying to lock, just looking */ #define FL_EXISTS 16 /* when unlocking, test for existence */ #define FL_LEASE 32 /* lease held on this file */