Implement OFPT_TABLE_STATUS Message.
[cascardo/ovs.git] / ofproto / connmgr.c
index 9a5167d..59a870c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 
 #include "coverage.h"
+#include "dynamic-string.h"
 #include "fail-open.h"
 #include "in-band.h"
 #include "odp-util.h"
 #include "simap.h"
 #include "stream.h"
 #include "timeval.h"
-#include "vconn.h"
-#include "vlog.h"
+#include "openvswitch/vconn.h"
+#include "openvswitch/vlog.h"
+
+#include "bundles.h"
 
 VLOG_DEFINE_THIS_MODULE(connmgr);
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -57,7 +60,7 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 struct ofconn {
 /* Configuration that persists from one connection to the next. */
 
-    struct list node;           /* In struct connmgr's "all_conns" list. */
+    struct ovs_list node;       /* In struct connmgr's "all_conns" list. */
     struct hmap_node hmap_node; /* In struct connmgr's "controllers" map. */
 
     struct connmgr *connmgr;    /* Connection's manager. */
@@ -73,11 +76,6 @@ struct ofconn {
     enum ofputil_protocol protocol; /* Current protocol variant. */
     enum nx_packet_in_format packet_in_format; /* OFPT_PACKET_IN format. */
 
-    /* Asynchronous flow table operation support. */
-    struct list opgroups;       /* Contains pending "ofopgroups", if any. */
-    struct ofpbuf *blocked;     /* Postponed OpenFlow message, if any. */
-    bool retry;                 /* True if 'blocked' is ready to try again. */
-
     /* OFPT_PACKET_IN related data. */
     struct rconn_packet_counter *packet_in_counter; /* # queued on 'rconn'. */
 #define N_SCHEDULERS 2
@@ -92,12 +90,17 @@ struct ofconn {
 #define OFCONN_REPLY_MAX 100
     struct rconn_packet_counter *reply_counter;
 
-    /* Asynchronous message configuration in each possible roles.
+    /* Asynchronous message configuration in each possible role.
      *
      * A 1-bit enables sending an asynchronous message for one possible reason
      * that the message might be generated, a 0-bit disables it. */
-    uint32_t master_async_config[OAM_N_TYPES]; /* master, other */
-    uint32_t slave_async_config[OAM_N_TYPES];  /* slave */
+    struct ofputil_async_cfg *async_cfg;
+
+    /* Flow table operation logging. */
+    int n_add, n_delete, n_modify; /* Number of unreported ops of each kind. */
+    long long int first_op, last_op; /* Range of times for unreported ops. */
+    long long int next_op_report;    /* Time to report ops, or LLONG_MAX. */
+    long long int op_backoff;        /* Earliest time to report ops again. */
 
 /* Flow monitors (e.g. NXST_FLOW_MONITOR). */
 
@@ -127,8 +130,11 @@ struct ofconn {
      *
      * When 'updates' is nonempty, 'sent_abbrev_update' is true if 'updates'
      * contains an update event of type NXFME_ABBREV and false otherwise.. */
-    struct list updates OVS_GUARDED_BY(ofproto_mutex);
+    struct ovs_list updates OVS_GUARDED_BY(ofproto_mutex);
     bool sent_abbrev_update OVS_GUARDED_BY(ofproto_mutex);
+
+    /* Active bundles. Contains "struct ofp_bundle"s. */
+    struct hmap bundles;
 };
 
 static struct ofconn *ofconn_create(struct connmgr *, struct rconn *,
@@ -141,9 +147,11 @@ static void ofconn_reconfigure(struct ofconn *,
                                const struct ofproto_controller *);
 
 static void ofconn_run(struct ofconn *,
-                       bool (*handle_openflow)(struct ofconn *,
+                       void (*handle_openflow)(struct ofconn *,
                                                const struct ofpbuf *ofp_msg));
-static void ofconn_wait(struct ofconn *, bool handling_openflow);
+static void ofconn_wait(struct ofconn *);
+
+static void ofconn_log_flow_mods(struct ofconn *);
 
 static const char *ofconn_get_target(const struct ofconn *);
 static char *ofconn_make_name(const struct connmgr *, const char *target);
@@ -153,7 +161,7 @@ static void ofconn_set_rate_limit(struct ofconn *, int rate, int burst);
 static void ofconn_send(const struct ofconn *, struct ofpbuf *,
                         struct rconn_packet_counter *);
 
-static void do_send_packet_ins(struct ofconn *, struct list *txq);
+static void do_send_packet_ins(struct ofconn *, struct ovs_list *txq);
 
 /* A listener for incoming OpenFlow "service" connections. */
 struct ofservice {
@@ -186,8 +194,8 @@ struct connmgr {
     char *local_port_name;
 
     /* OpenFlow connections. */
-    struct hmap controllers;   /* Controller "struct ofconn"s. */
-    struct list all_conns;     /* Contains "struct ofconn"s. */
+    struct hmap controllers;     /* All OFCONN_PRIMARY controllers. */
+    struct ovs_list all_conns;   /* All controllers. */
     uint64_t master_election_id; /* monotonically increasing sequence number
                                   * for master election */
     bool master_election_id_defined;
@@ -290,23 +298,13 @@ connmgr_destroy(struct connmgr *mgr)
     free(mgr);
 }
 
-/* Does all of the periodic maintenance required by 'mgr'.
- *
- * If 'handle_openflow' is nonnull, calls 'handle_openflow' for each message
- * received on an OpenFlow connection, passing along the OpenFlow connection
- * itself and the message that was sent.  If 'handle_openflow' returns true,
- * the message is considered to be fully processed.  If 'handle_openflow'
- * returns false, the message is considered not to have been processed at all;
- * it will be stored and re-presented to 'handle_openflow' following the next
- * call to connmgr_retry().  'handle_openflow' must not modify or free the
- * message.
- *
- * If 'handle_openflow' is NULL, no OpenFlow messages will be processed and
- * other activities that could affect the flow table (in-band processing,
- * fail-open processing) are suppressed too. */
+/* Does all of the periodic maintenance required by 'mgr'.  Calls
+ * 'handle_openflow' for each message received on an OpenFlow connection,
+ * passing along the OpenFlow connection itself and the message that was sent.
+ * 'handle_openflow' must not modify or free the message. */
 void
 connmgr_run(struct connmgr *mgr,
-            bool (*handle_openflow)(struct ofconn *,
+            void (*handle_openflow)(struct ofconn *,
                                     const struct ofpbuf *ofp_msg))
     OVS_EXCLUDED(ofproto_mutex)
 {
@@ -314,7 +312,7 @@ connmgr_run(struct connmgr *mgr,
     struct ofservice *ofservice;
     size_t i;
 
-    if (handle_openflow && mgr->in_band) {
+    if (mgr->in_band) {
         if (!in_band_run(mgr->in_band)) {
             in_band_destroy(mgr->in_band);
             mgr->in_band = NULL;
@@ -328,7 +326,7 @@ connmgr_run(struct connmgr *mgr,
 
     /* Fail-open maintenance.  Do this after processing the ofconns since
      * fail-open checks the status of the controller rconn. */
-    if (handle_openflow && mgr->fail_open) {
+    if (mgr->fail_open) {
         fail_open_run(mgr->fail_open);
     }
 
@@ -373,26 +371,22 @@ connmgr_run(struct connmgr *mgr,
     }
 }
 
-/* Causes the poll loop to wake up when connmgr_run() needs to run.
- *
- * If 'handling_openflow' is true, arriving OpenFlow messages and other
- * activities that affect the flow table will wake up the poll loop.  If
- * 'handling_openflow' is false, they will not. */
+/* Causes the poll loop to wake up when connmgr_run() needs to run. */
 void
-connmgr_wait(struct connmgr *mgr, bool handling_openflow)
+connmgr_wait(struct connmgr *mgr)
 {
     struct ofservice *ofservice;
     struct ofconn *ofconn;
     size_t i;
 
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
-        ofconn_wait(ofconn, handling_openflow);
+        ofconn_wait(ofconn);
     }
     ofmonitor_wait(mgr);
-    if (handling_openflow && mgr->in_band) {
+    if (mgr->in_band) {
         in_band_wait(mgr->in_band);
     }
-    if (handling_openflow && mgr->fail_open) {
+    if (mgr->fail_open) {
         fail_open_wait(mgr->fail_open);
     }
     HMAP_FOR_EACH (ofservice, node, &mgr->services) {
@@ -419,7 +413,10 @@ connmgr_get_memory_usage(const struct connmgr *mgr, struct simap *usage)
 
         packets += rconn_count_txqlen(ofconn->rconn);
         for (i = 0; i < N_SCHEDULERS; i++) {
-            packets += pinsched_count_txqlen(ofconn->schedulers[i]);
+            struct pinsched_stats stats;
+
+            pinsched_get_stats(ofconn->schedulers[i], &stats);
+            packets += stats.n_queued;
         }
         packets += pktbuf_count_packets(ofconn->pktbuf);
     }
@@ -433,19 +430,6 @@ ofconn_get_ofproto(const struct ofconn *ofconn)
 {
     return ofconn->connmgr->ofproto;
 }
-
-/* If processing of OpenFlow messages was blocked on any 'mgr' ofconns by
- * returning false to the 'handle_openflow' callback to connmgr_run(), this
- * re-enables them. */
-void
-connmgr_retry(struct connmgr *mgr)
-{
-    struct ofconn *ofconn;
-
-    LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
-        ofconn->retry = true;
-    }
-}
 \f
 /* OpenFlow configuration. */
 
@@ -489,34 +473,50 @@ connmgr_get_controller_info(struct connmgr *mgr, struct shash *info)
             time_t last_connection = rconn_get_last_connection(rconn);
             time_t last_disconnect = rconn_get_last_disconnect(rconn);
             int last_error = rconn_get_last_error(rconn);
+            int i;
 
             shash_add(info, target, cinfo);
 
             cinfo->is_connected = rconn_is_connected(rconn);
             cinfo->role = ofconn->role;
 
-            cinfo->pairs.n = 0;
-
+            smap_init(&cinfo->pairs);
             if (last_error) {
-                cinfo->pairs.keys[cinfo->pairs.n] = "last_error";
-                cinfo->pairs.values[cinfo->pairs.n++]
-                    = xstrdup(ovs_retval_to_string(last_error));
+                smap_add(&cinfo->pairs, "last_error",
+                         ovs_retval_to_string(last_error));
             }
 
-            cinfo->pairs.keys[cinfo->pairs.n] = "state";
-            cinfo->pairs.values[cinfo->pairs.n++]
-                = xstrdup(rconn_get_state(rconn));
+            smap_add(&cinfo->pairs, "state", rconn_get_state(rconn));
 
             if (last_connection != TIME_MIN) {
-                cinfo->pairs.keys[cinfo->pairs.n] = "sec_since_connect";
-                cinfo->pairs.values[cinfo->pairs.n++]
-                    = xasprintf("%ld", (long int) (now - last_connection));
+                smap_add_format(&cinfo->pairs, "sec_since_connect",
+                                "%ld", (long int) (now - last_connection));
             }
 
             if (last_disconnect != TIME_MIN) {
-                cinfo->pairs.keys[cinfo->pairs.n] = "sec_since_disconnect";
-                cinfo->pairs.values[cinfo->pairs.n++]
-                    = xasprintf("%ld", (long int) (now - last_disconnect));
+                smap_add_format(&cinfo->pairs, "sec_since_disconnect",
+                                "%ld", (long int) (now - last_disconnect));
+            }
+
+            for (i = 0; i < N_SCHEDULERS; i++) {
+                if (ofconn->schedulers[i]) {
+                    const char *name = i ? "miss" : "action";
+                    struct pinsched_stats stats;
+
+                    pinsched_get_stats(ofconn->schedulers[i], &stats);
+                    smap_add_nocopy(&cinfo->pairs,
+                                    xasprintf("packet-in-%s-backlog", name),
+                                    xasprintf("%u", stats.n_queued));
+                    smap_add_nocopy(&cinfo->pairs,
+                                    xasprintf("packet-in-%s-bypassed", name),
+                                    xasprintf("%llu", stats.n_normal));
+                    smap_add_nocopy(&cinfo->pairs,
+                                    xasprintf("packet-in-%s-queued", name),
+                                    xasprintf("%llu", stats.n_limited));
+                    smap_add_nocopy(&cinfo->pairs,
+                                    xasprintf("packet-in-%s-dropped", name),
+                                    xasprintf("%llu", stats.n_queue_dropped));
+                }
             }
         }
     }
@@ -529,9 +529,7 @@ connmgr_free_controller_info(struct shash *info)
 
     SHASH_FOR_EACH (node, info) {
         struct ofproto_controller_info *cinfo = node->data;
-        while (cinfo->pairs.n) {
-            free(CONST_CAST(char *, cinfo->pairs.values[--cinfo->pairs.n]));
-        }
+        smap_destroy(&cinfo->pairs);
         free(cinfo);
     }
     shash_destroy(info);
@@ -735,12 +733,15 @@ update_in_band_remotes(struct connmgr *mgr)
     /* Add all the remotes. */
     HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) {
         const char *target = rconn_get_target(ofconn->rconn);
-        struct sockaddr_storage ss;
+        union {
+            struct sockaddr_storage ss;
+            struct sockaddr_in in;
+        } sa;
 
         if (ofconn->band == OFPROTO_IN_BAND
-            && stream_parse_target_with_default_port(target, OFP_OLD_PORT, &ss)
-            && ss.ss_family == AF_INET) {
-            addrs[n_addrs++] = *(struct sockaddr_in *) &ss;
+            && stream_parse_target_with_default_port(target, OFP_PORT, &sa.ss)
+            && sa.ss.ss_family == AF_INET) {
+            addrs[n_addrs++] = sa.in;
         }
     }
     for (i = 0; i < mgr->n_extra_remotes; i++) {
@@ -921,8 +922,9 @@ ofconn_send_role_status(struct ofconn *ofconn, uint32_t role, uint8_t reason)
     ofconn_get_master_election_id(ofconn, &status.generation_id);
 
     buf = ofputil_encode_role_status(&status, ofconn_get_protocol(ofconn));
-
-    ofconn_send(ofconn, buf, NULL);
+    if (buf) {
+        ofconn_send(ofconn, buf, NULL);
+    }
 }
 
 /* Changes 'ofconn''s role to 'role'.  If 'role' is OFPCR12_ROLE_MASTER then
@@ -933,7 +935,7 @@ ofconn_set_role(struct ofconn *ofconn, enum ofp12_controller_role role)
     if (role != ofconn->role && role == OFPCR12_ROLE_MASTER) {
         struct ofconn *other;
 
-        HMAP_FOR_EACH (other, hmap_node, &ofconn->connmgr->controllers) {
+        LIST_FOR_EACH (other, node, &ofconn->connmgr->all_conns) {
             if (other->role == OFPCR12_ROLE_MASTER) {
                 other->role = OFPCR12_ROLE_SLAVE;
                 ofconn_send_role_status(other, OFPCR12_ROLE_SLAVE, OFPCRR_MASTER_REQUEST);
@@ -946,19 +948,22 @@ ofconn_set_role(struct ofconn *ofconn, enum ofp12_controller_role role)
 void
 ofconn_set_invalid_ttl_to_controller(struct ofconn *ofconn, bool enable)
 {
+    struct ofputil_async_cfg ac = ofconn_get_async_config(ofconn);
     uint32_t bit = 1u << OFPR_INVALID_TTL;
     if (enable) {
-        ofconn->master_async_config[OAM_PACKET_IN] |= bit;
+        ac.master[OAM_PACKET_IN] |= bit;
     } else {
-        ofconn->master_async_config[OAM_PACKET_IN] &= ~bit;
+        ac.master[OAM_PACKET_IN] &= ~bit;
     }
+    ofconn_set_async_config(ofconn, &ac);
 }
 
 bool
 ofconn_get_invalid_ttl_to_controller(struct ofconn *ofconn)
 {
+    struct ofputil_async_cfg ac = ofconn_get_async_config(ofconn);
     uint32_t bit = 1u << OFPR_INVALID_TTL;
-    return (ofconn->master_async_config[OAM_PACKET_IN] & bit) != 0;
+    return (ac.master[OAM_PACKET_IN] & bit) != 0;
 }
 
 /* Returns the currently configured protocol for 'ofconn', one of OFPUTIL_P_*.
@@ -997,7 +1002,7 @@ ofconn_set_protocol(struct ofconn *ofconn, enum ofputil_protocol protocol)
 /* Returns the currently configured packet in format for 'ofconn', one of
  * NXPIF_*.
  *
- * The default, if no other format has been set, is NXPIF_OPENFLOW10. */
+ * The default, if no other format has been set, is NXPIF_STANDARD. */
 enum nx_packet_in_format
 ofconn_get_packet_in_format(struct ofconn *ofconn)
 {
@@ -1039,21 +1044,25 @@ ofconn_set_miss_send_len(struct ofconn *ofconn, int miss_send_len)
 
 void
 ofconn_set_async_config(struct ofconn *ofconn,
-                        const uint32_t master_masks[OAM_N_TYPES],
-                        const uint32_t slave_masks[OAM_N_TYPES])
+                        const struct ofputil_async_cfg *ac)
 {
-    size_t size = sizeof ofconn->master_async_config;
-    memcpy(ofconn->master_async_config, master_masks, size);
-    memcpy(ofconn->slave_async_config, slave_masks, size);
+    if (!ofconn->async_cfg) {
+        ofconn->async_cfg = xmalloc(sizeof *ofconn->async_cfg);
+    }
+    *ofconn->async_cfg = *ac;
 }
 
-void
-ofconn_get_async_config(struct ofconn *ofconn,
-                        uint32_t *master_masks, uint32_t *slave_masks)
+struct ofputil_async_cfg
+ofconn_get_async_config(const struct ofconn *ofconn)
 {
-    size_t size = sizeof ofconn->master_async_config;
-    memcpy(master_masks, ofconn->master_async_config, size);
-    memcpy(slave_masks, ofconn->slave_async_config, size);
+    if (ofconn->async_cfg) {
+        return *ofconn->async_cfg;
+    }
+
+    int version = rconn_get_version(ofconn->rconn);
+    return (version < 0 || !ofconn->enable_async_msgs
+            ? OFPUTIL_ASYNC_CFG_INIT
+            : ofputil_async_cfg_default(version));
 }
 
 /* Sends 'msg' on 'ofconn', accounting it as a reply.  (If there is a
@@ -1069,12 +1078,11 @@ ofconn_send_reply(const struct ofconn *ofconn, struct ofpbuf *msg)
 /* Sends each of the messages in list 'replies' on 'ofconn' in order,
  * accounting them as replies. */
 void
-ofconn_send_replies(const struct ofconn *ofconn, struct list *replies)
+ofconn_send_replies(const struct ofconn *ofconn, struct ovs_list *replies)
 {
-    struct ofpbuf *reply, *next;
+    struct ofpbuf *reply;
 
-    LIST_FOR_EACH_SAFE (reply, next, list_node, replies) {
-        list_remove(&reply->list_node);
+    LIST_FOR_EACH_POP (reply, list_node, replies) {
         ofconn_send_reply(ofconn, reply);
     }
 }
@@ -1110,31 +1118,92 @@ ofconn_send_error(const struct ofconn *ofconn,
 /* Same as pktbuf_retrieve(), using the pktbuf owned by 'ofconn'. */
 enum ofperr
 ofconn_pktbuf_retrieve(struct ofconn *ofconn, uint32_t id,
-                       struct ofpbuf **bufferp, ofp_port_t *in_port)
+                       struct dp_packet **bufferp, ofp_port_t *in_port)
 {
     return pktbuf_retrieve(ofconn->pktbuf, id, bufferp, in_port);
 }
 
-/* Returns true if 'ofconn' has any pending opgroups. */
-bool
-ofconn_has_pending_opgroups(const struct ofconn *ofconn)
+/* Reports that a flow_mod operation of the type specified by 'command' was
+ * successfully executed by 'ofconn', so that the connmgr can log it. */
+void
+ofconn_report_flow_mod(struct ofconn *ofconn,
+                       enum ofp_flow_mod_command command)
 {
-    return !list_is_empty(&ofconn->opgroups);
+    long long int now;
+
+    switch (command) {
+    case OFPFC_ADD:
+        ofconn->n_add++;
+        break;
+
+    case OFPFC_MODIFY:
+    case OFPFC_MODIFY_STRICT:
+        ofconn->n_modify++;
+        break;
+
+    case OFPFC_DELETE:
+    case OFPFC_DELETE_STRICT:
+        ofconn->n_delete++;
+        break;
+    }
+
+    now = time_msec();
+    if (ofconn->next_op_report == LLONG_MAX) {
+        ofconn->first_op = now;
+        ofconn->next_op_report = MAX(now + 10 * 1000, ofconn->op_backoff);
+        ofconn->op_backoff = ofconn->next_op_report + 60 * 1000;
+    }
+    ofconn->last_op = now;
 }
+\f
+/* OpenFlow 1.4 bundles. */
 
-/* Adds 'ofconn_node' to 'ofconn''s list of pending opgroups.
- *
- * If 'ofconn' is destroyed or its connection drops, then 'ofconn' will remove
- * 'ofconn_node' from the list and re-initialize it with list_init().  The
- * client may, therefore, use list_is_empty(ofconn_node) to determine whether
- * 'ofconn_node' is still associated with an active ofconn.
- *
- * The client may also remove ofconn_node from the list itself, with
- * list_remove(). */
-void
-ofconn_add_opgroup(struct ofconn *ofconn, struct list *ofconn_node)
+static inline uint32_t
+bundle_hash(uint32_t id)
+{
+    return hash_int(id, 0);
+}
+
+struct ofp_bundle *
+ofconn_get_bundle(struct ofconn *ofconn, uint32_t id)
+{
+    struct ofp_bundle *bundle;
+
+    HMAP_FOR_EACH_IN_BUCKET(bundle, node, bundle_hash(id), &ofconn->bundles) {
+        if (bundle->id == id) {
+            return bundle;
+        }
+    }
+
+    return NULL;
+}
+
+enum ofperr
+ofconn_insert_bundle(struct ofconn *ofconn, struct ofp_bundle *bundle)
+{
+    /* XXX: Check the limit of open bundles */
+
+    hmap_insert(&ofconn->bundles, &bundle->node, bundle_hash(bundle->id));
+
+    return 0;
+}
+
+enum ofperr
+ofconn_remove_bundle(struct ofconn *ofconn, struct ofp_bundle *bundle)
+{
+    hmap_remove(&ofconn->bundles, &bundle->node);
+
+    return 0;
+}
+
+static void
+bundle_remove_all(struct ofconn *ofconn)
 {
-    list_push_back(&ofconn->opgroups, ofconn_node);
+    struct ofp_bundle *b, *next;
+
+    HMAP_FOR_EACH_SAFE (b, next, node, &ofconn->bundles) {
+        ofp_bundle_remove__(ofconn, b, false);
+    }
 }
 \f
 /* Private ofconn functions. */
@@ -1158,11 +1227,11 @@ ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type,
     ofconn->type = type;
     ofconn->enable_async_msgs = enable_async_msgs;
 
-    list_init(&ofconn->opgroups);
-
     hmap_init(&ofconn->monitors);
     list_init(&ofconn->updates);
 
+    hmap_init(&ofconn->bundles);
+
     ofconn_flush(ofconn);
 
     return ofconn;
@@ -1177,21 +1246,11 @@ ofconn_flush(struct ofconn *ofconn)
     struct ofmonitor *monitor, *next_monitor;
     int i;
 
+    ofconn_log_flow_mods(ofconn);
+
     ofconn->role = OFPCR12_ROLE_EQUAL;
     ofconn_set_protocol(ofconn, OFPUTIL_P_NONE);
-    ofconn->packet_in_format = NXPIF_OPENFLOW10;
-
-    /* Disassociate 'ofconn' from all of the ofopgroups that it initiated that
-     * have not yet completed.  (Those ofopgroups will still run to completion
-     * in the usual way, but any errors that they run into will not be reported
-     * on any OpenFlow channel.)
-     *
-     * Also discard any blocked operation on 'ofconn'. */
-    while (!list_is_empty(&ofconn->opgroups)) {
-        list_init(list_pop_front(&ofconn->opgroups));
-    }
-    ofpbuf_delete(ofconn->blocked);
-    ofconn->blocked = NULL;
+    ofconn->packet_in_format = NXPIF_STANDARD;
 
     rconn_packet_counter_destroy(ofconn->packet_in_counter);
     ofconn->packet_in_counter = rconn_packet_counter_create();
@@ -1216,33 +1275,13 @@ ofconn_flush(struct ofconn *ofconn)
     rconn_packet_counter_destroy(ofconn->reply_counter);
     ofconn->reply_counter = rconn_packet_counter_create();
 
-    if (ofconn->enable_async_msgs) {
-        uint32_t *master = ofconn->master_async_config;
-        uint32_t *slave = ofconn->slave_async_config;
-
-        /* "master" and "other" roles get all asynchronous messages by default,
-         * except that the controller needs to enable nonstandard "packet-in"
-         * reasons itself. */
-        master[OAM_PACKET_IN] = (1u << OFPR_NO_MATCH) | (1u << OFPR_ACTION);
-        master[OAM_PORT_STATUS] = ((1u << OFPPR_ADD)
-                                   | (1u << OFPPR_DELETE)
-                                   | (1u << OFPPR_MODIFY));
-        master[OAM_FLOW_REMOVED] = ((1u << OFPRR_IDLE_TIMEOUT)
-                                    | (1u << OFPRR_HARD_TIMEOUT)
-                                    | (1u << OFPRR_DELETE));
-
-        /* "slave" role gets port status updates by default. */
-        slave[OAM_PACKET_IN] = 0;
-        slave[OAM_PORT_STATUS] = ((1u << OFPPR_ADD)
-                                  | (1u << OFPPR_DELETE)
-                                  | (1u << OFPPR_MODIFY));
-        slave[OAM_FLOW_REMOVED] = 0;
-    } else {
-        memset(ofconn->master_async_config, 0,
-               sizeof ofconn->master_async_config);
-        memset(ofconn->slave_async_config, 0,
-               sizeof ofconn->slave_async_config);
-    }
+    free(ofconn->async_cfg);
+    ofconn->async_cfg = NULL;
+
+    ofconn->n_add = ofconn->n_delete = ofconn->n_modify = 0;
+    ofconn->first_op = ofconn->last_op = LLONG_MIN;
+    ofconn->next_op_report = LLONG_MAX;
+    ofconn->op_backoff = LLONG_MIN;
 
     HMAP_FOR_EACH_SAFE (monitor, next_monitor, ofconn_node,
                         &ofconn->monitors) {
@@ -1263,6 +1302,9 @@ ofconn_destroy(struct ofconn *ofconn)
         hmap_remove(&ofconn->connmgr->controllers, &ofconn->hmap_node);
     }
 
+    bundle_remove_all(ofconn);
+    hmap_destroy(&ofconn->bundles);
+
     hmap_destroy(&ofconn->monitors);
     list_remove(&ofconn->node);
     rconn_destroy(ofconn->rconn);
@@ -1303,19 +1345,19 @@ static bool
 ofconn_may_recv(const struct ofconn *ofconn)
 {
     int count = rconn_packet_counter_n_packets(ofconn->reply_counter);
-    return (!ofconn->blocked || ofconn->retry) && count < OFCONN_REPLY_MAX;
+    return count < OFCONN_REPLY_MAX;
 }
 
 static void
 ofconn_run(struct ofconn *ofconn,
-           bool (*handle_openflow)(struct ofconn *,
+           void (*handle_openflow)(struct ofconn *,
                                    const struct ofpbuf *ofp_msg))
 {
     struct connmgr *mgr = ofconn->connmgr;
     size_t i;
 
     for (i = 0; i < N_SCHEDULERS; i++) {
-        struct list txq;
+        struct ovs_list txq;
 
         pinsched_run(ofconn->schedulers[i], &txq);
         do_send_packet_ins(ofconn, &txq);
@@ -1323,29 +1365,23 @@ ofconn_run(struct ofconn *ofconn,
 
     rconn_run(ofconn->rconn);
 
-    if (handle_openflow) {
-        /* Limit the number of iterations to avoid starving other tasks. */
-        for (i = 0; i < 50 && ofconn_may_recv(ofconn); i++) {
-            struct ofpbuf *of_msg;
-
-            of_msg = (ofconn->blocked
-                      ? ofconn->blocked
-                      : rconn_recv(ofconn->rconn));
-            if (!of_msg) {
-                break;
-            }
-            if (mgr->fail_open) {
-                fail_open_maybe_recover(mgr->fail_open);
-            }
+    /* Limit the number of iterations to avoid starving other tasks. */
+    for (i = 0; i < 50 && ofconn_may_recv(ofconn); i++) {
+        struct ofpbuf *of_msg = rconn_recv(ofconn->rconn);
+        if (!of_msg) {
+            break;
+        }
 
-            if (handle_openflow(ofconn, of_msg)) {
-                ofpbuf_delete(of_msg);
-                ofconn->blocked = NULL;
-            } else {
-                ofconn->blocked = of_msg;
-                ofconn->retry = false;
-            }
+        if (mgr->fail_open) {
+            fail_open_maybe_recover(mgr->fail_open);
         }
+
+        handle_openflow(ofconn, of_msg);
+        ofpbuf_delete(of_msg);
+    }
+
+    if (time_msec() >= ofconn->next_op_report) {
+        ofconn_log_flow_mods(ofconn);
     }
 
     ovs_mutex_lock(&ofproto_mutex);
@@ -1358,7 +1394,7 @@ ofconn_run(struct ofconn *ofconn,
 }
 
 static void
-ofconn_wait(struct ofconn *ofconn, bool handling_openflow)
+ofconn_wait(struct ofconn *ofconn)
 {
     int i;
 
@@ -1366,9 +1402,53 @@ ofconn_wait(struct ofconn *ofconn, bool handling_openflow)
         pinsched_wait(ofconn->schedulers[i]);
     }
     rconn_run_wait(ofconn->rconn);
-    if (handling_openflow && ofconn_may_recv(ofconn)) {
+    if (ofconn_may_recv(ofconn)) {
         rconn_recv_wait(ofconn->rconn);
     }
+    if (ofconn->next_op_report != LLONG_MAX) {
+        poll_timer_wait_until(ofconn->next_op_report);
+    }
+}
+
+static void
+ofconn_log_flow_mods(struct ofconn *ofconn)
+{
+    int n_flow_mods = ofconn->n_add + ofconn->n_delete + ofconn->n_modify;
+    if (n_flow_mods) {
+        long long int ago = (time_msec() - ofconn->first_op) / 1000;
+        long long int interval = (ofconn->last_op - ofconn->first_op) / 1000;
+        struct ds s;
+
+        ds_init(&s);
+        ds_put_format(&s, "%d flow_mods ", n_flow_mods);
+        if (interval == ago) {
+            ds_put_format(&s, "in the last %lld s", ago);
+        } else if (interval) {
+            ds_put_format(&s, "in the %lld s starting %lld s ago",
+                          interval, ago);
+        } else {
+            ds_put_format(&s, "%lld s ago", ago);
+        }
+
+        ds_put_cstr(&s, " (");
+        if (ofconn->n_add) {
+            ds_put_format(&s, "%d adds, ", ofconn->n_add);
+        }
+        if (ofconn->n_delete) {
+            ds_put_format(&s, "%d deletes, ", ofconn->n_delete);
+        }
+        if (ofconn->n_modify) {
+            ds_put_format(&s, "%d modifications, ", ofconn->n_modify);
+        }
+        s.length -= 2;
+        ds_put_char(&s, ')');
+
+        VLOG_INFO("%s: %s", rconn_get_name(ofconn->rconn), ds_cstr(&s));
+        ds_destroy(&s);
+
+        ofconn->n_add = ofconn->n_delete = ofconn->n_modify = 0;
+    }
+    ofconn->next_op_report = LLONG_MAX;
 }
 
 /* Returns true if 'ofconn' should receive asynchronous messages of the given
@@ -1378,19 +1458,12 @@ ofconn_wait(struct ofconn *ofconn, bool handling_openflow)
  * 'ofconn'. */
 static bool
 ofconn_receives_async_msg(const struct ofconn *ofconn,
-                          enum ofconn_async_msg_type type,
+                          enum ofputil_async_msg_type type,
                           unsigned int reason)
 {
-    const uint32_t *async_config;
-
     ovs_assert(reason < 32);
     ovs_assert((unsigned int) type < OAM_N_TYPES);
 
-    if (ofconn_get_protocol(ofconn) == OFPUTIL_P_NONE
-        || !rconn_is_connected(ofconn->rconn)) {
-        return false;
-    }
-
     /* Keep the following code in sync with the documentation in the
      * "Asynchronous Messages" section in DESIGN. */
 
@@ -1400,50 +1473,17 @@ ofconn_receives_async_msg(const struct ofconn *ofconn,
         return false;
     }
 
-    async_config = (ofconn->role == OFPCR12_ROLE_SLAVE
-                    ? ofconn->slave_async_config
-                    : ofconn->master_async_config);
-    if (!(async_config[type] & (1u << reason))) {
-        return false;
-    }
-
-    return true;
+    struct ofputil_async_cfg ac = ofconn_get_async_config(ofconn);
+    uint32_t *masks = (ofconn->role == OFPCR12_ROLE_SLAVE
+                       ? ac.slave
+                       : ac.master);
+    return (masks[type] & (1u << reason)) != 0;
 }
 
 /* The default "table-miss" behaviour for OpenFlow1.3+ is to drop the
  * packet rather than to send the packet to the controller.
  *
- * This function returns false to indicate the packet should be dropped if
- * the controller action was the result of the default table-miss behaviour
- * and the controller is using OpenFlow1.3+.
- *
- * Otherwise true is returned to indicate the packet should be forwarded to
- * the controller */
-static bool
-ofconn_wants_packet_in_on_miss(struct ofconn *ofconn,
-                               const struct ofproto_packet_in *pin)
-{
-    if (pin->miss_type == OFPROTO_PACKET_IN_MISS_WITHOUT_FLOW) {
-        enum ofputil_protocol protocol = ofconn_get_protocol(ofconn);
-
-        if (protocol != OFPUTIL_P_NONE
-            && ofputil_protocol_to_ofp_version(protocol) >= OFP13_VERSION) {
-            enum ofproto_table_config config;
-
-            config = ofproto_table_get_config(ofconn->connmgr->ofproto,
-                                              pin->up.table_id);
-            if (config == OFPROTO_TABLE_MISS_DEFAULT) {
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-/* The default "table-miss" behaviour for OpenFlow1.3+ is to drop the
- * packet rather than to send the packet to the controller.
- *
- * This function returns false to indicate that a packet_in message
+ * This function returns true to indicate that a packet_in message
  * for a "table-miss" should be sent to at least one controller.
  * That is there is at least one controller with controller_id 0
  * which connected using an OpenFlow version earlier than OpenFlow1.3.
@@ -1453,19 +1493,23 @@ ofconn_wants_packet_in_on_miss(struct ofconn *ofconn,
  * This logic assumes that "table-miss" packet_in messages
  * are always sent to controller_id 0. */
 bool
-connmgr_wants_packet_in_on_miss(struct connmgr *mgr)
+connmgr_wants_packet_in_on_miss(struct connmgr *mgr) OVS_EXCLUDED(ofproto_mutex)
 {
     struct ofconn *ofconn;
 
+    ovs_mutex_lock(&ofproto_mutex);
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
         enum ofputil_protocol protocol = ofconn_get_protocol(ofconn);
 
         if (ofconn->controller_id == 0 &&
             (protocol == OFPUTIL_P_NONE ||
              ofputil_protocol_to_ofp_version(protocol) < OFP13_VERSION)) {
+            ovs_mutex_unlock(&ofproto_mutex);
             return true;
         }
     }
+    ovs_mutex_unlock(&ofproto_mutex);
+
     return false;
 }
 
@@ -1511,9 +1555,6 @@ ofconn_send(const struct ofconn *ofconn, struct ofpbuf *msg,
 \f
 /* Sending asynchronous messages. */
 
-static void schedule_packet_in(struct ofconn *, struct ofproto_packet_in,
-                               enum ofp_packet_in_reason wire_reason);
-
 /* Sends an OFPT_PORT_STATUS message with 'opp' and 'reason' to appropriate
  * controllers managed by 'mgr'.  For messages caused by a controller
  * OFPT_PORT_MOD, specify 'source' as the controller connection that sent the
@@ -1562,6 +1603,28 @@ connmgr_send_port_status(struct connmgr *mgr, struct ofconn *source,
     }
 }
 
+/* Sends an OFPT_REQUESTFORWARD message with 'request' and 'reason' to
+ * appropriate controllers managed by 'mgr'.  For messages caused by a
+ * controller OFPT_GROUP_MOD and OFPT_METER_MOD, specify 'source' as the
+ * controller connection that sent the request; otherwise, specify 'source'
+ * as NULL. */
+void
+connmgr_send_requestforward(struct connmgr *mgr, const struct ofconn *source,
+                            const struct ofputil_requestforward *rf)
+{
+    struct ofconn *ofconn;
+
+    LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+        if (ofconn_receives_async_msg(ofconn, OAM_REQUESTFORWARD, rf->reason)
+            && rconn_get_version(ofconn->rconn) >= OFP14_VERSION
+            && ofconn != source) {
+            enum ofputil_protocol protocol = ofconn_get_protocol(ofconn);
+            ofconn_send(ofconn, ofputil_encode_requestforward(rf, protocol),
+                        NULL);
+        }
+    }
+}
+
 /* Sends an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message based on 'fr' to
  * appropriate controllers managed by 'mgr'. */
 void
@@ -1585,55 +1648,76 @@ connmgr_send_flow_removed(struct connmgr *mgr,
     }
 }
 
-/* Normally a send-to-controller action uses reason OFPR_ACTION.  However, in
- * OpenFlow 1.3 and later, packet_ins generated by a send-to-controller action
- * in a "table-miss" flow (one with priority 0 and completely wildcarded) are
- * sent as OFPR_NO_MATCH.  This function returns the reason that should
- * actually be sent on 'ofconn' for 'pin'. */
-static enum ofp_packet_in_reason
-wire_reason(struct ofconn *ofconn, const struct ofproto_packet_in *pin)
+/* Sends an OFPT_TABLE_STATUS message with 'reason' to appropriate controllers
+ * managed by 'mgr'. When the table state changes, the controller needs to be
+ * informed with the OFPT_TABLE_STATUS message. The reason values
+ * OFPTR_VACANCY_DOWN and OFPTR_VACANCY_UP identify a vacancy message. The
+ * vacancy events are generated when the remaining space in the flow table
+ * changes and crosses one of the vacancy thereshold specified by
+ * OFPT_TABLE_MOD. */
+void
+connmgr_send_table_status(struct connmgr *mgr,
+                          const struct ofputil_table_desc *td,
+                          uint8_t reason)
 {
-    if (pin->miss_type == OFPROTO_PACKET_IN_MISS_FLOW
-        && pin->up.reason == OFPR_ACTION) {
-        enum ofputil_protocol protocol = ofconn_get_protocol(ofconn);
+    struct ofputil_table_status ts;
+    struct ofconn *ofconn;
+
+    ts.reason = reason;
+    ts.desc = *td;
+
+    LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
+        if (ofconn_receives_async_msg(ofconn, OAM_TABLE_STATUS, reason)) {
+            struct ofpbuf *msg;
 
-        if (protocol != OFPUTIL_P_NONE
-            && ofputil_protocol_to_ofp_version(protocol) >= OFP13_VERSION) {
-            return OFPR_NO_MATCH;
+            msg = ofputil_encode_table_status(&ts,
+                                              ofconn_get_protocol(ofconn));
+            if (msg) {
+                ofconn_send(ofconn, msg, NULL);
+            }
         }
     }
-    return pin->up.reason;
 }
 
 /* Given 'pin', sends an OFPT_PACKET_IN message to each OpenFlow controller as
- * necessary according to their individual configurations.
- *
- * The caller doesn't need to fill in pin->buffer_id or pin->total_len. */
+ * necessary according to their individual configurations. */
 void
-connmgr_send_packet_in(struct connmgr *mgr,
-                       const struct ofproto_packet_in *pin)
+connmgr_send_async_msg(struct connmgr *mgr,
+                       const struct ofproto_async_msg *am)
 {
     struct ofconn *ofconn;
 
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
-        enum ofp_packet_in_reason reason = wire_reason(ofconn, pin);
-
-        if (ofconn_wants_packet_in_on_miss(ofconn, pin)
-            && ofconn_receives_async_msg(ofconn, OAM_PACKET_IN, pin->up.reason)
-            && ofconn->controller_id == pin->controller_id) {
-            schedule_packet_in(ofconn, *pin, reason);
+        enum ofputil_protocol protocol = ofconn_get_protocol(ofconn);
+        if (protocol == OFPUTIL_P_NONE || !rconn_is_connected(ofconn->rconn)
+            || ofconn->controller_id != am->controller_id
+            || !ofconn_receives_async_msg(ofconn, am->oam,
+                                          am->pin.up.public.reason)) {
+            continue;
         }
+
+        struct ofpbuf *msg = ofputil_encode_packet_in_private(
+            &am->pin.up, protocol, ofconn->packet_in_format,
+            am->pin.max_len >= 0 ? am->pin.max_len : ofconn->miss_send_len,
+            ofconn->pktbuf);
+
+        struct ovs_list txq;
+        bool is_miss = (am->pin.up.public.reason == OFPR_NO_MATCH ||
+                        am->pin.up.public.reason == OFPR_EXPLICIT_MISS ||
+                        am->pin.up.public.reason == OFPR_IMPLICIT_MISS);
+        pinsched_send(ofconn->schedulers[is_miss],
+                      am->pin.up.public.flow_metadata.flow.in_port.ofp_port,
+                      msg, &txq);
+        do_send_packet_ins(ofconn, &txq);
     }
 }
 
 static void
-do_send_packet_ins(struct ofconn *ofconn, struct list *txq)
+do_send_packet_ins(struct ofconn *ofconn, struct ovs_list *txq)
 {
-    struct ofpbuf *pin, *next_pin;
-
-    LIST_FOR_EACH_SAFE (pin, next_pin, list_node, txq) {
-        list_remove(&pin->list_node);
+    struct ofpbuf *pin;
 
+    LIST_FOR_EACH_POP (pin, list_node, txq) {
         if (rconn_send_with_limit(ofconn->rconn, pin,
                                   ofconn->packet_in_counter, 100) == EAGAIN) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
@@ -1643,58 +1727,6 @@ do_send_packet_ins(struct ofconn *ofconn, struct list *txq)
         }
     }
 }
-
-/* Takes 'pin', composes an OpenFlow packet-in message from it, and passes it
- * to 'ofconn''s packet scheduler for sending. */
-static void
-schedule_packet_in(struct ofconn *ofconn, struct ofproto_packet_in pin,
-                   enum ofp_packet_in_reason wire_reason)
-{
-    struct connmgr *mgr = ofconn->connmgr;
-    uint16_t controller_max_len;
-    struct list txq;
-
-    pin.up.total_len = pin.up.packet_len;
-
-    pin.up.reason = wire_reason;
-    if (pin.up.reason == OFPR_ACTION) {
-        controller_max_len = pin.send_len;  /* max_len */
-    } else {
-        controller_max_len = ofconn->miss_send_len;
-    }
-
-    /* Get OpenFlow buffer_id.
-     * For OpenFlow 1.2+, OFPCML_NO_BUFFER (== UINT16_MAX) specifies
-     * unbuffered.  This behaviour doesn't violate prior versions, too. */
-    if (controller_max_len == UINT16_MAX) {
-        pin.up.buffer_id = UINT32_MAX;
-    } else if (mgr->fail_open && fail_open_is_active(mgr->fail_open)) {
-        pin.up.buffer_id = pktbuf_get_null();
-    } else if (!ofconn->pktbuf) {
-        pin.up.buffer_id = UINT32_MAX;
-    } else {
-        pin.up.buffer_id = pktbuf_save(ofconn->pktbuf,
-                                       pin.up.packet, pin.up.packet_len,
-                                       pin.up.fmd.in_port);
-    }
-
-    /* Figure out how much of the packet to send.
-     * If not buffered, send the entire packet.  Otherwise, depending on
-     * the reason of packet-in, send what requested by the controller. */
-    if (pin.up.buffer_id != UINT32_MAX
-        && controller_max_len < pin.up.packet_len) {
-        pin.up.packet_len = controller_max_len;
-    }
-
-    /* Make OFPT_PACKET_IN and hand over to packet scheduler. */
-    pinsched_send(ofconn->schedulers[pin.up.reason == OFPR_NO_MATCH ? 0 : 1],
-                  pin.up.fmd.in_port,
-                  ofputil_encode_packet_in(&pin.up,
-                                           ofconn_get_protocol(ofconn),
-                                           ofconn->packet_in_format),
-                  &txq);
-    do_send_packet_ins(ofconn, &txq);
-}
 \f
 /* Fail-open settings. */
 
@@ -1880,15 +1912,31 @@ connmgr_flushed(struct connmgr *mgr)
 
         ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE);
         ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
-        ofpact_pad(&ofpacts);
 
         match_init_catchall(&match);
-        ofproto_add_flow(mgr->ofproto, &match, 0, ofpbuf_data(&ofpacts),
-                                                  ofpbuf_size(&ofpacts));
+        ofproto_add_flow(mgr->ofproto, &match, 0, ofpacts.data,
+                                                  ofpacts.size);
 
         ofpbuf_uninit(&ofpacts);
     }
 }
+
+/* Returns the number of hidden rules created by the in-band and fail-open
+ * implementations in table 0.  (Subtracting this count from the number of
+ * rules in the table 0 classifier, as maintained in struct oftable, yields
+ * the number of flows that OVS should report via OpenFlow for table 0.) */
+int
+connmgr_count_hidden_rules(const struct connmgr *mgr)
+{
+    int n_hidden = 0;
+    if (mgr->in_band) {
+        n_hidden += in_band_count_rules(mgr->in_band);
+    }
+    if (mgr->fail_open) {
+        n_hidden += fail_open_count_rules(mgr->fail_open);
+    }
+    return n_hidden;
+}
 \f
 /* Creates a new ofservice for 'target' in 'mgr'.  Returns 0 if successful,
  * otherwise a positive errno value.
@@ -1987,7 +2035,7 @@ ofmonitor_create(const struct ofputil_flow_monitor_request *request,
 
     m = ofmonitor_lookup(ofconn, request->id);
     if (m) {
-        return OFPERR_NXBRC_FM_DUPLICATE_ID;
+        return OFPERR_OFPMOFC_MONITOR_EXISTS;
     }
 
     m = xmalloc(sizeof *m);
@@ -2033,12 +2081,17 @@ void
 ofmonitor_report(struct connmgr *mgr, struct rule *rule,
                  enum nx_flow_update_event event,
                  enum ofp_flow_removed_reason reason,
-                 const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid)
+                 const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid,
+                 const struct rule_actions *old_actions)
     OVS_REQUIRES(ofproto_mutex)
 {
     enum nx_flow_monitor_flags update;
     struct ofconn *ofconn;
 
+    if (rule_is_hidden(rule)) {
+        return;
+    }
+
     switch (event) {
     case NXFME_ADDED:
         update = NXFMF_ADD;
@@ -2075,7 +2128,11 @@ ofmonitor_report(struct connmgr *mgr, struct rule *rule,
         HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
             if (m->flags & update
                 && (m->table_id == 0xff || m->table_id == rule->table_id)
-                && ofoperation_has_out_port(rule->pending, m->out_port)
+                && (ofproto_rule_has_out_port(rule, m->out_port)
+                    || (old_actions
+                        && ofpacts_output_to_port(old_actions->ofpacts,
+                                                  old_actions->ofpacts_len,
+                                                  m->out_port)))
                 && cls_rule_is_loose_match(&rule->cr, &m->match)) {
                 flags |= m->flags;
             }
@@ -2087,7 +2144,8 @@ ofmonitor_report(struct connmgr *mgr, struct rule *rule,
                 ofconn->sent_abbrev_update = false;
             }
 
-            if (ofconn != abbrev_ofconn || ofconn->monitor_paused) {
+            if (flags & NXFMF_OWN || ofconn != abbrev_ofconn
+                || ofconn->monitor_paused) {
                 struct ofputil_flow_update fu;
                 struct match match;
 
@@ -2133,12 +2191,11 @@ ofmonitor_flush(struct connmgr *mgr)
     struct ofconn *ofconn;
 
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
-        struct ofpbuf *msg, *next;
+        struct ofpbuf *msg;
 
-        LIST_FOR_EACH_SAFE (msg, next, list_node, &ofconn->updates) {
+        LIST_FOR_EACH_POP (msg, list_node, &ofconn->updates) {
             unsigned int n_bytes;
 
-            list_remove(&msg->list_node);
             ofconn_send(ofconn, msg, ofconn->monitor_counter);
             n_bytes = rconn_packet_counter_n_bytes(ofconn->monitor_counter);
             if (!ofconn->monitor_paused && n_bytes > 128 * 1024) {
@@ -2161,7 +2218,7 @@ ofmonitor_resume(struct ofconn *ofconn)
     struct rule_collection rules;
     struct ofpbuf *resumed;
     struct ofmonitor *m;
-    struct list msgs;
+    struct ovs_list msgs;
 
     rule_collection_init(&rules);
     HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) {
@@ -2215,3 +2272,14 @@ ofmonitor_wait(struct connmgr *mgr)
     }
     ovs_mutex_unlock(&ofproto_mutex);
 }
+
+void
+ofproto_async_msg_free(struct ofproto_async_msg *am)
+{
+    free(am->pin.up.public.packet);
+    free(am->pin.up.public.userdata);
+    free(am->pin.up.stack);
+    free(am->pin.up.actions);
+    free(am->pin.up.action_set);
+    free(am);
+}