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