From 9d53b09450fe6022bd252ca063313e473e7a3569 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Fri, 9 Aug 2024 17:31:15 +0200 Subject: [PATCH] wifi: Add main PHY switch trace source to EMLSR manager Based on suggestions from Sharan Naribole --- RELEASE_NOTES.md | 1 + src/wifi/model/channel-access-manager.cc | 6 + src/wifi/model/channel-access-manager.h | 5 + src/wifi/model/eht/advanced-emlsr-manager.cc | 48 +++++- src/wifi/model/eht/advanced-emlsr-manager.h | 22 +++ src/wifi/model/eht/default-emlsr-manager.cc | 42 +++-- src/wifi/model/eht/default-emlsr-manager.h | 4 +- src/wifi/model/eht/emlsr-manager.cc | 22 ++- src/wifi/model/eht/emlsr-manager.h | 171 ++++++++++++++++++- src/wifi/test/wifi-emlsr-test.cc | 10 +- 10 files changed, 298 insertions(+), 33 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6f23a0563..edb9246ac 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -43,6 +43,7 @@ The required Doxygen version for documentation generation is version 1.11. - (wifi) Added a new `BaEstablished` trace source to `QosTxop` to notify that a block ack agreement has been established with a given recipient for a given TID. - (zigbee) Added Zigbee module support. - (energy) Added new information and reformatted energy module documentation. +- (wifi) Added a new `MainPhySwitch` trace source to EmlsrManager, which is fired when the main PHY switches channel to operate on another link and provides information about the reason for starting the switch. ### Bugs fixed diff --git a/src/wifi/model/channel-access-manager.cc b/src/wifi/model/channel-access-manager.cc index 961f152b5..a64af1d7a 100644 --- a/src/wifi/model/channel-access-manager.cc +++ b/src/wifi/model/channel-access-manager.cc @@ -722,6 +722,12 @@ ChannelAccessManager::GetBackoffEndFor(Ptr txop, Time accessGrantStart) co return backoffEnd; } +Time +ChannelAccessManager::GetNavEnd() const +{ + return m_lastNavEnd; +} + void ChannelAccessManager::UpdateBackoff() { diff --git a/src/wifi/model/channel-access-manager.h b/src/wifi/model/channel-access-manager.h index 4be1908ba..40f7cf572 100644 --- a/src/wifi/model/channel-access-manager.h +++ b/src/wifi/model/channel-access-manager.h @@ -170,6 +170,11 @@ class ChannelAccessManager : public Object */ Time GetBackoffEndFor(Ptr txop) const; + /** + * @return the time until the NAV has been set + */ + Time GetNavEnd() const; + /** * @param qosTxop a QosTxop that needs to be disabled * @param duration the amount of time during which the QosTxop is disabled diff --git a/src/wifi/model/eht/advanced-emlsr-manager.cc b/src/wifi/model/eht/advanced-emlsr-manager.cc index 6c74c5164..5c7a3dec0 100644 --- a/src/wifi/model/eht/advanced-emlsr-manager.cc +++ b/src/wifi/model/eht/advanced-emlsr-manager.cc @@ -305,17 +305,27 @@ AdvancedEmlsrManager::DoNotifyTxopEnd(uint8_t linkId) !m_switchAuxPhy || m_mainPhySwitchInfo.end >= Simulator::Now(), "Aux PHY next link ID should have a value when interrupting a main PHY switch"); uint8_t nextLinkId = m_switchAuxPhy ? m_mainPhySwitchInfo.from : GetMainPhyId(); - SwitchMainPhy(nextLinkId, false, DONT_RESET_BACKOFF, REQUEST_ACCESS); + const auto delay = mainPhy->IsStateSwitching() ? mainPhy->GetDelayUntilIdle() : Time{0}; + SwitchMainPhy(nextLinkId, + false, + DONT_RESET_BACKOFF, + REQUEST_ACCESS, + EmlsrTxopEndedTrace(delay)); } else { // delay link switch until current channel switching is completed - Simulator::Schedule(mainPhy->GetDelayUntilIdle(), [=, this]() { + const auto delay = mainPhy->GetDelayUntilIdle(); + Simulator::Schedule(delay, [=, this]() { // request the main PHY to switch back to the preferred link only if in the meantime // no TXOP started on another link (which will require the main PHY to switch link) if (!GetEhtFem(linkId)->UsingOtherEmlsrLink()) { - SwitchMainPhy(GetMainPhyId(), false, DONT_RESET_BACKOFF, REQUEST_ACCESS); + SwitchMainPhy(GetMainPhyId(), + false, + DONT_RESET_BACKOFF, + REQUEST_ACCESS, + EmlsrTxopEndedTrace(delay)); } }); } @@ -385,7 +395,7 @@ AdvancedEmlsrManager::CheckNavAndCcaLastPifs(Ptr phy, uint8_t linkId, P else if (!m_switchAuxPhy) { // switch main PHY back to preferred link if SwitchAuxPhy is false - SwitchMainPhyBackToPreferredLink(linkId); + SwitchMainPhyBackToPreferredLink(linkId, EmlsrSwitchMainPhyBackTrace(true)); } }); } @@ -405,7 +415,7 @@ AdvancedEmlsrManager::CheckNavAndCcaLastPifs(Ptr phy, uint8_t linkId, P m_switchMainPhyBackEvent = Simulator::Schedule(m_switchMainPhyBackDelay, [this, linkId]() { if (!m_switchAuxPhy) { - SwitchMainPhyBackToPreferredLink(linkId); + SwitchMainPhyBackToPreferredLink(linkId, EmlsrSwitchMainPhyBackTrace(false)); } }); } @@ -578,7 +588,18 @@ AdvancedEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex } // switch main PHY - SwitchMainPhy(linkId, false, RESET_BACKOFF, DONT_REQUEST_ACCESS); + Time remNav{0}; + if (const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy)) + { + auto mainPhyNavEnd = GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetNavEnd(); + remNav = Max(remNav, mainPhyNavEnd - Simulator::Now()); + } + + SwitchMainPhy(linkId, + false, + RESET_BACKOFF, + DONT_REQUEST_ACCESS, + EmlsrUlTxopAuxPhyNotTxCapableTrace(aci, Time{0}, remNav)); return; } @@ -678,7 +699,18 @@ AdvancedEmlsrManager::SwitchMainPhyIfTxopToBeGainedByAuxPhy(uint8_t linkId, } // switch main PHY - SwitchMainPhy(linkId, false, RESET_BACKOFF, DONT_REQUEST_ACCESS); + Time remNav{0}; + if (const auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy)) + { + auto mainPhyNavEnd = GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetNavEnd(); + remNav = Max(remNav, mainPhyNavEnd - Simulator::Now()); + } + + SwitchMainPhy(linkId, + false, + RESET_BACKOFF, + DONT_REQUEST_ACCESS, + EmlsrUlTxopAuxPhyNotTxCapableTrace(aci, delay, remNav)); // if the remaining backoff time is shorter than PIFS when the main PHY completes the switch, // we need to schedule a CCA check a PIFS after the end of the main PHY switch @@ -710,7 +742,7 @@ AdvancedEmlsrManager::SwitchMainPhyIfTxopToBeGainedByAuxPhy(uint8_t linkId, Simulator::Schedule(minDelay + m_switchMainPhyBackDelay, [this, linkId]() { if (!m_switchAuxPhy) { - SwitchMainPhyBackToPreferredLink(linkId); + SwitchMainPhyBackToPreferredLink(linkId, EmlsrSwitchMainPhyBackTrace(false)); } }); } diff --git a/src/wifi/model/eht/advanced-emlsr-manager.h b/src/wifi/model/eht/advanced-emlsr-manager.h index 270e73dbb..c2396e88e 100644 --- a/src/wifi/model/eht/advanced-emlsr-manager.h +++ b/src/wifi/model/eht/advanced-emlsr-manager.h @@ -116,6 +116,28 @@ class AdvancedEmlsrManager : public DefaultEmlsrManager //!< preceding/following the main PHY switch end }; +/** + * Struct to trace that main PHY switched to leave a link on which an aux PHY was expected to gain + * a TXOP but the main PHY did not manage to gain a TXOP in the pre-configured amount of time. + */ +struct EmlsrSwitchMainPhyBackTrace : public EmlsrMainPhySwitchTraceImpl +{ + static constexpr std::string_view m_name = "TxopNotGainedOnAuxPhyLink"; //!< trace name + + bool nothingToTx; //!< if true, the main PHY managed to gain a TXOP but had nothing to transmit + + /** + * Constructor provided because this struct is not an aggregate (it has a base struct), hence + * we cannot use designated initializers. + * + * @param nothing the value for the nothingToTx field + */ + EmlsrSwitchMainPhyBackTrace(bool nothing) + : nothingToTx(nothing) + { + } +}; + } // namespace ns3 #endif /* ADVANCED_EMLSR_MANAGER_H */ diff --git a/src/wifi/model/eht/default-emlsr-manager.cc b/src/wifi/model/eht/default-emlsr-manager.cc index 8a7813c71..64ba8de91 100644 --- a/src/wifi/model/eht/default-emlsr-manager.cc +++ b/src/wifi/model/eht/default-emlsr-manager.cc @@ -189,14 +189,17 @@ DefaultEmlsrManager::DoNotifyTxopEnd(uint8_t linkId) // switch main PHY to the previous link, if needed if (!m_switchAuxPhy) { - SwitchMainPhyBackToPreferredLink(linkId); + const auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId); + const auto delay = mainPhy->IsStateSwitching() ? mainPhy->GetDelayUntilIdle() : Time{0}; + SwitchMainPhyBackToPreferredLink(linkId, EmlsrTxopEndedTrace(delay)); } } void -DefaultEmlsrManager::SwitchMainPhyBackToPreferredLink(uint8_t linkId) +DefaultEmlsrManager::SwitchMainPhyBackToPreferredLink(uint8_t linkId, + EmlsrMainPhySwitchTrace&& traceInfo) { - NS_LOG_FUNCTION(this << linkId); + NS_LOG_FUNCTION(this << linkId << traceInfo.GetName()); NS_ABORT_MSG_IF(m_switchAuxPhy, "This method can only be called when SwitchAuxPhy is false"); @@ -216,16 +219,25 @@ DefaultEmlsrManager::SwitchMainPhyBackToPreferredLink(uint8_t linkId) // a new backoff value must be generated. if (!mainPhy->IsStateSwitching()) { - SwitchMainPhy(GetMainPhyId(), false, DONT_RESET_BACKOFF, REQUEST_ACCESS); + SwitchMainPhy(GetMainPhyId(), + false, + DONT_RESET_BACKOFF, + REQUEST_ACCESS, + std::forward(traceInfo)); } else { - Simulator::Schedule(mainPhy->GetDelayUntilIdle(), [=, this]() { - // request the main PHY to switch back to the preferred link only if in the meantime - // no TXOP started on another link (which will require the main PHY to switch link) + Simulator::Schedule(mainPhy->GetDelayUntilIdle(), [=, info = traceInfo.Clone(), this]() { + // request the main PHY to switch back to the preferred link only if + // in the meantime no TXOP started on another link (which will + // require the main PHY to switch link) if (!GetEhtFem(linkId)->UsingOtherEmlsrLink()) { - SwitchMainPhy(GetMainPhyId(), false, DONT_RESET_BACKOFF, REQUEST_ACCESS); + SwitchMainPhy(GetMainPhyId(), + false, + DONT_RESET_BACKOFF, + REQUEST_ACCESS, + std::move(*info)); } }); } @@ -331,13 +343,13 @@ DefaultEmlsrManager::NotifyRtsSent(uint8_t linkId, "RTS is being sent, but not enough time for main PHY to switch"); NS_LOG_DEBUG("Schedule main Phy switch in " << delay.As(Time::US)); - m_ulMainPhySwitch[linkId] = Simulator::Schedule(delay, - &DefaultEmlsrManager::SwitchMainPhy, - this, - linkId, - false, - RESET_BACKOFF, - DONT_REQUEST_ACCESS); + m_ulMainPhySwitch[linkId] = Simulator::Schedule(delay, [=, this]() { + SwitchMainPhy(linkId, + false, + RESET_BACKOFF, + DONT_REQUEST_ACCESS, + EmlsrUlTxopRtsSentByAuxPhyTrace{}); + }); m_switchMainPhyOnRtsTx.erase(it); } diff --git a/src/wifi/model/eht/default-emlsr-manager.h b/src/wifi/model/eht/default-emlsr-manager.h index aa0448a16..210a104df 100644 --- a/src/wifi/model/eht/default-emlsr-manager.h +++ b/src/wifi/model/eht/default-emlsr-manager.h @@ -70,8 +70,10 @@ class DefaultEmlsrManager : public EmlsrManager * main PHY. * * @param linkId the ID of the link that the main PHY is leaving + * @param traceInfo information to pass to the main PHY switch traced callback (the fromLinkId + * and toLinkId fields are set by SwitchMainPhy) */ - void SwitchMainPhyBackToPreferredLink(uint8_t linkId); + void SwitchMainPhyBackToPreferredLink(uint8_t linkId, EmlsrMainPhySwitchTrace&& traceInfo); /// Store information about a main PHY switch. struct MainPhySwitchInfo diff --git a/src/wifi/model/eht/emlsr-manager.cc b/src/wifi/model/eht/emlsr-manager.cc index 2d60e3ca7..40c8fd008 100644 --- a/src/wifi/model/eht/emlsr-manager.cc +++ b/src/wifi/model/eht/emlsr-manager.cc @@ -124,7 +124,15 @@ EmlsrManager::GetTypeId() BooleanValue(false), MakeBooleanAccessor(&EmlsrManager::SetCamStateReset, &EmlsrManager::GetCamStateReset), - MakeBooleanChecker()); + MakeBooleanChecker()) + .AddTraceSource("MainPhySwitch", + "This trace source is fired when the main PHY switches channel to " + "operate on another link. Information associated with the main PHY " + "switch is provided through a struct that is inherited from struct " + "EmlsrMainPhySwitchTrace (use the GetName() method to get the type " + "of the provided object).", + MakeTraceSourceAccessor(&EmlsrManager::m_mainPhySwitchTrace), + "ns3::EmlsrManager::MainPhySwitchCallback"); return tid; } @@ -443,7 +451,8 @@ EmlsrManager::NotifyIcfReceived(uint8_t linkId) SwitchMainPhy(linkId, true, // channel switch should occur instantaneously RESET_BACKOFF, - DONT_REQUEST_ACCESS); + DONT_REQUEST_ACCESS, + EmlsrDlTxopIcfReceivedByAuxPhyTrace{}); // aux PHY received the ICF but main PHY will send the response auto uid = auxPhy->GetPreviouslyRxPpduUid(); @@ -668,9 +677,11 @@ void EmlsrManager::SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool resetBackoff, - bool requestAccess) + bool requestAccess, + EmlsrMainPhySwitchTrace&& traceInfo) { - NS_LOG_FUNCTION(this << linkId << noSwitchDelay << resetBackoff << requestAccess); + NS_LOG_FUNCTION(this << linkId << noSwitchDelay << resetBackoff << requestAccess + << traceInfo.GetName()); auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId); @@ -679,6 +690,9 @@ EmlsrManager::SwitchMainPhy(uint8_t linkId, // find the link on which the main PHY is operating auto currMainPhyLinkId = m_staMac->GetLinkForPhy(mainPhy); + traceInfo.fromLinkId = currMainPhyLinkId; + traceInfo.toLinkId = linkId; + m_mainPhySwitchTrace(traceInfo); NS_ASSERT_MSG(currMainPhyLinkId.has_value() || mainPhy->IsStateSwitching(), "If the main PHY is not operating on a link, it must be switching"); diff --git a/src/wifi/model/eht/emlsr-manager.h b/src/wifi/model/eht/emlsr-manager.h index 01f9a38af..a01852c37 100644 --- a/src/wifi/model/eht/emlsr-manager.h +++ b/src/wifi/model/eht/emlsr-manager.h @@ -16,8 +16,10 @@ #include "ns3/wifi-phy-operating-channel.h" #include +#include #include #include +#include #include class EmlsrCcaBusyTest; @@ -29,6 +31,45 @@ class EhtFrameExchangeManager; class MgtEmlOmn; class WifiMpdu; +/** + * @ingroup wifi + * Base struct for EMLSR Main PHY switch traces. + */ +struct EmlsrMainPhySwitchTrace +{ + virtual ~EmlsrMainPhySwitchTrace() = default; + + /// @return the name of this instance + virtual std::string_view GetName() const = 0; + + /// @return a pointer to the clone of this object + virtual std::shared_ptr Clone() const = 0; + + std::optional fromLinkId; //!< ID of the link the main PHY is moving from (if any) + uint8_t toLinkId{WIFI_LINKID_UNDEFINED}; //!< ID of the link the main PHY is moving to +}; + +/** + * Implementation for the EMLSR Main PHY switch trace base struct. Child structs are inherited + * from this implementation according to the CRTP idiom and must define a static string_view + * member containing the name of the child. + */ +template +struct EmlsrMainPhySwitchTraceImpl : public EmlsrMainPhySwitchTrace +{ + /// @copydoc ns3::EmlsrMainPhySwitchTrace::GetName + std::string_view GetName() const override + { + return T::m_name; + } + + /// @copydoc ns3::EmlsrMainPhySwitchTrace::Clone + std::shared_ptr Clone() const override + { + return std::shared_ptr(new T(static_cast(*this))); + } +}; + /** * @ingroup wifi * @@ -351,8 +392,14 @@ class EmlsrManager : public Object * is operating * @param requestAccess whether channel access should be requested on the link on which the * main PHY is moving onto + * @param traceInfo information to pass to the main PHY switch traced callback (the fromLinkId + * and toLinkId fields are set by this function) */ - void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool resetBackoff, bool requestAccess); + void SwitchMainPhy(uint8_t linkId, + bool noSwitchDelay, + bool resetBackoff, + bool requestAccess, + EmlsrMainPhySwitchTrace&& traceInfo); static constexpr bool RESET_BACKOFF = true; //!< reset backoff on main PHY switch static constexpr bool DONT_RESET_BACKOFF = false; //!< do not reset backoff on main PHY switch @@ -574,6 +621,18 @@ class EmlsrManager : public Object //!< MediumSyncDelay timer is running }; + /** + * TracedCallback signature for main PHY switch events. + * + * @param info the information associated with the main PHY switch event + */ + typedef void (*MainPhySwitchCallback)(const EmlsrMainPhySwitchTrace& info); + + /// TracedCallback for main PHY switch events typedef + using MainPhySwitchTracedCallback = TracedCallback; + + MainPhySwitchTracedCallback m_mainPhySwitchTrace; //!< main PHY switch trace source + Ptr m_staMac; //!< the MAC of the managed non-AP MLD std::optional