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  		rcu_read_lock();
160  		nfs_to->nfsd_serv_put(net);
161  		rcu_read_unlock();
162  	}
163  	return localio;
164  }
165  EXPORT_SYMBOL_GPL(nfs_open_local_fh);
166  
167  /*
168   * The NFS LOCALIO code needs to call into NFSD using various symbols,
169   * but cannot be statically linked, because that will make the NFS
170   * module always depend on the NFSD module.
171   *
172   * 'nfs_to' provides NFS access to NFSD functions needed for LOCALIO,
173   * its lifetime is tightly coupled to the NFSD module and will always
174   * be available to NFS LOCALIO because any successful client<->server
175   * LOCALIO handshake results in a reference on the NFSD module (above),
176   * so NFS implicitly holds a reference to the NFSD module and its
177   * functions in the 'nfs_to' nfsd_localio_operations cannot disappear.
178   *
179   * If the last NFS client using LOCALIO disconnects (and its reference
180   * on NFSD dropped) then NFSD could be unloaded, resulting in 'nfs_to'
181   * functions being invalid pointers. But if NFSD isn't loaded then NFS
182   * will not be able to handshake with NFSD and will have no cause to
183   * try to call 'nfs_to' function pointers. If/when NFSD is reloaded it
184   * will reinitialize the 'nfs_to' function pointers and make LOCALIO
185   * possible.
186   */
187  const struct nfsd_localio_operations *nfs_to;
188  EXPORT_SYMBOL_GPL(nfs_to);
189