wifi: Add main PHY switch trace source to EMLSR manager

Based on suggestions from Sharan Naribole
This commit is contained in:
Stefano Avallone
2024-08-09 17:31:15 +02:00
committed by Stefano Avallone
parent 93ff99d46c
commit 9d53b09450
10 changed files with 298 additions and 33 deletions

View File

@@ -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

View File

@@ -722,6 +722,12 @@ ChannelAccessManager::GetBackoffEndFor(Ptr<Txop> txop, Time accessGrantStart) co
return backoffEnd;
}
Time
ChannelAccessManager::GetNavEnd() const
{
return m_lastNavEnd;
}
void
ChannelAccessManager::UpdateBackoff()
{

View File

@@ -170,6 +170,11 @@ class ChannelAccessManager : public Object
*/
Time GetBackoffEndFor(Ptr<Txop> 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

View File

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

View File

@@ -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<EmlsrSwitchMainPhyBackTrace>
{
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 */

View File

@@ -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<EmlsrMainPhySwitchTrace>(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);
}

View File

@@ -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

View File

@@ -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");

View File

@@ -16,8 +16,10 @@
#include "ns3/wifi-phy-operating-channel.h"
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string_view>
#include <utility>
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<EmlsrMainPhySwitchTrace> Clone() const = 0;
std::optional<uint8_t> 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 <class T>
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<EmlsrMainPhySwitchTrace> Clone() const override
{
return std::shared_ptr<EmlsrMainPhySwitchTrace>(new T(static_cast<const T&>(*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<const EmlsrMainPhySwitchTrace&>;
MainPhySwitchTracedCallback m_mainPhySwitchTrace; //!< main PHY switch trace source
Ptr<StaWifiMac> m_staMac; //!< the MAC of the managed non-AP MLD
std::optional<Time> m_emlsrTransitionTimeout; /**< Transition timeout advertised by APs with
EMLSR activated */
@@ -608,6 +667,116 @@ class EmlsrManager : public Object
m_noPhySince; //!< link ID-indexed map of the time since no PHY is operating on the link
};
/**
* Struct to trace that main PHY switched to start a DL TXOP after that an aux PHY received an ICF.
*/
struct EmlsrDlTxopIcfReceivedByAuxPhyTrace
: public EmlsrMainPhySwitchTraceImpl<EmlsrDlTxopIcfReceivedByAuxPhyTrace>
{
static constexpr std::string_view m_name = "DlTxopIcfReceivedByAuxPhy"; //!< trace name
};
/**
* Struct to trace that main PHY switched to start an UL TXOP after that an aux PHY transmitted an
* RTS.
*/
struct EmlsrUlTxopRtsSentByAuxPhyTrace
: public EmlsrMainPhySwitchTraceImpl<EmlsrUlTxopRtsSentByAuxPhyTrace>
{
static constexpr std::string_view m_name = "UlTxopRtsSentByAuxPhy"; //!< trace name
};
/**
* Struct to trace that main PHY switched when a (DL or UL) TXOP ended.
*
* This trace is normally called when aux PHYs do not switch link and the main PHY switches back to
* the preferred link when a TXOP carried out on another link ends. In such a case, the remTime
* field is set to zero.
*
* Note that the main PHY may be already switching when the TXOP ends; this happens, e.g., when the
* main PHY starts switching to a link on which an aux PHY gained a TXOP and sent an RTS, but the
* CTS is not received and the UL TXOP ends before the main PHY channel switch is completed. In this
* case, the main PHY switch is postponed until the previous switch is completed and the remTime
* field is set to the remaining channel switch delay at the time the TXOP ends:
*
* |-- main PHY switch --|
* |----- to link 1 -----|
* ┌───────────┐
* │ CTS │
* ────────────────────────┬───────────┬───┴X──────────┴─────────────────────────────
* [link 1] │ RTS │ │-remTime-│
* └───────────┘ │ |-- main PHY switch --|
* │ |- to preferred link -|
* CTS timeout
*
* Note also that the Advanced EMLSR manager may allow a main PHY switch to be interrupted. If this
* option is enabled and the main PHY is switching when the TXOP ends, the previous switch is
* interrupted and the main PHY starts switching to the preferred link (in this case, the remTime
* field indicates the time that was left to complete the previous switch). Also note that, with
* the Advanced EMLSR manager, this trace may also be called when aux PHYs switch link. This happens
* when the TXOP ends while the main PHY is switching; in this case, the previous switch is
* interrupted and the main PHY returns to the link on which it was operating before the previous
* switch.
*
* |-- main PHY switch --|
* |----- to link 1 -----|(interrupted)
* ┌───────────┐
* │ CTS │
* ────────────────────────┬───────────┬───┴X──────────┴─────────────────────────────
* [link 1] │ RTS │ │-remTime-│
* └───────────┘ │-- main PHY switch --|
* │- to preferred link -|
* CTS timeout
*/
struct EmlsrTxopEndedTrace : public EmlsrMainPhySwitchTraceImpl<EmlsrTxopEndedTrace>
{
static constexpr std::string_view m_name = "TxopEnded"; //!< trace name
Time remTime; //!< the remaining time (at TXOP end) until the main PHY completes the
//!< channel switch, in case the main PHY is completing a previous switch
//!< when the TXOP ends
/**
* Constructor provided because this struct is not an aggregate (it has a base struct), hence
* we cannot use designated initializers.
*
* @param t the value for the sinceTxopEnd field
*/
EmlsrTxopEndedTrace(const Time& t)
: remTime(t)
{
}
};
/**
* Struct to trace that main PHY switched to operate on a link on which an aux PHY that is not
* TX capable has gained or is expected to shortly gain a TXOP.
*/
struct EmlsrUlTxopAuxPhyNotTxCapableTrace
: public EmlsrMainPhySwitchTraceImpl<EmlsrUlTxopAuxPhyNotTxCapableTrace>
{
static constexpr std::string_view m_name = "UlTxopAuxPhyNotTxCapable"; //!< trace name
AcIndex acIndex; //!< Access category of TXOP on aux PHY
Time remTime; //!< Remaining time to complete backoff countdown on the aux PHY link
Time remNav; //!< the remaining NAV on main PHY link when main PHY is requested to switch
/**
* Constructor provided because this struct is not an aggregate (it has a base struct), hence
* we cannot use designated initializers.
*
* @param aci the value for the acIndex field
* @param delay the value for the remTime field
* @param navLeft the value for the remNav field
*/
EmlsrUlTxopAuxPhyNotTxCapableTrace(AcIndex aci, const Time& delay, const Time& navLeft)
: acIndex(aci),
remTime(delay),
remNav(navLeft)
{
}
};
} // namespace ns3
#endif /* EMLSR_MANAGER_H */

View File

@@ -4655,10 +4655,12 @@ EmlsrCcaBusyTest::StartTraffic()
m_nextMainPhyLinkId = (m_currMainPhyLinkId + 1) % 2;
// request the main PHY to switch to another link
m_staMacs[0]->GetEmlsrManager()->SwitchMainPhy(m_nextMainPhyLinkId,
false,
EmlsrManager::DONT_RESET_BACKOFF,
EmlsrManager::DONT_REQUEST_ACCESS);
m_staMacs[0]->GetEmlsrManager()->SwitchMainPhy(
m_nextMainPhyLinkId,
false,
EmlsrManager::DONT_RESET_BACKOFF,
EmlsrManager::DONT_REQUEST_ACCESS,
EmlsrDlTxopIcfReceivedByAuxPhyTrace{}); // trace info not used
// the other MLD transmits a packet to the AP
TransmitPacketToAp(m_nextMainPhyLinkId);