Skip to content

Commit

Permalink
ofproto-dpif-upcall: Fix push_dp_ops to handle all errors.
Browse files Browse the repository at this point in the history
push_dp_ops only handles delete ops errors but ignores the modify
ops results. It's better to handle all the dp operation errors in
a consistent way.

This patch prevents the inconsistency by considering modify failure
in revalidators.

To note, we cannot perform two state transitions and change ukey_state
into UKEY_EVICTED directly here, because, if we do so, the
sweep will remove the ukey alone and leave dp flow alive. Later, the
dump will retrieve the dp flow and might even recover it. This will
contribute the stats of this dp flow twice.

Signed-off-by: Peng He <[email protected]>
Signed-off-by: Eelco Chaudron <[email protected]>
  • Loading branch information
Peng He authored and chaudron committed Aug 15, 2023
1 parent eac54ee commit cf11766
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 14 deletions.
50 changes: 36 additions & 14 deletions ofproto/ofproto-dpif-upcall.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
VLOG_DEFINE_THIS_MODULE(ofproto_dpif_upcall);

COVERAGE_DEFINE(dumped_duplicate_flow);
COVERAGE_DEFINE(dumped_inconsistent_flow);
COVERAGE_DEFINE(dumped_new_flow);
COVERAGE_DEFINE(handler_duplicate_upcall);
COVERAGE_DEFINE(revalidate_missed_dp_flow);
Expand Down Expand Up @@ -258,6 +259,7 @@ enum ukey_state {
UKEY_CREATED = 0,
UKEY_VISIBLE, /* Ukey is in umap, datapath flow install is queued. */
UKEY_OPERATIONAL, /* Ukey is in umap, datapath flow is installed. */
UKEY_INCONSISTENT, /* Ukey is in umap, datapath flow is inconsistent. */
UKEY_EVICTING, /* Ukey is in umap, datapath flow delete is queued. */
UKEY_EVICTED, /* Ukey is in umap, datapath flow is deleted. */
UKEY_DELETED, /* Ukey removed from umap, ukey free is deferred. */
Expand Down Expand Up @@ -1999,15 +2001,20 @@ transition_ukey_at(struct udpif_key *ukey, enum ukey_state dst,
* UKEY_VISIBLE -> UKEY_EVICTED
* A handler attempts to install the flow, but the datapath rejects it.
* Consider that the datapath has already destroyed it.
* UKEY_OPERATIONAL -> UKEY_INCONSISTENT
* A revalidator modifies the flow with error returns.
* UKEY_INCONSISTENT -> UKEY_EVICTING
* A revalidator decides to evict the datapath flow.
* UKEY_OPERATIONAL -> UKEY_EVICTING
* A revalidator decides to evict the datapath flow.
* UKEY_EVICTING -> UKEY_EVICTED
* A revalidator has evicted the datapath flow.
* UKEY_EVICTED -> UKEY_DELETED
* A revalidator has removed the ukey from the umap and is deleting it.
*/
if (ukey->state == dst - 1 || (ukey->state == UKEY_VISIBLE &&
dst < UKEY_DELETED)) {
if (ukey->state == dst - 1 ||
(ukey->state == UKEY_VISIBLE && dst < UKEY_DELETED) ||
(ukey->state == UKEY_OPERATIONAL && dst == UKEY_EVICTING)) {
ukey->state = dst;
} else {
struct ds ds = DS_EMPTY_INITIALIZER;
Expand Down Expand Up @@ -2490,26 +2497,31 @@ push_dp_ops(struct udpif *udpif, struct ukey_op *ops, size_t n_ops)

for (i = 0; i < n_ops; i++) {
struct ukey_op *op = &ops[i];
struct dpif_flow_stats *push, *stats, push_buf;

stats = op->dop.flow_del.stats;
push = &push_buf;

if (op->dop.type != DPIF_OP_FLOW_DEL) {
/* Only deleted flows need their stats pushed. */
continue;
}

if (op->dop.error) {
/* flow_del error, 'stats' is unusable. */
if (op->ukey) {
ovs_mutex_lock(&op->ukey->mutex);
transition_ukey(op->ukey, UKEY_EVICTED);
if (op->dop.type == DPIF_OP_FLOW_DEL) {
transition_ukey(op->ukey, UKEY_EVICTED);
} else {
/* Modification of the flow failed. */
transition_ukey(op->ukey, UKEY_INCONSISTENT);
}
ovs_mutex_unlock(&op->ukey->mutex);
}
continue;
}

if (op->dop.type != DPIF_OP_FLOW_DEL) {
/* Only deleted flows need their stats pushed. */
continue;
}

struct dpif_flow_stats *push, *stats, push_buf;

stats = op->dop.flow_del.stats;
push = &push_buf;

if (op->ukey) {
ovs_mutex_lock(&op->ukey->mutex);
transition_ukey(op->ukey, UKEY_EVICTED);
Expand Down Expand Up @@ -2857,6 +2869,15 @@ revalidate(struct revalidator *revalidator)
continue;
}

if (ukey->state == UKEY_INCONSISTENT) {
ukey->dump_seq = dump_seq;
reval_op_init(&ops[n_ops++], UKEY_DELETE, udpif, ukey,
&recircs, &odp_actions);
ovs_mutex_unlock(&ukey->mutex);
COVERAGE_INC(dumped_inconsistent_flow);
continue;
}

if (ukey->state <= UKEY_OPERATIONAL) {
/* The flow is now confirmed to be in the datapath. */
transition_ukey(ukey, UKEY_OPERATIONAL);
Expand Down Expand Up @@ -2945,13 +2966,14 @@ revalidator_sweep__(struct revalidator *revalidator, bool purge)
}
ukey_state = ukey->state;
if (ukey_state == UKEY_OPERATIONAL
|| (ukey_state == UKEY_INCONSISTENT)
|| (ukey_state == UKEY_VISIBLE && purge)) {
struct recirc_refs recircs = RECIRC_REFS_EMPTY_INITIALIZER;
bool seq_mismatch = (ukey->dump_seq != dump_seq
&& ukey->reval_seq != reval_seq);
enum reval_result result;

if (purge) {
if (purge || ukey_state == UKEY_INCONSISTENT) {
result = UKEY_DELETE;
} else if (!seq_mismatch) {
result = UKEY_KEEP;
Expand Down
43 changes: 43 additions & 0 deletions tests/dpif-netdev.at
Original file line number Diff line number Diff line change
Expand Up @@ -812,3 +812,46 @@ AT_CHECK([tail -n 1 p2.pcap.txt], [0], [dnl
])
OVS_VSWITCHD_STOP
AT_CLEANUP

AT_SETUP([dpif-netdev - revalidators handle dp modification fail correctly])
OVS_VSWITCHD_START(
[add-port br0 p1 \
-- set interface p1 type=dummy \
-- set bridge br0 datapath-type=dummy \
-- add-port br0 p2 \
-- set interface p2 type=dummy --
])

AT_CHECK([ovs-ofctl add-flow br0 'table=0,in_port=p1,actions=p2'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.0.0.2),tcp(src=1,dst=2)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'ipv4(src=10.0.0.1,dst=10.0.0.2),tcp(src=1,dst=2)'])

AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*thread://' | strip_xout_keep_actions ], [0], [
recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:0.0s, actions:2
])

dnl Wait for the dp flow to enter OPERATIONAL state.
AT_CHECK([ovs-appctl revalidator/wait])

AT_CHECK([ovs-appctl revalidator/pause])

dnl Delete all dp flows, so flow modification will fail.
AT_CHECK([ovs-appctl dpctl/del-flows])

AT_CHECK([ovs-appctl revalidator/resume])

dnl Replace OpenFlow rules, trigger revalidation and wait for it to complete.
AT_CHECK([echo 'table=0,in_port=p1,ip actions=ct(commit)' | ovs-ofctl --bundle replace-flows br0 -])
AT_CHECK([ovs-appctl revalidator/wait])

dnl Inconsistent ukey should be deleted.
AT_CHECK([ovs-appctl upcall/show | grep keys | grep -q -v 0], [1])

dnl Check the log for the flow modification error.
AT_CHECK([grep -q -E ".*failed to put.*$" ovs-vswitchd.log])

dnl Remove warning logs to let test suite pass.
OVS_VSWITCHD_STOP(["dnl
/.*failed to put.*$/d
/.*failed to flow_del.*$/d"])
AT_CLEANUP

0 comments on commit cf11766

Please sign in to comment.