mirror of
https://github.com/espressif/openthread.git
synced 2026-07-28 22:57:47 +00:00
[mesh-forwarder] allow limiting max num of frames in direct tx queue (#7908)
This commit adds a new feature in `MeshForwarder` to specify a max limit for the number of frames in tx queue marked for direct transmission. `OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE` specifies the maximum number. If set to zero then this behavior is disabled, i.e., no check is performed on tx queue length. This commit also adds a new message action log "Dropping (dir queue full)" which indicates when a message is being dropped due to the newly added check.
This commit is contained in:
@@ -483,6 +483,22 @@
|
||||
#define OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_FRAG_TAG_ENTRY_LIST_SIZE 16
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE
|
||||
*
|
||||
* Specifies the maximum number of frames in direct tx queue before new direct tx messages are dropped.
|
||||
*
|
||||
* If set to zero then the behavior is disabled, i.e., no check is performed on tx queue length.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE
|
||||
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_3)
|
||||
#define OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE 100
|
||||
#else
|
||||
#define OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
|
||||
*
|
||||
|
||||
@@ -438,6 +438,89 @@ Error MeshForwarder::RemoveAgedMessages(void)
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
|
||||
|
||||
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
|
||||
|
||||
bool MeshForwarder::IsDirectTxQueueOverMaxFrameThreshold(void) const
|
||||
{
|
||||
uint16_t frameCount = 0;
|
||||
|
||||
for (const Message &message : mSendQueue)
|
||||
{
|
||||
if (!message.IsDirectTransmission() || (&message == mSendMessage))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (message.GetType())
|
||||
{
|
||||
case Message::kTypeIp6:
|
||||
{
|
||||
// If it is an IPv6 message, we estimate the number of
|
||||
// fragment frames assuming typical header sizes and lowpan
|
||||
// compression. Since this estimate is only used for queue
|
||||
// management, we lean towards an under estimate in sense
|
||||
// that we may allow few more frames in the tx queue over
|
||||
// threshold in some rare cases.
|
||||
//
|
||||
// The constants below are derived as follows: Typical MAC
|
||||
// header (15 bytes) and MAC footer (6 bytes) leave 106
|
||||
// bytes for MAC payload. Next fragment header is 5 bytes
|
||||
// leaving 96 for next fragment payload. Lowpan compression
|
||||
// on average compresses 40 bytes IPv6 header into about 19
|
||||
// bytes leaving 87 bytes for the IPv6 payload, so the first
|
||||
// fragment can fit 87 + 40 = 127 bytes.
|
||||
|
||||
static constexpr uint16_t kFirstFragmentMaxLength = 127;
|
||||
static constexpr uint16_t kNextFragmentSize = 96;
|
||||
|
||||
uint16_t length = message.GetLength();
|
||||
|
||||
frameCount++;
|
||||
|
||||
if (length > kFirstFragmentMaxLength)
|
||||
{
|
||||
frameCount += (length - kFirstFragmentMaxLength) / kNextFragmentSize;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Message::kType6lowpan:
|
||||
case Message::kTypeMacEmptyData:
|
||||
frameCount++;
|
||||
break;
|
||||
|
||||
case Message::kTypeSupervision:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (frameCount > OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE);
|
||||
}
|
||||
|
||||
void MeshForwarder::ApplyDirectTxQueueLimit(Message &aMessage)
|
||||
{
|
||||
VerifyOrExit(aMessage.IsDirectTransmission());
|
||||
VerifyOrExit(IsDirectTxQueueOverMaxFrameThreshold());
|
||||
|
||||
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
|
||||
if (RemoveAgedMessages() == kErrorNone)
|
||||
{
|
||||
VerifyOrExit(IsDirectTxQueueOverMaxFrameThreshold());
|
||||
}
|
||||
#endif
|
||||
|
||||
LogMessage(kMessageFullQueueDrop, aMessage);
|
||||
aMessage.ClearDirectTransmission();
|
||||
RemoveMessageIfNoPendingTx(aMessage);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#endif // (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
|
||||
|
||||
void MeshForwarder::ScheduleTransmissionTask(Tasklet &aTasklet)
|
||||
{
|
||||
aTasklet.Get<MeshForwarder>().ScheduleTransmissionTask();
|
||||
@@ -1809,6 +1892,9 @@ const char *MeshForwarder::MessageActionToString(MessageAction aAction, Error aE
|
||||
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
|
||||
"Marked ECN", // (6) kMessageMarkEcn
|
||||
"Dropping (queue mgmt)", // (7) kMessageQueueMgmtDrop
|
||||
#endif
|
||||
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
|
||||
"Dropping (dir queue full)", // (8) kMessageFullQueueDrop
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -1823,6 +1909,13 @@ const char *MeshForwarder::MessageActionToString(MessageAction aAction, Error aE
|
||||
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
|
||||
static_assert(kMessageMarkEcn == 6, "kMessageMarkEcn is incorrect");
|
||||
static_assert(kMessageQueueMgmtDrop == 7, "kMessageQueueMgmtDrop is incorrect");
|
||||
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
|
||||
static_assert(kMessageFullQueueDrop == 8, "kMessageFullQueueDrop is incorrect");
|
||||
#endif
|
||||
#else
|
||||
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
|
||||
static_assert(kMessageFullQueueDrop == 6, "kMessageFullQueueDrop is incorrect");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if ((aAction == kMessageTransmit) && (aError != kErrorNone))
|
||||
@@ -1932,6 +2025,9 @@ void MeshForwarder::LogMessage(MessageAction aAction,
|
||||
case kMessageEvict:
|
||||
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
|
||||
case kMessageQueueMgmtDrop:
|
||||
#endif
|
||||
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
|
||||
case kMessageFullQueueDrop:
|
||||
#endif
|
||||
logLevel = kLogLevelNote;
|
||||
break;
|
||||
|
||||
@@ -352,6 +352,9 @@ private:
|
||||
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
|
||||
kMessageMarkEcn, // Indicates that ECN is marked on an outbound message by delay-aware queue management.
|
||||
kMessageQueueMgmtDrop, // Indicates that an outbound message is dropped by delay-aware queue management.
|
||||
#endif
|
||||
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
|
||||
kMessageFullQueueDrop, // Indicates message drop due to reaching max allowed frames in direct tx queue.
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -460,6 +463,10 @@ private:
|
||||
#if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE
|
||||
Error UpdateEcnOrDrop(Message &aMessage, bool aPreparingToSend = true);
|
||||
Error RemoveAgedMessages(void);
|
||||
#endif
|
||||
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
|
||||
bool IsDirectTxQueueOverMaxFrameThreshold(void) const;
|
||||
void ApplyDirectTxQueueLimit(Message &aMessage);
|
||||
#endif
|
||||
void SendMesh(Message &aMessage, Mac::TxFrame &aFrame);
|
||||
void SendDestinationUnreachable(uint16_t aMeshSource, const Ip6::Headers &aIp6Headers);
|
||||
|
||||
@@ -134,6 +134,10 @@ Error MeshForwarder::SendMessage(Message &aMessage)
|
||||
break;
|
||||
}
|
||||
|
||||
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
|
||||
ApplyDirectTxQueueLimit(aMessage);
|
||||
#endif
|
||||
|
||||
mScheduleTransmissionTask.Post();
|
||||
|
||||
return error;
|
||||
|
||||
@@ -47,6 +47,10 @@ Error MeshForwarder::SendMessage(Message &aMessage)
|
||||
mSendQueue.Enqueue(aMessage);
|
||||
mScheduleTransmissionTask.Post();
|
||||
|
||||
#if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0)
|
||||
ApplyDirectTxQueueLimit(aMessage);
|
||||
#endif
|
||||
|
||||
return kErrorNone;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user