1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com>
4 * Copyright (C) 2024 NeilBrown <neilb@suse.de>
7 #include <linux/module.h>
8 #include <linux/list.h>
9 #include <linux/nfslocalio.h>
10 #include <net/netns/generic.h>
12 MODULE_LICENSE("GPL");
13 MODULE_DESCRIPTION("NFS localio protocol bypass support");
15 static DEFINE_SPINLOCK(nfs_uuid_lock
);
18 * Global list of nfs_uuid_t instances
19 * that is protected by nfs_uuid_lock.
21 static LIST_HEAD(nfs_uuids
);
23 void nfs_uuid_init(nfs_uuid_t
*nfs_uuid
)
27 INIT_LIST_HEAD(&nfs_uuid
->list
);
29 EXPORT_SYMBOL_GPL(nfs_uuid_init
);
31 bool nfs_uuid_begin(nfs_uuid_t
*nfs_uuid
)
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
);
39 uuid_gen(&nfs_uuid
->uuid
);
40 list_add_tail(&nfs_uuid
->list
, &nfs_uuids
);
41 spin_unlock(&nfs_uuid_lock
);
45 EXPORT_SYMBOL_GPL(nfs_uuid_begin
);
47 void nfs_uuid_end(nfs_uuid_t
*nfs_uuid
)
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
);
56 EXPORT_SYMBOL_GPL(nfs_uuid_end
);
58 static nfs_uuid_t
* nfs_uuid_lookup_locked(const uuid_t
*uuid
)
62 list_for_each_entry(nfs_uuid
, &nfs_uuids
, list
)
63 if (uuid_equal(&nfs_uuid
->uuid
, uuid
))
69 static struct module
*nfsd_mod
;
71 void nfs_uuid_is_local(const uuid_t
*uuid
, struct list_head
*list
,
72 struct net
*net
, struct auth_domain
*dom
,
77 spin_lock(&nfs_uuid_lock
);
78 nfs_uuid
= nfs_uuid_lookup_locked(uuid
);
83 * We don't hold a ref on the net, but instead put
84 * ourselves on a list so the net pointer can be
87 list_move(&nfs_uuid
->list
, list
);
88 rcu_assign_pointer(nfs_uuid
->net
, net
);
93 spin_unlock(&nfs_uuid_lock
);
95 EXPORT_SYMBOL_GPL(nfs_uuid_is_local
);
97 static void nfs_uuid_put_locked(nfs_uuid_t
*nfs_uuid
)
100 module_put(nfsd_mod
);
101 nfs_uuid
->net
= NULL
;
104 auth_domain_put(nfs_uuid
->dom
);
105 nfs_uuid
->dom
= NULL
;
107 list_del_init(&nfs_uuid
->list
);
110 void nfs_uuid_invalidate_clients(struct list_head
*list
)
112 nfs_uuid_t
*nfs_uuid
, *tmp
;
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
);
119 EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_clients
);
121 void nfs_uuid_invalidate_one_client(nfs_uuid_t
*nfs_uuid
)
124 spin_lock(&nfs_uuid_lock
);
125 nfs_uuid_put_locked(nfs_uuid
);
126 spin_unlock(&nfs_uuid_lock
);
129 EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_one_client
);
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
)
136 struct nfsd_file
*localio
;
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.
145 * Otherwise NFS may not have ref on NFSD and therefore cannot safely
146 * make 'nfs_to' calls.
149 net
= rcu_dereference(uuid
->net
);
150 if (!net
|| !nfs_to
->nfsd_serv_try_get(net
)) {
152 return ERR_PTR(-ENXIO
);
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
);
159 nfs_to_nfsd_net_put(net
);
163 EXPORT_SYMBOL_GPL(nfs_open_local_fh
);
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.
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.
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
185 const struct nfsd_localio_operations
*nfs_to
;
186 EXPORT_SYMBOL_GPL(nfs_to
);