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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2020-03-30 17:54:59 -07:00
commit 6fe9a949d3
16 changed files with 1778 additions and 51 deletions

View File

@ -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 - Contains packet traps for packets that were dropped by the device during
ACL processing 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 Testing
======= =======

View File

@ -1198,6 +1198,72 @@ mlxsw_devlink_trap_group_init(struct devlink *devlink,
return mlxsw_driver->trap_group_init(mlxsw_core, group); 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 = { static const struct devlink_ops mlxsw_devlink_ops = {
.reload_down = mlxsw_devlink_core_bus_device_reload_down, .reload_down = mlxsw_devlink_core_bus_device_reload_down,
.reload_up = mlxsw_devlink_core_bus_device_reload_up, .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_fini = mlxsw_devlink_trap_fini,
.trap_action_set = mlxsw_devlink_trap_action_set, .trap_action_set = mlxsw_devlink_trap_action_set,
.trap_group_init = mlxsw_devlink_trap_group_init, .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 static int

View File

@ -327,6 +327,20 @@ struct mlxsw_driver {
enum devlink_trap_action action); enum devlink_trap_action action);
int (*trap_group_init)(struct mlxsw_core *mlxsw_core, int (*trap_group_init)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group); 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, void (*txhdr_construct)(struct sk_buff *skb,
const struct mlxsw_tx_info *tx_info); const struct mlxsw_tx_info *tx_info);
int (*resources_register)(struct mlxsw_core *mlxsw_core); int (*resources_register)(struct mlxsw_core *mlxsw_core);

View File

@ -3296,6 +3296,12 @@ MLXSW_ITEM32(reg, qpcr, g, 0x00, 14, 2);
*/ */
MLXSW_ITEM32(reg, qpcr, pid, 0x00, 0, 14); 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 /* reg_qpcr_color_aware
* Is the policer aware of colors. * Is the policer aware of colors.
* Must be 0 (unaware) for cpu port. * 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); 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, static inline void mlxsw_reg_qpcr_pack(char *payload, u16 pid,
enum mlxsw_reg_qpcr_ir_units ir_units, enum mlxsw_reg_qpcr_ir_units ir_units,
bool bytes, u32 cir, u16 cbs) 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_PIM,
MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST, MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST,
MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP, 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_ROUTER_EXP,
MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE, MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE,
MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME, MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME,
MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP, 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_EVENT,
MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD, MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD,
MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND, MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND,

View File

@ -43,6 +43,7 @@
#include "spectrum_acl_flex_actions.h" #include "spectrum_acl_flex_actions.h"
#include "spectrum_span.h" #include "spectrum_span.h"
#include "spectrum_ptp.h" #include "spectrum_ptp.h"
#include "spectrum_trap.h"
#include "../mlxfw/mlxfw.h" #include "../mlxfw/mlxfw.h"
#define MLXSW_SP1_FWREV_MAJOR 13 #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) 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]; char qpcr_pl[MLXSW_REG_QPCR_LEN];
enum mlxsw_reg_qpcr_ir_units ir_units; enum mlxsw_reg_qpcr_ir_units ir_units;
int max_cpu_policers; 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_LLDP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF: case MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_PIM: case MLXSW_REG_HTGT_TRAP_GROUP_SP_PIM:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_RPF:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR: case MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR:
rate = 128; rate = 128;
burst_size = 7; 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_BGP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP: 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_ROUTER_EXP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE: case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND: 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; continue;
} }
__set_bit(i, mlxsw_sp->trap->policers_usage);
mlxsw_reg_qpcr_pack(qpcr_pl, i, ir_units, is_bytes, rate, mlxsw_reg_qpcr_pack(qpcr_pl, i, ir_units, is_bytes, rate,
burst_size); burst_size);
err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(qpcr), qpcr_pl); 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; break;
case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND: 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: case MLXSW_REG_HTGT_TRAP_GROUP_SP_PTP1:
priority = 2; priority = 2;
tc = 2; tc = 2;
break; 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_ROUTER_EXP:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE: case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST: case MLXSW_REG_HTGT_TRAP_GROUP_SP_MULTICAST:
case MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR:
priority = 1; priority = 1;
tc = 1; tc = 1;
break; break;
case MLXSW_REG_HTGT_TRAP_GROUP_SP_LBERROR:
priority = 0;
tc = 1;
break;
case MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT: case MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT:
priority = MLXSW_REG_HTGT_DEFAULT_PRIORITY; priority = MLXSW_REG_HTGT_DEFAULT_PRIORITY;
tc = MLXSW_REG_HTGT_DEFAULT_TC; 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) static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp)
{ {
struct mlxsw_sp_trap *trap;
u64 max_policers;
int err; 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); err = mlxsw_sp_cpu_policers_set(mlxsw_sp->core);
if (err) if (err)
return err; goto err_cpu_policers_set;
err = mlxsw_sp_trap_groups_set(mlxsw_sp->core); err = mlxsw_sp_trap_groups_set(mlxsw_sp->core);
if (err) if (err)
return err; goto err_trap_groups_set;
err = mlxsw_sp_traps_register(mlxsw_sp, mlxsw_sp_listener, err = mlxsw_sp_traps_register(mlxsw_sp, mlxsw_sp_listener,
ARRAY_SIZE(mlxsw_sp_listener)); ARRAY_SIZE(mlxsw_sp_listener));
if (err) if (err)
return err; goto err_traps_register;
err = mlxsw_sp_traps_register(mlxsw_sp, mlxsw_sp->listeners, err = mlxsw_sp_traps_register(mlxsw_sp, mlxsw_sp->listeners,
mlxsw_sp->listeners_count); mlxsw_sp->listeners_count);
@ -4772,6 +4786,10 @@ static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp)
err_extra_traps_init: err_extra_traps_init:
mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp_listener, mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp_listener,
ARRAY_SIZE(mlxsw_sp_listener)); ARRAY_SIZE(mlxsw_sp_listener));
err_traps_register:
err_trap_groups_set:
err_cpu_policers_set:
kfree(trap);
return err; return err;
} }
@ -4781,6 +4799,7 @@ static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp)
mlxsw_sp->listeners_count); mlxsw_sp->listeners_count);
mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp_listener, mlxsw_sp_traps_unregister(mlxsw_sp, mlxsw_sp_listener,
ARRAY_SIZE(mlxsw_sp_listener)); ARRAY_SIZE(mlxsw_sp_listener));
kfree(mlxsw_sp->trap);
} }
#define MLXSW_SP_LAG_SEED_INIT 0xcafecafe #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_fini = mlxsw_sp_trap_fini,
.trap_action_set = mlxsw_sp_trap_action_set, .trap_action_set = mlxsw_sp_trap_action_set,
.trap_group_init = mlxsw_sp_trap_group_init, .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, .txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp1_resources_register, .resources_register = mlxsw_sp1_resources_register,
.kvd_sizes_get = mlxsw_sp_kvd_sizes_get, .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_fini = mlxsw_sp_trap_fini,
.trap_action_set = mlxsw_sp_trap_action_set, .trap_action_set = mlxsw_sp_trap_action_set,
.trap_group_init = mlxsw_sp_trap_group_init, .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, .txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp2_resources_register, .resources_register = mlxsw_sp2_resources_register,
.params_register = mlxsw_sp2_params_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_fini = mlxsw_sp_trap_fini,
.trap_action_set = mlxsw_sp_trap_action_set, .trap_action_set = mlxsw_sp_trap_action_set,
.trap_group_init = mlxsw_sp_trap_group_init, .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, .txhdr_construct = mlxsw_sp_txhdr_construct,
.resources_register = mlxsw_sp2_resources_register, .resources_register = mlxsw_sp2_resources_register,
.params_register = mlxsw_sp2_params_register, .params_register = mlxsw_sp2_params_register,

View File

@ -176,6 +176,7 @@ struct mlxsw_sp {
struct mlxsw_sp_ptp_state *ptp_state; struct mlxsw_sp_ptp_state *ptp_state;
struct mlxsw_sp_counter_pool *counter_pool; struct mlxsw_sp_counter_pool *counter_pool;
struct mlxsw_sp_span *span; struct mlxsw_sp_span *span;
struct mlxsw_sp_trap *trap;
const struct mlxsw_fw_rev *req_rev; const struct mlxsw_fw_rev *req_rev;
const char *fw_filename; const char *fw_filename;
const struct mlxsw_sp_kvdl_ops *kvdl_ops; 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); enum devlink_trap_action action);
int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core, int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group); 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) static inline struct net *mlxsw_sp_net(struct mlxsw_sp *mlxsw_sp)
{ {

View File

@ -1,13 +1,16 @@
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2019 Mellanox Technologies. All rights reserved */ /* Copyright (c) 2019 Mellanox Technologies. All rights reserved */
#include <linux/bitops.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/netlink.h>
#include <net/devlink.h> #include <net/devlink.h>
#include <uapi/linux/devlink.h> #include <uapi/linux/devlink.h>
#include "core.h" #include "core.h"
#include "reg.h" #include "reg.h"
#include "spectrum.h" #include "spectrum.h"
#include "spectrum_trap.h"
/* All driver-specific traps must be documented in /* All driver-specific traps must be documented in
* Documentation/networking/devlink/mlxsw.rst * Documentation/networking/devlink/mlxsw.rst
@ -165,11 +168,23 @@ static void mlxsw_sp_rx_exception_listener(struct sk_buff *skb, u8 local_port,
MLXSW_RXL(mlxsw_sp_rx_exception_listener, _id, \ MLXSW_RXL(mlxsw_sp_rx_exception_listener, _id, \
_action, false, SP_##_group_id, SET_FW_DEFAULT) _action, false, SP_##_group_id, SET_FW_DEFAULT)
#define MLXSW_SP_TRAP_POLICER(_id, _rate, _burst) \
DEVLINK_TRAP_POLICER(_id, _rate, _burst, \
MLXSW_REG_QPCR_HIGHEST_CIR, \
MLXSW_REG_QPCR_LOWEST_CIR, \
1 << MLXSW_REG_QPCR_HIGHEST_CBS, \
1 << MLXSW_REG_QPCR_LOWEST_CBS)
/* Ordered by policer identifier */
static const struct devlink_trap_policer mlxsw_sp_trap_policers_arr[] = {
MLXSW_SP_TRAP_POLICER(1, 10 * 1024, 128),
};
static const struct devlink_trap_group mlxsw_sp_trap_groups_arr[] = { static const struct devlink_trap_group mlxsw_sp_trap_groups_arr[] = {
DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS), DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 1),
DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS), DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 1),
DEVLINK_TRAP_GROUP_GENERIC(TUNNEL_DROPS), DEVLINK_TRAP_GROUP_GENERIC(TUNNEL_DROPS, 1),
DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS), DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 1),
}; };
static const struct devlink_trap mlxsw_sp_traps_arr[] = { static const struct devlink_trap mlxsw_sp_traps_arr[] = {
@ -225,23 +240,24 @@ static const struct mlxsw_listener mlxsw_sp_listeners_arr[] = {
MLXSW_SP_RXL_DISCARD(ING_ROUTER_IPV4_SIP_BC, L3_DISCARDS), MLXSW_SP_RXL_DISCARD(ING_ROUTER_IPV4_SIP_BC, L3_DISCARDS),
MLXSW_SP_RXL_DISCARD(IPV6_MC_DIP_RESERVED_SCOPE, L3_DISCARDS), MLXSW_SP_RXL_DISCARD(IPV6_MC_DIP_RESERVED_SCOPE, L3_DISCARDS),
MLXSW_SP_RXL_DISCARD(IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE, L3_DISCARDS), MLXSW_SP_RXL_DISCARD(IPV6_MC_DIP_INTERFACE_LOCAL_SCOPE, L3_DISCARDS),
MLXSW_SP_RXL_EXCEPTION(MTUERROR, ROUTER_EXP, TRAP_TO_CPU), MLXSW_SP_RXL_EXCEPTION(MTUERROR, L3_DISCARDS, TRAP_TO_CPU),
MLXSW_SP_RXL_EXCEPTION(TTLERROR, ROUTER_EXP, TRAP_TO_CPU), MLXSW_SP_RXL_EXCEPTION(TTLERROR, L3_DISCARDS, TRAP_TO_CPU),
MLXSW_SP_RXL_EXCEPTION(RPF, RPF, TRAP_TO_CPU), MLXSW_SP_RXL_EXCEPTION(RPF, L3_DISCARDS, TRAP_TO_CPU),
MLXSW_SP_RXL_EXCEPTION(RTR_INGRESS1, REMOTE_ROUTE, TRAP_TO_CPU), MLXSW_SP_RXL_EXCEPTION(RTR_INGRESS1, L3_DISCARDS, TRAP_TO_CPU),
MLXSW_SP_RXL_EXCEPTION(HOST_MISS_IPV4, HOST_MISS, TRAP_TO_CPU), MLXSW_SP_RXL_EXCEPTION(HOST_MISS_IPV4, L3_DISCARDS, TRAP_TO_CPU),
MLXSW_SP_RXL_EXCEPTION(HOST_MISS_IPV6, HOST_MISS, TRAP_TO_CPU), MLXSW_SP_RXL_EXCEPTION(HOST_MISS_IPV6, L3_DISCARDS, TRAP_TO_CPU),
MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER3, REMOTE_ROUTE, MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER3, L3_DISCARDS,
TRAP_EXCEPTION_TO_CPU), TRAP_EXCEPTION_TO_CPU),
MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER_LPM4, ROUTER_EXP, MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER_LPM4, L3_DISCARDS,
TRAP_EXCEPTION_TO_CPU), TRAP_EXCEPTION_TO_CPU),
MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER_LPM6, ROUTER_EXP, MLXSW_SP_RXL_EXCEPTION(DISCARD_ROUTER_LPM6, L3_DISCARDS,
TRAP_EXCEPTION_TO_CPU), TRAP_EXCEPTION_TO_CPU),
MLXSW_SP_RXL_DISCARD(ROUTER_IRIF_EN, L3_DISCARDS), MLXSW_SP_RXL_DISCARD(ROUTER_IRIF_EN, L3_DISCARDS),
MLXSW_SP_RXL_DISCARD(ROUTER_ERIF_EN, L3_DISCARDS), MLXSW_SP_RXL_DISCARD(ROUTER_ERIF_EN, L3_DISCARDS),
MLXSW_SP_RXL_DISCARD(NON_ROUTABLE, L3_DISCARDS), MLXSW_SP_RXL_DISCARD(NON_ROUTABLE, L3_DISCARDS),
MLXSW_SP_RXL_EXCEPTION(DECAP_ECN0, ROUTER_EXP, TRAP_EXCEPTION_TO_CPU), MLXSW_SP_RXL_EXCEPTION(DECAP_ECN0, TUNNEL_DISCARDS,
MLXSW_SP_RXL_EXCEPTION(IPIP_DECAP_ERROR, ROUTER_EXP, TRAP_EXCEPTION_TO_CPU),
MLXSW_SP_RXL_EXCEPTION(IPIP_DECAP_ERROR, TUNNEL_DISCARDS,
TRAP_EXCEPTION_TO_CPU), TRAP_EXCEPTION_TO_CPU),
MLXSW_SP_RXL_EXCEPTION(DISCARD_DEC_PKT, TUNNEL_DISCARDS, MLXSW_SP_RXL_EXCEPTION(DISCARD_DEC_PKT, TUNNEL_DISCARDS,
TRAP_EXCEPTION_TO_CPU), TRAP_EXCEPTION_TO_CPU),
@ -292,23 +308,30 @@ static const u16 mlxsw_sp_listener_devlink_map[] = {
DEVLINK_TRAP_GENERIC_ID_EGRESS_FLOW_ACTION_DROP, DEVLINK_TRAP_GENERIC_ID_EGRESS_FLOW_ACTION_DROP,
}; };
#define MLXSW_SP_DISCARD_POLICER_ID (MLXSW_REG_HTGT_TRAP_GROUP_MAX + 1) #define MLXSW_SP_THIN_POLICER_ID (MLXSW_REG_HTGT_TRAP_GROUP_MAX + 1)
#define MLXSW_SP_THIN_POLICER_ID (MLXSW_SP_DISCARD_POLICER_ID + 1)
static struct mlxsw_sp_trap_policer_item *
mlxsw_sp_trap_policer_item_lookup(struct mlxsw_sp *mlxsw_sp, u32 id)
{
struct mlxsw_sp_trap_policer_item *policer_item;
struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
list_for_each_entry(policer_item, &trap->policer_item_list, list) {
if (policer_item->id == id)
return policer_item;
}
return NULL;
}
static int mlxsw_sp_trap_cpu_policers_set(struct mlxsw_sp *mlxsw_sp) static int mlxsw_sp_trap_cpu_policers_set(struct mlxsw_sp *mlxsw_sp)
{ {
char qpcr_pl[MLXSW_REG_QPCR_LEN]; char qpcr_pl[MLXSW_REG_QPCR_LEN];
int err;
mlxsw_reg_qpcr_pack(qpcr_pl, MLXSW_SP_DISCARD_POLICER_ID,
MLXSW_REG_QPCR_IR_UNITS_M, false, 10 * 1024, 7);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
if (err)
return err;
/* The purpose of "thin" policer is to drop as many packets /* The purpose of "thin" policer is to drop as many packets
* as possible. The dummy group is using it. * as possible. The dummy group is using it.
*/ */
__set_bit(MLXSW_SP_THIN_POLICER_ID, mlxsw_sp->trap->policers_usage);
mlxsw_reg_qpcr_pack(qpcr_pl, MLXSW_SP_THIN_POLICER_ID, mlxsw_reg_qpcr_pack(qpcr_pl, MLXSW_SP_THIN_POLICER_ID,
MLXSW_REG_QPCR_IR_UNITS_M, false, 1, 4); MLXSW_REG_QPCR_IR_UNITS_M, false, 1, 4);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
@ -323,6 +346,74 @@ static int mlxsw_sp_trap_dummy_group_init(struct mlxsw_sp *mlxsw_sp)
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl);
} }
static int mlxsw_sp_trap_policers_init(struct mlxsw_sp *mlxsw_sp)
{
struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
u64 free_policers = 0;
u32 last_id = 0;
int err, i;
for_each_clear_bit(i, trap->policers_usage, trap->max_policers)
free_policers++;
if (ARRAY_SIZE(mlxsw_sp_trap_policers_arr) > free_policers) {
dev_err(mlxsw_sp->bus_info->dev, "Exceeded number of supported packet trap policers\n");
return -ENOBUFS;
}
trap->policers_arr = kcalloc(free_policers,
sizeof(struct devlink_trap_policer),
GFP_KERNEL);
if (!trap->policers_arr)
return -ENOMEM;
trap->policers_count = free_policers;
for (i = 0; i < free_policers; i++) {
const struct devlink_trap_policer *policer;
if (i < ARRAY_SIZE(mlxsw_sp_trap_policers_arr)) {
policer = &mlxsw_sp_trap_policers_arr[i];
trap->policers_arr[i] = *policer;
last_id = policer->id;
} else {
/* Use parameters set for first policer and override
* relevant ones.
*/
policer = &mlxsw_sp_trap_policers_arr[0];
trap->policers_arr[i] = *policer;
trap->policers_arr[i].id = ++last_id;
trap->policers_arr[i].init_rate = 1;
trap->policers_arr[i].init_burst = 16;
}
}
INIT_LIST_HEAD(&trap->policer_item_list);
err = devlink_trap_policers_register(devlink, trap->policers_arr,
trap->policers_count);
if (err)
goto err_trap_policers_register;
return 0;
err_trap_policers_register:
kfree(trap->policers_arr);
return err;
}
static void mlxsw_sp_trap_policers_fini(struct mlxsw_sp *mlxsw_sp)
{
struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
devlink_trap_policers_unregister(devlink, trap->policers_arr,
trap->policers_count);
WARN_ON(!list_empty(&trap->policer_item_list));
kfree(trap->policers_arr);
}
int mlxsw_sp_devlink_traps_init(struct mlxsw_sp *mlxsw_sp) int mlxsw_sp_devlink_traps_init(struct mlxsw_sp *mlxsw_sp)
{ {
size_t groups_count = ARRAY_SIZE(mlxsw_sp_trap_groups_arr); size_t groups_count = ARRAY_SIZE(mlxsw_sp_trap_groups_arr);
@ -341,10 +432,14 @@ int mlxsw_sp_devlink_traps_init(struct mlxsw_sp *mlxsw_sp)
ARRAY_SIZE(mlxsw_sp_listeners_arr))) ARRAY_SIZE(mlxsw_sp_listeners_arr)))
return -EINVAL; return -EINVAL;
err = mlxsw_sp_trap_policers_init(mlxsw_sp);
if (err)
return err;
err = devlink_trap_groups_register(devlink, mlxsw_sp_trap_groups_arr, err = devlink_trap_groups_register(devlink, mlxsw_sp_trap_groups_arr,
groups_count); groups_count);
if (err) if (err)
return err; goto err_trap_groups_register;
err = devlink_traps_register(devlink, mlxsw_sp_traps_arr, err = devlink_traps_register(devlink, mlxsw_sp_traps_arr,
ARRAY_SIZE(mlxsw_sp_traps_arr), mlxsw_sp); ARRAY_SIZE(mlxsw_sp_traps_arr), mlxsw_sp);
@ -356,6 +451,8 @@ int mlxsw_sp_devlink_traps_init(struct mlxsw_sp *mlxsw_sp)
err_traps_register: err_traps_register:
devlink_trap_groups_unregister(devlink, mlxsw_sp_trap_groups_arr, devlink_trap_groups_unregister(devlink, mlxsw_sp_trap_groups_arr,
groups_count); groups_count);
err_trap_groups_register:
mlxsw_sp_trap_policers_fini(mlxsw_sp);
return err; return err;
} }
@ -368,6 +465,7 @@ void mlxsw_sp_devlink_traps_fini(struct mlxsw_sp *mlxsw_sp)
ARRAY_SIZE(mlxsw_sp_traps_arr)); ARRAY_SIZE(mlxsw_sp_traps_arr));
devlink_trap_groups_unregister(devlink, mlxsw_sp_trap_groups_arr, devlink_trap_groups_unregister(devlink, mlxsw_sp_trap_groups_arr,
groups_count); groups_count);
mlxsw_sp_trap_policers_fini(mlxsw_sp);
} }
int mlxsw_sp_trap_init(struct mlxsw_core *mlxsw_core, int mlxsw_sp_trap_init(struct mlxsw_core *mlxsw_core,
@ -439,35 +537,34 @@ int mlxsw_sp_trap_action_set(struct mlxsw_core *mlxsw_core,
return 0; return 0;
} }
int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core, static int
const struct devlink_trap_group *group) __mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group,
u32 policer_id)
{ {
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
u16 hw_policer_id = MLXSW_REG_HTGT_INVALID_POLICER;
char htgt_pl[MLXSW_REG_HTGT_LEN]; char htgt_pl[MLXSW_REG_HTGT_LEN];
u8 priority, tc, group_id; u8 priority, tc, group_id;
u16 policer_id;
switch (group->id) { switch (group->id) {
case DEVLINK_TRAP_GROUP_GENERIC_ID_L2_DROPS: case DEVLINK_TRAP_GROUP_GENERIC_ID_L2_DROPS:
group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L2_DISCARDS; group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L2_DISCARDS;
policer_id = MLXSW_SP_DISCARD_POLICER_ID;
priority = 0; priority = 0;
tc = 1; tc = 1;
break; break;
case DEVLINK_TRAP_GROUP_GENERIC_ID_L3_DROPS: case DEVLINK_TRAP_GROUP_GENERIC_ID_L3_DROPS:
group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L3_DISCARDS; group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_L3_DISCARDS;
policer_id = MLXSW_SP_DISCARD_POLICER_ID;
priority = 0; priority = 0;
tc = 1; tc = 1;
break; break;
case DEVLINK_TRAP_GROUP_GENERIC_ID_TUNNEL_DROPS: case DEVLINK_TRAP_GROUP_GENERIC_ID_TUNNEL_DROPS:
group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_TUNNEL_DISCARDS; group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_TUNNEL_DISCARDS;
policer_id = MLXSW_SP_DISCARD_POLICER_ID;
priority = 0; priority = 0;
tc = 1; tc = 1;
break; break;
case DEVLINK_TRAP_GROUP_GENERIC_ID_ACL_DROPS: case DEVLINK_TRAP_GROUP_GENERIC_ID_ACL_DROPS:
group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_ACL_DISCARDS; group_id = MLXSW_REG_HTGT_TRAP_GROUP_SP_ACL_DISCARDS;
policer_id = MLXSW_SP_DISCARD_POLICER_ID;
priority = 0; priority = 0;
tc = 1; tc = 1;
break; break;
@ -475,6 +572,179 @@ int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
return -EINVAL; return -EINVAL;
} }
mlxsw_reg_htgt_pack(htgt_pl, group_id, policer_id, priority, tc); if (policer_id) {
struct mlxsw_sp_trap_policer_item *policer_item;
policer_item = mlxsw_sp_trap_policer_item_lookup(mlxsw_sp,
policer_id);
if (WARN_ON(!policer_item))
return -EINVAL;
hw_policer_id = policer_item->hw_id;
}
mlxsw_reg_htgt_pack(htgt_pl, group_id, hw_policer_id, priority, tc);
return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
} }
int mlxsw_sp_trap_group_init(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group)
{
return __mlxsw_sp_trap_group_init(mlxsw_core, group,
group->init_policer_id);
}
int mlxsw_sp_trap_group_set(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_group *group,
const struct devlink_trap_policer *policer)
{
u32 policer_id = policer ? policer->id : 0;
return __mlxsw_sp_trap_group_init(mlxsw_core, group, policer_id);
}
static struct mlxsw_sp_trap_policer_item *
mlxsw_sp_trap_policer_item_init(struct mlxsw_sp *mlxsw_sp, u32 id)
{
struct mlxsw_sp_trap_policer_item *policer_item;
struct mlxsw_sp_trap *trap = mlxsw_sp->trap;
u16 hw_id;
/* We should be able to allocate a policer because the number of
* policers we registered with devlink is in according with the number
* of available policers.
*/
hw_id = find_first_zero_bit(trap->policers_usage, trap->max_policers);
if (WARN_ON(hw_id == trap->max_policers))
return ERR_PTR(-ENOBUFS);
policer_item = kzalloc(sizeof(*policer_item), GFP_KERNEL);
if (!policer_item)
return ERR_PTR(-ENOMEM);
__set_bit(hw_id, trap->policers_usage);
policer_item->hw_id = hw_id;
policer_item->id = id;
list_add_tail(&policer_item->list, &trap->policer_item_list);
return policer_item;
}
static void
mlxsw_sp_trap_policer_item_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_trap_policer_item *policer_item)
{
list_del(&policer_item->list);
__clear_bit(policer_item->hw_id, mlxsw_sp->trap->policers_usage);
kfree(policer_item);
}
static int mlxsw_sp_trap_policer_bs(u64 burst, u8 *p_burst_size,
struct netlink_ext_ack *extack)
{
int bs = fls64(burst) - 1;
if (burst != (1 << bs)) {
NL_SET_ERR_MSG_MOD(extack, "Policer burst size is not power of two");
return -EINVAL;
}
*p_burst_size = bs;
return 0;
}
static int __mlxsw_sp_trap_policer_set(struct mlxsw_sp *mlxsw_sp, u16 hw_id,
u64 rate, u64 burst, bool clear_counter,
struct netlink_ext_ack *extack)
{
char qpcr_pl[MLXSW_REG_QPCR_LEN];
u8 burst_size;
int err;
err = mlxsw_sp_trap_policer_bs(burst, &burst_size, extack);
if (err)
return err;
mlxsw_reg_qpcr_pack(qpcr_pl, hw_id, MLXSW_REG_QPCR_IR_UNITS_M, false,
rate, burst_size);
mlxsw_reg_qpcr_clear_counter_set(qpcr_pl, clear_counter);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
}
int mlxsw_sp_trap_policer_init(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
struct mlxsw_sp_trap_policer_item *policer_item;
int err;
policer_item = mlxsw_sp_trap_policer_item_init(mlxsw_sp, policer->id);
if (IS_ERR(policer_item))
return PTR_ERR(policer_item);
err = __mlxsw_sp_trap_policer_set(mlxsw_sp, policer_item->hw_id,
policer->init_rate,
policer->init_burst, true, NULL);
if (err)
goto err_trap_policer_set;
return 0;
err_trap_policer_set:
mlxsw_sp_trap_policer_item_fini(mlxsw_sp, policer_item);
return err;
}
void mlxsw_sp_trap_policer_fini(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
struct mlxsw_sp_trap_policer_item *policer_item;
policer_item = mlxsw_sp_trap_policer_item_lookup(mlxsw_sp, policer->id);
if (WARN_ON(!policer_item))
return;
mlxsw_sp_trap_policer_item_fini(mlxsw_sp, policer_item);
}
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)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
struct mlxsw_sp_trap_policer_item *policer_item;
policer_item = mlxsw_sp_trap_policer_item_lookup(mlxsw_sp, policer->id);
if (WARN_ON(!policer_item))
return -EINVAL;
return __mlxsw_sp_trap_policer_set(mlxsw_sp, policer_item->hw_id,
rate, burst, false, extack);
}
int
mlxsw_sp_trap_policer_counter_get(struct mlxsw_core *mlxsw_core,
const struct devlink_trap_policer *policer,
u64 *p_drops)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core);
struct mlxsw_sp_trap_policer_item *policer_item;
char qpcr_pl[MLXSW_REG_QPCR_LEN];
int err;
policer_item = mlxsw_sp_trap_policer_item_lookup(mlxsw_sp, policer->id);
if (WARN_ON(!policer_item))
return -EINVAL;
mlxsw_reg_qpcr_pack(qpcr_pl, policer_item->hw_id,
MLXSW_REG_QPCR_IR_UNITS_M, false, 0, 0);
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(qpcr), qpcr_pl);
if (err)
return err;
*p_drops = mlxsw_reg_qpcr_violate_count_get(qpcr_pl);
return 0;
}

View File

@ -0,0 +1,24 @@
/* 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

View File

@ -215,6 +215,15 @@ static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
&nsim_dev->fail_reload); &nsim_dev->fail_reload);
debugfs_create_file("trap_flow_action_cookie", 0600, nsim_dev->ddir, debugfs_create_file("trap_flow_action_cookie", 0600, nsim_dev->ddir,
nsim_dev, &nsim_dev_trap_fa_cookie_fops); 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; return 0;
} }
@ -392,6 +401,7 @@ struct nsim_trap_item {
struct nsim_trap_data { struct nsim_trap_data {
struct delayed_work trap_report_dw; struct delayed_work trap_report_dw;
struct nsim_trap_item *trap_items_arr; struct nsim_trap_item *trap_items_arr;
u64 *trap_policers_cnt_arr;
struct nsim_dev *nsim_dev; struct nsim_dev *nsim_dev;
spinlock_t trap_lock; /* Protects trap_items_arr */ spinlock_t trap_lock; /* Protects trap_items_arr */
}; };
@ -426,11 +436,29 @@ enum {
DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
NSIM_TRAP_METADATA) 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[] = { static const struct devlink_trap_group nsim_trap_groups_arr[] = {
DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS), DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0),
DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS), DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 1),
DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS), DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 2),
DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS), DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 3),
}; };
static const struct devlink_trap nsim_traps_arr[] = { 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) 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_dev *nsim_dev = devlink_priv(devlink);
struct nsim_trap_data *nsim_trap_data; struct nsim_trap_data *nsim_trap_data;
int err; int err;
@ -584,6 +613,14 @@ static int nsim_dev_traps_init(struct devlink *devlink)
goto err_trap_data_free; 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 /* 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 * traps. The value is written by user and read in delayed work when
* iterating over all the traps. * 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_trap_data->nsim_dev = nsim_dev;
nsim_dev->trap_data = nsim_trap_data; 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, err = devlink_trap_groups_register(devlink, nsim_trap_groups_arr,
ARRAY_SIZE(nsim_trap_groups_arr)); ARRAY_SIZE(nsim_trap_groups_arr));
if (err) if (err)
goto err_trap_items_free; goto err_trap_policers_unregister;
err = devlink_traps_register(devlink, nsim_traps_arr, err = devlink_traps_register(devlink, nsim_traps_arr,
ARRAY_SIZE(nsim_traps_arr), NULL); ARRAY_SIZE(nsim_traps_arr), NULL);
@ -612,6 +654,11 @@ static int nsim_dev_traps_init(struct devlink *devlink)
err_trap_groups_unregister: err_trap_groups_unregister:
devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr, devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr,
ARRAY_SIZE(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: err_trap_items_free:
kfree(nsim_trap_data->trap_items_arr); kfree(nsim_trap_data->trap_items_arr);
err_trap_data_free: err_trap_data_free:
@ -628,6 +675,9 @@ static void nsim_dev_traps_exit(struct devlink *devlink)
ARRAY_SIZE(nsim_traps_arr)); ARRAY_SIZE(nsim_traps_arr));
devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr, devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr,
ARRAY_SIZE(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->trap_items_arr);
kfree(nsim_dev->trap_data); kfree(nsim_dev->trap_data);
} }
@ -766,6 +816,53 @@ nsim_dev_devlink_trap_action_set(struct devlink *devlink,
return 0; 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 = { static const struct devlink_ops nsim_dev_devlink_ops = {
.reload_down = nsim_dev_reload_down, .reload_down = nsim_dev_reload_down,
.reload_up = nsim_dev_reload_up, .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, .flash_update = nsim_dev_flash_update,
.trap_init = nsim_dev_devlink_trap_init, .trap_init = nsim_dev_devlink_trap_init,
.trap_action_set = nsim_dev_devlink_trap_action_set, .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 #define NSIM_DEV_MAX_MACS_DEFAULT 32

View File

@ -180,6 +180,9 @@ struct nsim_dev {
struct nsim_dev_health health; struct nsim_dev_health health;
struct flow_action_cookie *fa_cookie; struct flow_action_cookie *fa_cookie;
spinlock_t fa_cookie_lock; /* protects 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) static inline struct net *nsim_dev_net(struct nsim_dev *nsim_dev)

View File

@ -35,6 +35,7 @@ struct devlink {
struct devlink_dpipe_headers *dpipe_headers; struct devlink_dpipe_headers *dpipe_headers;
struct list_head trap_list; struct list_head trap_list;
struct list_head trap_group_list; struct list_head trap_group_list;
struct list_head trap_policer_list;
const struct devlink_ops *ops; const struct devlink_ops *ops;
struct xarray snapshot_ids; struct xarray snapshot_ids;
struct device *dev; struct device *dev;
@ -545,11 +546,35 @@ struct devlink_health_reporter_ops {
struct netlink_ext_ack *extack); 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. * struct devlink_trap_group - Immutable packet trap group attributes.
* @name: Trap group name. * @name: Trap group name.
* @id: Trap group identifier. * @id: Trap group identifier.
* @generic: Whether the trap group is generic or not. * @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 * Describes immutable attributes of packet trap groups that drivers register
* with devlink. * with devlink.
@ -558,6 +583,7 @@ struct devlink_trap_group {
const char *name; const char *name;
u16 id; u16 id;
bool generic; bool generic;
u32 init_policer_id;
}; };
#define DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT BIT(0) #define DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT BIT(0)
@ -735,11 +761,24 @@ enum devlink_trap_group_generic_id {
.metadata_cap = _metadata_cap, \ .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, \ .name = DEVLINK_TRAP_GROUP_GENERIC_NAME_##_id, \
.id = DEVLINK_TRAP_GROUP_GENERIC_ID_##_id, \ .id = DEVLINK_TRAP_GROUP_GENERIC_ID_##_id, \
.generic = true, \ .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 { struct devlink_ops {
@ -838,6 +877,47 @@ struct devlink_ops {
*/ */
int (*trap_group_init)(struct devlink *devlink, int (*trap_group_init)(struct devlink *devlink,
const struct devlink_trap_group *group); 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) 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, void devlink_trap_groups_unregister(struct devlink *devlink,
const struct devlink_trap_group *groups, const struct devlink_trap_group *groups,
size_t groups_count); 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) #if IS_ENABLED(CONFIG_NET_DEVLINK)

View File

@ -117,6 +117,11 @@ enum devlink_command {
DEVLINK_CMD_TRAP_GROUP_NEW, DEVLINK_CMD_TRAP_GROUP_NEW,
DEVLINK_CMD_TRAP_GROUP_DEL, 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 */ /* add new commands above here */
__DEVLINK_CMD_MAX, __DEVLINK_CMD_MAX,
DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1 DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
@ -217,6 +222,7 @@ enum devlink_param_reset_dev_on_drv_probe_value {
enum { enum {
DEVLINK_ATTR_STATS_RX_PACKETS, /* u64 */ DEVLINK_ATTR_STATS_RX_PACKETS, /* u64 */
DEVLINK_ATTR_STATS_RX_BYTES, /* 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 = __DEVLINK_ATTR_STATS_MAX - 1 DEVLINK_ATTR_STATS_MAX = __DEVLINK_ATTR_STATS_MAX - 1
@ -431,6 +437,11 @@ enum devlink_attr {
DEVLINK_ATTR_NETNS_ID, /* u32 */ DEVLINK_ATTR_NETNS_ID, /* u32 */
DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP, /* u8 */ 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 */ /* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX, __DEVLINK_ATTR_MAX,

View File

@ -5719,9 +5719,27 @@ struct devlink_stats {
struct u64_stats_sync syncp; struct u64_stats_sync syncp;
}; };
/**
* struct devlink_trap_policer_item - Packet trap policer attributes.
* @policer: Immutable packet trap policer attributes.
* @rate: Rate in packets / sec.
* @burst: Burst size in packets.
* @list: trap_policer_list member.
*
* Describes packet trap policer attributes. Created by devlink during trap
* policer registration.
*/
struct devlink_trap_policer_item {
const struct devlink_trap_policer *policer;
u64 rate;
u64 burst;
struct list_head list;
};
/** /**
* struct devlink_trap_group_item - Packet trap group attributes. * struct devlink_trap_group_item - Packet trap group attributes.
* @group: Immutable packet trap group attributes. * @group: Immutable packet trap group attributes.
* @policer_item: Associated policer item. Can be NULL.
* @list: trap_group_list member. * @list: trap_group_list member.
* @stats: Trap group statistics. * @stats: Trap group statistics.
* *
@ -5730,6 +5748,7 @@ struct devlink_stats {
*/ */
struct devlink_trap_group_item { struct devlink_trap_group_item {
const struct devlink_trap_group *group; const struct devlink_trap_group *group;
struct devlink_trap_policer_item *policer_item;
struct list_head list; struct list_head list;
struct devlink_stats __percpu *stats; struct devlink_stats __percpu *stats;
}; };
@ -5755,6 +5774,19 @@ struct devlink_trap_item {
void *priv; void *priv;
}; };
static struct devlink_trap_policer_item *
devlink_trap_policer_item_lookup(struct devlink *devlink, u32 id)
{
struct devlink_trap_policer_item *policer_item;
list_for_each_entry(policer_item, &devlink->trap_policer_list, list) {
if (policer_item->policer->id == id)
return policer_item;
}
return NULL;
}
static struct devlink_trap_item * static struct devlink_trap_item *
devlink_trap_item_lookup(struct devlink *devlink, const char *name) devlink_trap_item_lookup(struct devlink *devlink, const char *name)
{ {
@ -6131,6 +6163,11 @@ devlink_nl_trap_group_fill(struct sk_buff *msg, struct devlink *devlink,
nla_put_flag(msg, DEVLINK_ATTR_TRAP_GENERIC)) nla_put_flag(msg, DEVLINK_ATTR_TRAP_GENERIC))
goto nla_put_failure; goto nla_put_failure;
if (group_item->policer_item &&
nla_put_u32(msg, DEVLINK_ATTR_TRAP_POLICER_ID,
group_item->policer_item->policer->id))
goto nla_put_failure;
err = devlink_trap_stats_put(msg, group_item->stats); err = devlink_trap_stats_put(msg, group_item->stats);
if (err) if (err)
goto nla_put_failure; goto nla_put_failure;
@ -6246,7 +6283,7 @@ __devlink_trap_group_action_set(struct devlink *devlink,
static int static int
devlink_trap_group_action_set(struct devlink *devlink, devlink_trap_group_action_set(struct devlink *devlink,
struct devlink_trap_group_item *group_item, struct devlink_trap_group_item *group_item,
struct genl_info *info) struct genl_info *info, bool *p_modified)
{ {
enum devlink_trap_action trap_action; enum devlink_trap_action trap_action;
int err; int err;
@ -6265,6 +6302,47 @@ devlink_trap_group_action_set(struct devlink *devlink,
if (err) if (err)
return err; return err;
*p_modified = true;
return 0;
}
static int devlink_trap_group_set(struct devlink *devlink,
struct devlink_trap_group_item *group_item,
struct genl_info *info)
{
struct devlink_trap_policer_item *policer_item;
struct netlink_ext_ack *extack = info->extack;
const struct devlink_trap_policer *policer;
struct nlattr **attrs = info->attrs;
int err;
if (!attrs[DEVLINK_ATTR_TRAP_POLICER_ID])
return 0;
if (!devlink->ops->trap_group_set)
return -EOPNOTSUPP;
policer_item = group_item->policer_item;
if (attrs[DEVLINK_ATTR_TRAP_POLICER_ID]) {
u32 policer_id;
policer_id = nla_get_u32(attrs[DEVLINK_ATTR_TRAP_POLICER_ID]);
policer_item = devlink_trap_policer_item_lookup(devlink,
policer_id);
if (policer_id && !policer_item) {
NL_SET_ERR_MSG_MOD(extack, "Device did not register this trap policer");
return -ENOENT;
}
}
policer = policer_item ? policer_item->policer : NULL;
err = devlink->ops->trap_group_set(devlink, group_item->group, policer);
if (err)
return err;
group_item->policer_item = policer_item;
return 0; return 0;
} }
@ -6274,6 +6352,7 @@ static int devlink_nl_cmd_trap_group_set_doit(struct sk_buff *skb,
struct netlink_ext_ack *extack = info->extack; struct netlink_ext_ack *extack = info->extack;
struct devlink *devlink = info->user_ptr[0]; struct devlink *devlink = info->user_ptr[0];
struct devlink_trap_group_item *group_item; struct devlink_trap_group_item *group_item;
bool modified = false;
int err; int err;
if (list_empty(&devlink->trap_group_list)) if (list_empty(&devlink->trap_group_list))
@ -6285,14 +6364,262 @@ static int devlink_nl_cmd_trap_group_set_doit(struct sk_buff *skb,
return -ENOENT; return -ENOENT;
} }
err = devlink_trap_group_action_set(devlink, group_item, info); err = devlink_trap_group_action_set(devlink, group_item, info,
&modified);
if (err) if (err)
return err; return err;
err = devlink_trap_group_set(devlink, group_item, info);
if (err)
goto err_trap_group_set;
return 0;
err_trap_group_set:
if (modified)
NL_SET_ERR_MSG_MOD(extack, "Trap group set failed, but some changes were committed already");
return err;
}
static struct devlink_trap_policer_item *
devlink_trap_policer_item_get_from_info(struct devlink *devlink,
struct genl_info *info)
{
u32 id;
if (!info->attrs[DEVLINK_ATTR_TRAP_POLICER_ID])
return NULL;
id = nla_get_u32(info->attrs[DEVLINK_ATTR_TRAP_POLICER_ID]);
return devlink_trap_policer_item_lookup(devlink, id);
}
static int
devlink_trap_policer_stats_put(struct sk_buff *msg, struct devlink *devlink,
const struct devlink_trap_policer *policer)
{
struct nlattr *attr;
u64 drops;
int err;
if (!devlink->ops->trap_policer_counter_get)
return 0;
err = devlink->ops->trap_policer_counter_get(devlink, policer, &drops);
if (err)
return err;
attr = nla_nest_start(msg, DEVLINK_ATTR_STATS);
if (!attr)
return -EMSGSIZE;
if (nla_put_u64_64bit(msg, DEVLINK_ATTR_STATS_RX_DROPPED, drops,
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_policer_fill(struct sk_buff *msg, struct devlink *devlink,
const struct devlink_trap_policer_item *policer_item,
enum devlink_command cmd, u32 portid, u32 seq,
int flags)
{
void *hdr;
int err;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr)
return -EMSGSIZE;
if (devlink_nl_put_handle(msg, devlink))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_TRAP_POLICER_ID,
policer_item->policer->id))
goto nla_put_failure;
if (nla_put_u64_64bit(msg, DEVLINK_ATTR_TRAP_POLICER_RATE,
policer_item->rate, DEVLINK_ATTR_PAD))
goto nla_put_failure;
if (nla_put_u64_64bit(msg, DEVLINK_ATTR_TRAP_POLICER_BURST,
policer_item->burst, DEVLINK_ATTR_PAD))
goto nla_put_failure;
err = devlink_trap_policer_stats_put(msg, devlink,
policer_item->policer);
if (err)
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
static int devlink_nl_cmd_trap_policer_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink_trap_policer_item *policer_item;
struct netlink_ext_ack *extack = info->extack;
struct devlink *devlink = info->user_ptr[0];
struct sk_buff *msg;
int err;
if (list_empty(&devlink->trap_policer_list))
return -EOPNOTSUPP;
policer_item = devlink_trap_policer_item_get_from_info(devlink, info);
if (!policer_item) {
NL_SET_ERR_MSG_MOD(extack, "Device did not register this trap policer");
return -ENOENT;
}
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
err = devlink_nl_trap_policer_fill(msg, devlink, policer_item,
DEVLINK_CMD_TRAP_POLICER_NEW,
info->snd_portid, info->snd_seq, 0);
if (err)
goto err_trap_policer_fill;
return genlmsg_reply(msg, info);
err_trap_policer_fill:
nlmsg_free(msg);
return err;
}
static int devlink_nl_cmd_trap_policer_get_dumpit(struct sk_buff *msg,
struct netlink_callback *cb)
{
enum devlink_command cmd = DEVLINK_CMD_TRAP_POLICER_NEW;
struct devlink_trap_policer_item *policer_item;
u32 portid = NETLINK_CB(cb->skb).portid;
struct devlink *devlink;
int start = cb->args[0];
int idx = 0;
int err;
mutex_lock(&devlink_mutex);
list_for_each_entry(devlink, &devlink_list, list) {
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
continue;
mutex_lock(&devlink->lock);
list_for_each_entry(policer_item, &devlink->trap_policer_list,
list) {
if (idx < start) {
idx++;
continue;
}
err = devlink_nl_trap_policer_fill(msg, devlink,
policer_item, cmd,
portid,
cb->nlh->nlmsg_seq,
NLM_F_MULTI);
if (err) {
mutex_unlock(&devlink->lock);
goto out;
}
idx++;
}
mutex_unlock(&devlink->lock);
}
out:
mutex_unlock(&devlink_mutex);
cb->args[0] = idx;
return msg->len;
}
static int
devlink_trap_policer_set(struct devlink *devlink,
struct devlink_trap_policer_item *policer_item,
struct genl_info *info)
{
struct netlink_ext_ack *extack = info->extack;
struct nlattr **attrs = info->attrs;
u64 rate, burst;
int err;
rate = policer_item->rate;
burst = policer_item->burst;
if (attrs[DEVLINK_ATTR_TRAP_POLICER_RATE])
rate = nla_get_u64(attrs[DEVLINK_ATTR_TRAP_POLICER_RATE]);
if (attrs[DEVLINK_ATTR_TRAP_POLICER_BURST])
burst = nla_get_u64(attrs[DEVLINK_ATTR_TRAP_POLICER_BURST]);
if (rate < policer_item->policer->min_rate) {
NL_SET_ERR_MSG_MOD(extack, "Policer rate lower than limit");
return -EINVAL;
}
if (rate > policer_item->policer->max_rate) {
NL_SET_ERR_MSG_MOD(extack, "Policer rate higher than limit");
return -EINVAL;
}
if (burst < policer_item->policer->min_burst) {
NL_SET_ERR_MSG_MOD(extack, "Policer burst size lower than limit");
return -EINVAL;
}
if (burst > policer_item->policer->max_burst) {
NL_SET_ERR_MSG_MOD(extack, "Policer burst size higher than limit");
return -EINVAL;
}
err = devlink->ops->trap_policer_set(devlink, policer_item->policer,
rate, burst, info->extack);
if (err)
return err;
policer_item->rate = rate;
policer_item->burst = burst;
return 0; return 0;
} }
static int devlink_nl_cmd_trap_policer_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink_trap_policer_item *policer_item;
struct netlink_ext_ack *extack = info->extack;
struct devlink *devlink = info->user_ptr[0];
if (list_empty(&devlink->trap_policer_list))
return -EOPNOTSUPP;
if (!devlink->ops->trap_policer_set)
return -EOPNOTSUPP;
policer_item = devlink_trap_policer_item_get_from_info(devlink, info);
if (!policer_item) {
NL_SET_ERR_MSG_MOD(extack, "Device did not register this trap policer");
return -ENOENT;
}
return devlink_trap_policer_set(devlink, policer_item, info);
}
static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
[DEVLINK_ATTR_UNSPEC] = { .strict_start_type =
DEVLINK_ATTR_TRAP_POLICER_ID },
[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING }, [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING }, [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },
[DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 }, [DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32 },
@ -6331,6 +6658,9 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
[DEVLINK_ATTR_NETNS_FD] = { .type = NLA_U32 }, [DEVLINK_ATTR_NETNS_FD] = { .type = NLA_U32 },
[DEVLINK_ATTR_NETNS_ID] = { .type = NLA_U32 }, [DEVLINK_ATTR_NETNS_ID] = { .type = NLA_U32 },
[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP] = { .type = NLA_U8 }, [DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP] = { .type = NLA_U8 },
[DEVLINK_ATTR_TRAP_POLICER_ID] = { .type = NLA_U32 },
[DEVLINK_ATTR_TRAP_POLICER_RATE] = { .type = NLA_U64 },
[DEVLINK_ATTR_TRAP_POLICER_BURST] = { .type = NLA_U64 },
}; };
static const struct genl_ops devlink_nl_ops[] = { static const struct genl_ops devlink_nl_ops[] = {
@ -6666,6 +6996,19 @@ static const struct genl_ops devlink_nl_ops[] = {
.flags = GENL_ADMIN_PERM, .flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
}, },
{
.cmd = DEVLINK_CMD_TRAP_POLICER_GET,
.doit = devlink_nl_cmd_trap_policer_get_doit,
.dumpit = devlink_nl_cmd_trap_policer_get_dumpit,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
/* can be retrieved by unprivileged users */
},
{
.cmd = DEVLINK_CMD_TRAP_POLICER_SET,
.doit = devlink_nl_cmd_trap_policer_set_doit,
.flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
},
}; };
static struct genl_family devlink_nl_family __ro_after_init = { static struct genl_family devlink_nl_family __ro_after_init = {
@ -6714,6 +7057,7 @@ struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size)
INIT_LIST_HEAD(&devlink->reporter_list); INIT_LIST_HEAD(&devlink->reporter_list);
INIT_LIST_HEAD(&devlink->trap_list); INIT_LIST_HEAD(&devlink->trap_list);
INIT_LIST_HEAD(&devlink->trap_group_list); INIT_LIST_HEAD(&devlink->trap_group_list);
INIT_LIST_HEAD(&devlink->trap_policer_list);
mutex_init(&devlink->lock); mutex_init(&devlink->lock);
mutex_init(&devlink->reporters_lock); mutex_init(&devlink->reporters_lock);
return devlink; return devlink;
@ -6798,6 +7142,7 @@ void devlink_free(struct devlink *devlink)
{ {
mutex_destroy(&devlink->reporters_lock); mutex_destroy(&devlink->reporters_lock);
mutex_destroy(&devlink->lock); mutex_destroy(&devlink->lock);
WARN_ON(!list_empty(&devlink->trap_policer_list));
WARN_ON(!list_empty(&devlink->trap_group_list)); WARN_ON(!list_empty(&devlink->trap_group_list));
WARN_ON(!list_empty(&devlink->trap_list)); WARN_ON(!list_empty(&devlink->trap_list));
WARN_ON(!list_empty(&devlink->reporter_list)); WARN_ON(!list_empty(&devlink->reporter_list));
@ -8473,6 +8818,25 @@ void *devlink_trap_ctx_priv(void *trap_ctx)
} }
EXPORT_SYMBOL_GPL(devlink_trap_ctx_priv); EXPORT_SYMBOL_GPL(devlink_trap_ctx_priv);
static int
devlink_trap_group_item_policer_link(struct devlink *devlink,
struct devlink_trap_group_item *group_item)
{
u32 policer_id = group_item->group->init_policer_id;
struct devlink_trap_policer_item *policer_item;
if (policer_id == 0)
return 0;
policer_item = devlink_trap_policer_item_lookup(devlink, policer_id);
if (WARN_ON_ONCE(!policer_item))
return -EINVAL;
group_item->policer_item = policer_item;
return 0;
}
static int static int
devlink_trap_group_register(struct devlink *devlink, devlink_trap_group_register(struct devlink *devlink,
const struct devlink_trap_group *group) const struct devlink_trap_group *group)
@ -8495,6 +8859,10 @@ devlink_trap_group_register(struct devlink *devlink,
group_item->group = group; group_item->group = group;
err = devlink_trap_group_item_policer_link(devlink, group_item);
if (err)
goto err_policer_link;
if (devlink->ops->trap_group_init) { if (devlink->ops->trap_group_init) {
err = devlink->ops->trap_group_init(devlink, group); err = devlink->ops->trap_group_init(devlink, group);
if (err) if (err)
@ -8508,6 +8876,7 @@ devlink_trap_group_register(struct devlink *devlink,
return 0; return 0;
err_group_init: err_group_init:
err_policer_link:
free_percpu(group_item->stats); free_percpu(group_item->stats);
err_stats_alloc: err_stats_alloc:
kfree(group_item); kfree(group_item);
@ -8589,6 +8958,148 @@ void devlink_trap_groups_unregister(struct devlink *devlink,
} }
EXPORT_SYMBOL_GPL(devlink_trap_groups_unregister); EXPORT_SYMBOL_GPL(devlink_trap_groups_unregister);
static void
devlink_trap_policer_notify(struct devlink *devlink,
const struct devlink_trap_policer_item *policer_item,
enum devlink_command cmd)
{
struct sk_buff *msg;
int err;
WARN_ON_ONCE(cmd != DEVLINK_CMD_TRAP_POLICER_NEW &&
cmd != DEVLINK_CMD_TRAP_POLICER_DEL);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return;
err = devlink_nl_trap_policer_fill(msg, devlink, policer_item, cmd, 0,
0, 0);
if (err) {
nlmsg_free(msg);
return;
}
genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
}
static int
devlink_trap_policer_register(struct devlink *devlink,
const struct devlink_trap_policer *policer)
{
struct devlink_trap_policer_item *policer_item;
int err;
if (devlink_trap_policer_item_lookup(devlink, policer->id))
return -EEXIST;
policer_item = kzalloc(sizeof(*policer_item), GFP_KERNEL);
if (!policer_item)
return -ENOMEM;
policer_item->policer = policer;
policer_item->rate = policer->init_rate;
policer_item->burst = policer->init_burst;
if (devlink->ops->trap_policer_init) {
err = devlink->ops->trap_policer_init(devlink, policer);
if (err)
goto err_policer_init;
}
list_add_tail(&policer_item->list, &devlink->trap_policer_list);
devlink_trap_policer_notify(devlink, policer_item,
DEVLINK_CMD_TRAP_POLICER_NEW);
return 0;
err_policer_init:
kfree(policer_item);
return err;
}
static void
devlink_trap_policer_unregister(struct devlink *devlink,
const struct devlink_trap_policer *policer)
{
struct devlink_trap_policer_item *policer_item;
policer_item = devlink_trap_policer_item_lookup(devlink, policer->id);
if (WARN_ON_ONCE(!policer_item))
return;
devlink_trap_policer_notify(devlink, policer_item,
DEVLINK_CMD_TRAP_POLICER_DEL);
list_del(&policer_item->list);
if (devlink->ops->trap_policer_fini)
devlink->ops->trap_policer_fini(devlink, policer);
kfree(policer_item);
}
/**
* devlink_trap_policers_register - Register packet trap policers with devlink.
* @devlink: devlink.
* @policers: Packet trap policers.
* @policers_count: Count of provided packet trap policers.
*
* Return: Non-zero value on failure.
*/
int
devlink_trap_policers_register(struct devlink *devlink,
const struct devlink_trap_policer *policers,
size_t policers_count)
{
int i, err;
mutex_lock(&devlink->lock);
for (i = 0; i < policers_count; i++) {
const struct devlink_trap_policer *policer = &policers[i];
if (WARN_ON(policer->id == 0 ||
policer->max_rate < policer->min_rate ||
policer->max_burst < policer->min_burst)) {
err = -EINVAL;
goto err_trap_policer_verify;
}
err = devlink_trap_policer_register(devlink, policer);
if (err)
goto err_trap_policer_register;
}
mutex_unlock(&devlink->lock);
return 0;
err_trap_policer_register:
err_trap_policer_verify:
for (i--; i >= 0; i--)
devlink_trap_policer_unregister(devlink, &policers[i]);
mutex_unlock(&devlink->lock);
return err;
}
EXPORT_SYMBOL_GPL(devlink_trap_policers_register);
/**
* devlink_trap_policers_unregister - Unregister packet trap policers from devlink.
* @devlink: devlink.
* @policers: Packet trap policers.
* @policers_count: Count of provided packet trap policers.
*/
void
devlink_trap_policers_unregister(struct devlink *devlink,
const struct devlink_trap_policer *policers,
size_t policers_count)
{
int i;
mutex_lock(&devlink->lock);
for (i = policers_count - 1; i >= 0; i--)
devlink_trap_policer_unregister(devlink, &policers[i]);
mutex_unlock(&devlink->lock);
}
EXPORT_SYMBOL_GPL(devlink_trap_policers_unregister);
static void __devlink_compat_running_version(struct devlink *devlink, static void __devlink_compat_running_version(struct devlink *devlink,
char *buf, size_t len) char *buf, size_t len)
{ {

View File

@ -0,0 +1,384 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Test devlink-trap policer functionality over mlxsw.
# +---------------------------------+
# | H1 (vrf) |
# | + $h1 |
# | | 192.0.2.1/24 |
# | | |
# | | default via 192.0.2.2 |
# +----|----------------------------+
# |
# +----|----------------------------------------------------------------------+
# | SW | |
# | + $rp1 |
# | 192.0.2.2/24 |
# | |
# | 198.51.100.2/24 |
# | + $rp2 |
# | | |
# +----|----------------------------------------------------------------------+
# |
# +----|----------------------------+
# | | default via 198.51.100.2 |
# | | |
# | | 198.51.100.1/24 |
# | + $h2 |
# | H2 (vrf) |
# +---------------------------------+
lib_dir=$(dirname $0)/../../../net/forwarding
ALL_TESTS="
rate_limits_test
burst_limits_test
rate_test
burst_test
"
NUM_NETIFS=4
source $lib_dir/tc_common.sh
source $lib_dir/lib.sh
source $lib_dir/devlink_lib.sh
h1_create()
{
simple_if_init $h1 192.0.2.1/24
mtu_set $h1 10000
ip -4 route add default vrf v$h1 nexthop via 192.0.2.2
}
h1_destroy()
{
ip -4 route del default vrf v$h1 nexthop via 192.0.2.2
mtu_restore $h1
simple_if_fini $h1 192.0.2.1/24
}
h2_create()
{
simple_if_init $h2 198.51.100.1/24
mtu_set $h2 10000
ip -4 route add default vrf v$h2 nexthop via 198.51.100.2
}
h2_destroy()
{
ip -4 route del default vrf v$h2 nexthop via 198.51.100.2
mtu_restore $h2
simple_if_fini $h2 198.51.100.1/24
}
router_create()
{
ip link set dev $rp1 up
ip link set dev $rp2 up
__addr_add_del $rp1 add 192.0.2.2/24
__addr_add_del $rp2 add 198.51.100.2/24
mtu_set $rp1 10000
mtu_set $rp2 10000
ip -4 route add blackhole 198.51.100.100
devlink trap set $DEVLINK_DEV trap blackhole_route action trap
}
router_destroy()
{
devlink trap set $DEVLINK_DEV trap blackhole_route action drop
ip -4 route del blackhole 198.51.100.100
mtu_restore $rp2
mtu_restore $rp1
__addr_add_del $rp2 del 198.51.100.2/24
__addr_add_del $rp1 del 192.0.2.2/24
ip link set dev $rp2 down
ip link set dev $rp1 down
}
setup_prepare()
{
h1=${NETIFS[p1]}
rp1=${NETIFS[p2]}
rp2=${NETIFS[p3]}
h2=${NETIFS[p4]}
rp1_mac=$(mac_get $rp1)
vrf_prepare
h1_create
h2_create
router_create
}
cleanup()
{
pre_cleanup
router_destroy
h2_destroy
h1_destroy
vrf_cleanup
# Reload to ensure devlink-trap settings are back to default.
devlink_reload
}
rate_limits_test()
{
RET=0
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 2000000001 &> /dev/null
check_fail $? "Policer rate was changed to rate higher than limit"
devlink trap policer set $DEVLINK_DEV policer 1 rate 1
check_err $? "Failed to set policer rate to minimum"
devlink trap policer set $DEVLINK_DEV policer 1 rate 2000000000
check_err $? "Failed to set policer rate to maximum"
log_test "Trap policer rate limits"
}
burst_limits_test()
{
RET=0
devlink trap policer set $DEVLINK_DEV policer 1 burst 0 &> /dev/null
check_fail $? "Policer burst size was changed to 0"
devlink trap policer set $DEVLINK_DEV policer 1 burst 17 &> /dev/null
check_fail $? "Policer burst size was changed to burst size that is not power of 2"
devlink trap policer set $DEVLINK_DEV policer 1 burst 8 &> /dev/null
check_fail $? "Policer burst size was changed to burst size lower than limit"
devlink trap policer set $DEVLINK_DEV policer 1 \
burst $((2**25)) &> /dev/null
check_fail $? "Policer burst size was changed to burst size higher than limit"
devlink trap policer set $DEVLINK_DEV policer 1 burst 16
check_err $? "Failed to set policer burst size to minimum"
devlink trap policer set $DEVLINK_DEV policer 1 burst $((2**24))
check_err $? "Failed to set policer burst size to maximum"
log_test "Trap policer burst size limits"
}
trap_rate_get()
{
local t0 t1
t0=$(devlink_trap_rx_packets_get blackhole_route)
sleep 10
t1=$(devlink_trap_rx_packets_get blackhole_route)
echo $(((t1 - t0) / 10))
}
policer_drop_rate_get()
{
local id=$1; shift
local t0 t1
t0=$(devlink_trap_policer_rx_dropped_get $id)
sleep 10
t1=$(devlink_trap_policer_rx_dropped_get $id)
echo $(((t1 - t0) / 10))
}
__rate_test()
{
local rate pct drop_rate
local id=$1; shift
RET=0
devlink trap policer set $DEVLINK_DEV policer $id rate 1000 burst 16
devlink trap group set $DEVLINK_DEV group l3_drops policer $id
# Send packets at highest possible rate and make sure they are dropped
# by the policer. Make sure measured received rate is about 1000 pps
log_info "=== Tx rate: Highest, Policer rate: 1000 pps ==="
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac
sleep 5 # Take measurements when rate is stable
rate=$(trap_rate_get)
pct=$((100 * (rate - 1000) / 1000))
((-5 <= pct && pct <= 5))
check_err $? "Expected rate 1000 pps, got $rate pps, which is $pct% off. Required accuracy is +-5%"
log_info "Expected rate 1000 pps, measured rate $rate pps"
drop_rate=$(policer_drop_rate_get $id)
(( drop_rate > 0 ))
check_err $? "Expected non-zero policer drop rate, got 0"
log_info "Measured policer drop rate of $drop_rate pps"
stop_traffic
# Send packets at a rate of 1000 pps and make sure they are not dropped
# by the policer
log_info "=== Tx rate: 1000 pps, Policer rate: 1000 pps ==="
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac -d 1msec
sleep 5 # Take measurements when rate is stable
drop_rate=$(policer_drop_rate_get $id)
(( drop_rate == 0 ))
check_err $? "Expected zero policer drop rate, got a drop rate of $drop_rate pps"
log_info "Measured policer drop rate of $drop_rate pps"
stop_traffic
# Unbind the policer and send packets at highest possible rate. Make
# sure they are not dropped by the policer and that the measured
# received rate is higher than 1000 pps
log_info "=== Tx rate: Highest, Policer rate: No policer ==="
devlink trap group set $DEVLINK_DEV group l3_drops nopolicer
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac
rate=$(trap_rate_get)
(( rate > 1000 ))
check_err $? "Expected rate higher than 1000 pps, got $rate pps"
log_info "Measured rate $rate pps"
drop_rate=$(policer_drop_rate_get $id)
(( drop_rate == 0 ))
check_err $? "Expected zero policer drop rate, got a drop rate of $drop_rate pps"
log_info "Measured policer drop rate of $drop_rate pps"
stop_traffic
log_test "Trap policer rate"
}
rate_test()
{
local id
for id in $(devlink_trap_policer_ids_get); do
echo
log_info "Running rate test for policer $id"
__rate_test $id
done
}
__burst_test()
{
local t0_rx t0_drop t1_rx t1_drop rx drop
local id=$1; shift
RET=0
devlink trap policer set $DEVLINK_DEV policer $id rate 1000 burst 32
devlink trap group set $DEVLINK_DEV group l3_drops policer $id
# Send a burst of 64 packets and make sure that about 32 are received
# and the rest are dropped by the policer
log_info "=== Tx burst size: 64, Policer burst size: 32 pps ==="
t0_rx=$(devlink_trap_rx_packets_get blackhole_route)
t0_drop=$(devlink_trap_policer_rx_dropped_get $id)
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac -c 64
t1_rx=$(devlink_trap_rx_packets_get blackhole_route)
t1_drop=$(devlink_trap_policer_rx_dropped_get $id)
rx=$((t1_rx - t0_rx))
pct=$((100 * (rx - 32) / 32))
((-20 <= pct && pct <= 20))
check_err $? "Expected burst size of 32 packets, got $rx packets, which is $pct% off. Required accuracy is +-20%"
log_info "Expected burst size of 32 packets, measured burst size of $rx packets"
drop=$((t1_drop - t0_drop))
(( drop > 0 ))
check_err $? "Expected non-zero policer drops, got 0"
log_info "Measured policer drops of $drop packets"
# Send a burst of 16 packets and make sure that 16 are received
# and that none are dropped by the policer
log_info "=== Tx burst size: 16, Policer burst size: 32 pps ==="
t0_rx=$(devlink_trap_rx_packets_get blackhole_route)
t0_drop=$(devlink_trap_policer_rx_dropped_get $id)
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac -c 16
t1_rx=$(devlink_trap_rx_packets_get blackhole_route)
t1_drop=$(devlink_trap_policer_rx_dropped_get $id)
rx=$((t1_rx - t0_rx))
(( rx == 16 ))
check_err $? "Expected burst size of 16 packets, got $rx packets"
log_info "Expected burst size of 16 packets, measured burst size of $rx packets"
drop=$((t1_drop - t0_drop))
(( drop == 0 ))
check_err $? "Expected zero policer drops, got $drop"
log_info "Measured policer drops of $drop packets"
# Unbind the policer and send a burst of 64 packets. Make sure that
# 64 packets are received and that none are dropped by the policer
log_info "=== Tx burst size: 64, Policer burst size: No policer ==="
devlink trap group set $DEVLINK_DEV group l3_drops nopolicer
t0_rx=$(devlink_trap_rx_packets_get blackhole_route)
t0_drop=$(devlink_trap_policer_rx_dropped_get $id)
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac -c 64
t1_rx=$(devlink_trap_rx_packets_get blackhole_route)
t1_drop=$(devlink_trap_policer_rx_dropped_get $id)
rx=$((t1_rx - t0_rx))
(( rx == 64 ))
check_err $? "Expected burst size of 64 packets, got $rx packets"
log_info "Expected burst size of 64 packets, measured burst size of $rx packets"
drop=$((t1_drop - t0_drop))
(( drop == 0 ))
check_err $? "Expected zero policer drops, got $drop"
log_info "Measured policer drops of $drop packets"
log_test "Trap policer burst size"
}
burst_test()
{
local id
for id in $(devlink_trap_policer_ids_get); do
echo
log_info "Running burst size test for policer $id"
__burst_test $id
done
}
trap cleanup EXIT
setup_prepare
setup_wait
tests_run
exit $EXIT_STATUS

View File

@ -16,6 +16,8 @@ ALL_TESTS="
trap_group_action_test trap_group_action_test
bad_trap_group_test bad_trap_group_test
trap_group_stats_test trap_group_stats_test
trap_policer_test
trap_policer_bind_test
port_del_test port_del_test
dev_del_test dev_del_test
" "
@ -23,6 +25,7 @@ NETDEVSIM_PATH=/sys/bus/netdevsim/
DEV_ADDR=1337 DEV_ADDR=1337
DEV=netdevsim${DEV_ADDR} DEV=netdevsim${DEV_ADDR}
DEVLINK_DEV=netdevsim/${DEV} DEVLINK_DEV=netdevsim/${DEV}
DEBUGFS_DIR=/sys/kernel/debug/netdevsim/$DEV/
SLEEP_TIME=1 SLEEP_TIME=1
NETDEV="" NETDEV=""
NUM_NETIFS=0 NUM_NETIFS=0
@ -256,6 +259,119 @@ trap_group_stats_test()
log_test "Trap group statistics" 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() port_del_test()
{ {
local group_name local group_name

View File

@ -420,6 +420,49 @@ devlink_trap_drop_cleanup()
tc filter del dev $dev egress protocol $proto pref $pref handle $handle flower 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() devlink_port_by_netdev()
{ {
local if_name=$1 local if_name=$1