ofproto: Move function find_meter() into ofpacts as ofpacts_get_meter().
[cascardo/ovs.git] / ofproto / ofproto.c
index 852a18d..7f7e42d 100644 (file)
@@ -240,7 +240,8 @@ static enum ofperr add_flow(struct ofproto *, struct ofconn *,
                             const struct ofp_header *);
 static enum ofperr modify_flows__(struct ofproto *, struct ofconn *,
                                   struct ofputil_flow_mod *,
-                                  const struct ofp_header *, struct list *);
+                                  const struct ofp_header *,
+                                  const struct rule_collection *);
 static void delete_flow__(struct rule *rule, struct ofopgroup *,
                           enum ofp_flow_removed_reason)
     OVS_RELEASES(rule->rwlock);
@@ -1109,10 +1110,20 @@ ofproto_get_snoops(const struct ofproto *ofproto, struct sset *snoops)
 
 /* Deletes 'rule' from 'cls' within 'ofproto'.
  *
+ * Within an ofproto implementation, this function allows an ofproto
+ * implementation to destroy any rules that remain when its ->destruct()
+ * function is called.  This function is not suitable for use elsewhere in an
+ * ofproto implementation.
+ *
+ * This function is also used internally in ofproto.c.
+ *
+ * This function implements steps 4.4 and 4.5 in the section titled "Rule Life
+ * Cycle" in ofproto-provider.h.
+
  * The 'cls' argument is redundant (it is &ofproto->tables[rule->table_id].cls)
  * but it allows Clang to do better checking. */
-static void
-ofproto_delete_rule(struct ofproto *ofproto, struct classifier *cls,
+void
+ofproto_rule_delete(struct ofproto *ofproto, struct classifier *cls,
                     struct rule *rule)
     OVS_REQ_WRLOCK(cls->rwlock)
 {
@@ -1150,7 +1161,7 @@ ofproto_flush__(struct ofproto *ofproto)
         cls_cursor_init(&cursor, &table->cls, NULL);
         CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, cr, &cursor) {
             if (!rule->pending) {
-                ofproto_delete_rule(ofproto, &table->cls, rule);
+                ofproto_rule_delete(ofproto, &table->cls, rule);
             }
         }
         ovs_rwlock_unlock(&table->cls.rwlock);
@@ -1740,10 +1751,9 @@ ofproto_add_flow(struct ofproto *ofproto, const struct match *match,
         fm.match = *match;
         fm.priority = priority;
         fm.buffer_id = UINT32_MAX;
-        fm.ofpacts = xmemdup(ofpacts, ofpacts_len);
+        fm.ofpacts = CONST_CAST(struct ofpact *, ofpacts);
         fm.ofpacts_len = ofpacts_len;
         add_flow(ofproto, NULL, &fm, NULL);
-        free(fm.ofpacts);
     }
 }
 
@@ -1783,7 +1793,7 @@ ofproto_delete_flow(struct ofproto *ofproto,
     } else {
         /* Initiate deletion -> success. */
         ovs_rwlock_wrlock(&cls->rwlock);
-        ofproto_delete_rule(ofproto, cls, rule);
+        ofproto_rule_delete(ofproto, cls, rule);
         ovs_rwlock_unlock(&cls->rwlock);
 
         return true;
@@ -2298,21 +2308,6 @@ ofproto_rule_destroy__(struct rule *rule)
     rule->ofproto->ofproto_class->rule_dealloc(rule);
 }
 
-/* This function allows an ofproto implementation to destroy any rules that
- * remain when its ->destruct() function is called..  This function implements
- * steps 4.4 and 4.5 in the section titled "Rule Life Cycle" in
- * ofproto-provider.h.
- *
- * This function should only be called from an ofproto implementation's
- * ->destruct() function.  It is not suitable elsewhere. */
-void
-ofproto_rule_delete(struct ofproto *ofproto, struct classifier *cls,
-                    struct rule *rule)
-    OVS_REQ_WRLOCK(cls->rwlock)
-{
-    ofproto_delete_rule(ofproto, cls, rule);
-}
-
 /* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
  * that outputs to 'port' (output to OFPP_FLOOD and OFPP_ALL doesn't count). */
 bool
@@ -2510,30 +2505,6 @@ reject_slave_controller(struct ofconn *ofconn)
     }
 }
 
-/* Finds the OFPACT_METER action, if any, in the 'ofpacts_len' bytes of
- * 'ofpacts'.  If found, returns its meter ID; if not, returns 0.
- *
- * This function relies on the order of 'ofpacts' being correct (as checked by
- * ofpacts_verify()). */
-static uint32_t
-find_meter(const struct ofpact ofpacts[], size_t ofpacts_len)
-{
-    const struct ofpact *a;
-
-    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
-        enum ovs_instruction_type inst;
-
-        inst = ovs_instruction_type_from_ofpact_type(a->type);
-        if (a->type == OFPACT_METER) {
-            return ofpact_get_METER(a)->meter_id;
-        } else if (inst > OVSINST_OFPIT13_METER) {
-            break;
-        }
-    }
-
-    return 0;
-}
-
 /* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are appropriate
  * for a packet with the prerequisites satisfied by 'flow' in table 'table_id'.
  * 'flow' may be temporarily modified, but is restored at return.
@@ -2552,7 +2523,7 @@ ofproto_check_ofpacts(struct ofproto *ofproto,
         return error;
     }
 
-    mid = find_meter(ofpacts, ofpacts_len);
+    mid = ofpacts_get_meter(ofpacts, ofpacts_len);
     if (mid && ofproto_get_provider_meter_id(ofproto, mid) == UINT32_MAX) {
         return OFPERR_OFPMMFC_INVALID_METER;
     }
@@ -2974,9 +2945,46 @@ rule_criteria_destroy(struct rule_criteria *criteria)
     cls_rule_destroy(&criteria->cr);
 }
 
+void
+rule_collection_init(struct rule_collection *rules)
+{
+    rules->rules = rules->stub;
+    rules->n = 0;
+    rules->capacity = ARRAY_SIZE(rules->stub);
+}
+
+void
+rule_collection_add(struct rule_collection *rules, struct rule *rule)
+{
+    if (rules->n >= rules->capacity) {
+        size_t old_size, new_size;
+
+        old_size = rules->capacity * sizeof *rules->rules;
+        rules->capacity *= 2;
+        new_size = rules->capacity * sizeof *rules->rules;
+
+        if (rules->rules == rules->stub) {
+            rules->rules = xmalloc(new_size);
+            memcpy(rules->rules, rules->stub, old_size);
+        } else {
+            rules->rules = xrealloc(rules->rules, new_size);
+        }
+    }
+
+    rules->rules[rules->n++] = rule;
+}
+
+void
+rule_collection_destroy(struct rule_collection *rules)
+{
+    if (rules->rules != rules->stub) {
+        free(rules->rules);
+    }
+}
+
 static enum ofperr
 collect_rule(struct rule *rule, const struct rule_criteria *c,
-             struct list *rules)
+             struct rule_collection *rules)
 {
     if (ofproto_rule_is_hidden(rule)) {
         return 0;
@@ -2986,7 +2994,7 @@ collect_rule(struct rule *rule, const struct rule_criteria *c,
         if ((c->table_id == rule->table_id || c->table_id == 0xff)
             && ofproto_rule_has_out_port(rule, c->out_port)
             && !((rule->flow_cookie ^ c->cookie) & c->cookie_mask)) {
-            list_push_back(rules, &rule->ofproto_node);
+            rule_collection_add(rules, rule);
         }
         return 0;
     }
@@ -3002,18 +3010,19 @@ collect_rule(struct rule *rule, const struct rule_criteria *c,
  * Returns 0 on success, otherwise an OpenFlow error code. */
 static enum ofperr
 collect_rules_loose(struct ofproto *ofproto,
-                    const struct rule_criteria *criteria, struct list *rules)
+                    const struct rule_criteria *criteria,
+                    struct rule_collection *rules)
 {
     struct oftable *table;
     enum ofperr error;
 
+    rule_collection_init(rules);
+
     error = check_table_id(ofproto, criteria->table_id);
     if (error) {
-        return error;
+        goto exit;
     }
 
-    list_init(rules);
-
     if (criteria->cookie_mask == htonll(UINT64_MAX)) {
         struct rule *rule;
 
@@ -3044,6 +3053,10 @@ collect_rules_loose(struct ofproto *ofproto,
         }
     }
 
+exit:
+    if (error) {
+        rule_collection_destroy(rules);
+    }
     return error;
 }
 
@@ -3057,18 +3070,19 @@ collect_rules_loose(struct ofproto *ofproto,
  * Returns 0 on success, otherwise an OpenFlow error code. */
 static enum ofperr
 collect_rules_strict(struct ofproto *ofproto,
-                     const struct rule_criteria *criteria, struct list *rules)
+                     const struct rule_criteria *criteria,
+                     struct rule_collection *rules)
 {
     struct oftable *table;
     int error;
 
+    rule_collection_init(rules);
+
     error = check_table_id(ofproto, criteria->table_id);
     if (error) {
-        return error;
+        goto exit;
     }
 
-    list_init(rules);
-
     if (criteria->cookie_mask == htonll(UINT64_MAX)) {
         struct rule *rule;
 
@@ -3099,6 +3113,10 @@ collect_rules_strict(struct ofproto *ofproto,
         }
     }
 
+exit:
+    if (error) {
+        rule_collection_destroy(rules);
+    }
     return error;
 }
 
@@ -3119,10 +3137,10 @@ handle_flow_stats_request(struct ofconn *ofconn,
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
     struct ofputil_flow_stats_request fsr;
     struct rule_criteria criteria;
+    struct rule_collection rules;
     struct list replies;
-    struct list rules;
-    struct rule *rule;
     enum ofperr error;
+    size_t i;
 
     error = ofputil_decode_flow_stats_request(&fsr, request);
     if (error) {
@@ -3138,7 +3156,8 @@ handle_flow_stats_request(struct ofconn *ofconn,
     }
 
     ofpmp_init(&replies, request);
-    LIST_FOR_EACH (rule, ofproto_node, &rules) {
+    for (i = 0; i < rules.n; i++) {
+        struct rule *rule = rules.rules[i];
         long long int now = time_msec();
         struct ofputil_flow_stats fs;
 
@@ -3167,6 +3186,8 @@ handle_flow_stats_request(struct ofconn *ofconn,
         }
         ofputil_append_flow_stats_reply(&fs, &replies);
     }
+    rule_collection_destroy(&rules);
+
     ofconn_send_replies(ofconn, &replies);
 
     return 0;
@@ -3248,10 +3269,10 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
     struct ofputil_aggregate_stats stats;
     bool unknown_packets, unknown_bytes;
     struct rule_criteria criteria;
+    struct rule_collection rules;
     struct ofpbuf *reply;
-    struct list rules;
-    struct rule *rule;
     enum ofperr error;
+    size_t i;
 
     error = ofputil_decode_flow_stats_request(&request, oh);
     if (error) {
@@ -3269,7 +3290,8 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
 
     memset(&stats, 0, sizeof stats);
     unknown_packets = unknown_bytes = false;
-    LIST_FOR_EACH (rule, ofproto_node, &rules) {
+    for (i = 0; i < rules.n; i++) {
+        struct rule *rule = rules.rules[i];
         uint64_t packet_count;
         uint64_t byte_count;
 
@@ -3297,6 +3319,8 @@ handle_aggregate_stats_request(struct ofconn *ofconn,
         stats.byte_count = UINT64_MAX;
     }
 
+    rule_collection_destroy(&rules);
+
     reply = ofputil_encode_aggregate_stats_reply(&stats, oh);
     ofconn_send_reply(ofconn, reply);
 
@@ -3457,8 +3481,7 @@ evict_rule_from_table(struct ofproto *ofproto, struct oftable *table)
  * error code on failure, or OFPROTO_POSTPONE if the operation cannot be
  * initiated now but may be retried later.
  *
- * Upon successful return, takes ownership of 'fm->ofpacts'.  On failure,
- * ownership remains with the caller.
+ * The caller retains ownership of 'fm->ofpacts'.
  *
  * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
  * if any. */
@@ -3516,12 +3539,15 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
         } else if (rule->pending) {
             return OFPROTO_POSTPONE;
         } else {
-            struct list rules;
+            struct rule_collection rules;
 
-            list_init(&rules);
-            list_push_back(&rules, &rule->ofproto_node);
+            rule_collection_init(&rules);
+            rule_collection_add(&rules, rule);
             fm->modify_cookie = true;
-            return modify_flows__(ofproto, ofconn, fm, request, &rules);
+            error = modify_flows__(ofproto, ofconn, fm, request, &rules);
+            rule_collection_destroy(&rules);
+
+            return error;
         }
     }
 
@@ -3586,7 +3612,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     rule->send_flow_removed = (fm->flags & OFPUTIL_FF_SEND_FLOW_REM) != 0;
     rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
     rule->ofpacts_len = fm->ofpacts_len;
-    rule->meter_id = find_meter(rule->ofpacts, rule->ofpacts_len);
+    rule->meter_id = ofpacts_get_meter(rule->ofpacts, rule->ofpacts_len);
     list_init(&rule->meter_list_node);
     rule->eviction_group = NULL;
     list_init(&rule->expirable);
@@ -3625,17 +3651,18 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
 static enum ofperr
 modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
                struct ofputil_flow_mod *fm, const struct ofp_header *request,
-               struct list *rules)
+               const struct rule_collection *rules)
 {
     enum ofoperation_type type;
     struct ofopgroup *group;
-    struct rule *rule;
     enum ofperr error;
+    size_t i;
 
     type = fm->command == OFPFC_ADD ? OFOPERATION_REPLACE : OFOPERATION_MODIFY;
     group = ofopgroup_create(ofproto, ofconn, request, fm->buffer_id);
     error = OFPERR_OFPBRC_EPERM;
-    LIST_FOR_EACH (rule, ofproto_node, rules) {
+    for (i = 0; i < rules->n; i++) {
+        struct rule *rule = rules->rules[i];
         struct ofoperation *op;
         bool actions_changed;
         bool reset_counters;
@@ -3693,7 +3720,8 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
             rule->ofpacts_len = fm->ofpacts_len;
             ovs_rwlock_unlock(&rule->rwlock);
 
-            rule->meter_id = find_meter(rule->ofpacts, rule->ofpacts_len);
+            rule->meter_id = ofpacts_get_meter(rule->ofpacts,
+                                               rule->ofpacts_len);
             rule->ofproto->ofproto_class->rule_modify_actions(rule,
                                                               reset_counters);
         } else {
@@ -3726,7 +3754,7 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
                    const struct ofp_header *request)
 {
     struct rule_criteria criteria;
-    struct list rules;
+    struct rule_collection rules;
     int error;
 
     rule_criteria_init(&criteria, fm->table_id, &fm->match, 0,
@@ -3734,13 +3762,15 @@ modify_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
     error = collect_rules_loose(ofproto, &criteria, &rules);
     rule_criteria_destroy(&criteria);
 
-    if (error) {
-        return error;
-    } else if (list_is_empty(&rules)) {
-        return modify_flows_add(ofproto, ofconn, fm, request);
-    } else {
-        return modify_flows__(ofproto, ofconn, fm, request, &rules);
+    if (!error) {
+        error = (rules.n > 0
+                 ? modify_flows__(ofproto, ofconn, fm, request, &rules)
+                 : modify_flows_add(ofproto, ofconn, fm, request));
     }
+
+    rule_collection_destroy(&rules);
+
+    return error;
 }
 
 /* Implements OFPFC_MODIFY_STRICT.  Returns 0 on success or an OpenFlow error
@@ -3754,7 +3784,7 @@ modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
                    const struct ofp_header *request)
 {
     struct rule_criteria criteria;
-    struct list rules;
+    struct rule_collection rules;
     int error;
 
     rule_criteria_init(&criteria, fm->table_id, &fm->match, fm->priority,
@@ -3762,15 +3792,17 @@ modify_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
     error = collect_rules_strict(ofproto, &criteria, &rules);
     rule_criteria_destroy(&criteria);
 
-    if (error) {
-        return error;
-    } else if (list_is_empty(&rules)) {
-        return modify_flows_add(ofproto, ofconn, fm, request);
-    } else {
-        return list_is_singleton(&rules) ? modify_flows__(ofproto, ofconn,
-                                                          fm, request, &rules)
-                                         : 0;
+    if (!error) {
+        if (rules.n == 0) {
+            error =  modify_flows_add(ofproto, ofconn, fm, request);
+        } else if (rules.n == 1) {
+            error = modify_flows__(ofproto, ofconn, fm, request, &rules);
+        }
     }
+
+    rule_collection_destroy(&rules);
+
+    return error;
 }
 \f
 /* OFPFC_DELETE implementation. */
@@ -3793,14 +3825,16 @@ delete_flow__(struct rule *rule, struct ofopgroup *group,
  * Returns 0 on success, otherwise an OpenFlow error code. */
 static enum ofperr
 delete_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
-               const struct ofp_header *request, struct list *rules,
+               const struct ofp_header *request,
+               const struct rule_collection *rules,
                enum ofp_flow_removed_reason reason)
 {
-    struct rule *rule, *next;
     struct ofopgroup *group;
+    size_t i;
 
     group = ofopgroup_create(ofproto, ofconn, request, UINT32_MAX);
-    LIST_FOR_EACH_SAFE (rule, next, ofproto_node, rules) {
+    for (i = 0; i < rules->n; i++) {
+        struct rule *rule = rules->rules[i];
         ovs_rwlock_wrlock(&rule->rwlock);
         delete_flow__(rule, group, reason);
     }
@@ -3816,7 +3850,7 @@ delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
                    const struct ofp_header *request)
 {
     struct rule_criteria criteria;
-    struct list rules;
+    struct rule_collection rules;
     enum ofperr error;
 
     rule_criteria_init(&criteria, fm->table_id, &fm->match, 0,
@@ -3825,10 +3859,12 @@ delete_flows_loose(struct ofproto *ofproto, struct ofconn *ofconn,
     error = collect_rules_loose(ofproto, &criteria, &rules);
     rule_criteria_destroy(&criteria);
 
-    return (error ? error
-            : !list_is_empty(&rules) ? delete_flows__(ofproto, ofconn, request,
-                                                      &rules, OFPRR_DELETE)
-            : 0);
+    if (!error && rules.n > 0) {
+        error = delete_flows__(ofproto, ofconn, request, &rules, OFPRR_DELETE);
+    }
+    rule_collection_destroy(&rules);
+
+    return error;
 }
 
 /* Implements OFPFC_DELETE_STRICT. */
@@ -3838,7 +3874,7 @@ delete_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
                    const struct ofp_header *request)
 {
     struct rule_criteria criteria;
-    struct list rules;
+    struct rule_collection rules;
     enum ofperr error;
 
     rule_criteria_init(&criteria, fm->table_id, &fm->match, fm->priority,
@@ -3846,11 +3882,12 @@ delete_flow_strict(struct ofproto *ofproto, struct ofconn *ofconn,
     error = collect_rules_strict(ofproto, &criteria, &rules);
     rule_criteria_destroy(&criteria);
 
-    return (error ? error
-            : list_is_singleton(&rules) ? delete_flows__(ofproto, ofconn,
-                                                         request, &rules,
-                                                         OFPRR_DELETE)
-            : 0);
+    if (!error && rules.n > 0) {
+        error = delete_flows__(ofproto, ofconn, request, &rules, OFPRR_DELETE);
+    }
+    rule_collection_destroy(&rules);
+
+    return error;
 }
 
 static void
@@ -3898,7 +3935,7 @@ ofproto_rule_expire(struct rule *rule, uint8_t reason)
     ofproto_rule_send_removed(rule, reason);
 
     ovs_rwlock_wrlock(&cls->rwlock);
-    ofproto_delete_rule(ofproto, cls, rule);
+    ofproto_rule_delete(ofproto, cls, rule);
     ovs_rwlock_unlock(&cls->rwlock);
 }
 
@@ -4247,11 +4284,13 @@ ofproto_compose_flow_refresh_update(const struct rule *rule,
 }
 
 void
-ofmonitor_compose_refresh_updates(struct list *rules, struct list *msgs)
+ofmonitor_compose_refresh_updates(struct rule_collection *rules,
+                                  struct list *msgs)
 {
-    struct rule *rule;
+    size_t i;
 
-    LIST_FOR_EACH (rule, ofproto_node, rules) {
+    for (i = 0; i < rules->n; i++) {
+        struct rule *rule = rules->rules[i];
         enum nx_flow_monitor_flags flags = rule->monitor_flags;
         rule->monitor_flags = 0;
 
@@ -4262,7 +4301,7 @@ ofmonitor_compose_refresh_updates(struct list *rules, struct list *msgs)
 static void
 ofproto_collect_ofmonitor_refresh_rule(const struct ofmonitor *m,
                                        struct rule *rule, uint64_t seqno,
-                                       struct list *rules)
+                                       struct rule_collection *rules)
 {
     enum nx_flow_monitor_flags update;
 
@@ -4293,7 +4332,7 @@ ofproto_collect_ofmonitor_refresh_rule(const struct ofmonitor *m,
     }
 
     if (!rule->monitor_flags) {
-        list_push_back(rules, &rule->ofproto_node);
+        rule_collection_add(rules, rule);
     }
     rule->monitor_flags |= update | (m->flags & NXFMF_ACTIONS);
 }
@@ -4301,7 +4340,7 @@ ofproto_collect_ofmonitor_refresh_rule(const struct ofmonitor *m,
 static void
 ofproto_collect_ofmonitor_refresh_rules(const struct ofmonitor *m,
                                         uint64_t seqno,
-                                        struct list *rules)
+                                        struct rule_collection *rules)
 {
     const struct ofproto *ofproto = ofconn_get_ofproto(m->ofconn);
     const struct ofoperation *op;
@@ -4337,7 +4376,7 @@ ofproto_collect_ofmonitor_refresh_rules(const struct ofmonitor *m,
 
 static void
 ofproto_collect_ofmonitor_initial_rules(struct ofmonitor *m,
-                                        struct list *rules)
+                                        struct rule_collection *rules)
 {
     if (m->flags & NXFMF_INITIAL) {
         ofproto_collect_ofmonitor_refresh_rules(m, 0, rules);
@@ -4346,7 +4385,7 @@ ofproto_collect_ofmonitor_initial_rules(struct ofmonitor *m,
 
 void
 ofmonitor_collect_resume_rules(struct ofmonitor *m,
-                               uint64_t seqno, struct list *rules)
+                               uint64_t seqno, struct rule_collection *rules)
 {
     ofproto_collect_ofmonitor_refresh_rules(m, seqno, rules);
 }
@@ -4357,9 +4396,9 @@ handle_flow_monitor_request(struct ofconn *ofconn, const struct ofp_header *oh)
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
     struct ofmonitor **monitors;
     size_t n_monitors, allocated_monitors;
+    struct rule_collection rules;
     struct list replies;
     enum ofperr error;
-    struct list rules;
     struct ofpbuf b;
     size_t i;
 
@@ -4398,13 +4437,15 @@ handle_flow_monitor_request(struct ofconn *ofconn, const struct ofp_header *oh)
         monitors[n_monitors++] = m;
     }
 
-    list_init(&rules);
+    rule_collection_init(&rules);
     for (i = 0; i < n_monitors; i++) {
         ofproto_collect_ofmonitor_initial_rules(monitors[i], &rules);
     }
 
     ofpmp_init(&replies, oh);
     ofmonitor_compose_refresh_updates(&rules, &replies);
+    rule_collection_destroy(&rules);
+
     ofconn_send_replies(ofconn, &replies);
 
     free(monitors);
@@ -4563,8 +4604,9 @@ handle_delete_meter(struct ofconn *ofconn, const struct ofp_header *oh,
 {
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
     uint32_t meter_id = mm->meter.meter_id;
+    struct rule_collection rules;
+    enum ofperr error = 0;
     uint32_t first, last;
-    struct list rules;
 
     if (meter_id == OFPM13_ALL) {
         first = 1;
@@ -4578,7 +4620,7 @@ handle_delete_meter(struct ofconn *ofconn, const struct ofp_header *oh,
 
     /* First delete the rules that use this meter.  If any of those rules are
      * currently being modified, postpone the whole operation until later. */
-    list_init(&rules);
+    rule_collection_init(&rules);
     for (meter_id = first; meter_id <= last; ++meter_id) {
         struct meter *meter = ofproto->meters[meter_id];
         if (meter && !list_is_empty(&meter->rules)) {
@@ -4586,20 +4628,24 @@ handle_delete_meter(struct ofconn *ofconn, const struct ofp_header *oh,
 
             LIST_FOR_EACH (rule, meter_list_node, &meter->rules) {
                 if (rule->pending) {
-                    return OFPROTO_POSTPONE;
+                    error = OFPROTO_POSTPONE;
+                    goto exit;
                 }
-                list_push_back(&rules, &rule->ofproto_node);
+                rule_collection_add(&rules, rule);
             }
         }
     }
-    if (!list_is_empty(&rules)) {
+    if (rules.n > 0) {
         delete_flows__(ofproto, ofconn, oh, &rules, OFPRR_METER_DELETE);
     }
 
     /* Delete the meters. */
     meter_delete(ofproto, first, last);
 
-    return 0;
+exit:
+    rule_collection_destroy(&rules);
+
+    return error;
 }
 
 static enum ofperr