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  */
9 struct 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 
17 struct 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 
24 static void
esw_vport_tbl_init(struct mlx5_eswitch * esw,struct esw_vport_tbl_namespace * ns)25 esw_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 
32 static struct mlx5_flow_table *
esw_vport_tbl_create(struct mlx5_eswitch * esw,struct mlx5_flow_namespace * ns,const struct esw_vport_tbl_namespace * vport_ns)33 esw_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 
flow_attr_to_vport_key(struct mlx5_eswitch * esw,struct mlx5_vport_tbl_attr * attr,struct mlx5_vport_key * key)55 static 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 */
68 static struct mlx5_vport_table *
esw_vport_tbl_lookup(struct mlx5_eswitch * esw,struct mlx5_vport_key * skey,u32 key)69 esw_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 
80 struct mlx5_flow_table *
mlx5_esw_vporttbl_get(struct mlx5_eswitch * esw,struct mlx5_vport_tbl_attr * attr)81 mlx5_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);
120 out:
121 	mutex_unlock(&esw->fdb_table.offloads.vports.lock);
122 	return e->fdb;
123 
124 err_ns:
125 	kfree(e);
126 err_alloc:
127 	mutex_unlock(&esw->fdb_table.offloads.vports.lock);
128 	return fdb;
129 }
130 
131 void
mlx5_esw_vporttbl_put(struct mlx5_eswitch * esw,struct mlx5_vport_tbl_attr * attr)132 mlx5_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);
148 out:
149 	mutex_unlock(&esw->fdb_table.offloads.vports.lock);
150 }
151