![Arkadi Sharshevsky](/assets/img/avatar_default.png)
The hardware processes which are modeled via dpipe commonly use some internal hardware resources. Such relation can improve the understanding of hardware limitations. The number of resource's unit consumed per table's entry are also provided for each table. 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>
3311 lines
84 KiB
C
3311 lines
84 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>
|
|
|
|
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(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(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(devlink_dpipe_header_ipv6);
|
|
|
|
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);
|
|
|
|
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)
|
|
|
|
/* The per devlink instance lock is taken by default in the pre-doit
|
|
* operation, yet several commands do not require this. The global
|
|
* devlink lock is taken and protects from disruption by user-calls.
|
|
*/
|
|
#define DEVLINK_NL_FLAG_NO_LOCK BIT(3)
|
|
|
|
static int devlink_nl_pre_doit(const struct genl_ops *ops,
|
|
struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct devlink *devlink;
|
|
int err;
|
|
|
|
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_NO_LOCK)
|
|
mutex_lock(&devlink->lock);
|
|
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;
|
|
|
|
devlink_port = devlink_port_get_from_info(devlink, info);
|
|
if (IS_ERR(devlink_port)) {
|
|
err = PTR_ERR(devlink_port);
|
|
goto unlock;
|
|
}
|
|
info->user_ptr[0] = devlink_port;
|
|
}
|
|
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)) {
|
|
err = PTR_ERR(devlink_sb);
|
|
goto unlock;
|
|
}
|
|
info->user_ptr[1] = devlink_sb;
|
|
}
|
|
return 0;
|
|
|
|
unlock:
|
|
if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
|
|
mutex_unlock(&devlink->lock);
|
|
mutex_unlock(&devlink_mutex);
|
|
return err;
|
|
}
|
|
|
|
static void devlink_nl_post_doit(const struct genl_ops *ops,
|
|
struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct devlink *devlink;
|
|
|
|
devlink = devlink_get_from_info(info);
|
|
if (~ops->internal_flags & DEVLINK_NL_FLAG_NO_LOCK)
|
|
mutex_unlock(&devlink->lock);
|
|
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);
|
|
list_for_each_entry(devlink, &devlink_list, list) {
|
|
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
|
|
continue;
|
|
mutex_lock(&devlink->lock);
|
|
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) {
|
|
mutex_unlock(&devlink->lock);
|
|
goto out;
|
|
}
|
|
idx++;
|
|
}
|
|
mutex_unlock(&devlink->lock);
|
|
}
|
|
out:
|
|
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;
|
|
mutex_lock(&devlink->lock);
|
|
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) {
|
|
mutex_unlock(&devlink->lock);
|
|
goto out;
|
|
}
|
|
idx++;
|
|
}
|
|
mutex_unlock(&devlink->lock);
|
|
}
|
|
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;
|
|
mutex_lock(&devlink->lock);
|
|
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) {
|
|
mutex_unlock(&devlink->lock);
|
|
goto out;
|
|
}
|
|
}
|
|
mutex_unlock(&devlink->lock);
|
|
}
|
|
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);
|
|
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;
|
|
mutex_lock(&devlink->lock);
|
|
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) {
|
|
mutex_unlock(&devlink->lock);
|
|
goto out;
|
|
}
|
|
}
|
|
mutex_unlock(&devlink->lock);
|
|
}
|
|
out:
|
|
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);
|
|
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;
|
|
|
|
mutex_lock(&devlink->lock);
|
|
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) {
|
|
mutex_unlock(&devlink->lock);
|
|
goto out;
|
|
}
|
|
}
|
|
mutex_unlock(&devlink->lock);
|
|
}
|
|
out:
|
|
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;
|
|
u8 inline_mode, encap_mode;
|
|
void *hdr;
|
|
int err = 0;
|
|
u16 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;
|
|
}
|
|
|
|
if (ops->eswitch_encap_mode_get) {
|
|
err = ops->eswitch_encap_mode_get(devlink, &encap_mode);
|
|
if (err)
|
|
goto nla_put_failure;
|
|
err = nla_put_u8(msg, DEVLINK_ATTR_ESWITCH_ENCAP_MODE, encap_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;
|
|
u8 inline_mode, encap_mode;
|
|
int err = 0;
|
|
u16 mode;
|
|
|
|
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;
|
|
}
|
|
|
|
if (info->attrs[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]) {
|
|
if (!ops->eswitch_encap_mode_set)
|
|
return -EOPNOTSUPP;
|
|
encap_mode = nla_get_u8(info->attrs[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]);
|
|
err = ops->eswitch_encap_mode_set(devlink, encap_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;
|
|
u64 table_size;
|
|
|
|
table_size = table->table_ops->size_get(table->priv);
|
|
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 (table->resource_valid) {
|
|
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);
|
|
}
|
|
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(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);
|
|
|
|
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(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)
|
|
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_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(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(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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
size_valid = false;
|
|
out:
|
|
resource->size_valid = size_valid;
|
|
}
|
|
|
|
static 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 (!info->attrs[DEVLINK_ATTR_RESOURCE_ID] ||
|
|
!info->attrs[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;
|
|
|
|
if (!resource->resource_ops->size_validate)
|
|
return -EINVAL;
|
|
|
|
size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
|
|
err = resource->resource_ops->size_validate(devlink, 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 void
|
|
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;
|
|
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);
|
|
}
|
|
|
|
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(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);
|
|
if (resource->resource_ops && resource->resource_ops->occ_get)
|
|
nla_put_u64_64bit(skb, DEVLINK_ATTR_RESOURCE_OCC,
|
|
resource->resource_ops->occ_get(devlink),
|
|
DEVLINK_ATTR_PAD);
|
|
devlink_resource_size_params_put(resource, skb);
|
|
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(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_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;
|
|
|
|
resources_attr = nla_nest_start(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_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_resource_put:
|
|
err_skb_send_alloc:
|
|
genlmsg_cancel(skb, hdr);
|
|
nlmsg_free(skb);
|
|
return err;
|
|
}
|
|
|
|
static 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);
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
static int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct devlink *devlink = info->user_ptr[0];
|
|
int err;
|
|
|
|
if (!devlink->ops->reload)
|
|
return -EOPNOTSUPP;
|
|
|
|
err = devlink_resources_validate(devlink, NULL, info);
|
|
if (err) {
|
|
NL_SET_ERR_MSG_MOD(info->extack, "resources size validation failed");
|
|
return err;
|
|
}
|
|
return devlink->ops->reload(devlink);
|
|
}
|
|
|
|
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_ESWITCH_ENCAP_MODE] = { .type = NLA_U8 },
|
|
[DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING },
|
|
[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 },
|
|
[DEVLINK_ATTR_RESOURCE_ID] = { .type = NLA_U64},
|
|
[DEVLINK_ATTR_RESOURCE_SIZE] = { .type = NLA_U64},
|
|
};
|
|
|
|
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 |
|
|
DEVLINK_NL_FLAG_NO_LOCK,
|
|
},
|
|
{
|
|
.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 |
|
|
DEVLINK_NL_FLAG_NO_LOCK,
|
|
},
|
|
{
|
|
.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,
|
|
},
|
|
{
|
|
.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,
|
|
},
|
|
{
|
|
.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,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_RESOURCE_SET,
|
|
.doit = devlink_nl_cmd_resource_set,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_RESOURCE_DUMP,
|
|
.doit = devlink_nl_cmd_resource_dump,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
|
|
},
|
|
{
|
|
.cmd = DEVLINK_CMD_RELOAD,
|
|
.doit = devlink_nl_cmd_reload,
|
|
.policy = devlink_nl_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
|
|
DEVLINK_NL_FLAG_NO_LOCK,
|
|
},
|
|
};
|
|
|
|
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);
|
|
INIT_LIST_HEAD(&devlink->resource_list);
|
|
mutex_init(&devlink->lock);
|
|
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->lock);
|
|
if (devlink_port_index_exists(devlink, port_index)) {
|
|
mutex_unlock(&devlink->lock);
|
|
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->lock);
|
|
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)
|
|
{
|
|
struct devlink *devlink = devlink_port->devlink;
|
|
|
|
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
|
|
mutex_lock(&devlink->lock);
|
|
list_del(&devlink_port->list);
|
|
mutex_unlock(&devlink->lock);
|
|
}
|
|
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->lock);
|
|
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->lock);
|
|
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->lock);
|
|
devlink_sb = devlink_sb_get_by_index(devlink, sb_index);
|
|
WARN_ON(!devlink_sb);
|
|
list_del(&devlink_sb->list);
|
|
mutex_unlock(&devlink->lock);
|
|
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->lock);
|
|
devlink->dpipe_headers = dpipe_headers;
|
|
mutex_unlock(&devlink->lock);
|
|
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->lock);
|
|
devlink->dpipe_headers = NULL;
|
|
mutex_unlock(&devlink->lock);
|
|
}
|
|
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
|
|
* @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, bool counter_control_extern)
|
|
{
|
|
struct devlink_dpipe_table *table;
|
|
|
|
if (devlink_dpipe_table_find(&devlink->dpipe_table_list, table_name))
|
|
return -EEXIST;
|
|
|
|
if (WARN_ON(!table_ops->size_get))
|
|
return -EINVAL;
|
|
|
|
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;
|
|
|
|
mutex_lock(&devlink->lock);
|
|
list_add_tail_rcu(&table->list, &devlink->dpipe_table_list);
|
|
mutex_unlock(&devlink->lock);
|
|
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->lock);
|
|
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
|
|
table_name);
|
|
if (!table)
|
|
goto unlock;
|
|
list_del_rcu(&table->list);
|
|
mutex_unlock(&devlink->lock);
|
|
kfree_rcu(table, rcu);
|
|
return;
|
|
unlock:
|
|
mutex_unlock(&devlink->lock);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_table_unregister);
|
|
|
|
/**
|
|
* devlink_resource_register - devlink resource register
|
|
*
|
|
* @devlink: devlink
|
|
* @resource_name: resource's name
|
|
* @top_hierarchy: top hierarchy
|
|
* @reload_required: reload is required for new configuration to
|
|
* apply
|
|
* @resource_size: resource's size
|
|
* @resource_id: resource's id
|
|
* @parent_reosurce_id: resource's parent id
|
|
* @size params: size parameters
|
|
* @resource_ops: resource ops
|
|
*/
|
|
int devlink_resource_register(struct devlink *devlink,
|
|
const char *resource_name,
|
|
bool top_hierarchy,
|
|
u64 resource_size,
|
|
u64 resource_id,
|
|
u64 parent_resource_id,
|
|
struct devlink_resource_size_params *size_params,
|
|
const struct devlink_resource_ops *resource_ops)
|
|
{
|
|
struct devlink_resource *resource;
|
|
struct list_head *resource_list;
|
|
int err = 0;
|
|
|
|
mutex_lock(&devlink->lock);
|
|
resource = devlink_resource_find(devlink, NULL, resource_id);
|
|
if (resource) {
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
resource = kzalloc(sizeof(*resource), GFP_KERNEL);
|
|
if (!resource) {
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
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 {
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
resource->name = resource_name;
|
|
resource->size = resource_size;
|
|
resource->size_new = resource_size;
|
|
resource->id = resource_id;
|
|
resource->resource_ops = resource_ops;
|
|
resource->size_valid = true;
|
|
resource->size_params = size_params;
|
|
INIT_LIST_HEAD(&resource->resource_list);
|
|
list_add_tail(&resource->list, resource_list);
|
|
out:
|
|
mutex_unlock(&devlink->lock);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_resource_register);
|
|
|
|
/**
|
|
* devlink_resources_unregister - free all resources
|
|
*
|
|
* @devlink: devlink
|
|
* @resource: resource
|
|
*/
|
|
void devlink_resources_unregister(struct devlink *devlink,
|
|
struct devlink_resource *resource)
|
|
{
|
|
struct devlink_resource *tmp, *child_resource;
|
|
struct list_head *resource_list;
|
|
|
|
if (resource)
|
|
resource_list = &resource->resource_list;
|
|
else
|
|
resource_list = &devlink->resource_list;
|
|
|
|
if (!resource)
|
|
mutex_lock(&devlink->lock);
|
|
|
|
list_for_each_entry_safe(child_resource, tmp, resource_list, list) {
|
|
devlink_resources_unregister(devlink, child_resource);
|
|
list_del(&child_resource->list);
|
|
kfree(child_resource);
|
|
}
|
|
|
|
if (!resource)
|
|
mutex_unlock(&devlink->lock);
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_resources_unregister);
|
|
|
|
/**
|
|
* devlink_resource_size_get - get and update size
|
|
*
|
|
* @devlink: devlink
|
|
* @resource_id: the requested resource id
|
|
* @p_resource_size: ptr to update
|
|
*/
|
|
int devlink_resource_size_get(struct devlink *devlink,
|
|
u64 resource_id,
|
|
u64 *p_resource_size)
|
|
{
|
|
struct devlink_resource *resource;
|
|
int err = 0;
|
|
|
|
mutex_lock(&devlink->lock);
|
|
resource = devlink_resource_find(devlink, NULL, resource_id);
|
|
if (!resource) {
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
*p_resource_size = resource->size_new;
|
|
resource->size = resource->size_new;
|
|
out:
|
|
mutex_unlock(&devlink->lock);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_resource_size_get);
|
|
|
|
/**
|
|
* devlink_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 devlink_dpipe_table_resource_set(struct devlink *devlink,
|
|
const char *table_name, u64 resource_id,
|
|
u64 resource_units)
|
|
{
|
|
struct devlink_dpipe_table *table;
|
|
int err = 0;
|
|
|
|
mutex_lock(&devlink->lock);
|
|
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
|
|
table_name);
|
|
if (!table) {
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
table->resource_id = resource_id;
|
|
table->resource_units = resource_units;
|
|
table->resource_valid = true;
|
|
out:
|
|
mutex_unlock(&devlink->lock);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(devlink_dpipe_table_resource_set);
|
|
|
|
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);
|