mirror of
https://github.com/espressif/openthread.git
synced 2026-08-15 23:27:47 +00:00
[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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user