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 } 210 EXPORT_SYMBOL_GPL(nfs_local_probe_async); 211 212 - static inline void nfs_local_file_put(struct nfsd_file *nf) 213 { 214 - nfs_to_nfsd_file_put_local(nf); 215 } 216 217 /*
··· 209 } 210 EXPORT_SYMBOL_GPL(nfs_local_probe_async); 211 212 + static inline void nfs_local_file_put(struct nfsd_file *localio) 213 { 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); 222 } 223 224 /*
+6 -18
fs/nfs_common/nfslocalio.c
··· 170 while ((nfl = list_first_entry_or_null(&nfs_uuid->files, 171 struct nfs_file_localio, 172 list)) != NULL) { 173 - struct nfsd_file *ro_nf; 174 - struct nfsd_file *rw_nf; 175 - 176 /* If nfs_uuid is already NULL, nfs_close_local_fh is 177 * closing and we must wait, else we unlink and close. 178 */ ··· 186 continue; 187 } 188 189 - ro_nf = unrcu_pointer(xchg(&nfl->ro_file, NULL)); 190 - rw_nf = unrcu_pointer(xchg(&nfl->rw_file, NULL)); 191 - 192 /* Remove nfl from nfs_uuid->files list */ 193 list_del_init(&nfl->list); 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); 199 cond_resched(); 200 spin_lock(&nfs_uuid->lock); 201 /* Now we can allow racing nfs_close_local_fh() to 202 * skip the locking. ··· 297 298 void nfs_close_local_fh(struct nfs_file_localio *nfl) 299 { 300 - struct nfsd_file *ro_nf; 301 - struct nfsd_file *rw_nf; 302 nfs_uuid_t *nfs_uuid; 303 304 rcu_read_lock(); ··· 329 spin_unlock(&nfs_uuid->lock); 330 rcu_read_unlock(); 331 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); 338 339 /* Remove nfl from nfs_uuid->files list and signal nfs_uuid_put() 340 * that we are done. The moment we drop the spinlock the
··· 170 while ((nfl = list_first_entry_or_null(&nfs_uuid->files, 171 struct nfs_file_localio, 172 list)) != NULL) { 173 /* If nfs_uuid is already NULL, nfs_close_local_fh is 174 * closing and we must wait, else we unlink and close. 175 */ ··· 189 continue; 190 } 191 192 /* Remove nfl from nfs_uuid->files list */ 193 list_del_init(&nfl->list); 194 spin_unlock(&nfs_uuid->lock); 195 + 196 + nfs_to_nfsd_file_put_local(&nfl->ro_file); 197 + nfs_to_nfsd_file_put_local(&nfl->rw_file); 198 cond_resched(); 199 + 200 spin_lock(&nfs_uuid->lock); 201 /* Now we can allow racing nfs_close_local_fh() to 202 * skip the locking. ··· 303 304 void nfs_close_local_fh(struct nfs_file_localio *nfl) 305 { 306 nfs_uuid_t *nfs_uuid; 307 308 rcu_read_lock(); ··· 337 spin_unlock(&nfs_uuid->lock); 338 rcu_read_unlock(); 339 340 + nfs_to_nfsd_file_put_local(&nfl->ro_file); 341 + nfs_to_nfsd_file_put_local(&nfl->rw_file); 342 343 /* Remove nfl from nfs_uuid->files list and signal nfs_uuid_put() 344 * that we are done. The moment we drop the spinlock the
+8 -3
fs/nfsd/filecache.c
··· 378 * the reference of the nfsd_file. 379 */ 380 struct net * 381 - nfsd_file_put_local(struct nfsd_file *nf) 382 { 383 - struct net *net = nf->nf_net; 384 385 - nfsd_file_put(nf); 386 return net; 387 } 388
··· 378 * the reference of the nfsd_file. 379 */ 380 struct net * 381 + nfsd_file_put_local(struct nfsd_file __rcu **pnf) 382 { 383 + struct nfsd_file *nf; 384 + struct net *net = NULL; 385 386 + nf = unrcu_pointer(xchg(pnf, NULL)); 387 + if (nf) { 388 + net = nf->nf_net; 389 + nfsd_file_put(nf); 390 + } 391 return net; 392 } 393
+1 -1
fs/nfsd/filecache.h
··· 62 int nfsd_file_cache_start_net(struct net *net); 63 void nfsd_file_cache_shutdown_net(struct net *net); 64 void nfsd_file_put(struct nfsd_file *nf); 65 - struct net *nfsd_file_put_local(struct nfsd_file *nf); 66 struct nfsd_file *nfsd_file_get_local(struct nfsd_file *nf); 67 struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); 68 struct file *nfsd_file_file(struct nfsd_file *nf);
··· 62 int nfsd_file_cache_start_net(struct net *net); 63 void nfsd_file_cache_shutdown_net(struct net *net); 64 void nfsd_file_put(struct nfsd_file *nf); 65 + struct net *nfsd_file_put_local(struct nfsd_file __rcu **nf); 66 struct nfsd_file *nfsd_file_get_local(struct nfsd_file *nf); 67 struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); 68 struct file *nfsd_file_file(struct nfsd_file *nf);
+12 -11
include/linux/nfslocalio.h
··· 50 spinlock_t *nn_local_clients_lock); 51 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 void nfs_close_local_fh(struct nfs_file_localio *); 58 59 struct nfsd_localio_operations { ··· 60 struct rpc_clnt *, 61 const struct cred *, 62 const struct nfs_fh *, 63 const fmode_t); 64 - struct net *(*nfsd_file_put_local)(struct nfsd_file *); 65 struct nfsd_file *(*nfsd_file_get_local)(struct nfsd_file *); 66 struct file *(*nfsd_file_file)(struct nfsd_file *); 67 } ____cacheline_aligned; ··· 73 struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *, 74 struct rpc_clnt *, const struct cred *, 75 const struct nfs_fh *, struct nfs_file_localio *, 76 const fmode_t); 77 78 static inline void nfs_to_nfsd_net_put(struct net *net) ··· 88 rcu_read_unlock(); 89 } 90 91 - static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio) 92 { 93 /* 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). 97 */ 98 - struct net *net = nfs_to->nfsd_file_put_local(localio); 99 100 - nfs_to_nfsd_net_put(net); 101 } 102 103 #else /* CONFIG_NFS_LOCALIO */
··· 50 spinlock_t *nn_local_clients_lock); 51 52 /* localio needs to map filehandle -> struct nfsd_file */ 53 void nfs_close_local_fh(struct nfs_file_localio *); 54 55 struct nfsd_localio_operations { ··· 64 struct rpc_clnt *, 65 const struct cred *, 66 const struct nfs_fh *, 67 + struct nfsd_file __rcu **pnf, 68 const fmode_t); 69 + struct net *(*nfsd_file_put_local)(struct nfsd_file __rcu **); 70 struct nfsd_file *(*nfsd_file_get_local)(struct nfsd_file *); 71 struct file *(*nfsd_file_file)(struct nfsd_file *); 72 } ____cacheline_aligned; ··· 76 struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *, 77 struct rpc_clnt *, const struct cred *, 78 const struct nfs_fh *, struct nfs_file_localio *, 79 + struct nfsd_file __rcu **pnf, 80 const fmode_t); 81 82 static inline void nfs_to_nfsd_net_put(struct net *net) ··· 90 rcu_read_unlock(); 91 } 92 93 + static inline void nfs_to_nfsd_file_put_local(struct nfsd_file __rcu **localio) 94 { 95 /* 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. 99 */ 100 + struct net *net; 101 102 + net = nfs_to->nfsd_file_put_local(localio); 103 + 104 + if (net) 105 + nfs_to_nfsd_net_put(net); 106 } 107 108 #else /* CONFIG_NFS_LOCALIO */