xref: /linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
19948a064SJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
29948a064SJiri Pirko /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
3a629ef21SPetr Machata 
4946a11e7SPetr Machata #include <linux/if_bridge.h>
5a629ef21SPetr Machata #include <linux/list.h>
6ed04458dSIdo Schimmel #include <linux/mutex.h>
74c00dafcSIdo Schimmel #include <linux/refcount.h>
8a8e7e6e7SIdo Schimmel #include <linux/rtnetlink.h>
9a8e7e6e7SIdo Schimmel #include <linux/workqueue.h>
1027cf76feSPetr Machata #include <net/arp.h>
1127cf76feSPetr Machata #include <net/gre.h>
12b5de82f3SPetr Machata #include <net/lag.h>
138f08a528SPetr Machata #include <net/ndisc.h>
148f08a528SPetr Machata #include <net/ip6_tunnel.h>
15a629ef21SPetr Machata 
16a629ef21SPetr Machata #include "spectrum.h"
1727cf76feSPetr Machata #include "spectrum_ipip.h"
18946a11e7SPetr Machata #include "spectrum_span.h"
19946a11e7SPetr Machata #include "spectrum_switchdev.h"
20a629ef21SPetr Machata 
219a9f8d1eSIdo Schimmel struct mlxsw_sp_span {
22a8e7e6e7SIdo Schimmel 	struct work_struct work;
23a8e7e6e7SIdo Schimmel 	struct mlxsw_sp *mlxsw_sp;
2408a3641fSIdo Schimmel 	const struct mlxsw_sp_span_trigger_ops **span_trigger_ops_arr;
2534e4ace5SIdo Schimmel 	const struct mlxsw_sp_span_entry_ops **span_entry_ops_arr;
2634e4ace5SIdo Schimmel 	size_t span_entry_ops_arr_size;
27ed04458dSIdo Schimmel 	struct list_head analyzed_ports_list;
28ed04458dSIdo Schimmel 	struct mutex analyzed_ports_lock; /* Protects analyzed_ports_list */
29c056618cSIdo Schimmel 	struct list_head trigger_entries_list;
304039504eSIdo Schimmel 	u16 policer_id_base;
314039504eSIdo Schimmel 	refcount_t policer_id_base_ref_count;
32eb833eecSIdo Schimmel 	atomic_t active_entries_count;
339a9f8d1eSIdo Schimmel 	int entries_count;
3418cee9daSKees Cook 	struct mlxsw_sp_span_entry entries[] __counted_by(entries_count);
359a9f8d1eSIdo Schimmel };
369a9f8d1eSIdo Schimmel 
37ed04458dSIdo Schimmel struct mlxsw_sp_span_analyzed_port {
38ed04458dSIdo Schimmel 	struct list_head list; /* Member of analyzed_ports_list */
39ed04458dSIdo Schimmel 	refcount_t ref_count;
40c934757dSAmit Cohen 	u16 local_port;
41ed04458dSIdo Schimmel 	bool ingress;
42ed04458dSIdo Schimmel };
43ed04458dSIdo Schimmel 
44c056618cSIdo Schimmel struct mlxsw_sp_span_trigger_entry {
45c056618cSIdo Schimmel 	struct list_head list; /* Member of trigger_entries_list */
4608a3641fSIdo Schimmel 	struct mlxsw_sp_span *span;
4708a3641fSIdo Schimmel 	const struct mlxsw_sp_span_trigger_ops *ops;
48c056618cSIdo Schimmel 	refcount_t ref_count;
49c934757dSAmit Cohen 	u16 local_port;
50c056618cSIdo Schimmel 	enum mlxsw_sp_span_trigger trigger;
51c056618cSIdo Schimmel 	struct mlxsw_sp_span_trigger_parms parms;
52c056618cSIdo Schimmel };
53c056618cSIdo Schimmel 
5408a3641fSIdo Schimmel enum mlxsw_sp_span_trigger_type {
5508a3641fSIdo Schimmel 	MLXSW_SP_SPAN_TRIGGER_TYPE_PORT,
56ab8c06b7SIdo Schimmel 	MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL,
5708a3641fSIdo Schimmel };
5808a3641fSIdo Schimmel 
5908a3641fSIdo Schimmel struct mlxsw_sp_span_trigger_ops {
6008a3641fSIdo Schimmel 	int (*bind)(struct mlxsw_sp_span_trigger_entry *trigger_entry);
6108a3641fSIdo Schimmel 	void (*unbind)(struct mlxsw_sp_span_trigger_entry *trigger_entry);
6208a3641fSIdo Schimmel 	bool (*matches)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
6308a3641fSIdo Schimmel 			enum mlxsw_sp_span_trigger trigger,
6408a3641fSIdo Schimmel 			struct mlxsw_sp_port *mlxsw_sp_port);
652bafb216SIdo Schimmel 	int (*enable)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
662bafb216SIdo Schimmel 		      struct mlxsw_sp_port *mlxsw_sp_port, u8 tc);
672bafb216SIdo Schimmel 	void (*disable)(struct mlxsw_sp_span_trigger_entry *trigger_entry,
682bafb216SIdo Schimmel 			struct mlxsw_sp_port *mlxsw_sp_port, u8 tc);
6908a3641fSIdo Schimmel };
7008a3641fSIdo Schimmel 
71a8e7e6e7SIdo Schimmel static void mlxsw_sp_span_respin_work(struct work_struct *work);
72a8e7e6e7SIdo Schimmel 
mlxsw_sp_span_occ_get(void * priv)73868678c5SDanielle Ratson static u64 mlxsw_sp_span_occ_get(void *priv)
74868678c5SDanielle Ratson {
75868678c5SDanielle Ratson 	const struct mlxsw_sp *mlxsw_sp = priv;
76868678c5SDanielle Ratson 
77eb833eecSIdo Schimmel 	return atomic_read(&mlxsw_sp->span->active_entries_count);
78868678c5SDanielle Ratson }
79868678c5SDanielle Ratson 
mlxsw_sp_span_init(struct mlxsw_sp * mlxsw_sp)80a629ef21SPetr Machata int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
81a629ef21SPetr Machata {
82868678c5SDanielle Ratson 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
839a9f8d1eSIdo Schimmel 	struct mlxsw_sp_span *span;
8408a3641fSIdo Schimmel 	int i, entries_count, err;
85a629ef21SPetr Machata 
86a629ef21SPetr Machata 	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_SPAN))
87a629ef21SPetr Machata 		return -EIO;
88a629ef21SPetr Machata 
896627b93bSIdo Schimmel 	entries_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_SPAN);
906627b93bSIdo Schimmel 	span = kzalloc(struct_size(span, entries, entries_count), GFP_KERNEL);
919a9f8d1eSIdo Schimmel 	if (!span)
929a9f8d1eSIdo Schimmel 		return -ENOMEM;
934039504eSIdo Schimmel 	refcount_set(&span->policer_id_base_ref_count, 0);
946627b93bSIdo Schimmel 	span->entries_count = entries_count;
95eb833eecSIdo Schimmel 	atomic_set(&span->active_entries_count, 0);
96ed04458dSIdo Schimmel 	mutex_init(&span->analyzed_ports_lock);
97ed04458dSIdo Schimmel 	INIT_LIST_HEAD(&span->analyzed_ports_list);
98c056618cSIdo Schimmel 	INIT_LIST_HEAD(&span->trigger_entries_list);
99a8e7e6e7SIdo Schimmel 	span->mlxsw_sp = mlxsw_sp;
1009a9f8d1eSIdo Schimmel 	mlxsw_sp->span = span;
1019a9f8d1eSIdo Schimmel 
102ca089223SIdo Schimmel 	for (i = 0; i < mlxsw_sp->span->entries_count; i++)
103ca089223SIdo Schimmel 		mlxsw_sp->span->entries[i].id = i;
104a629ef21SPetr Machata 
10508a3641fSIdo Schimmel 	err = mlxsw_sp->span_ops->init(mlxsw_sp);
10608a3641fSIdo Schimmel 	if (err)
10708a3641fSIdo Schimmel 		goto err_init;
10808a3641fSIdo Schimmel 
10972a4c8c9SJiri Pirko 	devl_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_SPAN,
110868678c5SDanielle Ratson 				       mlxsw_sp_span_occ_get, mlxsw_sp);
111a8e7e6e7SIdo Schimmel 	INIT_WORK(&span->work, mlxsw_sp_span_respin_work);
112868678c5SDanielle Ratson 
113a629ef21SPetr Machata 	return 0;
11408a3641fSIdo Schimmel 
11508a3641fSIdo Schimmel err_init:
11608a3641fSIdo Schimmel 	mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock);
11708a3641fSIdo Schimmel 	kfree(mlxsw_sp->span);
11808a3641fSIdo Schimmel 	return err;
119a629ef21SPetr Machata }
120a629ef21SPetr Machata 
mlxsw_sp_span_fini(struct mlxsw_sp * mlxsw_sp)121a629ef21SPetr Machata void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp)
122a629ef21SPetr Machata {
123868678c5SDanielle Ratson 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
124a629ef21SPetr Machata 
125a8e7e6e7SIdo Schimmel 	cancel_work_sync(&mlxsw_sp->span->work);
12672a4c8c9SJiri Pirko 	devl_resource_occ_get_unregister(devlink, MLXSW_SP_RESOURCE_SPAN);
127868678c5SDanielle Ratson 
128c056618cSIdo Schimmel 	WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->trigger_entries_list));
129ed04458dSIdo Schimmel 	WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->analyzed_ports_list));
130ed04458dSIdo Schimmel 	mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock);
1319a9f8d1eSIdo Schimmel 	kfree(mlxsw_sp->span);
132a629ef21SPetr Machata }
133a629ef21SPetr Machata 
mlxsw_sp1_span_cpu_can_handle(const struct net_device * dev)134fa8c08b8SIdo Schimmel static bool mlxsw_sp1_span_cpu_can_handle(const struct net_device *dev)
135fa8c08b8SIdo Schimmel {
136fa8c08b8SIdo Schimmel 	return !dev;
137fa8c08b8SIdo Schimmel }
138fa8c08b8SIdo Schimmel 
mlxsw_sp1_span_entry_cpu_parms(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,struct mlxsw_sp_span_parms * sparmsp)139fa8c08b8SIdo Schimmel static int mlxsw_sp1_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp,
140fa8c08b8SIdo Schimmel 					  const struct net_device *to_dev,
141fa8c08b8SIdo Schimmel 					  struct mlxsw_sp_span_parms *sparmsp)
142fa8c08b8SIdo Schimmel {
143fa8c08b8SIdo Schimmel 	return -EOPNOTSUPP;
144fa8c08b8SIdo Schimmel }
145fa8c08b8SIdo Schimmel 
146fa8c08b8SIdo Schimmel static int
mlxsw_sp1_span_entry_cpu_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)147fa8c08b8SIdo Schimmel mlxsw_sp1_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry,
148fa8c08b8SIdo Schimmel 				   struct mlxsw_sp_span_parms sparms)
149fa8c08b8SIdo Schimmel {
150fa8c08b8SIdo Schimmel 	return -EOPNOTSUPP;
151fa8c08b8SIdo Schimmel }
152fa8c08b8SIdo Schimmel 
153fa8c08b8SIdo Schimmel static void
mlxsw_sp1_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry * span_entry)154fa8c08b8SIdo Schimmel mlxsw_sp1_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry)
155fa8c08b8SIdo Schimmel {
156fa8c08b8SIdo Schimmel }
157fa8c08b8SIdo Schimmel 
158fa8c08b8SIdo Schimmel static const
159fa8c08b8SIdo Schimmel struct mlxsw_sp_span_entry_ops mlxsw_sp1_span_entry_ops_cpu = {
160b6f6881aSIdo Schimmel 	.is_static = true,
161fa8c08b8SIdo Schimmel 	.can_handle = mlxsw_sp1_span_cpu_can_handle,
162fa8c08b8SIdo Schimmel 	.parms_set = mlxsw_sp1_span_entry_cpu_parms,
163fa8c08b8SIdo Schimmel 	.configure = mlxsw_sp1_span_entry_cpu_configure,
164fa8c08b8SIdo Schimmel 	.deconfigure = mlxsw_sp1_span_entry_cpu_deconfigure,
165fa8c08b8SIdo Schimmel };
166fa8c08b8SIdo Schimmel 
1677b2ef81fSPetr Machata 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)168f4a626e2SIdo Schimmel mlxsw_sp_span_entry_phys_parms(struct mlxsw_sp *mlxsw_sp,
169f4a626e2SIdo Schimmel 			       const struct net_device *to_dev,
170169b5d95SPetr Machata 			       struct mlxsw_sp_span_parms *sparmsp)
1717b2ef81fSPetr Machata {
172169b5d95SPetr Machata 	sparmsp->dest_port = netdev_priv(to_dev);
173169b5d95SPetr Machata 	return 0;
174169b5d95SPetr Machata }
175169b5d95SPetr Machata 
176169b5d95SPetr Machata static int
mlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)177169b5d95SPetr Machata mlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry *span_entry,
178169b5d95SPetr Machata 				   struct mlxsw_sp_span_parms sparms)
179169b5d95SPetr Machata {
180169b5d95SPetr Machata 	struct mlxsw_sp_port *dest_port = sparms.dest_port;
181169b5d95SPetr Machata 	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
182c934757dSAmit Cohen 	u16 local_port = dest_port->local_port;
1837b2ef81fSPetr Machata 	char mpat_pl[MLXSW_REG_MPAT_LEN];
1847b2ef81fSPetr Machata 	int pa_id = span_entry->id;
1857b2ef81fSPetr Machata 
1867b2ef81fSPetr Machata 	/* Create a new port analayzer entry for local_port. */
1877b2ef81fSPetr Machata 	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
1887b2ef81fSPetr Machata 			    MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
1895c7659ebSIdo Schimmel 	mlxsw_reg_mpat_session_id_set(mpat_pl, sparms.session_id);
1904039504eSIdo Schimmel 	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
1914039504eSIdo Schimmel 	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
192169b5d95SPetr Machata 
1937b2ef81fSPetr Machata 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
1947b2ef81fSPetr Machata }
1957b2ef81fSPetr Machata 
1967b2ef81fSPetr Machata static void
mlxsw_sp_span_entry_deconfigure_common(struct mlxsw_sp_span_entry * span_entry,enum mlxsw_reg_mpat_span_type span_type)197169b5d95SPetr Machata mlxsw_sp_span_entry_deconfigure_common(struct mlxsw_sp_span_entry *span_entry,
198169b5d95SPetr Machata 				       enum mlxsw_reg_mpat_span_type span_type)
1997b2ef81fSPetr Machata {
200169b5d95SPetr Machata 	struct mlxsw_sp_port *dest_port = span_entry->parms.dest_port;
201169b5d95SPetr Machata 	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
202c934757dSAmit Cohen 	u16 local_port = dest_port->local_port;
2037b2ef81fSPetr Machata 	char mpat_pl[MLXSW_REG_MPAT_LEN];
2047b2ef81fSPetr Machata 	int pa_id = span_entry->id;
2057b2ef81fSPetr Machata 
206169b5d95SPetr Machata 	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false, span_type);
2075c7659ebSIdo Schimmel 	mlxsw_reg_mpat_session_id_set(mpat_pl, span_entry->parms.session_id);
2087b2ef81fSPetr Machata 	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
2097b2ef81fSPetr Machata }
2107b2ef81fSPetr Machata 
211169b5d95SPetr Machata static void
mlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry * span_entry)212169b5d95SPetr Machata mlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry *span_entry)
213169b5d95SPetr Machata {
214169b5d95SPetr Machata 	mlxsw_sp_span_entry_deconfigure_common(span_entry,
215169b5d95SPetr Machata 					    MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
216169b5d95SPetr Machata }
217169b5d95SPetr Machata 
218169b5d95SPetr Machata static const
219169b5d95SPetr Machata struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = {
220b6f6881aSIdo Schimmel 	.is_static = true,
221169b5d95SPetr Machata 	.can_handle = mlxsw_sp_port_dev_check,
2227f9b099bSAmit Cohen 	.parms_set = mlxsw_sp_span_entry_phys_parms,
223169b5d95SPetr Machata 	.configure = mlxsw_sp_span_entry_phys_configure,
224169b5d95SPetr Machata 	.deconfigure = mlxsw_sp_span_entry_phys_deconfigure,
225169b5d95SPetr Machata };
226169b5d95SPetr Machata 
mlxsw_sp_span_dmac(struct neigh_table * tbl,const void * pkey,struct net_device * dev,unsigned char dmac[ETH_ALEN])22727cf76feSPetr Machata static int mlxsw_sp_span_dmac(struct neigh_table *tbl,
22827cf76feSPetr Machata 			      const void *pkey,
229fc74ecbcSPetr Machata 			      struct net_device *dev,
23027cf76feSPetr Machata 			      unsigned char dmac[ETH_ALEN])
23127cf76feSPetr Machata {
232fc74ecbcSPetr Machata 	struct neighbour *neigh = neigh_lookup(tbl, pkey, dev);
23327cf76feSPetr Machata 	int err = 0;
23427cf76feSPetr Machata 
23527cf76feSPetr Machata 	if (!neigh) {
236fc74ecbcSPetr Machata 		neigh = neigh_create(tbl, pkey, dev);
23727cf76feSPetr Machata 		if (IS_ERR(neigh))
23827cf76feSPetr Machata 			return PTR_ERR(neigh);
23927cf76feSPetr Machata 	}
24027cf76feSPetr Machata 
24127cf76feSPetr Machata 	neigh_event_send(neigh, NULL);
24227cf76feSPetr Machata 
24327cf76feSPetr Machata 	read_lock_bh(&neigh->lock);
24427cf76feSPetr Machata 	if ((neigh->nud_state & NUD_VALID) && !neigh->dead)
24527cf76feSPetr Machata 		memcpy(dmac, neigh->ha, ETH_ALEN);
24627cf76feSPetr Machata 	else
24727cf76feSPetr Machata 		err = -ENOENT;
24827cf76feSPetr Machata 	read_unlock_bh(&neigh->lock);
24927cf76feSPetr Machata 
25027cf76feSPetr Machata 	neigh_release(neigh);
25127cf76feSPetr Machata 	return err;
25227cf76feSPetr Machata }
25327cf76feSPetr Machata 
25427cf76feSPetr Machata static int
mlxsw_sp_span_entry_unoffloadable(struct mlxsw_sp_span_parms * sparmsp)25527cf76feSPetr Machata mlxsw_sp_span_entry_unoffloadable(struct mlxsw_sp_span_parms *sparmsp)
25627cf76feSPetr Machata {
25727cf76feSPetr Machata 	sparmsp->dest_port = NULL;
25827cf76feSPetr Machata 	return 0;
25927cf76feSPetr Machata }
26027cf76feSPetr Machata 
261946a11e7SPetr Machata static struct net_device *
mlxsw_sp_span_entry_bridge_8021q(const struct net_device * br_dev,unsigned char * dmac,u16 * p_vid)262946a11e7SPetr Machata mlxsw_sp_span_entry_bridge_8021q(const struct net_device *br_dev,
263946a11e7SPetr Machata 				 unsigned char *dmac,
264946a11e7SPetr Machata 				 u16 *p_vid)
265946a11e7SPetr Machata {
266946a11e7SPetr Machata 	struct bridge_vlan_info vinfo;
267946a11e7SPetr Machata 	struct net_device *edev;
26803c44132SPetr Machata 	u16 vid = *p_vid;
269946a11e7SPetr Machata 
27003c44132SPetr Machata 	if (!vid && WARN_ON(br_vlan_get_pvid(br_dev, &vid)))
271946a11e7SPetr Machata 		return NULL;
27242c9135fSIdo Schimmel 	if (!vid || br_vlan_get_info(br_dev, vid, &vinfo) ||
27342c9135fSIdo Schimmel 	    !(vinfo.flags & BRIDGE_VLAN_INFO_BRENTRY))
274946a11e7SPetr Machata 		return NULL;
275946a11e7SPetr Machata 
27603c44132SPetr Machata 	edev = br_fdb_find_port(br_dev, dmac, vid);
277946a11e7SPetr Machata 	if (!edev)
278946a11e7SPetr Machata 		return NULL;
279946a11e7SPetr Machata 
28003c44132SPetr Machata 	if (br_vlan_get_info(edev, vid, &vinfo))
281946a11e7SPetr Machata 		return NULL;
2821fc68bb7SPetr Machata 	if (vinfo.flags & BRIDGE_VLAN_INFO_UNTAGGED)
2831fc68bb7SPetr Machata 		*p_vid = 0;
2841fc68bb7SPetr Machata 	else
28503c44132SPetr Machata 		*p_vid = vid;
286946a11e7SPetr Machata 	return edev;
287946a11e7SPetr Machata }
288946a11e7SPetr Machata 
289946a11e7SPetr Machata static struct net_device *
mlxsw_sp_span_entry_bridge_8021d(const struct net_device * br_dev,unsigned char * dmac)290946a11e7SPetr Machata mlxsw_sp_span_entry_bridge_8021d(const struct net_device *br_dev,
291946a11e7SPetr Machata 				 unsigned char *dmac)
292946a11e7SPetr Machata {
293946a11e7SPetr Machata 	return br_fdb_find_port(br_dev, dmac, 0);
294946a11e7SPetr Machata }
295946a11e7SPetr Machata 
296946a11e7SPetr Machata static struct net_device *
mlxsw_sp_span_entry_bridge(const struct net_device * br_dev,unsigned char dmac[ETH_ALEN],u16 * p_vid)297946a11e7SPetr Machata mlxsw_sp_span_entry_bridge(const struct net_device *br_dev,
298946a11e7SPetr Machata 			   unsigned char dmac[ETH_ALEN],
299946a11e7SPetr Machata 			   u16 *p_vid)
300946a11e7SPetr Machata {
301946a11e7SPetr Machata 	struct mlxsw_sp_bridge_port *bridge_port;
302946a11e7SPetr Machata 	enum mlxsw_reg_spms_state spms_state;
30303c44132SPetr Machata 	struct net_device *dev = NULL;
304946a11e7SPetr Machata 	struct mlxsw_sp_port *port;
305946a11e7SPetr Machata 	u8 stp_state;
306946a11e7SPetr Machata 
307946a11e7SPetr Machata 	if (br_vlan_enabled(br_dev))
308946a11e7SPetr Machata 		dev = mlxsw_sp_span_entry_bridge_8021q(br_dev, dmac, p_vid);
30903c44132SPetr Machata 	else if (!*p_vid)
310946a11e7SPetr Machata 		dev = mlxsw_sp_span_entry_bridge_8021d(br_dev, dmac);
311946a11e7SPetr Machata 	if (!dev)
312946a11e7SPetr Machata 		return NULL;
313946a11e7SPetr Machata 
314946a11e7SPetr Machata 	port = mlxsw_sp_port_dev_lower_find(dev);
315946a11e7SPetr Machata 	if (!port)
316946a11e7SPetr Machata 		return NULL;
317946a11e7SPetr Machata 
318946a11e7SPetr Machata 	bridge_port = mlxsw_sp_bridge_port_find(port->mlxsw_sp->bridge, dev);
319946a11e7SPetr Machata 	if (!bridge_port)
320946a11e7SPetr Machata 		return NULL;
321946a11e7SPetr Machata 
322946a11e7SPetr Machata 	stp_state = mlxsw_sp_bridge_port_stp_state(bridge_port);
323946a11e7SPetr Machata 	spms_state = mlxsw_sp_stp_spms_state(stp_state);
324946a11e7SPetr Machata 	if (spms_state != MLXSW_REG_SPMS_STATE_FORWARDING)
325946a11e7SPetr Machata 		return NULL;
326946a11e7SPetr Machata 
327946a11e7SPetr Machata 	return dev;
328946a11e7SPetr Machata }
329946a11e7SPetr Machata 
330e00698d1SPetr Machata static struct net_device *
mlxsw_sp_span_entry_vlan(const struct net_device * vlan_dev,u16 * p_vid)331e00698d1SPetr Machata mlxsw_sp_span_entry_vlan(const struct net_device *vlan_dev,
332e00698d1SPetr Machata 			 u16 *p_vid)
333e00698d1SPetr Machata {
334e00698d1SPetr Machata 	*p_vid = vlan_dev_vlan_id(vlan_dev);
335e00698d1SPetr Machata 	return vlan_dev_real_dev(vlan_dev);
336e00698d1SPetr Machata }
337e00698d1SPetr Machata 
33855c0211dSPetr Machata static struct net_device *
mlxsw_sp_span_entry_lag(struct net_device * lag_dev)33955c0211dSPetr Machata mlxsw_sp_span_entry_lag(struct net_device *lag_dev)
34055c0211dSPetr Machata {
34155c0211dSPetr Machata 	struct net_device *dev;
34255c0211dSPetr Machata 	struct list_head *iter;
34355c0211dSPetr Machata 
34455c0211dSPetr Machata 	netdev_for_each_lower_dev(lag_dev, dev, iter)
345b5de82f3SPetr Machata 		if (netif_carrier_ok(dev) &&
346b5de82f3SPetr Machata 		    net_lag_port_dev_txable(dev) &&
347b5de82f3SPetr Machata 		    mlxsw_sp_port_dev_check(dev))
34855c0211dSPetr Machata 			return dev;
34955c0211dSPetr Machata 
35055c0211dSPetr Machata 	return NULL;
35155c0211dSPetr Machata }
35255c0211dSPetr Machata 
35399db5229SPetr Machata 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)354fc74ecbcSPetr Machata mlxsw_sp_span_entry_tunnel_parms_common(struct net_device *edev,
35527cf76feSPetr Machata 					union mlxsw_sp_l3addr saddr,
35627cf76feSPetr Machata 					union mlxsw_sp_l3addr daddr,
35727cf76feSPetr Machata 					union mlxsw_sp_l3addr gw,
35827cf76feSPetr Machata 					__u8 ttl,
35927cf76feSPetr Machata 					struct neigh_table *tbl,
36027cf76feSPetr Machata 					struct mlxsw_sp_span_parms *sparmsp)
36127cf76feSPetr Machata {
36227cf76feSPetr Machata 	unsigned char dmac[ETH_ALEN];
363946a11e7SPetr Machata 	u16 vid = 0;
36427cf76feSPetr Machata 
36527cf76feSPetr Machata 	if (mlxsw_sp_l3addr_is_zero(gw))
36627cf76feSPetr Machata 		gw = daddr;
36727cf76feSPetr Machata 
368fc74ecbcSPetr Machata 	if (!edev || mlxsw_sp_span_dmac(tbl, &gw, edev, dmac))
369946a11e7SPetr Machata 		goto unoffloadable;
370946a11e7SPetr Machata 
371fc74ecbcSPetr Machata 	if (is_vlan_dev(edev))
372fc74ecbcSPetr Machata 		edev = mlxsw_sp_span_entry_vlan(edev, &vid);
37303c44132SPetr Machata 
374fc74ecbcSPetr Machata 	if (netif_is_bridge_master(edev)) {
375fc74ecbcSPetr Machata 		edev = mlxsw_sp_span_entry_bridge(edev, dmac, &vid);
376fc74ecbcSPetr Machata 		if (!edev)
377946a11e7SPetr Machata 			goto unoffloadable;
378946a11e7SPetr Machata 	}
379946a11e7SPetr Machata 
380fc74ecbcSPetr Machata 	if (is_vlan_dev(edev)) {
381fc74ecbcSPetr Machata 		if (vid || !(edev->flags & IFF_UP))
38203c44132SPetr Machata 			goto unoffloadable;
383fc74ecbcSPetr Machata 		edev = mlxsw_sp_span_entry_vlan(edev, &vid);
38403c44132SPetr Machata 	}
38503c44132SPetr Machata 
38655c0211dSPetr Machata 	if (netif_is_lag_master(edev)) {
38755c0211dSPetr Machata 		if (!(edev->flags & IFF_UP))
38855c0211dSPetr Machata 			goto unoffloadable;
38955c0211dSPetr Machata 		edev = mlxsw_sp_span_entry_lag(edev);
39055c0211dSPetr Machata 		if (!edev)
39155c0211dSPetr Machata 			goto unoffloadable;
39255c0211dSPetr Machata 	}
39355c0211dSPetr Machata 
394fc74ecbcSPetr Machata 	if (!mlxsw_sp_port_dev_check(edev))
395946a11e7SPetr Machata 		goto unoffloadable;
39627cf76feSPetr Machata 
397fc74ecbcSPetr Machata 	sparmsp->dest_port = netdev_priv(edev);
39827cf76feSPetr Machata 	sparmsp->ttl = ttl;
39927cf76feSPetr Machata 	memcpy(sparmsp->dmac, dmac, ETH_ALEN);
400fc74ecbcSPetr Machata 	memcpy(sparmsp->smac, edev->dev_addr, ETH_ALEN);
40127cf76feSPetr Machata 	sparmsp->saddr = saddr;
40227cf76feSPetr Machata 	sparmsp->daddr = daddr;
403946a11e7SPetr Machata 	sparmsp->vid = vid;
40427cf76feSPetr Machata 	return 0;
405946a11e7SPetr Machata 
406946a11e7SPetr Machata unoffloadable:
407946a11e7SPetr Machata 	return mlxsw_sp_span_entry_unoffloadable(sparmsp);
40827cf76feSPetr Machata }
40927cf76feSPetr Machata 
41099db5229SPetr Machata #if IS_ENABLED(CONFIG_NET_IPGRE)
41136a1c3bdSPetr Machata static struct net_device *
mlxsw_sp_span_gretap4_route(const struct net_device * to_dev,__be32 * saddrp,__be32 * daddrp)41236a1c3bdSPetr Machata mlxsw_sp_span_gretap4_route(const struct net_device *to_dev,
41336a1c3bdSPetr Machata 			    __be32 *saddrp, __be32 *daddrp)
41436a1c3bdSPetr Machata {
41536a1c3bdSPetr Machata 	struct ip_tunnel *tun = netdev_priv(to_dev);
416117aef12SAlexander Lobakin 	struct ip_tunnel_parm_kern parms;
41736a1c3bdSPetr Machata 	struct net_device *dev = NULL;
41836a1c3bdSPetr Machata 	struct rtable *rt = NULL;
41936a1c3bdSPetr Machata 	struct flowi4 fl4;
42036a1c3bdSPetr Machata 
42136a1c3bdSPetr Machata 	/* We assume "dev" stays valid after rt is put. */
42236a1c3bdSPetr Machata 	ASSERT_RTNL();
42336a1c3bdSPetr Machata 
42436a1c3bdSPetr Machata 	parms = mlxsw_sp_ipip_netdev_parms4(to_dev);
42536a1c3bdSPetr Machata 	ip_tunnel_init_flow(&fl4, parms.iph.protocol, *daddrp, *saddrp,
4267ec9fce4SEyal Birger 			    0, 0, dev_net(to_dev), parms.link, tun->fwmark, 0,
4277ec9fce4SEyal Birger 			    0);
42836a1c3bdSPetr Machata 
42936a1c3bdSPetr Machata 	rt = ip_route_output_key(tun->net, &fl4);
43036a1c3bdSPetr Machata 	if (IS_ERR(rt))
43136a1c3bdSPetr Machata 		return NULL;
43236a1c3bdSPetr Machata 
43336a1c3bdSPetr Machata 	if (rt->rt_type != RTN_UNICAST)
43436a1c3bdSPetr Machata 		goto out;
43536a1c3bdSPetr Machata 
43636a1c3bdSPetr Machata 	dev = rt->dst.dev;
43736a1c3bdSPetr Machata 	*saddrp = fl4.saddr;
4381550c171SDavid Ahern 	if (rt->rt_gw_family == AF_INET)
4391550c171SDavid Ahern 		*daddrp = rt->rt_gw4;
4400f5f7d7bSDavid Ahern 	/* can not offload if route has an IPv6 gateway */
4410f5f7d7bSDavid Ahern 	else if (rt->rt_gw_family == AF_INET6)
4420f5f7d7bSDavid Ahern 		dev = NULL;
44336a1c3bdSPetr Machata 
44436a1c3bdSPetr Machata out:
44536a1c3bdSPetr Machata 	ip_rt_put(rt);
44636a1c3bdSPetr Machata 	return dev;
44736a1c3bdSPetr Machata }
44836a1c3bdSPetr Machata 
44927cf76feSPetr Machata 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)450f4a626e2SIdo Schimmel mlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp,
451f4a626e2SIdo Schimmel 				  const struct net_device *to_dev,
45227cf76feSPetr Machata 				  struct mlxsw_sp_span_parms *sparmsp)
45327cf76feSPetr Machata {
454117aef12SAlexander Lobakin 	struct ip_tunnel_parm_kern tparm = mlxsw_sp_ipip_netdev_parms4(to_dev);
45527cf76feSPetr Machata 	union mlxsw_sp_l3addr saddr = { .addr4 = tparm.iph.saddr };
45627cf76feSPetr Machata 	union mlxsw_sp_l3addr daddr = { .addr4 = tparm.iph.daddr };
45727cf76feSPetr Machata 	bool inherit_tos = tparm.iph.tos & 0x1;
45827cf76feSPetr Machata 	bool inherit_ttl = !tparm.iph.ttl;
45927cf76feSPetr Machata 	union mlxsw_sp_l3addr gw = daddr;
46027cf76feSPetr Machata 	struct net_device *l3edev;
46127cf76feSPetr Machata 
46227cf76feSPetr Machata 	if (!(to_dev->flags & IFF_UP) ||
46327cf76feSPetr Machata 	    /* Reject tunnels with GRE keys, checksums, etc. */
4645832c4a7SAlexander Lobakin 	    !ip_tunnel_flags_empty(tparm.i_flags) ||
4655832c4a7SAlexander Lobakin 	    !ip_tunnel_flags_empty(tparm.o_flags) ||
46627cf76feSPetr Machata 	    /* Require a fixed TTL and a TOS copied from the mirrored packet. */
46727cf76feSPetr Machata 	    inherit_ttl || !inherit_tos ||
46827cf76feSPetr Machata 	    /* A destination address may not be "any". */
46927cf76feSPetr Machata 	    mlxsw_sp_l3addr_is_zero(daddr))
47027cf76feSPetr Machata 		return mlxsw_sp_span_entry_unoffloadable(sparmsp);
47127cf76feSPetr Machata 
47227cf76feSPetr Machata 	l3edev = mlxsw_sp_span_gretap4_route(to_dev, &saddr.addr4, &gw.addr4);
47327cf76feSPetr Machata 	return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
47427cf76feSPetr Machata 						       tparm.iph.ttl,
47527cf76feSPetr Machata 						       &arp_tbl, sparmsp);
47627cf76feSPetr Machata }
47727cf76feSPetr Machata 
47827cf76feSPetr Machata static int
mlxsw_sp_span_entry_gretap4_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)47927cf76feSPetr Machata mlxsw_sp_span_entry_gretap4_configure(struct mlxsw_sp_span_entry *span_entry,
48027cf76feSPetr Machata 				      struct mlxsw_sp_span_parms sparms)
48127cf76feSPetr Machata {
48227cf76feSPetr Machata 	struct mlxsw_sp_port *dest_port = sparms.dest_port;
48327cf76feSPetr Machata 	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
484c934757dSAmit Cohen 	u16 local_port = dest_port->local_port;
48527cf76feSPetr Machata 	char mpat_pl[MLXSW_REG_MPAT_LEN];
48627cf76feSPetr Machata 	int pa_id = span_entry->id;
48727cf76feSPetr Machata 
48827cf76feSPetr Machata 	/* Create a new port analayzer entry for local_port. */
48927cf76feSPetr Machata 	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
49027cf76feSPetr Machata 			    MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
4914039504eSIdo Schimmel 	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
4924039504eSIdo Schimmel 	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
493946a11e7SPetr Machata 	mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
49427cf76feSPetr Machata 	mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
49527cf76feSPetr Machata 				    MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
496946a11e7SPetr Machata 				    sparms.dmac, !!sparms.vid);
49727cf76feSPetr Machata 	mlxsw_reg_mpat_eth_rspan_l3_ipv4_pack(mpat_pl,
49827cf76feSPetr Machata 					      sparms.ttl, sparms.smac,
49927cf76feSPetr Machata 					      be32_to_cpu(sparms.saddr.addr4),
50027cf76feSPetr Machata 					      be32_to_cpu(sparms.daddr.addr4));
50127cf76feSPetr Machata 
50227cf76feSPetr Machata 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
50327cf76feSPetr Machata }
50427cf76feSPetr Machata 
50527cf76feSPetr Machata static void
mlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry * span_entry)50627cf76feSPetr Machata mlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry *span_entry)
50727cf76feSPetr Machata {
50827cf76feSPetr Machata 	mlxsw_sp_span_entry_deconfigure_common(span_entry,
50927cf76feSPetr Machata 					MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
51027cf76feSPetr Machata }
51127cf76feSPetr Machata 
51227cf76feSPetr Machata static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap4 = {
5130621e6fcSOz Shlomo 	.can_handle = netif_is_gretap,
5147f9b099bSAmit Cohen 	.parms_set = mlxsw_sp_span_entry_gretap4_parms,
51527cf76feSPetr Machata 	.configure = mlxsw_sp_span_entry_gretap4_configure,
51627cf76feSPetr Machata 	.deconfigure = mlxsw_sp_span_entry_gretap4_deconfigure,
51727cf76feSPetr Machata };
51899db5229SPetr Machata #endif
51927cf76feSPetr Machata 
52099db5229SPetr Machata #if IS_ENABLED(CONFIG_IPV6_GRE)
5218f08a528SPetr Machata static struct net_device *
mlxsw_sp_span_gretap6_route(const struct net_device * to_dev,struct in6_addr * saddrp,struct in6_addr * daddrp)5228f08a528SPetr Machata mlxsw_sp_span_gretap6_route(const struct net_device *to_dev,
5238f08a528SPetr Machata 			    struct in6_addr *saddrp,
5248f08a528SPetr Machata 			    struct in6_addr *daddrp)
5258f08a528SPetr Machata {
5268f08a528SPetr Machata 	struct ip6_tnl *t = netdev_priv(to_dev);
5278f08a528SPetr Machata 	struct flowi6 fl6 = t->fl.u.ip6;
5288f08a528SPetr Machata 	struct net_device *dev = NULL;
5298f08a528SPetr Machata 	struct dst_entry *dst;
5308f08a528SPetr Machata 	struct rt6_info *rt6;
5318f08a528SPetr Machata 
5328f08a528SPetr Machata 	/* We assume "dev" stays valid after dst is released. */
5338f08a528SPetr Machata 	ASSERT_RTNL();
5348f08a528SPetr Machata 
5358f08a528SPetr Machata 	fl6.flowi6_mark = t->parms.fwmark;
5368f08a528SPetr Machata 	if (!ip6_tnl_xmit_ctl(t, &fl6.saddr, &fl6.daddr))
5378f08a528SPetr Machata 		return NULL;
5388f08a528SPetr Machata 
5398f08a528SPetr Machata 	dst = ip6_route_output(t->net, NULL, &fl6);
5408f08a528SPetr Machata 	if (!dst || dst->error)
5418f08a528SPetr Machata 		goto out;
5428f08a528SPetr Machata 
543*e8dfd42cSEric Dumazet 	rt6 = dst_rt6_info(dst);
5448f08a528SPetr Machata 
5458f08a528SPetr Machata 	dev = dst->dev;
5468f08a528SPetr Machata 	*saddrp = fl6.saddr;
5478f08a528SPetr Machata 	*daddrp = rt6->rt6i_gateway;
5488f08a528SPetr Machata 
5498f08a528SPetr Machata out:
5508f08a528SPetr Machata 	dst_release(dst);
5518f08a528SPetr Machata 	return dev;
5528f08a528SPetr Machata }
5538f08a528SPetr Machata 
5548f08a528SPetr Machata 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)555f4a626e2SIdo Schimmel mlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp,
556f4a626e2SIdo Schimmel 				  const struct net_device *to_dev,
5578f08a528SPetr Machata 				  struct mlxsw_sp_span_parms *sparmsp)
5588f08a528SPetr Machata {
5598f08a528SPetr Machata 	struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(to_dev);
5608f08a528SPetr Machata 	bool inherit_tos = tparm.flags & IP6_TNL_F_USE_ORIG_TCLASS;
5618f08a528SPetr Machata 	union mlxsw_sp_l3addr saddr = { .addr6 = tparm.laddr };
5628f08a528SPetr Machata 	union mlxsw_sp_l3addr daddr = { .addr6 = tparm.raddr };
5638f08a528SPetr Machata 	bool inherit_ttl = !tparm.hop_limit;
5648f08a528SPetr Machata 	union mlxsw_sp_l3addr gw = daddr;
5658f08a528SPetr Machata 	struct net_device *l3edev;
5668f08a528SPetr Machata 
5678f08a528SPetr Machata 	if (!(to_dev->flags & IFF_UP) ||
5688f08a528SPetr Machata 	    /* Reject tunnels with GRE keys, checksums, etc. */
5695832c4a7SAlexander Lobakin 	    !ip_tunnel_flags_empty(tparm.i_flags) ||
5705832c4a7SAlexander Lobakin 	    !ip_tunnel_flags_empty(tparm.o_flags) ||
5718f08a528SPetr Machata 	    /* Require a fixed TTL and a TOS copied from the mirrored packet. */
5728f08a528SPetr Machata 	    inherit_ttl || !inherit_tos ||
5738f08a528SPetr Machata 	    /* A destination address may not be "any". */
5748f08a528SPetr Machata 	    mlxsw_sp_l3addr_is_zero(daddr))
5758f08a528SPetr Machata 		return mlxsw_sp_span_entry_unoffloadable(sparmsp);
5768f08a528SPetr Machata 
5778f08a528SPetr Machata 	l3edev = mlxsw_sp_span_gretap6_route(to_dev, &saddr.addr6, &gw.addr6);
5788f08a528SPetr Machata 	return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
5798f08a528SPetr Machata 						       tparm.hop_limit,
5808f08a528SPetr Machata 						       &nd_tbl, sparmsp);
5818f08a528SPetr Machata }
5828f08a528SPetr Machata 
5838f08a528SPetr Machata static int
mlxsw_sp_span_entry_gretap6_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)5848f08a528SPetr Machata mlxsw_sp_span_entry_gretap6_configure(struct mlxsw_sp_span_entry *span_entry,
5858f08a528SPetr Machata 				      struct mlxsw_sp_span_parms sparms)
5868f08a528SPetr Machata {
5878f08a528SPetr Machata 	struct mlxsw_sp_port *dest_port = sparms.dest_port;
5888f08a528SPetr Machata 	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
589c934757dSAmit Cohen 	u16 local_port = dest_port->local_port;
5908f08a528SPetr Machata 	char mpat_pl[MLXSW_REG_MPAT_LEN];
5918f08a528SPetr Machata 	int pa_id = span_entry->id;
5928f08a528SPetr Machata 
5938f08a528SPetr Machata 	/* Create a new port analayzer entry for local_port. */
5948f08a528SPetr Machata 	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
5958f08a528SPetr Machata 			    MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
5964039504eSIdo Schimmel 	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
5974039504eSIdo Schimmel 	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
598946a11e7SPetr Machata 	mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
5998f08a528SPetr Machata 	mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
6008f08a528SPetr Machata 				    MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
601946a11e7SPetr Machata 				    sparms.dmac, !!sparms.vid);
6028f08a528SPetr Machata 	mlxsw_reg_mpat_eth_rspan_l3_ipv6_pack(mpat_pl, sparms.ttl, sparms.smac,
6038f08a528SPetr Machata 					      sparms.saddr.addr6,
6048f08a528SPetr Machata 					      sparms.daddr.addr6);
6058f08a528SPetr Machata 
6068f08a528SPetr Machata 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
6078f08a528SPetr Machata }
6088f08a528SPetr Machata 
6098f08a528SPetr Machata static void
mlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry * span_entry)6108f08a528SPetr Machata mlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry *span_entry)
6118f08a528SPetr Machata {
6128f08a528SPetr Machata 	mlxsw_sp_span_entry_deconfigure_common(span_entry,
6138f08a528SPetr Machata 					MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
6148f08a528SPetr Machata }
6158f08a528SPetr Machata 
6168f08a528SPetr Machata static const
6178f08a528SPetr Machata struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap6 = {
6180621e6fcSOz Shlomo 	.can_handle = netif_is_ip6gretap,
6197f9b099bSAmit Cohen 	.parms_set = mlxsw_sp_span_entry_gretap6_parms,
6208f08a528SPetr Machata 	.configure = mlxsw_sp_span_entry_gretap6_configure,
6218f08a528SPetr Machata 	.deconfigure = mlxsw_sp_span_entry_gretap6_deconfigure,
6228f08a528SPetr Machata };
62399db5229SPetr Machata #endif
6248f08a528SPetr Machata 
625e00698d1SPetr Machata static bool
mlxsw_sp_span_vlan_can_handle(const struct net_device * dev)626e00698d1SPetr Machata mlxsw_sp_span_vlan_can_handle(const struct net_device *dev)
627e00698d1SPetr Machata {
628e00698d1SPetr Machata 	return is_vlan_dev(dev) &&
629e00698d1SPetr Machata 	       mlxsw_sp_port_dev_check(vlan_dev_real_dev(dev));
630e00698d1SPetr Machata }
631e00698d1SPetr Machata 
632e00698d1SPetr Machata 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)633f4a626e2SIdo Schimmel mlxsw_sp_span_entry_vlan_parms(struct mlxsw_sp *mlxsw_sp,
634f4a626e2SIdo Schimmel 			       const struct net_device *to_dev,
635e00698d1SPetr Machata 			       struct mlxsw_sp_span_parms *sparmsp)
636e00698d1SPetr Machata {
637e00698d1SPetr Machata 	struct net_device *real_dev;
638e00698d1SPetr Machata 	u16 vid;
639e00698d1SPetr Machata 
640e00698d1SPetr Machata 	if (!(to_dev->flags & IFF_UP))
641e00698d1SPetr Machata 		return mlxsw_sp_span_entry_unoffloadable(sparmsp);
642e00698d1SPetr Machata 
643e00698d1SPetr Machata 	real_dev = mlxsw_sp_span_entry_vlan(to_dev, &vid);
644e00698d1SPetr Machata 	sparmsp->dest_port = netdev_priv(real_dev);
645e00698d1SPetr Machata 	sparmsp->vid = vid;
646e00698d1SPetr Machata 	return 0;
647e00698d1SPetr Machata }
648e00698d1SPetr Machata 
649e00698d1SPetr Machata static int
mlxsw_sp_span_entry_vlan_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)650e00698d1SPetr Machata mlxsw_sp_span_entry_vlan_configure(struct mlxsw_sp_span_entry *span_entry,
651e00698d1SPetr Machata 				   struct mlxsw_sp_span_parms sparms)
652e00698d1SPetr Machata {
653e00698d1SPetr Machata 	struct mlxsw_sp_port *dest_port = sparms.dest_port;
654e00698d1SPetr Machata 	struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
655c934757dSAmit Cohen 	u16 local_port = dest_port->local_port;
656e00698d1SPetr Machata 	char mpat_pl[MLXSW_REG_MPAT_LEN];
657e00698d1SPetr Machata 	int pa_id = span_entry->id;
658e00698d1SPetr Machata 
659e00698d1SPetr Machata 	mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
660e00698d1SPetr Machata 			    MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH);
6614039504eSIdo Schimmel 	mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable);
6624039504eSIdo Schimmel 	mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id);
663e00698d1SPetr Machata 	mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
664e00698d1SPetr Machata 
665e00698d1SPetr Machata 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
666e00698d1SPetr Machata }
667e00698d1SPetr Machata 
668e00698d1SPetr Machata static void
mlxsw_sp_span_entry_vlan_deconfigure(struct mlxsw_sp_span_entry * span_entry)669e00698d1SPetr Machata mlxsw_sp_span_entry_vlan_deconfigure(struct mlxsw_sp_span_entry *span_entry)
670e00698d1SPetr Machata {
671e00698d1SPetr Machata 	mlxsw_sp_span_entry_deconfigure_common(span_entry,
672e00698d1SPetr Machata 					MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH);
673e00698d1SPetr Machata }
674e00698d1SPetr Machata 
675e00698d1SPetr Machata static const
676e00698d1SPetr Machata struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_vlan = {
677e00698d1SPetr Machata 	.can_handle = mlxsw_sp_span_vlan_can_handle,
6787f9b099bSAmit Cohen 	.parms_set = mlxsw_sp_span_entry_vlan_parms,
679e00698d1SPetr Machata 	.configure = mlxsw_sp_span_entry_vlan_configure,
680e00698d1SPetr Machata 	.deconfigure = mlxsw_sp_span_entry_vlan_deconfigure,
681e00698d1SPetr Machata };
682e00698d1SPetr Machata 
683169b5d95SPetr Machata static const
68434e4ace5SIdo Schimmel struct mlxsw_sp_span_entry_ops *mlxsw_sp1_span_entry_ops_arr[] = {
685fa8c08b8SIdo Schimmel 	&mlxsw_sp1_span_entry_ops_cpu,
68634e4ace5SIdo Schimmel 	&mlxsw_sp_span_entry_ops_phys,
68734e4ace5SIdo Schimmel #if IS_ENABLED(CONFIG_NET_IPGRE)
68834e4ace5SIdo Schimmel 	&mlxsw_sp_span_entry_ops_gretap4,
68934e4ace5SIdo Schimmel #endif
69034e4ace5SIdo Schimmel #if IS_ENABLED(CONFIG_IPV6_GRE)
69134e4ace5SIdo Schimmel 	&mlxsw_sp_span_entry_ops_gretap6,
69234e4ace5SIdo Schimmel #endif
69334e4ace5SIdo Schimmel 	&mlxsw_sp_span_entry_ops_vlan,
69434e4ace5SIdo Schimmel };
69534e4ace5SIdo Schimmel 
mlxsw_sp2_span_cpu_can_handle(const struct net_device * dev)696fa8c08b8SIdo Schimmel static bool mlxsw_sp2_span_cpu_can_handle(const struct net_device *dev)
697fa8c08b8SIdo Schimmel {
698fa8c08b8SIdo Schimmel 	return !dev;
699fa8c08b8SIdo Schimmel }
700fa8c08b8SIdo Schimmel 
mlxsw_sp2_span_entry_cpu_parms(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev,struct mlxsw_sp_span_parms * sparmsp)701fa8c08b8SIdo Schimmel static int mlxsw_sp2_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp,
702fa8c08b8SIdo Schimmel 					  const struct net_device *to_dev,
703fa8c08b8SIdo Schimmel 					  struct mlxsw_sp_span_parms *sparmsp)
704fa8c08b8SIdo Schimmel {
705fa8c08b8SIdo Schimmel 	sparmsp->dest_port = mlxsw_sp->ports[MLXSW_PORT_CPU_PORT];
706fa8c08b8SIdo Schimmel 	return 0;
707fa8c08b8SIdo Schimmel }
708fa8c08b8SIdo Schimmel 
709fa8c08b8SIdo Schimmel static int
mlxsw_sp2_span_entry_cpu_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)710fa8c08b8SIdo Schimmel mlxsw_sp2_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry,
711fa8c08b8SIdo Schimmel 				   struct mlxsw_sp_span_parms sparms)
712fa8c08b8SIdo Schimmel {
713fa8c08b8SIdo Schimmel 	/* Mirroring to the CPU port is like mirroring to any other physical
714fa8c08b8SIdo Schimmel 	 * port. Its local port is used instead of that of the physical port.
715fa8c08b8SIdo Schimmel 	 */
716fa8c08b8SIdo Schimmel 	return mlxsw_sp_span_entry_phys_configure(span_entry, sparms);
717fa8c08b8SIdo Schimmel }
718fa8c08b8SIdo Schimmel 
719fa8c08b8SIdo Schimmel static void
mlxsw_sp2_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry * span_entry)720fa8c08b8SIdo Schimmel mlxsw_sp2_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry)
721fa8c08b8SIdo Schimmel {
722fa8c08b8SIdo Schimmel 	enum mlxsw_reg_mpat_span_type span_type;
723fa8c08b8SIdo Schimmel 
724fa8c08b8SIdo Schimmel 	span_type = MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH;
725fa8c08b8SIdo Schimmel 	mlxsw_sp_span_entry_deconfigure_common(span_entry, span_type);
726fa8c08b8SIdo Schimmel }
727fa8c08b8SIdo Schimmel 
728fa8c08b8SIdo Schimmel static const
729fa8c08b8SIdo Schimmel struct mlxsw_sp_span_entry_ops mlxsw_sp2_span_entry_ops_cpu = {
730b6f6881aSIdo Schimmel 	.is_static = true,
731fa8c08b8SIdo Schimmel 	.can_handle = mlxsw_sp2_span_cpu_can_handle,
732fa8c08b8SIdo Schimmel 	.parms_set = mlxsw_sp2_span_entry_cpu_parms,
733fa8c08b8SIdo Schimmel 	.configure = mlxsw_sp2_span_entry_cpu_configure,
734fa8c08b8SIdo Schimmel 	.deconfigure = mlxsw_sp2_span_entry_cpu_deconfigure,
735fa8c08b8SIdo Schimmel };
736fa8c08b8SIdo Schimmel 
73734e4ace5SIdo Schimmel static const
73834e4ace5SIdo Schimmel struct mlxsw_sp_span_entry_ops *mlxsw_sp2_span_entry_ops_arr[] = {
739fa8c08b8SIdo Schimmel 	&mlxsw_sp2_span_entry_ops_cpu,
740169b5d95SPetr Machata 	&mlxsw_sp_span_entry_ops_phys,
74199db5229SPetr Machata #if IS_ENABLED(CONFIG_NET_IPGRE)
74227cf76feSPetr Machata 	&mlxsw_sp_span_entry_ops_gretap4,
74399db5229SPetr Machata #endif
74499db5229SPetr Machata #if IS_ENABLED(CONFIG_IPV6_GRE)
7458f08a528SPetr Machata 	&mlxsw_sp_span_entry_ops_gretap6,
74699db5229SPetr Machata #endif
747e00698d1SPetr Machata 	&mlxsw_sp_span_entry_ops_vlan,
748169b5d95SPetr Machata };
749169b5d95SPetr Machata 
750169b5d95SPetr Machata 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)751f4a626e2SIdo Schimmel mlxsw_sp_span_entry_nop_parms(struct mlxsw_sp *mlxsw_sp,
752f4a626e2SIdo Schimmel 			      const struct net_device *to_dev,
753169b5d95SPetr Machata 			      struct mlxsw_sp_span_parms *sparmsp)
754169b5d95SPetr Machata {
75527cf76feSPetr Machata 	return mlxsw_sp_span_entry_unoffloadable(sparmsp);
756169b5d95SPetr Machata }
757169b5d95SPetr Machata 
758169b5d95SPetr Machata static int
mlxsw_sp_span_entry_nop_configure(struct mlxsw_sp_span_entry * span_entry,struct mlxsw_sp_span_parms sparms)759169b5d95SPetr Machata mlxsw_sp_span_entry_nop_configure(struct mlxsw_sp_span_entry *span_entry,
760169b5d95SPetr Machata 				  struct mlxsw_sp_span_parms sparms)
761169b5d95SPetr Machata {
762169b5d95SPetr Machata 	return 0;
763169b5d95SPetr Machata }
764169b5d95SPetr Machata 
765169b5d95SPetr Machata static void
mlxsw_sp_span_entry_nop_deconfigure(struct mlxsw_sp_span_entry * span_entry)766169b5d95SPetr Machata mlxsw_sp_span_entry_nop_deconfigure(struct mlxsw_sp_span_entry *span_entry)
767169b5d95SPetr Machata {
768169b5d95SPetr Machata }
769169b5d95SPetr Machata 
770169b5d95SPetr Machata static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_nop = {
7717f9b099bSAmit Cohen 	.parms_set = mlxsw_sp_span_entry_nop_parms,
772169b5d95SPetr Machata 	.configure = mlxsw_sp_span_entry_nop_configure,
773169b5d95SPetr Machata 	.deconfigure = mlxsw_sp_span_entry_nop_deconfigure,
774169b5d95SPetr Machata };
775169b5d95SPetr Machata 
776169b5d95SPetr Machata 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)777169b5d95SPetr Machata mlxsw_sp_span_entry_configure(struct mlxsw_sp *mlxsw_sp,
778169b5d95SPetr Machata 			      struct mlxsw_sp_span_entry *span_entry,
779169b5d95SPetr Machata 			      struct mlxsw_sp_span_parms sparms)
780169b5d95SPetr Machata {
7818146458fSAmit Cohen 	int err;
7828146458fSAmit Cohen 
7838146458fSAmit Cohen 	if (!sparms.dest_port)
7848146458fSAmit Cohen 		goto set_parms;
7858146458fSAmit Cohen 
78652a6444cSPetr Machata 	if (sparms.dest_port->mlxsw_sp != mlxsw_sp) {
7876edc8beaSIdo Schimmel 		dev_err(mlxsw_sp->bus_info->dev,
7886edc8beaSIdo Schimmel 			"Cannot mirror to a port which belongs to a different mlxsw instance\n");
78952a6444cSPetr Machata 		sparms.dest_port = NULL;
7908146458fSAmit Cohen 		goto set_parms;
7918146458fSAmit Cohen 	}
7928146458fSAmit Cohen 
7938146458fSAmit Cohen 	err = span_entry->ops->configure(span_entry, sparms);
7948146458fSAmit Cohen 	if (err) {
7956edc8beaSIdo Schimmel 		dev_err(mlxsw_sp->bus_info->dev, "Failed to offload mirror\n");
796169b5d95SPetr Machata 		sparms.dest_port = NULL;
7978146458fSAmit Cohen 		goto set_parms;
798169b5d95SPetr Machata 	}
799169b5d95SPetr Machata 
8008146458fSAmit Cohen set_parms:
801169b5d95SPetr Machata 	span_entry->parms = sparms;
802169b5d95SPetr Machata }
803169b5d95SPetr Machata 
804169b5d95SPetr Machata static void
mlxsw_sp_span_entry_deconfigure(struct mlxsw_sp_span_entry * span_entry)805169b5d95SPetr Machata mlxsw_sp_span_entry_deconfigure(struct mlxsw_sp_span_entry *span_entry)
806169b5d95SPetr Machata {
807169b5d95SPetr Machata 	if (span_entry->parms.dest_port)
808169b5d95SPetr Machata 		span_entry->ops->deconfigure(span_entry);
809169b5d95SPetr Machata }
810169b5d95SPetr Machata 
mlxsw_sp_span_policer_id_base_set(struct mlxsw_sp_span * span,u16 policer_id)8114039504eSIdo Schimmel static int mlxsw_sp_span_policer_id_base_set(struct mlxsw_sp_span *span,
8124039504eSIdo Schimmel 					     u16 policer_id)
8134039504eSIdo Schimmel {
8144039504eSIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = span->mlxsw_sp;
8154039504eSIdo Schimmel 	u16 policer_id_base;
8164039504eSIdo Schimmel 	int err;
8174039504eSIdo Schimmel 
8184039504eSIdo Schimmel 	/* Policers set on SPAN agents must be in the range of
8194039504eSIdo Schimmel 	 * `policer_id_base .. policer_id_base + max_span_agents - 1`. If the
8204039504eSIdo Schimmel 	 * base is set and the new policer is not within the range, then we
8214039504eSIdo Schimmel 	 * must error out.
8224039504eSIdo Schimmel 	 */
8234039504eSIdo Schimmel 	if (refcount_read(&span->policer_id_base_ref_count)) {
8244039504eSIdo Schimmel 		if (policer_id < span->policer_id_base ||
8254039504eSIdo Schimmel 		    policer_id >= span->policer_id_base + span->entries_count)
8264039504eSIdo Schimmel 			return -EINVAL;
8274039504eSIdo Schimmel 
8284039504eSIdo Schimmel 		refcount_inc(&span->policer_id_base_ref_count);
8294039504eSIdo Schimmel 		return 0;
8304039504eSIdo Schimmel 	}
8314039504eSIdo Schimmel 
8324039504eSIdo Schimmel 	/* Base must be even. */
8334039504eSIdo Schimmel 	policer_id_base = policer_id % 2 == 0 ? policer_id : policer_id - 1;
8344039504eSIdo Schimmel 	err = mlxsw_sp->span_ops->policer_id_base_set(mlxsw_sp,
8354039504eSIdo Schimmel 						      policer_id_base);
8364039504eSIdo Schimmel 	if (err)
8374039504eSIdo Schimmel 		return err;
8384039504eSIdo Schimmel 
8394039504eSIdo Schimmel 	span->policer_id_base = policer_id_base;
8404039504eSIdo Schimmel 	refcount_set(&span->policer_id_base_ref_count, 1);
8414039504eSIdo Schimmel 
8424039504eSIdo Schimmel 	return 0;
8434039504eSIdo Schimmel }
8444039504eSIdo Schimmel 
mlxsw_sp_span_policer_id_base_unset(struct mlxsw_sp_span * span)8454039504eSIdo Schimmel static void mlxsw_sp_span_policer_id_base_unset(struct mlxsw_sp_span *span)
8464039504eSIdo Schimmel {
847928345c0SPetr Machata 	if (refcount_dec_and_test(&span->policer_id_base_ref_count))
848928345c0SPetr Machata 		span->policer_id_base = 0;
8494039504eSIdo Schimmel }
8504039504eSIdo Schimmel 
851a629ef21SPetr Machata 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)852079c9f39SPetr Machata mlxsw_sp_span_entry_create(struct mlxsw_sp *mlxsw_sp,
853169b5d95SPetr Machata 			   const struct net_device *to_dev,
854169b5d95SPetr Machata 			   const struct mlxsw_sp_span_entry_ops *ops,
855169b5d95SPetr Machata 			   struct mlxsw_sp_span_parms sparms)
856a629ef21SPetr Machata {
8573546b03fSPetr Machata 	struct mlxsw_sp_span_entry *span_entry = NULL;
858a629ef21SPetr Machata 	int i;
859a629ef21SPetr Machata 
860a629ef21SPetr Machata 	/* find a free entry to use */
8619a9f8d1eSIdo Schimmel 	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
8624c00dafcSIdo Schimmel 		if (!refcount_read(&mlxsw_sp->span->entries[i].ref_count)) {
8639a9f8d1eSIdo Schimmel 			span_entry = &mlxsw_sp->span->entries[i];
864a629ef21SPetr Machata 			break;
865a629ef21SPetr Machata 		}
866a629ef21SPetr Machata 	}
8673546b03fSPetr Machata 	if (!span_entry)
868a629ef21SPetr Machata 		return NULL;
869a629ef21SPetr Machata 
8704039504eSIdo Schimmel 	if (sparms.policer_enable) {
8714039504eSIdo Schimmel 		int err;
8724039504eSIdo Schimmel 
8734039504eSIdo Schimmel 		err = mlxsw_sp_span_policer_id_base_set(mlxsw_sp->span,
8744039504eSIdo Schimmel 							sparms.policer_id);
8754039504eSIdo Schimmel 		if (err)
8764039504eSIdo Schimmel 			return NULL;
8774039504eSIdo Schimmel 	}
8784039504eSIdo Schimmel 
879eb833eecSIdo Schimmel 	atomic_inc(&mlxsw_sp->span->active_entries_count);
880169b5d95SPetr Machata 	span_entry->ops = ops;
8814c00dafcSIdo Schimmel 	refcount_set(&span_entry->ref_count, 1);
882079c9f39SPetr Machata 	span_entry->to_dev = to_dev;
883169b5d95SPetr Machata 	mlxsw_sp_span_entry_configure(mlxsw_sp, span_entry, sparms);
884169b5d95SPetr Machata 
885a629ef21SPetr Machata 	return span_entry;
886a629ef21SPetr Machata }
887a629ef21SPetr Machata 
mlxsw_sp_span_entry_destroy(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_span_entry * span_entry)888eb833eecSIdo Schimmel static void mlxsw_sp_span_entry_destroy(struct mlxsw_sp *mlxsw_sp,
889eb833eecSIdo Schimmel 					struct mlxsw_sp_span_entry *span_entry)
890a629ef21SPetr Machata {
891169b5d95SPetr Machata 	mlxsw_sp_span_entry_deconfigure(span_entry);
892eb833eecSIdo Schimmel 	atomic_dec(&mlxsw_sp->span->active_entries_count);
8934039504eSIdo Schimmel 	if (span_entry->parms.policer_enable)
8944039504eSIdo Schimmel 		mlxsw_sp_span_policer_id_base_unset(mlxsw_sp->span);
895a629ef21SPetr Machata }
896a629ef21SPetr Machata 
897a629ef21SPetr Machata struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev)898079c9f39SPetr Machata mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp,
899079c9f39SPetr Machata 				 const struct net_device *to_dev)
900a629ef21SPetr Machata {
901a629ef21SPetr Machata 	int i;
902a629ef21SPetr Machata 
9039a9f8d1eSIdo Schimmel 	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
9049a9f8d1eSIdo Schimmel 		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
905a629ef21SPetr Machata 
9064c00dafcSIdo Schimmel 		if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev)
907a629ef21SPetr Machata 			return curr;
908a629ef21SPetr Machata 	}
909a629ef21SPetr Machata 	return NULL;
910a629ef21SPetr Machata }
911a629ef21SPetr Machata 
mlxsw_sp_span_entry_invalidate(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_span_entry * span_entry)912079c9f39SPetr Machata void mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp,
913079c9f39SPetr Machata 				    struct mlxsw_sp_span_entry *span_entry)
914079c9f39SPetr Machata {
915169b5d95SPetr Machata 	mlxsw_sp_span_entry_deconfigure(span_entry);
916169b5d95SPetr Machata 	span_entry->ops = &mlxsw_sp_span_entry_ops_nop;
917079c9f39SPetr Machata }
918079c9f39SPetr Machata 
919a629ef21SPetr Machata static struct mlxsw_sp_span_entry *
mlxsw_sp_span_entry_find_by_id(struct mlxsw_sp * mlxsw_sp,int span_id)92098977089SPetr Machata mlxsw_sp_span_entry_find_by_id(struct mlxsw_sp *mlxsw_sp, int span_id)
92198977089SPetr Machata {
92298977089SPetr Machata 	int i;
92398977089SPetr Machata 
9249a9f8d1eSIdo Schimmel 	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
9259a9f8d1eSIdo Schimmel 		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
92698977089SPetr Machata 
9274c00dafcSIdo Schimmel 		if (refcount_read(&curr->ref_count) && curr->id == span_id)
92898977089SPetr Machata 			return curr;
92998977089SPetr Machata 	}
93098977089SPetr Machata 	return NULL;
93198977089SPetr Machata }
93298977089SPetr Machata 
93398977089SPetr Machata 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)9344039504eSIdo Schimmel mlxsw_sp_span_entry_find_by_parms(struct mlxsw_sp *mlxsw_sp,
9354039504eSIdo Schimmel 				  const struct net_device *to_dev,
9364039504eSIdo Schimmel 				  const struct mlxsw_sp_span_parms *sparms)
9374039504eSIdo Schimmel {
9384039504eSIdo Schimmel 	int i;
9394039504eSIdo Schimmel 
9404039504eSIdo Schimmel 	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
9414039504eSIdo Schimmel 		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
9424039504eSIdo Schimmel 
9434039504eSIdo Schimmel 		if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev &&
9444039504eSIdo Schimmel 		    curr->parms.policer_enable == sparms->policer_enable &&
9455c7659ebSIdo Schimmel 		    curr->parms.policer_id == sparms->policer_id &&
9465c7659ebSIdo Schimmel 		    curr->parms.session_id == sparms->session_id)
9474039504eSIdo Schimmel 			return curr;
9484039504eSIdo Schimmel 	}
9494039504eSIdo Schimmel 	return NULL;
9504039504eSIdo Schimmel }
9514039504eSIdo Schimmel 
9524039504eSIdo Schimmel 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)953079c9f39SPetr Machata mlxsw_sp_span_entry_get(struct mlxsw_sp *mlxsw_sp,
954169b5d95SPetr Machata 			const struct net_device *to_dev,
955169b5d95SPetr Machata 			const struct mlxsw_sp_span_entry_ops *ops,
956169b5d95SPetr Machata 			struct mlxsw_sp_span_parms sparms)
957a629ef21SPetr Machata {
958a629ef21SPetr Machata 	struct mlxsw_sp_span_entry *span_entry;
959a629ef21SPetr Machata 
9604039504eSIdo Schimmel 	span_entry = mlxsw_sp_span_entry_find_by_parms(mlxsw_sp, to_dev,
9614039504eSIdo Schimmel 						       &sparms);
962a629ef21SPetr Machata 	if (span_entry) {
963a629ef21SPetr Machata 		/* Already exists, just take a reference */
9644c00dafcSIdo Schimmel 		refcount_inc(&span_entry->ref_count);
965a629ef21SPetr Machata 		return span_entry;
966a629ef21SPetr Machata 	}
967a629ef21SPetr Machata 
968169b5d95SPetr Machata 	return mlxsw_sp_span_entry_create(mlxsw_sp, to_dev, ops, sparms);
969a629ef21SPetr Machata }
970a629ef21SPetr Machata 
mlxsw_sp_span_entry_put(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_span_entry * span_entry)971a629ef21SPetr Machata static int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp,
972a629ef21SPetr Machata 				   struct mlxsw_sp_span_entry *span_entry)
973a629ef21SPetr Machata {
9744c00dafcSIdo Schimmel 	if (refcount_dec_and_test(&span_entry->ref_count))
975eb833eecSIdo Schimmel 		mlxsw_sp_span_entry_destroy(mlxsw_sp, span_entry);
976a629ef21SPetr Machata 	return 0;
977a629ef21SPetr Machata }
978a629ef21SPetr Machata 
mlxsw_sp_span_port_buffer_update(struct mlxsw_sp_port * mlxsw_sp_port,bool enable)97922881adfSPetr Machata static int mlxsw_sp_span_port_buffer_update(struct mlxsw_sp_port *mlxsw_sp_port, bool enable)
9804bafb85aSIdo Schimmel {
98122881adfSPetr Machata 	struct mlxsw_sp_hdroom hdroom;
9824bafb85aSIdo Schimmel 
98322881adfSPetr Machata 	hdroom = *mlxsw_sp_port->hdroom;
98422881adfSPetr Machata 	hdroom.int_buf.enable = enable;
98522881adfSPetr Machata 	mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
98622881adfSPetr Machata 
98722881adfSPetr Machata 	return mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
9884bafb85aSIdo Schimmel }
9894bafb85aSIdo Schimmel 
99031c25b94SJiri Pirko static int
mlxsw_sp_span_port_buffer_enable(struct mlxsw_sp_port * mlxsw_sp_port)991532b49e4SPetr Machata mlxsw_sp_span_port_buffer_enable(struct mlxsw_sp_port *mlxsw_sp_port)
99231c25b94SJiri Pirko {
99322881adfSPetr Machata 	return mlxsw_sp_span_port_buffer_update(mlxsw_sp_port, true);
99431c25b94SJiri Pirko }
99531c25b94SJiri Pirko 
mlxsw_sp_span_port_buffer_disable(struct mlxsw_sp_port * mlxsw_sp_port)99622881adfSPetr Machata static void mlxsw_sp_span_port_buffer_disable(struct mlxsw_sp_port *mlxsw_sp_port)
99714366da6SIdo Schimmel {
99822881adfSPetr Machata 	mlxsw_sp_span_port_buffer_update(mlxsw_sp_port, false);
99914366da6SIdo Schimmel }
100014366da6SIdo Schimmel 
1001835d6b8cSIdo Schimmel static struct mlxsw_sp_span_analyzed_port *
mlxsw_sp_span_analyzed_port_find(struct mlxsw_sp_span * span,u16 local_port,bool ingress)1002c934757dSAmit Cohen mlxsw_sp_span_analyzed_port_find(struct mlxsw_sp_span *span, u16 local_port,
1003835d6b8cSIdo Schimmel 				 bool ingress)
1004835d6b8cSIdo Schimmel {
1005835d6b8cSIdo Schimmel 	struct mlxsw_sp_span_analyzed_port *analyzed_port;
1006835d6b8cSIdo Schimmel 
1007835d6b8cSIdo Schimmel 	list_for_each_entry(analyzed_port, &span->analyzed_ports_list, list) {
1008835d6b8cSIdo Schimmel 		if (analyzed_port->local_port == local_port &&
1009835d6b8cSIdo Schimmel 		    analyzed_port->ingress == ingress)
1010835d6b8cSIdo Schimmel 			return analyzed_port;
1011835d6b8cSIdo Schimmel 	}
1012835d6b8cSIdo Schimmel 
1013835d6b8cSIdo Schimmel 	return NULL;
1014835d6b8cSIdo Schimmel }
1015835d6b8cSIdo Schimmel 
1016169b5d95SPetr Machata static const struct mlxsw_sp_span_entry_ops *
mlxsw_sp_span_entry_ops(struct mlxsw_sp * mlxsw_sp,const struct net_device * to_dev)1017169b5d95SPetr Machata mlxsw_sp_span_entry_ops(struct mlxsw_sp *mlxsw_sp,
1018169b5d95SPetr Machata 			const struct net_device *to_dev)
1019169b5d95SPetr Machata {
102034e4ace5SIdo Schimmel 	struct mlxsw_sp_span *span = mlxsw_sp->span;
1021169b5d95SPetr Machata 	size_t i;
1022169b5d95SPetr Machata 
102334e4ace5SIdo Schimmel 	for (i = 0; i < span->span_entry_ops_arr_size; ++i)
102434e4ace5SIdo Schimmel 		if (span->span_entry_ops_arr[i]->can_handle(to_dev))
102534e4ace5SIdo Schimmel 			return span->span_entry_ops_arr[i];
1026169b5d95SPetr Machata 
1027169b5d95SPetr Machata 	return NULL;
1028169b5d95SPetr Machata }
1029169b5d95SPetr Machata 
mlxsw_sp_span_respin_work(struct work_struct * work)1030a8e7e6e7SIdo Schimmel static void mlxsw_sp_span_respin_work(struct work_struct *work)
1031a8e7e6e7SIdo Schimmel {
1032a8e7e6e7SIdo Schimmel 	struct mlxsw_sp_span *span;
1033a8e7e6e7SIdo Schimmel 	struct mlxsw_sp *mlxsw_sp;
1034a8e7e6e7SIdo Schimmel 	int i, err;
1035a8e7e6e7SIdo Schimmel 
1036a8e7e6e7SIdo Schimmel 	span = container_of(work, struct mlxsw_sp_span, work);
1037a8e7e6e7SIdo Schimmel 	mlxsw_sp = span->mlxsw_sp;
1038a8e7e6e7SIdo Schimmel 
1039a8e7e6e7SIdo Schimmel 	rtnl_lock();
1040a8e7e6e7SIdo Schimmel 	for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
1041a8e7e6e7SIdo Schimmel 		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
1042a8e7e6e7SIdo Schimmel 		struct mlxsw_sp_span_parms sparms = {NULL};
1043a8e7e6e7SIdo Schimmel 
10444c00dafcSIdo Schimmel 		if (!refcount_read(&curr->ref_count))
1045a8e7e6e7SIdo Schimmel 			continue;
1046a8e7e6e7SIdo Schimmel 
1047b6f6881aSIdo Schimmel 		if (curr->ops->is_static)
1048b6f6881aSIdo Schimmel 			continue;
1049b6f6881aSIdo Schimmel 
1050f4a626e2SIdo Schimmel 		err = curr->ops->parms_set(mlxsw_sp, curr->to_dev, &sparms);
1051a8e7e6e7SIdo Schimmel 		if (err)
1052a8e7e6e7SIdo Schimmel 			continue;
1053a8e7e6e7SIdo Schimmel 
1054a8e7e6e7SIdo Schimmel 		if (memcmp(&sparms, &curr->parms, sizeof(sparms))) {
1055a8e7e6e7SIdo Schimmel 			mlxsw_sp_span_entry_deconfigure(curr);
1056a8e7e6e7SIdo Schimmel 			mlxsw_sp_span_entry_configure(mlxsw_sp, curr, sparms);
1057a8e7e6e7SIdo Schimmel 		}
1058a8e7e6e7SIdo Schimmel 	}
1059a8e7e6e7SIdo Schimmel 	rtnl_unlock();
1060a8e7e6e7SIdo Schimmel }
1061622110f2SIdo Schimmel 
mlxsw_sp_span_respin(struct mlxsw_sp * mlxsw_sp)1062622110f2SIdo Schimmel void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp)
1063622110f2SIdo Schimmel {
1064eb833eecSIdo Schimmel 	if (atomic_read(&mlxsw_sp->span->active_entries_count) == 0)
1065eb833eecSIdo Schimmel 		return;
1066622110f2SIdo Schimmel 	mlxsw_core_schedule_work(&mlxsw_sp->span->work);
1067622110f2SIdo Schimmel }
106846601034SIdo Schimmel 
mlxsw_sp_span_agent_get(struct mlxsw_sp * mlxsw_sp,int * p_span_id,const struct mlxsw_sp_span_agent_parms * parms)1069a120ecc3SIdo Schimmel int mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp, int *p_span_id,
1070a120ecc3SIdo Schimmel 			    const struct mlxsw_sp_span_agent_parms *parms)
107146601034SIdo Schimmel {
1072a120ecc3SIdo Schimmel 	const struct net_device *to_dev = parms->to_dev;
107346601034SIdo Schimmel 	const struct mlxsw_sp_span_entry_ops *ops;
107446601034SIdo Schimmel 	struct mlxsw_sp_span_entry *span_entry;
107546601034SIdo Schimmel 	struct mlxsw_sp_span_parms sparms;
107646601034SIdo Schimmel 	int err;
107746601034SIdo Schimmel 
107846601034SIdo Schimmel 	ASSERT_RTNL();
107946601034SIdo Schimmel 
108046601034SIdo Schimmel 	ops = mlxsw_sp_span_entry_ops(mlxsw_sp, to_dev);
108146601034SIdo Schimmel 	if (!ops) {
108246601034SIdo Schimmel 		dev_err(mlxsw_sp->bus_info->dev, "Cannot mirror to requested destination\n");
108346601034SIdo Schimmel 		return -EOPNOTSUPP;
108446601034SIdo Schimmel 	}
108546601034SIdo Schimmel 
108646601034SIdo Schimmel 	memset(&sparms, 0, sizeof(sparms));
1087f4a626e2SIdo Schimmel 	err = ops->parms_set(mlxsw_sp, to_dev, &sparms);
108846601034SIdo Schimmel 	if (err)
108946601034SIdo Schimmel 		return err;
109046601034SIdo Schimmel 
10914039504eSIdo Schimmel 	sparms.policer_id = parms->policer_id;
10924039504eSIdo Schimmel 	sparms.policer_enable = parms->policer_enable;
10935c7659ebSIdo Schimmel 	sparms.session_id = parms->session_id;
109446601034SIdo Schimmel 	span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms);
109546601034SIdo Schimmel 	if (!span_entry)
109646601034SIdo Schimmel 		return -ENOBUFS;
109746601034SIdo Schimmel 
109846601034SIdo Schimmel 	*p_span_id = span_entry->id;
109946601034SIdo Schimmel 
110046601034SIdo Schimmel 	return 0;
110146601034SIdo Schimmel }
110246601034SIdo Schimmel 
mlxsw_sp_span_agent_put(struct mlxsw_sp * mlxsw_sp,int span_id)110346601034SIdo Schimmel void mlxsw_sp_span_agent_put(struct mlxsw_sp *mlxsw_sp, int span_id)
110446601034SIdo Schimmel {
110546601034SIdo Schimmel 	struct mlxsw_sp_span_entry *span_entry;
110646601034SIdo Schimmel 
110746601034SIdo Schimmel 	ASSERT_RTNL();
110846601034SIdo Schimmel 
110946601034SIdo Schimmel 	span_entry = mlxsw_sp_span_entry_find_by_id(mlxsw_sp, span_id);
111046601034SIdo Schimmel 	if (WARN_ON_ONCE(!span_entry))
111146601034SIdo Schimmel 		return;
111246601034SIdo Schimmel 
111346601034SIdo Schimmel 	mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
111446601034SIdo Schimmel }
1115ed04458dSIdo Schimmel 
1116ed04458dSIdo Schimmel 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)1117ed04458dSIdo Schimmel mlxsw_sp_span_analyzed_port_create(struct mlxsw_sp_span *span,
1118ed04458dSIdo Schimmel 				   struct mlxsw_sp_port *mlxsw_sp_port,
1119ed04458dSIdo Schimmel 				   bool ingress)
1120ed04458dSIdo Schimmel {
1121ed04458dSIdo Schimmel 	struct mlxsw_sp_span_analyzed_port *analyzed_port;
1122ed04458dSIdo Schimmel 	int err;
1123ed04458dSIdo Schimmel 
1124ed04458dSIdo Schimmel 	analyzed_port = kzalloc(sizeof(*analyzed_port), GFP_KERNEL);
1125ed04458dSIdo Schimmel 	if (!analyzed_port)
1126ed04458dSIdo Schimmel 		return ERR_PTR(-ENOMEM);
1127ed04458dSIdo Schimmel 
1128ed04458dSIdo Schimmel 	refcount_set(&analyzed_port->ref_count, 1);
1129ed04458dSIdo Schimmel 	analyzed_port->local_port = mlxsw_sp_port->local_port;
1130ed04458dSIdo Schimmel 	analyzed_port->ingress = ingress;
1131ed04458dSIdo Schimmel 	list_add_tail(&analyzed_port->list, &span->analyzed_ports_list);
1132ed04458dSIdo Schimmel 
1133ed04458dSIdo Schimmel 	/* An egress mirror buffer should be allocated on the egress port which
1134ed04458dSIdo Schimmel 	 * does the mirroring.
1135ed04458dSIdo Schimmel 	 */
1136ed04458dSIdo Schimmel 	if (!ingress) {
1137532b49e4SPetr Machata 		err = mlxsw_sp_span_port_buffer_enable(mlxsw_sp_port);
1138ed04458dSIdo Schimmel 		if (err)
1139eb773c3aSIdo Schimmel 			goto err_buffer_update;
1140ed04458dSIdo Schimmel 	}
1141ed04458dSIdo Schimmel 
1142ed04458dSIdo Schimmel 	return analyzed_port;
1143ed04458dSIdo Schimmel 
1144eb773c3aSIdo Schimmel err_buffer_update:
1145ed04458dSIdo Schimmel 	list_del(&analyzed_port->list);
1146ed04458dSIdo Schimmel 	kfree(analyzed_port);
1147ed04458dSIdo Schimmel 	return ERR_PTR(err);
1148ed04458dSIdo Schimmel }
1149ed04458dSIdo Schimmel 
1150ed04458dSIdo Schimmel static void
mlxsw_sp_span_analyzed_port_destroy(struct mlxsw_sp_port * mlxsw_sp_port,struct mlxsw_sp_span_analyzed_port * analyzed_port)115122881adfSPetr Machata mlxsw_sp_span_analyzed_port_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
1152ed04458dSIdo Schimmel 				    struct mlxsw_sp_span_analyzed_port *
1153ed04458dSIdo Schimmel 				    analyzed_port)
1154ed04458dSIdo Schimmel {
1155ed04458dSIdo Schimmel 	/* Remove egress mirror buffer now that port is no longer analyzed
1156ed04458dSIdo Schimmel 	 * at egress.
1157ed04458dSIdo Schimmel 	 */
115814366da6SIdo Schimmel 	if (!analyzed_port->ingress)
115922881adfSPetr Machata 		mlxsw_sp_span_port_buffer_disable(mlxsw_sp_port);
1160ed04458dSIdo Schimmel 
1161ed04458dSIdo Schimmel 	list_del(&analyzed_port->list);
1162ed04458dSIdo Schimmel 	kfree(analyzed_port);
1163ed04458dSIdo Schimmel }
1164ed04458dSIdo Schimmel 
mlxsw_sp_span_analyzed_port_get(struct mlxsw_sp_port * mlxsw_sp_port,bool ingress)1165ed04458dSIdo Schimmel int mlxsw_sp_span_analyzed_port_get(struct mlxsw_sp_port *mlxsw_sp_port,
1166ed04458dSIdo Schimmel 				    bool ingress)
1167ed04458dSIdo Schimmel {
1168ed04458dSIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1169ed04458dSIdo Schimmel 	struct mlxsw_sp_span_analyzed_port *analyzed_port;
1170c934757dSAmit Cohen 	u16 local_port = mlxsw_sp_port->local_port;
1171ed04458dSIdo Schimmel 	int err = 0;
1172ed04458dSIdo Schimmel 
1173ed04458dSIdo Schimmel 	mutex_lock(&mlxsw_sp->span->analyzed_ports_lock);
1174ed04458dSIdo Schimmel 
1175ed04458dSIdo Schimmel 	analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span,
1176ed04458dSIdo Schimmel 							 local_port, ingress);
1177ed04458dSIdo Schimmel 	if (analyzed_port) {
1178ed04458dSIdo Schimmel 		refcount_inc(&analyzed_port->ref_count);
1179ed04458dSIdo Schimmel 		goto out_unlock;
1180ed04458dSIdo Schimmel 	}
1181ed04458dSIdo Schimmel 
1182ed04458dSIdo Schimmel 	analyzed_port = mlxsw_sp_span_analyzed_port_create(mlxsw_sp->span,
1183ed04458dSIdo Schimmel 							   mlxsw_sp_port,
1184ed04458dSIdo Schimmel 							   ingress);
1185ed04458dSIdo Schimmel 	if (IS_ERR(analyzed_port))
1186ed04458dSIdo Schimmel 		err = PTR_ERR(analyzed_port);
1187ed04458dSIdo Schimmel 
1188ed04458dSIdo Schimmel out_unlock:
1189ed04458dSIdo Schimmel 	mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock);
1190ed04458dSIdo Schimmel 	return err;
1191ed04458dSIdo Schimmel }
1192ed04458dSIdo Schimmel 
mlxsw_sp_span_analyzed_port_put(struct mlxsw_sp_port * mlxsw_sp_port,bool ingress)1193ed04458dSIdo Schimmel void mlxsw_sp_span_analyzed_port_put(struct mlxsw_sp_port *mlxsw_sp_port,
1194ed04458dSIdo Schimmel 				     bool ingress)
1195ed04458dSIdo Schimmel {
1196ed04458dSIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1197ed04458dSIdo Schimmel 	struct mlxsw_sp_span_analyzed_port *analyzed_port;
1198c934757dSAmit Cohen 	u16 local_port = mlxsw_sp_port->local_port;
1199ed04458dSIdo Schimmel 
1200ed04458dSIdo Schimmel 	mutex_lock(&mlxsw_sp->span->analyzed_ports_lock);
1201ed04458dSIdo Schimmel 
1202ed04458dSIdo Schimmel 	analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span,
1203ed04458dSIdo Schimmel 							 local_port, ingress);
1204ed04458dSIdo Schimmel 	if (WARN_ON_ONCE(!analyzed_port))
1205ed04458dSIdo Schimmel 		goto out_unlock;
1206ed04458dSIdo Schimmel 
1207ed04458dSIdo Schimmel 	if (!refcount_dec_and_test(&analyzed_port->ref_count))
1208ed04458dSIdo Schimmel 		goto out_unlock;
1209ed04458dSIdo Schimmel 
121022881adfSPetr Machata 	mlxsw_sp_span_analyzed_port_destroy(mlxsw_sp_port, analyzed_port);
1211ed04458dSIdo Schimmel 
1212ed04458dSIdo Schimmel out_unlock:
1213ed04458dSIdo Schimmel 	mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock);
1214ed04458dSIdo Schimmel }
1215c056618cSIdo Schimmel 
1216c056618cSIdo Schimmel static int
__mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span * span,struct mlxsw_sp_span_trigger_entry * trigger_entry,bool enable)121708a3641fSIdo Schimmel __mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span *span,
1218c056618cSIdo Schimmel 				  struct mlxsw_sp_span_trigger_entry *
1219c056618cSIdo Schimmel 				  trigger_entry, bool enable)
1220c056618cSIdo Schimmel {
1221c056618cSIdo Schimmel 	char mpar_pl[MLXSW_REG_MPAR_LEN];
1222c056618cSIdo Schimmel 	enum mlxsw_reg_mpar_i_e i_e;
1223c056618cSIdo Schimmel 
1224c056618cSIdo Schimmel 	switch (trigger_entry->trigger) {
1225c056618cSIdo Schimmel 	case MLXSW_SP_SPAN_TRIGGER_INGRESS:
1226c056618cSIdo Schimmel 		i_e = MLXSW_REG_MPAR_TYPE_INGRESS;
1227c056618cSIdo Schimmel 		break;
1228c056618cSIdo Schimmel 	case MLXSW_SP_SPAN_TRIGGER_EGRESS:
1229c056618cSIdo Schimmel 		i_e = MLXSW_REG_MPAR_TYPE_EGRESS;
1230c056618cSIdo Schimmel 		break;
1231c056618cSIdo Schimmel 	default:
1232c056618cSIdo Schimmel 		WARN_ON_ONCE(1);
1233c056618cSIdo Schimmel 		return -EINVAL;
1234c056618cSIdo Schimmel 	}
1235c056618cSIdo Schimmel 
12362dcbd920SIdo Schimmel 	if (trigger_entry->parms.probability_rate > MLXSW_REG_MPAR_RATE_MAX)
12372dcbd920SIdo Schimmel 		return -EINVAL;
12382dcbd920SIdo Schimmel 
1239c056618cSIdo Schimmel 	mlxsw_reg_mpar_pack(mpar_pl, trigger_entry->local_port, i_e, enable,
12402dcbd920SIdo Schimmel 			    trigger_entry->parms.span_id,
12412dcbd920SIdo Schimmel 			    trigger_entry->parms.probability_rate);
1242c056618cSIdo Schimmel 	return mlxsw_reg_write(span->mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl);
1243c056618cSIdo Schimmel }
1244c056618cSIdo Schimmel 
1245c056618cSIdo Schimmel static int
mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span_trigger_entry * trigger_entry)124608a3641fSIdo Schimmel mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span_trigger_entry *
1247c056618cSIdo Schimmel 				trigger_entry)
1248c056618cSIdo Schimmel {
124908a3641fSIdo Schimmel 	return __mlxsw_sp_span_trigger_port_bind(trigger_entry->span,
125008a3641fSIdo Schimmel 						 trigger_entry, true);
1251c056618cSIdo Schimmel }
1252c056618cSIdo Schimmel 
1253c056618cSIdo Schimmel static void
mlxsw_sp_span_trigger_port_unbind(struct mlxsw_sp_span_trigger_entry * trigger_entry)125408a3641fSIdo Schimmel mlxsw_sp_span_trigger_port_unbind(struct mlxsw_sp_span_trigger_entry *
1255c056618cSIdo Schimmel 				  trigger_entry)
1256c056618cSIdo Schimmel {
125708a3641fSIdo Schimmel 	__mlxsw_sp_span_trigger_port_bind(trigger_entry->span, trigger_entry,
125808a3641fSIdo Schimmel 					  false);
125908a3641fSIdo Schimmel }
126008a3641fSIdo Schimmel 
126108a3641fSIdo Schimmel 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)126208a3641fSIdo Schimmel mlxsw_sp_span_trigger_port_matches(struct mlxsw_sp_span_trigger_entry *
126308a3641fSIdo Schimmel 				   trigger_entry,
126408a3641fSIdo Schimmel 				   enum mlxsw_sp_span_trigger trigger,
126508a3641fSIdo Schimmel 				   struct mlxsw_sp_port *mlxsw_sp_port)
126608a3641fSIdo Schimmel {
126708a3641fSIdo Schimmel 	return trigger_entry->trigger == trigger &&
126808a3641fSIdo Schimmel 	       trigger_entry->local_port == mlxsw_sp_port->local_port;
126908a3641fSIdo Schimmel }
127008a3641fSIdo Schimmel 
12712bafb216SIdo Schimmel 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)12722bafb216SIdo Schimmel mlxsw_sp_span_trigger_port_enable(struct mlxsw_sp_span_trigger_entry *
12732bafb216SIdo Schimmel 				  trigger_entry,
12742bafb216SIdo Schimmel 				  struct mlxsw_sp_port *mlxsw_sp_port, u8 tc)
12752bafb216SIdo Schimmel {
12762bafb216SIdo Schimmel 	/* Port trigger are enabled during binding. */
12772bafb216SIdo Schimmel 	return 0;
12782bafb216SIdo Schimmel }
12792bafb216SIdo Schimmel 
12802bafb216SIdo Schimmel 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)12812bafb216SIdo Schimmel mlxsw_sp_span_trigger_port_disable(struct mlxsw_sp_span_trigger_entry *
12822bafb216SIdo Schimmel 				   trigger_entry,
12832bafb216SIdo Schimmel 				   struct mlxsw_sp_port *mlxsw_sp_port, u8 tc)
12842bafb216SIdo Schimmel {
12852bafb216SIdo Schimmel }
12862bafb216SIdo Schimmel 
128708a3641fSIdo Schimmel static const struct mlxsw_sp_span_trigger_ops
128808a3641fSIdo Schimmel mlxsw_sp_span_trigger_port_ops = {
128908a3641fSIdo Schimmel 	.bind = mlxsw_sp_span_trigger_port_bind,
129008a3641fSIdo Schimmel 	.unbind = mlxsw_sp_span_trigger_port_unbind,
129108a3641fSIdo Schimmel 	.matches = mlxsw_sp_span_trigger_port_matches,
12922bafb216SIdo Schimmel 	.enable = mlxsw_sp_span_trigger_port_enable,
12932bafb216SIdo Schimmel 	.disable = mlxsw_sp_span_trigger_port_disable,
129408a3641fSIdo Schimmel };
129508a3641fSIdo Schimmel 
1296ab8c06b7SIdo Schimmel static int
mlxsw_sp1_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry * trigger_entry)1297ab8c06b7SIdo Schimmel mlxsw_sp1_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry *
1298ab8c06b7SIdo Schimmel 				   trigger_entry)
1299ab8c06b7SIdo Schimmel {
1300ab8c06b7SIdo Schimmel 	return -EOPNOTSUPP;
1301ab8c06b7SIdo Schimmel }
1302ab8c06b7SIdo Schimmel 
1303ab8c06b7SIdo Schimmel static void
mlxsw_sp1_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry * trigger_entry)1304ab8c06b7SIdo Schimmel mlxsw_sp1_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry *
1305ab8c06b7SIdo Schimmel 				     trigger_entry)
1306ab8c06b7SIdo Schimmel {
1307ab8c06b7SIdo Schimmel }
1308ab8c06b7SIdo Schimmel 
1309ab8c06b7SIdo Schimmel 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)1310ab8c06b7SIdo Schimmel mlxsw_sp1_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry *
1311ab8c06b7SIdo Schimmel 				      trigger_entry,
1312ab8c06b7SIdo Schimmel 				      enum mlxsw_sp_span_trigger trigger,
1313ab8c06b7SIdo Schimmel 				      struct mlxsw_sp_port *mlxsw_sp_port)
1314ab8c06b7SIdo Schimmel {
1315ab8c06b7SIdo Schimmel 	WARN_ON_ONCE(1);
1316ab8c06b7SIdo Schimmel 	return false;
1317ab8c06b7SIdo Schimmel }
1318ab8c06b7SIdo Schimmel 
13192bafb216SIdo Schimmel 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)13202bafb216SIdo Schimmel mlxsw_sp1_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
13212bafb216SIdo Schimmel 				     trigger_entry,
13222bafb216SIdo Schimmel 				     struct mlxsw_sp_port *mlxsw_sp_port,
13232bafb216SIdo Schimmel 				     u8 tc)
13242bafb216SIdo Schimmel {
13252bafb216SIdo Schimmel 	return -EOPNOTSUPP;
13262bafb216SIdo Schimmel }
13272bafb216SIdo Schimmel 
13282bafb216SIdo Schimmel 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)13292bafb216SIdo Schimmel mlxsw_sp1_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry *
13302bafb216SIdo Schimmel 				      trigger_entry,
13312bafb216SIdo Schimmel 				      struct mlxsw_sp_port *mlxsw_sp_port,
13322bafb216SIdo Schimmel 				      u8 tc)
13332bafb216SIdo Schimmel {
13342bafb216SIdo Schimmel }
13352bafb216SIdo Schimmel 
1336ab8c06b7SIdo Schimmel static const struct mlxsw_sp_span_trigger_ops
1337ab8c06b7SIdo Schimmel mlxsw_sp1_span_trigger_global_ops = {
1338ab8c06b7SIdo Schimmel 	.bind = mlxsw_sp1_span_trigger_global_bind,
1339ab8c06b7SIdo Schimmel 	.unbind = mlxsw_sp1_span_trigger_global_unbind,
1340ab8c06b7SIdo Schimmel 	.matches = mlxsw_sp1_span_trigger_global_matches,
13412bafb216SIdo Schimmel 	.enable = mlxsw_sp1_span_trigger_global_enable,
13422bafb216SIdo Schimmel 	.disable = mlxsw_sp1_span_trigger_global_disable,
1343ab8c06b7SIdo Schimmel };
1344ab8c06b7SIdo Schimmel 
134508a3641fSIdo Schimmel static const struct mlxsw_sp_span_trigger_ops *
1346ab8c06b7SIdo Schimmel mlxsw_sp1_span_trigger_ops_arr[] = {
134708a3641fSIdo Schimmel 	[MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops,
1348ab8c06b7SIdo Schimmel 	[MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] =
1349ab8c06b7SIdo Schimmel 		&mlxsw_sp1_span_trigger_global_ops,
1350ab8c06b7SIdo Schimmel };
1351ab8c06b7SIdo Schimmel 
1352ab8c06b7SIdo Schimmel static int
mlxsw_sp2_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry * trigger_entry)1353ab8c06b7SIdo Schimmel mlxsw_sp2_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry *
1354ab8c06b7SIdo Schimmel 				   trigger_entry)
1355ab8c06b7SIdo Schimmel {
1356ab8c06b7SIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp;
1357ab8c06b7SIdo Schimmel 	enum mlxsw_reg_mpagr_trigger trigger;
1358ab8c06b7SIdo Schimmel 	char mpagr_pl[MLXSW_REG_MPAGR_LEN];
1359ab8c06b7SIdo Schimmel 
1360ab8c06b7SIdo Schimmel 	switch (trigger_entry->trigger) {
1361ab8c06b7SIdo Schimmel 	case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
1362ab8c06b7SIdo Schimmel 		trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_SHARED_BUFFER;
1363ab8c06b7SIdo Schimmel 		break;
1364ab8c06b7SIdo Schimmel 	case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
1365ab8c06b7SIdo Schimmel 		trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_WRED;
1366ab8c06b7SIdo Schimmel 		break;
1367ab8c06b7SIdo Schimmel 	case MLXSW_SP_SPAN_TRIGGER_ECN:
1368ab8c06b7SIdo Schimmel 		trigger = MLXSW_REG_MPAGR_TRIGGER_EGRESS_ECN;
1369ab8c06b7SIdo Schimmel 		break;
1370ab8c06b7SIdo Schimmel 	default:
1371ab8c06b7SIdo Schimmel 		WARN_ON_ONCE(1);
1372ab8c06b7SIdo Schimmel 		return -EINVAL;
1373ab8c06b7SIdo Schimmel 	}
1374ab8c06b7SIdo Schimmel 
13752dcbd920SIdo Schimmel 	if (trigger_entry->parms.probability_rate > MLXSW_REG_MPAGR_RATE_MAX)
13762dcbd920SIdo Schimmel 		return -EINVAL;
13772dcbd920SIdo Schimmel 
1378ab8c06b7SIdo Schimmel 	mlxsw_reg_mpagr_pack(mpagr_pl, trigger, trigger_entry->parms.span_id,
13792dcbd920SIdo Schimmel 			     trigger_entry->parms.probability_rate);
1380ab8c06b7SIdo Schimmel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpagr), mpagr_pl);
1381ab8c06b7SIdo Schimmel }
1382ab8c06b7SIdo Schimmel 
1383ab8c06b7SIdo Schimmel static void
mlxsw_sp2_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry * trigger_entry)1384ab8c06b7SIdo Schimmel mlxsw_sp2_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry *
1385ab8c06b7SIdo Schimmel 				     trigger_entry)
1386ab8c06b7SIdo Schimmel {
1387ab8c06b7SIdo Schimmel 	/* There is no unbinding for global triggers. The trigger should be
1388ab8c06b7SIdo Schimmel 	 * disabled on all ports by now.
1389ab8c06b7SIdo Schimmel 	 */
1390ab8c06b7SIdo Schimmel }
1391ab8c06b7SIdo Schimmel 
1392ab8c06b7SIdo Schimmel 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)1393ab8c06b7SIdo Schimmel mlxsw_sp2_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry *
1394ab8c06b7SIdo Schimmel 				      trigger_entry,
1395ab8c06b7SIdo Schimmel 				      enum mlxsw_sp_span_trigger trigger,
1396ab8c06b7SIdo Schimmel 				      struct mlxsw_sp_port *mlxsw_sp_port)
1397ab8c06b7SIdo Schimmel {
1398ab8c06b7SIdo Schimmel 	return trigger_entry->trigger == trigger;
1399ab8c06b7SIdo Schimmel }
1400ab8c06b7SIdo Schimmel 
14012bafb216SIdo Schimmel 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)14022bafb216SIdo Schimmel __mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
14032bafb216SIdo Schimmel 				       trigger_entry,
14042bafb216SIdo Schimmel 				       struct mlxsw_sp_port *mlxsw_sp_port,
14052bafb216SIdo Schimmel 				       u8 tc, bool enable)
14062bafb216SIdo Schimmel {
14072bafb216SIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp;
14082bafb216SIdo Schimmel 	char momte_pl[MLXSW_REG_MOMTE_LEN];
14092bafb216SIdo Schimmel 	enum mlxsw_reg_momte_type type;
14102bafb216SIdo Schimmel 	int err;
14112bafb216SIdo Schimmel 
14122bafb216SIdo Schimmel 	switch (trigger_entry->trigger) {
14132bafb216SIdo Schimmel 	case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
14142bafb216SIdo Schimmel 		type = MLXSW_REG_MOMTE_TYPE_SHARED_BUFFER_TCLASS;
14152bafb216SIdo Schimmel 		break;
14162bafb216SIdo Schimmel 	case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
14172bafb216SIdo Schimmel 		type = MLXSW_REG_MOMTE_TYPE_WRED;
14182bafb216SIdo Schimmel 		break;
14192bafb216SIdo Schimmel 	case MLXSW_SP_SPAN_TRIGGER_ECN:
14202bafb216SIdo Schimmel 		type = MLXSW_REG_MOMTE_TYPE_ECN;
14212bafb216SIdo Schimmel 		break;
14222bafb216SIdo Schimmel 	default:
14232bafb216SIdo Schimmel 		WARN_ON_ONCE(1);
14242bafb216SIdo Schimmel 		return -EINVAL;
14252bafb216SIdo Schimmel 	}
14262bafb216SIdo Schimmel 
14272bafb216SIdo Schimmel 	/* Query existing configuration in order to only change the state of
14282bafb216SIdo Schimmel 	 * the specified traffic class.
14292bafb216SIdo Schimmel 	 */
14302bafb216SIdo Schimmel 	mlxsw_reg_momte_pack(momte_pl, mlxsw_sp_port->local_port, type);
14312bafb216SIdo Schimmel 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(momte), momte_pl);
14322bafb216SIdo Schimmel 	if (err)
14332bafb216SIdo Schimmel 		return err;
14342bafb216SIdo Schimmel 
14352bafb216SIdo Schimmel 	mlxsw_reg_momte_tclass_en_set(momte_pl, tc, enable);
14362bafb216SIdo Schimmel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(momte), momte_pl);
14372bafb216SIdo Schimmel }
14382bafb216SIdo Schimmel 
14392bafb216SIdo Schimmel 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)14402bafb216SIdo Schimmel mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry *
14412bafb216SIdo Schimmel 				     trigger_entry,
14422bafb216SIdo Schimmel 				     struct mlxsw_sp_port *mlxsw_sp_port,
14432bafb216SIdo Schimmel 				     u8 tc)
14442bafb216SIdo Schimmel {
14452bafb216SIdo Schimmel 	return __mlxsw_sp2_span_trigger_global_enable(trigger_entry,
14462bafb216SIdo Schimmel 						      mlxsw_sp_port, tc, true);
14472bafb216SIdo Schimmel }
14482bafb216SIdo Schimmel 
14492bafb216SIdo Schimmel 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)14502bafb216SIdo Schimmel mlxsw_sp2_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry *
14512bafb216SIdo Schimmel 				      trigger_entry,
14522bafb216SIdo Schimmel 				      struct mlxsw_sp_port *mlxsw_sp_port,
14532bafb216SIdo Schimmel 				      u8 tc)
14542bafb216SIdo Schimmel {
14552bafb216SIdo Schimmel 	__mlxsw_sp2_span_trigger_global_enable(trigger_entry, mlxsw_sp_port, tc,
14562bafb216SIdo Schimmel 					       false);
14572bafb216SIdo Schimmel }
14582bafb216SIdo Schimmel 
1459ab8c06b7SIdo Schimmel static const struct mlxsw_sp_span_trigger_ops
1460ab8c06b7SIdo Schimmel mlxsw_sp2_span_trigger_global_ops = {
1461ab8c06b7SIdo Schimmel 	.bind = mlxsw_sp2_span_trigger_global_bind,
1462ab8c06b7SIdo Schimmel 	.unbind = mlxsw_sp2_span_trigger_global_unbind,
1463ab8c06b7SIdo Schimmel 	.matches = mlxsw_sp2_span_trigger_global_matches,
14642bafb216SIdo Schimmel 	.enable = mlxsw_sp2_span_trigger_global_enable,
14652bafb216SIdo Schimmel 	.disable = mlxsw_sp2_span_trigger_global_disable,
1466ab8c06b7SIdo Schimmel };
1467ab8c06b7SIdo Schimmel 
1468ab8c06b7SIdo Schimmel static const struct mlxsw_sp_span_trigger_ops *
1469ab8c06b7SIdo Schimmel mlxsw_sp2_span_trigger_ops_arr[] = {
1470ab8c06b7SIdo Schimmel 	[MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops,
1471ab8c06b7SIdo Schimmel 	[MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] =
1472ab8c06b7SIdo Schimmel 		&mlxsw_sp2_span_trigger_global_ops,
147308a3641fSIdo Schimmel };
147408a3641fSIdo Schimmel 
147508a3641fSIdo Schimmel static void
mlxsw_sp_span_trigger_ops_set(struct mlxsw_sp_span_trigger_entry * trigger_entry)147608a3641fSIdo Schimmel mlxsw_sp_span_trigger_ops_set(struct mlxsw_sp_span_trigger_entry *trigger_entry)
147708a3641fSIdo Schimmel {
147808a3641fSIdo Schimmel 	struct mlxsw_sp_span *span = trigger_entry->span;
147908a3641fSIdo Schimmel 	enum mlxsw_sp_span_trigger_type type;
148008a3641fSIdo Schimmel 
148108a3641fSIdo Schimmel 	switch (trigger_entry->trigger) {
1482df561f66SGustavo A. R. Silva 	case MLXSW_SP_SPAN_TRIGGER_INGRESS:
148308a3641fSIdo Schimmel 	case MLXSW_SP_SPAN_TRIGGER_EGRESS:
148408a3641fSIdo Schimmel 		type = MLXSW_SP_SPAN_TRIGGER_TYPE_PORT;
148508a3641fSIdo Schimmel 		break;
1486df561f66SGustavo A. R. Silva 	case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
1487df561f66SGustavo A. R. Silva 	case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
1488ab8c06b7SIdo Schimmel 	case MLXSW_SP_SPAN_TRIGGER_ECN:
1489ab8c06b7SIdo Schimmel 		type = MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL;
1490ab8c06b7SIdo Schimmel 		break;
149108a3641fSIdo Schimmel 	default:
149208a3641fSIdo Schimmel 		WARN_ON_ONCE(1);
149308a3641fSIdo Schimmel 		return;
149408a3641fSIdo Schimmel 	}
149508a3641fSIdo Schimmel 
149608a3641fSIdo Schimmel 	trigger_entry->ops = span->span_trigger_ops_arr[type];
1497c056618cSIdo Schimmel }
1498c056618cSIdo Schimmel 
1499c056618cSIdo Schimmel 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)1500c056618cSIdo Schimmel mlxsw_sp_span_trigger_entry_create(struct mlxsw_sp_span *span,
1501c056618cSIdo Schimmel 				   enum mlxsw_sp_span_trigger trigger,
1502c056618cSIdo Schimmel 				   struct mlxsw_sp_port *mlxsw_sp_port,
1503c056618cSIdo Schimmel 				   const struct mlxsw_sp_span_trigger_parms
1504c056618cSIdo Schimmel 				   *parms)
1505c056618cSIdo Schimmel {
1506c056618cSIdo Schimmel 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
1507c056618cSIdo Schimmel 	int err;
1508c056618cSIdo Schimmel 
1509c056618cSIdo Schimmel 	trigger_entry = kzalloc(sizeof(*trigger_entry), GFP_KERNEL);
1510c056618cSIdo Schimmel 	if (!trigger_entry)
1511c056618cSIdo Schimmel 		return ERR_PTR(-ENOMEM);
1512c056618cSIdo Schimmel 
1513c056618cSIdo Schimmel 	refcount_set(&trigger_entry->ref_count, 1);
151408a3641fSIdo Schimmel 	trigger_entry->local_port = mlxsw_sp_port ? mlxsw_sp_port->local_port :
151508a3641fSIdo Schimmel 						    0;
1516c056618cSIdo Schimmel 	trigger_entry->trigger = trigger;
1517c056618cSIdo Schimmel 	memcpy(&trigger_entry->parms, parms, sizeof(trigger_entry->parms));
151808a3641fSIdo Schimmel 	trigger_entry->span = span;
151908a3641fSIdo Schimmel 	mlxsw_sp_span_trigger_ops_set(trigger_entry);
1520c056618cSIdo Schimmel 	list_add_tail(&trigger_entry->list, &span->trigger_entries_list);
1521c056618cSIdo Schimmel 
152208a3641fSIdo Schimmel 	err = trigger_entry->ops->bind(trigger_entry);
1523c056618cSIdo Schimmel 	if (err)
1524c056618cSIdo Schimmel 		goto err_trigger_entry_bind;
1525c056618cSIdo Schimmel 
1526c056618cSIdo Schimmel 	return trigger_entry;
1527c056618cSIdo Schimmel 
1528c056618cSIdo Schimmel err_trigger_entry_bind:
1529c056618cSIdo Schimmel 	list_del(&trigger_entry->list);
1530c056618cSIdo Schimmel 	kfree(trigger_entry);
1531c056618cSIdo Schimmel 	return ERR_PTR(err);
1532c056618cSIdo Schimmel }
1533c056618cSIdo Schimmel 
1534c056618cSIdo Schimmel static void
mlxsw_sp_span_trigger_entry_destroy(struct mlxsw_sp_span * span,struct mlxsw_sp_span_trigger_entry * trigger_entry)1535c056618cSIdo Schimmel mlxsw_sp_span_trigger_entry_destroy(struct mlxsw_sp_span *span,
1536c056618cSIdo Schimmel 				    struct mlxsw_sp_span_trigger_entry *
1537c056618cSIdo Schimmel 				    trigger_entry)
1538c056618cSIdo Schimmel {
153908a3641fSIdo Schimmel 	trigger_entry->ops->unbind(trigger_entry);
1540c056618cSIdo Schimmel 	list_del(&trigger_entry->list);
1541c056618cSIdo Schimmel 	kfree(trigger_entry);
1542c056618cSIdo Schimmel }
1543c056618cSIdo Schimmel 
1544c056618cSIdo Schimmel 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)1545c056618cSIdo Schimmel mlxsw_sp_span_trigger_entry_find(struct mlxsw_sp_span *span,
1546c056618cSIdo Schimmel 				 enum mlxsw_sp_span_trigger trigger,
1547c056618cSIdo Schimmel 				 struct mlxsw_sp_port *mlxsw_sp_port)
1548c056618cSIdo Schimmel {
1549c056618cSIdo Schimmel 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
1550c056618cSIdo Schimmel 
1551c056618cSIdo Schimmel 	list_for_each_entry(trigger_entry, &span->trigger_entries_list, list) {
155208a3641fSIdo Schimmel 		if (trigger_entry->ops->matches(trigger_entry, trigger,
155308a3641fSIdo Schimmel 						mlxsw_sp_port))
1554c056618cSIdo Schimmel 			return trigger_entry;
1555c056618cSIdo Schimmel 	}
1556c056618cSIdo Schimmel 
1557c056618cSIdo Schimmel 	return NULL;
1558c056618cSIdo Schimmel }
1559c056618cSIdo Schimmel 
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)1560c056618cSIdo Schimmel int mlxsw_sp_span_agent_bind(struct mlxsw_sp *mlxsw_sp,
1561c056618cSIdo Schimmel 			     enum mlxsw_sp_span_trigger trigger,
1562c056618cSIdo Schimmel 			     struct mlxsw_sp_port *mlxsw_sp_port,
1563c056618cSIdo Schimmel 			     const struct mlxsw_sp_span_trigger_parms *parms)
1564c056618cSIdo Schimmel {
1565c056618cSIdo Schimmel 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
1566c056618cSIdo Schimmel 	int err = 0;
1567c056618cSIdo Schimmel 
1568c056618cSIdo Schimmel 	ASSERT_RTNL();
1569c056618cSIdo Schimmel 
1570c056618cSIdo Schimmel 	if (!mlxsw_sp_span_entry_find_by_id(mlxsw_sp, parms->span_id))
1571c056618cSIdo Schimmel 		return -EINVAL;
1572c056618cSIdo Schimmel 
1573c056618cSIdo Schimmel 	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
1574c056618cSIdo Schimmel 							 trigger,
1575c056618cSIdo Schimmel 							 mlxsw_sp_port);
1576c056618cSIdo Schimmel 	if (trigger_entry) {
15772dcbd920SIdo Schimmel 		if (trigger_entry->parms.span_id != parms->span_id ||
15782dcbd920SIdo Schimmel 		    trigger_entry->parms.probability_rate !=
15792dcbd920SIdo Schimmel 		    parms->probability_rate)
1580c056618cSIdo Schimmel 			return -EINVAL;
1581c056618cSIdo Schimmel 		refcount_inc(&trigger_entry->ref_count);
1582c056618cSIdo Schimmel 		goto out;
1583c056618cSIdo Schimmel 	}
1584c056618cSIdo Schimmel 
1585c056618cSIdo Schimmel 	trigger_entry = mlxsw_sp_span_trigger_entry_create(mlxsw_sp->span,
1586c056618cSIdo Schimmel 							   trigger,
1587c056618cSIdo Schimmel 							   mlxsw_sp_port,
1588c056618cSIdo Schimmel 							   parms);
1589c056618cSIdo Schimmel 	if (IS_ERR(trigger_entry))
1590c056618cSIdo Schimmel 		err = PTR_ERR(trigger_entry);
1591c056618cSIdo Schimmel 
1592c056618cSIdo Schimmel out:
1593c056618cSIdo Schimmel 	return err;
1594c056618cSIdo Schimmel }
1595c056618cSIdo Schimmel 
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)1596c056618cSIdo Schimmel void mlxsw_sp_span_agent_unbind(struct mlxsw_sp *mlxsw_sp,
1597c056618cSIdo Schimmel 				enum mlxsw_sp_span_trigger trigger,
1598c056618cSIdo Schimmel 				struct mlxsw_sp_port *mlxsw_sp_port,
1599c056618cSIdo Schimmel 				const struct mlxsw_sp_span_trigger_parms *parms)
1600c056618cSIdo Schimmel {
1601c056618cSIdo Schimmel 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
1602c056618cSIdo Schimmel 
1603c056618cSIdo Schimmel 	ASSERT_RTNL();
1604c056618cSIdo Schimmel 
1605c056618cSIdo Schimmel 	if (WARN_ON_ONCE(!mlxsw_sp_span_entry_find_by_id(mlxsw_sp,
1606c056618cSIdo Schimmel 							 parms->span_id)))
1607c056618cSIdo Schimmel 		return;
1608c056618cSIdo Schimmel 
1609c056618cSIdo Schimmel 	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
1610c056618cSIdo Schimmel 							 trigger,
1611c056618cSIdo Schimmel 							 mlxsw_sp_port);
1612c056618cSIdo Schimmel 	if (WARN_ON_ONCE(!trigger_entry))
1613c056618cSIdo Schimmel 		return;
1614c056618cSIdo Schimmel 
1615c056618cSIdo Schimmel 	if (!refcount_dec_and_test(&trigger_entry->ref_count))
1616c056618cSIdo Schimmel 		return;
1617c056618cSIdo Schimmel 
1618c056618cSIdo Schimmel 	mlxsw_sp_span_trigger_entry_destroy(mlxsw_sp->span, trigger_entry);
1619c056618cSIdo Schimmel }
16204bafb85aSIdo Schimmel 
mlxsw_sp_span_trigger_enable(struct mlxsw_sp_port * mlxsw_sp_port,enum mlxsw_sp_span_trigger trigger,u8 tc)16212bafb216SIdo Schimmel int mlxsw_sp_span_trigger_enable(struct mlxsw_sp_port *mlxsw_sp_port,
16222bafb216SIdo Schimmel 				 enum mlxsw_sp_span_trigger trigger, u8 tc)
16232bafb216SIdo Schimmel {
16242bafb216SIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
16252bafb216SIdo Schimmel 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
16262bafb216SIdo Schimmel 
16272bafb216SIdo Schimmel 	ASSERT_RTNL();
16282bafb216SIdo Schimmel 
16292bafb216SIdo Schimmel 	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
16302bafb216SIdo Schimmel 							 trigger,
16312bafb216SIdo Schimmel 							 mlxsw_sp_port);
16322bafb216SIdo Schimmel 	if (WARN_ON_ONCE(!trigger_entry))
16332bafb216SIdo Schimmel 		return -EINVAL;
16342bafb216SIdo Schimmel 
16352bafb216SIdo Schimmel 	return trigger_entry->ops->enable(trigger_entry, mlxsw_sp_port, tc);
16362bafb216SIdo Schimmel }
16372bafb216SIdo Schimmel 
mlxsw_sp_span_trigger_disable(struct mlxsw_sp_port * mlxsw_sp_port,enum mlxsw_sp_span_trigger trigger,u8 tc)16382bafb216SIdo Schimmel void mlxsw_sp_span_trigger_disable(struct mlxsw_sp_port *mlxsw_sp_port,
16392bafb216SIdo Schimmel 				   enum mlxsw_sp_span_trigger trigger, u8 tc)
16402bafb216SIdo Schimmel {
16412bafb216SIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
16422bafb216SIdo Schimmel 	struct mlxsw_sp_span_trigger_entry *trigger_entry;
16432bafb216SIdo Schimmel 
16442bafb216SIdo Schimmel 	ASSERT_RTNL();
16452bafb216SIdo Schimmel 
16462bafb216SIdo Schimmel 	trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
16472bafb216SIdo Schimmel 							 trigger,
16482bafb216SIdo Schimmel 							 mlxsw_sp_port);
16492bafb216SIdo Schimmel 	if (WARN_ON_ONCE(!trigger_entry))
16502bafb216SIdo Schimmel 		return;
16512bafb216SIdo Schimmel 
16522bafb216SIdo Schimmel 	return trigger_entry->ops->disable(trigger_entry, mlxsw_sp_port, tc);
16532bafb216SIdo Schimmel }
16542bafb216SIdo Schimmel 
mlxsw_sp_span_trigger_is_ingress(enum mlxsw_sp_span_trigger trigger)16550908e42aSPetr Machata bool mlxsw_sp_span_trigger_is_ingress(enum mlxsw_sp_span_trigger trigger)
16560908e42aSPetr Machata {
16570908e42aSPetr Machata 	switch (trigger) {
16580908e42aSPetr Machata 	case MLXSW_SP_SPAN_TRIGGER_INGRESS:
16590908e42aSPetr Machata 	case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP:
16600908e42aSPetr Machata 	case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP:
16610908e42aSPetr Machata 		return true;
16620908e42aSPetr Machata 	case MLXSW_SP_SPAN_TRIGGER_EGRESS:
16630908e42aSPetr Machata 	case MLXSW_SP_SPAN_TRIGGER_ECN:
16640908e42aSPetr Machata 		return false;
16650908e42aSPetr Machata 	}
16660908e42aSPetr Machata 
16670908e42aSPetr Machata 	WARN_ON_ONCE(1);
16680908e42aSPetr Machata 	return false;
16690908e42aSPetr Machata }
16700908e42aSPetr Machata 
mlxsw_sp1_span_init(struct mlxsw_sp * mlxsw_sp)167108a3641fSIdo Schimmel static int mlxsw_sp1_span_init(struct mlxsw_sp *mlxsw_sp)
167208a3641fSIdo Schimmel {
167334e4ace5SIdo Schimmel 	size_t arr_size = ARRAY_SIZE(mlxsw_sp1_span_entry_ops_arr);
167434e4ace5SIdo Schimmel 
1675fa8c08b8SIdo Schimmel 	/* Must be first to avoid NULL pointer dereference by subsequent
1676fa8c08b8SIdo Schimmel 	 * can_handle() callbacks.
1677fa8c08b8SIdo Schimmel 	 */
1678fa8c08b8SIdo Schimmel 	if (WARN_ON(mlxsw_sp1_span_entry_ops_arr[0] !=
1679fa8c08b8SIdo Schimmel 		    &mlxsw_sp1_span_entry_ops_cpu))
1680fa8c08b8SIdo Schimmel 		return -EINVAL;
1681fa8c08b8SIdo Schimmel 
1682ab8c06b7SIdo Schimmel 	mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp1_span_trigger_ops_arr;
168334e4ace5SIdo Schimmel 	mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp1_span_entry_ops_arr;
168434e4ace5SIdo Schimmel 	mlxsw_sp->span->span_entry_ops_arr_size = arr_size;
168508a3641fSIdo Schimmel 
168608a3641fSIdo Schimmel 	return 0;
168708a3641fSIdo Schimmel }
168808a3641fSIdo Schimmel 
mlxsw_sp1_span_policer_id_base_set(struct mlxsw_sp * mlxsw_sp,u16 policer_id_base)16894039504eSIdo Schimmel static int mlxsw_sp1_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp,
16904039504eSIdo Schimmel 					      u16 policer_id_base)
16914039504eSIdo Schimmel {
16924039504eSIdo Schimmel 	return -EOPNOTSUPP;
16934039504eSIdo Schimmel }
16944039504eSIdo Schimmel 
16954bafb85aSIdo Schimmel const struct mlxsw_sp_span_ops mlxsw_sp1_span_ops = {
169608a3641fSIdo Schimmel 	.init = mlxsw_sp1_span_init,
16974039504eSIdo Schimmel 	.policer_id_base_set = mlxsw_sp1_span_policer_id_base_set,
16984bafb85aSIdo Schimmel };
16994bafb85aSIdo Schimmel 
mlxsw_sp2_span_init(struct mlxsw_sp * mlxsw_sp)170008a3641fSIdo Schimmel static int mlxsw_sp2_span_init(struct mlxsw_sp *mlxsw_sp)
170108a3641fSIdo Schimmel {
170234e4ace5SIdo Schimmel 	size_t arr_size = ARRAY_SIZE(mlxsw_sp2_span_entry_ops_arr);
170334e4ace5SIdo Schimmel 
1704fa8c08b8SIdo Schimmel 	/* Must be first to avoid NULL pointer dereference by subsequent
1705fa8c08b8SIdo Schimmel 	 * can_handle() callbacks.
1706fa8c08b8SIdo Schimmel 	 */
1707fa8c08b8SIdo Schimmel 	if (WARN_ON(mlxsw_sp2_span_entry_ops_arr[0] !=
1708fa8c08b8SIdo Schimmel 		    &mlxsw_sp2_span_entry_ops_cpu))
1709fa8c08b8SIdo Schimmel 		return -EINVAL;
1710fa8c08b8SIdo Schimmel 
1711ab8c06b7SIdo Schimmel 	mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp2_span_trigger_ops_arr;
171234e4ace5SIdo Schimmel 	mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp2_span_entry_ops_arr;
171334e4ace5SIdo Schimmel 	mlxsw_sp->span->span_entry_ops_arr_size = arr_size;
171408a3641fSIdo Schimmel 
171508a3641fSIdo Schimmel 	return 0;
171608a3641fSIdo Schimmel }
171708a3641fSIdo Schimmel 
17184bafb85aSIdo Schimmel #define MLXSW_SP2_SPAN_EG_MIRROR_BUFFER_FACTOR 38
17194bafb85aSIdo Schimmel #define MLXSW_SP3_SPAN_EG_MIRROR_BUFFER_FACTOR 50
17204bafb85aSIdo Schimmel 
mlxsw_sp2_span_policer_id_base_set(struct mlxsw_sp * mlxsw_sp,u16 policer_id_base)17214039504eSIdo Schimmel static int mlxsw_sp2_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp,
17224039504eSIdo Schimmel 					      u16 policer_id_base)
17234039504eSIdo Schimmel {
17244039504eSIdo Schimmel 	char mogcr_pl[MLXSW_REG_MOGCR_LEN];
17254039504eSIdo Schimmel 	int err;
17264039504eSIdo Schimmel 
17274039504eSIdo Schimmel 	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl);
17284039504eSIdo Schimmel 	if (err)
17294039504eSIdo Schimmel 		return err;
17304039504eSIdo Schimmel 
17314039504eSIdo Schimmel 	mlxsw_reg_mogcr_mirroring_pid_base_set(mogcr_pl, policer_id_base);
17324039504eSIdo Schimmel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl);
17334039504eSIdo Schimmel }
17344039504eSIdo Schimmel 
17354bafb85aSIdo Schimmel const struct mlxsw_sp_span_ops mlxsw_sp2_span_ops = {
173608a3641fSIdo Schimmel 	.init = mlxsw_sp2_span_init,
17374039504eSIdo Schimmel 	.policer_id_base_set = mlxsw_sp2_span_policer_id_base_set,
17384bafb85aSIdo Schimmel };
17394bafb85aSIdo Schimmel 
17404bafb85aSIdo Schimmel const struct mlxsw_sp_span_ops mlxsw_sp3_span_ops = {
174108a3641fSIdo Schimmel 	.init = mlxsw_sp2_span_init,
17424039504eSIdo Schimmel 	.policer_id_base_set = mlxsw_sp2_span_policer_id_base_set,
17434bafb85aSIdo Schimmel };
1744