[mle] Periodic Parent Search feature (#2330)

This commit implements a "Periodic Parent Search" feature in MLE.
This feature is disabled by default and can be enabled through
`OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH` configuration
option.

When enabled, an end-device/child (while staying attached) will
periodically search for a possible better parent and will switch
if it finds a better one.

The child will periodically check the average RSS value for the
current parent, and only if it is below a specific threshold, a
parent search is performed. Since the parent search process can be
power consuming (child needs to stays in RX mode to collect parent
response) and to limit its impact on battery-powered devices, after a
parent search is triggered, the child will not trigger another one
before a specified longer backoff interval. The check and backoff
intervals along with the RSS threshold used to trigger the parent
search can be set from a set of configuration options.
This commit is contained in:
Abtin Keshavarzian
2017-12-05 17:17:02 +00:00
committed by Jonathan Hui
parent 0097c403bc
commit fc170d85fd
3 changed files with 212 additions and 0 deletions
+58
View File
@@ -970,6 +970,64 @@
#define OPENTHREAD_CONFIG_INFORM_PREVIOUS_PARENT_ON_REATTACH 0
#endif
/**
* @def OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH
*
* Define as 1 to enable periodic parent search feature.
*
* When this feature is enabled an end-device/child (while staying attached) will periodically search for a possible
* better parent and will switch parent if a better one is found.
*
* The child will periodically check the average RSS value for the current parent, and only if it is below a specific
* threshold, a parent search is performed. The `OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL` specifies the the
* check interval (in seconds) and `OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD` gives the RSS threshold.
*
* Since the parent search process can be power consuming (child needs to stays in RX mode to collect parent response)
* and to limit its impact on battery-powered devices, after a parent search is triggered, the child will not trigger
* another one before a specified backoff interval specified by `OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL`
*
*/
#ifndef OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH
#define OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH 0
#endif
/**
* @def OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL
*
* Specifies the interval in seconds for a child to check the trigger condition to perform a parent search.
*
* Applicable only if periodic parent search feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH`).
*
*/
#ifndef OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL
#define OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL (9 * 60)
#endif
/**
* @def OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL
*
* Specifies the backoff interval in seconds for a child to not perform a parent search after triggering one.
*
* Applicable only if periodic parent search feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH`).
*
*
*/
#ifndef OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL
#define OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL (10* 60 * 60)
#endif
/**
* @def OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD
*
* Specifies the RSS threshold used to trigger a parent search.
*
* Applicable only if periodic parent search feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH`).
*
*/
#ifndef OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD
#define OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD -65
#endif
/**
* @def OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
*
+128
View File
@@ -93,6 +93,13 @@ Mle::Mle(Instance &aInstance) :
mEnableEui64Filtering(false),
#if OPENTHREAD_CONFIG_INFORM_PREVIOUS_PARENT_ON_REATTACH
mPreviousParentRloc(Mac::kShortAddrInvalid),
#endif
#if OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH
mParentSearchIsInBackoff(false),
mParentSearchBackoffWasCanceled(false),
mParentSearchRecentlyDetached(false),
mParentSearchBackoffCancelTime(0),
mParentSearchTimer(aInstance, &Mle::HandleParentSearchTimer, this),
#endif
mAnnounceChannel(OT_RADIO_CHANNEL_MIN),
mPreviousChannel(0),
@@ -194,6 +201,10 @@ Mle::Mle(Instance &aInstance) :
mNetifCallback.Set(&Mle::HandleNetifStateChanged, this);
GetNetif().RegisterCallback(mNetifCallback);
#if OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH
StartParentSearchTimer();
#endif
}
otError Mle::Enable(void)
@@ -501,6 +512,10 @@ otError Mle::BecomeDetached(void)
netif.GetPendingDataset().HandleDetach();
}
#if OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH
mParentSearchRecentlyDetached = true;
#endif
SetStateDetached();
mParent.SetState(Neighbor::kStateInvalid);
SetRloc16(Mac::kShortAddrInvalid);
@@ -636,6 +651,10 @@ otError Mle::SetStateChild(uint16_t aRloc16)
netif.GetAnnounceBeginServer().SendAnnounce(1 << mPreviousChannel);
}
#if OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH
UpdateParentSearchState();
#endif
#if OPENTHREAD_CONFIG_INFORM_PREVIOUS_PARENT_ON_REATTACH
InformPreviousParent();
mPreviousParentRloc = mParent.GetRloc16();
@@ -3446,6 +3465,115 @@ exit:
}
#endif // OPENTHREAD_CONFIG_INFORM_PREVIOUS_PARENT_ON_REATTACH
#if OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH
void Mle::HandleParentSearchTimer(Timer &aTimer)
{
aTimer.GetOwner<Mle>().HandleParentSearchTimer();
}
void Mle::HandleParentSearchTimer(void)
{
int8_t parentRss;
otLogInfoMle(GetInstance(), "PeriodicParentSearch: %s interval passed",
mParentSearchIsInBackoff ? "Backoff" : "Check");
if (mParentSearchBackoffWasCanceled)
{
// Backoff can be canceled if the device switches to a new parent.
// from `UpdateParentSearchState()`. We want to limit this to happen
// only once within a backoff interval.
if (TimerMilli::GetNow() - mParentSearchBackoffCancelTime >= kParentSearchBackoffInterval)
{
mParentSearchBackoffWasCanceled = false;
otLogInfoMle(GetInstance(), "PeriodicParentSearch: Backoff cancellation is allowed on parent switch");
}
}
mParentSearchIsInBackoff = false;
VerifyOrExit(mRole == OT_DEVICE_ROLE_CHILD);
parentRss = GetParent()->GetLinkInfo().GetAverageRss();
otLogInfoMle(GetInstance(), "PeriodicParentSearch: Parent RSS %d", parentRss);
VerifyOrExit(parentRss != OT_RADIO_RSSI_INVALID);
if (parentRss < kParentSearchRssThreadhold)
{
otLogInfoMle(GetInstance(), "PeriodicParentSearch: Parent RSS less than %d, searching for new parents",
kParentSearchRssThreadhold);
mParentSearchIsInBackoff = true;
BecomeChild(kAttachAny);
}
exit:
StartParentSearchTimer();
}
void Mle::StartParentSearchTimer(void)
{
uint32_t interval;
interval = (otPlatRandomGet() % kParentSearchJitterInterval);
if (mParentSearchIsInBackoff)
{
interval += kParentSearchBackoffInterval;
}
else
{
interval += kParentSearchCheckInterval;
}
mParentSearchTimer.Start(interval);
otLogInfoMle(GetInstance(), "PeriodicParentSearch: (Re)starting timer for %s interval",
mParentSearchIsInBackoff ? "backoff" : "check");
}
void Mle::UpdateParentSearchState(void)
{
#if OPENTHREAD_CONFIG_INFORM_PREVIOUS_PARENT_ON_REATTACH
// If we are in middle of backoff and backoff was not canceled
// recently and we recently detached from a previous parent,
// then we check if the new parent is different from the previous
// one, and if so, we cancel the backoff mode and also remember
// the backoff cancel time. This way the canceling of backoff
// is allowed only once within a backoff window.
//
// The reason behind the canceling of the backoff is to handle
// the scenario where a previous parent is not available for a
// short duration (e.g., it is going through a software update)
// and the child switches to a less desirable parent. With this
// model the child will check for other parents sooner and have
// the chance to switch back to the original (and possibly
// preferred) parent more quickly.
if (mParentSearchIsInBackoff && !mParentSearchBackoffWasCanceled && mParentSearchRecentlyDetached)
{
if ((mPreviousParentRloc != Mac::kShortAddrInvalid) && (mPreviousParentRloc != mParent.GetRloc16()))
{
mParentSearchIsInBackoff = false;
mParentSearchBackoffWasCanceled = true;
mParentSearchBackoffCancelTime = TimerMilli::GetNow();
otLogInfoMle(GetInstance(), "PeriodicParentSearch: Canceling backoff on switching to a new parent");
}
}
#endif // OPENTHREAD_CONFIG_INFORM_PREVIOUS_PARENT_ON_REATTACH
mParentSearchRecentlyDetached = false;
if (!mParentSearchIsInBackoff)
{
StartParentSearchTimer();
}
}
#endif // OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH
void Mle::LogMleMessage(const char *aLogString, const Ip6::Address &aAddress) const
{
#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+26
View File
@@ -1405,6 +1405,17 @@ private:
kMleHopLimit = 255,
};
#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,
uint8_t *aNonce);
@@ -1452,6 +1463,13 @@ private:
otError InformPreviousParent(void);
#endif
#if OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH
static void HandleParentSearchTimer(Timer &aTimer);
void HandleParentSearchTimer(void);
void StartParentSearchTimer(void);
void UpdateParentSearchState(void);
#endif
MessageQueue mDelayedResponses;
struct
@@ -1492,6 +1510,14 @@ private:
uint16_t mPreviousParentRloc;
#endif
#if OPENTHREAD_CONFIG_ENABLE_PERIODIC_PARENT_SEARCH
bool mParentSearchIsInBackoff : 1;
bool mParentSearchBackoffWasCanceled : 1;
bool mParentSearchRecentlyDetached : 1;
uint32_t mParentSearchBackoffCancelTime;
TimerMilli mParentSearchTimer;
#endif
uint8_t mAnnounceChannel;
uint8_t mPreviousChannel;
uint16_t mPreviousPanId;