HwInitResult BcmSwitch::init()

in fboss/agent/hw/bcm/BcmSwitch.cpp [764:954]


HwInitResult BcmSwitch::init(
    Callback* callback,
    bool /*failHwCallsOnWarmboot*/) {
  HwInitResult ret;
  ret.rib = std::make_unique<RoutingInformationBase>();

  std::lock_guard<std::mutex> g(lock_);

  steady_clock::time_point begin = steady_clock::now();
  CHECK(!unitObject_);
  unitObject_ = BcmAPI::createOnlyUnit(platform_);
  unit_ = unitObject_->getNumber();
  unitObject_->setCookie(this);

  BcmAPI::initUnit(unit_, platform_);

  bootType_ = platform_->getWarmBootHelper()->canWarmBoot()
      ? BootType::WARM_BOOT
      : BootType::COLD_BOOT;
  auto warmBoot = bootType_ == BootType::WARM_BOOT;
  callback_ = callback;

  ret.initializedTime =
      duration_cast<duration<float>>(steady_clock::now() - begin).count();

  XLOG(INFO) << "Initializing BcmSwitch for unit " << unit_;

  // Add callbacks for unit and parity errors as early as possible to handle
  // critical events
  BcmSwitchEventUtils::initUnit(unit_, this);
  auto fatalCob = make_shared<BcmSwitchEventUnitFatalErrorCallback>();
  auto nonFatalCob = make_shared<BcmSwitchEventUnitNonFatalErrorCallback>();
  BcmSwitchEventUtils::registerSwitchEventCallback(
      unit_, BCM_SWITCH_EVENT_STABLE_FULL, fatalCob);
  BcmSwitchEventUtils::registerSwitchEventCallback(
      unit_, BCM_SWITCH_EVENT_STABLE_ERROR, fatalCob);
  BcmSwitchEventUtils::registerSwitchEventCallback(
      unit_, BCM_SWITCH_EVENT_UNCONTROLLED_SHUTDOWN, fatalCob);
  BcmSwitchEventUtils::registerSwitchEventCallback(
      unit_, BCM_SWITCH_EVENT_WARM_BOOT_DOWNGRADE, fatalCob);
  BcmSwitchEventUtils::registerSwitchEventCallback(
      unit_, BCM_SWITCH_EVENT_PARITY_ERROR, nonFatalCob);

  // Create bcmStatUpdater to cache the stat ids
  bcmStatUpdater_ = std::make_unique<BcmStatUpdater>(this);

  XLOG(INFO) << " Is ALPM enabled: " << BcmAPI::isAlpmEnabled();
  // Additional switch configuration
  auto state = make_shared<SwitchState>();
  bcm_port_config_t pcfg;
  bcm_port_config_t_init(&pcfg);
  auto rv = bcm_port_config_get(unit_, &pcfg);
  bcmCheckError(rv, "failed to get port configuration");

  if (!warmBoot) {
    LOG(INFO) << " Performing cold boot ";
    /* initialize mirroring module */
    initMirrorModule();
    /* initialize MPLS */
    initMplsModule();
  } else {
    LOG(INFO) << "Performing warm boot ";
    // This dumps debug info about initial sdk state. Useful after warm boot.
    dumpState(platform_->getWarmBootHelper()->startupSdkDumpFile());
  }

  // If the platform doesn't support auto enabling l3 egress mode
  if (!platform_->getAsic()->isSupported(
          HwAsic::Feature::L3_EGRESS_MODE_AUTO_ENABLED)) {
    rv = bcm_switch_control_set(unit_, bcmSwitchL3EgressMode, 1);
    bcmCheckError(rv, "failed to set L3 egress mode");
  }

  if (getPlatform()->getAsic()->getAsicType() ==
      HwAsic::AsicType::ASIC_TYPE_TOMAHAWK4) {
    rv = bcm_l3_enable_set(unit_, 1);
    bcmCheckError(rv, "failed to enable l3");
  }

  // Trap IPv4 Address Resolution Protocol (ARP) packets.
  // TODO: We may want to trap ARP on a per-port or per-VLAN basis.
  rv = bcm_switch_control_set(unit_, bcmSwitchArpRequestToCpu, 1);
  bcmCheckError(rv, "failed to set ARP request trapping");
  rv = bcm_switch_control_set(unit_, bcmSwitchArpReplyToCpu, 1);
  bcmCheckError(rv, "failed to set ARP reply trapping");
  // Trap IP header TTL or hoplimit 1 to CPU
  rv = bcm_switch_control_set(unit_, bcmSwitchL3UcastTtl1ToCpu, 1);
  bcmCheckError(rv, "failed to set L3 header error trapping");
  // Trap DHCP packets to CPU
  rv = bcm_switch_control_set(unit_, bcmSwitchDhcpPktToCpu, 1);
  bcmCheckError(rv, "failed to set DHCP packet trapping");
  // Trap Dest miss
  rv = bcm_switch_control_set(unit_, bcmSwitchUnknownL3DestToCpu, 1);
  bcmCheckError(rv, "failed to set destination miss trapping");
  rv = bcm_switch_control_set(unit_, bcmSwitchV6L3DstMissToCpu, 1);
  bcmCheckError(rv, "failed to set IPv6 destination miss trapping");
  // Trap IPv6 Neighbor Discovery Protocol (NDP) packets.
  // TODO: We may want to trap NDP on a per-port or per-VLAN basis.
  rv = bcm_switch_control_set(unit_, bcmSwitchNdPktToCpu, 1);
  bcmCheckError(rv, "failed to set NDP trapping");

  disableHotSwap();

  if (FLAGS_force_init_fp || !warmBoot || haveMissingOrQSetChangedFPGroups()) {
    initFieldProcessor();
    setupFPGroups();
  }

  dropDhcpPackets();
  setL3MtuFailPackets();
  mmuState_ = BcmAPI::getMmuState();

  // enable IPv4 and IPv6 on CPU port
  bcm_port_t idx;
  BCM_PBMP_ITER(pcfg.cpu, idx) {
    rv = bcm_port_control_set(unit_, idx, bcmPortControlIP4, 1);
    bcmCheckError(rv, "failed to enable IPv4 on cpu port ", idx);
    rv = bcm_port_control_set(unit_, idx, bcmPortControlIP6, 1);
    bcmCheckError(rv, "failed to enable IPv6 on cpu port ", idx);
    XLOG(DBG2) << "Enabled IPv4/IPv6 on CPU port " << idx;
  }

  // Setup the default drop egress
  BcmEgress::setupDefaultDropEgress(unit_, getDropEgressId());

  setupCos();

  folly::dynamic switchStateJson;
  if (warmBoot) {
    // This needs to be done after we have set
    // bcmSwitchL3EgressMode else the egress ids
    // in the host table don't show up correctly.
    switchStateJson = getPlatform()->getWarmBootHelper()->getWarmBootState();
    warmBootCache_->populate(switchStateJson);
  }
  setupToCpuEgress();
  portTable_->initPorts(&pcfg, warmBoot);

  bstStatsMgr_->startBufferStatCollection();

  // Set the spanning tree state of all ports to forwarding.
  // TODO: Eventually the spanning tree state should be part of the Port
  // state, and this should be handled in applyConfig().
  //
  // Spanning tree group settings
  // TODO: This should eventually be done as part of applyConfig()
  bcm_stg_t stg = 1;
  BCM_PBMP_ITER(pcfg.port, idx) {
    rv = bcm_stg_stp_set(unit_, stg, idx, BCM_STG_STP_FORWARD);
    bcmCheckError(rv, "failed to set spanning tree state on port ", idx);
  }

  ret.bootType = bootType_;

  if (warmBoot) {
    ret.switchState = warmBootCache_->getDumpedSwSwitchState().clone();
    getPlatform()->preWarmbootStateApplied();
    if (switchStateJson.find(kRib) != switchStateJson.items().end()) {
      ret.rib = RoutingInformationBase::fromFollyDynamic(
          switchStateJson[kRib],
          ret.switchState->getFibs(),
          ret.switchState->getLabelForwardingInformationBase());
    }
    stateChangedImpl(StateDelta(make_shared<SwitchState>(), ret.switchState));
    hostTable_->warmBootHostEntriesSynced();
    // Done with warm boot, clear warm boot cache
    warmBootCache_->clear();
    if (getPlatform()->getAsic()->getAsicType() ==
        HwAsic::AsicType::ASIC_TYPE_TRIDENT2) {
      for (auto ip : {folly::IPAddress("0.0.0.0"), folly::IPAddress("::")}) {
        bcm_l3_route_t rt;
        bcm_l3_route_t_init(&rt);
        rt.l3a_flags |= ip.isV6() ? BCM_L3_IP6 : 0;
        bcm_l3_route_get(getUnit(), &rt);
        rt.l3a_flags |= BCM_L3_REPLACE;
        bcm_l3_route_add(getUnit(), &rt);
      }
    }
  } else {
    ret.switchState = getColdBootSwitchState();
    setMacAging(std::chrono::seconds(
        ret.switchState->getSwitchSettings()->getL2AgeTimerSeconds()));
  }

  macTable_ = std::make_unique<BcmMacTable>(this);

  ret.bootTime =
      duration_cast<duration<float>>(steady_clock::now() - begin).count();
  ret.switchState->publish();
  return ret;
}