diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp index 8d448fc0c..1b19693cd 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 0ebff78d1..7c1289b06 100644 --- a/src/core/backbone_router/bbr_local.cpp +++ b/src/core/backbone_router/bbr_local.cpp @@ -289,9 +289,10 @@ exit: 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 591aacb30..934f33870 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 0eaedd9d8..fa67446df 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 4c79a65f9..414bb7556 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 @@ exit: 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 @@ exit: 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 @@ exit: } #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 c4adf715d..1a1025c33 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -265,26 +265,36 @@ public: /** * 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 @@ private: 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 @@ private: 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 09597cad7..657c0e3b5 100644 --- a/src/core/thread/network_data_notifier.cpp +++ b/src/core/thread/network_data_notifier.cpp @@ -108,7 +108,7 @@ exit: break; #if OPENTHREAD_FTD case kErrorInvalidState: - mTimer.Start(Time::SecToMsec(Get().GetRouterSelectionJitterTimeout() + 1)); + mTimer.Start(Time::SecToMsec(Get().GetRouterRoleTransitionTimeout() + 1)); break; #endif case kErrorNotFound: