ofproto-dpif: Introduce multicast snooping handler
[cascardo/ovs.git] / ofproto / ofproto-dpif.c
index 45c1393..27233cb 100644 (file)
@@ -37,6 +37,7 @@
 #include "lacp.h"
 #include "learn.h"
 #include "mac-learning.h"
+#include "mcast-snooping.h"
 #include "meta-flow.h"
 #include "multipath.h"
 #include "netdev-vport.h"
@@ -73,9 +74,6 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif);
 COVERAGE_DEFINE(ofproto_dpif_expired);
 COVERAGE_DEFINE(packet_in_overflow);
 
-/* No bfd/cfm status change. */
-#define NO_STATUS_CHANGE -1
-
 struct flow_miss;
 
 struct rule_dpif {
@@ -87,6 +85,12 @@ struct rule_dpif {
      *   recently been processed by a revalidator. */
     struct ovs_mutex stats_mutex;
     struct dpif_flow_stats stats OVS_GUARDED;
+
+    /* If non-zero then the recirculation id that has
+     * been allocated for use with this rule.
+     * The recirculation id and associated internal flow should
+     * be freed when the rule is freed */
+    uint32_t recirc_id;
 };
 
 /* RULE_CAST() depends on this. */
@@ -224,6 +228,7 @@ enum revalidate_reason {
     REV_PORT_TOGGLED,          /* Port enabled or disabled by CFM, LACP, ...*/
     REV_FLOW_TABLE,            /* Flow table changed. */
     REV_MAC_LEARNING,          /* Mac learning changed. */
+    REV_MCAST_SNOOPING,        /* Multicast snooping changed. */
 };
 COVERAGE_DEFINE(rev_reconfigure);
 COVERAGE_DEFINE(rev_stp);
@@ -231,6 +236,7 @@ COVERAGE_DEFINE(rev_bond);
 COVERAGE_DEFINE(rev_port_toggled);
 COVERAGE_DEFINE(rev_flow_table);
 COVERAGE_DEFINE(rev_mac_learning);
+COVERAGE_DEFINE(rev_mcast_snooping);
 
 /* All datapaths of a given type share a single dpif backer instance. */
 struct dpif_backer {
@@ -283,6 +289,7 @@ struct ofproto_dpif {
     struct dpif_ipfix *ipfix;
     struct hmap bundles;        /* Contains "struct ofbundle"s. */
     struct mac_learning *ml;
+    struct mcast_snooping *ms;
     bool has_bonded_bundles;
     bool lacp_enabled;
     struct mbridge *mbridge;
@@ -571,6 +578,7 @@ type_run(const char *type)
         case REV_PORT_TOGGLED:   COVERAGE_INC(rev_port_toggled);   break;
         case REV_FLOW_TABLE:     COVERAGE_INC(rev_flow_table);     break;
         case REV_MAC_LEARNING:   COVERAGE_INC(rev_mac_learning);   break;
+        case REV_MCAST_SNOOPING: COVERAGE_INC(rev_mcast_snooping); break;
         }
         backer->need_revalidate = 0;
 
@@ -586,7 +594,7 @@ type_run(const char *type)
             xlate_ofproto_set(ofproto, ofproto->up.name,
                               ofproto->backer->dpif, ofproto->miss_rule,
                               ofproto->no_packet_in_rule, ofproto->ml,
-                              ofproto->stp, ofproto->mbridge,
+                              ofproto->stp, ofproto->ms, ofproto->mbridge,
                               ofproto->sflow, ofproto->ipfix,
                               ofproto->netflow, ofproto->up.frag_handling,
                               ofproto->up.forward_bpdu,
@@ -1127,6 +1135,7 @@ construct(struct ofproto *ofproto_)
     ofproto->dump_seq = 0;
     hmap_init(&ofproto->bundles);
     ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME);
+    ofproto->ms = NULL;
     ofproto->mbridge = mbridge_create();
     ofproto->has_bonded_bundles = false;
     ofproto->lacp_enabled = false;
@@ -1188,7 +1197,8 @@ add_internal_miss_flow(struct ofproto_dpif *ofproto, int id,
     match_init_catchall(&match);
     match_set_reg(&match, 0, id);
 
-    error = ofproto_dpif_add_internal_flow(ofproto, &match, 0, ofpacts, &rule);
+    error = ofproto_dpif_add_internal_flow(ofproto, &match, 0, 0, ofpacts,
+                                           &rule);
     *rulep = error ? NULL : rule_dpif_cast(rule);
 
     return error;
@@ -1246,7 +1256,7 @@ add_internal_flows(struct ofproto_dpif *ofproto)
     match_init_catchall(&match);
     match_set_recirc_id(&match, 0);
 
-    error = ofproto_dpif_add_internal_flow(ofproto, &match, 2, &ofpacts,
+    error = ofproto_dpif_add_internal_flow(ofproto, &match, 2, 0, &ofpacts,
                                            &unused_rulep);
     if (error) {
         return error;
@@ -1259,7 +1269,7 @@ add_internal_flows(struct ofproto_dpif *ofproto)
      */
     ofpbuf_clear(&ofpacts);
     match_init_catchall(&match);
-    error = ofproto_dpif_add_internal_flow(ofproto, &match, 1, &ofpacts,
+    error = ofproto_dpif_add_internal_flow(ofproto, &match, 1, 0, &ofpacts,
                                            &unused_rulep);
 
     return error;
@@ -1310,6 +1320,7 @@ destruct(struct ofproto *ofproto_)
     dpif_sflow_unref(ofproto->sflow);
     hmap_destroy(&ofproto->bundles);
     mac_learning_unref(ofproto->ml);
+    mcast_snooping_unref(ofproto->ms);
 
     hmap_destroy(&ofproto->vlandev_map);
     hmap_destroy(&ofproto->realdev_vid_map);
@@ -1337,6 +1348,7 @@ run(struct ofproto *ofproto_)
         ovs_rwlock_wrlock(&ofproto->ml->rwlock);
         mac_learning_flush(ofproto->ml);
         ovs_rwlock_unlock(&ofproto->ml->rwlock);
+        mcast_snooping_mdb_flush(ofproto->ms);
     }
 
     /* Always updates the ofproto->pins_seqno to avoid frequent wakeup during
@@ -1395,6 +1407,10 @@ run(struct ofproto *ofproto_)
     }
     ovs_rwlock_unlock(&ofproto->ml->rwlock);
 
+    if (mcast_snooping_run(ofproto->ms)) {
+        ofproto->backer->need_revalidate = REV_MCAST_SNOOPING;
+    }
+
     new_dump_seq = seq_read(udpif_dump_seq(ofproto->backer->udpif));
     if (ofproto->dump_seq != new_dump_seq) {
         struct rule *rule, *next_rule;
@@ -1456,6 +1472,7 @@ wait(struct ofproto *ofproto_)
     ovs_rwlock_rdlock(&ofproto->ml->rwlock);
     mac_learning_wait(ofproto->ml);
     ovs_rwlock_unlock(&ofproto->ml->rwlock);
+    mcast_snooping_wait(ofproto->ms);
     stp_wait(ofproto);
     if (ofproto->backer->need_revalidate) {
         /* Shouldn't happen, but if it does just go around again. */
@@ -1802,23 +1819,23 @@ out:
     return error;
 }
 
+static bool
+cfm_status_changed(struct ofport *ofport_)
+{
+    struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+
+    return ofport->cfm ? cfm_check_status_change(ofport->cfm) : true;
+}
+
 static int
 get_cfm_status(const struct ofport *ofport_,
-               struct ofproto_cfm_status *status)
+               struct cfm_status *status)
 {
     struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
     int ret = 0;
 
     if (ofport->cfm) {
-        if (cfm_check_status_change(ofport->cfm)) {
-            status->faults = cfm_get_fault(ofport->cfm);
-            status->flap_count = cfm_get_flap_count(ofport->cfm);
-            status->remote_opstate = cfm_get_opup(ofport->cfm);
-            status->health = cfm_get_health(ofport->cfm);
-            cfm_get_remote_mpids(ofport->cfm, &status->rmps, &status->n_rmps);
-        } else {
-            ret = NO_STATUS_CHANGE;
-        }
+        cfm_get_status(ofport->cfm, status);
     } else {
         ret = ENOENT;
     }
@@ -1844,6 +1861,14 @@ set_bfd(struct ofport *ofport_, const struct smap *cfg)
     return 0;
 }
 
+static bool
+bfd_status_changed(struct ofport *ofport_)
+{
+    struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+
+    return ofport->bfd ? bfd_check_status_change(ofport->bfd) : true;
+}
+
 static int
 get_bfd_status(struct ofport *ofport_, struct smap *smap)
 {
@@ -1851,11 +1876,7 @@ get_bfd_status(struct ofport *ofport_, struct smap *smap)
     int ret = 0;
 
     if (ofport->bfd) {
-        if (bfd_check_status_change(ofport->bfd)) {
-            bfd_get_status(ofport->bfd, smap);
-        } else {
-            ret = NO_STATUS_CHANGE;
-        }
+        bfd_get_status(ofport->bfd, smap);
     } else {
         ret = ENOENT;
     }
@@ -1969,6 +1990,7 @@ update_stp_port_state(struct ofport_dpif *ofport)
             ovs_rwlock_wrlock(&ofproto->ml->rwlock);
             mac_learning_flush(ofproto->ml);
             ovs_rwlock_unlock(&ofproto->ml->rwlock);
+            mcast_snooping_mdb_flush(ofproto->ms);
         }
         fwd_change = stp_forward_in_state(ofport->stp_state)
                         != stp_forward_in_state(state);
@@ -2094,6 +2116,7 @@ stp_run(struct ofproto_dpif *ofproto)
             ovs_rwlock_wrlock(&ofproto->ml->rwlock);
             mac_learning_flush(ofproto->ml);
             ovs_rwlock_unlock(&ofproto->ml->rwlock);
+            mcast_snooping_mdb_flush(ofproto->ms);
         }
     }
 }
@@ -3046,8 +3069,6 @@ rule_expire(struct rule_dpif *rule)
     long long int now = time_msec();
     int reason = -1;
 
-    ovs_assert(!rule->up.pending);
-
     hard_timeout = rule->up.hard_timeout;
     idle_timeout = rule->up.idle_timeout;
 
@@ -3166,6 +3187,39 @@ rule_dpif_get_actions(const struct rule_dpif *rule)
     return rule_get_actions(&rule->up);
 }
 
+/* Sets 'rule''s recirculation id. */
+static void
+rule_dpif_set_recirc_id(struct rule_dpif *rule, uint32_t id)
+    OVS_REQUIRES(rule->up.mutex)
+{
+    ovs_assert(!rule->recirc_id);
+    rule->recirc_id = id;
+}
+
+/* Returns 'rule''s recirculation id. */
+uint32_t
+rule_dpif_get_recirc_id(struct rule_dpif *rule)
+    OVS_REQUIRES(rule->up.mutex)
+{
+    if (!rule->recirc_id) {
+        struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+
+        rule_dpif_set_recirc_id(rule, ofproto_dpif_alloc_recirc_id(ofproto));
+    }
+    return rule->recirc_id;
+}
+
+/* Sets 'rule''s recirculation id. */
+void
+rule_set_recirc_id(struct rule *rule_, uint32_t id)
+{
+    struct rule_dpif *rule = rule_dpif_cast(rule_);
+
+    ovs_mutex_lock(&rule->up.mutex);
+    rule_dpif_set_recirc_id(rule, id);
+    ovs_mutex_unlock(&rule->up.mutex);
+}
+
 /* Lookup 'flow' in table 0 of 'ofproto''s classifier.
  * If 'wc' is non-null, sets the fields that were relevant as part of
  * the lookup. Returns the table_id where a match or miss occurred.
@@ -3397,7 +3451,6 @@ complete_operation(struct rule_dpif *rule)
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
 
     ofproto->backer->need_revalidate = REV_FLOW_TABLE;
-    ofoperation_complete(rule->up.pending, 0);
 }
 
 static struct rule_dpif *rule_dpif_cast(const struct rule *rule)
@@ -3428,15 +3481,18 @@ rule_construct(struct rule *rule_)
     rule->stats.n_packets = 0;
     rule->stats.n_bytes = 0;
     rule->stats.used = rule->up.modified;
+    rule->recirc_id = 0;
+
     return 0;
 }
 
-static void
+static enum ofperr
 rule_insert(struct rule *rule_)
     OVS_REQUIRES(ofproto_mutex)
 {
     struct rule_dpif *rule = rule_dpif_cast(rule_);
     complete_operation(rule);
+    return 0;
 }
 
 static void
@@ -3451,7 +3507,13 @@ static void
 rule_destruct(struct rule *rule_)
 {
     struct rule_dpif *rule = rule_dpif_cast(rule_);
+
     ovs_mutex_destroy(&rule->stats_mutex);
+    if (rule->recirc_id) {
+        struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+
+        ofproto_dpif_free_recirc_id(ofproto, rule->recirc_id);
+    }
 }
 
 static void
@@ -4785,6 +4847,7 @@ ofproto_dpif_free_recirc_id(struct ofproto_dpif *ofproto, uint32_t recirc_id)
 int
 ofproto_dpif_add_internal_flow(struct ofproto_dpif *ofproto,
                                const struct match *match, int priority,
+                               uint16_t idle_timeout,
                                const struct ofpbuf *ofpacts,
                                struct rule **rulep)
 {
@@ -4800,7 +4863,7 @@ ofproto_dpif_add_internal_flow(struct ofproto_dpif *ofproto,
     fm.modify_cookie = false;
     fm.table_id = TBL_INTERNAL;
     fm.command = OFPFC_ADD;
-    fm.idle_timeout = 0;
+    fm.idle_timeout = idle_timeout;
     fm.hard_timeout = 0;
     fm.buffer_id = 0;
     fm.out_port = 0;
@@ -4897,6 +4960,7 @@ const struct ofproto_class ofproto_dpif_class = {
     rule_dealloc,
     rule_get_stats,
     rule_execute,
+    NULL,                       /* rule_premodify_actions */
     rule_modify_actions,
     set_frag_handling,
     packet_out,
@@ -4905,8 +4969,10 @@ const struct ofproto_class ofproto_dpif_class = {
     set_sflow,
     set_ipfix,
     set_cfm,
+    cfm_status_changed,
     get_cfm_status,
     set_bfd,
+    bfd_status_changed,
     get_bfd_status,
     set_stp,
     get_stp_status,