NFSD: add listener-{set,get} netlink command
Introduce write_ports netlink command. For listener-set, userspace is expected to provide a NFS listeners list it wants enabled. All other sockets will be closed. Reviewed-by: Jeff Layton <jlayton@kernel.org> Co-developed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
parent
cf619507ae
commit
16a4711774
5 changed files with 296 additions and 0 deletions
|
@ -98,6 +98,23 @@ attribute-sets:
|
|||
type: nest
|
||||
nested-attributes: version
|
||||
multi-attr: true
|
||||
-
|
||||
name: sock
|
||||
attributes:
|
||||
-
|
||||
name: addr
|
||||
type: binary
|
||||
-
|
||||
name: transport-name
|
||||
type: string
|
||||
-
|
||||
name: server-sock
|
||||
attributes:
|
||||
-
|
||||
name: addr
|
||||
type: nest
|
||||
nested-attributes: sock
|
||||
multi-attr: true
|
||||
|
||||
operations:
|
||||
list:
|
||||
|
@ -163,3 +180,20 @@ operations:
|
|||
reply:
|
||||
attributes:
|
||||
- version
|
||||
-
|
||||
name: listener-set
|
||||
doc: set nfs running sockets
|
||||
attribute-set: server-sock
|
||||
flags: [ admin-perm ]
|
||||
do:
|
||||
request:
|
||||
attributes:
|
||||
- addr
|
||||
-
|
||||
name: listener-get
|
||||
doc: get nfs running listeners
|
||||
attribute-set: server-sock
|
||||
do:
|
||||
reply:
|
||||
attributes:
|
||||
- addr
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
#include <uapi/linux/nfsd_netlink.h>
|
||||
|
||||
/* Common nested types */
|
||||
const struct nla_policy nfsd_sock_nl_policy[NFSD_A_SOCK_TRANSPORT_NAME + 1] = {
|
||||
[NFSD_A_SOCK_ADDR] = { .type = NLA_BINARY, },
|
||||
[NFSD_A_SOCK_TRANSPORT_NAME] = { .type = NLA_NUL_STRING, },
|
||||
};
|
||||
|
||||
const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1] = {
|
||||
[NFSD_A_VERSION_MAJOR] = { .type = NLA_U32, },
|
||||
[NFSD_A_VERSION_MINOR] = { .type = NLA_U32, },
|
||||
|
@ -30,6 +35,11 @@ static const struct nla_policy nfsd_version_set_nl_policy[NFSD_A_SERVER_PROTO_VE
|
|||
[NFSD_A_SERVER_PROTO_VERSION] = NLA_POLICY_NESTED(nfsd_version_nl_policy),
|
||||
};
|
||||
|
||||
/* NFSD_CMD_LISTENER_SET - do */
|
||||
static const struct nla_policy nfsd_listener_set_nl_policy[NFSD_A_SERVER_SOCK_ADDR + 1] = {
|
||||
[NFSD_A_SERVER_SOCK_ADDR] = NLA_POLICY_NESTED(nfsd_sock_nl_policy),
|
||||
};
|
||||
|
||||
/* Ops table for nfsd */
|
||||
static const struct genl_split_ops nfsd_nl_ops[] = {
|
||||
{
|
||||
|
@ -63,6 +73,18 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
|
|||
.doit = nfsd_nl_version_get_doit,
|
||||
.flags = GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = NFSD_CMD_LISTENER_SET,
|
||||
.doit = nfsd_nl_listener_set_doit,
|
||||
.policy = nfsd_listener_set_nl_policy,
|
||||
.maxattr = NFSD_A_SERVER_SOCK_ADDR,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = NFSD_CMD_LISTENER_GET,
|
||||
.doit = nfsd_nl_listener_get_doit,
|
||||
.flags = GENL_CMD_CAP_DO,
|
||||
},
|
||||
};
|
||||
|
||||
struct genl_family nfsd_nl_family __ro_after_init = {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <uapi/linux/nfsd_netlink.h>
|
||||
|
||||
/* Common nested types */
|
||||
extern const struct nla_policy nfsd_sock_nl_policy[NFSD_A_SOCK_TRANSPORT_NAME + 1];
|
||||
extern const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1];
|
||||
|
||||
int nfsd_nl_rpc_status_get_start(struct netlink_callback *cb);
|
||||
|
@ -23,6 +24,8 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info);
|
|||
int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
|
||||
extern struct genl_family nfsd_nl_family;
|
||||
|
||||
|
|
220
fs/nfsd/nfsctl.c
220
fs/nfsd/nfsctl.c
|
@ -1946,6 +1946,226 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_nl_listener_set_doit - set the nfs running sockets
|
||||
* @skb: reply buffer
|
||||
* @info: netlink metadata and command arguments
|
||||
*
|
||||
* Return 0 on success or a negative errno.
|
||||
*/
|
||||
int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct net *net = genl_info_net(info);
|
||||
struct svc_xprt *xprt, *tmp;
|
||||
const struct nlattr *attr;
|
||||
struct svc_serv *serv;
|
||||
LIST_HEAD(permsocks);
|
||||
struct nfsd_net *nn;
|
||||
int err, rem;
|
||||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
|
||||
err = nfsd_create_serv(net);
|
||||
if (err) {
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
nn = net_generic(net, nfsd_net_id);
|
||||
serv = nn->nfsd_serv;
|
||||
|
||||
spin_lock_bh(&serv->sv_lock);
|
||||
|
||||
/* Move all of the old listener sockets to a temp list */
|
||||
list_splice_init(&serv->sv_permsocks, &permsocks);
|
||||
|
||||
/*
|
||||
* Walk the list of server_socks from userland and move any that match
|
||||
* back to sv_permsocks
|
||||
*/
|
||||
nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) {
|
||||
struct nlattr *tb[NFSD_A_SOCK_MAX + 1];
|
||||
const char *xcl_name;
|
||||
struct sockaddr *sa;
|
||||
|
||||
if (nla_type(attr) != NFSD_A_SERVER_SOCK_ADDR)
|
||||
continue;
|
||||
|
||||
if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
|
||||
nfsd_sock_nl_policy, info->extack) < 0)
|
||||
continue;
|
||||
|
||||
if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME])
|
||||
continue;
|
||||
|
||||
if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa))
|
||||
continue;
|
||||
|
||||
xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]);
|
||||
sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
|
||||
|
||||
/* Put back any matching sockets */
|
||||
list_for_each_entry_safe(xprt, tmp, &permsocks, xpt_list) {
|
||||
/* This shouldn't be possible */
|
||||
if (WARN_ON_ONCE(xprt->xpt_net != net)) {
|
||||
list_move(&xprt->xpt_list, &serv->sv_permsocks);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If everything matches, put it back */
|
||||
if (!strcmp(xprt->xpt_class->xcl_name, xcl_name) &&
|
||||
rpc_cmp_addr_port(sa, (struct sockaddr *)&xprt->xpt_local)) {
|
||||
list_move(&xprt->xpt_list, &serv->sv_permsocks);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* For now, no removing old sockets while server is running */
|
||||
if (serv->sv_nrthreads && !list_empty(&permsocks)) {
|
||||
list_splice_init(&permsocks, &serv->sv_permsocks);
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
err = -EBUSY;
|
||||
goto out_unlock_mtx;
|
||||
}
|
||||
|
||||
/* Close the remaining sockets on the permsocks list */
|
||||
while (!list_empty(&permsocks)) {
|
||||
xprt = list_first_entry(&permsocks, struct svc_xprt, xpt_list);
|
||||
list_move(&xprt->xpt_list, &serv->sv_permsocks);
|
||||
|
||||
/*
|
||||
* Newly-created sockets are born with the BUSY bit set. Clear
|
||||
* it if there are no threads, since nothing can pick it up
|
||||
* in that case.
|
||||
*/
|
||||
if (!serv->sv_nrthreads)
|
||||
clear_bit(XPT_BUSY, &xprt->xpt_flags);
|
||||
|
||||
set_bit(XPT_CLOSE, &xprt->xpt_flags);
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
svc_xprt_close(xprt);
|
||||
spin_lock_bh(&serv->sv_lock);
|
||||
}
|
||||
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
|
||||
/* walk list of addrs again, open any that still don't exist */
|
||||
nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) {
|
||||
struct nlattr *tb[NFSD_A_SOCK_MAX + 1];
|
||||
const char *xcl_name;
|
||||
struct sockaddr *sa;
|
||||
int ret;
|
||||
|
||||
if (nla_type(attr) != NFSD_A_SERVER_SOCK_ADDR)
|
||||
continue;
|
||||
|
||||
if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
|
||||
nfsd_sock_nl_policy, info->extack) < 0)
|
||||
continue;
|
||||
|
||||
if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME])
|
||||
continue;
|
||||
|
||||
if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa))
|
||||
continue;
|
||||
|
||||
xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]);
|
||||
sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
|
||||
|
||||
xprt = svc_find_listener(serv, xcl_name, net, sa);
|
||||
if (xprt) {
|
||||
svc_xprt_put(xprt);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = svc_xprt_create_from_sa(serv, xcl_name, net, sa,
|
||||
SVC_SOCK_ANONYMOUS,
|
||||
get_current_cred());
|
||||
/* always save the latest error */
|
||||
if (ret < 0)
|
||||
err = ret;
|
||||
}
|
||||
|
||||
if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks))
|
||||
nfsd_destroy_serv(net);
|
||||
|
||||
out_unlock_mtx:
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_nl_listener_get_doit - get the nfs running listeners
|
||||
* @skb: reply buffer
|
||||
* @info: netlink metadata and command arguments
|
||||
*
|
||||
* Return 0 on success or a negative errno.
|
||||
*/
|
||||
int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct svc_xprt *xprt;
|
||||
struct svc_serv *serv;
|
||||
struct nfsd_net *nn;
|
||||
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;
|
||||
}
|
||||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
nn = net_generic(genl_info_net(info), nfsd_net_id);
|
||||
|
||||
/* no nfs server? Just send empty socket list */
|
||||
if (!nn->nfsd_serv)
|
||||
goto out_unlock_mtx;
|
||||
|
||||
serv = nn->nfsd_serv;
|
||||
spin_lock_bh(&serv->sv_lock);
|
||||
list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) {
|
||||
struct nlattr *attr;
|
||||
|
||||
attr = nla_nest_start(skb, NFSD_A_SERVER_SOCK_ADDR);
|
||||
if (!attr) {
|
||||
err = -EINVAL;
|
||||
goto err_serv_unlock;
|
||||
}
|
||||
|
||||
if (nla_put_string(skb, NFSD_A_SOCK_TRANSPORT_NAME,
|
||||
xprt->xpt_class->xcl_name) ||
|
||||
nla_put(skb, NFSD_A_SOCK_ADDR,
|
||||
sizeof(struct sockaddr_storage),
|
||||
&xprt->xpt_local)) {
|
||||
err = -EINVAL;
|
||||
goto err_serv_unlock;
|
||||
}
|
||||
|
||||
nla_nest_end(skb, attr);
|
||||
}
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
out_unlock_mtx:
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
genlmsg_end(skb, hdr);
|
||||
|
||||
return genlmsg_reply(skb, info);
|
||||
|
||||
err_serv_unlock:
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
err_free_msg:
|
||||
nlmsg_free(skb);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_net_init - Prepare the nfsd_net portion of a new net namespace
|
||||
* @net: a freshly-created network namespace
|
||||
|
|
|
@ -55,12 +55,29 @@ enum {
|
|||
NFSD_A_SERVER_PROTO_MAX = (__NFSD_A_SERVER_PROTO_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
NFSD_A_SOCK_ADDR = 1,
|
||||
NFSD_A_SOCK_TRANSPORT_NAME,
|
||||
|
||||
__NFSD_A_SOCK_MAX,
|
||||
NFSD_A_SOCK_MAX = (__NFSD_A_SOCK_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
NFSD_A_SERVER_SOCK_ADDR = 1,
|
||||
|
||||
__NFSD_A_SERVER_SOCK_MAX,
|
||||
NFSD_A_SERVER_SOCK_MAX = (__NFSD_A_SERVER_SOCK_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
NFSD_CMD_RPC_STATUS_GET = 1,
|
||||
NFSD_CMD_THREADS_SET,
|
||||
NFSD_CMD_THREADS_GET,
|
||||
NFSD_CMD_VERSION_SET,
|
||||
NFSD_CMD_VERSION_GET,
|
||||
NFSD_CMD_LISTENER_SET,
|
||||
NFSD_CMD_LISTENER_GET,
|
||||
|
||||
__NFSD_CMD_MAX,
|
||||
NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
|
||||
|
|
Loading…
Reference in a new issue