| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * NFS server support for local clients to bypass network stack
- *
- * Copyright (C) 2014 Weston Andros Adamson <dros@primarydata.com>
- * Copyright (C) 2019 Trond Myklebust <trond.myklebust@hammerspace.com>
- * Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com>
- * Copyright (C) 2024 NeilBrown <neilb@suse.de>
- */
- #include <linux/exportfs.h>
- #include <linux/sunrpc/svcauth.h>
- #include <linux/sunrpc/clnt.h>
- #include <linux/nfs.h>
- #include <linux/nfs_common.h>
- #include <linux/nfslocalio.h>
- #include <linux/nfs_fs.h>
- #include <linux/nfs_xdr.h>
- #include <linux/string.h>
- #include "nfsd.h"
- #include "vfs.h"
- #include "netns.h"
- #include "filecache.h"
- #include "cache.h"
- /**
- * nfsd_open_local_fh - lookup a local filehandle @nfs_fh and map to nfsd_file
- *
- * @net: 'struct net' to get the proper nfsd_net required for LOCALIO access
- * @dom: 'struct auth_domain' required for LOCALIO access
- * @rpc_clnt: rpc_clnt that the client established
- * @cred: cred that the client established
- * @nfs_fh: filehandle to lookup
- * @pnf: place to find the nfsd_file, or store it if it was non-NULL
- * @fmode: fmode_t to use for open
- *
- * This function maps a local fh to a path on a local filesystem.
- * This is useful when the nfs client has the local server mounted - it can
- * avoid all the NFS overhead with reads, writes and commits.
- *
- * On successful return, returned nfsd_file will have its nf_net member
- * set. Caller (NFS client) is responsible for calling nfsd_net_put and
- * nfsd_file_put (via nfs_to_nfsd_file_put_local).
- */
- static struct nfsd_file *
- nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
- struct rpc_clnt *rpc_clnt, const struct cred *cred,
- const struct nfs_fh *nfs_fh, struct nfsd_file __rcu **pnf,
- const fmode_t fmode)
- {
- int mayflags = NFSD_MAY_LOCALIO;
- struct svc_cred rq_cred;
- struct svc_fh fh;
- struct nfsd_file *localio;
- __be32 beres;
- if (nfs_fh->size > NFS4_FHSIZE)
- return ERR_PTR(-EINVAL);
- if (!nfsd_net_try_get(net))
- return ERR_PTR(-ENXIO);
- rcu_read_lock();
- localio = nfsd_file_get(rcu_dereference(*pnf));
- rcu_read_unlock();
- if (localio)
- return localio;
- /* nfs_fh -> svc_fh */
- fh_init(&fh, NFS4_FHSIZE);
- fh.fh_handle.fh_size = nfs_fh->size;
- memcpy(fh.fh_handle.fh_raw, nfs_fh->data, nfs_fh->size);
- if (fmode & FMODE_READ)
- mayflags |= NFSD_MAY_READ;
- if (fmode & FMODE_WRITE)
- mayflags |= NFSD_MAY_WRITE;
- svcauth_map_clnt_to_svc_cred_local(rpc_clnt, cred, &rq_cred);
- beres = nfsd_file_acquire_local(net, &rq_cred, dom,
- &fh, mayflags, &localio);
- if (beres)
- localio = ERR_PTR(nfs_stat_to_errno(be32_to_cpu(beres)));
- fh_put(&fh);
- if (rq_cred.cr_group_info)
- put_group_info(rq_cred.cr_group_info);
- if (!IS_ERR(localio)) {
- struct nfsd_file *new;
- if (!nfsd_net_try_get(net)) {
- nfsd_file_put(localio);
- nfsd_net_put(net);
- return ERR_PTR(-ENXIO);
- }
- nfsd_file_get(localio);
- again:
- new = unrcu_pointer(cmpxchg(pnf, NULL, RCU_INITIALIZER(localio)));
- if (new) {
- /* Some other thread installed an nfsd_file */
- if (nfsd_file_get(new) == NULL)
- goto again;
- /*
- * Drop the ref we were going to install (both file and
- * net) and the one we were going to return (only file).
- */
- nfsd_file_put(localio);
- nfsd_net_put(net);
- nfsd_file_put(localio);
- localio = new;
- }
- } else
- nfsd_net_put(net);
- return localio;
- }
- static void nfsd_file_dio_alignment(struct nfsd_file *nf,
- u32 *nf_dio_mem_align,
- u32 *nf_dio_offset_align,
- u32 *nf_dio_read_offset_align)
- {
- *nf_dio_mem_align = nf->nf_dio_mem_align;
- *nf_dio_offset_align = nf->nf_dio_offset_align;
- *nf_dio_read_offset_align = nf->nf_dio_read_offset_align;
- }
- static const struct nfsd_localio_operations nfsd_localio_ops = {
- .nfsd_net_try_get = nfsd_net_try_get,
- .nfsd_net_put = nfsd_net_put,
- .nfsd_open_local_fh = nfsd_open_local_fh,
- .nfsd_file_put_local = nfsd_file_put_local,
- .nfsd_file_file = nfsd_file_file,
- .nfsd_file_dio_alignment = nfsd_file_dio_alignment,
- };
- void nfsd_localio_ops_init(void)
- {
- nfs_to = &nfsd_localio_ops;
- }
- /*
- * UUID_IS_LOCAL XDR functions
- */
- static __be32 localio_proc_null(struct svc_rqst *rqstp)
- {
- return rpc_success;
- }
- struct localio_uuidarg {
- uuid_t uuid;
- };
- static __be32 localio_proc_uuid_is_local(struct svc_rqst *rqstp)
- {
- struct localio_uuidarg *argp = rqstp->rq_argp;
- struct net *net = SVC_NET(rqstp);
- struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- nfs_uuid_is_local(&argp->uuid, &nn->local_clients,
- &nn->local_clients_lock,
- net, rqstp->rq_client, THIS_MODULE);
- return rpc_success;
- }
- static bool localio_decode_uuidarg(struct svc_rqst *rqstp,
- struct xdr_stream *xdr)
- {
- struct localio_uuidarg *argp = rqstp->rq_argp;
- u8 uuid[UUID_SIZE];
- if (decode_opaque_fixed(xdr, uuid, UUID_SIZE))
- return false;
- import_uuid(&argp->uuid, uuid);
- return true;
- }
- static const struct svc_procedure localio_procedures1[] = {
- [LOCALIOPROC_NULL] = {
- .pc_func = localio_proc_null,
- .pc_decode = nfssvc_decode_voidarg,
- .pc_encode = nfssvc_encode_voidres,
- .pc_argsize = sizeof(struct nfsd_voidargs),
- .pc_ressize = sizeof(struct nfsd_voidres),
- .pc_cachetype = RC_NOCACHE,
- .pc_xdrressize = 0,
- .pc_name = "NULL",
- },
- [LOCALIOPROC_UUID_IS_LOCAL] = {
- .pc_func = localio_proc_uuid_is_local,
- .pc_decode = localio_decode_uuidarg,
- .pc_encode = nfssvc_encode_voidres,
- .pc_argsize = sizeof(struct localio_uuidarg),
- .pc_argzero = sizeof(struct localio_uuidarg),
- .pc_ressize = sizeof(struct nfsd_voidres),
- .pc_cachetype = RC_NOCACHE,
- .pc_name = "UUID_IS_LOCAL",
- },
- };
- #define LOCALIO_NR_PROCEDURES ARRAY_SIZE(localio_procedures1)
- static DEFINE_PER_CPU_ALIGNED(unsigned long,
- localio_count[LOCALIO_NR_PROCEDURES]);
- const struct svc_version localio_version1 = {
- .vs_vers = 1,
- .vs_nproc = LOCALIO_NR_PROCEDURES,
- .vs_proc = localio_procedures1,
- .vs_dispatch = nfsd_dispatch,
- .vs_count = localio_count,
- .vs_xdrsize = XDR_QUADLEN(UUID_SIZE),
- .vs_hidden = true,
- };
|