Skip to content

Commit

Permalink
[mle-router] add RouterRoleTransition nested class (openthread#9490)
Browse files Browse the repository at this point in the history
This commit adds `RouterRoleTransition` nested class to `MleRouter`.
This class tracks the timeout and jitter intervals for router role
transitions, i.e., device upgrading to router role from REED or
downgrading from router to REED. It provides helper methods like
`IsPending()` and `StartTimeout()` to check if role transition
is pending or to start the timeout countdown. These methods help to
simplify the code and make it more readable.
  • Loading branch information
abtink authored Oct 6, 2023
1 parent 4f6b492 commit 50e20c8
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 65 deletions.
2 changes: 1 addition & 1 deletion src/core/api/thread_ftd_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ uint8_t otThreadGetRouterSelectionJitter(otInstance *aInstance)

void otThreadSetRouterSelectionJitter(otInstance *aInstance, uint8_t aRouterJitter)
{
IgnoreError(AsCoreType(aInstance).Get<Mle::MleRouter>().SetRouterSelectionJitter(aRouterJitter));
AsCoreType(aInstance).Get<Mle::MleRouter>().SetRouterSelectionJitter(aRouterJitter);
}

otError otThreadGetChildInfoById(otInstance *aInstance, uint16_t aChildId, otChildInfo *aChildInfo)
Expand Down
7 changes: 4 additions & 3 deletions src/core/backbone_router/bbr_local.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,10 @@ void Local::HandleBackboneRouterPrimaryUpdate(Leader::State aState, const Config

void Local::HandleTimeTick(void)
{
// Delay registration when `GetRouterSelectionJitterTimeout()` is non-zero,
// which indicates device may soon switch its role (e.g., REED to router).
VerifyOrExit(Get<Mle::MleRouter>().GetRouterSelectionJitterTimeout() == 0);
// Delay registration while router role transition is pending
// (i.e., device may soon switch from REED to router role).

VerifyOrExit(!Get<Mle::MleRouter>().IsRouterRoleTransitionPending());

if (mRegistrationTimeout > 0)
{
Expand Down
2 changes: 1 addition & 1 deletion src/core/thread/dua_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ void DuaManager::HandleNotifierEvents(Events aEvents)
else if (mle.IsExpectedToBecomeRouterSoon())
{
// Will check again in case the device decides to stay REED when jitter timeout expires.
UpdateRegistrationDelay(mle.GetRouterSelectionJitterTimeout() + kNewRouterRegistrationDelay + 1);
UpdateRegistrationDelay(mle.GetRouterRoleTransitionTimeout() + kNewRouterRegistrationDelay + 1);
}
#endif
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/thread/mle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,7 @@ void Mle::InformPreviousChannel(void)
VerifyOrExit(IsChild() || IsRouter());

#if OPENTHREAD_FTD
VerifyOrExit(!IsFullThreadDevice() || IsRouter() || Get<MleRouter>().GetRouterSelectionJitterTimeout() == 0);
VerifyOrExit(!IsFullThreadDevice() || IsRouter() || !Get<MleRouter>().IsRouterRoleTransitionPending());
#endif

mAlternatePanId = Mac::kPanIdBroadcast;
Expand Down
91 changes: 42 additions & 49 deletions src/core/thread/mle_router.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,6 @@ MleRouter::MleRouter(Instance &aInstance)
, mPreviousPartitionId(0)
, mPreviousPartitionRouterIdSequence(0)
, mPreviousPartitionIdTimeout(0)
, mRouterSelectionJitter(kRouterSelectionJitter)
, mRouterSelectionJitterTimeout(0)
, mChildRouterLinks(kChildRouterLinks)
, mParentPriority(kParentPriorityUnspecified)
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
Expand Down Expand Up @@ -193,16 +191,13 @@ void MleRouter::HandleSecurityPolicyChanged(void)

VerifyOrExit(IsRouterOrLeader() && !IsRouterEligible());

// `mRouterSelectionJitterTimeout` is non-zero if we are already
// waiting to downgrade.
VerifyOrExit(!mRouterRoleTransition.IsPending());

VerifyOrExit(mRouterSelectionJitterTimeout == 0);

mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter);
mRouterRoleTransition.StartTimeout();

if (IsLeader())
{
mRouterSelectionJitterTimeout += kLeaderDowngradeExtraDelay;
mRouterRoleTransition.IncreaseTimeout(kLeaderDowngradeExtraDelay);
}

exit:
Expand All @@ -229,7 +224,7 @@ Error MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus)
LogInfo("Attempt to become router");

Get<MeshForwarder>().SetRxOnWhenIdle(true);
mRouterSelectionJitterTimeout = 0;
mRouterRoleTransition.StopTimeout();

switch (mRole)
{
Expand Down Expand Up @@ -329,7 +324,7 @@ void MleRouter::HandleChildStart(AttachMode aMode)
{
mAddressSolicitRejected = false;

mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter);
mRouterRoleTransition.StartTimeout();

StopLeader();
Get<TimeTicker>().RegisterReceiver(TimeTicker::kMleRouter);
Expand Down Expand Up @@ -1072,18 +1067,6 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
return error;
}

Error MleRouter::SetRouterSelectionJitter(uint8_t aRouterJitter)
{
Error error = kErrorNone;

VerifyOrExit(aRouterJitter > 0, error = kErrorInvalidArgs);

mRouterSelectionJitter = aRouterJitter;

exit:
return error;
}

Error MleRouter::ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo)
{
// This method processes `aRouteTlv` read from an MLE message.
Expand Down Expand Up @@ -1292,9 +1275,9 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
ExitNow(error = kErrorDetached);
}

if ((mRouterSelectionJitterTimeout == 0) && (mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold))
if (!mRouterRoleTransition.IsPending() && (mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold))
{
mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter);
mRouterRoleTransition.StartTimeout();
}

mRouterTable.UpdateRoutesOnFed(routeTlv, routerId);
Expand Down Expand Up @@ -1323,12 +1306,9 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Update routers as a router or leader.

if (IsRouter())
if (IsRouter() && ShouldDowngrade(routerId, routeTlv))
{
if (ShouldDowngrade(routerId, routeTlv))
{
mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter);
}
mRouterRoleTransition.StartTimeout();
}

router = mRouterTable.FindRouterById(routerId);
Expand Down Expand Up @@ -1514,7 +1494,7 @@ bool MleRouter::HasNeighborWithGoodLinkQuality(void) const

void MleRouter::HandleTimeTick(void)
{
bool routerStateUpdate = false;
bool roleTransitionTimeoutExpired = false;

VerifyOrExit(IsFullThreadDevice(), Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMleRouter));

Expand All @@ -1528,15 +1508,7 @@ void MleRouter::HandleTimeTick(void)
mPreviousPartitionIdTimeout--;
}

if (mRouterSelectionJitterTimeout > 0)
{
mRouterSelectionJitterTimeout--;

if (mRouterSelectionJitterTimeout == 0)
{
routerStateUpdate = true;
}
}
roleTransitionTimeoutExpired = mRouterRoleTransition.HandleTimeTick();

switch (mRole)
{
Expand All @@ -1550,11 +1522,10 @@ void MleRouter::HandleTimeTick(void)
break;

case kRoleChild:
if (routerStateUpdate)
if (roleTransitionTimeoutExpired)
{
if (mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold && HasNeighborWithGoodLinkQuality())
{
// upgrade to Router
IgnoreError(BecomeRouter(ThreadStatusTlv::kTooFewRouters));
}
else
Expand All @@ -1576,7 +1547,6 @@ void MleRouter::HandleTimeTick(void)
OT_FALL_THROUGH;

case kRoleRouter:
// verify path to leader
LogDebg("network id timeout = %lu", ToUlong(mRouterTable.GetLeaderAge()));

if ((mRouterTable.GetActiveRouterCount() > 0) && (mRouterTable.GetLeaderAge() >= mNetworkIdTimeout))
Expand All @@ -1585,7 +1555,7 @@ void MleRouter::HandleTimeTick(void)
Attach(kSamePartition);
}

if (routerStateUpdate && mRouterTable.GetActiveRouterCount() > mRouterDowngradeThreshold)
if (roleTransitionTimeoutExpired && mRouterTable.GetActiveRouterCount() > mRouterDowngradeThreshold)
{
LogNote("Downgrade to REED");
Attach(kDowngradeToReed);
Expand All @@ -1594,7 +1564,7 @@ void MleRouter::HandleTimeTick(void)
OT_FALL_THROUGH;

case kRoleLeader:
if (routerStateUpdate && !IsRouterEligible())
if (roleTransitionTimeoutExpired && !IsRouterEligible())
{
LogInfo("No longer router eligible");
IgnoreError(BecomeDetached());
Expand Down Expand Up @@ -3573,7 +3543,7 @@ bool MleRouter::IsExpectedToBecomeRouterSoon(void) const
static constexpr uint8_t kMaxDelay = 10;

return IsRouterEligible() && IsChild() && !mAddressSolicitRejected &&
((GetRouterSelectionJitterTimeout() != 0 && GetRouterSelectionJitterTimeout() <= kMaxDelay) ||
((mRouterRoleTransition.IsPending() && mRouterRoleTransition.GetTimeout() <= kMaxDelay) ||
mAddressSolicitPending);
}

Expand Down Expand Up @@ -3826,10 +3796,7 @@ bool MleRouter::ShouldDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv)
VerifyOrExit(IsRouter());
VerifyOrExit(mRouterTable.IsAllocated(aNeighborId));

// `mRouterSelectionJitterTimeout` is non-zero if we are already
// waiting to downgrade.

VerifyOrExit(mRouterSelectionJitterTimeout == 0);
VerifyOrExit(!mRouterRoleTransition.IsPending());

VerifyOrExit(activeRouterCount > mRouterDowngradeThreshold);

Expand Down Expand Up @@ -4037,6 +4004,32 @@ Error MleRouter::SendTimeSync(void)
}
#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE

//----------------------------------------------------------------------------------------------------------------------
// RouterRoleTransition

MleRouter::RouterRoleTransition::RouterRoleTransition(void)
: mTimeout(0)
, mJitter(kRouterSelectionJitter)
{
}

void MleRouter::RouterRoleTransition::StartTimeout(void)
{
mTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mJitter);
}

bool MleRouter::RouterRoleTransition::HandleTimeTick(void)
{
bool expired = false;

VerifyOrExit(mTimeout > 0);
mTimeout--;
expired = (mTimeout == 0);

exit:
return expired;
}

} // namespace Mle
} // namespace ot

Expand Down
46 changes: 37 additions & 9 deletions src/core/thread/mle_router.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,26 +265,36 @@ class MleRouter : public Mle
/**
* Returns the ROUTER_SELECTION_JITTER value.
*
* @returns The ROUTER_SELECTION_JITTER value.
* @returns The ROUTER_SELECTION_JITTER value in seconds.
*
*/
uint8_t GetRouterSelectionJitter(void) const { return mRouterSelectionJitter; }
uint8_t GetRouterSelectionJitter(void) const { return mRouterRoleTransition.GetJitter(); }

/**
* Sets the ROUTER_SELECTION_JITTER value.
*
* @returns The ROUTER_SELECTION_JITTER value.
* @param[in] aRouterJitter The router selection jitter value (in seconds).
*
*/
Error SetRouterSelectionJitter(uint8_t aRouterJitter);
void SetRouterSelectionJitter(uint8_t aRouterJitter) { mRouterRoleTransition.SetJitter(aRouterJitter); }

/**
* Returns the current router selection jitter timeout value.
* Indicates whether or not router role transition (upgrade from REED or downgrade to REED) is pending.
*
* @returns The current router selection jitter timeout value.
* @retval TRUE Router role transition is pending.
* @retval FALSE Router role transition is not pending
*
*/
uint8_t GetRouterSelectionJitterTimeout(void) const { return mRouterSelectionJitterTimeout; }
bool IsRouterRoleTransitionPending(void) const { return mRouterRoleTransition.IsPending(); }

/**
* Returns the current timeout delay in seconds till router role transition (upgrade from REED or downgrade to
* REED).
*
* @returns The timeout in seconds till router role transition, or zero if not pending role transition.
*
*/
uint8_t GetRouterRoleTransitionTimeout(void) const { return mRouterRoleTransition.GetTimeout(); }

/**
* Returns the ROUTER_UPGRADE_THRESHOLD value.
Expand Down Expand Up @@ -616,6 +626,25 @@ class MleRouter : public Mle
static constexpr int8_t kParentPriorityLow = -1;
static constexpr int8_t kParentPriorityUnspecified = -2;

class RouterRoleTransition
{
public:
RouterRoleTransition(void);

bool IsPending(void) const { return (mTimeout != 0); }
void StartTimeout(void);
void StopTimeout(void) { mTimeout = 0; }
void IncreaseTimeout(uint8_t aIncrement) { mTimeout += aIncrement; }
uint8_t GetTimeout(void) const { return mTimeout; }
bool HandleTimeTick(void);
uint8_t GetJitter(void) const { return mJitter; }
void SetJitter(uint8_t aJitter) { mJitter = aJitter; }

private:
uint8_t mTimeout;
uint8_t mJitter;
};

void HandleDetachStart(void);
void HandleChildStart(AttachMode aMode);
void HandleSecurityPolicyChanged(void);
Expand Down Expand Up @@ -727,8 +756,7 @@ class MleRouter : public Mle
uint8_t mPreviousPartitionRouterIdSequence; ///< The router ID sequence when last attached
uint8_t mPreviousPartitionIdTimeout; ///< The partition ID timeout when last attached

uint8_t mRouterSelectionJitter; ///< The variable to save the assigned jitter value.
uint8_t mRouterSelectionJitterTimeout; ///< The Timeout prior to request/release Router ID.
RouterRoleTransition mRouterRoleTransition;

uint8_t mChildRouterLinks;

Expand Down
2 changes: 1 addition & 1 deletion src/core/thread/network_data_notifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ void Notifier::SynchronizeServerData(void)
break;
#if OPENTHREAD_FTD
case kErrorInvalidState:
mTimer.Start(Time::SecToMsec(Get<Mle::MleRouter>().GetRouterSelectionJitterTimeout() + 1));
mTimer.Start(Time::SecToMsec(Get<Mle::MleRouter>().GetRouterRoleTransitionTimeout() + 1));
break;
#endif
case kErrorNotFound:
Expand Down

0 comments on commit 50e20c8

Please sign in to comment.