[mac] feature to add delay on no-ack error before tx retry (#7346)

This commit adds a new feature in `SubMac` to add random delay before
frame retransmissions. The delay is added only if the previous tx
resulted in `kErrorNoAck`. A backoff algorithm similar to one used
for CSMA is used to determine the random delay, i.e., starting with a
min backoff exponent (BE) which is incremented each time delay is
added up to a max backoff exponent. The backoff interval is derived by
selecting a random value from `[0, 2^BE]` range multiplied by
`kUnitBackoffPeriod=20` times radio symbol time (20x16=320 usec).
The `OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY`
is added to enable this behavior (which is enabled by default).
This commit also adds other configs for this feature including
minimum and maximum backoff exponents (the values are from Thread
1.3 spec recommendations).
This commit is contained in:
Abtin Keshavarzian
2022-03-08 17:58:30 -08:00
committed by GitHub
parent 9475bce07a
commit b6cc940634
4 changed files with 117 additions and 11 deletions
+34
View File
@@ -85,6 +85,40 @@
#define OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_INDIRECT 0
#endif
/**
* @def OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
*
* Define as 1 to add random backoff delay in between frame transmission retries when the previous attempt resulted in
* no-ack error.
*
*/
#ifndef OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
#define OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY 1
#endif
/**
* @def OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT
*
* Specifies the minimum backoff exponent to start with when adding random delay in between frame transmission
* retries on no-ack error. It is applicable only when `OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY`
* is enabled.
*
*/
#ifndef OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT
#define OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT 0
#endif
/**
* @def OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT
*
* Specifies the maximum backoff exponent when adding random delay in between frame transmission retries on no-ack
* error. It is applicable only when `OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY` is enabled.
*
*/
#ifndef OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT
#define OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT 5
#endif
/**
* @def OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
*
+70 -11
View File
@@ -78,6 +78,9 @@ void SubMac::Init(void)
mRxOnWhenBackoff = true;
mEnergyScanMaxRssi = kInvalidRssiValue;
mEnergyScanEndTime = Time{0};
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent;
#endif
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
mRadioFilterEnabled = false;
@@ -342,6 +345,9 @@ Error SubMac::Send(void)
case kStateCslTransmit:
#endif
case kStateTransmit:
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
case kStateDelayBeforeRetx:
#endif
case kStateEnergyScan:
ExitNow(error = kErrorInvalidState);
OT_UNREACHABLE_CODE(break);
@@ -363,8 +369,14 @@ Error SubMac::Send(void)
#endif
ProcessTransmitSecurity();
mCsmaBackoffs = 0;
mTransmitRetries = 0;
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent;
#endif
StartCsmaBackoff();
exit:
@@ -414,8 +426,7 @@ exit:
void SubMac::StartCsmaBackoff(void)
{
uint32_t backoff;
uint32_t backoffExponent = kCsmaMinBe + mTransmitRetries + mCsmaBackoffs;
uint8_t backoffExponent = kCsmaMinBe + mCsmaBackoffs;
#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
if (mTransmitFrame.mInfo.mTxInfo.mTxDelay != 0)
@@ -444,6 +455,7 @@ void SubMac::StartCsmaBackoff(void)
ExitNow();
}
#endif // !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
SetState(kStateCsmaBackoff);
VerifyOrExit(ShouldHandleCsmaBackOff(), BeginTransmit());
@@ -453,8 +465,18 @@ void SubMac::StartCsmaBackoff(void)
backoffExponent = kCsmaMaxBe;
}
backoff = Random::NonCrypto::GetUint32InRange(0, static_cast<uint32_t>(1UL << backoffExponent));
backoff *= (kUnitBackoffPeriod * OT_RADIO_SYMBOL_TIME);
StartTimerForBackoff(backoffExponent);
exit:
return;
}
void SubMac::StartTimerForBackoff(uint8_t aBackoffExponent)
{
uint32_t backoff;
backoff = Random::NonCrypto::GetUint32InRange(0, static_cast<uint32_t>(1UL << aBackoffExponent));
backoff *= (kUnitBackoffPeriod * Radio::kSymbolTime);
if (mRxOnWhenBackoff)
{
@@ -471,8 +493,12 @@ void SubMac::StartCsmaBackoff(void)
mTimer.Start(backoff / 1000UL);
#endif
exit:
return;
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
if (mState == kStateDelayBeforeRetx)
{
LogDebg("Delaying retx for %u usec (be=%d)", backoff, aBackoffExponent);
}
#endif
}
void SubMac::BeginTransmit(void)
@@ -593,6 +619,17 @@ void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aErro
{
mTransmitRetries++;
aFrame.SetIsARetransmission(true);
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
if (aError == kErrorNoAck)
{
SetState(kStateDelayBeforeRetx);
StartTimerForBackoff(mRetxDelayBackOffExponent);
mRetxDelayBackOffExponent = OT_MIN(mRetxDelayBackOffExponent + 1, kRetxDelayMaxBackoffExponent);
ExitNow();
}
#endif
StartCsmaBackoff();
ExitNow();
}
@@ -672,6 +709,9 @@ Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration)
case kStateTransmit:
#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kStateCslTransmit:
#endif
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
case kStateDelayBeforeRetx:
#endif
case kStateEnergyScan:
ExitNow(error = kErrorInvalidState);
@@ -769,6 +809,12 @@ void SubMac::HandleTimer(void)
HandleTransmitDone(mTransmitFrame, nullptr, kErrorNoAck);
break;
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
case kStateDelayBeforeRetx:
StartCsmaBackoff();
break;
#endif
case kStateEnergyScan:
SampleRssi();
break;
@@ -968,11 +1014,14 @@ const char *SubMac::StateToString(State aState)
"CsmaBackoff", // (3) kStateCsmaBackoff
"Transmit", // (4) kStateTransmit
"EnergyScan", // (5) kStateEnergyScan
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
"DelayBeforeRetx", // (6) kStateDelayBeforeRetx
#endif
#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
"CslTransmit", // (6) kStateCslTransmit
"CslTransmit", // (7) kStateCslTransmit
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
"CslSample", // (7) kStateCslSample
"CslSample", // (8) kStateCslSample
#endif
};
@@ -982,15 +1031,25 @@ const char *SubMac::StateToString(State aState)
static_assert(kStateCsmaBackoff == 3, "kStateCsmaBackoff value is not correct");
static_assert(kStateTransmit == 4, "kStateTransmit value is not correct");
static_assert(kStateEnergyScan == 5, "kStateEnergyScan value is not correct");
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
static_assert(kStateDelayBeforeRetx == 6, "kStateDelayBeforeRetx value is not correct");
#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
static_assert(kStateCslTransmit == 7, "kStateCslTransmit value is not correct");
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static_assert(kStateCslSample == 8, "kStateCslSample value is not correct");
#endif
#elif OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static_assert(kStateCslSample == 7, "kStateCslSample value is not correct");
#endif
#elif !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
static_assert(kStateCslTransmit == 6, "kStateCslTransmit value is not correct");
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static_assert(kStateCslSample == 7, "kStateCslSample value is not correct");
#endif
#else
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#elif OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static_assert(kStateCslSample == 6, "kStateCslSample value is not correct");
#endif
#endif
return kStateStrings[aState];
+12
View File
@@ -584,6 +584,11 @@ private:
static constexpr uint32_t kAckTimeout = 16; // Timeout for waiting on an ACK (in msec).
static constexpr uint32_t kCcaSampleInterval = 128; // CCA sample interval, 128 usec.
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
static constexpr uint8_t kRetxDelayMinBackoffExponent = OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT;
static constexpr uint8_t kRetxDelayMaxBackoffExponent = OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT;
#endif
#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
static constexpr uint32_t kEnergyScanRssiSampleInterval = 128; // RSSI sample interval for energy scan, 128 usec
#else
@@ -598,6 +603,9 @@ private:
kStateCsmaBackoff, // CSMA backoff before transmission.
kStateTransmit, // Radio is transmitting.
kStateEnergyScan, // Energy scan.
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
kStateDelayBeforeRetx, // Delay before retx
#endif
#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
kStateCslTransmit, // CSL transmission.
#endif
@@ -650,6 +658,7 @@ private:
void ProcessTransmitSecurity(void);
void SignalFrameCounterUsed(uint32_t aFrameCounter);
void StartCsmaBackoff(void);
void StartTimerForBackoff(uint8_t aBackoffExponent);
void BeginTransmit(void);
void SampleRssi(void);
@@ -689,6 +698,9 @@ private:
KeyMaterial mNextKey;
uint32_t mFrameCounter;
uint8_t mKeyId;
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
uint8_t mRetxDelayBackOffExponent;
#endif
#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
TimerMicro mTimer;
#else
+1
View File
@@ -81,6 +81,7 @@ class Radio : public InstanceLocator, private NonCopyable
friend class Instance;
public:
static constexpr uint32_t kSymbolTime = OT_RADIO_SYMBOL_TIME;
#if (OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT && OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT)
static constexpr uint16_t kNumChannelPages = 2;
static constexpr uint32_t kSupportedChannels =