ofproto-dpif-xlate: Fix mac learning deadlock.
[sliver-openvswitch.git] / ofproto / ofproto-dpif-xlate.c
index 8be8088..ef254d9 100644 (file)
@@ -289,6 +289,12 @@ xlate_remove_ofproto(struct ofproto_dpif *ofproto)
     }
 
     hmap_remove(&xbridges, &xbridge->hmap_node);
+    mac_learning_unref(xbridge->ml);
+    mbridge_unref(xbridge->mbridge);
+    dpif_sflow_unref(xbridge->sflow);
+    dpif_ipfix_unref(xbridge->ipfix);
+    stp_unref(xbridge->stp);
+    hmap_destroy(&xbridge->xports);
     free(xbridge->name);
     free(xbridge);
 }
@@ -1218,6 +1224,7 @@ xlate_normal(struct xlate_ctx *ctx)
     struct xbundle *in_xbundle;
     struct xport *in_port;
     struct mac_entry *mac;
+    void *mac_port;
     uint16_t vlan;
     uint16_t vid;
 
@@ -1280,8 +1287,11 @@ xlate_normal(struct xlate_ctx *ctx)
     /* Determine output bundle. */
     ovs_rwlock_rdlock(&ctx->xbridge->ml->rwlock);
     mac = mac_learning_lookup(ctx->xbridge->ml, flow->dl_dst, vlan);
-    if (mac) {
-        struct xbundle *mac_xbundle = xbundle_lookup(mac->port.p);
+    mac_port = mac ? mac->port.p : NULL;
+    ovs_rwlock_unlock(&ctx->xbridge->ml->rwlock);
+
+    if (mac_port) {
+        struct xbundle *mac_xbundle = xbundle_lookup(mac_port);
         if (mac_xbundle && mac_xbundle != in_xbundle) {
             xlate_report(ctx, "forwarding to learned port");
             output_normal(ctx, mac_xbundle, vlan);
@@ -1304,7 +1314,6 @@ xlate_normal(struct xlate_ctx *ctx)
         }
         ctx->xout->nf_output_iface = NF_OUT_FLOOD;
     }
-    ovs_rwlock_unlock(&ctx->xbridge->ml->rwlock);
 }
 
 /* Compose SAMPLE action for sFlow or IPFIX.  The given probability is
@@ -1657,6 +1666,25 @@ compose_output_action(struct xlate_ctx *ctx, ofp_port_t ofp_port)
     compose_output_action__(ctx, ofp_port, true);
 }
 
+static void
+xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule)
+    OVS_RELEASES(rule->up.evict)
+{
+    struct rule_dpif *old_rule = ctx->rule;
+
+    if (ctx->xin->resubmit_stats) {
+        rule_credit_stats(rule, ctx->xin->resubmit_stats);
+    }
+
+    ctx->recurse++;
+    ctx->rule = rule;
+    do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
+    ctx->rule = old_rule;
+    ctx->recurse--;
+
+    rule_release(rule);
+}
+
 static void
 xlate_table_action(struct xlate_ctx *ctx,
                    ofp_port_t in_port, uint8_t table_id, bool may_packet_in)
@@ -1665,28 +1693,28 @@ xlate_table_action(struct xlate_ctx *ctx,
         struct rule_dpif *rule;
         ofp_port_t old_in_port = ctx->xin->flow.in_port.ofp_port;
         uint8_t old_table_id = ctx->table_id;
+        bool got_rule;
 
         ctx->table_id = table_id;
 
-        /* Look up a flow with 'in_port' as the input port. */
+        /* Look up a flow with 'in_port' as the input port.  Then restore the
+         * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
+         * have surprising behavior). */
         ctx->xin->flow.in_port.ofp_port = in_port;
-        rule_dpif_lookup_in_table(ctx->xbridge->ofproto, &ctx->xin->flow,
-                                  &ctx->xout->wc, table_id, &rule);
-
-        /* Restore the original input port.  Otherwise OFPP_NORMAL and
-         * OFPP_IN_PORT will have surprising behavior. */
+        got_rule = rule_dpif_lookup_in_table(ctx->xbridge->ofproto,
+                                             &ctx->xin->flow, &ctx->xout->wc,
+                                             table_id, &rule);
         ctx->xin->flow.in_port.ofp_port = old_in_port;
 
         if (ctx->xin->resubmit_hook) {
             ctx->xin->resubmit_hook(ctx->xin, rule, ctx->recurse);
         }
 
-        if (rule == NULL && may_packet_in) {
+        if (got_rule) {
+            xlate_recursively(ctx, rule);
+        } else if (may_packet_in) {
             struct xport *xport;
 
-            /* Makes clang's thread safety analysis happy. */
-            rule_release(rule);
-
             /* XXX
              * check if table configuration flags
              * OFPTC_TABLE_MISS_CONTROLLER, default.
@@ -1698,23 +1726,9 @@ xlate_table_action(struct xlate_ctx *ctx,
                                     ctx->xbridge->miss_rule,
                                     ctx->xbridge->no_packet_in_rule);
             ovs_rwlock_rdlock(&rule->up.evict);
+            xlate_recursively(ctx, rule);
         }
 
-        if (rule && ctx->xin->resubmit_stats) {
-            rule_credit_stats(rule, ctx->xin->resubmit_stats);
-        }
-
-        if (rule) {
-            struct rule_dpif *old_rule = ctx->rule;
-
-            ctx->recurse++;
-            ctx->rule = rule;
-            do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
-            ctx->rule = old_rule;
-            ctx->recurse--;
-        }
-        rule_release(rule);
-
         ctx->table_id = old_table_id;
     } else {
         static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
@@ -2100,33 +2114,13 @@ xlate_learn_action(struct xlate_ctx *ctx,
     ofproto_dpif_flow_mod(ctx->xbridge->ofproto, fm);
 }
 
-/* Reduces '*timeout' to no more than 'max'.  A value of zero in either case
- * means "infinite". */
-static void
-reduce_timeout(uint16_t max, uint16_t *timeout)
-{
-    if (max && (!*timeout || *timeout > max)) {
-        *timeout = max;
-    }
-}
-
 static void
 xlate_fin_timeout(struct xlate_ctx *ctx,
                   const struct ofpact_fin_timeout *oft)
 {
     if (ctx->xin->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
-        struct rule_dpif *rule = ctx->rule;
-
-        ovs_mutex_lock(&rule->up.ofproto->expirable_mutex);
-        if (list_is_empty(&rule->up.expirable)) {
-            list_insert(&rule->up.ofproto->expirable, &rule->up.expirable);
-        }
-        ovs_mutex_unlock(&rule->up.ofproto->expirable_mutex);
-
-        ovs_mutex_lock(&rule->up.timeout_mutex);
-        reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout);
-        reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout);
-        ovs_mutex_unlock(&rule->up.timeout_mutex);
+        ofproto_rule_reduce_timeouts(&ctx->rule->up, oft->fin_idle_timeout,
+                                     oft->fin_hard_timeout);
     }
 }
 
@@ -2190,6 +2184,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
                                 ofpact_get_OUTPUT(a)->max_len, true);
             break;
 
+        case OFPACT_GROUP:
+            /* XXX not yet implemented */
+            break;
+
         case OFPACT_CONTROLLER:
             controller = ofpact_get_CONTROLLER(a);
             execute_controller_action(ctx, controller->max_len,
@@ -2518,7 +2516,10 @@ actions_output_to_local_port(const struct xlate_ctx *ctx)
 }
 
 /* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts'
- * into datapath actions in 'odp_actions', using 'ctx'. */
+ * into datapath actions in 'odp_actions', using 'ctx'.
+ *
+ * The caller must take responsibility for eventually freeing 'xout', with
+ * xlate_out_uninit(). */
 void
 xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
 {