mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 12:21:22 -05:00
Pull nfsd updates from Chuck Lever:
"Mike Snitzer has prototyped a mechanism for disabling I/O caching in
NFSD. This is introduced in v6.18 as an experimental feature. This
enables scaling NFSD in /both/ directions:
- NFS service can be supported on systems with small memory
footprints, such as low-cost cloud instances
- Large NFS workloads will be less likely to force the eviction of
server-local activity, helping it avoid thrashing
Jeff Layton contributed a number of fixes to the new attribute
delegation implementation (based on a pending Internet RFC) that we
hope will make attribute delegation reliable enough to enable by
default, as it is on the Linux NFS client.
The remaining patches in this pull request are clean-ups and minor
optimizations. Many thanks to the contributors, reviewers, testers,
and bug reporters who participated during the v6.18 NFSD development
cycle"
* tag 'nfsd-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (42 commits)
nfsd: discard nfserr_dropit
SUNRPC: Make RPCSEC_GSS_KRB5 select CRYPTO instead of depending on it
NFSD: Add io_cache_{read,write} controls to debugfs
NFSD: Do the grace period check in ->proc_layoutget
nfsd: delete unnecessary NULL check in __fh_verify()
NFSD: Allow layoutcommit during grace period
NFSD: Disallow layoutget during grace period
sunrpc: fix "occurence"->"occurrence"
nfsd: Don't force CRYPTO_LIB_SHA256 to be built-in
nfsd: nfserr_jukebox in nlm_fopen should lead to a retry
NFSD: Reduce DRC bucket size
NFSD: Delay adding new entries to LRU
SUNRPC: Move the svc_rpcb_cleanup() call sites
NFS: Remove rpcbind cleanup for NFSv4.0 callback
nfsd: unregister with rpcbind when deleting a transport
NFSD: Drop redundant conversion to bool
sunrpc: eliminate return pointer in svc_tcp_sendmsg()
sunrpc: fix pr_notice in svc_tcp_sendto() to show correct length
nfsd: decouple the xprtsec policy check from check_nfsd_access()
NFSD: Fix destination buffer size in nfsd4_ssc_setup_dul()
...
218 lines
5.9 KiB
C
218 lines
5.9 KiB
C
// 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,
|
|
};
|