X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=lib%2Frstp.c;h=f24ca0143578e36c421a2edb37ec525c70d8a54a;hb=981cbd52c9424fd625d6383d28fda2b9d5ba0fd5;hp=e4571598f26403a8a50fad96317228a96a80d83f;hpb=67e8c1ac3142a652bc02a6bb1c1d86658605c9f5;p=cascardo%2Fovs.git diff --git a/lib/rstp.c b/lib/rstp.c index e4571598f..f24ca0143 100644 --- a/lib/rstp.c +++ b/lib/rstp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014 M3S, Srl - Italy + * Copyright (c) 2011-2015 M3S, Srl - Italy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ * Authors: * Martino Fornasa * Daniele Venturino + * Carlo Andreotti * * References to IEEE 802.1D-2004 standard are enclosed in square brackets. * E.g. [17.3], [Table 17-1], etc. @@ -40,18 +41,19 @@ #include "connectivity.h" #include "ofpbuf.h" #include "ofproto/ofproto.h" +#include "dp-packet.h" #include "packets.h" #include "seq.h" #include "unixctl.h" #include "util.h" -#include "vlog.h" +#include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(rstp); struct ovs_mutex rstp_mutex = OVS_MUTEX_INITIALIZER; -static struct list all_rstps__ = LIST_INITIALIZER(&all_rstps__); -static struct list *const all_rstps OVS_GUARDED_BY(rstp_mutex) = &all_rstps__; +static struct ovs_list all_rstps__ = OVS_LIST_INITIALIZER(&all_rstps__); +static struct ovs_list *const all_rstps OVS_GUARDED_BY(rstp_mutex) = &all_rstps__; /* Internal use only. */ static void rstp_set_bridge_address__(struct rstp *, rstp_identifier) @@ -104,7 +106,8 @@ static void rstp_port_set_port_number__(struct rstp_port *, static void rstp_port_set_path_cost__(struct rstp_port *, uint32_t path_cost) OVS_REQUIRES(rstp_mutex); static void rstp_port_set_administrative_bridge_port__(struct rstp_port *, - uint8_t admin_port_state) + uint8_t admin_port_state, + bool initializing) OVS_REQUIRES(rstp_mutex); static void rstp_port_set_admin_edge__(struct rstp_port *, bool admin_edge) OVS_REQUIRES(rstp_mutex); @@ -183,6 +186,7 @@ rstp_unref(struct rstp *rstp) list_remove(&rstp->node); ovs_mutex_unlock(&rstp_mutex); + hmap_destroy(&rstp->ports); free(rstp->name); free(rstp); } @@ -242,7 +246,7 @@ rstp_init(void) /* Creates and returns a new RSTP instance that initially has no ports. */ struct rstp * rstp_create(const char *name, rstp_identifier bridge_address, - void (*send_bpdu)(struct ofpbuf *bpdu, void *port_aux, + void (*send_bpdu)(struct dp_packet *bpdu, void *port_aux, void *rstp_aux), void *aux) OVS_EXCLUDED(rstp_mutex) @@ -278,6 +282,8 @@ rstp_create(const char *name, rstp_identifier bridge_address, rstp->aux = aux; rstp->changes = false; rstp->begin = true; + rstp->old_root_aux = NULL; + rstp->new_root_aux = NULL; ovs_refcount_init(&rstp->ref_cnt); @@ -321,10 +327,12 @@ rstp_set_bridge_address__(struct rstp *rstp, rstp_identifier bridge_address) { VLOG_DBG("%s: set bridge address to: "RSTP_ID_FMT"", rstp->name, RSTP_ID_ARGS(bridge_address)); - - rstp->address = bridge_address; - rstp->bridge_identifier = bridge_address; - set_bridge_priority__(rstp); + if (rstp->address != bridge_address) { + rstp->address = bridge_address; + rstp->bridge_identifier &= 0xffff000000000000ULL; + rstp->bridge_identifier |= bridge_address; + set_bridge_priority__(rstp); + } } /* Sets the bridge address. */ @@ -369,7 +377,8 @@ rstp_set_bridge_priority__(struct rstp *rstp, int new_priority) { new_priority = ROUND_DOWN(new_priority, RSTP_PRIORITY_STEP); - if (new_priority >= RSTP_MIN_PRIORITY + if (rstp->priority != new_priority + && new_priority >= RSTP_MIN_PRIORITY && new_priority <= RSTP_MAX_PRIORITY) { VLOG_DBG("%s: set bridge priority to %d", rstp->name, new_priority); @@ -523,8 +532,9 @@ static void rstp_set_bridge_max_age__(struct rstp *rstp, int new_max_age) OVS_REQUIRES(rstp_mutex) { - if (new_max_age >= RSTP_MIN_BRIDGE_MAX_AGE && - new_max_age <= RSTP_MAX_BRIDGE_MAX_AGE) { + if (rstp->bridge_max_age != new_max_age + && new_max_age >= RSTP_MIN_BRIDGE_MAX_AGE + && new_max_age <= RSTP_MAX_BRIDGE_MAX_AGE) { /* [17.13] */ if ((2 * (rstp->bridge_forward_delay - 1) >= new_max_age) && (new_max_age >= 2 * rstp->bridge_hello_time)) { @@ -533,6 +543,8 @@ rstp_set_bridge_max_age__(struct rstp *rstp, int new_max_age) rstp->bridge_max_age = new_max_age; rstp->bridge_times.max_age = new_max_age; + rstp->changes = true; + updt_roles_tree__(rstp); } } } @@ -551,13 +563,16 @@ static void rstp_set_bridge_forward_delay__(struct rstp *rstp, int new_forward_delay) OVS_REQUIRES(rstp_mutex) { - if (new_forward_delay >= RSTP_MIN_BRIDGE_FORWARD_DELAY - && new_forward_delay <= RSTP_MAX_BRIDGE_FORWARD_DELAY) { + if (rstp->bridge_forward_delay != new_forward_delay + && new_forward_delay >= RSTP_MIN_BRIDGE_FORWARD_DELAY + && new_forward_delay <= RSTP_MAX_BRIDGE_FORWARD_DELAY) { if (2 * (new_forward_delay - 1) >= rstp->bridge_max_age) { VLOG_DBG("%s: set RSTP Forward Delay to %d", rstp->name, new_forward_delay); rstp->bridge_forward_delay = new_forward_delay; rstp->bridge_times.forward_delay = new_forward_delay; + rstp->changes = true; + updt_roles_tree__(rstp); } } } @@ -577,7 +592,8 @@ rstp_set_bridge_transmit_hold_count__(struct rstp *rstp, int new_transmit_hold_count) OVS_REQUIRES(rstp_mutex) { - if (new_transmit_hold_count >= RSTP_MIN_TRANSMIT_HOLD_COUNT + if (rstp->transmit_hold_count != new_transmit_hold_count + && new_transmit_hold_count >= RSTP_MIN_TRANSMIT_HOLD_COUNT && new_transmit_hold_count <= RSTP_MAX_TRANSMIT_HOLD_COUNT) { struct rstp_port *p; @@ -657,7 +673,8 @@ static void rstp_port_set_priority__(struct rstp_port *port, int priority) OVS_REQUIRES(rstp_mutex) { - if (priority >= RSTP_MIN_PORT_PRIORITY + if (port->priority != priority + && priority >= RSTP_MIN_PORT_PRIORITY && priority <= RSTP_MAX_PORT_PRIORITY) { VLOG_DBG("%s, port %u: set RSTP port priority to %d", port->rstp->name, port->port_number, priority); @@ -704,21 +721,34 @@ static void rstp_port_set_port_number__(struct rstp_port *port, uint16_t port_number) OVS_REQUIRES(rstp_mutex) { + int old_port_number = port->port_number; + /* If new_port_number is available, use it, otherwise use the first free * available port number. */ - port->port_number = - is_port_number_available__(port->rstp, port_number, port) - ? port_number - : rstp_first_free_number__(port->rstp, port); - - set_port_id__(port); - /* [17.13] is not clear. I suppose that a port number change - * should trigger reselection like a port priority change. */ - port->selected = false; - port->reselect = true; + if (port->port_number != port_number || port_number == 0) { + port->port_number = + is_port_number_available__(port->rstp, port_number, port) + ? port_number + : rstp_first_free_number__(port->rstp, port); + + if (port->port_number != old_port_number) { + set_port_id__(port); + /* [17.13] is not clear. I suppose that a port number change + * should trigger reselection like a port priority change. */ + port->selected = false; + port->reselect = true; + + /* Adjust the ports hmap. */ + if (!hmap_node_is_null(&port->node)) { + hmap_remove(&port->rstp->ports, &port->node); + } + hmap_insert(&port->rstp->ports, &port->node, + hash_int(port->port_number, 0)); - VLOG_DBG("%s: set new RSTP port number %d", port->rstp->name, - port->port_number); + VLOG_DBG("%s: set new RSTP port number %d", port->rstp->name, + port->port_number); + } + } } /* Converts the link speed to a port path cost [Table 17-3]. */ @@ -745,7 +775,8 @@ static void rstp_port_set_path_cost__(struct rstp_port *port, uint32_t path_cost) OVS_REQUIRES(rstp_mutex) { - if (path_cost >= RSTP_MIN_PORT_PATH_COST + if (port->port_path_cost != path_cost + && path_cost >= RSTP_MIN_PORT_PATH_COST && path_cost <= RSTP_MAX_PORT_PATH_COST) { VLOG_DBG("%s, port %u, set RSTP port path cost to %d", port->rstp->name, port->port_number, path_cost); @@ -769,30 +800,55 @@ rstp_get_root_path_cost(const struct rstp *rstp) return cost; } -/* Returns true if something has happened to 'rstp' which necessitates - * flushing the client's MAC learning table. - */ -bool -rstp_check_and_reset_fdb_flush(struct rstp *rstp) +/* Finds a port which needs to flush its own MAC learning table. A NULL + * pointer is returned if no port needs to flush its MAC learning table. + * '*port' needs to be NULL in the first call to start the iteration. If + * '*port' is passed as non-NULL, it must be the value set by the last + * invocation of this function. + * + * This function may only be called by the thread that creates and deletes + * ports. Otherwise this function is not thread safe, as the returned + * '*port' could become stale before it is used in the next invocation. */ +void * +rstp_check_and_reset_fdb_flush(struct rstp *rstp, struct rstp_port **port) OVS_EXCLUDED(rstp_mutex) { - bool needs_flush; - struct rstp_port *p; - - needs_flush = false; + void *aux = NULL; ovs_mutex_lock(&rstp_mutex); - HMAP_FOR_EACH (p, node, &rstp->ports) { - if (p->fdb_flush) { - needs_flush = true; - /* fdb_flush should be reset by the filtering database - * once the entries are removed if rstp_version is TRUE, and - * immediately if stp_version is TRUE.*/ - p->fdb_flush = false; + if (*port == NULL) { + struct rstp_port *p; + + HMAP_FOR_EACH (p, node, &rstp->ports) { + if (p->fdb_flush) { + aux = p->aux; + *port = p; + goto out; + } } + } else { /* continue */ + struct rstp_port *p = *port; + + HMAP_FOR_EACH_CONTINUE (p, node, &rstp->ports) { + if (p->fdb_flush) { + aux = p->aux; + *port = p; + goto out; + } + } + } + /* No port needs flushing. */ + *port = NULL; +out: + /* fdb_flush should be reset by the filtering database + * once the entries are removed if rstp_version is TRUE, and + * immediately if stp_version is TRUE.*/ + if (*port != NULL) { + (*port)->fdb_flush = false; } ovs_mutex_unlock(&rstp_mutex); - return needs_flush; + + return aux; } /* Finds a port whose state has changed, and returns the aux pointer set for @@ -839,9 +895,54 @@ rstp_get_next_changed_port_aux(struct rstp *rstp, struct rstp_port **portp) *portp = NULL; out: ovs_mutex_unlock(&rstp_mutex); + + return aux; +} + +bool +rstp_shift_root_learned_address(struct rstp *rstp) +{ + bool ret; + + ovs_mutex_lock(&rstp_mutex); + ret = rstp->root_changed; + ovs_mutex_unlock(&rstp_mutex); + + return ret; +} + +void * +rstp_get_old_root_aux(struct rstp *rstp) +{ + void *aux; + + ovs_mutex_lock(&rstp_mutex); + aux = rstp->old_root_aux; + ovs_mutex_unlock(&rstp_mutex); + + return aux; +} + +void * +rstp_get_new_root_aux(struct rstp *rstp) +{ + void *aux; + + ovs_mutex_lock(&rstp_mutex); + aux = rstp->new_root_aux; + ovs_mutex_unlock(&rstp_mutex); + return aux; } +void +rstp_reset_root_changed(struct rstp *rstp) +{ + ovs_mutex_lock(&rstp_mutex); + rstp->root_changed = false; + ovs_mutex_unlock(&rstp_mutex); +} + /* Returns the port in 'rstp' with number 'port_number'. * * XXX: May only be called while concurrent deletion of ports is excluded. */ @@ -875,17 +976,15 @@ rstp_get_port(struct rstp *rstp, uint16_t port_number) } void * -rstp_get_port_aux(struct rstp *rstp, uint16_t port_number) - OVS_EXCLUDED(rstp_mutex) +rstp_get_port_aux__(struct rstp *rstp, uint16_t port_number) + OVS_REQUIRES(rstp_mutex) { struct rstp_port *p; - void *aux; - - ovs_mutex_lock(&rstp_mutex); p = rstp_get_port__(rstp, port_number); - aux = p->aux; - ovs_mutex_unlock(&rstp_mutex); - return aux; + if (p) { + return p->aux; + } + return NULL; } /* Updates the port_enabled parameter. */ @@ -922,17 +1021,25 @@ rstp_port_set_mac_operational(struct rstp_port *p, bool new_mac_operational) /* Sets the port Administrative Bridge Port parameter. */ static void rstp_port_set_administrative_bridge_port__(struct rstp_port *p, - uint8_t admin_port_state) + uint8_t admin_port_state, + bool initializing) OVS_REQUIRES(rstp_mutex) { VLOG_DBG("%s, port %u: set RSTP port admin-port-state to %d", p->rstp->name, p->port_number, admin_port_state); - if (admin_port_state == RSTP_ADMIN_BRIDGE_PORT_STATE_DISABLED - || admin_port_state == RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED) { - + if (p->is_administrative_bridge_port != admin_port_state + && (admin_port_state == RSTP_ADMIN_BRIDGE_PORT_STATE_DISABLED + || admin_port_state == RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED)) { p->is_administrative_bridge_port = admin_port_state; update_port_enabled__(p); + + if (!initializing) { + struct rstp *rstp = p->rstp; + + rstp->changes = true; + move_rstp__(rstp); + } } } @@ -942,8 +1049,9 @@ rstp_port_set_oper_point_to_point_mac__(struct rstp_port *p, uint8_t new_oper_p2p_mac) OVS_REQUIRES(rstp_mutex) { - if (new_oper_p2p_mac == RSTP_OPER_P2P_MAC_STATE_DISABLED - || new_oper_p2p_mac == RSTP_OPER_P2P_MAC_STATE_ENABLED) { + if (p->oper_point_to_point_mac != new_oper_p2p_mac + && (new_oper_p2p_mac == RSTP_OPER_P2P_MAC_STATE_DISABLED + || new_oper_p2p_mac == RSTP_OPER_P2P_MAC_STATE_ENABLED)) { p->oper_point_to_point_mac = new_oper_p2p_mac; update_port_enabled__(p); @@ -956,7 +1064,8 @@ rstp_initialize_port_defaults__(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { rstp_port_set_administrative_bridge_port__(p, - RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED); + RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED, + true); rstp_port_set_oper_point_to_point_mac__(p, RSTP_OPER_P2P_MAC_STATE_ENABLED); rstp_port_set_path_cost__(p, RSTP_DEFAULT_PORT_PATH_COST); @@ -1048,6 +1157,7 @@ rstp_add_port(struct rstp *rstp) struct rstp_port *p = xzalloc(sizeof *p); ovs_refcount_init(&p->ref_cnt); + hmap_node_nullify(&p->node); ovs_mutex_lock(&rstp_mutex); p->rstp = rstp; @@ -1059,7 +1169,6 @@ rstp_add_port(struct rstp *rstp) p->port_id); rstp_port_set_state__(p, RSTP_DISCARDING); - hmap_insert(&rstp->ports, &p->node, hash_int(p->port_number, 0)); rstp->changes = true; move_rstp__(rstp); VLOG_DBG("%s: added port "RSTP_PORT_ID_FMT"", rstp->name, p->port_id); @@ -1133,28 +1242,29 @@ static void rstp_port_set_admin_point_to_point_mac__(struct rstp_port *port, { VLOG_DBG("%s, port %u: set RSTP port admin-point-to-point-mac to %d", port->rstp->name, port->port_number, admin_p2p_mac_state); - - if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_FORCE_TRUE) { - port->admin_point_to_point_mac = admin_p2p_mac_state; - rstp_port_set_oper_point_to_point_mac__(port, - RSTP_OPER_P2P_MAC_STATE_ENABLED); - } else if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_FORCE_FALSE) { - port->admin_point_to_point_mac = admin_p2p_mac_state; - rstp_port_set_oper_point_to_point_mac__(port, - RSTP_OPER_P2P_MAC_STATE_DISABLED); - } else if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_AUTO) { - /* If adminPointToPointMAC is set to Auto, then the value of - * operPointToPointMAC is determined in accordance with the - * specific procedures defined for the MAC entity concerned, as - * defined in 6.5. If these procedures determine that the MAC - * entity is connected to a point-to-point LAN, then - * operPointToPointMAC is set TRUE; otherwise it is set FALSE. - * In the absence of a specific definition of how to determine - * whether the MAC is connected to a point-to-point LAN or not, - * the value of operPointToPointMAC shall be FALSE. */ - port->admin_point_to_point_mac = admin_p2p_mac_state; - rstp_port_set_oper_point_to_point_mac__( - port, RSTP_OPER_P2P_MAC_STATE_DISABLED); + if (port->admin_point_to_point_mac != admin_p2p_mac_state) { + if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_FORCE_TRUE) { + port->admin_point_to_point_mac = admin_p2p_mac_state; + rstp_port_set_oper_point_to_point_mac__( + port, RSTP_OPER_P2P_MAC_STATE_ENABLED); + } else if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_FORCE_FALSE) { + port->admin_point_to_point_mac = admin_p2p_mac_state; + rstp_port_set_oper_point_to_point_mac__( + port, RSTP_OPER_P2P_MAC_STATE_DISABLED); + } else if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_AUTO) { + /* If adminPointToPointMAC is set to Auto, then the value of + * operPointToPointMAC is determined in accordance with the + * specific procedures defined for the MAC entity concerned, as + * defined in 6.5. If these procedures determine that the MAC + * entity is connected to a point-to-point LAN, then + * operPointToPointMAC is set TRUE; otherwise it is set FALSE. + * In the absence of a specific definition of how to determine + * whether the MAC is connected to a point-to-point LAN or not, + * the value of operPointToPointMAC shall be FALSE. */ + port->admin_point_to_point_mac = admin_p2p_mac_state; + rstp_port_set_oper_point_to_point_mac__( + port, RSTP_OPER_P2P_MAC_STATE_DISABLED); + } } } @@ -1288,6 +1398,7 @@ rstp_get_root_port(struct rstp *rstp) /* Returns the state of port 'p'. */ enum rstp_state rstp_port_get_state(const struct rstp_port *p) + OVS_EXCLUDED(rstp_mutex) { enum rstp_state state; @@ -1339,7 +1450,7 @@ rstp_port_set(struct rstp_port *port, uint16_t port_num, int priority, rstp_port_set_admin_edge__(port, is_admin_edge); rstp_port_set_auto_edge__(port, is_auto_edge); rstp_port_set_admin_point_to_point_mac__(port, admin_p2p_mac_state); - rstp_port_set_administrative_bridge_port__(port, admin_port_state); + rstp_port_set_administrative_bridge_port__(port, admin_port_state, false); rstp_port_set_mcheck__(port, do_mcheck); ovs_mutex_unlock(&rstp_mutex); }