xref: /OK3568_Linux_fs/kernel/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2*4882a593Smuzhiyun /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
3*4882a593Smuzhiyun 
4*4882a593Smuzhiyun #include <linux/if_bridge.h>
5*4882a593Smuzhiyun #include <linux/list.h>
6*4882a593Smuzhiyun #include <linux/mutex.h>
7*4882a593Smuzhiyun #include <linux/refcount.h>
8*4882a593Smuzhiyun #include <linux/rtnetlink.h>
9*4882a593Smuzhiyun #include <linux/workqueue.h>
10*4882a593Smuzhiyun #include <net/arp.h>
11*4882a593Smuzhiyun #include <net/gre.h>
12*4882a593Smuzhiyun #include <net/lag.h>
13*4882a593Smuzhiyun #include <net/ndisc.h>
14*4882a593Smuzhiyun #include <net/ip6_tunnel.h>
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include "spectrum.h"
17*4882a593Smuzhiyun #include "spectrum_ipip.h"
18*4882a593Smuzhiyun #include "spectrum_span.h"
19*4882a593Smuzhiyun #include "spectrum_switchdev.h"
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun struct mlxsw_sp_span {
22*4882a593Smuzhiyun 	struct work_struct work;
23*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp;
24*4882a593Smuzhiyun 	const struct mlxsw_sp_span_trigger_ops **span_trigger_ops_arr;
25*4882a593Smuzhiyun 	const struct mlxsw_sp_span_entry_ops **span_entry_ops_arr;
26*4882a593Smuzhiyun 	size_t span_entry_ops_arr_size;
27*4882a593Smuzhiyun 	struct list_head analyzed_ports_list;
28*4882a593Smuzhiyun 	struct mutex analyzed_ports_lock; /* Protects analyzed_ports_list */
29*4882a593Smuzhiyun 	struct list_head trigger_entries_list;
30*4882a593Smuzhiyun 	u16 policer_id_base;
31*4882a593Smuzhiyun 	refcount_t policer_id_base_ref_count;
32*4882a593Smuzhiyun 	atomic_t active_entries_count;
33*4882a593Smuzhiyun 	int entries_count;
34*4882a593Smuzhiyun 	struct mlxsw_sp_span_entry entries[];
35*4882a593Smuzhiyun };
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun struct mlxsw_sp_span_analyzed_port {
38*4882a593Smuzhiyun 	struct list_head list; /* Member of analyzed_ports_list */
39*4882a593Smuzhiyun 	refcount_t ref_count;
40*4882a593Smuzhiyun 	u8 local_port;
41*4882a593Smuzhiyun 	bool ingress;
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun struct mlxsw_sp_span_trigger_entry {
45*4882a593Smuzhiyun 	struct list_head list; /* Member of trigger_entries_list */
46*4882a593Smuzhiyun 	struct mlxsw_sp_span *span;
47*4882a593Smuzhiyun 	const struct mlxsw_sp_span_trigger_ops *ops;
48*4882a593Smuzhiyun 	refcount_t ref_count;
49*4882a593Smuzhiyun 	u8 local_port;
50*4882a593Smuzhiyun 	enum mlxsw_sp_span_trigger trigger;
51*4882a593Smuzhiyun 	struct mlxsw_sp_span_trigger_parms parms;
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun enum mlxsw_sp_span_trigger_type {
55*4882a593Smuzhiyun 	MLXSW_SP_SPAN_TRIGGER_TYPE_PORT,
56*4882a593Smuzhiyun 	MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL,
57*4882a593Smuzhiyun };
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun struct mlxsw_sp_span_trigger_ops {
60*4882a593Smuzhiyun 	int (*bind)(struct mlxsw_sp_span_trigger_entry *trigger_entry);
61*4882a593Smuzhiyun 	void (*unbind)(struct mlxsw_sp_span_trigger_entry *trigger_entry);
62*4882a593Smuzhiyun 	bool (*matches)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
63*4882a593Smuzhiyun 			enum mlxsw_sp_span_trigger trigger,
64*4882a593Smuzhiyun 			struct mlxsw_sp_port *mlxsw_sp_port);
65*4882a593Smuzhiyun 	int (*enable)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
66*4882a593Smuzhiyun 		      struct mlxsw_sp_port *mlxsw_sp_port, u8 tc);
67*4882a593Smuzhiyun 	void (*disable)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
68*4882a593Smuzhiyun 			struct mlxsw_sp_port *mlxsw_sp_port, u8 tc);
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun static void mlxsw_sp_span_respin_work(struct work_struct *work);
72*4882a593Smuzhiyun 
mlxsw_sp_span_occ_get(void * priv)73*4882a593Smuzhiyun static u64 mlxsw_sp_span_occ_get(void *priv)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun 	const struct mlxsw_sp *mlxsw_sp = priv;
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	return atomic_read(&mlxsw_sp->span->active_entries_count);
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun 
mlxsw_sp_span_init(struct mlxsw_sp * mlxsw_sp)80*4882a593Smuzhiyun int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
83*4882a593Smuzhiyun 	struct mlxsw_sp_span *span;
84*4882a593Smuzhiyun 	int i, entries_count, err;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_SPAN))
87*4882a593Smuzhiyun 		return -EIO;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	entries_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_SPAN);
90*4882a593Smuzhiyun 	span = kzalloc(struct_size(span, entries, entries_count), GFP_KERNEL);
91*4882a593Smuzhiyun 	if (!span)
92*4882a593Smuzhiyun 		return -ENOMEM;
93*4882a593Smuzhiyun 	refcount_set(&span->policer_id_base_ref_count, 0);
94*4882a593Smuzhiyun 	span->entries_count = entries_count;
95*4882a593Smuzhiyun 	atomic_set(&span->active_entries_count, 0);
96*4882a593Smuzhiyun 	mutex_init(&span->analyzed_ports_lock);
97*4882a593Smuzhiyun 	INIT_LIST_HEAD(&span->analyzed_ports_list);
98*4882a593Smuzhiyun 	INIT_LIST_HEAD(&span->trigger_entries_list);
99*4882a593Smuzhiyun 	span->mlxsw_sp = mlxsw_sp;
100*4882a593Smuzhiyun 	mlxsw_sp->span = span;
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	for (i = 0; i < mlxsw_sp->span->entries_count; i++)
103*4882a593Smuzhiyun 		mlxsw_sp->span->entries[i].id = i;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	err = mlxsw_sp->span_ops->init(mlxsw_sp);
106*4882a593Smuzhiyun 	if (err)
107*4882a593Smuzhiyun 		goto err_init;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	devlink_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_SPAN,
110*4882a593Smuzhiyun 					  mlxsw_sp_span_occ_get, mlxsw_sp);
111*4882a593Smuzhiyun 	INIT_WORK(&span->work, mlxsw_sp_span_respin_work);
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	return 0;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun err_init:
116*4882a593Smuzhiyun 	mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock);
117*4882a593Smuzhiyun 	kfree(mlxsw_sp->span);
118*4882a593Smuzhiyun 	return err;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun 
mlxsw_sp_span_fini(struct mlxsw_sp * mlxsw_sp)121*4882a593Smuzhiyun void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	cancel_work_sync(&mlxsw_sp->span->work);
126*4882a593Smuzhiyun 	devlink_resource_occ_get_unregister(devlink, MLXSW_SP_RESOURCE_SPAN);
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->trigger_entries_list));
129*4882a593Smuzhiyun 	WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->analyzed_ports_list));
130*4882a593Smuzhiyun 	mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock);
131*4882a593Smuzhiyun 	kfree(mlxsw_sp->span);
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
mlxsw_sp1_span_cpu_can_handle(const struct net_device * dev)134*4882a593Smuzhiyun static bool mlxsw_sp1_span_cpu_can_handle(const struct net_device *dev)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	return !dev;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun 
mlxsw_sp1_span_entry_cpu_parms(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,struct mlxsw_sp_span_parms * sparmsp)139*4882a593Smuzhiyun static int mlxsw_sp1_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp,
140*4882a593Smuzhiyun 					  const struct net_device *to_dev,
141*4882a593Smuzhiyun 					  struct mlxsw_sp_span_parms *sparmsp)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun 	return -EOPNOTSUPP;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun static int
mlxsw_sp1_span_entry_cpu_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)147*4882a593Smuzhiyun mlxsw_sp1_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry,
148*4882a593Smuzhiyun 				   struct mlxsw_sp_span_parms sparms)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun 	return -EOPNOTSUPP;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun static void
mlxsw_sp1_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry * span_entry)154*4882a593Smuzhiyun mlxsw_sp1_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun static const
159*4882a593Smuzhiyun struct mlxsw_sp_span_entry_ops mlxsw_sp1_span_entry_ops_cpu = {
160*4882a593Smuzhiyun 	.is_static = true,
161*4882a593Smuzhiyun 	.can_handle = mlxsw_sp1_span_cpu_can_handle,
162*4882a593Smuzhiyun 	.parms_set = mlxsw_sp1_span_entry_cpu_parms,
163*4882a593Smuzhiyun 	.configure = mlxsw_sp1_span_entry_cpu_configure,
164*4882a593Smuzhiyun 	.deconfigure = mlxsw_sp1_span_entry_cpu_deconfigure,
165*4882a593Smuzhiyun };
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun static int
mlxsw_sp_span_entry_phys_parms(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,struct mlxsw_sp_span_parms * sparmsp)168*4882a593Smuzhiyun mlxsw_sp_span_entry_phys_parms(struct mlxsw_sp *mlxsw_sp,
169*4882a593Smuzhiyun 			       const struct net_device *to_dev,
170*4882a593Smuzhiyun 			       struct mlxsw_sp_span_parms *sparmsp)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun 	sparmsp->dest_port = netdev_priv(to_dev);
173*4882a593Smuzhiyun 	return 0;
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun static int
mlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)177*4882a593Smuzhiyun mlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry *span_entry,
178*4882a593Smuzhiyun 				   struct mlxsw_sp_span_parms sparms)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun 	struct mlxsw_sp_port *dest_port = sparms.dest_port;
181*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
182*4882a593Smuzhiyun 	u8 local_port = dest_port->local_port;
183*4882a593Smuzhiyun 	char mpat_pl[MLXSW_REG_MPAT_LEN];
184*4882a593Smuzhiyun 	int pa_id = span_entry->id;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	/* Create a new port analayzer entry for local_port. */
187*4882a593Smuzhiyun 	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
188*4882a593Smuzhiyun 			    MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
189*4882a593Smuzhiyun 	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
190*4882a593Smuzhiyun 	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun static void
mlxsw_sp_span_entry_deconfigure_common(struct mlxsw_sp_span_entry * span_entry,enum mlxsw_reg_mpat_span_type span_type)196*4882a593Smuzhiyun mlxsw_sp_span_entry_deconfigure_common(struct mlxsw_sp_span_entry *span_entry,
197*4882a593Smuzhiyun 				       enum mlxsw_reg_mpat_span_type span_type)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 	struct mlxsw_sp_port *dest_port = span_entry->parms.dest_port;
200*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
201*4882a593Smuzhiyun 	u8 local_port = dest_port->local_port;
202*4882a593Smuzhiyun 	char mpat_pl[MLXSW_REG_MPAT_LEN];
203*4882a593Smuzhiyun 	int pa_id = span_entry->id;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false, span_type);
206*4882a593Smuzhiyun 	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun static void
mlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry * span_entry)210*4882a593Smuzhiyun mlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry *span_entry)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun 	mlxsw_sp_span_entry_deconfigure_common(span_entry,
213*4882a593Smuzhiyun 					    MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun static const
217*4882a593Smuzhiyun struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = {
218*4882a593Smuzhiyun 	.is_static = true,
219*4882a593Smuzhiyun 	.can_handle = mlxsw_sp_port_dev_check,
220*4882a593Smuzhiyun 	.parms_set = mlxsw_sp_span_entry_phys_parms,
221*4882a593Smuzhiyun 	.configure = mlxsw_sp_span_entry_phys_configure,
222*4882a593Smuzhiyun 	.deconfigure = mlxsw_sp_span_entry_phys_deconfigure,
223*4882a593Smuzhiyun };
224*4882a593Smuzhiyun 
mlxsw_sp_span_dmac(struct neigh_table * tbl,const void * pkey,struct net_device * dev,unsigned char dmac[ETH_ALEN])225*4882a593Smuzhiyun static int mlxsw_sp_span_dmac(struct neigh_table *tbl,
226*4882a593Smuzhiyun 			      const void *pkey,
227*4882a593Smuzhiyun 			      struct net_device *dev,
228*4882a593Smuzhiyun 			      unsigned char dmac[ETH_ALEN])
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun 	struct neighbour *neigh = neigh_lookup(tbl, pkey, dev);
231*4882a593Smuzhiyun 	int err = 0;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	if (!neigh) {
234*4882a593Smuzhiyun 		neigh = neigh_create(tbl, pkey, dev);
235*4882a593Smuzhiyun 		if (IS_ERR(neigh))
236*4882a593Smuzhiyun 			return PTR_ERR(neigh);
237*4882a593Smuzhiyun 	}
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	neigh_event_send(neigh, NULL);
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	read_lock_bh(&neigh->lock);
242*4882a593Smuzhiyun 	if ((neigh->nud_state & NUD_VALID) && !neigh->dead)
243*4882a593Smuzhiyun 		memcpy(dmac, neigh->ha, ETH_ALEN);
244*4882a593Smuzhiyun 	else
245*4882a593Smuzhiyun 		err = -ENOENT;
246*4882a593Smuzhiyun 	read_unlock_bh(&neigh->lock);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	neigh_release(neigh);
249*4882a593Smuzhiyun 	return err;
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun static int
mlxsw_sp_span_entry_unoffloadable(struct mlxsw_sp_span_parms * sparmsp)253*4882a593Smuzhiyun mlxsw_sp_span_entry_unoffloadable(struct mlxsw_sp_span_parms *sparmsp)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun 	sparmsp->dest_port = NULL;
256*4882a593Smuzhiyun 	return 0;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun static struct net_device *
mlxsw_sp_span_entry_bridge_8021q(const struct net_device * br_dev,unsigned char * dmac,u16 * p_vid)260*4882a593Smuzhiyun mlxsw_sp_span_entry_bridge_8021q(const struct net_device *br_dev,
261*4882a593Smuzhiyun 				 unsigned char *dmac,
262*4882a593Smuzhiyun 				 u16 *p_vid)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun 	struct bridge_vlan_info vinfo;
265*4882a593Smuzhiyun 	struct net_device *edev;
266*4882a593Smuzhiyun 	u16 vid = *p_vid;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	if (!vid && WARN_ON(br_vlan_get_pvid(br_dev, &vid)))
269*4882a593Smuzhiyun 		return NULL;
270*4882a593Smuzhiyun 	if (!vid ||
271*4882a593Smuzhiyun 	    br_vlan_get_info(br_dev, vid, &vinfo) ||
272*4882a593Smuzhiyun 	    !(vinfo.flags & BRIDGE_VLAN_INFO_BRENTRY))
273*4882a593Smuzhiyun 		return NULL;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	edev = br_fdb_find_port(br_dev, dmac, vid);
276*4882a593Smuzhiyun 	if (!edev)
277*4882a593Smuzhiyun 		return NULL;
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	if (br_vlan_get_info(edev, vid, &vinfo))
280*4882a593Smuzhiyun 		return NULL;
281*4882a593Smuzhiyun 	if (vinfo.flags & BRIDGE_VLAN_INFO_UNTAGGED)
282*4882a593Smuzhiyun 		*p_vid = 0;
283*4882a593Smuzhiyun 	else
284*4882a593Smuzhiyun 		*p_vid = vid;
285*4882a593Smuzhiyun 	return edev;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun static struct net_device *
mlxsw_sp_span_entry_bridge_8021d(const struct net_device * br_dev,unsigned char * dmac)289*4882a593Smuzhiyun mlxsw_sp_span_entry_bridge_8021d(const struct net_device *br_dev,
290*4882a593Smuzhiyun 				 unsigned char *dmac)
291*4882a593Smuzhiyun {
292*4882a593Smuzhiyun 	return br_fdb_find_port(br_dev, dmac, 0);
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun static struct net_device *
mlxsw_sp_span_entry_bridge(const struct net_device * br_dev,unsigned char dmac[ETH_ALEN],u16 * p_vid)296*4882a593Smuzhiyun mlxsw_sp_span_entry_bridge(const struct net_device *br_dev,
297*4882a593Smuzhiyun 			   unsigned char dmac[ETH_ALEN],
298*4882a593Smuzhiyun 			   u16 *p_vid)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun 	struct mlxsw_sp_bridge_port *bridge_port;
301*4882a593Smuzhiyun 	enum mlxsw_reg_spms_state spms_state;
302*4882a593Smuzhiyun 	struct net_device *dev = NULL;
303*4882a593Smuzhiyun 	struct mlxsw_sp_port *port;
304*4882a593Smuzhiyun 	u8 stp_state;
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	if (br_vlan_enabled(br_dev))
307*4882a593Smuzhiyun 		dev = mlxsw_sp_span_entry_bridge_8021q(br_dev, dmac, p_vid);
308*4882a593Smuzhiyun 	else if (!*p_vid)
309*4882a593Smuzhiyun 		dev = mlxsw_sp_span_entry_bridge_8021d(br_dev, dmac);
310*4882a593Smuzhiyun 	if (!dev)
311*4882a593Smuzhiyun 		return NULL;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	port = mlxsw_sp_port_dev_lower_find(dev);
314*4882a593Smuzhiyun 	if (!port)
315*4882a593Smuzhiyun 		return NULL;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	bridge_port = mlxsw_sp_bridge_port_find(port->mlxsw_sp->bridge, dev);
318*4882a593Smuzhiyun 	if (!bridge_port)
319*4882a593Smuzhiyun 		return NULL;
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 	stp_state = mlxsw_sp_bridge_port_stp_state(bridge_port);
322*4882a593Smuzhiyun 	spms_state = mlxsw_sp_stp_spms_state(stp_state);
323*4882a593Smuzhiyun 	if (spms_state != MLXSW_REG_SPMS_STATE_FORWARDING)
324*4882a593Smuzhiyun 		return NULL;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	return dev;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun static struct net_device *
mlxsw_sp_span_entry_vlan(const struct net_device * vlan_dev,u16 * p_vid)330*4882a593Smuzhiyun mlxsw_sp_span_entry_vlan(const struct net_device *vlan_dev,
331*4882a593Smuzhiyun 			 u16 *p_vid)
332*4882a593Smuzhiyun {
333*4882a593Smuzhiyun 	*p_vid = vlan_dev_vlan_id(vlan_dev);
334*4882a593Smuzhiyun 	return vlan_dev_real_dev(vlan_dev);
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun static struct net_device *
mlxsw_sp_span_entry_lag(struct net_device * lag_dev)338*4882a593Smuzhiyun mlxsw_sp_span_entry_lag(struct net_device *lag_dev)
339*4882a593Smuzhiyun {
340*4882a593Smuzhiyun 	struct net_device *dev;
341*4882a593Smuzhiyun 	struct list_head *iter;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	netdev_for_each_lower_dev(lag_dev, dev, iter)
344*4882a593Smuzhiyun 		if (netif_carrier_ok(dev) &&
345*4882a593Smuzhiyun 		    net_lag_port_dev_txable(dev) &&
346*4882a593Smuzhiyun 		    mlxsw_sp_port_dev_check(dev))
347*4882a593Smuzhiyun 			return dev;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	return NULL;
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun static __maybe_unused int
mlxsw_sp_span_entry_tunnel_parms_common(struct net_device * edev,union mlxsw_sp_l3addr saddr,union mlxsw_sp_l3addr daddr,union mlxsw_sp_l3addr gw,__u8 ttl,struct neigh_table * tbl,struct mlxsw_sp_span_parms * sparmsp)353*4882a593Smuzhiyun mlxsw_sp_span_entry_tunnel_parms_common(struct net_device *edev,
354*4882a593Smuzhiyun 					union mlxsw_sp_l3addr saddr,
355*4882a593Smuzhiyun 					union mlxsw_sp_l3addr daddr,
356*4882a593Smuzhiyun 					union mlxsw_sp_l3addr gw,
357*4882a593Smuzhiyun 					__u8 ttl,
358*4882a593Smuzhiyun 					struct neigh_table *tbl,
359*4882a593Smuzhiyun 					struct mlxsw_sp_span_parms *sparmsp)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun 	unsigned char dmac[ETH_ALEN];
362*4882a593Smuzhiyun 	u16 vid = 0;
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	if (mlxsw_sp_l3addr_is_zero(gw))
365*4882a593Smuzhiyun 		gw = daddr;
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	if (!edev || mlxsw_sp_span_dmac(tbl, &gw, edev, dmac))
368*4882a593Smuzhiyun 		goto unoffloadable;
369*4882a593Smuzhiyun 
370*4882a593Smuzhiyun 	if (is_vlan_dev(edev))
371*4882a593Smuzhiyun 		edev = mlxsw_sp_span_entry_vlan(edev, &vid);
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	if (netif_is_bridge_master(edev)) {
374*4882a593Smuzhiyun 		edev = mlxsw_sp_span_entry_bridge(edev, dmac, &vid);
375*4882a593Smuzhiyun 		if (!edev)
376*4882a593Smuzhiyun 			goto unoffloadable;
377*4882a593Smuzhiyun 	}
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	if (is_vlan_dev(edev)) {
380*4882a593Smuzhiyun 		if (vid || !(edev->flags & IFF_UP))
381*4882a593Smuzhiyun 			goto unoffloadable;
382*4882a593Smuzhiyun 		edev = mlxsw_sp_span_entry_vlan(edev, &vid);
383*4882a593Smuzhiyun 	}
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	if (netif_is_lag_master(edev)) {
386*4882a593Smuzhiyun 		if (!(edev->flags & IFF_UP))
387*4882a593Smuzhiyun 			goto unoffloadable;
388*4882a593Smuzhiyun 		edev = mlxsw_sp_span_entry_lag(edev);
389*4882a593Smuzhiyun 		if (!edev)
390*4882a593Smuzhiyun 			goto unoffloadable;
391*4882a593Smuzhiyun 	}
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	if (!mlxsw_sp_port_dev_check(edev))
394*4882a593Smuzhiyun 		goto unoffloadable;
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	sparmsp->dest_port = netdev_priv(edev);
397*4882a593Smuzhiyun 	sparmsp->ttl = ttl;
398*4882a593Smuzhiyun 	memcpy(sparmsp->dmac, dmac, ETH_ALEN);
399*4882a593Smuzhiyun 	memcpy(sparmsp->smac, edev->dev_addr, ETH_ALEN);
400*4882a593Smuzhiyun 	sparmsp->saddr = saddr;
401*4882a593Smuzhiyun 	sparmsp->daddr = daddr;
402*4882a593Smuzhiyun 	sparmsp->vid = vid;
403*4882a593Smuzhiyun 	return 0;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun unoffloadable:
406*4882a593Smuzhiyun 	return mlxsw_sp_span_entry_unoffloadable(sparmsp);
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_NET_IPGRE)
410*4882a593Smuzhiyun static struct net_device *
mlxsw_sp_span_gretap4_route(const struct net_device * to_dev,__be32 * saddrp,__be32 * daddrp)411*4882a593Smuzhiyun mlxsw_sp_span_gretap4_route(const struct net_device *to_dev,
412*4882a593Smuzhiyun 			    __be32 *saddrp, __be32 *daddrp)
413*4882a593Smuzhiyun {
414*4882a593Smuzhiyun 	struct ip_tunnel *tun = netdev_priv(to_dev);
415*4882a593Smuzhiyun 	struct net_device *dev = NULL;
416*4882a593Smuzhiyun 	struct ip_tunnel_parm parms;
417*4882a593Smuzhiyun 	struct rtable *rt = NULL;
418*4882a593Smuzhiyun 	struct flowi4 fl4;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	/* We assume "dev" stays valid after rt is put. */
421*4882a593Smuzhiyun 	ASSERT_RTNL();
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 	parms = mlxsw_sp_ipip_netdev_parms4(to_dev);
424*4882a593Smuzhiyun 	ip_tunnel_init_flow(&fl4, parms.iph.protocol, *daddrp, *saddrp,
425*4882a593Smuzhiyun 			    0, 0, parms.link, tun->fwmark, 0);
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	rt = ip_route_output_key(tun->net, &fl4);
428*4882a593Smuzhiyun 	if (IS_ERR(rt))
429*4882a593Smuzhiyun 		return NULL;
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	if (rt->rt_type != RTN_UNICAST)
432*4882a593Smuzhiyun 		goto out;
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	dev = rt->dst.dev;
435*4882a593Smuzhiyun 	*saddrp = fl4.saddr;
436*4882a593Smuzhiyun 	if (rt->rt_gw_family == AF_INET)
437*4882a593Smuzhiyun 		*daddrp = rt->rt_gw4;
438*4882a593Smuzhiyun 	/* can not offload if route has an IPv6 gateway */
439*4882a593Smuzhiyun 	else if (rt->rt_gw_family == AF_INET6)
440*4882a593Smuzhiyun 		dev = NULL;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun out:
443*4882a593Smuzhiyun 	ip_rt_put(rt);
444*4882a593Smuzhiyun 	return dev;
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun static int
mlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,struct mlxsw_sp_span_parms * sparmsp)448*4882a593Smuzhiyun mlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp,
449*4882a593Smuzhiyun 				  const struct net_device *to_dev,
450*4882a593Smuzhiyun 				  struct mlxsw_sp_span_parms *sparmsp)
451*4882a593Smuzhiyun {
452*4882a593Smuzhiyun 	struct ip_tunnel_parm tparm = mlxsw_sp_ipip_netdev_parms4(to_dev);
453*4882a593Smuzhiyun 	union mlxsw_sp_l3addr saddr = { .addr4 = tparm.iph.saddr };
454*4882a593Smuzhiyun 	union mlxsw_sp_l3addr daddr = { .addr4 = tparm.iph.daddr };
455*4882a593Smuzhiyun 	bool inherit_tos = tparm.iph.tos & 0x1;
456*4882a593Smuzhiyun 	bool inherit_ttl = !tparm.iph.ttl;
457*4882a593Smuzhiyun 	union mlxsw_sp_l3addr gw = daddr;
458*4882a593Smuzhiyun 	struct net_device *l3edev;
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	if (!(to_dev->flags & IFF_UP) ||
461*4882a593Smuzhiyun 	    /* Reject tunnels with GRE keys, checksums, etc. */
462*4882a593Smuzhiyun 	    tparm.i_flags || tparm.o_flags ||
463*4882a593Smuzhiyun 	    /* Require a fixed TTL and a TOS copied from the mirrored packet. */
464*4882a593Smuzhiyun 	    inherit_ttl || !inherit_tos ||
465*4882a593Smuzhiyun 	    /* A destination address may not be "any". */
466*4882a593Smuzhiyun 	    mlxsw_sp_l3addr_is_zero(daddr))
467*4882a593Smuzhiyun 		return mlxsw_sp_span_entry_unoffloadable(sparmsp);
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun 	l3edev = mlxsw_sp_span_gretap4_route(to_dev, &saddr.addr4, &gw.addr4);
470*4882a593Smuzhiyun 	return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
471*4882a593Smuzhiyun 						       tparm.iph.ttl,
472*4882a593Smuzhiyun 						       &arp_tbl, sparmsp);
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun static int
mlxsw_sp_span_entry_gretap4_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)476*4882a593Smuzhiyun mlxsw_sp_span_entry_gretap4_configure(struct mlxsw_sp_span_entry *span_entry,
477*4882a593Smuzhiyun 				      struct mlxsw_sp_span_parms sparms)
478*4882a593Smuzhiyun {
479*4882a593Smuzhiyun 	struct mlxsw_sp_port *dest_port = sparms.dest_port;
480*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
481*4882a593Smuzhiyun 	u8 local_port = dest_port->local_port;
482*4882a593Smuzhiyun 	char mpat_pl[MLXSW_REG_MPAT_LEN];
483*4882a593Smuzhiyun 	int pa_id = span_entry->id;
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	/* Create a new port analayzer entry for local_port. */
486*4882a593Smuzhiyun 	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
487*4882a593Smuzhiyun 			    MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
488*4882a593Smuzhiyun 	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
489*4882a593Smuzhiyun 	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
490*4882a593Smuzhiyun 	mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
491*4882a593Smuzhiyun 	mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
492*4882a593Smuzhiyun 				    MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
493*4882a593Smuzhiyun 				    sparms.dmac, !!sparms.vid);
494*4882a593Smuzhiyun 	mlxsw_reg_mpat_eth_rspan_l3_ipv4_pack(mpat_pl,
495*4882a593Smuzhiyun 					      sparms.ttl, sparms.smac,
496*4882a593Smuzhiyun 					      be32_to_cpu(sparms.saddr.addr4),
497*4882a593Smuzhiyun 					      be32_to_cpu(sparms.daddr.addr4));
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
500*4882a593Smuzhiyun }
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun static void
mlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry * span_entry)503*4882a593Smuzhiyun mlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry *span_entry)
504*4882a593Smuzhiyun {
505*4882a593Smuzhiyun 	mlxsw_sp_span_entry_deconfigure_common(span_entry,
506*4882a593Smuzhiyun 					MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap4 = {
510*4882a593Smuzhiyun 	.can_handle = netif_is_gretap,
511*4882a593Smuzhiyun 	.parms_set = mlxsw_sp_span_entry_gretap4_parms,
512*4882a593Smuzhiyun 	.configure = mlxsw_sp_span_entry_gretap4_configure,
513*4882a593Smuzhiyun 	.deconfigure = mlxsw_sp_span_entry_gretap4_deconfigure,
514*4882a593Smuzhiyun };
515*4882a593Smuzhiyun #endif
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_IPV6_GRE)
518*4882a593Smuzhiyun static struct net_device *
mlxsw_sp_span_gretap6_route(const struct net_device * to_dev,struct in6_addr * saddrp,struct in6_addr * daddrp)519*4882a593Smuzhiyun mlxsw_sp_span_gretap6_route(const struct net_device *to_dev,
520*4882a593Smuzhiyun 			    struct in6_addr *saddrp,
521*4882a593Smuzhiyun 			    struct in6_addr *daddrp)
522*4882a593Smuzhiyun {
523*4882a593Smuzhiyun 	struct ip6_tnl *t = netdev_priv(to_dev);
524*4882a593Smuzhiyun 	struct flowi6 fl6 = t->fl.u.ip6;
525*4882a593Smuzhiyun 	struct net_device *dev = NULL;
526*4882a593Smuzhiyun 	struct dst_entry *dst;
527*4882a593Smuzhiyun 	struct rt6_info *rt6;
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	/* We assume "dev" stays valid after dst is released. */
530*4882a593Smuzhiyun 	ASSERT_RTNL();
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	fl6.flowi6_mark = t->parms.fwmark;
533*4882a593Smuzhiyun 	if (!ip6_tnl_xmit_ctl(t, &fl6.saddr, &fl6.daddr))
534*4882a593Smuzhiyun 		return NULL;
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	dst = ip6_route_output(t->net, NULL, &fl6);
537*4882a593Smuzhiyun 	if (!dst || dst->error)
538*4882a593Smuzhiyun 		goto out;
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	rt6 = container_of(dst, struct rt6_info, dst);
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	dev = dst->dev;
543*4882a593Smuzhiyun 	*saddrp = fl6.saddr;
544*4882a593Smuzhiyun 	*daddrp = rt6->rt6i_gateway;
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun out:
547*4882a593Smuzhiyun 	dst_release(dst);
548*4882a593Smuzhiyun 	return dev;
549*4882a593Smuzhiyun }
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun static int
mlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,struct mlxsw_sp_span_parms * sparmsp)552*4882a593Smuzhiyun mlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp,
553*4882a593Smuzhiyun 				  const struct net_device *to_dev,
554*4882a593Smuzhiyun 				  struct mlxsw_sp_span_parms *sparmsp)
555*4882a593Smuzhiyun {
556*4882a593Smuzhiyun 	struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(to_dev);
557*4882a593Smuzhiyun 	bool inherit_tos = tparm.flags & IP6_TNL_F_USE_ORIG_TCLASS;
558*4882a593Smuzhiyun 	union mlxsw_sp_l3addr saddr = { .addr6 = tparm.laddr };
559*4882a593Smuzhiyun 	union mlxsw_sp_l3addr daddr = { .addr6 = tparm.raddr };
560*4882a593Smuzhiyun 	bool inherit_ttl = !tparm.hop_limit;
561*4882a593Smuzhiyun 	union mlxsw_sp_l3addr gw = daddr;
562*4882a593Smuzhiyun 	struct net_device *l3edev;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	if (!(to_dev->flags & IFF_UP) ||
565*4882a593Smuzhiyun 	    /* Reject tunnels with GRE keys, checksums, etc. */
566*4882a593Smuzhiyun 	    tparm.i_flags || tparm.o_flags ||
567*4882a593Smuzhiyun 	    /* Require a fixed TTL and a TOS copied from the mirrored packet. */
568*4882a593Smuzhiyun 	    inherit_ttl || !inherit_tos ||
569*4882a593Smuzhiyun 	    /* A destination address may not be "any". */
570*4882a593Smuzhiyun 	    mlxsw_sp_l3addr_is_zero(daddr))
571*4882a593Smuzhiyun 		return mlxsw_sp_span_entry_unoffloadable(sparmsp);
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	l3edev = mlxsw_sp_span_gretap6_route(to_dev, &saddr.addr6, &gw.addr6);
574*4882a593Smuzhiyun 	return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
575*4882a593Smuzhiyun 						       tparm.hop_limit,
576*4882a593Smuzhiyun 						       &nd_tbl, sparmsp);
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun static int
mlxsw_sp_span_entry_gretap6_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)580*4882a593Smuzhiyun mlxsw_sp_span_entry_gretap6_configure(struct mlxsw_sp_span_entry *span_entry,
581*4882a593Smuzhiyun 				      struct mlxsw_sp_span_parms sparms)
582*4882a593Smuzhiyun {
583*4882a593Smuzhiyun 	struct mlxsw_sp_port *dest_port = sparms.dest_port;
584*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
585*4882a593Smuzhiyun 	u8 local_port = dest_port->local_port;
586*4882a593Smuzhiyun 	char mpat_pl[MLXSW_REG_MPAT_LEN];
587*4882a593Smuzhiyun 	int pa_id = span_entry->id;
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	/* Create a new port analayzer entry for local_port. */
590*4882a593Smuzhiyun 	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
591*4882a593Smuzhiyun 			    MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
592*4882a593Smuzhiyun 	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
593*4882a593Smuzhiyun 	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
594*4882a593Smuzhiyun 	mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
595*4882a593Smuzhiyun 	mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
596*4882a593Smuzhiyun 				    MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
597*4882a593Smuzhiyun 				    sparms.dmac, !!sparms.vid);
598*4882a593Smuzhiyun 	mlxsw_reg_mpat_eth_rspan_l3_ipv6_pack(mpat_pl, sparms.ttl, sparms.smac,
599*4882a593Smuzhiyun 					      sparms.saddr.addr6,
600*4882a593Smuzhiyun 					      sparms.daddr.addr6);
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
603*4882a593Smuzhiyun }
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun static void
mlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry * span_entry)606*4882a593Smuzhiyun mlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry *span_entry)
607*4882a593Smuzhiyun {
608*4882a593Smuzhiyun 	mlxsw_sp_span_entry_deconfigure_common(span_entry,
609*4882a593Smuzhiyun 					MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
610*4882a593Smuzhiyun }
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun static const
613*4882a593Smuzhiyun struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap6 = {
614*4882a593Smuzhiyun 	.can_handle = netif_is_ip6gretap,
615*4882a593Smuzhiyun 	.parms_set = mlxsw_sp_span_entry_gretap6_parms,
616*4882a593Smuzhiyun 	.configure = mlxsw_sp_span_entry_gretap6_configure,
617*4882a593Smuzhiyun 	.deconfigure = mlxsw_sp_span_entry_gretap6_deconfigure,
618*4882a593Smuzhiyun };
619*4882a593Smuzhiyun #endif
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun static bool
mlxsw_sp_span_vlan_can_handle(const struct net_device * dev)622*4882a593Smuzhiyun mlxsw_sp_span_vlan_can_handle(const struct net_device *dev)
623*4882a593Smuzhiyun {
624*4882a593Smuzhiyun 	return is_vlan_dev(dev) &&
625*4882a593Smuzhiyun 	       mlxsw_sp_port_dev_check(vlan_dev_real_dev(dev));
626*4882a593Smuzhiyun }
627*4882a593Smuzhiyun 
628*4882a593Smuzhiyun static int
mlxsw_sp_span_entry_vlan_parms(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,struct mlxsw_sp_span_parms * sparmsp)629*4882a593Smuzhiyun mlxsw_sp_span_entry_vlan_parms(struct mlxsw_sp *mlxsw_sp,
630*4882a593Smuzhiyun 			       const struct net_device *to_dev,
631*4882a593Smuzhiyun 			       struct mlxsw_sp_span_parms *sparmsp)
632*4882a593Smuzhiyun {
633*4882a593Smuzhiyun 	struct net_device *real_dev;
634*4882a593Smuzhiyun 	u16 vid;
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	if (!(to_dev->flags & IFF_UP))
637*4882a593Smuzhiyun 		return mlxsw_sp_span_entry_unoffloadable(sparmsp);
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	real_dev = mlxsw_sp_span_entry_vlan(to_dev, &vid);
640*4882a593Smuzhiyun 	sparmsp->dest_port = netdev_priv(real_dev);
641*4882a593Smuzhiyun 	sparmsp->vid = vid;
642*4882a593Smuzhiyun 	return 0;
643*4882a593Smuzhiyun }
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun static int
mlxsw_sp_span_entry_vlan_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)646*4882a593Smuzhiyun mlxsw_sp_span_entry_vlan_configure(struct mlxsw_sp_span_entry *span_entry,
647*4882a593Smuzhiyun 				   struct mlxsw_sp_span_parms sparms)
648*4882a593Smuzhiyun {
649*4882a593Smuzhiyun 	struct mlxsw_sp_port *dest_port = sparms.dest_port;
650*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
651*4882a593Smuzhiyun 	u8 local_port = dest_port->local_port;
652*4882a593Smuzhiyun 	char mpat_pl[MLXSW_REG_MPAT_LEN];
653*4882a593Smuzhiyun 	int pa_id = span_entry->id;
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun 	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
656*4882a593Smuzhiyun 			    MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH);
657*4882a593Smuzhiyun 	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
658*4882a593Smuzhiyun 	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
659*4882a593Smuzhiyun 	mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
662*4882a593Smuzhiyun }
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun static void
mlxsw_sp_span_entry_vlan_deconfigure(struct mlxsw_sp_span_entry * span_entry)665*4882a593Smuzhiyun mlxsw_sp_span_entry_vlan_deconfigure(struct mlxsw_sp_span_entry *span_entry)
666*4882a593Smuzhiyun {
667*4882a593Smuzhiyun 	mlxsw_sp_span_entry_deconfigure_common(span_entry,
668*4882a593Smuzhiyun 					MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH);
669*4882a593Smuzhiyun }
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun static const
672*4882a593Smuzhiyun struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_vlan = {
673*4882a593Smuzhiyun 	.can_handle = mlxsw_sp_span_vlan_can_handle,
674*4882a593Smuzhiyun 	.parms_set = mlxsw_sp_span_entry_vlan_parms,
675*4882a593Smuzhiyun 	.configure = mlxsw_sp_span_entry_vlan_configure,
676*4882a593Smuzhiyun 	.deconfigure = mlxsw_sp_span_entry_vlan_deconfigure,
677*4882a593Smuzhiyun };
678*4882a593Smuzhiyun 
679*4882a593Smuzhiyun static const
680*4882a593Smuzhiyun struct mlxsw_sp_span_entry_ops *mlxsw_sp1_span_entry_ops_arr[] = {
681*4882a593Smuzhiyun 	&mlxsw_sp1_span_entry_ops_cpu,
682*4882a593Smuzhiyun 	&mlxsw_sp_span_entry_ops_phys,
683*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_NET_IPGRE)
684*4882a593Smuzhiyun 	&mlxsw_sp_span_entry_ops_gretap4,
685*4882a593Smuzhiyun #endif
686*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_IPV6_GRE)
687*4882a593Smuzhiyun 	&mlxsw_sp_span_entry_ops_gretap6,
688*4882a593Smuzhiyun #endif
689*4882a593Smuzhiyun 	&mlxsw_sp_span_entry_ops_vlan,
690*4882a593Smuzhiyun };
691*4882a593Smuzhiyun 
mlxsw_sp2_span_cpu_can_handle(const struct net_device * dev)692*4882a593Smuzhiyun static bool mlxsw_sp2_span_cpu_can_handle(const struct net_device *dev)
693*4882a593Smuzhiyun {
694*4882a593Smuzhiyun 	return !dev;
695*4882a593Smuzhiyun }
696*4882a593Smuzhiyun 
mlxsw_sp2_span_entry_cpu_parms(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,struct mlxsw_sp_span_parms * sparmsp)697*4882a593Smuzhiyun static int mlxsw_sp2_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp,
698*4882a593Smuzhiyun 					  const struct net_device *to_dev,
699*4882a593Smuzhiyun 					  struct mlxsw_sp_span_parms *sparmsp)
700*4882a593Smuzhiyun {
701*4882a593Smuzhiyun 	sparmsp->dest_port = mlxsw_sp->ports[MLXSW_PORT_CPU_PORT];
702*4882a593Smuzhiyun 	return 0;
703*4882a593Smuzhiyun }
704*4882a593Smuzhiyun 
705*4882a593Smuzhiyun static int
mlxsw_sp2_span_entry_cpu_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)706*4882a593Smuzhiyun mlxsw_sp2_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry,
707*4882a593Smuzhiyun 				   struct mlxsw_sp_span_parms sparms)
708*4882a593Smuzhiyun {
709*4882a593Smuzhiyun 	/* Mirroring to the CPU port is like mirroring to any other physical
710*4882a593Smuzhiyun 	 * port. Its local port is used instead of that of the physical port.
711*4882a593Smuzhiyun 	 */
712*4882a593Smuzhiyun 	return mlxsw_sp_span_entry_phys_configure(span_entry, sparms);
713*4882a593Smuzhiyun }
714*4882a593Smuzhiyun 
715*4882a593Smuzhiyun static void
mlxsw_sp2_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry * span_entry)716*4882a593Smuzhiyun mlxsw_sp2_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry)
717*4882a593Smuzhiyun {
718*4882a593Smuzhiyun 	enum mlxsw_reg_mpat_span_type span_type;
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun 	span_type = MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH;
721*4882a593Smuzhiyun 	mlxsw_sp_span_entry_deconfigure_common(span_entry, span_type);
722*4882a593Smuzhiyun }
723*4882a593Smuzhiyun 
724*4882a593Smuzhiyun static const
725*4882a593Smuzhiyun struct mlxsw_sp_span_entry_ops mlxsw_sp2_span_entry_ops_cpu = {
726*4882a593Smuzhiyun 	.is_static = true,
727*4882a593Smuzhiyun 	.can_handle = mlxsw_sp2_span_cpu_can_handle,
728*4882a593Smuzhiyun 	.parms_set = mlxsw_sp2_span_entry_cpu_parms,
729*4882a593Smuzhiyun 	.configure = mlxsw_sp2_span_entry_cpu_configure,
730*4882a593Smuzhiyun 	.deconfigure = mlxsw_sp2_span_entry_cpu_deconfigure,
731*4882a593Smuzhiyun };
732*4882a593Smuzhiyun 
733*4882a593Smuzhiyun static const
734*4882a593Smuzhiyun struct mlxsw_sp_span_entry_ops *mlxsw_sp2_span_entry_ops_arr[] = {
735*4882a593Smuzhiyun 	&mlxsw_sp2_span_entry_ops_cpu,
736*4882a593Smuzhiyun 	&mlxsw_sp_span_entry_ops_phys,
737*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_NET_IPGRE)
738*4882a593Smuzhiyun 	&mlxsw_sp_span_entry_ops_gretap4,
739*4882a593Smuzhiyun #endif
740*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_IPV6_GRE)
741*4882a593Smuzhiyun 	&mlxsw_sp_span_entry_ops_gretap6,
742*4882a593Smuzhiyun #endif
743*4882a593Smuzhiyun 	&mlxsw_sp_span_entry_ops_vlan,
744*4882a593Smuzhiyun };
745*4882a593Smuzhiyun 
746*4882a593Smuzhiyun static int
mlxsw_sp_span_entry_nop_parms(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,struct mlxsw_sp_span_parms * sparmsp)747*4882a593Smuzhiyun mlxsw_sp_span_entry_nop_parms(struct mlxsw_sp *mlxsw_sp,
748*4882a593Smuzhiyun 			      const struct net_device *to_dev,
749*4882a593Smuzhiyun 			      struct mlxsw_sp_span_parms *sparmsp)
750*4882a593Smuzhiyun {
751*4882a593Smuzhiyun 	return mlxsw_sp_span_entry_unoffloadable(sparmsp);
752*4882a593Smuzhiyun }
753*4882a593Smuzhiyun 
754*4882a593Smuzhiyun static int
mlxsw_sp_span_entry_nop_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)755*4882a593Smuzhiyun mlxsw_sp_span_entry_nop_configure(struct mlxsw_sp_span_entry *span_entry,
756*4882a593Smuzhiyun 				  struct mlxsw_sp_span_parms sparms)
757*4882a593Smuzhiyun {
758*4882a593Smuzhiyun 	return 0;
759*4882a593Smuzhiyun }
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun static void
mlxsw_sp_span_entry_nop_deconfigure(struct mlxsw_sp_span_entry * span_entry)762*4882a593Smuzhiyun mlxsw_sp_span_entry_nop_deconfigure(struct mlxsw_sp_span_entry *span_entry)
763*4882a593Smuzhiyun {
764*4882a593Smuzhiyun }
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_nop = {
767*4882a593Smuzhiyun 	.parms_set = mlxsw_sp_span_entry_nop_parms,
768*4882a593Smuzhiyun 	.configure = mlxsw_sp_span_entry_nop_configure,
769*4882a593Smuzhiyun 	.deconfigure = mlxsw_sp_span_entry_nop_deconfigure,
770*4882a593Smuzhiyun };
771*4882a593Smuzhiyun 
772*4882a593Smuzhiyun static void
mlxsw_sp_span_entry_configure(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)773*4882a593Smuzhiyun mlxsw_sp_span_entry_configure(struct mlxsw_sp *mlxsw_sp,
774*4882a593Smuzhiyun 			      struct mlxsw_sp_span_entry *span_entry,
775*4882a593Smuzhiyun 			      struct mlxsw_sp_span_parms sparms)
776*4882a593Smuzhiyun {
777*4882a593Smuzhiyun 	int err;
778*4882a593Smuzhiyun 
779*4882a593Smuzhiyun 	if (!sparms.dest_port)
780*4882a593Smuzhiyun 		goto set_parms;
781*4882a593Smuzhiyun 
782*4882a593Smuzhiyun 	if (sparms.dest_port->mlxsw_sp != mlxsw_sp) {
783*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev,
784*4882a593Smuzhiyun 			"Cannot mirror to a port which belongs to a different mlxsw instance\n");
785*4882a593Smuzhiyun 		sparms.dest_port = NULL;
786*4882a593Smuzhiyun 		goto set_parms;
787*4882a593Smuzhiyun 	}
788*4882a593Smuzhiyun 
789*4882a593Smuzhiyun 	err = span_entry->ops->configure(span_entry, sparms);
790*4882a593Smuzhiyun 	if (err) {
791*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Failed to offload mirror\n");
792*4882a593Smuzhiyun 		sparms.dest_port = NULL;
793*4882a593Smuzhiyun 		goto set_parms;
794*4882a593Smuzhiyun 	}
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun set_parms:
797*4882a593Smuzhiyun 	span_entry->parms = sparms;
798*4882a593Smuzhiyun }
799*4882a593Smuzhiyun 
800*4882a593Smuzhiyun static void
mlxsw_sp_span_entry_deconfigure(struct mlxsw_sp_span_entry * span_entry)801*4882a593Smuzhiyun mlxsw_sp_span_entry_deconfigure(struct mlxsw_sp_span_entry *span_entry)
802*4882a593Smuzhiyun {
803*4882a593Smuzhiyun 	if (span_entry->parms.dest_port)
804*4882a593Smuzhiyun 		span_entry->ops->deconfigure(span_entry);
805*4882a593Smuzhiyun }
806*4882a593Smuzhiyun 
mlxsw_sp_span_policer_id_base_set(struct mlxsw_sp_span * span,u16 policer_id)807*4882a593Smuzhiyun static int mlxsw_sp_span_policer_id_base_set(struct mlxsw_sp_span *span,
808*4882a593Smuzhiyun 					     u16 policer_id)
809*4882a593Smuzhiyun {
810*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = span->mlxsw_sp;
811*4882a593Smuzhiyun 	u16 policer_id_base;
812*4882a593Smuzhiyun 	int err;
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun 	/* Policers set on SPAN agents must be in the range of
815*4882a593Smuzhiyun 	 * `policer_id_base .. policer_id_base + max_span_agents - 1`. If the
816*4882a593Smuzhiyun 	 * base is set and the new policer is not within the range, then we
817*4882a593Smuzhiyun 	 * must error out.
818*4882a593Smuzhiyun 	 */
819*4882a593Smuzhiyun 	if (refcount_read(&span->policer_id_base_ref_count)) {
820*4882a593Smuzhiyun 		if (policer_id < span->policer_id_base ||
821*4882a593Smuzhiyun 		    policer_id >= span->policer_id_base + span->entries_count)
822*4882a593Smuzhiyun 			return -EINVAL;
823*4882a593Smuzhiyun 
824*4882a593Smuzhiyun 		refcount_inc(&span->policer_id_base_ref_count);
825*4882a593Smuzhiyun 		return 0;
826*4882a593Smuzhiyun 	}
827*4882a593Smuzhiyun 
828*4882a593Smuzhiyun 	/* Base must be even. */
829*4882a593Smuzhiyun 	policer_id_base = policer_id % 2 == 0 ? policer_id : policer_id - 1;
830*4882a593Smuzhiyun 	err = mlxsw_sp->span_ops->policer_id_base_set(mlxsw_sp,
831*4882a593Smuzhiyun 						      policer_id_base);
832*4882a593Smuzhiyun 	if (err)
833*4882a593Smuzhiyun 		return err;
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun 	span->policer_id_base = policer_id_base;
836*4882a593Smuzhiyun 	refcount_set(&span->policer_id_base_ref_count, 1);
837*4882a593Smuzhiyun 
838*4882a593Smuzhiyun 	return 0;
839*4882a593Smuzhiyun }
840*4882a593Smuzhiyun 
mlxsw_sp_span_policer_id_base_unset(struct mlxsw_sp_span * span)841*4882a593Smuzhiyun static void mlxsw_sp_span_policer_id_base_unset(struct mlxsw_sp_span *span)
842*4882a593Smuzhiyun {
843*4882a593Smuzhiyun 	if (refcount_dec_and_test(&span->policer_id_base_ref_count))
844*4882a593Smuzhiyun 		span->policer_id_base = 0;
845*4882a593Smuzhiyun }
846*4882a593Smuzhiyun 
847*4882a593Smuzhiyun static struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_create(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,const struct mlxsw_sp_span_entry_ops * ops,struct mlxsw_sp_span_parms sparms)848*4882a593Smuzhiyun mlxsw_sp_span_entry_create(struct mlxsw_sp *mlxsw_sp,
849*4882a593Smuzhiyun 			   const struct net_device *to_dev,
850*4882a593Smuzhiyun 			   const struct mlxsw_sp_span_entry_ops *ops,
851*4882a593Smuzhiyun 			   struct mlxsw_sp_span_parms sparms)
852*4882a593Smuzhiyun {
853*4882a593Smuzhiyun 	struct mlxsw_sp_span_entry *span_entry = NULL;
854*4882a593Smuzhiyun 	int i;
855*4882a593Smuzhiyun 
856*4882a593Smuzhiyun 	/* find a free entry to use */
857*4882a593Smuzhiyun 	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
858*4882a593Smuzhiyun 		if (!refcount_read(&mlxsw_sp->span->entries[i].ref_count)) {
859*4882a593Smuzhiyun 			span_entry = &mlxsw_sp->span->entries[i];
860*4882a593Smuzhiyun 			break;
861*4882a593Smuzhiyun 		}
862*4882a593Smuzhiyun 	}
863*4882a593Smuzhiyun 	if (!span_entry)
864*4882a593Smuzhiyun 		return NULL;
865*4882a593Smuzhiyun 
866*4882a593Smuzhiyun 	if (sparms.policer_enable) {
867*4882a593Smuzhiyun 		int err;
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun 		err = mlxsw_sp_span_policer_id_base_set(mlxsw_sp->span,
870*4882a593Smuzhiyun 							sparms.policer_id);
871*4882a593Smuzhiyun 		if (err)
872*4882a593Smuzhiyun 			return NULL;
873*4882a593Smuzhiyun 	}
874*4882a593Smuzhiyun 
875*4882a593Smuzhiyun 	atomic_inc(&mlxsw_sp->span->active_entries_count);
876*4882a593Smuzhiyun 	span_entry->ops = ops;
877*4882a593Smuzhiyun 	refcount_set(&span_entry->ref_count, 1);
878*4882a593Smuzhiyun 	span_entry->to_dev = to_dev;
879*4882a593Smuzhiyun 	mlxsw_sp_span_entry_configure(mlxsw_sp, span_entry, sparms);
880*4882a593Smuzhiyun 
881*4882a593Smuzhiyun 	return span_entry;
882*4882a593Smuzhiyun }
883*4882a593Smuzhiyun 
mlxsw_sp_span_entry_destroy(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_span_entry * span_entry)884*4882a593Smuzhiyun static void mlxsw_sp_span_entry_destroy(struct mlxsw_sp *mlxsw_sp,
885*4882a593Smuzhiyun 					struct mlxsw_sp_span_entry *span_entry)
886*4882a593Smuzhiyun {
887*4882a593Smuzhiyun 	mlxsw_sp_span_entry_deconfigure(span_entry);
888*4882a593Smuzhiyun 	atomic_dec(&mlxsw_sp->span->active_entries_count);
889*4882a593Smuzhiyun 	if (span_entry->parms.policer_enable)
890*4882a593Smuzhiyun 		mlxsw_sp_span_policer_id_base_unset(mlxsw_sp->span);
891*4882a593Smuzhiyun }
892*4882a593Smuzhiyun 
893*4882a593Smuzhiyun struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev)894*4882a593Smuzhiyun mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp,
895*4882a593Smuzhiyun 				 const struct net_device *to_dev)
896*4882a593Smuzhiyun {
897*4882a593Smuzhiyun 	int i;
898*4882a593Smuzhiyun 
899*4882a593Smuzhiyun 	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
900*4882a593Smuzhiyun 		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
901*4882a593Smuzhiyun 
902*4882a593Smuzhiyun 		if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev)
903*4882a593Smuzhiyun 			return curr;
904*4882a593Smuzhiyun 	}
905*4882a593Smuzhiyun 	return NULL;
906*4882a593Smuzhiyun }
907*4882a593Smuzhiyun 
mlxsw_sp_span_entry_invalidate(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_span_entry * span_entry)908*4882a593Smuzhiyun void mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp,
909*4882a593Smuzhiyun 				    struct mlxsw_sp_span_entry *span_entry)
910*4882a593Smuzhiyun {
911*4882a593Smuzhiyun 	mlxsw_sp_span_entry_deconfigure(span_entry);
912*4882a593Smuzhiyun 	span_entry->ops = &mlxsw_sp_span_entry_ops_nop;
913*4882a593Smuzhiyun }
914*4882a593Smuzhiyun 
915*4882a593Smuzhiyun static struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_find_by_id(struct mlxsw_sp * mlxsw_sp,int span_id)916*4882a593Smuzhiyun mlxsw_sp_span_entry_find_by_id(struct mlxsw_sp *mlxsw_sp, int span_id)
917*4882a593Smuzhiyun {
918*4882a593Smuzhiyun 	int i;
919*4882a593Smuzhiyun 
920*4882a593Smuzhiyun 	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
921*4882a593Smuzhiyun 		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
922*4882a593Smuzhiyun 
923*4882a593Smuzhiyun 		if (refcount_read(&curr->ref_count) && curr->id == span_id)
924*4882a593Smuzhiyun 			return curr;
925*4882a593Smuzhiyun 	}
926*4882a593Smuzhiyun 	return NULL;
927*4882a593Smuzhiyun }
928*4882a593Smuzhiyun 
929*4882a593Smuzhiyun static struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_find_by_parms(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,const struct mlxsw_sp_span_parms * sparms)930*4882a593Smuzhiyun mlxsw_sp_span_entry_find_by_parms(struct mlxsw_sp *mlxsw_sp,
931*4882a593Smuzhiyun 				  const struct net_device *to_dev,
932*4882a593Smuzhiyun 				  const struct mlxsw_sp_span_parms *sparms)
933*4882a593Smuzhiyun {
934*4882a593Smuzhiyun 	int i;
935*4882a593Smuzhiyun 
936*4882a593Smuzhiyun 	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
937*4882a593Smuzhiyun 		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
938*4882a593Smuzhiyun 
939*4882a593Smuzhiyun 		if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev &&
940*4882a593Smuzhiyun 		    curr->parms.policer_enable == sparms->policer_enable &&
941*4882a593Smuzhiyun 		    curr->parms.policer_id == sparms->policer_id)
942*4882a593Smuzhiyun 			return curr;
943*4882a593Smuzhiyun 	}
944*4882a593Smuzhiyun 	return NULL;
945*4882a593Smuzhiyun }
946*4882a593Smuzhiyun 
947*4882a593Smuzhiyun static struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_get(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,const struct mlxsw_sp_span_entry_ops * ops,struct mlxsw_sp_span_parms sparms)948*4882a593Smuzhiyun mlxsw_sp_span_entry_get(struct mlxsw_sp *mlxsw_sp,
949*4882a593Smuzhiyun 			const struct net_device *to_dev,
950*4882a593Smuzhiyun 			const struct mlxsw_sp_span_entry_ops *ops,
951*4882a593Smuzhiyun 			struct mlxsw_sp_span_parms sparms)
952*4882a593Smuzhiyun {
953*4882a593Smuzhiyun 	struct mlxsw_sp_span_entry *span_entry;
954*4882a593Smuzhiyun 
955*4882a593Smuzhiyun 	span_entry = mlxsw_sp_span_entry_find_by_parms(mlxsw_sp, to_dev,
956*4882a593Smuzhiyun 						       &sparms);
957*4882a593Smuzhiyun 	if (span_entry) {
958*4882a593Smuzhiyun 		/* Already exists, just take a reference */
959*4882a593Smuzhiyun 		refcount_inc(&span_entry->ref_count);
960*4882a593Smuzhiyun 		return span_entry;
961*4882a593Smuzhiyun 	}
962*4882a593Smuzhiyun 
963*4882a593Smuzhiyun 	return mlxsw_sp_span_entry_create(mlxsw_sp, to_dev, ops, sparms);
964*4882a593Smuzhiyun }
965*4882a593Smuzhiyun 
mlxsw_sp_span_entry_put(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_span_entry * span_entry)966*4882a593Smuzhiyun static int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp,
967*4882a593Smuzhiyun 				   struct mlxsw_sp_span_entry *span_entry)
968*4882a593Smuzhiyun {
969*4882a593Smuzhiyun 	if (refcount_dec_and_test(&span_entry->ref_count))
970*4882a593Smuzhiyun 		mlxsw_sp_span_entry_destroy(mlxsw_sp, span_entry);
971*4882a593Smuzhiyun 	return 0;
972*4882a593Smuzhiyun }
973*4882a593Smuzhiyun 
mlxsw_sp_span_port_buffer_update(struct mlxsw_sp_port * mlxsw_sp_port,bool enable)974*4882a593Smuzhiyun static int mlxsw_sp_span_port_buffer_update(struct mlxsw_sp_port *mlxsw_sp_port, bool enable)
975*4882a593Smuzhiyun {
976*4882a593Smuzhiyun 	struct mlxsw_sp_hdroom hdroom;
977*4882a593Smuzhiyun 
978*4882a593Smuzhiyun 	hdroom = *mlxsw_sp_port->hdroom;
979*4882a593Smuzhiyun 	hdroom.int_buf.enable = enable;
980*4882a593Smuzhiyun 	mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
981*4882a593Smuzhiyun 
982*4882a593Smuzhiyun 	return mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
983*4882a593Smuzhiyun }
984*4882a593Smuzhiyun 
985*4882a593Smuzhiyun static int
mlxsw_sp_span_port_buffer_enable(struct mlxsw_sp_port * mlxsw_sp_port)986*4882a593Smuzhiyun mlxsw_sp_span_port_buffer_enable(struct mlxsw_sp_port *mlxsw_sp_port)
987*4882a593Smuzhiyun {
988*4882a593Smuzhiyun 	return mlxsw_sp_span_port_buffer_update(mlxsw_sp_port, true);
989*4882a593Smuzhiyun }
990*4882a593Smuzhiyun 
mlxsw_sp_span_port_buffer_disable(struct mlxsw_sp_port * mlxsw_sp_port)991*4882a593Smuzhiyun static void mlxsw_sp_span_port_buffer_disable(struct mlxsw_sp_port *mlxsw_sp_port)
992*4882a593Smuzhiyun {
993*4882a593Smuzhiyun 	mlxsw_sp_span_port_buffer_update(mlxsw_sp_port, false);
994*4882a593Smuzhiyun }
995*4882a593Smuzhiyun 
996*4882a593Smuzhiyun static struct mlxsw_sp_span_analyzed_port *
mlxsw_sp_span_analyzed_port_find(struct mlxsw_sp_span * span,u8 local_port,bool ingress)997*4882a593Smuzhiyun mlxsw_sp_span_analyzed_port_find(struct mlxsw_sp_span *span, u8 local_port,
998*4882a593Smuzhiyun 				 bool ingress)
999*4882a593Smuzhiyun {
1000*4882a593Smuzhiyun 	struct mlxsw_sp_span_analyzed_port *analyzed_port;
1001*4882a593Smuzhiyun 
1002*4882a593Smuzhiyun 	list_for_each_entry(analyzed_port, &span->analyzed_ports_list, list) {
1003*4882a593Smuzhiyun 		if (analyzed_port->local_port == local_port &&
1004*4882a593Smuzhiyun 		    analyzed_port->ingress == ingress)
1005*4882a593Smuzhiyun 			return analyzed_port;
1006*4882a593Smuzhiyun 	}
1007*4882a593Smuzhiyun 
1008*4882a593Smuzhiyun 	return NULL;
1009*4882a593Smuzhiyun }
1010*4882a593Smuzhiyun 
1011*4882a593Smuzhiyun static const struct mlxsw_sp_span_entry_ops *
mlxsw_sp_span_entry_ops(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev)1012*4882a593Smuzhiyun mlxsw_sp_span_entry_ops(struct mlxsw_sp *mlxsw_sp,
1013*4882a593Smuzhiyun 			const struct net_device *to_dev)
1014*4882a593Smuzhiyun {
1015*4882a593Smuzhiyun 	struct mlxsw_sp_span *span = mlxsw_sp->span;
1016*4882a593Smuzhiyun 	size_t i;
1017*4882a593Smuzhiyun 
1018*4882a593Smuzhiyun 	for (i = 0; i < span->span_entry_ops_arr_size; ++i)
1019*4882a593Smuzhiyun 		if (span->span_entry_ops_arr[i]->can_handle(to_dev))
1020*4882a593Smuzhiyun 			return span->span_entry_ops_arr[i];
1021*4882a593Smuzhiyun 
1022*4882a593Smuzhiyun 	return NULL;
1023*4882a593Smuzhiyun }
1024*4882a593Smuzhiyun 
mlxsw_sp_span_respin_work(struct work_struct * work)1025*4882a593Smuzhiyun static void mlxsw_sp_span_respin_work(struct work_struct *work)
1026*4882a593Smuzhiyun {
1027*4882a593Smuzhiyun 	struct mlxsw_sp_span *span;
1028*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp;
1029*4882a593Smuzhiyun 	int i, err;
1030*4882a593Smuzhiyun 
1031*4882a593Smuzhiyun 	span = container_of(work, struct mlxsw_sp_span, work);
1032*4882a593Smuzhiyun 	mlxsw_sp = span->mlxsw_sp;
1033*4882a593Smuzhiyun 
1034*4882a593Smuzhiyun 	rtnl_lock();
1035*4882a593Smuzhiyun 	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
1036*4882a593Smuzhiyun 		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
1037*4882a593Smuzhiyun 		struct mlxsw_sp_span_parms sparms = {NULL};
1038*4882a593Smuzhiyun 
1039*4882a593Smuzhiyun 		if (!refcount_read(&curr->ref_count))
1040*4882a593Smuzhiyun 			continue;
1041*4882a593Smuzhiyun 
1042*4882a593Smuzhiyun 		if (curr->ops->is_static)
1043*4882a593Smuzhiyun 			continue;
1044*4882a593Smuzhiyun 
1045*4882a593Smuzhiyun 		err = curr->ops->parms_set(mlxsw_sp, curr->to_dev, &sparms);
1046*4882a593Smuzhiyun 		if (err)
1047*4882a593Smuzhiyun 			continue;
1048*4882a593Smuzhiyun 
1049*4882a593Smuzhiyun 		if (memcmp(&sparms, &curr->parms, sizeof(sparms))) {
1050*4882a593Smuzhiyun 			mlxsw_sp_span_entry_deconfigure(curr);
1051*4882a593Smuzhiyun 			mlxsw_sp_span_entry_configure(mlxsw_sp, curr, sparms);
1052*4882a593Smuzhiyun 		}
1053*4882a593Smuzhiyun 	}
1054*4882a593Smuzhiyun 	rtnl_unlock();
1055*4882a593Smuzhiyun }
1056*4882a593Smuzhiyun 
mlxsw_sp_span_respin(struct mlxsw_sp * mlxsw_sp)1057*4882a593Smuzhiyun void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp)
1058*4882a593Smuzhiyun {
1059*4882a593Smuzhiyun 	if (atomic_read(&mlxsw_sp->span->active_entries_count) == 0)
1060*4882a593Smuzhiyun 		return;
1061*4882a593Smuzhiyun 	mlxsw_core_schedule_work(&mlxsw_sp->span->work);
1062*4882a593Smuzhiyun }
1063*4882a593Smuzhiyun 
mlxsw_sp_span_agent_get(struct mlxsw_sp * mlxsw_sp,int * p_span_id,const struct mlxsw_sp_span_agent_parms * parms)1064*4882a593Smuzhiyun int mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp, int *p_span_id,
1065*4882a593Smuzhiyun 			    const struct mlxsw_sp_span_agent_parms *parms)
1066*4882a593Smuzhiyun {
1067*4882a593Smuzhiyun 	const struct net_device *to_dev = parms->to_dev;
1068*4882a593Smuzhiyun 	const struct mlxsw_sp_span_entry_ops *ops;
1069*4882a593Smuzhiyun 	struct mlxsw_sp_span_entry *span_entry;
1070*4882a593Smuzhiyun 	struct mlxsw_sp_span_parms sparms;
1071*4882a593Smuzhiyun 	int err;
1072*4882a593Smuzhiyun 
1073*4882a593Smuzhiyun 	ASSERT_RTNL();
1074*4882a593Smuzhiyun 
1075*4882a593Smuzhiyun 	ops = mlxsw_sp_span_entry_ops(mlxsw_sp, to_dev);
1076*4882a593Smuzhiyun 	if (!ops) {
1077*4882a593Smuzhiyun 		dev_err(mlxsw_sp->bus_info->dev, "Cannot mirror to requested destination\n");
1078*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1079*4882a593Smuzhiyun 	}
1080*4882a593Smuzhiyun 
1081*4882a593Smuzhiyun 	memset(&sparms, 0, sizeof(sparms));
1082*4882a593Smuzhiyun 	err = ops->parms_set(mlxsw_sp, to_dev, &sparms);
1083*4882a593Smuzhiyun 	if (err)
1084*4882a593Smuzhiyun 		return err;
1085*4882a593Smuzhiyun 
1086*4882a593Smuzhiyun 	sparms.policer_id = parms->policer_id;
1087*4882a593Smuzhiyun 	sparms.policer_enable = parms->policer_enable;
1088*4882a593Smuzhiyun 	span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms);
1089*4882a593Smuzhiyun 	if (!span_entry)
1090*4882a593Smuzhiyun 		return -ENOBUFS;
1091*4882a593Smuzhiyun 
1092*4882a593Smuzhiyun 	*p_span_id = span_entry->id;
1093*4882a593Smuzhiyun 
1094*4882a593Smuzhiyun 	return 0;
1095*4882a593Smuzhiyun }
1096*4882a593Smuzhiyun 
mlxsw_sp_span_agent_put(struct mlxsw_sp * mlxsw_sp,int span_id)1097*4882a593Smuzhiyun void mlxsw_sp_span_agent_put(struct mlxsw_sp *mlxsw_sp, int span_id)
1098*4882a593Smuzhiyun {
1099*4882a593Smuzhiyun 	struct mlxsw_sp_span_entry *span_entry;
1100*4882a593Smuzhiyun 
1101*4882a593Smuzhiyun 	ASSERT_RTNL();
1102*4882a593Smuzhiyun 
1103*4882a593Smuzhiyun 	span_entry = mlxsw_sp_span_entry_find_by_id(mlxsw_sp, span_id);
1104*4882a593Smuzhiyun 	if (WARN_ON_ONCE(!span_entry))
1105*4882a593Smuzhiyun 		return;
1106*4882a593Smuzhiyun 
1107*4882a593Smuzhiyun 	mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
1108*4882a593Smuzhiyun }
1109*4882a593Smuzhiyun 
1110*4882a593Smuzhiyun static struct mlxsw_sp_span_analyzed_port *
mlxsw_sp_span_analyzed_port_create(struct mlxsw_sp_span * span,struct mlxsw_sp_port * mlxsw_sp_port,bool ingress)1111*4882a593Smuzhiyun mlxsw_sp_span_analyzed_port_create(struct mlxsw_sp_span *span,
1112*4882a593Smuzhiyun 				   struct mlxsw_sp_port *mlxsw_sp_port,
1113*4882a593Smuzhiyun 				   bool ingress)
1114*4882a593Smuzhiyun {
1115*4882a593Smuzhiyun 	struct mlxsw_sp_span_analyzed_port *analyzed_port;
1116*4882a593Smuzhiyun 	int err;
1117*4882a593Smuzhiyun 
1118*4882a593Smuzhiyun 	analyzed_port = kzalloc(sizeof(*analyzed_port), GFP_KERNEL);
1119*4882a593Smuzhiyun 	if (!analyzed_port)
1120*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
1121*4882a593Smuzhiyun 
1122*4882a593Smuzhiyun 	refcount_set(&analyzed_port->ref_count, 1);
1123*4882a593Smuzhiyun 	analyzed_port->local_port = mlxsw_sp_port->local_port;
1124*4882a593Smuzhiyun 	analyzed_port->ingress = ingress;
1125*4882a593Smuzhiyun 	list_add_tail(&analyzed_port->list, &span->analyzed_ports_list);
1126*4882a593Smuzhiyun 
1127*4882a593Smuzhiyun 	/* An egress mirror buffer should be allocated on the egress port which
1128*4882a593Smuzhiyun 	 * does the mirroring.
1129*4882a593Smuzhiyun 	 */
1130*4882a593Smuzhiyun 	if (!ingress) {
1131*4882a593Smuzhiyun 		err = mlxsw_sp_span_port_buffer_enable(mlxsw_sp_port);
1132*4882a593Smuzhiyun 		if (err)
1133*4882a593Smuzhiyun 			goto err_buffer_update;
1134*4882a593Smuzhiyun 	}
1135*4882a593Smuzhiyun 
1136*4882a593Smuzhiyun 	return analyzed_port;
1137*4882a593Smuzhiyun 
1138*4882a593Smuzhiyun err_buffer_update:
1139*4882a593Smuzhiyun 	list_del(&analyzed_port->list);
1140*4882a593Smuzhiyun 	kfree(analyzed_port);
1141*4882a593Smuzhiyun 	return ERR_PTR(err);
1142*4882a593Smuzhiyun }
1143*4882a593Smuzhiyun 
1144*4882a593Smuzhiyun static void
mlxsw_sp_span_analyzed_port_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_span_analyzed_port * analyzed_port)1145*4882a593Smuzhiyun mlxsw_sp_span_analyzed_port_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
1146*4882a593Smuzhiyun 				    struct mlxsw_sp_span_analyzed_port *
1147*4882a593Smuzhiyun 				    analyzed_port)
1148*4882a593Smuzhiyun {
1149*4882a593Smuzhiyun 	/* Remove egress mirror buffer now that port is no longer analyzed
1150*4882a593Smuzhiyun 	 * at egress.
1151*4882a593Smuzhiyun 	 */
1152*4882a593Smuzhiyun 	if (!analyzed_port->ingress)
1153*4882a593Smuzhiyun 		mlxsw_sp_span_port_buffer_disable(mlxsw_sp_port);
1154*4882a593Smuzhiyun 
1155*4882a593Smuzhiyun 	list_del(&analyzed_port->list);
1156*4882a593Smuzhiyun 	kfree(analyzed_port);
1157*4882a593Smuzhiyun }
1158*4882a593Smuzhiyun 
mlxsw_sp_span_analyzed_port_get(struct mlxsw_sp_port * mlxsw_sp_port,bool ingress)1159*4882a593Smuzhiyun int mlxsw_sp_span_analyzed_port_get(struct mlxsw_sp_port *mlxsw_sp_port,
1160*4882a593Smuzhiyun 				    bool ingress)
1161*4882a593Smuzhiyun {
1162*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1163*4882a593Smuzhiyun 	struct mlxsw_sp_span_analyzed_port *analyzed_port;
1164*4882a593Smuzhiyun 	u8 local_port = mlxsw_sp_port->local_port;
1165*4882a593Smuzhiyun 	int err = 0;
1166*4882a593Smuzhiyun 
1167*4882a593Smuzhiyun 	mutex_lock(&mlxsw_sp->span->analyzed_ports_lock);
1168*4882a593Smuzhiyun 
1169*4882a593Smuzhiyun 	analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span,
1170*4882a593Smuzhiyun 							 local_port, ingress);
1171*4882a593Smuzhiyun 	if (analyzed_port) {
1172*4882a593Smuzhiyun 		refcount_inc(&analyzed_port->ref_count);
1173*4882a593Smuzhiyun 		goto out_unlock;
1174*4882a593Smuzhiyun 	}
1175*4882a593Smuzhiyun 
1176*4882a593Smuzhiyun 	analyzed_port = mlxsw_sp_span_analyzed_port_create(mlxsw_sp->span,
1177*4882a593Smuzhiyun 							   mlxsw_sp_port,
1178*4882a593Smuzhiyun 							   ingress);
1179*4882a593Smuzhiyun 	if (IS_ERR(analyzed_port))
1180*4882a593Smuzhiyun 		err = PTR_ERR(analyzed_port);
1181*4882a593Smuzhiyun 
1182*4882a593Smuzhiyun out_unlock:
1183*4882a593Smuzhiyun 	mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock);
1184*4882a593Smuzhiyun 	return err;
1185*4882a593Smuzhiyun }
1186*4882a593Smuzhiyun 
mlxsw_sp_span_analyzed_port_put(struct mlxsw_sp_port * mlxsw_sp_port,bool ingress)1187*4882a593Smuzhiyun void mlxsw_sp_span_analyzed_port_put(struct mlxsw_sp_port *mlxsw_sp_port,
1188*4882a593Smuzhiyun 				     bool ingress)
1189*4882a593Smuzhiyun {
1190*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1191*4882a593Smuzhiyun 	struct mlxsw_sp_span_analyzed_port *analyzed_port;
1192*4882a593Smuzhiyun 	u8 local_port = mlxsw_sp_port->local_port;
1193*4882a593Smuzhiyun 
1194*4882a593Smuzhiyun 	mutex_lock(&mlxsw_sp->span->analyzed_ports_lock);
1195*4882a593Smuzhiyun 
1196*4882a593Smuzhiyun 	analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span,
1197*4882a593Smuzhiyun 							 local_port, ingress);
1198*4882a593Smuzhiyun 	if (WARN_ON_ONCE(!analyzed_port))
1199*4882a593Smuzhiyun 		goto out_unlock;
1200*4882a593Smuzhiyun 
1201*4882a593Smuzhiyun 	if (!refcount_dec_and_test(&analyzed_port->ref_count))
1202*4882a593Smuzhiyun 		goto out_unlock;
1203*4882a593Smuzhiyun 
1204*4882a593Smuzhiyun 	mlxsw_sp_span_analyzed_port_destroy(mlxsw_sp_port, analyzed_port);
1205*4882a593Smuzhiyun 
1206*4882a593Smuzhiyun out_unlock:
1207*4882a593Smuzhiyun 	mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock);
1208*4882a593Smuzhiyun }
1209*4882a593Smuzhiyun 
1210*4882a593Smuzhiyun static int
__mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span * span,struct mlxsw_sp_span_trigger_entry * trigger_entry,bool enable)1211*4882a593Smuzhiyun __mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span *span,
1212*4882a593Smuzhiyun 				  struct mlxsw_sp_span_trigger_entry *
1213*4882a593Smuzhiyun 				  trigger_entry, bool enable)
1214*4882a593Smuzhiyun {
1215*4882a593Smuzhiyun 	char mpar_pl[MLXSW_REG_MPAR_LEN];
1216*4882a593Smuzhiyun 	enum mlxsw_reg_mpar_i_e i_e;
1217*4882a593Smuzhiyun 
1218*4882a593Smuzhiyun 	switch (trigger_entry->trigger) {
1219*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_INGRESS:
1220*4882a593Smuzhiyun 		i_e = MLXSW_REG_MPAR_TYPE_INGRESS;
1221*4882a593Smuzhiyun 		break;
1222*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_EGRESS:
1223*4882a593Smuzhiyun 		i_e = MLXSW_REG_MPAR_TYPE_EGRESS;
1224*4882a593Smuzhiyun 		break;
1225*4882a593Smuzhiyun 	default:
1226*4882a593Smuzhiyun 		WARN_ON_ONCE(1);
1227*4882a593Smuzhiyun 		return -EINVAL;
1228*4882a593Smuzhiyun 	}
1229*4882a593Smuzhiyun 
1230*4882a593Smuzhiyun 	mlxsw_reg_mpar_pack(mpar_pl, trigger_entry->local_port, i_e, enable,
1231*4882a593Smuzhiyun 			    trigger_entry->parms.span_id);
1232*4882a593Smuzhiyun 	return mlxsw_reg_write(span->mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl);
1233*4882a593Smuzhiyun }
1234*4882a593Smuzhiyun 
1235*4882a593Smuzhiyun static int
mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span_trigger_entry * trigger_entry)1236*4882a593Smuzhiyun mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span_trigger_entry *
1237*4882a593Smuzhiyun 				trigger_entry)
1238*4882a593Smuzhiyun {
1239*4882a593Smuzhiyun 	return __mlxsw_sp_span_trigger_port_bind(trigger_entry->span,
1240*4882a593Smuzhiyun 						 trigger_entry, true);
1241*4882a593Smuzhiyun }
1242*4882a593Smuzhiyun 
1243*4882a593Smuzhiyun static void
mlxsw_sp_span_trigger_port_unbind(struct mlxsw_sp_span_trigger_entry * trigger_entry)1244*4882a593Smuzhiyun mlxsw_sp_span_trigger_port_unbind(struct mlxsw_sp_span_trigger_entry *
1245*4882a593Smuzhiyun 				  trigger_entry)
1246*4882a593Smuzhiyun {
1247*4882a593Smuzhiyun 	__mlxsw_sp_span_trigger_port_bind(trigger_entry->span, trigger_entry,
1248*4882a593Smuzhiyun 					  false);
1249*4882a593Smuzhiyun }
1250*4882a593Smuzhiyun 
1251*4882a593Smuzhiyun static bool
mlxsw_sp_span_trigger_port_matches(struct mlxsw_sp_span_trigger_entry * trigger_entry,enum mlxsw_sp_span_trigger trigger,struct mlxsw_sp_port * mlxsw_sp_port)1252*4882a593Smuzhiyun mlxsw_sp_span_trigger_port_matches(struct mlxsw_sp_span_trigger_entry *
1253*4882a593Smuzhiyun 				   trigger_entry,
1254*4882a593Smuzhiyun 				   enum mlxsw_sp_span_trigger trigger,
1255*4882a593Smuzhiyun 				   struct mlxsw_sp_port *mlxsw_sp_port)
1256*4882a593Smuzhiyun {
1257*4882a593Smuzhiyun 	return trigger_entry->trigger == trigger &&
1258*4882a593Smuzhiyun 	       trigger_entry->local_port == mlxsw_sp_port->local_port;
1259*4882a593Smuzhiyun }
1260*4882a593Smuzhiyun 
1261*4882a593Smuzhiyun static int
mlxsw_sp_span_trigger_port_enable(struct mlxsw_sp_span_trigger_entry * trigger_entry,struct mlxsw_sp_port * mlxsw_sp_port,u8 tc)1262*4882a593Smuzhiyun mlxsw_sp_span_trigger_port_enable(struct mlxsw_sp_span_trigger_entry *
1263*4882a593Smuzhiyun 				  trigger_entry,
1264*4882a593Smuzhiyun 				  struct mlxsw_sp_port *mlxsw_sp_port, u8 tc)
1265*4882a593Smuzhiyun {
1266*4882a593Smuzhiyun 	/* Port trigger are enabled during binding. */
1267*4882a593Smuzhiyun 	return 0;
1268*4882a593Smuzhiyun }
1269*4882a593Smuzhiyun 
1270*4882a593Smuzhiyun static void
mlxsw_sp_span_trigger_port_disable(struct mlxsw_sp_span_trigger_entry * trigger_entry,struct mlxsw_sp_port * mlxsw_sp_port,u8 tc)1271*4882a593Smuzhiyun mlxsw_sp_span_trigger_port_disable(struct mlxsw_sp_span_trigger_entry *
1272*4882a593Smuzhiyun 				   trigger_entry,
1273*4882a593Smuzhiyun 				   struct mlxsw_sp_port *mlxsw_sp_port, u8 tc)
1274*4882a593Smuzhiyun {
1275*4882a593Smuzhiyun }
1276*4882a593Smuzhiyun 
1277*4882a593Smuzhiyun static const struct mlxsw_sp_span_trigger_ops
1278*4882a593Smuzhiyun mlxsw_sp_span_trigger_port_ops = {
1279*4882a593Smuzhiyun 	.bind = mlxsw_sp_span_trigger_port_bind,
1280*4882a593Smuzhiyun 	.unbind = mlxsw_sp_span_trigger_port_unbind,
1281*4882a593Smuzhiyun 	.matches = mlxsw_sp_span_trigger_port_matches,
1282*4882a593Smuzhiyun 	.enable = mlxsw_sp_span_trigger_port_enable,
1283*4882a593Smuzhiyun 	.disable = mlxsw_sp_span_trigger_port_disable,
1284*4882a593Smuzhiyun };
1285*4882a593Smuzhiyun 
1286*4882a593Smuzhiyun static int
mlxsw_sp1_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry * trigger_entry)1287*4882a593Smuzhiyun mlxsw_sp1_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry *
1288*4882a593Smuzhiyun 				   trigger_entry)
1289*4882a593Smuzhiyun {
1290*4882a593Smuzhiyun 	return -EOPNOTSUPP;
1291*4882a593Smuzhiyun }
1292*4882a593Smuzhiyun 
1293*4882a593Smuzhiyun static void
mlxsw_sp1_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry * trigger_entry)1294*4882a593Smuzhiyun mlxsw_sp1_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry *
1295*4882a593Smuzhiyun 				     trigger_entry)
1296*4882a593Smuzhiyun {
1297*4882a593Smuzhiyun }
1298*4882a593Smuzhiyun 
1299*4882a593Smuzhiyun static bool
mlxsw_sp1_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry * trigger_entry,enum mlxsw_sp_span_trigger trigger,struct mlxsw_sp_port * mlxsw_sp_port)1300*4882a593Smuzhiyun mlxsw_sp1_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry *
1301*4882a593Smuzhiyun 				      trigger_entry,
1302*4882a593Smuzhiyun 				      enum mlxsw_sp_span_trigger trigger,
1303*4882a593Smuzhiyun 				      struct mlxsw_sp_port *mlxsw_sp_port)
1304*4882a593Smuzhiyun {
1305*4882a593Smuzhiyun 	WARN_ON_ONCE(1);
1306*4882a593Smuzhiyun 	return false;
1307*4882a593Smuzhiyun }
1308*4882a593Smuzhiyun 
1309*4882a593Smuzhiyun static int
mlxsw_sp1_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry * trigger_entry,struct mlxsw_sp_port * mlxsw_sp_port,u8 tc)1310*4882a593Smuzhiyun mlxsw_sp1_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
1311*4882a593Smuzhiyun 				     trigger_entry,
1312*4882a593Smuzhiyun 				     struct mlxsw_sp_port *mlxsw_sp_port,
1313*4882a593Smuzhiyun 				     u8 tc)
1314*4882a593Smuzhiyun {
1315*4882a593Smuzhiyun 	return -EOPNOTSUPP;
1316*4882a593Smuzhiyun }
1317*4882a593Smuzhiyun 
1318*4882a593Smuzhiyun static void
mlxsw_sp1_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry * trigger_entry,struct mlxsw_sp_port * mlxsw_sp_port,u8 tc)1319*4882a593Smuzhiyun mlxsw_sp1_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry *
1320*4882a593Smuzhiyun 				      trigger_entry,
1321*4882a593Smuzhiyun 				      struct mlxsw_sp_port *mlxsw_sp_port,
1322*4882a593Smuzhiyun 				      u8 tc)
1323*4882a593Smuzhiyun {
1324*4882a593Smuzhiyun }
1325*4882a593Smuzhiyun 
1326*4882a593Smuzhiyun static const struct mlxsw_sp_span_trigger_ops
1327*4882a593Smuzhiyun mlxsw_sp1_span_trigger_global_ops = {
1328*4882a593Smuzhiyun 	.bind = mlxsw_sp1_span_trigger_global_bind,
1329*4882a593Smuzhiyun 	.unbind = mlxsw_sp1_span_trigger_global_unbind,
1330*4882a593Smuzhiyun 	.matches = mlxsw_sp1_span_trigger_global_matches,
1331*4882a593Smuzhiyun 	.enable = mlxsw_sp1_span_trigger_global_enable,
1332*4882a593Smuzhiyun 	.disable = mlxsw_sp1_span_trigger_global_disable,
1333*4882a593Smuzhiyun };
1334*4882a593Smuzhiyun 
1335*4882a593Smuzhiyun static const struct mlxsw_sp_span_trigger_ops *
1336*4882a593Smuzhiyun mlxsw_sp1_span_trigger_ops_arr[] = {
1337*4882a593Smuzhiyun 	[MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops,
1338*4882a593Smuzhiyun 	[MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] =
1339*4882a593Smuzhiyun 		&mlxsw_sp1_span_trigger_global_ops,
1340*4882a593Smuzhiyun };
1341*4882a593Smuzhiyun 
1342*4882a593Smuzhiyun static int
mlxsw_sp2_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry * trigger_entry)1343*4882a593Smuzhiyun mlxsw_sp2_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry *
1344*4882a593Smuzhiyun 				   trigger_entry)
1345*4882a593Smuzhiyun {
1346*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp;
1347*4882a593Smuzhiyun 	enum mlxsw_reg_mpagr_trigger trigger;
1348*4882a593Smuzhiyun 	char mpagr_pl[MLXSW_REG_MPAGR_LEN];
1349*4882a593Smuzhiyun 
1350*4882a593Smuzhiyun 	switch (trigger_entry->trigger) {
1351*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
1352*4882a593Smuzhiyun 		trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_SHARED_BUFFER;
1353*4882a593Smuzhiyun 		break;
1354*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
1355*4882a593Smuzhiyun 		trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_WRED;
1356*4882a593Smuzhiyun 		break;
1357*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_ECN:
1358*4882a593Smuzhiyun 		trigger = MLXSW_REG_MPAGR_TRIGGER_EGRESS_ECN;
1359*4882a593Smuzhiyun 		break;
1360*4882a593Smuzhiyun 	default:
1361*4882a593Smuzhiyun 		WARN_ON_ONCE(1);
1362*4882a593Smuzhiyun 		return -EINVAL;
1363*4882a593Smuzhiyun 	}
1364*4882a593Smuzhiyun 
1365*4882a593Smuzhiyun 	mlxsw_reg_mpagr_pack(mpagr_pl, trigger, trigger_entry->parms.span_id,
1366*4882a593Smuzhiyun 			     1);
1367*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpagr), mpagr_pl);
1368*4882a593Smuzhiyun }
1369*4882a593Smuzhiyun 
1370*4882a593Smuzhiyun static void
mlxsw_sp2_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry * trigger_entry)1371*4882a593Smuzhiyun mlxsw_sp2_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry *
1372*4882a593Smuzhiyun 				     trigger_entry)
1373*4882a593Smuzhiyun {
1374*4882a593Smuzhiyun 	/* There is no unbinding for global triggers. The trigger should be
1375*4882a593Smuzhiyun 	 * disabled on all ports by now.
1376*4882a593Smuzhiyun 	 */
1377*4882a593Smuzhiyun }
1378*4882a593Smuzhiyun 
1379*4882a593Smuzhiyun static bool
mlxsw_sp2_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry * trigger_entry,enum mlxsw_sp_span_trigger trigger,struct mlxsw_sp_port * mlxsw_sp_port)1380*4882a593Smuzhiyun mlxsw_sp2_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry *
1381*4882a593Smuzhiyun 				      trigger_entry,
1382*4882a593Smuzhiyun 				      enum mlxsw_sp_span_trigger trigger,
1383*4882a593Smuzhiyun 				      struct mlxsw_sp_port *mlxsw_sp_port)
1384*4882a593Smuzhiyun {
1385*4882a593Smuzhiyun 	return trigger_entry->trigger == trigger;
1386*4882a593Smuzhiyun }
1387*4882a593Smuzhiyun 
1388*4882a593Smuzhiyun static int
__mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry * trigger_entry,struct mlxsw_sp_port * mlxsw_sp_port,u8 tc,bool enable)1389*4882a593Smuzhiyun __mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
1390*4882a593Smuzhiyun 				       trigger_entry,
1391*4882a593Smuzhiyun 				       struct mlxsw_sp_port *mlxsw_sp_port,
1392*4882a593Smuzhiyun 				       u8 tc, bool enable)
1393*4882a593Smuzhiyun {
1394*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp;
1395*4882a593Smuzhiyun 	char momte_pl[MLXSW_REG_MOMTE_LEN];
1396*4882a593Smuzhiyun 	enum mlxsw_reg_momte_type type;
1397*4882a593Smuzhiyun 	int err;
1398*4882a593Smuzhiyun 
1399*4882a593Smuzhiyun 	switch (trigger_entry->trigger) {
1400*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
1401*4882a593Smuzhiyun 		type = MLXSW_REG_MOMTE_TYPE_SHARED_BUFFER_TCLASS;
1402*4882a593Smuzhiyun 		break;
1403*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
1404*4882a593Smuzhiyun 		type = MLXSW_REG_MOMTE_TYPE_WRED;
1405*4882a593Smuzhiyun 		break;
1406*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_ECN:
1407*4882a593Smuzhiyun 		type = MLXSW_REG_MOMTE_TYPE_ECN;
1408*4882a593Smuzhiyun 		break;
1409*4882a593Smuzhiyun 	default:
1410*4882a593Smuzhiyun 		WARN_ON_ONCE(1);
1411*4882a593Smuzhiyun 		return -EINVAL;
1412*4882a593Smuzhiyun 	}
1413*4882a593Smuzhiyun 
1414*4882a593Smuzhiyun 	/* Query existing configuration in order to only change the state of
1415*4882a593Smuzhiyun 	 * the specified traffic class.
1416*4882a593Smuzhiyun 	 */
1417*4882a593Smuzhiyun 	mlxsw_reg_momte_pack(momte_pl, mlxsw_sp_port->local_port, type);
1418*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(momte), momte_pl);
1419*4882a593Smuzhiyun 	if (err)
1420*4882a593Smuzhiyun 		return err;
1421*4882a593Smuzhiyun 
1422*4882a593Smuzhiyun 	mlxsw_reg_momte_tclass_en_set(momte_pl, tc, enable);
1423*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(momte), momte_pl);
1424*4882a593Smuzhiyun }
1425*4882a593Smuzhiyun 
1426*4882a593Smuzhiyun static int
mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry * trigger_entry,struct mlxsw_sp_port * mlxsw_sp_port,u8 tc)1427*4882a593Smuzhiyun mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
1428*4882a593Smuzhiyun 				     trigger_entry,
1429*4882a593Smuzhiyun 				     struct mlxsw_sp_port *mlxsw_sp_port,
1430*4882a593Smuzhiyun 				     u8 tc)
1431*4882a593Smuzhiyun {
1432*4882a593Smuzhiyun 	return __mlxsw_sp2_span_trigger_global_enable(trigger_entry,
1433*4882a593Smuzhiyun 						      mlxsw_sp_port, tc, true);
1434*4882a593Smuzhiyun }
1435*4882a593Smuzhiyun 
1436*4882a593Smuzhiyun static void
mlxsw_sp2_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry * trigger_entry,struct mlxsw_sp_port * mlxsw_sp_port,u8 tc)1437*4882a593Smuzhiyun mlxsw_sp2_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry *
1438*4882a593Smuzhiyun 				      trigger_entry,
1439*4882a593Smuzhiyun 				      struct mlxsw_sp_port *mlxsw_sp_port,
1440*4882a593Smuzhiyun 				      u8 tc)
1441*4882a593Smuzhiyun {
1442*4882a593Smuzhiyun 	__mlxsw_sp2_span_trigger_global_enable(trigger_entry, mlxsw_sp_port, tc,
1443*4882a593Smuzhiyun 					       false);
1444*4882a593Smuzhiyun }
1445*4882a593Smuzhiyun 
1446*4882a593Smuzhiyun static const struct mlxsw_sp_span_trigger_ops
1447*4882a593Smuzhiyun mlxsw_sp2_span_trigger_global_ops = {
1448*4882a593Smuzhiyun 	.bind = mlxsw_sp2_span_trigger_global_bind,
1449*4882a593Smuzhiyun 	.unbind = mlxsw_sp2_span_trigger_global_unbind,
1450*4882a593Smuzhiyun 	.matches = mlxsw_sp2_span_trigger_global_matches,
1451*4882a593Smuzhiyun 	.enable = mlxsw_sp2_span_trigger_global_enable,
1452*4882a593Smuzhiyun 	.disable = mlxsw_sp2_span_trigger_global_disable,
1453*4882a593Smuzhiyun };
1454*4882a593Smuzhiyun 
1455*4882a593Smuzhiyun static const struct mlxsw_sp_span_trigger_ops *
1456*4882a593Smuzhiyun mlxsw_sp2_span_trigger_ops_arr[] = {
1457*4882a593Smuzhiyun 	[MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops,
1458*4882a593Smuzhiyun 	[MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] =
1459*4882a593Smuzhiyun 		&mlxsw_sp2_span_trigger_global_ops,
1460*4882a593Smuzhiyun };
1461*4882a593Smuzhiyun 
1462*4882a593Smuzhiyun static void
mlxsw_sp_span_trigger_ops_set(struct mlxsw_sp_span_trigger_entry * trigger_entry)1463*4882a593Smuzhiyun mlxsw_sp_span_trigger_ops_set(struct mlxsw_sp_span_trigger_entry *trigger_entry)
1464*4882a593Smuzhiyun {
1465*4882a593Smuzhiyun 	struct mlxsw_sp_span *span = trigger_entry->span;
1466*4882a593Smuzhiyun 	enum mlxsw_sp_span_trigger_type type;
1467*4882a593Smuzhiyun 
1468*4882a593Smuzhiyun 	switch (trigger_entry->trigger) {
1469*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_INGRESS:
1470*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_EGRESS:
1471*4882a593Smuzhiyun 		type = MLXSW_SP_SPAN_TRIGGER_TYPE_PORT;
1472*4882a593Smuzhiyun 		break;
1473*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
1474*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
1475*4882a593Smuzhiyun 	case MLXSW_SP_SPAN_TRIGGER_ECN:
1476*4882a593Smuzhiyun 		type = MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL;
1477*4882a593Smuzhiyun 		break;
1478*4882a593Smuzhiyun 	default:
1479*4882a593Smuzhiyun 		WARN_ON_ONCE(1);
1480*4882a593Smuzhiyun 		return;
1481*4882a593Smuzhiyun 	}
1482*4882a593Smuzhiyun 
1483*4882a593Smuzhiyun 	trigger_entry->ops = span->span_trigger_ops_arr[type];
1484*4882a593Smuzhiyun }
1485*4882a593Smuzhiyun 
1486*4882a593Smuzhiyun static struct mlxsw_sp_span_trigger_entry *
mlxsw_sp_span_trigger_entry_create(struct mlxsw_sp_span * span,enum mlxsw_sp_span_trigger trigger,struct mlxsw_sp_port * mlxsw_sp_port,const struct mlxsw_sp_span_trigger_parms * parms)1487*4882a593Smuzhiyun mlxsw_sp_span_trigger_entry_create(struct mlxsw_sp_span *span,
1488*4882a593Smuzhiyun 				   enum mlxsw_sp_span_trigger trigger,
1489*4882a593Smuzhiyun 				   struct mlxsw_sp_port *mlxsw_sp_port,
1490*4882a593Smuzhiyun 				   const struct mlxsw_sp_span_trigger_parms
1491*4882a593Smuzhiyun 				   *parms)
1492*4882a593Smuzhiyun {
1493*4882a593Smuzhiyun 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
1494*4882a593Smuzhiyun 	int err;
1495*4882a593Smuzhiyun 
1496*4882a593Smuzhiyun 	trigger_entry = kzalloc(sizeof(*trigger_entry), GFP_KERNEL);
1497*4882a593Smuzhiyun 	if (!trigger_entry)
1498*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
1499*4882a593Smuzhiyun 
1500*4882a593Smuzhiyun 	refcount_set(&trigger_entry->ref_count, 1);
1501*4882a593Smuzhiyun 	trigger_entry->local_port = mlxsw_sp_port ? mlxsw_sp_port->local_port :
1502*4882a593Smuzhiyun 						    0;
1503*4882a593Smuzhiyun 	trigger_entry->trigger = trigger;
1504*4882a593Smuzhiyun 	memcpy(&trigger_entry->parms, parms, sizeof(trigger_entry->parms));
1505*4882a593Smuzhiyun 	trigger_entry->span = span;
1506*4882a593Smuzhiyun 	mlxsw_sp_span_trigger_ops_set(trigger_entry);
1507*4882a593Smuzhiyun 	list_add_tail(&trigger_entry->list, &span->trigger_entries_list);
1508*4882a593Smuzhiyun 
1509*4882a593Smuzhiyun 	err = trigger_entry->ops->bind(trigger_entry);
1510*4882a593Smuzhiyun 	if (err)
1511*4882a593Smuzhiyun 		goto err_trigger_entry_bind;
1512*4882a593Smuzhiyun 
1513*4882a593Smuzhiyun 	return trigger_entry;
1514*4882a593Smuzhiyun 
1515*4882a593Smuzhiyun err_trigger_entry_bind:
1516*4882a593Smuzhiyun 	list_del(&trigger_entry->list);
1517*4882a593Smuzhiyun 	kfree(trigger_entry);
1518*4882a593Smuzhiyun 	return ERR_PTR(err);
1519*4882a593Smuzhiyun }
1520*4882a593Smuzhiyun 
1521*4882a593Smuzhiyun static void
mlxsw_sp_span_trigger_entry_destroy(struct mlxsw_sp_span * span,struct mlxsw_sp_span_trigger_entry * trigger_entry)1522*4882a593Smuzhiyun mlxsw_sp_span_trigger_entry_destroy(struct mlxsw_sp_span *span,
1523*4882a593Smuzhiyun 				    struct mlxsw_sp_span_trigger_entry *
1524*4882a593Smuzhiyun 				    trigger_entry)
1525*4882a593Smuzhiyun {
1526*4882a593Smuzhiyun 	trigger_entry->ops->unbind(trigger_entry);
1527*4882a593Smuzhiyun 	list_del(&trigger_entry->list);
1528*4882a593Smuzhiyun 	kfree(trigger_entry);
1529*4882a593Smuzhiyun }
1530*4882a593Smuzhiyun 
1531*4882a593Smuzhiyun static struct mlxsw_sp_span_trigger_entry *
mlxsw_sp_span_trigger_entry_find(struct mlxsw_sp_span * span,enum mlxsw_sp_span_trigger trigger,struct mlxsw_sp_port * mlxsw_sp_port)1532*4882a593Smuzhiyun mlxsw_sp_span_trigger_entry_find(struct mlxsw_sp_span *span,
1533*4882a593Smuzhiyun 				 enum mlxsw_sp_span_trigger trigger,
1534*4882a593Smuzhiyun 				 struct mlxsw_sp_port *mlxsw_sp_port)
1535*4882a593Smuzhiyun {
1536*4882a593Smuzhiyun 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
1537*4882a593Smuzhiyun 
1538*4882a593Smuzhiyun 	list_for_each_entry(trigger_entry, &span->trigger_entries_list, list) {
1539*4882a593Smuzhiyun 		if (trigger_entry->ops->matches(trigger_entry, trigger,
1540*4882a593Smuzhiyun 						mlxsw_sp_port))
1541*4882a593Smuzhiyun 			return trigger_entry;
1542*4882a593Smuzhiyun 	}
1543*4882a593Smuzhiyun 
1544*4882a593Smuzhiyun 	return NULL;
1545*4882a593Smuzhiyun }
1546*4882a593Smuzhiyun 
mlxsw_sp_span_agent_bind(struct mlxsw_sp * mlxsw_sp,enum mlxsw_sp_span_trigger trigger,struct mlxsw_sp_port * mlxsw_sp_port,const struct mlxsw_sp_span_trigger_parms * parms)1547*4882a593Smuzhiyun int mlxsw_sp_span_agent_bind(struct mlxsw_sp *mlxsw_sp,
1548*4882a593Smuzhiyun 			     enum mlxsw_sp_span_trigger trigger,
1549*4882a593Smuzhiyun 			     struct mlxsw_sp_port *mlxsw_sp_port,
1550*4882a593Smuzhiyun 			     const struct mlxsw_sp_span_trigger_parms *parms)
1551*4882a593Smuzhiyun {
1552*4882a593Smuzhiyun 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
1553*4882a593Smuzhiyun 	int err = 0;
1554*4882a593Smuzhiyun 
1555*4882a593Smuzhiyun 	ASSERT_RTNL();
1556*4882a593Smuzhiyun 
1557*4882a593Smuzhiyun 	if (!mlxsw_sp_span_entry_find_by_id(mlxsw_sp, parms->span_id))
1558*4882a593Smuzhiyun 		return -EINVAL;
1559*4882a593Smuzhiyun 
1560*4882a593Smuzhiyun 	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
1561*4882a593Smuzhiyun 							 trigger,
1562*4882a593Smuzhiyun 							 mlxsw_sp_port);
1563*4882a593Smuzhiyun 	if (trigger_entry) {
1564*4882a593Smuzhiyun 		if (trigger_entry->parms.span_id != parms->span_id)
1565*4882a593Smuzhiyun 			return -EINVAL;
1566*4882a593Smuzhiyun 		refcount_inc(&trigger_entry->ref_count);
1567*4882a593Smuzhiyun 		goto out;
1568*4882a593Smuzhiyun 	}
1569*4882a593Smuzhiyun 
1570*4882a593Smuzhiyun 	trigger_entry = mlxsw_sp_span_trigger_entry_create(mlxsw_sp->span,
1571*4882a593Smuzhiyun 							   trigger,
1572*4882a593Smuzhiyun 							   mlxsw_sp_port,
1573*4882a593Smuzhiyun 							   parms);
1574*4882a593Smuzhiyun 	if (IS_ERR(trigger_entry))
1575*4882a593Smuzhiyun 		err = PTR_ERR(trigger_entry);
1576*4882a593Smuzhiyun 
1577*4882a593Smuzhiyun out:
1578*4882a593Smuzhiyun 	return err;
1579*4882a593Smuzhiyun }
1580*4882a593Smuzhiyun 
mlxsw_sp_span_agent_unbind(struct mlxsw_sp * mlxsw_sp,enum mlxsw_sp_span_trigger trigger,struct mlxsw_sp_port * mlxsw_sp_port,const struct mlxsw_sp_span_trigger_parms * parms)1581*4882a593Smuzhiyun void mlxsw_sp_span_agent_unbind(struct mlxsw_sp *mlxsw_sp,
1582*4882a593Smuzhiyun 				enum mlxsw_sp_span_trigger trigger,
1583*4882a593Smuzhiyun 				struct mlxsw_sp_port *mlxsw_sp_port,
1584*4882a593Smuzhiyun 				const struct mlxsw_sp_span_trigger_parms *parms)
1585*4882a593Smuzhiyun {
1586*4882a593Smuzhiyun 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
1587*4882a593Smuzhiyun 
1588*4882a593Smuzhiyun 	ASSERT_RTNL();
1589*4882a593Smuzhiyun 
1590*4882a593Smuzhiyun 	if (WARN_ON_ONCE(!mlxsw_sp_span_entry_find_by_id(mlxsw_sp,
1591*4882a593Smuzhiyun 							 parms->span_id)))
1592*4882a593Smuzhiyun 		return;
1593*4882a593Smuzhiyun 
1594*4882a593Smuzhiyun 	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
1595*4882a593Smuzhiyun 							 trigger,
1596*4882a593Smuzhiyun 							 mlxsw_sp_port);
1597*4882a593Smuzhiyun 	if (WARN_ON_ONCE(!trigger_entry))
1598*4882a593Smuzhiyun 		return;
1599*4882a593Smuzhiyun 
1600*4882a593Smuzhiyun 	if (!refcount_dec_and_test(&trigger_entry->ref_count))
1601*4882a593Smuzhiyun 		return;
1602*4882a593Smuzhiyun 
1603*4882a593Smuzhiyun 	mlxsw_sp_span_trigger_entry_destroy(mlxsw_sp->span, trigger_entry);
1604*4882a593Smuzhiyun }
1605*4882a593Smuzhiyun 
mlxsw_sp_span_trigger_enable(struct mlxsw_sp_port * mlxsw_sp_port,enum mlxsw_sp_span_trigger trigger,u8 tc)1606*4882a593Smuzhiyun int mlxsw_sp_span_trigger_enable(struct mlxsw_sp_port *mlxsw_sp_port,
1607*4882a593Smuzhiyun 				 enum mlxsw_sp_span_trigger trigger, u8 tc)
1608*4882a593Smuzhiyun {
1609*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1610*4882a593Smuzhiyun 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
1611*4882a593Smuzhiyun 
1612*4882a593Smuzhiyun 	ASSERT_RTNL();
1613*4882a593Smuzhiyun 
1614*4882a593Smuzhiyun 	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
1615*4882a593Smuzhiyun 							 trigger,
1616*4882a593Smuzhiyun 							 mlxsw_sp_port);
1617*4882a593Smuzhiyun 	if (WARN_ON_ONCE(!trigger_entry))
1618*4882a593Smuzhiyun 		return -EINVAL;
1619*4882a593Smuzhiyun 
1620*4882a593Smuzhiyun 	return trigger_entry->ops->enable(trigger_entry, mlxsw_sp_port, tc);
1621*4882a593Smuzhiyun }
1622*4882a593Smuzhiyun 
mlxsw_sp_span_trigger_disable(struct mlxsw_sp_port * mlxsw_sp_port,enum mlxsw_sp_span_trigger trigger,u8 tc)1623*4882a593Smuzhiyun void mlxsw_sp_span_trigger_disable(struct mlxsw_sp_port *mlxsw_sp_port,
1624*4882a593Smuzhiyun 				   enum mlxsw_sp_span_trigger trigger, u8 tc)
1625*4882a593Smuzhiyun {
1626*4882a593Smuzhiyun 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1627*4882a593Smuzhiyun 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
1628*4882a593Smuzhiyun 
1629*4882a593Smuzhiyun 	ASSERT_RTNL();
1630*4882a593Smuzhiyun 
1631*4882a593Smuzhiyun 	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
1632*4882a593Smuzhiyun 							 trigger,
1633*4882a593Smuzhiyun 							 mlxsw_sp_port);
1634*4882a593Smuzhiyun 	if (WARN_ON_ONCE(!trigger_entry))
1635*4882a593Smuzhiyun 		return;
1636*4882a593Smuzhiyun 
1637*4882a593Smuzhiyun 	return trigger_entry->ops->disable(trigger_entry, mlxsw_sp_port, tc);
1638*4882a593Smuzhiyun }
1639*4882a593Smuzhiyun 
mlxsw_sp1_span_init(struct mlxsw_sp * mlxsw_sp)1640*4882a593Smuzhiyun static int mlxsw_sp1_span_init(struct mlxsw_sp *mlxsw_sp)
1641*4882a593Smuzhiyun {
1642*4882a593Smuzhiyun 	size_t arr_size = ARRAY_SIZE(mlxsw_sp1_span_entry_ops_arr);
1643*4882a593Smuzhiyun 
1644*4882a593Smuzhiyun 	/* Must be first to avoid NULL pointer dereference by subsequent
1645*4882a593Smuzhiyun 	 * can_handle() callbacks.
1646*4882a593Smuzhiyun 	 */
1647*4882a593Smuzhiyun 	if (WARN_ON(mlxsw_sp1_span_entry_ops_arr[0] !=
1648*4882a593Smuzhiyun 		    &mlxsw_sp1_span_entry_ops_cpu))
1649*4882a593Smuzhiyun 		return -EINVAL;
1650*4882a593Smuzhiyun 
1651*4882a593Smuzhiyun 	mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp1_span_trigger_ops_arr;
1652*4882a593Smuzhiyun 	mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp1_span_entry_ops_arr;
1653*4882a593Smuzhiyun 	mlxsw_sp->span->span_entry_ops_arr_size = arr_size;
1654*4882a593Smuzhiyun 
1655*4882a593Smuzhiyun 	return 0;
1656*4882a593Smuzhiyun }
1657*4882a593Smuzhiyun 
mlxsw_sp1_span_policer_id_base_set(struct mlxsw_sp * mlxsw_sp,u16 policer_id_base)1658*4882a593Smuzhiyun static int mlxsw_sp1_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp,
1659*4882a593Smuzhiyun 					      u16 policer_id_base)
1660*4882a593Smuzhiyun {
1661*4882a593Smuzhiyun 	return -EOPNOTSUPP;
1662*4882a593Smuzhiyun }
1663*4882a593Smuzhiyun 
1664*4882a593Smuzhiyun const struct mlxsw_sp_span_ops mlxsw_sp1_span_ops = {
1665*4882a593Smuzhiyun 	.init = mlxsw_sp1_span_init,
1666*4882a593Smuzhiyun 	.policer_id_base_set = mlxsw_sp1_span_policer_id_base_set,
1667*4882a593Smuzhiyun };
1668*4882a593Smuzhiyun 
mlxsw_sp2_span_init(struct mlxsw_sp * mlxsw_sp)1669*4882a593Smuzhiyun static int mlxsw_sp2_span_init(struct mlxsw_sp *mlxsw_sp)
1670*4882a593Smuzhiyun {
1671*4882a593Smuzhiyun 	size_t arr_size = ARRAY_SIZE(mlxsw_sp2_span_entry_ops_arr);
1672*4882a593Smuzhiyun 
1673*4882a593Smuzhiyun 	/* Must be first to avoid NULL pointer dereference by subsequent
1674*4882a593Smuzhiyun 	 * can_handle() callbacks.
1675*4882a593Smuzhiyun 	 */
1676*4882a593Smuzhiyun 	if (WARN_ON(mlxsw_sp2_span_entry_ops_arr[0] !=
1677*4882a593Smuzhiyun 		    &mlxsw_sp2_span_entry_ops_cpu))
1678*4882a593Smuzhiyun 		return -EINVAL;
1679*4882a593Smuzhiyun 
1680*4882a593Smuzhiyun 	mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp2_span_trigger_ops_arr;
1681*4882a593Smuzhiyun 	mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp2_span_entry_ops_arr;
1682*4882a593Smuzhiyun 	mlxsw_sp->span->span_entry_ops_arr_size = arr_size;
1683*4882a593Smuzhiyun 
1684*4882a593Smuzhiyun 	return 0;
1685*4882a593Smuzhiyun }
1686*4882a593Smuzhiyun 
1687*4882a593Smuzhiyun #define MLXSW_SP2_SPAN_EG_MIRROR_BUFFER_FACTOR 38
1688*4882a593Smuzhiyun #define MLXSW_SP3_SPAN_EG_MIRROR_BUFFER_FACTOR 50
1689*4882a593Smuzhiyun 
mlxsw_sp2_span_policer_id_base_set(struct mlxsw_sp * mlxsw_sp,u16 policer_id_base)1690*4882a593Smuzhiyun static int mlxsw_sp2_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp,
1691*4882a593Smuzhiyun 					      u16 policer_id_base)
1692*4882a593Smuzhiyun {
1693*4882a593Smuzhiyun 	char mogcr_pl[MLXSW_REG_MOGCR_LEN];
1694*4882a593Smuzhiyun 	int err;
1695*4882a593Smuzhiyun 
1696*4882a593Smuzhiyun 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl);
1697*4882a593Smuzhiyun 	if (err)
1698*4882a593Smuzhiyun 		return err;
1699*4882a593Smuzhiyun 
1700*4882a593Smuzhiyun 	mlxsw_reg_mogcr_mirroring_pid_base_set(mogcr_pl, policer_id_base);
1701*4882a593Smuzhiyun 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl);
1702*4882a593Smuzhiyun }
1703*4882a593Smuzhiyun 
1704*4882a593Smuzhiyun const struct mlxsw_sp_span_ops mlxsw_sp2_span_ops = {
1705*4882a593Smuzhiyun 	.init = mlxsw_sp2_span_init,
1706*4882a593Smuzhiyun 	.policer_id_base_set = mlxsw_sp2_span_policer_id_base_set,
1707*4882a593Smuzhiyun };
1708*4882a593Smuzhiyun 
1709*4882a593Smuzhiyun const struct mlxsw_sp_span_ops mlxsw_sp3_span_ops = {
1710*4882a593Smuzhiyun 	.init = mlxsw_sp2_span_init,
1711*4882a593Smuzhiyun 	.policer_id_base_set = mlxsw_sp2_span_policer_id_base_set,
1712*4882a593Smuzhiyun };
1713