1555d204e7
The pipeline debug is used to export the pipeline abstractions for the main objects - tables, headers and entries. The only support for set is for changing the counter parameter on specific table. The basic structures: Header - can represent a real protocol header information or internal metadata. Generic protocol headers like IPv4 can be shared between drivers. Each driver can add local headers. Field - part of a header. Can represent protocol field or specific ASIC metadata field. Hardware special metadata fields can be mapped to different resources, for example switch ASIC ports can have internal number which from the systems point of view is mapped to netdeivce ifindex. Match - represent specific match rule. Can describe match on specific field or header. The header index should be specified as well in order to support several header instances of the same type (tunneling). Action - represents specific action rule. Actions can describe operations on specific field values for example like set, increment, etc. And header operation like add and delete. Value - represents value which can be associated with specific match or action. Table - represents a hardware block which can be described with match/ action behavior. The match/action can be done on the packets data or on the internal metadata that it gathered along the packets traversal throw the pipeline which is vendor specific and should be exported in order to provide understanding of ASICs behavior. Entry - represents single record in a specific table. The entry is identified by specific combination of values for match/action. Prior to accessing the tables/entries the drivers provide the header/ field data base which is used by driver to user-space. The data base is split between the shared headers and unique headers. Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2736 lines
69 KiB
C
2736 lines
69 KiB
C
/*
|
|
* net/core/devlink.c - Network physical/parent device Netlink interface
|
|
*
|
|
* Heavily inspired by net/wireless/
|
|
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
|
|
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/types.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/device.h>
|
|
#include <linux/list.h>
|
|
#include <linux/netdevice.h>
|
|
#include <rdma/ib_verbs.h>
|
|
#include <net/netlink.h>
|
|
#include <net/genetlink.h>
|
|
#include <net/rtnetlink.h>
|
|
#include <net/net_namespace.h>
|
|
#include <net/sock.h>
|
|
#include <net/devlink.h>
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/devlink.h>
|
|
|
|
EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg);
|
|
|
|
static LIST_HEAD(devlink_list);
|
|
|
|
/* devlink_mutex
|
|
*
|
|
* An overall lock guarding every operation coming from userspace.
|
|
* It also guards devlink devices list and it is taken when
|
|
* driver registers/unregisters it.
|
|
*/
|
|
static DEFINE_MUTEX(devlink_mutex);
|
|
|
|
/* devlink_port_mutex
|
|
*
|
|
* Shared lock to guard lists of ports in all devlink devices.
|
|
*/
|
|
static DEFINE_MUTEX(devlink_port_mutex);
|
|
|
|
static struct net *devlink_net(const struct devlink *devlink)
|
|
{
|
|
return read_pnet(&devlink->_net);
|
|
}
|
|
|
|
static void devlink_net_set(struct devlink *devlink, struct net *net)
|
|
{
|
|
write_pnet(&devlink->_net, net);
|
|
}
|
|
|
|
static struct devlink *devlink_get_from_attrs(struct net *net,
|
|
struct nlattr **attrs)
|
|
{
|
|
struct devlink *devlink;
|
|
char *busname;
|
|
char *devname;
|
|
|
|
if (!attrs[DEVLINK_ATTR_BUS_NAME] || !attrs[DEVLINK_ATTR_DEV_NAME])
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]);
|
|
devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]);
|
|
|
|
list_for_each_entry(devlink, &devlink_list, list) {
|
|
if (strcmp(devlink->dev->bus->name, busname) == 0 &&
|
|
strcmp(dev_name(devlink->dev), devname) == 0 &&
|
|
net_eq(devlink_net(devlink), net))
|
|
return devlink;
|
|
}
|
|
|
|
return ERR_PTR(-ENODEV);
|
|
}
|
|
|
|
static struct devlink *devlink_get_from_info(struct genl_info *info)
|
|
{
|
|
return devlink_get_from_attrs(genl_info_net(info), info->attrs);
|
|
}
|
|
|
|
static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,
|
|
int port_index)
|
|
{
|
|
struct devlink_port *devlink_port;
|
|
|
|
list_for_each_entry(devlink_port, &devlink->port_list, list) {
|
|
if (devlink_port->index == port_index)
|
|
return devlink_port;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static bool devlink_port_index_exists(struct devlink *devlink, int port_index)
|
|
{
|
|
return devlink_port_get_by_index(devlink, port_index);
|
|
}
|
|
|
|
static struct devlink_port *devlink_port_get_from_attrs(struct devlink *devlink,
|
|
struct nlattr **attrs)
|
|
{
|
|
if (attrs[DEVLINK_ATTR_PORT_INDEX]) {
|
|
u32 port_index = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
|
|
struct devlink_port *devlink_port;
|
|
|
|
devlink_port = devlink_port_get_by_index(devlink, port_index);
|
|
if (!devlink_port)
|
|
return ERR_PTR(-ENODEV);
|
|
return devlink_port;
|
|
}
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
static struct devlink_port *devlink_port_get_from_info(struct devlink *devlink,
|
|
struct genl_info *info)
|
|
{
|
|
return devlink_port_get_from_attrs(devlink, info->attrs);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#define DEVLINK_NL_FLAG_NEED_DEVLINK BIT(0)
|
|
#define DEVLINK_NL_FLAG_NEED_PORT BIT(1)
|
|
#define DEVLINK_NL_FLAG_NEED_SB BIT(2)
|
|
#define DEVLINK_NL_FLAG_LOCK_PORTS BIT(3)
|
|
/* port is not needed but we need to ensure they don't
|
|
* change in the middle of command
|
|
*/
|
|
|
|
static int devlink_nl_pre_doit(const struct genl_ops *ops,
|
|
struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct devlink *devlink;
|
|
|
|
mutex_lock(&devlink_mutex);
|
|
devlink = devlink_get_from_info(info);
|
|
if (IS_ERR(devlink)) {
|
|
mutex_unlock(&devlink_mutex);
|
|
return PTR_ERR(devlink);
|
|
}
|
|
if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_DEVLINK) {
|
|
info->user_ptr[0] = devlink;
|
|
} else if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT) {
|
|
struct devlink_port *devlink_port;
|
|
|
|
mutex_lock(&devlink_port_mutex);
|
|
devlink_port = devlink_port_get_from_info(devlink, info);
|
|
if (IS_ERR(devlink_port)) {
|
|
mutex_unlock(&devlink_port_mutex);
|
|
mutex_unlock(&devlink_mutex);
|
|
return PTR_ERR(devlink_port);
|
|
}
|
|
info->user_ptr[0] = devlink_port;
|
|
}
|
|
if (ops->internal_flags & DEVLINK_NL_FLAG_LOCK_PORTS) {
|
|
mutex_lock(&devlink_port_mutex);
|
|
}
|
|
if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_SB) {
|
|
struct devlink_sb *devlink_sb;
|
|
|
|
devlink_sb = devlink_sb_get_from_info(devlink, info);
|
|
if (IS_ERR(devlink_sb)) {
|
|
if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT)
|
|
mutex_unlock(&devlink_port_mutex);
|
|
mutex_unlock(&devlink_mutex);
|
|
return PTR_ERR(devlink_sb);
|
|
}
|
|
info->user_ptr[1] = devlink_sb;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void devlink_nl_post_doit(const struct genl_ops *ops,
|
|
struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
if (ops->internal_flags & DEVLINK_NL_FLAG_NEED_PORT ||
|
|
ops->internal_flags & DEVLINK_NL_FLAG_LOCK_PORTS)
|
|
mutex_unlock(&devlink_port_mutex);
|
|
mutex_unlock(&devlink_mutex);
|
|
}
|
|
|
|
static struct genl_family devlink_nl_family;
|
|
|
|
enum devlink_multicast_groups {
|
|
DEVLINK_MCGRP_CONFIG,
|
|
};
|
|
|
|
static const struct genl_multicast_group devlink_nl_mcgrps[] = {
|
|
[DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },
|
|
};
|
|
|
|
static int devlink_nl_put_handle(struct sk_buff *msg, struct devlink *devlink)
|
|
{
|
|
if (nla_put_string(msg, DEVLINK_ATTR_BUS_NAME, devlink->dev->bus->name))
|
|
return -EMSGSIZE;
|
|
if (nla_put_string(msg, DEVLINK_ATTR_DEV_NAME, dev_name(devlink->dev)))
|
|
return -EMSGSIZE;
|
|
return 0;
|
|
}
|
|
|
|
static int devlink_nl_fill(struct sk_buff *msg, struct devlink *devlink,
|
|
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;
|
|
|
|
genlmsg_end(msg, hdr);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
genlmsg_cancel(msg, hdr);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
static void devlink_notify(struct devlink *devlink, enum devlink_command cmd)
|
|
{
|
|
struct sk_buff *msg;
|
|
int err;
|
|
|
|
WARN_ON(cmd != DEVLINK_CMD_NEW && cmd != DEVLINK_CMD_DEL);
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
if (!msg)
|
|
return;
|
|
|
|
err = devlink_nl_fill(msg, devlink, 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 int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink,
|
|
struct devlink_port *devlink_port,
|
|
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_PORT_INDEX, devlink_port->index))
|
|
goto nla_put_failure;
|
|
if (nla_put_u16(msg, DEVLINK_ATTR_PORT_TYPE, devlink_port->type))
|
|
goto nla_put_failure;
|
|
if (devlink_port->desired_type != DEVLINK_PORT_TYPE_NOTSET &&
|
|
nla_put_u16(msg, DEVLINK_ATTR_PORT_DESIRED_TYPE,
|
|
devlink_port->desired_type))
|
|
goto nla_put_failure;
|
|
if (devlink_port->type == DEVLINK_PORT_TYPE_ETH) {
|
|
struct net_device *netdev = devlink_port->type_dev;
|
|
|
|
if (netdev &&
|
|
(nla_put_u32(msg, DEVLINK_ATTR_PORT_NETDEV_IFINDEX,
|
|
netdev->ifindex) ||
|
|
nla_put_string(msg, DEVLINK_ATTR_PORT_NETDEV_NAME,
|
|
netdev->name)))
|
|
goto nla_put_failure;
|
|
}
|
|
if (devlink_port->type == DEVLINK_PORT_TYPE_IB) {
|
|
struct ib_device *ibdev = devlink_port->type_dev;
|
|
|
|
if (ibdev &&
|
|
nla_put_string(msg, DEVLINK_ATTR_PORT_IBDEV_NAME,
|
|
ibdev->name))
|
|
goto nla_put_failure;
|
|
}
|
|
if (devlink_port->split &&
|
|
nla_put_u32(msg, DEVLINK_ATTR_PORT_SPLIT_GROUP,
|
|
devlink_port->split_group))
|
|
goto nla_put_failure;
|
|
|
|
genlmsg_end(msg, hdr);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
genlmsg_cancel(msg, hdr);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
static void devlink_port_notify(struct devlink_port *devlink_port,
|
|
enum devlink_command cmd)
|
|
{
|
|
struct devlink *devlink = devlink_port->devlink;
|
|
struct sk_buff *msg;
|
|
int err;
|
|
|
|
if (!devlink_port->registered)
|
|
return;
|
|
|
|
WARN_ON(cmd != DEVLINK_CMD_PORT_NEW && cmd != DEVLINK_CMD_PORT_DEL);
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
if (!msg)
|
|
return;
|
|
|
|
err = devlink_nl_port_fill(msg, devlink, devlink_port, 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 int devlink_nl_cmd_get_doit(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct sk_buff *msg;
|
|
int err;
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
if (!msg)
|
|
return -ENOMEM;
|
|
|
|
err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,
|
|
info->snd_portid, info->snd_seq, 0);
|
|
if (err) {
|
|
nlmsg_free(msg);
|
|
return err;
|
|
}
|
|
|
|
return genlmsg_reply(msg, info);
|
|
}
|
|
|
|
static int devlink_nl_cmd_get_dumpit(struct sk_buff *msg,
|
|
struct netlink_callback *cb)
|
|
{
|
|
struct devlink *devlink;
|
|
int start = cb->args[0];
|
|
int idx = 0;
|
|
int err;
|
|
|
|
mutex_lock(&devlink_mutex);
|
|
list_for_each_entry(devlink, &devlink_list, list) {
|
|
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
|
|
continue;
|
|
if (idx < start) {
|
|
idx++;
|
|
continue;
|
|
}
|
|
err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,
|
|
NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI);
|
|
if (err)
|
|
goto out;
|
|
idx++;
|
|
}
|
|
out:
|
|
mutex_unlock(&devlink_mutex);
|
|
|
|
cb->args[0] = idx;
|
|
return msg->len;
|
|
}
|
|
|
|
static int devlink_nl_cmd_port_get_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink_port *devlink_port = info->user_ptr[0];
|
|
struct devlink *devlink = devlink_port->devlink;
|
|
struct sk_buff *msg;
|
|
int err;
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
if (!msg)
|
|
return -ENOMEM;
|
|
|
|
err = devlink_nl_port_fill(msg, devlink, devlink_port,
|
|
DEVLINK_CMD_PORT_NEW,
|
|
info->snd_portid, info->snd_seq, 0);
|
|
if (err) {
|
|
nlmsg_free(msg);
|
|
return err;
|
|
}
|
|
|
|
return genlmsg_reply(msg, info);
|
|
}
|
|
|
|
static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg,
|
|
struct netlink_callback *cb)
|
|
{
|
|
struct devlink *devlink;
|
|
struct devlink_port *devlink_port;
|
|
int start = cb->args[0];
|
|
int idx = 0;
|
|
int err;
|
|
|
|
mutex_lock(&devlink_mutex);
|
|
mutex_lock(&devlink_port_mutex);
|
|
list_for_each_entry(devlink, &devlink_list, list) {
|
|
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
|
|
continue;
|
|
list_for_each_entry(devlink_port, &devlink->port_list, list) {
|
|
if (idx < start) {
|
|
idx++;
|
|
continue;
|
|
}
|
|
err = devlink_nl_port_fill(msg, devlink, devlink_port,
|
|
DEVLINK_CMD_NEW,
|
|
NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq,
|
|
NLM_F_MULTI);
|
|
if (err)
|
|
goto out;
|
|
idx++;
|
|
}
|
|
}
|
|
out:
|
|
mutex_unlock(&devlink_port_mutex);
|
|
mutex_unlock(&devlink_mutex);
|
|
|
|
cb->args[0] = idx;
|
|
return msg->len;
|
|
}
|
|
|
|
static int devlink_port_type_set(struct devlink *devlink,
|
|
struct devlink_port *devlink_port,
|
|
enum devlink_port_type port_type)
|
|
|
|
{
|
|
int err;
|
|
|
|
if (devlink->ops && devlink->ops->port_type_set) {
|
|
if (port_type == DEVLINK_PORT_TYPE_NOTSET)
|
|
return -EINVAL;
|
|
if (port_type == devlink_port->type)
|
|
return 0;
|
|
err = devlink->ops->port_type_set(devlink_port, port_type);
|
|
if (err)
|
|
return err;
|
|
devlink_port->desired_type = port_type;
|
|
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
|
|
return 0;
|
|
}
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink_port *devlink_port = info->user_ptr[0];
|
|
struct devlink *devlink = devlink_port->devlink;
|
|
int err;
|
|
|
|
if (info->attrs[DEVLINK_ATTR_PORT_TYPE]) {
|
|
enum devlink_port_type port_type;
|
|
|
|
port_type = nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_TYPE]);
|
|
err = devlink_port_type_set(devlink, devlink_port, port_type);
|
|
if (err)
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int devlink_port_split(struct devlink *devlink,
|
|
u32 port_index, u32 count)
|
|
|
|
{
|
|
if (devlink->ops && devlink->ops->port_split)
|
|
return devlink->ops->port_split(devlink, port_index, count);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static int devlink_nl_cmd_port_split_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
u32 port_index;
|
|
u32 count;
|
|
|
|
if (!info->attrs[DEVLINK_ATTR_PORT_INDEX] ||
|
|
!info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT])
|
|
return -EINVAL;
|
|
|
|
port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
|
|
count = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT]);
|
|
return devlink_port_split(devlink, port_index, count);
|
|
}
|
|
|
|
static int devlink_port_unsplit(struct devlink *devlink, u32 port_index)
|
|
|
|
{
|
|
if (devlink->ops && devlink->ops->port_unsplit)
|
|
return devlink->ops->port_unsplit(devlink, port_index);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static int devlink_nl_cmd_port_unsplit_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
u32 port_index;
|
|
|
|
if (!info->attrs[DEVLINK_ATTR_PORT_INDEX])
|
|
return -EINVAL;
|
|
|
|
port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
|
|
return devlink_port_unsplit(devlink, port_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;
|
|
}
|
|
|
|
static int devlink_nl_cmd_sb_get_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct devlink_sb *devlink_sb = info->user_ptr[1];
|
|
struct sk_buff *msg;
|
|
int err;
|
|
|
|
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_cmd_sb_get_dumpit(struct sk_buff *msg,
|
|
struct netlink_callback *cb)
|
|
{
|
|
struct devlink *devlink;
|
|
struct devlink_sb *devlink_sb;
|
|
int start = cb->args[0];
|
|
int idx = 0;
|
|
int err;
|
|
|
|
mutex_lock(&devlink_mutex);
|
|
list_for_each_entry(devlink, &devlink_list, list) {
|
|
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
|
|
continue;
|
|
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
|
|
if (idx < start) {
|
|
idx++;
|
|
continue;
|
|
}
|
|
err = devlink_nl_sb_fill(msg, devlink, devlink_sb,
|
|
DEVLINK_CMD_SB_NEW,
|
|
NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq,
|
|
NLM_F_MULTI);
|
|
if (err)
|
|
goto out;
|
|
idx++;
|
|
}
|
|
}
|
|
out:
|
|
mutex_unlock(&devlink_mutex);
|
|
|
|
cb->args[0] = idx;
|
|
return msg->len;
|
|
}
|
|
|
|
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;
|
|
|
|
genlmsg_end(msg, hdr);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
genlmsg_cancel(msg, hdr);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
static int devlink_nl_cmd_sb_pool_get_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct devlink_sb *devlink_sb = info->user_ptr[1];
|
|
struct sk_buff *msg;
|
|
u16 pool_index;
|
|
int err;
|
|
|
|
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
|
|
&pool_index);
|
|
if (err)
|
|
return err;
|
|
|
|
if (!devlink->ops || !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)
|
|
{
|
|
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, NLM_F_MULTI);
|
|
if (err)
|
|
return err;
|
|
(*p_idx)++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int devlink_nl_cmd_sb_pool_get_dumpit(struct sk_buff *msg,
|
|
struct netlink_callback *cb)
|
|
{
|
|
struct devlink *devlink;
|
|
struct devlink_sb *devlink_sb;
|
|
int start = cb->args[0];
|
|
int idx = 0;
|
|
int err;
|
|
|
|
mutex_lock(&devlink_mutex);
|
|
list_for_each_entry(devlink, &devlink_list, list) {
|
|
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) ||
|
|
!devlink->ops || !devlink->ops->sb_pool_get)
|
|
continue;
|
|
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
|
|
err = __sb_pool_get_dumpit(msg, start, &idx, devlink,
|
|
devlink_sb,
|
|
NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq);
|
|
if (err && err != -EOPNOTSUPP)
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
mutex_unlock(&devlink_mutex);
|
|
|
|
cb->args[0] = idx;
|
|
return msg->len;
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
|
const struct devlink_ops *ops = devlink->ops;
|
|
|
|
if (ops && ops->sb_pool_set)
|
|
return ops->sb_pool_set(devlink, sb_index, pool_index,
|
|
size, threshold_type);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static int devlink_nl_cmd_sb_pool_set_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct devlink_sb *devlink_sb = info->user_ptr[1];
|
|
enum devlink_sb_threshold_type threshold_type;
|
|
u16 pool_index;
|
|
u32 size;
|
|
int err;
|
|
|
|
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 (!info->attrs[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);
|
|
}
|
|
|
|
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)
|
|
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;
|
|
}
|
|
|
|
static int devlink_nl_cmd_sb_port_pool_get_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink_port *devlink_port = info->user_ptr[0];
|
|
struct devlink *devlink = devlink_port->devlink;
|
|
struct devlink_sb *devlink_sb = info->user_ptr[1];
|
|
struct sk_buff *msg;
|
|
u16 pool_index;
|
|
int err;
|
|
|
|
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
|
|
&pool_index);
|
|
if (err)
|
|
return err;
|
|
|
|
if (!devlink->ops || !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)
|
|
{
|
|
struct devlink_port *devlink_port;
|
|
u16 pool_count = devlink_sb_pool_count(devlink_sb);
|
|
u16 pool_index;
|
|
int err;
|
|
|
|
list_for_each_entry(devlink_port, &devlink->port_list, list) {
|
|
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,
|
|
NLM_F_MULTI);
|
|
if (err)
|
|
return err;
|
|
(*p_idx)++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int devlink_nl_cmd_sb_port_pool_get_dumpit(struct sk_buff *msg,
|
|
struct netlink_callback *cb)
|
|
{
|
|
struct devlink *devlink;
|
|
struct devlink_sb *devlink_sb;
|
|
int start = cb->args[0];
|
|
int idx = 0;
|
|
int err;
|
|
|
|
mutex_lock(&devlink_mutex);
|
|
mutex_lock(&devlink_port_mutex);
|
|
list_for_each_entry(devlink, &devlink_list, list) {
|
|
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) ||
|
|
!devlink->ops || !devlink->ops->sb_port_pool_get)
|
|
continue;
|
|
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
|
|
err = __sb_port_pool_get_dumpit(msg, start, &idx,
|
|
devlink, devlink_sb,
|
|
NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq);
|
|
if (err && err != -EOPNOTSUPP)
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
mutex_unlock(&devlink_port_mutex);
|
|
mutex_unlock(&devlink_mutex);
|
|
|
|
cb->args[0] = idx;
|
|
return msg->len;
|
|
}
|
|
|
|
static int devlink_sb_port_pool_set(struct devlink_port *devlink_port,
|
|
unsigned int sb_index, u16 pool_index,
|
|
u32 threshold)
|
|
|
|
{
|
|
const struct devlink_ops *ops = devlink_port->devlink->ops;
|
|
|
|
if (ops && ops->sb_port_pool_set)
|
|
return ops->sb_port_pool_set(devlink_port, sb_index,
|
|
pool_index, threshold);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static 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[0];
|
|
struct devlink_sb *devlink_sb = info->user_ptr[1];
|
|
u16 pool_index;
|
|
u32 threshold;
|
|
int err;
|
|
|
|
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
|
|
&pool_index);
|
|
if (err)
|
|
return err;
|
|
|
|
if (!info->attrs[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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int devlink_nl_cmd_sb_tc_pool_bind_get_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink_port *devlink_port = info->user_ptr[0];
|
|
struct devlink *devlink = devlink_port->devlink;
|
|
struct devlink_sb *devlink_sb = info->user_ptr[1];
|
|
struct sk_buff *msg;
|
|
enum devlink_sb_pool_type pool_type;
|
|
u16 tc_index;
|
|
int err;
|
|
|
|
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 || !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)
|
|
{
|
|
struct devlink_port *devlink_port;
|
|
u16 tc_index;
|
|
int err;
|
|
|
|
list_for_each_entry(devlink_port, &devlink->port_list, list) {
|
|
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,
|
|
NLM_F_MULTI);
|
|
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,
|
|
NLM_F_MULTI);
|
|
if (err)
|
|
return err;
|
|
(*p_idx)++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
devlink_nl_cmd_sb_tc_pool_bind_get_dumpit(struct sk_buff *msg,
|
|
struct netlink_callback *cb)
|
|
{
|
|
struct devlink *devlink;
|
|
struct devlink_sb *devlink_sb;
|
|
int start = cb->args[0];
|
|
int idx = 0;
|
|
int err;
|
|
|
|
mutex_lock(&devlink_mutex);
|
|
mutex_lock(&devlink_port_mutex);
|
|
list_for_each_entry(devlink, &devlink_list, list) {
|
|
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)) ||
|
|
!devlink->ops || !devlink->ops->sb_tc_pool_bind_get)
|
|
continue;
|
|
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
|
|
err = __sb_tc_pool_bind_get_dumpit(msg, start, &idx,
|
|
devlink,
|
|
devlink_sb,
|
|
NETLINK_CB(cb->skb).portid,
|
|
cb->nlh->nlmsg_seq);
|
|
if (err && err != -EOPNOTSUPP)
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
mutex_unlock(&devlink_port_mutex);
|
|
mutex_unlock(&devlink_mutex);
|
|
|
|
cb->args[0] = idx;
|
|
return msg->len;
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
|
const struct devlink_ops *ops = devlink_port->devlink->ops;
|
|
|
|
if (ops && ops->sb_tc_pool_bind_set)
|
|
return ops->sb_tc_pool_bind_set(devlink_port, sb_index,
|
|
tc_index, pool_type,
|
|
pool_index, threshold);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static 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[0];
|
|
struct devlink_sb *devlink_sb = info->user_ptr[1];
|
|
enum devlink_sb_pool_type pool_type;
|
|
u16 tc_index;
|
|
u16 pool_index;
|
|
u32 threshold;
|
|
int err;
|
|
|
|
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 (!info->attrs[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);
|
|
}
|
|
|
|
static int devlink_nl_cmd_sb_occ_snapshot_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct devlink_sb *devlink_sb = info->user_ptr[1];
|
|
const struct devlink_ops *ops = devlink->ops;
|
|
|
|
if (ops && ops->sb_occ_snapshot)
|
|
return ops->sb_occ_snapshot(devlink, devlink_sb->index);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static int devlink_nl_cmd_sb_occ_max_clear_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
struct devlink_sb *devlink_sb = info->user_ptr[1];
|
|
const struct devlink_ops *ops = devlink->ops;
|
|
|
|
if (ops && ops->sb_occ_max_clear)
|
|
return ops->sb_occ_max_clear(devlink, devlink_sb->index);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static int devlink_nl_eswitch_fill(struct sk_buff *msg, struct devlink *devlink,
|
|
enum devlink_command cmd, u32 portid,
|
|
u32 seq, int flags)
|
|
{
|
|
const struct devlink_ops *ops = devlink->ops;
|
|
void *hdr;
|
|
int err = 0;
|
|
u16 mode;
|
|
u8 inline_mode;
|
|
|
|
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
|
|
if (!hdr)
|
|
return -EMSGSIZE;
|
|
|
|
err = devlink_nl_put_handle(msg, devlink);
|
|
if (err)
|
|
goto nla_put_failure;
|
|
|
|
if (ops->eswitch_mode_get) {
|
|
err = ops->eswitch_mode_get(devlink, &mode);
|
|
if (err)
|
|
goto nla_put_failure;
|
|
err = nla_put_u16(msg, DEVLINK_ATTR_ESWITCH_MODE, mode);
|
|
if (err)
|
|
goto nla_put_failure;
|
|
}
|
|
|
|
if (ops->eswitch_inline_mode_get) {
|
|
err = ops->eswitch_inline_mode_get(devlink, &inline_mode);
|
|
if (err)
|
|
goto nla_put_failure;
|
|
err = nla_put_u8(msg, DEVLINK_ATTR_ESWITCH_INLINE_MODE,
|
|
inline_mode);
|
|
if (err)
|
|
goto nla_put_failure;
|
|
}
|
|
|
|
genlmsg_end(msg, hdr);
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
genlmsg_cancel(msg, hdr);
|
|
return err;
|
|
}
|
|
|
|
static int devlink_nl_cmd_eswitch_get_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
const struct devlink_ops *ops = devlink->ops;
|
|
struct sk_buff *msg;
|
|
int err;
|
|
|
|
if (!ops)
|
|
return -EOPNOTSUPP;
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
if (!msg)
|
|
return -ENOMEM;
|
|
|
|
err = devlink_nl_eswitch_fill(msg, devlink, DEVLINK_CMD_ESWITCH_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_eswitch_set_doit(struct sk_buff *skb,
|
|
struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
const struct devlink_ops *ops = devlink->ops;
|
|
u16 mode;
|
|
u8 inline_mode;
|
|
int err = 0;
|
|
|
|
if (!ops)
|
|
return -EOPNOTSUPP;
|
|
|
|
if (info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) {
|
|
if (!ops->eswitch_mode_set)
|
|
return -EOPNOTSUPP;
|
|
mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]);
|
|
err = ops->eswitch_mode_set(devlink, mode);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (info->attrs[DEVLINK_ATTR_ESWITCH_INLINE_MODE]) {
|
|
if (!ops->eswitch_inline_mode_set)
|
|
return -EOPNOTSUPP;
|
|
inline_mode = nla_get_u8(
|
|
info->attrs[DEVLINK_ATTR_ESWITCH_INLINE_MODE]);
|
|
err = ops->eswitch_inline_mode_set(devlink, inline_mode);
|
|
if (err)
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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(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(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(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(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;
|
|
|
|
table_attr = nla_nest_start(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 (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)
|
|
return -EMSGSIZE;
|
|
|
|
if (devlink_nl_put_handle(skb, devlink))
|
|
goto nla_put_failure;
|
|
tables_attr = nla_nest_start(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)
|
|
goto err_skb_send_alloc;
|
|
goto send_done;
|
|
}
|
|
|
|
return genlmsg_reply(skb, info);
|
|
|
|
nla_put_failure:
|
|
err = -EMSGSIZE;
|
|
err_table_put:
|
|
err_skb_send_alloc:
|
|
genlmsg_cancel(skb, hdr);
|
|
nlmsg_free(skb);
|
|
return err;
|
|
}
|
|
|
|
static 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(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(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(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(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(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_dpipe_table *table;
|
|
|
|
list_for_each_entry_rcu(table, dpipe_tables, list) {
|
|
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(dump_ctx->skb,
|
|
DEVLINK_ATTR_DPIPE_ENTRIES);
|
|
if (!dump_ctx->nest)
|
|
goto nla_put_failure;
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
genlmsg_cancel(dump_ctx->skb, dump_ctx->hdr);
|
|
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);
|
|
|
|
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)
|
|
goto err_entries_dump;
|
|
|
|
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)
|
|
goto err_skb_send_alloc;
|
|
goto send_done;
|
|
}
|
|
return genlmsg_reply(dump_ctx.skb, info);
|
|
|
|
err_entries_dump:
|
|
err_skb_send_alloc:
|
|
genlmsg_cancel(dump_ctx.skb, dump_ctx.hdr);
|
|
nlmsg_free(dump_ctx.skb);
|
|
return err;
|
|
}
|
|
|
|
static 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 (!info->attrs[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);
|
|
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(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(skb, DEVLINK_ATTR_DPIPE_HEADER);
|
|
if (!header)
|
|
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(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)
|
|
return -EMSGSIZE;
|
|
|
|
if (devlink_nl_put_handle(skb, devlink))
|
|
goto nla_put_failure;
|
|
headers_attr = nla_nest_start(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)
|
|
goto err_skb_send_alloc;
|
|
goto send_done;
|
|
}
|
|
return genlmsg_reply(skb, info);
|
|
|
|
nla_put_failure:
|
|
err = -EMSGSIZE;
|
|
err_table_put:
|
|
err_skb_send_alloc:
|
|
genlmsg_cancel(skb, hdr);
|
|
nlmsg_free(skb);
|
|
return err;
|
|
}
|
|
|
|
static 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);
|
|
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;
|
|
}
|
|
|
|
static 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 (!info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME] ||
|
|
!info->attrs[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);
|
|
}
|
|
|
|
static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
|
|
[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
|
|
[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },
|
|
[DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 },
|
|
[DEVLINK_ATTR_PORT_TYPE] = { .type = NLA_U16 },
|
|
[DEVLINK_ATTR_PORT_SPLIT_COUNT] = { .type = NLA_U32 },
|
|
[DEVLINK_ATTR_SB_INDEX] = { .type = NLA_U32 },
|
|
[DEVLINK_ATTR_SB_POOL_INDEX] = { .type = NLA_U16 },
|
|
[DEVLINK_ATTR_SB_POOL_TYPE] = { .type = NLA_U8 },
|
|
[DEVLINK_ATTR_SB_POOL_SIZE] = { .type = NLA_U32 },
|
|
[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE] = { .type = NLA_U8 },
|
|
[DEVLINK_ATTR_SB_THRESHOLD] = { .type = NLA_U32 },
|
|
[DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 },
|
|
[DEVLINK_ATTR_ESWITCH_MODE] = { .type = NLA_U16 },
|
|
[DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 },
|
|
[DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING },
|
|
[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 },
|
|
};
|
|
|
|
static const struct genl_ops devlink_nl_ops[] = {
|
|
{
|
|
.cmd = DEVLINK_CMD_GET,
|
|
.doit = devlink_nl_cmd_get_doit,
|
|
.dumpit = devlink_nl_cmd_get_dumpit,
|
|
.policy = devlink_nl_policy,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
|
|
/* can be retrieved by unprivileged users */
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_PORT_GET,
|
|
.doit = devlink_nl_cmd_port_get_doit,
|
|
.dumpit = devlink_nl_cmd_port_get_dumpit,
|
|
.policy = devlink_nl_policy,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
|
|
/* can be retrieved by unprivileged users */
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_PORT_SET,
|
|
.doit = devlink_nl_cmd_port_set_doit,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_PORT_SPLIT,
|
|
.doit = devlink_nl_cmd_port_split_doit,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_PORT_UNSPLIT,
|
|
.doit = devlink_nl_cmd_port_unsplit_doit,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_SB_GET,
|
|
.doit = devlink_nl_cmd_sb_get_doit,
|
|
.dumpit = devlink_nl_cmd_sb_get_dumpit,
|
|
.policy = devlink_nl_policy,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
|
|
DEVLINK_NL_FLAG_NEED_SB,
|
|
/* can be retrieved by unprivileged users */
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_SB_POOL_GET,
|
|
.doit = devlink_nl_cmd_sb_pool_get_doit,
|
|
.dumpit = devlink_nl_cmd_sb_pool_get_dumpit,
|
|
.policy = devlink_nl_policy,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
|
|
DEVLINK_NL_FLAG_NEED_SB,
|
|
/* can be retrieved by unprivileged users */
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_SB_POOL_SET,
|
|
.doit = devlink_nl_cmd_sb_pool_set_doit,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
|
|
DEVLINK_NL_FLAG_NEED_SB,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_SB_PORT_POOL_GET,
|
|
.doit = devlink_nl_cmd_sb_port_pool_get_doit,
|
|
.dumpit = devlink_nl_cmd_sb_port_pool_get_dumpit,
|
|
.policy = devlink_nl_policy,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
|
|
DEVLINK_NL_FLAG_NEED_SB,
|
|
/* can be retrieved by unprivileged users */
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_SB_PORT_POOL_SET,
|
|
.doit = devlink_nl_cmd_sb_port_pool_set_doit,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
|
|
DEVLINK_NL_FLAG_NEED_SB,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_SB_TC_POOL_BIND_GET,
|
|
.doit = devlink_nl_cmd_sb_tc_pool_bind_get_doit,
|
|
.dumpit = devlink_nl_cmd_sb_tc_pool_bind_get_dumpit,
|
|
.policy = devlink_nl_policy,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
|
|
DEVLINK_NL_FLAG_NEED_SB,
|
|
/* can be retrieved by unprivileged users */
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_SB_TC_POOL_BIND_SET,
|
|
.doit = devlink_nl_cmd_sb_tc_pool_bind_set_doit,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_PORT |
|
|
DEVLINK_NL_FLAG_NEED_SB,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_SB_OCC_SNAPSHOT,
|
|
.doit = devlink_nl_cmd_sb_occ_snapshot_doit,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
|
|
DEVLINK_NL_FLAG_NEED_SB |
|
|
DEVLINK_NL_FLAG_LOCK_PORTS,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_SB_OCC_MAX_CLEAR,
|
|
.doit = devlink_nl_cmd_sb_occ_max_clear_doit,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
|
|
DEVLINK_NL_FLAG_NEED_SB |
|
|
DEVLINK_NL_FLAG_LOCK_PORTS,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_ESWITCH_GET,
|
|
.doit = devlink_nl_cmd_eswitch_get_doit,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_ESWITCH_SET,
|
|
.doit = devlink_nl_cmd_eswitch_set_doit,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_DPIPE_TABLE_GET,
|
|
.doit = devlink_nl_cmd_dpipe_table_get,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_DPIPE_ENTRIES_GET,
|
|
.doit = devlink_nl_cmd_dpipe_entries_get,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_DPIPE_HEADERS_GET,
|
|
.doit = devlink_nl_cmd_dpipe_headers_get,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
|
|
.doit = devlink_nl_cmd_dpipe_table_counters_set,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
|
|
},
|
|
};
|
|
|
|
static struct genl_family devlink_nl_family __ro_after_init = {
|
|
.name = DEVLINK_GENL_NAME,
|
|
.version = DEVLINK_GENL_VERSION,
|
|
.maxattr = DEVLINK_ATTR_MAX,
|
|
.netnsok = true,
|
|
.pre_doit = devlink_nl_pre_doit,
|
|
.post_doit = devlink_nl_post_doit,
|
|
.module = THIS_MODULE,
|
|
.ops = devlink_nl_ops,
|
|
.n_ops = ARRAY_SIZE(devlink_nl_ops),
|
|
.mcgrps = devlink_nl_mcgrps,
|
|
.n_mcgrps = ARRAY_SIZE(devlink_nl_mcgrps),
|
|
};
|
|
|
|
/**
|
|
* devlink_alloc - Allocate new devlink instance resources
|
|
*
|
|
* @ops: ops
|
|
* @priv_size: size of user private data
|
|
*
|
|
* Allocate new devlink instance resources, including devlink index
|
|
* and name.
|
|
*/
|
|
struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size)
|
|
{
|
|
struct devlink *devlink;
|
|
|
|
devlink = kzalloc(sizeof(*devlink) + priv_size, GFP_KERNEL);
|
|
if (!devlink)
|
|
return NULL;
|
|
devlink->ops = ops;
|
|
devlink_net_set(devlink, &init_net);
|
|
INIT_LIST_HEAD(&devlink->port_list);
|
|
INIT_LIST_HEAD(&devlink->sb_list);
|
|
INIT_LIST_HEAD_RCU(&devlink->dpipe_table_list);
|
|
return devlink;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_alloc);
|
|
|
|
/**
|
|
* devlink_register - Register devlink instance
|
|
*
|
|
* @devlink: devlink
|
|
*/
|
|
int devlink_register(struct devlink *devlink, struct device *dev)
|
|
{
|
|
mutex_lock(&devlink_mutex);
|
|
devlink->dev = dev;
|
|
list_add_tail(&devlink->list, &devlink_list);
|
|
devlink_notify(devlink, DEVLINK_CMD_NEW);
|
|
mutex_unlock(&devlink_mutex);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_register);
|
|
|
|
/**
|
|
* devlink_unregister - Unregister devlink instance
|
|
*
|
|
* @devlink: devlink
|
|
*/
|
|
void devlink_unregister(struct devlink *devlink)
|
|
{
|
|
mutex_lock(&devlink_mutex);
|
|
devlink_notify(devlink, DEVLINK_CMD_DEL);
|
|
list_del(&devlink->list);
|
|
mutex_unlock(&devlink_mutex);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_unregister);
|
|
|
|
/**
|
|
* devlink_free - Free devlink instance resources
|
|
*
|
|
* @devlink: devlink
|
|
*/
|
|
void devlink_free(struct devlink *devlink)
|
|
{
|
|
kfree(devlink);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_free);
|
|
|
|
/**
|
|
* devlink_port_register - Register devlink port
|
|
*
|
|
* @devlink: devlink
|
|
* @devlink_port: devlink port
|
|
* @port_index
|
|
*
|
|
* Register devlink port with provided port index. User can use
|
|
* any indexing, even hw-related one. devlink_port structure
|
|
* is convenient to be embedded inside user driver private structure.
|
|
* Note that the caller should take care of zeroing the devlink_port
|
|
* structure.
|
|
*/
|
|
int devlink_port_register(struct devlink *devlink,
|
|
struct devlink_port *devlink_port,
|
|
unsigned int port_index)
|
|
{
|
|
mutex_lock(&devlink_port_mutex);
|
|
if (devlink_port_index_exists(devlink, port_index)) {
|
|
mutex_unlock(&devlink_port_mutex);
|
|
return -EEXIST;
|
|
}
|
|
devlink_port->devlink = devlink;
|
|
devlink_port->index = port_index;
|
|
devlink_port->registered = true;
|
|
list_add_tail(&devlink_port->list, &devlink->port_list);
|
|
mutex_unlock(&devlink_port_mutex);
|
|
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_port_register);
|
|
|
|
/**
|
|
* devlink_port_unregister - Unregister devlink port
|
|
*
|
|
* @devlink_port: devlink port
|
|
*/
|
|
void devlink_port_unregister(struct devlink_port *devlink_port)
|
|
{
|
|
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
|
|
mutex_lock(&devlink_port_mutex);
|
|
list_del(&devlink_port->list);
|
|
mutex_unlock(&devlink_port_mutex);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_port_unregister);
|
|
|
|
static void __devlink_port_type_set(struct devlink_port *devlink_port,
|
|
enum devlink_port_type type,
|
|
void *type_dev)
|
|
{
|
|
devlink_port->type = type;
|
|
devlink_port->type_dev = type_dev;
|
|
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
|
|
}
|
|
|
|
/**
|
|
* devlink_port_type_eth_set - Set port type to Ethernet
|
|
*
|
|
* @devlink_port: devlink port
|
|
* @netdev: related netdevice
|
|
*/
|
|
void devlink_port_type_eth_set(struct devlink_port *devlink_port,
|
|
struct net_device *netdev)
|
|
{
|
|
return __devlink_port_type_set(devlink_port,
|
|
DEVLINK_PORT_TYPE_ETH, netdev);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_port_type_eth_set);
|
|
|
|
/**
|
|
* devlink_port_type_ib_set - Set port type to InfiniBand
|
|
*
|
|
* @devlink_port: devlink port
|
|
* @ibdev: related IB device
|
|
*/
|
|
void devlink_port_type_ib_set(struct devlink_port *devlink_port,
|
|
struct ib_device *ibdev)
|
|
{
|
|
return __devlink_port_type_set(devlink_port,
|
|
DEVLINK_PORT_TYPE_IB, ibdev);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_port_type_ib_set);
|
|
|
|
/**
|
|
* devlink_port_type_clear - Clear port type
|
|
*
|
|
* @devlink_port: devlink port
|
|
*/
|
|
void devlink_port_type_clear(struct devlink_port *devlink_port)
|
|
{
|
|
return __devlink_port_type_set(devlink_port,
|
|
DEVLINK_PORT_TYPE_NOTSET, NULL);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_port_type_clear);
|
|
|
|
/**
|
|
* devlink_port_split_set - Set port is split
|
|
*
|
|
* @devlink_port: devlink port
|
|
* @split_group: split group - identifies group split port is part of
|
|
*/
|
|
void devlink_port_split_set(struct devlink_port *devlink_port,
|
|
u32 split_group)
|
|
{
|
|
devlink_port->split = true;
|
|
devlink_port->split_group = split_group;
|
|
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_port_split_set);
|
|
|
|
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)
|
|
{
|
|
struct devlink_sb *devlink_sb;
|
|
int err = 0;
|
|
|
|
mutex_lock(&devlink_mutex);
|
|
if (devlink_sb_index_exists(devlink, sb_index)) {
|
|
err = -EEXIST;
|
|
goto unlock;
|
|
}
|
|
|
|
devlink_sb = kzalloc(sizeof(*devlink_sb), GFP_KERNEL);
|
|
if (!devlink_sb) {
|
|
err = -ENOMEM;
|
|
goto unlock;
|
|
}
|
|
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);
|
|
unlock:
|
|
mutex_unlock(&devlink_mutex);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_sb_register);
|
|
|
|
void devlink_sb_unregister(struct devlink *devlink, unsigned int sb_index)
|
|
{
|
|
struct devlink_sb *devlink_sb;
|
|
|
|
mutex_lock(&devlink_mutex);
|
|
devlink_sb = devlink_sb_get_by_index(devlink, sb_index);
|
|
WARN_ON(!devlink_sb);
|
|
list_del(&devlink_sb->list);
|
|
mutex_unlock(&devlink_mutex);
|
|
kfree(devlink_sb);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_sb_unregister);
|
|
|
|
/**
|
|
* devlink_dpipe_headers_register - register dpipe headers
|
|
*
|
|
* @devlink: devlink
|
|
* @dpipe_headers: dpipe header array
|
|
*
|
|
* Register the headers supported by hardware.
|
|
*/
|
|
int devlink_dpipe_headers_register(struct devlink *devlink,
|
|
struct devlink_dpipe_headers *dpipe_headers)
|
|
{
|
|
mutex_lock(&devlink_mutex);
|
|
devlink->dpipe_headers = dpipe_headers;
|
|
mutex_unlock(&devlink_mutex);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_headers_register);
|
|
|
|
/**
|
|
* devlink_dpipe_headers_unregister - unregister dpipe headers
|
|
*
|
|
* @devlink: devlink
|
|
*
|
|
* Unregister the headers supported by hardware.
|
|
*/
|
|
void devlink_dpipe_headers_unregister(struct devlink *devlink)
|
|
{
|
|
mutex_lock(&devlink_mutex);
|
|
devlink->dpipe_headers = NULL;
|
|
mutex_unlock(&devlink_mutex);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_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);
|
|
enabled = false;
|
|
if (table)
|
|
enabled = table->counters_enabled;
|
|
rcu_read_unlock();
|
|
return enabled;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_table_counter_enabled);
|
|
|
|
/**
|
|
* devlink_dpipe_table_register - register dpipe table
|
|
*
|
|
* @devlink: devlink
|
|
* @table_name: table name
|
|
* @table_ops: table ops
|
|
* @priv: priv
|
|
* @size: size
|
|
* @counter_control_extern: external control for counters
|
|
*/
|
|
int devlink_dpipe_table_register(struct devlink *devlink,
|
|
const char *table_name,
|
|
struct devlink_dpipe_table_ops *table_ops,
|
|
void *priv, u64 size,
|
|
bool counter_control_extern)
|
|
{
|
|
struct devlink_dpipe_table *table;
|
|
|
|
if (devlink_dpipe_table_find(&devlink->dpipe_table_list, table_name))
|
|
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->size = size;
|
|
table->counter_control_extern = counter_control_extern;
|
|
|
|
mutex_lock(&devlink_mutex);
|
|
list_add_tail_rcu(&table->list, &devlink->dpipe_table_list);
|
|
mutex_unlock(&devlink_mutex);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_table_register);
|
|
|
|
/**
|
|
* devlink_dpipe_table_unregister - unregister dpipe table
|
|
*
|
|
* @devlink: devlink
|
|
* @table_name: table name
|
|
*/
|
|
void devlink_dpipe_table_unregister(struct devlink *devlink,
|
|
const char *table_name)
|
|
{
|
|
struct devlink_dpipe_table *table;
|
|
|
|
mutex_lock(&devlink_mutex);
|
|
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
|
|
table_name);
|
|
if (!table)
|
|
goto unlock;
|
|
list_del_rcu(&table->list);
|
|
mutex_unlock(&devlink_mutex);
|
|
kfree_rcu(table, rcu);
|
|
return;
|
|
unlock:
|
|
mutex_unlock(&devlink_mutex);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_table_unregister);
|
|
|
|
static int __init devlink_module_init(void)
|
|
{
|
|
return genl_register_family(&devlink_nl_family);
|
|
}
|
|
|
|
static void __exit devlink_module_exit(void)
|
|
{
|
|
genl_unregister_family(&devlink_nl_family);
|
|
}
|
|
|
|
module_init(devlink_module_init);
|
|
module_exit(devlink_module_exit);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
|
|
MODULE_DESCRIPTION("Network physical device Netlink interface");
|
|
MODULE_ALIAS_GENL_FAMILY(DEVLINK_GENL_NAME);
|