wifi: Extend Block Ack Manager for GCR Block Acks

This commit is contained in:
Sébastien Deronne
2023-09-25 20:20:07 +02:00
parent eaf6850df7
commit 7fd53be0ae
2 changed files with 161 additions and 0 deletions

View File

@@ -254,6 +254,7 @@ BlockAckManager::UpdateOriginatorAgreement(const MgtAddBaResponseHeader& respHdr
if (const auto gcrGroupAddr = respHdr.GetGcrGroupAddress())
{
agreement.SetGcrGroupAddress(*gcrGroupAddr);
m_gcrBlockAcks.emplace(*gcrGroupAddr, GcrBlockAcks{});
}
if (!it->second.first.IsEstablished())
{
@@ -678,6 +679,140 @@ BlockAckManager::NotifyGotBlockAck(uint8_t linkId,
return {nSuccessfulMpdus, nFailedMpdus};
}
std::optional<std::pair<uint16_t, uint16_t>>
BlockAckManager::NotifyGotGcrBlockAck(uint8_t linkId,
const CtrlBAckResponseHeader& blockAck,
const Mac48Address& recipient,
const GcrManager::GcrMembers& members)
{
NS_LOG_FUNCTION(this << linkId << blockAck << recipient);
NS_ABORT_MSG_IF(!blockAck.IsGcr(), "GCR Block Ack is expected");
NS_ABORT_MSG_IF(members.count(recipient) == 0,
"Received GCR Block Ack response from unexpected recipient");
const auto tid = blockAck.GetTidInfo();
auto it = GetOriginatorBaAgreement(recipient, tid, blockAck.GetGcrGroupAddress());
if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
{
return {};
}
NS_ASSERT_MSG(it->second.first.GetGcrGroupAddress().has_value() &&
it->second.first.GetGcrGroupAddress().value() ==
blockAck.GetGcrGroupAddress(),
"No GCR agreement for group address " << blockAck.GetGcrGroupAddress());
if (it->second.first.m_inactivityEvent.IsPending())
{
/* Upon reception of a BlockAck frame, the inactivity timer at the
originator must be reset.
For more details see section 11.5.3 in IEEE802.11e standard */
it->second.first.m_inactivityEvent.Cancel();
Time timeout = MicroSeconds(1024 * it->second.first.GetTimeout());
it->second.first.m_inactivityEvent =
Simulator::Schedule(timeout,
&BlockAckManager::InactivityTimeout,
this,
recipient,
tid,
blockAck.GetGcrGroupAddress());
}
auto itGcrBlockAcks = m_gcrBlockAcks.find(blockAck.GetGcrGroupAddress());
NS_ASSERT(itGcrBlockAcks != m_gcrBlockAcks.end());
NS_ASSERT(itGcrBlockAcks->second.count(recipient) == 0);
itGcrBlockAcks->second[recipient] = blockAck;
if (itGcrBlockAcks->second.size() < members.size())
{
// we need to collect feedback from all members
NS_LOG_DEBUG("Expecting more GCR Block ACK(s)");
return {};
}
std::vector<bool> acked;
for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end(); ++queueIt)
{
auto currentSeq = (*queueIt)->GetHeader().GetSequenceNumber();
NS_LOG_DEBUG("Current seq=" << currentSeq);
auto received = true;
for ([[maybe_unused]] const auto& [recipient, gcrBlockAcks] : itGcrBlockAcks->second)
{
received &= gcrBlockAcks.IsPacketReceived(currentSeq, 0);
}
acked.emplace_back(received);
}
uint16_t nSuccessfulMpdus = 0;
uint16_t nFailedMpdus = 0;
const auto now = Simulator::Now();
std::list<Ptr<const WifiMpdu>> ackedMpdus;
auto countAndNotify = true;
for (const auto& member : members)
{
std::size_t index = 0;
it = GetOriginatorBaAgreement(member, tid, blockAck.GetGcrGroupAddress());
NS_ASSERT(acked.size() == it->second.second.size());
for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end();)
{
if (acked.at(index++))
{
it->second.first.NotifyAckedMpdu(*queueIt);
if (countAndNotify)
{
nSuccessfulMpdus++;
if (!m_txOkCallback.IsNull())
{
m_txOkCallback(*queueIt);
}
ackedMpdus.emplace_back(*queueIt);
}
queueIt = HandleInFlightMpdu(linkId, queueIt, ACKNOWLEDGED, it, now);
}
else
{
++queueIt;
}
}
countAndNotify = false;
}
// Dequeue all acknowledged MPDUs at once
m_queue->DequeueIfQueued(ackedMpdus);
// Remaining outstanding MPDUs have not been acknowledged
countAndNotify = true;
for (const auto& member : members)
{
it = GetOriginatorBaAgreement(member, tid, blockAck.GetGcrGroupAddress());
for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end();)
{
// transmission actually failed if the MPDU is inflight only on the same link on
// which we received the BlockAck frame
auto linkIds = (*queueIt)->GetInFlightLinkIds();
if (linkIds.size() == 1 && *linkIds.begin() == linkId)
{
if (countAndNotify)
{
nFailedMpdus++;
if (!m_txFailedCallback.IsNull())
{
m_txFailedCallback(*queueIt);
}
}
queueIt = HandleInFlightMpdu(linkId, queueIt, TO_RETRANSMIT, it, now);
continue;
}
queueIt = HandleInFlightMpdu(linkId, queueIt, STAY_INFLIGHT, it, now);
}
countAndNotify = false;
}
itGcrBlockAcks->second.clear();
return std::make_pair(nSuccessfulMpdus, nFailedMpdus);
}
void
BlockAckManager::NotifyMissedBlockAck(uint8_t linkId, const Mac48Address& recipient, uint8_t tid)
{

View File

@@ -222,6 +222,27 @@ class BlockAckManager : public Object
const std::set<uint8_t>& tids,
size_t index = 0);
/**
* @param linkId the ID of the given link
* @param blockAck The received BlockAck frame.
* @param recipient Sender of BlockAck frame.
* @param members the list of member STAs for that GCR group
* @return a pair of values indicating the number of successfully received MPDUs
* and the number of failed MPDUs if GCR Block Ack frames for all member STAs
* have been received.
*
* Invoked upon receipt of a GCR Block Ack frame on the given link. Typically, this function
* is called by the frame exchange manager. Stores the received Block Ack response for each
* GCR member STA. Once all responses have been received, it performs a check on which MPDUs,
* previously sent with Ack Policy set to Block Ack, were correctly received by the recipient.
* An acknowledged MPDU is removed from the buffer, retransmitted otherwise.
*/
std::optional<std::pair<uint16_t, uint16_t>> NotifyGotGcrBlockAck(
uint8_t linkId,
const CtrlBAckResponseHeader& blockAck,
const Mac48Address& recipient,
const GcrManager::GcrMembers& members);
/**
* @param linkId the ID of the given link
* @param recipient Sender of the expected BlockAck frame.
@@ -644,6 +665,11 @@ class BlockAckManager : public Object
std::list<AgreementKey> m_sendBarIfDataQueued; ///< list of BA agreements for which a BAR shall
///< only be sent if data is queued
/// List of received GCR BlockAck frames indexed by originator
using GcrBlockAcks = std::map<Mac48Address, CtrlBAckResponseHeader>;
std::map<Mac48Address /* GCR group address */, GcrBlockAcks>
m_gcrBlockAcks; ///< received GCR Block ACKs
uint8_t m_blockAckThreshold; ///< block ack threshold
Ptr<WifiMacQueue> m_queue; ///< queue
Callback<void, Mac48Address, uint8_t, bool, std::optional<Mac48Address>>