diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 40f45992b..6bddfd7e3 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -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; diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index 794141903..6364b267d 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -444,64 +444,69 @@ private: }; #if OPENTHREAD_FTD - class FragmentPriorityList : public Clearable - { - public: - class Entry : public Clearable - { - 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(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(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; + #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; diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 97c68e40d..d3613c9ef 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -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().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();