void BcmPort::setSpeed(const shared_ptr<Port>& swPort) { int ret; cfg::PortSpeed desiredPortSpeed; if (swPort->getSpeed() == cfg::PortSpeed::DEFAULT) { int speed; ret = opennsl_port_speed_max(unit_, port_, &speed); bcmCheckError(ret, "failed to get max speed for port", swPort->getID()); desiredPortSpeed = cfg::PortSpeed(speed); } else { desiredPortSpeed = swPort->getSpeed(); } int desiredSpeed = static_cast<int>(desiredPortSpeed); // Unnecessarily updating BCM port speed actually causes // the port to flap, even if this should be a noop, so check current // speed before making speed related changes. Doing so fixes // the interface flaps we were seeing during warm boots int curSpeed = static_cast<int>(getSpeed()); // If the port is down or disabled its safe to update mode and speed to // desired values bool portUp = isUp(); // Update to correct mode and speed settings if the port is down/disabled // or if the speed changed. Ideally we would like to always update to the // desired mode and speed. However these changes are disruptive, in that // they cause a port flap. So to avoid that, we don't update to desired // mode if the port is UP and running at the desired speed. Speed changes // though are applied to UP ports as well, since running at wrong (lower than // desired) speed is pretty dangerous, and can trigger non obvious outages. // // Another practical reason for not updating to the desired mode on ports that // are UP is that there is at least one bug whereby SDK thinks that the ports // are in a different mode than they actually are. We are tracking that // separately. Once that is resolved, we can do a audit to see that if all // ports are in desired mode settings, we can make mode changes a first // class citizen as well. if (!portUp || curSpeed != desiredSpeed) { opennsl_port_if_t desiredMode = getDesiredInterfaceMode(desiredPortSpeed, swPort->getID(), swPort->getName()); // Check whether we have the correct interface set opennsl_port_if_t curMode; ret = opennsl_port_interface_get(unit_, port_, &curMode); bcmCheckError(ret, "Failed to get current interface setting for port ", swPort->getID()); if (curMode != desiredMode) { // Changes to the interface setting only seem to take effect on the next // call to opennsl_port_speed_set() ret = opennsl_port_interface_set(unit_, port_, desiredMode); bcmCheckError( ret, "failed to set interface type for port ", swPort->getID()); } if (portUp) { // Changing the port speed causes traffic disruptions, but not doing // it would cause inconsistency. Warn the user. LOG(WARNING) << "Changing port speed on up port. This will " << "disrupt traffic. Port: " << swPort->getName() << " id: " << swPort->getID(); } // Note that we call speed_set even if the speed is already set // properly and port is down. This is because speed_set // reinitializes the MAC layer of the port and allows us to pick // up changes in interface mode and finalize flex port // transitions. We ensure that the port is down for these // potentially unnecessary calls, as otherwise this will cause // port flaps on ports where link is up. ret = opennsl_port_speed_set(unit_, port_, desiredSpeed); bcmCheckError( ret, "failed to set speed to ", desiredSpeed, " from ", curSpeed, ", on port ", swPort->getID()); getPlatformPort()->linkSpeedChanged(desiredPortSpeed); } }
void BcmPort::setSpeed(const shared_ptr<Port>& swPort) { if (isEnabled()) { LOG(ERROR) << "Cannot set port speed while the port is enabled. Port: " << swPort->getName() << " id: " << swPort->getID(); return; } int ret; cfg::PortSpeed desiredPortSpeed; if (swPort->getSpeed() == cfg::PortSpeed::DEFAULT) { int speed; ret = opennsl_port_speed_max(unit_, port_, &speed); bcmCheckError(ret, "failed to get max speed for port", swPort->getID()); desiredPortSpeed = cfg::PortSpeed(speed); } else { desiredPortSpeed = swPort->getSpeed(); } opennsl_port_if_t desiredMode = getDesiredInterfaceMode(desiredPortSpeed, swPort->getID(), swPort->getName()); opennsl_port_if_t curMode; ret = opennsl_port_interface_get(unit_, port_, &curMode); bcmCheckError(ret, "Failed to get current interface setting for port ", swPort->getID()); bool updateSpeed = false; if (curMode != desiredMode) { ret = opennsl_port_interface_set(unit_, port_, desiredMode); bcmCheckError( ret, "failed to set interface type for port ", swPort->getID()); // Changes to the interface setting only seem to take effect on the next // call to opennsl_port_speed_set(). Therefore make sure we update the // speed below, even if the speed is already at the desired setting. updateSpeed = true; } int desiredSpeed = static_cast<int>(desiredPortSpeed); // Unnecessarily updating BCM port speed actually causes // the port to flap, even if this should be a noop, so check current // speed before making speed related changes. Doing so fixes // the interface flaps we were seeing during warm boots if (!updateSpeed && desiredMode != OPENNSL_PORT_IF_KR4) { int curSpeed; ret = opennsl_port_speed_get(unit_, port_, &curSpeed); bcmCheckError( ret, "Failed to get current speed for port ", swPort->getID()); updateSpeed |= (curSpeed != desiredSpeed); } if (updateSpeed) { if (desiredMode == OPENNSL_PORT_IF_KR4) { // We don't need to set speed when mode is KR4, since ports in KR4 mode // do autonegotiation to figure out the speed. setKR4Ability(); } else { ret = opennsl_port_speed_set(unit_, port_, desiredSpeed); bcmCheckError(ret, "failed to set speed, ", desiredSpeed, ", to port ", swPort->getID()); } } }