[ovs-dev] [PATCH v3 1/3] netdev: Dynamic per-port Flow API.
Roi Dayan
roid at mellanox.com
Tue May 14 15:16:59 UTC 2019
On 07/05/2019 12:24, Ilya Maximets wrote:
> Current issues with Flow API:
>
> * OVS calls offloading functions regardless of successful
> flow API initialization. (ex. on init_flow_api failure)
> * Static initilaization of Flow API for a netdev_class forbids
> having different offloading types for different instances
> of netdev with the same netdev_class. (ex. different vports in
> 'system' and 'netdev' datapaths at the same time)
>
> Solution:
>
> * Move Flow API from the netdev_class to netdev instance.
> * Make Flow API dynamic, i.e. probe the APIs and choose the
> suitable one.
>
> Side effects:
>
> * Flow API providers localized as possible in their modules.
> * Now we have an ability to make runtime checks. For example,
> we could check if particular device supports features we
> need, like if dpdk device supports RSS+MARK action.
>
> Signed-off-by: Ilya Maximets <i.maximets at samsung.com>
> ---
> lib/automake.mk | 2 +-
> lib/dpdk.c | 2 +
> lib/netdev-dpdk.c | 24 +++-
> lib/netdev-dpdk.h | 3 +
> lib/netdev-dummy.c | 24 +++-
> lib/netdev-linux.c | 3 -
> lib/netdev-linux.h | 10 --
> lib/netdev-offload-provider.h | 99 +++++++++++++++
> lib/netdev-provider.h | 67 +----------
> lib/netdev-rte-offloads.c | 40 +++++-
> lib/netdev-rte-offloads.h | 41 +------
> lib/netdev-tc-offloads.c | 39 ++++--
> lib/netdev-tc-offloads.h | 44 -------
> lib/netdev-vport.c | 6 +-
> lib/netdev.c | 221 +++++++++++++++++++++++++++-------
> tests/dpif-netdev.at | 4 +-
> tests/ofproto-macros.at | 1 -
> 17 files changed, 398 insertions(+), 232 deletions(-)
> create mode 100644 lib/netdev-offload-provider.h
> delete mode 100644 lib/netdev-tc-offloads.h
>
> diff --git a/lib/automake.mk b/lib/automake.mk
> index cc5dccf39..c70fda3f8 100644
> --- a/lib/automake.mk
> +++ b/lib/automake.mk
> @@ -137,6 +137,7 @@ lib_libopenvswitch_la_SOURCES = \
> lib/namemap.c \
> lib/netdev-dpdk.h \
> lib/netdev-dummy.c \
> + lib/netdev-offload-provider.h \
> lib/netdev-provider.h \
> lib/netdev-rte-offloads.h \
> lib/netdev-vport.c \
> @@ -393,7 +394,6 @@ lib_libopenvswitch_la_SOURCES += \
> lib/netdev-linux.c \
> lib/netdev-linux.h \
> lib/netdev-tc-offloads.c \
> - lib/netdev-tc-offloads.h \
> lib/netlink-conntrack.c \
> lib/netlink-conntrack.h \
> lib/netlink-notifier.c \
> diff --git a/lib/dpdk.c b/lib/dpdk.c
> index dc6171546..6c6298635 100644
> --- a/lib/dpdk.c
> +++ b/lib/dpdk.c
> @@ -34,6 +34,7 @@
> #include "dirs.h"
> #include "fatal-signal.h"
> #include "netdev-dpdk.h"
> +#include "netdev-rte-offloads.h"
> #include "openvswitch/dynamic-string.h"
> #include "openvswitch/vlog.h"
> #include "ovs-numa.h"
> @@ -442,6 +443,7 @@ dpdk_init__(const struct smap *ovs_other_config)
>
> /* Finally, register the dpdk classes */
> netdev_dpdk_register();
> + netdev_dpdk_flow_api_register();
> return true;
> }
>
> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index 47153dc60..c06c9ef81 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -4204,6 +4204,27 @@ unlock:
> return err;
> }
>
> +bool
> +netdev_dpdk_flow_api_supported(struct netdev *netdev)
> +{
> + struct netdev_dpdk *dev;
> + bool ret = false;
> +
> + if (!is_dpdk_class(netdev->netdev_class)) {
> + goto out;
> + }
> +
> + dev = netdev_dpdk_cast(netdev);
> + ovs_mutex_lock(&dev->mutex);
> + if (dev->type == DPDK_DEV_ETH) {
> + /* TODO: Check if we able to offload some minimal flow. */
> + ret = true;
> + }
> + ovs_mutex_unlock(&dev->mutex);
> +out:
> + return ret;
> +}
> +
> int
> netdev_dpdk_rte_flow_destroy(struct netdev *netdev,
> struct rte_flow *rte_flow,
> @@ -4268,8 +4289,7 @@ netdev_dpdk_rte_flow_create(struct netdev *netdev,
> .get_features = netdev_dpdk_get_features, \
> .get_status = netdev_dpdk_get_status, \
> .reconfigure = netdev_dpdk_reconfigure, \
> - .rxq_recv = netdev_dpdk_rxq_recv, \
> - DPDK_FLOW_OFFLOAD_API
> + .rxq_recv = netdev_dpdk_rxq_recv
>
> static const struct netdev_class dpdk_class = {
> .type = "dpdk",
> diff --git a/lib/netdev-dpdk.h b/lib/netdev-dpdk.h
> index 9bbb8d8d6..60631c4f0 100644
> --- a/lib/netdev-dpdk.h
> +++ b/lib/netdev-dpdk.h
> @@ -34,6 +34,9 @@ struct rte_flow_action;
>
> void netdev_dpdk_register(void);
> void free_dpdk_buf(struct dp_packet *);
> +
> +bool netdev_dpdk_flow_api_supported(struct netdev *);
> +
> int
> netdev_dpdk_rte_flow_destroy(struct netdev *netdev,
> struct rte_flow *rte_flow,
> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
> index 3f90ffa09..18eed4cf4 100644
> --- a/lib/netdev-dummy.c
> +++ b/lib/netdev-dummy.c
> @@ -24,6 +24,7 @@
> #include "dp-packet.h"
> #include "dpif-netdev.h"
> #include "flow.h"
> +#include "netdev-offload-provider.h"
> #include "netdev-provider.h"
> #include "netdev-vport.h"
> #include "odp-util.h"
> @@ -1523,10 +1524,6 @@ exit:
> return error ? -1 : 0;
> }
>
> -#define DUMMY_FLOW_OFFLOAD_API \
> - .flow_put = netdev_dummy_flow_put, \
> - .flow_del = netdev_dummy_flow_del
> -
> #define NETDEV_DUMMY_CLASS_COMMON \
> .run = netdev_dummy_run, \
> .wait = netdev_dummy_wait, \
> @@ -1559,8 +1556,7 @@ exit:
> .rxq_dealloc = netdev_dummy_rxq_dealloc, \
> .rxq_recv = netdev_dummy_rxq_recv, \
> .rxq_wait = netdev_dummy_rxq_wait, \
> - .rxq_drain = netdev_dummy_rxq_drain, \
> - DUMMY_FLOW_OFFLOAD_API
> + .rxq_drain = netdev_dummy_rxq_drain
>
> static const struct netdev_class dummy_class = {
> NETDEV_DUMMY_CLASS_COMMON,
> @@ -1578,6 +1574,20 @@ static const struct netdev_class dummy_pmd_class = {
> .is_pmd = true,
> .reconfigure = netdev_dummy_reconfigure
> };
> +
> +static int
> +netdev_dummy_offloads_init_flow_api(struct netdev *netdev)
> +{
> + return is_dummy_class(netdev->netdev_class) ? 0 : EOPNOTSUPP;
> +}
> +
> +static const struct netdev_flow_api netdev_dummy_offloads = {
> + .type = "dummy",
> + .flow_put = netdev_dummy_flow_put,
> + .flow_del = netdev_dummy_flow_del,
> + .init_flow_api = netdev_dummy_offloads_init_flow_api,
> +};
> +
>
> /* Helper functions. */
>
> @@ -2024,5 +2034,7 @@ netdev_dummy_register(enum dummy_level level)
> netdev_register_provider(&dummy_internal_class);
> netdev_register_provider(&dummy_pmd_class);
>
> + netdev_register_flow_api_provider(&netdev_dummy_offloads);
> +
> netdev_vport_tunnel_register();
> }
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> index f75d73fd3..e4ea94cf9 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -55,7 +55,6 @@
> #include "hash.h"
> #include "openvswitch/hmap.h"
> #include "netdev-provider.h"
> -#include "netdev-tc-offloads.h"
> #include "netdev-vport.h"
> #include "netlink-notifier.h"
> #include "netlink-socket.h"
> @@ -3321,7 +3320,6 @@ exit:
>
> const struct netdev_class netdev_linux_class = {
> NETDEV_LINUX_CLASS_COMMON,
> - LINUX_FLOW_OFFLOAD_API,
> .type = "system",
> .construct = netdev_linux_construct,
> .get_stats = netdev_linux_get_stats,
> @@ -3341,7 +3339,6 @@ const struct netdev_class netdev_tap_class = {
>
> const struct netdev_class netdev_internal_class = {
> NETDEV_LINUX_CLASS_COMMON,
> - LINUX_FLOW_OFFLOAD_API,
> .type = "internal",
> .construct = netdev_linux_construct,
> .get_stats = netdev_internal_get_stats,
> diff --git a/lib/netdev-linux.h b/lib/netdev-linux.h
> index 17ca91201..e1e30f806 100644
> --- a/lib/netdev-linux.h
> +++ b/lib/netdev-linux.h
> @@ -29,14 +29,4 @@ int netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
> const char *flag_name, bool enable);
> int linux_get_ifindex(const char *netdev_name);
>
> -#define LINUX_FLOW_OFFLOAD_API \
> - .flow_flush = netdev_tc_flow_flush, \
> - .flow_dump_create = netdev_tc_flow_dump_create, \
> - .flow_dump_destroy = netdev_tc_flow_dump_destroy, \
> - .flow_dump_next = netdev_tc_flow_dump_next, \
> - .flow_put = netdev_tc_flow_put, \
> - .flow_get = netdev_tc_flow_get, \
> - .flow_del = netdev_tc_flow_del, \
> - .init_flow_api = netdev_tc_init_flow_api
> -
> #endif /* netdev-linux.h */
> diff --git a/lib/netdev-offload-provider.h b/lib/netdev-offload-provider.h
> new file mode 100644
> index 000000000..ceeaa50b0
> --- /dev/null
> +++ b/lib/netdev-offload-provider.h
> @@ -0,0 +1,99 @@
> +/*
> + * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc.
> + * Copyright (c) 2019 Samsung Electronics Co.,Ltd.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + * https://eur03.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=02%7C01%7Croid%40mellanox.com%7C320331a5f32d437474f908d6d2cdca07%7Ca652971c7d2e4d9ba6a4d149256f461b%7C0%7C0%7C636928178634854039&sdata=%2BVQUoOrR5xFd6smiLUQOIrGGxxHi2FrVoHPkHGQ7hZU%3D&reserved=0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef NETDEV_FLOW_API_PROVIDER_H
> +#define NETDEV_FLOW_API_PROVIDER_H 1
> +
> +#include "flow.h"
> +#include "openvswitch/types.h"
> +#include "packets.h"
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +struct netdev_flow_api {
> + char *type;
> + /* Flush all offloaded flows from a netdev.
> + * Return 0 if successful, otherwise returns a positive errno value. */
> + int (*flow_flush)(struct netdev *);
> +
> + /* Flow dumping interface.
> + *
> + * This is the back-end for the flow dumping interface described in
> + * dpif.h. Please read the comments there first, because this code
> + * closely follows it.
> + *
> + * On success returns 0 and allocates data, on failure returns
> + * positive errno. */
> + int (*flow_dump_create)(struct netdev *, struct netdev_flow_dump **dump);
> + int (*flow_dump_destroy)(struct netdev_flow_dump *);
> +
> + /* Returns true if there are more flows to dump.
> + * 'rbuffer' is used as a temporary buffer and needs to be pre allocated
> + * by the caller. While there are more flows the same 'rbuffer'
> + * should be provided. 'wbuffer' is used to store dumped actions and needs
> + * to be pre allocated by the caller. */
> + bool (*flow_dump_next)(struct netdev_flow_dump *, struct match *,
> + struct nlattr **actions,
> + struct dpif_flow_stats *stats,
> + struct dpif_flow_attrs *attrs, ovs_u128 *ufid,
> + struct ofpbuf *rbuffer, struct ofpbuf *wbuffer);
> +
> + /* Offload the given flow on netdev.
> + * To modify a flow, use the same ufid.
> + * 'actions' are in netlink format, as with struct dpif_flow_put.
> + * 'info' is extra info needed to offload the flow.
> + * 'stats' is populated according to the rules set out in the description
> + * above 'struct dpif_flow_put'.
> + * Return 0 if successful, otherwise returns a positive errno value. */
> + int (*flow_put)(struct netdev *, struct match *, struct nlattr *actions,
> + size_t actions_len, const ovs_u128 *ufid,
> + struct offload_info *info, struct dpif_flow_stats *);
> +
> + /* Queries a flow specified by ufid on netdev.
> + * Fills output buffer as 'wbuffer' in flow_dump_next, which
> + * needs to be be pre allocated.
> + * Return 0 if successful, otherwise returns a positive errno value. */
> + int (*flow_get)(struct netdev *, struct match *, struct nlattr **actions,
> + const ovs_u128 *ufid, struct dpif_flow_stats *,
> + struct dpif_flow_attrs *, struct ofpbuf *wbuffer);
> +
> + /* Delete a flow specified by ufid from netdev.
> + * 'stats' is populated according to the rules set out in the description
> + * above 'struct dpif_flow_del'.
> + * Return 0 if successful, otherwise returns a positive errno value. */
> + int (*flow_del)(struct netdev *, const ovs_u128 *ufid,
> + struct dpif_flow_stats *);
> +
> + /* Initializies the netdev flow api.
> + * Return 0 if successful, otherwise returns a positive errno value. */
> + int (*init_flow_api)(struct netdev *);
> +};
> +
> +int netdev_register_flow_api_provider(const struct netdev_flow_api *);
> +int netdev_unregister_flow_api_provider(const char *type);
> +
> +#ifdef __linux__
> +extern const struct netdev_flow_api netdev_tc_offloads;
> +#endif
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* NETDEV_FLOW_API_PROVIDER_H */
> diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
> index fb0c27e6e..653854cbd 100644
> --- a/lib/netdev-provider.h
> +++ b/lib/netdev-provider.h
> @@ -23,6 +23,7 @@
> #include "netdev.h"
> #include "openvswitch/list.h"
> #include "ovs-numa.h"
> +#include "ovs-rcu.h"
> #include "packets.h"
> #include "seq.h"
> #include "openvswitch/shash.h"
> @@ -93,6 +94,9 @@ struct netdev {
> int n_rxq;
> struct shash_node *node; /* Pointer to element in global map. */
> struct ovs_list saved_flags_list; /* Contains "struct netdev_saved_flags". */
> +
> + /* Functions to control flow offloading. */
> + OVSRCU_TYPE(const struct netdev_flow_api *) flow_api;
> struct netdev_hw_info hw_info; /* offload-capable netdev info */
> };
>
> @@ -822,69 +826,6 @@ struct netdev_class {
> /* Discards all packets waiting to be received from 'rx'. */
> int (*rxq_drain)(struct netdev_rxq *rx);
>
> - /* ## -------------------------------- ## */
> - /* ## netdev flow offloading functions ## */
> - /* ## -------------------------------- ## */
> -
> - /* If a particular netdev class does not support offloading flows,
> - * all these function pointers must be NULL. */
> -
> - /* Flush all offloaded flows from a netdev.
> - * Return 0 if successful, otherwise returns a positive errno value. */
> - int (*flow_flush)(struct netdev *);
> -
> - /* Flow dumping interface.
> - *
> - * This is the back-end for the flow dumping interface described in
> - * dpif.h. Please read the comments there first, because this code
> - * closely follows it.
> - *
> - * On success returns 0 and allocates data, on failure returns
> - * positive errno. */
> - int (*flow_dump_create)(struct netdev *, struct netdev_flow_dump **dump);
> - int (*flow_dump_destroy)(struct netdev_flow_dump *);
> -
> - /* Returns true if there are more flows to dump.
> - * 'rbuffer' is used as a temporary buffer and needs to be pre allocated
> - * by the caller. While there are more flows the same 'rbuffer'
> - * should be provided. 'wbuffer' is used to store dumped actions and needs
> - * to be pre allocated by the caller. */
> - bool (*flow_dump_next)(struct netdev_flow_dump *, struct match *,
> - struct nlattr **actions,
> - struct dpif_flow_stats *stats,
> - struct dpif_flow_attrs *attrs, ovs_u128 *ufid,
> - struct ofpbuf *rbuffer, struct ofpbuf *wbuffer);
> -
> - /* Offload the given flow on netdev.
> - * To modify a flow, use the same ufid.
> - * 'actions' are in netlink format, as with struct dpif_flow_put.
> - * 'info' is extra info needed to offload the flow.
> - * 'stats' is populated according to the rules set out in the description
> - * above 'struct dpif_flow_put'.
> - * Return 0 if successful, otherwise returns a positive errno value. */
> - int (*flow_put)(struct netdev *, struct match *, struct nlattr *actions,
> - size_t actions_len, const ovs_u128 *ufid,
> - struct offload_info *info, struct dpif_flow_stats *);
> -
> - /* Queries a flow specified by ufid on netdev.
> - * Fills output buffer as 'wbuffer' in flow_dump_next, which
> - * needs to be be pre allocated.
> - * Return 0 if successful, otherwise returns a positive errno value. */
> - int (*flow_get)(struct netdev *, struct match *, struct nlattr **actions,
> - const ovs_u128 *ufid, struct dpif_flow_stats *,
> - struct dpif_flow_attrs *, struct ofpbuf *wbuffer);
> -
> - /* Delete a flow specified by ufid from netdev.
> - * 'stats' is populated according to the rules set out in the description
> - * above 'struct dpif_flow_del'.
> - * Return 0 if successful, otherwise returns a positive errno value. */
> - int (*flow_del)(struct netdev *, const ovs_u128 *ufid,
> - struct dpif_flow_stats *);
> -
> - /* Initializies the netdev flow api.
> - * Return 0 if successful, otherwise returns a positive errno value. */
> - int (*init_flow_api)(struct netdev *);
> -
> /* Get a block_id from the netdev.
> * Returns the block_id or 0 if none exists for netdev. */
> uint32_t (*get_block_id)(struct netdev *);
> diff --git a/lib/netdev-rte-offloads.c b/lib/netdev-rte-offloads.c
> index e9ab08624..683763f43 100644
> --- a/lib/netdev-rte-offloads.c
> +++ b/lib/netdev-rte-offloads.c
> @@ -21,6 +21,7 @@
>
> #include "cmap.h"
> #include "dpif-netdev.h"
> +#include "netdev-offload-provider.h"
> #include "netdev-provider.h"
> #include "openvswitch/match.h"
> #include "openvswitch/vlog.h"
> @@ -29,6 +30,23 @@
>
> VLOG_DEFINE_THIS_MODULE(netdev_rte_offloads);
>
> +/* Thread-safety
> + * =============
> + *
> + * Below API is NOT thread safe in following terms:
> + *
> + * - The caller must be sure that none of these functions will be called
> + * simultaneously. Even for different 'netdev's.
> + *
> + * - The caller must be sure that 'netdev' will not be destructed/deallocated.
> + *
> + * - The caller must be sure that 'netdev' configuration will not be changed.
> + * For example, simultaneous call of 'netdev_reconfigure()' for the same
> + * 'netdev' is forbidden.
> + *
> + * For current implementation all above restrictions could be fulfilled by
> + * taking the datapath 'port_mutex' in lib/dpif-netdev.c. */
> +
> /*
> * A mapping from ufid to dpdk rte_flow.
> */
> @@ -689,7 +707,7 @@ netdev_rte_offloads_destroy_flow(struct netdev *netdev,
> return ret;
> }
>
> -int
> +static int
> netdev_rte_offloads_flow_put(struct netdev *netdev, struct match *match,
> struct nlattr *actions, size_t actions_len,
> const ovs_u128 *ufid, struct offload_info *info,
> @@ -719,7 +737,7 @@ netdev_rte_offloads_flow_put(struct netdev *netdev, struct match *match,
> actions_len, ufid, info);
> }
>
> -int
> +static int
> netdev_rte_offloads_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
> struct dpif_flow_stats *stats OVS_UNUSED)
> {
> @@ -731,3 +749,21 @@ netdev_rte_offloads_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
>
> return netdev_rte_offloads_destroy_flow(netdev, ufid, rte_flow);
> }
> +
> +static int
> +netdev_rte_offloads_init_flow_api(struct netdev *netdev)
> +{
> + return netdev_dpdk_flow_api_supported(netdev) ? 0 : EOPNOTSUPP;
> +}
> +
> +static const struct netdev_flow_api netdev_dpdk_offloads = {
> + .type = "dpdk_flow_api",
> + .flow_put = netdev_rte_offloads_flow_put,
> + .flow_del = netdev_rte_offloads_flow_del,
> + .init_flow_api = netdev_rte_offloads_init_flow_api,
> +};
> +
> +void netdev_dpdk_flow_api_register(void)
> +{
> + netdev_register_flow_api_provider(&netdev_dpdk_offloads);
> +}
> diff --git a/lib/netdev-rte-offloads.h b/lib/netdev-rte-offloads.h
> index 18c8a7558..b9b292114 100644
> --- a/lib/netdev-rte-offloads.h
> +++ b/lib/netdev-rte-offloads.h
> @@ -14,44 +14,9 @@
> * limitations under the License.
> */
>
> -#ifndef NETDEV_VPORT_OFFLOADS_H
> -#define NETDEV_VPORT_OFFLOADS_H 1
> +#ifndef NETDEV_DPDK_OFFLOADS_H
> +#define NETDEV_DPDK_OFFLOADS_H 1
>
> -#include "openvswitch/types.h"
> -
> -struct netdev;
> -struct match;
> -struct nlattr;
> -struct offload_info;
> -struct dpif_flow_stats;
> -
> -/* Thread-safety
> - * =============
> - *
> - * Below API is NOT thread safe in following terms:
> - *
> - * - The caller must be sure that none of these functions will be called
> - * simultaneously. Even for different 'netdev's.
> - *
> - * - The caller must be sure that 'netdev' will not be destructed/deallocated.
> - *
> - * - The caller must be sure that 'netdev' configuration will not be changed.
> - * For example, simultaneous call of 'netdev_reconfigure()' for the same
> - * 'netdev' is forbidden.
> - *
> - * For current implementation all above restrictions could be fulfilled by
> - * taking the datapath 'port_mutex' in lib/dpif-netdev.c. */
> -
> -int netdev_rte_offloads_flow_put(struct netdev *netdev, struct match *match,
> - struct nlattr *actions, size_t actions_len,
> - const ovs_u128 *ufid,
> - struct offload_info *info,
> - struct dpif_flow_stats *stats);
> -int netdev_rte_offloads_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
> - struct dpif_flow_stats *stats);
> -
> -#define DPDK_FLOW_OFFLOAD_API \
> - .flow_put = netdev_rte_offloads_flow_put, \
> - .flow_del = netdev_rte_offloads_flow_del
> +void netdev_dpdk_flow_api_register(void);
>
> #endif /* netdev-rte-offloads.h */
> diff --git a/lib/netdev-tc-offloads.c b/lib/netdev-tc-offloads.c
> index d5c66acc1..b9a4a7302 100644
> --- a/lib/netdev-tc-offloads.c
> +++ b/lib/netdev-tc-offloads.c
> @@ -16,7 +16,6 @@
> */
>
> #include <config.h>
> -#include "netdev-tc-offloads.h"
>
> #include <errno.h>
> #include <linux/if_ether.h>
> @@ -31,6 +30,8 @@
> #include "openvswitch/util.h"
> #include "openvswitch/vlog.h"
> #include "netdev-linux.h"
> +#include "netdev-offload-provider.h"
> +#include "netdev-provider.h"
> #include "netlink.h"
> #include "netlink-socket.h"
> #include "odp-netlink.h"
> @@ -350,7 +351,7 @@ get_block_id_from_netdev(struct netdev *netdev)
> return 0;
> }
>
> -int
> +static int
> netdev_tc_flow_flush(struct netdev *netdev)
> {
> enum tc_qdisc_hook hook = get_tc_qdisc_hook(netdev);
> @@ -368,7 +369,7 @@ netdev_tc_flow_flush(struct netdev *netdev)
> return tc_flush(ifindex, block_id, hook);
> }
>
> -int
> +static int
> netdev_tc_flow_dump_create(struct netdev *netdev,
> struct netdev_flow_dump **dump_out)
> {
> @@ -395,7 +396,7 @@ netdev_tc_flow_dump_create(struct netdev *netdev,
> return 0;
> }
>
> -int
> +static int
> netdev_tc_flow_dump_destroy(struct netdev_flow_dump *dump)
> {
> nl_dump_done(dump->nl_dump);
> @@ -729,7 +730,7 @@ parse_tc_flower_to_match(struct tc_flower *flower,
> return 0;
> }
>
> -bool
> +static bool
> netdev_tc_flow_dump_next(struct netdev_flow_dump *dump,
> struct match *match,
> struct nlattr **actions,
> @@ -1082,7 +1083,7 @@ flower_match_to_tun_opt(struct tc_flower *flower, const struct flow_tnl *tnl,
> flower->mask.tunnel.metadata.present.len = tnl->metadata.present.len;
> }
>
> -int
> +static int
> netdev_tc_flow_put(struct netdev *netdev, struct match *match,
> struct nlattr *actions, size_t actions_len,
> const ovs_u128 *ufid, struct offload_info *info,
> @@ -1375,7 +1376,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
> return err;
> }
>
> -int
> +static int
> netdev_tc_flow_get(struct netdev *netdev OVS_UNUSED,
> struct match *match,
> struct nlattr **actions,
> @@ -1430,7 +1431,7 @@ netdev_tc_flow_get(struct netdev *netdev OVS_UNUSED,
> return 0;
> }
>
> -int
> +static int
> netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
> const ovs_u128 *ufid,
> struct dpif_flow_stats *stats)
> @@ -1550,7 +1551,7 @@ probe_tc_block_support(int ifindex)
> }
> }
>
> -int
> +static int
> netdev_tc_init_flow_api(struct netdev *netdev)
> {
> static struct ovsthread_once multi_mask_once = OVSTHREAD_ONCE_INITIALIZER;
> @@ -1562,8 +1563,8 @@ netdev_tc_init_flow_api(struct netdev *netdev)
>
> ifindex = netdev_get_ifindex(netdev);
> if (ifindex < 0) {
> - VLOG_ERR_RL(&error_rl, "init: failed to get ifindex for %s: %s",
> - netdev_get_name(netdev), ovs_strerror(-ifindex));
> + VLOG_INFO("init: failed to get ifindex for %s: %s",
> + netdev_get_name(netdev), ovs_strerror(-ifindex));
> return -ifindex;
> }
>
> @@ -1584,8 +1585,8 @@ netdev_tc_init_flow_api(struct netdev *netdev)
> error = tc_add_del_qdisc(ifindex, true, block_id, hook);
>
> if (error && error != EEXIST) {
> - VLOG_ERR("failed adding ingress qdisc required for offloading: %s",
> - ovs_strerror(error));
> + VLOG_INFO("failed adding ingress qdisc required for offloading: %s",
> + ovs_strerror(error));
> return error;
> }
>
> @@ -1593,3 +1594,15 @@ netdev_tc_init_flow_api(struct netdev *netdev)
>
> return 0;
> }
> +
> +const struct netdev_flow_api netdev_tc_offloads = {
> + .type = "linux_tc",
> + .flow_flush = netdev_tc_flow_flush,
> + .flow_dump_create = netdev_tc_flow_dump_create,
> + .flow_dump_destroy = netdev_tc_flow_dump_destroy,
> + .flow_dump_next = netdev_tc_flow_dump_next,
> + .flow_put = netdev_tc_flow_put,
> + .flow_get = netdev_tc_flow_get,
> + .flow_del = netdev_tc_flow_del,
> + .init_flow_api = netdev_tc_init_flow_api,
> +};
> diff --git a/lib/netdev-tc-offloads.h b/lib/netdev-tc-offloads.h
> deleted file mode 100644
> index ebd8ca884..000000000
> --- a/lib/netdev-tc-offloads.h
> +++ /dev/null
> @@ -1,44 +0,0 @@
> -/*
> - * Copyright (c) 2016 Mellanox Technologies, Ltd.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - * https://eur03.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0&data=02%7C01%7Croid%40mellanox.com%7C320331a5f32d437474f908d6d2cdca07%7Ca652971c7d2e4d9ba6a4d149256f461b%7C0%7C0%7C636928178634854039&sdata=%2BVQUoOrR5xFd6smiLUQOIrGGxxHi2FrVoHPkHGQ7hZU%3D&reserved=0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef NETDEV_TC_OFFLOADS_H
> -#define NETDEV_TC_OFFLOADS_H 1
> -
> -#include "netdev-provider.h"
> -
> -int netdev_tc_flow_flush(struct netdev *);
> -int netdev_tc_flow_dump_create(struct netdev *, struct netdev_flow_dump **);
> -int netdev_tc_flow_dump_destroy(struct netdev_flow_dump *);
> -bool netdev_tc_flow_dump_next(struct netdev_flow_dump *, struct match *,
> - struct nlattr **actions,
> - struct dpif_flow_stats *,
> - struct dpif_flow_attrs *,
> - ovs_u128 *ufid,
> - struct ofpbuf *rbuffer,
> - struct ofpbuf *wbuffer);
> -int netdev_tc_flow_put(struct netdev *, struct match *,
> - struct nlattr *actions, size_t actions_len,
> - const ovs_u128 *, struct offload_info *,
> - struct dpif_flow_stats *);
> -int netdev_tc_flow_get(struct netdev *, struct match *,
> - struct nlattr **actions, const ovs_u128 *,
> - struct dpif_flow_stats *,
> - struct dpif_flow_attrs *, struct ofpbuf *);
> -int netdev_tc_flow_del(struct netdev *, const ovs_u128 *,
> - struct dpif_flow_stats *);
> -int netdev_tc_init_flow_api(struct netdev *);
> -
> -#endif /* netdev-tc-offloads.h */
> diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
> index ab591667f..92a256af1 100644
> --- a/lib/netdev-vport.c
> +++ b/lib/netdev-vport.c
> @@ -47,7 +47,6 @@
> #include "unaligned.h"
> #include "unixctl.h"
> #include "openvswitch/vlog.h"
> -#include "netdev-tc-offloads.h"
> #ifdef __linux__
> #include "netdev-linux.h"
> #endif
> @@ -1116,10 +1115,8 @@ netdev_vport_get_ifindex(const struct netdev *netdev_)
> }
>
> #define NETDEV_VPORT_GET_IFINDEX netdev_vport_get_ifindex
> -#define NETDEV_FLOW_OFFLOAD_API , LINUX_FLOW_OFFLOAD_API
> #else /* !__linux__ */
> #define NETDEV_VPORT_GET_IFINDEX NULL
> -#define NETDEV_FLOW_OFFLOAD_API
> #endif /* __linux__ */
>
> #define VPORT_FUNCTIONS_COMMON \
> @@ -1133,8 +1130,7 @@ netdev_vport_get_ifindex(const struct netdev *netdev_)
> .get_etheraddr = netdev_vport_get_etheraddr, \
> .get_stats = netdev_vport_get_stats, \
> .get_pt_mode = netdev_vport_get_pt_mode, \
> - .update_flags = netdev_vport_update_flags \
> - NETDEV_FLOW_OFFLOAD_API
> + .update_flags = netdev_vport_update_flags
>
> #define TUNNEL_FUNCTIONS_COMMON \
> VPORT_FUNCTIONS_COMMON, \
> diff --git a/lib/netdev.c b/lib/netdev.c
> index 7d7ecf6f0..de40f72d8 100644
> --- a/lib/netdev.c
> +++ b/lib/netdev.c
> @@ -39,6 +39,7 @@
> #include "fatal-signal.h"
> #include "hash.h"
> #include "openvswitch/list.h"
> +#include "netdev-offload-provider.h"
> #include "netdev-provider.h"
> #include "netdev-vport.h"
> #include "odp-netlink.h"
> @@ -98,6 +99,22 @@ struct netdev_registered_class {
>
> static bool netdev_flow_api_enabled = false;
>
> +/* Protects 'netdev_flow_apis'. */
> +static struct ovs_mutex netdev_flow_api_provider_mutex = OVS_MUTEX_INITIALIZER;
> +
> +/* Contains 'struct netdev_registered_flow_api's. */
> +static struct cmap netdev_flow_apis = CMAP_INITIALIZER;
> +
> +struct netdev_registered_flow_api {
> + struct cmap_node cmap_node; /* In 'netdev_flow_apis', by flow_api->type. */
> + const struct netdev_flow_api *flow_api;
> +
> + /* Number of references: one for the flow_api itself and one for every
> + * instance of the netdev that uses it. */
> + struct ovs_refcount refcnt;
> +};
> +
> +
> /* This is set pretty low because we probably won't learn anything from the
> * additional log messages. */
> static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
> @@ -146,6 +163,8 @@ netdev_initialize(void)
> netdev_register_provider(&netdev_internal_class);
> netdev_register_provider(&netdev_tap_class);
> netdev_vport_tunnel_register();
> +
> + netdev_register_flow_api_provider(&netdev_tc_offloads);
> #endif
> #if defined(__FreeBSD__) || defined(__NetBSD__)
> netdev_register_provider(&netdev_tap_class);
> @@ -279,6 +298,87 @@ netdev_unregister_provider(const char *type)
> return error;
> }
>
> +static struct netdev_registered_flow_api *
> +netdev_lookup_flow_api(const char *type)
> +{
> + struct netdev_registered_flow_api *rfa;
> + CMAP_FOR_EACH_WITH_HASH (rfa, cmap_node, hash_string(type, 0),
> + &netdev_flow_apis) {
> + if (!strcmp(type, rfa->flow_api->type)) {
> + return rfa;
> + }
> + }
> + return NULL;
> +}
> +
> +/* Registers a new netdev flow api provider. */
> +int
> +netdev_register_flow_api_provider(const struct netdev_flow_api *new_flow_api)
> + OVS_EXCLUDED(netdev_flow_api_provider_mutex)
> +{
> + int error = 0;
> +
> + if (!new_flow_api->init_flow_api) {
> + VLOG_WARN("attempted to register invalid flow api provider: %s",
> + new_flow_api->type);
> + error = EINVAL;
> + }
> +
> + ovs_mutex_lock(&netdev_flow_api_provider_mutex);
> + if (netdev_lookup_flow_api(new_flow_api->type)) {
> + VLOG_WARN("attempted to register duplicate flow api provider: %s",
> + new_flow_api->type);
> + error = EEXIST;
> + } else {
> + struct netdev_registered_flow_api *rfa;
> +
> + rfa = xmalloc(sizeof *rfa);
> + cmap_insert(&netdev_flow_apis, &rfa->cmap_node,
> + hash_string(new_flow_api->type, 0));
> + rfa->flow_api = new_flow_api;
> + ovs_refcount_init(&rfa->refcnt);
> + VLOG_DBG("netdev: flow API '%s' registered.", new_flow_api->type);
> + }
> + ovs_mutex_unlock(&netdev_flow_api_provider_mutex);
> +
> + return error;
> +}
> +
> +/* Unregisters a netdev flow api provider. 'type' must have been previously
> + * registered and not currently be in use by any netdevs. After unregistration
> + * netdev flow api of that type cannot be used for netdevs. (However, the
> + * provider may still be accessible from other threads until the next RCU grace
> + * period, so the caller must not free or re-register the same netdev_flow_api
> + * until that has passed.) */
> +int
> +netdev_unregister_flow_api_provider(const char *type)
> + OVS_EXCLUDED(netdev_flow_api_provider_mutex)
> +{
> + struct netdev_registered_flow_api *rfa;
> + int error;
> +
> + ovs_mutex_lock(&netdev_flow_api_provider_mutex);
> + rfa = netdev_lookup_flow_api(type);
> + if (!rfa) {
> + VLOG_WARN("attempted to unregister a flow api provider that is not "
> + "registered: %s", type);
> + error = EAFNOSUPPORT;
> + } else if (ovs_refcount_unref(&rfa->refcnt) != 1) {
> + ovs_refcount_ref(&rfa->refcnt);
> + VLOG_WARN("attempted to unregister in use flow api provider: %s",
> + type);
> + error = EBUSY;
> + } else {
> + cmap_remove(&netdev_flow_apis, &rfa->cmap_node,
> + hash_string(rfa->flow_api->type, 0));
> + ovsrcu_postpone(free, rfa);
> + error = 0;
> + }
> + ovs_mutex_unlock(&netdev_flow_api_provider_mutex);
> +
> + return error;
> +}
> +
> /* Clears 'types' and enumerates the types of all currently registered netdev
> * providers into it. The caller must first initialize the sset. */
> void
> @@ -414,6 +514,7 @@ netdev_open(const char *name, const char *type, struct netdev **netdevp)
> netdev->reconfigure_seq = seq_create();
> netdev->last_reconfigure_seq =
> seq_read(netdev->reconfigure_seq);
> + ovsrcu_set(&netdev->flow_api, NULL);
> netdev->hw_info.oor = false;
> netdev->node = shash_add(&netdev_shash, name, netdev);
>
> @@ -562,6 +663,8 @@ netdev_unref(struct netdev *dev)
> ovs_assert(dev->ref_cnt);
> if (!--dev->ref_cnt) {
> const struct netdev_class *class = dev->netdev_class;
> + const struct netdev_flow_api *flow_api =
> + ovsrcu_get(const struct netdev_flow_api *, &dev->flow_api);
> struct netdev_registered_class *rc;
>
> dev->netdev_class->destruct(dev);
> @@ -576,6 +679,12 @@ netdev_unref(struct netdev *dev)
>
> rc = netdev_lookup_class(class->type);
> ovs_refcount_unref(&rc->refcnt);
> +
> + if (flow_api) {
> + struct netdev_registered_flow_api *rfa =
> + netdev_lookup_flow_api(flow_api->type);
> + ovs_refcount_unref(&rfa->refcnt);
> + }
> } else {
> ovs_mutex_unlock(&netdev_mutex);
> }
> @@ -2148,34 +2257,58 @@ netdev_reconfigure(struct netdev *netdev)
> : EOPNOTSUPP);
> }
>
> +static int
> +netdev_assign_flow_api(struct netdev *netdev)
> +{
> + struct netdev_registered_flow_api *rfa;
> +
> + CMAP_FOR_EACH (rfa, cmap_node, &netdev_flow_apis) {
> + if (!rfa->flow_api->init_flow_api(netdev)) {
> + ovs_refcount_ref(&rfa->refcnt);
> + ovsrcu_set(&netdev->flow_api, rfa->flow_api);
> + VLOG_INFO("%s: Assigned flow API '%s'.",
> + netdev_get_name(netdev), rfa->flow_api->type);
> + return 0;
> + }
> + VLOG_DBG("%s: flow API '%s' is not suitable.",
> + netdev_get_name(netdev), rfa->flow_api->type);
> + }
> + VLOG_INFO("%s: No suitable flow API found.", netdev_get_name(netdev));
> +
> + return -1;
> +}
> +
> int
> netdev_flow_flush(struct netdev *netdev)
> {
> - const struct netdev_class *class = netdev->netdev_class;
> + const struct netdev_flow_api *flow_api =
> + ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
>
> - return (class->flow_flush
> - ? class->flow_flush(netdev)
> - : EOPNOTSUPP);
> + return (flow_api && flow_api->flow_flush)
> + ? flow_api->flow_flush(netdev)
> + : EOPNOTSUPP;
> }
>
> int
> netdev_flow_dump_create(struct netdev *netdev, struct netdev_flow_dump **dump)
> {
> - const struct netdev_class *class = netdev->netdev_class;
> + const struct netdev_flow_api *flow_api =
> + ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
>
> - return (class->flow_dump_create
> - ? class->flow_dump_create(netdev, dump)
> - : EOPNOTSUPP);
> + return (flow_api && flow_api->flow_dump_create)
> + ? flow_api->flow_dump_create(netdev, dump)
> + : EOPNOTSUPP;
> }
>
> int
> netdev_flow_dump_destroy(struct netdev_flow_dump *dump)
> {
> - const struct netdev_class *class = dump->netdev->netdev_class;
> + const struct netdev_flow_api *flow_api =
> + ovsrcu_get(const struct netdev_flow_api *, &dump->netdev->flow_api);
>
> - return (class->flow_dump_destroy
> - ? class->flow_dump_destroy(dump)
> - : EOPNOTSUPP);
> + return (flow_api && flow_api->flow_dump_destroy)
> + ? flow_api->flow_dump_destroy(dump)
> + : EOPNOTSUPP;
> }
>
> bool
> @@ -2184,12 +2317,13 @@ netdev_flow_dump_next(struct netdev_flow_dump *dump, struct match *match,
> struct dpif_flow_attrs *attrs, ovs_u128 *ufid,
> struct ofpbuf *rbuffer, struct ofpbuf *wbuffer)
> {
> - const struct netdev_class *class = dump->netdev->netdev_class;
> + const struct netdev_flow_api *flow_api =
> + ovsrcu_get(const struct netdev_flow_api *, &dump->netdev->flow_api);
>
> - return (class->flow_dump_next
> - ? class->flow_dump_next(dump, match, actions, stats, attrs,
> - ufid, rbuffer, wbuffer)
> - : false);
> + return (flow_api && flow_api->flow_dump_next)
> + ? flow_api->flow_dump_next(dump, match, actions, stats, attrs,
> + ufid, rbuffer, wbuffer)
> + : false;
> }
>
> int
> @@ -2198,12 +2332,13 @@ netdev_flow_put(struct netdev *netdev, struct match *match,
> const ovs_u128 *ufid, struct offload_info *info,
> struct dpif_flow_stats *stats)
> {
> - const struct netdev_class *class = netdev->netdev_class;
> + const struct netdev_flow_api *flow_api =
> + ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
>
> - return (class->flow_put
> - ? class->flow_put(netdev, match, actions, act_len, ufid,
> - info, stats)
> - : EOPNOTSUPP);
> + return (flow_api && flow_api->flow_put)
> + ? flow_api->flow_put(netdev, match, actions, act_len, ufid,
> + info, stats)
> + : EOPNOTSUPP;
> }
>
> int
> @@ -2212,36 +2347,43 @@ netdev_flow_get(struct netdev *netdev, struct match *match,
> struct dpif_flow_stats *stats,
> struct dpif_flow_attrs *attrs, struct ofpbuf *buf)
> {
> - const struct netdev_class *class = netdev->netdev_class;
> + const struct netdev_flow_api *flow_api =
> + ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
>
> - return (class->flow_get
> - ? class->flow_get(netdev, match, actions, ufid, stats, attrs, buf)
> - : EOPNOTSUPP);
> + return (flow_api && flow_api->flow_get)
> + ? flow_api->flow_get(netdev, match, actions, ufid,
> + stats, attrs, buf)
> + : EOPNOTSUPP;
> }
>
> int
> netdev_flow_del(struct netdev *netdev, const ovs_u128 *ufid,
> struct dpif_flow_stats *stats)
> {
> - const struct netdev_class *class = netdev->netdev_class;
> + const struct netdev_flow_api *flow_api =
> + ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api);
>
> - return (class->flow_del
> - ? class->flow_del(netdev, ufid, stats)
> - : EOPNOTSUPP);
> + return (flow_api && flow_api->flow_del)
> + ? flow_api->flow_del(netdev, ufid, stats)
> + : EOPNOTSUPP;
> }
>
> int
> netdev_init_flow_api(struct netdev *netdev)
> {
> - const struct netdev_class *class = netdev->netdev_class;
> -
> if (!netdev_is_flow_api_enabled()) {
> return EOPNOTSUPP;
> }
>
> - return (class->init_flow_api
> - ? class->init_flow_api(netdev)
> - : EOPNOTSUPP);
> + if (ovsrcu_get(const struct netdev_flow_api *, &netdev->flow_api)) {
> + return 0;
> + }
> +
> + if (netdev_assign_flow_api(netdev)) {
> + return EOPNOTSUPP;
> + }
> +
> + return 0;
> }
>
> uint32_t
> @@ -2573,7 +2715,6 @@ netdev_is_offload_rebalance_policy_enabled(void)
> return netdev_offload_rebalance_policy;
> }
>
> -#ifdef __linux__
> static void
> netdev_ports_flow_init(void)
> {
> @@ -2597,8 +2738,10 @@ netdev_set_flow_api_enabled(const struct smap *ovs_other_config)
>
> VLOG_INFO("netdev: Flow API Enabled");
>
> +#ifdef __linux__
> tc_set_policy(smap_get_def(ovs_other_config, "tc-policy",
> TC_POLICY_DEFAULT));
> +#endif
>
> if (smap_get_bool(ovs_other_config, "offload-rebalance", false)) {
> netdev_offload_rebalance_policy = true;
> @@ -2610,9 +2753,3 @@ netdev_set_flow_api_enabled(const struct smap *ovs_other_config)
> }
> }
> }
> -#else
> -void
> -netdev_set_flow_api_enabled(const struct smap *ovs_other_config OVS_UNUSED)
> -{
> -}
> -#endif
> diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
> index 039ee971b..af8a29e44 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -361,9 +361,9 @@ AT_CLEANUP
>
> m4_define([DPIF_NETDEV_FLOW_HW_OFFLOAD],
> [AT_SETUP([dpif-netdev - partial hw offload - $1])
> - AT_SKIP_IF([test "$IS_WIN32" = "yes" || test "$IS_BSD" = "yes"])
> OVS_VSWITCHD_START(
> - [add-port br0 p1 -- set interface p1 type=$1 ofport_request=1 options:pstream=punix:$OVS_RUNDIR/p1.sock -- \
> + [add-port br0 p1 -- \
> + set interface p1 type=$1 ofport_request=1 options:pstream=punix:$OVS_RUNDIR/p1.sock options:ifindex=1 -- \
> set bridge br0 datapath-type=dummy \
> other-config:datapath-id=1234 fail-mode=secure], [], [],
> [m4_if([$1], [dummy-pmd], [--dummy-numa="0,0,0,0,1,1,1,1"], [])])
> diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
> index 2f33feabc..d0101b532 100644
> --- a/tests/ofproto-macros.at
> +++ b/tests/ofproto-macros.at
> @@ -350,7 +350,6 @@ m4_define([_OVS_VSWITCHD_START],
> /ofproto|INFO|datapath ID changed to fedcba9876543210/d
> /dpdk|INFO|DPDK Disabled - Use other_config:dpdk-init to enable/d
> /netlink_socket|INFO|netlink: could not enable listening to all nsid/d
> -/netdev: Flow API/d
> /probe tc:/d
> /tc: Using policy/d']])
> ])
>
Hi,
I did a quick ping test between two ports and after this commit the
command ovs-dpctl dump-flows doesn't show the flows that are in tc.
which did work before the commit.
ovs-appctl dpctl/dump-flows does show the flows fine.
I didn't have time to help debug but I'll try to help tomorrow.
Thanks,
Roi
More information about the dev
mailing list