diff --git a/buck2 b/buck2 index 5c84145371963..2d7262c04327f 100755 --- a/buck2 +++ b/buck2 @@ -4,62 +4,62 @@ "name": "buck2", "platforms": { "macos-aarch64": { - "size": 24793637, + "size": 24804372, "hash": "blake3", - "digest": "bb0035bb305bf3b6bb381b2c98063ab0912f77fcb3603a891cac4c59d17b2795", + "digest": "09468dd3a7114a853b84bd6befa2ab2dc101ca612435a3f6104a3206ad8ae4be", "format": "zst", "path": "buck2-aarch64-apple-darwin", "providers": [ { - "url": "https://github.com/facebook/buck2/releases/download/2024-11-15/buck2-aarch64-apple-darwin.zst" + "url": "https://github.com/facebook/buck2/releases/download/2024-12-02/buck2-aarch64-apple-darwin.zst" } ] }, "linux-aarch64": { - "size": 27097669, + "size": 27118699, "hash": "blake3", - "digest": "70c796a8e7470a497d6251963c1d9da1679266f4ba1fdbf44a252aeac1687d8c", + "digest": "de2c4c710767206a3441d54ea5b99c35cd18c49a5e13e280f9f0e14d2130d1f0", "format": "zst", "path": "buck2-aarch64-unknown-linux-musl", "providers": [ { - "url": "https://github.com/facebook/buck2/releases/download/2024-11-15/buck2-aarch64-unknown-linux-musl.zst" + "url": "https://github.com/facebook/buck2/releases/download/2024-12-02/buck2-aarch64-unknown-linux-musl.zst" } ] }, "macos-x86_64": { - "size": 26566564, + "size": 26609760, "hash": "blake3", - "digest": "f2d32397d72a0f3d96dd3265a525a9264ecc5aacc01c9f53ef1be7864f792c6c", + "digest": "e0f19c1ad37b9a00e9f5cc4143bec852fdbcdb063dc410fbcb3e99b4b3f383b6", "format": "zst", "path": "buck2-x86_64-apple-darwin", "providers": [ { - "url": "https://github.com/facebook/buck2/releases/download/2024-11-15/buck2-x86_64-apple-darwin.zst" + "url": "https://github.com/facebook/buck2/releases/download/2024-12-02/buck2-x86_64-apple-darwin.zst" } ] }, "windows-x86_64": { - "size": 21945285, + "size": 22070302, "hash": "blake3", - "digest": "e0ebb882535cc24439025b39a023ae2148fc9f4733ab3552dbce396f964dd549", + "digest": "be0abcf7746d803da8fd02e011db156dbda100dda281b8445faf6415827c8fe3", "format": "zst", "path": "buck2-x86_64-pc-windows-msvc.exe", "providers": [ { - "url": "https://github.com/facebook/buck2/releases/download/2024-11-15/buck2-x86_64-pc-windows-msvc.exe.zst" + "url": "https://github.com/facebook/buck2/releases/download/2024-12-02/buck2-x86_64-pc-windows-msvc.exe.zst" } ] }, "linux-x86_64": { - "size": 28049273, + "size": 28064232, "hash": "blake3", - "digest": "4ce1ccad2e051ac169d165905de2e3de68558a74e90756c71ab04fa78e38d23b", + "digest": "698197b24d042e2c38a253c92fe6b59ed09145200234dffe27cc5011913dd815", "format": "zst", "path": "buck2-x86_64-unknown-linux-musl", "providers": [ { - "url": "https://github.com/facebook/buck2/releases/download/2024-11-15/buck2-x86_64-unknown-linux-musl.zst" + "url": "https://github.com/facebook/buck2/releases/download/2024-12-02/buck2-x86_64-unknown-linux-musl.zst" } ] } diff --git a/build/fbcode_builder/getdeps.py b/build/fbcode_builder/getdeps.py index 01c6c1f68a828..5f66374f7d982 100755 --- a/build/fbcode_builder/getdeps.py +++ b/build/fbcode_builder/getdeps.py @@ -333,6 +333,12 @@ def run_project_cmd(self, args, loader, manifest): cache = cache_module.create_cache() for m in projects: + fetcher = loader.create_fetcher(m) + if isinstance(fetcher, SystemPackageFetcher): + # We are guaranteed that if the fetcher is set to + # SystemPackageFetcher then this item is completely + # satisfied by the appropriate system packages + continue cached_project = CachedProject(cache, loader, m) if cached_project.download(): continue @@ -348,7 +354,6 @@ def run_project_cmd(self, args, loader, manifest): continue # We need to fetch the sources - fetcher = loader.create_fetcher(m) fetcher.update() diff --git a/build/fbcode_builder/getdeps/manifest.py b/build/fbcode_builder/getdeps/manifest.py index e329da1ee69ce..5cec33aabba72 100644 --- a/build/fbcode_builder/getdeps/manifest.py +++ b/build/fbcode_builder/getdeps/manifest.py @@ -430,23 +430,27 @@ def _create_fetcher(self, build_options, ctx): # We can use the code from fbsource return ShipitTransformerFetcher(build_options, self.shipit_project) + # If both of these are None, the package can only be coming from + # preinstalled toolchain or system packages + repo_url = self.get_repo_url(ctx) + url = self.get("download", "url", ctx=ctx) + # Can we satisfy this dep with system packages? - if build_options.allow_system_packages: + if (repo_url is None and url is None) or build_options.allow_system_packages: if self._is_satisfied_by_preinstalled_environment(ctx): return PreinstalledNopFetcher() - packages = self.get_required_system_packages(ctx) - package_fetcher = SystemPackageFetcher(build_options, packages) - if package_fetcher.packages_are_installed(): - return package_fetcher + if build_options.host_type.get_package_manager(): + packages = self.get_required_system_packages(ctx) + package_fetcher = SystemPackageFetcher(build_options, packages) + if package_fetcher.packages_are_installed(): + return package_fetcher - repo_url = self.get_repo_url(ctx) if repo_url: rev = self.get("git", "rev") depth = self.get("git", "depth") return GitFetcher(build_options, self, repo_url, rev, depth) - url = self.get("download", "url", ctx=ctx) if url: # We need to defer this import until now to avoid triggering # a cycle when the facebook/__init__.py is loaded. @@ -464,7 +468,8 @@ def _create_fetcher(self, build_options, ctx): ) raise KeyError( - "project %s has no fetcher configuration matching %s" % (self.name, ctx) + "project %s has no fetcher configuration or system packages matching %s" + % (self.name, ctx) ) def create_fetcher(self, build_options, loader, ctx): diff --git a/build/fbcode_builder/manifests/openssl b/build/fbcode_builder/manifests/openssl index beef31c9e2e04..7dd40727cc2e8 100644 --- a/build/fbcode_builder/manifests/openssl +++ b/build/fbcode_builder/manifests/openssl @@ -5,7 +5,7 @@ name = openssl libssl-dev [homebrew] -openssl@1.1 +openssl # on homebrew need the matching curl and ca- [rpms] @@ -16,9 +16,11 @@ openssl-libs [pps] openssl -[download] -url = https://www.openssl.org/source/openssl-1.1.1l.tar.gz -sha256 = 0b7a3e5e59c34827fe0c3a74b7ec8baef302b98fa80088d7f9153aa16fa76bd1 +# no need to download on the systems where we always use the system libs +[download.not(any(os=linux, os=freebsd))] +# match the openssl version packages in ubuntu LTS folly current supports +url = https://www.openssl.org/source/openssl-3.0.15.tar.gz +sha256 = 23c666d0edf20f14249b3d8f0368acaee9ab585b09e1de82107c66e1f3ec9533 # We use the system openssl on these platforms even without --allow-system-packages [build.any(os=linux, os=freebsd)] @@ -26,7 +28,7 @@ builder = nop [build.not(any(os=linux, os=freebsd))] builder = openssl -subdir = openssl-1.1.1l +subdir = openssl-3.0.15 [dependencies.os=windows] perl diff --git a/cmake/AgentPacket.cmake b/cmake/AgentPacket.cmake index f804c3f543d6f..d5c30a25e452b 100644 --- a/cmake/AgentPacket.cmake +++ b/cmake/AgentPacket.cmake @@ -60,4 +60,5 @@ target_link_libraries(packet_factory ctrl_cpp2 switch_config_cpp2 Folly::folly + sflow_structs ) diff --git a/cmake/AgentTestAgentHwTests.cmake b/cmake/AgentTestAgentHwTests.cmake index f5c870f78efa6..565c62773bdda 100644 --- a/cmake/AgentTestAgentHwTests.cmake +++ b/cmake/AgentTestAgentHwTests.cmake @@ -49,6 +49,7 @@ add_library(agent_hw_test_src fboss/agent/test/agent_hw_tests/AgentMmuTuningTests.cpp fboss/agent/test/agent_hw_tests/AgentSflowMirrorTest.cpp fboss/agent/test/agent_hw_tests/AgentAclPriorityTests.cpp + fboss/agent/test/agent_hw_tests/AgentTrafficPauseTests.cpp fboss/agent/test/agent_hw_tests/AgentTrunkLoadBalancerTests.cpp fboss/agent/test/agent_hw_tests/AgentTrunkTests.cpp fboss/agent/test/agent_hw_tests/AgentRxReasonTests.cpp diff --git a/fboss/agent/ApplyThriftConfig.cpp b/fboss/agent/ApplyThriftConfig.cpp index 34ad9a53e24d7..ddbe445173d32 100644 --- a/fboss/agent/ApplyThriftConfig.cpp +++ b/fboss/agent/ApplyThriftConfig.cpp @@ -2505,7 +2505,9 @@ shared_ptr ThriftConfigApplier::updatePort( portFlowletConfigUnchanged && newFlowletConfigName == orig->getFlowletConfigName() && *portConf->conditionalEntropyRehash() == - orig->getConditionalEntropyRehash()) { + orig->getConditionalEntropyRehash() && + portConf->selfHealingECMPLagEnable().value_or(false) == + orig->getSelfHealingECMPLagEnable()) { return nullptr; } @@ -2549,6 +2551,16 @@ shared_ptr ThriftConfigApplier::updatePort( newPort->setPortFlowletConfig(portFlowletCfg); newPort->setScope(*portConf->scope()); newPort->setConditionalEntropyRehash(*portConf->conditionalEntropyRehash()); + if (auto selfHealingECMPLagEnable = portConf->selfHealingECMPLagEnable()) { + if (*selfHealingECMPLagEnable && + !cfg_->switchSettings()->selfHealingEcmpLagConfig().has_value()) { + throw FbossError( + "Switch selfHealingEcmpLagConfig needs to be enabled for port ", + newPort->getName(), + " to have selfHealingEcmpLag enable"); + } + newPort->setSelfHealingECMPLagEnable(*selfHealingECMPLagEnable); + } return newPort; } @@ -4838,6 +4850,19 @@ shared_ptr ThriftConfigApplier::updateSwitchSettings( switchSettingsChange = true; } + { + // SelfHealingEcmpLag switch configurations + std::optional newSwitchShelConfig; + if (cfg_->switchSettings()->selfHealingEcmpLagConfig()) { + newSwitchShelConfig = *cfg_->switchSettings()->selfHealingEcmpLagConfig(); + } + if (newSwitchShelConfig != + origSwitchSettings->getSelfHealingEcmpLagConfig()) { + newSwitchSettings->setSelfHealingEcmpLagConfig(newSwitchShelConfig); + switchSettingsChange = true; + } + } + if (switchSettingsChange) { return newSwitchSettings; } diff --git a/fboss/agent/hw/sai/switch/SaiPortManager.cpp b/fboss/agent/hw/sai/switch/SaiPortManager.cpp index a10319c429bcf..303458e70a528 100644 --- a/fboss/agent/hw/sai/switch/SaiPortManager.cpp +++ b/fboss/agent/hw/sai/switch/SaiPortManager.cpp @@ -1526,11 +1526,11 @@ std::shared_ptr SaiPortManager::swPortFromAttributes( #endif port->setScope(platform_->getPlatformMapping()->getPortScope(port->getID())); -// TODO(zecheng): Update flag when new 12.0 release has the attribute -#if defined(BRCM_SAI_SDK_DNX_GTE_11_0) && !defined(BRCM_SAI_SDK_DNX_GTE_12_0) - port->setReachabilityGroupId( - GET_OPT_ATTR(Port, CondEntropyRehashEnable, attributes)); +#if defined(SAI_VERSION_11_7_0_0_DNX_ODP) + auto shelEnable = GET_OPT_ATTR(Port, ShelEnable, attributes); + port->setSelfHealingECMPLagEnable(shelEnable); #endif + return port; } diff --git a/fboss/agent/hw/sai/switch/npu/SaiPortManager.cpp b/fboss/agent/hw/sai/switch/npu/SaiPortManager.cpp index d31874efcbc72..25c77be775879 100644 --- a/fboss/agent/hw/sai/switch/npu/SaiPortManager.cpp +++ b/fboss/agent/hw/sai/switch/npu/SaiPortManager.cpp @@ -661,6 +661,11 @@ SaiPortTraits::CreateAttributes SaiPortManager::attributesFromSwPort( #if defined(BRCM_SAI_SDK_DNX_GTE_11_0) && !defined(BRCM_SAI_SDK_DNX_GTE_12_0) condEntropyRehashEnable = swPort->getConditionalEntropyRehash(); #endif + std::optional shelEnable{}; +#if defined(SAI_VERSION_11_7_0_0_DNX_ODP) + shelEnable = SaiPortTraits::Attributes::ShelEnable{ + swPort->getSelfHealingECMPLagEnable()}; +#endif if (basicAttributeOnly) { return SaiPortTraits::CreateAttributes{ @@ -799,10 +804,10 @@ SaiPortTraits::CreateAttributes SaiPortManager::attributesFromSwPort( arsPortLoadFutureWeight, // ARS port load future weight #endif reachabilityGroup, - condEntropyRehashEnable, // CondEntropyRehashEnable + condEntropyRehashEnable, std::nullopt, // CondEntropyRehashPeriodUS std::nullopt, // CondEntropyRehashSeed - std::nullopt, // ShelEnable + shelEnable, }; } diff --git a/fboss/agent/packet/BUCK b/fboss/agent/packet/BUCK index df015b6dc1654..d7998861ac057 100644 --- a/fboss/agent/packet/BUCK +++ b/fboss/agent/packet/BUCK @@ -85,6 +85,7 @@ cpp_library( "//fboss/agent:fboss-types", "//fboss/agent:hw_switch", "//fboss/agent:packet", + "//fboss/agent/packet:sflow_structs", "//folly:network_address", "//folly/io:iobuf", ], diff --git a/fboss/agent/packet/PktFactory.cpp b/fboss/agent/packet/PktFactory.cpp index 447d7427d0b72..b25f8548e8967 100644 --- a/fboss/agent/packet/PktFactory.cpp +++ b/fboss/agent/packet/PktFactory.cpp @@ -789,4 +789,264 @@ std::unique_ptr makeTCPTxPacket( 255, payload); } + +template +std::unique_ptr makeSflowV5Packet( + const AllocatePktFn& allocatePacket, + const EthHdr& ethHdr, + const IPHDR& ipHdr, + const UDPHeader& udpHdr, + bool computeChecksum, + folly::IPAddress agentIp, + uint32_t ingressInterface, + uint32_t egressInterface, + uint32_t samplingRate, + const std::vector& payload) { + auto txPacket = allocatePacket( + ethHdr.size() + ipHdr.size() + udpHdr.size() + 104 + payload.size()); + + folly::io::RWPrivateCursor rwCursor(txPacket->buf()); + // Write EthHdr + writeEthHeader( + txPacket, + &rwCursor, + ethHdr.getDstMac(), + ethHdr.getSrcMac(), + ethHdr.getVlanTags(), + ethHdr.getEtherType()); + ipHdr.serialize(&rwCursor); + + // write UDP header, payload and compute checksum + rwCursor.writeBE(udpHdr.srcPort); + rwCursor.writeBE(udpHdr.dstPort); + rwCursor.writeBE(udpHdr.length); + // Skip 2 bytes and compuete checksum later + rwCursor.skip(2); + + sflow::SampleDatagram datagram; + sflow::SampleDatagramV5 datagramV5; + sflow::SampleRecord record; + sflow::FlowSample fsample; + sflow::FlowRecord frecord; + sflow::SampledHeader hdr; + + int bufSize = 1024; + + hdr.protocol = sflow::HeaderProtocol::ETHERNET_ISO88023; + hdr.frameLength = 0; + hdr.stripped = 0; + hdr.header = payload.data(); + hdr.headerLength = payload.size(); + auto hdrSize = hdr.size(); + if (hdrSize % sflow::XDR_BASIC_BLOCK_SIZE > 0) { + hdrSize = (hdrSize / sflow::XDR_BASIC_BLOCK_SIZE + 1) * + sflow::XDR_BASIC_BLOCK_SIZE; + } + + std::vector hb(bufSize); + auto hbuf = folly::IOBuf::wrapBuffer(hb.data(), bufSize); + auto hc = std::make_shared(hbuf.get()); + hdr.serialize(hc.get()); + + frecord.flowFormat = 1; // single flow sample + frecord.flowDataLen = hdrSize; + frecord.flowData = hb.data(); + + fsample.sequenceNumber = 0; + fsample.sourceID = 0; + fsample.samplingRate = samplingRate; + fsample.samplePool = 0; + fsample.drops = 0; + fsample.input = ingressInterface; + fsample.output = egressInterface; + fsample.flowRecordsCnt = 1; + fsample.flowRecords = &frecord; + + std::vector fsb(bufSize); + auto fbuf = folly::IOBuf::wrapBuffer(fsb.data(), bufSize); + auto fc = std::make_shared(fbuf.get()); + fsample.serialize(fc.get()); + size_t fsampleSize = bufSize - fc->length(); + + record.sampleType = 1; // raw header + record.sampleDataLen = fsampleSize; + record.sampleData = fsb.data(); + + datagramV5.agentAddress = agentIp; + datagramV5.subAgentID = 0; // no sub agent + datagramV5.sequenceNumber = 0; // not used + datagramV5.uptime = 0; // not used + datagramV5.samplesCnt = 1; // So far only 1 sample encapsuled + datagramV5.samples = &record; + + datagram.datagramV5 = datagramV5; + datagram.serialize(&rwCursor); + + if (computeChecksum) { + // Need to revisit when we compute the checksum of UDP payload + // folly::io::Cursor payloadStart(rwCursor); + // uint16_t csum = udpHdr.computeChecksum(ipHdr, payloadStart); + // csumCursor.writeBE(csum); + } + return txPacket; +} + +std::unique_ptr makeSflowV5Packet( + const AllocatePktFn& allocator, + std::optional vlan, + folly::MacAddress srcMac, + folly::MacAddress dstMac, + const folly::IPAddressV4& srcIp, + const folly::IPAddressV4& dstIp, + uint16_t srcPort, + uint16_t dstPort, + uint8_t dscp, + uint8_t ttl, + uint32_t ingressInterface, + uint32_t egressInterface, + uint32_t samplingRate, + bool computeChecksum, + std::optional> payload) { + if (!payload) { + payload = kDefaultPayload; + } + const auto& payloadBytes = payload.value(); + // EthHdr + auto ethHdr = makeEthHdr(srcMac, dstMac, vlan, ETHERTYPE::ETHERTYPE_IPV4); + // TODO: This assumes the Sflow V5 packet contains one sample header + // and one sample record. Need to be computed dynamically. + auto sampleHdrSize = 104; + + // IPv4Hdr - total_length field includes the payload + UDP hdr + ip hdr + IPv4Hdr ipHdr( + srcIp, + dstIp, + static_cast(IP_PROTO::IP_PROTO_UDP), + payloadBytes.size() + UDPHeader::size() + sampleHdrSize); + ipHdr.dscp = dscp; + ipHdr.ttl = ttl; + ipHdr.computeChecksum(); + // UDPHeader + UDPHeader udpHdr( + srcPort, + dstPort, + UDPHeader::size() + payloadBytes.size() + sampleHdrSize); + + return makeSflowV5Packet( + allocator, + ethHdr, + ipHdr, + udpHdr, + computeChecksum, + folly::IPAddress(srcIp), + ingressInterface, + egressInterface, + samplingRate, + payloadBytes); +} + +std::unique_ptr makeSflowV5Packet( + const AllocatePktFn& allocator, + std::optional vlan, + folly::MacAddress srcMac, + folly::MacAddress dstMac, + const folly::IPAddressV6& srcIp, + const folly::IPAddressV6& dstIp, + uint16_t srcPort, + uint16_t dstPort, + uint8_t trafficClass, + uint8_t hopLimit, + uint32_t ingressInterface, + uint32_t egressInterface, + uint32_t samplingRate, + bool computeChecksum, + std::optional> payload) { + if (!payload) { + payload = kDefaultPayload; + } + const auto& payloadBytes = payload.value(); + // EthHdr + auto ethHdr = makeEthHdr(srcMac, dstMac, vlan, ETHERTYPE::ETHERTYPE_IPV6); + // TODO: This assumes the Sflow V5 packet contains one sample header + // and one sample record. Need to be computed dynamically. + auto sampleHdrSize = 104; + + // IPv6Hdr + IPv6Hdr ipHdr(srcIp, dstIp); + ipHdr.nextHeader = static_cast(IP_PROTO::IP_PROTO_UDP); + ipHdr.trafficClass = trafficClass; + ipHdr.payloadLength = UDPHeader::size() + payloadBytes.size() + sampleHdrSize; + ipHdr.hopLimit = hopLimit; + // UDPHeader + UDPHeader udpHdr( + srcPort, + dstPort, + UDPHeader::size() + payloadBytes.size() + sampleHdrSize); + + return makeSflowV5Packet( + allocator, + ethHdr, + ipHdr, + udpHdr, + computeChecksum, + folly::IPAddress(srcIp), + ingressInterface, + egressInterface, + samplingRate, + payloadBytes); +} + +std::unique_ptr makeSflowV5Packet( + const AllocatePktFn& allocator, + std::optional vlan, + folly::MacAddress srcMac, + folly::MacAddress dstMac, + const folly::IPAddress& srcIp, + const folly::IPAddress& dstIp, + uint16_t srcPort, + uint16_t dstPort, + uint8_t trafficClass, + uint8_t hopLimit, + uint32_t ingressInterface, + uint32_t egressInterface, + uint32_t samplingRate, + bool computeChecksum, + std::optional> payload) { + CHECK_EQ(srcIp.isV6(), dstIp.isV6()); + if (srcIp.isV6()) { + return makeSflowV5Packet( + allocator, + vlan, + srcMac, + dstMac, + srcIp.asV6(), + dstIp.asV6(), + srcPort, + dstPort, + trafficClass, + hopLimit, + ingressInterface, + egressInterface, + samplingRate, + computeChecksum, + payload); + } + return makeSflowV5Packet( + allocator, + vlan, + srcMac, + dstMac, + srcIp.asV4(), + dstIp.asV4(), + srcPort, + dstPort, + trafficClass, + hopLimit, + ingressInterface, + egressInterface, + samplingRate, + computeChecksum, + payload); +} + } // namespace facebook::fboss::utility diff --git a/fboss/agent/packet/PktFactory.h b/fboss/agent/packet/PktFactory.h index 67358c7f3a819..abb73b8ce92cd 100644 --- a/fboss/agent/packet/PktFactory.h +++ b/fboss/agent/packet/PktFactory.h @@ -8,6 +8,7 @@ #include "fboss/agent/packet/ArpHdr.h" #include "fboss/agent/packet/EthHdr.h" #include "fboss/agent/packet/PTPHeader.h" +#include "fboss/agent/packet/SflowStructs.h" #include @@ -433,6 +434,58 @@ std::unique_ptr makeTCPTxPacket( payload); } -} // namespace utility +std::unique_ptr makeSflowV5Packet( + const AllocatePktFn& allocator, + std::optional vlan, + folly::MacAddress srcMac, + folly::MacAddress dstMac, + const folly::IPAddress& srcIp, + const folly::IPAddress& dstIp, + uint16_t srcPort, + uint16_t dstPort, + uint8_t trafficClass, + uint8_t hopLimit, + uint32_t ingressInterface, + uint32_t egressInterface, + uint32_t samplingRate, + bool computeChecksum, + std::optional> payload); + +template +std::unique_ptr makeSflowV5Packet( + const SwitchT* switchT, + std::optional vlan, + folly::MacAddress srcMac, + folly::MacAddress dstMac, + const folly::IPAddress& srcIp, + const folly::IPAddress& dstIp, + uint16_t srcPort, + uint16_t dstPort, + uint8_t trafficClass, + uint8_t hopLimit, + uint32_t ingressInterface, + uint32_t egressInterface, + uint32_t samplingRate, + bool computeChecksum, + std::optional> payload = + std::optional>()) { + return makeSflowV5Packet( + makeAllocator(switchT), + vlan, + srcMac, + dstMac, + srcIp, + dstIp, + srcPort, + dstPort, + trafficClass, + hopLimit, + ingressInterface, + egressInterface, + samplingRate, + computeChecksum, + payload); +} +} // namespace utility } // namespace facebook::fboss diff --git a/fboss/agent/test/agent_hw_tests/AgentTrafficPauseTests.cpp b/fboss/agent/test/agent_hw_tests/AgentTrafficPauseTests.cpp new file mode 100644 index 0000000000000..1ac50cd276265 --- /dev/null +++ b/fboss/agent/test/agent_hw_tests/AgentTrafficPauseTests.cpp @@ -0,0 +1,196 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#include "fboss/agent/TxPacket.h" +#include "fboss/agent/packet/PktFactory.h" +#include "fboss/agent/test/AgentHwTest.h" +#include "fboss/agent/test/EcmpSetupHelper.h" +#include "fboss/agent/test/utils/ConfigUtils.h" +#include "fboss/agent/test/utils/CoppTestUtils.h" +#include "fboss/agent/test/utils/OlympicTestUtils.h" +#include "fboss/agent/test/utils/QosTestUtils.h" + +namespace facebook::fboss { + +class AgentTrafficPauseTest : public AgentHwTest { + public: + const folly::IPAddressV6 kDestIp{ + folly::IPAddressV6("2620:0:1cfe:face:b00c::4")}; + + cfg::SwitchConfig initialConfig( + const AgentEnsemble& ensemble) const override { + auto config = utility::onePortPerInterfaceConfig( + ensemble.getSw(), + ensemble.masterLogicalPortIds(), + true /*interfaceHasSubnet*/); + utility::setTTLZeroCpuConfig(ensemble.getL3Asics(), config); + return config; + } + + void setupEcmpTraffic(const PortID& portId) { + utility::EcmpSetupTargetedPorts6 ecmpHelper{ + getProgrammedState(), + utility::getFirstInterfaceMac(getProgrammedState())}; + + const PortDescriptor port(portId); + RoutePrefixV6 route{kDestIp, 128}; + + applyNewState([&](const std::shared_ptr& state) { + return ecmpHelper.resolveNextHops(state, {port}); + }); + + auto routeUpdater = getSw()->getRouteUpdater(); + ecmpHelper.programRoutes(&routeUpdater, {port}, {route}); + + utility::ttlDecrementHandlingForLoopbackTraffic( + getAgentEnsemble(), ecmpHelper.getRouterId(), ecmpHelper.nhop(port)); + } + + void sendPauseFrames(const PortID& portId, const int count) { + // PAUSE frame to have the highest quanta of 0xffff + std::vector payload{0x00, 0x01, 0xff, 0xff}; + std::vector padding(42, 0); + payload.insert(payload.end(), padding.begin(), padding.end()); + auto intfMac = utility::getFirstInterfaceMac(getProgrammedState()); + for (int idx = 0; idx < count; idx++) { + auto pkt = utility::makeEthTxPacket( + getSw(), + utility::firstVlanID(getProgrammedState()), + utility::MacAddressGenerator().get(intfMac.u64NBO() + 1), + folly::MacAddress("01:80:C2:00:00:01"), + ETHERTYPE::ETHERTYPE_EPON, + payload); + // Inject pause frames on highest priority queue to avoid drops + getSw()->sendPacketOutOfPortAsync( + std::move(pkt), + portId, + utility::getOlympicQueueId(utility::OlympicQueueType::NC)); + } + } + + void pumpTraffic(const PortID& portId) { + auto vlanId = utility::firstVlanID(getProgrammedState()); + auto intfMac = utility::getFirstInterfaceMac(getProgrammedState()); + auto dscp = utility::kOlympicQueueToDscp().at(0).front(); + auto srcMac = utility::MacAddressGenerator().get(intfMac.u64NBO() + 1); + for (int i = 0; i < getAgentEnsemble()->getMinPktsForLineRate(portId); + i++) { + auto txPacket = utility::makeUDPTxPacket( + getSw(), + vlanId, + srcMac, + intfMac, + folly::IPAddressV6("2620:0:1cfe:face:b00c::3"), + kDestIp, + 8000, + 8001, + dscp << 2, + 255, + std::vector(2000, 0xff)); + getAgentEnsemble()->sendPacketAsync( + std::move(txPacket), PortDescriptor(portId), std::nullopt); + } + } + + std::vector + getProductionFeaturesVerified() const override { + return {production_features::ProductionFeature::PAUSE}; + } + + void validateTrafficWithPause( + cfg::PortPause& pauseCfg, + std::function rateChecker) { + const PortID kPortId{masterLogicalInterfacePortIds()[0]}; + auto setup = [&]() { + auto cfg = getAgentEnsemble()->getCurrentConfig(); + auto portCfg = std::find_if( + cfg.ports()->begin(), cfg.ports()->end(), [&kPortId](auto& port) { + return PortID(*port.logicalID()) == kPortId; + }); + portCfg->pause() = std::move(pauseCfg); + applyNewConfig(cfg); + setupEcmpTraffic(kPortId); + }; + auto verify = [&]() { + pumpTraffic(kPortId); + getAgentEnsemble()->waitForLineRateOnPort(kPortId); + auto curPortStats = getLatestPortStats(kPortId); + // Now that we have line rate traffic, send pause + // which should break the traffic loop + XLOG(DBG0) + << "Traffic on port reached line rate, now send back to back PAUSE!"; + std::atomic keepTxingPauseFrames{true}; + std::unique_ptr pauseTxThread = + std::make_unique( + [this, &keepTxingPauseFrames, &kPortId]() { + initThread("PauseFramesTransmitThread"); + while (keepTxingPauseFrames) { + this->sendPauseFrames(kPortId, 1000); + } + }); + HwPortStats prevPortStats{}; + WITH_RETRIES({ + curPortStats = getLatestPortStats(kPortId); + auto rate = + getAgentEnsemble()->getTrafficRate(prevPortStats, curPortStats, 1); + // Update prev stats for the next iteration + prevPortStats = curPortStats; + XLOG(DBG0) << "Current rate is : " << rate + << " bps, pause frames received: " + << curPortStats.inPause_().value(); + EXPECT_EVENTUALLY_TRUE(rateChecker(rate, kPortId)); + }); + keepTxingPauseFrames = false; + pauseTxThread->join(); + pauseTxThread.reset(); + }; + verifyAcrossWarmBoots(setup, verify); + } +}; +TEST_F(AgentTrafficPauseTest, verifyPauseRxOnly) { + // Enable pause RX alone + cfg::PortPause pauseCfg; + pauseCfg.tx() = false; + pauseCfg.rx() = true; + // Pause should slow down traffic significantly, lets say eventual + // rate should be less than 80% line rate! + auto rateChecker = [this](uint64_t rate, const PortID& portId) { + auto eightyPctLineRate = + static_cast( + getProgrammedState()->getPorts()->getNodeIf(portId)->getSpeed()) * + 1000 * 1000 * 0.8; + return rate && rate < eightyPctLineRate; + }; + validateTrafficWithPause(pauseCfg, rateChecker); +} +TEST_F(AgentTrafficPauseTest, verifyPauseTxOnly) { + // Enable pause TX alone + cfg::PortPause pauseCfg; + pauseCfg.tx() = true; + pauseCfg.rx() = false; + // Pause should have no impact on traffic given only TX is enabled + auto rateChecker = [this](uint64_t rate, const PortID& portId) { + auto lineRate = + static_cast( + getProgrammedState()->getPorts()->getNodeIf(portId)->getSpeed()) * + 1000 * 1000; + return rate >= lineRate; + }; + validateTrafficWithPause(pauseCfg, rateChecker); +} +TEST_F(AgentTrafficPauseTest, verifyPauseRxTx) { + // Enable both RX and TX pause + cfg::PortPause pauseCfg; + pauseCfg.tx() = true; + pauseCfg.rx() = true; + // Pause should slow down traffic significantly, lets say eventual + // rate should be less than 80% line rate! + auto rateChecker = [this](uint64_t rate, const PortID& portId) { + auto eightyPctLineRate = + static_cast( + getProgrammedState()->getPorts()->getNodeIf(portId)->getSpeed()) * + 1000 * 1000 * 0.8; + return rate && rate < eightyPctLineRate; + }; + validateTrafficWithPause(pauseCfg, rateChecker); +} +} // namespace facebook::fboss diff --git a/fboss/agent/test/agent_hw_tests/AgentVoqSwitchTests.cpp b/fboss/agent/test/agent_hw_tests/AgentVoqSwitchTests.cpp index 50e24ea9bb421..20ffc759d5507 100644 --- a/fboss/agent/test/agent_hw_tests/AgentVoqSwitchTests.cpp +++ b/fboss/agent/test/agent_hw_tests/AgentVoqSwitchTests.cpp @@ -2405,7 +2405,10 @@ TEST_F(AgentVoqSwitchFullScaleDsfNodesTest, stressProgramEcmpRoutes) { TEST_F(AgentVoqSwitchLineRateTest, dramBlockedTime) { auto setup = [=, this]() { - constexpr int kNumberOfPortsForDramBlock{6}; + // Use just one port for the dramBlockedTime test + // Note: If more than 3 ports are used, then traffic wouldn't + // reach line rate which is a prerequisite for this test + constexpr int kNumberOfPortsForDramBlock{1}; setupEcmpDataplaneLoopOnAllPorts(); createTrafficOnMultiplePorts(kNumberOfPortsForDramBlock); }; diff --git a/fboss/agent/test/agent_hw_tests/BUCK b/fboss/agent/test/agent_hw_tests/BUCK index 26744ba75d3e5..1e0432034fc21 100644 --- a/fboss/agent/test/agent_hw_tests/BUCK +++ b/fboss/agent/test/agent_hw_tests/BUCK @@ -65,6 +65,7 @@ cpp_library( "AgentStateReconstructionTests.cpp", "AgentSwitchStateReplayTest.cpp", "AgentSwitchStatsTxCounterTests.cpp", + "AgentTrafficPauseTests.cpp", "AgentTrafficPfcTests.cpp", "AgentTrunkLoadBalancerTests.cpp", "AgentTrunkTests.cpp",