1// SPDX-License-Identifier: GPL-2.0+
2
3#include "lan966x_main.h"
4
5int lan966x_mirror_port_add(struct lan966x_port *port,
6 struct flow_action_entry *action,
7 unsigned long mirror_id,
8 bool ingress,
9 struct netlink_ext_ack *extack)
10{
11 struct lan966x *lan966x = port->lan966x;
12 struct lan966x_port *monitor_port;
13
14 if (!lan966x_netdevice_check(dev: action->dev)) {
15 NL_SET_ERR_MSG_MOD(extack,
16 "Destination not an lan966x port");
17 return -EOPNOTSUPP;
18 }
19
20 monitor_port = netdev_priv(dev: action->dev);
21
22 if (lan966x->mirror_mask[ingress] & BIT(port->chip_port)) {
23 NL_SET_ERR_MSG_MOD(extack,
24 "Mirror already exists");
25 return -EEXIST;
26 }
27
28 if (lan966x->mirror_monitor &&
29 lan966x->mirror_monitor != monitor_port) {
30 NL_SET_ERR_MSG_MOD(extack,
31 "Cannot change mirror port while in use");
32 return -EBUSY;
33 }
34
35 if (port == monitor_port) {
36 NL_SET_ERR_MSG_MOD(extack,
37 "Cannot mirror the monitor port");
38 return -EINVAL;
39 }
40
41 lan966x->mirror_mask[ingress] |= BIT(port->chip_port);
42
43 lan966x->mirror_monitor = monitor_port;
44 lan_wr(BIT(monitor_port->chip_port), lan966x, ANA_MIRRORPORTS);
45
46 if (ingress) {
47 lan_rmw(ANA_PORT_CFG_SRC_MIRROR_ENA_SET(1),
48 ANA_PORT_CFG_SRC_MIRROR_ENA,
49 lan966x, ANA_PORT_CFG(port->chip_port));
50 } else {
51 lan_wr(val: lan966x->mirror_mask[0], lan966x,
52 ANA_EMIRRORPORTS);
53 }
54
55 lan966x->mirror_count++;
56
57 if (ingress)
58 port->tc.ingress_mirror_id = mirror_id;
59 else
60 port->tc.egress_mirror_id = mirror_id;
61
62 return 0;
63}
64
65int lan966x_mirror_port_del(struct lan966x_port *port,
66 bool ingress,
67 struct netlink_ext_ack *extack)
68{
69 struct lan966x *lan966x = port->lan966x;
70
71 if (!(lan966x->mirror_mask[ingress] & BIT(port->chip_port))) {
72 NL_SET_ERR_MSG_MOD(extack,
73 "There is no mirroring for this port");
74 return -ENOENT;
75 }
76
77 lan966x->mirror_mask[ingress] &= ~BIT(port->chip_port);
78
79 if (ingress) {
80 lan_rmw(ANA_PORT_CFG_SRC_MIRROR_ENA_SET(0),
81 ANA_PORT_CFG_SRC_MIRROR_ENA,
82 lan966x, ANA_PORT_CFG(port->chip_port));
83 } else {
84 lan_wr(val: lan966x->mirror_mask[0], lan966x,
85 ANA_EMIRRORPORTS);
86 }
87
88 lan966x->mirror_count--;
89
90 if (lan966x->mirror_count == 0) {
91 lan966x->mirror_monitor = NULL;
92 lan_wr(val: 0, lan966x, ANA_MIRRORPORTS);
93 }
94
95 if (ingress)
96 port->tc.ingress_mirror_id = 0;
97 else
98 port->tc.egress_mirror_id = 0;
99
100 return 0;
101}
102
103void lan966x_mirror_port_stats(struct lan966x_port *port,
104 struct flow_stats *stats,
105 bool ingress)
106{
107 struct rtnl_link_stats64 new_stats;
108 struct flow_stats *old_stats;
109
110 old_stats = &port->tc.mirror_stat;
111 lan966x_stats_get(dev: port->dev, stats: &new_stats);
112
113 if (ingress) {
114 flow_stats_update(flow_stats: stats,
115 bytes: new_stats.rx_bytes - old_stats->bytes,
116 pkts: new_stats.rx_packets - old_stats->pkts,
117 drops: new_stats.rx_dropped - old_stats->drops,
118 lastused: old_stats->lastused,
119 used_hw_stats: FLOW_ACTION_HW_STATS_IMMEDIATE);
120
121 old_stats->bytes = new_stats.rx_bytes;
122 old_stats->pkts = new_stats.rx_packets;
123 old_stats->drops = new_stats.rx_dropped;
124 old_stats->lastused = jiffies;
125 } else {
126 flow_stats_update(flow_stats: stats,
127 bytes: new_stats.tx_bytes - old_stats->bytes,
128 pkts: new_stats.tx_packets - old_stats->pkts,
129 drops: new_stats.tx_dropped - old_stats->drops,
130 lastused: old_stats->lastused,
131 used_hw_stats: FLOW_ACTION_HW_STATS_IMMEDIATE);
132
133 old_stats->bytes = new_stats.tx_bytes;
134 old_stats->pkts = new_stats.tx_packets;
135 old_stats->drops = new_stats.tx_dropped;
136 old_stats->lastused = jiffies;
137 }
138}
139

source code of linux/drivers/net/ethernet/microchip/lan966x/lan966x_mirror.c