[mesh-forwarder] use Array to track forwarded frame info (#10540)

This commit updates and simplifies the tracking of information
(message priority, drop status) for forwarded mesh-header fragmented
frames. This is used for consistent priority assignment to all
fragments of the same message and facilitates delay-aware queue
management, where dropping one fragment leads to dropping all
subsequent fragments of the same message.

The entry type is renamed to `FwdFrameInfo`, and an `Array` is used to
track the entries. Array helper methods such as `RemoveAllMatching()`
and `FindMatching()` help simplify the code. Since `Array` tracks the
current length, iterating over unused entries is avoided.
This commit is contained in:
Abtin Keshavarzian
2024-09-16 14:58:45 -07:00
committed by GitHub
parent 9d8fcfdb8b
commit d60ec8811c
3 changed files with 101 additions and 117 deletions
+6 -13
View File
@@ -129,10 +129,6 @@ MeshForwarder::MeshForwarder(Instance &aInstance)
ResetCounters();
#if OPENTHREAD_FTD
mFragmentPriorityList.Clear();
#endif
#if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE
mTxQueueStats.Clear();
#endif
@@ -164,7 +160,7 @@ void MeshForwarder::Stop(void)
#if OPENTHREAD_FTD
mIndirectSender.Stop();
mFragmentPriorityList.Clear();
mFwdFrameInfoArray.Clear();
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
@@ -327,9 +323,7 @@ Error MeshForwarder::UpdateEcnOrDrop(Message &aMessage, bool aPreparingToSend)
if ((shouldMarkEcn && !isEcnCapable) || (timeInQueue >= kTimeInQueueDropMsg))
{
FragmentPriorityList::Entry *entry;
entry = mFragmentPriorityList.FindEntry(meshHeader.GetSource(), fragmentHeader.GetDatagramTag());
FwdFrameInfo *entry = FindFwdFrameInfoEntry(meshHeader.GetSource(), fragmentHeader.GetDatagramTag());
if (entry != nullptr)
{
@@ -358,9 +352,8 @@ Error MeshForwarder::UpdateEcnOrDrop(Message &aMessage, bool aPreparingToSend)
}
else if (hasFragmentHeader)
{
FragmentPriorityList::Entry *entry;
FwdFrameInfo *entry = FindFwdFrameInfoEntry(meshHeader.GetSource(), fragmentHeader.GetDatagramTag());
entry = mFragmentPriorityList.FindEntry(meshHeader.GetSource(), fragmentHeader.GetDatagramTag());
VerifyOrExit(entry != nullptr);
if (entry->ShouldDrop())
@@ -368,12 +361,12 @@ Error MeshForwarder::UpdateEcnOrDrop(Message &aMessage, bool aPreparingToSend)
error = kErrorDrop;
}
// We can clear the entry if it is the last fragment and
// We can remove the entry if it is the last fragment and
// only if the message is being prepared to be sent out.
if (aPreparingToSend && (fragmentHeader.GetDatagramOffset() + aMessage.GetLength() - offset >=
fragmentHeader.GetDatagramSize()))
{
entry->Clear();
mFwdFrameInfoArray.Remove(*entry);
}
}
}
@@ -1588,7 +1581,7 @@ void MeshForwarder::HandleTimeTick(void)
bool continueRxingTicks = false;
#if OPENTHREAD_FTD
continueRxingTicks = mFragmentPriorityList.UpdateOnTimeTick();
continueRxingTicks = UpdateFwdFrameInfoArrayOnTimeTick();
#endif
continueRxingTicks = UpdateReassemblyList() || continueRxingTicks;
+56 -45
View File
@@ -444,64 +444,69 @@ private:
};
#if OPENTHREAD_FTD
class FragmentPriorityList : public Clearable<FragmentPriorityList>
{
public:
class Entry : public Clearable<Entry>
{
friend class FragmentPriorityList;
public:
// Lifetime of an entry in seconds.
static constexpr uint8_t kLifetime =
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
OT_MAX(kReassemblyTimeout, OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_FRAG_TAG_RETAIN_TIME);
static constexpr uint16_t kQmFwdEntries = OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_FRAG_TAG_ENTRY_LIST_SIZE;
#else
kReassemblyTimeout;
static constexpr uint16_t kQmFwdEntries = 0;
#endif
static constexpr uint16_t kPrioFwdEntries = OPENTHREAD_CONFIG_NUM_FRAGMENT_PRIORITY_ENTRIES;
static constexpr uint16_t kFwdInfoEntries = OT_MAX(kPrioFwdEntries, kQmFwdEntries);
Message::Priority GetPriority(void) const { return static_cast<Message::Priority>(mPriority); }
bool IsExpired(void) const { return (mLifetime == 0); }
void DecrementLifetime(void) { mLifetime--; }
void ResetLifetime(void) { mLifetime = kLifetime; }
class FwdFrameInfo
{
// Tracks information (priority, drop status) for forwarded
// mesh-header fragmented frames. This ensures consistent
// priority assignment to all fragments of the same message and
// facilitates delay-aware queue management, where dropping
// one fragment leads to dropping all subsequent fragments of
// the same message.
bool Matches(uint16_t aSrcRloc16, uint16_t aTag) const
{
return (mSrcRloc16 == aSrcRloc16) && (mDatagramTag == aTag);
}
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
bool ShouldDrop(void) const { return mShouldDrop; }
void MarkToDrop(void) { mShouldDrop = true; }
#endif
private:
uint16_t mSrcRloc16;
uint16_t mDatagramTag;
uint8_t mLifetime;
uint8_t mPriority : 2;
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
bool mShouldDrop : 1;
#endif
static_assert(Message::kNumPriorities <= 4, "mPriority as a 2-bit does not fit all `Priority` values");
public:
enum ExpireChecker : uint8_t
{
kIsExpired,
};
Entry *AllocateEntry(uint16_t aSrcRloc16, uint16_t aTag, Message::Priority aPriority);
Entry *FindEntry(uint16_t aSrcRloc16, uint16_t aTag);
bool UpdateOnTimeTick(void);
struct Info
{
uint16_t mSrcRloc16;
uint16_t mDatagramTag;
};
void Init(uint16_t aSrcRloc16, uint16_t aDatagramTag, Message::Priority aPriority);
bool Matches(const Info &aInfo) const;
bool Matches(const ExpireChecker) const { return IsExpired(); }
void ResetLifetime(void) { mLifetime = kLifetime; }
void DecrementLifetime(void) { mLifetime--; }
bool IsExpired(void) const { return (mLifetime == 0); }
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
bool ShouldDrop(void) const { return mShouldDrop; }
void MarkToDrop(void) { mShouldDrop = true; }
#endif
Message::Priority GetPriority(void) const { return static_cast<Message::Priority>(mPriority); }
private:
static constexpr uint16_t kNumEntries =
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
OT_MAX(OPENTHREAD_CONFIG_NUM_FRAGMENT_PRIORITY_ENTRIES,
OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_FRAG_TAG_ENTRY_LIST_SIZE);
static constexpr uint8_t kRetainTime = OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_FRAG_TAG_RETAIN_TIME;
#else
OPENTHREAD_CONFIG_NUM_FRAGMENT_PRIORITY_ENTRIES;
static constexpr uint8_t kRetainTime = 0;
#endif
static constexpr uint8_t kLifetime = OT_MAX(kReassemblyTimeout, kRetainTime);
uint16_t mSrcRloc16;
uint16_t mDatagramTag;
uint8_t mLifetime;
uint8_t mPriority : 2;
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
bool mShouldDrop : 1;
#endif
Entry mEntries[kNumEntries];
static_assert(Message::kNumPriorities <= 4, "mPriority as a 2-bit does not fit all `Priority` values");
};
using FwdFrameInfoArray = Array<FwdFrameInfo, kFwdInfoEntries>;
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE
@@ -591,10 +596,16 @@ private:
void ScheduleTransmissionTask(void);
Error GetFramePriority(RxInfo &aRxInfo, Message::Priority &aPriority);
#if OPENTHREAD_FTD
FwdFrameInfo *FindFwdFrameInfoEntry(uint16_t aSrcRloc16, uint16_t aDatagramTag);
bool UpdateFwdFrameInfoArrayOnTimeTick(void);
Error GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
uint16_t aSrcRloc16,
Message::Priority &aPriority);
void GetForwardFramePriority(RxInfo &aRxInfo, Message::Priority &aPriority);
#endif
bool CalcIePresent(const Message *aMessage);
Mac::Frame::Version CalcFrameVersion(const Neighbor *aNeighbor, bool aIePresent) const;
@@ -684,8 +695,8 @@ private:
otIpCounters mIpCounters;
#if OPENTHREAD_FTD
FragmentPriorityList mFragmentPriorityList;
IndirectSender mIndirectSender;
IndirectSender mIndirectSender;
FwdFrameInfoArray mFwdFrameInfoArray;
#endif
DataPollSender mDataPollSender;
+39 -59
View File
@@ -701,53 +701,37 @@ exit:
return;
}
bool MeshForwarder::FragmentPriorityList::UpdateOnTimeTick(void)
{
bool continueRxingTicks = false;
for (Entry &entry : mEntries)
{
if (!entry.IsExpired())
{
entry.DecrementLifetime();
if (!entry.IsExpired())
{
continueRxingTicks = true;
}
}
}
return continueRxingTicks;
}
void MeshForwarder::UpdateFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
uint16_t aFragmentLength,
uint16_t aSrcRloc16,
Message::Priority aPriority)
{
FragmentPriorityList::Entry *entry;
FwdFrameInfo *entry;
entry = mFragmentPriorityList.FindEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
entry = FindFwdFrameInfoEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
if (entry == nullptr)
{
VerifyOrExit(aFragmentHeader.GetDatagramOffset() == 0);
mFragmentPriorityList.AllocateEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag(), aPriority);
entry = mFwdFrameInfoArray.PushBack();
VerifyOrExit(entry != nullptr);
entry->Init(aSrcRloc16, aFragmentHeader.GetDatagramTag(), aPriority);
Get<TimeTicker>().RegisterReceiver(TimeTicker::kMeshForwarder);
ExitNow();
}
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
OT_UNUSED_VARIABLE(aFragmentLength);
#else
// We can clear the entry in `mFragmentPriorityList` if it is the
// We can remove the entry in `mFwdFrameInfoArray` if it is the
// last fragment. But if "delay aware active queue management" is
// used we need to keep entry until the message is sent.
if (aFragmentHeader.GetDatagramOffset() + aFragmentLength >= aFragmentHeader.GetDatagramSize())
{
entry->Clear();
mFwdFrameInfoArray.Remove(*entry);
}
else
#endif
@@ -759,55 +743,51 @@ exit:
return;
}
MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList::FindEntry(uint16_t aSrcRloc16,
uint16_t aTag)
void MeshForwarder::FwdFrameInfo::Init(uint16_t aSrcRloc16, uint16_t aDatagramTag, Message::Priority aPriority)
{
Entry *rval = nullptr;
for (Entry &entry : mEntries)
{
if (!entry.IsExpired() && entry.Matches(aSrcRloc16, aTag))
{
rval = &entry;
break;
}
}
return rval;
mSrcRloc16 = aSrcRloc16;
mDatagramTag = aDatagramTag;
mLifetime = kLifetime;
mPriority = aPriority;
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
mShouldDrop = false;
#endif
}
MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList::AllocateEntry(
uint16_t aSrcRloc16,
uint16_t aTag,
Message::Priority aPriority)
bool MeshForwarder::FwdFrameInfo::Matches(const Info &aInfo) const
{
Entry *newEntry = nullptr;
return (mSrcRloc16 == aInfo.mSrcRloc16) && (mDatagramTag == aInfo.mDatagramTag);
}
for (Entry &entry : mEntries)
MeshForwarder::FwdFrameInfo *MeshForwarder::FindFwdFrameInfoEntry(uint16_t aSrcRloc16, uint16_t aDatagramTag)
{
FwdFrameInfo::Info info;
info.mSrcRloc16 = aSrcRloc16;
info.mDatagramTag = aDatagramTag;
return mFwdFrameInfoArray.FindMatching(info);
}
bool MeshForwarder::UpdateFwdFrameInfoArrayOnTimeTick(void)
{
for (FwdFrameInfo &entry : mFwdFrameInfoArray)
{
if (entry.IsExpired())
{
entry.Clear();
entry.mSrcRloc16 = aSrcRloc16;
entry.mDatagramTag = aTag;
entry.mPriority = aPriority;
entry.ResetLifetime();
newEntry = &entry;
break;
}
entry.DecrementLifetime();
}
return newEntry;
mFwdFrameInfoArray.RemoveAllMatching(FwdFrameInfo::kIsExpired);
return !mFwdFrameInfoArray.IsEmpty();
}
Error MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader,
uint16_t aSrcRloc16,
Message::Priority &aPriority)
{
Error error = kErrorNone;
FragmentPriorityList::Entry *entry;
Error error = kErrorNone;
const FwdFrameInfo *entry = FindFwdFrameInfoEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
entry = mFragmentPriorityList.FindEntry(aSrcRloc16, aFragmentHeader.GetDatagramTag());
VerifyOrExit(entry != nullptr, error = kErrorNotFound);
aPriority = entry->GetPriority();