Commit 6fe9a949 authored by David S. Miller's avatar David S. Miller

Merge branch 'Add-packet-trap-policers-support'

Ido Schimmel says:

====================
Add packet trap policers support

Background
==========

Devices capable of offloading the kernel's datapath and perform
functions such as bridging and routing must also be able to send (trap)
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
trap IGMP membership reports to the kernel for processing by the bridge
module.

Motivation
==========

In most cases, the underlying device is capable of handling packet rates
that are several orders of magnitude higher compared to those that can
be handled by the CPU.

Therefore, in order to prevent the underlying device from overwhelming
the CPU, devices usually include packet trap policers that are able to
police the trapped packets to rates that can be handled by the CPU.

Proposed solution
=================

This patch set allows capable device drivers to register their supported
packet trap policers with devlink. User space can then tune the
parameters of these policers (currently, rate and burst size) and read
from the device the number of packets that were dropped by the policer,
if supported.

These packet trap policers can then be bound to existing packet trap
groups, which are used to aggregate logically related packet traps. As a
result, trapped packets are policed to rates that can be handled the
host CPU.

Example usage
=============

Instantiate netdevsim:

Dump available packet trap policers:
netdevsim/netdevsim10:
  policer 1 rate 1000 burst 128
  policer 2 rate 2000 burst 256
  policer 3 rate 3000 burst 512

Change the parameters of a packet trap policer:

Bind a packet trap policer to a packet trap group:

Dump parameters and statistics of a packet trap policer:
netdevsim/netdevsim10:
  policer 3 rate 100 burst 16
    stats:
        rx:
          dropped 92

Unbind a packet trap policer from a packet trap group:

Patch set overview
==================

Patch #1 adds the core infrastructure in devlink which allows capable
device drivers to register their supported packet trap policers with
devlink.

Patch #2 extends the existing devlink-trap documentation.

Patch #3 extends netdevsim to register a few dummy packet trap policers
with devlink. Used later on to selftests the core infrastructure.

Patches #4-#5 adds infrastructure in devlink to allow binding of packet
trap policers to packet trap groups.

Patch #6 extends netdevsim to allow such binding.

Patch #7 adds a selftest over netdevsim that verifies the core
devlink-trap policers functionality.

Patches #8-#14 gradually add devlink-trap policers support in mlxsw.

Patch #15 adds a selftest over mlxsw. All registered packet trap
policers are verified to handle the configured rate and burst size.

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

* Allow changing default association between packet traps and packet
  trap groups
* Add more packet traps. For example, for control packets (e.g., IGMP)

v3:
* Rebase

v2 (address comments from Jiri and Jakub):
* Patch #1: Add 'strict_start_type' in devlink policy
* Patch #1: Have device drivers provide max/min rate/burst size for each
  policer. Use them to check validity of user provided parameters
* Patch #3: Remove check about burst size being a power of 2 and instead
  add a debugfs knob to fail the operation
* Patch #3: Provide max/min rate/burst size when registering policers
  and remove the validity checks from nsim_dev_devlink_trap_policer_set()
* Patch #5: Check for presence of 'DEVLINK_ATTR_TRAP_POLICER_ID' in
  devlink_trap_group_set() and bail if not present
* Patch #5: Add extack error message in case trap group was partially
  modified
* Patch #7: Add test case with new 'fail_trap_policer_set' knob
* Patch #7: Add test case for partially modified trap group
* Patch #10: Provide max/min rate/burst size when registering policers
* Patch #11: Remove the max/min validity checks from
  __mlxsw_sp_trap_policer_set()
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2d39eab4 9f3e63c5
......@@ -287,6 +287,32 @@ narrow. The description of these groups must be added to the following table:
- Contains packet traps for packets that were dropped by the device during
ACL processing
Packet Trap Policers
====================
As previously explained, the underlying device can trap certain packets to the
CPU for processing. In most cases, the underlying device is capable of handling
packet rates that are several orders of magnitude higher compared to those that
can be handled by the CPU.
Therefore, in order to prevent the underlying device from overwhelming the CPU,
devices usually include packet trap policers that are able to police the
trapped packets to rates that can be handled by the CPU.
The ``devlink-trap`` mechanism allows capable device drivers to register their
supported packet trap policers with ``devlink``. The device driver can choose
to associate these policers with supported packet trap groups (see
:ref:`Generic-Packet-Trap-Groups`) during its initialization, thereby exposing
its default control plane policy to user space.
Device drivers should allow user space to change the parameters of the policers
(e.g., rate, burst size) as well as the association between the policers and
trap groups by implementing the relevant callbacks.
If possible, device drivers should implement a callback that allows user space
to retrieve the number of packets that were dropped by the policer because its
configured policy was violated.
Testing
=======
......
......@@ -1198,6 +1198,72 @@ mlxsw_devlink_trap_group_init(struct devlink *devlink,
return mlxsw_driver->trap_group_init(mlxsw_core, group);
}
static int
mlxsw_devlink_trap_group_set(struct devlink *devlink,
const struct devlink_trap_group *group,
const struct devlink_trap_policer *policer)
{
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
if (!mlxsw_driver->trap_group_set)
return -EOPNOTSUPP;
return mlxsw_driver->trap_group_set(mlxsw_core, group, policer);
}
static int
mlxsw_devlink_trap_policer_init(struct devlink *devlink,
const struct devlink_trap_policer *policer)
{
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
if (!mlxsw_driver->trap_policer_init)
return -EOPNOTSUPP;
return mlxsw_driver->trap_policer_init(mlxsw_core, policer);
}
static void
mlxsw_devlink_trap_policer_fini(struct devlink *devlink,
const struct devlink_trap_policer *policer)
{
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
if (!mlxsw_driver->trap_policer_fini)
return;
mlxsw_driver->trap_policer_fini(mlxsw_core, policer);
}
static int
mlxsw_devlink_trap_policer_set(struct devlink *devlink,
const struct devlink_trap_policer *policer,
u64 rate, u64 burst,
struct netlink_ext_ack *extack)
{
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
if (!mlxsw_driver->trap_policer_set)
return -EOPNOTSUPP;
return mlxsw_driver->trap_policer_set(mlxsw_core, policer, rate, burst,
extack);
}
static int
mlxsw_devlink_trap_policer_counter_get(struct devlink *devlink,
const struct devlink_trap_policer *policer,
u64 *p_drops)
{
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
if (!mlxsw_driver->trap_policer_counter_get)
return -EOPNOTSUPP;
return mlxsw_driver->trap_policer_counter_get(mlxsw_core, policer,
p_drops);
}
static const struct devlink_ops mlxsw_devlink_ops = {
.reload_down = mlxsw_devlink_core_bus_device_reload_down,
.reload_up = mlxsw_devlink_core_bus_device_reload_up,
......@@ -1220,6 +1286,11 @@ static const struct devlink_ops mlxsw_devlink_ops = {
.trap_fini = mlxsw_devlink_trap_fini,
.trap_action_set = mlxsw_devlink_trap_action_set,
.trap_group_init = mlxsw_devlink_trap_group_init,
.trap_group_set = mlxsw_devlink_trap_group_set,
.trap_policer_init = mlxsw_devlink_trap_policer_init,
.trap_policer_fini = mlxsw_devlink_trap_policer_fini,
.trap_policer_set = mlxsw_devlink_trap_policer_set,
.trap_policer_counter_get = mlxsw_devlink_trap_policer_counter_get,
};
static int
......
......@@ -327,6 +327,20 @@ struct mlxsw_driver {
enum devlink_trap_action action);
int (*trap_group_init)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group);
int (*trap_group_set)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group,
const struct devlink_trap_policer *policer);
int (*trap_policer_init)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer);
void (*trap_policer_fini)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer);
int (*trap_policer_set)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer,
u64 rate, u64 burst,
struct netlink_ext_ack *extack);
int (*trap_policer_counter_get)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer,
u64 *p_drops);
void (*txhdr_construct)(struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info);
int (*resources_register)(struct mlxsw_core *mlxsw_core);
......
......@@ -3296,6 +3296,12 @@ MLXSW_ITEM32(reg, qpcr, g, 0x00, 14, 2);
*/
MLXSW_ITEM32(reg, qpcr, pid, 0x00, 0, 14);
/* reg_qpcr_clear_counter
* Clear counters.
* Access: OP
*/
MLXSW_ITEM32(reg, qpcr, clear_counter, 0x04, 31, 1);
/* reg_qpcr_color_aware
* Is the policer aware of colors.
* Must be 0 (unaware) for cpu port.
......@@ -3393,6 +3399,17 @@ enum mlxsw_reg_qpcr_action {
*/
MLXSW_ITEM32(reg, qpcr, violate_action, 0x18, 0, 4);
/* reg_qpcr_violate_count
* Counts the number of times violate_action happened on this PID.
* Access: RW
*/
MLXSW_ITEM64(reg, qpcr, violate_count, 0x20, 0, 64);
#define MLXSW_REG_QPCR_LOWEST_CIR 1
#define MLXSW_REG_QPCR_HIGHEST_CIR (2 * 1000 * 1000 * 1000) /* 2Gpps */
#define MLXSW_REG_QPCR_LOWEST_CBS 4
#define MLXSW_REG_QPCR_HIGHEST_CBS 24
static inline void mlxsw_reg_qpcr_pack(char *payload, u16 pid,
enum mlxsw_reg_qpcr_ir_units ir_units,
bool bytes, u32 cir, u16 cbs)
......@@ -5520,12 +5537,10 @@ enum mlxsw_reg_htgt_trap_group {
MLXSW_REG_HTGT_TRAP_GROUP_SP_PIM,
MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST,
MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS,
MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE,
MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME,
MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_RPF,
MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT,
MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD,
MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND,
......
......@@ -43,6 +43,7 @@
#include "spectrum_acl_flex_actions.h"
#include "spectrum_span.h"
#include "spectrum_ptp.h"
#include "spectrum_trap.h"
#include "../mlxfw/mlxfw.h"
#define MLXSW_SP1_FWREV_MAJOR 13
......@@ -4556,6 +4557,7 @@ static const struct mlxsw_listener mlxsw_sp1_listener[] = {
static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
char qpcr_pl[MLXSW_REG_QPCR_LEN];
enum mlxsw_reg_qpcr_ir_units ir_units;
int max_cpu_policers;
......@@ -4578,7 +4580,6 @@ static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core)
case MLXSW_REG_HTGT_TRAP_GROUP_SP_LLDP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_PIM:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_RPF:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR:
rate = 128;
burst_size = 7;
......@@ -4591,7 +4592,6 @@ static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core)
case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND:
......@@ -4619,6 +4619,7 @@ static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core)
continue;
}
__set_bit(i, mlxsw_sp->trap->policers_usage);
mlxsw_reg_qpcr_pack(qpcr_pl, i, ir_units, is_bytes, rate,
burst_size);
err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(qpcr), qpcr_pl);
......@@ -4671,19 +4672,20 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core)
break;
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_RPF:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_PTP1:
priority = 2;
tc = 2;
break;
case MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR:
priority = 1;
tc = 1;
break;
case MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR:
priority = 0;
tc = 1;
break;
case MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT:
priority = MLXSW_REG_HTGT_DEFAULT_PRIORITY;
tc = MLXSW_REG_HTGT_DEFAULT_TC;
......@@ -4747,20 +4749,32 @@ static void mlxsw_sp_traps_unregister(struct mlxsw_sp *mlxsw_sp,
static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp)
{
struct mlxsw_sp_trap *trap;
u64 max_policers;
int err;
if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_CPU_POLICERS))
return -EIO;
max_policers = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_CPU_POLICERS);
trap = kzalloc(struct_size(trap, policers_usage,
BITS_TO_LONGS(max_policers)), GFP_KERNEL);
if (!trap)
return -ENOMEM;
trap->max_policers = max_policers;
mlxsw_sp->trap = trap;
err = mlxsw_sp_cpu_policers_set(mlxsw_sp->core);
if (err)
return err;
goto err_cpu_policers_set;
err = mlxsw_sp_trap_groups_set(mlxsw_sp->core);
if (err)
return err;
goto err_trap_groups_set;
err = mlxsw_sp_traps_register(mlxsw_sp, mlxsw_sp_listener,
ARRAY_SIZE(mlxsw_sp_listener));
if (err)
return err;
goto err_traps_register;
err = mlxsw_sp_traps_register(mlxsw_sp, mlxsw_sp->listeners,
mlxsw_sp->listeners_count);
......@@ -4772,6 +4786,10 @@ static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp)
err_extra_traps_init:
mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp_listener,
ARRAY_SIZE(mlxsw_sp_listener));
err_traps_register:
err_trap_groups_set:
err_cpu_policers_set:
kfree(trap);
return err;
}
......@@ -4781,6 +4799,7 @@ static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp)
mlxsw_sp->listeners_count);
mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp_listener,
ARRAY_SIZE(mlxsw_sp_listener));
kfree(mlxsw_sp->trap);
}
#define MLXSW_SP_LAG_SEED_INIT 0xcafecafe
......@@ -5655,6 +5674,11 @@ static struct mlxsw_driver mlxsw_sp1_driver = {
.trap_fini = mlxsw_sp_trap_fini,
.trap_action_set = mlxsw_sp_trap_action_set,
.trap_group_init = mlxsw_sp_trap_group_init,
.trap_group_set = mlxsw_sp_trap_group_set,
.trap_policer_init = mlxsw_sp_trap_policer_init,
.trap_policer_fini = mlxsw_sp_trap_policer_fini,
.trap_policer_set = mlxsw_sp_trap_policer_set,
.trap_policer_counter_get = mlxsw_sp_trap_policer_counter_get,
.txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp1_resources_register,
.kvd_sizes_get = mlxsw_sp_kvd_sizes_get,
......@@ -5689,6 +5713,11 @@ static struct mlxsw_driver mlxsw_sp2_driver = {
.trap_fini = mlxsw_sp_trap_fini,
.trap_action_set = mlxsw_sp_trap_action_set,
.trap_group_init = mlxsw_sp_trap_group_init,
.trap_group_set = mlxsw_sp_trap_group_set,
.trap_policer_init = mlxsw_sp_trap_policer_init,
.trap_policer_fini = mlxsw_sp_trap_policer_fini,
.trap_policer_set = mlxsw_sp_trap_policer_set,
.trap_policer_counter_get = mlxsw_sp_trap_policer_counter_get,
.txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp2_resources_register,
.params_register = mlxsw_sp2_params_register,
......@@ -5722,6 +5751,11 @@ static struct mlxsw_driver mlxsw_sp3_driver = {
.trap_fini = mlxsw_sp_trap_fini,
.trap_action_set = mlxsw_sp_trap_action_set,
.trap_group_init = mlxsw_sp_trap_group_init,
.trap_group_set = mlxsw_sp_trap_group_set,
.trap_policer_init = mlxsw_sp_trap_policer_init,
.trap_policer_fini = mlxsw_sp_trap_policer_fini,
.trap_policer_set = mlxsw_sp_trap_policer_set,
.trap_policer_counter_get = mlxsw_sp_trap_policer_counter_get,
.txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp2_resources_register,
.params_register = mlxsw_sp2_params_register,
......
......@@ -176,6 +176,7 @@ struct mlxsw_sp {
struct mlxsw_sp_ptp_state *ptp_state;
struct mlxsw_sp_counter_pool *counter_pool;
struct mlxsw_sp_span *span;
struct mlxsw_sp_trap *trap;
const struct mlxsw_fw_rev *req_rev;
const char *fw_filename;
const struct mlxsw_sp_kvdl_ops *kvdl_ops;
......@@ -1022,6 +1023,22 @@ int mlxsw_sp_trap_action_set(struct mlxsw_core *mlxsw_core,
enum devlink_trap_action action);
int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group);
int mlxsw_sp_trap_group_set(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group,
const struct devlink_trap_policer *policer);
int
mlxsw_sp_trap_policer_init(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer);
void mlxsw_sp_trap_policer_fini(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer);
int
mlxsw_sp_trap_policer_set(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer,
u64 rate, u64 burst, struct netlink_ext_ack *extack);
int
mlxsw_sp_trap_policer_counter_get(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer,
u64 *p_drops);
static inline struct net *mlxsw_sp_net(struct mlxsw_sp *mlxsw_sp)
{
......
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/* Copyright (c) 2020 Mellanox Technologies. All rights reserved */
#ifndef _MLXSW_SPECTRUM_TRAP_H
#define _MLXSW_SPECTRUM_TRAP_H
#include <linux/list.h>
#include <net/devlink.h>
struct mlxsw_sp_trap {
struct devlink_trap_policer *policers_arr; /* Registered policers */
u64 policers_count; /* Number of registered policers */
struct list_head policer_item_list;
u64 max_policers;
unsigned long policers_usage[]; /* Usage bitmap */
};
struct mlxsw_sp_trap_policer_item {
u16 hw_id;
u32 id;
struct list_head list; /* Member of policer_item_list */
};
#endif
......@@ -215,6 +215,15 @@ static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
&nsim_dev->fail_reload);
debugfs_create_file("trap_flow_action_cookie", 0600, nsim_dev->ddir,
nsim_dev, &nsim_dev_trap_fa_cookie_fops);
debugfs_create_bool("fail_trap_group_set", 0600,
nsim_dev->ddir,
&nsim_dev->fail_trap_group_set);
debugfs_create_bool("fail_trap_policer_set", 0600,
nsim_dev->ddir,
&nsim_dev->fail_trap_policer_set);
debugfs_create_bool("fail_trap_policer_counter_get", 0600,
nsim_dev->ddir,
&nsim_dev->fail_trap_policer_counter_get);
return 0;
}
......@@ -392,6 +401,7 @@ struct nsim_trap_item {
struct nsim_trap_data {
struct delayed_work trap_report_dw;
struct nsim_trap_item *trap_items_arr;
u64 *trap_policers_cnt_arr;
struct nsim_dev *nsim_dev;
spinlock_t trap_lock; /* Protects trap_items_arr */
};
......@@ -426,11 +436,29 @@ enum {
DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
NSIM_TRAP_METADATA)
#define NSIM_DEV_TRAP_POLICER_MIN_RATE 1
#define NSIM_DEV_TRAP_POLICER_MAX_RATE 8000
#define NSIM_DEV_TRAP_POLICER_MIN_BURST 8
#define NSIM_DEV_TRAP_POLICER_MAX_BURST 65536
#define NSIM_TRAP_POLICER(_id, _rate, _burst) \
DEVLINK_TRAP_POLICER(_id, _rate, _burst, \
NSIM_DEV_TRAP_POLICER_MAX_RATE, \
NSIM_DEV_TRAP_POLICER_MIN_RATE, \
NSIM_DEV_TRAP_POLICER_MAX_BURST, \
NSIM_DEV_TRAP_POLICER_MIN_BURST)
static const struct devlink_trap_policer nsim_trap_policers_arr[] = {
NSIM_TRAP_POLICER(1, 1000, 128),
NSIM_TRAP_POLICER(2, 2000, 256),
NSIM_TRAP_POLICER(3, 3000, 512),
};
static const struct devlink_trap_group nsim_trap_groups_arr[] = {
DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS),
DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS),
DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS),
DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS),
DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0),
DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 1),
DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 2),
DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 3),
};
static const struct devlink_trap nsim_traps_arr[] = {
......@@ -568,6 +596,7 @@ static void nsim_dev_trap_report_work(struct work_struct *work)
static int nsim_dev_traps_init(struct devlink *devlink)
{
size_t policers_count = ARRAY_SIZE(nsim_trap_policers_arr);
struct nsim_dev *nsim_dev = devlink_priv(devlink);
struct nsim_trap_data *nsim_trap_data;
int err;
......@@ -584,6 +613,14 @@ static int nsim_dev_traps_init(struct devlink *devlink)
goto err_trap_data_free;
}
nsim_trap_data->trap_policers_cnt_arr = kcalloc(policers_count,
sizeof(u64),
GFP_KERNEL);
if (!nsim_trap_data->trap_policers_cnt_arr) {
err = -ENOMEM;
goto err_trap_items_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.
......@@ -592,10 +629,15 @@ static int nsim_dev_traps_init(struct devlink *devlink)
nsim_trap_data->nsim_dev = nsim_dev;
nsim_dev->trap_data = nsim_trap_data;
err = devlink_trap_policers_register(devlink, nsim_trap_policers_arr,
policers_count);
if (err)
goto err_trap_policers_cnt_free;
err = devlink_trap_groups_register(devlink, nsim_trap_groups_arr,
ARRAY_SIZE(nsim_trap_groups_arr));
if (err)
goto err_trap_items_free;
goto err_trap_policers_unregister;
err = devlink_traps_register(devlink, nsim_traps_arr,
ARRAY_SIZE(nsim_traps_arr), NULL);
......@@ -612,6 +654,11 @@ static int nsim_dev_traps_init(struct devlink *devlink)
err_trap_groups_unregister:
devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr,
ARRAY_SIZE(nsim_trap_groups_arr));
err_trap_policers_unregister:
devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr,
ARRAY_SIZE(nsim_trap_policers_arr));
err_trap_policers_cnt_free:
kfree(nsim_trap_data->trap_policers_cnt_arr);
err_trap_items_free:
kfree(nsim_trap_data->trap_items_arr);
err_trap_data_free:
......@@ -628,6 +675,9 @@ static void nsim_dev_traps_exit(struct devlink *devlink)
ARRAY_SIZE(nsim_traps_arr));
devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr,
ARRAY_SIZE(nsim_trap_groups_arr));
devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr,
ARRAY_SIZE(nsim_trap_policers_arr));
kfree(nsim_dev->trap_data->trap_policers_cnt_arr);
kfree(nsim_dev->trap_data->trap_items_arr);
kfree(nsim_dev->trap_data);
}
......@@ -766,6 +816,53 @@ nsim_dev_devlink_trap_action_set(struct devlink *devlink,
return 0;
}
static int
nsim_dev_devlink_trap_group_set(struct devlink *devlink,
const struct devlink_trap_group *group,
const struct devlink_trap_policer *policer)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
if (nsim_dev->fail_trap_group_set)
return -EINVAL;
return 0;
}
static int
nsim_dev_devlink_trap_policer_set(struct devlink *devlink,
const struct devlink_trap_policer *policer,
u64 rate, u64 burst,
struct netlink_ext_ack *extack)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
if (nsim_dev->fail_trap_policer_set) {
NL_SET_ERR_MSG_MOD(extack, "User setup the operation to fail for testing purposes");
return -EINVAL;
}
return 0;
}
static int
nsim_dev_devlink_trap_policer_counter_get(struct devlink *devlink,
const struct devlink_trap_policer *policer,
u64 *p_drops)
{
struct nsim_dev *nsim_dev = devlink_priv(devlink);
u64 *cnt;
if (nsim_dev->fail_trap_policer_counter_get)
return -EINVAL;
cnt = &nsim_dev->trap_data->trap_policers_cnt_arr[policer->id - 1];
*p_drops = *cnt;
*cnt += jiffies % 64;
return 0;
}
static const struct devlink_ops nsim_dev_devlink_ops = {
.reload_down = nsim_dev_reload_down,
.reload_up = nsim_dev_reload_up,
......@@ -773,6 +870,9 @@ static const struct devlink_ops nsim_dev_devlink_ops = {
.flash_update = nsim_dev_flash_update,
.trap_init = nsim_dev_devlink_trap_init,
.trap_action_set = nsim_dev_devlink_trap_action_set,
.trap_group_set = nsim_dev_devlink_trap_group_set,
.trap_policer_set = nsim_dev_devlink_trap_policer_set,
.trap_policer_counter_get = nsim_dev_devlink_trap_policer_counter_get,
};
#define NSIM_DEV_MAX_MACS_DEFAULT 32
......
......@@ -180,6 +180,9 @@ struct nsim_dev {
struct nsim_dev_health health;
struct flow_action_cookie *fa_cookie;
spinlock_t fa_cookie_lock; /* protects fa_cookie */
bool fail_trap_group_set;
bool fail_trap_policer_set;
bool fail_trap_policer_counter_get;
};
static inline struct net *nsim_dev_net(struct nsim_dev *nsim_dev)
......
......@@ -35,6 +35,7 @@ struct devlink {
struct devlink_dpipe_headers *dpipe_headers;
struct list_head trap_list;
struct list_head trap_group_list;
struct list_head trap_policer_list;
const struct devlink_ops *ops;
struct xarray snapshot_ids;
struct device *dev;
......@@ -545,11 +546,35 @@ struct devlink_health_reporter_ops {
struct netlink_ext_ack *extack);
};
/**
* struct devlink_trap_policer - Immutable packet trap policer attributes.
* @id: Policer identifier.
* @init_rate: Initial rate in packets / sec.
* @init_burst: Initial burst size in packets.
* @max_rate: Maximum rate.
* @min_rate: Minimum rate.
* @max_burst: Maximum burst size.
* @min_burst: Minimum burst size.
*
* Describes immutable attributes of packet trap policers that drivers register
* with devlink.
*/
struct devlink_trap_policer {
u32 id;
u64 init_rate;
u64 init_burst;
u64 max_rate;
u64 min_rate;
u64 max_burst;
u64 min_burst;
};
/**
* 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.
* @init_policer_id: Initial policer identifier.
*
* Describes immutable attributes of packet trap groups that drivers register
* with devlink.
......@@ -558,6 +583,7 @@ struct devlink_trap_group {
const char *name;
u16 id;
bool generic;
u32 init_policer_id;
};
#define DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT BIT(0)
......@@ -735,11 +761,24 @@ enum devlink_trap_group_generic_id {
.metadata_cap = _metadata_cap, \
}
#define DEVLINK_TRAP_GROUP_GENERIC(_id) \
#define DEVLINK_TRAP_GROUP_GENERIC(_id, _policer_id) \
{ \
.name = DEVLINK_TRAP_GROUP_GENERIC_NAME_##_id, \
.id = DEVLINK_TRAP_GROUP_GENERIC_ID_##_id, \
.generic = true, \
.init_policer_id = _policer_id, \
}
#define DEVLINK_TRAP_POLICER(_id, _rate, _burst, _max_rate, _min_rate, \
_max_burst, _min_burst) \
{ \
.id = _id, \
.init_rate = _rate, \
.init_burst = _burst, \
.max_rate = _max_rate, \
.min_rate = _min_rate, \
.max_burst = _max_burst, \
.min_burst = _min_burst, \
}
struct devlink_ops {
......@@ -838,6 +877,47 @@ struct devlink_ops {
*/
int (*trap_group_init)(struct devlink *devlink,
const struct devlink_trap_group *group);
/**
* @trap_group_set: Trap group parameters set function.
*
* Note: @policer can be NULL when a policer is being unbound from
* @group.
*/
int (*trap_group_set)(struct devlink *devlink,
const struct devlink_trap_group *group,
const struct devlink_trap_policer *policer);
/**
* @trap_policer_init: Trap policer initialization function.
*
* Should be used by device drivers to initialize the trap policer in
* the underlying device.
*/
int (*trap_policer_init)(struct devlink *devlink,
const struct devlink_trap_policer *policer);
/**
* @trap_policer_fini: Trap policer de-initialization function.
*
* Should be used by device drivers to de-initialize the trap policer
* in the underlying device.
*/
void (*trap_policer_fini)(struct devlink *devlink,
const struct devlink_trap_policer *policer);
/**
* @trap_policer_set: Trap policer parameters set function.
*/
int (*trap_policer_set)(struct devlink *devlink,
const struct devlink_trap_policer *policer,
u64 rate, u64 burst,
struct netlink_ext_ack *extack);
/**
* @trap_policer_counter_get: Trap policer counter get function.
*
* Should be used by device drivers to report number of packets dropped
* by the policer.
*/
int (*trap_policer_counter_get)(struct devlink *devlink,
const struct devlink_trap_policer *policer,
u64 *p_drops);
};
static inline void *devlink_priv(struct devlink *devlink)
......@@ -1080,6 +1160,14 @@ int devlink_trap_groups_register(struct devlink *devlink,
void devlink_trap_groups_unregister(struct devlink *devlink,
const struct devlink_trap_group *groups,
size_t groups_count);
int
devlink_trap_policers_register(struct devlink *devlink,
const struct devlink_trap_policer *policers,
size_t policers_count);
void
devlink_trap_policers_unregister(struct devlink *devlink,
const struct devlink_trap_policer *policers,
size_t policers_count);
#if IS_ENABLED(CONFIG_NET_DEVLINK)
......
......@@ -117,6 +117,11 @@ enum devlink_command {
DEVLINK_CMD_TRAP_GROUP_NEW,
DEVLINK_CMD_TRAP_GROUP_DEL,
DEVLINK_CMD_TRAP_POLICER_GET, /* can dump */
DEVLINK_CMD_TRAP_POLICER_SET,
DEVLINK_CMD_TRAP_POLICER_NEW,
DEVLINK_CMD_TRAP_POLICER_DEL,
/* add new commands above here */
__DEVLINK_CMD_MAX,
DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
......@@ -217,6 +222,7 @@ enum devlink_param_reset_dev_on_drv_probe_value {
enum {
DEVLINK_ATTR_STATS_RX_PACKETS, /* u64 */
DEVLINK_ATTR_STATS_RX_BYTES, /* u64 */
DEVLINK_ATTR_STATS_RX_DROPPED, /* u64 */
__DEVLINK_ATTR_STATS_MAX,
DEVLINK_ATTR_STATS_MAX = __DEVLINK_ATTR_STATS_MAX - 1
......@@ -431,6 +437,11 @@ enum devlink_attr {
DEVLINK_ATTR_NETNS_ID, /* u32 */
DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP, /* u8 */
DEVLINK_ATTR_TRAP_POLICER_ID, /* u32 */
DEVLINK_ATTR_TRAP_POLICER_RATE, /* u64 */
DEVLINK_ATTR_TRAP_POLICER_BURST, /* u64 */
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,
......
This diff is collapsed.
......@@ -16,6 +16,8 @@ ALL_TESTS="
trap_group_action_test
bad_trap_group_test
trap_group_stats_test
trap_policer_test
trap_policer_bind_test
port_del_test
dev_del_test
"
......@@ -23,6 +25,7 @@ NETDEVSIM_PATH=/sys/bus/netdevsim/
DEV_ADDR=1337
DEV=netdevsim${DEV_ADDR}
DEVLINK_DEV=netdevsim/${DEV}
DEBUGFS_DIR=/sys/kernel/debug/netdevsim/$DEV/
SLEEP_TIME=1
NETDEV=""
NUM_NETIFS=0
......@@ -256,6 +259,119 @@ trap_group_stats_test()
log_test "Trap group statistics"
}
trap_policer_test()
{
local packets_t0
local packets_t1
if [ $(devlink_trap_policers_num_get) -eq 0 ]; then
check_err 1 "Failed to dump policers"
fi
devlink trap policer set $DEVLINK_DEV policer 1337 &> /dev/null
check_fail $? "Did not get an error for setting a non-existing policer"
devlink trap policer show $DEVLINK_DEV policer 1337 &> /dev/null
check_fail $? "Did not get an error for getting a non-existing policer"
devlink trap policer set $DEVLINK_DEV policer 1 rate 2000 burst 16
check_err $? "Failed to set valid parameters for a valid policer"
if [ $(devlink_trap_policer_rate_get 1) -ne 2000 ]; then
check_err 1 "Policer rate was not changed"
fi
if [ $(devlink_trap_policer_burst_get 1) -ne 16 ]; then
check_err 1 "Policer burst size was not changed"
fi
devlink trap policer set $DEVLINK_DEV policer 1 rate 0 &> /dev/null
check_fail $? "Policer rate was changed to rate lower than limit"
devlink trap policer set $DEVLINK_DEV policer 1 rate 9000 &> /dev/null
check_fail $? "Policer rate was changed to rate higher than limit"
devlink trap policer set $DEVLINK_DEV policer 1 burst 2 &> /dev/null
check_fail $? "Policer burst size was changed to burst size lower than limit"
devlink trap policer set $DEVLINK_DEV policer 1 rate 65537 &> /dev/null
check_fail $? "Policer burst size was changed to burst size higher than limit"
echo "y" > $DEBUGFS_DIR/fail_trap_policer_set
devlink trap policer set $DEVLINK_DEV policer 1 rate 3000 &> /dev/null
check_fail $? "Managed to set policer rate when should not"
echo "n" > $DEBUGFS_DIR/fail_trap_policer_set
if [ $(devlink_trap_policer_rate_get 1) -ne 2000 ]; then
check_err 1 "Policer rate was changed to an invalid value"
fi
if [ $(devlink_trap_policer_burst_get 1) -ne 16 ]; then
check_err 1 "Policer burst size was changed to an invalid value"
fi
packets_t0=$(devlink_trap_policer_rx_dropped_get 1)
sleep .5
packets_t1=$(devlink_trap_policer_rx_dropped_get 1)
if [ ! $packets_t1 -gt $packets_t0 ]; then
check_err 1 "Policer drop counter was not incremented"
fi
echo "y"> $DEBUGFS_DIR/fail_trap_policer_counter_get
devlink -s trap policer show $DEVLINK_DEV policer 1 &> /dev/null
check_fail $? "Managed to read policer drop counter when should not"
echo "n"> $DEBUGFS_DIR/fail_trap_policer_counter_get
devlink -s trap policer show $DEVLINK_DEV policer 1 &> /dev/null
check_err $? "Did not manage to read policer drop counter when should"
log_test "Trap policer"
}
trap_group_check_policer()
{
local group_name=$1; shift
devlink -j -p trap group show $DEVLINK_DEV group $group_name \
| jq -e '.[][][]["policer"]' &> /dev/null
}
trap_policer_bind_test()
{
devlink trap group set $DEVLINK_DEV group l2_drops policer 1
check_err $? "Failed to bind a valid policer"
if [ $(devlink_trap_group_policer_get "l2_drops") -ne 1 ]; then
check_err 1 "Bound policer was not changed"
fi
devlink trap group set $DEVLINK_DEV group l2_drops policer 1337 \
&> /dev/null
check_fail $? "Did not get an error for binding a non-existing policer"
if [ $(devlink_trap_group_policer_get "l2_drops") -ne 1 ]; then
check_err 1 "Bound policer was changed when should not"
fi
devlink trap group set $DEVLINK_DEV group l2_drops policer 0
check_err $? "Failed to unbind a policer when using ID 0"
trap_group_check_policer "l2_drops"
check_fail $? "Trap group has a policer after unbinding with ID 0"
devlink trap group set $DEVLINK_DEV group l2_drops policer 1
check_err $? "Failed to bind a valid policer"
devlink trap group set $DEVLINK_DEV group l2_drops nopolicer
check_err $? "Failed to unbind a policer when using 'nopolicer' keyword"
trap_group_check_policer "l2_drops"
check_fail $? "Trap group has a policer after unbinding with 'nopolicer' keyword"
devlink trap group set $DEVLINK_DEV group l2_drops policer 1
check_err $? "Failed to bind a valid policer"
echo "y"> $DEBUGFS_DIR/fail_trap_group_set
devlink trap group set $DEVLINK_DEV group l2_drops policer 2 \
&> /dev/null
check_fail $? "Managed to bind a policer when should not"
echo "n"> $DEBUGFS_DIR/fail_trap_group_set
devlink trap group set $DEVLINK_DEV group l2_drops policer 2
check_err $? "Did not manage to bind a policer when should"
devlink trap group set $DEVLINK_DEV group l2_drops action drop \
policer 1337 &> /dev/null
check_fail $? "Did not get an error for partially modified trap group"
log_test "Trap policer binding"
}
port_del_test()
{
local group_name
......
......@@ -420,6 +420,49 @@ devlink_trap_drop_cleanup()
tc filter del dev $dev egress protocol $proto pref $pref handle $handle flower
}
devlink_trap_policers_num_get()
{
devlink -j -p trap policer show | jq '.[]["'$DEVLINK_DEV'"] | length'
}
devlink_trap_policer_rate_get()
{
local policer_id=$1; shift
devlink -j -p trap policer show $DEVLINK_DEV policer $policer_id \
| jq '.[][][]["rate"]'
}
devlink_trap_policer_burst_get()
{
local policer_id=$1; shift
devlink -j -p trap policer show $DEVLINK_DEV policer $policer_id \
| jq '.[][][]["burst"]'
}
devlink_trap_policer_rx_dropped_get()
{
local policer_id=$1; shift
devlink -j -p -s trap policer show $DEVLINK_DEV policer $policer_id \
| jq '.[][][]["stats"]["rx"]["dropped"]'
}
devlink_trap_group_policer_get()
{
local group_name=$1; shift
devlink -j -p trap group show $DEVLINK_DEV group $group_name \
| jq '.[][][]["policer"]'
}
devlink_trap_policer_ids_get()
{
devlink -j -p trap policer show \
| jq '.[]["'$DEVLINK_DEV'"][]["policer"]'
}
devlink_port_by_netdev()
{
local if_name=$1
......
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