xref: /linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c (revision 9a379e77033f02c4a071891afdf0f0a01eff8ccb)
1 /*
2  * drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
3  * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
4  * Copyright (c) 2017 Arkadi Sharshevsky <arakdis@mellanox.com>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the names of the copyright holders nor the names of its
15  *    contributors may be used to endorse or promote products derived from
16  *    this software without specific prior written permission.
17  *
18  * Alternatively, this software may be distributed under the terms of the
19  * GNU General Public License ("GPL") version 2 as published by the Free
20  * Software Foundation.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <linux/kernel.h>
36 #include <net/devlink.h>
37 
38 #include "spectrum.h"
39 #include "spectrum_dpipe.h"
40 #include "spectrum_router.h"
41 
42 enum mlxsw_sp_field_metadata_id {
43 	MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT,
44 	MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD,
45 	MLXSW_SP_DPIPE_FIELD_METADATA_L3_DROP,
46 	MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_INDEX,
47 	MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_SIZE,
48 	MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_HASH_INDEX,
49 };
50 
51 static struct devlink_dpipe_field mlxsw_sp_dpipe_fields_metadata[] = {
52 	{
53 		.name = "erif_port",
54 		.id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT,
55 		.bitwidth = 32,
56 		.mapping_type = DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX,
57 	},
58 	{
59 		.name = "l3_forward",
60 		.id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD,
61 		.bitwidth = 1,
62 	},
63 	{
64 		.name = "l3_drop",
65 		.id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_DROP,
66 		.bitwidth = 1,
67 	},
68 	{
69 		.name = "adj_index",
70 		.id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_INDEX,
71 		.bitwidth = 32,
72 	},
73 	{
74 		.name = "adj_size",
75 		.id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_SIZE,
76 		.bitwidth = 32,
77 	},
78 	{
79 		.name = "adj_hash_index",
80 		.id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_HASH_INDEX,
81 		.bitwidth = 32,
82 	},
83 };
84 
85 enum mlxsw_sp_dpipe_header_id {
86 	MLXSW_SP_DPIPE_HEADER_METADATA,
87 };
88 
89 static struct devlink_dpipe_header mlxsw_sp_dpipe_header_metadata = {
90 	.name = "mlxsw_meta",
91 	.id = MLXSW_SP_DPIPE_HEADER_METADATA,
92 	.fields = mlxsw_sp_dpipe_fields_metadata,
93 	.fields_count = ARRAY_SIZE(mlxsw_sp_dpipe_fields_metadata),
94 };
95 
96 static struct devlink_dpipe_header *mlxsw_dpipe_headers[] = {
97 	&mlxsw_sp_dpipe_header_metadata,
98 	&devlink_dpipe_header_ethernet,
99 	&devlink_dpipe_header_ipv4,
100 	&devlink_dpipe_header_ipv6,
101 };
102 
103 static struct devlink_dpipe_headers mlxsw_sp_dpipe_headers = {
104 	.headers = mlxsw_dpipe_headers,
105 	.headers_count = ARRAY_SIZE(mlxsw_dpipe_headers),
106 };
107 
108 static int mlxsw_sp_dpipe_table_erif_actions_dump(void *priv,
109 						  struct sk_buff *skb)
110 {
111 	struct devlink_dpipe_action action = {0};
112 	int err;
113 
114 	action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
115 	action.header = &mlxsw_sp_dpipe_header_metadata;
116 	action.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD;
117 
118 	err = devlink_dpipe_action_put(skb, &action);
119 	if (err)
120 		return err;
121 
122 	action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
123 	action.header = &mlxsw_sp_dpipe_header_metadata;
124 	action.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_DROP;
125 
126 	return devlink_dpipe_action_put(skb, &action);
127 }
128 
129 static int mlxsw_sp_dpipe_table_erif_matches_dump(void *priv,
130 						  struct sk_buff *skb)
131 {
132 	struct devlink_dpipe_match match = {0};
133 
134 	match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
135 	match.header = &mlxsw_sp_dpipe_header_metadata;
136 	match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT;
137 
138 	return devlink_dpipe_match_put(skb, &match);
139 }
140 
141 static void
142 mlxsw_sp_erif_match_action_prepare(struct devlink_dpipe_match *match,
143 				   struct devlink_dpipe_action *action)
144 {
145 	action->type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
146 	action->header = &mlxsw_sp_dpipe_header_metadata;
147 	action->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD;
148 
149 	match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
150 	match->header = &mlxsw_sp_dpipe_header_metadata;
151 	match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT;
152 }
153 
154 static int mlxsw_sp_erif_entry_prepare(struct devlink_dpipe_entry *entry,
155 				       struct devlink_dpipe_value *match_value,
156 				       struct devlink_dpipe_match *match,
157 				       struct devlink_dpipe_value *action_value,
158 				       struct devlink_dpipe_action *action)
159 {
160 	entry->match_values = match_value;
161 	entry->match_values_count = 1;
162 
163 	entry->action_values = action_value;
164 	entry->action_values_count = 1;
165 
166 	match_value->match = match;
167 	match_value->value_size = sizeof(u32);
168 	match_value->value = kmalloc(match_value->value_size, GFP_KERNEL);
169 	if (!match_value->value)
170 		return -ENOMEM;
171 
172 	action_value->action = action;
173 	action_value->value_size = sizeof(u32);
174 	action_value->value = kmalloc(action_value->value_size, GFP_KERNEL);
175 	if (!action_value->value)
176 		goto err_action_alloc;
177 	return 0;
178 
179 err_action_alloc:
180 	kfree(match_value->value);
181 	return -ENOMEM;
182 }
183 
184 static int mlxsw_sp_erif_entry_get(struct mlxsw_sp *mlxsw_sp,
185 				   struct devlink_dpipe_entry *entry,
186 				   struct mlxsw_sp_rif *rif,
187 				   bool counters_enabled)
188 {
189 	u32 *action_value;
190 	u32 *rif_value;
191 	u64 cnt;
192 	int err;
193 
194 	/* Set Match RIF index */
195 	rif_value = entry->match_values->value;
196 	*rif_value = mlxsw_sp_rif_index(rif);
197 	entry->match_values->mapping_value = mlxsw_sp_rif_dev_ifindex(rif);
198 	entry->match_values->mapping_valid = true;
199 
200 	/* Set Action Forwarding */
201 	action_value = entry->action_values->value;
202 	*action_value = 1;
203 
204 	entry->counter_valid = false;
205 	entry->counter = 0;
206 	entry->index = mlxsw_sp_rif_index(rif);
207 
208 	if (!counters_enabled)
209 		return 0;
210 
211 	err = mlxsw_sp_rif_counter_value_get(mlxsw_sp, rif,
212 					     MLXSW_SP_RIF_COUNTER_EGRESS,
213 					     &cnt);
214 	if (!err) {
215 		entry->counter = cnt;
216 		entry->counter_valid = true;
217 	}
218 	return 0;
219 }
220 
221 static int
222 mlxsw_sp_dpipe_table_erif_entries_dump(void *priv, bool counters_enabled,
223 				       struct devlink_dpipe_dump_ctx *dump_ctx)
224 {
225 	struct devlink_dpipe_value match_value, action_value;
226 	struct devlink_dpipe_action action = {0};
227 	struct devlink_dpipe_match match = {0};
228 	struct devlink_dpipe_entry entry = {0};
229 	struct mlxsw_sp *mlxsw_sp = priv;
230 	unsigned int rif_count;
231 	int i, j;
232 	int err;
233 
234 	memset(&match_value, 0, sizeof(match_value));
235 	memset(&action_value, 0, sizeof(action_value));
236 
237 	mlxsw_sp_erif_match_action_prepare(&match, &action);
238 	err = mlxsw_sp_erif_entry_prepare(&entry, &match_value, &match,
239 					  &action_value, &action);
240 	if (err)
241 		return err;
242 
243 	rif_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
244 	rtnl_lock();
245 	i = 0;
246 start_again:
247 	err = devlink_dpipe_entry_ctx_prepare(dump_ctx);
248 	if (err)
249 		return err;
250 	j = 0;
251 	for (; i < rif_count; i++) {
252 		struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i);
253 
254 		if (!rif)
255 			continue;
256 		err = mlxsw_sp_erif_entry_get(mlxsw_sp, &entry, rif,
257 					      counters_enabled);
258 		if (err)
259 			goto err_entry_get;
260 		err = devlink_dpipe_entry_ctx_append(dump_ctx, &entry);
261 		if (err) {
262 			if (err == -EMSGSIZE) {
263 				if (!j)
264 					goto err_entry_append;
265 				break;
266 			}
267 			goto err_entry_append;
268 		}
269 		j++;
270 	}
271 
272 	devlink_dpipe_entry_ctx_close(dump_ctx);
273 	if (i != rif_count)
274 		goto start_again;
275 	rtnl_unlock();
276 
277 	devlink_dpipe_entry_clear(&entry);
278 	return 0;
279 err_entry_append:
280 err_entry_get:
281 	rtnl_unlock();
282 	devlink_dpipe_entry_clear(&entry);
283 	return err;
284 }
285 
286 static int mlxsw_sp_dpipe_table_erif_counters_update(void *priv, bool enable)
287 {
288 	struct mlxsw_sp *mlxsw_sp = priv;
289 	int i;
290 
291 	rtnl_lock();
292 	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
293 		struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i);
294 
295 		if (!rif)
296 			continue;
297 		if (enable)
298 			mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif,
299 						   MLXSW_SP_RIF_COUNTER_EGRESS);
300 		else
301 			mlxsw_sp_rif_counter_free(mlxsw_sp, rif,
302 						  MLXSW_SP_RIF_COUNTER_EGRESS);
303 	}
304 	rtnl_unlock();
305 	return 0;
306 }
307 
308 static u64 mlxsw_sp_dpipe_table_erif_size_get(void *priv)
309 {
310 	struct mlxsw_sp *mlxsw_sp = priv;
311 
312 	return MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
313 }
314 
315 static struct devlink_dpipe_table_ops mlxsw_sp_erif_ops = {
316 	.matches_dump = mlxsw_sp_dpipe_table_erif_matches_dump,
317 	.actions_dump = mlxsw_sp_dpipe_table_erif_actions_dump,
318 	.entries_dump = mlxsw_sp_dpipe_table_erif_entries_dump,
319 	.counters_set_update = mlxsw_sp_dpipe_table_erif_counters_update,
320 	.size_get = mlxsw_sp_dpipe_table_erif_size_get,
321 };
322 
323 static int mlxsw_sp_dpipe_erif_table_init(struct mlxsw_sp *mlxsw_sp)
324 {
325 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
326 
327 	return devlink_dpipe_table_register(devlink,
328 					    MLXSW_SP_DPIPE_TABLE_NAME_ERIF,
329 					    &mlxsw_sp_erif_ops,
330 					    mlxsw_sp, false);
331 }
332 
333 static void mlxsw_sp_dpipe_erif_table_fini(struct mlxsw_sp *mlxsw_sp)
334 {
335 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
336 
337 	devlink_dpipe_table_unregister(devlink, MLXSW_SP_DPIPE_TABLE_NAME_ERIF);
338 }
339 
340 static int mlxsw_sp_dpipe_table_host_matches_dump(struct sk_buff *skb, int type)
341 {
342 	struct devlink_dpipe_match match = {0};
343 	int err;
344 
345 	match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
346 	match.header = &mlxsw_sp_dpipe_header_metadata;
347 	match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT;
348 
349 	err = devlink_dpipe_match_put(skb, &match);
350 	if (err)
351 		return err;
352 
353 	switch (type) {
354 	case AF_INET:
355 		match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
356 		match.header = &devlink_dpipe_header_ipv4;
357 		match.field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP;
358 		break;
359 	case AF_INET6:
360 		match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
361 		match.header = &devlink_dpipe_header_ipv6;
362 		match.field_id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP;
363 		break;
364 	default:
365 		WARN_ON(1);
366 		return -EINVAL;
367 	}
368 
369 	return devlink_dpipe_match_put(skb, &match);
370 }
371 
372 static int
373 mlxsw_sp_dpipe_table_host4_matches_dump(void *priv, struct sk_buff *skb)
374 {
375 	return mlxsw_sp_dpipe_table_host_matches_dump(skb, AF_INET);
376 }
377 
378 static int
379 mlxsw_sp_dpipe_table_host_actions_dump(void *priv, struct sk_buff *skb)
380 {
381 	struct devlink_dpipe_action action = {0};
382 
383 	action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
384 	action.header = &devlink_dpipe_header_ethernet;
385 	action.field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC;
386 
387 	return devlink_dpipe_action_put(skb, &action);
388 }
389 
390 enum mlxsw_sp_dpipe_table_host_match {
391 	MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF,
392 	MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP,
393 	MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT,
394 };
395 
396 static void
397 mlxsw_sp_dpipe_table_host_match_action_prepare(struct devlink_dpipe_match *matches,
398 					       struct devlink_dpipe_action *action,
399 					       int type)
400 {
401 	struct devlink_dpipe_match *match;
402 
403 	match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF];
404 	match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
405 	match->header = &mlxsw_sp_dpipe_header_metadata;
406 	match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT;
407 
408 	match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP];
409 	match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
410 	switch (type) {
411 	case AF_INET:
412 		match->header = &devlink_dpipe_header_ipv4;
413 		match->field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP;
414 		break;
415 	case AF_INET6:
416 		match->header = &devlink_dpipe_header_ipv6;
417 		match->field_id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP;
418 		break;
419 	default:
420 		WARN_ON(1);
421 		return;
422 	}
423 
424 	action->type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
425 	action->header = &devlink_dpipe_header_ethernet;
426 	action->field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC;
427 }
428 
429 static int
430 mlxsw_sp_dpipe_table_host_entry_prepare(struct devlink_dpipe_entry *entry,
431 					struct devlink_dpipe_value *match_values,
432 					struct devlink_dpipe_match *matches,
433 					struct devlink_dpipe_value *action_value,
434 					struct devlink_dpipe_action *action,
435 					int type)
436 {
437 	struct devlink_dpipe_value *match_value;
438 	struct devlink_dpipe_match *match;
439 
440 	entry->match_values = match_values;
441 	entry->match_values_count = MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT;
442 
443 	entry->action_values = action_value;
444 	entry->action_values_count = 1;
445 
446 	match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF];
447 	match_value = &match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF];
448 
449 	match_value->match = match;
450 	match_value->value_size = sizeof(u32);
451 	match_value->value = kmalloc(match_value->value_size, GFP_KERNEL);
452 	if (!match_value->value)
453 		return -ENOMEM;
454 
455 	match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP];
456 	match_value = &match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP];
457 
458 	match_value->match = match;
459 	switch (type) {
460 	case AF_INET:
461 		match_value->value_size = sizeof(u32);
462 		break;
463 	case AF_INET6:
464 		match_value->value_size = sizeof(struct in6_addr);
465 		break;
466 	default:
467 		WARN_ON(1);
468 		return -EINVAL;
469 	}
470 
471 	match_value->value = kmalloc(match_value->value_size, GFP_KERNEL);
472 	if (!match_value->value)
473 		return -ENOMEM;
474 
475 	action_value->action = action;
476 	action_value->value_size = sizeof(u64);
477 	action_value->value = kmalloc(action_value->value_size, GFP_KERNEL);
478 	if (!action_value->value)
479 		return -ENOMEM;
480 
481 	return 0;
482 }
483 
484 static void
485 __mlxsw_sp_dpipe_table_host_entry_fill(struct devlink_dpipe_entry *entry,
486 				       struct mlxsw_sp_rif *rif,
487 				       unsigned char *ha, void *dip)
488 {
489 	struct devlink_dpipe_value *value;
490 	u32 *rif_value;
491 	u8 *ha_value;
492 
493 	/* Set Match RIF index */
494 	value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF];
495 
496 	rif_value = value->value;
497 	*rif_value = mlxsw_sp_rif_index(rif);
498 	value->mapping_value = mlxsw_sp_rif_dev_ifindex(rif);
499 	value->mapping_valid = true;
500 
501 	/* Set Match DIP */
502 	value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP];
503 	memcpy(value->value, dip, value->value_size);
504 
505 	/* Set Action DMAC */
506 	value = entry->action_values;
507 	ha_value = value->value;
508 	ether_addr_copy(ha_value, ha);
509 }
510 
511 static void
512 mlxsw_sp_dpipe_table_host4_entry_fill(struct devlink_dpipe_entry *entry,
513 				      struct mlxsw_sp_neigh_entry *neigh_entry,
514 				      struct mlxsw_sp_rif *rif)
515 {
516 	unsigned char *ha;
517 	u32 dip;
518 
519 	ha = mlxsw_sp_neigh_entry_ha(neigh_entry);
520 	dip = mlxsw_sp_neigh4_entry_dip(neigh_entry);
521 	__mlxsw_sp_dpipe_table_host_entry_fill(entry, rif, ha, &dip);
522 }
523 
524 static void
525 mlxsw_sp_dpipe_table_host6_entry_fill(struct devlink_dpipe_entry *entry,
526 				      struct mlxsw_sp_neigh_entry *neigh_entry,
527 				      struct mlxsw_sp_rif *rif)
528 {
529 	struct in6_addr *dip;
530 	unsigned char *ha;
531 
532 	ha = mlxsw_sp_neigh_entry_ha(neigh_entry);
533 	dip = mlxsw_sp_neigh6_entry_dip(neigh_entry);
534 
535 	__mlxsw_sp_dpipe_table_host_entry_fill(entry, rif, ha, dip);
536 }
537 
538 static void
539 mlxsw_sp_dpipe_table_host_entry_fill(struct mlxsw_sp *mlxsw_sp,
540 				     struct devlink_dpipe_entry *entry,
541 				     struct mlxsw_sp_neigh_entry *neigh_entry,
542 				     struct mlxsw_sp_rif *rif,
543 				     int type)
544 {
545 	int err;
546 
547 	switch (type) {
548 	case AF_INET:
549 		mlxsw_sp_dpipe_table_host4_entry_fill(entry, neigh_entry, rif);
550 		break;
551 	case AF_INET6:
552 		mlxsw_sp_dpipe_table_host6_entry_fill(entry, neigh_entry, rif);
553 		break;
554 	default:
555 		WARN_ON(1);
556 		return;
557 	}
558 
559 	err = mlxsw_sp_neigh_counter_get(mlxsw_sp, neigh_entry,
560 					 &entry->counter);
561 	if (!err)
562 		entry->counter_valid = true;
563 }
564 
565 static int
566 mlxsw_sp_dpipe_table_host_entries_get(struct mlxsw_sp *mlxsw_sp,
567 				      struct devlink_dpipe_entry *entry,
568 				      bool counters_enabled,
569 				      struct devlink_dpipe_dump_ctx *dump_ctx,
570 				      int type)
571 {
572 	int rif_neigh_count = 0;
573 	int rif_neigh_skip = 0;
574 	int neigh_count = 0;
575 	int rif_count;
576 	int i, j;
577 	int err;
578 
579 	rtnl_lock();
580 	i = 0;
581 	rif_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
582 start_again:
583 	err = devlink_dpipe_entry_ctx_prepare(dump_ctx);
584 	if (err)
585 		goto err_ctx_prepare;
586 	j = 0;
587 	rif_neigh_skip = rif_neigh_count;
588 	for (; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
589 		struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i);
590 		struct mlxsw_sp_neigh_entry *neigh_entry;
591 
592 		if (!rif)
593 			continue;
594 
595 		rif_neigh_count = 0;
596 		mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) {
597 			int neigh_type = mlxsw_sp_neigh_entry_type(neigh_entry);
598 
599 			if (neigh_type != type)
600 				continue;
601 
602 			if (neigh_type == AF_INET6 &&
603 			    mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
604 				continue;
605 
606 			if (rif_neigh_count < rif_neigh_skip)
607 				goto skip;
608 
609 			mlxsw_sp_dpipe_table_host_entry_fill(mlxsw_sp, entry,
610 							     neigh_entry, rif,
611 							     type);
612 			entry->index = neigh_count;
613 			err = devlink_dpipe_entry_ctx_append(dump_ctx, entry);
614 			if (err) {
615 				if (err == -EMSGSIZE) {
616 					if (!j)
617 						goto err_entry_append;
618 					else
619 						goto out;
620 				}
621 				goto err_entry_append;
622 			}
623 			neigh_count++;
624 			j++;
625 skip:
626 			rif_neigh_count++;
627 		}
628 		rif_neigh_skip = 0;
629 	}
630 out:
631 	devlink_dpipe_entry_ctx_close(dump_ctx);
632 	if (i != rif_count)
633 		goto start_again;
634 
635 	rtnl_unlock();
636 	return 0;
637 
638 err_ctx_prepare:
639 err_entry_append:
640 	rtnl_unlock();
641 	return err;
642 }
643 
644 static int
645 mlxsw_sp_dpipe_table_host_entries_dump(struct mlxsw_sp *mlxsw_sp,
646 				       bool counters_enabled,
647 				       struct devlink_dpipe_dump_ctx *dump_ctx,
648 				       int type)
649 {
650 	struct devlink_dpipe_value match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT];
651 	struct devlink_dpipe_match matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT];
652 	struct devlink_dpipe_value action_value;
653 	struct devlink_dpipe_action action = {0};
654 	struct devlink_dpipe_entry entry = {0};
655 	int err;
656 
657 	memset(matches, 0, MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT *
658 			   sizeof(matches[0]));
659 	memset(match_values, 0, MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT *
660 				sizeof(match_values[0]));
661 	memset(&action_value, 0, sizeof(action_value));
662 
663 	mlxsw_sp_dpipe_table_host_match_action_prepare(matches, &action, type);
664 	err = mlxsw_sp_dpipe_table_host_entry_prepare(&entry, match_values,
665 						      matches, &action_value,
666 						      &action, type);
667 	if (err)
668 		goto out;
669 
670 	err = mlxsw_sp_dpipe_table_host_entries_get(mlxsw_sp, &entry,
671 						    counters_enabled, dump_ctx,
672 						    type);
673 out:
674 	devlink_dpipe_entry_clear(&entry);
675 	return err;
676 }
677 
678 static int
679 mlxsw_sp_dpipe_table_host4_entries_dump(void *priv, bool counters_enabled,
680 					struct devlink_dpipe_dump_ctx *dump_ctx)
681 {
682 	struct mlxsw_sp *mlxsw_sp = priv;
683 
684 	return mlxsw_sp_dpipe_table_host_entries_dump(mlxsw_sp,
685 						      counters_enabled,
686 						      dump_ctx, AF_INET);
687 }
688 
689 static void
690 mlxsw_sp_dpipe_table_host_counters_update(struct mlxsw_sp *mlxsw_sp,
691 					  bool enable, int type)
692 {
693 	int i;
694 
695 	rtnl_lock();
696 	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
697 		struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i);
698 		struct mlxsw_sp_neigh_entry *neigh_entry;
699 
700 		if (!rif)
701 			continue;
702 		mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) {
703 			int neigh_type = mlxsw_sp_neigh_entry_type(neigh_entry);
704 
705 			if (neigh_type != type)
706 				continue;
707 
708 			if (neigh_type == AF_INET6 &&
709 			    mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
710 				continue;
711 
712 			mlxsw_sp_neigh_entry_counter_update(mlxsw_sp,
713 							    neigh_entry,
714 							    enable);
715 		}
716 	}
717 	rtnl_unlock();
718 }
719 
720 static int mlxsw_sp_dpipe_table_host4_counters_update(void *priv, bool enable)
721 {
722 	struct mlxsw_sp *mlxsw_sp = priv;
723 
724 	mlxsw_sp_dpipe_table_host_counters_update(mlxsw_sp, enable, AF_INET);
725 	return 0;
726 }
727 
728 static u64
729 mlxsw_sp_dpipe_table_host_size_get(struct mlxsw_sp *mlxsw_sp, int type)
730 {
731 	u64 size = 0;
732 	int i;
733 
734 	rtnl_lock();
735 	for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) {
736 		struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i);
737 		struct mlxsw_sp_neigh_entry *neigh_entry;
738 
739 		if (!rif)
740 			continue;
741 		mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) {
742 			int neigh_type = mlxsw_sp_neigh_entry_type(neigh_entry);
743 
744 			if (neigh_type != type)
745 				continue;
746 
747 			if (neigh_type == AF_INET6 &&
748 			    mlxsw_sp_neigh_ipv6_ignore(neigh_entry))
749 				continue;
750 
751 			size++;
752 		}
753 	}
754 	rtnl_unlock();
755 
756 	return size;
757 }
758 
759 static u64 mlxsw_sp_dpipe_table_host4_size_get(void *priv)
760 {
761 	struct mlxsw_sp *mlxsw_sp = priv;
762 
763 	return mlxsw_sp_dpipe_table_host_size_get(mlxsw_sp, AF_INET);
764 }
765 
766 static struct devlink_dpipe_table_ops mlxsw_sp_host4_ops = {
767 	.matches_dump = mlxsw_sp_dpipe_table_host4_matches_dump,
768 	.actions_dump = mlxsw_sp_dpipe_table_host_actions_dump,
769 	.entries_dump = mlxsw_sp_dpipe_table_host4_entries_dump,
770 	.counters_set_update = mlxsw_sp_dpipe_table_host4_counters_update,
771 	.size_get = mlxsw_sp_dpipe_table_host4_size_get,
772 };
773 
774 #define MLXSW_SP_DPIPE_TABLE_RESOURCE_UNIT_HOST4 1
775 
776 static int mlxsw_sp_dpipe_host4_table_init(struct mlxsw_sp *mlxsw_sp)
777 {
778 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
779 	int err;
780 
781 	err = devlink_dpipe_table_register(devlink,
782 					   MLXSW_SP_DPIPE_TABLE_NAME_HOST4,
783 					   &mlxsw_sp_host4_ops,
784 					   mlxsw_sp, false);
785 	if (err)
786 		return err;
787 
788 	err = devlink_dpipe_table_resource_set(devlink,
789 					       MLXSW_SP_DPIPE_TABLE_NAME_HOST4,
790 					       MLXSW_SP_RESOURCE_KVD_HASH_SINGLE,
791 					       MLXSW_SP_DPIPE_TABLE_RESOURCE_UNIT_HOST4);
792 	if (err)
793 		goto err_resource_set;
794 
795 	return 0;
796 
797 err_resource_set:
798 	devlink_dpipe_table_unregister(devlink,
799 				       MLXSW_SP_DPIPE_TABLE_NAME_HOST4);
800 	return err;
801 }
802 
803 static void mlxsw_sp_dpipe_host4_table_fini(struct mlxsw_sp *mlxsw_sp)
804 {
805 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
806 
807 	devlink_dpipe_table_unregister(devlink,
808 				       MLXSW_SP_DPIPE_TABLE_NAME_HOST4);
809 }
810 
811 static int
812 mlxsw_sp_dpipe_table_host6_matches_dump(void *priv, struct sk_buff *skb)
813 {
814 	return mlxsw_sp_dpipe_table_host_matches_dump(skb, AF_INET6);
815 }
816 
817 static int
818 mlxsw_sp_dpipe_table_host6_entries_dump(void *priv, bool counters_enabled,
819 					struct devlink_dpipe_dump_ctx *dump_ctx)
820 {
821 	struct mlxsw_sp *mlxsw_sp = priv;
822 
823 	return mlxsw_sp_dpipe_table_host_entries_dump(mlxsw_sp,
824 						      counters_enabled,
825 						      dump_ctx, AF_INET6);
826 }
827 
828 static int mlxsw_sp_dpipe_table_host6_counters_update(void *priv, bool enable)
829 {
830 	struct mlxsw_sp *mlxsw_sp = priv;
831 
832 	mlxsw_sp_dpipe_table_host_counters_update(mlxsw_sp, enable, AF_INET6);
833 	return 0;
834 }
835 
836 static u64 mlxsw_sp_dpipe_table_host6_size_get(void *priv)
837 {
838 	struct mlxsw_sp *mlxsw_sp = priv;
839 
840 	return mlxsw_sp_dpipe_table_host_size_get(mlxsw_sp, AF_INET6);
841 }
842 
843 static struct devlink_dpipe_table_ops mlxsw_sp_host6_ops = {
844 	.matches_dump = mlxsw_sp_dpipe_table_host6_matches_dump,
845 	.actions_dump = mlxsw_sp_dpipe_table_host_actions_dump,
846 	.entries_dump = mlxsw_sp_dpipe_table_host6_entries_dump,
847 	.counters_set_update = mlxsw_sp_dpipe_table_host6_counters_update,
848 	.size_get = mlxsw_sp_dpipe_table_host6_size_get,
849 };
850 
851 #define MLXSW_SP_DPIPE_TABLE_RESOURCE_UNIT_HOST6 2
852 
853 static int mlxsw_sp_dpipe_host6_table_init(struct mlxsw_sp *mlxsw_sp)
854 {
855 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
856 	int err;
857 
858 	err = devlink_dpipe_table_register(devlink,
859 					   MLXSW_SP_DPIPE_TABLE_NAME_HOST6,
860 					   &mlxsw_sp_host6_ops,
861 					   mlxsw_sp, false);
862 	if (err)
863 		return err;
864 
865 	err = devlink_dpipe_table_resource_set(devlink,
866 					       MLXSW_SP_DPIPE_TABLE_NAME_HOST6,
867 					       MLXSW_SP_RESOURCE_KVD_HASH_DOUBLE,
868 					       MLXSW_SP_DPIPE_TABLE_RESOURCE_UNIT_HOST6);
869 	if (err)
870 		goto err_resource_set;
871 
872 	return 0;
873 
874 err_resource_set:
875 	devlink_dpipe_table_unregister(devlink,
876 				       MLXSW_SP_DPIPE_TABLE_NAME_HOST6);
877 	return err;
878 }
879 
880 static void mlxsw_sp_dpipe_host6_table_fini(struct mlxsw_sp *mlxsw_sp)
881 {
882 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
883 
884 	devlink_dpipe_table_unregister(devlink,
885 				       MLXSW_SP_DPIPE_TABLE_NAME_HOST6);
886 }
887 
888 static int mlxsw_sp_dpipe_table_adj_matches_dump(void *priv,
889 						 struct sk_buff *skb)
890 {
891 	struct devlink_dpipe_match match = {0};
892 	int err;
893 
894 	match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
895 	match.header = &mlxsw_sp_dpipe_header_metadata;
896 	match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_INDEX;
897 
898 	err = devlink_dpipe_match_put(skb, &match);
899 	if (err)
900 		return err;
901 
902 	match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
903 	match.header = &mlxsw_sp_dpipe_header_metadata;
904 	match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_SIZE;
905 
906 	err = devlink_dpipe_match_put(skb, &match);
907 	if (err)
908 		return err;
909 
910 	match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
911 	match.header = &mlxsw_sp_dpipe_header_metadata;
912 	match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_HASH_INDEX;
913 
914 	return devlink_dpipe_match_put(skb, &match);
915 }
916 
917 static int mlxsw_sp_dpipe_table_adj_actions_dump(void *priv,
918 						 struct sk_buff *skb)
919 {
920 	struct devlink_dpipe_action action = {0};
921 	int err;
922 
923 	action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
924 	action.header = &devlink_dpipe_header_ethernet;
925 	action.field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC;
926 
927 	err = devlink_dpipe_action_put(skb, &action);
928 	if (err)
929 		return err;
930 
931 	action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
932 	action.header = &mlxsw_sp_dpipe_header_metadata;
933 	action.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT;
934 
935 	return devlink_dpipe_action_put(skb, &action);
936 }
937 
938 static u64 mlxsw_sp_dpipe_table_adj_size(struct mlxsw_sp *mlxsw_sp)
939 {
940 	struct mlxsw_sp_nexthop *nh;
941 	u64 size = 0;
942 
943 	mlxsw_sp_nexthop_for_each(nh, mlxsw_sp->router)
944 		if (mlxsw_sp_nexthop_offload(nh) &&
945 		    !mlxsw_sp_nexthop_group_has_ipip(nh))
946 			size++;
947 	return size;
948 }
949 
950 enum mlxsw_sp_dpipe_table_adj_match {
951 	MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_INDEX,
952 	MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_SIZE,
953 	MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_HASH_INDEX,
954 	MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT,
955 };
956 
957 enum mlxsw_sp_dpipe_table_adj_action {
958 	MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_DST_MAC,
959 	MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_ERIF_PORT,
960 	MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT,
961 };
962 
963 static void
964 mlxsw_sp_dpipe_table_adj_match_action_prepare(struct devlink_dpipe_match *matches,
965 					      struct devlink_dpipe_action *actions)
966 {
967 	struct devlink_dpipe_action *action;
968 	struct devlink_dpipe_match *match;
969 
970 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_INDEX];
971 	match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
972 	match->header = &mlxsw_sp_dpipe_header_metadata;
973 	match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_INDEX;
974 
975 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_SIZE];
976 	match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
977 	match->header = &mlxsw_sp_dpipe_header_metadata;
978 	match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_SIZE;
979 
980 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_HASH_INDEX];
981 	match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
982 	match->header = &mlxsw_sp_dpipe_header_metadata;
983 	match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_HASH_INDEX;
984 
985 	action = &actions[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_DST_MAC];
986 	action->type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
987 	action->header = &devlink_dpipe_header_ethernet;
988 	action->field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC;
989 
990 	action = &actions[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_ERIF_PORT];
991 	action->type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
992 	action->header = &mlxsw_sp_dpipe_header_metadata;
993 	action->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT;
994 }
995 
996 static int
997 mlxsw_sp_dpipe_table_adj_entry_prepare(struct devlink_dpipe_entry *entry,
998 				       struct devlink_dpipe_value *match_values,
999 				       struct devlink_dpipe_match *matches,
1000 				       struct devlink_dpipe_value *action_values,
1001 				       struct devlink_dpipe_action *actions)
1002 {	struct devlink_dpipe_value *action_value;
1003 	struct devlink_dpipe_value *match_value;
1004 	struct devlink_dpipe_action *action;
1005 	struct devlink_dpipe_match *match;
1006 
1007 	entry->match_values = match_values;
1008 	entry->match_values_count = MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT;
1009 
1010 	entry->action_values = action_values;
1011 	entry->action_values_count = MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT;
1012 
1013 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_INDEX];
1014 	match_value = &match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_INDEX];
1015 
1016 	match_value->match = match;
1017 	match_value->value_size = sizeof(u32);
1018 	match_value->value = kmalloc(match_value->value_size, GFP_KERNEL);
1019 	if (!match_value->value)
1020 		return -ENOMEM;
1021 
1022 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_SIZE];
1023 	match_value = &match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_SIZE];
1024 
1025 	match_value->match = match;
1026 	match_value->value_size = sizeof(u32);
1027 	match_value->value = kmalloc(match_value->value_size, GFP_KERNEL);
1028 	if (!match_value->value)
1029 		return -ENOMEM;
1030 
1031 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_HASH_INDEX];
1032 	match_value = &match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_HASH_INDEX];
1033 
1034 	match_value->match = match;
1035 	match_value->value_size = sizeof(u32);
1036 	match_value->value = kmalloc(match_value->value_size, GFP_KERNEL);
1037 	if (!match_value->value)
1038 		return -ENOMEM;
1039 
1040 	action = &actions[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_DST_MAC];
1041 	action_value = &action_values[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_DST_MAC];
1042 
1043 	action_value->action = action;
1044 	action_value->value_size = sizeof(u64);
1045 	action_value->value = kmalloc(action_value->value_size, GFP_KERNEL);
1046 	if (!action_value->value)
1047 		return -ENOMEM;
1048 
1049 	action = &actions[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_ERIF_PORT];
1050 	action_value = &action_values[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_ERIF_PORT];
1051 
1052 	action_value->action = action;
1053 	action_value->value_size = sizeof(u32);
1054 	action_value->value = kmalloc(action_value->value_size, GFP_KERNEL);
1055 	if (!action_value->value)
1056 		return -ENOMEM;
1057 
1058 	return 0;
1059 }
1060 
1061 static void
1062 __mlxsw_sp_dpipe_table_adj_entry_fill(struct devlink_dpipe_entry *entry,
1063 				      u32 adj_index, u32 adj_size,
1064 				      u32 adj_hash_index, unsigned char *ha,
1065 				      struct mlxsw_sp_rif *rif)
1066 {
1067 	struct devlink_dpipe_value *value;
1068 	u32 *p_rif_value;
1069 	u32 *p_index;
1070 
1071 	value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_INDEX];
1072 	p_index = value->value;
1073 	*p_index = adj_index;
1074 
1075 	value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_SIZE];
1076 	p_index = value->value;
1077 	*p_index = adj_size;
1078 
1079 	value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_HASH_INDEX];
1080 	p_index = value->value;
1081 	*p_index = adj_hash_index;
1082 
1083 	value = &entry->action_values[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_DST_MAC];
1084 	ether_addr_copy(value->value, ha);
1085 
1086 	value = &entry->action_values[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_ERIF_PORT];
1087 	p_rif_value = value->value;
1088 	*p_rif_value = mlxsw_sp_rif_index(rif);
1089 	value->mapping_value = mlxsw_sp_rif_dev_ifindex(rif);
1090 	value->mapping_valid = true;
1091 }
1092 
1093 static void mlxsw_sp_dpipe_table_adj_entry_fill(struct mlxsw_sp *mlxsw_sp,
1094 						struct mlxsw_sp_nexthop *nh,
1095 						struct devlink_dpipe_entry *entry)
1096 {
1097 	struct mlxsw_sp_rif *rif = mlxsw_sp_nexthop_rif(nh);
1098 	unsigned char *ha = mlxsw_sp_nexthop_ha(nh);
1099 	u32 adj_hash_index = 0;
1100 	u32 adj_index = 0;
1101 	u32 adj_size = 0;
1102 	int err;
1103 
1104 	mlxsw_sp_nexthop_indexes(nh, &adj_index, &adj_size, &adj_hash_index);
1105 	__mlxsw_sp_dpipe_table_adj_entry_fill(entry, adj_index, adj_size,
1106 					      adj_hash_index, ha, rif);
1107 	err = mlxsw_sp_nexthop_counter_get(mlxsw_sp, nh, &entry->counter);
1108 	if (!err)
1109 		entry->counter_valid = true;
1110 }
1111 
1112 static int
1113 mlxsw_sp_dpipe_table_adj_entries_get(struct mlxsw_sp *mlxsw_sp,
1114 				     struct devlink_dpipe_entry *entry,
1115 				     bool counters_enabled,
1116 				     struct devlink_dpipe_dump_ctx *dump_ctx)
1117 {
1118 	struct mlxsw_sp_nexthop *nh;
1119 	int entry_index = 0;
1120 	int nh_count_max;
1121 	int nh_count = 0;
1122 	int nh_skip;
1123 	int j;
1124 	int err;
1125 
1126 	rtnl_lock();
1127 	nh_count_max = mlxsw_sp_dpipe_table_adj_size(mlxsw_sp);
1128 start_again:
1129 	err = devlink_dpipe_entry_ctx_prepare(dump_ctx);
1130 	if (err)
1131 		goto err_ctx_prepare;
1132 	j = 0;
1133 	nh_skip = nh_count;
1134 	nh_count = 0;
1135 	mlxsw_sp_nexthop_for_each(nh, mlxsw_sp->router) {
1136 		if (!mlxsw_sp_nexthop_offload(nh) ||
1137 		    mlxsw_sp_nexthop_group_has_ipip(nh))
1138 			continue;
1139 
1140 		if (nh_count < nh_skip)
1141 			goto skip;
1142 
1143 		mlxsw_sp_dpipe_table_adj_entry_fill(mlxsw_sp, nh, entry);
1144 		entry->index = entry_index;
1145 		err = devlink_dpipe_entry_ctx_append(dump_ctx, entry);
1146 		if (err) {
1147 			if (err == -EMSGSIZE) {
1148 				if (!j)
1149 					goto err_entry_append;
1150 				break;
1151 			}
1152 			goto err_entry_append;
1153 		}
1154 		entry_index++;
1155 		j++;
1156 skip:
1157 		nh_count++;
1158 	}
1159 
1160 	devlink_dpipe_entry_ctx_close(dump_ctx);
1161 	if (nh_count != nh_count_max)
1162 		goto start_again;
1163 	rtnl_unlock();
1164 
1165 	return 0;
1166 
1167 err_ctx_prepare:
1168 err_entry_append:
1169 	rtnl_unlock();
1170 	return err;
1171 }
1172 
1173 static int
1174 mlxsw_sp_dpipe_table_adj_entries_dump(void *priv, bool counters_enabled,
1175 				      struct devlink_dpipe_dump_ctx *dump_ctx)
1176 {
1177 	struct devlink_dpipe_value action_values[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT];
1178 	struct devlink_dpipe_value match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT];
1179 	struct devlink_dpipe_action actions[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT];
1180 	struct devlink_dpipe_match matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT];
1181 	struct devlink_dpipe_entry entry = {0};
1182 	struct mlxsw_sp *mlxsw_sp = priv;
1183 	int err;
1184 
1185 	memset(matches, 0, MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT *
1186 			   sizeof(matches[0]));
1187 	memset(match_values, 0, MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT *
1188 				sizeof(match_values[0]));
1189 	memset(actions, 0, MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT *
1190 			   sizeof(actions[0]));
1191 	memset(action_values, 0, MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT *
1192 				 sizeof(action_values[0]));
1193 
1194 	mlxsw_sp_dpipe_table_adj_match_action_prepare(matches, actions);
1195 	err = mlxsw_sp_dpipe_table_adj_entry_prepare(&entry,
1196 						     match_values, matches,
1197 						     action_values, actions);
1198 	if (err)
1199 		goto out;
1200 
1201 	err = mlxsw_sp_dpipe_table_adj_entries_get(mlxsw_sp, &entry,
1202 						   counters_enabled, dump_ctx);
1203 out:
1204 	devlink_dpipe_entry_clear(&entry);
1205 	return err;
1206 }
1207 
1208 static int mlxsw_sp_dpipe_table_adj_counters_update(void *priv, bool enable)
1209 {
1210 	struct mlxsw_sp *mlxsw_sp = priv;
1211 	struct mlxsw_sp_nexthop *nh;
1212 	u32 adj_hash_index = 0;
1213 	u32 adj_index = 0;
1214 	u32 adj_size = 0;
1215 
1216 	mlxsw_sp_nexthop_for_each(nh, mlxsw_sp->router) {
1217 		if (!mlxsw_sp_nexthop_offload(nh) ||
1218 		    mlxsw_sp_nexthop_group_has_ipip(nh))
1219 			continue;
1220 
1221 		mlxsw_sp_nexthop_indexes(nh, &adj_index, &adj_size,
1222 					 &adj_hash_index);
1223 		if (enable)
1224 			mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh);
1225 		else
1226 			mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh);
1227 		mlxsw_sp_nexthop_update(mlxsw_sp,
1228 					adj_index + adj_hash_index, nh);
1229 	}
1230 	return 0;
1231 }
1232 
1233 static u64
1234 mlxsw_sp_dpipe_table_adj_size_get(void *priv)
1235 {
1236 	struct mlxsw_sp *mlxsw_sp = priv;
1237 	u64 size;
1238 
1239 	rtnl_lock();
1240 	size = mlxsw_sp_dpipe_table_adj_size(mlxsw_sp);
1241 	rtnl_unlock();
1242 
1243 	return size;
1244 }
1245 
1246 static struct devlink_dpipe_table_ops mlxsw_sp_dpipe_table_adj_ops = {
1247 	.matches_dump = mlxsw_sp_dpipe_table_adj_matches_dump,
1248 	.actions_dump = mlxsw_sp_dpipe_table_adj_actions_dump,
1249 	.entries_dump = mlxsw_sp_dpipe_table_adj_entries_dump,
1250 	.counters_set_update = mlxsw_sp_dpipe_table_adj_counters_update,
1251 	.size_get = mlxsw_sp_dpipe_table_adj_size_get,
1252 };
1253 
1254 #define MLXSW_SP_DPIPE_TABLE_RESOURCE_UNIT_ADJ 1
1255 
1256 static int mlxsw_sp_dpipe_adj_table_init(struct mlxsw_sp *mlxsw_sp)
1257 {
1258 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1259 	int err;
1260 
1261 	err = devlink_dpipe_table_register(devlink,
1262 					   MLXSW_SP_DPIPE_TABLE_NAME_ADJ,
1263 					   &mlxsw_sp_dpipe_table_adj_ops,
1264 					   mlxsw_sp, false);
1265 	if (err)
1266 		return err;
1267 
1268 	err = devlink_dpipe_table_resource_set(devlink,
1269 					       MLXSW_SP_DPIPE_TABLE_NAME_ADJ,
1270 					       MLXSW_SP_RESOURCE_KVD_LINEAR,
1271 					       MLXSW_SP_DPIPE_TABLE_RESOURCE_UNIT_ADJ);
1272 	if (err)
1273 		goto err_resource_set;
1274 
1275 	return 0;
1276 
1277 err_resource_set:
1278 	devlink_dpipe_table_unregister(devlink,
1279 				       MLXSW_SP_DPIPE_TABLE_NAME_ADJ);
1280 	return err;
1281 }
1282 
1283 static void mlxsw_sp_dpipe_adj_table_fini(struct mlxsw_sp *mlxsw_sp)
1284 {
1285 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1286 
1287 	devlink_dpipe_table_unregister(devlink,
1288 				       MLXSW_SP_DPIPE_TABLE_NAME_ADJ);
1289 }
1290 
1291 int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp)
1292 {
1293 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1294 	int err;
1295 
1296 	err = devlink_dpipe_headers_register(devlink,
1297 					     &mlxsw_sp_dpipe_headers);
1298 	if (err)
1299 		return err;
1300 	err = mlxsw_sp_dpipe_erif_table_init(mlxsw_sp);
1301 	if (err)
1302 		goto err_erif_table_init;
1303 
1304 	err = mlxsw_sp_dpipe_host4_table_init(mlxsw_sp);
1305 	if (err)
1306 		goto err_host4_table_init;
1307 
1308 	err = mlxsw_sp_dpipe_host6_table_init(mlxsw_sp);
1309 	if (err)
1310 		goto err_host6_table_init;
1311 
1312 	err = mlxsw_sp_dpipe_adj_table_init(mlxsw_sp);
1313 	if (err)
1314 		goto err_adj_table_init;
1315 
1316 	return 0;
1317 err_adj_table_init:
1318 	mlxsw_sp_dpipe_host6_table_fini(mlxsw_sp);
1319 err_host6_table_init:
1320 	mlxsw_sp_dpipe_host4_table_fini(mlxsw_sp);
1321 err_host4_table_init:
1322 	mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp);
1323 err_erif_table_init:
1324 	devlink_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core));
1325 	return err;
1326 }
1327 
1328 void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp)
1329 {
1330 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1331 
1332 	mlxsw_sp_dpipe_adj_table_fini(mlxsw_sp);
1333 	mlxsw_sp_dpipe_host6_table_fini(mlxsw_sp);
1334 	mlxsw_sp_dpipe_host4_table_fini(mlxsw_sp);
1335 	mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp);
1336 	devlink_dpipe_headers_unregister(devlink);
1337 }
1338