Commit | Line | Data |
---|---|---|
8a276873 PJV |
1 | /* |
2 | * Copyright (C) 2017 Netronome Systems, Inc. | |
3 | * | |
4 | * This software is dual licensed under the GNU General License Version 2, | |
5 | * June 1991 as shown in the file COPYING in the top-level directory of this | |
6 | * source tree or the BSD 2-Clause License provided below. You have the | |
7 | * option to license this software under the complete terms of either license. | |
8 | * | |
9 | * The BSD 2-Clause License: | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or | |
12 | * without modification, are permitted provided that the following | |
13 | * conditions are met: | |
14 | * | |
15 | * 1. Redistributions of source code must retain the above | |
16 | * copyright notice, this list of conditions and the following | |
17 | * disclaimer. | |
18 | * | |
19 | * 2. Redistributions in binary form must reproduce the above | |
20 | * copyright notice, this list of conditions and the following | |
21 | * disclaimer in the documentation and/or other materials | |
22 | * provided with the distribution. | |
23 | * | |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
31 | * SOFTWARE. | |
32 | */ | |
33 | ||
34 | #include <linux/skbuff.h> | |
35 | #include <net/devlink.h> | |
36 | #include <net/pkt_cls.h> | |
37 | ||
38 | #include "cmsg.h" | |
39 | #include "main.h" | |
40 | #include "../nfpcore/nfp_cpp.h" | |
41 | #include "../nfpcore/nfp_nsp.h" | |
42 | #include "../nfp_app.h" | |
43 | #include "../nfp_main.h" | |
44 | #include "../nfp_net.h" | |
45 | #include "../nfp_port.h" | |
46 | ||
81f3ddf2 PJV |
47 | static int |
48 | nfp_flower_xmit_flow(struct net_device *netdev, | |
49 | struct nfp_fl_payload *nfp_flow, u8 mtype) | |
50 | { | |
51 | u32 meta_len, key_len, mask_len, act_len, tot_len; | |
52 | struct nfp_repr *priv = netdev_priv(netdev); | |
53 | struct sk_buff *skb; | |
54 | unsigned char *msg; | |
55 | ||
56 | meta_len = sizeof(struct nfp_fl_rule_metadata); | |
57 | key_len = nfp_flow->meta.key_len; | |
58 | mask_len = nfp_flow->meta.mask_len; | |
59 | act_len = nfp_flow->meta.act_len; | |
60 | ||
61 | tot_len = meta_len + key_len + mask_len + act_len; | |
62 | ||
63 | /* Convert to long words as firmware expects | |
64 | * lengths in units of NFP_FL_LW_SIZ. | |
65 | */ | |
66 | nfp_flow->meta.key_len >>= NFP_FL_LW_SIZ; | |
67 | nfp_flow->meta.mask_len >>= NFP_FL_LW_SIZ; | |
68 | nfp_flow->meta.act_len >>= NFP_FL_LW_SIZ; | |
69 | ||
70 | skb = nfp_flower_cmsg_alloc(priv->app, tot_len, mtype); | |
71 | if (!skb) | |
72 | return -ENOMEM; | |
73 | ||
74 | msg = nfp_flower_cmsg_get_data(skb); | |
75 | memcpy(msg, &nfp_flow->meta, meta_len); | |
76 | memcpy(&msg[meta_len], nfp_flow->unmasked_data, key_len); | |
77 | memcpy(&msg[meta_len + key_len], nfp_flow->mask_data, mask_len); | |
78 | memcpy(&msg[meta_len + key_len + mask_len], | |
79 | nfp_flow->action_data, act_len); | |
80 | ||
81 | /* Convert back to bytes as software expects | |
82 | * lengths in units of bytes. | |
83 | */ | |
84 | nfp_flow->meta.key_len <<= NFP_FL_LW_SIZ; | |
85 | nfp_flow->meta.mask_len <<= NFP_FL_LW_SIZ; | |
86 | nfp_flow->meta.act_len <<= NFP_FL_LW_SIZ; | |
87 | ||
88 | nfp_ctrl_tx(priv->app->ctrl, skb); | |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
af9d842c PJV |
93 | static bool nfp_flower_check_higher_than_mac(struct tc_cls_flower_offload *f) |
94 | { | |
95 | return dissector_uses_key(f->dissector, | |
96 | FLOW_DISSECTOR_KEY_IPV4_ADDRS) || | |
97 | dissector_uses_key(f->dissector, | |
98 | FLOW_DISSECTOR_KEY_IPV6_ADDRS) || | |
99 | dissector_uses_key(f->dissector, | |
100 | FLOW_DISSECTOR_KEY_PORTS) || | |
101 | dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ICMP); | |
102 | } | |
103 | ||
104 | static int | |
105 | nfp_flower_calculate_key_layers(struct nfp_fl_key_ls *ret_key_ls, | |
106 | struct tc_cls_flower_offload *flow) | |
107 | { | |
108 | struct flow_dissector_key_control *mask_enc_ctl; | |
109 | struct flow_dissector_key_basic *mask_basic; | |
110 | struct flow_dissector_key_basic *key_basic; | |
111 | u32 key_layer_two; | |
112 | u8 key_layer; | |
113 | int key_size; | |
114 | ||
115 | mask_enc_ctl = skb_flow_dissector_target(flow->dissector, | |
116 | FLOW_DISSECTOR_KEY_ENC_CONTROL, | |
117 | flow->mask); | |
118 | ||
119 | mask_basic = skb_flow_dissector_target(flow->dissector, | |
120 | FLOW_DISSECTOR_KEY_BASIC, | |
121 | flow->mask); | |
122 | ||
123 | key_basic = skb_flow_dissector_target(flow->dissector, | |
124 | FLOW_DISSECTOR_KEY_BASIC, | |
125 | flow->key); | |
126 | key_layer_two = 0; | |
127 | key_layer = NFP_FLOWER_LAYER_PORT | NFP_FLOWER_LAYER_MAC; | |
128 | key_size = sizeof(struct nfp_flower_meta_one) + | |
129 | sizeof(struct nfp_flower_in_port) + | |
130 | sizeof(struct nfp_flower_mac_mpls); | |
131 | ||
132 | /* We are expecting a tunnel. For now we ignore offloading. */ | |
133 | if (mask_enc_ctl->addr_type) | |
134 | return -EOPNOTSUPP; | |
135 | ||
136 | if (mask_basic->n_proto) { | |
137 | /* Ethernet type is present in the key. */ | |
138 | switch (key_basic->n_proto) { | |
139 | case cpu_to_be16(ETH_P_IP): | |
140 | key_layer |= NFP_FLOWER_LAYER_IPV4; | |
141 | key_size += sizeof(struct nfp_flower_ipv4); | |
142 | break; | |
143 | ||
144 | case cpu_to_be16(ETH_P_IPV6): | |
145 | key_layer |= NFP_FLOWER_LAYER_IPV6; | |
146 | key_size += sizeof(struct nfp_flower_ipv6); | |
147 | break; | |
148 | ||
149 | /* Currently we do not offload ARP | |
150 | * because we rely on it to get to the host. | |
151 | */ | |
152 | case cpu_to_be16(ETH_P_ARP): | |
153 | return -EOPNOTSUPP; | |
154 | ||
155 | /* Will be included in layer 2. */ | |
156 | case cpu_to_be16(ETH_P_8021Q): | |
157 | break; | |
158 | ||
159 | default: | |
160 | /* Other ethtype - we need check the masks for the | |
161 | * remainder of the key to ensure we can offload. | |
162 | */ | |
163 | if (nfp_flower_check_higher_than_mac(flow)) | |
164 | return -EOPNOTSUPP; | |
165 | break; | |
166 | } | |
167 | } | |
168 | ||
169 | if (mask_basic->ip_proto) { | |
170 | /* Ethernet type is present in the key. */ | |
171 | switch (key_basic->ip_proto) { | |
172 | case IPPROTO_TCP: | |
173 | case IPPROTO_UDP: | |
174 | case IPPROTO_SCTP: | |
175 | case IPPROTO_ICMP: | |
176 | case IPPROTO_ICMPV6: | |
177 | key_layer |= NFP_FLOWER_LAYER_TP; | |
178 | key_size += sizeof(struct nfp_flower_tp_ports); | |
179 | break; | |
180 | default: | |
181 | /* Other ip proto - we need check the masks for the | |
182 | * remainder of the key to ensure we can offload. | |
183 | */ | |
184 | return -EOPNOTSUPP; | |
185 | } | |
186 | } | |
187 | ||
188 | ret_key_ls->key_layer = key_layer; | |
189 | ret_key_ls->key_layer_two = key_layer_two; | |
190 | ret_key_ls->key_size = key_size; | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
195 | static struct nfp_fl_payload * | |
196 | nfp_flower_allocate_new(struct nfp_fl_key_ls *key_layer) | |
197 | { | |
198 | struct nfp_fl_payload *flow_pay; | |
199 | ||
200 | flow_pay = kmalloc(sizeof(*flow_pay), GFP_KERNEL); | |
201 | if (!flow_pay) | |
202 | return NULL; | |
203 | ||
204 | flow_pay->meta.key_len = key_layer->key_size; | |
205 | flow_pay->unmasked_data = kmalloc(key_layer->key_size, GFP_KERNEL); | |
206 | if (!flow_pay->unmasked_data) | |
207 | goto err_free_flow; | |
208 | ||
209 | flow_pay->meta.mask_len = key_layer->key_size; | |
210 | flow_pay->mask_data = kmalloc(key_layer->key_size, GFP_KERNEL); | |
211 | if (!flow_pay->mask_data) | |
212 | goto err_free_unmasked; | |
213 | ||
1a1e586f PJV |
214 | flow_pay->action_data = kmalloc(NFP_FL_MAX_A_SIZ, GFP_KERNEL); |
215 | if (!flow_pay->action_data) | |
216 | goto err_free_mask; | |
217 | ||
af9d842c | 218 | flow_pay->meta.flags = 0; |
abfcdc1d | 219 | spin_lock_init(&flow_pay->lock); |
af9d842c PJV |
220 | |
221 | return flow_pay; | |
222 | ||
1a1e586f PJV |
223 | err_free_mask: |
224 | kfree(flow_pay->mask_data); | |
af9d842c PJV |
225 | err_free_unmasked: |
226 | kfree(flow_pay->unmasked_data); | |
227 | err_free_flow: | |
228 | kfree(flow_pay); | |
229 | return NULL; | |
230 | } | |
231 | ||
8a276873 PJV |
232 | /** |
233 | * nfp_flower_add_offload() - Adds a new flow to hardware. | |
234 | * @app: Pointer to the APP handle | |
235 | * @netdev: netdev structure. | |
236 | * @flow: TC flower classifier offload structure. | |
237 | * | |
238 | * Adds a new flow to the repeated hash structure and action payload. | |
239 | * | |
240 | * Return: negative value on error, 0 if configured successfully. | |
241 | */ | |
242 | static int | |
243 | nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev, | |
244 | struct tc_cls_flower_offload *flow) | |
245 | { | |
43f84b72 | 246 | struct nfp_flower_priv *priv = app->priv; |
af9d842c PJV |
247 | struct nfp_fl_payload *flow_pay; |
248 | struct nfp_fl_key_ls *key_layer; | |
249 | int err; | |
250 | ||
251 | key_layer = kmalloc(sizeof(*key_layer), GFP_KERNEL); | |
252 | if (!key_layer) | |
253 | return -ENOMEM; | |
254 | ||
255 | err = nfp_flower_calculate_key_layers(key_layer, flow); | |
256 | if (err) | |
257 | goto err_free_key_ls; | |
258 | ||
259 | flow_pay = nfp_flower_allocate_new(key_layer); | |
260 | if (!flow_pay) { | |
261 | err = -ENOMEM; | |
262 | goto err_free_key_ls; | |
263 | } | |
264 | ||
5571e8c9 PJV |
265 | err = nfp_flower_compile_flow_match(flow, key_layer, netdev, flow_pay); |
266 | if (err) | |
267 | goto err_destroy_flow; | |
268 | ||
1a1e586f PJV |
269 | err = nfp_flower_compile_action(flow, netdev, flow_pay); |
270 | if (err) | |
271 | goto err_destroy_flow; | |
272 | ||
43f84b72 PJV |
273 | err = nfp_compile_flow_metadata(app, flow, flow_pay); |
274 | if (err) | |
275 | goto err_destroy_flow; | |
276 | ||
81f3ddf2 PJV |
277 | err = nfp_flower_xmit_flow(netdev, flow_pay, |
278 | NFP_FLOWER_CMSG_TYPE_FLOW_ADD); | |
279 | if (err) | |
280 | goto err_destroy_flow; | |
281 | ||
43f84b72 PJV |
282 | INIT_HLIST_NODE(&flow_pay->link); |
283 | flow_pay->tc_flower_cookie = flow->cookie; | |
284 | hash_add_rcu(priv->flow_table, &flow_pay->link, flow->cookie); | |
285 | ||
286 | /* Deallocate flow payload when flower rule has been destroyed. */ | |
287 | kfree(key_layer); | |
288 | ||
289 | return 0; | |
af9d842c | 290 | |
5571e8c9 | 291 | err_destroy_flow: |
1a1e586f | 292 | kfree(flow_pay->action_data); |
af9d842c PJV |
293 | kfree(flow_pay->mask_data); |
294 | kfree(flow_pay->unmasked_data); | |
295 | kfree(flow_pay); | |
af9d842c PJV |
296 | err_free_key_ls: |
297 | kfree(key_layer); | |
298 | return err; | |
8a276873 PJV |
299 | } |
300 | ||
301 | /** | |
302 | * nfp_flower_del_offload() - Removes a flow from hardware. | |
303 | * @app: Pointer to the APP handle | |
304 | * @netdev: netdev structure. | |
305 | * @flow: TC flower classifier offload structure | |
306 | * | |
307 | * Removes a flow from the repeated hash structure and clears the | |
308 | * action payload. | |
309 | * | |
310 | * Return: negative value on error, 0 if removed successfully. | |
311 | */ | |
312 | static int | |
313 | nfp_flower_del_offload(struct nfp_app *app, struct net_device *netdev, | |
314 | struct tc_cls_flower_offload *flow) | |
315 | { | |
43f84b72 PJV |
316 | struct nfp_fl_payload *nfp_flow; |
317 | int err; | |
318 | ||
319 | nfp_flow = nfp_flower_search_fl_table(app, flow->cookie); | |
320 | if (!nfp_flow) | |
321 | return -ENOENT; | |
322 | ||
323 | err = nfp_modify_flow_metadata(app, nfp_flow); | |
81f3ddf2 PJV |
324 | if (err) |
325 | goto err_free_flow; | |
43f84b72 | 326 | |
81f3ddf2 PJV |
327 | err = nfp_flower_xmit_flow(netdev, nfp_flow, |
328 | NFP_FLOWER_CMSG_TYPE_FLOW_DEL); | |
329 | if (err) | |
330 | goto err_free_flow; | |
331 | ||
332 | err_free_flow: | |
43f84b72 PJV |
333 | hash_del_rcu(&nfp_flow->link); |
334 | kfree(nfp_flow->action_data); | |
335 | kfree(nfp_flow->mask_data); | |
336 | kfree(nfp_flow->unmasked_data); | |
337 | kfree_rcu(nfp_flow, rcu); | |
338 | return err; | |
8a276873 PJV |
339 | } |
340 | ||
341 | /** | |
342 | * nfp_flower_get_stats() - Populates flow stats obtained from hardware. | |
343 | * @app: Pointer to the APP handle | |
344 | * @flow: TC flower classifier offload structure | |
345 | * | |
346 | * Populates a flow statistics structure which which corresponds to a | |
347 | * specific flow. | |
348 | * | |
349 | * Return: negative value on error, 0 if stats populated successfully. | |
350 | */ | |
351 | static int | |
352 | nfp_flower_get_stats(struct nfp_app *app, struct tc_cls_flower_offload *flow) | |
353 | { | |
abfcdc1d PJV |
354 | struct nfp_fl_payload *nfp_flow; |
355 | ||
356 | nfp_flow = nfp_flower_search_fl_table(app, flow->cookie); | |
357 | if (!nfp_flow) | |
358 | return -EINVAL; | |
359 | ||
360 | spin_lock_bh(&nfp_flow->lock); | |
361 | tcf_exts_stats_update(flow->exts, nfp_flow->stats.bytes, | |
362 | nfp_flow->stats.pkts, nfp_flow->stats.used); | |
363 | ||
364 | nfp_flow->stats.pkts = 0; | |
365 | nfp_flow->stats.bytes = 0; | |
366 | spin_unlock_bh(&nfp_flow->lock); | |
367 | ||
368 | return 0; | |
8a276873 PJV |
369 | } |
370 | ||
371 | static int | |
372 | nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, | |
373 | struct tc_cls_flower_offload *flower) | |
374 | { | |
375 | switch (flower->command) { | |
376 | case TC_CLSFLOWER_REPLACE: | |
377 | return nfp_flower_add_offload(app, netdev, flower); | |
378 | case TC_CLSFLOWER_DESTROY: | |
379 | return nfp_flower_del_offload(app, netdev, flower); | |
380 | case TC_CLSFLOWER_STATS: | |
381 | return nfp_flower_get_stats(app, flower); | |
382 | } | |
383 | ||
384 | return -EOPNOTSUPP; | |
385 | } | |
386 | ||
387 | int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, | |
5fd9fc4e | 388 | enum tc_setup_type type, struct tc_to_netdev *tc) |
8a276873 | 389 | { |
5fd9fc4e JP |
390 | struct tc_cls_flower_offload *cls_flower = tc->cls_flower; |
391 | ||
37cba6b3 | 392 | if (type != TC_SETUP_CLSFLOWER || |
5fd9fc4e JP |
393 | TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || |
394 | !eth_proto_is_802_3(cls_flower->common.protocol) || | |
395 | cls_flower->common.chain_index) | |
8a276873 PJV |
396 | return -EOPNOTSUPP; |
397 | ||
5fd9fc4e | 398 | return nfp_flower_repr_offload(app, netdev, cls_flower); |
8a276873 | 399 | } |