From afa46abd12b397bef1fd519ed47e0574db3a8cc1 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Wed, 8 May 2024 17:56:49 +0200 Subject: [PATCH] wifi: Set all aux PHYs to sleep while main PHY carries out a TXOP --- src/wifi/model/eht/advanced-emlsr-manager.cc | 5 +- src/wifi/model/eht/default-emlsr-manager.cc | 25 +---- src/wifi/model/eht/default-emlsr-manager.h | 7 +- .../model/eht/eht-frame-exchange-manager.cc | 13 +++ .../model/eht/eht-frame-exchange-manager.h | 1 + src/wifi/model/eht/emlsr-manager.cc | 106 ++++++++++++++++++ src/wifi/model/eht/emlsr-manager.h | 24 ++++ 7 files changed, 150 insertions(+), 31 deletions(-) diff --git a/src/wifi/model/eht/advanced-emlsr-manager.cc b/src/wifi/model/eht/advanced-emlsr-manager.cc index 46fda8c34..c33f81d6f 100644 --- a/src/wifi/model/eht/advanced-emlsr-manager.cc +++ b/src/wifi/model/eht/advanced-emlsr-manager.cc @@ -296,12 +296,9 @@ AdvancedEmlsrManager::DoNotifyTxopEnd(uint8_t linkId) // 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 such cases, wait until the main PHY channel switch is completed (unless the channel - // switching can be interrupted) before requesting a new channel switch. Given that the - // TXOP ended, the event to put the aux PHY to sleep can be cancelled. + // switching can be interrupted) before requesting a new channel switch. // Backoff shall not be reset on the link left by the main PHY because a TXOP ended and // a new backoff value must be generated. - m_auxPhyToSleepEvent.Cancel(); - if (m_switchAuxPhy || !mainPhy->IsStateSwitching() || m_interruptSwitching) { NS_ASSERT_MSG( diff --git a/src/wifi/model/eht/default-emlsr-manager.cc b/src/wifi/model/eht/default-emlsr-manager.cc index a674fe607..ca6de4fdb 100644 --- a/src/wifi/model/eht/default-emlsr-manager.cc +++ b/src/wifi/model/eht/default-emlsr-manager.cc @@ -41,13 +41,6 @@ DefaultEmlsrManager::GetTypeId() "no PHY will be listening on that EMLSR link).", BooleanValue(true), MakeBooleanAccessor(&DefaultEmlsrManager::m_switchAuxPhy), - MakeBooleanChecker()) - .AddAttribute("PutAuxPhyToSleep", - "Whether Aux PHY should be put into sleep mode while the Main PHY " - "is operating on the same link as the Aux PHY (this only matters " - "when the Aux PHY does not switch channel).", - BooleanValue(true), - MakeBooleanAccessor(&DefaultEmlsrManager::m_auxPhyToSleep), MakeBooleanChecker()); return tid; } @@ -154,29 +147,18 @@ DefaultEmlsrManager::NotifyMainPhySwitch(std::optional currLinkId, // the Aux PHY is not actually switching (hence no switching delay) GetStaMac()->NotifySwitchingEmlsrLink(m_auxPhyToReconnect, *currLinkId, Seconds(0)); - // resume aux PHY from sleep (once reconnected to its original link) - m_auxPhyToReconnect->ResumeFromSleep(); SetCcaEdThresholdOnLinkSwitch(m_auxPhyToReconnect, *currLinkId); } // if currLinkId has no value, it means that the main PHY switch is interrupted, hence reset - // the aux PHY to reconnect and cancel the event to put the aux PHY to sleep. Doing so when - // the main PHY is leaving the preferred link makes no harm (the aux PHY to reconnect and the - // event to put the aux PHY to sleep are set below), thus no need to add an 'if' condition + // the aux PHY to reconnect. Doing so when the main PHY is leaving the preferred link makes + // no harm (the aux PHY to reconnect is set below), thus no need to add an 'if' condition m_auxPhyToReconnect = nullptr; - m_auxPhyToSleepEvent.Cancel(); if (nextLinkId != GetMainPhyId()) { // the main PHY is moving to an auxiliary link and the aux PHY does not switch link m_auxPhyToReconnect = GetStaMac()->GetWifiPhy(nextLinkId); - - if (m_auxPhyToSleep) - { - // aux PHY can be put into sleep mode when the main PHY completes the channel switch - m_auxPhyToSleepEvent = - Simulator::Schedule(duration, &WifiPhy::SetSleepMode, m_auxPhyToReconnect, false); - } } } @@ -229,7 +211,7 @@ DefaultEmlsrManager::SwitchMainPhyBackToPreferredLink(uint8_t linkId) // 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 such cases, wait until the main PHY channel switch is completed before requesting - // a new channel switch and cancel the event to put the aux PHY to sleep. + // a new channel switch. // Backoff shall not be reset on the link left by the main PHY because a TXOP ended and // a new backoff value must be generated. if (!mainPhy->IsStateSwitching()) @@ -238,7 +220,6 @@ DefaultEmlsrManager::SwitchMainPhyBackToPreferredLink(uint8_t linkId) } else { - m_auxPhyToSleepEvent.Cancel(); 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) diff --git a/src/wifi/model/eht/default-emlsr-manager.h b/src/wifi/model/eht/default-emlsr-manager.h index f902b5038..473c53d81 100644 --- a/src/wifi/model/eht/default-emlsr-manager.h +++ b/src/wifi/model/eht/default-emlsr-manager.h @@ -67,11 +67,8 @@ class DefaultEmlsrManager : public EmlsrManager uint8_t to; //!< ID of the link which the main PHY is moving to }; - bool m_switchAuxPhy; /**< whether Aux PHY should switch channel to operate on the link on which - the Main PHY was operating before moving to the link of the Aux PHY */ - bool m_auxPhyToSleep; //!< whether Aux PHY should be put into sleep mode while the Main PHY - //!< is operating on the same link as the Aux PHY - EventId m_auxPhyToSleepEvent; //!< the event scheduled to put an Aux PHY into sleep mode + bool m_switchAuxPhy; /**< whether Aux PHY should switch channel to operate on the link on which + the Main PHY was operating before moving to the link of the Aux PHY */ Ptr m_auxPhyToReconnect; //!< Aux PHY the ChannelAccessManager of the link on which //!< the main PHY is operating has to connect a listener to //!< when the main PHY is back operating on its previous link diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.cc b/src/wifi/model/eht/eht-frame-exchange-manager.cc index a8eb7864d..aaefaa5e6 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.cc +++ b/src/wifi/model/eht/eht-frame-exchange-manager.cc @@ -318,6 +318,19 @@ EhtFrameExchangeManager::StartTransmission(Ptr edca, MHz_u allowedWidth) return started; } +void +EhtFrameExchangeManager::ProtectionCompleted() +{ + NS_LOG_FUNCTION(this); + + if (m_staMac && m_staMac->GetEmlsrManager()) + { + m_staMac->GetEmlsrManager()->NotifyProtectionCompleted(m_linkId); + } + + HeFrameExchangeManager::ProtectionCompleted(); +} + void EhtFrameExchangeManager::ForwardPsduDown(Ptr psdu, WifiTxVector& txVector) { diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.h b/src/wifi/model/eht/eht-frame-exchange-manager.h index 347b34732..688feb3a0 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.h +++ b/src/wifi/model/eht/eht-frame-exchange-manager.h @@ -204,6 +204,7 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager const WifiMacHeader& hdr) override; void TbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations) override; void BlockAcksInTbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations) override; + void ProtectionCompleted() override; /** * \return whether this is an EMLSR client that cannot respond to an ICF received a SIFS before diff --git a/src/wifi/model/eht/emlsr-manager.cc b/src/wifi/model/eht/emlsr-manager.cc index d0e14d3ea..073b9a07f 100644 --- a/src/wifi/model/eht/emlsr-manager.cc +++ b/src/wifi/model/eht/emlsr-manager.cc @@ -101,6 +101,16 @@ EmlsrManager::GetTypeId() MakeBooleanAccessor(&EmlsrManager::SetInDeviceInterference, &EmlsrManager::GetInDeviceInterference), MakeBooleanChecker()) + .AddAttribute("PutAuxPhyToSleep", + "Whether Aux PHYs should be put into sleep mode while the Main PHY " + "is carrying out a (DL or UL) TXOP. Specifically, for DL TXOPs, aux " + "PHYs are put to sleep after receiving the ICF; for UL TXOPs, aux PHYs " + "are put to sleep when the CTS frame is received, if RTS/CTS is used, " + "or when the transmission of the data frame starts, otherwise. " + "Aux PHYs are resumed from sleep when the TXOP ends.", + BooleanValue(false), + MakeBooleanAccessor(&EmlsrManager::m_auxPhyToSleep), + MakeBooleanChecker()) .AddAttribute( "EmlsrLinkSet", "IDs of the links on which EMLSR mode will be enabled. An empty set " @@ -440,6 +450,12 @@ EmlsrManager::NotifyIcfReceived(uint8_t linkId) mainPhy->SetPreviouslyRxPpduUid(uid); } + // a DL TXOP started, set all aux PHYs to sleep + if (m_auxPhyToSleep) + { + SetSleepStateForAllAuxPhys(true); + } + DoNotifyIcfReceived(linkId); } @@ -502,6 +518,30 @@ EmlsrManager::NotifyUlTxopStart(uint8_t linkId) DoNotifyUlTxopStart(linkId); } +void +EmlsrManager::NotifyProtectionCompleted(uint8_t linkId) +{ + NS_LOG_FUNCTION(this << linkId); + + if (m_auxPhyToSleep && m_staMac->IsEmlsrLink(linkId)) + { + if (auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId); mainPhy->IsStateSwitching()) + { + // main PHY is switching to this link to take over the UL TXOP. Postpone aux PHY + // sleeping until after the main PHY has completed switching + Simulator::Schedule(mainPhy->GetDelayUntilIdle() + TimeStep(1), + &EmlsrManager::SetSleepStateForAllAuxPhys, + this, + true); + } + else + { + // put aux PHYs to sleep + SetSleepStateForAllAuxPhys(true); + } + } +} + void EmlsrManager::NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted, bool ongoingDlTxop) { @@ -544,6 +584,12 @@ EmlsrManager::NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted, bool ongoingD return; } + if (m_auxPhyToSleep) + { + // TXOP ended, resume all aux PHYs from sleep + SetSleepStateForAllAuxPhys(false); + } + DoNotifyTxopEnd(linkId); Simulator::ScheduleNow([=, this]() { @@ -1140,4 +1186,64 @@ EmlsrManager::GetChannelForAuxPhy(uint8_t linkId) const return it->second; } +void +EmlsrManager::CancelAllSleepEvents() +{ + NS_LOG_FUNCTION(this); + + for (auto& [id, event] : m_auxPhyToSleepEvents) + { + event.Cancel(); + } + m_auxPhyToSleepEvents.clear(); +} + +void +EmlsrManager::SetSleepStateForAllAuxPhys(bool sleep) +{ + NS_LOG_FUNCTION(this << sleep); + + CancelAllSleepEvents(); + + for (const auto& phy : m_staMac->GetDevice()->GetPhys()) + { + if (phy->GetPhyId() == m_mainPhyId) + { + continue; // do not set sleep mode/resume from sleep the main PHY + } + + if (auto linkId = m_staMac->GetLinkForPhy(phy); + linkId.has_value() && !m_staMac->IsEmlsrLink(*linkId)) + { + continue; // this PHY is not operating on an EMLSR link + } + + if (!sleep) + { + NS_LOG_DEBUG("PHY " << +phy->GetPhyId() << ": Resuming from sleep"); + phy->ResumeFromSleep(); + continue; + } + + // we force WifiPhy::SetSleepMode() to abort RX and switch immediately to sleep mode in + // case the state is RX. If the state is TX or SWITCHING, WifiPhy::SetSleepMode() postpones + // setting sleep mode to end of TX or SWITCHING. This is fine, but we schedule events here + // to be able to cancel them later if needed + std::stringstream ss; + auto s = std::string("PHY ") + std::to_string(phy->GetPhyId()) + ": Setting sleep mode"; + if (phy->IsStateTx() || phy->IsStateSwitching()) + { + const auto delay = phy->GetDelayUntilIdle(); + NS_LOG_DEBUG(s << " in " << delay.As(Time::US)); + m_auxPhyToSleepEvents[phy->GetPhyId()] = + Simulator::Schedule(delay, &WifiPhy::SetSleepMode, phy, true); + } + else + { + NS_LOG_DEBUG(s); + phy->SetSleepMode(true); + } + } +} + } // namespace ns3 diff --git a/src/wifi/model/eht/emlsr-manager.h b/src/wifi/model/eht/emlsr-manager.h index 258539a5f..572e429ae 100644 --- a/src/wifi/model/eht/emlsr-manager.h +++ b/src/wifi/model/eht/emlsr-manager.h @@ -206,6 +206,14 @@ class EmlsrManager : public Object */ void NotifyUlTxopStart(uint8_t linkId); + /** + * Notify that protection (if required) is completed and data frame exchange can start + * on the given link. + * + * \param linkId the ID of the given link + */ + void NotifyProtectionCompleted(uint8_t linkId); + /** * Notify the end of a TXOP on the given link. * @@ -410,12 +418,28 @@ class EmlsrManager : public Object */ virtual std::pair GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId) = 0; + /** + * Set sleep state or awake state for all aux PHYs. + * + * \param sleep set sleep state, if true, or awake state, otherwise + */ + void SetSleepStateForAllAuxPhys(bool sleep); + + /** + * Cancel all pending events to put aux PHYs into sleep/awake state. + */ + void CancelAllSleepEvents(); + Time m_emlsrPaddingDelay; //!< EMLSR Padding delay Time m_emlsrTransitionDelay; //!< EMLSR Transition delay uint8_t m_mainPhyId; //!< ID of main PHY (position in the vector of PHYs held by WifiNetDevice) MHz_u m_auxPhyMaxWidth; //!< max channel width supported by aux PHYs WifiModulationClass m_auxPhyMaxModClass; //!< max modulation class supported by aux PHYs bool m_auxPhyTxCapable; //!< whether Aux PHYs are capable of transmitting PPDUs + bool m_auxPhyToSleep; //!< whether Aux PHYs should be put into sleep mode while the Main PHY + //!< is carrying out a (DL or UL) TXOP + std::map m_auxPhyToSleepEvents; //!< PHY ID-indexed map of events scheduled to + //!< put an Aux PHY to sleep std::map m_ulMainPhySwitch; //!< link ID-indexed map of timers started when //!< an aux PHY gains an UL TXOP and schedules //!< a channel switch for the main PHY