mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-07 12:26:24 -05:00
Merge branch 'devlink-finish-file-split-and-get-retire-leftover-c'
Jiri Pirko says: ==================== devlink: finish file split and get retire leftover.c This patchset finishes a move Jakub started and Moshe continued in the past. I was planning to do this for a long time, so here it is, finally. This patchset does not change any behaviour. It just splits leftover.c into per-object files and do necessary changes, like declaring functions used from other code, on the way. The last 3 patches are pushing the rest of the code into appropriate existing files. ==================== Link: https://lore.kernel.org/r/20230828061657.300667-1-jiri@resnulli.us Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-y := leftover.o core.o netlink.o netlink_gen.o dev.o health.o
|
||||
obj-y := core.o netlink.o netlink_gen.o dev.o port.o sb.o dpipe.o \
|
||||
resource.o param.o region.o health.o trap.o rate.o linecard.o
|
||||
|
||||
@@ -5,9 +5,15 @@
|
||||
*/
|
||||
|
||||
#include <net/genetlink.h>
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/devlink.h>
|
||||
|
||||
#include "devl_internal.h"
|
||||
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report);
|
||||
|
||||
DEFINE_XARRAY_FLAGS(devlinks, XA_FLAGS_ALLOC);
|
||||
|
||||
void *devlink_priv(struct devlink *devlink)
|
||||
|
||||
@@ -174,7 +174,7 @@ static int devlink_nl_fill(struct sk_buff *msg, struct devlink *devlink,
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
void devlink_notify(struct devlink *devlink, enum devlink_command cmd)
|
||||
static void devlink_notify(struct devlink *devlink, enum devlink_command cmd)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
@@ -230,6 +230,32 @@ int devlink_nl_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
|
||||
return devlink_nl_dumpit(msg, cb, devlink_nl_get_dump_one);
|
||||
}
|
||||
|
||||
void devlink_notify_register(struct devlink *devlink)
|
||||
{
|
||||
devlink_notify(devlink, DEVLINK_CMD_NEW);
|
||||
devlink_linecards_notify_register(devlink);
|
||||
devlink_ports_notify_register(devlink);
|
||||
devlink_trap_policers_notify_register(devlink);
|
||||
devlink_trap_groups_notify_register(devlink);
|
||||
devlink_traps_notify_register(devlink);
|
||||
devlink_rates_notify_register(devlink);
|
||||
devlink_regions_notify_register(devlink);
|
||||
devlink_params_notify_register(devlink);
|
||||
}
|
||||
|
||||
void devlink_notify_unregister(struct devlink *devlink)
|
||||
{
|
||||
devlink_params_notify_unregister(devlink);
|
||||
devlink_regions_notify_unregister(devlink);
|
||||
devlink_rates_notify_unregister(devlink);
|
||||
devlink_traps_notify_unregister(devlink);
|
||||
devlink_trap_groups_notify_unregister(devlink);
|
||||
devlink_trap_policers_notify_unregister(devlink);
|
||||
devlink_ports_notify_unregister(devlink);
|
||||
devlink_linecards_notify_unregister(devlink);
|
||||
devlink_notify(devlink, DEVLINK_CMD_DEL);
|
||||
}
|
||||
|
||||
static void devlink_reload_failed_set(struct devlink *devlink,
|
||||
bool reload_failed)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
|
||||
*/
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/notifier.h>
|
||||
@@ -11,6 +12,8 @@
|
||||
#include <linux/xarray.h>
|
||||
#include <net/devlink.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/rtnetlink.h>
|
||||
#include <rdma/ib_verbs.h>
|
||||
|
||||
#include "netlink_gen.h"
|
||||
|
||||
@@ -118,14 +121,9 @@ typedef int devlink_nl_dump_one_func_t(struct sk_buff *msg,
|
||||
struct netlink_callback *cb,
|
||||
int flags);
|
||||
|
||||
extern const struct genl_small_ops devlink_nl_small_ops[40];
|
||||
|
||||
struct devlink *
|
||||
devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs);
|
||||
|
||||
void devlink_notify_unregister(struct devlink *devlink);
|
||||
void devlink_notify_register(struct devlink *devlink);
|
||||
|
||||
int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb,
|
||||
devlink_nl_dump_one_func_t *dump_one);
|
||||
|
||||
@@ -147,13 +145,36 @@ devlink_nl_put_handle(struct sk_buff *msg, struct devlink *devlink)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int devlink_nl_msg_reply_and_new(struct sk_buff **msg, struct genl_info *info);
|
||||
|
||||
/* Notify */
|
||||
void devlink_notify(struct devlink *devlink, enum devlink_command cmd);
|
||||
void devlink_notify_register(struct devlink *devlink);
|
||||
void devlink_notify_unregister(struct devlink *devlink);
|
||||
void devlink_ports_notify_register(struct devlink *devlink);
|
||||
void devlink_ports_notify_unregister(struct devlink *devlink);
|
||||
void devlink_params_notify_register(struct devlink *devlink);
|
||||
void devlink_params_notify_unregister(struct devlink *devlink);
|
||||
void devlink_regions_notify_register(struct devlink *devlink);
|
||||
void devlink_regions_notify_unregister(struct devlink *devlink);
|
||||
void devlink_trap_policers_notify_register(struct devlink *devlink);
|
||||
void devlink_trap_policers_notify_unregister(struct devlink *devlink);
|
||||
void devlink_trap_groups_notify_register(struct devlink *devlink);
|
||||
void devlink_trap_groups_notify_unregister(struct devlink *devlink);
|
||||
void devlink_traps_notify_register(struct devlink *devlink);
|
||||
void devlink_traps_notify_unregister(struct devlink *devlink);
|
||||
void devlink_rates_notify_register(struct devlink *devlink);
|
||||
void devlink_rates_notify_unregister(struct devlink *devlink);
|
||||
void devlink_linecards_notify_register(struct devlink *devlink);
|
||||
void devlink_linecards_notify_unregister(struct devlink *devlink);
|
||||
|
||||
/* Ports */
|
||||
#define ASSERT_DEVLINK_PORT_INITIALIZED(devlink_port) \
|
||||
WARN_ON_ONCE(!(devlink_port)->initialized)
|
||||
|
||||
struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,
|
||||
unsigned int port_index);
|
||||
int devlink_port_netdevice_event(struct notifier_block *nb,
|
||||
unsigned long event, void *ptr);
|
||||
|
||||
struct devlink_port *
|
||||
devlink_port_get_from_info(struct devlink *devlink, struct genl_info *info);
|
||||
struct devlink_port *devlink_port_get_from_attrs(struct devlink *devlink,
|
||||
@@ -184,12 +205,62 @@ int devlink_resources_validate(struct devlink *devlink,
|
||||
int devlink_rate_nodes_check(struct devlink *devlink, u16 mode,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
||||
/* Linecards */
|
||||
struct devlink_linecard {
|
||||
struct list_head list;
|
||||
struct devlink *devlink;
|
||||
unsigned int index;
|
||||
const struct devlink_linecard_ops *ops;
|
||||
void *priv;
|
||||
enum devlink_linecard_state state;
|
||||
struct mutex state_lock; /* Protects state */
|
||||
const char *type;
|
||||
struct devlink_linecard_type *types;
|
||||
unsigned int types_count;
|
||||
struct devlink *nested_devlink;
|
||||
};
|
||||
|
||||
/* Devlink nl cmds */
|
||||
int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_flash_update(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_selftests_run(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_port_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_port_split_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_port_unsplit_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_port_new_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_port_del_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_sb_pool_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_sb_port_pool_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_sb_tc_pool_bind_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_sb_occ_snapshot_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_sb_occ_max_clear_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_dpipe_table_get(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_dpipe_entries_get(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_dpipe_headers_get(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_dpipe_table_counters_set(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_resource_set(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_resource_dump(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_param_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg,
|
||||
struct netlink_callback *cb);
|
||||
int devlink_nl_cmd_port_param_get_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_port_param_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_region_del(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb);
|
||||
int devlink_nl_cmd_health_reporter_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_health_reporter_recover_doit(struct sk_buff *skb,
|
||||
@@ -202,3 +273,13 @@ int devlink_nl_cmd_health_reporter_dump_clear_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_health_reporter_test_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_trap_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_trap_group_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_trap_policer_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int devlink_nl_cmd_rate_set_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_rate_new_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_rate_del_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_cmd_linecard_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
|
||||
917
net/devlink/dpipe.c
Normal file
917
net/devlink/dpipe.c
Normal file
@@ -0,0 +1,917 @@
|
||||
// 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_cmd_dpipe_table_get(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_cmd_dpipe_entries_get(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_cmd_dpipe_headers_get(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_cmd_dpipe_table_counters_set(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);
|
||||
File diff suppressed because it is too large
Load Diff
606
net/devlink/linecard.c
Normal file
606
net/devlink/linecard.c
Normal file
@@ -0,0 +1,606 @@
|
||||
// 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_linecard *
|
||||
devlink_linecard_get_by_index(struct devlink *devlink,
|
||||
unsigned int linecard_index)
|
||||
{
|
||||
struct devlink_linecard *devlink_linecard;
|
||||
|
||||
list_for_each_entry(devlink_linecard, &devlink->linecard_list, list) {
|
||||
if (devlink_linecard->index == linecard_index)
|
||||
return devlink_linecard;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool devlink_linecard_index_exists(struct devlink *devlink,
|
||||
unsigned int linecard_index)
|
||||
{
|
||||
return devlink_linecard_get_by_index(devlink, linecard_index);
|
||||
}
|
||||
|
||||
static struct devlink_linecard *
|
||||
devlink_linecard_get_from_attrs(struct devlink *devlink, struct nlattr **attrs)
|
||||
{
|
||||
if (attrs[DEVLINK_ATTR_LINECARD_INDEX]) {
|
||||
u32 linecard_index = nla_get_u32(attrs[DEVLINK_ATTR_LINECARD_INDEX]);
|
||||
struct devlink_linecard *linecard;
|
||||
|
||||
linecard = devlink_linecard_get_by_index(devlink, linecard_index);
|
||||
if (!linecard)
|
||||
return ERR_PTR(-ENODEV);
|
||||
return linecard;
|
||||
}
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static struct devlink_linecard *
|
||||
devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info)
|
||||
{
|
||||
return devlink_linecard_get_from_attrs(devlink, info->attrs);
|
||||
}
|
||||
|
||||
static int devlink_nl_put_nested_handle(struct sk_buff *msg, struct devlink *devlink)
|
||||
{
|
||||
struct nlattr *nested_attr;
|
||||
|
||||
nested_attr = nla_nest_start(msg, DEVLINK_ATTR_NESTED_DEVLINK);
|
||||
if (!nested_attr)
|
||||
return -EMSGSIZE;
|
||||
if (devlink_nl_put_handle(msg, devlink))
|
||||
goto nla_put_failure;
|
||||
|
||||
nla_nest_end(msg, nested_attr);
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
nla_nest_cancel(msg, nested_attr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
struct devlink_linecard_type {
|
||||
const char *type;
|
||||
const void *priv;
|
||||
};
|
||||
|
||||
static int devlink_nl_linecard_fill(struct sk_buff *msg,
|
||||
struct devlink *devlink,
|
||||
struct devlink_linecard *linecard,
|
||||
enum devlink_command cmd, u32 portid,
|
||||
u32 seq, int flags,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct devlink_linecard_type *linecard_type;
|
||||
struct nlattr *attr;
|
||||
void *hdr;
|
||||
int i;
|
||||
|
||||
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (devlink_nl_put_handle(msg, devlink))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_LINECARD_INDEX, linecard->index))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u8(msg, DEVLINK_ATTR_LINECARD_STATE, linecard->state))
|
||||
goto nla_put_failure;
|
||||
if (linecard->type &&
|
||||
nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE, linecard->type))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (linecard->types_count) {
|
||||
attr = nla_nest_start(msg,
|
||||
DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES);
|
||||
if (!attr)
|
||||
goto nla_put_failure;
|
||||
for (i = 0; i < linecard->types_count; i++) {
|
||||
linecard_type = &linecard->types[i];
|
||||
if (nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE,
|
||||
linecard_type->type)) {
|
||||
nla_nest_cancel(msg, attr);
|
||||
goto nla_put_failure;
|
||||
}
|
||||
}
|
||||
nla_nest_end(msg, attr);
|
||||
}
|
||||
|
||||
if (linecard->nested_devlink &&
|
||||
devlink_nl_put_nested_handle(msg, linecard->nested_devlink))
|
||||
goto nla_put_failure;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static void devlink_linecard_notify(struct devlink_linecard *linecard,
|
||||
enum devlink_command cmd)
|
||||
{
|
||||
struct devlink *devlink = linecard->devlink;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
WARN_ON(cmd != DEVLINK_CMD_LINECARD_NEW &&
|
||||
cmd != DEVLINK_CMD_LINECARD_DEL);
|
||||
|
||||
if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED))
|
||||
return;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
err = devlink_nl_linecard_fill(msg, devlink, linecard, cmd, 0, 0, 0,
|
||||
NULL);
|
||||
if (err) {
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
|
||||
msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void devlink_linecards_notify_register(struct devlink *devlink)
|
||||
{
|
||||
struct devlink_linecard *linecard;
|
||||
|
||||
list_for_each_entry(linecard, &devlink->linecard_list, list)
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
}
|
||||
|
||||
void devlink_linecards_notify_unregister(struct devlink *devlink)
|
||||
{
|
||||
struct devlink_linecard *linecard;
|
||||
|
||||
list_for_each_entry_reverse(linecard, &devlink->linecard_list, list)
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
|
||||
}
|
||||
|
||||
int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_linecard *linecard;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
linecard = devlink_linecard_get_from_info(devlink, info);
|
||||
if (IS_ERR(linecard))
|
||||
return PTR_ERR(linecard);
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&linecard->state_lock);
|
||||
err = devlink_nl_linecard_fill(msg, devlink, linecard,
|
||||
DEVLINK_CMD_LINECARD_NEW,
|
||||
info->snd_portid, info->snd_seq, 0,
|
||||
info->extack);
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
if (err) {
|
||||
nlmsg_free(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
}
|
||||
|
||||
static int devlink_nl_linecard_get_dump_one(struct sk_buff *msg,
|
||||
struct devlink *devlink,
|
||||
struct netlink_callback *cb,
|
||||
int flags)
|
||||
{
|
||||
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
|
||||
struct devlink_linecard *linecard;
|
||||
int idx = 0;
|
||||
int err = 0;
|
||||
|
||||
list_for_each_entry(linecard, &devlink->linecard_list, list) {
|
||||
if (idx < state->idx) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
mutex_lock(&linecard->state_lock);
|
||||
err = devlink_nl_linecard_fill(msg, devlink, linecard,
|
||||
DEVLINK_CMD_LINECARD_NEW,
|
||||
NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, flags,
|
||||
cb->extack);
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
if (err) {
|
||||
state->idx = idx;
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_linecard_get_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
return devlink_nl_dumpit(skb, cb, devlink_nl_linecard_get_dump_one);
|
||||
}
|
||||
|
||||
static struct devlink_linecard_type *
|
||||
devlink_linecard_type_lookup(struct devlink_linecard *linecard,
|
||||
const char *type)
|
||||
{
|
||||
struct devlink_linecard_type *linecard_type;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < linecard->types_count; i++) {
|
||||
linecard_type = &linecard->types[i];
|
||||
if (!strcmp(type, linecard_type->type))
|
||||
return linecard_type;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int devlink_linecard_type_set(struct devlink_linecard *linecard,
|
||||
const char *type,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct devlink_linecard_ops *ops = linecard->ops;
|
||||
struct devlink_linecard_type *linecard_type;
|
||||
int err;
|
||||
|
||||
mutex_lock(&linecard->state_lock);
|
||||
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
|
||||
NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
|
||||
NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
linecard_type = devlink_linecard_type_lookup(linecard, type);
|
||||
if (!linecard_type) {
|
||||
NL_SET_ERR_MSG(extack, "Unsupported line card type provided");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (linecard->state != DEVLINK_LINECARD_STATE_UNPROVISIONED &&
|
||||
linecard->state != DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
|
||||
NL_SET_ERR_MSG(extack, "Line card already provisioned");
|
||||
err = -EBUSY;
|
||||
/* Check if the line card is provisioned in the same
|
||||
* way the user asks. In case it is, make the operation
|
||||
* to return success.
|
||||
*/
|
||||
if (ops->same_provision &&
|
||||
ops->same_provision(linecard, linecard->priv,
|
||||
linecard_type->type,
|
||||
linecard_type->priv))
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING;
|
||||
linecard->type = linecard_type->type;
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
err = ops->provision(linecard, linecard->priv, linecard_type->type,
|
||||
linecard_type->priv, extack);
|
||||
if (err) {
|
||||
/* Provisioning failed. Assume the linecard is unprovisioned
|
||||
* for future operations.
|
||||
*/
|
||||
mutex_lock(&linecard->state_lock);
|
||||
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
|
||||
linecard->type = NULL;
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
}
|
||||
return err;
|
||||
|
||||
out:
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int devlink_linecard_type_unset(struct devlink_linecard *linecard,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&linecard->state_lock);
|
||||
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
|
||||
NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
|
||||
NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
|
||||
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
|
||||
linecard->type = NULL;
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONED) {
|
||||
NL_SET_ERR_MSG(extack, "Line card is not provisioned");
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONING;
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
err = linecard->ops->unprovision(linecard, linecard->priv,
|
||||
extack);
|
||||
if (err) {
|
||||
/* Unprovisioning failed. Assume the linecard is unprovisioned
|
||||
* for future operations.
|
||||
*/
|
||||
mutex_lock(&linecard->state_lock);
|
||||
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
|
||||
linecard->type = NULL;
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
}
|
||||
return err;
|
||||
|
||||
out:
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_linecard_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_linecard *linecard;
|
||||
int err;
|
||||
|
||||
linecard = devlink_linecard_get_from_info(devlink, info);
|
||||
if (IS_ERR(linecard))
|
||||
return PTR_ERR(linecard);
|
||||
|
||||
if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) {
|
||||
const char *type;
|
||||
|
||||
type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]);
|
||||
if (strcmp(type, "")) {
|
||||
err = devlink_linecard_type_set(linecard, type, extack);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
err = devlink_linecard_type_unset(linecard, extack);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devlink_linecard_types_init(struct devlink_linecard *linecard)
|
||||
{
|
||||
struct devlink_linecard_type *linecard_type;
|
||||
unsigned int count;
|
||||
int i;
|
||||
|
||||
count = linecard->ops->types_count(linecard, linecard->priv);
|
||||
linecard->types = kmalloc_array(count, sizeof(*linecard_type),
|
||||
GFP_KERNEL);
|
||||
if (!linecard->types)
|
||||
return -ENOMEM;
|
||||
linecard->types_count = count;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
linecard_type = &linecard->types[i];
|
||||
linecard->ops->types_get(linecard, linecard->priv, i,
|
||||
&linecard_type->type,
|
||||
&linecard_type->priv);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void devlink_linecard_types_fini(struct devlink_linecard *linecard)
|
||||
{
|
||||
kfree(linecard->types);
|
||||
}
|
||||
|
||||
/**
|
||||
* devl_linecard_create - Create devlink linecard
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @linecard_index: driver-specific numerical identifier of the linecard
|
||||
* @ops: linecards ops
|
||||
* @priv: user priv pointer
|
||||
*
|
||||
* Create devlink linecard instance with provided linecard index.
|
||||
* Caller can use any indexing, even hw-related one.
|
||||
*
|
||||
* Return: Line card structure or an ERR_PTR() encoded error code.
|
||||
*/
|
||||
struct devlink_linecard *
|
||||
devl_linecard_create(struct devlink *devlink, unsigned int linecard_index,
|
||||
const struct devlink_linecard_ops *ops, void *priv)
|
||||
{
|
||||
struct devlink_linecard *linecard;
|
||||
int err;
|
||||
|
||||
if (WARN_ON(!ops || !ops->provision || !ops->unprovision ||
|
||||
!ops->types_count || !ops->types_get))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (devlink_linecard_index_exists(devlink, linecard_index))
|
||||
return ERR_PTR(-EEXIST);
|
||||
|
||||
linecard = kzalloc(sizeof(*linecard), GFP_KERNEL);
|
||||
if (!linecard)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
linecard->devlink = devlink;
|
||||
linecard->index = linecard_index;
|
||||
linecard->ops = ops;
|
||||
linecard->priv = priv;
|
||||
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
|
||||
mutex_init(&linecard->state_lock);
|
||||
|
||||
err = devlink_linecard_types_init(linecard);
|
||||
if (err) {
|
||||
mutex_destroy(&linecard->state_lock);
|
||||
kfree(linecard);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
list_add_tail(&linecard->list, &devlink->linecard_list);
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
return linecard;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_linecard_create);
|
||||
|
||||
/**
|
||||
* devl_linecard_destroy - Destroy devlink linecard
|
||||
*
|
||||
* @linecard: devlink linecard
|
||||
*/
|
||||
void devl_linecard_destroy(struct devlink_linecard *linecard)
|
||||
{
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
|
||||
list_del(&linecard->list);
|
||||
devlink_linecard_types_fini(linecard);
|
||||
mutex_destroy(&linecard->state_lock);
|
||||
kfree(linecard);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_linecard_destroy);
|
||||
|
||||
/**
|
||||
* devlink_linecard_provision_set - Set provisioning on linecard
|
||||
*
|
||||
* @linecard: devlink linecard
|
||||
* @type: linecard type
|
||||
*
|
||||
* This is either called directly from the provision() op call or
|
||||
* as a result of the provision() op call asynchronously.
|
||||
*/
|
||||
void devlink_linecard_provision_set(struct devlink_linecard *linecard,
|
||||
const char *type)
|
||||
{
|
||||
mutex_lock(&linecard->state_lock);
|
||||
WARN_ON(linecard->type && strcmp(linecard->type, type));
|
||||
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
|
||||
linecard->type = type;
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_linecard_provision_set);
|
||||
|
||||
/**
|
||||
* devlink_linecard_provision_clear - Clear provisioning on linecard
|
||||
*
|
||||
* @linecard: devlink linecard
|
||||
*
|
||||
* This is either called directly from the unprovision() op call or
|
||||
* as a result of the unprovision() op call asynchronously.
|
||||
*/
|
||||
void devlink_linecard_provision_clear(struct devlink_linecard *linecard)
|
||||
{
|
||||
mutex_lock(&linecard->state_lock);
|
||||
WARN_ON(linecard->nested_devlink);
|
||||
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
|
||||
linecard->type = NULL;
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear);
|
||||
|
||||
/**
|
||||
* devlink_linecard_provision_fail - Fail provisioning on linecard
|
||||
*
|
||||
* @linecard: devlink linecard
|
||||
*
|
||||
* This is either called directly from the provision() op call or
|
||||
* as a result of the provision() op call asynchronously.
|
||||
*/
|
||||
void devlink_linecard_provision_fail(struct devlink_linecard *linecard)
|
||||
{
|
||||
mutex_lock(&linecard->state_lock);
|
||||
WARN_ON(linecard->nested_devlink);
|
||||
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED;
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail);
|
||||
|
||||
/**
|
||||
* devlink_linecard_activate - Set linecard active
|
||||
*
|
||||
* @linecard: devlink linecard
|
||||
*/
|
||||
void devlink_linecard_activate(struct devlink_linecard *linecard)
|
||||
{
|
||||
mutex_lock(&linecard->state_lock);
|
||||
WARN_ON(linecard->state != DEVLINK_LINECARD_STATE_PROVISIONED);
|
||||
linecard->state = DEVLINK_LINECARD_STATE_ACTIVE;
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_linecard_activate);
|
||||
|
||||
/**
|
||||
* devlink_linecard_deactivate - Set linecard inactive
|
||||
*
|
||||
* @linecard: devlink linecard
|
||||
*/
|
||||
void devlink_linecard_deactivate(struct devlink_linecard *linecard)
|
||||
{
|
||||
mutex_lock(&linecard->state_lock);
|
||||
switch (linecard->state) {
|
||||
case DEVLINK_LINECARD_STATE_ACTIVE:
|
||||
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
break;
|
||||
case DEVLINK_LINECARD_STATE_UNPROVISIONING:
|
||||
/* Line card is being deactivated as part
|
||||
* of unprovisioning flow.
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_linecard_deactivate);
|
||||
|
||||
/**
|
||||
* devlink_linecard_nested_dl_set - Attach/detach nested devlink
|
||||
* instance to linecard.
|
||||
*
|
||||
* @linecard: devlink linecard
|
||||
* @nested_devlink: devlink instance to attach or NULL to detach
|
||||
*/
|
||||
void devlink_linecard_nested_dl_set(struct devlink_linecard *linecard,
|
||||
struct devlink *nested_devlink)
|
||||
{
|
||||
mutex_lock(&linecard->state_lock);
|
||||
linecard->nested_devlink = nested_devlink;
|
||||
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
|
||||
mutex_unlock(&linecard->state_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);
|
||||
@@ -82,6 +82,21 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
|
||||
[DEVLINK_ATTR_REGION_DIRECT] = { .type = NLA_FLAG },
|
||||
};
|
||||
|
||||
int devlink_nl_msg_reply_and_new(struct sk_buff **msg, struct genl_info *info)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (*msg) {
|
||||
err = genlmsg_reply(*msg, info);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
*msg = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!*msg)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct devlink *
|
||||
devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs)
|
||||
{
|
||||
@@ -240,6 +255,257 @@ int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb,
|
||||
return devlink_nl_inst_iter_dumpit(msg, cb, flags, dump_one);
|
||||
}
|
||||
|
||||
static const struct genl_small_ops devlink_nl_small_ops[40] = {
|
||||
{
|
||||
.cmd = DEVLINK_CMD_PORT_SET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_port_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_RATE_SET,
|
||||
.doit = devlink_nl_cmd_rate_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_RATE_NEW,
|
||||
.doit = devlink_nl_cmd_rate_new_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_RATE_DEL,
|
||||
.doit = devlink_nl_cmd_rate_del_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_PORT_SPLIT,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_port_split_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_PORT_UNSPLIT,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_port_unsplit_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_PORT_NEW,
|
||||
.doit = devlink_nl_cmd_port_new_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_PORT_DEL,
|
||||
.doit = devlink_nl_cmd_port_del_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
|
||||
},
|
||||
|
||||
{
|
||||
.cmd = DEVLINK_CMD_LINECARD_SET,
|
||||
.doit = devlink_nl_cmd_linecard_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_SB_POOL_SET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_sb_pool_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_SB_PORT_POOL_SET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_sb_port_pool_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_SB_TC_POOL_BIND_SET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_sb_tc_pool_bind_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_SB_OCC_SNAPSHOT,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_sb_occ_snapshot_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_SB_OCC_MAX_CLEAR,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_sb_occ_max_clear_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_ESWITCH_GET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_eswitch_get_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_ESWITCH_SET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_eswitch_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_DPIPE_TABLE_GET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_dpipe_table_get,
|
||||
/* can be retrieved by unprivileged users */
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_DPIPE_ENTRIES_GET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_dpipe_entries_get,
|
||||
/* can be retrieved by unprivileged users */
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_DPIPE_HEADERS_GET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_dpipe_headers_get,
|
||||
/* can be retrieved by unprivileged users */
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_dpipe_table_counters_set,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_RESOURCE_SET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_resource_set,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_RESOURCE_DUMP,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_resource_dump,
|
||||
/* can be retrieved by unprivileged users */
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_RELOAD,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_reload,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_PARAM_SET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_param_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_PORT_PARAM_GET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_port_param_get_doit,
|
||||
.dumpit = devlink_nl_cmd_port_param_get_dumpit,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
|
||||
/* can be retrieved by unprivileged users */
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_PORT_PARAM_SET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_port_param_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_REGION_NEW,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_region_new,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_REGION_DEL,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_region_del,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_REGION_READ,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT |
|
||||
GENL_DONT_VALIDATE_DUMP_STRICT,
|
||||
.dumpit = devlink_nl_cmd_region_read_dumpit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_HEALTH_REPORTER_SET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_health_reporter_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_HEALTH_REPORTER_RECOVER,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_health_reporter_recover_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_health_reporter_diagnose_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT |
|
||||
GENL_DONT_VALIDATE_DUMP_STRICT,
|
||||
.dumpit = devlink_nl_cmd_health_reporter_dump_get_dumpit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_health_reporter_dump_clear_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_HEALTH_REPORTER_TEST,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_health_reporter_test_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_FLASH_UPDATE,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
||||
.doit = devlink_nl_cmd_flash_update,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_TRAP_SET,
|
||||
.doit = devlink_nl_cmd_trap_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_TRAP_GROUP_SET,
|
||||
.doit = devlink_nl_cmd_trap_group_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_TRAP_POLICER_SET,
|
||||
.doit = devlink_nl_cmd_trap_policer_set_doit,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_SELFTESTS_RUN,
|
||||
.doit = devlink_nl_cmd_selftests_run,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
/* -- No new ops here! Use split ops going forward! -- */
|
||||
};
|
||||
|
||||
struct genl_family devlink_nl_family __ro_after_init = {
|
||||
.name = DEVLINK_GENL_NAME,
|
||||
.version = DEVLINK_GENL_VERSION,
|
||||
|
||||
865
net/devlink/param.c
Normal file
865
net/devlink/param.c
Normal file
@@ -0,0 +1,865 @@
|
||||
// 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 const struct devlink_param devlink_param_generic[] = {
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,
|
||||
.name = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
|
||||
.name = DEVLINK_PARAM_GENERIC_MAX_MACS_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_MAX_MACS_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_SRIOV,
|
||||
.name = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_REGION_SNAPSHOT,
|
||||
.name = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI,
|
||||
.name = DEVLINK_PARAM_GENERIC_IGNORE_ARI_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_IGNORE_ARI_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
|
||||
.name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
|
||||
.name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
|
||||
.name = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_RESET_DEV_ON_DRV_PROBE,
|
||||
.name = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE,
|
||||
.name = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_REMOTE_DEV_RESET,
|
||||
.name = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH,
|
||||
.name = DEVLINK_PARAM_GENERIC_ENABLE_ETH_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_ENABLE_ETH_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA,
|
||||
.name = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET,
|
||||
.name = DEVLINK_PARAM_GENERIC_ENABLE_VNET_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_ENABLE_VNET_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_ENABLE_IWARP,
|
||||
.name = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,
|
||||
.name = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_TYPE,
|
||||
},
|
||||
{
|
||||
.id = DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE,
|
||||
.name = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_NAME,
|
||||
.type = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
static int devlink_param_generic_verify(const struct devlink_param *param)
|
||||
{
|
||||
/* verify it match generic parameter by id and name */
|
||||
if (param->id > DEVLINK_PARAM_GENERIC_ID_MAX)
|
||||
return -EINVAL;
|
||||
if (strcmp(param->name, devlink_param_generic[param->id].name))
|
||||
return -ENOENT;
|
||||
|
||||
WARN_ON(param->type != devlink_param_generic[param->id].type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devlink_param_driver_verify(const struct devlink_param *param)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (param->id <= DEVLINK_PARAM_GENERIC_ID_MAX)
|
||||
return -EINVAL;
|
||||
/* verify no such name in generic params */
|
||||
for (i = 0; i <= DEVLINK_PARAM_GENERIC_ID_MAX; i++)
|
||||
if (!strcmp(param->name, devlink_param_generic[i].name))
|
||||
return -EEXIST;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct devlink_param_item *
|
||||
devlink_param_find_by_name(struct xarray *params, const char *param_name)
|
||||
{
|
||||
struct devlink_param_item *param_item;
|
||||
unsigned long param_id;
|
||||
|
||||
xa_for_each(params, param_id, param_item) {
|
||||
if (!strcmp(param_item->param->name, param_name))
|
||||
return param_item;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct devlink_param_item *
|
||||
devlink_param_find_by_id(struct xarray *params, u32 param_id)
|
||||
{
|
||||
return xa_load(params, param_id);
|
||||
}
|
||||
|
||||
static bool
|
||||
devlink_param_cmode_is_supported(const struct devlink_param *param,
|
||||
enum devlink_param_cmode cmode)
|
||||
{
|
||||
return test_bit(cmode, ¶m->supported_cmodes);
|
||||
}
|
||||
|
||||
static int devlink_param_get(struct devlink *devlink,
|
||||
const struct devlink_param *param,
|
||||
struct devlink_param_gset_ctx *ctx)
|
||||
{
|
||||
if (!param->get)
|
||||
return -EOPNOTSUPP;
|
||||
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)
|
||||
{
|
||||
switch (param_type) {
|
||||
case DEVLINK_PARAM_TYPE_U8:
|
||||
return NLA_U8;
|
||||
case DEVLINK_PARAM_TYPE_U16:
|
||||
return NLA_U16;
|
||||
case DEVLINK_PARAM_TYPE_U32:
|
||||
return NLA_U32;
|
||||
case DEVLINK_PARAM_TYPE_STRING:
|
||||
return NLA_STRING;
|
||||
case DEVLINK_PARAM_TYPE_BOOL:
|
||||
return NLA_FLAG;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_nl_param_value_fill_one(struct sk_buff *msg,
|
||||
enum devlink_param_type type,
|
||||
enum devlink_param_cmode cmode,
|
||||
union devlink_param_value val)
|
||||
{
|
||||
struct nlattr *param_value_attr;
|
||||
|
||||
param_value_attr = nla_nest_start_noflag(msg,
|
||||
DEVLINK_ATTR_PARAM_VALUE);
|
||||
if (!param_value_attr)
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_CMODE, cmode))
|
||||
goto value_nest_cancel;
|
||||
|
||||
switch (type) {
|
||||
case DEVLINK_PARAM_TYPE_U8:
|
||||
if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu8))
|
||||
goto value_nest_cancel;
|
||||
break;
|
||||
case DEVLINK_PARAM_TYPE_U16:
|
||||
if (nla_put_u16(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu16))
|
||||
goto value_nest_cancel;
|
||||
break;
|
||||
case DEVLINK_PARAM_TYPE_U32:
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu32))
|
||||
goto value_nest_cancel;
|
||||
break;
|
||||
case DEVLINK_PARAM_TYPE_STRING:
|
||||
if (nla_put_string(msg, DEVLINK_ATTR_PARAM_VALUE_DATA,
|
||||
val.vstr))
|
||||
goto value_nest_cancel;
|
||||
break;
|
||||
case DEVLINK_PARAM_TYPE_BOOL:
|
||||
if (val.vbool &&
|
||||
nla_put_flag(msg, DEVLINK_ATTR_PARAM_VALUE_DATA))
|
||||
goto value_nest_cancel;
|
||||
break;
|
||||
}
|
||||
|
||||
nla_nest_end(msg, param_value_attr);
|
||||
return 0;
|
||||
|
||||
value_nest_cancel:
|
||||
nla_nest_cancel(msg, param_value_attr);
|
||||
nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
|
||||
unsigned int port_index,
|
||||
struct devlink_param_item *param_item,
|
||||
enum devlink_command cmd,
|
||||
u32 portid, u32 seq, int flags)
|
||||
{
|
||||
union devlink_param_value param_value[DEVLINK_PARAM_CMODE_MAX + 1];
|
||||
bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};
|
||||
const struct devlink_param *param = param_item->param;
|
||||
struct devlink_param_gset_ctx ctx;
|
||||
struct nlattr *param_values_list;
|
||||
struct nlattr *param_attr;
|
||||
int nla_type;
|
||||
void *hdr;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
/* Get value from driver part to driverinit configuration mode */
|
||||
for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
|
||||
if (!devlink_param_cmode_is_supported(param, i))
|
||||
continue;
|
||||
if (i == DEVLINK_PARAM_CMODE_DRIVERINIT) {
|
||||
if (param_item->driverinit_value_new_valid)
|
||||
param_value[i] = param_item->driverinit_value_new;
|
||||
else if (param_item->driverinit_value_valid)
|
||||
param_value[i] = param_item->driverinit_value;
|
||||
else
|
||||
return -EOPNOTSUPP;
|
||||
} else {
|
||||
ctx.cmode = i;
|
||||
err = devlink_param_get(devlink, param, &ctx);
|
||||
if (err)
|
||||
return err;
|
||||
param_value[i] = ctx.val;
|
||||
}
|
||||
param_value_set[i] = true;
|
||||
}
|
||||
|
||||
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (devlink_nl_put_handle(msg, devlink))
|
||||
goto genlmsg_cancel;
|
||||
|
||||
if (cmd == DEVLINK_CMD_PORT_PARAM_GET ||
|
||||
cmd == DEVLINK_CMD_PORT_PARAM_NEW ||
|
||||
cmd == DEVLINK_CMD_PORT_PARAM_DEL)
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, port_index))
|
||||
goto genlmsg_cancel;
|
||||
|
||||
param_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_PARAM);
|
||||
if (!param_attr)
|
||||
goto genlmsg_cancel;
|
||||
if (nla_put_string(msg, DEVLINK_ATTR_PARAM_NAME, param->name))
|
||||
goto param_nest_cancel;
|
||||
if (param->generic && nla_put_flag(msg, DEVLINK_ATTR_PARAM_GENERIC))
|
||||
goto param_nest_cancel;
|
||||
|
||||
nla_type = devlink_param_type_to_nla_type(param->type);
|
||||
if (nla_type < 0)
|
||||
goto param_nest_cancel;
|
||||
if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_TYPE, nla_type))
|
||||
goto param_nest_cancel;
|
||||
|
||||
param_values_list = nla_nest_start_noflag(msg,
|
||||
DEVLINK_ATTR_PARAM_VALUES_LIST);
|
||||
if (!param_values_list)
|
||||
goto param_nest_cancel;
|
||||
|
||||
for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
|
||||
if (!param_value_set[i])
|
||||
continue;
|
||||
err = devlink_nl_param_value_fill_one(msg, param->type,
|
||||
i, param_value[i]);
|
||||
if (err)
|
||||
goto values_list_nest_cancel;
|
||||
}
|
||||
|
||||
nla_nest_end(msg, param_values_list);
|
||||
nla_nest_end(msg, param_attr);
|
||||
genlmsg_end(msg, hdr);
|
||||
return 0;
|
||||
|
||||
values_list_nest_cancel:
|
||||
nla_nest_end(msg, param_values_list);
|
||||
param_nest_cancel:
|
||||
nla_nest_cancel(msg, param_attr);
|
||||
genlmsg_cancel:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static void devlink_param_notify(struct devlink *devlink,
|
||||
unsigned int port_index,
|
||||
struct devlink_param_item *param_item,
|
||||
enum devlink_command cmd)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
WARN_ON(cmd != DEVLINK_CMD_PARAM_NEW && cmd != DEVLINK_CMD_PARAM_DEL &&
|
||||
cmd != DEVLINK_CMD_PORT_PARAM_NEW &&
|
||||
cmd != DEVLINK_CMD_PORT_PARAM_DEL);
|
||||
|
||||
/* devlink_notify_register() / devlink_notify_unregister()
|
||||
* will replay the notifications if the params are added/removed
|
||||
* outside of the lifetime of the instance.
|
||||
*/
|
||||
if (!devl_is_registered(devlink))
|
||||
return;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return;
|
||||
err = devlink_nl_param_fill(msg, devlink, port_index, param_item, cmd,
|
||||
0, 0, 0);
|
||||
if (err) {
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
|
||||
msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void devlink_params_notify(struct devlink *devlink,
|
||||
enum devlink_command cmd)
|
||||
{
|
||||
struct devlink_param_item *param_item;
|
||||
unsigned long param_id;
|
||||
|
||||
xa_for_each(&devlink->params, param_id, param_item)
|
||||
devlink_param_notify(devlink, 0, param_item, cmd);
|
||||
}
|
||||
|
||||
void devlink_params_notify_register(struct devlink *devlink)
|
||||
{
|
||||
devlink_params_notify(devlink, DEVLINK_CMD_PARAM_NEW);
|
||||
}
|
||||
|
||||
void devlink_params_notify_unregister(struct devlink *devlink)
|
||||
{
|
||||
devlink_params_notify(devlink, DEVLINK_CMD_PARAM_DEL);
|
||||
}
|
||||
|
||||
static int devlink_nl_param_get_dump_one(struct sk_buff *msg,
|
||||
struct devlink *devlink,
|
||||
struct netlink_callback *cb,
|
||||
int flags)
|
||||
{
|
||||
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
|
||||
struct devlink_param_item *param_item;
|
||||
unsigned long param_id;
|
||||
int err = 0;
|
||||
|
||||
xa_for_each_start(&devlink->params, param_id, param_item, state->idx) {
|
||||
err = devlink_nl_param_fill(msg, devlink, 0, param_item,
|
||||
DEVLINK_CMD_PARAM_GET,
|
||||
NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, flags);
|
||||
if (err == -EOPNOTSUPP) {
|
||||
err = 0;
|
||||
} else if (err) {
|
||||
state->idx = param_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_param_get_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
return devlink_nl_dumpit(skb, cb, devlink_nl_param_get_dump_one);
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_param_type_get_from_info(struct genl_info *info,
|
||||
enum devlink_param_type *param_type)
|
||||
{
|
||||
if (GENL_REQ_ATTR_CHECK(info, 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)
|
||||
{
|
||||
struct nlattr *param_data;
|
||||
int len;
|
||||
|
||||
param_data = info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA];
|
||||
|
||||
if (param->type != DEVLINK_PARAM_TYPE_BOOL && !param_data)
|
||||
return -EINVAL;
|
||||
|
||||
switch (param->type) {
|
||||
case DEVLINK_PARAM_TYPE_U8:
|
||||
if (nla_len(param_data) != sizeof(u8))
|
||||
return -EINVAL;
|
||||
value->vu8 = nla_get_u8(param_data);
|
||||
break;
|
||||
case DEVLINK_PARAM_TYPE_U16:
|
||||
if (nla_len(param_data) != sizeof(u16))
|
||||
return -EINVAL;
|
||||
value->vu16 = nla_get_u16(param_data);
|
||||
break;
|
||||
case DEVLINK_PARAM_TYPE_U32:
|
||||
if (nla_len(param_data) != sizeof(u32))
|
||||
return -EINVAL;
|
||||
value->vu32 = nla_get_u32(param_data);
|
||||
break;
|
||||
case DEVLINK_PARAM_TYPE_STRING:
|
||||
len = strnlen(nla_data(param_data), nla_len(param_data));
|
||||
if (len == nla_len(param_data) ||
|
||||
len >= __DEVLINK_PARAM_MAX_STRING_VALUE)
|
||||
return -EINVAL;
|
||||
strcpy(value->vstr, nla_data(param_data));
|
||||
break;
|
||||
case DEVLINK_PARAM_TYPE_BOOL:
|
||||
if (param_data && nla_len(param_data))
|
||||
return -EINVAL;
|
||||
value->vbool = nla_get_flag(param_data);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct devlink_param_item *
|
||||
devlink_param_get_from_info(struct xarray *params, struct genl_info *info)
|
||||
{
|
||||
char *param_name;
|
||||
|
||||
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_NAME))
|
||||
return NULL;
|
||||
|
||||
param_name = nla_data(info->attrs[DEVLINK_ATTR_PARAM_NAME]);
|
||||
return devlink_param_find_by_name(params, param_name);
|
||||
}
|
||||
|
||||
int devlink_nl_param_get_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_param_item *param_item;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
param_item = devlink_param_get_from_info(&devlink->params, info);
|
||||
if (!param_item)
|
||||
return -EINVAL;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
err = devlink_nl_param_fill(msg, devlink, 0, param_item,
|
||||
DEVLINK_CMD_PARAM_GET,
|
||||
info->snd_portid, info->snd_seq, 0);
|
||||
if (err) {
|
||||
nlmsg_free(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
}
|
||||
|
||||
static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
|
||||
unsigned int port_index,
|
||||
struct xarray *params,
|
||||
struct genl_info *info,
|
||||
enum devlink_command cmd)
|
||||
{
|
||||
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(params, info);
|
||||
if (!param_item)
|
||||
return -EINVAL;
|
||||
param = param_item->param;
|
||||
err = devlink_param_type_get_from_info(info, ¶m_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 (GENL_REQ_ATTR_CHECK(info, 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_new = value;
|
||||
param_item->driverinit_value_new_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;
|
||||
}
|
||||
|
||||
devlink_param_notify(devlink, port_index, param_item, cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_param_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
|
||||
return __devlink_nl_cmd_param_set_doit(devlink, 0, &devlink->params,
|
||||
info, DEVLINK_CMD_PARAM_NEW);
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
NL_SET_ERR_MSG(cb->extack, "Port params are not supported");
|
||||
return msg->len;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_port_param_get_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
NL_SET_ERR_MSG(info->extack, "Port params are not supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_port_param_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
NL_SET_ERR_MSG(info->extack, "Port params are not supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int devlink_param_verify(const struct devlink_param *param)
|
||||
{
|
||||
if (!param || !param->name || !param->supported_cmodes)
|
||||
return -EINVAL;
|
||||
if (param->generic)
|
||||
return devlink_param_generic_verify(param);
|
||||
else
|
||||
return devlink_param_driver_verify(param);
|
||||
}
|
||||
|
||||
static int devlink_param_register(struct devlink *devlink,
|
||||
const struct devlink_param *param)
|
||||
{
|
||||
struct devlink_param_item *param_item;
|
||||
int err;
|
||||
|
||||
WARN_ON(devlink_param_verify(param));
|
||||
WARN_ON(devlink_param_find_by_name(&devlink->params, param->name));
|
||||
|
||||
if (param->supported_cmodes == BIT(DEVLINK_PARAM_CMODE_DRIVERINIT))
|
||||
WARN_ON(param->get || param->set);
|
||||
else
|
||||
WARN_ON(!param->get || !param->set);
|
||||
|
||||
param_item = kzalloc(sizeof(*param_item), GFP_KERNEL);
|
||||
if (!param_item)
|
||||
return -ENOMEM;
|
||||
|
||||
param_item->param = param;
|
||||
|
||||
err = xa_insert(&devlink->params, param->id, param_item, GFP_KERNEL);
|
||||
if (err)
|
||||
goto err_xa_insert;
|
||||
|
||||
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
|
||||
return 0;
|
||||
|
||||
err_xa_insert:
|
||||
kfree(param_item);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void devlink_param_unregister(struct devlink *devlink,
|
||||
const struct devlink_param *param)
|
||||
{
|
||||
struct devlink_param_item *param_item;
|
||||
|
||||
param_item = devlink_param_find_by_id(&devlink->params, param->id);
|
||||
if (WARN_ON(!param_item))
|
||||
return;
|
||||
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_DEL);
|
||||
xa_erase(&devlink->params, param->id);
|
||||
kfree(param_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* devl_params_register - register configuration parameters
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @params: configuration parameters array
|
||||
* @params_count: number of parameters provided
|
||||
*
|
||||
* Register the configuration parameters supported by the driver.
|
||||
*/
|
||||
int devl_params_register(struct devlink *devlink,
|
||||
const struct devlink_param *params,
|
||||
size_t params_count)
|
||||
{
|
||||
const struct devlink_param *param = params;
|
||||
int i, err;
|
||||
|
||||
lockdep_assert_held(&devlink->lock);
|
||||
|
||||
for (i = 0; i < params_count; i++, param++) {
|
||||
err = devlink_param_register(devlink, param);
|
||||
if (err)
|
||||
goto rollback;
|
||||
}
|
||||
return 0;
|
||||
|
||||
rollback:
|
||||
if (!i)
|
||||
return err;
|
||||
|
||||
for (param--; i > 0; i--, param--)
|
||||
devlink_param_unregister(devlink, param);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_params_register);
|
||||
|
||||
int devlink_params_register(struct devlink *devlink,
|
||||
const struct devlink_param *params,
|
||||
size_t params_count)
|
||||
{
|
||||
int err;
|
||||
|
||||
devl_lock(devlink);
|
||||
err = devl_params_register(devlink, params, params_count);
|
||||
devl_unlock(devlink);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_params_register);
|
||||
|
||||
/**
|
||||
* devl_params_unregister - unregister configuration parameters
|
||||
* @devlink: devlink
|
||||
* @params: configuration parameters to unregister
|
||||
* @params_count: number of parameters provided
|
||||
*/
|
||||
void devl_params_unregister(struct devlink *devlink,
|
||||
const struct devlink_param *params,
|
||||
size_t params_count)
|
||||
{
|
||||
const struct devlink_param *param = params;
|
||||
int i;
|
||||
|
||||
lockdep_assert_held(&devlink->lock);
|
||||
|
||||
for (i = 0; i < params_count; i++, param++)
|
||||
devlink_param_unregister(devlink, param);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_params_unregister);
|
||||
|
||||
void devlink_params_unregister(struct devlink *devlink,
|
||||
const struct devlink_param *params,
|
||||
size_t params_count)
|
||||
{
|
||||
devl_lock(devlink);
|
||||
devl_params_unregister(devlink, params, params_count);
|
||||
devl_unlock(devlink);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_params_unregister);
|
||||
|
||||
/**
|
||||
* devl_param_driverinit_value_get - get configuration parameter
|
||||
* value for driver initializing
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @param_id: parameter ID
|
||||
* @val: pointer to store the value of parameter in driverinit
|
||||
* configuration mode
|
||||
*
|
||||
* This function should be used by the driver to get driverinit
|
||||
* configuration for initialization after reload command.
|
||||
*
|
||||
* Note that lockless call of this function relies on the
|
||||
* driver to maintain following basic sane behavior:
|
||||
* 1) Driver ensures a call to this function cannot race with
|
||||
* registering/unregistering the parameter with the same parameter ID.
|
||||
* 2) Driver ensures a call to this function cannot race with
|
||||
* devl_param_driverinit_value_set() call with the same parameter ID.
|
||||
* 3) Driver ensures a call to this function cannot race with
|
||||
* reload operation.
|
||||
* If the driver is not able to comply, it has to take the devlink->lock
|
||||
* while calling this.
|
||||
*/
|
||||
int devl_param_driverinit_value_get(struct devlink *devlink, u32 param_id,
|
||||
union devlink_param_value *val)
|
||||
{
|
||||
struct devlink_param_item *param_item;
|
||||
|
||||
if (WARN_ON(!devlink_reload_supported(devlink->ops)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
param_item = devlink_param_find_by_id(&devlink->params, param_id);
|
||||
if (!param_item)
|
||||
return -EINVAL;
|
||||
|
||||
if (!param_item->driverinit_value_valid)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (WARN_ON(!devlink_param_cmode_is_supported(param_item->param,
|
||||
DEVLINK_PARAM_CMODE_DRIVERINIT)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
*val = param_item->driverinit_value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_param_driverinit_value_get);
|
||||
|
||||
/**
|
||||
* devl_param_driverinit_value_set - set value of configuration
|
||||
* parameter for driverinit
|
||||
* configuration mode
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @param_id: parameter ID
|
||||
* @init_val: value of parameter to set for driverinit configuration mode
|
||||
*
|
||||
* This function should be used by the driver to set driverinit
|
||||
* configuration mode default value.
|
||||
*/
|
||||
void devl_param_driverinit_value_set(struct devlink *devlink, u32 param_id,
|
||||
union devlink_param_value init_val)
|
||||
{
|
||||
struct devlink_param_item *param_item;
|
||||
|
||||
devl_assert_locked(devlink);
|
||||
|
||||
param_item = devlink_param_find_by_id(&devlink->params, param_id);
|
||||
if (WARN_ON(!param_item))
|
||||
return;
|
||||
|
||||
if (WARN_ON(!devlink_param_cmode_is_supported(param_item->param,
|
||||
DEVLINK_PARAM_CMODE_DRIVERINIT)))
|
||||
return;
|
||||
|
||||
param_item->driverinit_value = init_val;
|
||||
param_item->driverinit_value_valid = true;
|
||||
|
||||
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_param_driverinit_value_set);
|
||||
|
||||
void devlink_params_driverinit_load_new(struct devlink *devlink)
|
||||
{
|
||||
struct devlink_param_item *param_item;
|
||||
unsigned long param_id;
|
||||
|
||||
xa_for_each(&devlink->params, param_id, param_item) {
|
||||
if (!devlink_param_cmode_is_supported(param_item->param,
|
||||
DEVLINK_PARAM_CMODE_DRIVERINIT) ||
|
||||
!param_item->driverinit_value_new_valid)
|
||||
continue;
|
||||
param_item->driverinit_value = param_item->driverinit_value_new;
|
||||
param_item->driverinit_value_valid = true;
|
||||
param_item->driverinit_value_new_valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* devl_param_value_changed - notify devlink on a parameter's value
|
||||
* change. Should be called by the driver
|
||||
* right after the change.
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @param_id: parameter ID
|
||||
*
|
||||
* This function should be used by the driver to notify devlink on value
|
||||
* change, excluding driverinit configuration mode.
|
||||
* For driverinit configuration mode driver should use the function
|
||||
*/
|
||||
void devl_param_value_changed(struct devlink *devlink, u32 param_id)
|
||||
{
|
||||
struct devlink_param_item *param_item;
|
||||
|
||||
param_item = devlink_param_find_by_id(&devlink->params, param_id);
|
||||
WARN_ON(!param_item);
|
||||
|
||||
devlink_param_notify(devlink, 0, param_item, DEVLINK_CMD_PARAM_NEW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_param_value_changed);
|
||||
1515
net/devlink/port.c
Normal file
1515
net/devlink/port.c
Normal file
File diff suppressed because it is too large
Load Diff
722
net/devlink/rate.c
Normal file
722
net/devlink/rate.c
Normal file
@@ -0,0 +1,722 @@
|
||||
// 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 inline bool
|
||||
devlink_rate_is_leaf(struct devlink_rate *devlink_rate)
|
||||
{
|
||||
return devlink_rate->type == DEVLINK_RATE_TYPE_LEAF;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
devlink_rate_is_node(struct devlink_rate *devlink_rate)
|
||||
{
|
||||
return devlink_rate->type == DEVLINK_RATE_TYPE_NODE;
|
||||
}
|
||||
|
||||
static struct devlink_rate *
|
||||
devlink_rate_leaf_get_from_info(struct devlink *devlink, struct genl_info *info)
|
||||
{
|
||||
struct devlink_rate *devlink_rate;
|
||||
struct devlink_port *devlink_port;
|
||||
|
||||
devlink_port = devlink_port_get_from_attrs(devlink, info->attrs);
|
||||
if (IS_ERR(devlink_port))
|
||||
return ERR_CAST(devlink_port);
|
||||
devlink_rate = devlink_port->devlink_rate;
|
||||
return devlink_rate ?: ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static struct devlink_rate *
|
||||
devlink_rate_node_get_by_name(struct devlink *devlink, const char *node_name)
|
||||
{
|
||||
static struct devlink_rate *devlink_rate;
|
||||
|
||||
list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
|
||||
if (devlink_rate_is_node(devlink_rate) &&
|
||||
!strcmp(node_name, devlink_rate->name))
|
||||
return devlink_rate;
|
||||
}
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static struct devlink_rate *
|
||||
devlink_rate_node_get_from_attrs(struct devlink *devlink, struct nlattr **attrs)
|
||||
{
|
||||
const char *rate_node_name;
|
||||
size_t len;
|
||||
|
||||
if (!attrs[DEVLINK_ATTR_RATE_NODE_NAME])
|
||||
return ERR_PTR(-EINVAL);
|
||||
rate_node_name = nla_data(attrs[DEVLINK_ATTR_RATE_NODE_NAME]);
|
||||
len = strlen(rate_node_name);
|
||||
/* Name cannot be empty or decimal number */
|
||||
if (!len || strspn(rate_node_name, "0123456789") == len)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return devlink_rate_node_get_by_name(devlink, rate_node_name);
|
||||
}
|
||||
|
||||
static struct devlink_rate *
|
||||
devlink_rate_node_get_from_info(struct devlink *devlink, struct genl_info *info)
|
||||
{
|
||||
return devlink_rate_node_get_from_attrs(devlink, info->attrs);
|
||||
}
|
||||
|
||||
static struct devlink_rate *
|
||||
devlink_rate_get_from_info(struct devlink *devlink, struct genl_info *info)
|
||||
{
|
||||
struct nlattr **attrs = info->attrs;
|
||||
|
||||
if (attrs[DEVLINK_ATTR_PORT_INDEX])
|
||||
return devlink_rate_leaf_get_from_info(devlink, info);
|
||||
else if (attrs[DEVLINK_ATTR_RATE_NODE_NAME])
|
||||
return devlink_rate_node_get_from_info(devlink, info);
|
||||
else
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static int devlink_nl_rate_fill(struct sk_buff *msg,
|
||||
struct devlink_rate *devlink_rate,
|
||||
enum devlink_command cmd, u32 portid, u32 seq,
|
||||
int flags, struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct devlink *devlink = devlink_rate->devlink;
|
||||
void *hdr;
|
||||
|
||||
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (devlink_nl_put_handle(msg, devlink))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_u16(msg, DEVLINK_ATTR_RATE_TYPE, devlink_rate->type))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (devlink_rate_is_leaf(devlink_rate)) {
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX,
|
||||
devlink_rate->devlink_port->index))
|
||||
goto nla_put_failure;
|
||||
} else if (devlink_rate_is_node(devlink_rate)) {
|
||||
if (nla_put_string(msg, DEVLINK_ATTR_RATE_NODE_NAME,
|
||||
devlink_rate->name))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_SHARE,
|
||||
devlink_rate->tx_share, DEVLINK_ATTR_PAD))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_u64_64bit(msg, DEVLINK_ATTR_RATE_TX_MAX,
|
||||
devlink_rate->tx_max, DEVLINK_ATTR_PAD))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_PRIORITY,
|
||||
devlink_rate->tx_priority))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_WEIGHT,
|
||||
devlink_rate->tx_weight))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (devlink_rate->parent)
|
||||
if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME,
|
||||
devlink_rate->parent->name))
|
||||
goto nla_put_failure;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static void devlink_rate_notify(struct devlink_rate *devlink_rate,
|
||||
enum devlink_command cmd)
|
||||
{
|
||||
struct devlink *devlink = devlink_rate->devlink;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
WARN_ON(cmd != DEVLINK_CMD_RATE_NEW && cmd != DEVLINK_CMD_RATE_DEL);
|
||||
|
||||
if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED))
|
||||
return;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
err = devlink_nl_rate_fill(msg, devlink_rate, cmd, 0, 0, 0, NULL);
|
||||
if (err) {
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), msg,
|
||||
0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void devlink_rates_notify_register(struct devlink *devlink)
|
||||
{
|
||||
struct devlink_rate *rate_node;
|
||||
|
||||
list_for_each_entry(rate_node, &devlink->rate_list, list)
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
|
||||
}
|
||||
|
||||
void devlink_rates_notify_unregister(struct devlink *devlink)
|
||||
{
|
||||
struct devlink_rate *rate_node;
|
||||
|
||||
list_for_each_entry_reverse(rate_node, &devlink->rate_list, list)
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_nl_rate_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
|
||||
struct netlink_callback *cb, int flags)
|
||||
{
|
||||
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
|
||||
struct devlink_rate *devlink_rate;
|
||||
int idx = 0;
|
||||
int err = 0;
|
||||
|
||||
list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
|
||||
enum devlink_command cmd = DEVLINK_CMD_RATE_NEW;
|
||||
u32 id = NETLINK_CB(cb->skb).portid;
|
||||
|
||||
if (idx < state->idx) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
err = devlink_nl_rate_fill(msg, devlink_rate, cmd, id,
|
||||
cb->nlh->nlmsg_seq, flags, NULL);
|
||||
if (err) {
|
||||
state->idx = idx;
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_rate_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
return devlink_nl_dumpit(skb, cb, devlink_nl_rate_get_dump_one);
|
||||
}
|
||||
|
||||
int devlink_nl_rate_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_rate *devlink_rate;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
devlink_rate = devlink_rate_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_rate))
|
||||
return PTR_ERR(devlink_rate);
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
err = devlink_nl_rate_fill(msg, devlink_rate, DEVLINK_CMD_RATE_NEW,
|
||||
info->snd_portid, info->snd_seq, 0,
|
||||
info->extack);
|
||||
if (err) {
|
||||
nlmsg_free(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
}
|
||||
|
||||
static bool
|
||||
devlink_rate_is_parent_node(struct devlink_rate *devlink_rate,
|
||||
struct devlink_rate *parent)
|
||||
{
|
||||
while (parent) {
|
||||
if (parent == devlink_rate)
|
||||
return true;
|
||||
parent = parent->parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate,
|
||||
struct genl_info *info,
|
||||
struct nlattr *nla_parent)
|
||||
{
|
||||
struct devlink *devlink = devlink_rate->devlink;
|
||||
const char *parent_name = nla_data(nla_parent);
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
size_t len = strlen(parent_name);
|
||||
struct devlink_rate *parent;
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
parent = devlink_rate->parent;
|
||||
|
||||
if (parent && !len) {
|
||||
if (devlink_rate_is_leaf(devlink_rate))
|
||||
err = ops->rate_leaf_parent_set(devlink_rate, NULL,
|
||||
devlink_rate->priv, NULL,
|
||||
info->extack);
|
||||
else if (devlink_rate_is_node(devlink_rate))
|
||||
err = ops->rate_node_parent_set(devlink_rate, NULL,
|
||||
devlink_rate->priv, NULL,
|
||||
info->extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
refcount_dec(&parent->refcnt);
|
||||
devlink_rate->parent = NULL;
|
||||
} else if (len) {
|
||||
parent = devlink_rate_node_get_by_name(devlink, parent_name);
|
||||
if (IS_ERR(parent))
|
||||
return -ENODEV;
|
||||
|
||||
if (parent == devlink_rate) {
|
||||
NL_SET_ERR_MSG(info->extack, "Parent to self is not allowed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (devlink_rate_is_node(devlink_rate) &&
|
||||
devlink_rate_is_parent_node(devlink_rate, parent->parent)) {
|
||||
NL_SET_ERR_MSG(info->extack, "Node is already a parent of parent node.");
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
if (devlink_rate_is_leaf(devlink_rate))
|
||||
err = ops->rate_leaf_parent_set(devlink_rate, parent,
|
||||
devlink_rate->priv, parent->priv,
|
||||
info->extack);
|
||||
else if (devlink_rate_is_node(devlink_rate))
|
||||
err = ops->rate_node_parent_set(devlink_rate, parent,
|
||||
devlink_rate->priv, parent->priv,
|
||||
info->extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (devlink_rate->parent)
|
||||
/* we're reassigning to other parent in this case */
|
||||
refcount_dec(&devlink_rate->parent->refcnt);
|
||||
|
||||
refcount_inc(&parent->refcnt);
|
||||
devlink_rate->parent = parent;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devlink_nl_rate_set(struct devlink_rate *devlink_rate,
|
||||
const struct devlink_ops *ops,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct nlattr *nla_parent, **attrs = info->attrs;
|
||||
int err = -EOPNOTSUPP;
|
||||
u32 priority;
|
||||
u32 weight;
|
||||
u64 rate;
|
||||
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_SHARE]) {
|
||||
rate = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_SHARE]);
|
||||
if (devlink_rate_is_leaf(devlink_rate))
|
||||
err = ops->rate_leaf_tx_share_set(devlink_rate, devlink_rate->priv,
|
||||
rate, info->extack);
|
||||
else if (devlink_rate_is_node(devlink_rate))
|
||||
err = ops->rate_node_tx_share_set(devlink_rate, devlink_rate->priv,
|
||||
rate, info->extack);
|
||||
if (err)
|
||||
return err;
|
||||
devlink_rate->tx_share = rate;
|
||||
}
|
||||
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_MAX]) {
|
||||
rate = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_MAX]);
|
||||
if (devlink_rate_is_leaf(devlink_rate))
|
||||
err = ops->rate_leaf_tx_max_set(devlink_rate, devlink_rate->priv,
|
||||
rate, info->extack);
|
||||
else if (devlink_rate_is_node(devlink_rate))
|
||||
err = ops->rate_node_tx_max_set(devlink_rate, devlink_rate->priv,
|
||||
rate, info->extack);
|
||||
if (err)
|
||||
return err;
|
||||
devlink_rate->tx_max = rate;
|
||||
}
|
||||
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]) {
|
||||
priority = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]);
|
||||
if (devlink_rate_is_leaf(devlink_rate))
|
||||
err = ops->rate_leaf_tx_priority_set(devlink_rate, devlink_rate->priv,
|
||||
priority, info->extack);
|
||||
else if (devlink_rate_is_node(devlink_rate))
|
||||
err = ops->rate_node_tx_priority_set(devlink_rate, devlink_rate->priv,
|
||||
priority, info->extack);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
devlink_rate->tx_priority = priority;
|
||||
}
|
||||
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]) {
|
||||
weight = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]);
|
||||
if (devlink_rate_is_leaf(devlink_rate))
|
||||
err = ops->rate_leaf_tx_weight_set(devlink_rate, devlink_rate->priv,
|
||||
weight, info->extack);
|
||||
else if (devlink_rate_is_node(devlink_rate))
|
||||
err = ops->rate_node_tx_weight_set(devlink_rate, devlink_rate->priv,
|
||||
weight, info->extack);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
devlink_rate->tx_weight = weight;
|
||||
}
|
||||
|
||||
nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME];
|
||||
if (nla_parent) {
|
||||
err = devlink_nl_rate_parent_node_set(devlink_rate, info,
|
||||
nla_parent);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops,
|
||||
struct genl_info *info,
|
||||
enum devlink_rate_type type)
|
||||
{
|
||||
struct nlattr **attrs = info->attrs;
|
||||
|
||||
if (type == DEVLINK_RATE_TYPE_LEAF) {
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_leaf_tx_share_set) {
|
||||
NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the leafs");
|
||||
return false;
|
||||
}
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_leaf_tx_max_set) {
|
||||
NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the leafs");
|
||||
return false;
|
||||
}
|
||||
if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] &&
|
||||
!ops->rate_leaf_parent_set) {
|
||||
NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the leafs");
|
||||
return false;
|
||||
}
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_leaf_tx_priority_set) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
attrs[DEVLINK_ATTR_RATE_TX_PRIORITY],
|
||||
"TX priority set isn't supported for the leafs");
|
||||
return false;
|
||||
}
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_leaf_tx_weight_set) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
attrs[DEVLINK_ATTR_RATE_TX_WEIGHT],
|
||||
"TX weight set isn't supported for the leafs");
|
||||
return false;
|
||||
}
|
||||
} else if (type == DEVLINK_RATE_TYPE_NODE) {
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_node_tx_share_set) {
|
||||
NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the nodes");
|
||||
return false;
|
||||
}
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_node_tx_max_set) {
|
||||
NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the nodes");
|
||||
return false;
|
||||
}
|
||||
if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] &&
|
||||
!ops->rate_node_parent_set) {
|
||||
NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the nodes");
|
||||
return false;
|
||||
}
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_node_tx_priority_set) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
attrs[DEVLINK_ATTR_RATE_TX_PRIORITY],
|
||||
"TX priority set isn't supported for the nodes");
|
||||
return false;
|
||||
}
|
||||
if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_node_tx_weight_set) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
attrs[DEVLINK_ATTR_RATE_TX_WEIGHT],
|
||||
"TX weight set isn't supported for the nodes");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
WARN(1, "Unknown type of rate object");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_rate_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_rate *devlink_rate;
|
||||
const struct devlink_ops *ops;
|
||||
int err;
|
||||
|
||||
devlink_rate = devlink_rate_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_rate))
|
||||
return PTR_ERR(devlink_rate);
|
||||
|
||||
ops = devlink->ops;
|
||||
if (!ops || !devlink_rate_set_ops_supported(ops, info, devlink_rate->type))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
err = devlink_nl_rate_set(devlink_rate, ops, info);
|
||||
|
||||
if (!err)
|
||||
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW);
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_rate *rate_node;
|
||||
const struct devlink_ops *ops;
|
||||
int err;
|
||||
|
||||
ops = devlink->ops;
|
||||
if (!ops || !ops->rate_node_new || !ops->rate_node_del) {
|
||||
NL_SET_ERR_MSG(info->extack, "Rate nodes aren't supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (!devlink_rate_set_ops_supported(ops, info, DEVLINK_RATE_TYPE_NODE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
rate_node = devlink_rate_node_get_from_attrs(devlink, info->attrs);
|
||||
if (!IS_ERR(rate_node))
|
||||
return -EEXIST;
|
||||
else if (rate_node == ERR_PTR(-EINVAL))
|
||||
return -EINVAL;
|
||||
|
||||
rate_node = kzalloc(sizeof(*rate_node), GFP_KERNEL);
|
||||
if (!rate_node)
|
||||
return -ENOMEM;
|
||||
|
||||
rate_node->devlink = devlink;
|
||||
rate_node->type = DEVLINK_RATE_TYPE_NODE;
|
||||
rate_node->name = nla_strdup(info->attrs[DEVLINK_ATTR_RATE_NODE_NAME], GFP_KERNEL);
|
||||
if (!rate_node->name) {
|
||||
err = -ENOMEM;
|
||||
goto err_strdup;
|
||||
}
|
||||
|
||||
err = ops->rate_node_new(rate_node, &rate_node->priv, info->extack);
|
||||
if (err)
|
||||
goto err_node_new;
|
||||
|
||||
err = devlink_nl_rate_set(rate_node, ops, info);
|
||||
if (err)
|
||||
goto err_rate_set;
|
||||
|
||||
refcount_set(&rate_node->refcnt, 1);
|
||||
list_add(&rate_node->list, &devlink->rate_list);
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
|
||||
return 0;
|
||||
|
||||
err_rate_set:
|
||||
ops->rate_node_del(rate_node, rate_node->priv, info->extack);
|
||||
err_node_new:
|
||||
kfree(rate_node->name);
|
||||
err_strdup:
|
||||
kfree(rate_node);
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_rate_del_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_rate *rate_node;
|
||||
int err;
|
||||
|
||||
rate_node = devlink_rate_node_get_from_info(devlink, info);
|
||||
if (IS_ERR(rate_node))
|
||||
return PTR_ERR(rate_node);
|
||||
|
||||
if (refcount_read(&rate_node->refcnt) > 1) {
|
||||
NL_SET_ERR_MSG(info->extack, "Node has children. Cannot delete node.");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);
|
||||
err = devlink->ops->rate_node_del(rate_node, rate_node->priv,
|
||||
info->extack);
|
||||
if (rate_node->parent)
|
||||
refcount_dec(&rate_node->parent->refcnt);
|
||||
list_del(&rate_node->list);
|
||||
kfree(rate_node->name);
|
||||
kfree(rate_node);
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_rate_nodes_check(struct devlink *devlink, u16 mode,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct devlink_rate *devlink_rate;
|
||||
|
||||
list_for_each_entry(devlink_rate, &devlink->rate_list, list)
|
||||
if (devlink_rate_is_node(devlink_rate)) {
|
||||
NL_SET_ERR_MSG(extack, "Rate node(s) exists.");
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* devl_rate_node_create - create devlink rate node
|
||||
* @devlink: devlink instance
|
||||
* @priv: driver private data
|
||||
* @node_name: name of the resulting node
|
||||
* @parent: parent devlink_rate struct
|
||||
*
|
||||
* Create devlink rate object of type node
|
||||
*/
|
||||
struct devlink_rate *
|
||||
devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name,
|
||||
struct devlink_rate *parent)
|
||||
{
|
||||
struct devlink_rate *rate_node;
|
||||
|
||||
rate_node = devlink_rate_node_get_by_name(devlink, node_name);
|
||||
if (!IS_ERR(rate_node))
|
||||
return ERR_PTR(-EEXIST);
|
||||
|
||||
rate_node = kzalloc(sizeof(*rate_node), GFP_KERNEL);
|
||||
if (!rate_node)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (parent) {
|
||||
rate_node->parent = parent;
|
||||
refcount_inc(&rate_node->parent->refcnt);
|
||||
}
|
||||
|
||||
rate_node->type = DEVLINK_RATE_TYPE_NODE;
|
||||
rate_node->devlink = devlink;
|
||||
rate_node->priv = priv;
|
||||
|
||||
rate_node->name = kstrdup(node_name, GFP_KERNEL);
|
||||
if (!rate_node->name) {
|
||||
kfree(rate_node);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
refcount_set(&rate_node->refcnt, 1);
|
||||
list_add(&rate_node->list, &devlink->rate_list);
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
|
||||
return rate_node;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_rate_node_create);
|
||||
|
||||
/**
|
||||
* devl_rate_leaf_create - create devlink rate leaf
|
||||
* @devlink_port: devlink port object to create rate object on
|
||||
* @priv: driver private data
|
||||
* @parent: parent devlink_rate struct
|
||||
*
|
||||
* Create devlink rate object of type leaf on provided @devlink_port.
|
||||
*/
|
||||
int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv,
|
||||
struct devlink_rate *parent)
|
||||
{
|
||||
struct devlink *devlink = devlink_port->devlink;
|
||||
struct devlink_rate *devlink_rate;
|
||||
|
||||
devl_assert_locked(devlink_port->devlink);
|
||||
|
||||
if (WARN_ON(devlink_port->devlink_rate))
|
||||
return -EBUSY;
|
||||
|
||||
devlink_rate = kzalloc(sizeof(*devlink_rate), GFP_KERNEL);
|
||||
if (!devlink_rate)
|
||||
return -ENOMEM;
|
||||
|
||||
if (parent) {
|
||||
devlink_rate->parent = parent;
|
||||
refcount_inc(&devlink_rate->parent->refcnt);
|
||||
}
|
||||
|
||||
devlink_rate->type = DEVLINK_RATE_TYPE_LEAF;
|
||||
devlink_rate->devlink = devlink;
|
||||
devlink_rate->devlink_port = devlink_port;
|
||||
devlink_rate->priv = priv;
|
||||
list_add_tail(&devlink_rate->list, &devlink->rate_list);
|
||||
devlink_port->devlink_rate = devlink_rate;
|
||||
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_rate_leaf_create);
|
||||
|
||||
/**
|
||||
* devl_rate_leaf_destroy - destroy devlink rate leaf
|
||||
*
|
||||
* @devlink_port: devlink port linked to the rate object
|
||||
*
|
||||
* Destroy the devlink rate object of type leaf on provided @devlink_port.
|
||||
*/
|
||||
void devl_rate_leaf_destroy(struct devlink_port *devlink_port)
|
||||
{
|
||||
struct devlink_rate *devlink_rate = devlink_port->devlink_rate;
|
||||
|
||||
devl_assert_locked(devlink_port->devlink);
|
||||
if (!devlink_rate)
|
||||
return;
|
||||
|
||||
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_DEL);
|
||||
if (devlink_rate->parent)
|
||||
refcount_dec(&devlink_rate->parent->refcnt);
|
||||
list_del(&devlink_rate->list);
|
||||
devlink_port->devlink_rate = NULL;
|
||||
kfree(devlink_rate);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_rate_leaf_destroy);
|
||||
|
||||
/**
|
||||
* devl_rate_nodes_destroy - destroy all devlink rate nodes on device
|
||||
* @devlink: devlink instance
|
||||
*
|
||||
* Unset parent for all rate objects and destroy all rate nodes
|
||||
* on specified device.
|
||||
*/
|
||||
void devl_rate_nodes_destroy(struct devlink *devlink)
|
||||
{
|
||||
static struct devlink_rate *devlink_rate, *tmp;
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
|
||||
devl_assert_locked(devlink);
|
||||
|
||||
list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
|
||||
if (!devlink_rate->parent)
|
||||
continue;
|
||||
|
||||
refcount_dec(&devlink_rate->parent->refcnt);
|
||||
if (devlink_rate_is_leaf(devlink_rate))
|
||||
ops->rate_leaf_parent_set(devlink_rate, NULL, devlink_rate->priv,
|
||||
NULL, NULL);
|
||||
else if (devlink_rate_is_node(devlink_rate))
|
||||
ops->rate_node_parent_set(devlink_rate, NULL, devlink_rate->priv,
|
||||
NULL, NULL);
|
||||
}
|
||||
list_for_each_entry_safe(devlink_rate, tmp, &devlink->rate_list, list) {
|
||||
if (devlink_rate_is_node(devlink_rate)) {
|
||||
ops->rate_node_del(devlink_rate, devlink_rate->priv, NULL);
|
||||
list_del(&devlink_rate->list);
|
||||
kfree(devlink_rate->name);
|
||||
kfree(devlink_rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_rate_nodes_destroy);
|
||||
1260
net/devlink/region.c
Normal file
1260
net/devlink/region.c
Normal file
File diff suppressed because it is too large
Load Diff
579
net/devlink/resource.c
Normal file
579
net/devlink/resource.c
Normal file
@@ -0,0 +1,579 @@
|
||||
// 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"
|
||||
|
||||
/**
|
||||
* struct devlink_resource - devlink resource
|
||||
* @name: name of the resource
|
||||
* @id: id, per devlink instance
|
||||
* @size: size of the resource
|
||||
* @size_new: updated size of the resource, reload is needed
|
||||
* @size_valid: valid in case the total size of the resource is valid
|
||||
* including its children
|
||||
* @parent: parent resource
|
||||
* @size_params: size parameters
|
||||
* @list: parent list
|
||||
* @resource_list: list of child resources
|
||||
* @occ_get: occupancy getter callback
|
||||
* @occ_get_priv: occupancy getter callback priv
|
||||
*/
|
||||
struct devlink_resource {
|
||||
const char *name;
|
||||
u64 id;
|
||||
u64 size;
|
||||
u64 size_new;
|
||||
bool size_valid;
|
||||
struct devlink_resource *parent;
|
||||
struct devlink_resource_size_params size_params;
|
||||
struct list_head list;
|
||||
struct list_head resource_list;
|
||||
devlink_resource_occ_get_t *occ_get;
|
||||
void *occ_get_priv;
|
||||
};
|
||||
|
||||
static struct devlink_resource *
|
||||
devlink_resource_find(struct devlink *devlink,
|
||||
struct devlink_resource *resource, u64 resource_id)
|
||||
{
|
||||
struct list_head *resource_list;
|
||||
|
||||
if (resource)
|
||||
resource_list = &resource->resource_list;
|
||||
else
|
||||
resource_list = &devlink->resource_list;
|
||||
|
||||
list_for_each_entry(resource, resource_list, list) {
|
||||
struct devlink_resource *child_resource;
|
||||
|
||||
if (resource->id == resource_id)
|
||||
return resource;
|
||||
|
||||
child_resource = devlink_resource_find(devlink, resource,
|
||||
resource_id);
|
||||
if (child_resource)
|
||||
return child_resource;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
devlink_resource_validate_children(struct devlink_resource *resource)
|
||||
{
|
||||
struct devlink_resource *child_resource;
|
||||
bool size_valid = true;
|
||||
u64 parts_size = 0;
|
||||
|
||||
if (list_empty(&resource->resource_list))
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(child_resource, &resource->resource_list, list)
|
||||
parts_size += child_resource->size_new;
|
||||
|
||||
if (parts_size > resource->size_new)
|
||||
size_valid = false;
|
||||
out:
|
||||
resource->size_valid = size_valid;
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_resource_validate_size(struct devlink_resource *resource, u64 size,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
u64 reminder;
|
||||
int err = 0;
|
||||
|
||||
if (size > resource->size_params.size_max) {
|
||||
NL_SET_ERR_MSG(extack, "Size larger than maximum");
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
if (size < resource->size_params.size_min) {
|
||||
NL_SET_ERR_MSG(extack, "Size smaller than minimum");
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
div64_u64_rem(size, resource->size_params.size_granularity, &reminder);
|
||||
if (reminder) {
|
||||
NL_SET_ERR_MSG(extack, "Wrong granularity");
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_resource_set(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_resource *resource;
|
||||
u64 resource_id;
|
||||
u64 size;
|
||||
int err;
|
||||
|
||||
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_ID) ||
|
||||
GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_SIZE))
|
||||
return -EINVAL;
|
||||
resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);
|
||||
|
||||
resource = devlink_resource_find(devlink, NULL, resource_id);
|
||||
if (!resource)
|
||||
return -EINVAL;
|
||||
|
||||
size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
|
||||
err = devlink_resource_validate_size(resource, size, info->extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
resource->size_new = size;
|
||||
devlink_resource_validate_children(resource);
|
||||
if (resource->parent)
|
||||
devlink_resource_validate_children(resource->parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_resource_size_params_put(struct devlink_resource *resource,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct devlink_resource_size_params *size_params;
|
||||
|
||||
size_params = &resource->size_params;
|
||||
if (nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_GRAN,
|
||||
size_params->size_granularity, DEVLINK_ATTR_PAD) ||
|
||||
nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MAX,
|
||||
size_params->size_max, DEVLINK_ATTR_PAD) ||
|
||||
nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_MIN,
|
||||
size_params->size_min, DEVLINK_ATTR_PAD) ||
|
||||
nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_UNIT, size_params->unit))
|
||||
return -EMSGSIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devlink_resource_occ_put(struct devlink_resource *resource,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
if (!resource->occ_get)
|
||||
return 0;
|
||||
return nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_OCC,
|
||||
resource->occ_get(resource->occ_get_priv),
|
||||
DEVLINK_ATTR_PAD);
|
||||
}
|
||||
|
||||
static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
|
||||
struct devlink_resource *resource)
|
||||
{
|
||||
struct devlink_resource *child_resource;
|
||||
struct nlattr *child_resource_attr;
|
||||
struct nlattr *resource_attr;
|
||||
|
||||
resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
|
||||
if (!resource_attr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
|
||||
nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size,
|
||||
DEVLINK_ATTR_PAD) ||
|
||||
nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id,
|
||||
DEVLINK_ATTR_PAD))
|
||||
goto nla_put_failure;
|
||||
if (resource->size != resource->size_new &&
|
||||
nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW,
|
||||
resource->size_new, DEVLINK_ATTR_PAD))
|
||||
goto nla_put_failure;
|
||||
if (devlink_resource_occ_put(resource, skb))
|
||||
goto nla_put_failure;
|
||||
if (devlink_resource_size_params_put(resource, skb))
|
||||
goto nla_put_failure;
|
||||
if (list_empty(&resource->resource_list))
|
||||
goto out;
|
||||
|
||||
if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
|
||||
resource->size_valid))
|
||||
goto nla_put_failure;
|
||||
|
||||
child_resource_attr = nla_nest_start_noflag(skb,
|
||||
DEVLINK_ATTR_RESOURCE_LIST);
|
||||
if (!child_resource_attr)
|
||||
goto nla_put_failure;
|
||||
|
||||
list_for_each_entry(child_resource, &resource->resource_list, list) {
|
||||
if (devlink_resource_put(devlink, skb, child_resource))
|
||||
goto resource_put_failure;
|
||||
}
|
||||
|
||||
nla_nest_end(skb, child_resource_attr);
|
||||
out:
|
||||
nla_nest_end(skb, resource_attr);
|
||||
return 0;
|
||||
|
||||
resource_put_failure:
|
||||
nla_nest_cancel(skb, child_resource_attr);
|
||||
nla_put_failure:
|
||||
nla_nest_cancel(skb, resource_attr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int devlink_resource_fill(struct genl_info *info,
|
||||
enum devlink_command cmd, int flags)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_resource *resource;
|
||||
struct nlattr *resources_attr;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct nlmsghdr *nlh;
|
||||
bool incomplete;
|
||||
void *hdr;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
resource = list_first_entry(&devlink->resource_list,
|
||||
struct devlink_resource, list);
|
||||
start_again:
|
||||
err = devlink_nl_msg_reply_and_new(&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;
|
||||
|
||||
resources_attr = nla_nest_start_noflag(skb,
|
||||
DEVLINK_ATTR_RESOURCE_LIST);
|
||||
if (!resources_attr)
|
||||
goto nla_put_failure;
|
||||
|
||||
incomplete = false;
|
||||
i = 0;
|
||||
list_for_each_entry_from(resource, &devlink->resource_list, list) {
|
||||
err = devlink_resource_put(devlink, skb, resource);
|
||||
if (err) {
|
||||
if (!i)
|
||||
goto err_resource_put;
|
||||
incomplete = true;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
nla_nest_end(skb, resources_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_nl_msg_reply_and_new(&skb, info);
|
||||
if (err)
|
||||
return err;
|
||||
goto send_done;
|
||||
}
|
||||
return genlmsg_reply(skb, info);
|
||||
|
||||
nla_put_failure:
|
||||
err = -EMSGSIZE;
|
||||
err_resource_put:
|
||||
nlmsg_free(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_resource_dump(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
|
||||
if (list_empty(&devlink->resource_list))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
|
||||
}
|
||||
|
||||
int devlink_resources_validate(struct devlink *devlink,
|
||||
struct devlink_resource *resource,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct list_head *resource_list;
|
||||
int err = 0;
|
||||
|
||||
if (resource)
|
||||
resource_list = &resource->resource_list;
|
||||
else
|
||||
resource_list = &devlink->resource_list;
|
||||
|
||||
list_for_each_entry(resource, resource_list, list) {
|
||||
if (!resource->size_valid)
|
||||
return -EINVAL;
|
||||
err = devlink_resources_validate(devlink, resource, info);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* devl_resource_register - devlink resource register
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @resource_name: resource's name
|
||||
* @resource_size: resource's size
|
||||
* @resource_id: resource's id
|
||||
* @parent_resource_id: resource's parent id
|
||||
* @size_params: size parameters
|
||||
*
|
||||
* Generic resources should reuse the same names across drivers.
|
||||
* Please see the generic resources list at:
|
||||
* Documentation/networking/devlink/devlink-resource.rst
|
||||
*/
|
||||
int devl_resource_register(struct devlink *devlink,
|
||||
const char *resource_name,
|
||||
u64 resource_size,
|
||||
u64 resource_id,
|
||||
u64 parent_resource_id,
|
||||
const struct devlink_resource_size_params *size_params)
|
||||
{
|
||||
struct devlink_resource *resource;
|
||||
struct list_head *resource_list;
|
||||
bool top_hierarchy;
|
||||
|
||||
lockdep_assert_held(&devlink->lock);
|
||||
|
||||
top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
|
||||
|
||||
resource = devlink_resource_find(devlink, NULL, resource_id);
|
||||
if (resource)
|
||||
return -EINVAL;
|
||||
|
||||
resource = kzalloc(sizeof(*resource), GFP_KERNEL);
|
||||
if (!resource)
|
||||
return -ENOMEM;
|
||||
|
||||
if (top_hierarchy) {
|
||||
resource_list = &devlink->resource_list;
|
||||
} else {
|
||||
struct devlink_resource *parent_resource;
|
||||
|
||||
parent_resource = devlink_resource_find(devlink, NULL,
|
||||
parent_resource_id);
|
||||
if (parent_resource) {
|
||||
resource_list = &parent_resource->resource_list;
|
||||
resource->parent = parent_resource;
|
||||
} else {
|
||||
kfree(resource);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
resource->name = resource_name;
|
||||
resource->size = resource_size;
|
||||
resource->size_new = resource_size;
|
||||
resource->id = resource_id;
|
||||
resource->size_valid = true;
|
||||
memcpy(&resource->size_params, size_params,
|
||||
sizeof(resource->size_params));
|
||||
INIT_LIST_HEAD(&resource->resource_list);
|
||||
list_add_tail(&resource->list, resource_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_resource_register);
|
||||
|
||||
/**
|
||||
* devlink_resource_register - devlink resource register
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @resource_name: resource's name
|
||||
* @resource_size: resource's size
|
||||
* @resource_id: resource's id
|
||||
* @parent_resource_id: resource's parent id
|
||||
* @size_params: size parameters
|
||||
*
|
||||
* Generic resources should reuse the same names across drivers.
|
||||
* Please see the generic resources list at:
|
||||
* Documentation/networking/devlink/devlink-resource.rst
|
||||
*
|
||||
* Context: Takes and release devlink->lock <mutex>.
|
||||
*/
|
||||
int devlink_resource_register(struct devlink *devlink,
|
||||
const char *resource_name,
|
||||
u64 resource_size,
|
||||
u64 resource_id,
|
||||
u64 parent_resource_id,
|
||||
const struct devlink_resource_size_params *size_params)
|
||||
{
|
||||
int err;
|
||||
|
||||
devl_lock(devlink);
|
||||
err = devl_resource_register(devlink, resource_name, resource_size,
|
||||
resource_id, parent_resource_id, size_params);
|
||||
devl_unlock(devlink);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_resource_register);
|
||||
|
||||
static void devlink_resource_unregister(struct devlink *devlink,
|
||||
struct devlink_resource *resource)
|
||||
{
|
||||
struct devlink_resource *tmp, *child_resource;
|
||||
|
||||
list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
|
||||
list) {
|
||||
devlink_resource_unregister(devlink, child_resource);
|
||||
list_del(&child_resource->list);
|
||||
kfree(child_resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* devl_resources_unregister - free all resources
|
||||
*
|
||||
* @devlink: devlink
|
||||
*/
|
||||
void devl_resources_unregister(struct devlink *devlink)
|
||||
{
|
||||
struct devlink_resource *tmp, *child_resource;
|
||||
|
||||
lockdep_assert_held(&devlink->lock);
|
||||
|
||||
list_for_each_entry_safe(child_resource, tmp, &devlink->resource_list,
|
||||
list) {
|
||||
devlink_resource_unregister(devlink, child_resource);
|
||||
list_del(&child_resource->list);
|
||||
kfree(child_resource);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_resources_unregister);
|
||||
|
||||
/**
|
||||
* devlink_resources_unregister - free all resources
|
||||
*
|
||||
* @devlink: devlink
|
||||
*
|
||||
* Context: Takes and release devlink->lock <mutex>.
|
||||
*/
|
||||
void devlink_resources_unregister(struct devlink *devlink)
|
||||
{
|
||||
devl_lock(devlink);
|
||||
devl_resources_unregister(devlink);
|
||||
devl_unlock(devlink);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_resources_unregister);
|
||||
|
||||
/**
|
||||
* devl_resource_size_get - get and update size
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @resource_id: the requested resource id
|
||||
* @p_resource_size: ptr to update
|
||||
*/
|
||||
int devl_resource_size_get(struct devlink *devlink,
|
||||
u64 resource_id,
|
||||
u64 *p_resource_size)
|
||||
{
|
||||
struct devlink_resource *resource;
|
||||
|
||||
lockdep_assert_held(&devlink->lock);
|
||||
|
||||
resource = devlink_resource_find(devlink, NULL, resource_id);
|
||||
if (!resource)
|
||||
return -EINVAL;
|
||||
*p_resource_size = resource->size_new;
|
||||
resource->size = resource->size_new;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_resource_size_get);
|
||||
|
||||
/**
|
||||
* devl_resource_occ_get_register - register occupancy getter
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @resource_id: resource id
|
||||
* @occ_get: occupancy getter callback
|
||||
* @occ_get_priv: occupancy getter callback priv
|
||||
*/
|
||||
void devl_resource_occ_get_register(struct devlink *devlink,
|
||||
u64 resource_id,
|
||||
devlink_resource_occ_get_t *occ_get,
|
||||
void *occ_get_priv)
|
||||
{
|
||||
struct devlink_resource *resource;
|
||||
|
||||
lockdep_assert_held(&devlink->lock);
|
||||
|
||||
resource = devlink_resource_find(devlink, NULL, resource_id);
|
||||
if (WARN_ON(!resource))
|
||||
return;
|
||||
WARN_ON(resource->occ_get);
|
||||
|
||||
resource->occ_get = occ_get;
|
||||
resource->occ_get_priv = occ_get_priv;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
|
||||
|
||||
/**
|
||||
* devlink_resource_occ_get_register - register occupancy getter
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @resource_id: resource id
|
||||
* @occ_get: occupancy getter callback
|
||||
* @occ_get_priv: occupancy getter callback priv
|
||||
*
|
||||
* Context: Takes and release devlink->lock <mutex>.
|
||||
*/
|
||||
void devlink_resource_occ_get_register(struct devlink *devlink,
|
||||
u64 resource_id,
|
||||
devlink_resource_occ_get_t *occ_get,
|
||||
void *occ_get_priv)
|
||||
{
|
||||
devl_lock(devlink);
|
||||
devl_resource_occ_get_register(devlink, resource_id,
|
||||
occ_get, occ_get_priv);
|
||||
devl_unlock(devlink);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_resource_occ_get_register);
|
||||
|
||||
/**
|
||||
* devl_resource_occ_get_unregister - unregister occupancy getter
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @resource_id: resource id
|
||||
*/
|
||||
void devl_resource_occ_get_unregister(struct devlink *devlink,
|
||||
u64 resource_id)
|
||||
{
|
||||
struct devlink_resource *resource;
|
||||
|
||||
lockdep_assert_held(&devlink->lock);
|
||||
|
||||
resource = devlink_resource_find(devlink, NULL, resource_id);
|
||||
if (WARN_ON(!resource))
|
||||
return;
|
||||
WARN_ON(!resource->occ_get);
|
||||
|
||||
resource->occ_get = NULL;
|
||||
resource->occ_get_priv = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
|
||||
|
||||
/**
|
||||
* devlink_resource_occ_get_unregister - unregister occupancy getter
|
||||
*
|
||||
* @devlink: devlink
|
||||
* @resource_id: resource id
|
||||
*
|
||||
* Context: Takes and release devlink->lock <mutex>.
|
||||
*/
|
||||
void devlink_resource_occ_get_unregister(struct devlink *devlink,
|
||||
u64 resource_id)
|
||||
{
|
||||
devl_lock(devlink);
|
||||
devl_resource_occ_get_unregister(devlink, resource_id);
|
||||
devl_unlock(devlink);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_resource_occ_get_unregister);
|
||||
996
net/devlink/sb.c
Normal file
996
net/devlink/sb.c
Normal file
@@ -0,0 +1,996 @@
|
||||
// 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"
|
||||
|
||||
struct devlink_sb {
|
||||
struct list_head list;
|
||||
unsigned int index;
|
||||
u32 size;
|
||||
u16 ingress_pools_count;
|
||||
u16 egress_pools_count;
|
||||
u16 ingress_tc_count;
|
||||
u16 egress_tc_count;
|
||||
};
|
||||
|
||||
static u16 devlink_sb_pool_count(struct devlink_sb *devlink_sb)
|
||||
{
|
||||
return devlink_sb->ingress_pools_count + devlink_sb->egress_pools_count;
|
||||
}
|
||||
|
||||
static struct devlink_sb *devlink_sb_get_by_index(struct devlink *devlink,
|
||||
unsigned int sb_index)
|
||||
{
|
||||
struct devlink_sb *devlink_sb;
|
||||
|
||||
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
|
||||
if (devlink_sb->index == sb_index)
|
||||
return devlink_sb;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool devlink_sb_index_exists(struct devlink *devlink,
|
||||
unsigned int sb_index)
|
||||
{
|
||||
return devlink_sb_get_by_index(devlink, sb_index);
|
||||
}
|
||||
|
||||
static struct devlink_sb *devlink_sb_get_from_attrs(struct devlink *devlink,
|
||||
struct nlattr **attrs)
|
||||
{
|
||||
if (attrs[DEVLINK_ATTR_SB_INDEX]) {
|
||||
u32 sb_index = nla_get_u32(attrs[DEVLINK_ATTR_SB_INDEX]);
|
||||
struct devlink_sb *devlink_sb;
|
||||
|
||||
devlink_sb = devlink_sb_get_by_index(devlink, sb_index);
|
||||
if (!devlink_sb)
|
||||
return ERR_PTR(-ENODEV);
|
||||
return devlink_sb;
|
||||
}
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static struct devlink_sb *devlink_sb_get_from_info(struct devlink *devlink,
|
||||
struct genl_info *info)
|
||||
{
|
||||
return devlink_sb_get_from_attrs(devlink, info->attrs);
|
||||
}
|
||||
|
||||
static int devlink_sb_pool_index_get_from_attrs(struct devlink_sb *devlink_sb,
|
||||
struct nlattr **attrs,
|
||||
u16 *p_pool_index)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
if (!attrs[DEVLINK_ATTR_SB_POOL_INDEX])
|
||||
return -EINVAL;
|
||||
|
||||
val = nla_get_u16(attrs[DEVLINK_ATTR_SB_POOL_INDEX]);
|
||||
if (val >= devlink_sb_pool_count(devlink_sb))
|
||||
return -EINVAL;
|
||||
*p_pool_index = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devlink_sb_pool_index_get_from_info(struct devlink_sb *devlink_sb,
|
||||
struct genl_info *info,
|
||||
u16 *p_pool_index)
|
||||
{
|
||||
return devlink_sb_pool_index_get_from_attrs(devlink_sb, info->attrs,
|
||||
p_pool_index);
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_sb_pool_type_get_from_attrs(struct nlattr **attrs,
|
||||
enum devlink_sb_pool_type *p_pool_type)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
if (!attrs[DEVLINK_ATTR_SB_POOL_TYPE])
|
||||
return -EINVAL;
|
||||
|
||||
val = nla_get_u8(attrs[DEVLINK_ATTR_SB_POOL_TYPE]);
|
||||
if (val != DEVLINK_SB_POOL_TYPE_INGRESS &&
|
||||
val != DEVLINK_SB_POOL_TYPE_EGRESS)
|
||||
return -EINVAL;
|
||||
*p_pool_type = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_sb_pool_type_get_from_info(struct genl_info *info,
|
||||
enum devlink_sb_pool_type *p_pool_type)
|
||||
{
|
||||
return devlink_sb_pool_type_get_from_attrs(info->attrs, p_pool_type);
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_sb_th_type_get_from_attrs(struct nlattr **attrs,
|
||||
enum devlink_sb_threshold_type *p_th_type)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
if (!attrs[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])
|
||||
return -EINVAL;
|
||||
|
||||
val = nla_get_u8(attrs[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]);
|
||||
if (val != DEVLINK_SB_THRESHOLD_TYPE_STATIC &&
|
||||
val != DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC)
|
||||
return -EINVAL;
|
||||
*p_th_type = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_sb_th_type_get_from_info(struct genl_info *info,
|
||||
enum devlink_sb_threshold_type *p_th_type)
|
||||
{
|
||||
return devlink_sb_th_type_get_from_attrs(info->attrs, p_th_type);
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_sb_tc_index_get_from_attrs(struct devlink_sb *devlink_sb,
|
||||
struct nlattr **attrs,
|
||||
enum devlink_sb_pool_type pool_type,
|
||||
u16 *p_tc_index)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
if (!attrs[DEVLINK_ATTR_SB_TC_INDEX])
|
||||
return -EINVAL;
|
||||
|
||||
val = nla_get_u16(attrs[DEVLINK_ATTR_SB_TC_INDEX]);
|
||||
if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS &&
|
||||
val >= devlink_sb->ingress_tc_count)
|
||||
return -EINVAL;
|
||||
if (pool_type == DEVLINK_SB_POOL_TYPE_EGRESS &&
|
||||
val >= devlink_sb->egress_tc_count)
|
||||
return -EINVAL;
|
||||
*p_tc_index = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_sb_tc_index_get_from_info(struct devlink_sb *devlink_sb,
|
||||
struct genl_info *info,
|
||||
enum devlink_sb_pool_type pool_type,
|
||||
u16 *p_tc_index)
|
||||
{
|
||||
return devlink_sb_tc_index_get_from_attrs(devlink_sb, info->attrs,
|
||||
pool_type, p_tc_index);
|
||||
}
|
||||
|
||||
static int devlink_nl_sb_fill(struct sk_buff *msg, struct devlink *devlink,
|
||||
struct devlink_sb *devlink_sb,
|
||||
enum devlink_command cmd, u32 portid,
|
||||
u32 seq, int flags)
|
||||
{
|
||||
void *hdr;
|
||||
|
||||
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (devlink_nl_put_handle(msg, devlink))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_INDEX, devlink_sb->index))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_SIZE, devlink_sb->size))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u16(msg, DEVLINK_ATTR_SB_INGRESS_POOL_COUNT,
|
||||
devlink_sb->ingress_pools_count))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u16(msg, DEVLINK_ATTR_SB_EGRESS_POOL_COUNT,
|
||||
devlink_sb->egress_pools_count))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u16(msg, DEVLINK_ATTR_SB_INGRESS_TC_COUNT,
|
||||
devlink_sb->ingress_tc_count))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u16(msg, DEVLINK_ATTR_SB_EGRESS_TC_COUNT,
|
||||
devlink_sb->egress_tc_count))
|
||||
goto nla_put_failure;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
int devlink_nl_sb_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_sb *devlink_sb;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
devlink_sb = devlink_sb_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_sb))
|
||||
return PTR_ERR(devlink_sb);
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
err = devlink_nl_sb_fill(msg, devlink, devlink_sb,
|
||||
DEVLINK_CMD_SB_NEW,
|
||||
info->snd_portid, info->snd_seq, 0);
|
||||
if (err) {
|
||||
nlmsg_free(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_nl_sb_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
|
||||
struct netlink_callback *cb, int flags)
|
||||
{
|
||||
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
|
||||
struct devlink_sb *devlink_sb;
|
||||
int idx = 0;
|
||||
int err = 0;
|
||||
|
||||
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
|
||||
if (idx < state->idx) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
err = devlink_nl_sb_fill(msg, devlink, devlink_sb,
|
||||
DEVLINK_CMD_SB_NEW,
|
||||
NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, flags);
|
||||
if (err) {
|
||||
state->idx = idx;
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_sb_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
return devlink_nl_dumpit(skb, cb, devlink_nl_sb_get_dump_one);
|
||||
}
|
||||
|
||||
static int devlink_nl_sb_pool_fill(struct sk_buff *msg, struct devlink *devlink,
|
||||
struct devlink_sb *devlink_sb,
|
||||
u16 pool_index, enum devlink_command cmd,
|
||||
u32 portid, u32 seq, int flags)
|
||||
{
|
||||
struct devlink_sb_pool_info pool_info;
|
||||
void *hdr;
|
||||
int err;
|
||||
|
||||
err = devlink->ops->sb_pool_get(devlink, devlink_sb->index,
|
||||
pool_index, &pool_info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (devlink_nl_put_handle(msg, devlink))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_INDEX, devlink_sb->index))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u16(msg, DEVLINK_ATTR_SB_POOL_INDEX, pool_index))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u8(msg, DEVLINK_ATTR_SB_POOL_TYPE, pool_info.pool_type))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_POOL_SIZE, pool_info.size))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u8(msg, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE,
|
||||
pool_info.threshold_type))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_POOL_CELL_SIZE,
|
||||
pool_info.cell_size))
|
||||
goto nla_put_failure;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
int devlink_nl_sb_pool_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_sb *devlink_sb;
|
||||
struct sk_buff *msg;
|
||||
u16 pool_index;
|
||||
int err;
|
||||
|
||||
devlink_sb = devlink_sb_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_sb))
|
||||
return PTR_ERR(devlink_sb);
|
||||
|
||||
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
|
||||
&pool_index);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!devlink->ops->sb_pool_get)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
err = devlink_nl_sb_pool_fill(msg, devlink, devlink_sb, pool_index,
|
||||
DEVLINK_CMD_SB_POOL_NEW,
|
||||
info->snd_portid, info->snd_seq, 0);
|
||||
if (err) {
|
||||
nlmsg_free(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
}
|
||||
|
||||
static int __sb_pool_get_dumpit(struct sk_buff *msg, int start, int *p_idx,
|
||||
struct devlink *devlink,
|
||||
struct devlink_sb *devlink_sb,
|
||||
u32 portid, u32 seq, int flags)
|
||||
{
|
||||
u16 pool_count = devlink_sb_pool_count(devlink_sb);
|
||||
u16 pool_index;
|
||||
int err;
|
||||
|
||||
for (pool_index = 0; pool_index < pool_count; pool_index++) {
|
||||
if (*p_idx < start) {
|
||||
(*p_idx)++;
|
||||
continue;
|
||||
}
|
||||
err = devlink_nl_sb_pool_fill(msg, devlink,
|
||||
devlink_sb,
|
||||
pool_index,
|
||||
DEVLINK_CMD_SB_POOL_NEW,
|
||||
portid, seq, flags);
|
||||
if (err)
|
||||
return err;
|
||||
(*p_idx)++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_nl_sb_pool_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
|
||||
struct netlink_callback *cb, int flags)
|
||||
{
|
||||
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
|
||||
struct devlink_sb *devlink_sb;
|
||||
int err = 0;
|
||||
int idx = 0;
|
||||
|
||||
if (!devlink->ops->sb_pool_get)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
|
||||
err = __sb_pool_get_dumpit(msg, state->idx, &idx,
|
||||
devlink, devlink_sb,
|
||||
NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, flags);
|
||||
if (err == -EOPNOTSUPP) {
|
||||
err = 0;
|
||||
} else if (err) {
|
||||
state->idx = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_sb_pool_get_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
return devlink_nl_dumpit(skb, cb, devlink_nl_sb_pool_get_dump_one);
|
||||
}
|
||||
|
||||
static int devlink_sb_pool_set(struct devlink *devlink, unsigned int sb_index,
|
||||
u16 pool_index, u32 size,
|
||||
enum devlink_sb_threshold_type threshold_type,
|
||||
struct netlink_ext_ack *extack)
|
||||
|
||||
{
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
|
||||
if (ops->sb_pool_set)
|
||||
return ops->sb_pool_set(devlink, sb_index, pool_index,
|
||||
size, threshold_type, extack);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_sb_pool_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
enum devlink_sb_threshold_type threshold_type;
|
||||
struct devlink_sb *devlink_sb;
|
||||
u16 pool_index;
|
||||
u32 size;
|
||||
int err;
|
||||
|
||||
devlink_sb = devlink_sb_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_sb))
|
||||
return PTR_ERR(devlink_sb);
|
||||
|
||||
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
|
||||
&pool_index);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = devlink_sb_th_type_get_from_info(info, &threshold_type);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SB_POOL_SIZE))
|
||||
return -EINVAL;
|
||||
|
||||
size = nla_get_u32(info->attrs[DEVLINK_ATTR_SB_POOL_SIZE]);
|
||||
return devlink_sb_pool_set(devlink, devlink_sb->index,
|
||||
pool_index, size, threshold_type,
|
||||
info->extack);
|
||||
}
|
||||
|
||||
static int devlink_nl_sb_port_pool_fill(struct sk_buff *msg,
|
||||
struct devlink *devlink,
|
||||
struct devlink_port *devlink_port,
|
||||
struct devlink_sb *devlink_sb,
|
||||
u16 pool_index,
|
||||
enum devlink_command cmd,
|
||||
u32 portid, u32 seq, int flags)
|
||||
{
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
u32 threshold;
|
||||
void *hdr;
|
||||
int err;
|
||||
|
||||
err = ops->sb_port_pool_get(devlink_port, devlink_sb->index,
|
||||
pool_index, &threshold);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (devlink_nl_put_handle(msg, devlink))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_INDEX, devlink_sb->index))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u16(msg, DEVLINK_ATTR_SB_POOL_INDEX, pool_index))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_THRESHOLD, threshold))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (ops->sb_occ_port_pool_get) {
|
||||
u32 cur;
|
||||
u32 max;
|
||||
|
||||
err = ops->sb_occ_port_pool_get(devlink_port, devlink_sb->index,
|
||||
pool_index, &cur, &max);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
goto sb_occ_get_failure;
|
||||
if (!err) {
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_OCC_CUR, cur))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_OCC_MAX, max))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
}
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
err = -EMSGSIZE;
|
||||
sb_occ_get_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_sb_port_pool_get_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = devlink_port->devlink;
|
||||
struct devlink_sb *devlink_sb;
|
||||
struct sk_buff *msg;
|
||||
u16 pool_index;
|
||||
int err;
|
||||
|
||||
devlink_sb = devlink_sb_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_sb))
|
||||
return PTR_ERR(devlink_sb);
|
||||
|
||||
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
|
||||
&pool_index);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!devlink->ops->sb_port_pool_get)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
err = devlink_nl_sb_port_pool_fill(msg, devlink, devlink_port,
|
||||
devlink_sb, pool_index,
|
||||
DEVLINK_CMD_SB_PORT_POOL_NEW,
|
||||
info->snd_portid, info->snd_seq, 0);
|
||||
if (err) {
|
||||
nlmsg_free(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
}
|
||||
|
||||
static int __sb_port_pool_get_dumpit(struct sk_buff *msg, int start, int *p_idx,
|
||||
struct devlink *devlink,
|
||||
struct devlink_sb *devlink_sb,
|
||||
u32 portid, u32 seq, int flags)
|
||||
{
|
||||
struct devlink_port *devlink_port;
|
||||
u16 pool_count = devlink_sb_pool_count(devlink_sb);
|
||||
unsigned long port_index;
|
||||
u16 pool_index;
|
||||
int err;
|
||||
|
||||
xa_for_each(&devlink->ports, port_index, devlink_port) {
|
||||
for (pool_index = 0; pool_index < pool_count; pool_index++) {
|
||||
if (*p_idx < start) {
|
||||
(*p_idx)++;
|
||||
continue;
|
||||
}
|
||||
err = devlink_nl_sb_port_pool_fill(msg, devlink,
|
||||
devlink_port,
|
||||
devlink_sb,
|
||||
pool_index,
|
||||
DEVLINK_CMD_SB_PORT_POOL_NEW,
|
||||
portid, seq, flags);
|
||||
if (err)
|
||||
return err;
|
||||
(*p_idx)++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_nl_sb_port_pool_get_dump_one(struct sk_buff *msg,
|
||||
struct devlink *devlink,
|
||||
struct netlink_callback *cb, int flags)
|
||||
{
|
||||
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
|
||||
struct devlink_sb *devlink_sb;
|
||||
int idx = 0;
|
||||
int err = 0;
|
||||
|
||||
if (!devlink->ops->sb_port_pool_get)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
|
||||
err = __sb_port_pool_get_dumpit(msg, state->idx, &idx,
|
||||
devlink, devlink_sb,
|
||||
NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, flags);
|
||||
if (err == -EOPNOTSUPP) {
|
||||
err = 0;
|
||||
} else if (err) {
|
||||
state->idx = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_sb_port_pool_get_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
return devlink_nl_dumpit(skb, cb, devlink_nl_sb_port_pool_get_dump_one);
|
||||
}
|
||||
|
||||
static int devlink_sb_port_pool_set(struct devlink_port *devlink_port,
|
||||
unsigned int sb_index, u16 pool_index,
|
||||
u32 threshold,
|
||||
struct netlink_ext_ack *extack)
|
||||
|
||||
{
|
||||
const struct devlink_ops *ops = devlink_port->devlink->ops;
|
||||
|
||||
if (ops->sb_port_pool_set)
|
||||
return ops->sb_port_pool_set(devlink_port, sb_index,
|
||||
pool_index, threshold, extack);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_sb_port_pool_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_sb *devlink_sb;
|
||||
u16 pool_index;
|
||||
u32 threshold;
|
||||
int err;
|
||||
|
||||
devlink_sb = devlink_sb_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_sb))
|
||||
return PTR_ERR(devlink_sb);
|
||||
|
||||
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
|
||||
&pool_index);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SB_THRESHOLD))
|
||||
return -EINVAL;
|
||||
|
||||
threshold = nla_get_u32(info->attrs[DEVLINK_ATTR_SB_THRESHOLD]);
|
||||
return devlink_sb_port_pool_set(devlink_port, devlink_sb->index,
|
||||
pool_index, threshold, info->extack);
|
||||
}
|
||||
|
||||
static int
|
||||
devlink_nl_sb_tc_pool_bind_fill(struct sk_buff *msg, struct devlink *devlink,
|
||||
struct devlink_port *devlink_port,
|
||||
struct devlink_sb *devlink_sb, u16 tc_index,
|
||||
enum devlink_sb_pool_type pool_type,
|
||||
enum devlink_command cmd,
|
||||
u32 portid, u32 seq, int flags)
|
||||
{
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
u16 pool_index;
|
||||
u32 threshold;
|
||||
void *hdr;
|
||||
int err;
|
||||
|
||||
err = ops->sb_tc_pool_bind_get(devlink_port, devlink_sb->index,
|
||||
tc_index, pool_type,
|
||||
&pool_index, &threshold);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (devlink_nl_put_handle(msg, devlink))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_INDEX, devlink_sb->index))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u16(msg, DEVLINK_ATTR_SB_TC_INDEX, tc_index))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u8(msg, DEVLINK_ATTR_SB_POOL_TYPE, pool_type))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u16(msg, DEVLINK_ATTR_SB_POOL_INDEX, pool_index))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_THRESHOLD, threshold))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (ops->sb_occ_tc_port_bind_get) {
|
||||
u32 cur;
|
||||
u32 max;
|
||||
|
||||
err = ops->sb_occ_tc_port_bind_get(devlink_port,
|
||||
devlink_sb->index,
|
||||
tc_index, pool_type,
|
||||
&cur, &max);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
if (!err) {
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_OCC_CUR, cur))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(msg, DEVLINK_ATTR_SB_OCC_MAX, max))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
}
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
int devlink_nl_sb_tc_pool_bind_get_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = devlink_port->devlink;
|
||||
struct devlink_sb *devlink_sb;
|
||||
struct sk_buff *msg;
|
||||
enum devlink_sb_pool_type pool_type;
|
||||
u16 tc_index;
|
||||
int err;
|
||||
|
||||
devlink_sb = devlink_sb_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_sb))
|
||||
return PTR_ERR(devlink_sb);
|
||||
|
||||
err = devlink_sb_pool_type_get_from_info(info, &pool_type);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = devlink_sb_tc_index_get_from_info(devlink_sb, info,
|
||||
pool_type, &tc_index);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!devlink->ops->sb_tc_pool_bind_get)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
err = devlink_nl_sb_tc_pool_bind_fill(msg, devlink, devlink_port,
|
||||
devlink_sb, tc_index, pool_type,
|
||||
DEVLINK_CMD_SB_TC_POOL_BIND_NEW,
|
||||
info->snd_portid,
|
||||
info->snd_seq, 0);
|
||||
if (err) {
|
||||
nlmsg_free(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
}
|
||||
|
||||
static int __sb_tc_pool_bind_get_dumpit(struct sk_buff *msg,
|
||||
int start, int *p_idx,
|
||||
struct devlink *devlink,
|
||||
struct devlink_sb *devlink_sb,
|
||||
u32 portid, u32 seq, int flags)
|
||||
{
|
||||
struct devlink_port *devlink_port;
|
||||
unsigned long port_index;
|
||||
u16 tc_index;
|
||||
int err;
|
||||
|
||||
xa_for_each(&devlink->ports, port_index, devlink_port) {
|
||||
for (tc_index = 0;
|
||||
tc_index < devlink_sb->ingress_tc_count; tc_index++) {
|
||||
if (*p_idx < start) {
|
||||
(*p_idx)++;
|
||||
continue;
|
||||
}
|
||||
err = devlink_nl_sb_tc_pool_bind_fill(msg, devlink,
|
||||
devlink_port,
|
||||
devlink_sb,
|
||||
tc_index,
|
||||
DEVLINK_SB_POOL_TYPE_INGRESS,
|
||||
DEVLINK_CMD_SB_TC_POOL_BIND_NEW,
|
||||
portid, seq,
|
||||
flags);
|
||||
if (err)
|
||||
return err;
|
||||
(*p_idx)++;
|
||||
}
|
||||
for (tc_index = 0;
|
||||
tc_index < devlink_sb->egress_tc_count; tc_index++) {
|
||||
if (*p_idx < start) {
|
||||
(*p_idx)++;
|
||||
continue;
|
||||
}
|
||||
err = devlink_nl_sb_tc_pool_bind_fill(msg, devlink,
|
||||
devlink_port,
|
||||
devlink_sb,
|
||||
tc_index,
|
||||
DEVLINK_SB_POOL_TYPE_EGRESS,
|
||||
DEVLINK_CMD_SB_TC_POOL_BIND_NEW,
|
||||
portid, seq,
|
||||
flags);
|
||||
if (err)
|
||||
return err;
|
||||
(*p_idx)++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devlink_nl_sb_tc_pool_bind_get_dump_one(struct sk_buff *msg,
|
||||
struct devlink *devlink,
|
||||
struct netlink_callback *cb,
|
||||
int flags)
|
||||
{
|
||||
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
|
||||
struct devlink_sb *devlink_sb;
|
||||
int idx = 0;
|
||||
int err = 0;
|
||||
|
||||
if (!devlink->ops->sb_tc_pool_bind_get)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
|
||||
err = __sb_tc_pool_bind_get_dumpit(msg, state->idx, &idx,
|
||||
devlink, devlink_sb,
|
||||
NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, flags);
|
||||
if (err == -EOPNOTSUPP) {
|
||||
err = 0;
|
||||
} else if (err) {
|
||||
state->idx = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_sb_tc_pool_bind_get_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
return devlink_nl_dumpit(skb, cb,
|
||||
devlink_nl_sb_tc_pool_bind_get_dump_one);
|
||||
}
|
||||
|
||||
static int devlink_sb_tc_pool_bind_set(struct devlink_port *devlink_port,
|
||||
unsigned int sb_index, u16 tc_index,
|
||||
enum devlink_sb_pool_type pool_type,
|
||||
u16 pool_index, u32 threshold,
|
||||
struct netlink_ext_ack *extack)
|
||||
|
||||
{
|
||||
const struct devlink_ops *ops = devlink_port->devlink->ops;
|
||||
|
||||
if (ops->sb_tc_pool_bind_set)
|
||||
return ops->sb_tc_pool_bind_set(devlink_port, sb_index,
|
||||
tc_index, pool_type,
|
||||
pool_index, threshold, extack);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_sb_tc_pool_bind_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
enum devlink_sb_pool_type pool_type;
|
||||
struct devlink_sb *devlink_sb;
|
||||
u16 tc_index;
|
||||
u16 pool_index;
|
||||
u32 threshold;
|
||||
int err;
|
||||
|
||||
devlink_sb = devlink_sb_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_sb))
|
||||
return PTR_ERR(devlink_sb);
|
||||
|
||||
err = devlink_sb_pool_type_get_from_info(info, &pool_type);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = devlink_sb_tc_index_get_from_info(devlink_sb, info,
|
||||
pool_type, &tc_index);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
|
||||
&pool_index);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SB_THRESHOLD))
|
||||
return -EINVAL;
|
||||
|
||||
threshold = nla_get_u32(info->attrs[DEVLINK_ATTR_SB_THRESHOLD]);
|
||||
return devlink_sb_tc_pool_bind_set(devlink_port, devlink_sb->index,
|
||||
tc_index, pool_type,
|
||||
pool_index, threshold, info->extack);
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_sb_occ_snapshot_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
struct devlink_sb *devlink_sb;
|
||||
|
||||
devlink_sb = devlink_sb_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_sb))
|
||||
return PTR_ERR(devlink_sb);
|
||||
|
||||
if (ops->sb_occ_snapshot)
|
||||
return ops->sb_occ_snapshot(devlink, devlink_sb->index);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int devlink_nl_cmd_sb_occ_max_clear_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
struct devlink_sb *devlink_sb;
|
||||
|
||||
devlink_sb = devlink_sb_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_sb))
|
||||
return PTR_ERR(devlink_sb);
|
||||
|
||||
if (ops->sb_occ_max_clear)
|
||||
return ops->sb_occ_max_clear(devlink, devlink_sb->index);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int devl_sb_register(struct devlink *devlink, unsigned int sb_index,
|
||||
u32 size, u16 ingress_pools_count,
|
||||
u16 egress_pools_count, u16 ingress_tc_count,
|
||||
u16 egress_tc_count)
|
||||
{
|
||||
struct devlink_sb *devlink_sb;
|
||||
|
||||
lockdep_assert_held(&devlink->lock);
|
||||
|
||||
if (devlink_sb_index_exists(devlink, sb_index))
|
||||
return -EEXIST;
|
||||
|
||||
devlink_sb = kzalloc(sizeof(*devlink_sb), GFP_KERNEL);
|
||||
if (!devlink_sb)
|
||||
return -ENOMEM;
|
||||
devlink_sb->index = sb_index;
|
||||
devlink_sb->size = size;
|
||||
devlink_sb->ingress_pools_count = ingress_pools_count;
|
||||
devlink_sb->egress_pools_count = egress_pools_count;
|
||||
devlink_sb->ingress_tc_count = ingress_tc_count;
|
||||
devlink_sb->egress_tc_count = egress_tc_count;
|
||||
list_add_tail(&devlink_sb->list, &devlink->sb_list);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_sb_register);
|
||||
|
||||
int devlink_sb_register(struct devlink *devlink, unsigned int sb_index,
|
||||
u32 size, u16 ingress_pools_count,
|
||||
u16 egress_pools_count, u16 ingress_tc_count,
|
||||
u16 egress_tc_count)
|
||||
{
|
||||
int err;
|
||||
|
||||
devl_lock(devlink);
|
||||
err = devl_sb_register(devlink, sb_index, size, ingress_pools_count,
|
||||
egress_pools_count, ingress_tc_count,
|
||||
egress_tc_count);
|
||||
devl_unlock(devlink);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_sb_register);
|
||||
|
||||
void devl_sb_unregister(struct devlink *devlink, unsigned int sb_index)
|
||||
{
|
||||
struct devlink_sb *devlink_sb;
|
||||
|
||||
lockdep_assert_held(&devlink->lock);
|
||||
|
||||
devlink_sb = devlink_sb_get_by_index(devlink, sb_index);
|
||||
WARN_ON(!devlink_sb);
|
||||
list_del(&devlink_sb->list);
|
||||
kfree(devlink_sb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_sb_unregister);
|
||||
|
||||
void devlink_sb_unregister(struct devlink *devlink, unsigned int sb_index)
|
||||
{
|
||||
devl_lock(devlink);
|
||||
devl_sb_unregister(devlink, sb_index);
|
||||
devl_unlock(devlink);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devlink_sb_unregister);
|
||||
1861
net/devlink/trap.c
Normal file
1861
net/devlink/trap.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user