On 2 Dec 2025, at 15:05, Eelco Chaudron via dev wrote:
> Add flow control API calls to the dummy offload provider, enabling
> the re-addition of previously disabled partial offload tests.
>
> Signed-off-by: Eelco Chaudron <[email protected]>
> ---
>
> v2 changes:
> - Fix indentation in dpif_offload_dummy_cleanup_flow_pmd_data().
> - Renamed remote_from_port to remove_from_port variable.
I just noticed that some of the actual partial HW offload was not implemented
due to changes in the latest RFC version, where partial HW offload was moved to
the dpif-offload provider level. The following change needs to be included in
this patch (will be part of the v3:
diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c
index 58a579206..a62197f89 100644
--- a/lib/dpif-offload-dummy.c
+++ b/lib/dpif-offload-dummy.c
@@ -284,6 +284,8 @@ static void
dpif_offload_dummy_enable_offload(struct dpif_offload *dpif_offload,
struct dpif_offload_port_mgr_port *port)
{
+ atomic_store_relaxed(&port->netdev->hw_info.post_process_api_supported,
+ true);
dpif_offload_set_netdev_offload(port->netdev, dpif_offload);
}
@@ -611,6 +613,41 @@ dpif_offload_dummy_get_port_by_netdev(const struct
dpif_offload *offload_,
return dpif_offload_dummy_cast_port(port);
}
+static int
+dpif_offload_dummy_netdev_hw_post_process(
+ const struct dpif_offload *offload_, struct netdev *netdev,
+ unsigned pmd_id, struct dp_packet *packet, void **flow_reference_)
+{
+ struct dummy_offloaded_flow *off_flow;
+ struct dpif_offload_dummy_port *port;
+ void *flow_reference = NULL;
+ uint32_t flow_mark;
+
+ port = dpif_offload_dummy_get_port_by_netdev(offload_, netdev);
+ if (!port || !dp_packet_has_flow_mark(packet, &flow_mark)) {
+ *flow_reference_ = NULL;
+ return 0;
+ }
+
+ ovs_mutex_lock(&port->port_mutex);
+ HMAP_FOR_EACH (off_flow, node, &port->offloaded_flows) {
+ struct pmd_id_data *pmd_data;
+
+ if (flow_mark == off_flow->mark) {
+ pmd_data = dpif_offload_dummy_find_flow_pmd_data(port, off_flow,
+ pmd_id);
+ if (pmd_data) {
+ flow_reference = pmd_data->flow_reference;
+ }
+ break;
+ }
+ }
+ ovs_mutex_unlock(&port->port_mutex);
+
+ *flow_reference_ = flow_reference;
+ return 0;
+}
+
static int
dpif_offload_dummy_netdev_flow_put(const struct dpif_offload *offload_,
struct netdev *netdev,
@@ -906,6 +943,8 @@ dpif_offload_dummy_pmd_thread_lifecycle(
.port_dump_next = dpif_offload_dummy_port_dump_next, \
.port_dump_done = dpif_offload_dummy_port_dump_done, \
.get_netdev = dpif_offload_dummy_get_netdev, \
+ .netdev_hw_post_process = \
+ dpif_offload_dummy_netdev_hw_post_process, \
.netdev_flow_put = dpif_offload_dummy_netdev_flow_put, \
.netdev_flow_del = dpif_offload_dummy_netdev_flow_del, \
.netdev_flow_stats = dpif_offload_dummy_netdev_flow_stats, \
diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
index e17a16de2..4a6b676f3 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -461,6 +461,13 @@
recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), p
AT_CHECK([filter_hw_flow_del < ovs-vswitchd.log | strip_xout], [0], [dnl
p1: flow del: mark: 1
])
+
+ # Check if partial hw offload was hit in dpif-netdev.
+ if test "$1" = "dummy-pmd"; then
+ AT_CHECK([ovs-appctl dpif-netdev/pmd-perf-show \
+ | grep -q "PHWOL hits: 1"])
+ fi
+
OVS_VSWITCHD_STOP
AT_CLEANUP])
@@ -538,6 +545,12 @@
udp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:
udp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,nw_src=192.168.0.7,nw_dst=127.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,nw_frag=no,tp_src=81,tp_dst=3773
])
+ # Check if partial hw offload was hit in dpif-netdev.
+ if test "$1" = "dummy-pmd"; then
+ AT_CHECK([ovs-appctl dpif-netdev/pmd-perf-show \
+ | grep -q "PHWOL hits: 1"])
+ fi
+
OVS_VSWITCHD_STOP
AT_CLEANUP])
@@ -615,6 +628,12 @@
arp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:
arp,in_port=ANY,dl_vlan=11,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:0a,dl_dst=00:01:02:03:04:05,arp_spa=127.0.0.1,arp_tpa=127.0.0.1,arp_op=1,arp_sha=00:0b:0c:0d:0e:0f,arp_tha=00:00:00:00:00:00
])
+ # Check if partial hw offload was hit in dpif-netdev.
+ if test "$1" = "dummy-pmd"; then
+ AT_CHECK([ovs-appctl dpif-netdev/pmd-perf-show \
+ | grep -q "PHWOL hits: 1"])
+ fi
+
OVS_VSWITCHD_STOP
AT_CLEANUP])
> ---
> lib/dpif-offload-dummy.c | 610 +++++++++++++++++++++++++++++++++++++--
> lib/dummy.h | 6 +
> lib/netdev-dummy.c | 4 +-
> tests/dpif-netdev.at | 33 +--
> 4 files changed, 608 insertions(+), 45 deletions(-)
>
> diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c
> index ce849e3dd..cd2474df8 100644
> --- a/lib/dpif-offload-dummy.c
> +++ b/lib/dpif-offload-dummy.c
> @@ -21,25 +21,262 @@
> #include "dpif-offload-provider.h"
> #include "dpif-offload.h"
> #include "dummy.h"
> +#include "id-fpool.h"
> #include "netdev-provider.h"
> +#include "odp-util.h"
> #include "util.h"
> +#include "uuid.h"
>
> #include "openvswitch/json.h"
> +#include "openvswitch/match.h"
> +#include "openvswitch/vlog.h"
> +
> +VLOG_DEFINE_THIS_MODULE(dpif_offload_dummy);
> +
> +struct pmd_id_data {
> + struct hmap_node node;
> + void *flow_reference;
> + unsigned pmd_id;
> +};
> +
> +struct dummy_offloaded_flow {
> + struct hmap_node node;
> + struct match match;
> + ovs_u128 ufid;
> + uint32_t mark;
> +
> + /* The pmd_id_map below is also protected by the port_mutex. */
> + struct hmap pmd_id_map;
> + };
>
> struct dpif_offload_dummy {
> struct dpif_offload offload;
> struct dpif_offload_port_mgr *port_mgr;
> + struct id_fpool *flow_mark_pool;
> + dpif_offload_flow_unreference_cb *unreference_cb;
>
> /* Configuration specific variables. */
> struct ovsthread_once once_enable; /* Track first-time enablement. */
> };
>
> +struct dpif_offload_dummy_port {
> + struct dpif_offload_port_mgr_port pm_port;
> +
> + struct ovs_mutex port_mutex; /* Protect all below members. */
> + struct hmap offloaded_flows OVS_GUARDED;
> +};
> +
> +static void dpif_offload_dummy_flow_unreference(struct dpif_offload_dummy *,
> + unsigned pmd_id,
> + void *flow_reference);
> +
> +static uint32_t
> +dpif_offload_dummy_allocate_flow_mark(struct dpif_offload_dummy
> *offload_dummy)
> +{
> + static struct ovsthread_once init_once = OVSTHREAD_ONCE_INITIALIZER;
> + uint32_t flow_mark;
> +
> + if (ovsthread_once_start(&init_once)) {
> + /* Haven't initiated yet, do it here. */
> + offload_dummy->flow_mark_pool = id_fpool_create(1, 1, UINT32_MAX -
> 1);
> + ovsthread_once_done(&init_once);
> + }
> +
> + if (id_fpool_new_id(offload_dummy->flow_mark_pool, 0, &flow_mark)) {
> + return flow_mark;
> + }
> +
> + return INVALID_FLOW_MARK;
> +}
> +
> +static void
> +dpif_offload_dummy_free_flow_mark(struct dpif_offload_dummy *offload_dummy,
> + uint32_t flow_mark)
> +{
> + if (flow_mark != INVALID_FLOW_MARK) {
> + id_fpool_free_id(offload_dummy->flow_mark_pool, 0, flow_mark);
> + }
> +}
> +
> +static struct dpif_offload_dummy_port *
> +dpif_offload_dummy_cast_port(struct dpif_offload_port_mgr_port *port)
> +{
> + return CONTAINER_OF(port, struct dpif_offload_dummy_port, pm_port);
> +}
> +
> static struct dpif_offload_dummy *
> dpif_offload_dummy_cast(const struct dpif_offload *offload)
> {
> return CONTAINER_OF(offload, struct dpif_offload_dummy, offload);
> }
>
> +static uint32_t
> +dpif_offload_dummy_flow_hash(const ovs_u128 *ufid)
> +{
> + return ufid->u32[0];
> +}
> +
> +static struct pmd_id_data *
> +dpif_offload_dummy_find_flow_pmd_data(
> + struct dpif_offload_dummy_port *port OVS_UNUSED,
> + struct dummy_offloaded_flow *off_flow, unsigned pmd_id)
> + OVS_REQUIRES(port->port_mutex)
> +{
> + size_t hash = hash_int(pmd_id, 0);
> + struct pmd_id_data *data;
> +
> + HMAP_FOR_EACH_WITH_HASH (data, node, hash, &off_flow->pmd_id_map) {
> + if (data->pmd_id == pmd_id) {
> + return data;
> + }
> + }
> + return NULL;
> +}
> +
> +static void
> +dpif_offload_dummy_add_flow_pmd_data(
> + struct dpif_offload_dummy_port *port OVS_UNUSED,
> + struct dummy_offloaded_flow *off_flow, unsigned pmd_id,
> + void *flow_reference) OVS_REQUIRES(port->port_mutex)
> +{
> + struct pmd_id_data *pmd_data = xmalloc(sizeof *pmd_data);
> +
> + pmd_data->pmd_id = pmd_id;
> + pmd_data->flow_reference = flow_reference;
> + hmap_insert(&off_flow->pmd_id_map, &pmd_data->node,
> + hash_int(pmd_id, 0));
> +}
> +
> +static void
> +dpif_offload_dummy_update_add_flow_pmd_data(
> + struct dpif_offload_dummy_port *port,
> + struct dummy_offloaded_flow *off_flow, unsigned pmd_id,
> + void *flow_reference, void **previous_flow_reference)
> + OVS_REQUIRES(port->port_mutex)
> +{
> + struct pmd_id_data *data = dpif_offload_dummy_find_flow_pmd_data(port,
> +
> off_flow,
> + pmd_id);
> +
> + if (data) {
> + *previous_flow_reference = data->flow_reference;
> + data->flow_reference = flow_reference;
> + } else {
> + dpif_offload_dummy_add_flow_pmd_data(port, off_flow, pmd_id,
> + flow_reference);
> + *previous_flow_reference = NULL;
> + }
> +}
> +
> +static bool
> +dpif_offload_dummy_del_flow_pmd_data(
> + struct dpif_offload_dummy_port *port OVS_UNUSED,
> + struct dummy_offloaded_flow *off_flow, unsigned pmd_id,
> + void *flow_reference) OVS_REQUIRES(port->port_mutex)
> +{
> + size_t hash = hash_int(pmd_id, 0);
> + struct pmd_id_data *data;
> +
> + HMAP_FOR_EACH_WITH_HASH (data, node, hash, &off_flow->pmd_id_map) {
> + if (data->pmd_id == pmd_id && data->flow_reference ==
> flow_reference) {
> + hmap_remove(&off_flow->pmd_id_map, &data->node);
> + free(data);
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> +static void
> +dpif_offload_dummy_cleanup_flow_pmd_data(
> + struct dpif_offload_dummy *offload,
> + struct dpif_offload_dummy_port *port OVS_UNUSED,
> + struct dummy_offloaded_flow *off_flow) OVS_REQUIRES(port->port_mutex)
> +{
> + struct pmd_id_data *data;
> +
> + HMAP_FOR_EACH_SAFE (data, node, &off_flow->pmd_id_map) {
> + hmap_remove(&off_flow->pmd_id_map, &data->node);
> +
> + dpif_offload_dummy_flow_unreference(offload, data->pmd_id,
> + data->flow_reference);
> + free(data);
> + }
> +}
> +
> +static struct dummy_offloaded_flow *
> +dpif_offload_dummy_add_flow(struct dpif_offload_dummy_port *port,
> + const ovs_u128 *ufid, unsigned pmd_id,
> + void *flow_reference, uint32_t mark)
> + OVS_REQUIRES(port->port_mutex)
> +{
> + struct dummy_offloaded_flow *off_flow = xzalloc(sizeof *off_flow);
> +
> + off_flow->mark = mark;
> + memcpy(&off_flow->ufid, ufid, sizeof off_flow->ufid);
> + hmap_init(&off_flow->pmd_id_map);
> + dpif_offload_dummy_add_flow_pmd_data(port, off_flow, pmd_id,
> + flow_reference);
> +
> + hmap_insert(&port->offloaded_flows, &off_flow->node,
> + dpif_offload_dummy_flow_hash(ufid));
> +
> + return off_flow;
> +}
> +
> +static void
> +dpif_offload_dummy_free_flow(struct dpif_offload_dummy_port *port,
> + struct dummy_offloaded_flow *off_flow,
> + bool remove_from_port)
> + OVS_REQUIRES(port->port_mutex)
> +{
> + if (remove_from_port) {
> + hmap_remove(&port->offloaded_flows, &off_flow->node);
> + }
> + ovs_assert(!hmap_count(&off_flow->pmd_id_map));
> +
> + hmap_destroy(&off_flow->pmd_id_map);
> + free(off_flow);
> +}
> +
> +static struct dummy_offloaded_flow *
> +dpif_offload_dummy_find_offloaded_flow(struct dpif_offload_dummy_port *port,
> + const ovs_u128 *ufid)
> + OVS_REQUIRES(port->port_mutex)
> +{
> + uint32_t hash = dpif_offload_dummy_flow_hash(ufid);
> + struct dummy_offloaded_flow *data;
> +
> + HMAP_FOR_EACH_WITH_HASH (data, node, hash, &port->offloaded_flows) {
> + if (ovs_u128_equals(*ufid, data->ufid)) {
> + return data;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +static struct dummy_offloaded_flow *
> +dpif_offload_dummy_find_offloaded_flow_and_update(
> + struct dpif_offload_dummy_port *port, const ovs_u128 *ufid,
> + unsigned pmd_id, void *new_flow_reference, void
> **previous_flow_reference)
> + OVS_REQUIRES(port->port_mutex)
> +{
> + struct dummy_offloaded_flow *off_flow;
> +
> + off_flow = dpif_offload_dummy_find_offloaded_flow(port, ufid);
> + if (!off_flow) {
> + return NULL;
> + }
> +
> + dpif_offload_dummy_update_add_flow_pmd_data(port, off_flow, pmd_id,
> + new_flow_reference,
> + previous_flow_reference);
> +
> + return off_flow;
> +}
> +
> static void
> dpif_offload_dummy_enable_offload(struct dpif_offload *dpif_offload,
> struct dpif_offload_port_mgr_port *port)
> @@ -55,24 +292,69 @@ dpif_offload_dummy_cleanup_offload(
> dpif_offload_set_netdev_offload(port->netdev, NULL);
> }
>
> +static void
> +dpif_offload_dummy_free_port_(struct dpif_offload_dummy *offload,
> + struct dpif_offload_dummy_port *port)
> +{
> + struct dummy_offloaded_flow *off_flow;
> +
> + ovs_mutex_lock(&port->port_mutex);
> + HMAP_FOR_EACH_POP (off_flow, node, &port->offloaded_flows) {
> + dpif_offload_dummy_cleanup_flow_pmd_data(offload, port, off_flow);
> + dpif_offload_dummy_free_flow(port, off_flow, false);
> + }
> + hmap_destroy(&port->offloaded_flows);
> + ovs_mutex_unlock(&port->port_mutex);
> + ovs_mutex_destroy(&port->port_mutex);
> + free(port);
> +}
> +
> +struct free_port_rcu {
> + struct dpif_offload_dummy *offload;
> + struct dpif_offload_dummy_port *port;
> +};
> +
> +static void
> +dpif_offload_dummy_free_port_rcu(struct free_port_rcu *fpc)
> +{
> + dpif_offload_dummy_free_port_(fpc->offload, fpc->port);
> + free(fpc);
> +}
> +
> +static void
> +dpif_offload_dummy_free_port(struct dpif_offload_dummy *offload,
> + struct dpif_offload_dummy_port *port)
> +{
> + struct free_port_rcu *fpc = xmalloc(sizeof *fpc);
> +
> + fpc->offload = offload;
> + fpc->port = port;
> + ovsrcu_postpone(dpif_offload_dummy_free_port_rcu, fpc);
> +}
> +
> static int
> dpif_offload_dummy_port_add(struct dpif_offload *dpif_offload,
> struct netdev *netdev, odp_port_t port_no)
> {
> - struct dpif_offload_port_mgr_port *port = xmalloc(sizeof *port);
> + struct dpif_offload_dummy_port *port = xmalloc(sizeof *port);
> struct dpif_offload_dummy *offload_dummy;
>
> + ovs_mutex_init(&port->port_mutex);
> + ovs_mutex_lock(&port->port_mutex);
> + hmap_init(&port->offloaded_flows);
> + ovs_mutex_unlock(&port->port_mutex);
> +
> offload_dummy = dpif_offload_dummy_cast(dpif_offload);
> - if (dpif_offload_port_mgr_add(offload_dummy->port_mgr, port, netdev,
> - port_no, false)) {
> + if (dpif_offload_port_mgr_add(offload_dummy->port_mgr, &port->pm_port,
> + netdev, port_no, false)) {
>
> if (dpif_offload_is_offload_enabled()) {
> - dpif_offload_dummy_enable_offload(dpif_offload, port);
> + dpif_offload_dummy_enable_offload(dpif_offload, &port->pm_port);
> }
> return 0;
> }
>
> - free(port);
> + dpif_offload_dummy_free_port_(offload_dummy, port);
> return EEXIST;
> }
>
> @@ -88,11 +370,15 @@ dpif_offload_dummy_port_del(struct dpif_offload
> *dpif_offload,
> port = dpif_offload_port_mgr_remove(offload_dummy->port_mgr, port_no,
> true);
> if (port) {
> + struct dpif_offload_dummy_port *dummy_port;
> +
> + dummy_port = dpif_offload_dummy_cast_port(port);
> if (dpif_offload_is_offload_enabled()) {
> dpif_offload_dummy_cleanup_offload(dpif_offload, port);
> }
> netdev_close(port->netdev);
> - ovsrcu_postpone(free, port);
> +
> + dpif_offload_dummy_free_port(offload_dummy, dummy_port);
> }
> return 0;
> }
> @@ -156,6 +442,8 @@ dpif_offload_dummy_open(const struct dpif_offload_class
> *offload_class,
> offload_dummy->port_mgr = dpif_offload_port_mgr_init();
> offload_dummy->once_enable = (struct ovsthread_once)
> OVSTHREAD_ONCE_INITIALIZER;
> + offload_dummy->flow_mark_pool = NULL;
> + offload_dummy->unreference_cb = NULL;
>
> *dpif_offload = &offload_dummy->offload;
> return 0;
> @@ -185,6 +473,9 @@ dpif_offload_dummy_close(struct dpif_offload
> *dpif_offload)
> dpif_offload);
>
> dpif_offload_port_mgr_uninit(offload_dummy->port_mgr);
> + if (offload_dummy->flow_mark_pool) {
> + id_fpool_destroy(offload_dummy->flow_mark_pool);
> + }
> free(offload_dummy);
> }
>
> @@ -297,26 +588,295 @@ dpif_offload_dummy_can_offload(struct dpif_offload
> *dpif_offload OVS_UNUSED,
> return is_dummy_netdev_class(netdev->netdev_class) ? true : false;
> }
>
> -#define DEFINE_DPIF_DUMMY_CLASS(NAME, TYPE_STR) \
> - struct dpif_offload_class NAME = { \
> - .type = TYPE_STR, \
> - .impl_type = DPIF_OFFLOAD_IMPL_HW_ONLY, \
> - .supported_dpif_types = (const char *const[]) { \
> - "dummy", \
> - NULL}, \
> - .open = dpif_offload_dummy_open, \
> - .close = dpif_offload_dummy_close, \
> - .set_config = dpif_offload_dummy_set_config, \
> - .get_debug = dpif_offload_dummy_get_debug, \
> - .get_global_stats = dpif_offload_dummy_get_global_stats, \
> - .can_offload = dpif_offload_dummy_can_offload, \
> - .port_add = dpif_offload_dummy_port_add, \
> - .port_del = dpif_offload_dummy_port_del, \
> - .port_dump_start = dpif_offload_dummy_port_dump_start, \
> - .port_dump_next = dpif_offload_dummy_port_dump_next, \
> - .port_dump_done = dpif_offload_dummy_port_dump_done, \
> - .get_netdev = dpif_offload_dummy_get_netdev, \
> +static void
> +dpif_offload_dummy_log_operation(const char *op, int error,
> + const ovs_u128 *ufid)
> +{
> + VLOG_DBG("%s to %s netdev flow "UUID_FMT,
> + error == 0 ? "succeed" : "failed", op,
> + UUID_ARGS((struct uuid *) ufid));
> +}
> +
> +static struct dpif_offload_dummy_port *
> +dpif_offload_dummy_get_port_by_netdev(const struct dpif_offload *offload_,
> + struct netdev *netdev)
> +{
> + struct dpif_offload_dummy *offload = dpif_offload_dummy_cast(offload_);
> + struct dpif_offload_port_mgr_port *port;
> +
> + port = dpif_offload_port_mgr_find_by_netdev(offload->port_mgr, netdev);
> + if (!port) {
> + return NULL;
> + }
> + return dpif_offload_dummy_cast_port(port);
> +}
> +
> +static int
> +dpif_offload_dummy_netdev_flow_put(const struct dpif_offload *offload_,
> + struct netdev *netdev,
> + struct dpif_offload_flow_put *put,
> + void **previous_flow_reference)
> +{
> + struct dpif_offload_dummy *offload = dpif_offload_dummy_cast(offload_);
> + struct dummy_offloaded_flow *off_flow;
> + struct dpif_offload_dummy_port *port;
> + bool modify = true;
> + int error = 0;
> +
> + port = dpif_offload_dummy_get_port_by_netdev(offload_, netdev);
> + if (!port) {
> + error = ENODEV;
> + goto exit;
> + }
> +
> + ovs_mutex_lock(&port->port_mutex);
> +
> + off_flow = dpif_offload_dummy_find_offloaded_flow_and_update(
> + port, put->ufid, put->pmd_id, put->flow_reference,
> + previous_flow_reference);
> +
> + if (!off_flow) {
> + /* Create new offloaded flow. */
> + uint32_t mark = dpif_offload_dummy_allocate_flow_mark(offload);
> +
> + if (mark == INVALID_FLOW_MARK) {
> + error = ENOSPC;
> + goto exit_unlock;
> + }
> +
> + off_flow = dpif_offload_dummy_add_flow(port, put->ufid, put->pmd_id,
> + put->flow_reference, mark);
> + modify = false;
> + *previous_flow_reference = NULL;
> + }
> + memcpy(&off_flow->match, put->match, sizeof *put->match);
> +
> + /* As we have per-netdev 'offloaded_flows', we don't need to match
> + * the 'in_port' for received packets. This will also allow offloading
> + * for packets passed to 'receive' command without specifying the
> + * 'in_port'. */
> + off_flow->match.wc.masks.in_port.odp_port = 0;
> +
> + if (VLOG_IS_DBG_ENABLED()) {
> + struct ds ds = DS_EMPTY_INITIALIZER;
> +
> + ds_put_format(&ds, "%s: flow put[%s]: ", netdev_get_name(netdev),
> + modify ? "modify" : "create");
> + odp_format_ufid(put->ufid, &ds);
> + ds_put_cstr(&ds, " flow match: ");
> + match_format(put->match, NULL, &ds, OFP_DEFAULT_PRIORITY);
> + ds_put_format(&ds, ", mark: %"PRIu32, off_flow->mark);
> +
> + VLOG_DBG("%s", ds_cstr(&ds));
> + ds_destroy(&ds);
> + }
> +
> +exit_unlock:
> + ovs_mutex_unlock(&port->port_mutex);
> +
> +exit:
> + if (put->stats) {
> + memset(put->stats, 0, sizeof *put->stats);
> + }
> +
> + dpif_offload_dummy_log_operation(modify ? "modify" : "add", error,
> + put->ufid);
> + return error;
> +}
> +
> +static int
> +dpif_offload_dummy_netdev_flow_del(const struct dpif_offload *offload_,
> + struct netdev *netdev,
> + struct dpif_offload_flow_del *del)
> +{
> + struct dpif_offload_dummy *offload = dpif_offload_dummy_cast(offload_);
> + struct dummy_offloaded_flow *off_flow;
> + struct dpif_offload_dummy_port *port;
> + uint32_t mark = INVALID_FLOW_MARK;
> + const char *error = NULL;
> +
> + port = dpif_offload_dummy_get_port_by_netdev(offload_, netdev);
> + if (!port) {
> + error = "No such (net)device.";
> + goto exit;
> + }
> +
> + ovs_mutex_lock(&port->port_mutex);
> +
> + off_flow = dpif_offload_dummy_find_offloaded_flow(port, del->ufid);
> + if (!off_flow) {
> + error = "No such flow.";
> + goto exit_unlock;
> + }
> +
> + if (!dpif_offload_dummy_del_flow_pmd_data(port, off_flow, del->pmd_id,
> + del->flow_reference)) {
> + error = "No such flow with pmd_id and reference.";
> + goto exit_unlock;
> + }
> +
> + mark = off_flow->mark;
> + if (!hmap_count(&off_flow->pmd_id_map)) {
> + dpif_offload_dummy_free_flow_mark(offload, mark);
> + dpif_offload_dummy_free_flow(port, off_flow, true);
> + }
> +
> +exit_unlock:
> + ovs_mutex_unlock(&port->port_mutex);
> +
> +exit:
> + if (error || VLOG_IS_DBG_ENABLED()) {
> + struct ds ds = DS_EMPTY_INITIALIZER;
> +
> + ds_put_format(&ds, "%s: ", netdev_get_name(netdev));
> + if (error) {
> + ds_put_cstr(&ds, "failed to ");
> + }
> + ds_put_cstr(&ds, "flow del: ");
> + odp_format_ufid(del->ufid, &ds);
> + if (error) {
> + ds_put_format(&ds, " error: %s", error);
> + } else {
> + ds_put_format(&ds, " mark: %"PRIu32, mark);
> + }
> + VLOG(error ? VLL_WARN : VLL_DBG, "%s", ds_cstr(&ds));
> + ds_destroy(&ds);
> + }
> +
> + if (del->stats) {
> + memset(del->stats, 0, sizeof *del->stats);
> + }
> +
> + dpif_offload_dummy_log_operation("delete", error ? -1 : 0, del->ufid);
> + return error ? ENOENT : 0;
> +}
> +
> +static bool
> +dpif_offload_dummy_netdev_flow_stats(const struct dpif_offload *offload_,
> + struct netdev *netdev,
> + const ovs_u128 *ufid,
> + struct dpif_flow_stats *stats,
> + struct dpif_flow_attrs *attrs)
> +{
> + struct dummy_offloaded_flow *off_flow = NULL;
> + struct dpif_offload_dummy_port *port;
> +
> + port = dpif_offload_dummy_get_port_by_netdev(offload_, netdev);
> + if (!port) {
> + return false;
> }
>
> + ovs_mutex_lock(&port->port_mutex);
> + off_flow = dpif_offload_dummy_find_offloaded_flow(port, ufid);
> + ovs_mutex_unlock(&port->port_mutex);
> +
> + memset(stats, 0, sizeof *stats);
> + attrs->offloaded = off_flow ? true : false;
> + attrs->dp_layer = "tc";
> + attrs->dp_extra_info = NULL;
> +
> + return off_flow ? true : false;
> +}
> +
> +static void
> +dpif_offload_dummy_register_flow_unreference_cb(
> + const struct dpif_offload *offload_, dpif_offload_flow_unreference_cb
> *cb)
> +{
> + struct dpif_offload_dummy *offload = dpif_offload_dummy_cast(offload_);
> +
> + offload->unreference_cb = cb;
> +}
> +
> +static void
> +dpif_offload_dummy_flow_unreference(struct dpif_offload_dummy *offload,
> + unsigned pmd_id, void *flow_reference)
> +{
> + if (offload->unreference_cb) {
> + offload->unreference_cb(pmd_id, flow_reference);
> + }
> +}
> +
> +void
> +dpif_offload_dummy_netdev_simulate_offload(struct netdev *netdev,
> + struct dp_packet *packet,
> + struct flow *flow)
> +{
> + const struct dpif_offload *offload_ = ovsrcu_get(
> + const struct dpif_offload *, &netdev->dpif_offload);
> + struct dpif_offload_dummy_port *port;
> + struct dummy_offloaded_flow *data;
> + struct flow packet_flow;
> +
> + if (!offload_ || strcmp(dpif_offload_class_type(offload_), "dummy")) {
> + return;
> + }
> +
> + port = dpif_offload_dummy_get_port_by_netdev(offload_, netdev);
> + if (!port) {
> + return;
> + }
> +
> + if (!flow) {
> + flow = &packet_flow;
> + flow_extract(packet, flow);
> + }
> +
> + ovs_mutex_lock(&port->port_mutex);
> + HMAP_FOR_EACH (data, node, &port->offloaded_flows) {
> + if (flow_equal_except(flow, &data->match.flow, &data->match.wc)) {
> +
> + dp_packet_set_flow_mark(packet, data->mark);
> +
> + if (VLOG_IS_DBG_ENABLED()) {
> + struct ds ds = DS_EMPTY_INITIALIZER;
> +
> + ds_put_format(&ds, "%s: packet: ",
> + netdev_get_name(netdev));
> + /* 'flow' does not contain proper port number here.
> + * Let's just clear it as it's wildcarded anyway. */
> + flow->in_port.ofp_port = 0;
> + flow_format(&ds, flow, NULL);
> +
> + ds_put_cstr(&ds, " matches with flow: ");
> + odp_format_ufid(&data->ufid, &ds);
> + ds_put_cstr(&ds, " ");
> + match_format(&data->match, NULL, &ds, OFP_DEFAULT_PRIORITY);
> + ds_put_format(&ds, " with mark: %"PRIu32, data->mark);
> +
> + VLOG_DBG("%s", ds_cstr(&ds));
> + ds_destroy(&ds);
> + }
> + break;
> + }
> + }
> + ovs_mutex_unlock(&port->port_mutex);
> +}
> +
> +#define DEFINE_DPIF_DUMMY_CLASS(NAME, TYPE_STR) \
> + struct dpif_offload_class NAME = { \
> + .type = TYPE_STR, \
> + .impl_type = DPIF_OFFLOAD_IMPL_HW_ONLY, \
> + .supported_dpif_types = (const char *const[]){ \
> + "dummy", \
> + NULL}, \
> + .open = dpif_offload_dummy_open, \
> + .close = dpif_offload_dummy_close, \
> + .set_config = dpif_offload_dummy_set_config, \
> + .get_debug = dpif_offload_dummy_get_debug, \
> + .get_global_stats = dpif_offload_dummy_get_global_stats, \
> + .can_offload = dpif_offload_dummy_can_offload, \
> + .port_add = dpif_offload_dummy_port_add, \
> + .port_del = dpif_offload_dummy_port_del, \
> + .port_dump_start = dpif_offload_dummy_port_dump_start, \
> + .port_dump_next = dpif_offload_dummy_port_dump_next, \
> + .port_dump_done = dpif_offload_dummy_port_dump_done, \
> + .get_netdev = dpif_offload_dummy_get_netdev, \
> + .netdev_flow_put = dpif_offload_dummy_netdev_flow_put, \
> + .netdev_flow_del = dpif_offload_dummy_netdev_flow_del, \
> + .netdev_flow_stats = dpif_offload_dummy_netdev_flow_stats, \
> + .register_flow_unreference_cb = \
> + dpif_offload_dummy_register_flow_unreference_cb, \
> +}
> +
> DEFINE_DPIF_DUMMY_CLASS(dpif_offload_dummy_class, "dummy");
> DEFINE_DPIF_DUMMY_CLASS(dpif_offload_dummy_x_class, "dummy_x");
> diff --git a/lib/dummy.h b/lib/dummy.h
> index f0eb30ee2..266c200c3 100644
> --- a/lib/dummy.h
> +++ b/lib/dummy.h
> @@ -20,6 +20,9 @@
> #include <stdbool.h>
>
> struct netdev_class;
> +struct dp_packet;
> +struct netdev;
> +struct flow;
>
> /* Degree of dummy support.
> *
> @@ -41,5 +44,8 @@ void netdev_dummy_register(enum dummy_level);
> void timeval_dummy_register(void);
> void ofpact_dummy_enable(void);
> bool is_dummy_netdev_class(const struct netdev_class *);
> +void dpif_offload_dummy_netdev_simulate_offload(struct netdev *,
> + struct dp_packet *,
> + struct flow *);
>
> #endif /* dummy.h */
> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
> index 6a99f4ced..b0eb27383 100644
> --- a/lib/netdev-dummy.c
> +++ b/lib/netdev-dummy.c
> @@ -1808,7 +1808,7 @@ netdev_dummy_queue_packet__(struct netdev_rxq_dummy
> *rx, struct dp_packet *packe
>
> static void
> netdev_dummy_queue_packet(struct netdev_dummy *dummy, struct dp_packet
> *packet,
> - struct flow *flow OVS_UNUSED, int queue_id)
> + struct flow *flow, int queue_id)
> OVS_REQUIRES(dummy->mutex)
> {
> struct netdev_rxq_dummy *rx, *prev;
> @@ -1817,6 +1817,8 @@ netdev_dummy_queue_packet(struct netdev_dummy *dummy,
> struct dp_packet *packet,
> ovs_pcap_write(dummy->rxq_pcap, packet);
> }
>
> + dpif_offload_dummy_netdev_simulate_offload(&dummy->up, packet, flow);
> +
> prev = NULL;
> LIST_FOR_EACH (rx, node, &dummy->rxes) {
> if (rx->up.queue_id == queue_id &&
> diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
> index 839648d96..e17a16de2 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -37,15 +37,17 @@ filter_flow_install () {
> }
>
> filter_hw_flow_install () {
> - grep 'netdev_dummy.*flow put\[create\]' | sed 's/.*|DBG|//' | sort | uniq
> + grep 'dpif_offload_dummy.*flow put\[create\]' | sed 's/.*|DBG|//' | sort
> \
> + | uniq
> }
>
> filter_hw_flow_del () {
> - grep 'netdev_dummy.*flow del' | sed 's/.*|DBG|//' | sort | uniq
> + grep 'dpif_offload_dummy.*flow del' | sed 's/.*|DBG|//' | sort | uniq
> }
>
> filter_hw_packet_netdev_dummy () {
> - grep 'netdev_dummy.*: packet:.*with mark' | sed 's/.*|DBG|//' | sort |
> uniq
> + grep 'dpif_offload_dummy.*: packet:.*with mark' | sed 's/.*|DBG|//' \
> + | sort | uniq
> }
>
> filter_flow_dump () {
> @@ -411,7 +413,7 @@ m4_define([DPIF_NETDEV_FLOW_HW_OFFLOAD],
> 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"], [])])
> - AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg
> netdev_dummy:file:dbg])
> + AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg
> dpif_offload_dummy:file:dbg])
>
> AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true])
> OVS_WAIT_UNTIL([grep "hw-offload API Enabled" ovs-vswitchd.log])
> @@ -462,11 +464,8 @@ p1: flow del: mark: 1
> OVS_VSWITCHD_STOP
> AT_CLEANUP])
>
> -# XXX: Remove these tests for now as we do not have a dummy implementation
> -# to add flows. See netdev_offload_dummy. We will fix and re-add later.
> -# DPIF_NETDEV_FLOW_HW_OFFLOAD([dummy])
> -# DPIF_NETDEV_FLOW_HW_OFFLOAD([dummy-pmd])
> -
> +DPIF_NETDEV_FLOW_HW_OFFLOAD([dummy])
> +DPIF_NETDEV_FLOW_HW_OFFLOAD([dummy-pmd])
>
> m4_define([DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS],
> [AT_SETUP([dpif-netdev - partial hw offload with packet modifications -
> $1])
> @@ -476,7 +475,7 @@ m4_define([DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS],
> 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"], [])])
> - AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg
> netdev_dummy:file:dbg])
> + AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg
> dpif_offload_dummy:file:dbg])
>
> AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true])
> OVS_WAIT_UNTIL([grep "hw-offload API Enabled" ovs-vswitchd.log])
> @@ -542,10 +541,8 @@
> udp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:
> OVS_VSWITCHD_STOP
> AT_CLEANUP])
>
> -# XXX: Remove these tests for now as we do not have a dummy implementation
> -# to add flows. See netdev_offload_dummy. We will fix and re-add later.
> -# DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS([dummy])
> -# DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS([dummy-pmd])
> +DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS([dummy])
> +DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS([dummy-pmd])
>
> m4_define([DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS_VID_ARP],
> [AT_SETUP([dpif-netdev - partial hw offload with arp vlan id packet
> modifications - $1])
> @@ -555,7 +552,7 @@ m4_define([DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS_VID_ARP],
> 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"], [])])
> - AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg
> netdev_dummy:file:dbg])
> + AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg
> dpif_offload_dummy:file:dbg])
>
> AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true])
> OVS_WAIT_UNTIL([grep "hw-offload API Enabled" ovs-vswitchd.log])
> @@ -621,10 +618,8 @@
> arp,in_port=ANY,dl_vlan=11,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:
> OVS_VSWITCHD_STOP
> AT_CLEANUP])
>
> -# XXX: Remove these tests for now as we do not have a dummy implementation
> -# to add flows. See netdev_offload_dummy. We will fix and re-add later.
> -# DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS_VID_ARP([dummy])
> -# DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS_VID_ARP([dummy-pmd])
> +DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS_VID_ARP([dummy])
> +DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS_VID_ARP([dummy-pmd])
>
> AT_SETUP([dpif-netdev - check dpctl/add-flow in_port exact match])
> OVS_VSWITCHD_START(
> --
> 2.50.1
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev