wifi: Handle timeout of Basic/BSRP/MU-BAR TF when EMLSR clients are recipients
This commit is contained in:
committed by
Stefano Avallone
parent
0bc0931d64
commit
988b4ab26c
@@ -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,51 +611,66 @@ EhtFrameExchangeManager::EmlsrSwitchToListening(const Mac48Address& address, con
|
||||
{
|
||||
NS_LOG_DEBUG("EMLSR client " << address
|
||||
<< " has been sent an ICF, do not unblock links");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// this EMLSR client switches back to listening operation a transition delay
|
||||
// after the given delay
|
||||
auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
|
||||
NS_ASSERT(emlCapabilities);
|
||||
// unblock DL transmissions with reason USING_OTHER_EMLSR_LINK
|
||||
m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
|
||||
*mldAddress,
|
||||
linkIds);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::set<uint8_t> linkIds;
|
||||
for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
|
||||
{
|
||||
if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
|
||||
{
|
||||
linkIds.insert(linkId);
|
||||
}
|
||||
}
|
||||
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]() {
|
||||
// the reason for blocking the other EMLSR links has changed now
|
||||
m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
|
||||
*mldAddress,
|
||||
linkIds);
|
||||
if (!UnblockEmlsrLinksIfAllowed(address))
|
||||
{
|
||||
NS_LOG_DEBUG("Could not unblock transmissions to " << address);
|
||||
return;
|
||||
}
|
||||
|
||||
// this EMLSR client switches back to listening operation
|
||||
std::set<uint8_t> linkIds;
|
||||
for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
|
||||
{
|
||||
if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
|
||||
{
|
||||
linkIds.insert(linkId);
|
||||
}
|
||||
}
|
||||
|
||||
// block DL transmissions on this link until transition delay elapses
|
||||
m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
|
||||
*mldAddress,
|
||||
linkIds);
|
||||
|
||||
auto unblockLinks = [=, this]() {
|
||||
m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
|
||||
*mldAddress,
|
||||
linkIds);
|
||||
};
|
||||
|
||||
// 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));
|
||||
|
||||
// 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(
|
||||
emlCapabilities->get().emlsrTransitionDelay);
|
||||
|
||||
endDelay.IsZero() ? unblockLinks()
|
||||
: static_cast<void>(m_transDelayTimer[*mldAddress] =
|
||||
Simulator::Schedule(endDelay, unblockLinks));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
m_edca->UpdateFailedCw(m_linkId);
|
||||
if (updateFailedCw)
|
||||
{
|
||||
m_edca->UpdateFailedCw(m_linkId);
|
||||
}
|
||||
|
||||
CtrlTriggerHeader trigger;
|
||||
psduMap->cbegin()->second->GetPayload(0)->PeekHeader(trigger);
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user