devlink: Add param set command
authorMoshe Shemesh <moshe@mellanox.com>
Wed, 4 Jul 2018 11:30:30 +0000 (14:30 +0300)
committerDavid S. Miller <davem@davemloft.net>
Thu, 5 Jul 2018 10:58:35 +0000 (19:58 +0900)
Add param set command to set value for a parameter.
Value can be set to any of the supported configuration modes.

Signed-off-by: Moshe Shemesh <moshe@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/devlink.h
include/uapi/linux/devlink.h
net/core/devlink.c

index 4a0687a1fb998982b07605291549395b14c14c62..88062752dcd70908cbc7f853086309c3b9afec4f 100644 (file)
@@ -328,6 +328,7 @@ struct devlink_param_gset_ctx {
  *       configuration modes
  * @set: set parameter value, used for runtime and permanent
  *       configuration modes
+ * @validate: validate input value is applicable (within value range, etc.)
  *
  * This struct should be used by the driver to fill the data for
  * a parameter it registers.
@@ -342,6 +343,9 @@ struct devlink_param {
                   struct devlink_param_gset_ctx *ctx);
        int (*set)(struct devlink *devlink, u32 id,
                   struct devlink_param_gset_ctx *ctx);
+       int (*validate)(struct devlink *devlink, u32 id,
+                       union devlink_param_value val,
+                       struct netlink_ext_ack *extack);
 };
 
 struct devlink_param_item {
index 2ccfe84176bfc916bc29a6d25bc3fbb1490ce6f4..ea0623e568f037a54e529449f8ab668159ad3fa1 100644 (file)
@@ -79,6 +79,7 @@ enum devlink_command {
        DEVLINK_CMD_RELOAD,
 
        DEVLINK_CMD_PARAM_GET,          /* can dump */
+       DEVLINK_CMD_PARAM_SET,
 
        /* add new commands above here */
        __DEVLINK_CMD_MAX,
index b22d41275f0be8f9063a926049a309d401f79a78..0cd7a42dcec2ed4d9982bb87deca2d03b1da55d4 100644 (file)
@@ -2661,6 +2661,15 @@ static int devlink_param_get(struct devlink *devlink,
        return param->get(devlink, param->id, ctx);
 }
 
+static int devlink_param_set(struct devlink *devlink,
+                            const struct devlink_param *param,
+                            struct devlink_param_gset_ctx *ctx)
+{
+       if (!param->set)
+               return -EOPNOTSUPP;
+       return param->set(devlink, param->id, ctx);
+}
+
 static int
 devlink_param_type_to_nla_type(enum devlink_param_type param_type)
 {
@@ -2847,6 +2856,69 @@ out:
        return msg->len;
 }
 
+static int
+devlink_param_type_get_from_info(struct genl_info *info,
+                                enum devlink_param_type *param_type)
+{
+       if (!info->attrs[DEVLINK_ATTR_PARAM_TYPE])
+               return -EINVAL;
+
+       switch (nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_TYPE])) {
+       case NLA_U8:
+               *param_type = DEVLINK_PARAM_TYPE_U8;
+               break;
+       case NLA_U16:
+               *param_type = DEVLINK_PARAM_TYPE_U16;
+               break;
+       case NLA_U32:
+               *param_type = DEVLINK_PARAM_TYPE_U32;
+               break;
+       case NLA_STRING:
+               *param_type = DEVLINK_PARAM_TYPE_STRING;
+               break;
+       case NLA_FLAG:
+               *param_type = DEVLINK_PARAM_TYPE_BOOL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+devlink_param_value_get_from_info(const struct devlink_param *param,
+                                 struct genl_info *info,
+                                 union devlink_param_value *value)
+{
+       if (param->type != DEVLINK_PARAM_TYPE_BOOL &&
+           !info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA])
+               return -EINVAL;
+
+       switch (param->type) {
+       case DEVLINK_PARAM_TYPE_U8:
+               value->vu8 = nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA]);
+               break;
+       case DEVLINK_PARAM_TYPE_U16:
+               value->vu16 = nla_get_u16(info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA]);
+               break;
+       case DEVLINK_PARAM_TYPE_U32:
+               value->vu32 = nla_get_u32(info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA]);
+               break;
+       case DEVLINK_PARAM_TYPE_STRING:
+               if (nla_len(info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA]) >
+                   DEVLINK_PARAM_MAX_STRING_VALUE)
+                       return -EINVAL;
+               value->vstr = nla_data(info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA]);
+               break;
+       case DEVLINK_PARAM_TYPE_BOOL:
+               value->vbool = info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA] ?
+                              true : false;
+               break;
+       }
+       return 0;
+}
+
 static struct devlink_param_item *
 devlink_param_get_from_info(struct devlink *devlink,
                            struct genl_info *info)
@@ -2887,6 +2959,58 @@ static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
        return genlmsg_reply(msg, info);
 }
 
+static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
+                                        struct genl_info *info)
+{
+       struct devlink *devlink = info->user_ptr[0];
+       enum devlink_param_type param_type;
+       struct devlink_param_gset_ctx ctx;
+       enum devlink_param_cmode cmode;
+       struct devlink_param_item *param_item;
+       const struct devlink_param *param;
+       union devlink_param_value value;
+       int err = 0;
+
+       param_item = devlink_param_get_from_info(devlink, info);
+       if (!param_item)
+               return -EINVAL;
+       param = param_item->param;
+       err = devlink_param_type_get_from_info(info, &param_type);
+       if (err)
+               return err;
+       if (param_type != param->type)
+               return -EINVAL;
+       err = devlink_param_value_get_from_info(param, info, &value);
+       if (err)
+               return err;
+       if (param->validate) {
+               err = param->validate(devlink, param->id, value, info->extack);
+               if (err)
+                       return err;
+       }
+
+       if (!info->attrs[DEVLINK_ATTR_PARAM_VALUE_CMODE])
+               return -EINVAL;
+       cmode = nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_VALUE_CMODE]);
+       if (!devlink_param_cmode_is_supported(param, cmode))
+               return -EOPNOTSUPP;
+
+       if (cmode == DEVLINK_PARAM_CMODE_DRIVERINIT) {
+               param_item->driverinit_value = value;
+               param_item->driverinit_value_valid = true;
+       } else {
+               if (!param->set)
+                       return -EOPNOTSUPP;
+               ctx.val = value;
+               ctx.cmode = cmode;
+               err = devlink_param_set(devlink, param, &ctx);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 static int devlink_param_register_one(struct devlink *devlink,
                                      const struct devlink_param *param)
 {
@@ -2942,6 +3066,9 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
        [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 },
        [DEVLINK_ATTR_RESOURCE_ID] = { .type = NLA_U64},
        [DEVLINK_ATTR_RESOURCE_SIZE] = { .type = NLA_U64},
+       [DEVLINK_ATTR_PARAM_NAME] = { .type = NLA_NUL_STRING },
+       [DEVLINK_ATTR_PARAM_TYPE] = { .type = NLA_U8 },
+       [DEVLINK_ATTR_PARAM_VALUE_CMODE] = { .type = NLA_U8 },
 };
 
 static const struct genl_ops devlink_nl_ops[] = {
@@ -3133,6 +3260,13 @@ static const struct genl_ops devlink_nl_ops[] = {
                .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
                /* can be retrieved by unprivileged users */
        },
+       {
+               .cmd = DEVLINK_CMD_PARAM_SET,
+               .doit = devlink_nl_cmd_param_set_doit,
+               .policy = devlink_nl_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
+       },
 };
 
 static struct genl_family devlink_nl_family __ro_after_init = {