NFSD: add write_version to netlink command
Introduce write_version netlink command through a "declarative" interface. This patch introduces a change in behavior since for version-set userspace is expected to provide a NFS major/minor version list it wants to enable while all the other ones will be disabled. (procfs write_version command implements imperative interface where the admin writes +3/-3 to enable/disable a single version. Reviewed-by: Jeff Layton <jlayton@kernel.org> Tested-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
924f4fb003
commit
5a939bea25
@ -78,6 +78,26 @@ attribute-sets:
|
||||
-
|
||||
name: scope
|
||||
type: string
|
||||
-
|
||||
name: version
|
||||
attributes:
|
||||
-
|
||||
name: major
|
||||
type: u32
|
||||
-
|
||||
name: minor
|
||||
type: u32
|
||||
-
|
||||
name: enabled
|
||||
type: flag
|
||||
-
|
||||
name: server-proto
|
||||
attributes:
|
||||
-
|
||||
name: version
|
||||
type: nest
|
||||
nested-attributes: version
|
||||
multi-attr: true
|
||||
|
||||
operations:
|
||||
list:
|
||||
@ -126,3 +146,20 @@ operations:
|
||||
- gracetime
|
||||
- leasetime
|
||||
- scope
|
||||
-
|
||||
name: version-set
|
||||
doc: set nfs enabled versions
|
||||
attribute-set: server-proto
|
||||
flags: [ admin-perm ]
|
||||
do:
|
||||
request:
|
||||
attributes:
|
||||
- version
|
||||
-
|
||||
name: version-get
|
||||
doc: get nfs enabled versions
|
||||
attribute-set: server-proto
|
||||
do:
|
||||
reply:
|
||||
attributes:
|
||||
- version
|
||||
|
@ -10,6 +10,13 @@
|
||||
|
||||
#include <uapi/linux/nfsd_netlink.h>
|
||||
|
||||
/* Common nested types */
|
||||
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, },
|
||||
[NFSD_A_VERSION_ENABLED] = { .type = NLA_FLAG, },
|
||||
};
|
||||
|
||||
/* NFSD_CMD_THREADS_SET - do */
|
||||
static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_SCOPE + 1] = {
|
||||
[NFSD_A_SERVER_THREADS] = { .type = NLA_U32, },
|
||||
@ -18,6 +25,11 @@ static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_SCOPE +
|
||||
[NFSD_A_SERVER_SCOPE] = { .type = NLA_NUL_STRING, },
|
||||
};
|
||||
|
||||
/* NFSD_CMD_VERSION_SET - do */
|
||||
static const struct nla_policy nfsd_version_set_nl_policy[NFSD_A_SERVER_PROTO_VERSION + 1] = {
|
||||
[NFSD_A_SERVER_PROTO_VERSION] = NLA_POLICY_NESTED(nfsd_version_nl_policy),
|
||||
};
|
||||
|
||||
/* Ops table for nfsd */
|
||||
static const struct genl_split_ops nfsd_nl_ops[] = {
|
||||
{
|
||||
@ -39,6 +51,18 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
|
||||
.doit = nfsd_nl_threads_get_doit,
|
||||
.flags = GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = NFSD_CMD_VERSION_SET,
|
||||
.doit = nfsd_nl_version_set_doit,
|
||||
.policy = nfsd_version_set_nl_policy,
|
||||
.maxattr = NFSD_A_SERVER_PROTO_VERSION,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = NFSD_CMD_VERSION_GET,
|
||||
.doit = nfsd_nl_version_get_doit,
|
||||
.flags = GENL_CMD_CAP_DO,
|
||||
},
|
||||
};
|
||||
|
||||
struct genl_family nfsd_nl_family __ro_after_init = {
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
#include <uapi/linux/nfsd_netlink.h>
|
||||
|
||||
/* Common nested types */
|
||||
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_done(struct netlink_callback *cb);
|
||||
|
||||
@ -18,6 +21,8 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb);
|
||||
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);
|
||||
|
||||
extern struct genl_family nfsd_nl_family;
|
||||
|
||||
|
@ -218,6 +218,7 @@ struct nfsd_net {
|
||||
/* Simple check to find out if a given net was properly initialized */
|
||||
#define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl)
|
||||
|
||||
extern bool nfsd_support_version(int vers);
|
||||
extern void nfsd_netns_free_versions(struct nfsd_net *nn);
|
||||
|
||||
extern unsigned int nfsd_net_id;
|
||||
|
150
fs/nfsd/nfsctl.c
150
fs/nfsd/nfsctl.c
@ -1796,6 +1796,156 @@ err_free_msg:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_nl_version_set_doit - set the nfs enabled versions
|
||||
* @skb: reply buffer
|
||||
* @info: netlink metadata and command arguments
|
||||
*
|
||||
* Return 0 on success or a negative errno.
|
||||
*/
|
||||
int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
const struct nlattr *attr;
|
||||
struct nfsd_net *nn;
|
||||
int i, rem;
|
||||
|
||||
if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_PROTO_VERSION))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
|
||||
nn = net_generic(genl_info_net(info), nfsd_net_id);
|
||||
if (nn->nfsd_serv) {
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* clear current supported versions. */
|
||||
nfsd_vers(nn, 2, NFSD_CLEAR);
|
||||
nfsd_vers(nn, 3, NFSD_CLEAR);
|
||||
for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++)
|
||||
nfsd_minorversion(nn, i, NFSD_CLEAR);
|
||||
|
||||
nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) {
|
||||
struct nlattr *tb[NFSD_A_VERSION_MAX + 1];
|
||||
u32 major, minor = 0;
|
||||
bool enabled;
|
||||
|
||||
if (nla_type(attr) != NFSD_A_SERVER_PROTO_VERSION)
|
||||
continue;
|
||||
|
||||
if (nla_parse_nested(tb, NFSD_A_VERSION_MAX, attr,
|
||||
nfsd_version_nl_policy, info->extack) < 0)
|
||||
continue;
|
||||
|
||||
if (!tb[NFSD_A_VERSION_MAJOR])
|
||||
continue;
|
||||
|
||||
major = nla_get_u32(tb[NFSD_A_VERSION_MAJOR]);
|
||||
if (tb[NFSD_A_VERSION_MINOR])
|
||||
minor = nla_get_u32(tb[NFSD_A_VERSION_MINOR]);
|
||||
|
||||
enabled = nla_get_flag(tb[NFSD_A_VERSION_ENABLED]);
|
||||
|
||||
switch (major) {
|
||||
case 4:
|
||||
nfsd_minorversion(nn, minor, enabled ? NFSD_SET : NFSD_CLEAR);
|
||||
break;
|
||||
case 3:
|
||||
case 2:
|
||||
if (!minor)
|
||||
nfsd_vers(nn, major, enabled ? NFSD_SET : NFSD_CLEAR);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_nl_version_get_doit - get the enabled status for all supported nfs versions
|
||||
* @skb: reply buffer
|
||||
* @info: netlink metadata and command arguments
|
||||
*
|
||||
* Return 0 on success or a negative errno.
|
||||
*/
|
||||
int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct nfsd_net *nn;
|
||||
int i, err;
|
||||
void *hdr;
|
||||
|
||||
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);
|
||||
|
||||
for (i = 2; i <= 4; i++) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j <= NFSD_SUPPORTED_MINOR_VERSION; j++) {
|
||||
struct nlattr *attr;
|
||||
|
||||
/* Don't record any versions the kernel doesn't have
|
||||
* compiled in
|
||||
*/
|
||||
if (!nfsd_support_version(i))
|
||||
continue;
|
||||
|
||||
/* NFSv{2,3} does not support minor numbers */
|
||||
if (i < 4 && j)
|
||||
continue;
|
||||
|
||||
attr = nla_nest_start(skb,
|
||||
NFSD_A_SERVER_PROTO_VERSION);
|
||||
if (!attr) {
|
||||
err = -EINVAL;
|
||||
goto err_nfsd_unlock;
|
||||
}
|
||||
|
||||
if (nla_put_u32(skb, NFSD_A_VERSION_MAJOR, i) ||
|
||||
nla_put_u32(skb, NFSD_A_VERSION_MINOR, j)) {
|
||||
err = -EINVAL;
|
||||
goto err_nfsd_unlock;
|
||||
}
|
||||
|
||||
/* Set the enabled flag if the version is enabled */
|
||||
if (nfsd_vers(nn, i, NFSD_TEST) &&
|
||||
(i < 4 || nfsd_minorversion(nn, j, NFSD_TEST)) &&
|
||||
nla_put_flag(skb, NFSD_A_VERSION_ENABLED)) {
|
||||
err = -EINVAL;
|
||||
goto err_nfsd_unlock;
|
||||
}
|
||||
|
||||
nla_nest_end(skb, attr);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
genlmsg_end(skb, hdr);
|
||||
|
||||
return genlmsg_reply(skb, info);
|
||||
|
||||
err_nfsd_unlock:
|
||||
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
|
||||
|
@ -133,8 +133,7 @@ struct svc_program nfsd_program = {
|
||||
.pg_rpcbind_set = nfsd_rpcbind_set,
|
||||
};
|
||||
|
||||
static bool
|
||||
nfsd_support_version(int vers)
|
||||
bool nfsd_support_version(int vers)
|
||||
{
|
||||
if (vers >= NFSD_MINVERS && vers < NFSD_NRVERS)
|
||||
return nfsd_version[vers] != NULL;
|
||||
|
@ -39,10 +39,28 @@ enum {
|
||||
NFSD_A_SERVER_MAX = (__NFSD_A_SERVER_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
NFSD_A_VERSION_MAJOR = 1,
|
||||
NFSD_A_VERSION_MINOR,
|
||||
NFSD_A_VERSION_ENABLED,
|
||||
|
||||
__NFSD_A_VERSION_MAX,
|
||||
NFSD_A_VERSION_MAX = (__NFSD_A_VERSION_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
NFSD_A_SERVER_PROTO_VERSION = 1,
|
||||
|
||||
__NFSD_A_SERVER_PROTO_MAX,
|
||||
NFSD_A_SERVER_PROTO_MAX = (__NFSD_A_SERVER_PROTO_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_MAX,
|
||||
NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
|
||||
|
Loading…
x
Reference in New Issue
Block a user