lockd: add netlink control interface

The legacy rpc.nfsd tool will set the nlm_grace_period if the NFSv4
grace period is set. nfsdctl is missing this functionality, so add a new
netlink control interface for lockd that it can use. For now, it only
allows setting the grace period, and the tcp and udp listener ports.

lockd currently uses module parameters and sysctls for configuration, so
all of its settings are global. With this change, lockd now tracks these
values on a per-net-ns basis. It will only fall back to using the global
values if any of them are 0.

Finally, as a backward compatibility measure, if updating the nlm
settings in the init_net namespace, also update the legacy global
values to match.

Link: https://issues.redhat.com/browse/RHEL-71698
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
Jeff Layton 2025-01-08 16:00:15 -05:00 committed by Chuck Lever
parent fc2a169c56
commit 9a28ac1762
7 changed files with 253 additions and 7 deletions

View File

@ -0,0 +1,45 @@
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
name: lockd
protocol: genetlink
uapi-header: linux/lockd_netlink.h
doc: lockd configuration over generic netlink
attribute-sets:
-
name: server
attributes:
-
name: gracetime
type: u32
-
name: tcp-port
type: u16
-
name: udp-port
type: u16
operations:
list:
-
name: server-set
doc: set the lockd server parameters
attribute-set: server
flags: [ admin-perm ]
do:
request:
attributes:
- gracetime
- tcp-port
- udp-port
-
name: server-get
doc: get the lockd server parameters
attribute-set: server
do:
reply:
attributes:
- gracetime
- tcp-port
- udp-port

View File

@ -8,6 +8,6 @@ ccflags-y += -I$(src) # needed for trace events
obj-$(CONFIG_LOCKD) += lockd.o
lockd-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \
svcshare.o svcproc.o svcsubs.o mon.o trace.o xdr.o
svcshare.o svcproc.o svcsubs.o mon.o trace.o xdr.o netlink.o
lockd-$(CONFIG_LOCKD_V4) += clnt4xdr.o xdr4.o svc4proc.o
lockd-$(CONFIG_PROC_FS) += procfs.o

44
fs/lockd/netlink.c Normal file
View File

@ -0,0 +1,44 @@
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/lockd.yaml */
/* YNL-GEN kernel source */
#include <net/netlink.h>
#include <net/genetlink.h>
#include "netlink.h"
#include <uapi/linux/lockd_netlink.h>
/* LOCKD_CMD_SERVER_SET - do */
static const struct nla_policy lockd_server_set_nl_policy[LOCKD_A_SERVER_UDP_PORT + 1] = {
[LOCKD_A_SERVER_GRACETIME] = { .type = NLA_U32, },
[LOCKD_A_SERVER_TCP_PORT] = { .type = NLA_U16, },
[LOCKD_A_SERVER_UDP_PORT] = { .type = NLA_U16, },
};
/* Ops table for lockd */
static const struct genl_split_ops lockd_nl_ops[] = {
{
.cmd = LOCKD_CMD_SERVER_SET,
.doit = lockd_nl_server_set_doit,
.policy = lockd_server_set_nl_policy,
.maxattr = LOCKD_A_SERVER_UDP_PORT,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
.cmd = LOCKD_CMD_SERVER_GET,
.doit = lockd_nl_server_get_doit,
.flags = GENL_CMD_CAP_DO,
},
};
struct genl_family lockd_nl_family __ro_after_init = {
.name = LOCKD_FAMILY_NAME,
.version = LOCKD_FAMILY_VERSION,
.netnsok = true,
.parallel_ops = true,
.module = THIS_MODULE,
.split_ops = lockd_nl_ops,
.n_split_ops = ARRAY_SIZE(lockd_nl_ops),
};

19
fs/lockd/netlink.h Normal file
View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/lockd.yaml */
/* YNL-GEN kernel header */
#ifndef _LINUX_LOCKD_GEN_H
#define _LINUX_LOCKD_GEN_H
#include <net/netlink.h>
#include <net/genetlink.h>
#include <uapi/linux/lockd_netlink.h>
int lockd_nl_server_set_doit(struct sk_buff *skb, struct genl_info *info);
int lockd_nl_server_get_doit(struct sk_buff *skb, struct genl_info *info);
extern struct genl_family lockd_nl_family;
#endif /* _LINUX_LOCKD_GEN_H */

View File

@ -10,6 +10,9 @@ struct lockd_net {
unsigned int nlmsvc_users;
unsigned long next_gc;
unsigned long nrhosts;
u32 gracetime;
u16 tcp_port;
u16 udp_port;
struct delayed_work grace_period_end;
struct lock_manager lockd_manager;

View File

@ -41,6 +41,7 @@
#include "netns.h"
#include "procfs.h"
#include "netlink.h"
#define NLMDBG_FACILITY NLMDBG_SVC
#define LOCKD_BUFSIZE (1024 + NLMSVC_XDRSIZE)
@ -83,8 +84,14 @@ static const int nlm_port_min = 0, nlm_port_max = 65535;
static struct ctl_table_header * nlm_sysctl_table;
#endif
static unsigned long get_lockd_grace_period(void)
static unsigned long get_lockd_grace_period(struct net *net)
{
struct lockd_net *ln = net_generic(net, lockd_net_id);
/* Return the net-ns specific grace period, if there is one */
if (ln->gracetime)
return ln->gracetime * HZ;
/* Note: nlm_timeout should always be nonzero */
if (nlm_grace_period)
return roundup(nlm_grace_period, nlm_timeout) * HZ;
@ -103,7 +110,7 @@ static void grace_ender(struct work_struct *grace)
static void set_grace_period(struct net *net)
{
unsigned long grace_period = get_lockd_grace_period();
unsigned long grace_period = get_lockd_grace_period(net);
struct lockd_net *ln = net_generic(net, lockd_net_id);
locks_start_grace(net, &ln->lockd_manager);
@ -166,15 +173,16 @@ static int create_lockd_listener(struct svc_serv *serv, const char *name,
static int create_lockd_family(struct svc_serv *serv, struct net *net,
const int family, const struct cred *cred)
{
struct lockd_net *ln = net_generic(net, lockd_net_id);
int err;
err = create_lockd_listener(serv, "udp", net, family, nlm_udpport,
cred);
err = create_lockd_listener(serv, "udp", net, family,
ln->udp_port ? ln->udp_port : nlm_udpport, cred);
if (err < 0)
return err;
return create_lockd_listener(serv, "tcp", net, family, nlm_tcpport,
cred);
return create_lockd_listener(serv, "tcp", net, family,
ln->tcp_port ? ln->tcp_port : nlm_tcpport, cred);
}
/*
@ -588,6 +596,10 @@ static int __init init_nlm(void)
if (err)
goto err_pernet;
err = genl_register_family(&lockd_nl_family);
if (err)
goto err_netlink;
err = lockd_create_procfs();
if (err)
goto err_procfs;
@ -595,6 +607,8 @@ static int __init init_nlm(void)
return 0;
err_procfs:
genl_unregister_family(&lockd_nl_family);
err_netlink:
unregister_pernet_subsys(&lockd_net_ops);
err_pernet:
#ifdef CONFIG_SYSCTL
@ -608,6 +622,7 @@ static void __exit exit_nlm(void)
{
/* FIXME: delete all NLM clients */
nlm_shutdown_hosts();
genl_unregister_family(&lockd_nl_family);
lockd_remove_procfs();
unregister_pernet_subsys(&lockd_net_ops);
#ifdef CONFIG_SYSCTL
@ -710,3 +725,94 @@ static struct svc_program nlmsvc_program = {
.pg_init_request = svc_generic_init_request,
.pg_rpcbind_set = svc_generic_rpcbind_set,
};
/**
* lockd_nl_server_set_doit - set the lockd server parameters via netlink
* @skb: reply buffer
* @info: netlink metadata and command arguments
*
* This updates the per-net values. When updating the values in the init_net
* namespace, also update the "legacy" global values.
*
* Return 0 on success or a negative errno.
*/
int lockd_nl_server_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net *net = genl_info_net(info);
struct lockd_net *ln = net_generic(net, lockd_net_id);
const struct nlattr *attr;
if (GENL_REQ_ATTR_CHECK(info, LOCKD_A_SERVER_GRACETIME))
return -EINVAL;
if (info->attrs[LOCKD_A_SERVER_GRACETIME] ||
info->attrs[LOCKD_A_SERVER_TCP_PORT] ||
info->attrs[LOCKD_A_SERVER_UDP_PORT]) {
attr = info->attrs[LOCKD_A_SERVER_GRACETIME];
if (attr) {
u32 gracetime = nla_get_u32(attr);
if (gracetime > nlm_grace_period_max)
return -EINVAL;
ln->gracetime = gracetime;
if (net == &init_net)
nlm_grace_period = gracetime;
}
attr = info->attrs[LOCKD_A_SERVER_TCP_PORT];
if (attr) {
ln->tcp_port = nla_get_u16(attr);
if (net == &init_net)
nlm_tcpport = ln->tcp_port;
}
attr = info->attrs[LOCKD_A_SERVER_UDP_PORT];
if (attr) {
ln->udp_port = nla_get_u16(attr);
if (net == &init_net)
nlm_udpport = ln->udp_port;
}
}
return 0;
}
/**
* lockd_nl_server_get_doit - get lockd server parameters via netlink
* @skb: reply buffer
* @info: netlink metadata and command arguments
*
* Return 0 on success or a negative errno.
*/
int lockd_nl_server_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net *net = genl_info_net(info);
struct lockd_net *ln = net_generic(net, lockd_net_id);
void *hdr;
int err;
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = genlmsg_iput(skb, info);
if (!hdr) {
err = -EMSGSIZE;
goto err_free_msg;
}
err = nla_put_u32(skb, LOCKD_A_SERVER_GRACETIME, ln->gracetime) ||
nla_put_u16(skb, LOCKD_A_SERVER_TCP_PORT, ln->tcp_port) ||
nla_put_u16(skb, LOCKD_A_SERVER_UDP_PORT, ln->udp_port);
if (err)
goto err_free_msg;
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, info);
err_free_msg:
nlmsg_free(skb);
return err;
}

View File

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/lockd.yaml */
/* YNL-GEN uapi header */
#ifndef _UAPI_LINUX_LOCKD_NETLINK_H
#define _UAPI_LINUX_LOCKD_NETLINK_H
#define LOCKD_FAMILY_NAME "lockd"
#define LOCKD_FAMILY_VERSION 1
enum {
LOCKD_A_SERVER_GRACETIME = 1,
LOCKD_A_SERVER_TCP_PORT,
LOCKD_A_SERVER_UDP_PORT,
__LOCKD_A_SERVER_MAX,
LOCKD_A_SERVER_MAX = (__LOCKD_A_SERVER_MAX - 1)
};
enum {
LOCKD_CMD_SERVER_SET = 1,
LOCKD_CMD_SERVER_GET,
__LOCKD_CMD_MAX,
LOCKD_CMD_MAX = (__LOCKD_CMD_MAX - 1)
};
#endif /* _UAPI_LINUX_LOCKD_NETLINK_H */