void BcmIntf::program()

in fboss/agent/hw/bcm/BcmIntf.cpp [124:287]


void BcmIntf::program(const shared_ptr<Interface>& intf) {
  const auto& oldIntf = intf_;
  if (oldIntf != nullptr) {
    // vrf and vid cannot be changed by this method.
    if (oldIntf->getRouterID() != intf->getRouterID() ||
        oldIntf->getVlanID() != intf->getVlanID()) {
      throw FbossError(
          "Interface router ID (",
          oldIntf->getRouterID(),
          " vs ",
          intf->getRouterID(),
          " or VLAN ID (",
          oldIntf->getVlanID(),
          " vs ",
          intf->getVlanID(),
          " is changed during programming");
    }
  }

  // create or update a station entry
  if (station_ == nullptr) {
    station_ = unique_ptr<BcmStation>(new BcmStation(hw_));
  }
  if (oldIntf == nullptr || oldIntf->getMac() != intf->getMac()) {
    // Program BCM station to have the same ID as interface
    // so we can look them up during warm boot
    station_->program(intf->getMac(), intf->getID());
  }

  const auto vrf = BcmSwitch::getBcmVrfId(intf->getRouterID());

  // Lambda to compare with existing intf to determine if we need to
  // reprogram
  auto updateIntfIfNeeded = [&](bcm_l3_intf_t& newIntf,
                                const bcm_l3_intf_t& oldIntf) {
    auto equivalent = [=](const bcm_l3_intf_t& newIntf,
                          const bcm_l3_intf_t& existingIntf) {
      CHECK(newIntf.l3a_vrf == existingIntf.l3a_vrf);
      return macFromBcm(newIntf.l3a_mac_addr) ==
          macFromBcm(existingIntf.l3a_mac_addr) &&
          newIntf.l3a_vid == existingIntf.l3a_vid &&
          newIntf.l3a_mtu == existingIntf.l3a_mtu;
    };

    if (!equivalent(newIntf, oldIntf)) {
      XLOG(DBG1) << "Updating interface for vlan : " << intf->getVlanID()
                 << " and mac: " << intf->getMac();
      CHECK_NE(bcmIfId_, INVALID);
      newIntf.l3a_intf_id = bcmIfId_;
      newIntf.l3a_flags = BCM_L3_WITH_ID | BCM_L3_REPLACE;
      return true;
    }
    return false;
  };

  const auto intftoIfparam = [=](const auto& Intf) {
    bcm_l3_intf_t ifParams;
    bcm_l3_intf_t_init(&ifParams);
#ifndef IS_OSS
    // Something about the open source build breaks here in ubuntu 16_04
    //
    // Specifically g++-5.4 seems to segfault (!!) compiling this line.
    // It's a simple assignment from ultimately a
    // BOOST_STRONG_TYPE RouterID (which is an int) to
    // a bcm_vrf_t (which is also an int!!).  The casting should
    // be a NOOP but somehow kills g++'s type checking system
    // This is not a problem with g++-6 that the ONL OSS build uses
    //
    // -- hack around this until compilers fix themselves :-/
    //
    ifParams.l3a_vrf = vrf;
#else
    ifParams.l3a_vrf = 0; // Currently VRF is always zero anyway
#endif
    memcpy(
        &ifParams.l3a_mac_addr,
        Intf->getMac().bytes(),
        sizeof(ifParams.l3a_mac_addr));
    ifParams.l3a_vid = Intf->getVlanID();
    ifParams.l3a_mtu = Intf->getMtu();
    return ifParams;
  };

  auto ifParams = intftoIfparam(intf);
  // create the interface if needed
  if (bcmIfId_ == INVALID) {
    // create a BCM l3 interface
    bool addInterface = false;
    const auto warmBootCache = hw_->getWarmBootCache();
    auto vlanMac2IntfItr =
        warmBootCache->findL3Intf(intf->getVlanID(), intf->getMac());
    if (vlanMac2IntfItr != warmBootCache->vlanAndMac2Intf_end()) {
      const auto& existingIntf = vlanMac2IntfItr->second;
      bcmIfId_ = existingIntf.l3a_intf_id;
      if (updateIntfIfNeeded(ifParams, existingIntf)) {
        // Set add interface to true, we will no issue the call to add
        // but with the above flags set this will cause the entry to be
        // updated.
        addInterface = true;
      } else {
        XLOG(DBG1) << "Interface for vlan " << intf->getVlanID() << " and mac "
                   << intf->getMac() << " already exists";
      }

    } else {
      addInterface = true;
    }
    if (addInterface) {
      bool updateIngress = true;
      if (vlanMac2IntfItr == warmBootCache->vlanAndMac2Intf_end()) {
        XLOG(DBG1) << "Adding interface for vlan : " << intf->getVlanID()
                   << " and mac: " << intf->getMac();
        updateIngress = false;
      }
      auto rc = bcm_l3_intf_create(hw_->getUnit(), &ifParams);
      bcmCheckError(rc, "failed to create L3 interface ", intf->getID());
      bcmIfId_ = ifParams.l3a_intf_id;
      programIngressIfNeeded(intf, updateIngress);
    }
    if (vlanMac2IntfItr != warmBootCache->vlanAndMac2Intf_end()) {
      warmBootCache->programmed(vlanMac2IntfItr);
    }
    CHECK_NE(bcmIfId_, INVALID);
  }

  if (oldIntf) {
    auto oldIfParams = intftoIfparam(oldIntf);
    if (updateIntfIfNeeded(ifParams, oldIfParams)) {
      auto rc = bcm_l3_intf_create(hw_->getUnit(), &ifParams);
      bcmCheckError(rc, "failed to update L3 interface ", intf->getID());
      programIngressIfNeeded(intf, true);
    }
  }
  std::unordered_set<std::shared_ptr<BcmHostIf>> hosts;
  for (const auto& addr : intf->getAddresses()) {
    std::shared_ptr<BcmHostIf> host;
    BcmHostKey key = BcmHostKey(vrf, addr.first, intf->getID());
    if (hw_->getPlatform()->getAsic()->isSupported(
            HwAsic::Feature::HOSTTABLE)) {
      host = hw_->writableHostTable()->refOrEmplaceHost(key);
    } else {
      host = hw_->writableRouteTable()->refOrEmplaceHost(key);
    }
    CHECK(host);
    if (!host->isProgrammed()) {
      // new host has been created
      if (addr.first.isV4()) {
        host->setLookupClassId(BcmAclEntry::kLocalIp4DstClassL3Id);
      } else {
        host->setLookupClassId(BcmAclEntry::kLocalIp6DstClassL3Id);
      }
      host->programToCPU(bcmIfId_);
    }
    hosts.emplace(std::move(host));
  }
  // create new host entries and discard updated or deleted ones,
  // existing host entries are not "deleted", they carry over.
  hosts_ = std::move(hosts);

  // all new info have been programmed, store the interface configuration
  intf_ = intf;
  XLOG(DBG3) << "updated L3 interface " << bcmIfId_ << " for interface ID "
             << intf->getID();
}