mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
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
@ -98,6 +98,23 @@ attribute-sets:
|
|||||||
type: nest
|
type: nest
|
||||||
nested-attributes: version
|
nested-attributes: version
|
||||||
multi-attr: true
|
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:
|
operations:
|
||||||
list:
|
list:
|
||||||
@ -163,3 +180,20 @@ operations:
|
|||||||
reply:
|
reply:
|
||||||
attributes:
|
attributes:
|
||||||
- version
|
- 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>
|
#include <uapi/linux/nfsd_netlink.h>
|
||||||
|
|
||||||
/* Common nested types */
|
/* 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] = {
|
const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1] = {
|
||||||
[NFSD_A_VERSION_MAJOR] = { .type = NLA_U32, },
|
[NFSD_A_VERSION_MAJOR] = { .type = NLA_U32, },
|
||||||
[NFSD_A_VERSION_MINOR] = { .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_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 */
|
/* Ops table for nfsd */
|
||||||
static const struct genl_split_ops nfsd_nl_ops[] = {
|
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,
|
.doit = nfsd_nl_version_get_doit,
|
||||||
.flags = GENL_CMD_CAP_DO,
|
.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 = {
|
struct genl_family nfsd_nl_family __ro_after_init = {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <uapi/linux/nfsd_netlink.h>
|
#include <uapi/linux/nfsd_netlink.h>
|
||||||
|
|
||||||
/* Common nested types */
|
/* 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];
|
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);
|
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_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_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_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;
|
extern struct genl_family nfsd_nl_family;
|
||||||
|
|
||||||
|
220
fs/nfsd/nfsctl.c
220
fs/nfsd/nfsctl.c
@ -1946,6 +1946,226 @@ err_free_msg:
|
|||||||
return err;
|
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
|
* nfsd_net_init - Prepare the nfsd_net portion of a new net namespace
|
||||||
* @net: a freshly-created network namespace
|
* @net: a freshly-created network namespace
|
||||||
|
@ -55,12 +55,29 @@ enum {
|
|||||||
NFSD_A_SERVER_PROTO_MAX = (__NFSD_A_SERVER_PROTO_MAX - 1)
|
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 {
|
enum {
|
||||||
NFSD_CMD_RPC_STATUS_GET = 1,
|
NFSD_CMD_RPC_STATUS_GET = 1,
|
||||||
NFSD_CMD_THREADS_SET,
|
NFSD_CMD_THREADS_SET,
|
||||||
NFSD_CMD_THREADS_GET,
|
NFSD_CMD_THREADS_GET,
|
||||||
NFSD_CMD_VERSION_SET,
|
NFSD_CMD_VERSION_SET,
|
||||||
NFSD_CMD_VERSION_GET,
|
NFSD_CMD_VERSION_GET,
|
||||||
|
NFSD_CMD_LISTENER_SET,
|
||||||
|
NFSD_CMD_LISTENER_GET,
|
||||||
|
|
||||||
__NFSD_CMD_MAX,
|
__NFSD_CMD_MAX,
|
||||||
NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
|
NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
|
||||||
|
Loading…
Reference in New Issue
Block a user