From cc414213200ef7bf77c1e573abb23c0a556922f3 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Wed, 29 Jan 2025 11:01:48 +0100 Subject: [PATCH] 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 --- .../model/eht/eht-frame-exchange-manager.cc | 44 ++++++++++++++----- .../model/eht/eht-frame-exchange-manager.h | 4 +- src/wifi/model/frame-exchange-manager.cc | 37 +++++++++++----- src/wifi/model/frame-exchange-manager.h | 11 +++++ .../model/he/he-frame-exchange-manager.cc | 7 +-- .../model/ht/ht-frame-exchange-manager.cc | 10 +---- src/wifi/model/qos-frame-exchange-manager.cc | 3 +- 7 files changed, 79 insertions(+), 37 deletions(-) diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.cc b/src/wifi/model/eht/eht-frame-exchange-manager.cc index bc3f4d5e3..bf17230cd 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.cc +++ b/src/wifi/model/eht/eht-frame-exchange-manager.cc @@ -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(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(Simulator::Schedule(delay, [=]() { blockLinks(true); })); } void @@ -922,8 +930,8 @@ EhtFrameExchangeManager::SwitchToListeningOrUnblockLinks(const std::set 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); diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.h b/src/wifi/model/eht/eht-frame-exchange-manager.h index a2675f6e2..2f2eb9c3f 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.h +++ b/src/wifi/model/eht/eht-frame-exchange-manager.h @@ -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: /** diff --git a/src/wifi/model/frame-exchange-manager.cc b/src/wifi/model/frame-exchange-manager.cc index 77ff9eefd..ab79274ff 100644 --- a/src/wifi/model/frame-exchange-manager.cc +++ b/src/wifi/model/frame-exchange-manager.cc @@ -612,14 +612,8 @@ FrameExchangeManager::ForwardMpduDown(Ptr mpdu, WifiTxVector& txVector auto psdu = Create(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 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 { diff --git a/src/wifi/model/frame-exchange-manager.h b/src/wifi/model/frame-exchange-manager.h index 70a180001..06274dcba 100644 --- a/src/wifi/model/frame-exchange-manager.h +++ b/src/wifi/model/frame-exchange-manager.h @@ -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 mpdu, const Time& txDuration); + + /// Reset the TXNAV + void ResetTxNav(); + /** * This method is called when the reception of a PSDU fails. * diff --git a/src/wifi/model/he/he-frame-exchange-manager.cc b/src/wifi/model/he/he-frame-exchange-manager.cc index f5b31ea20..6c4979100 100644 --- a/src/wifi/model/he/he-frame-exchange-manager.cc +++ b/src/wifi/model/he/he-frame-exchange-manager.cc @@ -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); } diff --git a/src/wifi/model/ht/ht-frame-exchange-manager.cc b/src/wifi/model/ht/ht-frame-exchange-manager.cc index 0b8cb541e..56e84ab97 100644 --- a/src/wifi/model/ht/ht-frame-exchange-manager.cc +++ b/src/wifi/model/ht/ht-frame-exchange-manager.cc @@ -1367,14 +1367,8 @@ HtFrameExchangeManager::ForwardPsduDown(Ptr 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); } diff --git a/src/wifi/model/qos-frame-exchange-manager.cc b/src/wifi/model/qos-frame-exchange-manager.cc index e21455ed0..c451fb247 100644 --- a/src/wifi/model/qos-frame-exchange-manager.cc +++ b/src/wifi/model/qos-frame-exchange-manager.cc @@ -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