Linux-libre 5.4.47-gnu
[librecmc/linux-libre.git] / drivers / net / ethernet / mellanox / mlx5 / core / steering / dr_table.c
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2019 Mellanox Technologies. */
3
4 #include "dr_types.h"
5
6 int mlx5dr_table_set_miss_action(struct mlx5dr_table *tbl,
7                                  struct mlx5dr_action *action)
8 {
9         struct mlx5dr_matcher *last_matcher = NULL;
10         struct mlx5dr_htbl_connect_info info;
11         struct mlx5dr_ste_htbl *last_htbl;
12         int ret;
13
14         if (action && action->action_type != DR_ACTION_TYP_FT)
15                 return -EOPNOTSUPP;
16
17         mutex_lock(&tbl->dmn->mutex);
18
19         if (!list_empty(&tbl->matcher_list))
20                 last_matcher = list_last_entry(&tbl->matcher_list,
21                                                struct mlx5dr_matcher,
22                                                matcher_list);
23
24         if (tbl->dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX ||
25             tbl->dmn->type == MLX5DR_DOMAIN_TYPE_FDB) {
26                 if (last_matcher)
27                         last_htbl = last_matcher->rx.e_anchor;
28                 else
29                         last_htbl = tbl->rx.s_anchor;
30
31                 tbl->rx.default_icm_addr = action ?
32                         action->dest_tbl.tbl->rx.s_anchor->chunk->icm_addr :
33                         tbl->rx.nic_dmn->default_icm_addr;
34
35                 info.type = CONNECT_MISS;
36                 info.miss_icm_addr = tbl->rx.default_icm_addr;
37
38                 ret = mlx5dr_ste_htbl_init_and_postsend(tbl->dmn,
39                                                         tbl->rx.nic_dmn,
40                                                         last_htbl,
41                                                         &info, true);
42                 if (ret) {
43                         mlx5dr_dbg(tbl->dmn, "Failed to set RX miss action, ret %d\n", ret);
44                         goto out;
45                 }
46         }
47
48         if (tbl->dmn->type == MLX5DR_DOMAIN_TYPE_NIC_TX ||
49             tbl->dmn->type == MLX5DR_DOMAIN_TYPE_FDB) {
50                 if (last_matcher)
51                         last_htbl = last_matcher->tx.e_anchor;
52                 else
53                         last_htbl = tbl->tx.s_anchor;
54
55                 tbl->tx.default_icm_addr = action ?
56                         action->dest_tbl.tbl->tx.s_anchor->chunk->icm_addr :
57                         tbl->tx.nic_dmn->default_icm_addr;
58
59                 info.type = CONNECT_MISS;
60                 info.miss_icm_addr = tbl->tx.default_icm_addr;
61
62                 ret = mlx5dr_ste_htbl_init_and_postsend(tbl->dmn,
63                                                         tbl->tx.nic_dmn,
64                                                         last_htbl, &info, true);
65                 if (ret) {
66                         mlx5dr_dbg(tbl->dmn, "Failed to set TX miss action, ret %d\n", ret);
67                         goto out;
68                 }
69         }
70
71         /* Release old action */
72         if (tbl->miss_action)
73                 refcount_dec(&tbl->miss_action->refcount);
74
75         /* Set new miss action */
76         tbl->miss_action = action;
77         if (tbl->miss_action)
78                 refcount_inc(&action->refcount);
79
80 out:
81         mutex_unlock(&tbl->dmn->mutex);
82         return ret;
83 }
84
85 static void dr_table_uninit_nic(struct mlx5dr_table_rx_tx *nic_tbl)
86 {
87         mlx5dr_htbl_put(nic_tbl->s_anchor);
88 }
89
90 static void dr_table_uninit_fdb(struct mlx5dr_table *tbl)
91 {
92         dr_table_uninit_nic(&tbl->rx);
93         dr_table_uninit_nic(&tbl->tx);
94 }
95
96 static void dr_table_uninit(struct mlx5dr_table *tbl)
97 {
98         mutex_lock(&tbl->dmn->mutex);
99
100         switch (tbl->dmn->type) {
101         case MLX5DR_DOMAIN_TYPE_NIC_RX:
102                 dr_table_uninit_nic(&tbl->rx);
103                 break;
104         case MLX5DR_DOMAIN_TYPE_NIC_TX:
105                 dr_table_uninit_nic(&tbl->tx);
106                 break;
107         case MLX5DR_DOMAIN_TYPE_FDB:
108                 dr_table_uninit_fdb(tbl);
109                 break;
110         default:
111                 WARN_ON(true);
112                 break;
113         }
114
115         mutex_unlock(&tbl->dmn->mutex);
116 }
117
118 static int dr_table_init_nic(struct mlx5dr_domain *dmn,
119                              struct mlx5dr_table_rx_tx *nic_tbl)
120 {
121         struct mlx5dr_domain_rx_tx *nic_dmn = nic_tbl->nic_dmn;
122         struct mlx5dr_htbl_connect_info info;
123         int ret;
124
125         nic_tbl->default_icm_addr = nic_dmn->default_icm_addr;
126
127         nic_tbl->s_anchor = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool,
128                                                   DR_CHUNK_SIZE_1,
129                                                   MLX5DR_STE_LU_TYPE_DONT_CARE,
130                                                   0);
131         if (!nic_tbl->s_anchor)
132                 return -ENOMEM;
133
134         info.type = CONNECT_MISS;
135         info.miss_icm_addr = nic_dmn->default_icm_addr;
136         ret = mlx5dr_ste_htbl_init_and_postsend(dmn, nic_dmn,
137                                                 nic_tbl->s_anchor,
138                                                 &info, true);
139         if (ret)
140                 goto free_s_anchor;
141
142         mlx5dr_htbl_get(nic_tbl->s_anchor);
143
144         return 0;
145
146 free_s_anchor:
147         mlx5dr_ste_htbl_free(nic_tbl->s_anchor);
148         return ret;
149 }
150
151 static int dr_table_init_fdb(struct mlx5dr_table *tbl)
152 {
153         int ret;
154
155         ret = dr_table_init_nic(tbl->dmn, &tbl->rx);
156         if (ret)
157                 return ret;
158
159         ret = dr_table_init_nic(tbl->dmn, &tbl->tx);
160         if (ret)
161                 goto destroy_rx;
162
163         return 0;
164
165 destroy_rx:
166         dr_table_uninit_nic(&tbl->rx);
167         return ret;
168 }
169
170 static int dr_table_init(struct mlx5dr_table *tbl)
171 {
172         int ret = 0;
173
174         INIT_LIST_HEAD(&tbl->matcher_list);
175
176         mutex_lock(&tbl->dmn->mutex);
177
178         switch (tbl->dmn->type) {
179         case MLX5DR_DOMAIN_TYPE_NIC_RX:
180                 tbl->table_type = MLX5_FLOW_TABLE_TYPE_NIC_RX;
181                 tbl->rx.nic_dmn = &tbl->dmn->info.rx;
182                 ret = dr_table_init_nic(tbl->dmn, &tbl->rx);
183                 break;
184         case MLX5DR_DOMAIN_TYPE_NIC_TX:
185                 tbl->table_type = MLX5_FLOW_TABLE_TYPE_NIC_TX;
186                 tbl->tx.nic_dmn = &tbl->dmn->info.tx;
187                 ret = dr_table_init_nic(tbl->dmn, &tbl->tx);
188                 break;
189         case MLX5DR_DOMAIN_TYPE_FDB:
190                 tbl->table_type = MLX5_FLOW_TABLE_TYPE_FDB;
191                 tbl->rx.nic_dmn = &tbl->dmn->info.rx;
192                 tbl->tx.nic_dmn = &tbl->dmn->info.tx;
193                 ret = dr_table_init_fdb(tbl);
194                 break;
195         default:
196                 WARN_ON(true);
197                 break;
198         }
199
200         mutex_unlock(&tbl->dmn->mutex);
201
202         return ret;
203 }
204
205 static int dr_table_destroy_sw_owned_tbl(struct mlx5dr_table *tbl)
206 {
207         return mlx5dr_cmd_destroy_flow_table(tbl->dmn->mdev,
208                                              tbl->table_id,
209                                              tbl->table_type);
210 }
211
212 static int dr_table_create_sw_owned_tbl(struct mlx5dr_table *tbl)
213 {
214         u64 icm_addr_rx = 0;
215         u64 icm_addr_tx = 0;
216         int ret;
217
218         if (tbl->rx.s_anchor)
219                 icm_addr_rx = tbl->rx.s_anchor->chunk->icm_addr;
220
221         if (tbl->tx.s_anchor)
222                 icm_addr_tx = tbl->tx.s_anchor->chunk->icm_addr;
223
224         ret = mlx5dr_cmd_create_flow_table(tbl->dmn->mdev,
225                                            tbl->table_type,
226                                            icm_addr_rx,
227                                            icm_addr_tx,
228                                            tbl->dmn->info.caps.max_ft_level - 1,
229                                            true, false, NULL,
230                                            &tbl->table_id);
231
232         return ret;
233 }
234
235 struct mlx5dr_table *mlx5dr_table_create(struct mlx5dr_domain *dmn, u32 level)
236 {
237         struct mlx5dr_table *tbl;
238         int ret;
239
240         refcount_inc(&dmn->refcount);
241
242         tbl = kzalloc(sizeof(*tbl), GFP_KERNEL);
243         if (!tbl)
244                 goto dec_ref;
245
246         tbl->dmn = dmn;
247         tbl->level = level;
248         refcount_set(&tbl->refcount, 1);
249
250         ret = dr_table_init(tbl);
251         if (ret)
252                 goto free_tbl;
253
254         ret = dr_table_create_sw_owned_tbl(tbl);
255         if (ret)
256                 goto uninit_tbl;
257
258         return tbl;
259
260 uninit_tbl:
261         dr_table_uninit(tbl);
262 free_tbl:
263         kfree(tbl);
264 dec_ref:
265         refcount_dec(&dmn->refcount);
266         return NULL;
267 }
268
269 int mlx5dr_table_destroy(struct mlx5dr_table *tbl)
270 {
271         int ret;
272
273         if (refcount_read(&tbl->refcount) > 1)
274                 return -EBUSY;
275
276         ret = dr_table_destroy_sw_owned_tbl(tbl);
277         if (ret)
278                 return ret;
279
280         dr_table_uninit(tbl);
281
282         if (tbl->miss_action)
283                 refcount_dec(&tbl->miss_action->refcount);
284
285         refcount_dec(&tbl->dmn->refcount);
286         kfree(tbl);
287
288         return ret;
289 }
290
291 u32 mlx5dr_table_get_id(struct mlx5dr_table *tbl)
292 {
293         return tbl->table_id;
294 }