123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978 |
- // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
- /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
- #include <linux/if_bridge.h>
- #include <linux/list.h>
- #include <net/arp.h>
- #include <net/gre.h>
- #include <net/lag.h>
- #include <net/ndisc.h>
- #include <net/ip6_tunnel.h>
- #include "spectrum.h"
- #include "spectrum_ipip.h"
- #include "spectrum_span.h"
- #include "spectrum_switchdev.h"
- int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
- {
- int i;
- if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_SPAN))
- return -EIO;
- mlxsw_sp->span.entries_count = MLXSW_CORE_RES_GET(mlxsw_sp->core,
- MAX_SPAN);
- mlxsw_sp->span.entries = kcalloc(mlxsw_sp->span.entries_count,
- sizeof(struct mlxsw_sp_span_entry),
- GFP_KERNEL);
- if (!mlxsw_sp->span.entries)
- return -ENOMEM;
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
- INIT_LIST_HEAD(&curr->bound_ports_list);
- curr->id = i;
- }
- return 0;
- }
- void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp)
- {
- int i;
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
- WARN_ON_ONCE(!list_empty(&curr->bound_ports_list));
- }
- kfree(mlxsw_sp->span.entries);
- }
- static int
- mlxsw_sp_span_entry_phys_parms(const struct net_device *to_dev,
- struct mlxsw_sp_span_parms *sparmsp)
- {
- sparmsp->dest_port = netdev_priv(to_dev);
- return 0;
- }
- static int
- mlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry *span_entry,
- struct mlxsw_sp_span_parms sparms)
- {
- struct mlxsw_sp_port *dest_port = sparms.dest_port;
- struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
- u8 local_port = dest_port->local_port;
- char mpat_pl[MLXSW_REG_MPAT_LEN];
- int pa_id = span_entry->id;
- /* Create a new port analayzer entry for local_port. */
- mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
- MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
- return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
- }
- static void
- mlxsw_sp_span_entry_deconfigure_common(struct mlxsw_sp_span_entry *span_entry,
- enum mlxsw_reg_mpat_span_type span_type)
- {
- struct mlxsw_sp_port *dest_port = span_entry->parms.dest_port;
- struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
- u8 local_port = dest_port->local_port;
- char mpat_pl[MLXSW_REG_MPAT_LEN];
- int pa_id = span_entry->id;
- mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false, span_type);
- mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
- }
- static void
- mlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry *span_entry)
- {
- mlxsw_sp_span_entry_deconfigure_common(span_entry,
- MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH);
- }
- static const
- struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = {
- .can_handle = mlxsw_sp_port_dev_check,
- .parms = mlxsw_sp_span_entry_phys_parms,
- .configure = mlxsw_sp_span_entry_phys_configure,
- .deconfigure = mlxsw_sp_span_entry_phys_deconfigure,
- };
- static int mlxsw_sp_span_dmac(struct neigh_table *tbl,
- const void *pkey,
- struct net_device *dev,
- unsigned char dmac[ETH_ALEN])
- {
- struct neighbour *neigh = neigh_lookup(tbl, pkey, dev);
- int err = 0;
- if (!neigh) {
- neigh = neigh_create(tbl, pkey, dev);
- if (IS_ERR(neigh))
- return PTR_ERR(neigh);
- }
- neigh_event_send(neigh, NULL);
- read_lock_bh(&neigh->lock);
- if ((neigh->nud_state & NUD_VALID) && !neigh->dead)
- memcpy(dmac, neigh->ha, ETH_ALEN);
- else
- err = -ENOENT;
- read_unlock_bh(&neigh->lock);
- neigh_release(neigh);
- return err;
- }
- static int
- mlxsw_sp_span_entry_unoffloadable(struct mlxsw_sp_span_parms *sparmsp)
- {
- sparmsp->dest_port = NULL;
- return 0;
- }
- static struct net_device *
- mlxsw_sp_span_entry_bridge_8021q(const struct net_device *br_dev,
- unsigned char *dmac,
- u16 *p_vid)
- {
- struct bridge_vlan_info vinfo;
- struct net_device *edev;
- u16 vid = *p_vid;
- if (!vid && WARN_ON(br_vlan_get_pvid(br_dev, &vid)))
- return NULL;
- if (!vid ||
- br_vlan_get_info(br_dev, vid, &vinfo) ||
- !(vinfo.flags & BRIDGE_VLAN_INFO_BRENTRY))
- return NULL;
- edev = br_fdb_find_port(br_dev, dmac, vid);
- if (!edev)
- return NULL;
- if (br_vlan_get_info(edev, vid, &vinfo))
- return NULL;
- if (vinfo.flags & BRIDGE_VLAN_INFO_UNTAGGED)
- *p_vid = 0;
- else
- *p_vid = vid;
- return edev;
- }
- static struct net_device *
- mlxsw_sp_span_entry_bridge_8021d(const struct net_device *br_dev,
- unsigned char *dmac)
- {
- return br_fdb_find_port(br_dev, dmac, 0);
- }
- static struct net_device *
- mlxsw_sp_span_entry_bridge(const struct net_device *br_dev,
- unsigned char dmac[ETH_ALEN],
- u16 *p_vid)
- {
- struct mlxsw_sp_bridge_port *bridge_port;
- enum mlxsw_reg_spms_state spms_state;
- struct net_device *dev = NULL;
- struct mlxsw_sp_port *port;
- u8 stp_state;
- if (br_vlan_enabled(br_dev))
- dev = mlxsw_sp_span_entry_bridge_8021q(br_dev, dmac, p_vid);
- else if (!*p_vid)
- dev = mlxsw_sp_span_entry_bridge_8021d(br_dev, dmac);
- if (!dev)
- return NULL;
- port = mlxsw_sp_port_dev_lower_find(dev);
- if (!port)
- return NULL;
- bridge_port = mlxsw_sp_bridge_port_find(port->mlxsw_sp->bridge, dev);
- if (!bridge_port)
- return NULL;
- stp_state = mlxsw_sp_bridge_port_stp_state(bridge_port);
- spms_state = mlxsw_sp_stp_spms_state(stp_state);
- if (spms_state != MLXSW_REG_SPMS_STATE_FORWARDING)
- return NULL;
- return dev;
- }
- static struct net_device *
- mlxsw_sp_span_entry_vlan(const struct net_device *vlan_dev,
- u16 *p_vid)
- {
- *p_vid = vlan_dev_vlan_id(vlan_dev);
- return vlan_dev_real_dev(vlan_dev);
- }
- static struct net_device *
- mlxsw_sp_span_entry_lag(struct net_device *lag_dev)
- {
- struct net_device *dev;
- struct list_head *iter;
- netdev_for_each_lower_dev(lag_dev, dev, iter)
- if (netif_carrier_ok(dev) &&
- net_lag_port_dev_txable(dev) &&
- mlxsw_sp_port_dev_check(dev))
- return dev;
- return NULL;
- }
- 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)
- {
- unsigned char dmac[ETH_ALEN];
- u16 vid = 0;
- if (mlxsw_sp_l3addr_is_zero(gw))
- gw = daddr;
- if (!edev || mlxsw_sp_span_dmac(tbl, &gw, edev, dmac))
- goto unoffloadable;
- if (is_vlan_dev(edev))
- edev = mlxsw_sp_span_entry_vlan(edev, &vid);
- if (netif_is_bridge_master(edev)) {
- edev = mlxsw_sp_span_entry_bridge(edev, dmac, &vid);
- if (!edev)
- goto unoffloadable;
- }
- if (is_vlan_dev(edev)) {
- if (vid || !(edev->flags & IFF_UP))
- goto unoffloadable;
- edev = mlxsw_sp_span_entry_vlan(edev, &vid);
- }
- if (netif_is_lag_master(edev)) {
- if (!(edev->flags & IFF_UP))
- goto unoffloadable;
- edev = mlxsw_sp_span_entry_lag(edev);
- if (!edev)
- goto unoffloadable;
- }
- if (!mlxsw_sp_port_dev_check(edev))
- goto unoffloadable;
- sparmsp->dest_port = netdev_priv(edev);
- sparmsp->ttl = ttl;
- memcpy(sparmsp->dmac, dmac, ETH_ALEN);
- memcpy(sparmsp->smac, edev->dev_addr, ETH_ALEN);
- sparmsp->saddr = saddr;
- sparmsp->daddr = daddr;
- sparmsp->vid = vid;
- return 0;
- unoffloadable:
- return mlxsw_sp_span_entry_unoffloadable(sparmsp);
- }
- #if IS_ENABLED(CONFIG_NET_IPGRE)
- static struct net_device *
- mlxsw_sp_span_gretap4_route(const struct net_device *to_dev,
- __be32 *saddrp, __be32 *daddrp)
- {
- struct ip_tunnel *tun = netdev_priv(to_dev);
- struct net_device *dev = NULL;
- struct ip_tunnel_parm parms;
- struct rtable *rt = NULL;
- struct flowi4 fl4;
- /* We assume "dev" stays valid after rt is put. */
- ASSERT_RTNL();
- parms = mlxsw_sp_ipip_netdev_parms4(to_dev);
- ip_tunnel_init_flow(&fl4, parms.iph.protocol, *daddrp, *saddrp,
- 0, 0, parms.link, tun->fwmark);
- rt = ip_route_output_key(tun->net, &fl4);
- if (IS_ERR(rt))
- return NULL;
- if (rt->rt_type != RTN_UNICAST)
- goto out;
- dev = rt->dst.dev;
- *saddrp = fl4.saddr;
- *daddrp = rt->rt_gateway;
- out:
- ip_rt_put(rt);
- return dev;
- }
- static int
- mlxsw_sp_span_entry_gretap4_parms(const struct net_device *to_dev,
- struct mlxsw_sp_span_parms *sparmsp)
- {
- struct ip_tunnel_parm tparm = mlxsw_sp_ipip_netdev_parms4(to_dev);
- union mlxsw_sp_l3addr saddr = { .addr4 = tparm.iph.saddr };
- union mlxsw_sp_l3addr daddr = { .addr4 = tparm.iph.daddr };
- bool inherit_tos = tparm.iph.tos & 0x1;
- bool inherit_ttl = !tparm.iph.ttl;
- union mlxsw_sp_l3addr gw = daddr;
- struct net_device *l3edev;
- if (!(to_dev->flags & IFF_UP) ||
- /* Reject tunnels with GRE keys, checksums, etc. */
- tparm.i_flags || tparm.o_flags ||
- /* Require a fixed TTL and a TOS copied from the mirrored packet. */
- inherit_ttl || !inherit_tos ||
- /* A destination address may not be "any". */
- mlxsw_sp_l3addr_is_zero(daddr))
- return mlxsw_sp_span_entry_unoffloadable(sparmsp);
- l3edev = mlxsw_sp_span_gretap4_route(to_dev, &saddr.addr4, &gw.addr4);
- return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
- tparm.iph.ttl,
- &arp_tbl, sparmsp);
- }
- static int
- mlxsw_sp_span_entry_gretap4_configure(struct mlxsw_sp_span_entry *span_entry,
- struct mlxsw_sp_span_parms sparms)
- {
- struct mlxsw_sp_port *dest_port = sparms.dest_port;
- struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
- u8 local_port = dest_port->local_port;
- char mpat_pl[MLXSW_REG_MPAT_LEN];
- int pa_id = span_entry->id;
- /* Create a new port analayzer entry for local_port. */
- mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
- MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
- mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
- mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
- MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
- sparms.dmac, !!sparms.vid);
- mlxsw_reg_mpat_eth_rspan_l3_ipv4_pack(mpat_pl,
- sparms.ttl, sparms.smac,
- be32_to_cpu(sparms.saddr.addr4),
- be32_to_cpu(sparms.daddr.addr4));
- return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
- }
- static void
- mlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry *span_entry)
- {
- mlxsw_sp_span_entry_deconfigure_common(span_entry,
- MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
- }
- static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap4 = {
- .can_handle = is_gretap_dev,
- .parms = mlxsw_sp_span_entry_gretap4_parms,
- .configure = mlxsw_sp_span_entry_gretap4_configure,
- .deconfigure = mlxsw_sp_span_entry_gretap4_deconfigure,
- };
- #endif
- #if IS_ENABLED(CONFIG_IPV6_GRE)
- static struct net_device *
- mlxsw_sp_span_gretap6_route(const struct net_device *to_dev,
- struct in6_addr *saddrp,
- struct in6_addr *daddrp)
- {
- struct ip6_tnl *t = netdev_priv(to_dev);
- struct flowi6 fl6 = t->fl.u.ip6;
- struct net_device *dev = NULL;
- struct dst_entry *dst;
- struct rt6_info *rt6;
- /* We assume "dev" stays valid after dst is released. */
- ASSERT_RTNL();
- fl6.flowi6_mark = t->parms.fwmark;
- if (!ip6_tnl_xmit_ctl(t, &fl6.saddr, &fl6.daddr))
- return NULL;
- dst = ip6_route_output(t->net, NULL, &fl6);
- if (!dst || dst->error)
- goto out;
- rt6 = container_of(dst, struct rt6_info, dst);
- dev = dst->dev;
- *saddrp = fl6.saddr;
- *daddrp = rt6->rt6i_gateway;
- out:
- dst_release(dst);
- return dev;
- }
- static int
- mlxsw_sp_span_entry_gretap6_parms(const struct net_device *to_dev,
- struct mlxsw_sp_span_parms *sparmsp)
- {
- struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(to_dev);
- bool inherit_tos = tparm.flags & IP6_TNL_F_USE_ORIG_TCLASS;
- union mlxsw_sp_l3addr saddr = { .addr6 = tparm.laddr };
- union mlxsw_sp_l3addr daddr = { .addr6 = tparm.raddr };
- bool inherit_ttl = !tparm.hop_limit;
- union mlxsw_sp_l3addr gw = daddr;
- struct net_device *l3edev;
- if (!(to_dev->flags & IFF_UP) ||
- /* Reject tunnels with GRE keys, checksums, etc. */
- tparm.i_flags || tparm.o_flags ||
- /* Require a fixed TTL and a TOS copied from the mirrored packet. */
- inherit_ttl || !inherit_tos ||
- /* A destination address may not be "any". */
- mlxsw_sp_l3addr_is_zero(daddr))
- return mlxsw_sp_span_entry_unoffloadable(sparmsp);
- l3edev = mlxsw_sp_span_gretap6_route(to_dev, &saddr.addr6, &gw.addr6);
- return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
- tparm.hop_limit,
- &nd_tbl, sparmsp);
- }
- static int
- mlxsw_sp_span_entry_gretap6_configure(struct mlxsw_sp_span_entry *span_entry,
- struct mlxsw_sp_span_parms sparms)
- {
- struct mlxsw_sp_port *dest_port = sparms.dest_port;
- struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
- u8 local_port = dest_port->local_port;
- char mpat_pl[MLXSW_REG_MPAT_LEN];
- int pa_id = span_entry->id;
- /* Create a new port analayzer entry for local_port. */
- mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
- MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
- mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
- mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl,
- MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER,
- sparms.dmac, !!sparms.vid);
- mlxsw_reg_mpat_eth_rspan_l3_ipv6_pack(mpat_pl, sparms.ttl, sparms.smac,
- sparms.saddr.addr6,
- sparms.daddr.addr6);
- return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
- }
- static void
- mlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry *span_entry)
- {
- mlxsw_sp_span_entry_deconfigure_common(span_entry,
- MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3);
- }
- static const
- struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap6 = {
- .can_handle = is_ip6gretap_dev,
- .parms = mlxsw_sp_span_entry_gretap6_parms,
- .configure = mlxsw_sp_span_entry_gretap6_configure,
- .deconfigure = mlxsw_sp_span_entry_gretap6_deconfigure,
- };
- #endif
- static bool
- mlxsw_sp_span_vlan_can_handle(const struct net_device *dev)
- {
- return is_vlan_dev(dev) &&
- mlxsw_sp_port_dev_check(vlan_dev_real_dev(dev));
- }
- static int
- mlxsw_sp_span_entry_vlan_parms(const struct net_device *to_dev,
- struct mlxsw_sp_span_parms *sparmsp)
- {
- struct net_device *real_dev;
- u16 vid;
- if (!(to_dev->flags & IFF_UP))
- return mlxsw_sp_span_entry_unoffloadable(sparmsp);
- real_dev = mlxsw_sp_span_entry_vlan(to_dev, &vid);
- sparmsp->dest_port = netdev_priv(real_dev);
- sparmsp->vid = vid;
- return 0;
- }
- static int
- mlxsw_sp_span_entry_vlan_configure(struct mlxsw_sp_span_entry *span_entry,
- struct mlxsw_sp_span_parms sparms)
- {
- struct mlxsw_sp_port *dest_port = sparms.dest_port;
- struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp;
- u8 local_port = dest_port->local_port;
- char mpat_pl[MLXSW_REG_MPAT_LEN];
- int pa_id = span_entry->id;
- mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true,
- MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH);
- mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid);
- return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl);
- }
- static void
- mlxsw_sp_span_entry_vlan_deconfigure(struct mlxsw_sp_span_entry *span_entry)
- {
- mlxsw_sp_span_entry_deconfigure_common(span_entry,
- MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH);
- }
- static const
- struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_vlan = {
- .can_handle = mlxsw_sp_span_vlan_can_handle,
- .parms = mlxsw_sp_span_entry_vlan_parms,
- .configure = mlxsw_sp_span_entry_vlan_configure,
- .deconfigure = mlxsw_sp_span_entry_vlan_deconfigure,
- };
- static const
- struct mlxsw_sp_span_entry_ops *const mlxsw_sp_span_entry_types[] = {
- &mlxsw_sp_span_entry_ops_phys,
- #if IS_ENABLED(CONFIG_NET_IPGRE)
- &mlxsw_sp_span_entry_ops_gretap4,
- #endif
- #if IS_ENABLED(CONFIG_IPV6_GRE)
- &mlxsw_sp_span_entry_ops_gretap6,
- #endif
- &mlxsw_sp_span_entry_ops_vlan,
- };
- static int
- mlxsw_sp_span_entry_nop_parms(const struct net_device *to_dev,
- struct mlxsw_sp_span_parms *sparmsp)
- {
- return mlxsw_sp_span_entry_unoffloadable(sparmsp);
- }
- static int
- mlxsw_sp_span_entry_nop_configure(struct mlxsw_sp_span_entry *span_entry,
- struct mlxsw_sp_span_parms sparms)
- {
- return 0;
- }
- static void
- mlxsw_sp_span_entry_nop_deconfigure(struct mlxsw_sp_span_entry *span_entry)
- {
- }
- static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_nop = {
- .parms = mlxsw_sp_span_entry_nop_parms,
- .configure = mlxsw_sp_span_entry_nop_configure,
- .deconfigure = mlxsw_sp_span_entry_nop_deconfigure,
- };
- 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)
- {
- if (sparms.dest_port) {
- if (sparms.dest_port->mlxsw_sp != mlxsw_sp) {
- netdev_err(span_entry->to_dev, "Cannot mirror to %s, which belongs to a different mlxsw instance",
- sparms.dest_port->dev->name);
- sparms.dest_port = NULL;
- } else if (span_entry->ops->configure(span_entry, sparms)) {
- netdev_err(span_entry->to_dev, "Failed to offload mirror to %s",
- sparms.dest_port->dev->name);
- sparms.dest_port = NULL;
- }
- }
- span_entry->parms = sparms;
- }
- static void
- mlxsw_sp_span_entry_deconfigure(struct mlxsw_sp_span_entry *span_entry)
- {
- if (span_entry->parms.dest_port)
- span_entry->ops->deconfigure(span_entry);
- }
- 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)
- {
- struct mlxsw_sp_span_entry *span_entry = NULL;
- int i;
- /* find a free entry to use */
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- if (!mlxsw_sp->span.entries[i].ref_count) {
- span_entry = &mlxsw_sp->span.entries[i];
- break;
- }
- }
- if (!span_entry)
- return NULL;
- span_entry->ops = ops;
- span_entry->ref_count = 1;
- span_entry->to_dev = to_dev;
- mlxsw_sp_span_entry_configure(mlxsw_sp, span_entry, sparms);
- return span_entry;
- }
- static void mlxsw_sp_span_entry_destroy(struct mlxsw_sp_span_entry *span_entry)
- {
- mlxsw_sp_span_entry_deconfigure(span_entry);
- }
- struct mlxsw_sp_span_entry *
- mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp,
- const struct net_device *to_dev)
- {
- int i;
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
- if (curr->ref_count && curr->to_dev == to_dev)
- return curr;
- }
- return NULL;
- }
- void mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp,
- struct mlxsw_sp_span_entry *span_entry)
- {
- mlxsw_sp_span_entry_deconfigure(span_entry);
- span_entry->ops = &mlxsw_sp_span_entry_ops_nop;
- }
- static struct mlxsw_sp_span_entry *
- mlxsw_sp_span_entry_find_by_id(struct mlxsw_sp *mlxsw_sp, int span_id)
- {
- int i;
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
- if (curr->ref_count && curr->id == span_id)
- return curr;
- }
- return NULL;
- }
- 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)
- {
- struct mlxsw_sp_span_entry *span_entry;
- span_entry = mlxsw_sp_span_entry_find_by_port(mlxsw_sp, to_dev);
- if (span_entry) {
- /* Already exists, just take a reference */
- span_entry->ref_count++;
- return span_entry;
- }
- return mlxsw_sp_span_entry_create(mlxsw_sp, to_dev, ops, sparms);
- }
- static int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp,
- struct mlxsw_sp_span_entry *span_entry)
- {
- WARN_ON(!span_entry->ref_count);
- if (--span_entry->ref_count == 0)
- mlxsw_sp_span_entry_destroy(span_entry);
- return 0;
- }
- static bool mlxsw_sp_span_is_egress_mirror(struct mlxsw_sp_port *port)
- {
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
- struct mlxsw_sp_span_inspected_port *p;
- int i;
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
- list_for_each_entry(p, &curr->bound_ports_list, list)
- if (p->local_port == port->local_port &&
- p->type == MLXSW_SP_SPAN_EGRESS)
- return true;
- }
- return false;
- }
- static int mlxsw_sp_span_mtu_to_buffsize(const struct mlxsw_sp *mlxsw_sp,
- int mtu)
- {
- return mlxsw_sp_bytes_cells(mlxsw_sp, mtu * 5 / 2) + 1;
- }
- int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu)
- {
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
- char sbib_pl[MLXSW_REG_SBIB_LEN];
- int err;
- /* If port is egress mirrored, the shared buffer size should be
- * updated according to the mtu value
- */
- if (mlxsw_sp_span_is_egress_mirror(port)) {
- u32 buffsize = mlxsw_sp_span_mtu_to_buffsize(mlxsw_sp, mtu);
- mlxsw_reg_sbib_pack(sbib_pl, port->local_port, buffsize);
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
- if (err) {
- netdev_err(port->dev, "Could not update shared buffer for mirroring\n");
- return err;
- }
- }
- return 0;
- }
- static struct mlxsw_sp_span_inspected_port *
- mlxsw_sp_span_entry_bound_port_find(struct mlxsw_sp_span_entry *span_entry,
- enum mlxsw_sp_span_type type,
- struct mlxsw_sp_port *port,
- bool bind)
- {
- struct mlxsw_sp_span_inspected_port *p;
- list_for_each_entry(p, &span_entry->bound_ports_list, list)
- if (type == p->type &&
- port->local_port == p->local_port &&
- bind == p->bound)
- return p;
- return NULL;
- }
- static int
- mlxsw_sp_span_inspected_port_bind(struct mlxsw_sp_port *port,
- struct mlxsw_sp_span_entry *span_entry,
- enum mlxsw_sp_span_type type,
- bool bind)
- {
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
- char mpar_pl[MLXSW_REG_MPAR_LEN];
- int pa_id = span_entry->id;
- /* bind the port to the SPAN entry */
- mlxsw_reg_mpar_pack(mpar_pl, port->local_port,
- (enum mlxsw_reg_mpar_i_e)type, bind, pa_id);
- return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl);
- }
- static int
- mlxsw_sp_span_inspected_port_add(struct mlxsw_sp_port *port,
- struct mlxsw_sp_span_entry *span_entry,
- enum mlxsw_sp_span_type type,
- bool bind)
- {
- struct mlxsw_sp_span_inspected_port *inspected_port;
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
- char sbib_pl[MLXSW_REG_SBIB_LEN];
- int i;
- int err;
- /* A given (source port, direction) can only be bound to one analyzer,
- * so if a binding is requested, check for conflicts.
- */
- if (bind)
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- struct mlxsw_sp_span_entry *curr =
- &mlxsw_sp->span.entries[i];
- if (mlxsw_sp_span_entry_bound_port_find(curr, type,
- port, bind))
- return -EEXIST;
- }
- /* if it is an egress SPAN, bind a shared buffer to it */
- if (type == MLXSW_SP_SPAN_EGRESS) {
- u32 buffsize = mlxsw_sp_span_mtu_to_buffsize(mlxsw_sp,
- port->dev->mtu);
- mlxsw_reg_sbib_pack(sbib_pl, port->local_port, buffsize);
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
- if (err) {
- netdev_err(port->dev, "Could not create shared buffer for mirroring\n");
- return err;
- }
- }
- if (bind) {
- err = mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
- true);
- if (err)
- goto err_port_bind;
- }
- inspected_port = kzalloc(sizeof(*inspected_port), GFP_KERNEL);
- if (!inspected_port) {
- err = -ENOMEM;
- goto err_inspected_port_alloc;
- }
- inspected_port->local_port = port->local_port;
- inspected_port->type = type;
- inspected_port->bound = bind;
- list_add_tail(&inspected_port->list, &span_entry->bound_ports_list);
- return 0;
- err_inspected_port_alloc:
- if (bind)
- mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
- false);
- err_port_bind:
- if (type == MLXSW_SP_SPAN_EGRESS) {
- mlxsw_reg_sbib_pack(sbib_pl, port->local_port, 0);
- mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
- }
- return err;
- }
- static void
- mlxsw_sp_span_inspected_port_del(struct mlxsw_sp_port *port,
- struct mlxsw_sp_span_entry *span_entry,
- enum mlxsw_sp_span_type type,
- bool bind)
- {
- struct mlxsw_sp_span_inspected_port *inspected_port;
- struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
- char sbib_pl[MLXSW_REG_SBIB_LEN];
- inspected_port = mlxsw_sp_span_entry_bound_port_find(span_entry, type,
- port, bind);
- if (!inspected_port)
- return;
- if (bind)
- mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
- false);
- /* remove the SBIB buffer if it was egress SPAN */
- if (type == MLXSW_SP_SPAN_EGRESS) {
- mlxsw_reg_sbib_pack(sbib_pl, port->local_port, 0);
- mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
- }
- mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
- list_del(&inspected_port->list);
- kfree(inspected_port);
- }
- static const struct mlxsw_sp_span_entry_ops *
- mlxsw_sp_span_entry_ops(struct mlxsw_sp *mlxsw_sp,
- const struct net_device *to_dev)
- {
- size_t i;
- for (i = 0; i < ARRAY_SIZE(mlxsw_sp_span_entry_types); ++i)
- if (mlxsw_sp_span_entry_types[i]->can_handle(to_dev))
- return mlxsw_sp_span_entry_types[i];
- return NULL;
- }
- int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
- const struct net_device *to_dev,
- enum mlxsw_sp_span_type type, bool bind,
- int *p_span_id)
- {
- struct mlxsw_sp *mlxsw_sp = from->mlxsw_sp;
- const struct mlxsw_sp_span_entry_ops *ops;
- struct mlxsw_sp_span_parms sparms = {NULL};
- struct mlxsw_sp_span_entry *span_entry;
- int err;
- ops = mlxsw_sp_span_entry_ops(mlxsw_sp, to_dev);
- if (!ops) {
- netdev_err(to_dev, "Cannot mirror to %s", to_dev->name);
- return -EOPNOTSUPP;
- }
- err = ops->parms(to_dev, &sparms);
- if (err)
- return err;
- span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms);
- if (!span_entry)
- return -ENOBUFS;
- netdev_dbg(from->dev, "Adding inspected port to SPAN entry %d\n",
- span_entry->id);
- err = mlxsw_sp_span_inspected_port_add(from, span_entry, type, bind);
- if (err)
- goto err_port_bind;
- *p_span_id = span_entry->id;
- return 0;
- err_port_bind:
- mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
- return err;
- }
- void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, int span_id,
- enum mlxsw_sp_span_type type, bool bind)
- {
- struct mlxsw_sp_span_entry *span_entry;
- span_entry = mlxsw_sp_span_entry_find_by_id(from->mlxsw_sp, span_id);
- if (!span_entry) {
- netdev_err(from->dev, "no span entry found\n");
- return;
- }
- netdev_dbg(from->dev, "removing inspected port from SPAN entry %d\n",
- span_entry->id);
- mlxsw_sp_span_inspected_port_del(from, span_entry, type, bind);
- }
- void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp)
- {
- int i;
- int err;
- ASSERT_RTNL();
- for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
- struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
- struct mlxsw_sp_span_parms sparms = {NULL};
- if (!curr->ref_count)
- continue;
- err = curr->ops->parms(curr->to_dev, &sparms);
- if (err)
- continue;
- if (memcmp(&sparms, &curr->parms, sizeof(sparms))) {
- mlxsw_sp_span_entry_deconfigure(curr);
- mlxsw_sp_span_entry_configure(mlxsw_sp, curr, sparms);
- }
- }
- }
|