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/rculist.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_begin(nfs_uuid_t *nfs_uuid) 24 { 25 nfs_uuid->net = NULL; 26 nfs_uuid->dom = NULL; 27 uuid_gen(&nfs_uuid->uuid); 28 29 spin_lock(&nfs_uuid_lock); 30 list_add_tail_rcu(&nfs_uuid->list, &nfs_uuids); 31 spin_unlock(&nfs_uuid_lock); 32 } 33 EXPORT_SYMBOL_GPL(nfs_uuid_begin); 34 35 void nfs_uuid_end(nfs_uuid_t *nfs_uuid) 36 { 37 if (nfs_uuid->net == NULL) { 38 spin_lock(&nfs_uuid_lock); 39 list_del_init(&nfs_uuid->list); 40 spin_unlock(&nfs_uuid_lock); 41 } 42 } 43 EXPORT_SYMBOL_GPL(nfs_uuid_end); 44 45 static nfs_uuid_t * nfs_uuid_lookup_locked(const uuid_t *uuid) 46 { 47 nfs_uuid_t *nfs_uuid; 48 49 list_for_each_entry(nfs_uuid, &nfs_uuids, list) 50 if (uuid_equal(&nfs_uuid->uuid, uuid)) 51 return nfs_uuid; 52 53 return NULL; 54 } 55 56 static struct module *nfsd_mod; 57 58 void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list, 59 struct net *net, struct auth_domain *dom, 60 struct module *mod) 61 { 62 nfs_uuid_t *nfs_uuid; 63 64 spin_lock(&nfs_uuid_lock); 65 nfs_uuid = nfs_uuid_lookup_locked(uuid); 66 if (nfs_uuid) { 67 kref_get(&dom->ref); 68 nfs_uuid->dom = dom; 69 /* 70 * We don't hold a ref on the net, but instead put 71 * ourselves on a list so the net pointer can be 72 * invalidated. 73 */ 74 list_move(&nfs_uuid->list, list); 75 rcu_assign_pointer(nfs_uuid->net, net); 76 77 __module_get(mod); 78 nfsd_mod = mod; 79 } 80 spin_unlock(&nfs_uuid_lock); 81 } 82 EXPORT_SYMBOL_GPL(nfs_uuid_is_local); 83 84 static void nfs_uuid_put_locked(nfs_uuid_t *nfs_uuid) 85 { 86 if (nfs_uuid->net) { 87 module_put(nfsd_mod); 88 nfs_uuid->net = NULL; 89 } 90 if (nfs_uuid->dom) { 91 auth_domain_put(nfs_uuid->dom); 92 nfs_uuid->dom = NULL; 93 } 94 list_del_init(&nfs_uuid->list); 95 } 96 97 void nfs_uuid_invalidate_clients(struct list_head *list) 98 { 99 nfs_uuid_t *nfs_uuid, *tmp; 100 101 spin_lock(&nfs_uuid_lock); 102 list_for_each_entry_safe(nfs_uuid, tmp, list, list) 103 nfs_uuid_put_locked(nfs_uuid); 104 spin_unlock(&nfs_uuid_lock); 105 } 106 EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_clients); 107 108 void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid) 109 { 110 if (nfs_uuid->net) { 111 spin_lock(&nfs_uuid_lock); 112 nfs_uuid_put_locked(nfs_uuid); 113 spin_unlock(&nfs_uuid_lock); 114 } 115 } 116 EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_one_client); 117 118 struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid, 119 struct rpc_clnt *rpc_clnt, const struct cred *cred, 120 const struct nfs_fh *nfs_fh, const fmode_t fmode) 121 { 122 struct net *net; 123 struct nfsd_file *localio; 124 125 /* 126 * Not running in nfsd context, so must safely get reference on nfsd_serv. 127 * But the server may already be shutting down, if so disallow new localio. 128 * uuid->net is NOT a counted reference, but rcu_read_lock() ensures that 129 * if uuid->net is not NULL, then calling nfsd_serv_try_get() is safe 130 * and if it succeeds we will have an implied reference to the net. 131 * 132 * Otherwise NFS may not have ref on NFSD and therefore cannot safely 133 * make 'nfs_to' calls. 134 */ 135 rcu_read_lock(); 136 net = rcu_dereference(uuid->net); 137 if (!net || !nfs_to->nfsd_serv_try_get(net)) { 138 rcu_read_unlock(); 139 return ERR_PTR(-ENXIO); 140 } 141 rcu_read_unlock(); 142 /* We have an implied reference to net thanks to nfsd_serv_try_get */ 143 localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt, 144 cred, nfs_fh, fmode); 145 if (IS_ERR(localio)) { 146 rcu_read_lock(); 147 nfs_to->nfsd_serv_put(net); 148 rcu_read_unlock(); 149 } 150 return localio; 151 } 152 EXPORT_SYMBOL_GPL(nfs_open_local_fh); 153 154 /* 155 * The NFS LOCALIO code needs to call into NFSD using various symbols, 156 * but cannot be statically linked, because that will make the NFS 157 * module always depend on the NFSD module. 158 * 159 * 'nfs_to' provides NFS access to NFSD functions needed for LOCALIO, 160 * its lifetime is tightly coupled to the NFSD module and will always 161 * be available to NFS LOCALIO because any successful client<->server 162 * LOCALIO handshake results in a reference on the NFSD module (above), 163 * so NFS implicitly holds a reference to the NFSD module and its 164 * functions in the 'nfs_to' nfsd_localio_operations cannot disappear. 165 * 166 * If the last NFS client using LOCALIO disconnects (and its reference 167 * on NFSD dropped) then NFSD could be unloaded, resulting in 'nfs_to' 168 * functions being invalid pointers. But if NFSD isn't loaded then NFS 169 * will not be able to handshake with NFSD and will have no cause to 170 * try to call 'nfs_to' function pointers. If/when NFSD is reloaded it 171 * will reinitialize the 'nfs_to' function pointers and make LOCALIO 172 * possible. 173 */ 174 const struct nfsd_localio_operations *nfs_to; 175 EXPORT_SYMBOL_GPL(nfs_to); 176