1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * NFS server support for local clients to bypass network stack 4 * 5 * Copyright (C) 2014 Weston Andros Adamson <dros@primarydata.com> 6 * Copyright (C) 2019 Trond Myklebust <trond.myklebust@hammerspace.com> 7 * Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com> 8 * Copyright (C) 2024 NeilBrown <neilb@suse.de> 9 */ 10 11 #include <linux/exportfs.h> 12 #include <linux/sunrpc/svcauth.h> 13 #include <linux/sunrpc/clnt.h> 14 #include <linux/nfs.h> 15 #include <linux/nfs_common.h> 16 #include <linux/nfslocalio.h> 17 #include <linux/nfs_fs.h> 18 #include <linux/nfs_xdr.h> 19 #include <linux/string.h> 20 21 #include "nfsd.h" 22 #include "vfs.h" 23 #include "netns.h" 24 #include "filecache.h" 25 #include "cache.h" 26 27 /** 28 * nfsd_open_local_fh - lookup a local filehandle @nfs_fh and map to nfsd_file 29 * 30 * @net: 'struct net' to get the proper nfsd_net required for LOCALIO access 31 * @dom: 'struct auth_domain' required for LOCALIO access 32 * @rpc_clnt: rpc_clnt that the client established 33 * @cred: cred that the client established 34 * @nfs_fh: filehandle to lookup 35 * @pnf: place to find the nfsd_file, or store it if it was non-NULL 36 * @fmode: fmode_t to use for open 37 * 38 * This function maps a local fh to a path on a local filesystem. 39 * This is useful when the nfs client has the local server mounted - it can 40 * avoid all the NFS overhead with reads, writes and commits. 41 * 42 * On successful return, returned nfsd_file will have its nf_net member 43 * set. Caller (NFS client) is responsible for calling nfsd_net_put and 44 * nfsd_file_put (via nfs_to_nfsd_file_put_local). 45 */ 46 static struct nfsd_file * 47 nfsd_open_local_fh(struct net *net, struct auth_domain *dom, 48 struct rpc_clnt *rpc_clnt, const struct cred *cred, 49 const struct nfs_fh *nfs_fh, struct nfsd_file __rcu **pnf, 50 const fmode_t fmode) 51 { 52 int mayflags = NFSD_MAY_LOCALIO; 53 struct svc_cred rq_cred; 54 struct svc_fh fh; 55 struct nfsd_file *localio; 56 __be32 beres; 57 58 if (nfs_fh->size > NFS4_FHSIZE) 59 return ERR_PTR(-EINVAL); 60 61 if (!nfsd_net_try_get(net)) 62 return ERR_PTR(-ENXIO); 63 64 rcu_read_lock(); 65 localio = nfsd_file_get(rcu_dereference(*pnf)); 66 rcu_read_unlock(); 67 if (localio) 68 return localio; 69 70 /* nfs_fh -> svc_fh */ 71 fh_init(&fh, NFS4_FHSIZE); 72 fh.fh_handle.fh_size = nfs_fh->size; 73 memcpy(fh.fh_handle.fh_raw, nfs_fh->data, nfs_fh->size); 74 75 if (fmode & FMODE_READ) 76 mayflags |= NFSD_MAY_READ; 77 if (fmode & FMODE_WRITE) 78 mayflags |= NFSD_MAY_WRITE; 79 80 svcauth_map_clnt_to_svc_cred_local(rpc_clnt, cred, &rq_cred); 81 82 beres = nfsd_file_acquire_local(net, &rq_cred, dom, 83 &fh, mayflags, &localio); 84 if (beres) 85 localio = ERR_PTR(nfs_stat_to_errno(be32_to_cpu(beres))); 86 87 fh_put(&fh); 88 if (rq_cred.cr_group_info) 89 put_group_info(rq_cred.cr_group_info); 90 91 if (!IS_ERR(localio)) { 92 struct nfsd_file *new; 93 if (!nfsd_net_try_get(net)) { 94 nfsd_file_put(localio); 95 nfsd_net_put(net); 96 return ERR_PTR(-ENXIO); 97 } 98 nfsd_file_get(localio); 99 again: 100 new = unrcu_pointer(cmpxchg(pnf, NULL, RCU_INITIALIZER(localio))); 101 if (new) { 102 /* Some other thread installed an nfsd_file */ 103 if (nfsd_file_get(new) == NULL) 104 goto again; 105 /* 106 * Drop the ref we were going to install (both file and 107 * net) and the one we were going to return (only file). 108 */ 109 nfsd_file_put(localio); 110 nfsd_net_put(net); 111 nfsd_file_put(localio); 112 localio = new; 113 } 114 } else 115 nfsd_net_put(net); 116 117 return localio; 118 } 119 120 static const struct nfsd_localio_operations nfsd_localio_ops = { 121 .nfsd_net_try_get = nfsd_net_try_get, 122 .nfsd_net_put = nfsd_net_put, 123 .nfsd_open_local_fh = nfsd_open_local_fh, 124 .nfsd_file_put_local = nfsd_file_put_local, 125 .nfsd_file_get_local = nfsd_file_get_local, 126 .nfsd_file_file = nfsd_file_file, 127 }; 128 129 void nfsd_localio_ops_init(void) 130 { 131 nfs_to = &nfsd_localio_ops; 132 } 133 134 /* 135 * UUID_IS_LOCAL XDR functions 136 */ 137 138 static __be32 localio_proc_null(struct svc_rqst *rqstp) 139 { 140 return rpc_success; 141 } 142 143 struct localio_uuidarg { 144 uuid_t uuid; 145 }; 146 147 static __be32 localio_proc_uuid_is_local(struct svc_rqst *rqstp) 148 { 149 struct localio_uuidarg *argp = rqstp->rq_argp; 150 struct net *net = SVC_NET(rqstp); 151 struct nfsd_net *nn = net_generic(net, nfsd_net_id); 152 153 nfs_uuid_is_local(&argp->uuid, &nn->local_clients, 154 &nn->local_clients_lock, 155 net, rqstp->rq_client, THIS_MODULE); 156 157 return rpc_success; 158 } 159 160 static bool localio_decode_uuidarg(struct svc_rqst *rqstp, 161 struct xdr_stream *xdr) 162 { 163 struct localio_uuidarg *argp = rqstp->rq_argp; 164 u8 uuid[UUID_SIZE]; 165 166 if (decode_opaque_fixed(xdr, uuid, UUID_SIZE)) 167 return false; 168 import_uuid(&argp->uuid, uuid); 169 170 return true; 171 } 172 173 static const struct svc_procedure localio_procedures1[] = { 174 [LOCALIOPROC_NULL] = { 175 .pc_func = localio_proc_null, 176 .pc_decode = nfssvc_decode_voidarg, 177 .pc_encode = nfssvc_encode_voidres, 178 .pc_argsize = sizeof(struct nfsd_voidargs), 179 .pc_ressize = sizeof(struct nfsd_voidres), 180 .pc_cachetype = RC_NOCACHE, 181 .pc_xdrressize = 0, 182 .pc_name = "NULL", 183 }, 184 [LOCALIOPROC_UUID_IS_LOCAL] = { 185 .pc_func = localio_proc_uuid_is_local, 186 .pc_decode = localio_decode_uuidarg, 187 .pc_encode = nfssvc_encode_voidres, 188 .pc_argsize = sizeof(struct localio_uuidarg), 189 .pc_argzero = sizeof(struct localio_uuidarg), 190 .pc_ressize = sizeof(struct nfsd_voidres), 191 .pc_cachetype = RC_NOCACHE, 192 .pc_name = "UUID_IS_LOCAL", 193 }, 194 }; 195 196 #define LOCALIO_NR_PROCEDURES ARRAY_SIZE(localio_procedures1) 197 static DEFINE_PER_CPU_ALIGNED(unsigned long, 198 localio_count[LOCALIO_NR_PROCEDURES]); 199 const struct svc_version localio_version1 = { 200 .vs_vers = 1, 201 .vs_nproc = LOCALIO_NR_PROCEDURES, 202 .vs_proc = localio_procedures1, 203 .vs_dispatch = nfsd_dispatch, 204 .vs_count = localio_count, 205 .vs_xdrsize = XDR_QUADLEN(UUID_SIZE), 206 .vs_hidden = true, 207 }; 208