xref: /linux/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c (revision 42d37fc0c819b81f6f6afd108b55d04ba9d32d0f)
1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip VCAP API
3  *
4  * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
5  */
6 
7 #include "sparx5_tc.h"
8 #include "vcap_api.h"
9 #include "vcap_api_client.h"
10 #include "sparx5_main_regs.h"
11 #include "sparx5_main.h"
12 #include "sparx5_vcap_impl.h"
13 
14 static struct sparx5_mall_entry *
15 sparx5_tc_matchall_entry_find(struct list_head *entries, unsigned long cookie)
16 {
17 	struct sparx5_mall_entry *entry;
18 
19 	list_for_each_entry(entry, entries, list) {
20 		if (entry->cookie == cookie)
21 			return entry;
22 	}
23 
24 	return NULL;
25 }
26 
27 static void sparx5_tc_matchall_parse_action(struct sparx5_port *port,
28 					    struct sparx5_mall_entry *entry,
29 					    struct flow_action_entry *action,
30 					    bool ingress,
31 					    unsigned long cookie)
32 {
33 	entry->port = port;
34 	entry->type = action->id;
35 	entry->ingress = ingress;
36 	entry->cookie = cookie;
37 }
38 
39 static void
40 sparx5_tc_matchall_parse_mirror_action(struct sparx5_mall_entry *entry,
41 				       struct flow_action_entry *action)
42 {
43 	entry->mirror.port = netdev_priv(action->dev);
44 }
45 
46 static int sparx5_tc_matchall_replace(struct net_device *ndev,
47 				      struct tc_cls_matchall_offload *tmo,
48 				      bool ingress)
49 {
50 	struct sparx5_port *port = netdev_priv(ndev);
51 	struct sparx5_mall_entry *mall_entry;
52 	struct flow_action_entry *action;
53 	struct sparx5 *sparx5;
54 	int err;
55 
56 	if (!flow_offload_has_one_action(&tmo->rule->action)) {
57 		NL_SET_ERR_MSG_MOD(tmo->common.extack,
58 				   "Only one action per filter is supported");
59 		return -EOPNOTSUPP;
60 	}
61 	action = &tmo->rule->action.entries[0];
62 
63 	mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
64 	if (!mall_entry)
65 		return -ENOMEM;
66 
67 	sparx5_tc_matchall_parse_action(port,
68 					mall_entry,
69 					action,
70 					ingress,
71 					tmo->cookie);
72 
73 	sparx5 = port->sparx5;
74 	switch (action->id) {
75 	case FLOW_ACTION_MIRRED:
76 		sparx5_tc_matchall_parse_mirror_action(mall_entry, action);
77 		err = sparx5_mirror_add(mall_entry);
78 		if (err) {
79 			switch (err) {
80 			case -EEXIST:
81 				NL_SET_ERR_MSG_MOD(tmo->common.extack,
82 						   "Mirroring already exists");
83 				break;
84 			case -EINVAL:
85 				NL_SET_ERR_MSG_MOD(tmo->common.extack,
86 						   "Cannot mirror a monitor port");
87 				break;
88 			case -ENOENT:
89 				NL_SET_ERR_MSG_MOD(tmo->common.extack,
90 						   "No more mirror probes available");
91 				break;
92 			default:
93 				NL_SET_ERR_MSG_MOD(tmo->common.extack,
94 						   "Unknown error");
95 				break;
96 			}
97 			return err;
98 		}
99 		/* Get baseline stats for this port */
100 		sparx5_mirror_stats(mall_entry, &tmo->stats);
101 		break;
102 	case FLOW_ACTION_GOTO:
103 		err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
104 					  tmo->common.chain_index,
105 					  action->chain_index, tmo->cookie,
106 					  true);
107 		if (err == -EFAULT) {
108 			NL_SET_ERR_MSG_MOD(tmo->common.extack,
109 					   "Unsupported goto chain");
110 			return -EOPNOTSUPP;
111 		}
112 		if (err == -EADDRINUSE) {
113 			NL_SET_ERR_MSG_MOD(tmo->common.extack,
114 					   "VCAP already enabled");
115 			return -EOPNOTSUPP;
116 		}
117 		if (err == -EADDRNOTAVAIL) {
118 			NL_SET_ERR_MSG_MOD(tmo->common.extack,
119 					   "Already matching this chain");
120 			return -EOPNOTSUPP;
121 		}
122 		if (err) {
123 			NL_SET_ERR_MSG_MOD(tmo->common.extack,
124 					   "Could not enable VCAP lookups");
125 			return err;
126 		}
127 		break;
128 	default:
129 		NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
130 		return -EOPNOTSUPP;
131 	}
132 
133 	list_add_tail(&mall_entry->list, &sparx5->mall_entries);
134 
135 	return 0;
136 }
137 
138 static int sparx5_tc_matchall_destroy(struct net_device *ndev,
139 				      struct tc_cls_matchall_offload *tmo,
140 				      bool ingress)
141 {
142 	struct sparx5_port *port = netdev_priv(ndev);
143 	struct sparx5 *sparx5 = port->sparx5;
144 	struct sparx5_mall_entry *entry;
145 	int err = 0;
146 
147 	entry = sparx5_tc_matchall_entry_find(&sparx5->mall_entries,
148 					      tmo->cookie);
149 	if (!entry)
150 		return -ENOENT;
151 
152 	if (entry->type == FLOW_ACTION_MIRRED) {
153 		sparx5_mirror_del(entry);
154 	} else if (entry->type == FLOW_ACTION_GOTO) {
155 		err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
156 					  0, 0, tmo->cookie, false);
157 	} else {
158 		NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
159 		err = -EOPNOTSUPP;
160 	}
161 
162 	list_del(&entry->list);
163 
164 	return err;
165 }
166 
167 static int sparx5_tc_matchall_stats(struct net_device *ndev,
168 				    struct tc_cls_matchall_offload *tmo,
169 				    bool ingress)
170 {
171 	struct sparx5_port *port = netdev_priv(ndev);
172 	struct sparx5 *sparx5 = port->sparx5;
173 	struct sparx5_mall_entry *entry;
174 
175 	entry = sparx5_tc_matchall_entry_find(&sparx5->mall_entries,
176 					      tmo->cookie);
177 	if (!entry)
178 		return -ENOENT;
179 
180 	if (entry->type == FLOW_ACTION_MIRRED) {
181 		sparx5_mirror_stats(entry, &tmo->stats);
182 	} else {
183 		NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
184 		return -EOPNOTSUPP;
185 	}
186 
187 	return 0;
188 }
189 
190 int sparx5_tc_matchall(struct net_device *ndev,
191 		       struct tc_cls_matchall_offload *tmo,
192 		       bool ingress)
193 {
194 	switch (tmo->command) {
195 	case TC_CLSMATCHALL_REPLACE:
196 		return sparx5_tc_matchall_replace(ndev, tmo, ingress);
197 	case TC_CLSMATCHALL_DESTROY:
198 		return sparx5_tc_matchall_destroy(ndev, tmo, ingress);
199 	case TC_CLSMATCHALL_STATS:
200 		return sparx5_tc_matchall_stats(ndev, tmo, ingress);
201 	default:
202 		return -EOPNOTSUPP;
203 	}
204 }
205