From 226a998dbf3c6f9b85f67d08a52c5a2143ed9d88 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 14 Feb 2007 14:25:00 -0500 Subject: locks: trivial removal of unnecessary parentheses Remove some unnecessary parentheses. Signed-off-by: "J. Bruce Fields" --- fs/locks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 52a81005dab4..1a00b8bc65ed 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1738,7 +1738,7 @@ again: else { for (;;) { error = posix_lock_file(filp, file_lock); - if ((error != -EAGAIN) || (cmd == F_SETLK)) + if (error != -EAGAIN || cmd == F_SETLK) break; error = wait_event_interruptible(file_lock->fl_wait, !file_lock->fl_next); @@ -1881,7 +1881,7 @@ again: else { for (;;) { error = posix_lock_file(filp, file_lock); - if ((error != -EAGAIN) || (cmd == F_SETLK64)) + if (error != -EAGAIN || cmd == F_SETLK64) break; error = wait_event_interruptible(file_lock->fl_wait, !file_lock->fl_next); -- cgit v1.2.3-58-ga151 From c2fa1b8a6c059dd08a802545fed3badc8df2adc1 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 20 Feb 2007 16:10:11 -0500 Subject: locks: create posix-to-flock helper functions Factor out a bit of messy code by creating posix-to-flock counterparts to the existing flock-to-posix helper functions. Cc: Christoph Hellwig Signed-off-by: "J. Bruce Fields" --- fs/locks.c | 61 ++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 1a00b8bc65ed..957775ba6468 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1611,6 +1611,38 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) return error; } +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(). */ @@ -1645,24 +1677,9 @@ int fcntl_getlk(struct file *filp, struct flock __user *l) 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)) + error = posix_lock_to_flock(&flock, fl); + 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))) @@ -1798,14 +1815,8 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l) } 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; - } + if (fl != NULL) + posix_lock_to_flock64(&flock, fl); error = -EFAULT; if (!copy_to_user(l, &flock, sizeof(flock))) error = 0; -- cgit v1.2.3-58-ga151 From 70cc6487a4e08b8698c0e2ec935fb48d10490162 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 22 Feb 2007 18:48:53 -0500 Subject: locks: make ->lock release private data before returning in GETLK case The file_lock argument to ->lock is used to return the conflicting lock when found. There's no reason for the filesystem to return any private information with this conflicting lock, but nfsv4 is. Fix nfsv4 client, and modify locks.c to stop calling fl_release_private for it in this case. Signed-off-by: "J. Bruce Fields" Cc: "Trond Myklebust" " --- fs/locks.c | 4 ---- fs/nfs/nfs4proc.c | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 957775ba6468..b07e6e6f819b 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1665,8 +1665,6 @@ int fcntl_getlk(struct file *filp, struct flock __user *l) 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 @@ -1804,8 +1802,6 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l) 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 diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f52cf5c33c6c..d9000ec52f72 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3018,6 +3018,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock case -NFS4ERR_DENIED: status = 0; } + request->fl_ops->fl_release_private(request); out: up_read(&clp->cl_sem); return status; -- cgit v1.2.3-58-ga151 From 9d6a8c5c213e34c475e72b245a8eb709258e968c Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Wed, 21 Feb 2007 00:55:18 -0500 Subject: locks: give posix_test_lock same interface as ->lock posix_test_lock() and ->lock() do the same job but have gratuitously different interfaces. Modify posix_test_lock() so the two agree, simplifying some code in the process. Signed-off-by: Marc Eshel Signed-off-by: "J. Bruce Fields" --- fs/fuse/file.c | 3 +-- fs/gfs2/locking/nolock/main.c | 8 +------- fs/gfs2/ops_file.c | 7 +------ fs/lockd/svclock.c | 13 ++++++++----- fs/locks.c | 37 ++++++++++++++++--------------------- fs/nfs/file.c | 7 +------ fs/nfsd/nfs4state.c | 6 +++--- include/linux/fs.h | 2 +- 8 files changed, 32 insertions(+), 51 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 2fd06927e851..acfad65a6e8e 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -738,8 +738,7 @@ static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl) 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); err = 0; } else err = fuse_getlk(file, fl); diff --git a/fs/gfs2/locking/nolock/main.c b/fs/gfs2/locking/nolock/main.c index acfbc941f319..5cc1dfa7944a 100644 --- a/fs/gfs2/locking/nolock/main.c +++ b/fs/gfs2/locking/nolock/main.c @@ -164,13 +164,7 @@ static void nolock_unhold_lvb(void *lock, char *lvb) 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); return 0; } diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index b50180e22779..48b248d7c823 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c @@ -513,12 +513,7 @@ static int gfs2_lock(struct file *file, int cmd, struct file_lock *fl) 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); return 0; } else { return posix_lock_file_wait(file, fl); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index cf51f849e76c..97b0160ef10f 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -426,15 +426,18 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); - if (posix_test_lock(file->f_file, &lock->fl, &conflock->fl)) { + if (posix_test_lock(file->f_file, &lock->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); + 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 = conflock->fl.fl_pid; + 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; return nlm_lck_denied; } diff --git a/fs/locks.c b/fs/locks.c index b07e6e6f819b..749a0dc7cd4b 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -666,11 +666,11 @@ static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *w } int -posix_test_lock(struct file *filp, struct file_lock *fl, - struct file_lock *conflock) +posix_test_lock(struct file *filp, struct file_lock *fl) { struct file_lock *cfl; + fl->fl_type = F_UNLCK; lock_kernel(); for (cfl = filp->f_path.dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) { if (!IS_POSIX(cfl)) @@ -679,7 +679,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl, break; } if (cfl) { - __locks_copy_lock(conflock, cfl); + __locks_copy_lock(fl, cfl); unlock_kernel(); return 1; } @@ -1648,7 +1648,7 @@ static void posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl) */ 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; @@ -1667,15 +1667,12 @@ int fcntl_getlk(struct file *filp, struct flock __user *l) error = filp->f_op->lock(filp, F_GETLK, &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); - } + } else + posix_test_lock(filp, &file_lock); - flock.l_type = F_UNLCK; - if (fl != NULL) { - error = posix_lock_to_flock(&flock, fl); + 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; } @@ -1785,7 +1782,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; @@ -1804,15 +1801,13 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l) error = filp->f_op->lock(filp, F_GETLK, &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); - } + } else + posix_test_lock(filp, &file_lock); - flock.l_type = F_UNLCK; - if (fl != NULL) - posix_lock_to_flock64(&flock, fl); + 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; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 8e66b5a2d490..5eaee6dd040b 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -391,17 +391,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)) { goto out; } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index af360705e551..e42c7a0eb6fa 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2813,7 +2813,6 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct inode *inode; struct file file; struct file_lock file_lock; - struct file_lock conflock; __be32 status; if (nfs4_in_grace()) @@ -2878,9 +2877,10 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, file.f_path.dentry = cstate->current_fh.fh_dentry; status = nfs_ok; - if (posix_test_lock(&file, &file_lock, &conflock)) { + posix_test_lock(&file, &file_lock); + 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(); diff --git a/include/linux/fs.h b/include/linux/fs.h index 86ec3f4a7da6..9e1ddffe3884 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -851,7 +851,7 @@ extern void locks_init_lock(struct file_lock *); extern void locks_copy_lock(struct file_lock *, struct file_lock *); extern void locks_remove_posix(struct file *, fl_owner_t); extern void locks_remove_flock(struct file *); -extern int posix_test_lock(struct file *, struct file_lock *, struct file_lock *); +extern int posix_test_lock(struct file *, struct file_lock *); extern int posix_lock_file_conf(struct file *, struct file_lock *, struct file_lock *); extern int posix_lock_file(struct file *, struct file_lock *); extern int posix_lock_file_wait(struct file *, struct file_lock *); -- cgit v1.2.3-58-ga151 From 3ee17abd14c728d4e0ca7a991c58f2250cb091af Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 21 Feb 2007 00:58:50 -0500 Subject: locks: factor out generic/filesystem switch from test_lock Factor out the code that switches between generic and filesystem-specific lock methods; eventually we want to call this from lock managers (lockd and nfsd) too; currently they only call the generic methods. This patch does that for test_lock. Note that this hasn't been necessary until recently, because the few filesystems that define ->lock() (nfs, cifs...) aren't exportable via NFS. However GFS (and, in the future, other cluster filesystems) need to implement their own locking to get cluster-coherent locking, and also want to be able to export locking to NFS (lockd and NFSv4). So we accomplish this by factoring out code such as this and exporting it for the use of lockd and nfsd. Signed-off-by: "J. Bruce Fields" --- fs/locks.c | 38 +++++++++++++++++++++++++------------- include/linux/fs.h | 1 + 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 749a0dc7cd4b..a31648e3ec1b 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1611,6 +1611,24 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) return error; } +/** + * vfs_test_lock - test file byte range lock + * @filp: The file to test lock for + * @fl: The lock to test + * @conf: Place to return a copy of the conflicting lock, if found + * + * Returns -ERRNO on failure. Indicates presence of conflicting lock by + * setting conf->fl_type to something other than F_UNLCK. + */ +int vfs_test_lock(struct file *filp, struct file_lock *fl) +{ + if (filp->f_op && filp->f_op->lock) + return filp->f_op->lock(filp, F_GETLK, fl); + posix_test_lock(filp, fl); + return 0; +} +EXPORT_SYMBOL_GPL(vfs_test_lock); + static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl) { flock->l_pid = fl->fl_pid; @@ -1663,12 +1681,9 @@ int fcntl_getlk(struct file *filp, struct flock __user *l) if (error) goto out; - if (filp->f_op && filp->f_op->lock) { - error = filp->f_op->lock(filp, F_GETLK, &file_lock); - if (error < 0) - goto out; - } else - posix_test_lock(filp, &file_lock); + 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) { @@ -1797,13 +1812,10 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l) if (error) goto out; - if (filp->f_op && filp->f_op->lock) { - error = filp->f_op->lock(filp, F_GETLK, &file_lock); - if (error < 0) - goto out; - } else - posix_test_lock(filp, &file_lock); - + 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); diff --git a/include/linux/fs.h b/include/linux/fs.h index 9e1ddffe3884..2a2a43988f50 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -856,6 +856,7 @@ extern int posix_lock_file_conf(struct file *, struct file_lock *, struct file_l 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 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); -- cgit v1.2.3-58-ga151 From 7723ec9777d9832849b76475b1a21a2872a40d20 Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Thu, 18 Jan 2007 15:08:55 -0500 Subject: locks: factor out generic/filesystem switch from setlock code Factor out the code that switches between generic and filesystem-specific lock methods; eventually we want to call this from lock managers (lockd and nfsd) too; currently they only call the generic methods. This patch does that for all the setlk code. Signed-off-by: Marc Eshel Signed-off-by: "J. Bruce Fields" --- fs/locks.c | 68 +++++++++++++++++++++++++++++------------------------- include/linux/fs.h | 1 + 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index a31648e3ec1b..f4fd1515b6e2 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1698,6 +1698,21 @@ 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 + */ +int vfs_lock_file(struct file *filp, unsigned int cmd, struct file_lock *fl) +{ + if (filp->f_op && filp->f_op->lock) + return filp->f_op->lock(filp, cmd, fl); + else + return posix_lock_file(filp, fl); +} +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(). */ @@ -1760,21 +1775,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); + 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; } /* @@ -1890,21 +1901,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_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); + 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; } /* @@ -1949,10 +1956,7 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner) 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); if (lock.fl_ops && lock.fl_ops->fl_release_private) lock.fl_ops->fl_release_private(&lock); diff --git a/include/linux/fs.h b/include/linux/fs.h index 2a2a43988f50..1d5ccdd7c68d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -857,6 +857,7 @@ 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 *); 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); -- cgit v1.2.3-58-ga151 From 150b393456e5a23513cace286a019e87151e47f0 Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Thu, 18 Jan 2007 16:15:35 -0500 Subject: locks: allow {vfs,posix}_lock_file to return conflicting lock The nfsv4 protocol's lock operation, in the case of a conflict, returns information about the conflicting lock. It's unclear how clients can use this, so for now we're not going so far as to add a filesystem method that can return a conflicting lock, but we may as well return something in the local case when it's easy to. Signed-off-by: Marc Eshel Signed-off-by: "J. Bruce Fields" --- fs/lockd/svclock.c | 6 +++--- fs/lockd/svcsubs.c | 2 +- fs/locks.c | 45 ++++++++++++++++++++------------------------- fs/nfsd/nfs4state.c | 10 ++++++---- include/linux/fs.h | 5 ++--- 5 files changed, 32 insertions(+), 36 deletions(-) diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 97b0160ef10f..3b0e7a4b817b 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -363,7 +363,7 @@ again: } else lock = &block->b_call->a_args.lock; - error = posix_lock_file(file->f_file, &lock->fl); + error = posix_lock_file(file->f_file, &lock->fl, NULL); lock->fl.fl_flags &= ~FL_SLEEP; dprintk("lockd: posix_lock_file returned %d\n", error); @@ -467,7 +467,7 @@ nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock) nlmsvc_cancel_blocked(file, lock); lock->fl.fl_type = F_UNLCK; - error = posix_lock_file(file->f_file, &lock->fl); + error = posix_lock_file(file->f_file, &lock->fl, NULL); return (error < 0)? nlm_lck_denied_nolocks : nlm_granted; } @@ -569,7 +569,7 @@ nlmsvc_grant_blocked(struct nlm_block *block) /* Try the lock operation again */ lock->fl.fl_flags |= FL_SLEEP; - error = posix_lock_file(file->f_file, &lock->fl); + error = posix_lock_file(file->f_file, &lock->fl, NULL); lock->fl.fl_flags &= ~FL_SLEEP; switch (error) { diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index c0df00c74ce3..50957089be1f 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -182,7 +182,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 (posix_lock_file(file->f_file, &lock, NULL) < 0) { printk("lockd: unlock failure in %s:%d\n", __FILE__, __LINE__); return 1; diff --git a/fs/locks.c b/fs/locks.c index f4fd1515b6e2..ee46584c1a40 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -801,7 +801,7 @@ out: return error; } -static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request, struct file_lock *conflock) +static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock) { struct file_lock *fl; struct file_lock *new_fl = NULL; @@ -1007,6 +1007,7 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request * posix_lock_file - Apply a POSIX-style lock to a file * @filp: The file to apply the lock to * @fl: The lock to be applied + * @conflock: Place to return a copy of the conflicting lock, if found. * * Add a POSIX style lock to a file. * We merge adjacent & overlapping locks whenever possible. @@ -1016,26 +1017,12 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request * whether or not a lock was successfully freed by testing the return * value for -ENOENT. */ -int posix_lock_file(struct file *filp, struct file_lock *fl) -{ - return __posix_lock_file_conf(filp->f_path.dentry->d_inode, fl, NULL); -} -EXPORT_SYMBOL(posix_lock_file); - -/** - * posix_lock_file_conf - Apply a POSIX-style lock to a file - * @filp: The file to apply the lock to - * @fl: The lock to be applied - * @conflock: Place to return a copy of the conflicting lock, if found. - * - * Except for the conflock parameter, acts just like posix_lock_file. - */ -int posix_lock_file_conf(struct file *filp, struct file_lock *fl, +int posix_lock_file(struct file *filp, struct file_lock *fl, struct file_lock *conflock) { - return __posix_lock_file_conf(filp->f_path.dentry->d_inode, fl, conflock); + return __posix_lock_file(filp->f_path.dentry->d_inode, fl, conflock); } -EXPORT_SYMBOL(posix_lock_file_conf); +EXPORT_SYMBOL(posix_lock_file); /** * posix_lock_file_wait - Apply a POSIX-style lock to a file @@ -1051,7 +1038,7 @@ int posix_lock_file_wait(struct file *filp, struct file_lock *fl) int error; might_sleep (); for (;;) { - error = posix_lock_file(filp, fl); + error = posix_lock_file(filp, fl, NULL); if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP)) break; error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); @@ -1123,7 +1110,7 @@ int locks_mandatory_area(int read_write, struct inode *inode, fl.fl_end = offset + count - 1; for (;;) { - error = __posix_lock_file_conf(inode, &fl, NULL); + error = __posix_lock_file(inode, &fl, NULL); if (error != -EAGAIN) break; if (!(fl.fl_flags & FL_SLEEP)) @@ -1703,13 +1690,21 @@ out: * @filp: The file to apply the lock to * @cmd: type of locking operation (F_SETLK, F_GETLK, etc.) * @fl: The lock to be applied + * @conf: Place to return a copy of the conflicting lock, if found. + * + * A caller that doesn't care about the conflicting lock may pass NULL + * as the final argument. + * + * If the filesystem defines a private ->lock() method, then @conf will + * be left unchanged; so a caller that cares should initialize it to + * some acceptable default. */ -int vfs_lock_file(struct file *filp, unsigned int cmd, struct file_lock *fl) +int vfs_lock_file(struct file *filp, unsigned int cmd, struct file_lock *fl, struct file_lock *conf) { if (filp->f_op && filp->f_op->lock) return filp->f_op->lock(filp, cmd, fl); else - return posix_lock_file(filp, fl); + return posix_lock_file(filp, fl, conf); } EXPORT_SYMBOL_GPL(vfs_lock_file); @@ -1776,7 +1771,7 @@ again: goto out; for (;;) { - error = vfs_lock_file(filp, cmd, file_lock); + error = vfs_lock_file(filp, cmd, file_lock, NULL); if (error != -EAGAIN || cmd == F_SETLK) break; error = wait_event_interruptible(file_lock->fl_wait, @@ -1902,7 +1897,7 @@ again: goto out; for (;;) { - error = vfs_lock_file(filp, cmd, file_lock); + error = vfs_lock_file(filp, cmd, file_lock, NULL); if (error != -EAGAIN || cmd == F_SETLK64) break; error = wait_event_interruptible(file_lock->fl_wait, @@ -1956,7 +1951,7 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner) lock.fl_ops = NULL; lock.fl_lmops = NULL; - vfs_lock_file(filp, F_SETLK, &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); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e42c7a0eb6fa..03b0578781cb 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2657,6 +2657,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct file_lock conflock; __be32 status = 0; unsigned int strhashval; + unsigned int cmd; int err; dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", @@ -2739,10 +2740,12 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 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; @@ -2769,9 +2772,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* XXX?: Just to divert the locks_release_private at the start of * locks_copy_lock: */ - conflock.fl_ops = NULL; - conflock.fl_lmops = NULL; - err = posix_lock_file_conf(filp, &file_lock, &conflock); + locks_init_lock(&conflock); + err = posix_lock_file(filp, &file_lock, &conflock); switch (-err) { case 0: /* success! */ update_stateid(&lock_stp->st_stateid); @@ -2933,7 +2935,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* * Try to unlock the file in the VFS. */ - err = posix_lock_file(filp, &file_lock); + err = posix_lock_file(filp, &file_lock, NULL); if (err) { dprintk("NFSD: nfs4_locku: posix_lock_file failed!\n"); goto out_nfserr; diff --git a/include/linux/fs.h b/include/linux/fs.h index 1d5ccdd7c68d..c92d0bdff39f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -852,12 +852,11 @@ extern void locks_copy_lock(struct file_lock *, struct file_lock *); extern void locks_remove_posix(struct file *, fl_owner_t); extern void locks_remove_flock(struct file *); extern int posix_test_lock(struct file *, struct file_lock *); -extern int posix_lock_file_conf(struct file *, struct file_lock *, struct file_lock *); -extern int posix_lock_file(struct file *, struct file_lock *); +extern int posix_lock_file(struct file *, struct file_lock *, 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 *); +extern int vfs_lock_file(struct file *, unsigned int, struct file_lock *, struct file_lock *); 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); -- cgit v1.2.3-58-ga151 From 9b9d2ab4154a42ea4a119f7d3e4e0288bfe0bb79 Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Thu, 18 Jan 2007 17:52:58 -0500 Subject: locks: add lock cancel command Lock managers need to be able to cancel pending lock requests. In the case where the exported filesystem manages its own locks, it's not sufficient just to call posix_unblock_lock(); we need to let the filesystem know what's happening too. We do this by adding a new fcntl lock command: FL_CANCELLK. Some day this might also be made available to userspace applications that could benefit from an asynchronous locking api. Signed-off-by: Marc Eshel Signed-off-by: "J. Bruce Fields" --- fs/locks.c | 16 ++++++++++++++++ include/linux/fcntl.h | 4 ++++ include/linux/fs.h | 1 + 3 files changed, 21 insertions(+) diff --git a/fs/locks.c b/fs/locks.c index ee46584c1a40..242328e17f32 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -2028,6 +2028,22 @@ posix_unblock_lock(struct file *filp, struct file_lock *waiter) EXPORT_SYMBOL(posix_unblock_lock); +/** + * vfs_cancel_lock - file byte range unblock lock + * @filp: The file to apply the unblock to + * @fl: The lock to be unblocked + * + * Used by lock managers to cancel blocked requests + */ +int vfs_cancel_lock(struct file *filp, struct file_lock *fl) +{ + if (filp->f_op && filp->f_op->lock) + return filp->f_op->lock(filp, F_CANCELLK, fl); + return 0; +} + +EXPORT_SYMBOL_GPL(vfs_cancel_lock); + static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx) { struct inode *inode = NULL; diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index 996f5611cd59..40b93265d4ba 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -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 --git a/include/linux/fs.h b/include/linux/fs.h index c92d0bdff39f..64b8ae205309 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -857,6 +857,7 @@ 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); -- cgit v1.2.3-58-ga151 From fd85b8170dabbf021987875ef7f903791f4f181e Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Tue, 28 Nov 2006 16:26:41 -0500 Subject: nfsd4: Convert NFSv4 to new lock interface Convert NFSv4 to the new lock interface. We don't define any callback for now, so we're not taking advantage of the asynchronous feature--that's less critical for the multi-threaded nfsd then it is for the single-threaded lockd. But this does allow a cluster filesystems to export cluster-coherent locking to NFS. Note that it's cluster filesystems that are the issue--of the filesystems that define lock methods (nfs, cifs, etc.), most are not exportable by nfsd. Signed-off-by: Marc Eshel Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 03b0578781cb..678f3be88ac0 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -50,6 +50,7 @@ #include #include #include +#include #define NFSDDBG_FACILITY NFSDDBG_PROC @@ -2773,7 +2774,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* XXX?: Just to divert the locks_release_private at the start of * locks_copy_lock: */ locks_init_lock(&conflock); - err = posix_lock_file(filp, &file_lock, &conflock); + err = vfs_lock_file(filp, cmd, &file_lock, &conflock); switch (-err) { case 0: /* success! */ update_stateid(&lock_stp->st_stateid); @@ -2790,7 +2791,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfserr_deadlock; break; default: - dprintk("NFSD: nfsd4_lock: posix_lock_file_conf() failed! status %d\n",err); + dprintk("NFSD: nfsd4_lock: vfs_lock_file() failed! status %d\n",err); status = nfserr_resource; break; } @@ -2815,6 +2816,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct inode *inode; struct file file; struct file_lock file_lock; + int error; __be32 status; if (nfs4_in_grace()) @@ -2870,16 +2872,20 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 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_path.dentry = cstate->current_fh.fh_dentry; status = nfs_ok; - posix_test_lock(&file, &file_lock); + 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(&file_lock, &lockt->lt_denied); @@ -2935,9 +2941,9 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* * Try to unlock the file in the VFS. */ - err = posix_lock_file(filp, &file_lock, NULL); + err = vfs_lock_file(filp, F_SETLK, &file_lock, NULL); if (err) { - dprintk("NFSD: nfs4_locku: posix_lock_file failed!\n"); + dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n"); goto out_nfserr; } /* -- cgit v1.2.3-58-ga151 From 2beb6614f5e36c6165b704c167d82ef3e4ceaa0c Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Tue, 5 Dec 2006 23:31:28 -0500 Subject: locks: add fl_grant callback for asynchronous lock return Acquiring a lock on a cluster filesystem may require communication with remote hosts, and to avoid blocking lockd or nfsd threads during such communication, we allow the results to be returned asynchronously. When a ->lock() call needs to block, the file system will return -EINPROGRESS, and then later return the results with a call to the routine in the fl_grant field of the lock_manager_operations struct. This differs from the case when ->lock returns -EAGAIN to a blocking lock request; in that case, the filesystem calls fl_notify when the lock is granted, and the caller retries the original lock. So while fl_notify is merely a hint to the caller that it should retry, fl_grant actually communicates the final result of the lock operation (with the lock already acquired in the succesful case). Therefore fl_grant takes a lock, a status and, for the test lock case, a conflicting lock. We also allow fl_grant to return an error to the filesystem, to handle the case where the fl_grant requests arrives after the lock manager has already given up waiting for it. Signed-off-by: Marc Eshel Signed-off-by: J. Bruce Fields --- fs/locks.c | 19 +++++++++++++++++++ include/linux/fs.h | 1 + 2 files changed, 20 insertions(+) diff --git a/fs/locks.c b/fs/locks.c index 242328e17f32..53b0cd153202 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1698,6 +1698,25 @@ out: * If the filesystem defines a private ->lock() method, then @conf will * be left unchanged; so a caller that cares should initialize it to * some acceptable default. + * + * To avoid blocking kernel daemons, such as lockd, that need to acquire POSIX + * locks, the ->lock() interface may return asynchronously, before the lock has + * been granted or denied by the underlying filesystem, if (and only if) + * fl_grant is set. Callers expecting ->lock() to return asynchronously + * will only use F_SETLK, not F_SETLKW; they will set FL_SLEEP if (and only if) + * the request is for a blocking lock. When ->lock() does return asynchronously, + * it must return -EINPROGRESS, and call ->fl_grant() when the lock + * request completes. + * If the request is for non-blocking lock the file system should return + * -EINPROGRESS then try to get the lock and call the callback routine with + * the result. If the request timed out the callback routine will return a + * nonzero return code and the file system should release the lock. The file + * system is also responsible to keep a corresponding posix lock when it + * grants a lock so the VFS can find out which locks are locally held and do + * the correct lock cleanup when required. + * The underlying filesystem must not drop the kernel lock or call + * ->fl_grant() before returning to the caller with a -EINPROGRESS + * return code. */ int vfs_lock_file(struct file *filp, unsigned int cmd, struct file_lock *fl, struct file_lock *conf) { diff --git a/include/linux/fs.h b/include/linux/fs.h index 64b8ae205309..b22991d5f16b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -785,6 +785,7 @@ struct file_lock_operations { struct lock_manager_operations { int (*fl_compare_owner)(struct file_lock *, struct file_lock *); void (*fl_notify)(struct file_lock *); /* unblock callback */ + int (*fl_grant)(struct file_lock *, struct file_lock *, int); void (*fl_copy_lock)(struct file_lock *, struct file_lock *); void (*fl_release_private)(struct file_lock *); void (*fl_break)(struct file_lock *); -- cgit v1.2.3-58-ga151 From 2b36f412ab6f2e5b64af9832b20eb7ef67d025b4 Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Tue, 28 Nov 2006 16:26:47 -0500 Subject: lockd: save lock state on deferral We need to keep some state for a pending asynchronous lock request, so this patch adds that state to struct nlm_block. This also adds a function which defers the request, by calling rqstp->rq_chandle.defer and storing the resulting deferred request in a nlm_block structure which we insert into lockd's global block list. That new function isn't called yet, so it's dead code until a later patch. Signed-off-by: Marc Eshel Signed-off-by: J. Bruce Fields --- fs/lockd/svclock.c | 25 +++++++++++++++++++++++++ include/linux/lockd/lockd.h | 10 ++++++++++ 2 files changed, 35 insertions(+) diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 3b0e7a4b817b..f2449265e2eb 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -330,6 +330,31 @@ static void nlmsvc_freegrantargs(struct nlm_rqst *call) kfree(call->a_args.lock.oh.data); } +/* + * Deferred lock request handling for non-blocking lock + */ +static u32 +nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block) +{ + u32 status = nlm_lck_denied_nolocks; + + block->b_flags |= B_QUEUED; + + nlmsvc_insert_block(block, NLM_TIMEOUT); + + block->b_cache_req = &rqstp->rq_chandle; + if (rqstp->rq_chandle.defer) { + block->b_deferred_req = + rqstp->rq_chandle.defer(block->b_cache_req); + if (block->b_deferred_req != NULL) + status = nlm_drop_reply; + } + dprintk("lockd: nlmsvc_defer_lock_rqst block %p flags %d status %d\n", + block, block->b_flags, status); + + return status; +} + /* * Attempt to establish a lock, and if it can't be granted, block it * if required. diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index ac25b5649c59..d4c4de753bca 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -119,6 +119,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 list_head b_list; /* linked list of all blocks */ @@ -130,6 +133,13 @@ struct nlm_block { unsigned int b_id; /* block id */ 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_TIMED_OUT 4 /* filesystem too slow to respond */ }; /* -- cgit v1.2.3-58-ga151 From 0e4ac9d93515b27fd7635332d73eae3192ed5d4e Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Tue, 28 Nov 2006 16:26:51 -0500 Subject: lockd: handle fl_grant callbacks Add code to handle file system callback when the lock is finally granted. Signed-off-by: Marc Eshel Signed-off-by: J. Bruce Fields --- fs/lockd/svclock.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index f2449265e2eb..6e748573e0c6 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -261,6 +261,7 @@ static void nlmsvc_free_block(struct kref *kref) nlmsvc_freegrantargs(block->b_call); nlm_release_call(block->b_call); nlm_release_file(block->b_file); + kfree(block->b_fl); kfree(block); } @@ -527,6 +528,64 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) return status ? nlm_lck_denied : nlm_granted; } +/* + * This is a callback from the filesystem for VFS file lock requests. + * It will be used if fl_grant is defined and the filesystem can not + * respond to the request immediately. + * For GETLK request it will copy the reply to the nlm_block. + * For SETLK or SETLKW request it will get the local posix lock. + * In all cases it will move the block to the head of nlm_blocked q where + * nlmsvc_retry_blocked() can send back a reply for SETLKW or revisit the + * deferred rpc for GETLK and SETLK. + */ +static void +nlmsvc_update_deferred_block(struct nlm_block *block, struct file_lock *conf, + int result) +{ + block->b_flags |= B_GOT_CALLBACK; + if (result == 0) + block->b_granted = 1; + else + block->b_flags |= B_TIMED_OUT; + if (conf) { + block->b_fl = kzalloc(sizeof(struct file_lock), GFP_KERNEL); + if (block->b_fl) + locks_copy_lock(block->b_fl, conf); + } +} + +static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf, + int result) +{ + struct nlm_block *block; + int rc = -ENOENT; + + lock_kernel(); + list_for_each_entry(block, &nlm_blocked, b_list) { + if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) { + dprintk("lockd: nlmsvc_notify_blocked block %p flags %d\n", + block, block->b_flags); + if (block->b_flags & B_QUEUED) { + if (block->b_flags & B_TIMED_OUT) { + rc = -ENOLCK; + break; + } + nlmsvc_update_deferred_block(block, conf, result); + } else if (result == 0) + block->b_granted = 1; + + nlmsvc_insert_block(block, 0); + svc_wake_up(block->b_daemon); + rc = 0; + break; + } + } + unlock_kernel(); + if (rc == -ENOENT) + printk(KERN_WARNING "lockd: grant for unknown block\n"); + return rc; +} + /* * Unblock a blocked lock request. This is a callback invoked from the * VFS layer when a lock on which we blocked is removed. @@ -559,6 +618,7 @@ static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2) struct lock_manager_operations nlmsvc_lock_operations = { .fl_compare_owner = nlmsvc_same_owner, .fl_notify = nlmsvc_notify_blocked, + .fl_grant = nlmsvc_grant_deferred, }; /* @@ -581,6 +641,8 @@ nlmsvc_grant_blocked(struct nlm_block *block) dprintk("lockd: grant blocked lock %p\n", block); + kref_get(&block->b_count); + /* Unlink block request from list */ nlmsvc_unlink_block(block); @@ -603,11 +665,13 @@ nlmsvc_grant_blocked(struct nlm_block *block) case -EAGAIN: dprintk("lockd: lock still blocked\n"); 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; } @@ -620,7 +684,6 @@ callback: nlmsvc_insert_block(block, 30 * HZ); /* Call the client */ - kref_get(&block->b_count); nlm_async_call(block->b_call, NLMPROC_GRANTED_MSG, &nlmsvc_grant_ops); } @@ -693,6 +756,23 @@ nlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status) nlmsvc_release_block(block); } +/* Helper function to handle retry of a deferred block. + * If it is a blocking lock, call grant_blocked. + * For a non-blocking lock or test lock, revisit the request. + */ +static void +retry_deferred_block(struct nlm_block *block) +{ + if (!(block->b_flags & B_GOT_CALLBACK)) + block->b_flags |= B_TIMED_OUT; + nlmsvc_insert_block(block, NLM_TIMEOUT); + dprintk("revisit block %p flags %d\n", block, block->b_flags); + if (block->b_deferred_req) { + block->b_deferred_req->revisit(block->b_deferred_req, 0); + block->b_deferred_req = NULL; + } +} + /* * Retry all blocked locks that have been notified. This is where lockd * picks up locks that can be granted, or grant notifications that must @@ -716,9 +796,12 @@ nlmsvc_retry_blocked(void) 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); } return timeout; -- cgit v1.2.3-58-ga151 From 85f3f1b3f7a6197b51a2ab98d927517df730214c Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Tue, 28 Nov 2006 16:27:06 -0500 Subject: lockd: pass cookie in nlmsvc_testlock Change NLM internal interface to pass more information for test lock; we need this to make sure the cookie information is pushed down to the place where we do request deferral, which is handled for testlock by the following patch. Signed-off-by: Marc Eshel Signed-off-by: J. Bruce Fields --- fs/lockd/svc4proc.c | 2 +- fs/lockd/svclock.c | 5 +++-- fs/lockd/svcproc.c | 2 +- include/linux/lockd/lockd.h | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 47a66aa5d55b..a1bccad7ebd3 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -99,7 +99,7 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, 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); dprintk("lockd: TEST4 status %d\n", ntohl(resp->status)); nlm_release_host(host); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 6e748573e0c6..7ab2e60c51f7 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -442,8 +442,9 @@ out: * Test for presence of a conflicting lock. */ __be32 -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) { dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n", file->f_file->f_path.dentry->d_inode->i_sb->s_id, diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 31cb48425733..d13b827dfd34 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -127,7 +127,7 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, 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)); dprintk("lockd: TEST status %d vers %d\n", ntohl(resp->status), rqstp->rq_vers); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index d4c4de753bca..424c3de333d3 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -195,8 +195,8 @@ typedef int (*nlm_host_match_fn_t)(struct nlm_host *cur, struct nlm_host *ref) __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, struct nlm_lock *, int, struct nlm_cookie *); __be32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); -__be32 nlmsvc_testlock(struct nlm_file *, struct nlm_lock *, - struct nlm_lock *); +__be32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *, + struct nlm_lock *, struct nlm_lock *, struct nlm_cookie *); __be32 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 *, -- cgit v1.2.3-58-ga151 From 5ea0d75037b93baa453b4d326c6319968fe91cea Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Tue, 28 Nov 2006 16:27:06 -0500 Subject: lockd: handle test_lock deferrals Rewrite nlmsvc_testlock() to use the new asynchronous interface: instead of immediately doing a posix_test_lock(), we first look for a matching block. If the subsequent test_lock returns anything other than -EINPROGRESS, we then remove the block we've found and return the results. If it returns -EINPROGRESS, then we defer the lock request. In the case where the block we find in the first step has B_QUEUED set, we bypass the vfs_test_lock entirely, instead using the block to decide how to respond: with nlm_lck_denied if B_TIMED_OUT is set. with nlm_granted if B_GOT_CALLBACK is set. by dropping if neither B_TIMED_OUT nor B_GOT_CALLBACK is set Signed-off-by: Marc Eshel Signed-off-by: J. Bruce Fields --- fs/lockd/svc4proc.c | 2 ++ fs/lockd/svclock.c | 84 +++++++++++++++++++++++++++++++++++++++++++---------- fs/lockd/svcproc.c | 3 ++ 3 files changed, 73 insertions(+), 16 deletions(-) diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index a1bccad7ebd3..205d8befbf1b 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -100,6 +100,8 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, /* Now check for conflicting locks */ 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); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 7ab2e60c51f7..b7a8174fd1dc 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -173,7 +173,7 @@ found: */ 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) { struct nlm_block *block; struct nlm_host *host; @@ -210,6 +210,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, block->b_daemon = rqstp->rq_server; block->b_host = host; block->b_file = file; + block->b_fl = NULL; file->f_count++; /* Add to file's list of blocks */ @@ -446,6 +447,10 @@ 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_path.dentry->d_inode->i_sb->s_id, file->f_file->f_path.dentry->d_inode->i_ino, @@ -453,22 +458,70 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); - if (posix_test_lock(file->f_file, &lock->fl)) { - 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; - return nlm_lck_denied; + /* Get existing block (in case client is busy-waiting) */ + block = nlmsvc_lookup_block(file, lock); + + if (block == NULL) { + struct file_lock *conf = kzalloc(sizeof(*conf), GFP_KERNEL); + + if (conf == NULL) + return nlm_granted; + block = nlmsvc_create_block(rqstp, file, lock, cookie); + if (block == NULL) { + kfree(conf); + return nlm_granted; + } + block->b_fl = conf; + } + if (block->b_flags & B_QUEUED) { + dprintk("lockd: nlmsvc_testlock deferred block %p flags %d fl %p\n", + block, block->b_flags, block->b_fl); + if (block->b_flags & B_TIMED_OUT) { + nlmsvc_unlink_block(block); + return nlm_lck_denied; + } + if (block->b_flags & B_GOT_CALLBACK) { + if (block->b_fl != NULL + && block->b_fl->fl_type != F_UNLCK) { + lock->fl = *block->b_fl; + goto conf_lock; + } + else { + nlmsvc_unlink_block(block); + return nlm_granted; + } + } + return nlm_drop_reply; + } + + error = vfs_test_lock(file->f_file, &lock->fl); + if (error == -EINPROGRESS) + return nlmsvc_defer_lock_rqst(rqstp, block); + if (error) { + ret = nlm_lck_denied_nolocks; + goto out; + } + if (lock->fl.fl_type == F_UNLCK) { + ret = nlm_granted; + goto out; } - 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; } /* @@ -549,7 +602,6 @@ nlmsvc_update_deferred_block(struct nlm_block *block, struct file_lock *conf, else block->b_flags |= B_TIMED_OUT; if (conf) { - block->b_fl = kzalloc(sizeof(struct file_lock), GFP_KERNEL); if (block->b_fl) locks_copy_lock(block->b_fl, conf); } diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index d13b827dfd34..40b5cf496b51 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -33,6 +33,7 @@ cast_to_nlm(__be32 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; @@ -128,6 +129,8 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, /* Now check for conflicting locks */ 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); -- cgit v1.2.3-58-ga151 From f812048020282fdfa9b72a6cf539c33b6df1fd07 Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Tue, 5 Dec 2006 23:48:10 -0500 Subject: lockd: always preallocate block in nlmsvc_lock() Normally we could skip ever having to allocate a block in the case where the client asks for a non-blocking lock, or asks for a blocking lock that succeeds immediately. However we're going to want to always look up a block first in order to check whether we're revisiting a deferred lock call, and to be prepared to handle the case where the filesystem returns -EINPROGRESS--in that case we want to make sure the lock we've given the filesystem is the one embedded in the block that we'll use to track the deferred request. Signed-off-by: Marc Eshel Signed-off-by: J. Bruce Fields --- fs/lockd/svclock.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index b7a8174fd1dc..0d7398e3804f 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -365,7 +365,7 @@ __be32 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; __be32 ret; @@ -378,17 +378,20 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, wait); - lock->fl.fl_flags &= ~FL_SLEEP; -again: /* Lock file against concurrent access */ mutex_lock(&file->f_mutex); - /* Get existing block (in case client is busy-waiting) */ + /* Get existing block (in case client is busy-waiting) + * or create new block + */ block = nlmsvc_lookup_block(file, lock); if (block == NULL) { - if (newblock != NULL) - lock = &newblock->b_call->a_args.lock; - } else + block = nlmsvc_create_block(rqstp, file, lock, cookie); + 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, NULL); lock->fl.fl_flags &= ~FL_SLEEP; @@ -414,26 +417,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_mutex as nlmsvc_create_block may try to - * to claim it while doing host garbage collection */ - if (newblock == NULL) { - mutex_unlock(&file->f_mutex); - dprintk("lockd: blocking on this lock (allocating).\n"); - if (!(newblock = nlmsvc_create_block(rqstp, file, lock, cookie))) - return nlm_lck_denied_nolocks; - goto again; - } /* Append to list of blocked */ - nlmsvc_insert_block(newblock, NLM_NEVER); + nlmsvc_insert_block(block, NLM_NEVER); out: mutex_unlock(&file->f_mutex); - nlmsvc_release_block(newblock); nlmsvc_release_block(block); dprintk("lockd: nlmsvc_lock returned %u\n", ret); return ret; -- cgit v1.2.3-58-ga151 From 1a8322b2b02071b0c7ac37a28357b93e6362f13e Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Tue, 28 Nov 2006 16:27:06 -0500 Subject: lockd: add code to handle deferred lock requests Rewrite nlmsvc_lock() to use the asynchronous interface. As with testlock, we answer nlm requests in nlmsvc_lock by first looking up the block and then using the results we find in the block if B_QUEUED is set, and calling vfs_lock_file() otherwise. If this a new lock request and we get -EINPROGRESS return on a non-blocking request then we defer the request. Also modify nlmsvc_unlock() to call the filesystem method if appropriate. Signed-off-by: Marc Eshel Signed-off-by: J. Bruce Fields --- fs/lockd/svc4proc.c | 2 ++ fs/lockd/svclock.c | 41 +++++++++++++++++++++++++++++++++++------ fs/lockd/svcproc.c | 2 ++ fs/lockd/svcsubs.c | 2 +- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 205d8befbf1b..bf27b6c6cb6b 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -145,6 +145,8 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, /* 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); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 0d7398e3804f..b3efa4536cc5 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -393,17 +393,43 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, } else lock->fl.fl_flags &= ~FL_SLEEP; - error = posix_lock_file(file->f_file, &lock->fl, NULL); - 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_TIMED_OUT) { + 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; @@ -535,7 +561,7 @@ nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock) nlmsvc_cancel_blocked(file, lock); lock->fl.fl_type = F_UNLCK; - error = posix_lock_file(file->f_file, &lock->fl, NULL); + error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); return (error < 0)? nlm_lck_denied_nolocks : nlm_granted; } @@ -564,6 +590,8 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) block = nlmsvc_lookup_block(file, lock); mutex_unlock(&file->f_mutex); 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); } @@ -697,14 +725,15 @@ nlmsvc_grant_blocked(struct nlm_block *block) /* Try the lock operation again */ lock->fl.fl_flags |= FL_SLEEP; - error = posix_lock_file(file->f_file, &lock->fl, NULL); + 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; diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 40b5cf496b51..9cd5c8b37593 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -175,6 +175,8 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, /* 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); diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 50957089be1f..84ebba33b98d 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -182,7 +182,7 @@ again: lock.fl_type = F_UNLCK; lock.fl_start = 0; lock.fl_end = OFFSET_MAX; - if (posix_lock_file(file->f_file, &lock, NULL) < 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; -- cgit v1.2.3-58-ga151 From 586759f03e2e9031ac5589912a51a909ed53c30a Mon Sep 17 00:00:00 2001 From: Marc Eshel Date: Tue, 14 Nov 2006 16:37:25 -0500 Subject: gfs2: nfs lock support for gfs2 Add NFS lock support to GFS2. Signed-off-by: Marc Eshel Signed-off-by: J. Bruce Fields Acked-by: Steven Whitehouse --- fs/gfs2/locking/dlm/plock.c | 109 ++++++++++++++++++++++++++++++++++++++++---- fs/gfs2/ops_file.c | 5 ++ 2 files changed, 104 insertions(+), 10 deletions(-) diff --git a/fs/gfs2/locking/dlm/plock.c b/fs/gfs2/locking/dlm/plock.c index 1dd4215b83d0..f82495e18c2d 100644 --- a/fs/gfs2/locking/dlm/plock.c +++ b/fs/gfs2/locking/dlm/plock.c @@ -25,6 +25,15 @@ struct plock_op { struct gdlm_plock_info info; }; +struct plock_xop { + struct plock_op xop; + void *callback; + void *fl; + void *file; + struct file_lock flc; +}; + + static inline void set_version(struct gdlm_plock_info *info) { info->version[0] = GDLM_PLOCK_VERSION_MAJOR; @@ -64,12 +73,14 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name, { 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); @@ -79,9 +90,21 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name, 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; + locks_init_lock(&xop->flc); + locks_copy_lock(&xop->flc, fl); + 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)) { @@ -99,7 +122,63 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name, (unsigned long long)name->ln_number); } - kfree(op); + kfree(xop); + return rv; +} + +/* Returns failure iff a succesful lock operation should be canceled */ +static int gdlm_plock_callback(struct plock_op *op) +{ + struct file *file; + struct file_lock *fl; + struct file_lock *flc; + int (*notify)(void *, void *, int) = NULL; + struct plock_xop *xop = (struct plock_xop *)op; + int rv = 0; + + spin_lock(&ops_lock); + if (!list_empty(&op->list)) { + printk(KERN_INFO "plock op on list\n"); + list_del(&op->list); + } + spin_unlock(&ops_lock); + + /* check if the following 2 are still valid or make a copy */ + file = xop->file; + flc = &xop->flc; + fl = xop->fl; + notify = xop->callback; + + if (op->info.rv) { + notify(flc, NULL, op->info.rv); + goto out; + } + + /* got fs lock; bookkeep locally as well: */ + flc->fl_flags &= ~FL_SLEEP; + if (posix_lock_file(file, flc, NULL)) { + /* + * This can only happen in the case of kmalloc() failure. + * The filesystem's own lock is the authoritative lock, + * so a failure to get the lock locally is not a disaster. + * As long as GFS cannot reliably cancel locks (especially + * in a low-memory situation), we're better off ignoring + * this failure than trying to recover. + */ + log_error("gdlm_plock: vfs lock error file %p fl %p", + file, fl); + } + + rv = notify(flc, NULL, 0); + if (rv) { + /* XXX: We need to cancel the fs lock here: */ + printk("gfs2 lock granted after lock request failed;" + " dangling lock!\n"); + goto out; + } + +out: + kfree(xop); return rv; } @@ -138,6 +217,9 @@ int gdlm_punlock(void *lockspace, struct lm_lockname *name, rv = op->info.rv; + if (rv == -ENOENT) + rv = 0; + kfree(op); return rv; } @@ -161,6 +243,7 @@ int gdlm_plock_get(void *lockspace, struct lm_lockname *name, op->info.start = fl->fl_start; op->info.end = fl->fl_end; + send_op(op); wait_event(recv_wq, (op->done != 0)); @@ -173,9 +256,10 @@ int gdlm_plock_get(void *lockspace, struct lm_lockname *name, 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; @@ -243,9 +327,14 @@ static ssize_t dev_write(struct file *file, const char __user *u, size_t count, } 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 --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index 48b248d7c823..329c4dcdecdb 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c @@ -520,6 +520,11 @@ static int gfs2_lock(struct file *file, int cmd, struct file_lock *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) -- cgit v1.2.3-58-ga151