ovsdb-idl: Optimize out transactions that are complete no-ops.
[openvswitch] / ofproto / ofproto-dpif.c
index f632ad0ecafc03e8f7c42bec116ac7bbc3bf7240..5604d5e47fec9077b8d00f8066436fdad8e26ec8 100644 (file)
@@ -184,9 +184,9 @@ struct action_xlate_ctx {
  * reason to look at them. */
 
     int recurse;                /* Recursion level, via xlate_table_action. */
-    int last_pop_priority;      /* Offset in 'odp_actions' just past most
-                                 * recent ODP_ACTION_ATTR_SET_PRIORITY. */
     uint32_t priority;          /* Current flow priority. 0 if none. */
+    struct flow base_flow;      /* Flow at the last commit. */
+    uint32_t base_priority;     /* Priority at the last commit. */
 };
 
 static void action_xlate_ctx_init(struct action_xlate_ctx *,
@@ -300,6 +300,11 @@ static void port_run(struct ofport_dpif *);
 static void port_wait(struct ofport_dpif *);
 static int set_cfm(struct ofport *, const struct cfm_settings *);
 
+struct dpif_completion {
+    struct list list_node;
+    struct ofoperation *op;
+};
+
 struct ofproto_dpif {
     struct ofproto up;
     struct dpif *dpif;
@@ -323,8 +328,15 @@ struct ofproto_dpif {
     struct hmap facets;
     bool need_revalidate;
     struct tag_set revalidate_set;
+
+    /* Support for debugging async flow mods. */
+    struct list completions;
 };
 
+/* Defer flow mod completion until "ovs-appctl ofproto/unclog"?  (Useful only
+ * for debugging the asynchronous flow_mod implementation.) */
+static bool clogged;
+
 static void ofproto_dpif_unixctl_init(void);
 
 static struct ofproto_dpif *
@@ -446,6 +458,8 @@ construct(struct ofproto *ofproto_)
     ofproto->need_revalidate = false;
     tag_set_init(&ofproto->revalidate_set);
 
+    list_init(&ofproto->completions);
+
     ofproto->up.tables = xmalloc(sizeof *ofproto->up.tables);
     classifier_init(&ofproto->up.tables[0]);
     ofproto->up.n_tables = 1;
@@ -455,12 +469,33 @@ construct(struct ofproto *ofproto_)
     return 0;
 }
 
+static void
+complete_operations(struct ofproto_dpif *ofproto)
+{
+    struct dpif_completion *c, *next;
+
+    LIST_FOR_EACH_SAFE (c, next, list_node, &ofproto->completions) {
+        ofoperation_complete(c->op, 0);
+        list_remove(&c->list_node);
+        free(c);
+    }
+}
+
 static void
 destruct(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+    struct rule_dpif *rule, *next_rule;
+    struct cls_cursor cursor;
     int i;
 
+    complete_operations(ofproto);
+
+    cls_cursor_init(&cursor, &ofproto->up.tables[0], NULL);
+    CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
+        ofproto_rule_destroy(&rule->up);
+    }
+
     for (i = 0; i < MAX_MIRRORS; i++) {
         mirror_destroy(ofproto->mirrors[i]);
     }
@@ -483,6 +518,9 @@ run(struct ofproto *ofproto_)
     struct ofbundle *bundle;
     int i;
 
+    if (!clogged) {
+        complete_operations(ofproto);
+    }
     dpif_run(ofproto->dpif);
 
     for (i = 0; i < 50; i++) {
@@ -549,6 +587,10 @@ wait(struct ofproto *ofproto_)
     struct ofport_dpif *ofport;
     struct ofbundle *bundle;
 
+    if (!clogged && !list_is_empty(&ofproto->completions)) {
+        poll_immediate_wake();
+    }
+
     dpif_wait(ofproto->dpif);
     dpif_recv_wait(ofproto->dpif);
     if (ofproto->sflow) {
@@ -2534,6 +2576,21 @@ rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow)
                                                 flow)));
 }
 
+static void
+complete_operation(struct rule_dpif *rule)
+{
+    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+
+    ofproto->need_revalidate = true;
+    if (clogged) {
+        struct dpif_completion *c = xmalloc(sizeof *c);
+        c->op = rule->up.pending;
+        list_push_back(&ofproto->completions, &c->list_node);
+    } else {
+        ofoperation_complete(rule->up.pending, 0);
+    }
+}
+
 static struct rule *
 rule_alloc(void)
 {
@@ -2553,7 +2610,7 @@ rule_construct(struct rule *rule_)
 {
     struct rule_dpif *rule = rule_dpif_cast(rule_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
-    struct rule_dpif *old_rule;
+    struct rule_dpif *victim;
     int error;
 
     error = validate_actions(rule->up.actions, rule->up.n_actions,
@@ -2562,21 +2619,25 @@ rule_construct(struct rule *rule_)
         return error;
     }
 
-    old_rule = rule_dpif_cast(rule_from_cls_rule(classifier_find_rule_exactly(
-                                                     &ofproto->up.tables[0],
-                                                     &rule->up.cr)));
-    if (old_rule) {
-        ofproto_rule_destroy(&old_rule->up);
-    }
-
     rule->used = rule->up.created;
     rule->packet_count = 0;
     rule->byte_count = 0;
-    list_init(&rule->facets);
-    classifier_insert(&ofproto->up.tables[0], &rule->up.cr);
 
-    ofproto->need_revalidate = true;
+    victim = rule_dpif_cast(ofoperation_get_victim(rule->up.pending));
+    if (victim && !list_is_empty(&victim->facets)) {
+        struct facet *facet;
+
+        rule->facets = victim->facets;
+        list_moved(&rule->facets);
+        LIST_FOR_EACH (facet, list_node, &rule->facets) {
+            facet->rule = rule;
+        }
+    } else {
+        /* Must avoid list_moved() in this case. */
+        list_init(&rule->facets);
+    }
 
+    complete_operation(rule);
     return 0;
 }
 
@@ -2587,11 +2648,11 @@ rule_destruct(struct rule *rule_)
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
     struct facet *facet, *next_facet;
 
-    classifier_remove(&ofproto->up.tables[0], &rule->up.cr);
     LIST_FOR_EACH_SAFE (facet, next_facet, list_node, &rule->facets) {
         facet_revalidate(ofproto, facet);
     }
-    ofproto->need_revalidate = true;
+
+    complete_operation(rule);
 }
 
 static void
@@ -2657,20 +2718,21 @@ rule_execute(struct rule *rule_, struct flow *flow, struct ofpbuf *packet)
     return 0;
 }
 
-static int
-rule_modify_actions(struct rule *rule_,
-                    const union ofp_action *actions, size_t n_actions)
+static void
+rule_modify_actions(struct rule *rule_)
 {
     struct rule_dpif *rule = rule_dpif_cast(rule_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
     int error;
 
-    error = validate_actions(actions, n_actions, &rule->up.cr.flow,
-                             ofproto->max_ports);
-    if (!error) {
-        ofproto->need_revalidate = true;
+    error = validate_actions(rule->up.actions, rule->up.n_actions,
+                             &rule->up.cr.flow, ofproto->max_ports);
+    if (error) {
+        ofoperation_complete(rule->up.pending, error);
+        return;
     }
-    return error;
+
+    complete_operation(rule);
 }
 \f
 /* Sends 'packet' out of port 'odp_port' within 'p'.
@@ -2709,6 +2771,71 @@ static void do_xlate_actions(const union ofp_action *in, size_t n_in,
                              struct action_xlate_ctx *ctx);
 static bool xlate_normal(struct action_xlate_ctx *);
 
+static void
+commit_odp_actions(struct action_xlate_ctx *ctx)
+{
+    const struct flow *flow = &ctx->flow;
+    struct flow *base = &ctx->base_flow;
+    struct ofpbuf *odp_actions = ctx->odp_actions;
+
+    if (base->tun_id != flow->tun_id) {
+        nl_msg_put_be64(odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, flow->tun_id);
+        base->tun_id = flow->tun_id;
+    }
+
+    if (base->nw_src != flow->nw_src) {
+        nl_msg_put_be32(odp_actions, ODP_ACTION_ATTR_SET_NW_SRC, flow->nw_src);
+        base->nw_src = flow->nw_src;
+    }
+
+    if (base->nw_dst != flow->nw_dst) {
+        nl_msg_put_be32(odp_actions, ODP_ACTION_ATTR_SET_NW_DST, flow->nw_dst);
+        base->nw_dst = flow->nw_dst;
+    }
+
+    if (base->vlan_tci != flow->vlan_tci) {
+        if (!(flow->vlan_tci & htons(VLAN_CFI))) {
+            nl_msg_put_flag(odp_actions, ODP_ACTION_ATTR_STRIP_VLAN);
+        } else {
+            nl_msg_put_be16(odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
+                            flow->vlan_tci & ~htons(VLAN_CFI));
+        }
+        base->vlan_tci = flow->vlan_tci;
+    }
+
+    if (base->tp_src != flow->tp_src) {
+        nl_msg_put_be16(odp_actions, ODP_ACTION_ATTR_SET_TP_SRC, flow->tp_src);
+        base->tp_src = flow->tp_src;
+    }
+
+    if (base->tp_dst != flow->tp_dst) {
+        nl_msg_put_be16(odp_actions, ODP_ACTION_ATTR_SET_TP_DST, flow->tp_dst);
+        base->tp_dst = flow->tp_dst;
+    }
+
+    if (!eth_addr_equals(base->dl_src, flow->dl_src)) {
+        nl_msg_put_unspec(odp_actions, ODP_ACTION_ATTR_SET_DL_SRC,
+                          flow->dl_src, ETH_ADDR_LEN);
+        memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
+    }
+
+    if (!eth_addr_equals(base->dl_dst, flow->dl_dst)) {
+        nl_msg_put_unspec(odp_actions, ODP_ACTION_ATTR_SET_DL_DST,
+                          flow->dl_dst, ETH_ADDR_LEN);
+        memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
+    }
+
+    if (ctx->base_priority != ctx->priority) {
+        if (ctx->priority) {
+            nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_SET_PRIORITY,
+                           ctx->priority);
+        } else {
+            nl_msg_put_flag(odp_actions, ODP_ACTION_ATTR_POP_PRIORITY);
+        }
+        ctx->base_priority = ctx->priority;
+    }
+}
+
 static void
 add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port)
 {
@@ -2728,6 +2855,7 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port)
          */
     }
 
+    commit_odp_actions(ctx);
     nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_OUTPUT, odp_port);
     ctx->nf_output_iface = ofp_port;
 }
@@ -2765,20 +2893,20 @@ xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
 }
 
 static void
-flood_packets(struct ofproto_dpif *ofproto,
-              uint16_t ofp_in_port, ovs_be32 mask,
-              uint16_t *nf_output_iface, struct ofpbuf *odp_actions)
+flood_packets(struct action_xlate_ctx *ctx, ovs_be32 mask)
 {
     struct ofport_dpif *ofport;
 
-    HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
+    commit_odp_actions(ctx);
+    HMAP_FOR_EACH (ofport, up.hmap_node, &ctx->ofproto->up.ports) {
         uint16_t ofp_port = ofport->up.ofp_port;
-        if (ofp_port != ofp_in_port && !(ofport->up.opp.config & mask)) {
-            nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT,
+        if (ofp_port != ctx->flow.in_port && !(ofport->up.opp.config & mask)) {
+            nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_OUTPUT,
                            ofport->odp_port);
         }
     }
-    *nf_output_iface = NF_OUT_FLOOD;
+
+    ctx->nf_output_iface = NF_OUT_FLOOD;
 }
 
 static void
@@ -2800,14 +2928,13 @@ xlate_output_action__(struct action_xlate_ctx *ctx,
         xlate_normal(ctx);
         break;
     case OFPP_FLOOD:
-        flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(OFPPC_NO_FLOOD),
-                      &ctx->nf_output_iface, ctx->odp_actions);
+        flood_packets(ctx,  htonl(OFPPC_NO_FLOOD));
         break;
     case OFPP_ALL:
-        flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(0),
-                      &ctx->nf_output_iface, ctx->odp_actions);
+        flood_packets(ctx, htonl(0));
         break;
     case OFPP_CONTROLLER:
+        commit_odp_actions(ctx);
         nl_msg_put_u64(ctx->odp_actions, ODP_ACTION_ATTR_CONTROLLER, max_len);
         break;
     case OFPP_LOCAL:
@@ -2837,34 +2964,12 @@ xlate_output_action(struct action_xlate_ctx *ctx,
     xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len));
 }
 
-/* If the final ODP action in 'ctx' is "pop priority", drop it, as an
- * optimization, because we're going to add another action that sets the
- * priority immediately after, or because there are no actions following the
- * pop.  */
-static void
-remove_pop_action(struct action_xlate_ctx *ctx)
-{
-    if (ctx->odp_actions->size == ctx->last_pop_priority) {
-        ctx->odp_actions->size -= NLA_ALIGN(NLA_HDRLEN);
-        ctx->last_pop_priority = -1;
-    }
-}
-
-static void
-add_pop_action(struct action_xlate_ctx *ctx)
-{
-    if (ctx->odp_actions->size != ctx->last_pop_priority) {
-        nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_POP_PRIORITY);
-        ctx->last_pop_priority = ctx->odp_actions->size;
-    }
-}
-
 static void
 xlate_enqueue_action(struct action_xlate_ctx *ctx,
                      const struct ofp_action_enqueue *oae)
 {
     uint16_t ofp_port, odp_port;
-    uint32_t priority;
+    uint32_t ctx_priority, priority;
     int error;
 
     error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id),
@@ -2883,16 +2988,10 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx,
     odp_port = ofp_port_to_odp_port(ofp_port);
 
     /* Add ODP actions. */
-    remove_pop_action(ctx);
-    nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
+    ctx_priority = ctx->priority;
+    ctx->priority = priority;
     add_output_action(ctx, odp_port);
-
-    if (ctx->priority) {
-        nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY,
-                       ctx->priority);
-    } else {
-        add_pop_action(ctx);
-    }
+    ctx->priority = ctx_priority;
 
     /* Update NetFlow output port. */
     if (ctx->nf_output_iface == NF_OUT_DROP) {
@@ -2918,20 +3017,6 @@ xlate_set_queue_action(struct action_xlate_ctx *ctx,
     }
 
     ctx->priority = priority;
-    remove_pop_action(ctx);
-    nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
-}
-
-static void
-xlate_set_dl_tci(struct action_xlate_ctx *ctx)
-{
-    ovs_be16 tci = ctx->flow.vlan_tci;
-    if (!(tci & htons(VLAN_CFI))) {
-        nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_STRIP_VLAN);
-    } else {
-        nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
-                        tci & ~htons(VLAN_CFI));
-    }
 }
 
 struct xlate_reg_state {
@@ -2939,27 +3024,6 @@ struct xlate_reg_state {
     ovs_be64 tun_id;
 };
 
-static void
-save_reg_state(const struct action_xlate_ctx *ctx,
-               struct xlate_reg_state *state)
-{
-    state->vlan_tci = ctx->flow.vlan_tci;
-    state->tun_id = ctx->flow.tun_id;
-}
-
-static void
-update_reg_state(struct action_xlate_ctx *ctx,
-                 const struct xlate_reg_state *state)
-{
-    if (ctx->flow.vlan_tci != state->vlan_tci) {
-        xlate_set_dl_tci(ctx);
-    }
-    if (ctx->flow.tun_id != state->tun_id) {
-        nl_msg_put_be64(ctx->odp_actions,
-                        ODP_ACTION_ATTR_SET_TUNNEL, ctx->flow.tun_id);
-    }
-}
-
 static void
 xlate_autopath(struct action_xlate_ctx *ctx,
                const struct nx_action_autopath *naa)
@@ -2990,7 +3054,6 @@ xlate_nicira_action(struct action_xlate_ctx *ctx,
     const struct nx_action_multipath *nam;
     const struct nx_action_autopath *naa;
     enum nx_action_subtype subtype = ntohs(nah->subtype);
-    struct xlate_reg_state state;
     ovs_be64 tun_id;
 
     assert(nah->vendor == htonl(NX_VENDOR_ID));
@@ -3003,17 +3066,9 @@ xlate_nicira_action(struct action_xlate_ctx *ctx,
     case NXAST_SET_TUNNEL:
         nast = (const struct nx_action_set_tunnel *) nah;
         tun_id = htonll(ntohl(nast->tun_id));
-        nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
         ctx->flow.tun_id = tun_id;
         break;
 
-    case NXAST_DROP_SPOOFED_ARP:
-        if (ctx->flow.dl_type == htons(ETH_TYPE_ARP)) {
-            nl_msg_put_flag(ctx->odp_actions,
-                            ODP_ACTION_ATTR_DROP_SPOOFED_ARP);
-        }
-        break;
-
     case NXAST_SET_QUEUE:
         nasq = (const struct nx_action_set_queue *) nah;
         xlate_set_queue_action(ctx, nasq);
@@ -3021,21 +3076,16 @@ xlate_nicira_action(struct action_xlate_ctx *ctx,
 
     case NXAST_POP_QUEUE:
         ctx->priority = 0;
-        add_pop_action(ctx);
         break;
 
     case NXAST_REG_MOVE:
-        save_reg_state(ctx, &state);
         nxm_execute_reg_move((const struct nx_action_reg_move *) nah,
                              &ctx->flow);
-        update_reg_state(ctx, &state);
         break;
 
     case NXAST_REG_LOAD:
-        save_reg_state(ctx, &state);
         nxm_execute_reg_load((const struct nx_action_reg_load *) nah,
                              &ctx->flow);
-        update_reg_state(ctx, &state);
         break;
 
     case NXAST_NOTE:
@@ -3044,7 +3094,6 @@ xlate_nicira_action(struct action_xlate_ctx *ctx,
 
     case NXAST_SET_TUNNEL64:
         tun_id = ((const struct nx_action_set_tunnel64 *) nah)->tun_id;
-        nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
         ctx->flow.tun_id = tun_id;
         break;
 
@@ -3062,6 +3111,7 @@ xlate_nicira_action(struct action_xlate_ctx *ctx,
      * update the flow key in ctx->flow at the same time. */
 
     case NXAST_SNAT__OBSOLETE:
+    case NXAST_DROP_SPOOFED_ARP__OBSOLETE:
     default:
         VLOG_DBG_RL(&rl, "unknown Nicira action type %d", (int) subtype);
         break;
@@ -3098,62 +3148,45 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
         case OFPAT_SET_VLAN_VID:
             ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
             ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
-            xlate_set_dl_tci(ctx);
             break;
 
         case OFPAT_SET_VLAN_PCP:
             ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
             ctx->flow.vlan_tci |= htons(
                 (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
-            xlate_set_dl_tci(ctx);
             break;
 
         case OFPAT_STRIP_VLAN:
             ctx->flow.vlan_tci = htons(0);
-            xlate_set_dl_tci(ctx);
             break;
 
         case OFPAT_SET_DL_SRC:
             oada = ((struct ofp_action_dl_addr *) ia);
-            nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_SRC,
-                              oada->dl_addr, ETH_ADDR_LEN);
             memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
             break;
 
         case OFPAT_SET_DL_DST:
             oada = ((struct ofp_action_dl_addr *) ia);
-            nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_DST,
-                              oada->dl_addr, ETH_ADDR_LEN);
             memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN);
             break;
 
         case OFPAT_SET_NW_SRC:
-            nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_SRC,
-                            ia->nw_addr.nw_addr);
             ctx->flow.nw_src = ia->nw_addr.nw_addr;
             break;
 
         case OFPAT_SET_NW_DST:
-            nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_DST,
-                            ia->nw_addr.nw_addr);
             ctx->flow.nw_dst = ia->nw_addr.nw_addr;
             break;
 
         case OFPAT_SET_NW_TOS:
-            nl_msg_put_u8(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_TOS,
-                          ia->nw_tos.nw_tos);
             ctx->flow.nw_tos = ia->nw_tos.nw_tos;
             break;
 
         case OFPAT_SET_TP_SRC:
-            nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_SRC,
-                            ia->tp_port.tp_port);
             ctx->flow.tp_src = ia->tp_port.tp_port;
             break;
 
         case OFPAT_SET_TP_DST:
-            nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_DST,
-                            ia->tp_port.tp_port);
             ctx->flow.tp_dst = ia->tp_port.tp_port;
             break;
 
@@ -3194,8 +3227,9 @@ xlate_actions(struct action_xlate_ctx *ctx,
     ctx->may_set_up_flow = true;
     ctx->nf_output_iface = NF_OUT_DROP;
     ctx->recurse = 0;
-    ctx->last_pop_priority = -1;
     ctx->priority = 0;
+    ctx->base_priority = 0;
+    ctx->base_flow = ctx->flow;
 
     if (process_special(ctx->ofproto, &ctx->flow, ctx->packet)) {
         ctx->may_set_up_flow = false;
@@ -3203,8 +3237,6 @@ xlate_actions(struct action_xlate_ctx *ctx,
         do_xlate_actions(in, n_in, ctx);
     }
 
-    remove_pop_action(ctx);
-
     /* Check with in-band control to see if we're allowed to set up this
      * flow. */
     if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow,
@@ -3946,6 +3978,22 @@ exit:
     free(args);
 }
 
+static void
+ofproto_dpif_clog(struct unixctl_conn *conn OVS_UNUSED,
+                  const char *args_ OVS_UNUSED, void *aux OVS_UNUSED)
+{
+    clogged = true;
+    unixctl_command_reply(conn, 200, NULL);
+}
+
+static void
+ofproto_dpif_unclog(struct unixctl_conn *conn OVS_UNUSED,
+                    const char *args_ OVS_UNUSED, void *aux OVS_UNUSED)
+{
+    clogged = false;
+    unixctl_command_reply(conn, 200, NULL);
+}
+
 static void
 ofproto_dpif_unixctl_init(void)
 {
@@ -3957,6 +4005,9 @@ ofproto_dpif_unixctl_init(void)
 
     unixctl_command_register("ofproto/trace", ofproto_unixctl_trace, NULL);
     unixctl_command_register("fdb/show", ofproto_unixctl_fdb_show, NULL);
+
+    unixctl_command_register("ofproto/clog", ofproto_dpif_clog, NULL);
+    unixctl_command_register("ofproto/unclog", ofproto_dpif_unclog, NULL);
 }
 \f
 const struct ofproto_class ofproto_dpif_class = {