diff options
author | Alexander Aring <aahringo@redhat.com> | 2023-09-12 23:53:20 +0200 |
---|---|---|
committer | Chuck Lever <chuck.lever@oracle.com> | 2023-10-16 18:44:02 +0200 |
commit | afb13302aa664170684c76b0c12ece37b4e91d12 (patch) | |
tree | f36064e400683539fb27cfbf5a785630f18c467f | |
parent | lockd: don't call vfs_lock_file() for pending requests (diff) | |
download | linux-afb13302aa664170684c76b0c12ece37b4e91d12.tar.xz linux-afb13302aa664170684c76b0c12ece37b4e91d12.zip |
lockd: fix race in async lock request handling
This patch fixes a race in async lock request handling between adding
the relevant struct nlm_block to nlm_blocked list after the request was
sent by vfs_lock_file() and nlmsvc_grant_deferred() does a lookup of the
nlm_block in the nlm_blocked list. It could be that the async request is
completed before the nlm_block was added to the list. This would end
in a -ENOENT and a kernel log message of "lockd: grant for unknown
block".
To solve this issue we add the nlm_block before the vfs_lock_file() call
to be sure it has been added when a possible nlmsvc_grant_deferred() is
called. If the vfs_lock_file() results in an case when it wouldn't be
added to nlm_blocked list, the nlm_block struct will be removed from
this list again.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Alexander Aring <aahringo@redhat.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
-rw-r--r-- | fs/lockd/svclock.c | 11 |
1 files changed, 8 insertions, 3 deletions
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index c313622a9578..993999297e31 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -555,6 +555,9 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, ret = nlm_lck_blocked; goto out; } + + /* Append to list of blocked */ + nlmsvc_insert_block_locked(block, NLM_NEVER); spin_unlock(&nlm_blocked_lock); if (!wait) @@ -566,9 +569,12 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, dprintk("lockd: vfs_lock_file returned %d\n", error); switch (error) { case 0: + nlmsvc_remove_block(block); ret = nlm_granted; goto out; case -EAGAIN: + if (!wait) + nlmsvc_remove_block(block); ret = async_block ? nlm_lck_blocked : nlm_lck_denied; goto out; case FILE_LOCK_DEFERRED: @@ -579,17 +585,16 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, ret = nlmsvc_defer_lock_rqst(rqstp, block); goto out; case -EDEADLK: + nlmsvc_remove_block(block); ret = nlm_deadlock; goto out; default: /* includes ENOLCK */ + nlmsvc_remove_block(block); ret = nlm_lck_denied_nolocks; goto out; } ret = nlm_lck_blocked; - - /* Append to list of blocked */ - nlmsvc_insert_block(block, NLM_NEVER); out: mutex_unlock(&file->f_mutex); nlmsvc_release_block(block); |