* [PATCH v2 0/2] lockd/locks: address issues with vfs_test_lock()
@ 2025-11-22 1:00 NeilBrown
2025-11-22 1:00 ` [PATCH v2 1/2] lockd: fix vfs_test_lock() calls NeilBrown
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: NeilBrown @ 2025-11-22 1:00 UTC (permalink / raw)
To: Chuck Lever, Jeff Layton
Cc: Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs
v2 contains fixes to problem found by Chuck (thanks) and also improves
the documentation for vfs_test_lock().
Documentation says that it returns 0 or -ERRNO, but one caller check for
a FILE_LOCK_DEFERRED which is neither of those. So I address that in
the second patch.
Thanks,
NeilBrown
[PATCH v2 1/2] lockd: fix vfs_test_lock() calls
[PATCH v2 2/2] locks: ensure vfs_test_lock() never returns
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 1/2] lockd: fix vfs_test_lock() calls
2025-11-22 1:00 [PATCH v2 0/2] lockd/locks: address issues with vfs_test_lock() NeilBrown
@ 2025-11-22 1:00 ` NeilBrown
2025-11-22 16:10 ` Chuck Lever
2025-11-22 1:00 ` [PATCH v2 2/2] locks: ensure vfs_test_lock() never returns FILE_LOCK_DEFERRED NeilBrown
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: NeilBrown @ 2025-11-22 1:00 UTC (permalink / raw)
To: Chuck Lever, Jeff Layton
Cc: Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs
From: NeilBrown <neil@brown.name>
Usage of vfs_test_lock() is somewhat confused. Documentation suggests
it is given a "lock" but this is not the case. It is given a struct
file_lock which contains some details of the sort of lock it should be
looking for.
In particular passing a "file_lock" containing fl_lmops or fl_ops is
meaningless and possibly confusing.
This is particularly problematic in lockd. nlmsvc_testlock() receives
an initialised "file_lock" from xdr-decode, including manager ops and an
owner. It then mistakenly passes this to vfs_test_lock() which might
replace the owner and the ops. This can lead to confusion when freeing
the lock.
The primary role of the 'struct file_lock' passed to vfs_test_lock() is
to report a conflicting lock that was found, so it makes more sense for
nlmsvc_testlock() to pass "conflock", which it uses for returning the
conflicting lock.
With this change, freeing of the lock is not confused and code in
__nlm4svc_proc_test() and __nlmsvc_proc_test() can be simplified.
Documentation for vfs_test_lock() is improved to reflect its real
purpose, and a WARN_ON_ONCE() is added to avoid a similar problem in the
future.
Reported-by: Olga Kornievskaia <okorniev@redhat.com>
Closes: https://lore.kernel.org/all/20251021130506.45065-1-okorniev@redhat.com
Signed-off-by: NeilBrown <neil@brown.name>
---
changes since v1:
- use conflock more consistently in nlmsvc_testlock()
---
fs/lockd/svc4proc.c | 4 +---
fs/lockd/svclock.c | 21 ++++++++++++---------
fs/lockd/svcproc.c | 5 +----
fs/locks.c | 12 ++++++++++--
4 files changed, 24 insertions(+), 18 deletions(-)
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index 109e5caae8c7..4b6f18d97734 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -97,7 +97,6 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
struct nlm_args *argp = rqstp->rq_argp;
struct nlm_host *host;
struct nlm_file *file;
- struct nlm_lockowner *test_owner;
__be32 rc = rpc_success;
dprintk("lockd: TEST4 called\n");
@@ -107,7 +106,6 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
- test_owner = argp->lock.fl.c.flc_owner;
/* Now check for conflicting locks */
resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock,
&resp->lock);
@@ -116,7 +114,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
else
dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));
- nlmsvc_put_lockowner(test_owner);
+ nlmsvc_release_lockowner(&argp->lock);
nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index a31dc9588eb8..d66e82851599 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -627,7 +627,13 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
}
mode = lock_to_openmode(&lock->fl);
- error = vfs_test_lock(file->f_file[mode], &lock->fl);
+ locks_init_lock(&conflock->fl);
+ /* vfs_test_lock only uses start, end, and owner, but tests flc_file */
+ conflock->fl.c.flc_file = lock->fl.c.flc_file;
+ conflock->fl.fl_start = lock->fl.fl_start;
+ conflock->fl.fl_end = lock->fl.fl_end;
+ conflock->fl.c.flc_owner = lock->fl.c.flc_owner;
+ error = vfs_test_lock(file->f_file[mode], &conflock->fl);
if (error) {
/* We can't currently deal with deferred test requests */
if (error == FILE_LOCK_DEFERRED)
@@ -637,22 +643,19 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
goto out;
}
- if (lock->fl.c.flc_type == F_UNLCK) {
+ if (conflock->fl.c.flc_type == F_UNLCK) {
ret = nlm_granted;
goto out;
}
dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
- lock->fl.c.flc_type, (long long)lock->fl.fl_start,
- (long long)lock->fl.fl_end);
+ conflock->fl.c.flc_type, (long long)conflock->fl.fl_start,
+ (long long)conflock->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.c.flc_pid;
- conflock->fl.c.flc_type = lock->fl.c.flc_type;
- conflock->fl.fl_start = lock->fl.fl_start;
- conflock->fl.fl_end = lock->fl.fl_end;
- locks_release_private(&lock->fl);
+ conflock->svid = conflock->fl.c.flc_pid;
+ locks_release_private(&conflock->fl);
ret = nlm_lck_denied;
out:
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index f53d5177f267..5817ef272332 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -117,7 +117,6 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
struct nlm_args *argp = rqstp->rq_argp;
struct nlm_host *host;
struct nlm_file *file;
- struct nlm_lockowner *test_owner;
__be32 rc = rpc_success;
dprintk("lockd: TEST called\n");
@@ -127,8 +126,6 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
- test_owner = argp->lock.fl.c.flc_owner;
-
/* Now check for conflicting locks */
resp->status = cast_status(nlmsvc_testlock(rqstp, file, host,
&argp->lock, &resp->lock));
@@ -138,7 +135,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
dprintk("lockd: TEST status %d vers %d\n",
ntohl(resp->status), rqstp->rq_vers);
- nlmsvc_put_lockowner(test_owner);
+ nlmsvc_release_lockowner(&argp->lock);
nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
diff --git a/fs/locks.c b/fs/locks.c
index 04a3f0e20724..bf5e0d05a026 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -2185,13 +2185,21 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
/**
* vfs_test_lock - test file byte range lock
* @filp: The file to test lock for
- * @fl: The lock to test; also used to hold result
+ * @fl: The byte-range in the file to test; also used to hold result
*
+ * On entry, @fl does not contain a lock, but identifies a range (fl_start, fl_end)
+ * in the file (c.flc_file), and an owner (c.flc_owner) for whom existing locks
+ * should be ignored. c.flc_type and c.flc_flags are ignored.
+ * Both fl_lmops and fl_ops in @fl must be NULL.
* Returns -ERRNO on failure. Indicates presence of conflicting lock by
- * setting conf->fl_type to something other than F_UNLCK.
+ * setting fl->fl_type to something other than F_UNLCK.
+ *
+ * If vfs_test_lock() does find a lock and return it, the caller must
+ * use locks_free_lock() or locks_release_private() on the returned lock.
*/
int vfs_test_lock(struct file *filp, struct file_lock *fl)
{
+ WARN_ON_ONCE(fl->fl_ops || fl->fl_lmops);
WARN_ON_ONCE(filp != fl->c.flc_file);
if (filp->f_op->lock)
return filp->f_op->lock(filp, F_GETLK, fl);
--
2.50.0.107.gf914562f5916.dirty
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 2/2] locks: ensure vfs_test_lock() never returns FILE_LOCK_DEFERRED
2025-11-22 1:00 [PATCH v2 0/2] lockd/locks: address issues with vfs_test_lock() NeilBrown
2025-11-22 1:00 ` [PATCH v2 1/2] lockd: fix vfs_test_lock() calls NeilBrown
@ 2025-11-22 1:00 ` NeilBrown
2025-11-22 16:29 ` [PATCH v2 0/2] lockd/locks: address issues with vfs_test_lock() Chuck Lever
2025-11-23 12:16 ` Jeff Layton
3 siblings, 0 replies; 8+ messages in thread
From: NeilBrown @ 2025-11-22 1:00 UTC (permalink / raw)
To: Chuck Lever, Jeff Layton
Cc: Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs
From: NeilBrown <neil@brown.name>
FILE_LOCK_DEFERRED can be returned when creating or removing a lock, but
not when testing for a lock. This support was explicitly removed in
Commit 09802fd2a8ca ("lockd: rip out deferred lock handling from testlock codepath")
However the test in nlmsvc_testlock() suggests that it *can* be returned,
only nlm cannot handle it.
To aid clarity, remove the test and instead put a similar test and
warning in vfs_test_lock(). If the impossible happens, convert
FILE_LOCK_DEFERRED to -EIO.
Signed-off-by: NeilBrown <neil@brown.name>
---
fs/lockd/svclock.c | 4 ----
fs/locks.c | 17 ++++++++++++++---
2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index d66e82851599..dfd1a12b7887 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -635,10 +635,6 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
conflock->fl.c.flc_owner = lock->fl.c.flc_owner;
error = vfs_test_lock(file->f_file[mode], &conflock->fl);
if (error) {
- /* We can't currently deal with deferred test requests */
- if (error == FILE_LOCK_DEFERRED)
- WARN_ON_ONCE(1);
-
ret = nlm_lck_denied_nolocks;
goto out;
}
diff --git a/fs/locks.c b/fs/locks.c
index bf5e0d05a026..b5a3bb8790f3 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -2199,12 +2199,23 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
*/
int vfs_test_lock(struct file *filp, struct file_lock *fl)
{
+ int error = 0;
+
WARN_ON_ONCE(fl->fl_ops || fl->fl_lmops);
WARN_ON_ONCE(filp != fl->c.flc_file);
if (filp->f_op->lock)
- return filp->f_op->lock(filp, F_GETLK, fl);
- posix_test_lock(filp, fl);
- return 0;
+ error = filp->f_op->lock(filp, F_GETLK, fl);
+ else
+ posix_test_lock(filp, fl);
+
+ /*
+ * We don't expect FILE_LOCK_DEFERRED and callers cannot
+ * handle it.
+ */
+ if (WARN_ON_ONCE(error == FILE_LOCK_DEFERRED))
+ error = -EIO;
+
+ return error;
}
EXPORT_SYMBOL_GPL(vfs_test_lock);
--
2.50.0.107.gf914562f5916.dirty
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2 1/2] lockd: fix vfs_test_lock() calls
2025-11-22 1:00 ` [PATCH v2 1/2] lockd: fix vfs_test_lock() calls NeilBrown
@ 2025-11-22 16:10 ` Chuck Lever
2025-11-22 20:48 ` NeilBrown
0 siblings, 1 reply; 8+ messages in thread
From: Chuck Lever @ 2025-11-22 16:10 UTC (permalink / raw)
To: NeilBrown, Chuck Lever, Jeff Layton
Cc: Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs
On Fri, Nov 21, 2025, at 8:00 PM, NeilBrown wrote:
> From: NeilBrown <neil@brown.name>
>
> Usage of vfs_test_lock() is somewhat confused. Documentation suggests
> it is given a "lock" but this is not the case. It is given a struct
> file_lock which contains some details of the sort of lock it should be
> looking for.
>
> In particular passing a "file_lock" containing fl_lmops or fl_ops is
> meaningless and possibly confusing.
>
> This is particularly problematic in lockd. nlmsvc_testlock() receives
> an initialised "file_lock" from xdr-decode, including manager ops and an
> owner. It then mistakenly passes this to vfs_test_lock() which might
> replace the owner and the ops. This can lead to confusion when freeing
> the lock.
>
> The primary role of the 'struct file_lock' passed to vfs_test_lock() is
> to report a conflicting lock that was found, so it makes more sense for
> nlmsvc_testlock() to pass "conflock", which it uses for returning the
> conflicting lock.
>
> With this change, freeing of the lock is not confused and code in
> __nlm4svc_proc_test() and __nlmsvc_proc_test() can be simplified.
>
> Documentation for vfs_test_lock() is improved to reflect its real
> purpose, and a WARN_ON_ONCE() is added to avoid a similar problem in the
> future.
>
> Reported-by: Olga Kornievskaia <okorniev@redhat.com>
> Closes: https://lore.kernel.org/all/20251021130506.45065-1-okorniev@redhat.com
> Signed-off-by: NeilBrown <neil@brown.name>
At a guess:
Fixes: 5ea0d75037b9 ("lockd: handle test_lock deferrals") ??
I suspect this also needs a Cc: stable.
> ---
> changes since v1:
> - use conflock more consistently in nlmsvc_testlock()
> ---
> fs/lockd/svc4proc.c | 4 +---
> fs/lockd/svclock.c | 21 ++++++++++++---------
> fs/lockd/svcproc.c | 5 +----
> fs/locks.c | 12 ++++++++++--
> 4 files changed, 24 insertions(+), 18 deletions(-)
>
> diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
> index 109e5caae8c7..4b6f18d97734 100644
> --- a/fs/lockd/svc4proc.c
> +++ b/fs/lockd/svc4proc.c
> @@ -97,7 +97,6 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct
> nlm_res *resp)
> struct nlm_args *argp = rqstp->rq_argp;
> struct nlm_host *host;
> struct nlm_file *file;
> - struct nlm_lockowner *test_owner;
> __be32 rc = rpc_success;
>
> dprintk("lockd: TEST4 called\n");
> @@ -107,7 +106,6 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct
> nlm_res *resp)
> if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
> return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
>
> - test_owner = argp->lock.fl.c.flc_owner;
> /* Now check for conflicting locks */
> resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock,
> &resp->lock);
> @@ -116,7 +114,7 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct
> nlm_res *resp)
> else
> dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));
>
> - nlmsvc_put_lockowner(test_owner);
> + nlmsvc_release_lockowner(&argp->lock);
> nlmsvc_release_host(host);
> nlm_release_file(file);
> return rc;
> diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
> index a31dc9588eb8..d66e82851599 100644
> --- a/fs/lockd/svclock.c
> +++ b/fs/lockd/svclock.c
> @@ -627,7 +627,13 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct
> nlm_file *file,
> }
>
> mode = lock_to_openmode(&lock->fl);
> - error = vfs_test_lock(file->f_file[mode], &lock->fl);
> + locks_init_lock(&conflock->fl);
> + /* vfs_test_lock only uses start, end, and owner, but tests flc_file
> */
> + conflock->fl.c.flc_file = lock->fl.c.flc_file;
> + conflock->fl.fl_start = lock->fl.fl_start;
> + conflock->fl.fl_end = lock->fl.fl_end;
> + conflock->fl.c.flc_owner = lock->fl.c.flc_owner;
> + error = vfs_test_lock(file->f_file[mode], &conflock->fl);
> if (error) {
> /* We can't currently deal with deferred test requests */
> if (error == FILE_LOCK_DEFERRED)
> @@ -637,22 +643,19 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct
> nlm_file *file,
> goto out;
> }
>
> - if (lock->fl.c.flc_type == F_UNLCK) {
> + if (conflock->fl.c.flc_type == F_UNLCK) {
> ret = nlm_granted;
> goto out;
> }
>
> dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
> - lock->fl.c.flc_type, (long long)lock->fl.fl_start,
> - (long long)lock->fl.fl_end);
> + conflock->fl.c.flc_type, (long long)conflock->fl.fl_start,
> + (long long)conflock->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.c.flc_pid;
> - conflock->fl.c.flc_type = lock->fl.c.flc_type;
> - conflock->fl.fl_start = lock->fl.fl_start;
> - conflock->fl.fl_end = lock->fl.fl_end;
> - locks_release_private(&lock->fl);
> + conflock->svid = conflock->fl.c.flc_pid;
> + locks_release_private(&conflock->fl);
>
> ret = nlm_lck_denied;
> out:
> diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
> index f53d5177f267..5817ef272332 100644
> --- a/fs/lockd/svcproc.c
> +++ b/fs/lockd/svcproc.c
> @@ -117,7 +117,6 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct
> nlm_res *resp)
> struct nlm_args *argp = rqstp->rq_argp;
> struct nlm_host *host;
> struct nlm_file *file;
> - struct nlm_lockowner *test_owner;
> __be32 rc = rpc_success;
>
> dprintk("lockd: TEST called\n");
> @@ -127,8 +126,6 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct
> nlm_res *resp)
> if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
> return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
>
> - test_owner = argp->lock.fl.c.flc_owner;
> -
> /* Now check for conflicting locks */
> resp->status = cast_status(nlmsvc_testlock(rqstp, file, host,
> &argp->lock, &resp->lock));
> @@ -138,7 +135,7 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct
> nlm_res *resp)
> dprintk("lockd: TEST status %d vers %d\n",
> ntohl(resp->status), rqstp->rq_vers);
>
> - nlmsvc_put_lockowner(test_owner);
> + nlmsvc_release_lockowner(&argp->lock);
> nlmsvc_release_host(host);
> nlm_release_file(file);
> return rc;
> diff --git a/fs/locks.c b/fs/locks.c
> index 04a3f0e20724..bf5e0d05a026 100644
> --- a/fs/locks.c
> +++ b/fs/locks.c
> @@ -2185,13 +2185,21 @@ SYSCALL_DEFINE2(flock, unsigned int, fd,
> unsigned int, cmd)
> /**
> * vfs_test_lock - test file byte range lock
> * @filp: The file to test lock for
> - * @fl: The lock to test; also used to hold result
> + * @fl: The byte-range in the file to test; also used to hold result
> *
> + * On entry, @fl does not contain a lock, but identifies a range
> (fl_start, fl_end)
> + * in the file (c.flc_file), and an owner (c.flc_owner) for whom
> existing locks
> + * should be ignored. c.flc_type and c.flc_flags are ignored.
> + * Both fl_lmops and fl_ops in @fl must be NULL.
> * Returns -ERRNO on failure. Indicates presence of conflicting lock
> by
> - * setting conf->fl_type to something other than F_UNLCK.
> + * setting fl->fl_type to something other than F_UNLCK.
> + *
> + * If vfs_test_lock() does find a lock and return it, the caller must
> + * use locks_free_lock() or locks_release_private() on the returned
> lock.
> */
> int vfs_test_lock(struct file *filp, struct file_lock *fl)
> {
> + WARN_ON_ONCE(fl->fl_ops || fl->fl_lmops);
> WARN_ON_ONCE(filp != fl->c.flc_file);
> if (filp->f_op->lock)
> return filp->f_op->lock(filp, F_GETLK, fl);
> --
> 2.50.0.107.gf914562f5916.dirty
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 0/2] lockd/locks: address issues with vfs_test_lock()
2025-11-22 1:00 [PATCH v2 0/2] lockd/locks: address issues with vfs_test_lock() NeilBrown
2025-11-22 1:00 ` [PATCH v2 1/2] lockd: fix vfs_test_lock() calls NeilBrown
2025-11-22 1:00 ` [PATCH v2 2/2] locks: ensure vfs_test_lock() never returns FILE_LOCK_DEFERRED NeilBrown
@ 2025-11-22 16:29 ` Chuck Lever
2025-11-23 12:16 ` Jeff Layton
3 siblings, 0 replies; 8+ messages in thread
From: Chuck Lever @ 2025-11-22 16:29 UTC (permalink / raw)
To: Jeff Layton, NeilBrown
Cc: Chuck Lever, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs
From: Chuck Lever <chuck.lever@oracle.com>
On Sat, 22 Nov 2025 12:00:35 +1100, NeilBrown wrote:
> v2 contains fixes to problem found by Chuck (thanks) and also improves
> the documentation for vfs_test_lock().
>
> Documentation says that it returns 0 or -ERRNO, but one caller check for
> a FILE_LOCK_DEFERRED which is neither of those. So I address that in
> the second patch.
>
> [...]
Applied to nfsd-testing, thanks!
[1/2] lockd: fix vfs_test_lock() calls
commit: bc99611cfa1b1cf67a17718feca65e43eeed3118
[2/2] locks: ensure vfs_test_lock() never returns FILE_LOCK_DEFERRED
commit: 569ad3b4cf6ae8d71bf73ad13079e7b88d26b339
--
Chuck Lever
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 1/2] lockd: fix vfs_test_lock() calls
2025-11-22 16:10 ` Chuck Lever
@ 2025-11-22 20:48 ` NeilBrown
2025-11-23 12:15 ` Jeff Layton
0 siblings, 1 reply; 8+ messages in thread
From: NeilBrown @ 2025-11-22 20:48 UTC (permalink / raw)
To: Chuck Lever
Cc: Chuck Lever, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey,
linux-nfs
On Sun, 23 Nov 2025, Chuck Lever wrote:
> On Fri, Nov 21, 2025, at 8:00 PM, NeilBrown wrote:
> > From: NeilBrown <neil@brown.name>
> >
> > Usage of vfs_test_lock() is somewhat confused. Documentation suggests
> > it is given a "lock" but this is not the case. It is given a struct
> > file_lock which contains some details of the sort of lock it should be
> > looking for.
> >
> > In particular passing a "file_lock" containing fl_lmops or fl_ops is
> > meaningless and possibly confusing.
> >
> > This is particularly problematic in lockd. nlmsvc_testlock() receives
> > an initialised "file_lock" from xdr-decode, including manager ops and an
> > owner. It then mistakenly passes this to vfs_test_lock() which might
> > replace the owner and the ops. This can lead to confusion when freeing
> > the lock.
> >
> > The primary role of the 'struct file_lock' passed to vfs_test_lock() is
> > to report a conflicting lock that was found, so it makes more sense for
> > nlmsvc_testlock() to pass "conflock", which it uses for returning the
> > conflicting lock.
> >
> > With this change, freeing of the lock is not confused and code in
> > __nlm4svc_proc_test() and __nlmsvc_proc_test() can be simplified.
> >
> > Documentation for vfs_test_lock() is improved to reflect its real
> > purpose, and a WARN_ON_ONCE() is added to avoid a similar problem in the
> > future.
> >
> > Reported-by: Olga Kornievskaia <okorniev@redhat.com>
> > Closes: https://lore.kernel.org/all/20251021130506.45065-1-okorniev@redhat.com
> > Signed-off-by: NeilBrown <neil@brown.name>
>
> At a guess:
>
> Fixes: 5ea0d75037b9 ("lockd: handle test_lock deferrals") ??
I think the problem is, in practice, more recent.
I think there is only a problem if the filesystem that lockd is
accessing sets fl_lmops on locks.
So maybe
Fixes: 20fa19027286 ("nfs: add export operations")
>
> I suspect this also needs a Cc: stable.
I think that would be appropriate - yes.
NeilBrown
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 1/2] lockd: fix vfs_test_lock() calls
2025-11-22 20:48 ` NeilBrown
@ 2025-11-23 12:15 ` Jeff Layton
0 siblings, 0 replies; 8+ messages in thread
From: Jeff Layton @ 2025-11-23 12:15 UTC (permalink / raw)
To: NeilBrown, Chuck Lever
Cc: Chuck Lever, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs
On Sun, 2025-11-23 at 07:48 +1100, NeilBrown wrote:
> On Sun, 23 Nov 2025, Chuck Lever wrote:
> > On Fri, Nov 21, 2025, at 8:00 PM, NeilBrown wrote:
> > > From: NeilBrown <neil@brown.name>
> > >
> > > Usage of vfs_test_lock() is somewhat confused. Documentation suggests
> > > it is given a "lock" but this is not the case. It is given a struct
> > > file_lock which contains some details of the sort of lock it should be
> > > looking for.
> > >
> > > In particular passing a "file_lock" containing fl_lmops or fl_ops is
> > > meaningless and possibly confusing.
> > >
> > > This is particularly problematic in lockd. nlmsvc_testlock() receives
> > > an initialised "file_lock" from xdr-decode, including manager ops and an
> > > owner. It then mistakenly passes this to vfs_test_lock() which might
> > > replace the owner and the ops. This can lead to confusion when freeing
> > > the lock.
> > >
> > > The primary role of the 'struct file_lock' passed to vfs_test_lock() is
> > > to report a conflicting lock that was found, so it makes more sense for
> > > nlmsvc_testlock() to pass "conflock", which it uses for returning the
> > > conflicting lock.
> > >
> > > With this change, freeing of the lock is not confused and code in
> > > __nlm4svc_proc_test() and __nlmsvc_proc_test() can be simplified.
> > >
> > > Documentation for vfs_test_lock() is improved to reflect its real
> > > purpose, and a WARN_ON_ONCE() is added to avoid a similar problem in the
> > > future.
> > >
> > > Reported-by: Olga Kornievskaia <okorniev@redhat.com>
> > > Closes: https://lore.kernel.org/all/20251021130506.45065-1-okorniev@redhat.com
> > > Signed-off-by: NeilBrown <neil@brown.name>
> >
> > At a guess:
> >
> > Fixes: 5ea0d75037b9 ("lockd: handle test_lock deferrals") ??
>
> I think the problem is, in practice, more recent.
> I think there is only a problem if the filesystem that lockd is
> accessing sets fl_lmops on locks.
>
...and specifically if the filesystem sets lm_get_owner/lm_put_owner.
That's only NFS/NLM, currently.
> So maybe
>
> Fixes: 20fa19027286 ("nfs: add export operations")
>
That's likely the place where it became a real problem, yes.
> >
> > I suspect this also needs a Cc: stable.
>
> I think that would be appropriate - yes.
>
> NeilBrown
Ditto.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 0/2] lockd/locks: address issues with vfs_test_lock()
2025-11-22 1:00 [PATCH v2 0/2] lockd/locks: address issues with vfs_test_lock() NeilBrown
` (2 preceding siblings ...)
2025-11-22 16:29 ` [PATCH v2 0/2] lockd/locks: address issues with vfs_test_lock() Chuck Lever
@ 2025-11-23 12:16 ` Jeff Layton
3 siblings, 0 replies; 8+ messages in thread
From: Jeff Layton @ 2025-11-23 12:16 UTC (permalink / raw)
To: NeilBrown, Chuck Lever; +Cc: Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs
On Sat, 2025-11-22 at 12:00 +1100, NeilBrown wrote:
> v2 contains fixes to problem found by Chuck (thanks) and also improves
> the documentation for vfs_test_lock().
>
> Documentation says that it returns 0 or -ERRNO, but one caller check for
> a FILE_LOCK_DEFERRED which is neither of those. So I address that in
> the second patch.
>
> Thanks,
> NeilBrown
>
>
> [PATCH v2 1/2] lockd: fix vfs_test_lock() calls
> [PATCH v2 2/2] locks: ensure vfs_test_lock() never returns
Nice cleanup.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-11-23 12:16 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-22 1:00 [PATCH v2 0/2] lockd/locks: address issues with vfs_test_lock() NeilBrown
2025-11-22 1:00 ` [PATCH v2 1/2] lockd: fix vfs_test_lock() calls NeilBrown
2025-11-22 16:10 ` Chuck Lever
2025-11-22 20:48 ` NeilBrown
2025-11-23 12:15 ` Jeff Layton
2025-11-22 1:00 ` [PATCH v2 2/2] locks: ensure vfs_test_lock() never returns FILE_LOCK_DEFERRED NeilBrown
2025-11-22 16:29 ` [PATCH v2 0/2] lockd/locks: address issues with vfs_test_lock() Chuck Lever
2025-11-23 12:16 ` Jeff Layton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox