in fboss/agent/ApplyThriftConfig.cpp [1206:1477]
shared_ptr<Port> ThriftConfigApplier::updatePort(
const shared_ptr<Port>& orig,
const cfg::Port* portConf,
const shared_ptr<Transceiver>& transceiver) {
CHECK_EQ(orig->getID(), PortID(*portConf->logicalID()));
auto vlans = portVlans_[orig->getID()];
std::vector<cfg::PortQueue> cfgPortQueues;
if (auto portQueueConfigName = portConf->portQueueConfigName()) {
auto it = cfg_->portQueueConfigs()->find(*portQueueConfigName);
if (it == cfg_->portQueueConfigs()->end()) {
throw FbossError(
"Port queue config name: ",
*portQueueConfigName,
" does not exist in PortQueueConfig map");
}
cfgPortQueues = it->second;
}
const auto& oldIngressMirror = orig->getIngressMirror();
const auto& oldEgressMirror = orig->getEgressMirror();
auto newIngressMirror = std::optional<std::string>();
auto newEgressMirror = std::optional<std::string>();
if (auto ingressMirror = portConf->ingressMirror()) {
newIngressMirror = *ingressMirror;
}
if (auto egressMirror = portConf->egressMirror()) {
newEgressMirror = *egressMirror;
}
bool mirrorsUnChanged = (oldIngressMirror == newIngressMirror) &&
(oldEgressMirror == newEgressMirror);
auto newQosPolicy = std::optional<std::string>();
if (auto dataPlaneTrafficPolicy = cfg_->dataPlaneTrafficPolicy()) {
if (auto defaultDataPlaneQosPolicy =
dataPlaneTrafficPolicy->defaultQosPolicy()) {
newQosPolicy = *defaultDataPlaneQosPolicy;
}
if (auto portIdToQosPolicy = dataPlaneTrafficPolicy->portIdToQosPolicy()) {
auto qosPolicyItr = portIdToQosPolicy->find(*portConf->logicalID());
if (qosPolicyItr != portIdToQosPolicy->end()) {
newQosPolicy = qosPolicyItr->second;
}
}
}
std::optional<cfg::QosMap> qosMap;
if (newQosPolicy) {
bool qosPolicyFound = false;
for (auto qosPolicy : *cfg_->qosPolicies()) {
if (qosPolicyFound) {
break;
}
qosPolicyFound = (*qosPolicy.name() == newQosPolicy.value());
if (qosPolicyFound && qosPolicy.qosMap()) {
qosMap = qosPolicy.qosMap().value();
}
}
if (!qosPolicyFound) {
throw FbossError("qos policy ", newQosPolicy.value(), " not found");
}
}
// For now, we only support update unicast port queues for ports
QueueConfig portQueues;
for (auto streamType : platform_->getAsic()->getQueueStreamTypes(false)) {
auto maxQueues =
platform_->getAsic()->getDefaultNumPortQueues(streamType, false);
auto tmpPortQueues = updatePortQueues(
orig->getPortQueues(), cfgPortQueues, maxQueues, streamType, qosMap);
portQueues.insert(
portQueues.begin(), tmpPortQueues.begin(), tmpPortQueues.end());
}
bool queuesUnchanged = portQueues.size() == orig->getPortQueues().size();
for (int i = 0; i < portQueues.size() && queuesUnchanged; i++) {
if (*(portQueues.at(i)) != *(orig->getPortQueues().at(i))) {
queuesUnchanged = false;
break;
}
}
auto newSampleDest = std::optional<cfg::SampleDestination>();
if (portConf->sampleDest()) {
newSampleDest = portConf->sampleDest().value();
if (newSampleDest.value() == cfg::SampleDestination::MIRROR &&
*portConf->sFlowEgressRate() > 0) {
throw FbossError(
"Port ",
orig->getID(),
": Egress sampling to mirror destination is unsupported");
}
}
auto newPfc = std::optional<cfg::PortPfc>();
auto newPfcPriorities = std::optional<std::vector<PfcPriority>>();
std::optional<PortPgConfigs> portPgCfgs;
// lets compare the portPgConfigs
bool portPgConfigUnchanged = true;
if (portConf->pfc().has_value()) {
newPfc = portConf->pfc().value();
auto pause = portConf->pause().value();
bool pfc_rx = *newPfc->rx();
bool pfc_tx = *newPfc->tx();
bool pause_rx = *pause.rx();
bool pause_tx = *pause.tx();
if (pfc_rx || pfc_tx) {
if (!platform_->getAsic()->isSupported(HwAsic::Feature::PFC)) {
throw FbossError(
"Port ",
orig->getID(),
" has PFC enabled, but its not supported feature for this platform");
}
if (pause_rx || pause_tx) {
throw FbossError(
"Port ",
orig->getID(),
" PAUSE and PFC cannot be enabled on the same port");
}
}
auto portPgConfigName = newPfc->portPgConfigName();
if (newPfc->watchdog().has_value() && (*portPgConfigName).empty()) {
throw FbossError(
"Port ",
orig->getID(),
" Priority group must be associated with port "
"when PFC watchdog is configured");
}
if (auto portPgConfigs = cfg_->portPgConfigs()) {
auto it = portPgConfigs->find(*portPgConfigName);
if (it == portPgConfigs->end()) {
throw FbossError(
"Port ",
orig->getID(),
" pg name",
*portPgConfigName,
"does not exist in portPgConfig map");
}
portPgCfgs = updatePortPgConfigs(it->second, orig);
// validate that the given pg profile points to valid
// buffer pool
validateUpdatePgBufferPoolName(
portPgCfgs.value(), orig, *portPgConfigName);
/*
* Keep track of enabled pfcPriorities which are 1:1
* mapped to PG id.
*/
newPfcPriorities = findEnabledPfcPriorities(portPgCfgs.value());
} else if (!(*portPgConfigName).empty()) {
throw FbossError(
"Port: ",
orig->getID(),
" pg name: ",
*portPgConfigName,
" exist but not the portPgConfig map");
}
}
portPgConfigUnchanged = isPgConfigUnchanged(portPgCfgs, orig);
/*
* The list of lookup classes would be different when first enabling the
* feature and if we had to change the number of lookup classes (unlikely)
* or disable the queue-per-host feature (for emergency).
*
* To avoid unncessary shuffling when only the order of lookup classes
* changes, (e.g. due to config change), sort and compare. This requires a
* deep copy and sorting, but in practice, the list of lookup classes would
* be small (< 10).
*/
auto origLookupClasses{orig->getLookupClassesToDistributeTrafficOn()};
auto newLookupClasses{*portConf->lookupClasses()};
sort(origLookupClasses.begin(), origLookupClasses.end());
sort(newLookupClasses.begin(), newLookupClasses.end());
auto lookupClassesUnchanged = (origLookupClasses == newLookupClasses);
// Now use TransceiverMap as the source of truth to build matcher
// Prepare the new profileConfig
std::optional<cfg::PlatformPortConfigOverrideFactor> factor;
if (transceiver != nullptr) {
factor = transceiver->toPlatformPortConfigOverrideFactor();
}
platform_->getPlatformMapping()->customizePlatformPortConfigOverrideFactor(
factor);
PlatformPortProfileConfigMatcher matcher{
*portConf->profileID(), orig->getID(), factor};
auto portProfileCfg = platform_->getPortProfileConfig(matcher);
if (!portProfileCfg) {
throw FbossError(
"No port profile config found with matcher:", matcher.toString());
}
if (*portConf->state() == cfg::PortState::ENABLED &&
*portProfileCfg->speed() != *portConf->speed()) {
throw FbossError(
orig->getName(),
" has mismatched speed on profile:",
apache::thrift::util::enumNameSafe(*portConf->profileID()),
" and config:",
apache::thrift::util::enumNameSafe(*portConf->speed()));
}
auto newProfileConfigRef = portProfileCfg->iphy();
auto profileConfigUnchanged =
(*newProfileConfigRef == orig->getProfileConfig());
const auto& newPinConfigs =
platform_->getPlatformMapping()->getPortIphyPinConfigs(matcher);
auto pinConfigsUnchanged = (newPinConfigs == orig->getPinConfigs());
XLOG_IF(DBG2, !profileConfigUnchanged || !pinConfigsUnchanged)
<< orig->getName() << " has profileConfig: "
<< (profileConfigUnchanged ? "UNCHANGED" : "CHANGED")
<< ", pinConfigs: " << (pinConfigsUnchanged ? "UNCHANGED" : "CHANGED")
<< ", with matcher:" << matcher.toString();
// Ensure portConf has actually changed, before applying
if (*portConf->state() == orig->getAdminState() &&
VlanID(*portConf->ingressVlan()) == orig->getIngressVlan() &&
*portConf->speed() == orig->getSpeed() &&
*portConf->profileID() == orig->getProfileID() &&
*portConf->pause() == orig->getPause() && newPfc == orig->getPfc() &&
newPfcPriorities == orig->getPfcPriorities() &&
*portConf->sFlowIngressRate() == orig->getSflowIngressRate() &&
*portConf->sFlowEgressRate() == orig->getSflowEgressRate() &&
newSampleDest == orig->getSampleDestination() &&
portConf->name().value_or({}) == orig->getName() &&
portConf->description().value_or({}) == orig->getDescription() &&
vlans == orig->getVlans() && queuesUnchanged && portPgConfigUnchanged &&
*portConf->loopbackMode() == orig->getLoopbackMode() &&
mirrorsUnChanged && newQosPolicy == orig->getQosPolicy() &&
*portConf->expectedLLDPValues() == orig->getLLDPValidations() &&
*portConf->maxFrameSize() == orig->getMaxFrameSize() &&
lookupClassesUnchanged && profileConfigUnchanged && pinConfigsUnchanged) {
return nullptr;
}
auto newPort = orig->clone();
auto lldpmap = newPort->getLLDPValidations();
for (const auto& tag : *portConf->expectedLLDPValues()) {
lldpmap[tag.first] = tag.second;
}
newPort->setAdminState(*portConf->state());
newPort->setIngressVlan(VlanID(*portConf->ingressVlan()));
newPort->setVlans(vlans);
newPort->setSpeed(*portConf->speed());
newPort->setProfileId(*portConf->profileID());
newPort->setPause(*portConf->pause());
newPort->setSflowIngressRate(*portConf->sFlowIngressRate());
newPort->setSflowEgressRate(*portConf->sFlowEgressRate());
newPort->setSampleDestination(newSampleDest);
newPort->setName(portConf->name().value_or({}));
newPort->setDescription(portConf->description().value_or({}));
newPort->setLoopbackMode(*portConf->loopbackMode());
newPort->resetPortQueues(portQueues);
newPort->setIngressMirror(newIngressMirror);
newPort->setEgressMirror(newEgressMirror);
newPort->setQosPolicy(newQosPolicy);
newPort->setExpectedLLDPValues(lldpmap);
newPort->setLookupClassesToDistributeTrafficOn(*portConf->lookupClasses());
newPort->setMaxFrameSize(*portConf->maxFrameSize());
newPort->setPfc(newPfc);
newPort->setPfcPriorities(newPfcPriorities);
newPort->resetPgConfigs(portPgCfgs);
newPort->setProfileConfig(*newProfileConfigRef);
newPort->resetPinConfigs(newPinConfigs);
return newPort;
}