summaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/microchip/lan966x/lan966x_mirror.c
blob: 7e1ba3f40c35ea7dc46a89ad554cb01ad7280c35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// SPDX-License-Identifier: GPL-2.0+

#include "lan966x_main.h"

int lan966x_mirror_port_add(struct lan966x_port *port,
			    struct flow_action_entry *action,
			    unsigned long mirror_id,
			    bool ingress,
			    struct netlink_ext_ack *extack)
{
	struct lan966x *lan966x = port->lan966x;
	struct lan966x_port *monitor_port;

	if (!lan966x_netdevice_check(action->dev)) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Destination not an lan966x port");
		return -EOPNOTSUPP;
	}

	monitor_port = netdev_priv(action->dev);

	if (lan966x->mirror_mask[ingress] & BIT(port->chip_port)) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Mirror already exists");
		return -EEXIST;
	}

	if (lan966x->mirror_monitor &&
	    lan966x->mirror_monitor != monitor_port) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Cannot change mirror port while in use");
		return -EBUSY;
	}

	if (port == monitor_port) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Cannot mirror the monitor port");
		return -EINVAL;
	}

	lan966x->mirror_mask[ingress] |= BIT(port->chip_port);

	lan966x->mirror_monitor = monitor_port;
	lan_wr(BIT(monitor_port->chip_port), lan966x, ANA_MIRRORPORTS);

	if (ingress) {
		lan_rmw(ANA_PORT_CFG_SRC_MIRROR_ENA_SET(1),
			ANA_PORT_CFG_SRC_MIRROR_ENA,
			lan966x, ANA_PORT_CFG(port->chip_port));
	} else {
		lan_wr(lan966x->mirror_mask[0], lan966x,
		       ANA_EMIRRORPORTS);
	}

	lan966x->mirror_count++;

	if (ingress)
		port->tc.ingress_mirror_id = mirror_id;
	else
		port->tc.egress_mirror_id = mirror_id;

	return 0;
}

int lan966x_mirror_port_del(struct lan966x_port *port,
			    bool ingress,
			    struct netlink_ext_ack *extack)
{
	struct lan966x *lan966x = port->lan966x;

	if (!(lan966x->mirror_mask[ingress] & BIT(port->chip_port))) {
		NL_SET_ERR_MSG_MOD(extack,
				   "There is no mirroring for this port");
		return -ENOENT;
	}

	lan966x->mirror_mask[ingress] &= ~BIT(port->chip_port);

	if (ingress) {
		lan_rmw(ANA_PORT_CFG_SRC_MIRROR_ENA_SET(0),
			ANA_PORT_CFG_SRC_MIRROR_ENA,
			lan966x, ANA_PORT_CFG(port->chip_port));
	} else {
		lan_wr(lan966x->mirror_mask[0], lan966x,
		       ANA_EMIRRORPORTS);
	}

	lan966x->mirror_count--;

	if (lan966x->mirror_count == 0) {
		lan966x->mirror_monitor = NULL;
		lan_wr(0, lan966x, ANA_MIRRORPORTS);
	}

	if (ingress)
		port->tc.ingress_mirror_id = 0;
	else
		port->tc.egress_mirror_id = 0;

	return 0;
}

void lan966x_mirror_port_stats(struct lan966x_port *port,
			       struct flow_stats *stats,
			       bool ingress)
{
	struct rtnl_link_stats64 new_stats;
	struct flow_stats *old_stats;

	old_stats = &port->tc.mirror_stat;
	lan966x_stats_get(port->dev, &new_stats);

	if (ingress) {
		flow_stats_update(stats,
				  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(stats,
				  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;
	}
}