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 static const struct nfsd_localio_operations nfsd_localio_ops = { 28 .nfsd_net_try_get = nfsd_net_try_get, 29 .nfsd_net_put = nfsd_net_put, 30 .nfsd_open_local_fh = nfsd_open_local_fh, 31 .nfsd_file_put_local = nfsd_file_put_local, 32 .nfsd_file_get = nfsd_file_get, 33 .nfsd_file_put = nfsd_file_put, 34 .nfsd_file_file = nfsd_file_file, 35 }; 36 37 void nfsd_localio_ops_init(void) 38 { 39 nfs_to = &nfsd_localio_ops; 40 } 41 42 /** 43 * nfsd_open_local_fh - lookup a local filehandle @nfs_fh and map to nfsd_file 44 * 45 * @net: 'struct net' to get the proper nfsd_net required for LOCALIO access 46 * @dom: 'struct auth_domain' required for LOCALIO access 47 * @rpc_clnt: rpc_clnt that the client established 48 * @cred: cred that the client established 49 * @nfs_fh: filehandle to lookup 50 * @fmode: fmode_t to use for open 51 * 52 * This function maps a local fh to a path on a local filesystem. 53 * This is useful when the nfs client has the local server mounted - it can 54 * avoid all the NFS overhead with reads, writes and commits. 55 * 56 * On successful return, returned nfsd_file will have its nf_net member 57 * set. Caller (NFS client) is responsible for calling nfsd_net_put and 58 * nfsd_file_put (via nfs_to_nfsd_file_put_local). 59 */ 60 struct nfsd_file * 61 nfsd_open_local_fh(struct net *net, struct auth_domain *dom, 62 struct rpc_clnt *rpc_clnt, const struct cred *cred, 63 const struct nfs_fh *nfs_fh, const fmode_t fmode) 64 { 65 int mayflags = NFSD_MAY_LOCALIO; 66 struct svc_cred rq_cred; 67 struct svc_fh fh; 68 struct nfsd_file *localio; 69 __be32 beres; 70 71 if (nfs_fh->size > NFS4_FHSIZE) 72 return ERR_PTR(-EINVAL); 73 74 /* nfs_fh -> svc_fh */ 75 fh_init(&fh, NFS4_FHSIZE); 76 fh.fh_handle.fh_size = nfs_fh->size; 77 memcpy(fh.fh_handle.fh_raw, nfs_fh->data, nfs_fh->size); 78 79 if (fmode & FMODE_READ) 80 mayflags |= NFSD_MAY_READ; 81 if (fmode & FMODE_WRITE) 82 mayflags |= NFSD_MAY_WRITE; 83 84 svcauth_map_clnt_to_svc_cred_local(rpc_clnt, cred, &rq_cred); 85 86 beres = nfsd_file_acquire_local(net, &rq_cred, dom, 87 &fh, mayflags, &localio); 88 if (beres) 89 localio = ERR_PTR(nfs_stat_to_errno(be32_to_cpu(beres))); 90 91 fh_put(&fh); 92 if (rq_cred.cr_group_info) 93 put_group_info(rq_cred.cr_group_info); 94 95 return localio; 96 } 97 EXPORT_SYMBOL_GPL(nfsd_open_local_fh); 98 99 /* 100 * UUID_IS_LOCAL XDR functions 101 */ 102 103 static __be32 localio_proc_null(struct svc_rqst *rqstp) 104 { 105 return rpc_success; 106 } 107 108 struct localio_uuidarg { 109 uuid_t uuid; 110 }; 111 112 static __be32 localio_proc_uuid_is_local(struct svc_rqst *rqstp) 113 { 114 struct localio_uuidarg *argp = rqstp->rq_argp; 115 struct net *net = SVC_NET(rqstp); 116 struct nfsd_net *nn = net_generic(net, nfsd_net_id); 117 118 nfs_uuid_is_local(&argp->uuid, &nn->local_clients, 119 &nn->local_clients_lock, 120 net, rqstp->rq_client, THIS_MODULE); 121 122 return rpc_success; 123 } 124 125 static bool localio_decode_uuidarg(struct svc_rqst *rqstp, 126 struct xdr_stream *xdr) 127 { 128 struct localio_uuidarg *argp = rqstp->rq_argp; 129 u8 uuid[UUID_SIZE]; 130 131 if (decode_opaque_fixed(xdr, uuid, UUID_SIZE)) 132 return false; 133 import_uuid(&argp->uuid, uuid); 134 135 return true; 136 } 137 138 static const struct svc_procedure localio_procedures1[] = { 139 [LOCALIOPROC_NULL] = { 140 .pc_func = localio_proc_null, 141 .pc_decode = nfssvc_decode_voidarg, 142 .pc_encode = nfssvc_encode_voidres, 143 .pc_argsize = sizeof(struct nfsd_voidargs), 144 .pc_ressize = sizeof(struct nfsd_voidres), 145 .pc_cachetype = RC_NOCACHE, 146 .pc_xdrressize = 0, 147 .pc_name = "NULL", 148 }, 149 [LOCALIOPROC_UUID_IS_LOCAL] = { 150 .pc_func = localio_proc_uuid_is_local, 151 .pc_decode = localio_decode_uuidarg, 152 .pc_encode = nfssvc_encode_voidres, 153 .pc_argsize = sizeof(struct localio_uuidarg), 154 .pc_argzero = sizeof(struct localio_uuidarg), 155 .pc_ressize = sizeof(struct nfsd_voidres), 156 .pc_cachetype = RC_NOCACHE, 157 .pc_name = "UUID_IS_LOCAL", 158 }, 159 }; 160 161 #define LOCALIO_NR_PROCEDURES ARRAY_SIZE(localio_procedures1) 162 static DEFINE_PER_CPU_ALIGNED(unsigned long, 163 localio_count[LOCALIO_NR_PROCEDURES]); 164 const struct svc_version localio_version1 = { 165 .vs_vers = 1, 166 .vs_nproc = LOCALIO_NR_PROCEDURES, 167 .vs_proc = localio_procedures1, 168 .vs_dispatch = nfsd_dispatch, 169 .vs_count = localio_count, 170 .vs_xdrsize = XDR_QUADLEN(UUID_SIZE), 171 .vs_hidden = true, 172 }; 173