Skip to content

Commit

Permalink
[mesh-forwarder] use Array to track forwarded frame info (#10540)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
abtink authored Sep 16, 2024
1 parent 9d8fcfd commit d60ec88
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 113 deletions.
19 changes: 6 additions & 13 deletions src/core/thread/mesh_forwarder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -358,22 +352,21 @@ 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())
{
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);
}
}
}
Expand Down Expand Up @@ -1588,7 +1581,7 @@ void MeshForwarder::HandleTimeTick(void)
bool continueRxingTicks = false;

#if OPENTHREAD_FTD
continueRxingTicks = mFragmentPriorityList.UpdateOnTimeTick();
continueRxingTicks = UpdateFwdFrameInfoArrayOnTimeTick();
#endif

continueRxingTicks = UpdateReassemblyList() || continueRxingTicks;
Expand Down
95 changes: 53 additions & 42 deletions src/core/thread/mesh_forwarder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,64 +444,69 @@ class MeshForwarder : public InstanceLocator, private NonCopyable
};

#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; }

bool Matches(uint16_t aSrcRloc16, uint16_t aTag) const
{
return (mSrcRloc16 == aSrcRloc16) && (mDatagramTag == aTag);
}
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.

#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
bool ShouldDrop(void) const { return mShouldDrop; }
void MarkToDrop(void) { mShouldDrop = true; }
#endif
public:
enum ExpireChecker : uint8_t
{
kIsExpired,
};

private:
struct Info
{
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");
};

Entry *AllocateEntry(uint16_t aSrcRloc16, uint16_t aTag, Message::Priority aPriority);
Entry *FindEntry(uint16_t aSrcRloc16, uint16_t aTag);
bool UpdateOnTimeTick(void);
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
Expand Down Expand Up @@ -591,10 +596,16 @@ class MeshForwarder : public InstanceLocator, private NonCopyable
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;
Expand Down Expand Up @@ -684,8 +695,8 @@ class MeshForwarder : public InstanceLocator, private NonCopyable
otIpCounters mIpCounters;

#if OPENTHREAD_FTD
FragmentPriorityList mFragmentPriorityList;
IndirectSender mIndirectSender;
IndirectSender mIndirectSender;
FwdFrameInfoArray mFwdFrameInfoArray;
#endif

DataPollSender mDataPollSender;
Expand Down
96 changes: 38 additions & 58 deletions src/core/thread/mesh_forwarder_ftd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -701,53 +701,37 @@ void MeshForwarder::UpdateRoutes(RxInfo &aRxInfo)
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
Expand All @@ -759,55 +743,51 @@ void MeshForwarder::UpdateFragmentPriority(Lowpan::FragmentHeader &aFragmentHead
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;
}
}
mSrcRloc16 = aSrcRloc16;
mDatagramTag = aDatagramTag;
mLifetime = kLifetime;
mPriority = aPriority;
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
mShouldDrop = false;
#endif
}

return rval;
bool MeshForwarder::FwdFrameInfo::Matches(const Info &aInfo) const
{
return (mSrcRloc16 == aInfo.mSrcRloc16) && (mDatagramTag == aInfo.mDatagramTag);
}

MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList::AllocateEntry(
uint16_t aSrcRloc16,
uint16_t aTag,
Message::Priority aPriority)
MeshForwarder::FwdFrameInfo *MeshForwarder::FindFwdFrameInfoEntry(uint16_t aSrcRloc16, uint16_t aDatagramTag)
{
Entry *newEntry = nullptr;
FwdFrameInfo::Info info;

for (Entry &entry : mEntries)
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();

Expand Down

0 comments on commit d60ec88

Please sign in to comment.