wifi: Set all aux PHYs to sleep while main PHY carries out a TXOP

This commit is contained in:
Stefano Avallone
2024-05-08 17:56:49 +02:00
parent 13ea483aa9
commit afa46abd12
7 changed files with 150 additions and 31 deletions

View File

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

View File

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

View File

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

View File

@@ -318,6 +318,19 @@ EhtFrameExchangeManager::StartTransmission(Ptr<Txop> 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<const WifiPsdu> psdu, WifiTxVector& txVector)
{

View File

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

View File

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

View File

@@ -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<bool, Time> 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<uint8_t, EventId> m_auxPhyToSleepEvents; //!< PHY ID-indexed map of events scheduled to
//!< put an Aux PHY to sleep
std::map<uint8_t, EventId> 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