[ovs-dev] [PATCH v5 3/4] ofproto: Generalize deferred work execution.

Jarno Rajahalme jrajahalme at nicira.com
Fri Jun 12 21:36:23 UTC 2015


Previous patch adds more work being executed from the ovs-rcu thread.
Executing big chunks of work from ovs-rcu thread should be avoided,
however, as the rcu callback facility is shared resource used by all
threads.

This patch generalizes the exising "rule_executes" list to be useful
for also other kinds of work items.  First new use is for sending flow
removed messages from the main thread instead of the ovs-rcu thread.

Even more work could be executed outside of the ovs-rcu thread if the
size of the 'deferred_work' list was made unlimited.

Signed-off-by: Jarno Rajahalme <jrajahalme at nicira.com>
---
 ofproto/ofproto-provider.h |   10 +--
 ofproto/ofproto.c          |  195 +++++++++++++++++++++++++++++---------------
 2 files changed, 134 insertions(+), 71 deletions(-)

diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 0d25f68..4f08888 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -113,13 +113,15 @@ struct ofproto {
     /* OpenFlow connections. */
     struct connmgr *connmgr;
 
-    /* Delayed rule executions.
+    /* Delayed work.
      *
      * We delay calls to ->ofproto_class->rule_execute() past releasing
      * ofproto_mutex during a flow_mod, because otherwise a "learn" action
      * triggered by the executing the packet would try to recursively modify
-     * the flow table and reacquire the global lock. */
-    struct guarded_list rule_executes; /* Contains "struct rule_execute"s. */
+     * the flow table and reacquire the global lock.
+     */
+    struct guarded_list deferred_work; /* Contains
+                                        * "struct deferred_work_item"s. */
 
     /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
      *
@@ -433,8 +435,6 @@ struct rule_actions {
 };
 BUILD_ASSERT_DECL(offsetof(struct rule_actions, ofpacts) % OFPACT_ALIGNTO == 0);
 
-const struct rule_actions *rule_actions_create(const struct ofpact *, size_t);
-void rule_actions_destroy(const struct rule_actions *);
 bool ofproto_rule_has_out_port(const struct rule *, ofp_port_t port)
     OVS_REQUIRES(ofproto_mutex);
 
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index cf5fa34..7a807bf 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -160,20 +160,56 @@ static void rule_criteria_destroy(struct rule_criteria *);
 static enum ofperr collect_rules_loose(struct ofproto *,
                                        const struct rule_criteria *,
                                        struct rule_collection *);
+
+/* Ofproto deferred work queue. */
+
+enum deferred_work_type {
+    DEFERRED_RULE_EXECUTE,
+    DEFERRED_SEND_REMOVED
+};
 
 /* A packet that needs to be passed to rule_execute().
  *
  * (We can't do this immediately from ofopgroup_complete() because that holds
  * ofproto_mutex, which rule_execute() needs released.) */
 struct rule_execute {
-    struct ovs_list list_node;  /* In struct ofproto's "rule_executes" list. */
     struct rule *rule;          /* Owns a reference to the rule. */
     ofp_port_t in_port;
     struct dp_packet *packet;      /* Owns the packet. */
 };
 
-static void run_rule_executes(struct ofproto *) OVS_EXCLUDED(ofproto_mutex);
-static void destroy_rule_executes(struct ofproto *);
+struct deferred_work_item {
+    struct ovs_list list_node;  /* In struct ofproto's "deferred_work" list. */
+    enum deferred_work_type type;
+    union {
+        struct rule_execute re;
+        struct ofputil_flow_removed fr;
+    };
+};
+
+/* The sizes of the deferred work items vary a lot, so we use this to calculate
+ * the exact size needed. */
+static inline size_t
+deferred_work_size(enum deferred_work_type type OVS_UNUSED)
+{
+    return offsetof(struct deferred_work_item, re)
+        + (type == DEFERRED_RULE_EXECUTE ? sizeof(struct rule_execute)
+           : type == DEFERRED_SEND_REMOVED ? sizeof(struct ofputil_flow_removed)
+           : 0);
+}
+
+static struct deferred_work_item *
+deferred_work_alloc(enum deferred_work_type type)
+{
+    struct deferred_work_item *dwi = xmalloc(deferred_work_size(type));
+
+    dwi->type = type;
+
+    return dwi;
+}
+
+static void run_deferred_work(struct ofproto *) OVS_EXCLUDED(ofproto_mutex);
+static void destroy_deferred_work(struct ofproto *);
 
 struct learned_cookie {
     union {
@@ -232,7 +268,7 @@ struct ofport_usage {
 };
 
 /* rule. */
-static void ofproto_rule_send_removed(struct rule *)
+static void ofproto_schedule_rule_send_removed(struct rule *)
         OVS_EXCLUDED(ofproto_mutex);
 static bool rule_is_readonly(const struct rule *);
 static void ofproto_rule_insert__(struct ofproto *, struct rule *)
@@ -344,6 +380,9 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 /* The default value of true waits for flow restore. */
 static bool flow_restore_wait = true;
 
+static const struct rule_actions *
+rule_actions_create(const struct ofpact *ofpacts, size_t ofpacts_len);
+
 /* Must be called to initialize the ofproto library.
  *
  * The caller may pass in 'iface_hints', which contains an shash of
@@ -552,7 +591,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     hmap_init(&ofproto->learned_cookies);
     list_init(&ofproto->expirable);
     ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
-    guarded_list_init(&ofproto->rule_executes);
+    guarded_list_init(&ofproto->deferred_work);
     ofproto->vlan_bitmap = NULL;
     ofproto->vlans_changed = false;
     ofproto->min_mtu = INT_MAX;
@@ -1529,10 +1568,10 @@ ofproto_destroy__(struct ofproto *ofproto)
 {
     struct oftable *table;
 
-    destroy_rule_executes(ofproto);
+    destroy_deferred_work(ofproto);
     delete_group(ofproto, OFPG_ALL);
 
-    guarded_list_destroy(&ofproto->rule_executes);
+    guarded_list_destroy(&ofproto->deferred_work);
     ovs_rwlock_destroy(&ofproto->groups_rwlock);
     hmap_destroy(&ofproto->groups);
 
@@ -1681,7 +1720,7 @@ ofproto_run(struct ofproto *p)
         VLOG_ERR_RL(&rl, "%s: run failed (%s)", p->name, ovs_strerror(error));
     }
 
-    run_rule_executes(p);
+    run_deferred_work(p);
 
     /* Restore the eviction group heap invariant occasionally. */
     if (p->eviction_group_timer < time_msec()) {
@@ -2752,7 +2791,7 @@ ofproto_rule_destroy__(struct rule *rule)
     OVS_NO_THREAD_SAFETY_ANALYSIS
 {
     cls_rule_destroy(CONST_CAST(struct cls_rule *, &rule->cr));
-    rule_actions_destroy(rule_get_actions(rule));
+    free(CONST_CAST(struct rule_actions *, rule->actions));
     ovs_mutex_destroy(&rule->mutex);
     rule->ofproto->ofproto_class->rule_dealloc(rule);
 }
@@ -2765,7 +2804,7 @@ rule_destroy_cb(struct rule *rule)
     if (rule->flags & OFPUTIL_FF_SEND_FLOW_REM
         && rule->removed_reason != OVS_OFPRR_NONE
         && !rule_is_hidden(rule)) {
-        ofproto_rule_send_removed(rule);
+        ofproto_schedule_rule_send_removed(rule);
     }
     rule->ofproto->ofproto_class->rule_destruct(rule);
     ofproto_rule_destroy__(rule);
@@ -2889,7 +2928,7 @@ static uint32_t get_provider_meter_id(const struct ofproto *,
 
 /* Creates and returns a new 'struct rule_actions', whose actions are a copy
  * of from the 'ofpacts_len' bytes of 'ofpacts'. */
-const struct rule_actions *
+static const struct rule_actions *
 rule_actions_create(const struct ofpact *ofpacts, size_t ofpacts_len)
 {
     struct rule_actions *actions;
@@ -2905,15 +2944,6 @@ rule_actions_create(const struct ofpact *ofpacts, size_t ofpacts_len)
     return actions;
 }
 
-/* Free the actions after the RCU quiescent period is reached. */
-void
-rule_actions_destroy(const struct rule_actions *actions)
-{
-    if (actions) {
-        ovsrcu_postpone(free, CONST_CAST(struct rule_actions *, actions));
-    }
-}
-
 /* 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
@@ -2944,46 +2974,71 @@ ofproto_rule_has_out_group(const struct rule *rule, uint32_t group_id)
 }
 
 static void
-rule_execute_destroy(struct rule_execute *e)
+deferred_work_destroy(struct deferred_work_item *dwi)
 {
-    ofproto_rule_unref(e->rule);
-    list_remove(&e->list_node);
-    free(e);
+    switch (dwi->type) {
+    case DEFERRED_RULE_EXECUTE:
+        ofproto_rule_unref(dwi->re.rule);
+        break;
+
+    case DEFERRED_SEND_REMOVED:
+        break;
+    }
+    list_remove(&dwi->list_node);
+    free(dwi);
 }
 
-/* Executes all "rule_execute" operations queued up in ofproto->rule_executes,
+/* Executes all "rule_execute" operations queued up in ofproto->deferred_work,
  * by passing them to the ofproto provider. */
 static void
-run_rule_executes(struct ofproto *ofproto)
+run_deferred_work(struct ofproto *ofproto)
     OVS_EXCLUDED(ofproto_mutex)
 {
-    struct rule_execute *e, *next;
-    struct ovs_list executes;
+    struct deferred_work_item *e, *next;
+    struct ovs_list deferred_work;
 
-    guarded_list_pop_all(&ofproto->rule_executes, &executes);
-    LIST_FOR_EACH_SAFE (e, next, list_node, &executes) {
-        struct flow flow;
+    guarded_list_pop_all(&ofproto->deferred_work, &deferred_work);
+    LIST_FOR_EACH_SAFE (e, next, list_node, &deferred_work) {
+        switch (e->type) {
+        case DEFERRED_RULE_EXECUTE: {
+            struct flow flow;
 
-        flow_extract(e->packet, &flow);
-        flow.in_port.ofp_port = e->in_port;
-        ofproto->ofproto_class->rule_execute(e->rule, &flow, e->packet);
+            flow_extract(e->re.packet, &flow);
+            flow.in_port.ofp_port = e->re.in_port;
+            ofproto->ofproto_class->rule_execute(e->re.rule, &flow,
+                                                 e->re.packet);
+            break;
+        }
+        case DEFERRED_SEND_REMOVED:
+            /* XXX: Not sure if the mutex is needed here or not. */
+            ovs_mutex_lock(&ofproto_mutex);
+            connmgr_send_flow_removed(ofproto->connmgr, &e->fr);
+            ovs_mutex_unlock(&ofproto_mutex);
+            break;
+        }
 
-        rule_execute_destroy(e);
+        deferred_work_destroy(e);
     }
 }
 
 /* Destroys and discards all "rule_execute" operations queued up in
- * ofproto->rule_executes. */
+ * ofproto->deferred_work. */
 static void
-destroy_rule_executes(struct ofproto *ofproto)
+destroy_deferred_work(struct ofproto *ofproto)
 {
-    struct rule_execute *e, *next;
-    struct ovs_list executes;
+    struct deferred_work_item *e, *next;
+    struct ovs_list deferred_work;
 
-    guarded_list_pop_all(&ofproto->rule_executes, &executes);
-    LIST_FOR_EACH_SAFE (e, next, list_node, &executes) {
-        dp_packet_delete(e->packet);
-        rule_execute_destroy(e);
+    guarded_list_pop_all(&ofproto->deferred_work, &deferred_work);
+    LIST_FOR_EACH_SAFE (e, next, list_node, &deferred_work) {
+        switch (e->type) {
+        case DEFERRED_RULE_EXECUTE:
+            dp_packet_delete(e->re.packet);
+            break;
+        case DEFERRED_SEND_REMOVED:
+            break;
+        }
+        deferred_work_destroy(e);
     }
 }
 
@@ -5083,29 +5138,35 @@ delete_flow_start_strict(struct ofproto *ofproto,
 
 /* This may only be called by rule_destroy_cb()! */
 static void
-ofproto_rule_send_removed(struct rule *rule)
+ofproto_schedule_rule_send_removed(struct rule *rule)
     OVS_EXCLUDED(ofproto_mutex)
 {
-    struct ofputil_flow_removed fr;
+    struct deferred_work_item *dwi;
+    struct ofputil_flow_removed *fr;
     long long int used;
 
-    minimatch_expand(&rule->cr.match, &fr.match);
-    fr.priority = rule->cr.priority;
+    dwi = deferred_work_alloc(DEFERRED_SEND_REMOVED);
+    fr = &dwi->fr;
+
+    minimatch_expand(&rule->cr.match, &fr->match);
+    fr->priority = rule->cr.priority;
 
-    ovs_mutex_lock(&ofproto_mutex);
-    fr.cookie = rule->flow_cookie;
-    fr.reason = rule->removed_reason;
-    fr.table_id = rule->table_id;
-    calc_duration(rule->created, time_msec(),
-                  &fr.duration_sec, &fr.duration_nsec);
     ovs_mutex_lock(&rule->mutex);
-    fr.idle_timeout = rule->idle_timeout;
-    fr.hard_timeout = rule->hard_timeout;
+    fr->cookie = rule->flow_cookie;
+    fr->reason = rule->removed_reason;
+    fr->table_id = rule->table_id;
+    calc_duration(rule->created, time_msec(),
+                  &fr->duration_sec, &fr->duration_nsec);
+    fr->idle_timeout = rule->idle_timeout;
+    fr->hard_timeout = rule->hard_timeout;
     ovs_mutex_unlock(&rule->mutex);
-    rule->ofproto->ofproto_class->rule_get_stats(rule, &fr.packet_count,
-                                                 &fr.byte_count, &used);
-    connmgr_send_flow_removed(rule->ofproto->connmgr, &fr);
-    ovs_mutex_unlock(&ofproto_mutex);
+
+    rule->ofproto->ofproto_class->rule_get_stats(rule, &fr->packet_count,
+                                                 &fr->byte_count, &used);
+    if (!guarded_list_push_back(&rule->ofproto->deferred_work,
+                                &dwi->list_node, 1024)) {
+        free(dwi);
+    }
 }
 
 /* Sends an OpenFlow "flow removed" message with the given 'reason' (either
@@ -5221,7 +5282,7 @@ handle_flow_mod__(struct ofproto *ofproto, struct ofputil_flow_mod *fm,
     ofmonitor_flush(ofproto->connmgr);
     ovs_mutex_unlock(&ofproto_mutex);
 
-    run_rule_executes(ofproto);
+    run_deferred_work(ofproto);
     return error;
 }
 
@@ -6741,7 +6802,7 @@ do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags)
         ofmonitor_flush(ofproto->connmgr);
         ovs_mutex_unlock(&ofproto_mutex);
 
-        run_rule_executes(ofproto);
+        run_deferred_work(ofproto);
     }
 
     /* The bundle is discarded regardless the outcome. */
@@ -7065,20 +7126,22 @@ send_buffered_packet(const struct flow_mod_requester *req, uint32_t buffer_id,
         error = ofconn_pktbuf_retrieve(req->ofconn, buffer_id, &packet,
                                        &in_port);
         if (packet) {
+            struct deferred_work_item *dwi;
             struct rule_execute *re;
 
-            ofproto_rule_ref(rule);
+            dwi = deferred_work_alloc(DEFERRED_RULE_EXECUTE);
+            re = &dwi->re;
 
-            re = xmalloc(sizeof *re);
+            ofproto_rule_ref(rule);
             re->rule = rule;
             re->in_port = in_port;
             re->packet = packet;
 
-            if (!guarded_list_push_back(&ofproto->rule_executes,
-                                        &re->list_node, 1024)) {
+            if (!guarded_list_push_back(&ofproto->deferred_work,
+                                        &dwi->list_node, 1024)) {
                 ofproto_rule_unref(rule);
                 dp_packet_delete(re->packet);
-                free(re);
+                free(dwi);
             }
         } else {
             ofconn_send_error(req->ofconn, req->request, error);
-- 
1.7.10.4




More information about the dev mailing list