xref: /linux/fs/nfs_common/nfslocalio.c (revision ec8c17e5ecb4a5a74069687ccb6d2cfe1851302e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com>
4  * Copyright (C) 2024 NeilBrown <neilb@suse.de>
5  */
6 
7 #include <linux/module.h>
8 #include <linux/list.h>
9 #include <linux/nfslocalio.h>
10 #include <net/netns/generic.h>
11 
12 MODULE_LICENSE("GPL");
13 MODULE_DESCRIPTION("NFS localio protocol bypass support");
14 
15 static DEFINE_SPINLOCK(nfs_uuid_lock);
16 
17 /*
18  * Global list of nfs_uuid_t instances
19  * that is protected by nfs_uuid_lock.
20  */
21 static LIST_HEAD(nfs_uuids);
22 
nfs_uuid_init(nfs_uuid_t * nfs_uuid)23 void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
24 {
25 	nfs_uuid->net = NULL;
26 	nfs_uuid->dom = NULL;
27 	INIT_LIST_HEAD(&nfs_uuid->list);
28 }
29 EXPORT_SYMBOL_GPL(nfs_uuid_init);
30 
nfs_uuid_begin(nfs_uuid_t * nfs_uuid)31 bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
32 {
33 	spin_lock(&nfs_uuid_lock);
34 	/* Is this nfs_uuid already in use? */
35 	if (!list_empty(&nfs_uuid->list)) {
36 		spin_unlock(&nfs_uuid_lock);
37 		return false;
38 	}
39 	uuid_gen(&nfs_uuid->uuid);
40 	list_add_tail(&nfs_uuid->list, &nfs_uuids);
41 	spin_unlock(&nfs_uuid_lock);
42 
43 	return true;
44 }
45 EXPORT_SYMBOL_GPL(nfs_uuid_begin);
46 
nfs_uuid_end(nfs_uuid_t * nfs_uuid)47 void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
48 {
49 	if (nfs_uuid->net == NULL) {
50 		spin_lock(&nfs_uuid_lock);
51 		if (nfs_uuid->net == NULL)
52 			list_del_init(&nfs_uuid->list);
53 		spin_unlock(&nfs_uuid_lock);
54 	}
55 }
56 EXPORT_SYMBOL_GPL(nfs_uuid_end);
57 
nfs_uuid_lookup_locked(const uuid_t * uuid)58 static nfs_uuid_t * nfs_uuid_lookup_locked(const uuid_t *uuid)
59 {
60 	nfs_uuid_t *nfs_uuid;
61 
62 	list_for_each_entry(nfs_uuid, &nfs_uuids, list)
63 		if (uuid_equal(&nfs_uuid->uuid, uuid))
64 			return nfs_uuid;
65 
66 	return NULL;
67 }
68 
69 static struct module *nfsd_mod;
70 
nfs_uuid_is_local(const uuid_t * uuid,struct list_head * list,struct net * net,struct auth_domain * dom,struct module * mod)71 void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list,
72 		       struct net *net, struct auth_domain *dom,
73 		       struct module *mod)
74 {
75 	nfs_uuid_t *nfs_uuid;
76 
77 	spin_lock(&nfs_uuid_lock);
78 	nfs_uuid = nfs_uuid_lookup_locked(uuid);
79 	if (nfs_uuid) {
80 		kref_get(&dom->ref);
81 		nfs_uuid->dom = dom;
82 		/*
83 		 * We don't hold a ref on the net, but instead put
84 		 * ourselves on a list so the net pointer can be
85 		 * invalidated.
86 		 */
87 		list_move(&nfs_uuid->list, list);
88 		rcu_assign_pointer(nfs_uuid->net, net);
89 
90 		__module_get(mod);
91 		nfsd_mod = mod;
92 	}
93 	spin_unlock(&nfs_uuid_lock);
94 }
95 EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
96 
nfs_uuid_put_locked(nfs_uuid_t * nfs_uuid)97 static void nfs_uuid_put_locked(nfs_uuid_t *nfs_uuid)
98 {
99 	if (nfs_uuid->net) {
100 		module_put(nfsd_mod);
101 		nfs_uuid->net = NULL;
102 	}
103 	if (nfs_uuid->dom) {
104 		auth_domain_put(nfs_uuid->dom);
105 		nfs_uuid->dom = NULL;
106 	}
107 	list_del_init(&nfs_uuid->list);
108 }
109 
nfs_uuid_invalidate_clients(struct list_head * list)110 void nfs_uuid_invalidate_clients(struct list_head *list)
111 {
112 	nfs_uuid_t *nfs_uuid, *tmp;
113 
114 	spin_lock(&nfs_uuid_lock);
115 	list_for_each_entry_safe(nfs_uuid, tmp, list, list)
116 		nfs_uuid_put_locked(nfs_uuid);
117 	spin_unlock(&nfs_uuid_lock);
118 }
119 EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_clients);
120 
nfs_uuid_invalidate_one_client(nfs_uuid_t * nfs_uuid)121 void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid)
122 {
123 	if (nfs_uuid->net) {
124 		spin_lock(&nfs_uuid_lock);
125 		nfs_uuid_put_locked(nfs_uuid);
126 		spin_unlock(&nfs_uuid_lock);
127 	}
128 }
129 EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_one_client);
130 
nfs_open_local_fh(nfs_uuid_t * uuid,struct rpc_clnt * rpc_clnt,const struct cred * cred,const struct nfs_fh * nfs_fh,const fmode_t fmode)131 struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
132 		   struct rpc_clnt *rpc_clnt, const struct cred *cred,
133 		   const struct nfs_fh *nfs_fh, const fmode_t fmode)
134 {
135 	struct net *net;
136 	struct nfsd_file *localio;
137 
138 	/*
139 	 * Not running in nfsd context, so must safely get reference on nfsd_serv.
140 	 * But the server may already be shutting down, if so disallow new localio.
141 	 * uuid->net is NOT a counted reference, but rcu_read_lock() ensures that
142 	 * if uuid->net is not NULL, then calling nfsd_serv_try_get() is safe
143 	 * and if it succeeds we will have an implied reference to the net.
144 	 *
145 	 * Otherwise NFS may not have ref on NFSD and therefore cannot safely
146 	 * make 'nfs_to' calls.
147 	 */
148 	rcu_read_lock();
149 	net = rcu_dereference(uuid->net);
150 	if (!net || !nfs_to->nfsd_serv_try_get(net)) {
151 		rcu_read_unlock();
152 		return ERR_PTR(-ENXIO);
153 	}
154 	rcu_read_unlock();
155 	/* We have an implied reference to net thanks to nfsd_serv_try_get */
156 	localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt,
157 					     cred, nfs_fh, fmode);
158 	if (IS_ERR(localio))
159 		nfs_to_nfsd_net_put(net);
160 
161 	return localio;
162 }
163 EXPORT_SYMBOL_GPL(nfs_open_local_fh);
164 
165 /*
166  * The NFS LOCALIO code needs to call into NFSD using various symbols,
167  * but cannot be statically linked, because that will make the NFS
168  * module always depend on the NFSD module.
169  *
170  * 'nfs_to' provides NFS access to NFSD functions needed for LOCALIO,
171  * its lifetime is tightly coupled to the NFSD module and will always
172  * be available to NFS LOCALIO because any successful client<->server
173  * LOCALIO handshake results in a reference on the NFSD module (above),
174  * so NFS implicitly holds a reference to the NFSD module and its
175  * functions in the 'nfs_to' nfsd_localio_operations cannot disappear.
176  *
177  * If the last NFS client using LOCALIO disconnects (and its reference
178  * on NFSD dropped) then NFSD could be unloaded, resulting in 'nfs_to'
179  * functions being invalid pointers. But if NFSD isn't loaded then NFS
180  * will not be able to handshake with NFSD and will have no cause to
181  * try to call 'nfs_to' function pointers. If/when NFSD is reloaded it
182  * will reinitialize the 'nfs_to' function pointers and make LOCALIO
183  * possible.
184  */
185 const struct nfsd_localio_operations *nfs_to;
186 EXPORT_SYMBOL_GPL(nfs_to);
187