Skip to content

Commit ed9be31

Browse files
neilbrownAnna Schumaker
authored andcommitted
nfs_localio: use cmpxchg() to install new nfs_file_localio
Rather than using nfs_uuid.lock to protect installing a new ro_file or rw_file, change to use cmpxchg(). Removing the file already uses xchg() so this improves symmetry and also makes the code a little simpler. Also remove the optimisation of not taking the lock, and not removing the nfs_file_localio from the linked list, when both ->ro_file and ->rw_file are already NULL. Given that ->nfs_uuid was not NULL, it is extremely unlikely that neither ->ro_file or ->rw_file is NULL so this optimisation can be of little value and it complicates understanding of the code - why can the list_del_init() be skipped? Finally, move the assignment of NULL to ->nfs_uuid until after the last action on the nfs_file_localio (the list_del_init). As soon as this is NULL a racing nfs_close_local_fh() can bypass all the locking and go on to free the nfs_file_localio, so we must be certain to be finished with it first. Fixes: 86e0041 ("nfs: cache all open LOCALIO nfsd_file(s) in client") Signed-off-by: NeilBrown <[email protected]> Signed-off-by: Anna Schumaker <[email protected]>
1 parent 111f9e4 commit ed9be31

File tree

2 files changed

+20
-30
lines changed

2 files changed

+20
-30
lines changed

fs/nfs/localio.c

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,9 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
282282
return NULL;
283283
rcu_read_lock();
284284
/* try to swap in the pointer */
285-
spin_lock(&clp->cl_uuid.lock);
286-
nf = rcu_dereference_protected(*pnf, 1);
287-
if (!nf) {
288-
nf = new;
289-
new = NULL;
290-
rcu_assign_pointer(*pnf, nf);
291-
}
292-
spin_unlock(&clp->cl_uuid.lock);
285+
nf = unrcu_pointer(cmpxchg(pnf, NULL, RCU_INITIALIZER(new)));
286+
if (!nf)
287+
swap(nf, new);
293288
}
294289
nf = nfs_local_file_get(nf);
295290
rcu_read_unlock();

fs/nfs_common/nfslocalio.c

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,8 @@ EXPORT_SYMBOL_GPL(nfs_open_local_fh);
273273

274274
void nfs_close_local_fh(struct nfs_file_localio *nfl)
275275
{
276-
struct nfsd_file *ro_nf = NULL;
277-
struct nfsd_file *rw_nf = NULL;
276+
struct nfsd_file *ro_nf;
277+
struct nfsd_file *rw_nf;
278278
nfs_uuid_t *nfs_uuid;
279279

280280
rcu_read_lock();
@@ -285,28 +285,23 @@ void nfs_close_local_fh(struct nfs_file_localio *nfl)
285285
return;
286286
}
287287

288-
ro_nf = rcu_access_pointer(nfl->ro_file);
289-
rw_nf = rcu_access_pointer(nfl->rw_file);
290-
if (ro_nf || rw_nf) {
291-
spin_lock(&nfs_uuid->lock);
292-
if (ro_nf)
293-
ro_nf = rcu_dereference_protected(xchg(&nfl->ro_file, NULL), 1);
294-
if (rw_nf)
295-
rw_nf = rcu_dereference_protected(xchg(&nfl->rw_file, NULL), 1);
296-
297-
/* Remove nfl from nfs_uuid->files list */
298-
RCU_INIT_POINTER(nfl->nfs_uuid, NULL);
299-
list_del_init(&nfl->list);
300-
spin_unlock(&nfs_uuid->lock);
301-
rcu_read_unlock();
288+
ro_nf = unrcu_pointer(xchg(&nfl->ro_file, NULL));
289+
rw_nf = unrcu_pointer(xchg(&nfl->rw_file, NULL));
302290

303-
if (ro_nf)
304-
nfs_to_nfsd_file_put_local(ro_nf);
305-
if (rw_nf)
306-
nfs_to_nfsd_file_put_local(rw_nf);
307-
return;
308-
}
291+
spin_lock(&nfs_uuid->lock);
292+
/* Remove nfl from nfs_uuid->files list */
293+
list_del_init(&nfl->list);
294+
spin_unlock(&nfs_uuid->lock);
309295
rcu_read_unlock();
296+
/* Now we can allow racing nfs_close_local_fh() to
297+
* skip the locking.
298+
*/
299+
RCU_INIT_POINTER(nfl->nfs_uuid, NULL);
300+
301+
if (ro_nf)
302+
nfs_to_nfsd_file_put_local(ro_nf);
303+
if (rw_nf)
304+
nfs_to_nfsd_file_put_local(rw_nf);
310305
}
311306
EXPORT_SYMBOL_GPL(nfs_close_local_fh);
312307

0 commit comments

Comments
 (0)