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