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