wifi: Handle timeout of Basic/BSRP/MU-BAR TF when EMLSR clients are recipients

This commit is contained in:
Stefano Avallone
2024-06-03 09:20:03 +02:00
committed by Stefano Avallone
parent 0bc0931d64
commit 988b4ab26c
4 changed files with 198 additions and 54 deletions

View File

@@ -190,6 +190,7 @@ EhtFrameExchangeManager::StartTransmission(Ptr<Txop> edca, MHz_u allowedWidth)
m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(
ehtFem->m_txopHolder.value()))
{
NS_LOG_DEBUG("Involved in UL TXOP: " << ehtFem->m_txopHolder.value());
emlsrClients.insert(ehtFem->m_txopHolder.value());
}
@@ -198,6 +199,7 @@ EhtFrameExchangeManager::StartTransmission(Ptr<Txop> edca, MHz_u allowedWidth)
{
if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(address))
{
NS_LOG_DEBUG("Involved in DL TXOP: " << address);
emlsrClients.insert(address);
}
}
@@ -538,14 +540,15 @@ EhtFrameExchangeManager::IntraBssNavResetTimeout()
HeFrameExchangeManager::IntraBssNavResetTimeout();
}
void
EhtFrameExchangeManager::EmlsrSwitchToListening(const Mac48Address& address, const Time& delay)
bool
EhtFrameExchangeManager::UnblockEmlsrLinksIfAllowed(Mac48Address address)
{
NS_LOG_FUNCTION(this << address << delay.As(Time::US));
NS_LOG_FUNCTION(this << address);
auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
NS_ASSERT_MSG(m_apMac, "This function shall only be called by AP MLDs");
std::set<uint8_t> linkIds{m_linkId};
/**
* Do nothing if the EMLSR client is involved in a DL or UL TXOP on another EMLSR link. This
@@ -576,10 +579,9 @@ EhtFrameExchangeManager::EmlsrSwitchToListening(const Mac48Address& address, con
for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
{
if (linkId == m_linkId ||
!m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
if (!m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
{
continue;
continue; // not an EMLSR link
}
auto ehtFem = StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
@@ -590,9 +592,17 @@ EhtFrameExchangeManager::EmlsrSwitchToListening(const Mac48Address& address, con
{
NS_LOG_DEBUG("EMLSR client " << *mldAddress << " is the holder of an UL TXOP on link "
<< +linkId << ", do not unblock links");
return;
return false;
}
if (linkId == m_linkId)
{
// no need to check if the EMLSR client is involved in a DL TXOP on this link
continue;
}
linkIds.insert(linkId);
if (auto linkAddr =
m_apMac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(*mldAddress);
linkAddr &&
@@ -601,15 +611,34 @@ EhtFrameExchangeManager::EmlsrSwitchToListening(const Mac48Address& address, con
{
NS_LOG_DEBUG("EMLSR client " << address
<< " has been sent an ICF, do not unblock links");
return false;
}
}
// unblock DL transmissions with reason USING_OTHER_EMLSR_LINK
m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
*mldAddress,
linkIds);
return true;
}
void
EhtFrameExchangeManager::EmlsrSwitchToListening(Mac48Address address, const Time& delay)
{
NS_LOG_FUNCTION(this << address << delay.As(Time::US));
auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
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))
{
NS_LOG_DEBUG("Could not unblock transmissions to " << address);
return;
}
}
// this EMLSR client switches back to listening operation a transition delay
// after the given delay
auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
NS_ASSERT(emlCapabilities);
// this EMLSR client switches back to listening operation
std::set<uint8_t> linkIds;
for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
{
@@ -619,33 +648,29 @@ EhtFrameExchangeManager::EmlsrSwitchToListening(const Mac48Address& address, con
}
}
auto blockLinks = [=, this]() {
// the reason for blocking the other EMLSR links has changed now
m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
*mldAddress,
linkIds);
// block DL transmissions on this link until transition delay elapses
m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
*mldAddress,
linkIds);
};
delay.IsZero() ? blockLinks() : static_cast<void>(Simulator::Schedule(delay, blockLinks));
// unblock all EMLSR links when the transition delay elapses
auto unblockLinks = [=, this]() {
m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
*mldAddress,
linkIds);
};
auto endDelay = delay + CommonInfoBasicMle::DecodeEmlsrTransitionDelay(
// unblock all EMLSR links when the transition delay elapses
auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
NS_ASSERT(emlCapabilities);
auto endDelay = CommonInfoBasicMle::DecodeEmlsrTransitionDelay(
emlCapabilities->get().emlsrTransitionDelay);
endDelay.IsZero() ? unblockLinks()
: static_cast<void>(m_transDelayTimer[*mldAddress] =
Simulator::Schedule(endDelay, unblockLinks));
};
delay.IsZero() ? blockLinks() : static_cast<void>(Simulator::Schedule(delay, blockLinks));
}
void
@@ -847,19 +872,91 @@ EhtFrameExchangeManager::SendQosNullFramesInTbPpdu(const CtrlTriggerHeader& trig
HeFrameExchangeManager::SendQosNullFramesInTbPpdu(trigger, hdr);
}
void
EhtFrameExchangeManager::SwitchToListeningOrUnblockLinks(const std::set<Mac48Address>& clients)
{
NS_LOG_FUNCTION(this);
for (const auto& address : clients)
{
if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
{
// 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.erase(address);
}
}
}
void
EhtFrameExchangeManager::CtsAfterMuRtsTimeout(Ptr<WifiMpdu> muRts, const WifiTxVector& txVector)
{
NS_LOG_FUNCTION(this << *muRts << txVector);
// check if all the clients solicited by the MU-RTS are EMLSR clients that have sent (or
// are sending) a frame to the AP
const auto crossLinkCollision = IsCrossLinkCollision(m_sentRtsTo);
SwitchToListeningOrUnblockLinks(m_sentRtsTo);
const auto apEmlsrManager = m_apMac->GetApEmlsrManager();
const auto updateFailedCw =
crossLinkCollision && apEmlsrManager ? apEmlsrManager->UpdateCwAfterFailedIcf() : true;
DoCtsAfterMuRtsTimeout(muRts, txVector, updateFailedCw);
}
void
EhtFrameExchangeManager::TbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations)
{
NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
const auto crossLinkCollision = IsCrossLinkCollision(staMissedTbPpduFrom);
if (staMissedTbPpduFrom.size() != nSolicitedStations)
{
// some STAs replied, hence the transmission succeeded. EMLSR clients that did not
// respond are switching back to listening operations
SwitchToListeningOrUnblockLinks(staMissedTbPpduFrom);
}
const auto apEmlsrManager = m_apMac->GetApEmlsrManager();
const auto updateFailedCw =
crossLinkCollision && apEmlsrManager ? apEmlsrManager->UpdateCwAfterFailedIcf() : true;
DoTbPpduTimeout(psduMap, nSolicitedStations, updateFailedCw);
}
void
EhtFrameExchangeManager::BlockAcksInTbPpduTimeout(WifiPsduMap* psduMap,
std::size_t nSolicitedStations)
{
NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
if (staMissedTbPpduFrom.size() != nSolicitedStations)
{
// some STAs replied, hence the transmission succeeded. EMLSR clients that did not
// respond are switching back to listening operations
SwitchToListeningOrUnblockLinks(staMissedTbPpduFrom);
}
HeFrameExchangeManager::BlockAcksInTbPpduTimeout(psduMap, nSolicitedStations);
}
bool
EhtFrameExchangeManager::IsCrossLinkCollision(const std::set<Mac48Address>& staMissedResponseFrom)
{
NS_LOG_FUNCTION(this << staMissedResponseFrom.size());
// check if all the clients that did not respond to the ICF are EMLSR clients that have sent
// (or are sending) a frame to the AP
auto crossLinkCollision = true;
// we blocked transmissions on the other EMLSR links for the EMLSR clients we sent the ICF to.
// Given that no client responded, we can unblock transmissions for a client if there is no
// ongoing UL TXOP held by that client
for (const auto& address : m_sentRtsTo)
// For clients that did not respond, we can unblock transmissions if there is no ongoing
// UL TXOP held by that client
for (const auto& address : staMissedResponseFrom)
{
if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
{
@@ -908,16 +1005,9 @@ EhtFrameExchangeManager::CtsAfterMuRtsTimeout(Ptr<WifiMpdu> muRts, const WifiTxV
{
crossLinkCollision = false;
}
linkIds.erase(m_linkId);
m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
*mldAddress,
linkIds);
}
auto updateFailedCw =
crossLinkCollision ? m_apMac->GetApEmlsrManager()->UpdateCwAfterFailedIcf() : true;
DoCtsAfterMuRtsTimeout(muRts, txVector, updateFailedCw);
return crossLinkCollision;
}
void

View File

@@ -153,7 +153,7 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager
* \param address the link MAC address of the given EMLSR client
* \param delay the given delay
*/
void EmlsrSwitchToListening(const Mac48Address& address, const Time& delay);
void EmlsrSwitchToListening(Mac48Address address, const Time& delay);
/**
* \return a reference to the event indicating the possible end of the current TXOP (of
@@ -202,12 +202,33 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager
void ReceivedQosNullAfterBsrpTf(Mac48Address sender) override;
void SendQosNullFramesInTbPpdu(const CtrlTriggerHeader& trigger,
const WifiMacHeader& hdr) override;
void TbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations) override;
void BlockAcksInTbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations) override;
/**
* \return whether this is an EMLSR client that cannot respond to an ICF received a SIFS before
*/
bool EmlsrClientCannotRespondToIcf() const;
/**
* Check whether all the stations that did not respond (to a certain frame) are EMLSR clients
* trying to start an UL TXOP on another link.
*
* \param staMissedResponseFrom stations that did not respond
* \return whether all the stations that did not respond are EMLSR clients trying to start an
* UL TXOP on another link
*/
bool IsCrossLinkCollision(const std::set<Mac48Address>& staMissedResponseFrom);
/**
* Unblock transmissions on all the links of the given EMLSR client, provided that the latter
* is not involved in any DL or UL TXOP on another link.
*
* \param address the link MAC address of the given EMLSR client
* \return whether transmissions could be unblocked
*/
bool UnblockEmlsrLinksIfAllowed(Mac48Address address);
private:
/**
* \return whether the received ICF must be dropped because we are unable to process it
@@ -215,6 +236,15 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager
*/
bool DropReceivedIcf();
/**
* For each EMLSR client in the given set of clients that did not respond to a frame requesting
* a response from multiple clients, have the client switch to listening or simply unblock
* links depending on whether the EMLSR client was protected or not.
*
* \param clients the given set of clients
*/
void SwitchToListeningOrUnblockLinks(const std::set<Mac48Address>& clients);
/**
* Generate an in-device interference of the given power on the given link for the given
* duration.

View File

@@ -1312,9 +1312,19 @@ HeFrameExchangeManager::GetTxDuration(uint32_t ppduPayloadSize,
void
HeFrameExchangeManager::TbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations)
{
NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
DoTbPpduTimeout(psduMap, nSolicitedStations, true);
}
void
HeFrameExchangeManager::DoTbPpduTimeout(WifiPsduMap* psduMap,
std::size_t nSolicitedStations,
bool updateFailedCw)
{
const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
NS_LOG_FUNCTION(this << psduMap << staMissedTbPpduFrom.size() << nSolicitedStations);
NS_LOG_FUNCTION(this << psduMap << staMissedTbPpduFrom.size() << nSolicitedStations
<< updateFailedCw);
NS_ASSERT(psduMap);
NS_ASSERT(IsTrigger(*psduMap));
@@ -1326,7 +1336,10 @@ HeFrameExchangeManager::TbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicit
if (staMissedTbPpduFrom.size() == nSolicitedStations)
{
// no station replied, the transmission failed
if (updateFailedCw)
{
m_edca->UpdateFailedCw(m_linkId);
}
CtrlTriggerHeader trigger;
psduMap->cbegin()->second->GetPayload(0)->PeekHeader(trigger);

View File

@@ -278,6 +278,17 @@ class HeFrameExchangeManager : public VhtFrameExchangeManager
*/
virtual void TbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations);
/**
* Take the necessary actions after that some TB PPDUs are missing in
* response to Trigger Frame. This method must not be called if all the
* expected TB PPDUs were received.
*
* \param psduMap a pointer to PSDU map transmitted in a DL MU PPDU
* \param nSolicitedStations the number of stations solicited to send a TB PPDU
* \param updateFailedCw whether to update CW in case the transmission failed
*/
void DoTbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations, bool updateFailedCw);
/**
* Take the necessary actions after that a Block Ack is missing after a
* TB PPDU solicited through a Trigger Frame.