From 50e20c89587b647bd732809a7e931917d448482a Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 6 Oct 2023 12:09:48 -0700 Subject: [PATCH] [mle-router] add `RouterRoleTransition` nested class (#9490) 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. --- src/core/api/thread_ftd_api.cpp | 2 +- src/core/backbone_router/bbr_local.cpp | 7 +- src/core/thread/dua_manager.cpp | 2 +- src/core/thread/mle.cpp | 2 +- src/core/thread/mle_router.cpp | 91 +++++++++++------------ src/core/thread/mle_router.hpp | 46 +++++++++--- src/core/thread/network_data_notifier.cpp | 2 +- 7 files changed, 87 insertions(+), 65 deletions(-) diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp index 8d448fc0cb0..1b19693cd25 100644 --- a/src/core/api/thread_ftd_api.cpp +++ b/src/core/api/thread_ftd_api.cpp @@ -222,7 +222,7 @@ uint8_t otThreadGetRouterSelectionJitter(otInstance *aInstance) void otThreadSetRouterSelectionJitter(otInstance *aInstance, uint8_t aRouterJitter) { - IgnoreError(AsCoreType(aInstance).Get().SetRouterSelectionJitter(aRouterJitter)); + AsCoreType(aInstance).Get().SetRouterSelectionJitter(aRouterJitter); } otError otThreadGetChildInfoById(otInstance *aInstance, uint16_t aChildId, otChildInfo *aChildInfo) diff --git a/src/core/backbone_router/bbr_local.cpp b/src/core/backbone_router/bbr_local.cpp index 0ebff78d1e7..7c1289b06ea 100644 --- a/src/core/backbone_router/bbr_local.cpp +++ b/src/core/backbone_router/bbr_local.cpp @@ -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().GetRouterSelectionJitterTimeout() == 0); + // Delay registration while router role transition is pending + // (i.e., device may soon switch from REED to router role). + + VerifyOrExit(!Get().IsRouterRoleTransitionPending()); if (mRegistrationTimeout > 0) { diff --git a/src/core/thread/dua_manager.cpp b/src/core/thread/dua_manager.cpp index 591aacb30ff..934f3387000 100644 --- a/src/core/thread/dua_manager.cpp +++ b/src/core/thread/dua_manager.cpp @@ -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 } diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 0eaedd9d8b4..fa67446df98 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -770,7 +770,7 @@ void Mle::InformPreviousChannel(void) VerifyOrExit(IsChild() || IsRouter()); #if OPENTHREAD_FTD - VerifyOrExit(!IsFullThreadDevice() || IsRouter() || Get().GetRouterSelectionJitterTimeout() == 0); + VerifyOrExit(!IsFullThreadDevice() || IsRouter() || !Get().IsRouterRoleTransitionPending()); #endif mAlternatePanId = Mac::kPanIdBroadcast; diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 4c79a65f997..414bb75563d 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -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 @@ -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: @@ -229,7 +224,7 @@ Error MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus) LogInfo("Attempt to become router"); Get().SetRxOnWhenIdle(true); - mRouterSelectionJitterTimeout = 0; + mRouterRoleTransition.StopTimeout(); switch (mRole) { @@ -329,7 +324,7 @@ void MleRouter::HandleChildStart(AttachMode aMode) { mAddressSolicitRejected = false; - mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter); + mRouterRoleTransition.StartTimeout(); StopLeader(); Get().RegisterReceiver(TimeTicker::kMleRouter); @@ -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. @@ -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); @@ -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); @@ -1514,7 +1494,7 @@ bool MleRouter::HasNeighborWithGoodLinkQuality(void) const void MleRouter::HandleTimeTick(void) { - bool routerStateUpdate = false; + bool roleTransitionTimeoutExpired = false; VerifyOrExit(IsFullThreadDevice(), Get().UnregisterReceiver(TimeTicker::kMleRouter)); @@ -1528,15 +1508,7 @@ void MleRouter::HandleTimeTick(void) mPreviousPartitionIdTimeout--; } - if (mRouterSelectionJitterTimeout > 0) - { - mRouterSelectionJitterTimeout--; - - if (mRouterSelectionJitterTimeout == 0) - { - routerStateUpdate = true; - } - } + roleTransitionTimeoutExpired = mRouterRoleTransition.HandleTimeTick(); switch (mRole) { @@ -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 @@ -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)) @@ -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); @@ -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()); @@ -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); } @@ -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); @@ -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 diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index c4adf715dc5..1a1025c338f 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -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. @@ -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); @@ -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; diff --git a/src/core/thread/network_data_notifier.cpp b/src/core/thread/network_data_notifier.cpp index 09597cad779..657c0e3b548 100644 --- a/src/core/thread/network_data_notifier.cpp +++ b/src/core/thread/network_data_notifier.cpp @@ -108,7 +108,7 @@ void Notifier::SynchronizeServerData(void) break; #if OPENTHREAD_FTD case kErrorInvalidState: - mTimer.Start(Time::SecToMsec(Get().GetRouterSelectionJitterTimeout() + 1)); + mTimer.Start(Time::SecToMsec(Get().GetRouterRoleTransitionTimeout() + 1)); break; #endif case kErrorNotFound: