[trel] add PeerTable using OwningList and allow heap use (#11484)

This commit introduces `PeerTable` as a separate class to track TREL
peers, separating this logic from `Trel::Interface`. The peer table
uses `OwningList`, ensuring that `Peer` entries are properly freed
upon removal.

The logic for allocating a new peer is simplified, including the
mechanism to evict a peer to make room for a new one.

This commit also adds a new configuration option to allow TREL to use
heap-allocated `Peer` entries instead of a `Pool<Peer>` with a fixed
size. The `Peer` class now has a `Free()` method to ensure `Peer`
instances are properly freed, regardless of whether they are heap or
pool allocated. This, combined with the use of `OwningList`,
simplifies memory management.

To cover all configurations, `toranj` build configurations for the
POSIX platform are configured to disallow TREL heap usage, while
`toranj` configurations for the simulation platform enable it.
This commit is contained in:
Abtin Keshavarzian
2025-05-12 14:48:50 -07:00
committed by GitHub
parent adf306c9f5
commit c3823f648f
11 changed files with 320 additions and 171 deletions

View File

@ -50,17 +50,17 @@ bool otTrelIsEnabled(otInstance *aInstance) { return AsCoreType(aInstance).Get<T
void otTrelInitPeerIterator(otInstance *aInstance, otTrelPeerIterator *aIterator)
{
AsCoreType(aInstance).Get<Trel::Interface>().InitIterator(*aIterator);
AsCoreType(aInstance).Get<Trel::PeerTable>().InitIterator(*aIterator);
}
const otTrelPeer *otTrelGetNextPeer(otInstance *aInstance, otTrelPeerIterator *aIterator)
{
return AsCoreType(aInstance).Get<Trel::Interface>().GetNextPeer(*aIterator);
return AsCoreType(aInstance).Get<Trel::PeerTable>().GetNextPeer(*aIterator);
}
uint16_t otTrelGetNumberOfPeers(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<Trel::Interface>().GetNumberOfPeers();
return AsCoreType(aInstance).Get<Trel::PeerTable>().GetNumberOfPeers();
}
void otTrelSetFilterEnabled(otInstance *aInstance, bool aEnable)

View File

@ -43,14 +43,23 @@
* @{
*/
/**
* @def OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE
*
* Define as 1 to allow TREL modules to use heap allocated objects (e.g. for the TREL peer table).
*/
#ifndef OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE
#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_TREL_PEER_TABLE_SIZE
*
* Specifies the capacity of TREL peer table. Only non-zero value
* will be directly used for setting the TREL peer table capacity.
* Zero value lets the size to be determined by the OT stack itself
* which is derived based on other configurations such as a child
* table size, neighbor table size, etc.
* Specifies the capacity of TREL peer table. Only non-zero value will be directly used for setting the TREL peer table
* capacity. Zero value lets the size to be determined by the OT stack itself which is derived based on other
* configurations such as a child table size, neighbor table size, etc.
*
* Applicable when `OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE` is not used.
*/
#ifndef OPENTHREAD_CONFIG_TREL_PEER_TABLE_SIZE
#define OPENTHREAD_CONFIG_TREL_PEER_TABLE_SIZE (0)

View File

@ -799,6 +799,8 @@ template <> inline Mac::SubMac &Instance::Get(void) { return mMac.mLinks.mSubMac
template <> inline Trel::Link &Instance::Get(void) { return mMac.mLinks.mTrel; }
template <> inline Trel::Interface &Instance::Get(void) { return mMac.mLinks.mTrel.mInterface; }
template <> inline Trel::PeerTable &Instance::Get(void) { return mMac.mLinks.mTrel.mPeerTable; }
#endif
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE

View File

@ -102,21 +102,13 @@ void Interface::Disable(void)
VerifyOrExit(mInitialized);
otPlatTrelDisable(&GetInstance());
ClearPeerList();
Get<PeerTable>().Clear();
LogDebg("Disabled interface");
exit:
return;
}
void Interface::ClearPeerList(void)
{
mPeerList.Clear();
mPeerPool.FreeAll();
}
Peer *Interface::FindPeer(const Mac::ExtAddress &aExtAddress) { return mPeerList.FindMatching(aExtAddress); }
void Interface::NotifyPeerSocketAddressDifference(const Ip6::SockAddr &aPeerSockAddr, const Ip6::SockAddr &aRxSockAddr)
{
otPlatTrelNotifyPeerSocketAddressDifference(&GetInstance(), &aPeerSockAddr, &aRxSockAddr);
@ -183,7 +175,7 @@ exit:
void Interface::HandleDiscoveredPeerInfo(const PeerInfo &aInfo)
{
Peer *entry;
Peer *peer;
Mac::ExtAddress extAddress;
MeshCoP::ExtendedPanId extPanId;
bool isNew = false;
@ -196,9 +188,7 @@ void Interface::HandleDiscoveredPeerInfo(const PeerInfo &aInfo)
if (aInfo.IsRemoved())
{
entry = FindPeer(extAddress);
VerifyOrExit(entry != nullptr);
RemovePeerEntry(*entry);
Get<PeerTable>().RemoveAndFreeAllMatching(extAddress);
ExitNow();
}
@ -208,37 +198,37 @@ void Interface::HandleDiscoveredPeerInfo(const PeerInfo &aInfo)
// different Extended MAC address. This ensures that we do not
// keep stale entries in the peer table.
entry = mPeerList.FindMatching(aInfo.GetSockAddr());
peer = Get<PeerTable>().FindMatching(aInfo.GetSockAddr());
if ((entry != nullptr) && !entry->Matches(extAddress))
if ((peer != nullptr) && !peer->Matches(extAddress))
{
RemovePeerEntry(*entry);
entry = nullptr;
Get<PeerTable>().RemoveMatching(aInfo.GetSockAddr());
peer = nullptr;
}
if (entry == nullptr)
if (peer == nullptr)
{
entry = mPeerList.FindMatching(extAddress);
peer = Get<PeerTable>().FindMatching(extAddress);
}
if (entry == nullptr)
if (peer == nullptr)
{
entry = GetNewPeerEntry();
VerifyOrExit(entry != nullptr);
peer = Get<PeerTable>().AllocateAndAddNewPeer();
VerifyOrExit(peer != nullptr);
entry->SetExtAddress(extAddress);
peer->SetExtAddress(extAddress);
isNew = true;
}
if (!isNew)
{
VerifyOrExit((entry->GetExtPanId() != extPanId) || (entry->GetSockAddr() != aInfo.GetSockAddr()));
VerifyOrExit((peer->GetExtPanId() != extPanId) || (peer->GetSockAddr() != aInfo.GetSockAddr()));
}
entry->SetExtPanId(extPanId);
entry->SetSockAddr(aInfo.GetSockAddr());
peer->SetExtPanId(extPanId);
peer->SetSockAddr(aInfo.GetSockAddr());
entry->Log(isNew ? Peer::kAdded : Peer::kUpdated);
peer->Log(isNew ? Peer::kAdded : Peer::kUpdated);
exit:
return;
@ -294,60 +284,6 @@ exit:
return error;
}
Peer *Interface::GetNewPeerEntry(void)
{
Peer *peerEntry;
peerEntry = mPeerPool.Allocate();
if (peerEntry != nullptr)
{
mPeerList.Push(*peerEntry);
ExitNow();
}
for (Peer &entry : mPeerList)
{
if (entry.GetExtPanId() != Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId())
{
ExitNow(peerEntry = &entry);
}
}
for (Peer &entry : mPeerList)
{
// We skip over any existing entry in neighbor table (even if the
// entry is in invalid state).
if (Get<NeighborTable>().FindNeighbor(entry.GetExtAddress(), Neighbor::kInStateAny) != nullptr)
{
continue;
}
#if OPENTHREAD_FTD
if (Get<NeighborTable>().FindRxOnlyNeighborRouter(entry.GetExtAddress()) != nullptr)
{
continue;
}
#endif
ExitNow(peerEntry = &entry);
}
exit:
return peerEntry;
}
void Interface::RemovePeerEntry(Peer &aEntry)
{
aEntry.Log(Peer::kRemoving);
if (mPeerList.Remove(aEntry) == kErrorNone)
{
mPeerPool.Free(aEntry);
}
}
const Counters *Interface::GetCounters(void) const { return otPlatTrelGetCounters(&GetInstance()); }
void Interface::ResetCounters(void) { otPlatTrelResetCounters(&GetInstance()); }
@ -363,20 +299,20 @@ Error Interface::Send(const Packet &aPacket, bool aIsDiscovery)
switch (aPacket.GetHeader().GetType())
{
case Header::kTypeBroadcast:
for (Peer &entry : mPeerList)
for (const Peer &peer : Get<PeerTable>())
{
if (!aIsDiscovery && (entry.GetExtPanId() != Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()))
if (!aIsDiscovery && (peer.GetExtPanId() != Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()))
{
continue;
}
otPlatTrelSend(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &entry.mSockAddr);
otPlatTrelSend(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &peer.mSockAddr);
}
break;
case Header::kTypeUnicast:
case Header::kTypeAck:
peerEntry = mPeerList.FindMatching(aPacket.GetHeader().GetDestination());
peerEntry = Get<PeerTable>().FindMatching(aPacket.GetHeader().GetDestination());
VerifyOrExit(peerEntry != nullptr, error = kErrorAbort);
otPlatTrelSend(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &peerEntry->mSockAddr);
break;
@ -413,30 +349,6 @@ exit:
return;
}
const Peer *Interface::GetNextPeer(PeerIterator &aIterator) const
{
const Peer *entry = static_cast<const Peer *>(aIterator);
VerifyOrExit(entry != nullptr);
aIterator = entry->GetNext();
exit:
return entry;
}
uint16_t Interface::GetNumberOfPeers(void) const
{
uint16_t count = 0;
for (const Peer &peer : mPeerList)
{
OT_UNUSED_VARIABLE(peer);
count++;
}
return count;
}
} // namespace Trel
} // namespace ot

View File

@ -79,11 +79,6 @@ class Interface : public InstanceLocator
friend void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo);
public:
/**
* Represents an iterator for iterating over TREL peer table entries.
*/
typedef otTrelPeerIterator PeerIterator;
/**
* Enables or disables the TREL interface.
*
@ -117,29 +112,6 @@ public:
*/
bool IsEnabled(void) const { return mEnabled; }
/**
* Initializes a peer table iterator.
*
* @param[in] aIterator The iterator to initialize.
*/
void InitIterator(PeerIterator &aIterator) const { aIterator = mPeerList.GetHead(); }
/**
* Iterates over the peer table entries.
*
* @param[in] aIterator The iterator. MUST be initialized.
*
* @returns A pointer to the next `Peer` entry or `nullptr` if no more entries in the table.
*/
const Peer *GetNextPeer(PeerIterator &aIterator) const;
/**
* Returns the number of TREL peers.
*
* @returns The number of TREL peers.
*/
uint16_t GetNumberOfPeers(void) const;
/**
* Sets the filter mode (enables/disables filtering).
*
@ -180,15 +152,6 @@ public:
*/
uint16_t GetUdpPort(void) const { return mUdpPort; }
/**
* Finds the TREL peer associated with a given Extended Address.
*
* @param[in] aExtAddress The extended address.
*
* @returns The peer associated with @ aExtAddress, or `nullptr` if not found.
*/
Peer *FindPeer(const Mac::ExtAddress &aExtAddress);
/**
* Notifies platform that a TREL packet is received from a peer using a different socket address than the one
* reported earlier.
@ -228,9 +191,6 @@ private:
void HandleDiscoveredPeerInfo(const PeerInfo &aInfo);
void RegisterService(void);
Peer *GetNewPeerEntry(void);
void RemovePeerEntry(Peer &aEntry);
void ClearPeerList(void);
using RegisterServiceTask = TaskletIn<Interface, &Interface::RegisterService>;
@ -240,8 +200,6 @@ private:
RegisterServiceTask mRegisterServiceTask;
uint16_t mUdpPort;
Packet mRxPacket;
LinkedList<Peer> mPeerList;
Pool<Peer, kPeerPoolSize> mPeerPool;
};
} // namespace Trel

View File

@ -50,6 +50,7 @@ Link::Link(Instance &aInstance)
, mTxTasklet(aInstance)
, mTimer(aInstance)
, mInterface(aInstance)
, mPeerTable(aInstance)
{
ClearAllBytes(mTxFrame);
ClearAllBytes(mRxFrame);
@ -343,7 +344,7 @@ void Link::ProcessReceivedPacket(Packet &aPacket, const Ip6::SockAddr &aSockAddr
VerifyOrExit(aPacket.GetHeader().GetSource() != Get<Mac::Mac>().GetExtAddress());
mRxPacketSenderAddr = aSockAddr;
mRxPacketPeer = Get<Interface>().FindPeer(aPacket.GetHeader().GetSource());
mRxPacketPeer = Get<PeerTable>().FindMatching(aPacket.GetHeader().GetSource());
if (type != Header::kTypeBroadcast)
{

View File

@ -47,6 +47,7 @@
#include "mac/mac_types.hpp"
#include "radio/trel_interface.hpp"
#include "radio/trel_packet.hpp"
#include "radio/trel_peer.hpp"
namespace ot {
@ -199,6 +200,7 @@ private:
TxTasklet mTxTasklet;
TimeoutTimer mTimer;
Interface mInterface;
PeerTable mPeerTable;
Ip6::SockAddr mRxPacketSenderAddr;
Peer *mRxPacketPeer;
Mac::RxFrame mRxFrame;

View File

@ -39,7 +39,50 @@
namespace ot {
namespace Trel {
RegisterLogModule("TrelInterface");
RegisterLogModule("TrelPeerTable");
//---------------------------------------------------------------------------------------------------------------------
// Peer
void Peer::Init(Instance &aInstance)
{
InstanceLocatorInit::Init(aInstance);
AsCoreType(&mExtAddress).Clear();
AsCoreType(&mExtPanId).Clear();
AsCoreType(&mSockAddr).Clear();
}
void Peer::Free(void)
{
Log(kRemoving);
#if OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE
Heap::Allocatable<Peer>::Free();
#else
this->~Peer();
Get<PeerTable>().mPool.Free(*this);
#endif
}
bool Peer::Matches(const NonNeighborMatcher &aMatcher) const
{
// Matches only if the peer is not a neighbor. This is used when
// evicting a peer to make room for a new one, where we search
// for and remove a non-neighbor from the list.
bool matches = false;
VerifyOrExit(aMatcher.mNeighborTable.FindNeighbor(GetExtAddress(), Neighbor::kInStateAny) == nullptr);
#if OPENTHREAD_FTD
VerifyOrExit(aMatcher.mNeighborTable.FindRxOnlyNeighborRouter(GetExtAddress()) == nullptr);
#endif
matches = true;
exit:
return matches;
}
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
@ -55,6 +98,7 @@ const char *Peer::ActionToString(Action aAction)
"Added", // (0) kAdded
"Updated", // (1) kUpdated
"Removing", // (2) kRemoving
"Evicting", // (3) kEvicting
};
struct EnumCheck
@ -63,6 +107,7 @@ const char *Peer::ActionToString(Action aAction)
ValidateNextEnum(kAdded);
ValidateNextEnum(kUpdated);
ValidateNextEnum(kRemoving);
ValidateNextEnum(kEvicting);
};
return kActionStrings[aAction];
@ -70,6 +115,98 @@ const char *Peer::ActionToString(Action aAction)
#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
//---------------------------------------------------------------------------------------------------------------------
// PeerTable
PeerTable::PeerTable(Instance &aInstance)
: InstanceLocator(aInstance)
{
}
Peer *PeerTable::AllocatePeer(void)
{
Peer *newPeer;
#if OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE
newPeer = Peer::Allocate();
#else
newPeer = mPool.Allocate();
#endif
return newPeer;
}
Peer *PeerTable::AllocateAndAddNewPeer(void)
{
Peer *newPeer = nullptr;
do
{
newPeer = AllocatePeer();
if (newPeer != nullptr)
{
break;
}
} while (EvictPeer() == kErrorNone);
VerifyOrExit(newPeer != nullptr);
newPeer->Init(GetInstance());
Push(*newPeer);
exit:
return newPeer;
}
Error PeerTable::EvictPeer(void)
{
Error error = kErrorNotFound;
OwnedPtr<Peer> peerToEvict;
// We first try to evict a peer belonging to a different PAN.
// If not found, we evict a non-neighbor peer.
peerToEvict = RemoveMatching(Peer::OtherExtPanIdMatcher(Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()));
if (peerToEvict == nullptr)
{
peerToEvict = RemoveMatching(Peer::NonNeighborMatcher(Get<NeighborTable>()));
}
VerifyOrExit(peerToEvict != nullptr);
peerToEvict->Log(Peer::kEvicting);
error = kErrorNone;
exit:
return error;
}
const Peer *PeerTable::GetNextPeer(PeerIterator &aIterator) const
{
const Peer *entry = static_cast<const Peer *>(aIterator);
VerifyOrExit(entry != nullptr);
aIterator = entry->GetNext();
exit:
return entry;
}
uint16_t PeerTable::GetNumberOfPeers(void) const
{
uint16_t count = 0;
for (const Peer &peer : *this)
{
OT_UNUSED_VARIABLE(peer);
count++;
}
return count;
}
} // namespace Trel
} // namespace ot

View File

@ -42,25 +42,43 @@
#include <openthread/platform/trel.h>
#include "common/as_core_type.hpp"
#include "common/linked_list.hpp"
#include "common/heap_allocatable.hpp"
#include "common/locator.hpp"
#include "common/log.hpp"
#include "common/non_copyable.hpp"
#include "common/owning_list.hpp"
#include "common/pool.hpp"
#include "mac/mac_types.hpp"
#include "meshcop/extended_panid.hpp"
#include "net/socket.hpp"
#include "thread/mle_types.hpp"
namespace ot {
class NeighborTable;
namespace Trel {
class Interface;
class PeerTable;
/**
* Represents a discovered TREL peer.
*/
class Peer : public otTrelPeer, public LinkedListEntry<Peer>, private NonCopyable
class Peer : public InstanceLocatorInit,
public otTrelPeer,
public LinkedListEntry<Peer>,
#if OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE
public Heap::Allocatable<Peer>,
#endif
private NonCopyable
{
friend class Interface;
friend class PeerTable;
friend class OwnedPtr<Peer>;
friend class OwningList<Peer>;
friend class LinkedList<Peer>;
friend class LinkedListEntry<Peer>;
public:
@ -118,10 +136,35 @@ private:
kAdded,
kUpdated,
kRemoving,
kEvicting,
};
struct OtherExtPanIdMatcher // Matches if Ext PAN ID is different.
{
explicit OtherExtPanIdMatcher(const MeshCoP::ExtendedPanId &aExtPanId)
: mExtPanId(aExtPanId)
{
}
const MeshCoP::ExtendedPanId &mExtPanId;
};
struct NonNeighborMatcher
{
explicit NonNeighborMatcher(NeighborTable &aNeighborTable)
: mNeighborTable(aNeighborTable)
{
}
NeighborTable &mNeighborTable;
};
void Init(Instance &aInstance);
void Free(void);
void SetExtAddress(const Mac::ExtAddress &aExtAddress) { mExtAddress = aExtAddress; }
void SetExtPanId(const MeshCoP::ExtendedPanId &aExtPanId) { mExtPanId = aExtPanId; }
bool Matches(const OtherExtPanIdMatcher &aMatcher) const { return GetExtPanId() != aMatcher.mExtPanId; }
bool Matches(const NonNeighborMatcher &aMatcher) const;
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
void Log(Action aAction) const;
@ -133,6 +176,84 @@ private:
Peer *mNext;
};
//---------------------------------------------------------------------------------------------------------------------
/**
* Represents the TREL peer table.
*/
class PeerTable : public InstanceLocator, public OwningList<Peer>
{
friend class Peer;
public:
/**
* Represents an iterator for iterating over TREL peer table entries.
*/
typedef otTrelPeerIterator PeerIterator;
/**
* Initializes the TREL peer table.
*
* @param[in] aInstance The OpenThread instance.
*/
explicit PeerTable(Instance &aInstance);
/**
* Allocates a new peer entry and adds it to the table.
*
* This method attempts to allocate a new `Peer`. If successful, the new peer is added to the peer table and
* becomes owned by it.
*
* If the initial allocation fail, the method will then attempt to make space by evicting an existing peer from the
* table. The eviction strategy prioritizes removing a peer associated with a different Extended PAN ID. If no
* such suitable peer is found, a non-neighbor peer will be targeted for eviction. If an existing peer is
* successfully evicted, allocation for the new `Peer` object is re-attempted.
*
* @returns A pointer to the newly allocated and added `Peer`, or `nullptr` if the allocation fails.
*/
Peer *AllocateAndAddNewPeer(void);
/**
* Initializes a peer table iterator.
*
* @param[in] aIterator The iterator to initialize.
*/
void InitIterator(PeerIterator &aIterator) const { aIterator = GetHead(); }
/**
* Iterates over the peer table entries.
*
* @param[in] aIterator The iterator. MUST be initialized.
*
* @returns A pointer to the next `Peer` entry or `nullptr` if no more entries in the table.
*/
const Peer *GetNextPeer(PeerIterator &aIterator) const;
/**
* Returns the number of TREL peers.
*
* @returns The number of TREL peers.
*/
uint16_t GetNumberOfPeers(void) const;
private:
#if !OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE
#if OPENTHREAD_CONFIG_TREL_PEER_TABLE_SIZE != 0
static constexpr uint16_t PoolSize = OPENTHREAD_CONFIG_TREL_PEER_TABLE_SIZE;
#else
static constexpr uint16_t kExtraEntries = 32;
static constexpr uint16_t PoolSize = Mle::kMaxRouters + Mle::kMaxChildren + kExtraEntries;
#endif
#endif
Peer *AllocatePeer(void);
Error EvictPeer(void);
#if !OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE
Pool<Peer, PoolSize> mPool;
#endif
};
} // namespace Trel
} // namespace ot

View File

@ -66,7 +66,12 @@
#define OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE 1
// Disabled explicitly on posix `toranj` to validate the build with this config
// The following two features (`USE_HEAP`) are disabled explicitly on posix `toranj`
// to validate the build with these config. The `toranj` build under simulation
// platform covers the opposite configs (allows `USE_HEAP`).
#define OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE 0
#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE 0
#endif /* OPENTHREAD_CORE_TORANJ_CONFIG_POSIX_H_ */

View File

@ -87,4 +87,6 @@
#define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 0
#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE 1
#endif /* OPENTHREAD_CORE_TORANJ_CONFIG_SIMULATION_H_ */