]> git.proxmox.com Git - mirror_ovs.git/blob - lib/netdev-offload.c
ovsdb-idl: Fix iteration over tracked rows with no actual data.
[mirror_ovs.git] / lib / netdev-offload.c
1 /*
2 * Copyright (c) 2008 - 2014, 2016, 2017 Nicira, Inc.
3 * Copyright (c) 2019 Samsung Electronics Co.,Ltd.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <config.h>
19 #include "netdev-offload.h"
20
21 #include <errno.h>
22 #include <inttypes.h>
23 #include <sys/types.h>
24 #include <netinet/in.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include "cmap.h"
30 #include "coverage.h"
31 #include "dpif.h"
32 #include "dp-packet.h"
33 #include "openvswitch/dynamic-string.h"
34 #include "fatal-signal.h"
35 #include "hash.h"
36 #include "openvswitch/list.h"
37 #include "netdev-offload-provider.h"
38 #include "netdev-provider.h"
39 #include "netdev-vport.h"
40 #include "odp-netlink.h"
41 #include "openflow/openflow.h"
42 #include "packets.h"
43 #include "openvswitch/ofp-print.h"
44 #include "openvswitch/poll-loop.h"
45 #include "seq.h"
46 #include "openvswitch/shash.h"
47 #include "smap.h"
48 #include "socket-util.h"
49 #include "sset.h"
50 #include "svec.h"
51 #include "openvswitch/vlog.h"
52 #include "flow.h"
53 #include "util.h"
54 #ifdef __linux__
55 #include "tc.h"
56 #endif
57
58 VLOG_DEFINE_THIS_MODULE(netdev_offload);
59
60
61 static bool netdev_flow_api_enabled = false;
62
63 /* Protects 'netdev_flow_apis'. */
64 static struct ovs_mutex netdev_flow_api_provider_mutex = OVS_MUTEX_INITIALIZER;
65
66 /* Contains 'struct netdev_registered_flow_api's. */
67 static struct cmap netdev_flow_apis = CMAP_INITIALIZER;
68
69 struct netdev_registered_flow_api {
70 struct cmap_node cmap_node; /* In 'netdev_flow_apis', by flow_api->type. */
71 const struct netdev_flow_api *flow_api;
72
73 /* Number of references: one for the flow_api itself and one for every
74 * instance of the netdev that uses it. */
75 struct ovs_refcount refcnt;
76 };
77
78 static struct netdev_registered_flow_api *
79 netdev_lookup_flow_api(const char *type)
80 {
81 struct netdev_registered_flow_api *rfa;
82 CMAP_FOR_EACH_WITH_HASH (rfa, cmap_node, hash_string(type, 0),
83 &netdev_flow_apis) {
84 if (!strcmp(type, rfa->flow_api->type)) {
85 return rfa;
86 }
87 }
88 return NULL;
89 }
90
91 /* Registers a new netdev flow api provider. */
92 int
93 netdev_register_flow_api_provider(const struct netdev_flow_api *new_flow_api)
94 OVS_EXCLUDED(netdev_flow_api_provider_mutex)
95 {
96 int error = 0;
97
98 if (!new_flow_api->init_flow_api) {
99 VLOG_WARN("attempted to register invalid flow api provider: %s",
100 new_flow_api->type);
101 error = EINVAL;
102 }
103
104 ovs_mutex_lock(&netdev_flow_api_provider_mutex);
105 if (netdev_lookup_flow_api(new_flow_api->type)) {
106 VLOG_WARN("attempted to register duplicate flow api provider: %s",
107 new_flow_api->type);
108 error = EEXIST;
109 } else {
110 struct netdev_registered_flow_api *rfa;
111
112 rfa = xmalloc(sizeof *rfa);
113 cmap_insert(&netdev_flow_apis, &rfa->cmap_node,
114 hash_string(new_flow_api->type, 0));
115 rfa->flow_api = new_flow_api;
116 ovs_refcount_init(&rfa->refcnt);
117 VLOG_DBG("netdev: flow API '%s' registered.", new_flow_api->type);
118 }
119 ovs_mutex_unlock(&netdev_flow_api_provider_mutex);
120
121 return error;
122 }
123
124 /* Unregisters a netdev flow api provider. 'type' must have been previously
125 * registered and not currently be in use by any netdevs. After unregistration
126 * netdev flow api of that type cannot be used for netdevs. (However, the
127 * provider may still be accessible from other threads until the next RCU grace
128 * period, so the caller must not free or re-register the same netdev_flow_api
129 * until that has passed.) */
130 int
131 netdev_unregister_flow_api_provider(const char *type)
132 OVS_EXCLUDED(netdev_flow_api_provider_mutex)
133 {
134 struct netdev_registered_flow_api *rfa;
135 int error;
136
137 ovs_mutex_lock(&netdev_flow_api_provider_mutex);
138 rfa = netdev_lookup_flow_api(type);
139 if (!rfa) {
140 VLOG_WARN("attempted to unregister a flow api provider that is not "
141 "registered: %s", type);
142 error = EAFNOSUPPORT;
143 } else if (ovs_refcount_unref(&rfa->refcnt) != 1) {
144 ovs_refcount_ref(&rfa->refcnt);
145 VLOG_WARN("attempted to unregister in use flow api provider: %s",
146 type);
147 error = EBUSY;
148 } else {
149 cmap_remove(&netdev_flow_apis, &rfa->cmap_node,
150 hash_string(rfa->flow_api->type, 0));
151 ovsrcu_postpone(free, rfa);
152 error = 0;
153 }
154 ovs_mutex_unlock(&netdev_flow_api_provider_mutex);
155
156 return error;
157 }
158
159 bool
160 netdev_flow_api_equals(const struct netdev *netdev1,
161 const struct netdev *netdev2)
162 {
163 const struct netdev_flow_api *netdev_flow_api1 =
164 ovsrcu_get(const struct netdev_flow_api *, &netdev1->flow_api);
165 const struct netdev_flow_api *netdev_flow_api2 =
166 ovsrcu_get(const struct netdev_flow_api *, &netdev2->flow_api);
167
168 return netdev_flow_api1 == netdev_flow_api2;
169 }
170
171 static int
172 netdev_assign_flow_api(struct netdev *netdev)
173 {
174 struct netdev_registered_flow_api *rfa;
175
176 CMAP_FOR_EACH (rfa, cmap_node, &netdev_flow_apis) {
177 if (!rfa->flow_api->init_flow_api(netdev)) {
178 ovs_refcount_ref(&rfa->refcnt);
179 ovsrcu_set(&netdev->flow_api, rfa->flow_api);
180 VLOG_INFO("%s: Assigned flow API '%s'.",
181 netdev_get_name(netdev), rfa->flow_api->type);
182 return 0;
183 }
184 VLOG_DBG("%s: flow API '%s' is not suitable.",
185 netdev_get_name(netdev), rfa->flow_api->type);
186 }
187 VLOG_INFO("%s: No suitable flow API found.", netdev_get_name(netdev));
188
189 return -1;
190 }
191
192 int
193 netdev_flow_flush(struct netdev *netdev)
194 {
195 const struct netdev_flow_api *flow_api =
196 ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
197
198 return (flow_api && flow_api->flow_flush)
199 ? flow_api->flow_flush(netdev)
200 : EOPNOTSUPP;
201 }
202
203 int
204 netdev_flow_dump_create(struct netdev *netdev, struct netdev_flow_dump **dump,
205 bool terse)
206 {
207 const struct netdev_flow_api *flow_api =
208 ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
209
210 return (flow_api && flow_api->flow_dump_create)
211 ? flow_api->flow_dump_create(netdev, dump, terse)
212 : EOPNOTSUPP;
213 }
214
215 int
216 netdev_flow_dump_destroy(struct netdev_flow_dump *dump)
217 {
218 const struct netdev_flow_api *flow_api =
219 ovsrcu_get(const struct netdev_flow_api *, &dump->netdev->flow_api);
220
221 return (flow_api && flow_api->flow_dump_destroy)
222 ? flow_api->flow_dump_destroy(dump)
223 : EOPNOTSUPP;
224 }
225
226 bool
227 netdev_flow_dump_next(struct netdev_flow_dump *dump, struct match *match,
228 struct nlattr **actions, struct dpif_flow_stats *stats,
229 struct dpif_flow_attrs *attrs, ovs_u128 *ufid,
230 struct ofpbuf *rbuffer, struct ofpbuf *wbuffer)
231 {
232 const struct netdev_flow_api *flow_api =
233 ovsrcu_get(const struct netdev_flow_api *, &dump->netdev->flow_api);
234
235 return (flow_api && flow_api->flow_dump_next)
236 ? flow_api->flow_dump_next(dump, match, actions, stats, attrs,
237 ufid, rbuffer, wbuffer)
238 : false;
239 }
240
241 int
242 netdev_flow_put(struct netdev *netdev, struct match *match,
243 struct nlattr *actions, size_t act_len,
244 const ovs_u128 *ufid, struct offload_info *info,
245 struct dpif_flow_stats *stats)
246 {
247 const struct netdev_flow_api *flow_api =
248 ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
249
250 return (flow_api && flow_api->flow_put)
251 ? flow_api->flow_put(netdev, match, actions, act_len, ufid,
252 info, stats)
253 : EOPNOTSUPP;
254 }
255
256 int
257 netdev_flow_get(struct netdev *netdev, struct match *match,
258 struct nlattr **actions, const ovs_u128 *ufid,
259 struct dpif_flow_stats *stats,
260 struct dpif_flow_attrs *attrs, struct ofpbuf *buf)
261 {
262 const struct netdev_flow_api *flow_api =
263 ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
264
265 return (flow_api && flow_api->flow_get)
266 ? flow_api->flow_get(netdev, match, actions, ufid,
267 stats, attrs, buf)
268 : EOPNOTSUPP;
269 }
270
271 int
272 netdev_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
273 struct dpif_flow_stats *stats)
274 {
275 const struct netdev_flow_api *flow_api =
276 ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
277
278 return (flow_api && flow_api->flow_del)
279 ? flow_api->flow_del(netdev, ufid, stats)
280 : EOPNOTSUPP;
281 }
282
283 int
284 netdev_init_flow_api(struct netdev *netdev)
285 {
286 if (!netdev_is_flow_api_enabled()) {
287 return EOPNOTSUPP;
288 }
289
290 if (ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api)) {
291 return 0;
292 }
293
294 if (netdev_assign_flow_api(netdev)) {
295 return EOPNOTSUPP;
296 }
297
298 return 0;
299 }
300
301 void
302 netdev_uninit_flow_api(struct netdev *netdev)
303 {
304 struct netdev_registered_flow_api *rfa;
305 const struct netdev_flow_api *flow_api =
306 ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
307
308 if (!flow_api) {
309 return;
310 }
311
312 ovsrcu_set(&netdev->flow_api, NULL);
313 rfa = netdev_lookup_flow_api(flow_api->type);
314 ovs_refcount_unref(&rfa->refcnt);
315 }
316
317 uint32_t
318 netdev_get_block_id(struct netdev *netdev)
319 {
320 const struct netdev_class *class = netdev->netdev_class;
321
322 return (class->get_block_id
323 ? class->get_block_id(netdev)
324 : 0);
325 }
326
327 /*
328 * Get the value of the hw info parameter specified by type.
329 * Returns the value on success (>= 0). Returns -1 on failure.
330 */
331 int
332 netdev_get_hw_info(struct netdev *netdev, int type)
333 {
334 int val = -1;
335
336 switch (type) {
337 case HW_INFO_TYPE_OOR:
338 val = netdev->hw_info.oor;
339 break;
340 case HW_INFO_TYPE_PEND_COUNT:
341 val = netdev->hw_info.pending_count;
342 break;
343 case HW_INFO_TYPE_OFFL_COUNT:
344 val = netdev->hw_info.offload_count;
345 break;
346 default:
347 break;
348 }
349
350 return val;
351 }
352
353 /*
354 * Set the value of the hw info parameter specified by type.
355 */
356 void
357 netdev_set_hw_info(struct netdev *netdev, int type, int val)
358 {
359 switch (type) {
360 case HW_INFO_TYPE_OOR:
361 if (val == 0) {
362 VLOG_DBG("Offload rebalance: netdev: %s is not OOR", netdev->name);
363 }
364 netdev->hw_info.oor = val;
365 break;
366 case HW_INFO_TYPE_PEND_COUNT:
367 netdev->hw_info.pending_count = val;
368 break;
369 case HW_INFO_TYPE_OFFL_COUNT:
370 netdev->hw_info.offload_count = val;
371 break;
372 default:
373 break;
374 }
375 }
376
377 /* Protects below port hashmaps. */
378 static struct ovs_rwlock netdev_hmap_rwlock = OVS_RWLOCK_INITIALIZER;
379
380 static struct hmap port_to_netdev OVS_GUARDED_BY(netdev_hmap_rwlock)
381 = HMAP_INITIALIZER(&port_to_netdev);
382 static struct hmap ifindex_to_port OVS_GUARDED_BY(netdev_hmap_rwlock)
383 = HMAP_INITIALIZER(&ifindex_to_port);
384
385 struct port_to_netdev_data {
386 struct hmap_node portno_node; /* By (dpif_type, dpif_port.port_no). */
387 struct hmap_node ifindex_node; /* By (dpif_type, ifindex). */
388 struct netdev *netdev;
389 struct dpif_port dpif_port;
390 int ifindex;
391 };
392
393 /*
394 * Find if any netdev is in OOR state. Return true if there's at least
395 * one netdev that's in OOR state; otherwise return false.
396 */
397 bool
398 netdev_any_oor(void)
399 OVS_EXCLUDED(netdev_hmap_rwlock)
400 {
401 struct port_to_netdev_data *data;
402 bool oor = false;
403
404 ovs_rwlock_rdlock(&netdev_hmap_rwlock);
405 HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
406 struct netdev *dev = data->netdev;
407
408 if (dev->hw_info.oor) {
409 oor = true;
410 break;
411 }
412 }
413 ovs_rwlock_unlock(&netdev_hmap_rwlock);
414
415 return oor;
416 }
417
418 bool
419 netdev_is_flow_api_enabled(void)
420 {
421 return netdev_flow_api_enabled;
422 }
423
424 void
425 netdev_ports_flow_flush(const char *dpif_type)
426 {
427 struct port_to_netdev_data *data;
428
429 ovs_rwlock_rdlock(&netdev_hmap_rwlock);
430 HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
431 if (netdev_get_dpif_type(data->netdev) == dpif_type) {
432 netdev_flow_flush(data->netdev);
433 }
434 }
435 ovs_rwlock_unlock(&netdev_hmap_rwlock);
436 }
437
438 struct netdev_flow_dump **
439 netdev_ports_flow_dump_create(const char *dpif_type, int *ports, bool terse)
440 {
441 struct port_to_netdev_data *data;
442 struct netdev_flow_dump **dumps;
443 int count = 0;
444 int i = 0;
445
446 ovs_rwlock_rdlock(&netdev_hmap_rwlock);
447 HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
448 if (netdev_get_dpif_type(data->netdev) == dpif_type) {
449 count++;
450 }
451 }
452
453 dumps = count ? xzalloc(sizeof *dumps * count) : NULL;
454
455 HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
456 if (netdev_get_dpif_type(data->netdev) == dpif_type) {
457 if (netdev_flow_dump_create(data->netdev, &dumps[i], terse)) {
458 continue;
459 }
460
461 dumps[i]->port = data->dpif_port.port_no;
462 i++;
463 }
464 }
465 ovs_rwlock_unlock(&netdev_hmap_rwlock);
466
467 *ports = i;
468 return dumps;
469 }
470
471 int
472 netdev_ports_flow_del(const char *dpif_type, const ovs_u128 *ufid,
473 struct dpif_flow_stats *stats)
474 {
475 struct port_to_netdev_data *data;
476
477 ovs_rwlock_rdlock(&netdev_hmap_rwlock);
478 HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
479 if (netdev_get_dpif_type(data->netdev) == dpif_type
480 && !netdev_flow_del(data->netdev, ufid, stats)) {
481 ovs_rwlock_unlock(&netdev_hmap_rwlock);
482 return 0;
483 }
484 }
485 ovs_rwlock_unlock(&netdev_hmap_rwlock);
486
487 return ENOENT;
488 }
489
490 int
491 netdev_ports_flow_get(const char *dpif_type, struct match *match,
492 struct nlattr **actions, const ovs_u128 *ufid,
493 struct dpif_flow_stats *stats,
494 struct dpif_flow_attrs *attrs, struct ofpbuf *buf)
495 {
496 struct port_to_netdev_data *data;
497
498 ovs_rwlock_rdlock(&netdev_hmap_rwlock);
499 HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
500 if (netdev_get_dpif_type(data->netdev) == dpif_type
501 && !netdev_flow_get(data->netdev, match, actions,
502 ufid, stats, attrs, buf)) {
503 ovs_rwlock_unlock(&netdev_hmap_rwlock);
504 return 0;
505 }
506 }
507 ovs_rwlock_unlock(&netdev_hmap_rwlock);
508 return ENOENT;
509 }
510
511 static uint32_t
512 netdev_ports_hash(odp_port_t port, const char *dpif_type)
513 {
514 return hash_int(odp_to_u32(port), hash_pointer(dpif_type, 0));
515 }
516
517 static struct port_to_netdev_data *
518 netdev_ports_lookup(odp_port_t port_no, const char *dpif_type)
519 OVS_REQ_RDLOCK(netdev_hmap_rwlock)
520 {
521 struct port_to_netdev_data *data;
522
523 HMAP_FOR_EACH_WITH_HASH (data, portno_node,
524 netdev_ports_hash(port_no, dpif_type),
525 &port_to_netdev) {
526 if (netdev_get_dpif_type(data->netdev) == dpif_type
527 && data->dpif_port.port_no == port_no) {
528 return data;
529 }
530 }
531 return NULL;
532 }
533
534 int
535 netdev_ports_insert(struct netdev *netdev, const char *dpif_type,
536 struct dpif_port *dpif_port)
537 {
538 struct port_to_netdev_data *data;
539 int ifindex = netdev_get_ifindex(netdev);
540
541 if (ifindex < 0) {
542 return ENODEV;
543 }
544
545 ovs_rwlock_wrlock(&netdev_hmap_rwlock);
546 if (netdev_ports_lookup(dpif_port->port_no, dpif_type)) {
547 ovs_rwlock_unlock(&netdev_hmap_rwlock);
548 return EEXIST;
549 }
550
551 data = xzalloc(sizeof *data);
552 data->netdev = netdev_ref(netdev);
553 dpif_port_clone(&data->dpif_port, dpif_port);
554 data->ifindex = ifindex;
555
556 netdev_set_dpif_type(netdev, dpif_type);
557
558 hmap_insert(&port_to_netdev, &data->portno_node,
559 netdev_ports_hash(dpif_port->port_no, dpif_type));
560 hmap_insert(&ifindex_to_port, &data->ifindex_node, ifindex);
561 ovs_rwlock_unlock(&netdev_hmap_rwlock);
562
563 netdev_init_flow_api(netdev);
564
565 return 0;
566 }
567
568 struct netdev *
569 netdev_ports_get(odp_port_t port_no, const char *dpif_type)
570 {
571 struct port_to_netdev_data *data;
572 struct netdev *ret = NULL;
573
574 ovs_rwlock_rdlock(&netdev_hmap_rwlock);
575 data = netdev_ports_lookup(port_no, dpif_type);
576 if (data) {
577 ret = netdev_ref(data->netdev);
578 }
579 ovs_rwlock_unlock(&netdev_hmap_rwlock);
580
581 return ret;
582 }
583
584 int
585 netdev_ports_remove(odp_port_t port_no, const char *dpif_type)
586 {
587 struct port_to_netdev_data *data;
588 int ret = ENOENT;
589
590 ovs_rwlock_wrlock(&netdev_hmap_rwlock);
591 data = netdev_ports_lookup(port_no, dpif_type);
592 if (data) {
593 dpif_port_destroy(&data->dpif_port);
594 netdev_close(data->netdev); /* unref and possibly close */
595 hmap_remove(&port_to_netdev, &data->portno_node);
596 hmap_remove(&ifindex_to_port, &data->ifindex_node);
597 free(data);
598 ret = 0;
599 }
600 ovs_rwlock_unlock(&netdev_hmap_rwlock);
601
602 return ret;
603 }
604
605 odp_port_t
606 netdev_ifindex_to_odp_port(int ifindex)
607 {
608 struct port_to_netdev_data *data;
609 odp_port_t ret = 0;
610
611 ovs_rwlock_rdlock(&netdev_hmap_rwlock);
612 HMAP_FOR_EACH_WITH_HASH (data, ifindex_node, ifindex, &ifindex_to_port) {
613 if (data->ifindex == ifindex) {
614 ret = data->dpif_port.port_no;
615 break;
616 }
617 }
618 ovs_rwlock_unlock(&netdev_hmap_rwlock);
619
620 return ret;
621 }
622
623 static bool netdev_offload_rebalance_policy = false;
624
625 bool
626 netdev_is_offload_rebalance_policy_enabled(void)
627 {
628 return netdev_offload_rebalance_policy;
629 }
630
631 static void
632 netdev_ports_flow_init(void)
633 {
634 struct port_to_netdev_data *data;
635
636 ovs_rwlock_rdlock(&netdev_hmap_rwlock);
637 HMAP_FOR_EACH (data, portno_node, &port_to_netdev) {
638 netdev_init_flow_api(data->netdev);
639 }
640 ovs_rwlock_unlock(&netdev_hmap_rwlock);
641 }
642
643 void
644 netdev_set_flow_api_enabled(const struct smap *ovs_other_config)
645 {
646 if (smap_get_bool(ovs_other_config, "hw-offload", false)) {
647 static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
648
649 if (ovsthread_once_start(&once)) {
650 netdev_flow_api_enabled = true;
651
652 VLOG_INFO("netdev: Flow API Enabled");
653
654 #ifdef __linux__
655 tc_set_policy(smap_get_def(ovs_other_config, "tc-policy",
656 TC_POLICY_DEFAULT));
657 #endif
658
659 if (smap_get_bool(ovs_other_config, "offload-rebalance", false)) {
660 netdev_offload_rebalance_policy = true;
661 }
662
663 netdev_ports_flow_init();
664
665 ovsthread_once_done(&once);
666 }
667 }
668 }