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 static int mlxsw_sp_dpipe_host4_table_init(struct mlxsw_sp *mlxsw_sp)
775 {
776 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
777 
778 	return devlink_dpipe_table_register(devlink,
779 					    MLXSW_SP_DPIPE_TABLE_NAME_HOST4,
780 					    &mlxsw_sp_host4_ops,
781 					    mlxsw_sp, false);
782 }
783 
784 static void mlxsw_sp_dpipe_host4_table_fini(struct mlxsw_sp *mlxsw_sp)
785 {
786 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
787 
788 	devlink_dpipe_table_unregister(devlink,
789 				       MLXSW_SP_DPIPE_TABLE_NAME_HOST4);
790 }
791 
792 static int
793 mlxsw_sp_dpipe_table_host6_matches_dump(void *priv, struct sk_buff *skb)
794 {
795 	return mlxsw_sp_dpipe_table_host_matches_dump(skb, AF_INET6);
796 }
797 
798 static int
799 mlxsw_sp_dpipe_table_host6_entries_dump(void *priv, bool counters_enabled,
800 					struct devlink_dpipe_dump_ctx *dump_ctx)
801 {
802 	struct mlxsw_sp *mlxsw_sp = priv;
803 
804 	return mlxsw_sp_dpipe_table_host_entries_dump(mlxsw_sp,
805 						      counters_enabled,
806 						      dump_ctx, AF_INET6);
807 }
808 
809 static int mlxsw_sp_dpipe_table_host6_counters_update(void *priv, bool enable)
810 {
811 	struct mlxsw_sp *mlxsw_sp = priv;
812 
813 	mlxsw_sp_dpipe_table_host_counters_update(mlxsw_sp, enable, AF_INET6);
814 	return 0;
815 }
816 
817 static u64 mlxsw_sp_dpipe_table_host6_size_get(void *priv)
818 {
819 	struct mlxsw_sp *mlxsw_sp = priv;
820 
821 	return mlxsw_sp_dpipe_table_host_size_get(mlxsw_sp, AF_INET6);
822 }
823 
824 static struct devlink_dpipe_table_ops mlxsw_sp_host6_ops = {
825 	.matches_dump = mlxsw_sp_dpipe_table_host6_matches_dump,
826 	.actions_dump = mlxsw_sp_dpipe_table_host_actions_dump,
827 	.entries_dump = mlxsw_sp_dpipe_table_host6_entries_dump,
828 	.counters_set_update = mlxsw_sp_dpipe_table_host6_counters_update,
829 	.size_get = mlxsw_sp_dpipe_table_host6_size_get,
830 };
831 
832 static int mlxsw_sp_dpipe_host6_table_init(struct mlxsw_sp *mlxsw_sp)
833 {
834 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
835 
836 	return devlink_dpipe_table_register(devlink,
837 					    MLXSW_SP_DPIPE_TABLE_NAME_HOST6,
838 					    &mlxsw_sp_host6_ops,
839 					    mlxsw_sp, false);
840 }
841 
842 static void mlxsw_sp_dpipe_host6_table_fini(struct mlxsw_sp *mlxsw_sp)
843 {
844 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
845 
846 	devlink_dpipe_table_unregister(devlink,
847 				       MLXSW_SP_DPIPE_TABLE_NAME_HOST6);
848 }
849 
850 static int mlxsw_sp_dpipe_table_adj_matches_dump(void *priv,
851 						 struct sk_buff *skb)
852 {
853 	struct devlink_dpipe_match match = {0};
854 	int err;
855 
856 	match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
857 	match.header = &mlxsw_sp_dpipe_header_metadata;
858 	match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_INDEX;
859 
860 	err = devlink_dpipe_match_put(skb, &match);
861 	if (err)
862 		return err;
863 
864 	match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
865 	match.header = &mlxsw_sp_dpipe_header_metadata;
866 	match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_SIZE;
867 
868 	err = devlink_dpipe_match_put(skb, &match);
869 	if (err)
870 		return err;
871 
872 	match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
873 	match.header = &mlxsw_sp_dpipe_header_metadata;
874 	match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_HASH_INDEX;
875 
876 	return devlink_dpipe_match_put(skb, &match);
877 }
878 
879 static int mlxsw_sp_dpipe_table_adj_actions_dump(void *priv,
880 						 struct sk_buff *skb)
881 {
882 	struct devlink_dpipe_action action = {0};
883 	int err;
884 
885 	action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
886 	action.header = &devlink_dpipe_header_ethernet;
887 	action.field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC;
888 
889 	err = devlink_dpipe_action_put(skb, &action);
890 	if (err)
891 		return err;
892 
893 	action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
894 	action.header = &mlxsw_sp_dpipe_header_metadata;
895 	action.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT;
896 
897 	return devlink_dpipe_action_put(skb, &action);
898 }
899 
900 static u64 mlxsw_sp_dpipe_table_adj_size(struct mlxsw_sp *mlxsw_sp)
901 {
902 	struct mlxsw_sp_nexthop *nh;
903 	u64 size = 0;
904 
905 	mlxsw_sp_nexthop_for_each(nh, mlxsw_sp->router)
906 		if (mlxsw_sp_nexthop_offload(nh) &&
907 		    !mlxsw_sp_nexthop_group_has_ipip(nh))
908 			size++;
909 	return size;
910 }
911 
912 enum mlxsw_sp_dpipe_table_adj_match {
913 	MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_INDEX,
914 	MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_SIZE,
915 	MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_HASH_INDEX,
916 	MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT,
917 };
918 
919 enum mlxsw_sp_dpipe_table_adj_action {
920 	MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_DST_MAC,
921 	MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_ERIF_PORT,
922 	MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT,
923 };
924 
925 static void
926 mlxsw_sp_dpipe_table_adj_match_action_prepare(struct devlink_dpipe_match *matches,
927 					      struct devlink_dpipe_action *actions)
928 {
929 	struct devlink_dpipe_action *action;
930 	struct devlink_dpipe_match *match;
931 
932 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_INDEX];
933 	match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
934 	match->header = &mlxsw_sp_dpipe_header_metadata;
935 	match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_INDEX;
936 
937 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_SIZE];
938 	match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
939 	match->header = &mlxsw_sp_dpipe_header_metadata;
940 	match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_SIZE;
941 
942 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_HASH_INDEX];
943 	match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT;
944 	match->header = &mlxsw_sp_dpipe_header_metadata;
945 	match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ADJ_HASH_INDEX;
946 
947 	action = &actions[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_DST_MAC];
948 	action->type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
949 	action->header = &devlink_dpipe_header_ethernet;
950 	action->field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC;
951 
952 	action = &actions[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_ERIF_PORT];
953 	action->type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY;
954 	action->header = &mlxsw_sp_dpipe_header_metadata;
955 	action->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT;
956 }
957 
958 static int
959 mlxsw_sp_dpipe_table_adj_entry_prepare(struct devlink_dpipe_entry *entry,
960 				       struct devlink_dpipe_value *match_values,
961 				       struct devlink_dpipe_match *matches,
962 				       struct devlink_dpipe_value *action_values,
963 				       struct devlink_dpipe_action *actions)
964 {	struct devlink_dpipe_value *action_value;
965 	struct devlink_dpipe_value *match_value;
966 	struct devlink_dpipe_action *action;
967 	struct devlink_dpipe_match *match;
968 
969 	entry->match_values = match_values;
970 	entry->match_values_count = MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT;
971 
972 	entry->action_values = action_values;
973 	entry->action_values_count = MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT;
974 
975 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_INDEX];
976 	match_value = &match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_INDEX];
977 
978 	match_value->match = match;
979 	match_value->value_size = sizeof(u32);
980 	match_value->value = kmalloc(match_value->value_size, GFP_KERNEL);
981 	if (!match_value->value)
982 		return -ENOMEM;
983 
984 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_SIZE];
985 	match_value = &match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_SIZE];
986 
987 	match_value->match = match;
988 	match_value->value_size = sizeof(u32);
989 	match_value->value = kmalloc(match_value->value_size, GFP_KERNEL);
990 	if (!match_value->value)
991 		return -ENOMEM;
992 
993 	match = &matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_HASH_INDEX];
994 	match_value = &match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_HASH_INDEX];
995 
996 	match_value->match = match;
997 	match_value->value_size = sizeof(u32);
998 	match_value->value = kmalloc(match_value->value_size, GFP_KERNEL);
999 	if (!match_value->value)
1000 		return -ENOMEM;
1001 
1002 	action = &actions[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_DST_MAC];
1003 	action_value = &action_values[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_DST_MAC];
1004 
1005 	action_value->action = action;
1006 	action_value->value_size = sizeof(u64);
1007 	action_value->value = kmalloc(action_value->value_size, GFP_KERNEL);
1008 	if (!action_value->value)
1009 		return -ENOMEM;
1010 
1011 	action = &actions[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_ERIF_PORT];
1012 	action_value = &action_values[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_ERIF_PORT];
1013 
1014 	action_value->action = action;
1015 	action_value->value_size = sizeof(u32);
1016 	action_value->value = kmalloc(action_value->value_size, GFP_KERNEL);
1017 	if (!action_value->value)
1018 		return -ENOMEM;
1019 
1020 	return 0;
1021 }
1022 
1023 static void
1024 __mlxsw_sp_dpipe_table_adj_entry_fill(struct devlink_dpipe_entry *entry,
1025 				      u32 adj_index, u32 adj_size,
1026 				      u32 adj_hash_index, unsigned char *ha,
1027 				      struct mlxsw_sp_rif *rif)
1028 {
1029 	struct devlink_dpipe_value *value;
1030 	u32 *p_rif_value;
1031 	u32 *p_index;
1032 
1033 	value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_INDEX];
1034 	p_index = value->value;
1035 	*p_index = adj_index;
1036 
1037 	value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_SIZE];
1038 	p_index = value->value;
1039 	*p_index = adj_size;
1040 
1041 	value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_HASH_INDEX];
1042 	p_index = value->value;
1043 	*p_index = adj_hash_index;
1044 
1045 	value = &entry->action_values[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_DST_MAC];
1046 	ether_addr_copy(value->value, ha);
1047 
1048 	value = &entry->action_values[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_ERIF_PORT];
1049 	p_rif_value = value->value;
1050 	*p_rif_value = mlxsw_sp_rif_index(rif);
1051 	value->mapping_value = mlxsw_sp_rif_dev_ifindex(rif);
1052 	value->mapping_valid = true;
1053 }
1054 
1055 static void mlxsw_sp_dpipe_table_adj_entry_fill(struct mlxsw_sp *mlxsw_sp,
1056 						struct mlxsw_sp_nexthop *nh,
1057 						struct devlink_dpipe_entry *entry)
1058 {
1059 	struct mlxsw_sp_rif *rif = mlxsw_sp_nexthop_rif(nh);
1060 	unsigned char *ha = mlxsw_sp_nexthop_ha(nh);
1061 	u32 adj_hash_index = 0;
1062 	u32 adj_index = 0;
1063 	u32 adj_size = 0;
1064 	int err;
1065 
1066 	mlxsw_sp_nexthop_indexes(nh, &adj_index, &adj_size, &adj_hash_index);
1067 	__mlxsw_sp_dpipe_table_adj_entry_fill(entry, adj_index, adj_size,
1068 					      adj_hash_index, ha, rif);
1069 	err = mlxsw_sp_nexthop_counter_get(mlxsw_sp, nh, &entry->counter);
1070 	if (!err)
1071 		entry->counter_valid = true;
1072 }
1073 
1074 static int
1075 mlxsw_sp_dpipe_table_adj_entries_get(struct mlxsw_sp *mlxsw_sp,
1076 				     struct devlink_dpipe_entry *entry,
1077 				     bool counters_enabled,
1078 				     struct devlink_dpipe_dump_ctx *dump_ctx)
1079 {
1080 	struct mlxsw_sp_nexthop *nh;
1081 	int entry_index = 0;
1082 	int nh_count_max;
1083 	int nh_count = 0;
1084 	int nh_skip;
1085 	int j;
1086 	int err;
1087 
1088 	rtnl_lock();
1089 	nh_count_max = mlxsw_sp_dpipe_table_adj_size(mlxsw_sp);
1090 start_again:
1091 	err = devlink_dpipe_entry_ctx_prepare(dump_ctx);
1092 	if (err)
1093 		goto err_ctx_prepare;
1094 	j = 0;
1095 	nh_skip = nh_count;
1096 	nh_count = 0;
1097 	mlxsw_sp_nexthop_for_each(nh, mlxsw_sp->router) {
1098 		if (!mlxsw_sp_nexthop_offload(nh) ||
1099 		    mlxsw_sp_nexthop_group_has_ipip(nh))
1100 			continue;
1101 
1102 		if (nh_count < nh_skip)
1103 			goto skip;
1104 
1105 		mlxsw_sp_dpipe_table_adj_entry_fill(mlxsw_sp, nh, entry);
1106 		entry->index = entry_index;
1107 		err = devlink_dpipe_entry_ctx_append(dump_ctx, entry);
1108 		if (err) {
1109 			if (err == -EMSGSIZE) {
1110 				if (!j)
1111 					goto err_entry_append;
1112 				break;
1113 			}
1114 			goto err_entry_append;
1115 		}
1116 		entry_index++;
1117 		j++;
1118 skip:
1119 		nh_count++;
1120 	}
1121 
1122 	devlink_dpipe_entry_ctx_close(dump_ctx);
1123 	if (nh_count != nh_count_max)
1124 		goto start_again;
1125 	rtnl_unlock();
1126 
1127 	return 0;
1128 
1129 err_ctx_prepare:
1130 err_entry_append:
1131 	rtnl_unlock();
1132 	return err;
1133 }
1134 
1135 static int
1136 mlxsw_sp_dpipe_table_adj_entries_dump(void *priv, bool counters_enabled,
1137 				      struct devlink_dpipe_dump_ctx *dump_ctx)
1138 {
1139 	struct devlink_dpipe_value action_values[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT];
1140 	struct devlink_dpipe_value match_values[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT];
1141 	struct devlink_dpipe_action actions[MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT];
1142 	struct devlink_dpipe_match matches[MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT];
1143 	struct devlink_dpipe_entry entry = {0};
1144 	struct mlxsw_sp *mlxsw_sp = priv;
1145 	int err;
1146 
1147 	memset(matches, 0, MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT *
1148 			   sizeof(matches[0]));
1149 	memset(match_values, 0, MLXSW_SP_DPIPE_TABLE_ADJ_MATCH_COUNT *
1150 				sizeof(match_values[0]));
1151 	memset(actions, 0, MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT *
1152 			   sizeof(actions[0]));
1153 	memset(action_values, 0, MLXSW_SP_DPIPE_TABLE_ADJ_ACTION_COUNT *
1154 				 sizeof(action_values[0]));
1155 
1156 	mlxsw_sp_dpipe_table_adj_match_action_prepare(matches, actions);
1157 	err = mlxsw_sp_dpipe_table_adj_entry_prepare(&entry,
1158 						     match_values, matches,
1159 						     action_values, actions);
1160 	if (err)
1161 		goto out;
1162 
1163 	err = mlxsw_sp_dpipe_table_adj_entries_get(mlxsw_sp, &entry,
1164 						   counters_enabled, dump_ctx);
1165 out:
1166 	devlink_dpipe_entry_clear(&entry);
1167 	return err;
1168 }
1169 
1170 static int mlxsw_sp_dpipe_table_adj_counters_update(void *priv, bool enable)
1171 {
1172 	struct mlxsw_sp *mlxsw_sp = priv;
1173 	struct mlxsw_sp_nexthop *nh;
1174 	u32 adj_hash_index = 0;
1175 	u32 adj_index = 0;
1176 	u32 adj_size = 0;
1177 
1178 	mlxsw_sp_nexthop_for_each(nh, mlxsw_sp->router) {
1179 		if (!mlxsw_sp_nexthop_offload(nh) ||
1180 		    mlxsw_sp_nexthop_group_has_ipip(nh))
1181 			continue;
1182 
1183 		mlxsw_sp_nexthop_indexes(nh, &adj_index, &adj_size,
1184 					 &adj_hash_index);
1185 		if (enable)
1186 			mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh);
1187 		else
1188 			mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh);
1189 		mlxsw_sp_nexthop_update(mlxsw_sp,
1190 					adj_index + adj_hash_index, nh);
1191 	}
1192 	return 0;
1193 }
1194 
1195 static u64
1196 mlxsw_sp_dpipe_table_adj_size_get(void *priv)
1197 {
1198 	struct mlxsw_sp *mlxsw_sp = priv;
1199 	u64 size;
1200 
1201 	rtnl_lock();
1202 	size = mlxsw_sp_dpipe_table_adj_size(mlxsw_sp);
1203 	rtnl_unlock();
1204 
1205 	return size;
1206 }
1207 
1208 static struct devlink_dpipe_table_ops mlxsw_sp_dpipe_table_adj_ops = {
1209 	.matches_dump = mlxsw_sp_dpipe_table_adj_matches_dump,
1210 	.actions_dump = mlxsw_sp_dpipe_table_adj_actions_dump,
1211 	.entries_dump = mlxsw_sp_dpipe_table_adj_entries_dump,
1212 	.counters_set_update = mlxsw_sp_dpipe_table_adj_counters_update,
1213 	.size_get = mlxsw_sp_dpipe_table_adj_size_get,
1214 };
1215 
1216 static int mlxsw_sp_dpipe_adj_table_init(struct mlxsw_sp *mlxsw_sp)
1217 {
1218 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1219 
1220 	return devlink_dpipe_table_register(devlink,
1221 					    MLXSW_SP_DPIPE_TABLE_NAME_ADJ,
1222 					    &mlxsw_sp_dpipe_table_adj_ops,
1223 					    mlxsw_sp, false);
1224 }
1225 
1226 static void mlxsw_sp_dpipe_adj_table_fini(struct mlxsw_sp *mlxsw_sp)
1227 {
1228 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1229 
1230 	devlink_dpipe_table_unregister(devlink,
1231 				       MLXSW_SP_DPIPE_TABLE_NAME_ADJ);
1232 }
1233 
1234 int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp)
1235 {
1236 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1237 	int err;
1238 
1239 	err = devlink_dpipe_headers_register(devlink,
1240 					     &mlxsw_sp_dpipe_headers);
1241 	if (err)
1242 		return err;
1243 	err = mlxsw_sp_dpipe_erif_table_init(mlxsw_sp);
1244 	if (err)
1245 		goto err_erif_table_init;
1246 
1247 	err = mlxsw_sp_dpipe_host4_table_init(mlxsw_sp);
1248 	if (err)
1249 		goto err_host4_table_init;
1250 
1251 	err = mlxsw_sp_dpipe_host6_table_init(mlxsw_sp);
1252 	if (err)
1253 		goto err_host6_table_init;
1254 
1255 	err = mlxsw_sp_dpipe_adj_table_init(mlxsw_sp);
1256 	if (err)
1257 		goto err_adj_table_init;
1258 
1259 	return 0;
1260 err_adj_table_init:
1261 	mlxsw_sp_dpipe_host6_table_fini(mlxsw_sp);
1262 err_host6_table_init:
1263 	mlxsw_sp_dpipe_host4_table_fini(mlxsw_sp);
1264 err_host4_table_init:
1265 	mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp);
1266 err_erif_table_init:
1267 	devlink_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core));
1268 	return err;
1269 }
1270 
1271 void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp)
1272 {
1273 	struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1274 
1275 	mlxsw_sp_dpipe_adj_table_fini(mlxsw_sp);
1276 	mlxsw_sp_dpipe_host6_table_fini(mlxsw_sp);
1277 	mlxsw_sp_dpipe_host4_table_fini(mlxsw_sp);
1278 	mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp);
1279 	devlink_dpipe_headers_unregister(devlink);
1280 }
1281