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]> --- lib/dpif-offload-dummy.c | 369 ++++++++++++++++++++++++++++++++++++--- lib/dummy.h | 6 + lib/netdev-dummy.c | 4 +- tests/dpif-netdev.at | 34 ++-- 4 files changed, 368 insertions(+), 45 deletions(-) diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c index 32360737f..dbd76e7a5 100644 --- a/lib/dpif-offload-dummy.c +++ b/lib/dpif-offload-dummy.c @@ -22,10 +22,22 @@ #include "dpif-offload.h" #include "dummy.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 dummy_offloaded_flow { + struct hmap_node node; + struct match match; + ovs_u128 ufid; + uint32_t mark; +}; struct dpif_offload_dummy { struct dpif_offload offload; @@ -35,12 +47,47 @@ struct dpif_offload_dummy { 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 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 dummy_offloaded_flow * +dpif_offload_dummy_find_offloaded_flow(const struct hmap *offloaded_flows, + const ovs_u128 *ufid) +{ + uint32_t hash = dpif_offload_dummy_flow_hash(ufid); + struct dummy_offloaded_flow *data; + + HMAP_FOR_EACH_WITH_HASH (data, node, hash, offloaded_flows) { + if (ovs_u128_equals(*ufid, data->ufid)) { + return data; + } + } + + return NULL; +} + static void dpif_offload_dummy_enable_offload(struct dpif_offload *dpif_offload, struct dpif_offload_port_mgr_port *port) @@ -56,24 +103,45 @@ dpif_offload_dummy_cleanup_offload( dpif_offload_set_netdev_offload(port->netdev, NULL); } + +static void +dpif_offload_dummy_free_port(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) { + free(off_flow); + } + hmap_destroy(&port->offloaded_flows); + ovs_mutex_unlock(&port->port_mutex); + ovs_mutex_destroy(&port->port_mutex); + free(port); +} + 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(port); return EEXIST; } @@ -89,11 +157,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); + + ovsrcu_postpone(dpif_offload_dummy_free_port, dummy_port); } return 0; } @@ -298,26 +370,273 @@ 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, + uint32_t *flow_mark) +{ + struct dummy_offloaded_flow *off_flow; + struct dpif_offload_dummy_port *port; + bool modify = true; + int error = 0; + + *flow_mark = INVALID_FLOW_MARK; + + 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(&port->offloaded_flows, + put->ufid); + if (!off_flow) { + /* Create new offloaded flow. */ + uint32_t mark = dpif_offload_allocate_flow_mark(); + + if (mark == INVALID_FLOW_MARK) { + error = ENOSPC; + goto exit_unlock; + } + + off_flow = xzalloc(sizeof *off_flow); + off_flow->mark = mark; + memcpy(&off_flow->ufid, put->ufid, sizeof *put->ufid); + hmap_insert(&port->offloaded_flows, &off_flow->node, + dpif_offload_dummy_flow_hash(put->ufid)); + modify = false; + } + 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); } + *flow_mark = off_flow->mark; + +exit_unlock: + ovs_mutex_unlock(&port->port_mutex); + +exit: + if (put->stats) { + memset(put->stats, 0, sizeof *put->stats); + } + + dpif_offload_dummy_log_operation(put->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, + uint32_t *flow_mark) +{ + 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->offloaded_flows, + del->ufid); + if (!off_flow) { + error = "No such flow."; + goto exit_unlock; + } + + mark = off_flow->mark; + hmap_remove(&port->offloaded_flows, &off_flow->node); + free(off_flow); + +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); + } + + *flow_mark = mark; + 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->offloaded_flows, + 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; +} + +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, \ +} + 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..ee9d7dadd 100644 --- a/tests/dpif-netdev.at +++ b/tests/dpif-netdev.at @@ -37,15 +37,18 @@ 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|//' \ + | sed -E 's/mark: [0-9]+/mark: 1/' | 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|//' \ + | sed -E 's/mark: [0-9]+/mark: 1/' | 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|//' \ + | sed -E 's/mark: [0-9]+/mark: 1/' | sort | uniq } filter_flow_dump () { @@ -411,7 +414,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 +465,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 +476,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 +542,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 +553,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 +619,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
