Commit 83beee5a authored by David S. Miller's avatar David S. Miller

Merge branch 'drop_monitor-for-offloaded-paths'

Ido Schimmel says:

====================
Add drop monitor for offloaded data paths

Users have several ways to debug the kernel and understand why a packet
was dropped. For example, using drop monitor and perf. Both utilities
trace kfree_skb(), which is the function called when a packet is freed
as part of a failure. The information provided by these tools is
invaluable when trying to understand the cause of a packet loss.

In recent years, large portions of the kernel data path were offloaded
to capable devices. Today, it is possible to perform L2 and L3
forwarding in hardware, as well as tunneling (IP-in-IP and VXLAN).
Different TC classifiers and actions are also offloaded to capable
devices, at both ingress and egress.

However, when the data path is offloaded it is not possible to achieve
the same level of introspection since packets are dropped by the
underlying device and never reach the kernel.

This patchset aims to solve this by allowing users to monitor packets
that the underlying device decided to drop along with relevant metadata
such as the drop reason and ingress port.

The above is achieved by exposing a fundamental capability of devices
capable of data path offloading - packet trapping. In much the same way
as drop monitor registers its probe function with the kfree_skb()
tracepoint, the device is instructed to pass to the CPU (trap) packets
that it decided to drop in various places in the pipeline.

The configuration of the device to pass such packets to the CPU is
performed using devlink, as it is not specific to a port, but rather to
a device. In the future, we plan to control the policing of such packets
using devlink, in order not to overwhelm the CPU.

While devlink is used as the control path, the dropped packets are
passed along with metadata to drop monitor, which reports them to
userspace as netlink events. This allows users to use the same interface
for the monitoring of both software and hardware drops.

Logically, the solution looks as follows:

                                    Netlink event: Packet w/ metadata
                                                   Or a summary of recent drops
                                  ^
                                  |
         Userspace                |
        +---------------------------------------------------+
         Kernel                   |
                                  |
                          +-------+--------+
                          |                |
                          |  drop_monitor  |
                          |                |
                          +-------^--------+
                                  |
                                  |
                                  |
                             +----+----+
                             |         |      Kernel's Rx path
                             | devlink |      (non-drop traps)
                             |         |
                             +----^----+      ^
                                  |           |
                                  +-----------+
                                  |
                          +-------+-------+
                          |               |
                          | Device driver |
                          |               |
                          +-------^-------+
         Kernel                   |
        +---------------------------------------------------+
         Hardware                 |
                                  | Trapped packet
                                  |
                               +--+---+
                               |      |
                               | ASIC |
                               |      |
                               +------+

In order to reduce the patch count, this patchset only includes
integration with netdevsim. A follow-up patchset will add devlink-trap
support in mlxsw.

Patches #1-#7 extend drop monitor to also monitor hardware originated
drops.

Patches #8-#10 add the devlink-trap infrastructure.

Patches #11-#12 add devlink-trap support in netdevsim.

Patches #13-#16 add tests for the generic infrastructure over netdevsim.

Example
=======

Instantiate netdevsim
---------------------

List supported traps
--------------------

netdevsim/netdevsim10:
  name source_mac_is_multicast type drop generic true action drop group l2_drops
  name vlan_tag_mismatch type drop generic true action drop group l2_drops
  name ingress_vlan_filter type drop generic true action drop group l2_drops
  name ingress_spanning_tree_filter type drop generic true action drop group l2_drops
  name port_list_is_empty type drop generic true action drop group l2_drops
  name port_loopback_filter type drop generic true action drop group l2_drops
  name fid_miss type exception generic false action trap group l2_drops
  name blackhole_route type drop generic true action drop group l3_drops
  name ttl_value_is_too_small type exception generic true action trap group l3_drops
  name tail_drop type drop generic true action drop group buffer_drops

Enable a trap
-------------

Query statistics
----------------

netdevsim/netdevsim10:
  name blackhole_route type drop generic true action trap group l3_drops
    stats:
        rx:
          bytes 7384 packets 52

Monitor dropped packets
-----------------------

dropwatch> set alertmode packet
Setting alert mode
Alert mode successfully set
dropwatch> set sw true
setting software drops monitoring to 1
dropwatch> set hw true
setting hardware drops monitoring to 1
dropwatch> start
Enabling monitoring...
Kernel monitoring activated.
Issue Ctrl-C to stop monitoring
drop at: ttl_value_is_too_small (l3_drops)
origin: hardware
input port ifindex: 55
input port name: eth0
timestamp: Mon Aug 12 10:52:20 2019 445911505 nsec
protocol: 0x800
length: 142
original length: 142

drop at: ip6_mc_input+0x8b8/0xef8 (0xffffffff9e2bb0e8)
origin: software
input port ifindex: 4
timestamp: Mon Aug 12 10:53:37 2019 024444587 nsec
protocol: 0x86dd
length: 110
original length: 110

Future plans
============

* Provide more drop reasons as well as more metadata
* Add dropmon support to libpcap, so that tcpdump/tshark could
  specifically listen on dropmon traffic, instead of capturing all
  netlink packets via nlmon interface

Changes in v3:
* Place test with the rest of the netdevsim tests
* Fix test to load netdevsim module
* Move devlink helpers from the test to devlink_lib.sh. Will be used
  by mlxsw tests
* Re-order netdevsim includes in alphabetical order
* Fix reverse xmas tree in netdevsim
* Remove double include in netdevsim

Changes in v2:
* Use drop monitor to report dropped packets instead of devlink
* Add drop monitor patches
* Add test cases
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents f7750830 95766451
.. SPDX-License-Identifier: GPL-2.0
======================
Devlink Trap netdevsim
======================
Driver-specific Traps
=====================
.. list-table:: List of Driver-specific Traps Registered by ``netdevsim``
:widths: 5 5 90
* - Name
- Type
- Description
* - ``fid_miss``
- ``exception``
- When a packet enters the device it is classified to a filtering
indentifier (FID) based on the ingress port and VLAN. This trap is used
to trap packets for which a FID could not be found
.. SPDX-License-Identifier: GPL-2.0
============
Devlink Trap
============
Background
==========
Devices capable of offloading the kernel's datapath and perform functions such
as bridging and routing must also be able to send specific packets to the
kernel (i.e., the CPU) for processing.
For example, a device acting as a multicast-aware bridge must be able to send
IGMP membership reports to the kernel for processing by the bridge module.
Without processing such packets, the bridge module could never populate its
MDB.
As another example, consider a device acting as router which has received an IP
packet with a TTL of 1. Upon routing the packet the device must send it to the
kernel so that it will route it as well and generate an ICMP Time Exceeded
error datagram. Without letting the kernel route such packets itself, utilities
such as ``traceroute`` could never work.
The fundamental ability of sending certain packets to the kernel for processing
is called "packet trapping".
Overview
========
The ``devlink-trap`` mechanism allows capable device drivers to register their
supported packet traps with ``devlink`` and report trapped packets to
``devlink`` for further analysis.
Upon receiving trapped packets, ``devlink`` will perform a per-trap packets and
bytes accounting and potentially report the packet to user space via a netlink
event along with all the provided metadata (e.g., trap reason, timestamp, input
port). This is especially useful for drop traps (see :ref:`Trap-Types`)
as it allows users to obtain further visibility into packet drops that would
otherwise be invisible.
The following diagram provides a general overview of ``devlink-trap``::
Netlink event: Packet w/ metadata
Or a summary of recent drops
^
|
Userspace |
+---------------------------------------------------+
Kernel |
|
+-------+--------+
| |
| drop_monitor |
| |
+-------^--------+
|
|
|
+----+----+
| | Kernel's Rx path
| devlink | (non-drop traps)
| |
+----^----+ ^
| |
+-----------+
|
+-------+-------+
| |
| Device driver |
| |
+-------^-------+
Kernel |
+---------------------------------------------------+
Hardware |
| Trapped packet
|
+--+---+
| |
| ASIC |
| |
+------+
.. _Trap-Types:
Trap Types
==========
The ``devlink-trap`` mechanism supports the following packet trap types:
* ``drop``: Trapped packets were dropped by the underlying device. Packets
are only processed by ``devlink`` and not injected to the kernel's Rx path.
The trap action (see :ref:`Trap-Actions`) can be changed.
* ``exception``: Trapped packets were not forwarded as intended by the
underlying device due to an exception (e.g., TTL error, missing neighbour
entry) and trapped to the control plane for resolution. Packets are
processed by ``devlink`` and injected to the kernel's Rx path. Changing the
action of such traps is not allowed, as it can easily break the control
plane.
.. _Trap-Actions:
Trap Actions
============
The ``devlink-trap`` mechanism supports the following packet trap actions:
* ``trap``: The sole copy of the packet is sent to the CPU.
* ``drop``: The packet is dropped by the underlying device and a copy is not
sent to the CPU.
Generic Packet Traps
====================
Generic packet traps are used to describe traps that trap well-defined packets
or packets that are trapped due to well-defined conditions (e.g., TTL error).
Such traps can be shared by multiple device drivers and their description must
be added to the following table:
.. list-table:: List of Generic Packet Traps
:widths: 5 5 90
* - Name
- Type
- Description
* - ``source_mac_is_multicast``
- ``drop``
- Traps incoming packets that the device decided to drop because of a
multicast source MAC
* - ``vlan_tag_mismatch``
- ``drop``
- Traps incoming packets that the device decided to drop in case of VLAN
tag mismatch: The ingress bridge port is not configured with a PVID and
the packet is untagged or prio-tagged
* - ``ingress_vlan_filter``
- ``drop``
- Traps incoming packets that the device decided to drop in case they are
tagged with a VLAN that is not configured on the ingress bridge port
* - ``ingress_spanning_tree_filter``
- ``drop``
- Traps incoming packets that the device decided to drop in case the STP
state of the ingress bridge port is not "forwarding"
* - ``port_list_is_empty``
- ``drop``
- Traps packets that the device decided to drop in case they need to be
flooded and the flood list is empty
* - ``port_loopback_filter``
- ``drop``
- Traps packets that the device decided to drop in case after layer 2
forwarding the only port from which they should be transmitted through
is the port from which they were received
* - ``blackhole_route``
- ``drop``
- Traps packets that the device decided to drop in case they hit a
blackhole route
* - ``ttl_value_is_too_small``
- ``exception``
- Traps unicast packets that should be forwarded by the device whose TTL
was decremented to 0 or less
* - ``tail_drop``
- ``drop``
- Traps packets that the device decided to drop because they could not be
enqueued to a transmission queue which is full
Driver-specific Packet Traps
============================
Device drivers can register driver-specific packet traps, but these must be
clearly documented. Such traps can correspond to device-specific exceptions and
help debug packet drops caused by these exceptions. The following list includes
links to the description of driver-specific traps registered by various device
drivers:
* :doc:`/devlink-trap-netdevsim`
Generic Packet Trap Groups
==========================
Generic packet trap groups are used to aggregate logically related packet
traps. These groups allow the user to batch operations such as setting the trap
action of all member traps. In addition, ``devlink-trap`` can report aggregated
per-group packets and bytes statistics, in case per-trap statistics are too
narrow. The description of these groups must be added to the following table:
.. list-table:: List of Generic Packet Trap Groups
:widths: 10 90
* - Name
- Description
* - ``l2_drops``
- Contains packet traps for packets that were dropped by the device during
layer 2 forwarding (i.e., bridge)
* - ``l3_drops``
- Contains packet traps for packets that were dropped by the device or hit
an exception (e.g., TTL error) during layer 3 forwarding
* - ``buffer_drops``
- Contains packet traps for packets that were dropped by the device due to
an enqueue decision
Testing
=======
See ``tools/testing/selftests/drivers/net/netdevsim/devlink_trap.sh`` for a
test covering the core infrastructure. Test cases should be added for any new
functionality.
Device drivers should focus their tests on device-specific functionality, such
as the triggering of supported packet traps.
......@@ -14,6 +14,8 @@ Contents:
device_drivers/index
dsa/index
devlink-info-versions
devlink-trap
devlink-trap-netdevsim
ieee802154
kapi
z8530book
......
......@@ -11156,6 +11156,7 @@ S: Maintained
W: https://fedorahosted.org/dropwatch/
F: net/core/drop_monitor.c
F: include/uapi/linux/net_dropmon.h
F: include/net/drop_monitor.h
NETWORKING DRIVERS
M: "David S. Miller" <davem@davemloft.net>
......
......@@ -17,11 +17,20 @@
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/inet.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/rtnetlink.h>
#include <linux/workqueue.h>
#include <net/devlink.h>
#include <net/ip.h>
#include <uapi/linux/devlink.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/udp.h>
#include "netdevsim.h"
......@@ -302,6 +311,218 @@ static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev)
devlink_region_destroy(nsim_dev->dummy_region);
}
struct nsim_trap_item {
void *trap_ctx;
enum devlink_trap_action action;
};
struct nsim_trap_data {
struct delayed_work trap_report_dw;
struct nsim_trap_item *trap_items_arr;
struct nsim_dev *nsim_dev;
spinlock_t trap_lock; /* Protects trap_items_arr */
};
/* All driver-specific traps must be documented in
* Documentation/networking/devlink-trap-netdevsim.rst
*/
enum {
NSIM_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX,
NSIM_TRAP_ID_FID_MISS,
};
#define NSIM_TRAP_NAME_FID_MISS "fid_miss"
#define NSIM_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT
#define NSIM_TRAP_DROP(_id, _group_id) \
DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \
DEVLINK_TRAP_GROUP_GENERIC(_group_id), \
NSIM_TRAP_METADATA)
#define NSIM_TRAP_EXCEPTION(_id, _group_id) \
DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id, \
DEVLINK_TRAP_GROUP_GENERIC(_group_id), \
NSIM_TRAP_METADATA)
#define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id) \
DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id, \
NSIM_TRAP_NAME_##_id, \
DEVLINK_TRAP_GROUP_GENERIC(_group_id), \
NSIM_TRAP_METADATA)
static const struct devlink_trap nsim_traps_arr[] = {
NSIM_TRAP_DROP(SMAC_MC, L2_DROPS),
NSIM_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS),
NSIM_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS),
NSIM_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS),
NSIM_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS),
NSIM_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS),
NSIM_TRAP_DRIVER_EXCEPTION(FID_MISS, L2_DROPS),
NSIM_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS),
NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_DROPS),
NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS),
};
#define NSIM_TRAP_L4_DATA_LEN 100
static struct sk_buff *nsim_dev_trap_skb_build(void)
{
int tot_len, data_len = NSIM_TRAP_L4_DATA_LEN;
struct sk_buff *skb;
struct udphdr *udph;
struct ethhdr *eth;
struct iphdr *iph;
skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
if (!skb)
return NULL;
tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + data_len;
eth = skb_put(skb, sizeof(struct ethhdr));
eth_random_addr(eth->h_dest);
eth_random_addr(eth->h_source);
eth->h_proto = htons(ETH_P_IP);
skb->protocol = htons(ETH_P_IP);
iph = skb_put(skb, sizeof(struct iphdr));
iph->protocol = IPPROTO_UDP;
iph->saddr = in_aton("192.0.2.1");
iph->daddr = in_aton("198.51.100.1");
iph->version = 0x4;
iph->frag_off = 0;
iph->ihl = 0x5;
iph->tot_len = htons(tot_len);
iph->ttl = 100;
ip_send_check(iph);
udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len);
get_random_bytes(&udph->source, sizeof(u16));
get_random_bytes(&udph->dest, sizeof(u16));
udph->len = htons(sizeof(struct udphdr) + data_len);
return skb;
}
static void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port)
{
struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev;
struct devlink *devlink = priv_to_devlink(nsim_dev);
struct nsim_trap_data *nsim_trap_data;
int i;
nsim_trap_data = nsim_dev->trap_data;
spin_lock(&nsim_trap_data->trap_lock);
for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) {
struct nsim_trap_item *nsim_trap_item;
struct sk_buff *skb;
nsim_trap_item = &nsim_trap_data->trap_items_arr[i];
if (nsim_trap_item->action == DEVLINK_TRAP_ACTION_DROP)
continue;
skb = nsim_dev_trap_skb_build();
if (!skb)
continue;
skb->dev = nsim_dev_port->ns->netdev;
/* Trapped packets are usually passed to devlink in softIRQ,
* but in this case they are generated in a workqueue. Disable
* softIRQs to prevent lockdep from complaining about
* "incosistent lock state".
*/
local_bh_disable();
devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx,
&nsim_dev_port->devlink_port);
local_bh_enable();
consume_skb(skb);
}
spin_unlock(&nsim_trap_data->trap_lock);
}
#define NSIM_TRAP_REPORT_INTERVAL_MS 100
static void nsim_dev_trap_report_work(struct work_struct *work)
{
struct nsim_trap_data *nsim_trap_data;
struct nsim_dev_port *nsim_dev_port;
struct nsim_dev *nsim_dev;
nsim_trap_data = container_of(work, struct nsim_trap_data,
trap_report_dw.work);
nsim_dev = nsim_trap_data->nsim_dev;
/* For each running port and enabled packet trap, generate a UDP
* packet with a random 5-tuple and report it.
*/
mutex_lock(&nsim_dev->port_list_lock);
list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) {
if (!netif_running(nsim_dev_port->ns->netdev))
continue;
nsim_dev_trap_report(nsim_dev_port);
}
mutex_unlock(&nsim_dev->port_list_lock);
schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw,
msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS));
}
static int nsim_dev_traps_init(struct devlink *devlink)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
struct nsim_trap_data *nsim_trap_data;
int err;
nsim_trap_data = kzalloc(sizeof(*nsim_trap_data), GFP_KERNEL);
if (!nsim_trap_data)
return -ENOMEM;
nsim_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(nsim_traps_arr),
sizeof(struct nsim_trap_item),
GFP_KERNEL);
if (!nsim_trap_data->trap_items_arr) {
err = -ENOMEM;
goto err_trap_data_free;
}
/* The lock is used to protect the action state of the registered
* traps. The value is written by user and read in delayed work when
* iterating over all the traps.
*/
spin_lock_init(&nsim_trap_data->trap_lock);
nsim_trap_data->nsim_dev = nsim_dev;
nsim_dev->trap_data = nsim_trap_data;
err = devlink_traps_register(devlink, nsim_traps_arr,
ARRAY_SIZE(nsim_traps_arr), NULL);
if (err)
goto err_trap_items_free;
INIT_DELAYED_WORK(&nsim_dev->trap_data->trap_report_dw,
nsim_dev_trap_report_work);
schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw,
msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS));
return 0;
err_trap_items_free:
kfree(nsim_trap_data->trap_items_arr);
err_trap_data_free:
kfree(nsim_trap_data);
return err;
}
static void nsim_dev_traps_exit(struct devlink *devlink)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw);
devlink_traps_unregister(devlink, nsim_traps_arr,
ARRAY_SIZE(nsim_traps_arr));
kfree(nsim_dev->trap_data->trap_items_arr);
kfree(nsim_dev->trap_data);
}
static int nsim_dev_reload(struct devlink *devlink,
struct netlink_ext_ack *extack)
{
......@@ -369,9 +590,61 @@ static int nsim_dev_flash_update(struct devlink *devlink, const char *file_name,
return 0;
}
static struct nsim_trap_item *
nsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id)
{
struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data;
int i;
for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) {
if (nsim_traps_arr[i].id == trap_id)
return &nsim_trap_data->trap_items_arr[i];
}
return NULL;
}
static int nsim_dev_devlink_trap_init(struct devlink *devlink,
const struct devlink_trap *trap,
void *trap_ctx)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
struct nsim_trap_item *nsim_trap_item;
nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
if (WARN_ON(!nsim_trap_item))
return -ENOENT;
nsim_trap_item->trap_ctx = trap_ctx;
nsim_trap_item->action = trap->init_action;
return 0;
}
static int
nsim_dev_devlink_trap_action_set(struct devlink *devlink,
const struct devlink_trap *trap,
enum devlink_trap_action action)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
struct nsim_trap_item *nsim_trap_item;
nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
if (WARN_ON(!nsim_trap_item))
return -ENOENT;
spin_lock(&nsim_dev->trap_data->trap_lock);
nsim_trap_item->action = action;
spin_unlock(&nsim_dev->trap_data->trap_lock);
return 0;
}
static const struct devlink_ops nsim_dev_devlink_ops = {
.reload = nsim_dev_reload,
.flash_update = nsim_dev_flash_update,
.trap_init = nsim_dev_devlink_trap_init,
.trap_action_set = nsim_dev_devlink_trap_action_set,
};
#define NSIM_DEV_MAX_MACS_DEFAULT 32
......@@ -421,10 +694,14 @@ nsim_dev_create(struct nsim_bus_dev *nsim_bus_dev, unsigned int port_count)
if (err)
goto err_params_unregister;
err = nsim_dev_debugfs_init(nsim_dev);
err = nsim_dev_traps_init(devlink);
if (err)
goto err_dummy_region_exit;
err = nsim_dev_debugfs_init(nsim_dev);
if (err)
goto err_traps_exit;
err = nsim_bpf_dev_init(nsim_dev);
if (err)
goto err_debugfs_exit;
......@@ -434,6 +711,8 @@ nsim_dev_create(struct nsim_bus_dev *nsim_bus_dev, unsigned int port_count)
err_debugfs_exit:
nsim_dev_debugfs_exit(nsim_dev);
err_traps_exit:
nsim_dev_traps_exit(devlink);
err_dummy_region_exit:
nsim_dev_dummy_region_exit(nsim_dev);
err_params_unregister:
......@@ -456,6 +735,7 @@ static void nsim_dev_destroy(struct nsim_dev *nsim_dev)
nsim_bpf_dev_exit(nsim_dev);
nsim_dev_debugfs_exit(nsim_dev);
nsim_dev_traps_exit(devlink);
nsim_dev_dummy_region_exit(nsim_dev);
devlink_params_unregister(devlink, nsim_devlink_params,
ARRAY_SIZE(nsim_devlink_params));
......
......@@ -145,6 +145,7 @@ struct nsim_dev_port {
struct nsim_dev {
struct nsim_bus_dev *nsim_bus_dev;
struct nsim_fib_data *fib_data;
struct nsim_trap_data *trap_data;
struct dentry *ddir;
struct dentry *ports_ddir;
struct bpf_offload_dev *bpf_dev;
......
......@@ -14,6 +14,7 @@
#include <linux/netdevice.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/refcount.h>
#include <net/net_namespace.h>
#include <uapi/linux/devlink.h>
......@@ -31,6 +32,8 @@ struct devlink {
struct list_head reporter_list;
struct mutex reporters_lock; /* protects reporter_list */
struct devlink_dpipe_headers *dpipe_headers;
struct list_head trap_list;
struct list_head trap_group_list;
const struct devlink_ops *ops;
struct device *dev;
possible_net_t _net;
......@@ -497,6 +500,135 @@ struct devlink_health_reporter_ops {
struct devlink_fmsg *fmsg);
};
/**
* struct devlink_trap_group - Immutable packet trap group attributes.
* @name: Trap group name.
* @id: Trap group identifier.
* @generic: Whether the trap group is generic or not.
*
* Describes immutable attributes of packet trap groups that drivers register
* with devlink.
*/
struct devlink_trap_group {
const char *name;
u16 id;
bool generic;
};
#define DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT BIT(0)
/**
* struct devlink_trap - Immutable packet trap attributes.
* @type: Trap type.
* @init_action: Initial trap action.
* @generic: Whether the trap is generic or not.
* @id: Trap identifier.
* @name: Trap name.
* @group: Immutable packet trap group attributes.
* @metadata_cap: Metadata types that can be provided by the trap.
*
* Describes immutable attributes of packet traps that drivers register with
* devlink.
*/
struct devlink_trap {
enum devlink_trap_type type;
enum devlink_trap_action init_action;
bool generic;
u16 id;
const char *name;
struct devlink_trap_group group;
u32 metadata_cap;
};
/* All traps must be documented in
* Documentation/networking/devlink-trap.rst
*/
enum devlink_trap_generic_id {
DEVLINK_TRAP_GENERIC_ID_SMAC_MC,
DEVLINK_TRAP_GENERIC_ID_VLAN_TAG_MISMATCH,
DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER,
DEVLINK_TRAP_GENERIC_ID_INGRESS_STP_FILTER,
DEVLINK_TRAP_GENERIC_ID_EMPTY_TX_LIST,
DEVLINK_TRAP_GENERIC_ID_PORT_LOOPBACK_FILTER,
DEVLINK_TRAP_GENERIC_ID_BLACKHOLE_ROUTE,
DEVLINK_TRAP_GENERIC_ID_TTL_ERROR,
DEVLINK_TRAP_GENERIC_ID_TAIL_DROP,
/* Add new generic trap IDs above */
__DEVLINK_TRAP_GENERIC_ID_MAX,
DEVLINK_TRAP_GENERIC_ID_MAX = __DEVLINK_TRAP_GENERIC_ID_MAX - 1,
};
/* All trap groups must be documented in
* Documentation/networking/devlink-trap.rst
*/
enum devlink_trap_group_generic_id {
DEVLINK_TRAP_GROUP_GENERIC_ID_L2_DROPS,
DEVLINK_TRAP_GROUP_GENERIC_ID_L3_DROPS,
DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS,
/* Add new generic trap group IDs above */
__DEVLINK_TRAP_GROUP_GENERIC_ID_MAX,
DEVLINK_TRAP_GROUP_GENERIC_ID_MAX =
__DEVLINK_TRAP_GROUP_GENERIC_ID_MAX - 1,
};
#define DEVLINK_TRAP_GENERIC_NAME_SMAC_MC \
"source_mac_is_multicast"
#define DEVLINK_TRAP_GENERIC_NAME_VLAN_TAG_MISMATCH \
"vlan_tag_mismatch"
#define DEVLINK_TRAP_GENERIC_NAME_INGRESS_VLAN_FILTER \
"ingress_vlan_filter"
#define DEVLINK_TRAP_GENERIC_NAME_INGRESS_STP_FILTER \
"ingress_spanning_tree_filter"
#define DEVLINK_TRAP_GENERIC_NAME_EMPTY_TX_LIST \
"port_list_is_empty"
#define DEVLINK_TRAP_GENERIC_NAME_PORT_LOOPBACK_FILTER \
"port_loopback_filter"
#define DEVLINK_TRAP_GENERIC_NAME_BLACKHOLE_ROUTE \
"blackhole_route"
#define DEVLINK_TRAP_GENERIC_NAME_TTL_ERROR \
"ttl_value_is_too_small"
#define DEVLINK_TRAP_GENERIC_NAME_TAIL_DROP \
"tail_drop"
#define DEVLINK_TRAP_GROUP_GENERIC_NAME_L2_DROPS \
"l2_drops"
#define DEVLINK_TRAP_GROUP_GENERIC_NAME_L3_DROPS \
"l3_drops"
#define DEVLINK_TRAP_GROUP_GENERIC_NAME_BUFFER_DROPS \
"buffer_drops"
#define DEVLINK_TRAP_GENERIC(_type, _init_action, _id, _group, _metadata_cap) \
{ \
.type = DEVLINK_TRAP_TYPE_##_type, \
.init_action = DEVLINK_TRAP_ACTION_##_init_action, \
.generic = true, \
.id = DEVLINK_TRAP_GENERIC_ID_##_id, \
.name = DEVLINK_TRAP_GENERIC_NAME_##_id, \
.group = _group, \
.metadata_cap = _metadata_cap, \
}
#define DEVLINK_TRAP_DRIVER(_type, _init_action, _id, _name, _group, \
_metadata_cap) \
{ \
.type = DEVLINK_TRAP_TYPE_##_type, \
.init_action = DEVLINK_TRAP_ACTION_##_init_action, \
.generic = false, \
.id = _id, \
.name = _name, \
.group = _group, \
.metadata_cap = _metadata_cap, \
}
#define DEVLINK_TRAP_GROUP_GENERIC(_id) \
{ \
.name = DEVLINK_TRAP_GROUP_GENERIC_NAME_##_id, \
.id = DEVLINK_TRAP_GROUP_GENERIC_ID_##_id, \
.generic = true, \
}
struct devlink_ops {
int (*reload)(struct devlink *devlink, struct netlink_ext_ack *extack);
int (*port_type_set)(struct devlink_port *devlink_port,
......@@ -558,6 +690,38 @@ struct devlink_ops {
int (*flash_update)(struct devlink *devlink, const char *file_name,
const char *component,
struct netlink_ext_ack *extack);
/**
* @trap_init: Trap initialization function.
*
* Should be used by device drivers to initialize the trap in the
* underlying device. Drivers should also store the provided trap
* context, so that they could efficiently pass it to
* devlink_trap_report() when the trap is triggered.
*/
int (*trap_init)(struct devlink *devlink,
const struct devlink_trap *trap, void *trap_ctx);
/**
* @trap_fini: Trap de-initialization function.
*
* Should be used by device drivers to de-initialize the trap in the
* underlying device.
*/
void (*trap_fini)(struct devlink *devlink,
const struct devlink_trap *trap, void *trap_ctx);
/**
* @trap_action_set: Trap action set function.
*/
int (*trap_action_set)(struct devlink *devlink,
const struct devlink_trap *trap,
enum devlink_trap_action action);
/**
* @trap_group_init: Trap group initialization function.
*
* Should be used by device drivers to initialize the trap group in the
* underlying device.
*/
int (*trap_group_init)(struct devlink *devlink,
const struct devlink_trap_group *group);
};
static inline void *devlink_priv(struct devlink *devlink)
......@@ -774,6 +938,17 @@ void devlink_flash_update_status_notify(struct devlink *devlink,
unsigned long done,
unsigned long total);
int devlink_traps_register(struct devlink *devlink,
const struct devlink_trap *traps,
size_t traps_count, void *priv);
void devlink_traps_unregister(struct devlink *devlink,
const struct devlink_trap *traps,
size_t traps_count);
void devlink_trap_report(struct devlink *devlink,
struct sk_buff *skb, void *trap_ctx,
struct devlink_port *in_devlink_port);
void *devlink_trap_ctx_priv(void *trap_ctx);
#if IS_ENABLED(CONFIG_NET_DEVLINK)
void devlink_compat_running_version(struct net_device *dev,
......
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _NET_DROP_MONITOR_H_
#define _NET_DROP_MONITOR_H_
#include <linux/ktime.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
/**
* struct net_dm_hw_metadata - Hardware-supplied packet metadata.
* @trap_group_name: Hardware trap group name.
* @trap_name: Hardware trap name.
* @input_dev: Input netdevice.
*/
struct net_dm_hw_metadata {
const char *trap_group_name;
const char *trap_name;
struct net_device *input_dev;
};
#if IS_ENABLED(CONFIG_NET_DROP_MONITOR)
void net_dm_hw_report(struct sk_buff *skb,
const struct net_dm_hw_metadata *hw_metadata);
#else
static inline void
net_dm_hw_report(struct sk_buff *skb,
const struct net_dm_hw_metadata *hw_metadata)
{
}
#endif
#endif /* _NET_DROP_MONITOR_H_ */
......@@ -107,6 +107,16 @@ enum devlink_command {
DEVLINK_CMD_FLASH_UPDATE_END, /* notification only */
DEVLINK_CMD_FLASH_UPDATE_STATUS, /* notification only */
DEVLINK_CMD_TRAP_GET, /* can dump */
DEVLINK_CMD_TRAP_SET,
DEVLINK_CMD_TRAP_NEW,
DEVLINK_CMD_TRAP_DEL,
DEVLINK_CMD_TRAP_GROUP_GET, /* can dump */
DEVLINK_CMD_TRAP_GROUP_SET,
DEVLINK_CMD_TRAP_GROUP_NEW,
DEVLINK_CMD_TRAP_GROUP_DEL,
/* add new commands above here */
__DEVLINK_CMD_MAX,
DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
......@@ -194,6 +204,47 @@ enum devlink_param_fw_load_policy_value {
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
};
enum {
DEVLINK_ATTR_STATS_RX_PACKETS, /* u64 */
DEVLINK_ATTR_STATS_RX_BYTES, /* u64 */
__DEVLINK_ATTR_STATS_MAX,
DEVLINK_ATTR_STATS_MAX = __DEVLINK_ATTR_STATS_MAX - 1
};
/**
* enum devlink_trap_action - Packet trap action.
* @DEVLINK_TRAP_ACTION_DROP: Packet is dropped by the device and a copy is not
* sent to the CPU.
* @DEVLINK_TRAP_ACTION_TRAP: The sole copy of the packet is sent to the CPU.
*/
enum devlink_trap_action {
DEVLINK_TRAP_ACTION_DROP,
DEVLINK_TRAP_ACTION_TRAP,
};
/**
* enum devlink_trap_type - Packet trap type.
* @DEVLINK_TRAP_TYPE_DROP: Trap reason is a drop. Trapped packets are only
* processed by devlink and not injected to the
* kernel's Rx path.
* @DEVLINK_TRAP_TYPE_EXCEPTION: Trap reason is an exception. Packet was not
* forwarded as intended due to an exception
* (e.g., missing neighbour entry) and trapped to
* control plane for resolution. Trapped packets
* are processed by devlink and injected to
* the kernel's Rx path.
*/
enum devlink_trap_type {
DEVLINK_TRAP_TYPE_DROP,
DEVLINK_TRAP_TYPE_EXCEPTION,
};
enum {
/* Trap can report input port as metadata */
DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT,
};
enum devlink_attr {
/* don't change the order or add anything between, this is ABI! */
DEVLINK_ATTR_UNSPEC,
......@@ -348,6 +399,17 @@ enum devlink_attr {
DEVLINK_ATTR_PORT_PCI_PF_NUMBER, /* u16 */
DEVLINK_ATTR_PORT_PCI_VF_NUMBER, /* u16 */
DEVLINK_ATTR_STATS, /* nested */
DEVLINK_ATTR_TRAP_NAME, /* string */
/* enum devlink_trap_action */
DEVLINK_ATTR_TRAP_ACTION, /* u8 */
/* enum devlink_trap_type */
DEVLINK_ATTR_TRAP_TYPE, /* u8 */
DEVLINK_ATTR_TRAP_GENERIC, /* flag */
DEVLINK_ATTR_TRAP_METADATA, /* nested */
DEVLINK_ATTR_TRAP_GROUP_NAME, /* string */
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,
......
......@@ -83,6 +83,15 @@ enum net_dm_attr {
NET_DM_ATTR_ORIG_LEN, /* u32 */
NET_DM_ATTR_QUEUE_LEN, /* u32 */
NET_DM_ATTR_STATS, /* nested */
NET_DM_ATTR_HW_STATS, /* nested */
NET_DM_ATTR_ORIGIN, /* u16 */
NET_DM_ATTR_HW_TRAP_GROUP_NAME, /* string */
NET_DM_ATTR_HW_TRAP_NAME, /* string */
NET_DM_ATTR_HW_ENTRIES, /* nested */
NET_DM_ATTR_HW_ENTRY, /* nested */
NET_DM_ATTR_HW_TRAP_COUNT, /* u32 */
NET_DM_ATTR_SW_DROPS, /* flag */
NET_DM_ATTR_HW_DROPS, /* flag */
__NET_DM_ATTR_MAX,
NET_DM_ATTR_MAX = __NET_DM_ATTR_MAX - 1
......@@ -101,6 +110,7 @@ enum net_dm_alert_mode {
enum {
NET_DM_ATTR_PORT_NETDEV_IFINDEX, /* u32 */
NET_DM_ATTR_PORT_NETDEV_NAME, /* string */
__NET_DM_ATTR_PORT_MAX,
NET_DM_ATTR_PORT_MAX = __NET_DM_ATTR_PORT_MAX - 1
......@@ -113,4 +123,9 @@ enum {
NET_DM_ATTR_STATS_MAX = __NET_DM_ATTR_STATS_MAX - 1
};
enum net_dm_origin {
NET_DM_ORIGIN_SW,
NET_DM_ORIGIN_HW,
};
#endif
......@@ -430,6 +430,7 @@ config NET_SOCK_MSG
config NET_DEVLINK
bool
default n
imply NET_DROP_MONITOR
config PAGE_POOL
bool
......
This diff is collapsed.
This diff is collapsed.
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# This test is for checking devlink-trap functionality. It makes use of
# netdevsim which implements the required callbacks.
lib_dir=$(dirname $0)/../../../net/forwarding
ALL_TESTS="
init_test
trap_action_test
trap_metadata_test
bad_trap_test
bad_trap_action_test
trap_stats_test
trap_group_action_test
bad_trap_group_test
trap_group_stats_test
port_del_test
dev_del_test
"
NETDEVSIM_PATH=/sys/bus/netdevsim/
DEV_ADDR=1337
DEV=netdevsim${DEV_ADDR}
DEVLINK_DEV=netdevsim/${DEV}
SLEEP_TIME=1
NETDEV=""
NUM_NETIFS=0
source $lib_dir/lib.sh
source $lib_dir/devlink_lib.sh
require_command udevadm
modprobe netdevsim &> /dev/null
if [ ! -d "$NETDEVSIM_PATH" ]; then
echo "SKIP: No netdevsim support"
exit 1
fi
if [ -d "${NETDEVSIM_PATH}/devices/netdevsim${DEV_ADDR}" ]; then
echo "SKIP: Device netdevsim${DEV_ADDR} already exists"
exit 1
fi
init_test()
{
RET=0
test $(devlink_traps_num_get) -ne 0
check_err $? "No traps were registered"
log_test "Initialization"
}
trap_action_test()
{
local orig_action
local trap_name
local action
RET=0
for trap_name in $(devlink_traps_get); do
# The action of non-drop traps cannot be changed.
if [ $(devlink_trap_type_get $trap_name) = "drop" ]; then
devlink_trap_action_set $trap_name "trap"
action=$(devlink_trap_action_get $trap_name)
if [ $action != "trap" ]; then
check_err 1 "Trap $trap_name did not change action to trap"
fi
devlink_trap_action_set $trap_name "drop"
action=$(devlink_trap_action_get $trap_name)
if [ $action != "drop" ]; then
check_err 1 "Trap $trap_name did not change action to drop"
fi
else
orig_action=$(devlink_trap_action_get $trap_name)
devlink_trap_action_set $trap_name "trap"
action=$(devlink_trap_action_get $trap_name)
if [ $action != $orig_action ]; then
check_err 1 "Trap $trap_name changed action when should not"
fi
devlink_trap_action_set $trap_name "drop"
action=$(devlink_trap_action_get $trap_name)
if [ $action != $orig_action ]; then
check_err 1 "Trap $trap_name changed action when should not"
fi
fi
done
log_test "Trap action"
}
trap_metadata_test()
{
local trap_name
RET=0
for trap_name in $(devlink_traps_get); do
devlink_trap_metadata_test $trap_name "input_port"
check_err $? "Input port not reported as metadata of trap $trap_name"
done
log_test "Trap metadata"
}
bad_trap_test()
{
RET=0
devlink_trap_action_set "made_up_trap" "drop"
check_fail $? "Did not get an error for non-existing trap"
log_test "Non-existing trap"
}
bad_trap_action_test()
{
local traps_arr
local trap_name
RET=0
# Pick first trap.
traps_arr=($(devlink_traps_get))
trap_name=${traps_arr[0]}
devlink_trap_action_set $trap_name "made_up_action"
check_fail $? "Did not get an error for non-existing trap action"
log_test "Non-existing trap action"
}
trap_stats_test()
{
local trap_name
RET=0
for trap_name in $(devlink_traps_get); do
devlink_trap_stats_idle_test $trap_name
check_err $? "Stats of trap $trap_name not idle when netdev down"
ip link set dev $NETDEV up
if [ $(devlink_trap_type_get $trap_name) = "drop" ]; then
devlink_trap_action_set $trap_name "trap"
devlink_trap_stats_idle_test $trap_name
check_fail $? "Stats of trap $trap_name idle when action is trap"
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"
else
devlink_trap_stats_idle_test $trap_name
check_fail $? "Stats of non-drop trap $trap_name idle when should not"
fi
ip link set dev $NETDEV down
done
log_test "Trap statistics"
}
trap_group_action_test()
{
local curr_group group_name
local trap_name
local trap_type
local action
RET=0
for group_name in $(devlink_trap_groups_get); do
devlink_trap_group_action_set $group_name "trap"
for trap_name in $(devlink_traps_get); do
curr_group=$(devlink_trap_group_get $trap_name)
if [ $curr_group != $group_name ]; then
continue
fi
trap_type=$(devlink_trap_type_get $trap_name)
if [ $trap_type != "drop" ]; then
continue
fi
action=$(devlink_trap_action_get $trap_name)
if [ $action != "trap" ]; then
check_err 1 "Trap $trap_name did not change action to trap"
fi
done
devlink_trap_group_action_set $group_name "drop"
for trap_name in $(devlink_traps_get); do
curr_group=$(devlink_trap_group_get $trap_name)
if [ $curr_group != $group_name ]; then
continue
fi
trap_type=$(devlink_trap_type_get $trap_name)
if [ $trap_type != "drop" ]; then
continue
fi
action=$(devlink_trap_action_get $trap_name)
if [ $action != "drop" ]; then
check_err 1 "Trap $trap_name did not change action to drop"
fi
done
done
log_test "Trap group action"
}
bad_trap_group_test()
{
RET=0
devlink_trap_group_action_set "made_up_trap_group" "drop"
check_fail $? "Did not get an error for non-existing trap group"
log_test "Non-existing trap group"
}
trap_group_stats_test()
{
local group_name
RET=0
for group_name in $(devlink_trap_groups_get); do
devlink_trap_group_stats_idle_test $group_name
check_err $? "Stats of trap group $group_name not idle when netdev down"
ip link set dev $NETDEV up
devlink_trap_group_action_set $group_name "trap"
devlink_trap_group_stats_idle_test $group_name
check_fail $? "Stats of trap group $group_name idle when action is trap"
devlink_trap_group_action_set $group_name "drop"
ip link set dev $NETDEV down
done
log_test "Trap group statistics"
}
port_del_test()
{
local group_name
local i
# The test never fails. It is meant to exercise different code paths
# and make sure we properly dismantle a port while packets are
# in-flight.
RET=0
devlink_traps_enable_all
for i in $(seq 1 10); do
ip link set dev $NETDEV up
sleep $SLEEP_TIME
netdevsim_port_destroy
netdevsim_port_create
udevadm settle
done
devlink_traps_disable_all
log_test "Port delete"
}
dev_del_test()
{
local group_name
local i
# The test never fails. It is meant to exercise different code paths
# and make sure we properly unregister traps while packets are
# in-flight.
RET=0
devlink_traps_enable_all
for i in $(seq 1 10); do
ip link set dev $NETDEV up
sleep $SLEEP_TIME
cleanup
setup_prepare
done
devlink_traps_disable_all
log_test "Device delete"
}
netdevsim_dev_create()
{
echo "$DEV_ADDR 0" > ${NETDEVSIM_PATH}/new_device
}
netdevsim_dev_destroy()
{
echo "$DEV_ADDR" > ${NETDEVSIM_PATH}/del_device
}
netdevsim_port_create()
{
echo 1 > ${NETDEVSIM_PATH}/devices/${DEV}/new_port
}
netdevsim_port_destroy()
{
echo 1 > ${NETDEVSIM_PATH}/devices/${DEV}/del_port
}
setup_prepare()
{
local netdev
netdevsim_dev_create
if [ ! -d "${NETDEVSIM_PATH}/devices/${DEV}" ]; then
echo "Failed to create netdevsim device"
exit 1
fi
netdevsim_port_create
if [ ! -d "${NETDEVSIM_PATH}/devices/${DEV}/net/" ]; then
echo "Failed to create netdevsim port"
exit 1
fi
# Wait for udev to rename newly created netdev.
udevadm settle
NETDEV=$(ls ${NETDEVSIM_PATH}/devices/${DEV}/net/)
}
cleanup()
{
pre_cleanup
netdevsim_port_destroy
netdevsim_dev_destroy
}
trap cleanup EXIT
setup_prepare
tests_run
exit $EXIT_STATUS
......@@ -4,19 +4,21 @@
##############################################################################
# Defines
DEVLINK_DEV=$(devlink port show "${NETIFS[p1]}" -j \
| jq -r '.port | keys[]' | cut -d/ -f-2)
if [ -z "$DEVLINK_DEV" ]; then
echo "SKIP: ${NETIFS[p1]} has no devlink device registered for it"
exit 1
fi
if [[ "$(echo $DEVLINK_DEV | grep -c pci)" -eq 0 ]]; then
echo "SKIP: devlink device's bus is not PCI"
exit 1
fi
if [[ ! -v DEVLINK_DEV ]]; then
DEVLINK_DEV=$(devlink port show "${NETIFS[p1]}" -j \
| jq -r '.port | keys[]' | cut -d/ -f-2)
if [ -z "$DEVLINK_DEV" ]; then
echo "SKIP: ${NETIFS[p1]} has no devlink device registered for it"
exit 1
fi
if [[ "$(echo $DEVLINK_DEV | grep -c pci)" -eq 0 ]]; then
echo "SKIP: devlink device's bus is not PCI"
exit 1
fi
DEVLINK_VIDDID=$(lspci -s $(echo $DEVLINK_DEV | cut -d"/" -f2) \
-n | cut -d" " -f3)
DEVLINK_VIDDID=$(lspci -s $(echo $DEVLINK_DEV | cut -d"/" -f2) \
-n | cut -d" " -f3)
fi
##############################################################################
# Sanity checks
......@@ -27,6 +29,12 @@ if [ $? -ne 0 ]; then
exit 1
fi
devlink help 2>&1 | grep trap &> /dev/null
if [ $? -ne 0 ]; then
echo "SKIP: iproute2 too old, missing devlink trap support"
exit 1
fi
##############################################################################
# Devlink helpers
......@@ -190,3 +198,160 @@ devlink_tc_bind_pool_th_restore()
devlink sb tc bind set $port tc $tc type $dir \
pool ${orig[0]} th ${orig[1]}
}
devlink_traps_num_get()
{
devlink -j trap | jq '.[]["'$DEVLINK_DEV'"] | length'
}
devlink_traps_get()
{
devlink -j trap | jq -r '.[]["'$DEVLINK_DEV'"][].name'
}
devlink_trap_type_get()
{
local trap_name=$1; shift
devlink -j trap show $DEVLINK_DEV trap $trap_name \
| jq -r '.[][][].type'
}
devlink_trap_action_set()
{
local trap_name=$1; shift
local action=$1; shift
# Pipe output to /dev/null to avoid expected warnings.
devlink trap set $DEVLINK_DEV trap $trap_name \
action $action &> /dev/null
}
devlink_trap_action_get()
{
local trap_name=$1; shift
devlink -j trap show $DEVLINK_DEV trap $trap_name \
| jq -r '.[][][].action'
}
devlink_trap_group_get()
{
devlink -j trap show $DEVLINK_DEV trap $trap_name \
| jq -r '.[][][].group'
}
devlink_trap_metadata_test()
{
local trap_name=$1; shift
local metadata=$1; shift
devlink -jv trap show $DEVLINK_DEV trap $trap_name \
| jq -e '.[][][].metadata | contains(["'$metadata'"])' \
&> /dev/null
}
devlink_trap_rx_packets_get()
{
local trap_name=$1; shift
devlink -js trap show $DEVLINK_DEV trap $trap_name \
| jq '.[][][]["stats"]["rx"]["packets"]'
}
devlink_trap_rx_bytes_get()
{
local trap_name=$1; shift
devlink -js trap show $DEVLINK_DEV trap $trap_name \
| jq '.[][][]["stats"]["rx"]["bytes"]'
}
devlink_trap_stats_idle_test()
{
local trap_name=$1; shift
local t0_packets t0_bytes
local t1_packets t1_bytes
t0_packets=$(devlink_trap_rx_packets_get $trap_name)
t0_bytes=$(devlink_trap_rx_bytes_get $trap_name)
sleep 1
t1_packets=$(devlink_trap_rx_packets_get $trap_name)
t1_bytes=$(devlink_trap_rx_bytes_get $trap_name)
if [[ $t0_packets -eq $t1_packets && $t0_bytes -eq $t1_bytes ]]; then
return 0
else
return 1
fi
}
devlink_traps_enable_all()
{
local trap_name
for trap_name in $(devlink_traps_get); do
devlink_trap_action_set $trap_name "trap"
done
}
devlink_traps_disable_all()
{
for trap_name in $(devlink_traps_get); do
devlink_trap_action_set $trap_name "drop"
done
}
devlink_trap_groups_get()
{
devlink -j trap group | jq -r '.[]["'$DEVLINK_DEV'"][].name'
}
devlink_trap_group_action_set()
{
local group_name=$1; shift
local action=$1; shift
# Pipe output to /dev/null to avoid expected warnings.
devlink trap group set $DEVLINK_DEV group $group_name action $action \
&> /dev/null
}
devlink_trap_group_rx_packets_get()
{
local group_name=$1; shift
devlink -js trap group show $DEVLINK_DEV group $group_name \
| jq '.[][][]["stats"]["rx"]["packets"]'
}
devlink_trap_group_rx_bytes_get()
{
local group_name=$1; shift
devlink -js trap group show $DEVLINK_DEV group $group_name \
| jq '.[][][]["stats"]["rx"]["bytes"]'
}
devlink_trap_group_stats_idle_test()
{
local group_name=$1; shift
local t0_packets t0_bytes
local t1_packets t1_bytes
t0_packets=$(devlink_trap_group_rx_packets_get $group_name)
t0_bytes=$(devlink_trap_group_rx_bytes_get $group_name)
sleep 1
t1_packets=$(devlink_trap_group_rx_packets_get $group_name)
t1_bytes=$(devlink_trap_group_rx_bytes_get $group_name)
if [[ $t0_packets -eq $t1_packets && $t0_bytes -eq $t1_bytes ]]; then
return 0
else
return 1
fi
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment