+ /* Every second, decrease the values of the timers. */
+ if (elapsed >= 1000) {
+ rstp_tick_timers(ofproto->rstp);
+ ofproto->rstp_last_tick = now;
+ }
+ rp = NULL;
+ while ((ofport = rstp_get_next_changed_port_aux(ofproto->rstp, &rp))) {
+ update_rstp_port_state(ofport);
+ }
+ /* FIXME: This check should be done on-event (i.e., when setting
+ * p->fdb_flush) and not periodically.
+ */
+ if (rstp_check_and_reset_fdb_flush(ofproto->rstp)) {
+ ovs_rwlock_wrlock(&ofproto->ml->rwlock);
+ /* FIXME: RSTP should be able to flush the entries pertaining to a
+ * single port, not the whole table.
+ */
+ mac_learning_flush(ofproto->ml);
+ ovs_rwlock_unlock(&ofproto->ml->rwlock);
+ }
+ }
+}
+
+/* Configures STP on 'ofproto_' using the settings defined in 's'. */
+static int
+set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ /* Only revalidate flows if the configuration changed. */
+ if (!s != !ofproto->stp) {
+ ofproto->backer->need_revalidate = REV_RECONFIGURE;
+ }
+
+ if (s) {
+ if (!ofproto->stp) {
+ ofproto->stp = stp_create(ofproto_->name, s->system_id,
+ send_bpdu_cb, ofproto);
+ ofproto->stp_last_tick = time_msec();
+ }
+
+ stp_set_bridge_id(ofproto->stp, s->system_id);
+ stp_set_bridge_priority(ofproto->stp, s->priority);
+ stp_set_hello_time(ofproto->stp, s->hello_time);
+ stp_set_max_age(ofproto->stp, s->max_age);
+ stp_set_forward_delay(ofproto->stp, s->fwd_delay);
+ } else {
+ struct ofport *ofport;
+
+ HMAP_FOR_EACH (ofport, hmap_node, &ofproto->up.ports) {
+ set_stp_port(ofport, NULL);
+ }
+
+ stp_unref(ofproto->stp);
+ ofproto->stp = NULL;
+ }
+
+ return 0;
+}
+
+static int
+get_stp_status(struct ofproto *ofproto_, struct ofproto_stp_status *s)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ if (ofproto->stp) {
+ s->enabled = true;
+ s->bridge_id = stp_get_bridge_id(ofproto->stp);
+ s->designated_root = stp_get_designated_root(ofproto->stp);
+ s->root_path_cost = stp_get_root_path_cost(ofproto->stp);
+ } else {
+ s->enabled = false;
+ }
+
+ return 0;
+}
+
+static void
+update_stp_port_state(struct ofport_dpif *ofport)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ enum stp_state state;
+
+ /* Figure out new state. */
+ state = ofport->stp_port ? stp_port_get_state(ofport->stp_port)
+ : STP_DISABLED;
+
+ /* Update state. */
+ if (ofport->stp_state != state) {
+ enum ofputil_port_state of_state;
+ bool fwd_change;
+
+ VLOG_DBG("port %s: STP state changed from %s to %s",
+ netdev_get_name(ofport->up.netdev),
+ stp_state_name(ofport->stp_state),
+ stp_state_name(state));
+ if (stp_learn_in_state(ofport->stp_state)
+ != stp_learn_in_state(state)) {
+ /* xxx Learning action flows should also be flushed. */
+ 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);
+
+ ofproto->backer->need_revalidate = REV_STP;
+ ofport->stp_state = state;
+ ofport->stp_state_entered = time_msec();
+
+ if (fwd_change && ofport->bundle) {
+ bundle_update(ofport->bundle);
+ }
+
+ /* Update the STP state bits in the OpenFlow port description. */
+ of_state = ofport->up.pp.state & ~OFPUTIL_PS_STP_MASK;
+ of_state |= (state == STP_LISTENING ? OFPUTIL_PS_STP_LISTEN
+ : state == STP_LEARNING ? OFPUTIL_PS_STP_LEARN
+ : state == STP_FORWARDING ? OFPUTIL_PS_STP_FORWARD
+ : state == STP_BLOCKING ? OFPUTIL_PS_STP_BLOCK
+ : 0);
+ ofproto_port_set_state(&ofport->up, of_state);
+ }
+}
+
+/* Configures STP on 'ofport_' using the settings defined in 's'. The
+ * caller is responsible for assigning STP port numbers and ensuring
+ * there are no duplicates. */
+static int
+set_stp_port(struct ofport *ofport_,
+ const struct ofproto_port_stp_settings *s)
+{
+ struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+ struct stp_port *sp = ofport->stp_port;
+
+ if (!s || !s->enable) {
+ if (sp) {