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