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
12enum 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
21static 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
55enum mlxsw_sp_dpipe_header_id {
56	MLXSW_SP_DPIPE_HEADER_METADATA,
57};
58
59static 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
66static 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
73static struct devlink_dpipe_headers mlxsw_sp_dpipe_headers = {
74	.headers = mlxsw_dpipe_headers,
75	.headers_count = ARRAY_SIZE(mlxsw_dpipe_headers),
76};
77
78static 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
99static 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
111static void
112mlxsw_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
124static 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
149err_action_alloc:
150	kfree(match_value->value);
151	return -ENOMEM;
152}
153
154static 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
191static int
192mlxsw_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;
216start_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;
249err_entry_append:
250err_entry_get:
251err_ctx_prepare:
252	mutex_unlock(&mlxsw_sp->router->lock);
253	devlink_dpipe_entry_clear(&entry);
254	return err;
255}
256
257static 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
279static 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
286static 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
294static 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
304static 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
311static 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
343static int
344mlxsw_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
349static int
350mlxsw_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
361enum 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
367static void
368mlxsw_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
400static int
401mlxsw_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
455static 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
482static void
483mlxsw_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
495static void
496mlxsw_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
509static void
510mlxsw_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
536static int
537mlxsw_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);
553start_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++;
596skip:
597			rif_neigh_count++;
598		}
599		rif_neigh_skip = 0;
600	}
601out:
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
609err_ctx_prepare:
610err_entry_append:
611	mutex_unlock(&mlxsw_sp->router->lock);
612	return err;
613}
614
615static int
616mlxsw_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);
644out:
645	devlink_dpipe_entry_clear(&entry);
646	return err;
647}
648
649static int
650mlxsw_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
660static void
661mlxsw_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
691static 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
699static u64
700mlxsw_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
730static 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
737static 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
747static 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
768err_resource_set:
769	devl_dpipe_table_unregister(devlink,
770				    MLXSW_SP_DPIPE_TABLE_NAME_HOST4);
771	return err;
772}
773
774static 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
782static int
783mlxsw_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
788static int
789mlxsw_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
799static 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
807static 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
814static 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
824static 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
845err_resource_set:
846	devl_dpipe_table_unregister(devlink,
847				    MLXSW_SP_DPIPE_TABLE_NAME_HOST6);
848	return err;
849}
850
851static 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
859static 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
888static 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
909static 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
921enum 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
928enum 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
934static void
935mlxsw_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
967static int
968mlxsw_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
1032static 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
1064static 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
1083static int
1084mlxsw_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);
1099start_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++;
1127skip:
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
1138err_ctx_prepare:
1139err_entry_append:
1140	mutex_unlock(&mlxsw_sp->router->lock);
1141	return err;
1142}
1143
1144static int
1145mlxsw_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);
1174out:
1175	devlink_dpipe_entry_clear(&entry);
1176	return err;
1177}
1178
1179static 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
1211err_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
1220static u64
1221mlxsw_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
1233static 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
1243static 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
1264err_resource_set:
1265	devl_dpipe_table_unregister(devlink,
1266				    MLXSW_SP_DPIPE_TABLE_NAME_ADJ);
1267	return err;
1268}
1269
1270static 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
1278int 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;
1302err_adj_table_init:
1303	mlxsw_sp_dpipe_host6_table_fini(mlxsw_sp);
1304err_host6_table_init:
1305	mlxsw_sp_dpipe_host4_table_fini(mlxsw_sp);
1306err_host4_table_init:
1307	mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp);
1308err_erif_table_init:
1309	devl_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core));
1310	return err;
1311}
1312
1313void 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