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