Merge branch 'marvell-prestera-devlink'

Oleksandr Mazur says:

====================
Marvell Prestera driver implementation of devlink functionality.

This patch series implement Prestera Switchdev driver devlink traps,
that are registered within the driver, as well as extend current devlink
functionality by adding new hard drop statistics counter, that could be
retrieved on-demand: the counter shows number of packets that have been
dropped by the underlying device and haven't been passed to the devlink
subsystem.

The core prestera-devlink functionality is implemented in the prestera_devlink.c.

The patch series also extends the existing devlink kernel API:
 - devlink: add trap_drop_counter_get callback for driver to register - make it possible
   to keep track of how many packets have been dropped (hard) by the switch device, before
   the packets even made it to the devlink subsystem (e.g. dropped due to RXDMA buffer
   overflow).

The core features that extend current functionality of prestera Switchdev driver:
 - add logic for driver traps and drops registration (also traps with DROP action).
 - add documentation for prestera driver traps and drops group.

PATCH v2:
 1) Rebase whole series on top of latest mater;
 2) Remove storm control-related patches, as they're out of devlink
    scope;
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2021-06-14 13:04:26 -07:00
commit 0a84a828d7
15 changed files with 848 additions and 7 deletions

View file

@ -0,0 +1,141 @@
.. SPDX-License-Identifier: GPL-2.0
=====================
prestera devlink support
=====================
This document describes the devlink features implemented by the ``prestera``
device driver.
Driver-specific Traps
=====================
.. list-table:: List of Driver-specific Traps Registered by ``prestera``
:widths: 5 5 90
* - Name
- Type
- Description
.. list-table:: List of Driver-specific Traps Registered by ``prestera``
:widths: 5 5 90
* - Name
- Type
- Description
* - ``arp_bc``
- ``trap``
- Traps ARP broadcast packets (both requests/responses)
* - ``is_is``
- ``trap``
- Traps IS-IS packets
* - ``ospf``
- ``trap``
- Traps OSPF packets
* - ``ip_bc_mac``
- ``trap``
- Traps IPv4 packets with broadcast DA Mac address
* - ``stp``
- ``trap``
- Traps STP BPDU
* - ``lacp``
- ``trap``
- Traps LACP packets
* - ``lldp``
- ``trap``
- Traps LLDP packets
* - ``router_mc``
- ``trap``
- Traps multicast packets
* - ``vrrp``
- ``trap``
- Traps VRRP packets
* - ``dhcp``
- ``trap``
- Traps DHCP packets
* - ``mtu_error``
- ``trap``
- Traps (exception) packets that exceeded port's MTU
* - ``mac_to_me``
- ``trap``
- Traps packets with switch-port's DA Mac address
* - ``ttl_error``
- ``trap``
- Traps (exception) IPv4 packets whose TTL exceeded
* - ``ipv4_options``
- ``trap``
- Traps (exception) packets due to the malformed IPV4 header options
* - ``ip_default_route``
- ``trap``
- Traps packets that have no specific IP interface (IP to me) and no forwarding prefix
* - ``local_route``
- ``trap``
- Traps packets that have been send to one of switch IP interfaces addresses
* - ``ipv4_icmp_redirect``
- ``trap``
- Traps (exception) IPV4 ICMP redirect packets
* - ``arp_response``
- ``trap``
- Traps ARP replies packets that have switch-port's DA Mac address
* - ``acl_code_0``
- ``trap``
- Traps packets that have ACL priority set to 0 (tc pref 0)
* - ``acl_code_1``
- ``trap``
- Traps packets that have ACL priority set to 1 (tc pref 1)
* - ``acl_code_2``
- ``trap``
- Traps packets that have ACL priority set to 2 (tc pref 2)
* - ``acl_code_3``
- ``trap``
- Traps packets that have ACL priority set to 3 (tc pref 3)
* - ``acl_code_4``
- ``trap``
- Traps packets that have ACL priority set to 4 (tc pref 4)
* - ``acl_code_5``
- ``trap``
- Traps packets that have ACL priority set to 5 (tc pref 5)
* - ``acl_code_6``
- ``trap``
- Traps packets that have ACL priority set to 6 (tc pref 6)
* - ``acl_code_7``
- ``trap``
- Traps packets that have ACL priority set to 7 (tc pref 7)
* - ``ipv4_bgp``
- ``trap``
- Traps IPv4 BGP packets
* - ``ssh``
- ``trap``
- Traps SSH packets
* - ``telnet``
- ``trap``
- Traps Telnet packets
* - ``icmp``
- ``trap``
- Traps ICMP packets
* - ``rxdma_drop``
- ``drop``
- Drops packets (RxDMA) due to the lack of ingress buffers etc.
* - ``port_no_vlan``
- ``drop``
- Drops packets due to faulty-configured network or due to internal bug (config issue).
* - ``local_port``
- ``drop``
- Drops packets whose decision (FDB entry) is to bridge packet back to the incoming port/trunk.
* - ``invalid_sa``
- ``drop``
- Drops packets with multicast source MAC address.
* - ``illegal_ip_addr``
- ``drop``
- Drops packets with illegal SIP/DIP multicast/unicast addresses.
* - ``illegal_ipv4_hdr``
- ``drop``
- Drops packets with illegal IPV4 header.
* - ``ip_uc_dip_da_mismatch``
- ``drop``
- Drops packets with destination MAC being unicast, but destination IP address being multicast.
* - ``ip_sip_is_zero``
- ``drop``
- Drops packets with zero (0) IPV4 source address.
* - ``met_red``
- ``drop``
- Drops non-conforming packets (dropped by Ingress policer, metering drop), e.g. packet rate exceeded configured bandwith.

View file

@ -170,6 +170,7 @@ struct prestera_event {
struct prestera_switchdev;
struct prestera_rxtx;
struct prestera_trap_data;
struct prestera_switch {
struct prestera_device *dev;
@ -177,6 +178,7 @@ struct prestera_switch {
struct prestera_rxtx *rxtx;
struct list_head event_handlers;
struct notifier_block netdev_nb;
struct prestera_trap_data *trap_data;
char base_mac[ETH_ALEN];
struct list_head port_list;
rwlock_t port_list_lock;

View file

@ -4,6 +4,352 @@
#include <net/devlink.h>
#include "prestera_devlink.h"
#include "prestera_hw.h"
/* All driver-specific traps must be documented in
* Documentation/networking/devlink/prestera.rst
*/
enum {
DEVLINK_PRESTERA_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX,
DEVLINK_PRESTERA_TRAP_ID_ARP_BC,
DEVLINK_PRESTERA_TRAP_ID_IS_IS,
DEVLINK_PRESTERA_TRAP_ID_OSPF,
DEVLINK_PRESTERA_TRAP_ID_IP_BC_MAC,
DEVLINK_PRESTERA_TRAP_ID_ROUTER_MC,
DEVLINK_PRESTERA_TRAP_ID_VRRP,
DEVLINK_PRESTERA_TRAP_ID_DHCP,
DEVLINK_PRESTERA_TRAP_ID_MAC_TO_ME,
DEVLINK_PRESTERA_TRAP_ID_IPV4_OPTIONS,
DEVLINK_PRESTERA_TRAP_ID_IP_DEFAULT_ROUTE,
DEVLINK_PRESTERA_TRAP_ID_IP_TO_ME,
DEVLINK_PRESTERA_TRAP_ID_IPV4_ICMP_REDIRECT,
DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_0,
DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_1,
DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_2,
DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_3,
DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_4,
DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_5,
DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_6,
DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_7,
DEVLINK_PRESTERA_TRAP_ID_BGP,
DEVLINK_PRESTERA_TRAP_ID_SSH,
DEVLINK_PRESTERA_TRAP_ID_TELNET,
DEVLINK_PRESTERA_TRAP_ID_ICMP,
DEVLINK_PRESTERA_TRAP_ID_MET_RED,
DEVLINK_PRESTERA_TRAP_ID_IP_SIP_IS_ZERO,
DEVLINK_PRESTERA_TRAP_ID_IP_UC_DIP_DA_MISMATCH,
DEVLINK_PRESTERA_TRAP_ID_ILLEGAL_IPV4_HDR,
DEVLINK_PRESTERA_TRAP_ID_ILLEGAL_IP_ADDR,
DEVLINK_PRESTERA_TRAP_ID_INVALID_SA,
DEVLINK_PRESTERA_TRAP_ID_LOCAL_PORT,
DEVLINK_PRESTERA_TRAP_ID_PORT_NO_VLAN,
DEVLINK_PRESTERA_TRAP_ID_RXDMA_DROP,
};
#define DEVLINK_PRESTERA_TRAP_NAME_ARP_BC \
"arp_bc"
#define DEVLINK_PRESTERA_TRAP_NAME_IS_IS \
"is_is"
#define DEVLINK_PRESTERA_TRAP_NAME_OSPF \
"ospf"
#define DEVLINK_PRESTERA_TRAP_NAME_IP_BC_MAC \
"ip_bc_mac"
#define DEVLINK_PRESTERA_TRAP_NAME_ROUTER_MC \
"router_mc"
#define DEVLINK_PRESTERA_TRAP_NAME_VRRP \
"vrrp"
#define DEVLINK_PRESTERA_TRAP_NAME_DHCP \
"dhcp"
#define DEVLINK_PRESTERA_TRAP_NAME_MAC_TO_ME \
"mac_to_me"
#define DEVLINK_PRESTERA_TRAP_NAME_IPV4_OPTIONS \
"ipv4_options"
#define DEVLINK_PRESTERA_TRAP_NAME_IP_DEFAULT_ROUTE \
"ip_default_route"
#define DEVLINK_PRESTERA_TRAP_NAME_IP_TO_ME \
"ip_to_me"
#define DEVLINK_PRESTERA_TRAP_NAME_IPV4_ICMP_REDIRECT \
"ipv4_icmp_redirect"
#define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_0 \
"acl_code_0"
#define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_1 \
"acl_code_1"
#define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_2 \
"acl_code_2"
#define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_3 \
"acl_code_3"
#define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_4 \
"acl_code_4"
#define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_5 \
"acl_code_5"
#define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_6 \
"acl_code_6"
#define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_7 \
"acl_code_7"
#define DEVLINK_PRESTERA_TRAP_NAME_BGP \
"bgp"
#define DEVLINK_PRESTERA_TRAP_NAME_SSH \
"ssh"
#define DEVLINK_PRESTERA_TRAP_NAME_TELNET \
"telnet"
#define DEVLINK_PRESTERA_TRAP_NAME_ICMP \
"icmp"
#define DEVLINK_PRESTERA_TRAP_NAME_RXDMA_DROP \
"rxdma_drop"
#define DEVLINK_PRESTERA_TRAP_NAME_PORT_NO_VLAN \
"port_no_vlan"
#define DEVLINK_PRESTERA_TRAP_NAME_LOCAL_PORT \
"local_port"
#define DEVLINK_PRESTERA_TRAP_NAME_INVALID_SA \
"invalid_sa"
#define DEVLINK_PRESTERA_TRAP_NAME_ILLEGAL_IP_ADDR \
"illegal_ip_addr"
#define DEVLINK_PRESTERA_TRAP_NAME_ILLEGAL_IPV4_HDR \
"illegal_ipv4_hdr"
#define DEVLINK_PRESTERA_TRAP_NAME_IP_UC_DIP_DA_MISMATCH \
"ip_uc_dip_da_mismatch"
#define DEVLINK_PRESTERA_TRAP_NAME_IP_SIP_IS_ZERO \
"ip_sip_is_zero"
#define DEVLINK_PRESTERA_TRAP_NAME_MET_RED \
"met_red"
struct prestera_trap {
struct devlink_trap trap;
u8 cpu_code;
};
struct prestera_trap_item {
enum devlink_trap_action action;
void *trap_ctx;
};
struct prestera_trap_data {
struct prestera_switch *sw;
struct prestera_trap_item *trap_items_arr;
u32 traps_count;
};
#define PRESTERA_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT
#define PRESTERA_TRAP_CONTROL(_id, _group_id, _action) \
DEVLINK_TRAP_GENERIC(CONTROL, _action, _id, \
DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
PRESTERA_TRAP_METADATA)
#define PRESTERA_TRAP_DRIVER_CONTROL(_id, _group_id) \
DEVLINK_TRAP_DRIVER(CONTROL, TRAP, DEVLINK_PRESTERA_TRAP_ID_##_id, \
DEVLINK_PRESTERA_TRAP_NAME_##_id, \
DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
PRESTERA_TRAP_METADATA)
#define PRESTERA_TRAP_EXCEPTION(_id, _group_id) \
DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id, \
DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
PRESTERA_TRAP_METADATA)
#define PRESTERA_TRAP_DRIVER_EXCEPTION(_id, _group_id) \
DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, DEVLINK_PRESTERA_TRAP_ID_##_id, \
DEVLINK_PRESTERA_TRAP_NAME_##_id, \
DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
PRESTERA_TRAP_METADATA)
#define PRESTERA_TRAP_DRIVER_DROP(_id, _group_id) \
DEVLINK_TRAP_DRIVER(DROP, DROP, DEVLINK_PRESTERA_TRAP_ID_##_id, \
DEVLINK_PRESTERA_TRAP_NAME_##_id, \
DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
PRESTERA_TRAP_METADATA)
static const struct devlink_trap_group prestera_trap_groups_arr[] = {
/* No policer is associated with following groups (policerid == 0)*/
DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0),
DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 0),
DEVLINK_TRAP_GROUP_GENERIC(L3_EXCEPTIONS, 0),
DEVLINK_TRAP_GROUP_GENERIC(NEIGH_DISCOVERY, 0),
DEVLINK_TRAP_GROUP_GENERIC(ACL_TRAP, 0),
DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 0),
DEVLINK_TRAP_GROUP_GENERIC(ACL_SAMPLE, 0),
DEVLINK_TRAP_GROUP_GENERIC(OSPF, 0),
DEVLINK_TRAP_GROUP_GENERIC(STP, 0),
DEVLINK_TRAP_GROUP_GENERIC(LACP, 0),
DEVLINK_TRAP_GROUP_GENERIC(LLDP, 0),
DEVLINK_TRAP_GROUP_GENERIC(VRRP, 0),
DEVLINK_TRAP_GROUP_GENERIC(DHCP, 0),
DEVLINK_TRAP_GROUP_GENERIC(BGP, 0),
DEVLINK_TRAP_GROUP_GENERIC(LOCAL_DELIVERY, 0),
DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 0),
};
/* Initialize trap list, as well as associate CPU code with them. */
static struct prestera_trap prestera_trap_items_arr[] = {
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(ARP_BC, NEIGH_DISCOVERY),
.cpu_code = 5,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(IS_IS, LOCAL_DELIVERY),
.cpu_code = 13,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(OSPF, OSPF),
.cpu_code = 16,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(IP_BC_MAC, LOCAL_DELIVERY),
.cpu_code = 19,
},
{
.trap = PRESTERA_TRAP_CONTROL(STP, STP, TRAP),
.cpu_code = 26,
},
{
.trap = PRESTERA_TRAP_CONTROL(LACP, LACP, TRAP),
.cpu_code = 27,
},
{
.trap = PRESTERA_TRAP_CONTROL(LLDP, LLDP, TRAP),
.cpu_code = 28,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(ROUTER_MC, LOCAL_DELIVERY),
.cpu_code = 29,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(VRRP, VRRP),
.cpu_code = 30,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(DHCP, DHCP),
.cpu_code = 33,
},
{
.trap = PRESTERA_TRAP_EXCEPTION(MTU_ERROR, L3_EXCEPTIONS),
.cpu_code = 63,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(MAC_TO_ME, LOCAL_DELIVERY),
.cpu_code = 65,
},
{
.trap = PRESTERA_TRAP_EXCEPTION(TTL_ERROR, L3_EXCEPTIONS),
.cpu_code = 133,
},
{
.trap = PRESTERA_TRAP_DRIVER_EXCEPTION(IPV4_OPTIONS,
L3_EXCEPTIONS),
.cpu_code = 141,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(IP_DEFAULT_ROUTE,
LOCAL_DELIVERY),
.cpu_code = 160,
},
{
.trap = PRESTERA_TRAP_CONTROL(LOCAL_ROUTE, LOCAL_DELIVERY,
TRAP),
.cpu_code = 161,
},
{
.trap = PRESTERA_TRAP_DRIVER_EXCEPTION(IPV4_ICMP_REDIRECT,
L3_EXCEPTIONS),
.cpu_code = 180,
},
{
.trap = PRESTERA_TRAP_CONTROL(ARP_RESPONSE, NEIGH_DISCOVERY,
TRAP),
.cpu_code = 188,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_0, ACL_TRAP),
.cpu_code = 192,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_1, ACL_TRAP),
.cpu_code = 193,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_2, ACL_TRAP),
.cpu_code = 194,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_3, ACL_TRAP),
.cpu_code = 195,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_4, ACL_TRAP),
.cpu_code = 196,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_5, ACL_TRAP),
.cpu_code = 197,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_6, ACL_TRAP),
.cpu_code = 198,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_7, ACL_TRAP),
.cpu_code = 199,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(BGP, BGP),
.cpu_code = 206,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(SSH, LOCAL_DELIVERY),
.cpu_code = 207,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(TELNET, LOCAL_DELIVERY),
.cpu_code = 208,
},
{
.trap = PRESTERA_TRAP_DRIVER_CONTROL(ICMP, LOCAL_DELIVERY),
.cpu_code = 209,
},
{
.trap = PRESTERA_TRAP_DRIVER_DROP(RXDMA_DROP, BUFFER_DROPS),
.cpu_code = 37,
},
{
.trap = PRESTERA_TRAP_DRIVER_DROP(PORT_NO_VLAN, L2_DROPS),
.cpu_code = 39,
},
{
.trap = PRESTERA_TRAP_DRIVER_DROP(LOCAL_PORT, L2_DROPS),
.cpu_code = 56,
},
{
.trap = PRESTERA_TRAP_DRIVER_DROP(INVALID_SA, L2_DROPS),
.cpu_code = 60,
},
{
.trap = PRESTERA_TRAP_DRIVER_DROP(ILLEGAL_IP_ADDR, L3_DROPS),
.cpu_code = 136,
},
{
.trap = PRESTERA_TRAP_DRIVER_DROP(ILLEGAL_IPV4_HDR, L3_DROPS),
.cpu_code = 137,
},
{
.trap = PRESTERA_TRAP_DRIVER_DROP(IP_UC_DIP_DA_MISMATCH,
L3_DROPS),
.cpu_code = 138,
},
{
.trap = PRESTERA_TRAP_DRIVER_DROP(IP_SIP_IS_ZERO, L3_DROPS),
.cpu_code = 145,
},
{
.trap = PRESTERA_TRAP_DRIVER_DROP(MET_RED, BUFFER_DROPS),
.cpu_code = 185,
},
};
static void prestera_devlink_traps_fini(struct prestera_switch *sw);
static int prestera_drop_counter_get(struct devlink *devlink,
const struct devlink_trap *trap,
u64 *p_drops);
static int prestera_dl_info_get(struct devlink *dl,
struct devlink_info_req *req,
@ -27,8 +373,21 @@ static int prestera_dl_info_get(struct devlink *dl,
buf);
}
static int prestera_trap_init(struct devlink *devlink,
const struct devlink_trap *trap, void *trap_ctx);
static int prestera_trap_action_set(struct devlink *devlink,
const struct devlink_trap *trap,
enum devlink_trap_action action,
struct netlink_ext_ack *extack);
static int prestera_devlink_traps_register(struct prestera_switch *sw);
static const struct devlink_ops prestera_dl_ops = {
.info_get = prestera_dl_info_get,
.trap_init = prestera_trap_init,
.trap_action_set = prestera_trap_action_set,
.trap_drop_counter_get = prestera_drop_counter_get,
};
struct prestera_switch *prestera_devlink_alloc(void)
@ -53,17 +412,32 @@ int prestera_devlink_register(struct prestera_switch *sw)
int err;
err = devlink_register(dl, sw->dev->dev);
if (err)
if (err) {
dev_err(prestera_dev(sw), "devlink_register failed: %d\n", err);
return err;
}
return err;
err = prestera_devlink_traps_register(sw);
if (err) {
devlink_unregister(dl);
dev_err(sw->dev->dev, "devlink_traps_register failed: %d\n",
err);
return err;
}
return 0;
}
void prestera_devlink_unregister(struct prestera_switch *sw)
{
struct prestera_trap_data *trap_data = sw->trap_data;
struct devlink *dl = priv_to_devlink(sw);
prestera_devlink_traps_fini(sw);
devlink_unregister(dl);
kfree(trap_data->trap_items_arr);
kfree(trap_data);
}
int prestera_devlink_port_register(struct prestera_port *port)
@ -110,3 +484,155 @@ struct devlink_port *prestera_devlink_get_port(struct net_device *dev)
return &port->dl_port;
}
static int prestera_devlink_traps_register(struct prestera_switch *sw)
{
const u32 groups_count = ARRAY_SIZE(prestera_trap_groups_arr);
const u32 traps_count = ARRAY_SIZE(prestera_trap_items_arr);
struct devlink *devlink = priv_to_devlink(sw);
struct prestera_trap_data *trap_data;
struct prestera_trap *prestera_trap;
int err, i;
trap_data = kzalloc(sizeof(*trap_data), GFP_KERNEL);
if (!trap_data)
return -ENOMEM;
trap_data->trap_items_arr = kcalloc(traps_count,
sizeof(struct prestera_trap_item),
GFP_KERNEL);
if (!trap_data->trap_items_arr) {
err = -ENOMEM;
goto err_trap_items_alloc;
}
trap_data->sw = sw;
trap_data->traps_count = traps_count;
sw->trap_data = trap_data;
err = devlink_trap_groups_register(devlink, prestera_trap_groups_arr,
groups_count);
if (err)
goto err_groups_register;
for (i = 0; i < traps_count; i++) {
prestera_trap = &prestera_trap_items_arr[i];
err = devlink_traps_register(devlink, &prestera_trap->trap, 1,
sw);
if (err)
goto err_trap_register;
}
return 0;
err_trap_register:
for (i--; i >= 0; i--) {
prestera_trap = &prestera_trap_items_arr[i];
devlink_traps_unregister(devlink, &prestera_trap->trap, 1);
}
err_groups_register:
kfree(trap_data->trap_items_arr);
err_trap_items_alloc:
kfree(trap_data);
return err;
}
static struct prestera_trap_item *
prestera_get_trap_item_by_cpu_code(struct prestera_switch *sw, u8 cpu_code)
{
struct prestera_trap_data *trap_data = sw->trap_data;
struct prestera_trap *prestera_trap;
int i;
for (i = 0; i < trap_data->traps_count; i++) {
prestera_trap = &prestera_trap_items_arr[i];
if (cpu_code == prestera_trap->cpu_code)
return &trap_data->trap_items_arr[i];
}
return NULL;
}
void prestera_devlink_trap_report(struct prestera_port *port,
struct sk_buff *skb, u8 cpu_code)
{
struct prestera_trap_item *trap_item;
struct devlink *devlink;
devlink = port->dl_port.devlink;
trap_item = prestera_get_trap_item_by_cpu_code(port->sw, cpu_code);
if (unlikely(!trap_item))
return;
devlink_trap_report(devlink, skb, trap_item->trap_ctx,
&port->dl_port, NULL);
}
static struct prestera_trap_item *
prestera_devlink_trap_item_lookup(struct prestera_switch *sw, u16 trap_id)
{
struct prestera_trap_data *trap_data = sw->trap_data;
int i;
for (i = 0; i < ARRAY_SIZE(prestera_trap_items_arr); i++) {
if (prestera_trap_items_arr[i].trap.id == trap_id)
return &trap_data->trap_items_arr[i];
}
return NULL;
}
static int prestera_trap_init(struct devlink *devlink,
const struct devlink_trap *trap, void *trap_ctx)
{
struct prestera_switch *sw = devlink_priv(devlink);
struct prestera_trap_item *trap_item;
trap_item = prestera_devlink_trap_item_lookup(sw, trap->id);
if (WARN_ON(!trap_item))
return -EINVAL;
trap_item->trap_ctx = trap_ctx;
trap_item->action = trap->init_action;
return 0;
}
static int prestera_trap_action_set(struct devlink *devlink,
const struct devlink_trap *trap,
enum devlink_trap_action action,
struct netlink_ext_ack *extack)
{
/* Currently, driver does not support trap action altering */
return -EOPNOTSUPP;
}
static int prestera_drop_counter_get(struct devlink *devlink,
const struct devlink_trap *trap,
u64 *p_drops)
{
struct prestera_switch *sw = devlink_priv(devlink);
enum prestera_hw_cpu_code_cnt_t cpu_code_type =
PRESTERA_HW_CPU_CODE_CNT_TYPE_DROP;
struct prestera_trap *prestera_trap =
container_of(trap, struct prestera_trap, trap);
return prestera_hw_cpu_code_counters_get(sw, prestera_trap->cpu_code,
cpu_code_type, p_drops);
}
static void prestera_devlink_traps_fini(struct prestera_switch *sw)
{
struct devlink *dl = priv_to_devlink(sw);
const struct devlink_trap *trap;
int i;
for (i = 0; i < ARRAY_SIZE(prestera_trap_items_arr); ++i) {
trap = &prestera_trap_items_arr[i].trap;
devlink_traps_unregister(dl, trap, 1);
}
devlink_trap_groups_unregister(dl, prestera_trap_groups_arr,
ARRAY_SIZE(prestera_trap_groups_arr));
}

View file

@ -20,4 +20,7 @@ void prestera_devlink_port_clear(struct prestera_port *port);
struct devlink_port *prestera_devlink_get_port(struct net_device *dev);
void prestera_devlink_trap_report(struct prestera_port *port,
struct sk_buff *skb, u8 cpu_code);
#endif /* _PRESTERA_DEVLINK_H_ */

View file

@ -19,6 +19,7 @@
#define PRESTERA_DSA_W1_EXT_BIT BIT(31)
#define PRESTERA_DSA_W1_CFI_BIT BIT(30)
#define PRESTERA_DSA_W1_PORT_NUM GENMASK(11, 10)
#define PRESTERA_DSA_W1_MASK_CPU_CODE GENMASK(7, 0)
#define PRESTERA_DSA_W2_EXT_BIT BIT(31)
#define PRESTERA_DSA_W2_PORT_NUM BIT(20)
@ -74,6 +75,8 @@ int prestera_dsa_parse(struct prestera_dsa *dsa, const u8 *dsa_buf)
(FIELD_GET(PRESTERA_DSA_W1_PORT_NUM, words[1]) << 5) |
(FIELD_GET(PRESTERA_DSA_W2_PORT_NUM, words[2]) << 7);
dsa->cpu_code = FIELD_GET(PRESTERA_DSA_W1_MASK_CPU_CODE, words[1]);
return 0;
}

View file

@ -27,6 +27,7 @@ struct prestera_dsa {
struct prestera_dsa_vlan vlan;
u32 hw_dev_num;
u32 port_num;
u8 cpu_code;
};
int prestera_dsa_parse(struct prestera_dsa *dsa, const u8 *dsa_buf);

View file

@ -47,6 +47,8 @@ enum prestera_cmd_type_t {
PRESTERA_CMD_TYPE_STP_PORT_SET = 0x1000,
PRESTERA_CMD_TYPE_CPU_CODE_COUNTERS_GET = 0x2000,
PRESTERA_CMD_TYPE_ACK = 0x10000,
PRESTERA_CMD_TYPE_MAX
};
@ -330,6 +332,17 @@ struct prestera_msg_lag_req {
u16 lag_id;
};
struct prestera_msg_cpu_code_counter_req {
struct prestera_msg_cmd cmd;
u8 counter_type;
u8 code;
};
struct mvsw_msg_cpu_code_counter_ret {
struct prestera_msg_ret ret;
u64 packet_count;
};
struct prestera_msg_event {
u16 type;
u16 id;
@ -1451,6 +1464,28 @@ int prestera_hw_lag_member_enable(struct prestera_port *port, u16 lag_id,
return prestera_cmd(port->sw, cmd, &req.cmd, sizeof(req));
}
int
prestera_hw_cpu_code_counters_get(struct prestera_switch *sw, u8 code,
enum prestera_hw_cpu_code_cnt_t counter_type,
u64 *packet_count)
{
struct prestera_msg_cpu_code_counter_req req = {
.counter_type = counter_type,
.code = code,
};
struct mvsw_msg_cpu_code_counter_ret resp;
int err;
err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_CPU_CODE_COUNTERS_GET,
&req.cmd, sizeof(req), &resp.ret, sizeof(resp));
if (err)
return err;
*packet_count = resp.packet_count;
return 0;
}
int prestera_hw_event_handler_register(struct prestera_switch *sw,
enum prestera_event_type type,
prestera_event_cb_t fn,

View file

@ -89,6 +89,11 @@ enum {
PRESTERA_STP_FORWARD,
};
enum prestera_hw_cpu_code_cnt_t {
PRESTERA_HW_CPU_CODE_CNT_TYPE_DROP = 0,
PRESTERA_HW_CPU_CODE_CNT_TYPE_TRAP = 1,
};
struct prestera_switch;
struct prestera_port;
struct prestera_port_stats;
@ -194,4 +199,10 @@ int prestera_hw_fdb_flush_lag(struct prestera_switch *sw, u16 lag_id,
int prestera_hw_fdb_flush_lag_vlan(struct prestera_switch *sw,
u16 lag_id, u16 vid, u32 mode);
/* HW trap/drop counters API */
int
prestera_hw_cpu_code_counters_get(struct prestera_switch *sw, u8 code,
enum prestera_hw_cpu_code_cnt_t counter_type,
u64 *packet_count);
#endif /* _PRESTERA_HW_H_ */

View file

@ -14,6 +14,7 @@
#include "prestera.h"
#include "prestera_hw.h"
#include "prestera_rxtx.h"
#include "prestera_devlink.h"
#define PRESTERA_SDMA_WAIT_MUL 10
@ -214,9 +215,10 @@ static struct sk_buff *prestera_sdma_rx_skb_get(struct prestera_sdma *sdma,
static int prestera_rxtx_process_skb(struct prestera_sdma *sdma,
struct sk_buff *skb)
{
const struct prestera_port *port;
struct prestera_port *port;
struct prestera_dsa dsa;
u32 hw_port, dev_id;
u8 cpu_code;
int err;
skb_pull(skb, ETH_HLEN);
@ -259,6 +261,9 @@ static int prestera_rxtx_process_skb(struct prestera_sdma *sdma,
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tci);
}
cpu_code = dsa.cpu_code;
prestera_devlink_trap_report(port, skb, cpu_code);
return 0;
}

View file

@ -269,6 +269,9 @@ static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
err = PTR_ERR(nsim_dev->nodes_ddir);
goto err_out;
}
debugfs_create_bool("fail_trap_counter_get", 0600,
nsim_dev->ddir,
&nsim_dev->fail_trap_counter_get);
nsim_udp_tunnels_debugfs_create(nsim_dev);
return 0;
@ -563,6 +566,7 @@ struct nsim_trap_data {
struct delayed_work trap_report_dw;
struct nsim_trap_item *trap_items_arr;
u64 *trap_policers_cnt_arr;
u64 trap_pkt_cnt;
struct nsim_dev *nsim_dev;
spinlock_t trap_lock; /* Protects trap_items_arr */
};
@ -1203,6 +1207,23 @@ static int nsim_rate_node_parent_set(struct devlink_rate *child,
return 0;
}
static int
nsim_dev_devlink_trap_hw_counter_get(struct devlink *devlink,
const struct devlink_trap *trap,
u64 *p_drops)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
u64 *cnt;
if (nsim_dev->fail_trap_counter_get)
return -EINVAL;
cnt = &nsim_dev->trap_data->trap_pkt_cnt;
*p_drops = (*cnt)++;
return 0;
}
static const struct devlink_ops nsim_dev_devlink_ops = {
.eswitch_mode_set = nsim_devlink_eswitch_mode_set,
.eswitch_mode_get = nsim_devlink_eswitch_mode_get,
@ -1226,6 +1247,7 @@ static const struct devlink_ops nsim_dev_devlink_ops = {
.rate_node_del = nsim_rate_node_del,
.rate_leaf_parent_set = nsim_rate_leaf_parent_set,
.rate_node_parent_set = nsim_rate_node_parent_set,
.trap_drop_counter_get = nsim_dev_devlink_trap_hw_counter_get,
};
#define NSIM_DEV_MAX_MACS_DEFAULT 32

View file

@ -249,6 +249,7 @@ struct nsim_dev {
bool fail_trap_group_set;
bool fail_trap_policer_set;
bool fail_trap_policer_counter_get;
bool fail_trap_counter_get;
struct {
struct udp_tunnel_nic_shared utn_shared;
u32 __ports[2][NSIM_UDP_TUNNEL_N_PORTS];

View file

@ -1347,6 +1347,16 @@ struct devlink_ops {
const struct devlink_trap_group *group,
enum devlink_trap_action action,
struct netlink_ext_ack *extack);
/**
* @trap_drop_counter_get: Trap drop counter get function.
*
* Should be used by device drivers to report number of packets
* that have been dropped, and cannot be passed to the devlink
* subsystem by the underlying device.
*/
int (*trap_drop_counter_get)(struct devlink *devlink,
const struct devlink_trap *trap,
u64 *p_drops);
/**
* @trap_policer_init: Trap policer initialization function.
*

View file

@ -7519,8 +7519,9 @@ static void devlink_trap_stats_read(struct devlink_stats __percpu *trap_stats,
}
}
static int devlink_trap_stats_put(struct sk_buff *msg,
struct devlink_stats __percpu *trap_stats)
static int
devlink_trap_group_stats_put(struct sk_buff *msg,
struct devlink_stats __percpu *trap_stats)
{
struct devlink_stats stats;
struct nlattr *attr;
@ -7548,6 +7549,50 @@ static int devlink_trap_stats_put(struct sk_buff *msg,
return -EMSGSIZE;
}
static int devlink_trap_stats_put(struct sk_buff *msg, struct devlink *devlink,
const struct devlink_trap_item *trap_item)
{
struct devlink_stats stats;
struct nlattr *attr;
u64 drops = 0;
int err;
if (devlink->ops->trap_drop_counter_get) {
err = devlink->ops->trap_drop_counter_get(devlink,
trap_item->trap,
&drops);
if (err)
return err;
}
devlink_trap_stats_read(trap_item->stats, &stats);
attr = nla_nest_start(msg, DEVLINK_ATTR_STATS);
if (!attr)
return -EMSGSIZE;
if (devlink->ops->trap_drop_counter_get &&
nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_DROPPED, drops,
DEVLINK_ATTR_PAD))
goto nla_put_failure;
if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_PACKETS,
stats.rx_packets, DEVLINK_ATTR_PAD))
goto nla_put_failure;
if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_BYTES,
stats.rx_bytes, DEVLINK_ATTR_PAD))
goto nla_put_failure;
nla_nest_end(msg, attr);
return 0;
nla_put_failure:
nla_nest_cancel(msg, attr);
return -EMSGSIZE;
}
static int devlink_nl_trap_fill(struct sk_buff *msg, struct devlink *devlink,
const struct devlink_trap_item *trap_item,
enum devlink_command cmd, u32 portid, u32 seq,
@ -7585,7 +7630,7 @@ static int devlink_nl_trap_fill(struct sk_buff *msg, struct devlink *devlink,
if (err)
goto nla_put_failure;
err = devlink_trap_stats_put(msg, trap_item->stats);
err = devlink_trap_stats_put(msg, devlink, trap_item);
if (err)
goto nla_put_failure;
@ -7802,7 +7847,7 @@ devlink_nl_trap_group_fill(struct sk_buff *msg, struct devlink *devlink,
group_item->policer_item->policer->id))
goto nla_put_failure;
err = devlink_trap_stats_put(msg, group_item->stats);
err = devlink_trap_group_stats_put(msg, group_item->stats);
if (err)
goto nla_put_failure;

View file

@ -165,6 +165,16 @@ trap_stats_test()
devlink_trap_action_set $trap_name "drop"
devlink_trap_stats_idle_test $trap_name
check_err $? "Stats of trap $trap_name not idle when action is drop"
echo "y"> $DEBUGFS_DIR/fail_trap_drop_counter_get
devlink -s trap show $DEVLINK_DEV trap $trap_name &> /dev/null
check_fail $? "Managed to read trap (hard dropped) statistics when should not"
echo "n"> $DEBUGFS_DIR/fail_trap_drop_counter_get
devlink -s trap show $DEVLINK_DEV trap $trap_name &> /dev/null
check_err $? "Did not manage to read trap (hard dropped) statistics when should"
devlink_trap_drop_stats_idle_test $trap_name
check_fail $? "Drop stats of trap $trap_name idle when should not"
else
devlink_trap_stats_idle_test $trap_name
check_fail $? "Stats of non-drop trap $trap_name idle when should not"

View file

@ -324,6 +324,14 @@ devlink_trap_rx_bytes_get()
| jq '.[][][]["stats"]["rx"]["bytes"]'
}
devlink_trap_drop_packets_get()
{
local trap_name=$1; shift
devlink -js trap show $DEVLINK_DEV trap $trap_name \
| jq '.[][][]["stats"]["rx"]["dropped"]'
}
devlink_trap_stats_idle_test()
{
local trap_name=$1; shift
@ -345,6 +353,24 @@ devlink_trap_stats_idle_test()
fi
}
devlink_trap_drop_stats_idle_test()
{
local trap_name=$1; shift
local t0_packets t0_bytes
t0_packets=$(devlink_trap_drop_packets_get $trap_name)
sleep 1
t1_packets=$(devlink_trap_drop_packets_get $trap_name)
if [[ $t0_packets -eq $t1_packets ]]; then
return 0
else
return 1
fi
}
devlink_traps_enable_all()
{
local trap_name