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 * @nfp: 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 and the 107 * one we were going to return. 108 */ 109 nfsd_file_put(localio); 110 nfsd_file_put(localio); 111 localio = new; 112 } 113 } else 114 nfsd_net_put(net); 115 116 return localio; 117 } 118 119 static const struct nfsd_localio_operations nfsd_localio_ops = { 120 .nfsd_net_try_get = nfsd_net_try_get, 121 .nfsd_net_put = nfsd_net_put, 122 .nfsd_open_local_fh = nfsd_open_local_fh, 123 .nfsd_file_put_local = nfsd_file_put_local, 124 .nfsd_file_get_local = nfsd_file_get_local, 125 .nfsd_file_file = nfsd_file_file, 126 }; 127 128 void nfsd_localio_ops_init(void) 129 { 130 nfs_to = &nfsd_localio_ops; 131 } 132 133 /* 134 * UUID_IS_LOCAL XDR functions 135 */ 136 137 static __be32 localio_proc_null(struct svc_rqst *rqstp) 138 { 139 return rpc_success; 140 } 141 142 struct localio_uuidarg { 143 uuid_t uuid; 144 }; 145 146 static __be32 localio_proc_uuid_is_local(struct svc_rqst *rqstp) 147 { 148 struct localio_uuidarg *argp = rqstp->rq_argp; 149 struct net *net = SVC_NET(rqstp); 150 struct nfsd_net *nn = net_generic(net, nfsd_net_id); 151 152 nfs_uuid_is_local(&argp->uuid, &nn->local_clients, 153 &nn->local_clients_lock, 154 net, rqstp->rq_client, THIS_MODULE); 155 156 return rpc_success; 157 } 158 159 static bool localio_decode_uuidarg(struct svc_rqst *rqstp, 160 struct xdr_stream *xdr) 161 { 162 struct localio_uuidarg *argp = rqstp->rq_argp; 163 u8 uuid[UUID_SIZE]; 164 165 if (decode_opaque_fixed(xdr, uuid, UUID_SIZE)) 166 return false; 167 import_uuid(&argp->uuid, uuid); 168 169 return true; 170 } 171 172 static const struct svc_procedure localio_procedures1[] = { 173 [LOCALIOPROC_NULL] = { 174 .pc_func = localio_proc_null, 175 .pc_decode = nfssvc_decode_voidarg, 176 .pc_encode = nfssvc_encode_voidres, 177 .pc_argsize = sizeof(struct nfsd_voidargs), 178 .pc_ressize = sizeof(struct nfsd_voidres), 179 .pc_cachetype = RC_NOCACHE, 180 .pc_xdrressize = 0, 181 .pc_name = "NULL", 182 }, 183 [LOCALIOPROC_UUID_IS_LOCAL] = { 184 .pc_func = localio_proc_uuid_is_local, 185 .pc_decode = localio_decode_uuidarg, 186 .pc_encode = nfssvc_encode_voidres, 187 .pc_argsize = sizeof(struct localio_uuidarg), 188 .pc_argzero = sizeof(struct localio_uuidarg), 189 .pc_ressize = sizeof(struct nfsd_voidres), 190 .pc_cachetype = RC_NOCACHE, 191 .pc_name = "UUID_IS_LOCAL", 192 }, 193 }; 194 195 #define LOCALIO_NR_PROCEDURES ARRAY_SIZE(localio_procedures1) 196 static DEFINE_PER_CPU_ALIGNED(unsigned long, 197 localio_count[LOCALIO_NR_PROCEDURES]); 198 const struct svc_version localio_version1 = { 199 .vs_vers = 1, 200 .vs_nproc = LOCALIO_NR_PROCEDURES, 201 .vs_proc = localio_procedures1, 202 .vs_dispatch = nfsd_dispatch, 203 .vs_count = localio_count, 204 .vs_xdrsize = XDR_QUADLEN(UUID_SIZE), 205 .vs_hidden = true, 206 }; 207