From 1e68d2bbe0559f2be26c6448a4dd0472a82218f6 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 31 May 2018 12:30:59 -0700 Subject: [PATCH] [mle] attach backoff feature (#2713) This commit adds "attach backoff" feature in MLE. When enabled, an exponentially increasing backoff wait time is added between attach attempts. If device is sleepy, the radio will be put to sleep during the wait time. This ensures that a battery-powered sleepy end-device does not drain its battery by continuously searching for a parent to attach to (when there is no router/parent for it to attach). `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF` configuration option can be used to enable/disable this feature. It is enabled by default. --- src/core/openthread-core-default-config.h | 65 ++++++++++++++++++ src/core/thread/mle.cpp | 84 +++++++++++++++++++++-- src/core/thread/mle.hpp | 24 ++++--- src/core/thread/mle_constants.hpp | 2 +- 4 files changed, 157 insertions(+), 18 deletions(-) diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index d2b53d54f..4970f374b 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -1342,6 +1342,71 @@ #define OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD -65 #endif +/** + * @def OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF + * + * Define as 1 to enable attach backoff feature + * + * When this feature is enabled, an exponentially increasing backoff wait time is added between attach attempts. + * If device is sleepy, the radio will be put to sleep during the wait time. This ensures that a battery-powered sleepy + * end-device does not drain its battery by continuously searching for a parent to attach to (when there is no + * router/parent for it to attach). + * + * The backoff time starts from a minimum interval specified by `OPENTHREAD_CONFIG_ATTACH_BACKOFF_MINIMUM_INTERVAL`, + * and every attach attempt the wait time is doubled up to `OPENTHREAD_CONFIG_ATTACH_BACKOFF_MAXIMUM_INTERVAL` which + * specifies the maximum wait time. + * + * Once the wait time reaches the maximum, a random jitter interval is added to it. The maximum value for jitter is + * specified by `OPENTHREAD_CONFIG_ATTACH_BACKOFF_JITTER_INTERVAL`. The random jitter is selected uniformly within + * range `[-JITTER, +JITTER]`. It is only added when the backoff wait interval is at maximum value. + * + */ +#ifndef OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF +#define OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_ATTACH_BACKOFF_MINIMUM_INTERVAL + * + * Specifies the minimum backoff wait interval (in milliseconds) used by attach backoff feature. + * + * Applicable only if attach backoff feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF`). + * + * Please see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF` description for more details. + * + */ +#ifndef OPENTHREAD_CONFIG_ATTACH_BACKOFF_MINIMUM_INTERVAL +#define OPENTHREAD_CONFIG_ATTACH_BACKOFF_MINIMUM_INTERVAL 251 +#endif + +/** + * @def OPENTHREAD_CONFIG_ATTACH_BACKOFF_MAXIMUM_INTERVAL + * + * Specifies the maximum backoff wait interval (in milliseconds) used by attach backoff feature. + * + * Applicable only if attach backoff feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF`). + * + * Please see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF` description for more details. + * + */ +#ifndef OPENTHREAD_CONFIG_ATTACH_BACKOFF_MAXIMUM_INTERVAL +#define OPENTHREAD_CONFIG_ATTACH_BACKOFF_MAXIMUM_INTERVAL 1200000 // 1200 seconds = 20 minutes +#endif + +/** + * @def OPENTHREAD_CONFIG_ATTACH_BACKOFF_JITTER_INTERVAL + * + * Specifies the maximum jitter interval (in milliseconds) used by attach backoff feature. + * + * Applicable only if attach backoff feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF`). + * + * Please see `OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF` description for more details. + * + */ +#ifndef OPENTHREAD_CONFIG_ATTACH_BACKOFF_JITTER_INTERVAL +#define OPENTHREAD_CONFIG_ATTACH_BACKOFF_JITTER_INTERVAL 2000 +#endif + /** * @def OPENTHREAD_CONFIG_SEND_UNICAST_ANNOUNCE_RESPONSE * diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 773af43b3..68bef6f3a 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -520,6 +520,12 @@ otError Mle::BecomeDetached(void) VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE); + // In case role is already detached and attach state is `kAttachStateStart` + // (i.e., waiting to start an attach attempt), there is no need to make any + // changes. + + VerifyOrExit(mRole != OT_DEVICE_ROLE_DETACHED || mAttachState != kAttachStateStart); + // not in reattach stage after reset if (mReattachState == kReattachStop) { @@ -563,10 +569,6 @@ otError Mle::BecomeChild(AttachMode aMode) SetAttachState(kAttachStateStart); mParentRequestMode = aMode; - mAttachCounter++; - otLogInfoMle(GetInstance(), "Attempt to attach - attempt %d, %s %s", mAttachCounter, AttachModeToString(aMode), - ReattachStateToString(mReattachState)); - if (aMode != kAttachBetter) { if (IsFullThreadDevice()) @@ -575,12 +577,71 @@ otError Mle::BecomeChild(AttachMode aMode) } } - mAttachTimer.Start(1 + ((mAttachCounter == 1) ? Random::GetUint32InRange(0, kParentRequestRouterTimeout) : 0)); + mAttachTimer.Start(GetAttachStartDelay()); + + if (mRole == OT_DEVICE_ROLE_DETACHED) + { + mAttachCounter++; + + if (mAttachCounter == 0) + { + mAttachCounter--; + } + + if (!IsRxOnWhenIdle()) + { + netif.GetMac().SetRxOnWhenIdle(false); + } + } exit: return error; } +uint32_t Mle::GetAttachStartDelay(void) const +{ + uint32_t delay = 1; + uint32_t jitter; + + VerifyOrExit(mRole == OT_DEVICE_ROLE_DETACHED); + + if (mAttachCounter == 0) + { + delay = 1 + Random::GetUint32InRange(0, kParentRequestRouterTimeout); + ExitNow(); + } +#if OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF + else + { + uint16_t counter = mAttachCounter - 1; + const uint32_t ratio = kAttachBackoffMaxInterval / kAttachBackoffMinInterval; + + if ((counter <= sizeof(ratio) * CHAR_BIT) && ((1U << counter) <= ratio)) + { + delay = kAttachBackoffMinInterval; + delay <<= counter; + } + else + { + delay = Random::AddJitter(kAttachBackoffMaxInterval, kAttachBackoffJitter); + } + } +#endif // OPENTHREAD_CONFIG_ENABLE_ATTACH_BACKOFF + + jitter = Random::GetUint32InRange(0, kAttachStartJitter); + + if (jitter + delay > delay) // check for overflow + { + delay += jitter; + } + + otLogInfoMle(GetInstance(), "Attach attempt %d unsuccessful, will try again in %u.%03u seconds", mAttachCounter, + delay / 1000, delay % 1000); + +exit: + return delay; +} + bool Mle::IsAttached(void) const { return (mRole == OT_DEVICE_ROLE_CHILD || mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER); @@ -1484,6 +1545,17 @@ void Mle::HandleAttachTimer(void) break; case kAttachStateStart: + if (mAttachCounter > 0) + { + otLogInfoMle(GetInstance(), "Attempt to attach - attempt %d, %s %s", mAttachCounter, + AttachModeToString(mParentRequestMode), ReattachStateToString(mReattachState)); + } + else + { + otLogInfoMle(GetInstance(), "Attempt to attach - %s %s", AttachModeToString(mParentRequestMode), + ReattachStateToString(mReattachState)); + } + SetAttachState(kAttachStateParentRequestRouter); mParentCandidate.SetState(Neighbor::kStateInvalid); mReceivedResponseFromParent = false; @@ -1600,7 +1672,7 @@ uint32_t Mle::Reattach(void) netif.GetPendingDataset().ApplyConfiguration(); mReattachState = kReattachPending; SetAttachState(kAttachStateStart); - delay = 1 + Random::GetUint32InRange(0, kParentRequestJitter); + delay = 1 + Random::GetUint32InRange(0, kAttachStartJitter); } else { diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 49b6aacf8..cf1da67a8 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -1520,6 +1520,18 @@ private: { kMleMessagePriority = Message::kPriorityHigh, kMleHopLimit = 255, + + // Parameters related to "periodic parent search" feature (CONFIG_ENABLE_PERIODIC_PARENT_SEARCH). + // All timer intervals are converted to milliseconds. + kParentSearchCheckInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL * 1000u), + kParentSearchBackoffInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL * 1000u), + kParentSearchJitterInterval = (15 * 1000u), + kParentSearchRssThreadhold = OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD, + + // Parameters for "attach backoff" feature (CONFIG_ENABLE_ATTACH_BACKOFF) - Intervals are in milliseconds. + kAttachBackoffMinInterval = OPENTHREAD_CONFIG_ATTACH_BACKOFF_MINIMUM_INTERVAL, + kAttachBackoffMaxInterval = OPENTHREAD_CONFIG_ATTACH_BACKOFF_MAXIMUM_INTERVAL, + kAttachBackoffJitter = OPENTHREAD_CONFIG_ATTACH_BACKOFF_JITTER_INTERVAL, }; enum ParentRequestType @@ -1528,17 +1540,6 @@ private: kParentRequestTypeRoutersAndReeds, ///< Parent Request to all routers and REEDs. }; -#if OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH - enum - { - // All timer intervals are converted to milliseconds - kParentSearchCheckInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL * 1000u), - kParentSearchBackoffInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL * 1000u), - kParentSearchJitterInterval = (15 * 1000u), - kParentSearchRssThreadhold = OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD, - }; -#endif - void GenerateNonce(const Mac::ExtAddress &aMacAddr, uint32_t aFrameCounter, uint8_t aSecurityLevel, @@ -1568,6 +1569,7 @@ private: otError HandleLeaderData(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void ProcessAnnounce(void); + uint32_t GetAttachStartDelay(void) const; otError SendParentRequest(ParentRequestType aType); otError SendChildIdRequest(void); otError SendOrphanAnnounce(void); diff --git a/src/core/thread/mle_constants.hpp b/src/core/thread/mle_constants.hpp index 9bcd5bcfb..f38a0344b 100644 --- a/src/core/thread/mle_constants.hpp +++ b/src/core/thread/mle_constants.hpp @@ -61,7 +61,7 @@ enum kUdpPort = 19788, ///< MLE UDP Port kParentRequestRouterTimeout = 750, ///< Router Parent Request timeout kParentRequestReedTimeout = 1250, ///< Router and REEDs Parent Request timeout - kParentRequestJitter = 50, ///< Maximum jitter time added to Parent Request timeout + kAttachStartJitter = 50, ///< Maximum jitter time added to start of attach. kAnnounceProcessTimeout = 250, ///< Timeout after receiving Announcement before channel/pan-id change kAnnounceTimeout = 1400, ///< Total timeout used for sending Announcement messages kMinAnnounceDelay = 80, ///< Minimum delay between Announcement messages