Hi, On Thu, Jul 20, 2023 at 8:58 AM Alexander Aring <aahri...@redhat.com> wrote: > > 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. > > Signed-off-by: Alexander Aring <aahri...@redhat.com> > --- > fs/lockd/svclock.c | 80 +++++++++++++++++++++++++++---------- > include/linux/lockd/lockd.h | 1 + > 2 files changed, 60 insertions(+), 21 deletions(-) > > diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c > index 28abec5c451d..62ef27a69a9e 100644 > --- a/fs/lockd/svclock.c > +++ b/fs/lockd/svclock.c > @@ -297,6 +297,8 @@ static void nlmsvc_free_block(struct kref *kref) > > dprintk("lockd: freeing block %p...\n", block); > > + WARN_ON_ONCE(block->b_flags & B_PENDING_CALLBACK); > + > /* Remove block from file's list of blocks */ > list_del_init(&block->b_flist); > mutex_unlock(&file->f_mutex); > @@ -543,6 +545,12 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file > *file, > goto out; > } > > + if (block->b_flags & B_PENDING_CALLBACK) > + goto pending_request; > + > + /* Append to list of blocked */ > + nlmsvc_insert_block(block, NLM_NEVER); > + > if (!wait) > lock->fl.fl_flags &= ~FL_SLEEP; > mode = lock_to_openmode(&lock->fl); > @@ -552,9 +560,13 @@ 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);
reacting here with nlmsvc_remove_block() assumes that the block was not being added to the nlm_blocked list before nlmsvc_insert_block() was called. I am not sure if this is always the case here. Does somebody see a problem with that? > ret = nlm_granted; > goto out; > case -EAGAIN: > + if (!wait) > + nlmsvc_remove_block(block); > +pending_request: > /* > * If this is a blocking request for an > * already pending lock request then we need > @@ -565,6 +577,8 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, > ret = async_block ? nlm_lck_blocked : nlm_lck_denied; > goto out; > case FILE_LOCK_DEFERRED: > + block->b_flags |= B_PENDING_CALLBACK; > + > if (wait) > break; > /* Filesystem lock operation is in progress > @@ -572,17 +586,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); > @@ -739,34 +752,59 @@ nlmsvc_update_deferred_block(struct nlm_block *block, > int result) > block->b_flags |= B_TIMED_OUT; > } - Alex