wifi: AP MLD starts transition delay when EMLSR client switches to listening operations

... when the remaining TXOP time in a DL TXOP is not enough to send a
CF-End frame
This commit is contained in:
Stefano Avallone
2025-01-29 11:01:48 +01:00
parent e0195af625
commit cc41421320
7 changed files with 79 additions and 37 deletions

View File

@@ -580,9 +580,9 @@ EhtFrameExchangeManager::IntraBssNavResetTimeout()
}
bool
EhtFrameExchangeManager::UnblockEmlsrLinksIfAllowed(Mac48Address address)
EhtFrameExchangeManager::UnblockEmlsrLinksIfAllowed(Mac48Address address, bool checkThisLink)
{
NS_LOG_FUNCTION(this << address);
NS_LOG_FUNCTION(this << address << checkThisLink);
auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
@@ -634,9 +634,8 @@ EhtFrameExchangeManager::UnblockEmlsrLinksIfAllowed(Mac48Address address)
return false;
}
if (linkId == m_linkId)
if (linkId == m_linkId && !checkThisLink)
{
// no need to check if the EMLSR client is involved in a DL TXOP on this link
continue;
}
@@ -670,8 +669,8 @@ EhtFrameExchangeManager::EmlsrSwitchToListening(Mac48Address address, const Time
NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
NS_ASSERT_MSG(m_apMac, "This function shall only be called by AP MLDs");
auto blockLinks = [=, this]() {
if (!UnblockEmlsrLinksIfAllowed(address))
auto blockLinks = [=, this](bool checkThisLink) {
if (!UnblockEmlsrLinksIfAllowed(address, checkThisLink))
{
NS_LOG_DEBUG("Could not unblock transmissions to " << address);
return;
@@ -709,7 +708,16 @@ EhtFrameExchangeManager::EmlsrSwitchToListening(Mac48Address address, const Time
Simulator::Schedule(endDelay, unblockLinks));
};
delay.IsZero() ? blockLinks() : static_cast<void>(Simulator::Schedule(delay, blockLinks));
// it makes sense to check if the EMLSR client is involved in a DL TXOP on this link only if
// the transition delay start is scheduled to start after some delay, because the AP MLD may
// start another DL TXOP in the meantime. An example is when the AP MLD terminates a TXOP on
// this link due to the remaining TXOP time being not enough to send another frame (not even a
// CF-End), delays the start of the transition delay to align with the EMLSR client (which is
// waiting for a SIFS + slot + PHY RXSTART delay after the last frame to switch to listening
// operations), gains channel access on this link again before starting the transition delay
// timer and sends an ICF.
delay.IsZero() ? blockLinks(false)
: static_cast<void>(Simulator::Schedule(delay, [=]() { blockLinks(true); }));
}
void
@@ -922,8 +930,8 @@ EhtFrameExchangeManager::SwitchToListeningOrUnblockLinks(const std::set<Mac48Add
{
// EMLSR client switched to listening operations if it was protected, otherwise
// simply unblock transmissions
m_protectedStas.contains(address) ? EmlsrSwitchToListening(address, Seconds(0))
: (void)(UnblockEmlsrLinksIfAllowed(address));
m_protectedStas.contains(address) ? EmlsrSwitchToListening(address, Time{0})
: (void)(UnblockEmlsrLinksIfAllowed(address, false));
m_protectedStas.erase(address);
}
}
@@ -1233,13 +1241,21 @@ EhtFrameExchangeManager::NotifyChannelReleased(Ptr<Txop> txop)
if (m_apMac)
{
// the channel has been released; all EMLSR clients are switching back to
// listening operation
// the channel has been released; if the TXNAV is still set, it means that there is not
// enough time left to send a CF-End. In this case, EMLSR clients wait for a slot plus the
// PHY RX start delay before switching back to listening operation (in this case, this
// function is called a SIFS after the last frame in the TXOP)
Time delay{0};
if (const auto remTxNav = m_txNav - Simulator::Now(); remTxNav.IsStrictlyPositive())
{
delay = Min(m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY, remTxNav);
}
for (const auto& address : m_protectedStas)
{
if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
{
EmlsrSwitchToListening(address, Seconds(0));
EmlsrSwitchToListening(address, delay);
}
}
}
@@ -1748,6 +1764,8 @@ EhtFrameExchangeManager::UpdateTxopEndOnTxStart(Time txDuration, Time durationId
// after the end of this PPDU, hence we need to postpone the TXOP end in order to
// get the PHY-RXSTART.indication
delay = txDuration + m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
// TXOP end cannot be beyond the period protected via Duration/ID
delay = Min(delay, txDuration + durationId);
}
NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
@@ -1801,6 +1819,8 @@ EhtFrameExchangeManager::UpdateTxopEndOnRxEnd(Time durationId)
// we may send a response after a SIFS or we may receive another frame after a SIFS.
// Postpone the TXOP end by considering the latter (which takes longer)
auto delay = m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
// TXOP end cannot be beyond the period protected via Duration/ID
delay = Min(delay, durationId);
NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
m_ongoingTxopEnd =
Simulator::Schedule(delay, &EhtFrameExchangeManager::TxopEnd, this, m_txopHolder);

View File

@@ -245,9 +245,11 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager
* is not involved in any DL or UL TXOP on another link.
*
* @param address the link MAC address of the given EMLSR client
* @param checkThisLink whether to check if the EMLSR client is involved in a DL TXOP on
* this link
* @return whether transmissions could be unblocked
*/
bool UnblockEmlsrLinksIfAllowed(Mac48Address address);
bool UnblockEmlsrLinksIfAllowed(Mac48Address address, bool checkThisLink);
private:
/**

View File

@@ -612,14 +612,8 @@ FrameExchangeManager::ForwardMpduDown(Ptr<WifiMpdu> mpdu, WifiTxVector& txVector
auto psdu = Create<WifiPsdu>(mpdu, false);
FinalizeMacHeader(psdu);
m_allowedWidth = std::min(m_allowedWidth, txVector.GetChannelWidth());
auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
// The TXNAV timer is a single timer, shared by the EDCAFs within a STA, that is initialized
// with the duration from the Duration/ID field in the frame most recently successfully
// transmitted by the TXOP holder, except for PS-Poll frames. (Sec.10.23.2.2 IEEE 802.11-2020)
if (!mpdu->GetHeader().IsPsPoll())
{
m_txNav = Max(m_txNav, Simulator::Now() + txDuration + mpdu->GetHeader().GetDuration());
}
const auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
SetTxNav(mpdu, txDuration);
m_phy->Send(psdu, txVector);
}
@@ -1018,11 +1012,11 @@ FrameExchangeManager::TransmissionFailed(bool forceCurrentCw)
m_dcf->UpdateFailedCw(m_linkId);
}
m_sentFrameTo.clear();
// reset TXNAV because transmission failed
ResetTxNav();
// A non-QoS station always releases the channel upon a transmission failure
NotifyChannelReleased(m_dcf);
m_dcf = nullptr;
// reset TXNAV because transmission failed
m_txNav = Simulator::Now();
}
void
@@ -1368,6 +1362,29 @@ FrameExchangeManager::NavResetTimeout()
m_channelAccessManager->NotifyNavResetNow(Seconds(0));
}
void
FrameExchangeManager::SetTxNav(Ptr<const WifiMpdu> mpdu, const Time& txDuration)
{
// The TXNAV timer is a single timer, shared by the EDCAFs within a STA, that is initialized
// with the duration from the Duration/ID field in the frame most recently successfully
// transmitted by the TXOP holder, except for PS-Poll frames. The TXNAV timer begins counting
// down from the end of the transmission of the PPDU containing that frame.
// (Sec.10.23.2.2 IEEE 802.11-2020)
if (!mpdu->GetHeader().IsPsPoll())
{
const auto txNav = Simulator::Now() + txDuration + mpdu->GetHeader().GetDuration();
NS_LOG_DEBUG("Setting TXNAV to " << txNav.As(Time::S));
m_txNav = Max(m_txNav, txNav);
}
}
void
FrameExchangeManager::ResetTxNav()
{
NS_LOG_FUNCTION(this);
m_txNav = Simulator::Now();
}
bool
FrameExchangeManager::VirtualCsMediumIdle() const
{

View File

@@ -365,6 +365,17 @@ class FrameExchangeManager : public Object
*/
virtual void NavResetTimeout();
/**
* Set the TXNAV upon sending an MPDU.
*
* @param mpdu the MPDU being sent
* @param txDuration the TX duration of the MPDU
*/
void SetTxNav(Ptr<const WifiMpdu> mpdu, const Time& txDuration);
/// Reset the TXNAV
void ResetTxNav();
/**
* This method is called when the reception of a PSDU fails.
*

View File

@@ -941,11 +941,8 @@ HeFrameExchangeManager::ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVecto
txVector.SetAggregation(true);
}
auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
// The TXNAV timer is a single timer, shared by the EDCAFs within a STA, that is initialized
// with the duration from the Duration/ID field in the frame most recently successfully
// transmitted by the TXOP holder, except for PS-Poll frames. (Sec.10.23.2.2 IEEE 802.11-2020)
m_txNav = Max(m_txNav, Simulator::Now() + txDuration + psduMap.cbegin()->second->GetDuration());
const auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
SetTxNav(*psduMap.cbegin()->second->begin(), txDuration);
m_phy->Send(psduMap, txVector);
}

View File

@@ -1367,14 +1367,8 @@ HtFrameExchangeManager::ForwardPsduDown(Ptr<const WifiPsdu> psdu, WifiTxVector&
txVector.SetAggregation(true);
}
auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
// The TXNAV timer is a single timer, shared by the EDCAFs within a STA, that is initialized
// with the duration from the Duration/ID field in the frame most recently successfully
// transmitted by the TXOP holder, except for PS-Poll frames. (Sec.10.23.2.2 IEEE 802.11-2020)
if (!psdu->GetHeader(0).IsPsPoll())
{
m_txNav = Max(m_txNav, Simulator::Now() + txDuration + psdu->GetDuration());
}
const auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
SetTxNav(*psdu->begin(), txDuration);
m_phy->Send(psdu, txVector);
}

View File

@@ -101,6 +101,7 @@ QosFrameExchangeManager::SendCfEndIfNeeded()
&QosFrameExchangeManager::NotifyChannelReleased,
this,
m_edca);
ResetTxNav();
return true;
}
@@ -674,7 +675,7 @@ QosFrameExchangeManager::TransmissionFailed(bool forceCurrentCw)
m_initialFrame = false;
m_sentFrameTo.clear();
// reset TXNAV because transmission failed
m_txNav = Simulator::Now();
ResetTxNav();
}
void