summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Chaudron <echaudro@redhat.com>2023-02-27 16:29:26 +0100
committerIlya Maximets <i.maximets@ovn.org>2023-03-03 23:33:59 +0100
commit4a3f8845e9421d09148e4ae822ea0365904b70a4 (patch)
treef46c1f8a6199a3f8e10d0fd28901692005886990
parent132fa24b656e1bc45b6ce8ee9ab0206fa6930f65 (diff)
downloadopenvswitch-4a3f8845e9421d09148e4ae822ea0365904b70a4.tar.gz
ofproto-dpif-upcall: Reset ukey's last stats value if the datapath changed.
When the ukey's action set changes, it could cause the flow to use a different datapath, for example, when it moves from tc to kernel. This will cause the the cached previous datapath statistics to be used. This change will reset the cached statistics when a change in datapath is discovered. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Eelco Chaudron <echaudro@redhat.com> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
-rw-r--r--lib/dpif-netdev.c1
-rw-r--r--lib/dpif-netlink.c1
-rw-r--r--lib/dpif-provider.h8
-rw-r--r--lib/dpif.c6
-rw-r--r--lib/dpif.h1
-rw-r--r--ofproto/ofproto-dpif-upcall.c41
-rw-r--r--tests/system-offloads-traffic.at66
7 files changed, 121 insertions, 3 deletions
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index b9cafc273..0b543cf22 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -9527,6 +9527,7 @@ dpif_netdev_bond_stats_get(struct dpif *dpif, uint32_t bond_id,
const struct dpif_class dpif_netdev_class = {
"netdev",
true, /* cleanup_required */
+ true, /* synced_dp_layers */
dpif_netdev_init,
dpif_netdev_enumerate,
dpif_netdev_port_open_type,
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 484545cfb..6977888bb 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -4495,6 +4495,7 @@ dpif_netlink_cache_set_size(struct dpif *dpif_, uint32_t level, uint32_t size)
const struct dpif_class dpif_netlink_class = {
"system",
false, /* cleanup_required */
+ false, /* synced_dp_layers */
NULL, /* init */
dpif_netlink_enumerate,
NULL,
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index 12477a24f..b8ead8a02 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -127,6 +127,14 @@ struct dpif_class {
* datapaths that can not exist without it (e.g. netdev datapath). */
bool cleanup_required;
+ /* If 'true' the specific dpif implementation synchronizes the various
+ * datapath implementation layers, i.e., the dpif's layer in combination
+ * with the underlying netdev offload layers. For example, dpif-netlink
+ * does not sync its kernel flows with the tc ones, i.e., only one gets
+ * installed. On the other hand, dpif-netdev installs both flows,
+ * internally keeps track of both, and represents them as one. */
+ bool synced_dp_layers;
+
/* Called when the dpif provider is registered, typically at program
* startup. Returning an error from this function will prevent any
* datapath with this class from being created.
diff --git a/lib/dpif.c b/lib/dpif.c
index fe4db83fb..3305401fe 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -2109,3 +2109,9 @@ dpif_cache_set_size(struct dpif *dpif, uint32_t level, uint32_t size)
? dpif->dpif_class->cache_set_size(dpif, level, size)
: EOPNOTSUPP;
}
+
+bool
+dpif_synced_dp_layers(struct dpif *dpif)
+{
+ return dpif->dpif_class->synced_dp_layers;
+}
diff --git a/lib/dpif.h b/lib/dpif.h
index 6cb4dae6d..129cbf6a1 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -939,6 +939,7 @@ int dpif_get_pmds_for_port(const struct dpif * dpif, odp_port_t port_no,
char *dpif_get_dp_version(const struct dpif *);
bool dpif_supports_tnl_push_pop(const struct dpif *);
bool dpif_supports_explicit_drop_action(const struct dpif *);
+bool dpif_synced_dp_layers(struct dpif *);
/* Log functions. */
struct vlog_module;
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index 59126b901..9c8ab17f3 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -47,17 +47,20 @@
#define UPCALL_MAX_BATCH 64
#define REVALIDATE_MAX_BATCH 50
+#define UINT64_THREE_QUARTERS (UINT64_MAX / 4 * 3)
VLOG_DEFINE_THIS_MODULE(ofproto_dpif_upcall);
COVERAGE_DEFINE(dumped_duplicate_flow);
COVERAGE_DEFINE(dumped_new_flow);
COVERAGE_DEFINE(handler_duplicate_upcall);
-COVERAGE_DEFINE(upcall_ukey_contention);
-COVERAGE_DEFINE(upcall_ukey_replace);
COVERAGE_DEFINE(revalidate_missed_dp_flow);
+COVERAGE_DEFINE(ukey_dp_change);
+COVERAGE_DEFINE(ukey_invalid_stat_reset);
COVERAGE_DEFINE(upcall_flow_limit_hit);
COVERAGE_DEFINE(upcall_flow_limit_kill);
+COVERAGE_DEFINE(upcall_ukey_contention);
+COVERAGE_DEFINE(upcall_ukey_replace);
/* A thread that reads upcalls from dpif, forwards each upcall's packet,
* and possibly sets up a kernel flow as a cache. */
@@ -287,6 +290,7 @@ struct udpif_key {
struct ovs_mutex mutex; /* Guards the following. */
struct dpif_flow_stats stats OVS_GUARDED; /* Last known stats.*/
+ const char *dp_layer OVS_GUARDED; /* Last known dp_layer. */
long long int created OVS_GUARDED; /* Estimate of creation time. */
uint64_t dump_seq OVS_GUARDED; /* Tracks udpif->dump_seq. */
uint64_t reval_seq OVS_GUARDED; /* Tracks udpif->reval_seq. */
@@ -1754,6 +1758,7 @@ ukey_create__(const struct nlattr *key, size_t key_len,
ukey->created = ukey->flow_time = time_msec();
memset(&ukey->stats, 0, sizeof ukey->stats);
ukey->stats.used = used;
+ ukey->dp_layer = NULL;
ukey->xcache = NULL;
ukey->offloaded = false;
@@ -2330,6 +2335,13 @@ revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey,
? stats->n_bytes - ukey->stats.n_bytes
: 0);
+ if (stats->n_packets < ukey->stats.n_packets &&
+ ukey->stats.n_packets < UINT64_THREE_QUARTERS) {
+ /* Report cases where the packet counter is lower than the previous
+ * instance, but exclude the potential wrapping of an uint64_t. */
+ COVERAGE_INC(ukey_invalid_stat_reset);
+ }
+
if (need_revalidate) {
if (should_revalidate(udpif, push.n_packets, ukey->stats.used)) {
if (!ukey->xcache) {
@@ -2443,6 +2455,15 @@ push_dp_ops(struct udpif *udpif, struct ukey_op *ops, size_t n_ops)
push->tcp_flags = stats->tcp_flags | op->ukey->stats.tcp_flags;
push->n_packets = stats->n_packets - op->ukey->stats.n_packets;
push->n_bytes = stats->n_bytes - op->ukey->stats.n_bytes;
+
+ if (stats->n_packets < op->ukey->stats.n_packets &&
+ op->ukey->stats.n_packets < UINT64_THREE_QUARTERS) {
+ /* Report cases where the packet counter is lower than the
+ * previous instance, but exclude the potential wrapping of an
+ * uint64_t. */
+ COVERAGE_INC(ukey_invalid_stat_reset);
+ }
+
ovs_mutex_unlock(&op->ukey->mutex);
} else {
push = stats;
@@ -2747,6 +2768,22 @@ revalidate(struct revalidator *revalidator)
continue;
}
+ ukey->offloaded = f->attrs.offloaded;
+ if (!ukey->dp_layer
+ || (!dpif_synced_dp_layers(udpif->dpif)
+ && strcmp(ukey->dp_layer, f->attrs.dp_layer))) {
+
+ if (ukey->dp_layer) {
+ /* The dp_layer has changed this is probably due to an
+ * earlier revalidate cycle moving it to/from hw offload.
+ * In this case we should reset the ukey stored statistics,
+ * as they are from the deleted DP flow. */
+ COVERAGE_INC(ukey_dp_change);
+ memset(&ukey->stats, 0, sizeof ukey->stats);
+ }
+ ukey->dp_layer = f->attrs.dp_layer;
+ }
+
already_dumped = ukey->dump_seq == dump_seq;
if (already_dumped) {
/* The flow has already been handled during this flow dump
diff --git a/tests/system-offloads-traffic.at b/tests/system-offloads-traffic.at
index 9f50f3b01..84bab88be 100644
--- a/tests/system-offloads-traffic.at
+++ b/tests/system-offloads-traffic.at
@@ -171,7 +171,9 @@ AT_CLEANUP
AT_SETUP([offloads - simulated flow action update])
-OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true])
+OVS_TRAFFIC_VSWITCHD_START()
+
+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true])
ADD_NAMESPACES(at_ns0, at_ns1)
@@ -232,3 +234,65 @@ recirc_id(<recirc>),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:29, bytes
OVS_TRAFFIC_VSWITCHD_STOP
AT_CLEANUP
+
+
+AT_SETUP([offloads - offload flow to none-offload])
+OVS_TRAFFIC_VSWITCHD_START()
+
+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true])
+
+ADD_NAMESPACES(at_ns0, at_ns1)
+
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
+ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
+
+AT_DATA([flows.txt], [dnl
+add in_port=ovs-p0,actions=ovs-p1
+add in_port=ovs-p1,actions=ovs-p0
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
+10 packets transmitted, 10 received, 0% packet loss, time 0ms
+])
+
+AT_CHECK([ovs-appctl dpctl/dump-flows type=tc | grep "eth_type(0x0800)" | sort | strip_recirc | strip_used], [0], [dnl
+recirc_id(<recirc>),in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.0s, actions:3
+recirc_id(<recirc>),in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:9, bytes:756, used:0.0s, actions:2
+])
+
+dnl Here we use an output action with truncate, which will force a kernel flow.
+AT_DATA([flows2.txt], [dnl
+modify in_port=ovs-p0,actions=output(port=ovs-p1, max_len=128)
+modify in_port=ovs-p1,actions=output(port=ovs-p0, max_len=128)
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows2.txt])
+AT_CHECK([ovs-appctl revalidator/wait], [0])
+
+NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
+10 packets transmitted, 10 received, 0% packet loss, time 0ms
+])
+
+AT_CHECK([ovs-appctl dpctl/dump-flows type=ovs | grep "eth_type(0x0800)" | sort | strip_recirc | strip_used], [0], [dnl
+recirc_id(<recirc>),in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:980, used:0.0s, actions:trunc(128),3
+recirc_id(<recirc>),in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:980, used:0.0s, actions:trunc(128),2
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl revalidator/wait], [0])
+
+NS_CHECK_EXEC([at_ns0], [ping -q -c 10 -i 0.1 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
+10 packets transmitted, 10 received, 0% packet loss, time 0ms
+])
+
+AT_CHECK([ovs-appctl dpctl/dump-flows type=tc | grep "eth_type(0x0800)" | sort | strip_recirc | strip_used], [0], [dnl
+recirc_id(<recirc>),in_port(2),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:840, used:0.0s, actions:3
+recirc_id(<recirc>),in_port(3),eth(),eth_type(0x0800),ipv4(frag=no), packets:10, bytes:840, used:0.0s, actions:2
+])
+
+AT_CHECK([ovs-appctl coverage/read-counter ukey_invalid_stat_reset], [0], [dnl
+0
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP