mirror of
https://github.com/torvalds/linux.git
synced 2025-12-01 15:35:58 +07:00
Merge branch 'sparx5-port-mirroring'
Daniel Machon says: ==================== net: sparx5: add support for port mirroring This series adds support for port mirroring, and port mirroring stats, through tc matchall action FLOW_ACTION_MIRRED. The hardware has three independent mirroring probes. Each probe can be configured with a separate set of filtering conditions that must be fulfilled before traffic is mirrored. A mirror probe can have up to 64 source ports and a single monitor port. The direction of a mirror probe determines if rx or tx traffic is mirrored from the source port to the monitor port. To: David S. Miller <davem@davemloft.net> To: Eric Dumazet <edumazet@google.com> To: Jakub Kicinski <kuba@kernel.org> To: Paolo Abeni <pabeni@redhat.com> To: Lars Povlsen <lars.povlsen@microchip.com> To: Steen Hegelund <Steen.Hegelund@microchip.com> To: UNGLinuxDriver@microchip.com To: Russell King <linux@armlinux.org.uk> Cc: netdev@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Cc: Horatiu Vultur <horatiu.vultur@microchip.com> Cc: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> Cc: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Cc: Vladimir Oltean <vladimir.oltean@nxp.com> Cc: Yue Haibing <yuehaibing@huawei.com> --- Changes in v3: - Ditch do_div() (patch #3) to fix warning on hexagon arch, reported by intel bot - Link to v2: https://lore.kernel.org/r/20240418-port-mirroring-v2-0-20642868b386@microchip.com Changes in v2: - Fix clang build warning about uninitialized variable 'err' - Link to v1: https://lore.kernel.org/r/20240418-port-mirroring-v1-0-e05c35007c55@microchip.com ==================== Signed-off-by: Daniel Machon <daniel.machon@microchip.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -10,7 +10,8 @@ sparx5-switch-y := sparx5_main.o sparx5_packet.o \
|
|||||||
sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o \
|
sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o \
|
||||||
sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \
|
sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \
|
||||||
sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o \
|
sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o \
|
||||||
sparx5_tc_matchall.o sparx5_pool.o sparx5_sdlb.o sparx5_police.o sparx5_psfp.o
|
sparx5_tc_matchall.o sparx5_pool.o sparx5_sdlb.o sparx5_police.o \
|
||||||
|
sparx5_psfp.o sparx5_mirror.o
|
||||||
|
|
||||||
sparx5-switch-$(CONFIG_SPARX5_DCB) += sparx5_dcb.o
|
sparx5-switch-$(CONFIG_SPARX5_DCB) += sparx5_dcb.o
|
||||||
sparx5-switch-$(CONFIG_DEBUG_FS) += sparx5_vcap_debugfs.o
|
sparx5-switch-$(CONFIG_DEBUG_FS) += sparx5_vcap_debugfs.o
|
||||||
|
|||||||
@@ -899,6 +899,9 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
|
|||||||
dev_err(sparx5->dev, "PTP failed\n");
|
dev_err(sparx5->dev, "PTP failed\n");
|
||||||
goto cleanup_ports;
|
goto cleanup_ports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&sparx5->mall_entries);
|
||||||
|
|
||||||
goto cleanup_config;
|
goto cleanup_config;
|
||||||
|
|
||||||
cleanup_ports:
|
cleanup_ports:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <linux/ptp_clock_kernel.h>
|
#include <linux/ptp_clock_kernel.h>
|
||||||
#include <linux/hrtimer.h>
|
#include <linux/hrtimer.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
|
#include <net/flow_offload.h>
|
||||||
|
|
||||||
#include "sparx5_main_regs.h"
|
#include "sparx5_main_regs.h"
|
||||||
|
|
||||||
@@ -173,6 +174,7 @@ struct sparx5_port {
|
|||||||
struct phylink_config phylink_config;
|
struct phylink_config phylink_config;
|
||||||
struct phylink *phylink;
|
struct phylink *phylink;
|
||||||
struct phylink_pcs phylink_pcs;
|
struct phylink_pcs phylink_pcs;
|
||||||
|
struct flow_stats mirror_stats;
|
||||||
u16 portno;
|
u16 portno;
|
||||||
/* Ingress default VLAN (pvid) */
|
/* Ingress default VLAN (pvid) */
|
||||||
u16 pvid;
|
u16 pvid;
|
||||||
@@ -227,6 +229,22 @@ struct sparx5_mdb_entry {
|
|||||||
u16 pgid_idx;
|
u16 pgid_idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sparx5_mall_mirror_entry {
|
||||||
|
u32 idx;
|
||||||
|
struct sparx5_port *port;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sparx5_mall_entry {
|
||||||
|
struct list_head list;
|
||||||
|
struct sparx5_port *port;
|
||||||
|
unsigned long cookie;
|
||||||
|
enum flow_action_id type;
|
||||||
|
bool ingress;
|
||||||
|
union {
|
||||||
|
struct sparx5_mall_mirror_entry mirror;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
#define SPARX5_PTP_TIMEOUT msecs_to_jiffies(10)
|
#define SPARX5_PTP_TIMEOUT msecs_to_jiffies(10)
|
||||||
#define SPARX5_SKB_CB(skb) \
|
#define SPARX5_SKB_CB(skb) \
|
||||||
((struct sparx5_skb_cb *)((skb)->cb))
|
((struct sparx5_skb_cb *)((skb)->cb))
|
||||||
@@ -295,6 +313,7 @@ struct sparx5 {
|
|||||||
struct vcap_control *vcap_ctrl;
|
struct vcap_control *vcap_ctrl;
|
||||||
/* PGID allocation map */
|
/* PGID allocation map */
|
||||||
u8 pgid_map[PGID_TABLE_SIZE];
|
u8 pgid_map[PGID_TABLE_SIZE];
|
||||||
|
struct list_head mall_entries;
|
||||||
/* Common root for debugfs */
|
/* Common root for debugfs */
|
||||||
struct dentry *debugfs_root;
|
struct dentry *debugfs_root;
|
||||||
};
|
};
|
||||||
@@ -541,6 +560,12 @@ void sparx5_psfp_init(struct sparx5 *sparx5);
|
|||||||
void sparx5_new_base_time(struct sparx5 *sparx5, const u32 cycle_time,
|
void sparx5_new_base_time(struct sparx5 *sparx5, const u32 cycle_time,
|
||||||
const ktime_t org_base_time, ktime_t *new_base_time);
|
const ktime_t org_base_time, ktime_t *new_base_time);
|
||||||
|
|
||||||
|
/* sparx5_mirror.c */
|
||||||
|
int sparx5_mirror_add(struct sparx5_mall_entry *entry);
|
||||||
|
void sparx5_mirror_del(struct sparx5_mall_entry *entry);
|
||||||
|
void sparx5_mirror_stats(struct sparx5_mall_entry *entry,
|
||||||
|
struct flow_stats *fstats);
|
||||||
|
|
||||||
/* Clock period in picoseconds */
|
/* Clock period in picoseconds */
|
||||||
static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
|
static inline u32 sparx5_clk_period(enum sparx5_core_clockfreq cclock)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -83,6 +83,64 @@ enum sparx5_target {
|
|||||||
#define ANA_AC_OWN_UPSID_OWN_UPSID_GET(x)\
|
#define ANA_AC_OWN_UPSID_OWN_UPSID_GET(x)\
|
||||||
FIELD_GET(ANA_AC_OWN_UPSID_OWN_UPSID, x)
|
FIELD_GET(ANA_AC_OWN_UPSID_OWN_UPSID, x)
|
||||||
|
|
||||||
|
/* ANA_AC:MIRROR_PROBE:PROBE_CFG */
|
||||||
|
#define ANA_AC_PROBE_CFG(g) \
|
||||||
|
__REG(TARGET_ANA_AC, 0, 1, 893696, g, 3, 32, 0, 0, 1, 4)
|
||||||
|
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_RX_CPU_AND_VD GENMASK(31, 27)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_RX_CPU_AND_VD_SET(x)\
|
||||||
|
FIELD_PREP(ANA_AC_PROBE_CFG_PROBE_RX_CPU_AND_VD, x)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_RX_CPU_AND_VD_GET(x)\
|
||||||
|
FIELD_GET(ANA_AC_PROBE_CFG_PROBE_RX_CPU_AND_VD, x)
|
||||||
|
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_CPU_SET GENMASK(26, 19)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_CPU_SET_SET(x)\
|
||||||
|
FIELD_PREP(ANA_AC_PROBE_CFG_PROBE_CPU_SET, x)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_CPU_SET_GET(x)\
|
||||||
|
FIELD_GET(ANA_AC_PROBE_CFG_PROBE_CPU_SET, x)
|
||||||
|
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_VID GENMASK(18, 6)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_VID_SET(x)\
|
||||||
|
FIELD_PREP(ANA_AC_PROBE_CFG_PROBE_VID, x)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_VID_GET(x)\
|
||||||
|
FIELD_GET(ANA_AC_PROBE_CFG_PROBE_VID, x)
|
||||||
|
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_VLAN_MODE GENMASK(5, 4)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_VLAN_MODE_SET(x)\
|
||||||
|
FIELD_PREP(ANA_AC_PROBE_CFG_PROBE_VLAN_MODE, x)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_VLAN_MODE_GET(x)\
|
||||||
|
FIELD_GET(ANA_AC_PROBE_CFG_PROBE_VLAN_MODE, x)
|
||||||
|
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_MAC_MODE GENMASK(3, 2)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_MAC_MODE_SET(x)\
|
||||||
|
FIELD_PREP(ANA_AC_PROBE_CFG_PROBE_MAC_MODE, x)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_MAC_MODE_GET(x)\
|
||||||
|
FIELD_GET(ANA_AC_PROBE_CFG_PROBE_MAC_MODE, x)
|
||||||
|
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_DIRECTION GENMASK(1, 0)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_DIRECTION_SET(x)\
|
||||||
|
FIELD_PREP(ANA_AC_PROBE_CFG_PROBE_DIRECTION, x)
|
||||||
|
#define ANA_AC_PROBE_CFG_PROBE_DIRECTION_GET(x)\
|
||||||
|
FIELD_GET(ANA_AC_PROBE_CFG_PROBE_DIRECTION, x)
|
||||||
|
|
||||||
|
/* ANA_AC:MIRROR_PROBE:PROBE_PORT_CFG */
|
||||||
|
#define ANA_AC_PROBE_PORT_CFG(g) \
|
||||||
|
__REG(TARGET_ANA_AC, 0, 1, 893696, g, 3, 32, 8, 0, 1, 4)
|
||||||
|
|
||||||
|
/* ANA_AC:MIRROR_PROBE:PROBE_PORT_CFG1 */
|
||||||
|
#define ANA_AC_PROBE_PORT_CFG1(g) \
|
||||||
|
__REG(TARGET_ANA_AC, 0, 1, 893696, g, 3, 32, 12, 0, 1, 4)
|
||||||
|
|
||||||
|
/* ANA_AC:MIRROR_PROBE:PROBE_PORT_CFG2 */
|
||||||
|
#define ANA_AC_PROBE_PORT_CFG2(g) \
|
||||||
|
__REG(TARGET_ANA_AC, 0, 1, 893696, g, 3, 32, 16, 0, 1, 4)
|
||||||
|
|
||||||
|
#define ANA_AC_PROBE_PORT_CFG2_PROBE_PORT_MASK2 BIT(0)
|
||||||
|
#define ANA_AC_PROBE_PORT_CFG2_PROBE_PORT_MASK2_SET(x)\
|
||||||
|
FIELD_PREP(ANA_AC_PROBE_PORT_CFG2_PROBE_PORT_MASK2, x)
|
||||||
|
#define ANA_AC_PROBE_PORT_CFG2_PROBE_PORT_MASK2_GET(x)\
|
||||||
|
FIELD_GET(ANA_AC_PROBE_PORT_CFG2_PROBE_PORT_MASK2, x)
|
||||||
|
|
||||||
/* ANA_AC:SRC:SRC_CFG */
|
/* ANA_AC:SRC:SRC_CFG */
|
||||||
#define ANA_AC_SRC_CFG(g) __REG(TARGET_ANA_AC,\
|
#define ANA_AC_SRC_CFG(g) __REG(TARGET_ANA_AC,\
|
||||||
0, 1, 849920, g, 102, 16, 0, 0, 1, 4)
|
0, 1, 849920, g, 102, 16, 0, 0, 1, 4)
|
||||||
@@ -6203,6 +6261,16 @@ enum sparx5_target {
|
|||||||
#define QFWD_SWITCH_PORT_MODE_LEARNALL_MORE_GET(x)\
|
#define QFWD_SWITCH_PORT_MODE_LEARNALL_MORE_GET(x)\
|
||||||
FIELD_GET(QFWD_SWITCH_PORT_MODE_LEARNALL_MORE, x)
|
FIELD_GET(QFWD_SWITCH_PORT_MODE_LEARNALL_MORE, x)
|
||||||
|
|
||||||
|
/* QFWD:SYSTEM:FRAME_COPY_CFG */
|
||||||
|
#define QFWD_FRAME_COPY_CFG(r)\
|
||||||
|
__REG(TARGET_QFWD, 0, 1, 0, 0, 1, 340, 284, r, 12, 4)
|
||||||
|
|
||||||
|
#define QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL GENMASK(12, 6)
|
||||||
|
#define QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL_SET(x)\
|
||||||
|
FIELD_PREP(QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL, x)
|
||||||
|
#define QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL_GET(x)\
|
||||||
|
FIELD_GET(QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL, x)
|
||||||
|
|
||||||
/* QRES:RES_CTRL:RES_CFG */
|
/* QRES:RES_CTRL:RES_CFG */
|
||||||
#define QRES_RES_CFG(g) __REG(TARGET_QRES,\
|
#define QRES_RES_CFG(g) __REG(TARGET_QRES,\
|
||||||
0, 1, 0, g, 5120, 16, 0, 0, 1, 4)
|
0, 1, 0, g, 5120, 16, 0, 0, 1, 4)
|
||||||
|
|||||||
235
drivers/net/ethernet/microchip/sparx5/sparx5_mirror.c
Normal file
235
drivers/net/ethernet/microchip/sparx5/sparx5_mirror.c
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/* Microchip Sparx5 Switch driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sparx5_main.h"
|
||||||
|
#include "sparx5_main_regs.h"
|
||||||
|
#include "sparx5_tc.h"
|
||||||
|
|
||||||
|
#define SPX5_MIRROR_PROBE_MAX 3
|
||||||
|
#define SPX5_MIRROR_DISABLED 0
|
||||||
|
#define SPX5_MIRROR_EGRESS 1
|
||||||
|
#define SPX5_MIRROR_INGRESS 2
|
||||||
|
#define SPX5_MIRROR_MONITOR_PORT_DEFAULT 65
|
||||||
|
#define SPX5_QFWD_MP_OFFSET 9 /* Mirror port offset in the QFWD register */
|
||||||
|
|
||||||
|
/* Convert from bool ingress/egress to mirror direction */
|
||||||
|
static u32 sparx5_mirror_to_dir(bool ingress)
|
||||||
|
{
|
||||||
|
return ingress ? SPX5_MIRROR_INGRESS : SPX5_MIRROR_EGRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get ports belonging to this mirror */
|
||||||
|
static u64 sparx5_mirror_port_get(struct sparx5 *sparx5, u32 idx)
|
||||||
|
{
|
||||||
|
return (u64)spx5_rd(sparx5, ANA_AC_PROBE_PORT_CFG1(idx)) << 32 |
|
||||||
|
spx5_rd(sparx5, ANA_AC_PROBE_PORT_CFG(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add port to mirror (only front ports) */
|
||||||
|
static void sparx5_mirror_port_add(struct sparx5 *sparx5, u32 idx, u32 portno)
|
||||||
|
{
|
||||||
|
u32 val, reg = portno;
|
||||||
|
|
||||||
|
reg = portno / BITS_PER_BYTE;
|
||||||
|
val = BIT(portno % BITS_PER_BYTE);
|
||||||
|
|
||||||
|
if (reg == 0)
|
||||||
|
return spx5_rmw(val, val, sparx5, ANA_AC_PROBE_PORT_CFG(idx));
|
||||||
|
else
|
||||||
|
return spx5_rmw(val, val, sparx5, ANA_AC_PROBE_PORT_CFG1(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete port from mirror (only front ports) */
|
||||||
|
static void sparx5_mirror_port_del(struct sparx5 *sparx5, u32 idx, u32 portno)
|
||||||
|
{
|
||||||
|
u32 val, reg = portno;
|
||||||
|
|
||||||
|
reg = portno / BITS_PER_BYTE;
|
||||||
|
val = BIT(portno % BITS_PER_BYTE);
|
||||||
|
|
||||||
|
if (reg == 0)
|
||||||
|
return spx5_rmw(0, val, sparx5, ANA_AC_PROBE_PORT_CFG(idx));
|
||||||
|
else
|
||||||
|
return spx5_rmw(0, val, sparx5, ANA_AC_PROBE_PORT_CFG1(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if mirror contains port */
|
||||||
|
static bool sparx5_mirror_contains(struct sparx5 *sparx5, u32 idx, u32 portno)
|
||||||
|
{
|
||||||
|
return (sparx5_mirror_port_get(sparx5, idx) & BIT_ULL(portno)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if mirror is empty */
|
||||||
|
static bool sparx5_mirror_is_empty(struct sparx5 *sparx5, u32 idx)
|
||||||
|
{
|
||||||
|
return sparx5_mirror_port_get(sparx5, idx) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get direction of mirror */
|
||||||
|
static u32 sparx5_mirror_dir_get(struct sparx5 *sparx5, u32 idx)
|
||||||
|
{
|
||||||
|
u32 val = spx5_rd(sparx5, ANA_AC_PROBE_CFG(idx));
|
||||||
|
|
||||||
|
return ANA_AC_PROBE_CFG_PROBE_DIRECTION_GET(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set direction of mirror */
|
||||||
|
static void sparx5_mirror_dir_set(struct sparx5 *sparx5, u32 idx, u32 dir)
|
||||||
|
{
|
||||||
|
spx5_rmw(ANA_AC_PROBE_CFG_PROBE_DIRECTION_SET(dir),
|
||||||
|
ANA_AC_PROBE_CFG_PROBE_DIRECTION, sparx5,
|
||||||
|
ANA_AC_PROBE_CFG(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the monitor port for this mirror */
|
||||||
|
static void sparx5_mirror_monitor_set(struct sparx5 *sparx5, u32 idx,
|
||||||
|
u32 portno)
|
||||||
|
{
|
||||||
|
spx5_rmw(QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL_SET(portno),
|
||||||
|
QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL, sparx5,
|
||||||
|
QFWD_FRAME_COPY_CFG(idx + SPX5_QFWD_MP_OFFSET));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the monitor port of this mirror */
|
||||||
|
static u32 sparx5_mirror_monitor_get(struct sparx5 *sparx5, u32 idx)
|
||||||
|
{
|
||||||
|
u32 val = spx5_rd(sparx5,
|
||||||
|
QFWD_FRAME_COPY_CFG(idx + SPX5_QFWD_MP_OFFSET));
|
||||||
|
|
||||||
|
return QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL_GET(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if port is the monitor port of this mirror */
|
||||||
|
static bool sparx5_mirror_has_monitor(struct sparx5 *sparx5, u32 idx,
|
||||||
|
u32 portno)
|
||||||
|
{
|
||||||
|
return sparx5_mirror_monitor_get(sparx5, idx) == portno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a suitable mirror for this port */
|
||||||
|
static int sparx5_mirror_get(struct sparx5_port *sport,
|
||||||
|
struct sparx5_port *mport, u32 dir, u32 *idx)
|
||||||
|
{
|
||||||
|
struct sparx5 *sparx5 = sport->sparx5;
|
||||||
|
u32 i;
|
||||||
|
|
||||||
|
/* Check if this port is already used as a monitor port */
|
||||||
|
for (i = 0; i < SPX5_MIRROR_PROBE_MAX; i++)
|
||||||
|
if (sparx5_mirror_has_monitor(sparx5, i, sport->portno))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Check if existing mirror can be reused
|
||||||
|
* (same direction and monitor port).
|
||||||
|
*/
|
||||||
|
for (i = 0; i < SPX5_MIRROR_PROBE_MAX; i++) {
|
||||||
|
if (sparx5_mirror_dir_get(sparx5, i) == dir &&
|
||||||
|
sparx5_mirror_has_monitor(sparx5, i, mport->portno)) {
|
||||||
|
*idx = i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return free mirror */
|
||||||
|
for (i = 0; i < SPX5_MIRROR_PROBE_MAX; i++) {
|
||||||
|
if (sparx5_mirror_is_empty(sparx5, i)) {
|
||||||
|
*idx = i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sparx5_mirror_add(struct sparx5_mall_entry *entry)
|
||||||
|
{
|
||||||
|
u32 mirror_idx, dir = sparx5_mirror_to_dir(entry->ingress);
|
||||||
|
struct sparx5_port *sport, *mport;
|
||||||
|
struct sparx5 *sparx5;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Source port */
|
||||||
|
sport = entry->port;
|
||||||
|
/* monitor port */
|
||||||
|
mport = entry->mirror.port;
|
||||||
|
sparx5 = sport->sparx5;
|
||||||
|
|
||||||
|
if (sport->portno == mport->portno)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
err = sparx5_mirror_get(sport, mport, dir, &mirror_idx);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (sparx5_mirror_contains(sparx5, mirror_idx, sport->portno))
|
||||||
|
return -EEXIST;
|
||||||
|
|
||||||
|
/* Add port to mirror */
|
||||||
|
sparx5_mirror_port_add(sparx5, mirror_idx, sport->portno);
|
||||||
|
|
||||||
|
/* Set direction of mirror */
|
||||||
|
sparx5_mirror_dir_set(sparx5, mirror_idx, dir);
|
||||||
|
|
||||||
|
/* Set monitor port for mirror */
|
||||||
|
sparx5_mirror_monitor_set(sparx5, mirror_idx, mport->portno);
|
||||||
|
|
||||||
|
entry->mirror.idx = mirror_idx;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sparx5_mirror_del(struct sparx5_mall_entry *entry)
|
||||||
|
{
|
||||||
|
struct sparx5_port *port = entry->port;
|
||||||
|
struct sparx5 *sparx5 = port->sparx5;
|
||||||
|
u32 mirror_idx = entry->mirror.idx;
|
||||||
|
|
||||||
|
sparx5_mirror_port_del(sparx5, mirror_idx, port->portno);
|
||||||
|
if (!sparx5_mirror_is_empty(sparx5, mirror_idx))
|
||||||
|
return;
|
||||||
|
|
||||||
|
sparx5_mirror_dir_set(sparx5, mirror_idx, SPX5_MIRROR_DISABLED);
|
||||||
|
|
||||||
|
sparx5_mirror_monitor_set(sparx5,
|
||||||
|
mirror_idx,
|
||||||
|
SPX5_MIRROR_MONITOR_PORT_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sparx5_mirror_stats(struct sparx5_mall_entry *entry,
|
||||||
|
struct flow_stats *fstats)
|
||||||
|
{
|
||||||
|
struct sparx5_port *port = entry->port;
|
||||||
|
struct rtnl_link_stats64 new_stats;
|
||||||
|
struct flow_stats *old_stats;
|
||||||
|
|
||||||
|
old_stats = &entry->port->mirror_stats;
|
||||||
|
sparx5_get_stats64(port->ndev, &new_stats);
|
||||||
|
|
||||||
|
if (entry->ingress) {
|
||||||
|
flow_stats_update(fstats,
|
||||||
|
new_stats.rx_bytes - old_stats->bytes,
|
||||||
|
new_stats.rx_packets - old_stats->pkts,
|
||||||
|
new_stats.rx_dropped - old_stats->drops,
|
||||||
|
old_stats->lastused,
|
||||||
|
FLOW_ACTION_HW_STATS_IMMEDIATE);
|
||||||
|
|
||||||
|
old_stats->bytes = new_stats.rx_bytes;
|
||||||
|
old_stats->pkts = new_stats.rx_packets;
|
||||||
|
old_stats->drops = new_stats.rx_dropped;
|
||||||
|
old_stats->lastused = jiffies;
|
||||||
|
} else {
|
||||||
|
flow_stats_update(fstats,
|
||||||
|
new_stats.tx_bytes - old_stats->bytes,
|
||||||
|
new_stats.tx_packets - old_stats->pkts,
|
||||||
|
new_stats.tx_dropped - old_stats->drops,
|
||||||
|
old_stats->lastused,
|
||||||
|
FLOW_ACTION_HW_STATS_IMMEDIATE);
|
||||||
|
|
||||||
|
old_stats->bytes = new_stats.tx_bytes;
|
||||||
|
old_stats->pkts = new_stats.tx_packets;
|
||||||
|
old_stats->drops = new_stats.tx_dropped;
|
||||||
|
old_stats->lastused = jiffies;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,11 +11,44 @@
|
|||||||
#include "sparx5_main.h"
|
#include "sparx5_main.h"
|
||||||
#include "sparx5_vcap_impl.h"
|
#include "sparx5_vcap_impl.h"
|
||||||
|
|
||||||
|
static struct sparx5_mall_entry *
|
||||||
|
sparx5_tc_matchall_entry_find(struct list_head *entries, unsigned long cookie)
|
||||||
|
{
|
||||||
|
struct sparx5_mall_entry *entry;
|
||||||
|
|
||||||
|
list_for_each_entry(entry, entries, list) {
|
||||||
|
if (entry->cookie == cookie)
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sparx5_tc_matchall_parse_action(struct sparx5_port *port,
|
||||||
|
struct sparx5_mall_entry *entry,
|
||||||
|
struct flow_action_entry *action,
|
||||||
|
bool ingress,
|
||||||
|
unsigned long cookie)
|
||||||
|
{
|
||||||
|
entry->port = port;
|
||||||
|
entry->type = action->id;
|
||||||
|
entry->ingress = ingress;
|
||||||
|
entry->cookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sparx5_tc_matchall_parse_mirror_action(struct sparx5_mall_entry *entry,
|
||||||
|
struct flow_action_entry *action)
|
||||||
|
{
|
||||||
|
entry->mirror.port = netdev_priv(action->dev);
|
||||||
|
}
|
||||||
|
|
||||||
static int sparx5_tc_matchall_replace(struct net_device *ndev,
|
static int sparx5_tc_matchall_replace(struct net_device *ndev,
|
||||||
struct tc_cls_matchall_offload *tmo,
|
struct tc_cls_matchall_offload *tmo,
|
||||||
bool ingress)
|
bool ingress)
|
||||||
{
|
{
|
||||||
struct sparx5_port *port = netdev_priv(ndev);
|
struct sparx5_port *port = netdev_priv(ndev);
|
||||||
|
struct sparx5_mall_entry *mall_entry;
|
||||||
struct flow_action_entry *action;
|
struct flow_action_entry *action;
|
||||||
struct sparx5 *sparx5;
|
struct sparx5 *sparx5;
|
||||||
int err;
|
int err;
|
||||||
@@ -27,8 +60,45 @@ static int sparx5_tc_matchall_replace(struct net_device *ndev,
|
|||||||
}
|
}
|
||||||
action = &tmo->rule->action.entries[0];
|
action = &tmo->rule->action.entries[0];
|
||||||
|
|
||||||
|
mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
|
||||||
|
if (!mall_entry)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
sparx5_tc_matchall_parse_action(port,
|
||||||
|
mall_entry,
|
||||||
|
action,
|
||||||
|
ingress,
|
||||||
|
tmo->cookie);
|
||||||
|
|
||||||
sparx5 = port->sparx5;
|
sparx5 = port->sparx5;
|
||||||
switch (action->id) {
|
switch (action->id) {
|
||||||
|
case FLOW_ACTION_MIRRED:
|
||||||
|
sparx5_tc_matchall_parse_mirror_action(mall_entry, action);
|
||||||
|
err = sparx5_mirror_add(mall_entry);
|
||||||
|
if (err) {
|
||||||
|
switch (err) {
|
||||||
|
case -EEXIST:
|
||||||
|
NL_SET_ERR_MSG_MOD(tmo->common.extack,
|
||||||
|
"Mirroring already exists");
|
||||||
|
break;
|
||||||
|
case -EINVAL:
|
||||||
|
NL_SET_ERR_MSG_MOD(tmo->common.extack,
|
||||||
|
"Cannot mirror a monitor port");
|
||||||
|
break;
|
||||||
|
case -ENOENT:
|
||||||
|
NL_SET_ERR_MSG_MOD(tmo->common.extack,
|
||||||
|
"No more mirror probes available");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NL_SET_ERR_MSG_MOD(tmo->common.extack,
|
||||||
|
"Unknown error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
/* Get baseline stats for this port */
|
||||||
|
sparx5_mirror_stats(mall_entry, &tmo->stats);
|
||||||
|
break;
|
||||||
case FLOW_ACTION_GOTO:
|
case FLOW_ACTION_GOTO:
|
||||||
err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
|
err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
|
||||||
tmo->common.chain_index,
|
tmo->common.chain_index,
|
||||||
@@ -59,6 +129,9 @@ static int sparx5_tc_matchall_replace(struct net_device *ndev,
|
|||||||
NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
|
NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list_add_tail(&mall_entry->list, &sparx5->mall_entries);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,19 +140,51 @@ static int sparx5_tc_matchall_destroy(struct net_device *ndev,
|
|||||||
bool ingress)
|
bool ingress)
|
||||||
{
|
{
|
||||||
struct sparx5_port *port = netdev_priv(ndev);
|
struct sparx5_port *port = netdev_priv(ndev);
|
||||||
struct sparx5 *sparx5;
|
struct sparx5 *sparx5 = port->sparx5;
|
||||||
int err;
|
struct sparx5_mall_entry *entry;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
sparx5 = port->sparx5;
|
entry = sparx5_tc_matchall_entry_find(&sparx5->mall_entries,
|
||||||
if (!tmo->rule && tmo->cookie) {
|
tmo->cookie);
|
||||||
|
if (!entry)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
if (entry->type == FLOW_ACTION_MIRRED) {
|
||||||
|
sparx5_mirror_del(entry);
|
||||||
|
} else if (entry->type == FLOW_ACTION_GOTO) {
|
||||||
err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
|
err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
|
||||||
0, 0, tmo->cookie, false);
|
0, 0, tmo->cookie, false);
|
||||||
if (err)
|
} else {
|
||||||
return err;
|
NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
|
||||||
return 0;
|
err = -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
|
|
||||||
return -EOPNOTSUPP;
|
list_del(&entry->list);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sparx5_tc_matchall_stats(struct net_device *ndev,
|
||||||
|
struct tc_cls_matchall_offload *tmo,
|
||||||
|
bool ingress)
|
||||||
|
{
|
||||||
|
struct sparx5_port *port = netdev_priv(ndev);
|
||||||
|
struct sparx5 *sparx5 = port->sparx5;
|
||||||
|
struct sparx5_mall_entry *entry;
|
||||||
|
|
||||||
|
entry = sparx5_tc_matchall_entry_find(&sparx5->mall_entries,
|
||||||
|
tmo->cookie);
|
||||||
|
if (!entry)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
if (entry->type == FLOW_ACTION_MIRRED) {
|
||||||
|
sparx5_mirror_stats(entry, &tmo->stats);
|
||||||
|
} else {
|
||||||
|
NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sparx5_tc_matchall(struct net_device *ndev,
|
int sparx5_tc_matchall(struct net_device *ndev,
|
||||||
@@ -91,6 +196,8 @@ int sparx5_tc_matchall(struct net_device *ndev,
|
|||||||
return sparx5_tc_matchall_replace(ndev, tmo, ingress);
|
return sparx5_tc_matchall_replace(ndev, tmo, ingress);
|
||||||
case TC_CLSMATCHALL_DESTROY:
|
case TC_CLSMATCHALL_DESTROY:
|
||||||
return sparx5_tc_matchall_destroy(ndev, tmo, ingress);
|
return sparx5_tc_matchall_destroy(ndev, tmo, ingress);
|
||||||
|
case TC_CLSMATCHALL_STATS:
|
||||||
|
return sparx5_tc_matchall_stats(ndev, tmo, ingress);
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user