xref: /linux/fs/nfsd/localio.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
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 		rcu_read_lock();
101 		new = unrcu_pointer(cmpxchg(pnf, NULL, RCU_INITIALIZER(localio)));
102 		if (new) {
103 			/* Some other thread installed an nfsd_file */
104 			if (nfsd_file_get(new) == NULL) {
105 				rcu_read_unlock();
106 				goto again;
107 			}
108 			rcu_read_unlock();
109 			/*
110 			 * Drop the ref we were going to install (both file and
111 			 * net) and the one we were going to return (only file).
112 			 */
113 			nfsd_file_put(localio);
114 			nfsd_net_put(net);
115 			nfsd_file_put(localio);
116 			localio = new;
117 		} else {
118 			rcu_read_unlock();
119 		}
120 	} else
121 		nfsd_net_put(net);
122 
123 	return localio;
124 }
125 
126 static void nfsd_file_dio_alignment(struct nfsd_file *nf,
127 				    u32 *nf_dio_mem_align,
128 				    u32 *nf_dio_offset_align,
129 				    u32 *nf_dio_read_offset_align)
130 {
131 	*nf_dio_mem_align = nf->nf_dio_mem_align;
132 	*nf_dio_offset_align = nf->nf_dio_offset_align;
133 	*nf_dio_read_offset_align = nf->nf_dio_read_offset_align;
134 }
135 
136 static const struct nfsd_localio_operations nfsd_localio_ops = {
137 	.nfsd_net_try_get  = nfsd_net_try_get,
138 	.nfsd_net_put  = nfsd_net_put,
139 	.nfsd_open_local_fh = nfsd_open_local_fh,
140 	.nfsd_file_put_local = nfsd_file_put_local,
141 	.nfsd_file_file = nfsd_file_file,
142 	.nfsd_file_dio_alignment = nfsd_file_dio_alignment,
143 };
144 
145 void nfsd_localio_ops_init(void)
146 {
147 	nfs_to = &nfsd_localio_ops;
148 }
149 
150 /*
151  * UUID_IS_LOCAL XDR functions
152  */
153 
154 static __be32 localio_proc_null(struct svc_rqst *rqstp)
155 {
156 	return rpc_success;
157 }
158 
159 struct localio_uuidarg {
160 	uuid_t			uuid;
161 };
162 
163 static __be32 localio_proc_uuid_is_local(struct svc_rqst *rqstp)
164 {
165 	struct localio_uuidarg *argp = rqstp->rq_argp;
166 	struct net *net = SVC_NET(rqstp);
167 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
168 
169 	nfs_uuid_is_local(&argp->uuid, &nn->local_clients,
170 			  &nn->local_clients_lock,
171 			  net, rqstp->rq_client, THIS_MODULE);
172 
173 	return rpc_success;
174 }
175 
176 static bool localio_decode_uuidarg(struct svc_rqst *rqstp,
177 				   struct xdr_stream *xdr)
178 {
179 	struct localio_uuidarg *argp = rqstp->rq_argp;
180 	u8 uuid[UUID_SIZE];
181 
182 	if (decode_opaque_fixed(xdr, uuid, UUID_SIZE))
183 		return false;
184 	import_uuid(&argp->uuid, uuid);
185 
186 	return true;
187 }
188 
189 static const struct svc_procedure localio_procedures1[] = {
190 	[LOCALIOPROC_NULL] = {
191 		.pc_func = localio_proc_null,
192 		.pc_decode = nfssvc_decode_voidarg,
193 		.pc_encode = nfssvc_encode_voidres,
194 		.pc_argsize = sizeof(struct nfsd_voidargs),
195 		.pc_ressize = sizeof(struct nfsd_voidres),
196 		.pc_cachetype = RC_NOCACHE,
197 		.pc_xdrressize = 0,
198 		.pc_name = "NULL",
199 	},
200 	[LOCALIOPROC_UUID_IS_LOCAL] = {
201 		.pc_func = localio_proc_uuid_is_local,
202 		.pc_decode = localio_decode_uuidarg,
203 		.pc_encode = nfssvc_encode_voidres,
204 		.pc_argsize = sizeof(struct localio_uuidarg),
205 		.pc_argzero = sizeof(struct localio_uuidarg),
206 		.pc_ressize = sizeof(struct nfsd_voidres),
207 		.pc_cachetype = RC_NOCACHE,
208 		.pc_name = "UUID_IS_LOCAL",
209 	},
210 };
211 
212 #define LOCALIO_NR_PROCEDURES ARRAY_SIZE(localio_procedures1)
213 const struct svc_version localio_version1 = {
214 	.vs_vers	= 1,
215 	.vs_nproc	= LOCALIO_NR_PROCEDURES,
216 	.vs_proc	= localio_procedures1,
217 	.vs_dispatch	= nfsd_dispatch,
218 	.vs_xdrsize	= XDR_QUADLEN(UUID_SIZE),
219 	.vs_hidden	= true,
220 };
221