in fboss/agent/hw/bcm/BcmEgress.cpp [55:211]
void BcmEgress::program(
bcm_if_t intfId,
bcm_vrf_t vrf,
const IPAddress& ip,
const MacAddress* mac,
bcm_port_t port,
RouteForwardAction action) {
bcm_l3_egress_t eObj;
auto failedToProgramMsg = [=]() {
auto msg = folly::to<std::string>(
"failed to program L3 egress object ",
id_,
" ",
(mac) ? mac->toString() : "ToCPU",
" on port ",
port,
" on unit ",
hw_->getUnit());
if (hasLabel()) {
auto withLabelMsg =
folly::to<std::string>(" with label ", getLabel(), " ");
msg.append(withLabelMsg);
}
return msg;
};
auto succeededToProgramMsg = [=]() {
auto msg = folly::to<std::string>(
"programmed L3 egress object ",
id_,
" for ",
((mac) ? mac->toString() : "to CPU"),
" on unit ",
hw_->getUnit(),
" for ip: ",
ip,
" @ brcmif ",
intfId);
if (hasLabel()) {
auto withLabelMsg =
folly::to<std::string>(" with label ", getLabel(), " ");
msg.append(withLabelMsg);
}
return msg;
};
auto alreadyExistsMsg = [=]() {
auto msg = folly::to<std::string>(
"Identical egress object for : ",
ip,
" @ brcmif ",
intfId,
" pointing to ",
(mac ? mac->toString() : "CPU "),
" skipping egress programming");
if (hasLabel()) {
auto withLabelMsg =
folly::to<std::string>(" with label ", getLabel(), " ");
msg.append(withLabelMsg);
}
return msg;
};
prepareEgressObject(
intfId,
port,
!mac ? std::nullopt : std::make_optional<folly::MacAddress>(*mac),
action,
&eObj);
bool addOrUpdateEgress = false;
const auto warmBootCache = hw_->getWarmBootCache();
CHECK(warmBootCache);
// TODO(pshaikh) : look for labeled egress in warmboot cache
auto egressId2EgressCitr = findEgress(vrf, intfId, ip);
if (egressId2EgressCitr != warmBootCache->egressId2Egress_end()) {
// Lambda to compare with existing egress to know if should reprogram
auto equivalent = [](const bcm_l3_egress_t& newEgress,
const bcm_l3_egress_t& existingEgress) {
auto puntToCPU = [=](const bcm_l3_egress_t& egress) {
// Check if both BCM_L3_L2TOCPU and BCM_L3_COPY_TO_CPU are set
static const auto kCPUFlags = BCM_L3_L2TOCPU | BCM_L3_COPY_TO_CPU;
return (egress.flags & kCPUFlags) == kCPUFlags;
};
if (!puntToCPU(newEgress) && !puntToCPU(existingEgress)) {
// Both new and existing egress point to a valid nexthop
// Compare mac, port and interface of egress objects
return !memcmp(
newEgress.mac_addr,
existingEgress.mac_addr,
sizeof(newEgress.mac_addr)) &&
existingEgress.intf == newEgress.intf &&
existingEgress.port == newEgress.port &&
facebook::fboss::getLabel(existingEgress) ==
facebook::fboss::getLabel(newEgress);
}
if (puntToCPU(existingEgress)) {
// If existing entry and new entry both point to CPU we
// consider them equal (i.e. no need to update h/w)
return puntToCPU(newEgress);
} else {
// Existing entry does not point to CPU while the new
// one does. This is because the new entry does not know
// the result of ARP/NDP resolution yet. Leave the entry
// in h/w intact
return true;
}
return false;
};
const auto& existingEgressId = egressId2EgressCitr->first;
// Cache existing egress id
id_ = existingEgressId;
auto existingEgressObject = egressId2EgressCitr->second;
if (!equivalent(eObj, existingEgressObject)) {
XLOG(DBG1) << "Updating egress object for next hop : " << ip
<< " @ brcmif " << intfId;
addOrUpdateEgress = true;
} else {
XLOG(DBG1) << "Egress object for: " << ip << " @ brcmif " << intfId
<< " already exists";
}
} else {
addOrUpdateEgress = true;
}
if (addOrUpdateEgress) {
uint32_t flags = 0;
if (id_ != INVALID) {
flags |= BCM_L3_REPLACE | BCM_L3_WITH_ID;
}
if (!alreadyExists(eObj)) {
/*
* Only program the HW if a identical egress object does not
* exist. Per BCM documentation updating entries like so should not
* be a problem. However we found that if we reprogrammed a already
* existing egress object with the same parameters forwarding to
* the corresponding IP address sometimes broke. BCM issue is being
* tracked in t4324084
*/
auto rc = bcm_l3_egress_create(hw_->getUnit(), flags, &eObj, &id_);
bcmCheckError(rc, failedToProgramMsg());
XLOG(DBG2) << succeededToProgramMsg() << " flags " << eObj.flags
<< " towards port " << eObj.port;
} else {
// This could happen when neighbor entry is confirmed with the same MAC
// after warmboot, as it will trigger another egress programming with the
// same MAC.
XLOG(DBG1) << alreadyExistsMsg();
}
}
// update our internal fields
mac_ = (mac != nullptr) ? *mac : folly::MacAddress{};
intfId_ = intfId;
// update warmboot cache if needed
if (egressId2EgressCitr != warmBootCache->egressId2Egress_end()) {
warmBootCache->programmed(egressId2EgressCitr);
}
CHECK_NE(id_, INVALID);
}