1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2/* Copyright (c) 2020 Mellanox Technologies */
3
4#include "en/txrx.h"
5#include "en/params.h"
6#include "en/trap.h"
7
8static int mlx5e_trap_napi_poll(struct napi_struct *napi, int budget)
9{
10	struct mlx5e_trap *trap_ctx = container_of(napi, struct mlx5e_trap, napi);
11	struct mlx5e_ch_stats *ch_stats = trap_ctx->stats;
12	struct mlx5e_rq *rq = &trap_ctx->rq;
13	bool busy = false;
14	int work_done = 0;
15
16	rcu_read_lock();
17
18	ch_stats->poll++;
19
20	work_done = mlx5e_poll_rx_cq(&rq->cq, budget);
21	busy |= work_done == budget;
22	busy |= rq->post_wqes(rq);
23
24	if (busy) {
25		work_done = budget;
26		goto out;
27	}
28
29	if (unlikely(!napi_complete_done(napi, work_done)))
30		goto out;
31
32	mlx5e_cq_arm(&rq->cq);
33
34out:
35	rcu_read_unlock();
36	return work_done;
37}
38
39static void mlx5e_init_trap_rq(struct mlx5e_trap *t, struct mlx5e_params *params,
40			       struct mlx5e_rq *rq)
41{
42	struct mlx5_core_dev *mdev = t->mdev;
43	struct mlx5e_priv *priv = t->priv;
44
45	rq->wq_type      = params->rq_wq_type;
46	rq->pdev         = t->pdev;
47	rq->netdev       = priv->netdev;
48	rq->priv         = priv;
49	rq->clock        = &mdev->clock;
50	rq->tstamp       = &priv->tstamp;
51	rq->mdev         = mdev;
52	rq->hw_mtu       = MLX5E_SW2HW_MTU(params, params->sw_mtu);
53	rq->stats        = &priv->trap_stats.rq;
54	rq->ptp_cyc2time = mlx5_rq_ts_translator(mdev);
55	xdp_rxq_info_unused(&rq->xdp_rxq);
56	mlx5e_rq_set_trap_handlers(rq, params);
57}
58
59static int mlx5e_open_trap_rq(struct mlx5e_priv *priv, struct mlx5e_trap *t)
60{
61	struct mlx5e_rq_param *rq_param = &t->rq_param;
62	struct mlx5_core_dev *mdev = priv->mdev;
63	struct mlx5e_create_cq_param ccp = {};
64	struct dim_cq_moder trap_moder = {};
65	struct mlx5e_rq *rq = &t->rq;
66	u16 q_counter;
67	int node;
68	int err;
69
70	node = dev_to_node(mdev->device);
71	q_counter = priv->q_counter[0];
72
73	ccp.netdev   = priv->netdev;
74	ccp.wq       = priv->wq;
75	ccp.node     = node;
76	ccp.ch_stats = t->stats;
77	ccp.napi     = &t->napi;
78	ccp.ix       = 0;
79	err = mlx5e_open_cq(priv->mdev, trap_moder, &rq_param->cqp, &ccp, &rq->cq);
80	if (err)
81		return err;
82
83	mlx5e_init_trap_rq(t, &t->params, rq);
84	err = mlx5e_open_rq(&t->params, rq_param, NULL, node, q_counter, rq);
85	if (err)
86		goto err_destroy_cq;
87
88	return 0;
89
90err_destroy_cq:
91	mlx5e_close_cq(&rq->cq);
92
93	return err;
94}
95
96static void mlx5e_close_trap_rq(struct mlx5e_rq *rq)
97{
98	mlx5e_close_rq(rq);
99	mlx5e_close_cq(&rq->cq);
100}
101
102static int mlx5e_create_trap_direct_rq_tir(struct mlx5_core_dev *mdev, struct mlx5e_tir *tir,
103					   u32 rqn)
104{
105	struct mlx5e_tir_builder *builder;
106	int err;
107
108	builder = mlx5e_tir_builder_alloc(false);
109	if (!builder)
110		return -ENOMEM;
111
112	mlx5e_tir_builder_build_inline(builder, mdev->mlx5e_res.hw_objs.td.tdn, rqn);
113	err = mlx5e_tir_init(tir, builder, mdev, true);
114
115	mlx5e_tir_builder_free(builder);
116
117	return err;
118}
119
120static void mlx5e_build_trap_params(struct mlx5_core_dev *mdev,
121				    int max_mtu, struct mlx5e_trap *t)
122{
123	struct mlx5e_params *params = &t->params;
124
125	params->rq_wq_type = MLX5_WQ_TYPE_CYCLIC;
126	mlx5e_init_rq_type_params(mdev, params);
127	params->sw_mtu = max_mtu;
128	mlx5e_build_rq_param(mdev, params, NULL, &t->rq_param);
129}
130
131static struct mlx5e_trap *mlx5e_open_trap(struct mlx5e_priv *priv)
132{
133	int cpu = mlx5_comp_vector_get_cpu(priv->mdev, 0);
134	struct net_device *netdev = priv->netdev;
135	struct mlx5e_trap *t;
136	int err;
137
138	t = kvzalloc_node(sizeof(*t), GFP_KERNEL, cpu_to_node(cpu));
139	if (!t)
140		return ERR_PTR(-ENOMEM);
141
142	mlx5e_build_trap_params(priv->mdev, netdev->max_mtu, t);
143
144	t->priv     = priv;
145	t->mdev     = priv->mdev;
146	t->tstamp   = &priv->tstamp;
147	t->pdev     = mlx5_core_dma_dev(priv->mdev);
148	t->netdev   = priv->netdev;
149	t->mkey_be  = cpu_to_be32(priv->mdev->mlx5e_res.hw_objs.mkey);
150	t->stats    = &priv->trap_stats.ch;
151
152	netif_napi_add(netdev, &t->napi, mlx5e_trap_napi_poll);
153
154	err = mlx5e_open_trap_rq(priv, t);
155	if (unlikely(err))
156		goto err_napi_del;
157
158	err = mlx5e_create_trap_direct_rq_tir(t->mdev, &t->tir, t->rq.rqn);
159	if (err)
160		goto err_close_trap_rq;
161
162	return t;
163
164err_close_trap_rq:
165	mlx5e_close_trap_rq(&t->rq);
166err_napi_del:
167	netif_napi_del(&t->napi);
168	kvfree(t);
169	return ERR_PTR(err);
170}
171
172void mlx5e_close_trap(struct mlx5e_trap *trap)
173{
174	mlx5e_tir_destroy(&trap->tir);
175	mlx5e_close_trap_rq(&trap->rq);
176	netif_napi_del(&trap->napi);
177	kvfree(trap);
178}
179
180static void mlx5e_activate_trap(struct mlx5e_trap *trap)
181{
182	napi_enable(&trap->napi);
183	mlx5e_activate_rq(&trap->rq);
184	mlx5e_trigger_napi_sched(&trap->napi);
185}
186
187void mlx5e_deactivate_trap(struct mlx5e_priv *priv)
188{
189	struct mlx5e_trap *trap = priv->en_trap;
190
191	mlx5e_deactivate_rq(&trap->rq);
192	napi_disable(&trap->napi);
193}
194
195static struct mlx5e_trap *mlx5e_add_trap_queue(struct mlx5e_priv *priv)
196{
197	struct mlx5e_trap *trap;
198
199	trap = mlx5e_open_trap(priv);
200	if (IS_ERR(trap))
201		goto out;
202
203	mlx5e_activate_trap(trap);
204out:
205	return trap;
206}
207
208static void mlx5e_del_trap_queue(struct mlx5e_priv *priv)
209{
210	mlx5e_deactivate_trap(priv);
211	mlx5e_close_trap(priv->en_trap);
212	priv->en_trap = NULL;
213}
214
215static int mlx5e_trap_get_tirn(struct mlx5e_trap *en_trap)
216{
217	return en_trap->tir.tirn;
218}
219
220static int mlx5e_handle_action_trap(struct mlx5e_priv *priv, int trap_id)
221{
222	bool open_queue = !priv->en_trap;
223	struct mlx5e_trap *trap;
224	int err;
225
226	if (open_queue) {
227		trap = mlx5e_add_trap_queue(priv);
228		if (IS_ERR(trap))
229			return PTR_ERR(trap);
230		priv->en_trap = trap;
231	}
232
233	switch (trap_id) {
234	case DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER:
235		err = mlx5e_add_vlan_trap(priv->fs, trap_id, mlx5e_trap_get_tirn(priv->en_trap));
236		if (err)
237			goto err_out;
238		break;
239	case DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER:
240		err = mlx5e_add_mac_trap(priv->fs, trap_id, mlx5e_trap_get_tirn(priv->en_trap));
241		if (err)
242			goto err_out;
243		break;
244	default:
245		netdev_warn(priv->netdev, "%s: Unknown trap id %d\n", __func__, trap_id);
246		err = -EINVAL;
247		goto err_out;
248	}
249	return 0;
250
251err_out:
252	if (open_queue)
253		mlx5e_del_trap_queue(priv);
254	return err;
255}
256
257static int mlx5e_handle_action_drop(struct mlx5e_priv *priv, int trap_id)
258{
259	switch (trap_id) {
260	case DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER:
261		mlx5e_remove_vlan_trap(priv->fs);
262		break;
263	case DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER:
264		mlx5e_remove_mac_trap(priv->fs);
265		break;
266	default:
267		netdev_warn(priv->netdev, "%s: Unknown trap id %d\n", __func__, trap_id);
268		return -EINVAL;
269	}
270	if (priv->en_trap && !mlx5_devlink_trap_get_num_active(priv->mdev))
271		mlx5e_del_trap_queue(priv);
272
273	return 0;
274}
275
276int mlx5e_handle_trap_event(struct mlx5e_priv *priv, struct mlx5_trap_ctx *trap_ctx)
277{
278	int err = 0;
279
280	/* Traps are unarmed when interface is down, no need to update
281	 * them. The configuration is saved in the core driver,
282	 * queried and applied upon interface up operation in
283	 * mlx5e_open_locked().
284	 */
285	if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
286		return 0;
287
288	switch (trap_ctx->action) {
289	case DEVLINK_TRAP_ACTION_TRAP:
290		err = mlx5e_handle_action_trap(priv, trap_ctx->id);
291		break;
292	case DEVLINK_TRAP_ACTION_DROP:
293		err = mlx5e_handle_action_drop(priv, trap_ctx->id);
294		break;
295	default:
296		netdev_warn(priv->netdev, "%s: Unsupported action %d\n", __func__,
297			    trap_ctx->action);
298		err = -EINVAL;
299	}
300	return err;
301}
302
303static int mlx5e_apply_trap(struct mlx5e_priv *priv, int trap_id, bool enable)
304{
305	enum devlink_trap_action action;
306	int err;
307
308	err = mlx5_devlink_traps_get_action(priv->mdev, trap_id, &action);
309	if (err)
310		return err;
311	if (action == DEVLINK_TRAP_ACTION_TRAP)
312		err = enable ? mlx5e_handle_action_trap(priv, trap_id) :
313			       mlx5e_handle_action_drop(priv, trap_id);
314	return err;
315}
316
317static const int mlx5e_traps_arr[] = {
318	DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER,
319	DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER,
320};
321
322int mlx5e_apply_traps(struct mlx5e_priv *priv, bool enable)
323{
324	int err;
325	int i;
326
327	for (i = 0; i < ARRAY_SIZE(mlx5e_traps_arr); i++) {
328		err = mlx5e_apply_trap(priv, mlx5e_traps_arr[i], enable);
329		if (err)
330			return err;
331	}
332	return 0;
333}
334