18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/if_bridge.h> 58c2ecf20Sopenharmony_ci#include <linux/list.h> 68c2ecf20Sopenharmony_ci#include <linux/mutex.h> 78c2ecf20Sopenharmony_ci#include <linux/refcount.h> 88c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 98c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 108c2ecf20Sopenharmony_ci#include <net/arp.h> 118c2ecf20Sopenharmony_ci#include <net/gre.h> 128c2ecf20Sopenharmony_ci#include <net/lag.h> 138c2ecf20Sopenharmony_ci#include <net/ndisc.h> 148c2ecf20Sopenharmony_ci#include <net/ip6_tunnel.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "spectrum.h" 178c2ecf20Sopenharmony_ci#include "spectrum_ipip.h" 188c2ecf20Sopenharmony_ci#include "spectrum_span.h" 198c2ecf20Sopenharmony_ci#include "spectrum_switchdev.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct mlxsw_sp_span { 228c2ecf20Sopenharmony_ci struct work_struct work; 238c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 248c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_trigger_ops **span_trigger_ops_arr; 258c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_entry_ops **span_entry_ops_arr; 268c2ecf20Sopenharmony_ci size_t span_entry_ops_arr_size; 278c2ecf20Sopenharmony_ci struct list_head analyzed_ports_list; 288c2ecf20Sopenharmony_ci struct mutex analyzed_ports_lock; /* Protects analyzed_ports_list */ 298c2ecf20Sopenharmony_ci struct list_head trigger_entries_list; 308c2ecf20Sopenharmony_ci u16 policer_id_base; 318c2ecf20Sopenharmony_ci refcount_t policer_id_base_ref_count; 328c2ecf20Sopenharmony_ci atomic_t active_entries_count; 338c2ecf20Sopenharmony_ci int entries_count; 348c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry entries[]; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct mlxsw_sp_span_analyzed_port { 388c2ecf20Sopenharmony_ci struct list_head list; /* Member of analyzed_ports_list */ 398c2ecf20Sopenharmony_ci refcount_t ref_count; 408c2ecf20Sopenharmony_ci u8 local_port; 418c2ecf20Sopenharmony_ci bool ingress; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct mlxsw_sp_span_trigger_entry { 458c2ecf20Sopenharmony_ci struct list_head list; /* Member of trigger_entries_list */ 468c2ecf20Sopenharmony_ci struct mlxsw_sp_span *span; 478c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_trigger_ops *ops; 488c2ecf20Sopenharmony_ci refcount_t ref_count; 498c2ecf20Sopenharmony_ci u8 local_port; 508c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger trigger; 518c2ecf20Sopenharmony_ci struct mlxsw_sp_span_trigger_parms parms; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cienum mlxsw_sp_span_trigger_type { 558c2ecf20Sopenharmony_ci MLXSW_SP_SPAN_TRIGGER_TYPE_PORT, 568c2ecf20Sopenharmony_ci MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL, 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct mlxsw_sp_span_trigger_ops { 608c2ecf20Sopenharmony_ci int (*bind)(struct mlxsw_sp_span_trigger_entry *trigger_entry); 618c2ecf20Sopenharmony_ci void (*unbind)(struct mlxsw_sp_span_trigger_entry *trigger_entry); 628c2ecf20Sopenharmony_ci bool (*matches)(struct mlxsw_sp_span_trigger_entry *trigger_entry, 638c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger trigger, 648c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port); 658c2ecf20Sopenharmony_ci int (*enable)(struct mlxsw_sp_span_trigger_entry *trigger_entry, 668c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, u8 tc); 678c2ecf20Sopenharmony_ci void (*disable)(struct mlxsw_sp_span_trigger_entry *trigger_entry, 688c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, u8 tc); 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void mlxsw_sp_span_respin_work(struct work_struct *work); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic u64 mlxsw_sp_span_occ_get(void *priv) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci const struct mlxsw_sp *mlxsw_sp = priv; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return atomic_read(&mlxsw_sp->span->active_entries_count); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ciint mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 838c2ecf20Sopenharmony_ci struct mlxsw_sp_span *span; 848c2ecf20Sopenharmony_ci int i, entries_count, err; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_SPAN)) 878c2ecf20Sopenharmony_ci return -EIO; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci entries_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_SPAN); 908c2ecf20Sopenharmony_ci span = kzalloc(struct_size(span, entries, entries_count), GFP_KERNEL); 918c2ecf20Sopenharmony_ci if (!span) 928c2ecf20Sopenharmony_ci return -ENOMEM; 938c2ecf20Sopenharmony_ci refcount_set(&span->policer_id_base_ref_count, 0); 948c2ecf20Sopenharmony_ci span->entries_count = entries_count; 958c2ecf20Sopenharmony_ci atomic_set(&span->active_entries_count, 0); 968c2ecf20Sopenharmony_ci mutex_init(&span->analyzed_ports_lock); 978c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&span->analyzed_ports_list); 988c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&span->trigger_entries_list); 998c2ecf20Sopenharmony_ci span->mlxsw_sp = mlxsw_sp; 1008c2ecf20Sopenharmony_ci mlxsw_sp->span = span; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci for (i = 0; i < mlxsw_sp->span->entries_count; i++) 1038c2ecf20Sopenharmony_ci mlxsw_sp->span->entries[i].id = i; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci err = mlxsw_sp->span_ops->init(mlxsw_sp); 1068c2ecf20Sopenharmony_ci if (err) 1078c2ecf20Sopenharmony_ci goto err_init; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci devlink_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_SPAN, 1108c2ecf20Sopenharmony_ci mlxsw_sp_span_occ_get, mlxsw_sp); 1118c2ecf20Sopenharmony_ci INIT_WORK(&span->work, mlxsw_sp_span_respin_work); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cierr_init: 1168c2ecf20Sopenharmony_ci mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock); 1178c2ecf20Sopenharmony_ci kfree(mlxsw_sp->span); 1188c2ecf20Sopenharmony_ci return err; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_civoid mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci cancel_work_sync(&mlxsw_sp->span->work); 1268c2ecf20Sopenharmony_ci devlink_resource_occ_get_unregister(devlink, MLXSW_SP_RESOURCE_SPAN); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->trigger_entries_list)); 1298c2ecf20Sopenharmony_ci WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->analyzed_ports_list)); 1308c2ecf20Sopenharmony_ci mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock); 1318c2ecf20Sopenharmony_ci kfree(mlxsw_sp->span); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic bool mlxsw_sp1_span_cpu_can_handle(const struct net_device *dev) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci return !dev; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int mlxsw_sp1_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp, 1408c2ecf20Sopenharmony_ci const struct net_device *to_dev, 1418c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms *sparmsp) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int 1478c2ecf20Sopenharmony_cimlxsw_sp1_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry, 1488c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void 1548c2ecf20Sopenharmony_cimlxsw_sp1_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const 1598c2ecf20Sopenharmony_cistruct mlxsw_sp_span_entry_ops mlxsw_sp1_span_entry_ops_cpu = { 1608c2ecf20Sopenharmony_ci .is_static = true, 1618c2ecf20Sopenharmony_ci .can_handle = mlxsw_sp1_span_cpu_can_handle, 1628c2ecf20Sopenharmony_ci .parms_set = mlxsw_sp1_span_entry_cpu_parms, 1638c2ecf20Sopenharmony_ci .configure = mlxsw_sp1_span_entry_cpu_configure, 1648c2ecf20Sopenharmony_ci .deconfigure = mlxsw_sp1_span_entry_cpu_deconfigure, 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int 1688c2ecf20Sopenharmony_cimlxsw_sp_span_entry_phys_parms(struct mlxsw_sp *mlxsw_sp, 1698c2ecf20Sopenharmony_ci const struct net_device *to_dev, 1708c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms *sparmsp) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci sparmsp->dest_port = netdev_priv(to_dev); 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int 1778c2ecf20Sopenharmony_cimlxsw_sp_span_entry_phys_configure(struct mlxsw_sp_span_entry *span_entry, 1788c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct mlxsw_sp_port *dest_port = sparms.dest_port; 1818c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp; 1828c2ecf20Sopenharmony_ci u8 local_port = dest_port->local_port; 1838c2ecf20Sopenharmony_ci char mpat_pl[MLXSW_REG_MPAT_LEN]; 1848c2ecf20Sopenharmony_ci int pa_id = span_entry->id; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Create a new port analayzer entry for local_port. */ 1878c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true, 1888c2ecf20Sopenharmony_ci MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH); 1898c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable); 1908c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void 1968c2ecf20Sopenharmony_cimlxsw_sp_span_entry_deconfigure_common(struct mlxsw_sp_span_entry *span_entry, 1978c2ecf20Sopenharmony_ci enum mlxsw_reg_mpat_span_type span_type) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct mlxsw_sp_port *dest_port = span_entry->parms.dest_port; 2008c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp; 2018c2ecf20Sopenharmony_ci u8 local_port = dest_port->local_port; 2028c2ecf20Sopenharmony_ci char mpat_pl[MLXSW_REG_MPAT_LEN]; 2038c2ecf20Sopenharmony_ci int pa_id = span_entry->id; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, false, span_type); 2068c2ecf20Sopenharmony_ci mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void 2108c2ecf20Sopenharmony_cimlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry *span_entry) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_deconfigure_common(span_entry, 2138c2ecf20Sopenharmony_ci MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic const 2178c2ecf20Sopenharmony_cistruct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = { 2188c2ecf20Sopenharmony_ci .is_static = true, 2198c2ecf20Sopenharmony_ci .can_handle = mlxsw_sp_port_dev_check, 2208c2ecf20Sopenharmony_ci .parms_set = mlxsw_sp_span_entry_phys_parms, 2218c2ecf20Sopenharmony_ci .configure = mlxsw_sp_span_entry_phys_configure, 2228c2ecf20Sopenharmony_ci .deconfigure = mlxsw_sp_span_entry_phys_deconfigure, 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int mlxsw_sp_span_dmac(struct neigh_table *tbl, 2268c2ecf20Sopenharmony_ci const void *pkey, 2278c2ecf20Sopenharmony_ci struct net_device *dev, 2288c2ecf20Sopenharmony_ci unsigned char dmac[ETH_ALEN]) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct neighbour *neigh = neigh_lookup(tbl, pkey, dev); 2318c2ecf20Sopenharmony_ci int err = 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (!neigh) { 2348c2ecf20Sopenharmony_ci neigh = neigh_create(tbl, pkey, dev); 2358c2ecf20Sopenharmony_ci if (IS_ERR(neigh)) 2368c2ecf20Sopenharmony_ci return PTR_ERR(neigh); 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci neigh_event_send(neigh, NULL); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci read_lock_bh(&neigh->lock); 2428c2ecf20Sopenharmony_ci if ((neigh->nud_state & NUD_VALID) && !neigh->dead) 2438c2ecf20Sopenharmony_ci memcpy(dmac, neigh->ha, ETH_ALEN); 2448c2ecf20Sopenharmony_ci else 2458c2ecf20Sopenharmony_ci err = -ENOENT; 2468c2ecf20Sopenharmony_ci read_unlock_bh(&neigh->lock); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci neigh_release(neigh); 2498c2ecf20Sopenharmony_ci return err; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int 2538c2ecf20Sopenharmony_cimlxsw_sp_span_entry_unoffloadable(struct mlxsw_sp_span_parms *sparmsp) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci sparmsp->dest_port = NULL; 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic struct net_device * 2608c2ecf20Sopenharmony_cimlxsw_sp_span_entry_bridge_8021q(const struct net_device *br_dev, 2618c2ecf20Sopenharmony_ci unsigned char *dmac, 2628c2ecf20Sopenharmony_ci u16 *p_vid) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct bridge_vlan_info vinfo; 2658c2ecf20Sopenharmony_ci struct net_device *edev; 2668c2ecf20Sopenharmony_ci u16 vid = *p_vid; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (!vid && WARN_ON(br_vlan_get_pvid(br_dev, &vid))) 2698c2ecf20Sopenharmony_ci return NULL; 2708c2ecf20Sopenharmony_ci if (!vid || 2718c2ecf20Sopenharmony_ci br_vlan_get_info(br_dev, vid, &vinfo) || 2728c2ecf20Sopenharmony_ci !(vinfo.flags & BRIDGE_VLAN_INFO_BRENTRY)) 2738c2ecf20Sopenharmony_ci return NULL; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci edev = br_fdb_find_port(br_dev, dmac, vid); 2768c2ecf20Sopenharmony_ci if (!edev) 2778c2ecf20Sopenharmony_ci return NULL; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (br_vlan_get_info(edev, vid, &vinfo)) 2808c2ecf20Sopenharmony_ci return NULL; 2818c2ecf20Sopenharmony_ci if (vinfo.flags & BRIDGE_VLAN_INFO_UNTAGGED) 2828c2ecf20Sopenharmony_ci *p_vid = 0; 2838c2ecf20Sopenharmony_ci else 2848c2ecf20Sopenharmony_ci *p_vid = vid; 2858c2ecf20Sopenharmony_ci return edev; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic struct net_device * 2898c2ecf20Sopenharmony_cimlxsw_sp_span_entry_bridge_8021d(const struct net_device *br_dev, 2908c2ecf20Sopenharmony_ci unsigned char *dmac) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci return br_fdb_find_port(br_dev, dmac, 0); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic struct net_device * 2968c2ecf20Sopenharmony_cimlxsw_sp_span_entry_bridge(const struct net_device *br_dev, 2978c2ecf20Sopenharmony_ci unsigned char dmac[ETH_ALEN], 2988c2ecf20Sopenharmony_ci u16 *p_vid) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct mlxsw_sp_bridge_port *bridge_port; 3018c2ecf20Sopenharmony_ci enum mlxsw_reg_spms_state spms_state; 3028c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 3038c2ecf20Sopenharmony_ci struct mlxsw_sp_port *port; 3048c2ecf20Sopenharmony_ci u8 stp_state; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (br_vlan_enabled(br_dev)) 3078c2ecf20Sopenharmony_ci dev = mlxsw_sp_span_entry_bridge_8021q(br_dev, dmac, p_vid); 3088c2ecf20Sopenharmony_ci else if (!*p_vid) 3098c2ecf20Sopenharmony_ci dev = mlxsw_sp_span_entry_bridge_8021d(br_dev, dmac); 3108c2ecf20Sopenharmony_ci if (!dev) 3118c2ecf20Sopenharmony_ci return NULL; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci port = mlxsw_sp_port_dev_lower_find(dev); 3148c2ecf20Sopenharmony_ci if (!port) 3158c2ecf20Sopenharmony_ci return NULL; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci bridge_port = mlxsw_sp_bridge_port_find(port->mlxsw_sp->bridge, dev); 3188c2ecf20Sopenharmony_ci if (!bridge_port) 3198c2ecf20Sopenharmony_ci return NULL; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci stp_state = mlxsw_sp_bridge_port_stp_state(bridge_port); 3228c2ecf20Sopenharmony_ci spms_state = mlxsw_sp_stp_spms_state(stp_state); 3238c2ecf20Sopenharmony_ci if (spms_state != MLXSW_REG_SPMS_STATE_FORWARDING) 3248c2ecf20Sopenharmony_ci return NULL; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return dev; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic struct net_device * 3308c2ecf20Sopenharmony_cimlxsw_sp_span_entry_vlan(const struct net_device *vlan_dev, 3318c2ecf20Sopenharmony_ci u16 *p_vid) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci *p_vid = vlan_dev_vlan_id(vlan_dev); 3348c2ecf20Sopenharmony_ci return vlan_dev_real_dev(vlan_dev); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic struct net_device * 3388c2ecf20Sopenharmony_cimlxsw_sp_span_entry_lag(struct net_device *lag_dev) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct net_device *dev; 3418c2ecf20Sopenharmony_ci struct list_head *iter; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci netdev_for_each_lower_dev(lag_dev, dev, iter) 3448c2ecf20Sopenharmony_ci if (netif_carrier_ok(dev) && 3458c2ecf20Sopenharmony_ci net_lag_port_dev_txable(dev) && 3468c2ecf20Sopenharmony_ci mlxsw_sp_port_dev_check(dev)) 3478c2ecf20Sopenharmony_ci return dev; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return NULL; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic __maybe_unused int 3538c2ecf20Sopenharmony_cimlxsw_sp_span_entry_tunnel_parms_common(struct net_device *edev, 3548c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr saddr, 3558c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr daddr, 3568c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr gw, 3578c2ecf20Sopenharmony_ci __u8 ttl, 3588c2ecf20Sopenharmony_ci struct neigh_table *tbl, 3598c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms *sparmsp) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci unsigned char dmac[ETH_ALEN]; 3628c2ecf20Sopenharmony_ci u16 vid = 0; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (mlxsw_sp_l3addr_is_zero(gw)) 3658c2ecf20Sopenharmony_ci gw = daddr; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (!edev || mlxsw_sp_span_dmac(tbl, &gw, edev, dmac)) 3688c2ecf20Sopenharmony_ci goto unoffloadable; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (is_vlan_dev(edev)) 3718c2ecf20Sopenharmony_ci edev = mlxsw_sp_span_entry_vlan(edev, &vid); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (netif_is_bridge_master(edev)) { 3748c2ecf20Sopenharmony_ci edev = mlxsw_sp_span_entry_bridge(edev, dmac, &vid); 3758c2ecf20Sopenharmony_ci if (!edev) 3768c2ecf20Sopenharmony_ci goto unoffloadable; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (is_vlan_dev(edev)) { 3808c2ecf20Sopenharmony_ci if (vid || !(edev->flags & IFF_UP)) 3818c2ecf20Sopenharmony_ci goto unoffloadable; 3828c2ecf20Sopenharmony_ci edev = mlxsw_sp_span_entry_vlan(edev, &vid); 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (netif_is_lag_master(edev)) { 3868c2ecf20Sopenharmony_ci if (!(edev->flags & IFF_UP)) 3878c2ecf20Sopenharmony_ci goto unoffloadable; 3888c2ecf20Sopenharmony_ci edev = mlxsw_sp_span_entry_lag(edev); 3898c2ecf20Sopenharmony_ci if (!edev) 3908c2ecf20Sopenharmony_ci goto unoffloadable; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (!mlxsw_sp_port_dev_check(edev)) 3948c2ecf20Sopenharmony_ci goto unoffloadable; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci sparmsp->dest_port = netdev_priv(edev); 3978c2ecf20Sopenharmony_ci sparmsp->ttl = ttl; 3988c2ecf20Sopenharmony_ci memcpy(sparmsp->dmac, dmac, ETH_ALEN); 3998c2ecf20Sopenharmony_ci memcpy(sparmsp->smac, edev->dev_addr, ETH_ALEN); 4008c2ecf20Sopenharmony_ci sparmsp->saddr = saddr; 4018c2ecf20Sopenharmony_ci sparmsp->daddr = daddr; 4028c2ecf20Sopenharmony_ci sparmsp->vid = vid; 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ciunoffloadable: 4068c2ecf20Sopenharmony_ci return mlxsw_sp_span_entry_unoffloadable(sparmsp); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE) 4108c2ecf20Sopenharmony_cistatic struct net_device * 4118c2ecf20Sopenharmony_cimlxsw_sp_span_gretap4_route(const struct net_device *to_dev, 4128c2ecf20Sopenharmony_ci __be32 *saddrp, __be32 *daddrp) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct ip_tunnel *tun = netdev_priv(to_dev); 4158c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 4168c2ecf20Sopenharmony_ci struct ip_tunnel_parm parms; 4178c2ecf20Sopenharmony_ci struct rtable *rt = NULL; 4188c2ecf20Sopenharmony_ci struct flowi4 fl4; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* We assume "dev" stays valid after rt is put. */ 4218c2ecf20Sopenharmony_ci ASSERT_RTNL(); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci parms = mlxsw_sp_ipip_netdev_parms4(to_dev); 4248c2ecf20Sopenharmony_ci ip_tunnel_init_flow(&fl4, parms.iph.protocol, *daddrp, *saddrp, 4258c2ecf20Sopenharmony_ci 0, 0, parms.link, tun->fwmark, 0); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci rt = ip_route_output_key(tun->net, &fl4); 4288c2ecf20Sopenharmony_ci if (IS_ERR(rt)) 4298c2ecf20Sopenharmony_ci return NULL; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (rt->rt_type != RTN_UNICAST) 4328c2ecf20Sopenharmony_ci goto out; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci dev = rt->dst.dev; 4358c2ecf20Sopenharmony_ci *saddrp = fl4.saddr; 4368c2ecf20Sopenharmony_ci if (rt->rt_gw_family == AF_INET) 4378c2ecf20Sopenharmony_ci *daddrp = rt->rt_gw4; 4388c2ecf20Sopenharmony_ci /* can not offload if route has an IPv6 gateway */ 4398c2ecf20Sopenharmony_ci else if (rt->rt_gw_family == AF_INET6) 4408c2ecf20Sopenharmony_ci dev = NULL; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ciout: 4438c2ecf20Sopenharmony_ci ip_rt_put(rt); 4448c2ecf20Sopenharmony_ci return dev; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int 4488c2ecf20Sopenharmony_cimlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp, 4498c2ecf20Sopenharmony_ci const struct net_device *to_dev, 4508c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms *sparmsp) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct ip_tunnel_parm tparm = mlxsw_sp_ipip_netdev_parms4(to_dev); 4538c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr saddr = { .addr4 = tparm.iph.saddr }; 4548c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr daddr = { .addr4 = tparm.iph.daddr }; 4558c2ecf20Sopenharmony_ci bool inherit_tos = tparm.iph.tos & 0x1; 4568c2ecf20Sopenharmony_ci bool inherit_ttl = !tparm.iph.ttl; 4578c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr gw = daddr; 4588c2ecf20Sopenharmony_ci struct net_device *l3edev; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (!(to_dev->flags & IFF_UP) || 4618c2ecf20Sopenharmony_ci /* Reject tunnels with GRE keys, checksums, etc. */ 4628c2ecf20Sopenharmony_ci tparm.i_flags || tparm.o_flags || 4638c2ecf20Sopenharmony_ci /* Require a fixed TTL and a TOS copied from the mirrored packet. */ 4648c2ecf20Sopenharmony_ci inherit_ttl || !inherit_tos || 4658c2ecf20Sopenharmony_ci /* A destination address may not be "any". */ 4668c2ecf20Sopenharmony_ci mlxsw_sp_l3addr_is_zero(daddr)) 4678c2ecf20Sopenharmony_ci return mlxsw_sp_span_entry_unoffloadable(sparmsp); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci l3edev = mlxsw_sp_span_gretap4_route(to_dev, &saddr.addr4, &gw.addr4); 4708c2ecf20Sopenharmony_ci return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw, 4718c2ecf20Sopenharmony_ci tparm.iph.ttl, 4728c2ecf20Sopenharmony_ci &arp_tbl, sparmsp); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int 4768c2ecf20Sopenharmony_cimlxsw_sp_span_entry_gretap4_configure(struct mlxsw_sp_span_entry *span_entry, 4778c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct mlxsw_sp_port *dest_port = sparms.dest_port; 4808c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp; 4818c2ecf20Sopenharmony_ci u8 local_port = dest_port->local_port; 4828c2ecf20Sopenharmony_ci char mpat_pl[MLXSW_REG_MPAT_LEN]; 4838c2ecf20Sopenharmony_ci int pa_id = span_entry->id; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Create a new port analayzer entry for local_port. */ 4868c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true, 4878c2ecf20Sopenharmony_ci MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3); 4888c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable); 4898c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id); 4908c2ecf20Sopenharmony_ci mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid); 4918c2ecf20Sopenharmony_ci mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl, 4928c2ecf20Sopenharmony_ci MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER, 4938c2ecf20Sopenharmony_ci sparms.dmac, !!sparms.vid); 4948c2ecf20Sopenharmony_ci mlxsw_reg_mpat_eth_rspan_l3_ipv4_pack(mpat_pl, 4958c2ecf20Sopenharmony_ci sparms.ttl, sparms.smac, 4968c2ecf20Sopenharmony_ci be32_to_cpu(sparms.saddr.addr4), 4978c2ecf20Sopenharmony_ci be32_to_cpu(sparms.daddr.addr4)); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic void 5038c2ecf20Sopenharmony_cimlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry *span_entry) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_deconfigure_common(span_entry, 5068c2ecf20Sopenharmony_ci MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3); 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap4 = { 5108c2ecf20Sopenharmony_ci .can_handle = netif_is_gretap, 5118c2ecf20Sopenharmony_ci .parms_set = mlxsw_sp_span_entry_gretap4_parms, 5128c2ecf20Sopenharmony_ci .configure = mlxsw_sp_span_entry_gretap4_configure, 5138c2ecf20Sopenharmony_ci .deconfigure = mlxsw_sp_span_entry_gretap4_deconfigure, 5148c2ecf20Sopenharmony_ci}; 5158c2ecf20Sopenharmony_ci#endif 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_GRE) 5188c2ecf20Sopenharmony_cistatic struct net_device * 5198c2ecf20Sopenharmony_cimlxsw_sp_span_gretap6_route(const struct net_device *to_dev, 5208c2ecf20Sopenharmony_ci struct in6_addr *saddrp, 5218c2ecf20Sopenharmony_ci struct in6_addr *daddrp) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct ip6_tnl *t = netdev_priv(to_dev); 5248c2ecf20Sopenharmony_ci struct flowi6 fl6 = t->fl.u.ip6; 5258c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 5268c2ecf20Sopenharmony_ci struct dst_entry *dst; 5278c2ecf20Sopenharmony_ci struct rt6_info *rt6; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* We assume "dev" stays valid after dst is released. */ 5308c2ecf20Sopenharmony_ci ASSERT_RTNL(); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci fl6.flowi6_mark = t->parms.fwmark; 5338c2ecf20Sopenharmony_ci if (!ip6_tnl_xmit_ctl(t, &fl6.saddr, &fl6.daddr)) 5348c2ecf20Sopenharmony_ci return NULL; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci dst = ip6_route_output(t->net, NULL, &fl6); 5378c2ecf20Sopenharmony_ci if (!dst || dst->error) 5388c2ecf20Sopenharmony_ci goto out; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci rt6 = container_of(dst, struct rt6_info, dst); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci dev = dst->dev; 5438c2ecf20Sopenharmony_ci *saddrp = fl6.saddr; 5448c2ecf20Sopenharmony_ci *daddrp = rt6->rt6i_gateway; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ciout: 5478c2ecf20Sopenharmony_ci dst_release(dst); 5488c2ecf20Sopenharmony_ci return dev; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic int 5528c2ecf20Sopenharmony_cimlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp, 5538c2ecf20Sopenharmony_ci const struct net_device *to_dev, 5548c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms *sparmsp) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(to_dev); 5578c2ecf20Sopenharmony_ci bool inherit_tos = tparm.flags & IP6_TNL_F_USE_ORIG_TCLASS; 5588c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr saddr = { .addr6 = tparm.laddr }; 5598c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr daddr = { .addr6 = tparm.raddr }; 5608c2ecf20Sopenharmony_ci bool inherit_ttl = !tparm.hop_limit; 5618c2ecf20Sopenharmony_ci union mlxsw_sp_l3addr gw = daddr; 5628c2ecf20Sopenharmony_ci struct net_device *l3edev; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (!(to_dev->flags & IFF_UP) || 5658c2ecf20Sopenharmony_ci /* Reject tunnels with GRE keys, checksums, etc. */ 5668c2ecf20Sopenharmony_ci tparm.i_flags || tparm.o_flags || 5678c2ecf20Sopenharmony_ci /* Require a fixed TTL and a TOS copied from the mirrored packet. */ 5688c2ecf20Sopenharmony_ci inherit_ttl || !inherit_tos || 5698c2ecf20Sopenharmony_ci /* A destination address may not be "any". */ 5708c2ecf20Sopenharmony_ci mlxsw_sp_l3addr_is_zero(daddr)) 5718c2ecf20Sopenharmony_ci return mlxsw_sp_span_entry_unoffloadable(sparmsp); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci l3edev = mlxsw_sp_span_gretap6_route(to_dev, &saddr.addr6, &gw.addr6); 5748c2ecf20Sopenharmony_ci return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw, 5758c2ecf20Sopenharmony_ci tparm.hop_limit, 5768c2ecf20Sopenharmony_ci &nd_tbl, sparmsp); 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int 5808c2ecf20Sopenharmony_cimlxsw_sp_span_entry_gretap6_configure(struct mlxsw_sp_span_entry *span_entry, 5818c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci struct mlxsw_sp_port *dest_port = sparms.dest_port; 5848c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp; 5858c2ecf20Sopenharmony_ci u8 local_port = dest_port->local_port; 5868c2ecf20Sopenharmony_ci char mpat_pl[MLXSW_REG_MPAT_LEN]; 5878c2ecf20Sopenharmony_ci int pa_id = span_entry->id; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* Create a new port analayzer entry for local_port. */ 5908c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true, 5918c2ecf20Sopenharmony_ci MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3); 5928c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable); 5938c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id); 5948c2ecf20Sopenharmony_ci mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid); 5958c2ecf20Sopenharmony_ci mlxsw_reg_mpat_eth_rspan_l2_pack(mpat_pl, 5968c2ecf20Sopenharmony_ci MLXSW_REG_MPAT_ETH_RSPAN_VERSION_NO_HEADER, 5978c2ecf20Sopenharmony_ci sparms.dmac, !!sparms.vid); 5988c2ecf20Sopenharmony_ci mlxsw_reg_mpat_eth_rspan_l3_ipv6_pack(mpat_pl, sparms.ttl, sparms.smac, 5998c2ecf20Sopenharmony_ci sparms.saddr.addr6, 6008c2ecf20Sopenharmony_ci sparms.daddr.addr6); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl); 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic void 6068c2ecf20Sopenharmony_cimlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry *span_entry) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_deconfigure_common(span_entry, 6098c2ecf20Sopenharmony_ci MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH_L3); 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic const 6138c2ecf20Sopenharmony_cistruct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap6 = { 6148c2ecf20Sopenharmony_ci .can_handle = netif_is_ip6gretap, 6158c2ecf20Sopenharmony_ci .parms_set = mlxsw_sp_span_entry_gretap6_parms, 6168c2ecf20Sopenharmony_ci .configure = mlxsw_sp_span_entry_gretap6_configure, 6178c2ecf20Sopenharmony_ci .deconfigure = mlxsw_sp_span_entry_gretap6_deconfigure, 6188c2ecf20Sopenharmony_ci}; 6198c2ecf20Sopenharmony_ci#endif 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic bool 6228c2ecf20Sopenharmony_cimlxsw_sp_span_vlan_can_handle(const struct net_device *dev) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci return is_vlan_dev(dev) && 6258c2ecf20Sopenharmony_ci mlxsw_sp_port_dev_check(vlan_dev_real_dev(dev)); 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic int 6298c2ecf20Sopenharmony_cimlxsw_sp_span_entry_vlan_parms(struct mlxsw_sp *mlxsw_sp, 6308c2ecf20Sopenharmony_ci const struct net_device *to_dev, 6318c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms *sparmsp) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct net_device *real_dev; 6348c2ecf20Sopenharmony_ci u16 vid; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (!(to_dev->flags & IFF_UP)) 6378c2ecf20Sopenharmony_ci return mlxsw_sp_span_entry_unoffloadable(sparmsp); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci real_dev = mlxsw_sp_span_entry_vlan(to_dev, &vid); 6408c2ecf20Sopenharmony_ci sparmsp->dest_port = netdev_priv(real_dev); 6418c2ecf20Sopenharmony_ci sparmsp->vid = vid; 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic int 6468c2ecf20Sopenharmony_cimlxsw_sp_span_entry_vlan_configure(struct mlxsw_sp_span_entry *span_entry, 6478c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct mlxsw_sp_port *dest_port = sparms.dest_port; 6508c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = dest_port->mlxsw_sp; 6518c2ecf20Sopenharmony_ci u8 local_port = dest_port->local_port; 6528c2ecf20Sopenharmony_ci char mpat_pl[MLXSW_REG_MPAT_LEN]; 6538c2ecf20Sopenharmony_ci int pa_id = span_entry->id; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pack(mpat_pl, pa_id, local_port, true, 6568c2ecf20Sopenharmony_ci MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH); 6578c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pide_set(mpat_pl, sparms.policer_enable); 6588c2ecf20Sopenharmony_ci mlxsw_reg_mpat_pid_set(mpat_pl, sparms.policer_id); 6598c2ecf20Sopenharmony_ci mlxsw_reg_mpat_eth_rspan_pack(mpat_pl, sparms.vid); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpat), mpat_pl); 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic void 6658c2ecf20Sopenharmony_cimlxsw_sp_span_entry_vlan_deconfigure(struct mlxsw_sp_span_entry *span_entry) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_deconfigure_common(span_entry, 6688c2ecf20Sopenharmony_ci MLXSW_REG_MPAT_SPAN_TYPE_REMOTE_ETH); 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic const 6728c2ecf20Sopenharmony_cistruct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_vlan = { 6738c2ecf20Sopenharmony_ci .can_handle = mlxsw_sp_span_vlan_can_handle, 6748c2ecf20Sopenharmony_ci .parms_set = mlxsw_sp_span_entry_vlan_parms, 6758c2ecf20Sopenharmony_ci .configure = mlxsw_sp_span_entry_vlan_configure, 6768c2ecf20Sopenharmony_ci .deconfigure = mlxsw_sp_span_entry_vlan_deconfigure, 6778c2ecf20Sopenharmony_ci}; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic const 6808c2ecf20Sopenharmony_cistruct mlxsw_sp_span_entry_ops *mlxsw_sp1_span_entry_ops_arr[] = { 6818c2ecf20Sopenharmony_ci &mlxsw_sp1_span_entry_ops_cpu, 6828c2ecf20Sopenharmony_ci &mlxsw_sp_span_entry_ops_phys, 6838c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE) 6848c2ecf20Sopenharmony_ci &mlxsw_sp_span_entry_ops_gretap4, 6858c2ecf20Sopenharmony_ci#endif 6868c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_GRE) 6878c2ecf20Sopenharmony_ci &mlxsw_sp_span_entry_ops_gretap6, 6888c2ecf20Sopenharmony_ci#endif 6898c2ecf20Sopenharmony_ci &mlxsw_sp_span_entry_ops_vlan, 6908c2ecf20Sopenharmony_ci}; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic bool mlxsw_sp2_span_cpu_can_handle(const struct net_device *dev) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci return !dev; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic int mlxsw_sp2_span_entry_cpu_parms(struct mlxsw_sp *mlxsw_sp, 6988c2ecf20Sopenharmony_ci const struct net_device *to_dev, 6998c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms *sparmsp) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci sparmsp->dest_port = mlxsw_sp->ports[MLXSW_PORT_CPU_PORT]; 7028c2ecf20Sopenharmony_ci return 0; 7038c2ecf20Sopenharmony_ci} 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic int 7068c2ecf20Sopenharmony_cimlxsw_sp2_span_entry_cpu_configure(struct mlxsw_sp_span_entry *span_entry, 7078c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci /* Mirroring to the CPU port is like mirroring to any other physical 7108c2ecf20Sopenharmony_ci * port. Its local port is used instead of that of the physical port. 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_ci return mlxsw_sp_span_entry_phys_configure(span_entry, sparms); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic void 7168c2ecf20Sopenharmony_cimlxsw_sp2_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci enum mlxsw_reg_mpat_span_type span_type; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci span_type = MLXSW_REG_MPAT_SPAN_TYPE_LOCAL_ETH; 7218c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_deconfigure_common(span_entry, span_type); 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic const 7258c2ecf20Sopenharmony_cistruct mlxsw_sp_span_entry_ops mlxsw_sp2_span_entry_ops_cpu = { 7268c2ecf20Sopenharmony_ci .is_static = true, 7278c2ecf20Sopenharmony_ci .can_handle = mlxsw_sp2_span_cpu_can_handle, 7288c2ecf20Sopenharmony_ci .parms_set = mlxsw_sp2_span_entry_cpu_parms, 7298c2ecf20Sopenharmony_ci .configure = mlxsw_sp2_span_entry_cpu_configure, 7308c2ecf20Sopenharmony_ci .deconfigure = mlxsw_sp2_span_entry_cpu_deconfigure, 7318c2ecf20Sopenharmony_ci}; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic const 7348c2ecf20Sopenharmony_cistruct mlxsw_sp_span_entry_ops *mlxsw_sp2_span_entry_ops_arr[] = { 7358c2ecf20Sopenharmony_ci &mlxsw_sp2_span_entry_ops_cpu, 7368c2ecf20Sopenharmony_ci &mlxsw_sp_span_entry_ops_phys, 7378c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE) 7388c2ecf20Sopenharmony_ci &mlxsw_sp_span_entry_ops_gretap4, 7398c2ecf20Sopenharmony_ci#endif 7408c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_GRE) 7418c2ecf20Sopenharmony_ci &mlxsw_sp_span_entry_ops_gretap6, 7428c2ecf20Sopenharmony_ci#endif 7438c2ecf20Sopenharmony_ci &mlxsw_sp_span_entry_ops_vlan, 7448c2ecf20Sopenharmony_ci}; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic int 7478c2ecf20Sopenharmony_cimlxsw_sp_span_entry_nop_parms(struct mlxsw_sp *mlxsw_sp, 7488c2ecf20Sopenharmony_ci const struct net_device *to_dev, 7498c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms *sparmsp) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci return mlxsw_sp_span_entry_unoffloadable(sparmsp); 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic int 7558c2ecf20Sopenharmony_cimlxsw_sp_span_entry_nop_configure(struct mlxsw_sp_span_entry *span_entry, 7568c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci return 0; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic void 7628c2ecf20Sopenharmony_cimlxsw_sp_span_entry_nop_deconfigure(struct mlxsw_sp_span_entry *span_entry) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_nop = { 7678c2ecf20Sopenharmony_ci .parms_set = mlxsw_sp_span_entry_nop_parms, 7688c2ecf20Sopenharmony_ci .configure = mlxsw_sp_span_entry_nop_configure, 7698c2ecf20Sopenharmony_ci .deconfigure = mlxsw_sp_span_entry_nop_deconfigure, 7708c2ecf20Sopenharmony_ci}; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic void 7738c2ecf20Sopenharmony_cimlxsw_sp_span_entry_configure(struct mlxsw_sp *mlxsw_sp, 7748c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *span_entry, 7758c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci int err; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (!sparms.dest_port) 7808c2ecf20Sopenharmony_ci goto set_parms; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (sparms.dest_port->mlxsw_sp != mlxsw_sp) { 7838c2ecf20Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, 7848c2ecf20Sopenharmony_ci "Cannot mirror to a port which belongs to a different mlxsw instance\n"); 7858c2ecf20Sopenharmony_ci sparms.dest_port = NULL; 7868c2ecf20Sopenharmony_ci goto set_parms; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci err = span_entry->ops->configure(span_entry, sparms); 7908c2ecf20Sopenharmony_ci if (err) { 7918c2ecf20Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, "Failed to offload mirror\n"); 7928c2ecf20Sopenharmony_ci sparms.dest_port = NULL; 7938c2ecf20Sopenharmony_ci goto set_parms; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ciset_parms: 7978c2ecf20Sopenharmony_ci span_entry->parms = sparms; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic void 8018c2ecf20Sopenharmony_cimlxsw_sp_span_entry_deconfigure(struct mlxsw_sp_span_entry *span_entry) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci if (span_entry->parms.dest_port) 8048c2ecf20Sopenharmony_ci span_entry->ops->deconfigure(span_entry); 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic int mlxsw_sp_span_policer_id_base_set(struct mlxsw_sp_span *span, 8088c2ecf20Sopenharmony_ci u16 policer_id) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = span->mlxsw_sp; 8118c2ecf20Sopenharmony_ci u16 policer_id_base; 8128c2ecf20Sopenharmony_ci int err; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* Policers set on SPAN agents must be in the range of 8158c2ecf20Sopenharmony_ci * `policer_id_base .. policer_id_base + max_span_agents - 1`. If the 8168c2ecf20Sopenharmony_ci * base is set and the new policer is not within the range, then we 8178c2ecf20Sopenharmony_ci * must error out. 8188c2ecf20Sopenharmony_ci */ 8198c2ecf20Sopenharmony_ci if (refcount_read(&span->policer_id_base_ref_count)) { 8208c2ecf20Sopenharmony_ci if (policer_id < span->policer_id_base || 8218c2ecf20Sopenharmony_ci policer_id >= span->policer_id_base + span->entries_count) 8228c2ecf20Sopenharmony_ci return -EINVAL; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci refcount_inc(&span->policer_id_base_ref_count); 8258c2ecf20Sopenharmony_ci return 0; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* Base must be even. */ 8298c2ecf20Sopenharmony_ci policer_id_base = policer_id % 2 == 0 ? policer_id : policer_id - 1; 8308c2ecf20Sopenharmony_ci err = mlxsw_sp->span_ops->policer_id_base_set(mlxsw_sp, 8318c2ecf20Sopenharmony_ci policer_id_base); 8328c2ecf20Sopenharmony_ci if (err) 8338c2ecf20Sopenharmony_ci return err; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci span->policer_id_base = policer_id_base; 8368c2ecf20Sopenharmony_ci refcount_set(&span->policer_id_base_ref_count, 1); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci return 0; 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic void mlxsw_sp_span_policer_id_base_unset(struct mlxsw_sp_span *span) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&span->policer_id_base_ref_count)) 8448c2ecf20Sopenharmony_ci span->policer_id_base = 0; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic struct mlxsw_sp_span_entry * 8488c2ecf20Sopenharmony_cimlxsw_sp_span_entry_create(struct mlxsw_sp *mlxsw_sp, 8498c2ecf20Sopenharmony_ci const struct net_device *to_dev, 8508c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_entry_ops *ops, 8518c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *span_entry = NULL; 8548c2ecf20Sopenharmony_ci int i; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* find a free entry to use */ 8578c2ecf20Sopenharmony_ci for (i = 0; i < mlxsw_sp->span->entries_count; i++) { 8588c2ecf20Sopenharmony_ci if (!refcount_read(&mlxsw_sp->span->entries[i].ref_count)) { 8598c2ecf20Sopenharmony_ci span_entry = &mlxsw_sp->span->entries[i]; 8608c2ecf20Sopenharmony_ci break; 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci if (!span_entry) 8648c2ecf20Sopenharmony_ci return NULL; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci if (sparms.policer_enable) { 8678c2ecf20Sopenharmony_ci int err; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci err = mlxsw_sp_span_policer_id_base_set(mlxsw_sp->span, 8708c2ecf20Sopenharmony_ci sparms.policer_id); 8718c2ecf20Sopenharmony_ci if (err) 8728c2ecf20Sopenharmony_ci return NULL; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci atomic_inc(&mlxsw_sp->span->active_entries_count); 8768c2ecf20Sopenharmony_ci span_entry->ops = ops; 8778c2ecf20Sopenharmony_ci refcount_set(&span_entry->ref_count, 1); 8788c2ecf20Sopenharmony_ci span_entry->to_dev = to_dev; 8798c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_configure(mlxsw_sp, span_entry, sparms); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci return span_entry; 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic void mlxsw_sp_span_entry_destroy(struct mlxsw_sp *mlxsw_sp, 8858c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *span_entry) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_deconfigure(span_entry); 8888c2ecf20Sopenharmony_ci atomic_dec(&mlxsw_sp->span->active_entries_count); 8898c2ecf20Sopenharmony_ci if (span_entry->parms.policer_enable) 8908c2ecf20Sopenharmony_ci mlxsw_sp_span_policer_id_base_unset(mlxsw_sp->span); 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistruct mlxsw_sp_span_entry * 8948c2ecf20Sopenharmony_cimlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp, 8958c2ecf20Sopenharmony_ci const struct net_device *to_dev) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci int i; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci for (i = 0; i < mlxsw_sp->span->entries_count; i++) { 9008c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i]; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev) 9038c2ecf20Sopenharmony_ci return curr; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci return NULL; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_civoid mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp, 9098c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *span_entry) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_deconfigure(span_entry); 9128c2ecf20Sopenharmony_ci span_entry->ops = &mlxsw_sp_span_entry_ops_nop; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic struct mlxsw_sp_span_entry * 9168c2ecf20Sopenharmony_cimlxsw_sp_span_entry_find_by_id(struct mlxsw_sp *mlxsw_sp, int span_id) 9178c2ecf20Sopenharmony_ci{ 9188c2ecf20Sopenharmony_ci int i; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci for (i = 0; i < mlxsw_sp->span->entries_count; i++) { 9218c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i]; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (refcount_read(&curr->ref_count) && curr->id == span_id) 9248c2ecf20Sopenharmony_ci return curr; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci return NULL; 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic struct mlxsw_sp_span_entry * 9308c2ecf20Sopenharmony_cimlxsw_sp_span_entry_find_by_parms(struct mlxsw_sp *mlxsw_sp, 9318c2ecf20Sopenharmony_ci const struct net_device *to_dev, 9328c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_parms *sparms) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci int i; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci for (i = 0; i < mlxsw_sp->span->entries_count; i++) { 9378c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i]; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev && 9408c2ecf20Sopenharmony_ci curr->parms.policer_enable == sparms->policer_enable && 9418c2ecf20Sopenharmony_ci curr->parms.policer_id == sparms->policer_id) 9428c2ecf20Sopenharmony_ci return curr; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci return NULL; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic struct mlxsw_sp_span_entry * 9488c2ecf20Sopenharmony_cimlxsw_sp_span_entry_get(struct mlxsw_sp *mlxsw_sp, 9498c2ecf20Sopenharmony_ci const struct net_device *to_dev, 9508c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_entry_ops *ops, 9518c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *span_entry; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci span_entry = mlxsw_sp_span_entry_find_by_parms(mlxsw_sp, to_dev, 9568c2ecf20Sopenharmony_ci &sparms); 9578c2ecf20Sopenharmony_ci if (span_entry) { 9588c2ecf20Sopenharmony_ci /* Already exists, just take a reference */ 9598c2ecf20Sopenharmony_ci refcount_inc(&span_entry->ref_count); 9608c2ecf20Sopenharmony_ci return span_entry; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci return mlxsw_sp_span_entry_create(mlxsw_sp, to_dev, ops, sparms); 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp, 9678c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *span_entry) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&span_entry->ref_count)) 9708c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_destroy(mlxsw_sp, span_entry); 9718c2ecf20Sopenharmony_ci return 0; 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic int mlxsw_sp_span_port_buffer_update(struct mlxsw_sp_port *mlxsw_sp_port, bool enable) 9758c2ecf20Sopenharmony_ci{ 9768c2ecf20Sopenharmony_ci struct mlxsw_sp_hdroom hdroom; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci hdroom = *mlxsw_sp_port->hdroom; 9798c2ecf20Sopenharmony_ci hdroom.int_buf.enable = enable; 9808c2ecf20Sopenharmony_ci mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci return mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic int 9868c2ecf20Sopenharmony_cimlxsw_sp_span_port_buffer_enable(struct mlxsw_sp_port *mlxsw_sp_port) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci return mlxsw_sp_span_port_buffer_update(mlxsw_sp_port, true); 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistatic void mlxsw_sp_span_port_buffer_disable(struct mlxsw_sp_port *mlxsw_sp_port) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci mlxsw_sp_span_port_buffer_update(mlxsw_sp_port, false); 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic struct mlxsw_sp_span_analyzed_port * 9978c2ecf20Sopenharmony_cimlxsw_sp_span_analyzed_port_find(struct mlxsw_sp_span *span, u8 local_port, 9988c2ecf20Sopenharmony_ci bool ingress) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci struct mlxsw_sp_span_analyzed_port *analyzed_port; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci list_for_each_entry(analyzed_port, &span->analyzed_ports_list, list) { 10038c2ecf20Sopenharmony_ci if (analyzed_port->local_port == local_port && 10048c2ecf20Sopenharmony_ci analyzed_port->ingress == ingress) 10058c2ecf20Sopenharmony_ci return analyzed_port; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci return NULL; 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_span_entry_ops * 10128c2ecf20Sopenharmony_cimlxsw_sp_span_entry_ops(struct mlxsw_sp *mlxsw_sp, 10138c2ecf20Sopenharmony_ci const struct net_device *to_dev) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci struct mlxsw_sp_span *span = mlxsw_sp->span; 10168c2ecf20Sopenharmony_ci size_t i; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci for (i = 0; i < span->span_entry_ops_arr_size; ++i) 10198c2ecf20Sopenharmony_ci if (span->span_entry_ops_arr[i]->can_handle(to_dev)) 10208c2ecf20Sopenharmony_ci return span->span_entry_ops_arr[i]; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci return NULL; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic void mlxsw_sp_span_respin_work(struct work_struct *work) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci struct mlxsw_sp_span *span; 10288c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 10298c2ecf20Sopenharmony_ci int i, err; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci span = container_of(work, struct mlxsw_sp_span, work); 10328c2ecf20Sopenharmony_ci mlxsw_sp = span->mlxsw_sp; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci rtnl_lock(); 10358c2ecf20Sopenharmony_ci for (i = 0; i < mlxsw_sp->span->entries_count; i++) { 10368c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i]; 10378c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms = {NULL}; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (!refcount_read(&curr->ref_count)) 10408c2ecf20Sopenharmony_ci continue; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (curr->ops->is_static) 10438c2ecf20Sopenharmony_ci continue; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci err = curr->ops->parms_set(mlxsw_sp, curr->to_dev, &sparms); 10468c2ecf20Sopenharmony_ci if (err) 10478c2ecf20Sopenharmony_ci continue; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (memcmp(&sparms, &curr->parms, sizeof(sparms))) { 10508c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_deconfigure(curr); 10518c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_configure(mlxsw_sp, curr, sparms); 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci rtnl_unlock(); 10558c2ecf20Sopenharmony_ci} 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_civoid mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci if (atomic_read(&mlxsw_sp->span->active_entries_count) == 0) 10608c2ecf20Sopenharmony_ci return; 10618c2ecf20Sopenharmony_ci mlxsw_core_schedule_work(&mlxsw_sp->span->work); 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ciint mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp, int *p_span_id, 10658c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_agent_parms *parms) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci const struct net_device *to_dev = parms->to_dev; 10688c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_entry_ops *ops; 10698c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *span_entry; 10708c2ecf20Sopenharmony_ci struct mlxsw_sp_span_parms sparms; 10718c2ecf20Sopenharmony_ci int err; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci ASSERT_RTNL(); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci ops = mlxsw_sp_span_entry_ops(mlxsw_sp, to_dev); 10768c2ecf20Sopenharmony_ci if (!ops) { 10778c2ecf20Sopenharmony_ci dev_err(mlxsw_sp->bus_info->dev, "Cannot mirror to requested destination\n"); 10788c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci memset(&sparms, 0, sizeof(sparms)); 10828c2ecf20Sopenharmony_ci err = ops->parms_set(mlxsw_sp, to_dev, &sparms); 10838c2ecf20Sopenharmony_ci if (err) 10848c2ecf20Sopenharmony_ci return err; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci sparms.policer_id = parms->policer_id; 10878c2ecf20Sopenharmony_ci sparms.policer_enable = parms->policer_enable; 10888c2ecf20Sopenharmony_ci span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms); 10898c2ecf20Sopenharmony_ci if (!span_entry) 10908c2ecf20Sopenharmony_ci return -ENOBUFS; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci *p_span_id = span_entry->id; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci return 0; 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_civoid mlxsw_sp_span_agent_put(struct mlxsw_sp *mlxsw_sp, int span_id) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci struct mlxsw_sp_span_entry *span_entry; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci ASSERT_RTNL(); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci span_entry = mlxsw_sp_span_entry_find_by_id(mlxsw_sp, span_id); 11048c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!span_entry)) 11058c2ecf20Sopenharmony_ci return; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci mlxsw_sp_span_entry_put(mlxsw_sp, span_entry); 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cistatic struct mlxsw_sp_span_analyzed_port * 11118c2ecf20Sopenharmony_cimlxsw_sp_span_analyzed_port_create(struct mlxsw_sp_span *span, 11128c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 11138c2ecf20Sopenharmony_ci bool ingress) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci struct mlxsw_sp_span_analyzed_port *analyzed_port; 11168c2ecf20Sopenharmony_ci int err; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci analyzed_port = kzalloc(sizeof(*analyzed_port), GFP_KERNEL); 11198c2ecf20Sopenharmony_ci if (!analyzed_port) 11208c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci refcount_set(&analyzed_port->ref_count, 1); 11238c2ecf20Sopenharmony_ci analyzed_port->local_port = mlxsw_sp_port->local_port; 11248c2ecf20Sopenharmony_ci analyzed_port->ingress = ingress; 11258c2ecf20Sopenharmony_ci list_add_tail(&analyzed_port->list, &span->analyzed_ports_list); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* An egress mirror buffer should be allocated on the egress port which 11288c2ecf20Sopenharmony_ci * does the mirroring. 11298c2ecf20Sopenharmony_ci */ 11308c2ecf20Sopenharmony_ci if (!ingress) { 11318c2ecf20Sopenharmony_ci err = mlxsw_sp_span_port_buffer_enable(mlxsw_sp_port); 11328c2ecf20Sopenharmony_ci if (err) 11338c2ecf20Sopenharmony_ci goto err_buffer_update; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci return analyzed_port; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_cierr_buffer_update: 11398c2ecf20Sopenharmony_ci list_del(&analyzed_port->list); 11408c2ecf20Sopenharmony_ci kfree(analyzed_port); 11418c2ecf20Sopenharmony_ci return ERR_PTR(err); 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic void 11458c2ecf20Sopenharmony_cimlxsw_sp_span_analyzed_port_destroy(struct mlxsw_sp_port *mlxsw_sp_port, 11468c2ecf20Sopenharmony_ci struct mlxsw_sp_span_analyzed_port * 11478c2ecf20Sopenharmony_ci analyzed_port) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci /* Remove egress mirror buffer now that port is no longer analyzed 11508c2ecf20Sopenharmony_ci * at egress. 11518c2ecf20Sopenharmony_ci */ 11528c2ecf20Sopenharmony_ci if (!analyzed_port->ingress) 11538c2ecf20Sopenharmony_ci mlxsw_sp_span_port_buffer_disable(mlxsw_sp_port); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci list_del(&analyzed_port->list); 11568c2ecf20Sopenharmony_ci kfree(analyzed_port); 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ciint mlxsw_sp_span_analyzed_port_get(struct mlxsw_sp_port *mlxsw_sp_port, 11608c2ecf20Sopenharmony_ci bool ingress) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 11638c2ecf20Sopenharmony_ci struct mlxsw_sp_span_analyzed_port *analyzed_port; 11648c2ecf20Sopenharmony_ci u8 local_port = mlxsw_sp_port->local_port; 11658c2ecf20Sopenharmony_ci int err = 0; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci mutex_lock(&mlxsw_sp->span->analyzed_ports_lock); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span, 11708c2ecf20Sopenharmony_ci local_port, ingress); 11718c2ecf20Sopenharmony_ci if (analyzed_port) { 11728c2ecf20Sopenharmony_ci refcount_inc(&analyzed_port->ref_count); 11738c2ecf20Sopenharmony_ci goto out_unlock; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci analyzed_port = mlxsw_sp_span_analyzed_port_create(mlxsw_sp->span, 11778c2ecf20Sopenharmony_ci mlxsw_sp_port, 11788c2ecf20Sopenharmony_ci ingress); 11798c2ecf20Sopenharmony_ci if (IS_ERR(analyzed_port)) 11808c2ecf20Sopenharmony_ci err = PTR_ERR(analyzed_port); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ciout_unlock: 11838c2ecf20Sopenharmony_ci mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock); 11848c2ecf20Sopenharmony_ci return err; 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_civoid mlxsw_sp_span_analyzed_port_put(struct mlxsw_sp_port *mlxsw_sp_port, 11888c2ecf20Sopenharmony_ci bool ingress) 11898c2ecf20Sopenharmony_ci{ 11908c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 11918c2ecf20Sopenharmony_ci struct mlxsw_sp_span_analyzed_port *analyzed_port; 11928c2ecf20Sopenharmony_ci u8 local_port = mlxsw_sp_port->local_port; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci mutex_lock(&mlxsw_sp->span->analyzed_ports_lock); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span, 11978c2ecf20Sopenharmony_ci local_port, ingress); 11988c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!analyzed_port)) 11998c2ecf20Sopenharmony_ci goto out_unlock; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci if (!refcount_dec_and_test(&analyzed_port->ref_count)) 12028c2ecf20Sopenharmony_ci goto out_unlock; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci mlxsw_sp_span_analyzed_port_destroy(mlxsw_sp_port, analyzed_port); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ciout_unlock: 12078c2ecf20Sopenharmony_ci mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock); 12088c2ecf20Sopenharmony_ci} 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_cistatic int 12118c2ecf20Sopenharmony_ci__mlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span *span, 12128c2ecf20Sopenharmony_ci struct mlxsw_sp_span_trigger_entry * 12138c2ecf20Sopenharmony_ci trigger_entry, bool enable) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci char mpar_pl[MLXSW_REG_MPAR_LEN]; 12168c2ecf20Sopenharmony_ci enum mlxsw_reg_mpar_i_e i_e; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci switch (trigger_entry->trigger) { 12198c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_INGRESS: 12208c2ecf20Sopenharmony_ci i_e = MLXSW_REG_MPAR_TYPE_INGRESS; 12218c2ecf20Sopenharmony_ci break; 12228c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_EGRESS: 12238c2ecf20Sopenharmony_ci i_e = MLXSW_REG_MPAR_TYPE_EGRESS; 12248c2ecf20Sopenharmony_ci break; 12258c2ecf20Sopenharmony_ci default: 12268c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 12278c2ecf20Sopenharmony_ci return -EINVAL; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci mlxsw_reg_mpar_pack(mpar_pl, trigger_entry->local_port, i_e, enable, 12318c2ecf20Sopenharmony_ci trigger_entry->parms.span_id); 12328c2ecf20Sopenharmony_ci return mlxsw_reg_write(span->mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl); 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic int 12368c2ecf20Sopenharmony_cimlxsw_sp_span_trigger_port_bind(struct mlxsw_sp_span_trigger_entry * 12378c2ecf20Sopenharmony_ci trigger_entry) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci return __mlxsw_sp_span_trigger_port_bind(trigger_entry->span, 12408c2ecf20Sopenharmony_ci trigger_entry, true); 12418c2ecf20Sopenharmony_ci} 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_cistatic void 12448c2ecf20Sopenharmony_cimlxsw_sp_span_trigger_port_unbind(struct mlxsw_sp_span_trigger_entry * 12458c2ecf20Sopenharmony_ci trigger_entry) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci __mlxsw_sp_span_trigger_port_bind(trigger_entry->span, trigger_entry, 12488c2ecf20Sopenharmony_ci false); 12498c2ecf20Sopenharmony_ci} 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_cistatic bool 12528c2ecf20Sopenharmony_cimlxsw_sp_span_trigger_port_matches(struct mlxsw_sp_span_trigger_entry * 12538c2ecf20Sopenharmony_ci trigger_entry, 12548c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger trigger, 12558c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port) 12568c2ecf20Sopenharmony_ci{ 12578c2ecf20Sopenharmony_ci return trigger_entry->trigger == trigger && 12588c2ecf20Sopenharmony_ci trigger_entry->local_port == mlxsw_sp_port->local_port; 12598c2ecf20Sopenharmony_ci} 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_cistatic int 12628c2ecf20Sopenharmony_cimlxsw_sp_span_trigger_port_enable(struct mlxsw_sp_span_trigger_entry * 12638c2ecf20Sopenharmony_ci trigger_entry, 12648c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, u8 tc) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci /* Port trigger are enabled during binding. */ 12678c2ecf20Sopenharmony_ci return 0; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_cistatic void 12718c2ecf20Sopenharmony_cimlxsw_sp_span_trigger_port_disable(struct mlxsw_sp_span_trigger_entry * 12728c2ecf20Sopenharmony_ci trigger_entry, 12738c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, u8 tc) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci} 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_span_trigger_ops 12788c2ecf20Sopenharmony_cimlxsw_sp_span_trigger_port_ops = { 12798c2ecf20Sopenharmony_ci .bind = mlxsw_sp_span_trigger_port_bind, 12808c2ecf20Sopenharmony_ci .unbind = mlxsw_sp_span_trigger_port_unbind, 12818c2ecf20Sopenharmony_ci .matches = mlxsw_sp_span_trigger_port_matches, 12828c2ecf20Sopenharmony_ci .enable = mlxsw_sp_span_trigger_port_enable, 12838c2ecf20Sopenharmony_ci .disable = mlxsw_sp_span_trigger_port_disable, 12848c2ecf20Sopenharmony_ci}; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic int 12878c2ecf20Sopenharmony_cimlxsw_sp1_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry * 12888c2ecf20Sopenharmony_ci trigger_entry) 12898c2ecf20Sopenharmony_ci{ 12908c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cistatic void 12948c2ecf20Sopenharmony_cimlxsw_sp1_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry * 12958c2ecf20Sopenharmony_ci trigger_entry) 12968c2ecf20Sopenharmony_ci{ 12978c2ecf20Sopenharmony_ci} 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_cistatic bool 13008c2ecf20Sopenharmony_cimlxsw_sp1_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry * 13018c2ecf20Sopenharmony_ci trigger_entry, 13028c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger trigger, 13038c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port) 13048c2ecf20Sopenharmony_ci{ 13058c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 13068c2ecf20Sopenharmony_ci return false; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic int 13108c2ecf20Sopenharmony_cimlxsw_sp1_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry * 13118c2ecf20Sopenharmony_ci trigger_entry, 13128c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 13138c2ecf20Sopenharmony_ci u8 tc) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13168c2ecf20Sopenharmony_ci} 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_cistatic void 13198c2ecf20Sopenharmony_cimlxsw_sp1_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry * 13208c2ecf20Sopenharmony_ci trigger_entry, 13218c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 13228c2ecf20Sopenharmony_ci u8 tc) 13238c2ecf20Sopenharmony_ci{ 13248c2ecf20Sopenharmony_ci} 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_span_trigger_ops 13278c2ecf20Sopenharmony_cimlxsw_sp1_span_trigger_global_ops = { 13288c2ecf20Sopenharmony_ci .bind = mlxsw_sp1_span_trigger_global_bind, 13298c2ecf20Sopenharmony_ci .unbind = mlxsw_sp1_span_trigger_global_unbind, 13308c2ecf20Sopenharmony_ci .matches = mlxsw_sp1_span_trigger_global_matches, 13318c2ecf20Sopenharmony_ci .enable = mlxsw_sp1_span_trigger_global_enable, 13328c2ecf20Sopenharmony_ci .disable = mlxsw_sp1_span_trigger_global_disable, 13338c2ecf20Sopenharmony_ci}; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_span_trigger_ops * 13368c2ecf20Sopenharmony_cimlxsw_sp1_span_trigger_ops_arr[] = { 13378c2ecf20Sopenharmony_ci [MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops, 13388c2ecf20Sopenharmony_ci [MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] = 13398c2ecf20Sopenharmony_ci &mlxsw_sp1_span_trigger_global_ops, 13408c2ecf20Sopenharmony_ci}; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_cistatic int 13438c2ecf20Sopenharmony_cimlxsw_sp2_span_trigger_global_bind(struct mlxsw_sp_span_trigger_entry * 13448c2ecf20Sopenharmony_ci trigger_entry) 13458c2ecf20Sopenharmony_ci{ 13468c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp; 13478c2ecf20Sopenharmony_ci enum mlxsw_reg_mpagr_trigger trigger; 13488c2ecf20Sopenharmony_ci char mpagr_pl[MLXSW_REG_MPAGR_LEN]; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci switch (trigger_entry->trigger) { 13518c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP: 13528c2ecf20Sopenharmony_ci trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_SHARED_BUFFER; 13538c2ecf20Sopenharmony_ci break; 13548c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP: 13558c2ecf20Sopenharmony_ci trigger = MLXSW_REG_MPAGR_TRIGGER_INGRESS_WRED; 13568c2ecf20Sopenharmony_ci break; 13578c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_ECN: 13588c2ecf20Sopenharmony_ci trigger = MLXSW_REG_MPAGR_TRIGGER_EGRESS_ECN; 13598c2ecf20Sopenharmony_ci break; 13608c2ecf20Sopenharmony_ci default: 13618c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 13628c2ecf20Sopenharmony_ci return -EINVAL; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci mlxsw_reg_mpagr_pack(mpagr_pl, trigger, trigger_entry->parms.span_id, 13668c2ecf20Sopenharmony_ci 1); 13678c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpagr), mpagr_pl); 13688c2ecf20Sopenharmony_ci} 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_cistatic void 13718c2ecf20Sopenharmony_cimlxsw_sp2_span_trigger_global_unbind(struct mlxsw_sp_span_trigger_entry * 13728c2ecf20Sopenharmony_ci trigger_entry) 13738c2ecf20Sopenharmony_ci{ 13748c2ecf20Sopenharmony_ci /* There is no unbinding for global triggers. The trigger should be 13758c2ecf20Sopenharmony_ci * disabled on all ports by now. 13768c2ecf20Sopenharmony_ci */ 13778c2ecf20Sopenharmony_ci} 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_cistatic bool 13808c2ecf20Sopenharmony_cimlxsw_sp2_span_trigger_global_matches(struct mlxsw_sp_span_trigger_entry * 13818c2ecf20Sopenharmony_ci trigger_entry, 13828c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger trigger, 13838c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci return trigger_entry->trigger == trigger; 13868c2ecf20Sopenharmony_ci} 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_cistatic int 13898c2ecf20Sopenharmony_ci__mlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry * 13908c2ecf20Sopenharmony_ci trigger_entry, 13918c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 13928c2ecf20Sopenharmony_ci u8 tc, bool enable) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = trigger_entry->span->mlxsw_sp; 13958c2ecf20Sopenharmony_ci char momte_pl[MLXSW_REG_MOMTE_LEN]; 13968c2ecf20Sopenharmony_ci enum mlxsw_reg_momte_type type; 13978c2ecf20Sopenharmony_ci int err; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci switch (trigger_entry->trigger) { 14008c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP: 14018c2ecf20Sopenharmony_ci type = MLXSW_REG_MOMTE_TYPE_SHARED_BUFFER_TCLASS; 14028c2ecf20Sopenharmony_ci break; 14038c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP: 14048c2ecf20Sopenharmony_ci type = MLXSW_REG_MOMTE_TYPE_WRED; 14058c2ecf20Sopenharmony_ci break; 14068c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_ECN: 14078c2ecf20Sopenharmony_ci type = MLXSW_REG_MOMTE_TYPE_ECN; 14088c2ecf20Sopenharmony_ci break; 14098c2ecf20Sopenharmony_ci default: 14108c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 14118c2ecf20Sopenharmony_ci return -EINVAL; 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci /* Query existing configuration in order to only change the state of 14158c2ecf20Sopenharmony_ci * the specified traffic class. 14168c2ecf20Sopenharmony_ci */ 14178c2ecf20Sopenharmony_ci mlxsw_reg_momte_pack(momte_pl, mlxsw_sp_port->local_port, type); 14188c2ecf20Sopenharmony_ci err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(momte), momte_pl); 14198c2ecf20Sopenharmony_ci if (err) 14208c2ecf20Sopenharmony_ci return err; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci mlxsw_reg_momte_tclass_en_set(momte_pl, tc, enable); 14238c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(momte), momte_pl); 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic int 14278c2ecf20Sopenharmony_cimlxsw_sp2_span_trigger_global_enable(struct mlxsw_sp_span_trigger_entry * 14288c2ecf20Sopenharmony_ci trigger_entry, 14298c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 14308c2ecf20Sopenharmony_ci u8 tc) 14318c2ecf20Sopenharmony_ci{ 14328c2ecf20Sopenharmony_ci return __mlxsw_sp2_span_trigger_global_enable(trigger_entry, 14338c2ecf20Sopenharmony_ci mlxsw_sp_port, tc, true); 14348c2ecf20Sopenharmony_ci} 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_cistatic void 14378c2ecf20Sopenharmony_cimlxsw_sp2_span_trigger_global_disable(struct mlxsw_sp_span_trigger_entry * 14388c2ecf20Sopenharmony_ci trigger_entry, 14398c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 14408c2ecf20Sopenharmony_ci u8 tc) 14418c2ecf20Sopenharmony_ci{ 14428c2ecf20Sopenharmony_ci __mlxsw_sp2_span_trigger_global_enable(trigger_entry, mlxsw_sp_port, tc, 14438c2ecf20Sopenharmony_ci false); 14448c2ecf20Sopenharmony_ci} 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_span_trigger_ops 14478c2ecf20Sopenharmony_cimlxsw_sp2_span_trigger_global_ops = { 14488c2ecf20Sopenharmony_ci .bind = mlxsw_sp2_span_trigger_global_bind, 14498c2ecf20Sopenharmony_ci .unbind = mlxsw_sp2_span_trigger_global_unbind, 14508c2ecf20Sopenharmony_ci .matches = mlxsw_sp2_span_trigger_global_matches, 14518c2ecf20Sopenharmony_ci .enable = mlxsw_sp2_span_trigger_global_enable, 14528c2ecf20Sopenharmony_ci .disable = mlxsw_sp2_span_trigger_global_disable, 14538c2ecf20Sopenharmony_ci}; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_span_trigger_ops * 14568c2ecf20Sopenharmony_cimlxsw_sp2_span_trigger_ops_arr[] = { 14578c2ecf20Sopenharmony_ci [MLXSW_SP_SPAN_TRIGGER_TYPE_PORT] = &mlxsw_sp_span_trigger_port_ops, 14588c2ecf20Sopenharmony_ci [MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL] = 14598c2ecf20Sopenharmony_ci &mlxsw_sp2_span_trigger_global_ops, 14608c2ecf20Sopenharmony_ci}; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_cistatic void 14638c2ecf20Sopenharmony_cimlxsw_sp_span_trigger_ops_set(struct mlxsw_sp_span_trigger_entry *trigger_entry) 14648c2ecf20Sopenharmony_ci{ 14658c2ecf20Sopenharmony_ci struct mlxsw_sp_span *span = trigger_entry->span; 14668c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger_type type; 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci switch (trigger_entry->trigger) { 14698c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_INGRESS: 14708c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_EGRESS: 14718c2ecf20Sopenharmony_ci type = MLXSW_SP_SPAN_TRIGGER_TYPE_PORT; 14728c2ecf20Sopenharmony_ci break; 14738c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_TAIL_DROP: 14748c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_EARLY_DROP: 14758c2ecf20Sopenharmony_ci case MLXSW_SP_SPAN_TRIGGER_ECN: 14768c2ecf20Sopenharmony_ci type = MLXSW_SP_SPAN_TRIGGER_TYPE_GLOBAL; 14778c2ecf20Sopenharmony_ci break; 14788c2ecf20Sopenharmony_ci default: 14798c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 14808c2ecf20Sopenharmony_ci return; 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci trigger_entry->ops = span->span_trigger_ops_arr[type]; 14848c2ecf20Sopenharmony_ci} 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_cistatic struct mlxsw_sp_span_trigger_entry * 14878c2ecf20Sopenharmony_cimlxsw_sp_span_trigger_entry_create(struct mlxsw_sp_span *span, 14888c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger trigger, 14898c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 14908c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_trigger_parms 14918c2ecf20Sopenharmony_ci *parms) 14928c2ecf20Sopenharmony_ci{ 14938c2ecf20Sopenharmony_ci struct mlxsw_sp_span_trigger_entry *trigger_entry; 14948c2ecf20Sopenharmony_ci int err; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci trigger_entry = kzalloc(sizeof(*trigger_entry), GFP_KERNEL); 14978c2ecf20Sopenharmony_ci if (!trigger_entry) 14988c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci refcount_set(&trigger_entry->ref_count, 1); 15018c2ecf20Sopenharmony_ci trigger_entry->local_port = mlxsw_sp_port ? mlxsw_sp_port->local_port : 15028c2ecf20Sopenharmony_ci 0; 15038c2ecf20Sopenharmony_ci trigger_entry->trigger = trigger; 15048c2ecf20Sopenharmony_ci memcpy(&trigger_entry->parms, parms, sizeof(trigger_entry->parms)); 15058c2ecf20Sopenharmony_ci trigger_entry->span = span; 15068c2ecf20Sopenharmony_ci mlxsw_sp_span_trigger_ops_set(trigger_entry); 15078c2ecf20Sopenharmony_ci list_add_tail(&trigger_entry->list, &span->trigger_entries_list); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci err = trigger_entry->ops->bind(trigger_entry); 15108c2ecf20Sopenharmony_ci if (err) 15118c2ecf20Sopenharmony_ci goto err_trigger_entry_bind; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci return trigger_entry; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_cierr_trigger_entry_bind: 15168c2ecf20Sopenharmony_ci list_del(&trigger_entry->list); 15178c2ecf20Sopenharmony_ci kfree(trigger_entry); 15188c2ecf20Sopenharmony_ci return ERR_PTR(err); 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cistatic void 15228c2ecf20Sopenharmony_cimlxsw_sp_span_trigger_entry_destroy(struct mlxsw_sp_span *span, 15238c2ecf20Sopenharmony_ci struct mlxsw_sp_span_trigger_entry * 15248c2ecf20Sopenharmony_ci trigger_entry) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci trigger_entry->ops->unbind(trigger_entry); 15278c2ecf20Sopenharmony_ci list_del(&trigger_entry->list); 15288c2ecf20Sopenharmony_ci kfree(trigger_entry); 15298c2ecf20Sopenharmony_ci} 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_cistatic struct mlxsw_sp_span_trigger_entry * 15328c2ecf20Sopenharmony_cimlxsw_sp_span_trigger_entry_find(struct mlxsw_sp_span *span, 15338c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger trigger, 15348c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port) 15358c2ecf20Sopenharmony_ci{ 15368c2ecf20Sopenharmony_ci struct mlxsw_sp_span_trigger_entry *trigger_entry; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci list_for_each_entry(trigger_entry, &span->trigger_entries_list, list) { 15398c2ecf20Sopenharmony_ci if (trigger_entry->ops->matches(trigger_entry, trigger, 15408c2ecf20Sopenharmony_ci mlxsw_sp_port)) 15418c2ecf20Sopenharmony_ci return trigger_entry; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci return NULL; 15458c2ecf20Sopenharmony_ci} 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ciint mlxsw_sp_span_agent_bind(struct mlxsw_sp *mlxsw_sp, 15488c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger trigger, 15498c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 15508c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_trigger_parms *parms) 15518c2ecf20Sopenharmony_ci{ 15528c2ecf20Sopenharmony_ci struct mlxsw_sp_span_trigger_entry *trigger_entry; 15538c2ecf20Sopenharmony_ci int err = 0; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci ASSERT_RTNL(); 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci if (!mlxsw_sp_span_entry_find_by_id(mlxsw_sp, parms->span_id)) 15588c2ecf20Sopenharmony_ci return -EINVAL; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span, 15618c2ecf20Sopenharmony_ci trigger, 15628c2ecf20Sopenharmony_ci mlxsw_sp_port); 15638c2ecf20Sopenharmony_ci if (trigger_entry) { 15648c2ecf20Sopenharmony_ci if (trigger_entry->parms.span_id != parms->span_id) 15658c2ecf20Sopenharmony_ci return -EINVAL; 15668c2ecf20Sopenharmony_ci refcount_inc(&trigger_entry->ref_count); 15678c2ecf20Sopenharmony_ci goto out; 15688c2ecf20Sopenharmony_ci } 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci trigger_entry = mlxsw_sp_span_trigger_entry_create(mlxsw_sp->span, 15718c2ecf20Sopenharmony_ci trigger, 15728c2ecf20Sopenharmony_ci mlxsw_sp_port, 15738c2ecf20Sopenharmony_ci parms); 15748c2ecf20Sopenharmony_ci if (IS_ERR(trigger_entry)) 15758c2ecf20Sopenharmony_ci err = PTR_ERR(trigger_entry); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ciout: 15788c2ecf20Sopenharmony_ci return err; 15798c2ecf20Sopenharmony_ci} 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_civoid mlxsw_sp_span_agent_unbind(struct mlxsw_sp *mlxsw_sp, 15828c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger trigger, 15838c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 15848c2ecf20Sopenharmony_ci const struct mlxsw_sp_span_trigger_parms *parms) 15858c2ecf20Sopenharmony_ci{ 15868c2ecf20Sopenharmony_ci struct mlxsw_sp_span_trigger_entry *trigger_entry; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci ASSERT_RTNL(); 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!mlxsw_sp_span_entry_find_by_id(mlxsw_sp, 15918c2ecf20Sopenharmony_ci parms->span_id))) 15928c2ecf20Sopenharmony_ci return; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span, 15958c2ecf20Sopenharmony_ci trigger, 15968c2ecf20Sopenharmony_ci mlxsw_sp_port); 15978c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!trigger_entry)) 15988c2ecf20Sopenharmony_ci return; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci if (!refcount_dec_and_test(&trigger_entry->ref_count)) 16018c2ecf20Sopenharmony_ci return; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci mlxsw_sp_span_trigger_entry_destroy(mlxsw_sp->span, trigger_entry); 16048c2ecf20Sopenharmony_ci} 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ciint mlxsw_sp_span_trigger_enable(struct mlxsw_sp_port *mlxsw_sp_port, 16078c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger trigger, u8 tc) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 16108c2ecf20Sopenharmony_ci struct mlxsw_sp_span_trigger_entry *trigger_entry; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci ASSERT_RTNL(); 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span, 16158c2ecf20Sopenharmony_ci trigger, 16168c2ecf20Sopenharmony_ci mlxsw_sp_port); 16178c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!trigger_entry)) 16188c2ecf20Sopenharmony_ci return -EINVAL; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci return trigger_entry->ops->enable(trigger_entry, mlxsw_sp_port, tc); 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_civoid mlxsw_sp_span_trigger_disable(struct mlxsw_sp_port *mlxsw_sp_port, 16248c2ecf20Sopenharmony_ci enum mlxsw_sp_span_trigger trigger, u8 tc) 16258c2ecf20Sopenharmony_ci{ 16268c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 16278c2ecf20Sopenharmony_ci struct mlxsw_sp_span_trigger_entry *trigger_entry; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci ASSERT_RTNL(); 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span, 16328c2ecf20Sopenharmony_ci trigger, 16338c2ecf20Sopenharmony_ci mlxsw_sp_port); 16348c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!trigger_entry)) 16358c2ecf20Sopenharmony_ci return; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci return trigger_entry->ops->disable(trigger_entry, mlxsw_sp_port, tc); 16388c2ecf20Sopenharmony_ci} 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_cistatic int mlxsw_sp1_span_init(struct mlxsw_sp *mlxsw_sp) 16418c2ecf20Sopenharmony_ci{ 16428c2ecf20Sopenharmony_ci size_t arr_size = ARRAY_SIZE(mlxsw_sp1_span_entry_ops_arr); 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci /* Must be first to avoid NULL pointer dereference by subsequent 16458c2ecf20Sopenharmony_ci * can_handle() callbacks. 16468c2ecf20Sopenharmony_ci */ 16478c2ecf20Sopenharmony_ci if (WARN_ON(mlxsw_sp1_span_entry_ops_arr[0] != 16488c2ecf20Sopenharmony_ci &mlxsw_sp1_span_entry_ops_cpu)) 16498c2ecf20Sopenharmony_ci return -EINVAL; 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp1_span_trigger_ops_arr; 16528c2ecf20Sopenharmony_ci mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp1_span_entry_ops_arr; 16538c2ecf20Sopenharmony_ci mlxsw_sp->span->span_entry_ops_arr_size = arr_size; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci return 0; 16568c2ecf20Sopenharmony_ci} 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_cistatic int mlxsw_sp1_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp, 16598c2ecf20Sopenharmony_ci u16 policer_id_base) 16608c2ecf20Sopenharmony_ci{ 16618c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 16628c2ecf20Sopenharmony_ci} 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ciconst struct mlxsw_sp_span_ops mlxsw_sp1_span_ops = { 16658c2ecf20Sopenharmony_ci .init = mlxsw_sp1_span_init, 16668c2ecf20Sopenharmony_ci .policer_id_base_set = mlxsw_sp1_span_policer_id_base_set, 16678c2ecf20Sopenharmony_ci}; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_cistatic int mlxsw_sp2_span_init(struct mlxsw_sp *mlxsw_sp) 16708c2ecf20Sopenharmony_ci{ 16718c2ecf20Sopenharmony_ci size_t arr_size = ARRAY_SIZE(mlxsw_sp2_span_entry_ops_arr); 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci /* Must be first to avoid NULL pointer dereference by subsequent 16748c2ecf20Sopenharmony_ci * can_handle() callbacks. 16758c2ecf20Sopenharmony_ci */ 16768c2ecf20Sopenharmony_ci if (WARN_ON(mlxsw_sp2_span_entry_ops_arr[0] != 16778c2ecf20Sopenharmony_ci &mlxsw_sp2_span_entry_ops_cpu)) 16788c2ecf20Sopenharmony_ci return -EINVAL; 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci mlxsw_sp->span->span_trigger_ops_arr = mlxsw_sp2_span_trigger_ops_arr; 16818c2ecf20Sopenharmony_ci mlxsw_sp->span->span_entry_ops_arr = mlxsw_sp2_span_entry_ops_arr; 16828c2ecf20Sopenharmony_ci mlxsw_sp->span->span_entry_ops_arr_size = arr_size; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci return 0; 16858c2ecf20Sopenharmony_ci} 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci#define MLXSW_SP2_SPAN_EG_MIRROR_BUFFER_FACTOR 38 16888c2ecf20Sopenharmony_ci#define MLXSW_SP3_SPAN_EG_MIRROR_BUFFER_FACTOR 50 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_cistatic int mlxsw_sp2_span_policer_id_base_set(struct mlxsw_sp *mlxsw_sp, 16918c2ecf20Sopenharmony_ci u16 policer_id_base) 16928c2ecf20Sopenharmony_ci{ 16938c2ecf20Sopenharmony_ci char mogcr_pl[MLXSW_REG_MOGCR_LEN]; 16948c2ecf20Sopenharmony_ci int err; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl); 16978c2ecf20Sopenharmony_ci if (err) 16988c2ecf20Sopenharmony_ci return err; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci mlxsw_reg_mogcr_mirroring_pid_base_set(mogcr_pl, policer_id_base); 17018c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl); 17028c2ecf20Sopenharmony_ci} 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ciconst struct mlxsw_sp_span_ops mlxsw_sp2_span_ops = { 17058c2ecf20Sopenharmony_ci .init = mlxsw_sp2_span_init, 17068c2ecf20Sopenharmony_ci .policer_id_base_set = mlxsw_sp2_span_policer_id_base_set, 17078c2ecf20Sopenharmony_ci}; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ciconst struct mlxsw_sp_span_ops mlxsw_sp3_span_ops = { 17108c2ecf20Sopenharmony_ci .init = mlxsw_sp2_span_init, 17118c2ecf20Sopenharmony_ci .policer_id_base_set = mlxsw_sp2_span_policer_id_base_set, 17128c2ecf20Sopenharmony_ci}; 1713