File Index
Files and directories
Sign in
FILE

localio.c

File preview with syntax highlighting.

linux-6.19.14/fs/nfsd/localio.c C
  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 */
 46static struct nfsd_file *
 47nfsd_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
120static void nfsd_file_dio_alignment(struct nfsd_file *nf,
121				    u32 *nf_dio_mem_align,
122				    u32 *nf_dio_offset_align,
123				    u32 *nf_dio_read_offset_align)
124{
125	*nf_dio_mem_align = nf->nf_dio_mem_align;
126	*nf_dio_offset_align = nf->nf_dio_offset_align;
127	*nf_dio_read_offset_align = nf->nf_dio_read_offset_align;
128}
129
130static const struct nfsd_localio_operations nfsd_localio_ops = {
131	.nfsd_net_try_get  = nfsd_net_try_get,
132	.nfsd_net_put  = nfsd_net_put,
133	.nfsd_open_local_fh = nfsd_open_local_fh,
134	.nfsd_file_put_local = nfsd_file_put_local,
135	.nfsd_file_file = nfsd_file_file,
136	.nfsd_file_dio_alignment = nfsd_file_dio_alignment,
137};
138
139void nfsd_localio_ops_init(void)
140{
141	nfs_to = &nfsd_localio_ops;
142}
143
144/*
145 * UUID_IS_LOCAL XDR functions
146 */
147
148static __be32 localio_proc_null(struct svc_rqst *rqstp)
149{
150	return rpc_success;
151}
152
153struct localio_uuidarg {
154	uuid_t			uuid;
155};
156
157static __be32 localio_proc_uuid_is_local(struct svc_rqst *rqstp)
158{
159	struct localio_uuidarg *argp = rqstp->rq_argp;
160	struct net *net = SVC_NET(rqstp);
161	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
162
163	nfs_uuid_is_local(&argp->uuid, &nn->local_clients,
164			  &nn->local_clients_lock,
165			  net, rqstp->rq_client, THIS_MODULE);
166
167	return rpc_success;
168}
169
170static bool localio_decode_uuidarg(struct svc_rqst *rqstp,
171				   struct xdr_stream *xdr)
172{
173	struct localio_uuidarg *argp = rqstp->rq_argp;
174	u8 uuid[UUID_SIZE];
175
176	if (decode_opaque_fixed(xdr, uuid, UUID_SIZE))
177		return false;
178	import_uuid(&argp->uuid, uuid);
179
180	return true;
181}
182
183static const struct svc_procedure localio_procedures1[] = {
184	[LOCALIOPROC_NULL] = {
185		.pc_func = localio_proc_null,
186		.pc_decode = nfssvc_decode_voidarg,
187		.pc_encode = nfssvc_encode_voidres,
188		.pc_argsize = sizeof(struct nfsd_voidargs),
189		.pc_ressize = sizeof(struct nfsd_voidres),
190		.pc_cachetype = RC_NOCACHE,
191		.pc_xdrressize = 0,
192		.pc_name = "NULL",
193	},
194	[LOCALIOPROC_UUID_IS_LOCAL] = {
195		.pc_func = localio_proc_uuid_is_local,
196		.pc_decode = localio_decode_uuidarg,
197		.pc_encode = nfssvc_encode_voidres,
198		.pc_argsize = sizeof(struct localio_uuidarg),
199		.pc_argzero = sizeof(struct localio_uuidarg),
200		.pc_ressize = sizeof(struct nfsd_voidres),
201		.pc_cachetype = RC_NOCACHE,
202		.pc_name = "UUID_IS_LOCAL",
203	},
204};
205
206#define LOCALIO_NR_PROCEDURES ARRAY_SIZE(localio_procedures1)
207static DEFINE_PER_CPU_ALIGNED(unsigned long,
208			      localio_count[LOCALIO_NR_PROCEDURES]);
209const struct svc_version localio_version1 = {
210	.vs_vers	= 1,
211	.vs_nproc	= LOCALIO_NR_PROCEDURES,
212	.vs_proc	= localio_procedures1,
213	.vs_dispatch	= nfsd_dispatch,
214	.vs_count	= localio_count,
215	.vs_xdrsize	= XDR_QUADLEN(UUID_SIZE),
216	.vs_hidden	= true,
217};
~/ Read-only file preview.