wifi: Extend Block Ack Manager for GCR Block Acks
This commit is contained in:
@@ -254,6 +254,7 @@ BlockAckManager::UpdateOriginatorAgreement(const MgtAddBaResponseHeader& respHdr
|
|||||||
if (const auto gcrGroupAddr = respHdr.GetGcrGroupAddress())
|
if (const auto gcrGroupAddr = respHdr.GetGcrGroupAddress())
|
||||||
{
|
{
|
||||||
agreement.SetGcrGroupAddress(*gcrGroupAddr);
|
agreement.SetGcrGroupAddress(*gcrGroupAddr);
|
||||||
|
m_gcrBlockAcks.emplace(*gcrGroupAddr, GcrBlockAcks{});
|
||||||
}
|
}
|
||||||
if (!it->second.first.IsEstablished())
|
if (!it->second.first.IsEstablished())
|
||||||
{
|
{
|
||||||
@@ -678,6 +679,140 @@ BlockAckManager::NotifyGotBlockAck(uint8_t linkId,
|
|||||||
return {nSuccessfulMpdus, nFailedMpdus};
|
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
|
void
|
||||||
BlockAckManager::NotifyMissedBlockAck(uint8_t linkId, const Mac48Address& recipient, uint8_t tid)
|
BlockAckManager::NotifyMissedBlockAck(uint8_t linkId, const Mac48Address& recipient, uint8_t tid)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -222,6 +222,27 @@ class BlockAckManager : public Object
|
|||||||
const std::set<uint8_t>& tids,
|
const std::set<uint8_t>& tids,
|
||||||
size_t index = 0);
|
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 linkId the ID of the given link
|
||||||
* @param recipient Sender of the expected BlockAck frame.
|
* @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
|
std::list<AgreementKey> m_sendBarIfDataQueued; ///< list of BA agreements for which a BAR shall
|
||||||
///< only be sent if data is queued
|
///< 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
|
uint8_t m_blockAckThreshold; ///< block ack threshold
|
||||||
Ptr<WifiMacQueue> m_queue; ///< queue
|
Ptr<WifiMacQueue> m_queue; ///< queue
|
||||||
Callback<void, Mac48Address, uint8_t, bool, std::optional<Mac48Address>>
|
Callback<void, Mac48Address, uint8_t, bool, std::optional<Mac48Address>>
|
||||||
|
|||||||
Reference in New Issue
Block a user