diff --git a/examples/wireless/examples-to-run.py b/examples/wireless/examples-to-run.py index ad9a9e695..dbefabcf3 100755 --- a/examples/wireless/examples-to-run.py +++ b/examples/wireless/examples-to-run.py @@ -165,7 +165,7 @@ cpp_examples = [ "True", ), ( - "wifi-eht-network --simulationTime=0.22 --udp=0 --downlink=1 --useRts=0 --nStations=4 --dlAckType=ACK-SU-FORMAT --enableUlOfdma=1 --enableBsrp=0 --mcs=4 --frequency2=6 --minExpectedThroughput=35 --maxExpectedThroughput=280", + "wifi-eht-network --simulationTime=0.23 --udp=0 --downlink=1 --useRts=0 --nStations=4 --dlAckType=ACK-SU-FORMAT --enableUlOfdma=1 --enableBsrp=0 --mcs=5 --frequency2=6 --minExpectedThroughput=35 --maxExpectedThroughput=280", "True", "True", ), diff --git a/src/wifi/model/channel-access-manager.cc b/src/wifi/model/channel-access-manager.cc index 4664e3dee..afdbbf4cc 100644 --- a/src/wifi/model/channel-access-manager.cc +++ b/src/wifi/model/channel-access-manager.cc @@ -391,9 +391,11 @@ ChannelAccessManager::IsBusy() const } bool -ChannelAccessManager::NeedBackoffUponAccess(Ptr txop) +ChannelAccessManager::NeedBackoffUponAccess(Ptr txop, + bool hadFramesToTransmit, + bool checkMediumBusy) { - NS_LOG_FUNCTION(this << txop); + NS_LOG_FUNCTION(this << txop << hadFramesToTransmit << checkMediumBusy); // No backoff needed if in sleep mode, off or when using another EMLSR link if (m_sleeping || m_off || m_usingOtherEmlsrLink) @@ -424,10 +426,10 @@ ChannelAccessManager::NeedBackoffUponAccess(Ptr txop) * with that AC has now become non-empty and any other transmit queues * associated with that AC are empty; the medium is busy on the primary channel */ - if (!txop->HasFramesToTransmit(m_linkId) && txop->GetAccessStatus(m_linkId) != Txop::GRANTED && - txop->GetBackoffSlots(m_linkId) == 0) + if (!hadFramesToTransmit && txop->HasFramesToTransmit(m_linkId) && + txop->GetAccessStatus(m_linkId) != Txop::GRANTED && txop->GetBackoffSlots(m_linkId) == 0) { - if (!IsBusy()) + if (checkMediumBusy && !IsBusy()) { // medium idle. If this is a DCF, use immediate access (we can transmit // in a DIFS if the medium remains idle). If this is an EDCAF, update diff --git a/src/wifi/model/channel-access-manager.h b/src/wifi/model/channel-access-manager.h index f64f89fd8..477846b9b 100644 --- a/src/wifi/model/channel-access-manager.h +++ b/src/wifi/model/channel-access-manager.h @@ -109,13 +109,22 @@ class ChannelAccessManager : public Object void Add(Ptr txop); /** - * Determine if a new backoff needs to be generated when a packet is queued - * for transmission. + * Determine if a new backoff needs to be generated as per letter a) of Section 10.23.2.2 + * of IEEE 802.11-2020 ("EDCA backoff procedure"). This method is called upon the occurrence + * of events such as the enqueuing of a packet or the unblocking of some links after they + * have been blocked for some reason (e.g., wait for ADDBA Response, wait for TX on another + * EMLSR link to finish, etc.). The checkMediumBusy argument allows to generate a new + * backoff regardless of the busy/idle state of the medium, as per Section 35.3.16.4 of + * 802.11be D4.0. * * \param txop the Txop requesting to generate a backoff + * \param hadFramesToTransmit whether packets available for transmission were queued just + * before the occurrence of the event triggering this call + * \param checkMediumBusy whether generation of backoff (also) depends on the busy/idle state + * of the medium * \return true if backoff needs to be generated, false otherwise */ - bool NeedBackoffUponAccess(Ptr txop); + bool NeedBackoffUponAccess(Ptr txop, bool hadFramesToTransmit, bool checkMediumBusy); /** * \param txop a Txop diff --git a/src/wifi/model/txop.cc b/src/wifi/model/txop.cc index c601c14eb..fd59629f8 100644 --- a/src/wifi/model/txop.cc +++ b/src/wifi/model/txop.cc @@ -535,12 +535,13 @@ Txop::Queue(Ptr mpdu) { NS_LOG_FUNCTION(this << *mpdu); const auto linkIds = m_mac->GetMacQueueScheduler()->GetLinkIds(m_queue->GetAc(), mpdu); + std::map hasFramesToTransmit; + + // save the status of the AC queues before enqueuing the MPDU (required to determine if + // backoff is needed) for (const auto linkId : linkIds) { - if (m_mac->GetChannelAccessManager(linkId)->NeedBackoffUponAccess(this)) - { - GenerateBackoff(linkId); - } + hasFramesToTransmit[linkId] = HasFramesToTransmit(linkId); } m_queue->Enqueue(mpdu); for (const auto linkId : linkIds) @@ -551,7 +552,11 @@ Txop::Queue(Ptr mpdu) // packet if (auto& event = GetLink(linkId).accessRequest.event; !event.IsRunning()) { - event = Simulator::ScheduleNow(&Txop::StartAccessIfNeeded, this, linkId); + event = Simulator::ScheduleNow(&Txop::StartAccessAfterEvent, + this, + linkId, + hasFramesToTransmit.at(linkId), + true); } } } @@ -564,6 +569,27 @@ Txop::AssignStreams(int64_t stream) return 1; } +void +Txop::StartAccessAfterEvent(uint8_t linkId, bool hadFramesToTransmit, bool checkMediumBusy) +{ + NS_LOG_FUNCTION(this << +linkId << hadFramesToTransmit << checkMediumBusy); + + if (GetLink(linkId).access != NOT_REQUESTED || !HasFramesToTransmit(linkId)) + { + NS_LOG_DEBUG("No need to request channel access on link " << +linkId); + return; + } + + if (m_mac->GetChannelAccessManager(linkId)->NeedBackoffUponAccess(this, + hadFramesToTransmit, + checkMediumBusy)) + { + GenerateBackoff(linkId); + } + + m_mac->GetChannelAccessManager(linkId)->RequestAccess(this); +} + void Txop::StartAccessIfNeeded(uint8_t linkId) { diff --git a/src/wifi/model/txop.h b/src/wifi/model/txop.h index 84a341fc8..9d16c7209 100644 --- a/src/wifi/model/txop.h +++ b/src/wifi/model/txop.h @@ -395,6 +395,23 @@ class Txop : public Object */ virtual ChannelAccessStatus GetAccessStatus(uint8_t linkId) const; + /** + * Request channel access on the given link after the occurrence of an event that possibly + * requires to generate a new backoff value. Examples of such an event are: a packet has been + * enqueued by the upper layer; the given link has been unblocked after being blocked for some + * reason (e.g., wait for ADDBA Response, wait for TX on another EMLSR link to finish, etc.); + * the PHY operating on the given link just woke up from sleep mode. The checkMediumBusy + * argument is forwarded to the NeedBackoffUponAccess method of the ChannelAccessManager. + * + * \param linkId the ID of the given link + * \param hadFramesToTransmit whether packets available for transmission were queued just + * before the occurrence of the event causing this channel access + * request + * \param checkMediumBusy whether generation of backoff (also) depends on the busy/idle state + * of the medium + */ + void StartAccessAfterEvent(uint8_t linkId, bool hadFramesToTransmit, bool checkMediumBusy); + /** * Request access from Txop on the given link if needed. * diff --git a/src/wifi/test/channel-access-manager-test.cc b/src/wifi/test/channel-access-manager-test.cc index 0b8ba4618..d12c25394 100644 --- a/src/wifi/test/channel-access-manager-test.cc +++ b/src/wifi/test/channel-access-manager-test.cc @@ -806,11 +806,12 @@ ChannelAccessManagerTest::DoAccessRequest(uint64_t txTime, uint64_t expectedGrantTime, Ptr> state) { - if (m_ChannelAccessManager->NeedBackoffUponAccess(state)) + auto hadFramesToTransmit = state->HasFramesToTransmit(SINGLE_LINK_OP_ID); + state->QueueTx(txTime, expectedGrantTime); + if (m_ChannelAccessManager->NeedBackoffUponAccess(state, hadFramesToTransmit, true)) { state->GenerateBackoff(0); } - state->QueueTx(txTime, expectedGrantTime); m_ChannelAccessManager->RequestAccess(state); }