2 * Copyright (c) 2008 - 2014, 2016, 2017 Nicira, Inc.
3 * Copyright (c) 2019 Samsung Electronics Co.,Ltd.
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:
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "netdev-offload.h"
23 #include <sys/types.h>
24 #include <netinet/in.h>
32 #include "dp-packet.h"
33 #include "openvswitch/dynamic-string.h"
34 #include "fatal-signal.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"
43 #include "openvswitch/ofp-print.h"
44 #include "openvswitch/poll-loop.h"
46 #include "openvswitch/shash.h"
48 #include "socket-util.h"
51 #include "openvswitch/vlog.h"
58 VLOG_DEFINE_THIS_MODULE(netdev_offload
);
61 static bool netdev_flow_api_enabled
= false;
63 /* Protects 'netdev_flow_apis'. */
64 static struct ovs_mutex netdev_flow_api_provider_mutex
= OVS_MUTEX_INITIALIZER
;
66 /* Contains 'struct netdev_registered_flow_api's. */
67 static struct cmap netdev_flow_apis
= CMAP_INITIALIZER
;
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
;
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
;
78 static struct netdev_registered_flow_api
*
79 netdev_lookup_flow_api(const char *type
)
81 struct netdev_registered_flow_api
*rfa
;
82 CMAP_FOR_EACH_WITH_HASH (rfa
, cmap_node
, hash_string(type
, 0),
84 if (!strcmp(type
, rfa
->flow_api
->type
)) {
91 /* Registers a new netdev flow api provider. */
93 netdev_register_flow_api_provider(const struct netdev_flow_api
*new_flow_api
)
94 OVS_EXCLUDED(netdev_flow_api_provider_mutex
)
98 if (!new_flow_api
->init_flow_api
) {
99 VLOG_WARN("attempted to register invalid flow api provider: %s",
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",
110 struct netdev_registered_flow_api
*rfa
;
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
);
119 ovs_mutex_unlock(&netdev_flow_api_provider_mutex
);
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.) */
131 netdev_unregister_flow_api_provider(const char *type
)
132 OVS_EXCLUDED(netdev_flow_api_provider_mutex
)
134 struct netdev_registered_flow_api
*rfa
;
137 ovs_mutex_lock(&netdev_flow_api_provider_mutex
);
138 rfa
= netdev_lookup_flow_api(type
);
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",
149 cmap_remove(&netdev_flow_apis
, &rfa
->cmap_node
,
150 hash_string(rfa
->flow_api
->type
, 0));
151 ovsrcu_postpone(free
, rfa
);
154 ovs_mutex_unlock(&netdev_flow_api_provider_mutex
);
160 netdev_flow_api_equals(const struct netdev
*netdev1
,
161 const struct netdev
*netdev2
)
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
);
168 return netdev_flow_api1
== netdev_flow_api2
;
172 netdev_assign_flow_api(struct netdev
*netdev
)
174 struct netdev_registered_flow_api
*rfa
;
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
);
184 VLOG_DBG("%s: flow API '%s' is not suitable.",
185 netdev_get_name(netdev
), rfa
->flow_api
->type
);
187 VLOG_INFO("%s: No suitable flow API found.", netdev_get_name(netdev
));
193 netdev_flow_flush(struct netdev
*netdev
)
195 const struct netdev_flow_api
*flow_api
=
196 ovsrcu_get(const struct netdev_flow_api
*, &netdev
->flow_api
);
198 return (flow_api
&& flow_api
->flow_flush
)
199 ? flow_api
->flow_flush(netdev
)
204 netdev_flow_dump_create(struct netdev
*netdev
, struct netdev_flow_dump
**dump
,
207 const struct netdev_flow_api
*flow_api
=
208 ovsrcu_get(const struct netdev_flow_api
*, &netdev
->flow_api
);
210 return (flow_api
&& flow_api
->flow_dump_create
)
211 ? flow_api
->flow_dump_create(netdev
, dump
, terse
)
216 netdev_flow_dump_destroy(struct netdev_flow_dump
*dump
)
218 const struct netdev_flow_api
*flow_api
=
219 ovsrcu_get(const struct netdev_flow_api
*, &dump
->netdev
->flow_api
);
221 return (flow_api
&& flow_api
->flow_dump_destroy
)
222 ? flow_api
->flow_dump_destroy(dump
)
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
)
232 const struct netdev_flow_api
*flow_api
=
233 ovsrcu_get(const struct netdev_flow_api
*, &dump
->netdev
->flow_api
);
235 return (flow_api
&& flow_api
->flow_dump_next
)
236 ? flow_api
->flow_dump_next(dump
, match
, actions
, stats
, attrs
,
237 ufid
, rbuffer
, wbuffer
)
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
)
247 const struct netdev_flow_api
*flow_api
=
248 ovsrcu_get(const struct netdev_flow_api
*, &netdev
->flow_api
);
250 return (flow_api
&& flow_api
->flow_put
)
251 ? flow_api
->flow_put(netdev
, match
, actions
, act_len
, ufid
,
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
)
262 const struct netdev_flow_api
*flow_api
=
263 ovsrcu_get(const struct netdev_flow_api
*, &netdev
->flow_api
);
265 return (flow_api
&& flow_api
->flow_get
)
266 ? flow_api
->flow_get(netdev
, match
, actions
, ufid
,
272 netdev_flow_del(struct netdev
*netdev
, const ovs_u128
*ufid
,
273 struct dpif_flow_stats
*stats
)
275 const struct netdev_flow_api
*flow_api
=
276 ovsrcu_get(const struct netdev_flow_api
*, &netdev
->flow_api
);
278 return (flow_api
&& flow_api
->flow_del
)
279 ? flow_api
->flow_del(netdev
, ufid
, stats
)
284 netdev_init_flow_api(struct netdev
*netdev
)
286 if (!netdev_is_flow_api_enabled()) {
290 if (ovsrcu_get(const struct netdev_flow_api
*, &netdev
->flow_api
)) {
294 if (netdev_assign_flow_api(netdev
)) {
302 netdev_uninit_flow_api(struct netdev
*netdev
)
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
);
312 ovsrcu_set(&netdev
->flow_api
, NULL
);
313 rfa
= netdev_lookup_flow_api(flow_api
->type
);
314 ovs_refcount_unref(&rfa
->refcnt
);
318 netdev_get_block_id(struct netdev
*netdev
)
320 const struct netdev_class
*class = netdev
->netdev_class
;
322 return (class->get_block_id
323 ? class->get_block_id(netdev
)
328 * Get the value of the hw info parameter specified by type.
329 * Returns the value on success (>= 0). Returns -1 on failure.
332 netdev_get_hw_info(struct netdev
*netdev
, int type
)
337 case HW_INFO_TYPE_OOR
:
338 val
= netdev
->hw_info
.oor
;
340 case HW_INFO_TYPE_PEND_COUNT
:
341 val
= netdev
->hw_info
.pending_count
;
343 case HW_INFO_TYPE_OFFL_COUNT
:
344 val
= netdev
->hw_info
.offload_count
;
354 * Set the value of the hw info parameter specified by type.
357 netdev_set_hw_info(struct netdev
*netdev
, int type
, int val
)
360 case HW_INFO_TYPE_OOR
:
362 VLOG_DBG("Offload rebalance: netdev: %s is not OOR", netdev
->name
);
364 netdev
->hw_info
.oor
= val
;
366 case HW_INFO_TYPE_PEND_COUNT
:
367 netdev
->hw_info
.pending_count
= val
;
369 case HW_INFO_TYPE_OFFL_COUNT
:
370 netdev
->hw_info
.offload_count
= val
;
377 /* Protects below port hashmaps. */
378 static struct ovs_rwlock netdev_hmap_rwlock
= OVS_RWLOCK_INITIALIZER
;
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
);
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
;
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.
399 OVS_EXCLUDED(netdev_hmap_rwlock
)
401 struct port_to_netdev_data
*data
;
404 ovs_rwlock_rdlock(&netdev_hmap_rwlock
);
405 HMAP_FOR_EACH (data
, portno_node
, &port_to_netdev
) {
406 struct netdev
*dev
= data
->netdev
;
408 if (dev
->hw_info
.oor
) {
413 ovs_rwlock_unlock(&netdev_hmap_rwlock
);
419 netdev_is_flow_api_enabled(void)
421 return netdev_flow_api_enabled
;
425 netdev_ports_flow_flush(const char *dpif_type
)
427 struct port_to_netdev_data
*data
;
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
);
435 ovs_rwlock_unlock(&netdev_hmap_rwlock
);
438 struct netdev_flow_dump
**
439 netdev_ports_flow_dump_create(const char *dpif_type
, int *ports
, bool terse
)
441 struct port_to_netdev_data
*data
;
442 struct netdev_flow_dump
**dumps
;
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
) {
453 dumps
= count
? xzalloc(sizeof *dumps
* count
) : NULL
;
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
)) {
461 dumps
[i
]->port
= data
->dpif_port
.port_no
;
465 ovs_rwlock_unlock(&netdev_hmap_rwlock
);
472 netdev_ports_flow_del(const char *dpif_type
, const ovs_u128
*ufid
,
473 struct dpif_flow_stats
*stats
)
475 struct port_to_netdev_data
*data
;
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
);
485 ovs_rwlock_unlock(&netdev_hmap_rwlock
);
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
)
496 struct port_to_netdev_data
*data
;
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
);
507 ovs_rwlock_unlock(&netdev_hmap_rwlock
);
512 netdev_ports_hash(odp_port_t port
, const char *dpif_type
)
514 return hash_int(odp_to_u32(port
), hash_pointer(dpif_type
, 0));
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
)
521 struct port_to_netdev_data
*data
;
523 HMAP_FOR_EACH_WITH_HASH (data
, portno_node
,
524 netdev_ports_hash(port_no
, dpif_type
),
526 if (netdev_get_dpif_type(data
->netdev
) == dpif_type
527 && data
->dpif_port
.port_no
== port_no
) {
535 netdev_ports_insert(struct netdev
*netdev
, const char *dpif_type
,
536 struct dpif_port
*dpif_port
)
538 struct port_to_netdev_data
*data
;
539 int ifindex
= netdev_get_ifindex(netdev
);
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
);
551 data
= xzalloc(sizeof *data
);
552 data
->netdev
= netdev_ref(netdev
);
553 dpif_port_clone(&data
->dpif_port
, dpif_port
);
554 data
->ifindex
= ifindex
;
556 netdev_set_dpif_type(netdev
, dpif_type
);
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
);
563 netdev_init_flow_api(netdev
);
569 netdev_ports_get(odp_port_t port_no
, const char *dpif_type
)
571 struct port_to_netdev_data
*data
;
572 struct netdev
*ret
= NULL
;
574 ovs_rwlock_rdlock(&netdev_hmap_rwlock
);
575 data
= netdev_ports_lookup(port_no
, dpif_type
);
577 ret
= netdev_ref(data
->netdev
);
579 ovs_rwlock_unlock(&netdev_hmap_rwlock
);
585 netdev_ports_remove(odp_port_t port_no
, const char *dpif_type
)
587 struct port_to_netdev_data
*data
;
590 ovs_rwlock_wrlock(&netdev_hmap_rwlock
);
591 data
= netdev_ports_lookup(port_no
, dpif_type
);
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
);
600 ovs_rwlock_unlock(&netdev_hmap_rwlock
);
606 netdev_ifindex_to_odp_port(int ifindex
)
608 struct port_to_netdev_data
*data
;
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
;
618 ovs_rwlock_unlock(&netdev_hmap_rwlock
);
623 static bool netdev_offload_rebalance_policy
= false;
626 netdev_is_offload_rebalance_policy_enabled(void)
628 return netdev_offload_rebalance_policy
;
632 netdev_ports_flow_init(void)
634 struct port_to_netdev_data
*data
;
636 ovs_rwlock_rdlock(&netdev_hmap_rwlock
);
637 HMAP_FOR_EACH (data
, portno_node
, &port_to_netdev
) {
638 netdev_init_flow_api(data
->netdev
);
640 ovs_rwlock_unlock(&netdev_hmap_rwlock
);
644 netdev_set_flow_api_enabled(const struct smap
*ovs_other_config
)
646 if (smap_get_bool(ovs_other_config
, "hw-offload", false)) {
647 static struct ovsthread_once once
= OVSTHREAD_ONCE_INITIALIZER
;
649 if (ovsthread_once_start(&once
)) {
650 netdev_flow_api_enabled
= true;
652 VLOG_INFO("netdev: Flow API Enabled");
655 tc_set_policy(smap_get_def(ovs_other_config
, "tc-policy",
659 if (smap_get_bool(ovs_other_config
, "offload-rebalance", false)) {
660 netdev_offload_rebalance_policy
= true;
663 netdev_ports_flow_init();
665 ovsthread_once_done(&once
);