void BcmEgress::program()

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);
}