mirror of
https://github.com/espressif/openthread.git
synced 2026-08-03 09:27:47 +00:00
[channel-manager] handle simultaneous channel change requests (#2543)
This commit adds new logic in `ChannelManager` to help handle situations where multiple devices within network request channel change around the same time. This simplifies how the channel change can be triggered by users allowing them to request a channel change on all routers/devices simultaneously (this would help with cases where the Thread network contains multiple partitions). In particular, this commit adds a jitter delay to start processing of a channel change request (random delay before a Pending Dataset is prepared and sent to leader). Also this commit changes how the Pending Dataset is prepared and sent. The code now checks if there is a valid Pending Dataset and if it is changing the channel to same one as the current channel change request, then it skips updating the Pending Dataset.
This commit is contained in:
committed by
Jonathan Hui
parent
6159e3a646
commit
9473c04b86
@@ -77,7 +77,8 @@ otError ChannelManager::RequestChannelChange(uint8_t aChannel)
|
||||
mState = kStateChangeRequested;
|
||||
mChannel = aChannel;
|
||||
mActiveTimestamp = 0;
|
||||
PreparePendingDataset();
|
||||
|
||||
mTimer.Start((otPlatRandomGet() % kRequestStartJitterInterval) + 1);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
@@ -98,18 +99,44 @@ void ChannelManager::PreparePendingDataset(void)
|
||||
{
|
||||
ThreadNetif &netif = GetInstance().GetThreadNetif();
|
||||
uint64_t pendingTimestamp = 0;
|
||||
uint64_t pendingActiveTimestamp = 0;
|
||||
uint32_t delayInMs = TimerMilli::SecToMsec(static_cast<uint32_t>(mDelay));
|
||||
otOperationalDataset dataset;
|
||||
uint32_t delayInMs;
|
||||
otError error;
|
||||
|
||||
VerifyOrExit(mState == kStateChangeRequested);
|
||||
|
||||
VerifyOrExit(mChannel != GetInstance().Get<Mac::Mac>().GetChannel());
|
||||
|
||||
if ((mSupportedChannels & (1U << mChannel)) == 0)
|
||||
{
|
||||
otLogInfoUtil(GetInstance(), "ChannelManager: Request rejected! Channel %d not in supported mask 0x%x",
|
||||
mChannel, mSupportedChannels);
|
||||
mState = kStateIdle;
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
if (netif.GetPendingDataset().Get(dataset) == OT_ERROR_NONE)
|
||||
{
|
||||
if (dataset.mIsPendingTimestampSet)
|
||||
{
|
||||
pendingTimestamp = dataset.mPendingTimestamp;
|
||||
}
|
||||
|
||||
// We check whether the Pending Dataset is changing the
|
||||
// channel to same one as the current request (i.e., channel
|
||||
// should match and delay should be less than the requested
|
||||
// delay).
|
||||
|
||||
if (dataset.mIsChannelSet && (mChannel == dataset.mChannel) &&
|
||||
dataset.mIsDelaySet && (dataset.mDelay <= delayInMs) &&
|
||||
dataset.mIsActiveTimestampSet)
|
||||
{
|
||||
// We save the active timestamp to later check and ensure it
|
||||
// is ahead of current ActiveDataset timestamp.
|
||||
|
||||
pendingActiveTimestamp = dataset.mActiveTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
pendingTimestamp += (otPlatRandomGet() % kMaxTimestampIncrease) + 1;
|
||||
@@ -137,6 +164,23 @@ void ChannelManager::PreparePendingDataset(void)
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
// `pendingActiveTimestamp` will be non-zero if the Pending
|
||||
// Dataset is valid and is performing the same channel change.
|
||||
// We check to ensure its timestamp is indeed ahead of current
|
||||
// Active Dataset's timestamp, and if so, we skip updating
|
||||
// the Pending Dataset.
|
||||
|
||||
if (pendingActiveTimestamp != 0)
|
||||
{
|
||||
if (dataset.mActiveTimestamp < pendingActiveTimestamp)
|
||||
{
|
||||
otLogInfoUtil(GetInstance(), "ChannelManager: Pending Dataset is valid for change channel to %d", mChannel);
|
||||
mState = kStateSentMgmtPendingDataset;
|
||||
mTimer.Start(delayInMs + kChangeCheckWaitInterval);
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
|
||||
// A non-zero `mActiveTimestamp` indicates that this is not the first
|
||||
// attempt to update the Dataset for the ongoing requested channel
|
||||
// change. In that case, if the Timestamp in current Active Dataset is
|
||||
@@ -170,7 +214,6 @@ void ChannelManager::PreparePendingDataset(void)
|
||||
dataset.mPendingTimestamp = pendingTimestamp;
|
||||
dataset.mIsPendingTimestampSet = true;
|
||||
|
||||
delayInMs = TimerMilli::SecToMsec(static_cast<uint32_t>(mDelay));
|
||||
dataset.mDelay = delayInMs;
|
||||
dataset.mIsDelaySet = true;
|
||||
|
||||
@@ -187,6 +230,8 @@ void ChannelManager::PreparePendingDataset(void)
|
||||
{
|
||||
otLogInfoUtil(GetInstance(), "ChannelManager: %s error in dataset update (channel change %d), retry in %d sec",
|
||||
otThreadErrorToString(error), mChannel, TimerMilli::MsecToSec(kPendingDatasetTxRetryInterval));
|
||||
|
||||
mTimer.Start(kPendingDatasetTxRetryInterval);
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -206,7 +251,8 @@ void ChannelManager::HandleTimer(void)
|
||||
break;
|
||||
|
||||
case kStateSentMgmtPendingDataset:
|
||||
otLogInfoUtil(GetInstance(), "ChannelManager: Timed out waiting for channel change to %d", mChannel);
|
||||
otLogInfoUtil(GetInstance(), "ChannelManager: Timed out waiting for change to %d, trying again.", mChannel);
|
||||
mState = kStateChangeRequested;
|
||||
|
||||
// fall through
|
||||
|
||||
|
||||
@@ -150,11 +150,11 @@ public:
|
||||
private:
|
||||
enum
|
||||
{
|
||||
kDefaultSupprotedChannelMask = OT_RADIO_SUPPORTED_CHANNELS,
|
||||
kMaxTimestampIncrease = 128,
|
||||
|
||||
kDefaultSupprotedChannelMask = OT_RADIO_SUPPORTED_CHANNELS,
|
||||
kMaxTimestampIncrease = 128,
|
||||
kPendingDatasetTxRetryInterval = 20000, // in ms
|
||||
kChangeCheckWaitInterval = 30000, // in ms
|
||||
kChangeCheckWaitInterval = 30000, // in ms
|
||||
kRequestStartJitterInterval = 10000, // in ms
|
||||
};
|
||||
|
||||
enum State
|
||||
|
||||
Reference in New Issue
Block a user