1 // SPDX-License-Identifier: GPL-2.0+
2 
3 #include "lan966x_main.h"
4 
lan966x_mirror_port_add(struct lan966x_port * port,struct flow_action_entry * action,unsigned long mirror_id,bool ingress,struct netlink_ext_ack * extack)5 int 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 
lan966x_mirror_port_del(struct lan966x_port * port,bool ingress,struct netlink_ext_ack * extack)65 int 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 
lan966x_mirror_port_stats(struct lan966x_port * port,struct flow_stats * stats,bool ingress)103 void 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