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
|
name: scope
|
||||||
type: string
|
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:
|
operations:
|
||||||
list:
|
list:
|
||||||
@ -126,3 +146,20 @@ operations:
|
|||||||
- gracetime
|
- gracetime
|
||||||
- leasetime
|
- leasetime
|
||||||
- scope
|
- 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>
|
#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 */
|
/* NFSD_CMD_THREADS_SET - do */
|
||||||
static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_SCOPE + 1] = {
|
static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_SCOPE + 1] = {
|
||||||
[NFSD_A_SERVER_THREADS] = { .type = NLA_U32, },
|
[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_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 */
|
/* Ops table for nfsd */
|
||||||
static const struct genl_split_ops nfsd_nl_ops[] = {
|
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,
|
.doit = nfsd_nl_threads_get_doit,
|
||||||
.flags = GENL_CMD_CAP_DO,
|
.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 = {
|
struct genl_family nfsd_nl_family __ro_after_init = {
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
|
|
||||||
#include <uapi/linux/nfsd_netlink.h>
|
#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_start(struct netlink_callback *cb);
|
||||||
int nfsd_nl_rpc_status_get_done(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);
|
struct netlink_callback *cb);
|
||||||
int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info);
|
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_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||||
|
|
||||||
extern struct genl_family nfsd_nl_family;
|
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 */
|
/* Simple check to find out if a given net was properly initialized */
|
||||||
#define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl)
|
#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 void nfsd_netns_free_versions(struct nfsd_net *nn);
|
||||||
|
|
||||||
extern unsigned int nfsd_net_id;
|
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;
|
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
|
* 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
|
||||||
|
@ -133,8 +133,7 @@ struct svc_program nfsd_program = {
|
|||||||
.pg_rpcbind_set = nfsd_rpcbind_set,
|
.pg_rpcbind_set = nfsd_rpcbind_set,
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool
|
bool nfsd_support_version(int vers)
|
||||||
nfsd_support_version(int vers)
|
|
||||||
{
|
{
|
||||||
if (vers >= NFSD_MINVERS && vers < NFSD_NRVERS)
|
if (vers >= NFSD_MINVERS && vers < NFSD_NRVERS)
|
||||||
return nfsd_version[vers] != NULL;
|
return nfsd_version[vers] != NULL;
|
||||||
|
@ -39,10 +39,28 @@ enum {
|
|||||||
NFSD_A_SERVER_MAX = (__NFSD_A_SERVER_MAX - 1)
|
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 {
|
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_GET,
|
||||||
|
|
||||||
__NFSD_CMD_MAX,
|
__NFSD_CMD_MAX,
|
||||||
NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
|
NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user