1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2// Copyright (c) 2021 Mellanox Technologies. 3 4#include "eswitch.h" 5 6/* This struct is used as a key to the hash table and we need it to be packed 7 * so hash result is consistent 8 */ 9struct mlx5_vport_key { 10 u32 chain; 11 u16 prio; 12 u16 vport; 13 u16 vhca_id; 14 struct esw_vport_tbl_namespace *vport_ns; 15} __packed; 16 17struct mlx5_vport_table { 18 struct hlist_node hlist; 19 struct mlx5_flow_table *fdb; 20 u32 num_rules; 21 struct mlx5_vport_key key; 22}; 23 24static void 25esw_vport_tbl_init(struct mlx5_eswitch *esw, struct esw_vport_tbl_namespace *ns) 26{ 27 if (esw->offloads.encap != DEVLINK_ESWITCH_ENCAP_MODE_NONE) 28 ns->flags |= (MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT | 29 MLX5_FLOW_TABLE_TUNNEL_EN_DECAP); 30} 31 32static struct mlx5_flow_table * 33esw_vport_tbl_create(struct mlx5_eswitch *esw, struct mlx5_flow_namespace *ns, 34 const struct esw_vport_tbl_namespace *vport_ns) 35{ 36 struct mlx5_flow_table_attr ft_attr = {}; 37 struct mlx5_flow_table *fdb; 38 39 if (vport_ns->max_num_groups) 40 ft_attr.autogroup.max_num_groups = vport_ns->max_num_groups; 41 else 42 ft_attr.autogroup.max_num_groups = esw->params.large_group_num; 43 ft_attr.max_fte = vport_ns->max_fte; 44 ft_attr.prio = FDB_PER_VPORT; 45 ft_attr.flags = vport_ns->flags; 46 fdb = mlx5_create_auto_grouped_flow_table(ns, &ft_attr); 47 if (IS_ERR(fdb)) { 48 esw_warn(esw->dev, "Failed to create per vport FDB Table err %ld\n", 49 PTR_ERR(fdb)); 50 } 51 52 return fdb; 53} 54 55static u32 flow_attr_to_vport_key(struct mlx5_eswitch *esw, 56 struct mlx5_vport_tbl_attr *attr, 57 struct mlx5_vport_key *key) 58{ 59 key->vport = attr->vport; 60 key->chain = attr->chain; 61 key->prio = attr->prio; 62 key->vhca_id = MLX5_CAP_GEN(esw->dev, vhca_id); 63 key->vport_ns = attr->vport_ns; 64 return jhash(key, sizeof(*key), 0); 65} 66 67/* caller must hold vports.lock */ 68static struct mlx5_vport_table * 69esw_vport_tbl_lookup(struct mlx5_eswitch *esw, struct mlx5_vport_key *skey, u32 key) 70{ 71 struct mlx5_vport_table *e; 72 73 hash_for_each_possible(esw->fdb_table.offloads.vports.table, e, hlist, key) 74 if (!memcmp(&e->key, skey, sizeof(*skey))) 75 return e; 76 77 return NULL; 78} 79 80struct mlx5_flow_table * 81mlx5_esw_vporttbl_get(struct mlx5_eswitch *esw, struct mlx5_vport_tbl_attr *attr) 82{ 83 struct mlx5_core_dev *dev = esw->dev; 84 struct mlx5_flow_namespace *ns; 85 struct mlx5_flow_table *fdb; 86 struct mlx5_vport_table *e; 87 struct mlx5_vport_key skey; 88 u32 hkey; 89 90 mutex_lock(&esw->fdb_table.offloads.vports.lock); 91 esw_vport_tbl_init(esw, attr->vport_ns); 92 hkey = flow_attr_to_vport_key(esw, attr, &skey); 93 e = esw_vport_tbl_lookup(esw, &skey, hkey); 94 if (e) { 95 e->num_rules++; 96 goto out; 97 } 98 99 e = kzalloc(sizeof(*e), GFP_KERNEL); 100 if (!e) { 101 fdb = ERR_PTR(-ENOMEM); 102 goto err_alloc; 103 } 104 105 ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB); 106 if (!ns) { 107 esw_warn(dev, "Failed to get FDB namespace\n"); 108 fdb = ERR_PTR(-ENOENT); 109 goto err_ns; 110 } 111 112 fdb = esw_vport_tbl_create(esw, ns, attr->vport_ns); 113 if (IS_ERR(fdb)) 114 goto err_ns; 115 116 e->fdb = fdb; 117 e->num_rules = 1; 118 e->key = skey; 119 hash_add(esw->fdb_table.offloads.vports.table, &e->hlist, hkey); 120out: 121 mutex_unlock(&esw->fdb_table.offloads.vports.lock); 122 return e->fdb; 123 124err_ns: 125 kfree(e); 126err_alloc: 127 mutex_unlock(&esw->fdb_table.offloads.vports.lock); 128 return fdb; 129} 130 131void 132mlx5_esw_vporttbl_put(struct mlx5_eswitch *esw, struct mlx5_vport_tbl_attr *attr) 133{ 134 struct mlx5_vport_table *e; 135 struct mlx5_vport_key key; 136 u32 hkey; 137 138 mutex_lock(&esw->fdb_table.offloads.vports.lock); 139 esw_vport_tbl_init(esw, attr->vport_ns); 140 hkey = flow_attr_to_vport_key(esw, attr, &key); 141 e = esw_vport_tbl_lookup(esw, &key, hkey); 142 if (!e || --e->num_rules) 143 goto out; 144 145 hash_del(&e->hlist); 146 mlx5_destroy_flow_table(e->fdb); 147 kfree(e); 148out: 149 mutex_unlock(&esw->fdb_table.offloads.vports.lock); 150} 151