53590934ba
All remaining doit and dumpit netlink callback functions are going to be used by generated split ops. They expect certain name format. Rename the callback to be aligned with generated names. Signed-off-by: Jiri Pirko <jiri@nvidia.com> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com> Link: https://lore.kernel.org/r/20231021112711.660606-8-jiri@resnulli.us Signed-off-by: Jakub Kicinski <kuba@kernel.org>
918 lines
23 KiB
C
918 lines
23 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
|
|
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
|
|
*/
|
|
|
|
#include "devl_internal.h"
|
|
|
|
static struct devlink_dpipe_field devlink_dpipe_fields_ethernet[] = {
|
|
{
|
|
.name = "destination mac",
|
|
.id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC,
|
|
.bitwidth = 48,
|
|
},
|
|
};
|
|
|
|
struct devlink_dpipe_header devlink_dpipe_header_ethernet = {
|
|
.name = "ethernet",
|
|
.id = DEVLINK_DPIPE_HEADER_ETHERNET,
|
|
.fields = devlink_dpipe_fields_ethernet,
|
|
.fields_count = ARRAY_SIZE(devlink_dpipe_fields_ethernet),
|
|
.global = true,
|
|
};
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_header_ethernet);
|
|
|
|
static struct devlink_dpipe_field devlink_dpipe_fields_ipv4[] = {
|
|
{
|
|
.name = "destination ip",
|
|
.id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP,
|
|
.bitwidth = 32,
|
|
},
|
|
};
|
|
|
|
struct devlink_dpipe_header devlink_dpipe_header_ipv4 = {
|
|
.name = "ipv4",
|
|
.id = DEVLINK_DPIPE_HEADER_IPV4,
|
|
.fields = devlink_dpipe_fields_ipv4,
|
|
.fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv4),
|
|
.global = true,
|
|
};
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_header_ipv4);
|
|
|
|
static struct devlink_dpipe_field devlink_dpipe_fields_ipv6[] = {
|
|
{
|
|
.name = "destination ip",
|
|
.id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP,
|
|
.bitwidth = 128,
|
|
},
|
|
};
|
|
|
|
struct devlink_dpipe_header devlink_dpipe_header_ipv6 = {
|
|
.name = "ipv6",
|
|
.id = DEVLINK_DPIPE_HEADER_IPV6,
|
|
.fields = devlink_dpipe_fields_ipv6,
|
|
.fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv6),
|
|
.global = true,
|
|
};
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_header_ipv6);
|
|
|
|
int devlink_dpipe_match_put(struct sk_buff *skb,
|
|
struct devlink_dpipe_match *match)
|
|
{
|
|
struct devlink_dpipe_header *header = match->header;
|
|
struct devlink_dpipe_field *field = &header->fields[match->field_id];
|
|
struct nlattr *match_attr;
|
|
|
|
match_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_MATCH);
|
|
if (!match_attr)
|
|
return -EMSGSIZE;
|
|
|
|
if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_MATCH_TYPE, match->type) ||
|
|
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_INDEX, match->header_index) ||
|
|
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) ||
|
|
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) ||
|
|
nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global))
|
|
goto nla_put_failure;
|
|
|
|
nla_nest_end(skb, match_attr);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
nla_nest_cancel(skb, match_attr);
|
|
return -EMSGSIZE;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_match_put);
|
|
|
|
static int devlink_dpipe_matches_put(struct devlink_dpipe_table *table,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct nlattr *matches_attr;
|
|
|
|
matches_attr = nla_nest_start_noflag(skb,
|
|
DEVLINK_ATTR_DPIPE_TABLE_MATCHES);
|
|
if (!matches_attr)
|
|
return -EMSGSIZE;
|
|
|
|
if (table->table_ops->matches_dump(table->priv, skb))
|
|
goto nla_put_failure;
|
|
|
|
nla_nest_end(skb, matches_attr);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
nla_nest_cancel(skb, matches_attr);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
int devlink_dpipe_action_put(struct sk_buff *skb,
|
|
struct devlink_dpipe_action *action)
|
|
{
|
|
struct devlink_dpipe_header *header = action->header;
|
|
struct devlink_dpipe_field *field = &header->fields[action->field_id];
|
|
struct nlattr *action_attr;
|
|
|
|
action_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_ACTION);
|
|
if (!action_attr)
|
|
return -EMSGSIZE;
|
|
|
|
if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_ACTION_TYPE, action->type) ||
|
|
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_INDEX, action->header_index) ||
|
|
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) ||
|
|
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) ||
|
|
nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global))
|
|
goto nla_put_failure;
|
|
|
|
nla_nest_end(skb, action_attr);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
nla_nest_cancel(skb, action_attr);
|
|
return -EMSGSIZE;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_action_put);
|
|
|
|
static int devlink_dpipe_actions_put(struct devlink_dpipe_table *table,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct nlattr *actions_attr;
|
|
|
|
actions_attr = nla_nest_start_noflag(skb,
|
|
DEVLINK_ATTR_DPIPE_TABLE_ACTIONS);
|
|
if (!actions_attr)
|
|
return -EMSGSIZE;
|
|
|
|
if (table->table_ops->actions_dump(table->priv, skb))
|
|
goto nla_put_failure;
|
|
|
|
nla_nest_end(skb, actions_attr);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
nla_nest_cancel(skb, actions_attr);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
static int devlink_dpipe_table_put(struct sk_buff *skb,
|
|
struct devlink_dpipe_table *table)
|
|
{
|
|
struct nlattr *table_attr;
|
|
u64 table_size;
|
|
|
|
table_size = table->table_ops->size_get(table->priv);
|
|
table_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_TABLE);
|
|
if (!table_attr)
|
|
return -EMSGSIZE;
|
|
|
|
if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_TABLE_NAME, table->name) ||
|
|
nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_SIZE, table_size,
|
|
DEVLINK_ATTR_PAD))
|
|
goto nla_put_failure;
|
|
if (nla_put_u8(skb, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED,
|
|
table->counters_enabled))
|
|
goto nla_put_failure;
|
|
|
|
if (table->resource_valid) {
|
|
if (nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID,
|
|
table->resource_id, DEVLINK_ATTR_PAD) ||
|
|
nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS,
|
|
table->resource_units, DEVLINK_ATTR_PAD))
|
|
goto nla_put_failure;
|
|
}
|
|
if (devlink_dpipe_matches_put(table, skb))
|
|
goto nla_put_failure;
|
|
|
|
if (devlink_dpipe_actions_put(table, skb))
|
|
goto nla_put_failure;
|
|
|
|
nla_nest_end(skb, table_attr);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
nla_nest_cancel(skb, table_attr);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
static int devlink_dpipe_send_and_alloc_skb(struct sk_buff **pskb,
|
|
struct genl_info *info)
|
|
{
|
|
int err;
|
|
|
|
if (*pskb) {
|
|
err = genlmsg_reply(*pskb, info);
|
|
if (err)
|
|
return err;
|
|
}
|
|
*pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
if (!*pskb)
|
|
return -ENOMEM;
|
|
return 0;
|
|
}
|
|
|
|
static int devlink_dpipe_tables_fill(struct genl_info *info,
|
|
enum devlink_command cmd, int flags,
|
|
struct list_head *dpipe_tables,
|
|
const char *table_name)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct devlink_dpipe_table *table;
|
|
struct nlattr *tables_attr;
|
|
struct sk_buff *skb = NULL;
|
|
struct nlmsghdr *nlh;
|
|
bool incomplete;
|
|
void *hdr;
|
|
int i;
|
|
int err;
|
|
|
|
table = list_first_entry(dpipe_tables,
|
|
struct devlink_dpipe_table, list);
|
|
start_again:
|
|
err = devlink_dpipe_send_and_alloc_skb(&skb, info);
|
|
if (err)
|
|
return err;
|
|
|
|
hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
|
|
&devlink_nl_family, NLM_F_MULTI, cmd);
|
|
if (!hdr) {
|
|
nlmsg_free(skb);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
if (devlink_nl_put_handle(skb, devlink))
|
|
goto nla_put_failure;
|
|
tables_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_TABLES);
|
|
if (!tables_attr)
|
|
goto nla_put_failure;
|
|
|
|
i = 0;
|
|
incomplete = false;
|
|
list_for_each_entry_from(table, dpipe_tables, list) {
|
|
if (!table_name) {
|
|
err = devlink_dpipe_table_put(skb, table);
|
|
if (err) {
|
|
if (!i)
|
|
goto err_table_put;
|
|
incomplete = true;
|
|
break;
|
|
}
|
|
} else {
|
|
if (!strcmp(table->name, table_name)) {
|
|
err = devlink_dpipe_table_put(skb, table);
|
|
if (err)
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
|
|
nla_nest_end(skb, tables_attr);
|
|
genlmsg_end(skb, hdr);
|
|
if (incomplete)
|
|
goto start_again;
|
|
|
|
send_done:
|
|
nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
|
|
NLMSG_DONE, 0, flags | NLM_F_MULTI);
|
|
if (!nlh) {
|
|
err = devlink_dpipe_send_and_alloc_skb(&skb, info);
|
|
if (err)
|
|
return err;
|
|
goto send_done;
|
|
}
|
|
|
|
return genlmsg_reply(skb, info);
|
|
|
|
nla_put_failure:
|
|
err = -EMSGSIZE;
|
|
err_table_put:
|
|
nlmsg_free(skb);
|
|
return err;
|
|
}
|
|
|
|
int devlink_nl_dpipe_table_get_doit(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
const char *table_name = NULL;
|
|
|
|
if (info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME])
|
|
table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]);
|
|
|
|
return devlink_dpipe_tables_fill(info, DEVLINK_CMD_DPIPE_TABLE_GET, 0,
|
|
&devlink->dpipe_table_list,
|
|
table_name);
|
|
}
|
|
|
|
static int devlink_dpipe_value_put(struct sk_buff *skb,
|
|
struct devlink_dpipe_value *value)
|
|
{
|
|
if (nla_put(skb, DEVLINK_ATTR_DPIPE_VALUE,
|
|
value->value_size, value->value))
|
|
return -EMSGSIZE;
|
|
if (value->mask)
|
|
if (nla_put(skb, DEVLINK_ATTR_DPIPE_VALUE_MASK,
|
|
value->value_size, value->mask))
|
|
return -EMSGSIZE;
|
|
if (value->mapping_valid)
|
|
if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_VALUE_MAPPING,
|
|
value->mapping_value))
|
|
return -EMSGSIZE;
|
|
return 0;
|
|
}
|
|
|
|
static int devlink_dpipe_action_value_put(struct sk_buff *skb,
|
|
struct devlink_dpipe_value *value)
|
|
{
|
|
if (!value->action)
|
|
return -EINVAL;
|
|
if (devlink_dpipe_action_put(skb, value->action))
|
|
return -EMSGSIZE;
|
|
if (devlink_dpipe_value_put(skb, value))
|
|
return -EMSGSIZE;
|
|
return 0;
|
|
}
|
|
|
|
static int devlink_dpipe_action_values_put(struct sk_buff *skb,
|
|
struct devlink_dpipe_value *values,
|
|
unsigned int values_count)
|
|
{
|
|
struct nlattr *action_attr;
|
|
int i;
|
|
int err;
|
|
|
|
for (i = 0; i < values_count; i++) {
|
|
action_attr = nla_nest_start_noflag(skb,
|
|
DEVLINK_ATTR_DPIPE_ACTION_VALUE);
|
|
if (!action_attr)
|
|
return -EMSGSIZE;
|
|
err = devlink_dpipe_action_value_put(skb, &values[i]);
|
|
if (err)
|
|
goto err_action_value_put;
|
|
nla_nest_end(skb, action_attr);
|
|
}
|
|
return 0;
|
|
|
|
err_action_value_put:
|
|
nla_nest_cancel(skb, action_attr);
|
|
return err;
|
|
}
|
|
|
|
static int devlink_dpipe_match_value_put(struct sk_buff *skb,
|
|
struct devlink_dpipe_value *value)
|
|
{
|
|
if (!value->match)
|
|
return -EINVAL;
|
|
if (devlink_dpipe_match_put(skb, value->match))
|
|
return -EMSGSIZE;
|
|
if (devlink_dpipe_value_put(skb, value))
|
|
return -EMSGSIZE;
|
|
return 0;
|
|
}
|
|
|
|
static int devlink_dpipe_match_values_put(struct sk_buff *skb,
|
|
struct devlink_dpipe_value *values,
|
|
unsigned int values_count)
|
|
{
|
|
struct nlattr *match_attr;
|
|
int i;
|
|
int err;
|
|
|
|
for (i = 0; i < values_count; i++) {
|
|
match_attr = nla_nest_start_noflag(skb,
|
|
DEVLINK_ATTR_DPIPE_MATCH_VALUE);
|
|
if (!match_attr)
|
|
return -EMSGSIZE;
|
|
err = devlink_dpipe_match_value_put(skb, &values[i]);
|
|
if (err)
|
|
goto err_match_value_put;
|
|
nla_nest_end(skb, match_attr);
|
|
}
|
|
return 0;
|
|
|
|
err_match_value_put:
|
|
nla_nest_cancel(skb, match_attr);
|
|
return err;
|
|
}
|
|
|
|
static int devlink_dpipe_entry_put(struct sk_buff *skb,
|
|
struct devlink_dpipe_entry *entry)
|
|
{
|
|
struct nlattr *entry_attr, *matches_attr, *actions_attr;
|
|
int err;
|
|
|
|
entry_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_ENTRY);
|
|
if (!entry_attr)
|
|
return -EMSGSIZE;
|
|
|
|
if (nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_ENTRY_INDEX, entry->index,
|
|
DEVLINK_ATTR_PAD))
|
|
goto nla_put_failure;
|
|
if (entry->counter_valid)
|
|
if (nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_ENTRY_COUNTER,
|
|
entry->counter, DEVLINK_ATTR_PAD))
|
|
goto nla_put_failure;
|
|
|
|
matches_attr = nla_nest_start_noflag(skb,
|
|
DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES);
|
|
if (!matches_attr)
|
|
goto nla_put_failure;
|
|
|
|
err = devlink_dpipe_match_values_put(skb, entry->match_values,
|
|
entry->match_values_count);
|
|
if (err) {
|
|
nla_nest_cancel(skb, matches_attr);
|
|
goto err_match_values_put;
|
|
}
|
|
nla_nest_end(skb, matches_attr);
|
|
|
|
actions_attr = nla_nest_start_noflag(skb,
|
|
DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES);
|
|
if (!actions_attr)
|
|
goto nla_put_failure;
|
|
|
|
err = devlink_dpipe_action_values_put(skb, entry->action_values,
|
|
entry->action_values_count);
|
|
if (err) {
|
|
nla_nest_cancel(skb, actions_attr);
|
|
goto err_action_values_put;
|
|
}
|
|
nla_nest_end(skb, actions_attr);
|
|
|
|
nla_nest_end(skb, entry_attr);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
err = -EMSGSIZE;
|
|
err_match_values_put:
|
|
err_action_values_put:
|
|
nla_nest_cancel(skb, entry_attr);
|
|
return err;
|
|
}
|
|
|
|
static struct devlink_dpipe_table *
|
|
devlink_dpipe_table_find(struct list_head *dpipe_tables,
|
|
const char *table_name, struct devlink *devlink)
|
|
{
|
|
struct devlink_dpipe_table *table;
|
|
|
|
list_for_each_entry_rcu(table, dpipe_tables, list,
|
|
lockdep_is_held(&devlink->lock)) {
|
|
if (!strcmp(table->name, table_name))
|
|
return table;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx)
|
|
{
|
|
struct devlink *devlink;
|
|
int err;
|
|
|
|
err = devlink_dpipe_send_and_alloc_skb(&dump_ctx->skb,
|
|
dump_ctx->info);
|
|
if (err)
|
|
return err;
|
|
|
|
dump_ctx->hdr = genlmsg_put(dump_ctx->skb,
|
|
dump_ctx->info->snd_portid,
|
|
dump_ctx->info->snd_seq,
|
|
&devlink_nl_family, NLM_F_MULTI,
|
|
dump_ctx->cmd);
|
|
if (!dump_ctx->hdr)
|
|
goto nla_put_failure;
|
|
|
|
devlink = dump_ctx->info->user_ptr[0];
|
|
if (devlink_nl_put_handle(dump_ctx->skb, devlink))
|
|
goto nla_put_failure;
|
|
dump_ctx->nest = nla_nest_start_noflag(dump_ctx->skb,
|
|
DEVLINK_ATTR_DPIPE_ENTRIES);
|
|
if (!dump_ctx->nest)
|
|
goto nla_put_failure;
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
nlmsg_free(dump_ctx->skb);
|
|
return -EMSGSIZE;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_prepare);
|
|
|
|
int devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx,
|
|
struct devlink_dpipe_entry *entry)
|
|
{
|
|
return devlink_dpipe_entry_put(dump_ctx->skb, entry);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_append);
|
|
|
|
int devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx)
|
|
{
|
|
nla_nest_end(dump_ctx->skb, dump_ctx->nest);
|
|
genlmsg_end(dump_ctx->skb, dump_ctx->hdr);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_close);
|
|
|
|
void devlink_dpipe_entry_clear(struct devlink_dpipe_entry *entry)
|
|
|
|
{
|
|
unsigned int value_count, value_index;
|
|
struct devlink_dpipe_value *value;
|
|
|
|
value = entry->action_values;
|
|
value_count = entry->action_values_count;
|
|
for (value_index = 0; value_index < value_count; value_index++) {
|
|
kfree(value[value_index].value);
|
|
kfree(value[value_index].mask);
|
|
}
|
|
|
|
value = entry->match_values;
|
|
value_count = entry->match_values_count;
|
|
for (value_index = 0; value_index < value_count; value_index++) {
|
|
kfree(value[value_index].value);
|
|
kfree(value[value_index].mask);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_entry_clear);
|
|
|
|
static int devlink_dpipe_entries_fill(struct genl_info *info,
|
|
enum devlink_command cmd, int flags,
|
|
struct devlink_dpipe_table *table)
|
|
{
|
|
struct devlink_dpipe_dump_ctx dump_ctx;
|
|
struct nlmsghdr *nlh;
|
|
int err;
|
|
|
|
dump_ctx.skb = NULL;
|
|
dump_ctx.cmd = cmd;
|
|
dump_ctx.info = info;
|
|
|
|
err = table->table_ops->entries_dump(table->priv,
|
|
table->counters_enabled,
|
|
&dump_ctx);
|
|
if (err)
|
|
return err;
|
|
|
|
send_done:
|
|
nlh = nlmsg_put(dump_ctx.skb, info->snd_portid, info->snd_seq,
|
|
NLMSG_DONE, 0, flags | NLM_F_MULTI);
|
|
if (!nlh) {
|
|
err = devlink_dpipe_send_and_alloc_skb(&dump_ctx.skb, info);
|
|
if (err)
|
|
return err;
|
|
goto send_done;
|
|
}
|
|
return genlmsg_reply(dump_ctx.skb, info);
|
|
}
|
|
|
|
int devlink_nl_dpipe_entries_get_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct devlink_dpipe_table *table;
|
|
const char *table_name;
|
|
|
|
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_DPIPE_TABLE_NAME))
|
|
return -EINVAL;
|
|
|
|
table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]);
|
|
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
|
|
table_name, devlink);
|
|
if (!table)
|
|
return -EINVAL;
|
|
|
|
if (!table->table_ops->entries_dump)
|
|
return -EINVAL;
|
|
|
|
return devlink_dpipe_entries_fill(info, DEVLINK_CMD_DPIPE_ENTRIES_GET,
|
|
0, table);
|
|
}
|
|
|
|
static int devlink_dpipe_fields_put(struct sk_buff *skb,
|
|
const struct devlink_dpipe_header *header)
|
|
{
|
|
struct devlink_dpipe_field *field;
|
|
struct nlattr *field_attr;
|
|
int i;
|
|
|
|
for (i = 0; i < header->fields_count; i++) {
|
|
field = &header->fields[i];
|
|
field_attr = nla_nest_start_noflag(skb,
|
|
DEVLINK_ATTR_DPIPE_FIELD);
|
|
if (!field_attr)
|
|
return -EMSGSIZE;
|
|
if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_FIELD_NAME, field->name) ||
|
|
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) ||
|
|
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH, field->bitwidth) ||
|
|
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE, field->mapping_type))
|
|
goto nla_put_failure;
|
|
nla_nest_end(skb, field_attr);
|
|
}
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
nla_nest_cancel(skb, field_attr);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
static int devlink_dpipe_header_put(struct sk_buff *skb,
|
|
struct devlink_dpipe_header *header)
|
|
{
|
|
struct nlattr *fields_attr, *header_attr;
|
|
int err;
|
|
|
|
header_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_HEADER);
|
|
if (!header_attr)
|
|
return -EMSGSIZE;
|
|
|
|
if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_HEADER_NAME, header->name) ||
|
|
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) ||
|
|
nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global))
|
|
goto nla_put_failure;
|
|
|
|
fields_attr = nla_nest_start_noflag(skb,
|
|
DEVLINK_ATTR_DPIPE_HEADER_FIELDS);
|
|
if (!fields_attr)
|
|
goto nla_put_failure;
|
|
|
|
err = devlink_dpipe_fields_put(skb, header);
|
|
if (err) {
|
|
nla_nest_cancel(skb, fields_attr);
|
|
goto nla_put_failure;
|
|
}
|
|
nla_nest_end(skb, fields_attr);
|
|
nla_nest_end(skb, header_attr);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
err = -EMSGSIZE;
|
|
nla_nest_cancel(skb, header_attr);
|
|
return err;
|
|
}
|
|
|
|
static int devlink_dpipe_headers_fill(struct genl_info *info,
|
|
enum devlink_command cmd, int flags,
|
|
struct devlink_dpipe_headers *
|
|
dpipe_headers)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct nlattr *headers_attr;
|
|
struct sk_buff *skb = NULL;
|
|
struct nlmsghdr *nlh;
|
|
void *hdr;
|
|
int i, j;
|
|
int err;
|
|
|
|
i = 0;
|
|
start_again:
|
|
err = devlink_dpipe_send_and_alloc_skb(&skb, info);
|
|
if (err)
|
|
return err;
|
|
|
|
hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
|
|
&devlink_nl_family, NLM_F_MULTI, cmd);
|
|
if (!hdr) {
|
|
nlmsg_free(skb);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
if (devlink_nl_put_handle(skb, devlink))
|
|
goto nla_put_failure;
|
|
headers_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_HEADERS);
|
|
if (!headers_attr)
|
|
goto nla_put_failure;
|
|
|
|
j = 0;
|
|
for (; i < dpipe_headers->headers_count; i++) {
|
|
err = devlink_dpipe_header_put(skb, dpipe_headers->headers[i]);
|
|
if (err) {
|
|
if (!j)
|
|
goto err_table_put;
|
|
break;
|
|
}
|
|
j++;
|
|
}
|
|
nla_nest_end(skb, headers_attr);
|
|
genlmsg_end(skb, hdr);
|
|
if (i != dpipe_headers->headers_count)
|
|
goto start_again;
|
|
|
|
send_done:
|
|
nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
|
|
NLMSG_DONE, 0, flags | NLM_F_MULTI);
|
|
if (!nlh) {
|
|
err = devlink_dpipe_send_and_alloc_skb(&skb, info);
|
|
if (err)
|
|
return err;
|
|
goto send_done;
|
|
}
|
|
return genlmsg_reply(skb, info);
|
|
|
|
nla_put_failure:
|
|
err = -EMSGSIZE;
|
|
err_table_put:
|
|
nlmsg_free(skb);
|
|
return err;
|
|
}
|
|
|
|
int devlink_nl_dpipe_headers_get_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
|
|
if (!devlink->dpipe_headers)
|
|
return -EOPNOTSUPP;
|
|
return devlink_dpipe_headers_fill(info, DEVLINK_CMD_DPIPE_HEADERS_GET,
|
|
0, devlink->dpipe_headers);
|
|
}
|
|
|
|
static int devlink_dpipe_table_counters_set(struct devlink *devlink,
|
|
const char *table_name,
|
|
bool enable)
|
|
{
|
|
struct devlink_dpipe_table *table;
|
|
|
|
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
|
|
table_name, devlink);
|
|
if (!table)
|
|
return -EINVAL;
|
|
|
|
if (table->counter_control_extern)
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!(table->counters_enabled ^ enable))
|
|
return 0;
|
|
|
|
table->counters_enabled = enable;
|
|
if (table->table_ops->counters_set_update)
|
|
table->table_ops->counters_set_update(table->priv, enable);
|
|
return 0;
|
|
}
|
|
|
|
int devlink_nl_dpipe_table_counters_set_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
const char *table_name;
|
|
bool counters_enable;
|
|
|
|
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_DPIPE_TABLE_NAME) ||
|
|
GENL_REQ_ATTR_CHECK(info,
|
|
DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED))
|
|
return -EINVAL;
|
|
|
|
table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]);
|
|
counters_enable = !!nla_get_u8(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]);
|
|
|
|
return devlink_dpipe_table_counters_set(devlink, table_name,
|
|
counters_enable);
|
|
}
|
|
|
|
/**
|
|
* devl_dpipe_headers_register - register dpipe headers
|
|
*
|
|
* @devlink: devlink
|
|
* @dpipe_headers: dpipe header array
|
|
*
|
|
* Register the headers supported by hardware.
|
|
*/
|
|
void devl_dpipe_headers_register(struct devlink *devlink,
|
|
struct devlink_dpipe_headers *dpipe_headers)
|
|
{
|
|
lockdep_assert_held(&devlink->lock);
|
|
|
|
devlink->dpipe_headers = dpipe_headers;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_dpipe_headers_register);
|
|
|
|
/**
|
|
* devl_dpipe_headers_unregister - unregister dpipe headers
|
|
*
|
|
* @devlink: devlink
|
|
*
|
|
* Unregister the headers supported by hardware.
|
|
*/
|
|
void devl_dpipe_headers_unregister(struct devlink *devlink)
|
|
{
|
|
lockdep_assert_held(&devlink->lock);
|
|
|
|
devlink->dpipe_headers = NULL;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_dpipe_headers_unregister);
|
|
|
|
/**
|
|
* devlink_dpipe_table_counter_enabled - check if counter allocation
|
|
* required
|
|
* @devlink: devlink
|
|
* @table_name: tables name
|
|
*
|
|
* Used by driver to check if counter allocation is required.
|
|
* After counter allocation is turned on the table entries
|
|
* are updated to include counter statistics.
|
|
*
|
|
* After that point on the driver must respect the counter
|
|
* state so that each entry added to the table is added
|
|
* with a counter.
|
|
*/
|
|
bool devlink_dpipe_table_counter_enabled(struct devlink *devlink,
|
|
const char *table_name)
|
|
{
|
|
struct devlink_dpipe_table *table;
|
|
bool enabled;
|
|
|
|
rcu_read_lock();
|
|
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
|
|
table_name, devlink);
|
|
enabled = false;
|
|
if (table)
|
|
enabled = table->counters_enabled;
|
|
rcu_read_unlock();
|
|
return enabled;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_table_counter_enabled);
|
|
|
|
/**
|
|
* devl_dpipe_table_register - register dpipe table
|
|
*
|
|
* @devlink: devlink
|
|
* @table_name: table name
|
|
* @table_ops: table ops
|
|
* @priv: priv
|
|
* @counter_control_extern: external control for counters
|
|
*/
|
|
int devl_dpipe_table_register(struct devlink *devlink,
|
|
const char *table_name,
|
|
struct devlink_dpipe_table_ops *table_ops,
|
|
void *priv, bool counter_control_extern)
|
|
{
|
|
struct devlink_dpipe_table *table;
|
|
|
|
lockdep_assert_held(&devlink->lock);
|
|
|
|
if (WARN_ON(!table_ops->size_get))
|
|
return -EINVAL;
|
|
|
|
if (devlink_dpipe_table_find(&devlink->dpipe_table_list, table_name,
|
|
devlink))
|
|
return -EEXIST;
|
|
|
|
table = kzalloc(sizeof(*table), GFP_KERNEL);
|
|
if (!table)
|
|
return -ENOMEM;
|
|
|
|
table->name = table_name;
|
|
table->table_ops = table_ops;
|
|
table->priv = priv;
|
|
table->counter_control_extern = counter_control_extern;
|
|
|
|
list_add_tail_rcu(&table->list, &devlink->dpipe_table_list);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_dpipe_table_register);
|
|
|
|
/**
|
|
* devl_dpipe_table_unregister - unregister dpipe table
|
|
*
|
|
* @devlink: devlink
|
|
* @table_name: table name
|
|
*/
|
|
void devl_dpipe_table_unregister(struct devlink *devlink,
|
|
const char *table_name)
|
|
{
|
|
struct devlink_dpipe_table *table;
|
|
|
|
lockdep_assert_held(&devlink->lock);
|
|
|
|
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
|
|
table_name, devlink);
|
|
if (!table)
|
|
return;
|
|
list_del_rcu(&table->list);
|
|
kfree_rcu(table, rcu);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_dpipe_table_unregister);
|
|
|
|
/**
|
|
* devl_dpipe_table_resource_set - set the resource id
|
|
*
|
|
* @devlink: devlink
|
|
* @table_name: table name
|
|
* @resource_id: resource id
|
|
* @resource_units: number of resource's units consumed per table's entry
|
|
*/
|
|
int devl_dpipe_table_resource_set(struct devlink *devlink,
|
|
const char *table_name, u64 resource_id,
|
|
u64 resource_units)
|
|
{
|
|
struct devlink_dpipe_table *table;
|
|
|
|
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
|
|
table_name, devlink);
|
|
if (!table)
|
|
return -EINVAL;
|
|
|
|
table->resource_id = resource_id;
|
|
table->resource_units = resource_units;
|
|
table->resource_valid = true;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devl_dpipe_table_resource_set);
|