nfs_common: add NFS LOCALIO auxiliary protocol enablement
fs/nfs_common/nfslocalio.c provides interfaces that enable an NFS
client to generate a nonce (single-use UUID) and associated nfs_uuid_t
struct, register it with nfs_common for subsequent lookup and
verification by the NFS server and if matched the NFS server populates
members in the nfs_uuid_t struct.
nfs_common's nfs_uuids list is the basis for localio enablement, as
such it has members that point to nfsd memory for direct use by the
client (e.g. 'net' is the server's network namespace, through it the
client can access nn->nfsd_serv).
This commit also provides the base nfs_uuid_t interfaces to allow
proper net namespace refcounting for the LOCALIO use case.
CONFIG_NFS_LOCALIO controls the nfs_common, NFS server and NFS client
enablement for LOCALIO. If both NFS_FS=m and NFSD=m then
NFS_COMMON_LOCALIO_SUPPORT=m and nfs_localio.ko is built (and provides
nfs_common's LOCALIO support).
# lsmod | grep nfs_localio
nfs_localio 12288 2 nfsd,nfs
sunrpc 745472 35 nfs_localio,nfsd,auth_rpcgss,lockd,nfsv3,nfs
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Co-developed-by: NeilBrown <neilb@suse.de>
Signed-off-by: NeilBrown <neilb@suse.de>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
2024-09-05 15:09:48 -04:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com>
|
|
|
|
* Copyright (C) 2024 NeilBrown <neilb@suse.de>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/rculist.h>
|
|
|
|
#include <linux/nfslocalio.h>
|
|
|
|
#include <net/netns/generic.h>
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_DESCRIPTION("NFS localio protocol bypass support");
|
|
|
|
|
|
|
|
static DEFINE_SPINLOCK(nfs_uuid_lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Global list of nfs_uuid_t instances
|
|
|
|
* that is protected by nfs_uuid_lock.
|
|
|
|
*/
|
|
|
|
static LIST_HEAD(nfs_uuids);
|
|
|
|
|
|
|
|
void nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
|
|
|
|
{
|
|
|
|
nfs_uuid->net = NULL;
|
|
|
|
nfs_uuid->dom = NULL;
|
|
|
|
uuid_gen(&nfs_uuid->uuid);
|
|
|
|
|
|
|
|
spin_lock(&nfs_uuid_lock);
|
|
|
|
list_add_tail_rcu(&nfs_uuid->list, &nfs_uuids);
|
|
|
|
spin_unlock(&nfs_uuid_lock);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs_uuid_begin);
|
|
|
|
|
|
|
|
void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
|
|
|
|
{
|
|
|
|
if (nfs_uuid->net == NULL) {
|
|
|
|
spin_lock(&nfs_uuid_lock);
|
|
|
|
list_del_init(&nfs_uuid->list);
|
|
|
|
spin_unlock(&nfs_uuid_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs_uuid_end);
|
|
|
|
|
|
|
|
static nfs_uuid_t * nfs_uuid_lookup_locked(const uuid_t *uuid)
|
|
|
|
{
|
|
|
|
nfs_uuid_t *nfs_uuid;
|
|
|
|
|
|
|
|
list_for_each_entry(nfs_uuid, &nfs_uuids, list)
|
|
|
|
if (uuid_equal(&nfs_uuid->uuid, uuid))
|
|
|
|
return nfs_uuid;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct module *nfsd_mod;
|
|
|
|
|
|
|
|
void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list,
|
|
|
|
struct net *net, struct auth_domain *dom,
|
|
|
|
struct module *mod)
|
|
|
|
{
|
|
|
|
nfs_uuid_t *nfs_uuid;
|
|
|
|
|
|
|
|
spin_lock(&nfs_uuid_lock);
|
|
|
|
nfs_uuid = nfs_uuid_lookup_locked(uuid);
|
|
|
|
if (nfs_uuid) {
|
|
|
|
kref_get(&dom->ref);
|
|
|
|
nfs_uuid->dom = dom;
|
|
|
|
/*
|
|
|
|
* We don't hold a ref on the net, but instead put
|
|
|
|
* ourselves on a list so the net pointer can be
|
|
|
|
* invalidated.
|
|
|
|
*/
|
|
|
|
list_move(&nfs_uuid->list, list);
|
2024-09-05 15:09:49 -04:00
|
|
|
rcu_assign_pointer(nfs_uuid->net, net);
|
nfs_common: add NFS LOCALIO auxiliary protocol enablement
fs/nfs_common/nfslocalio.c provides interfaces that enable an NFS
client to generate a nonce (single-use UUID) and associated nfs_uuid_t
struct, register it with nfs_common for subsequent lookup and
verification by the NFS server and if matched the NFS server populates
members in the nfs_uuid_t struct.
nfs_common's nfs_uuids list is the basis for localio enablement, as
such it has members that point to nfsd memory for direct use by the
client (e.g. 'net' is the server's network namespace, through it the
client can access nn->nfsd_serv).
This commit also provides the base nfs_uuid_t interfaces to allow
proper net namespace refcounting for the LOCALIO use case.
CONFIG_NFS_LOCALIO controls the nfs_common, NFS server and NFS client
enablement for LOCALIO. If both NFS_FS=m and NFSD=m then
NFS_COMMON_LOCALIO_SUPPORT=m and nfs_localio.ko is built (and provides
nfs_common's LOCALIO support).
# lsmod | grep nfs_localio
nfs_localio 12288 2 nfsd,nfs
sunrpc 745472 35 nfs_localio,nfsd,auth_rpcgss,lockd,nfsv3,nfs
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Co-developed-by: NeilBrown <neilb@suse.de>
Signed-off-by: NeilBrown <neilb@suse.de>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
2024-09-05 15:09:48 -04:00
|
|
|
|
|
|
|
__module_get(mod);
|
|
|
|
nfsd_mod = mod;
|
|
|
|
}
|
|
|
|
spin_unlock(&nfs_uuid_lock);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
|
|
|
|
|
|
|
|
static void nfs_uuid_put_locked(nfs_uuid_t *nfs_uuid)
|
|
|
|
{
|
|
|
|
if (nfs_uuid->net) {
|
|
|
|
module_put(nfsd_mod);
|
|
|
|
nfs_uuid->net = NULL;
|
|
|
|
}
|
|
|
|
if (nfs_uuid->dom) {
|
|
|
|
auth_domain_put(nfs_uuid->dom);
|
|
|
|
nfs_uuid->dom = NULL;
|
|
|
|
}
|
|
|
|
list_del_init(&nfs_uuid->list);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfs_uuid_invalidate_clients(struct list_head *list)
|
|
|
|
{
|
|
|
|
nfs_uuid_t *nfs_uuid, *tmp;
|
|
|
|
|
|
|
|
spin_lock(&nfs_uuid_lock);
|
|
|
|
list_for_each_entry_safe(nfs_uuid, tmp, list, list)
|
|
|
|
nfs_uuid_put_locked(nfs_uuid);
|
|
|
|
spin_unlock(&nfs_uuid_lock);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_clients);
|
|
|
|
|
|
|
|
void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid)
|
|
|
|
{
|
|
|
|
if (nfs_uuid->net) {
|
|
|
|
spin_lock(&nfs_uuid_lock);
|
|
|
|
nfs_uuid_put_locked(nfs_uuid);
|
|
|
|
spin_unlock(&nfs_uuid_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_one_client);
|
2024-09-05 15:09:49 -04:00
|
|
|
|
2024-09-05 15:09:53 -04:00
|
|
|
struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
|
|
|
|
struct rpc_clnt *rpc_clnt, const struct cred *cred,
|
|
|
|
const struct nfs_fh *nfs_fh, const fmode_t fmode)
|
|
|
|
{
|
|
|
|
struct net *net;
|
|
|
|
struct nfsd_file *localio;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Not running in nfsd context, so must safely get reference on nfsd_serv.
|
|
|
|
* But the server may already be shutting down, if so disallow new localio.
|
|
|
|
* uuid->net is NOT a counted reference, but rcu_read_lock() ensures that
|
|
|
|
* if uuid->net is not NULL, then calling nfsd_serv_try_get() is safe
|
|
|
|
* and if it succeeds we will have an implied reference to the net.
|
|
|
|
*
|
|
|
|
* Otherwise NFS may not have ref on NFSD and therefore cannot safely
|
|
|
|
* make 'nfs_to' calls.
|
|
|
|
*/
|
|
|
|
rcu_read_lock();
|
|
|
|
net = rcu_dereference(uuid->net);
|
|
|
|
if (!net || !nfs_to->nfsd_serv_try_get(net)) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return ERR_PTR(-ENXIO);
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
/* We have an implied reference to net thanks to nfsd_serv_try_get */
|
|
|
|
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt,
|
|
|
|
cred, nfs_fh, fmode);
|
2024-10-03 15:34:58 -04:00
|
|
|
if (IS_ERR(localio)) {
|
|
|
|
rcu_read_lock();
|
2024-09-05 15:09:53 -04:00
|
|
|
nfs_to->nfsd_serv_put(net);
|
2024-10-03 15:34:58 -04:00
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
2024-09-05 15:09:53 -04:00
|
|
|
return localio;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs_open_local_fh);
|
|
|
|
|
2024-09-05 15:09:49 -04:00
|
|
|
/*
|
|
|
|
* The NFS LOCALIO code needs to call into NFSD using various symbols,
|
|
|
|
* but cannot be statically linked, because that will make the NFS
|
|
|
|
* module always depend on the NFSD module.
|
|
|
|
*
|
|
|
|
* 'nfs_to' provides NFS access to NFSD functions needed for LOCALIO,
|
|
|
|
* its lifetime is tightly coupled to the NFSD module and will always
|
|
|
|
* be available to NFS LOCALIO because any successful client<->server
|
|
|
|
* LOCALIO handshake results in a reference on the NFSD module (above),
|
|
|
|
* so NFS implicitly holds a reference to the NFSD module and its
|
|
|
|
* functions in the 'nfs_to' nfsd_localio_operations cannot disappear.
|
|
|
|
*
|
|
|
|
* If the last NFS client using LOCALIO disconnects (and its reference
|
|
|
|
* on NFSD dropped) then NFSD could be unloaded, resulting in 'nfs_to'
|
|
|
|
* functions being invalid pointers. But if NFSD isn't loaded then NFS
|
|
|
|
* will not be able to handshake with NFSD and will have no cause to
|
|
|
|
* try to call 'nfs_to' function pointers. If/when NFSD is reloaded it
|
|
|
|
* will reinitialize the 'nfs_to' function pointers and make LOCALIO
|
|
|
|
* possible.
|
|
|
|
*/
|
|
|
|
const struct nfsd_localio_operations *nfs_to;
|
|
|
|
EXPORT_SYMBOL_GPL(nfs_to);
|