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 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 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 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 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 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 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 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 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 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.