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(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(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(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(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(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(port->dev, &new_stats);
112
113	if (ingress) {
114		flow_stats_update(stats,
115				  new_stats.rx_bytes - old_stats->bytes,
116				  new_stats.rx_packets - old_stats->pkts,
117				  new_stats.rx_dropped - old_stats->drops,
118				  old_stats->lastused,
119				  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(stats,
127				  new_stats.tx_bytes - old_stats->bytes,
128				  new_stats.tx_packets - old_stats->pkts,
129				  new_stats.tx_dropped - old_stats->drops,
130				  old_stats->lastused,
131				  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