nfs_localio: change nfsd_file_put_local() to take a pointer to __rcu pointer

Instead of calling xchg() and unrcu_pointer() before
nfsd_file_put_local(), we now pass pointer to the __rcu pointer and call
xchg() and unrcu_pointer() inside that function.

Where unrcu_pointer() is currently called the internals of "struct
nfsd_file" are not known and that causes older compilers such as gcc-8
to complain.

In some cases we have a __kernel (aka normal) pointer not an __rcu
pointer so we need to cast it to __rcu first. This is strictly a
weakening so no information is lost. Somewhat surprisingly, this cast
is accepted by gcc-8.

This has the pleasing result that the cmpxchg() which sets ro_file and
rw_file, and also the xchg() which clears them, are both now in the nfsd
code.

Reported-by: Pali Rohár <pali@kernel.org>
Reported-by: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
Fixes: 86e00412254a ("nfs: cache all open LOCALIO nfsd_file(s) in client")
Signed-off-by: NeilBrown <neil@brown.name>
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>

authored by NeilBrown and committed by Anna Schumaker c25a8977 21fb4403

+36 -35
+9 -2
fs/nfs/localio.c
··· 209 209 } 210 210 EXPORT_SYMBOL_GPL(nfs_local_probe_async); 211 211 212 - static inline void nfs_local_file_put(struct nfsd_file *nf) 212 + static inline void nfs_local_file_put(struct nfsd_file *localio) 213 213 { 214 - nfs_to_nfsd_file_put_local(nf); 214 + /* nfs_to_nfsd_file_put_local() expects an __rcu pointer 215 + * but we have a __kernel pointer. It is always safe 216 + * to cast a __kernel pointer to an __rcu pointer 217 + * because the cast only weakens what is known about the pointer. 218 + */ 219 + struct nfsd_file __rcu *nf = (struct nfsd_file __rcu*) localio; 220 + 221 + nfs_to_nfsd_file_put_local(&nf); 215 222 } 216 223 217 224 /*
+6 -18
fs/nfs_common/nfslocalio.c
··· 170 170 while ((nfl = list_first_entry_or_null(&nfs_uuid->files, 171 171 struct nfs_file_localio, 172 172 list)) != NULL) { 173 - struct nfsd_file *ro_nf; 174 - struct nfsd_file *rw_nf; 175 - 176 173 /* If nfs_uuid is already NULL, nfs_close_local_fh is 177 174 * closing and we must wait, else we unlink and close. 178 175 */ ··· 186 189 continue; 187 190 } 188 191 189 - ro_nf = unrcu_pointer(xchg(&nfl->ro_file, NULL)); 190 - rw_nf = unrcu_pointer(xchg(&nfl->rw_file, NULL)); 191 - 192 192 /* Remove nfl from nfs_uuid->files list */ 193 193 list_del_init(&nfl->list); 194 194 spin_unlock(&nfs_uuid->lock); 195 - if (ro_nf) 196 - nfs_to_nfsd_file_put_local(ro_nf); 197 - if (rw_nf) 198 - nfs_to_nfsd_file_put_local(rw_nf); 195 + 196 + nfs_to_nfsd_file_put_local(&nfl->ro_file); 197 + nfs_to_nfsd_file_put_local(&nfl->rw_file); 199 198 cond_resched(); 199 + 200 200 spin_lock(&nfs_uuid->lock); 201 201 /* Now we can allow racing nfs_close_local_fh() to 202 202 * skip the locking. ··· 297 303 298 304 void nfs_close_local_fh(struct nfs_file_localio *nfl) 299 305 { 300 - struct nfsd_file *ro_nf; 301 - struct nfsd_file *rw_nf; 302 306 nfs_uuid_t *nfs_uuid; 303 307 304 308 rcu_read_lock(); ··· 329 337 spin_unlock(&nfs_uuid->lock); 330 338 rcu_read_unlock(); 331 339 332 - ro_nf = unrcu_pointer(xchg(&nfl->ro_file, NULL)); 333 - rw_nf = unrcu_pointer(xchg(&nfl->rw_file, NULL)); 334 - if (ro_nf) 335 - nfs_to_nfsd_file_put_local(ro_nf); 336 - if (rw_nf) 337 - nfs_to_nfsd_file_put_local(rw_nf); 340 + nfs_to_nfsd_file_put_local(&nfl->ro_file); 341 + nfs_to_nfsd_file_put_local(&nfl->rw_file); 338 342 339 343 /* Remove nfl from nfs_uuid->files list and signal nfs_uuid_put() 340 344 * that we are done. The moment we drop the spinlock the
+8 -3
fs/nfsd/filecache.c
··· 378 378 * the reference of the nfsd_file. 379 379 */ 380 380 struct net * 381 - nfsd_file_put_local(struct nfsd_file *nf) 381 + nfsd_file_put_local(struct nfsd_file __rcu **pnf) 382 382 { 383 - struct net *net = nf->nf_net; 383 + struct nfsd_file *nf; 384 + struct net *net = NULL; 384 385 385 - nfsd_file_put(nf); 386 + nf = unrcu_pointer(xchg(pnf, NULL)); 387 + if (nf) { 388 + net = nf->nf_net; 389 + nfsd_file_put(nf); 390 + } 386 391 return net; 387 392 } 388 393
+1 -1
fs/nfsd/filecache.h
··· 62 62 int nfsd_file_cache_start_net(struct net *net); 63 63 void nfsd_file_cache_shutdown_net(struct net *net); 64 64 void nfsd_file_put(struct nfsd_file *nf); 65 - struct net *nfsd_file_put_local(struct nfsd_file *nf); 65 + struct net *nfsd_file_put_local(struct nfsd_file __rcu **nf); 66 66 struct nfsd_file *nfsd_file_get_local(struct nfsd_file *nf); 67 67 struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); 68 68 struct file *nfsd_file_file(struct nfsd_file *nf);
+12 -11
include/linux/nfslocalio.h
··· 50 50 spinlock_t *nn_local_clients_lock); 51 51 52 52 /* localio needs to map filehandle -> struct nfsd_file */ 53 - extern struct nfsd_file * 54 - nfsd_open_local_fh(struct net *, struct auth_domain *, struct rpc_clnt *, 55 - const struct cred *, const struct nfs_fh *, 56 - const fmode_t) __must_hold(rcu); 57 53 void nfs_close_local_fh(struct nfs_file_localio *); 58 54 59 55 struct nfsd_localio_operations { ··· 60 64 struct rpc_clnt *, 61 65 const struct cred *, 62 66 const struct nfs_fh *, 67 + struct nfsd_file __rcu **pnf, 63 68 const fmode_t); 64 - struct net *(*nfsd_file_put_local)(struct nfsd_file *); 69 + struct net *(*nfsd_file_put_local)(struct nfsd_file __rcu **); 65 70 struct nfsd_file *(*nfsd_file_get_local)(struct nfsd_file *); 66 71 struct file *(*nfsd_file_file)(struct nfsd_file *); 67 72 } ____cacheline_aligned; ··· 73 76 struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *, 74 77 struct rpc_clnt *, const struct cred *, 75 78 const struct nfs_fh *, struct nfs_file_localio *, 79 + struct nfsd_file __rcu **pnf, 76 80 const fmode_t); 77 81 78 82 static inline void nfs_to_nfsd_net_put(struct net *net) ··· 88 90 rcu_read_unlock(); 89 91 } 90 92 91 - static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio) 93 + static inline void nfs_to_nfsd_file_put_local(struct nfsd_file __rcu **localio) 92 94 { 93 95 /* 94 - * Must not hold RCU otherwise nfsd_file_put() can easily trigger: 95 - * "Voluntary context switch within RCU read-side critical section!" 96 - * by scheduling deep in underlying filesystem (e.g. XFS). 96 + * Either *localio must be guaranteed to be non-NULL, or caller 97 + * must prevent nfsd shutdown from completing as nfs_close_local_fh() 98 + * does by blocking the nfs_uuid from being finally put. 97 99 */ 98 - struct net *net = nfs_to->nfsd_file_put_local(localio); 100 + struct net *net; 99 101 100 - nfs_to_nfsd_net_put(net); 102 + net = nfs_to->nfsd_file_put_local(localio); 103 + 104 + if (net) 105 + nfs_to_nfsd_net_put(net); 101 106 } 102 107 103 108 #else /* CONFIG_NFS_LOCALIO */