From: Trond Myklebust <trond.myklebust@xxxxxxxxxxxxxxx> No reference to nfl is held when waiting in nfs_uuid_put(), so not only must the event condition check if the first entry in the list has changed, it must also check if the nfl->nfs_uuid field is still NULL, in case the old entry was replaced. Also change the event variable to be nfs_uuid, for the same reason that no reference is held to nfl. Acked-by: Mike Snitzer <snitzer@xxxxxxxxxx> Tested-by: Mike Snitzer <snitzer@xxxxxxxxxx> Fixes: 21fb44034695 ("nfs_localio: protect race between nfs_uuid_put() and nfs_close_local_fh()") Signed-off-by: Trond Myklebust <trond.myklebust@xxxxxxxxxxxxxxx> --- fs/nfs_common/nfslocalio.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c index 64949c46c174..d157fdc068d7 100644 --- a/fs/nfs_common/nfslocalio.c +++ b/fs/nfs_common/nfslocalio.c @@ -177,12 +177,13 @@ static bool nfs_uuid_put(nfs_uuid_t *nfs_uuid) /* nfs_close_local_fh() is doing the * close and we must wait. until it unlinks */ - wait_var_event_spinlock(nfl, - list_first_entry_or_null( - &nfs_uuid->files, - struct nfs_file_localio, - list) != nfl, - &nfs_uuid->lock); + wait_var_event_spinlock( + nfs_uuid, + list_first_entry_or_null( + &nfs_uuid->files, + struct nfs_file_localio, list) != nfl || + rcu_access_pointer(nfl->nfs_uuid), + &nfs_uuid->lock); continue; } @@ -338,7 +339,7 @@ void nfs_close_local_fh(struct nfs_file_localio *nfl) */ spin_lock(&nfs_uuid->lock); list_del_init(&nfl->list); - wake_up_var_locked(&nfl->nfs_uuid, &nfs_uuid->lock); + wake_up_var_locked(nfs_uuid, &nfs_uuid->lock); spin_unlock(&nfs_uuid->lock); } EXPORT_SYMBOL_GPL(nfs_close_local_fh); -- 2.50.1