[tcp] implement TCP Circular Send Buffer (#7867)

This commit is contained in:
Sam Kumar
2022-09-19 10:30:48 -07:00
committed by Jonathan Hui
parent 5b430bf80b
commit 19f9ba20a3
10 changed files with 479 additions and 8 deletions
+2
View File
@@ -212,6 +212,7 @@ LOCAL_SRC_FILES := \
src/core/api/srp_server_api.cpp \
src/core/api/tasklet_api.cpp \
src/core/api/tcp_api.cpp \
src/core/api/tcp_ext_api.cpp \
src/core/api/thread_api.cpp \
src/core/api/thread_ftd_api.cpp \
src/core/api/trel_api.cpp \
@@ -315,6 +316,7 @@ LOCAL_SRC_FILES := \
src/core/net/srp_client.cpp \
src/core/net/srp_server.cpp \
src/core/net/tcp6.cpp \
src/core/net/tcp6_ext.cpp \
src/core/net/udp6.cpp \
src/core/radio/radio.cpp \
src/core/radio/radio_callbacks.cpp \
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (244)
#define OPENTHREAD_API_VERSION (245)
/**
* @addtogroup api-instance
+18 -7
View File
@@ -88,10 +88,10 @@ extern "C" {
*/
typedef struct otTcpCircularSendBuffer
{
const uint8_t *mDataBuffer; ///< Pointer to data in the circular send buffer
size_t mCapacity; ///< Length of the circular send buffer
size_t mStartIndex; ///< Index of the first valid byte in the send buffer
size_t mCapacityUsed; ///< Number of bytes stored in the send buffer
uint8_t *mDataBuffer; ///< Pointer to data in the circular send buffer
size_t mCapacity; ///< Length of the circular send buffer
size_t mStartIndex; ///< Index of the first valid byte in the send buffer
size_t mCapacityUsed; ///< Number of bytes stored in the send buffer
otLinkedBuffer mSendLinks[2];
uint8_t mFirstSendLinkIndex;
@@ -107,6 +107,15 @@ typedef struct otTcpCircularSendBuffer
*/
void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, void *aDataBuffer, size_t aCapacity);
/**
* This enumeration defines flags passed to @p otTcpCircularSendBufferWrite.
*
*/
enum
{
OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME = 1 << 0,
};
/**
* Sends out data on a TCP endpoint, using the provided TCP circular send
* buffer to manage buffering.
@@ -136,15 +145,17 @@ void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, voi
* @param[in] aLength The length of the data pointed to by @p aData to copy into the TCP circular send buffer.
* @param[out] aWritten Populated with the amount of data copied into the send buffer, which might be less than
* @p aLength if the send buffer reaches capacity.
* @param[in] aFlags Flags specifying options for this operation (see enumeration above).
*
* @returns OT_ERROR_NONE Successfully copied data into the send buffer and sent it on the TCP endpoint.
* @returns OT_ERROR_FAILED Failed to send out data on the TCP endpoint.
*/
otError otTcpCircularSendBufferWrite(otTcpEndpoint * aEndpoint,
otTcpCircularSendBuffer *aSendBuffer,
void * aData,
const void * aData,
size_t aLength,
size_t * aWritten);
size_t * aWritten,
uint32_t aFlags);
/**
* Performs circular-send-buffer-specific handling in the otTcpForwardProgress
@@ -175,7 +186,7 @@ void otTcpCircularSendBufferHandleForwardProgress(otTcpCircularSendBuffer *aSend
*
* @return The amount of free space in the send buffer.
*/
size_t otTcpCircularSendBufferFreeSpace(otTcpCircularSendBuffer *aSendBuffer);
size_t otTcpCircularSendBufferGetFreeSpace(const otTcpCircularSendBuffer *aSendBuffer);
/**
* Forcibly discards all data in the circular send buffer.
+3
View File
@@ -351,6 +351,7 @@ openthread_core_files = [
"api/srp_server_api.cpp",
"api/tasklet_api.cpp",
"api/tcp_api.cpp",
"api/tcp_ext_api.cpp",
"api/thread_api.cpp",
"api/thread_ftd_api.cpp",
"api/trel_api.cpp",
@@ -583,6 +584,8 @@ openthread_core_files = [
"net/srp_server.hpp",
"net/tcp6.cpp",
"net/tcp6.hpp",
"net/tcp6_ext.cpp",
"net/tcp6_ext.hpp",
"net/udp6.cpp",
"net/udp6.hpp",
"radio/max_power_table.hpp",
+2
View File
@@ -78,6 +78,7 @@ set(COMMON_SOURCES
api/srp_server_api.cpp
api/tasklet_api.cpp
api/tcp_api.cpp
api/tcp_ext_api.cpp
api/thread_api.cpp
api/thread_ftd_api.cpp
api/trel_api.cpp
@@ -181,6 +182,7 @@ set(COMMON_SOURCES
net/srp_client.cpp
net/srp_server.cpp
net/tcp6.cpp
net/tcp6_ext.cpp
net/udp6.cpp
radio/radio.cpp
radio/radio_callbacks.cpp
+3
View File
@@ -168,6 +168,7 @@ SOURCES_COMMON = \
api/srp_server_api.cpp \
api/tasklet_api.cpp \
api/tcp_api.cpp \
api/tcp_ext_api.cpp \
api/thread_api.cpp \
api/thread_ftd_api.cpp \
api/trel_api.cpp \
@@ -271,6 +272,7 @@ SOURCES_COMMON = \
net/srp_client.cpp \
net/srp_server.cpp \
net/tcp6.cpp \
net/tcp6_ext.cpp \
net/udp6.cpp \
radio/radio.cpp \
radio/radio_callbacks.cpp \
@@ -584,6 +586,7 @@ HEADERS_COMMON = \
net/srp_client.hpp \
net/srp_server.hpp \
net/tcp6.hpp \
net/tcp6_ext.hpp \
net/udp6.hpp \
openthread-core-config.h \
radio/max_power_table.hpp \
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (c) 2022, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements extensions to the OpenThread TCP API.
*/
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_TCP_ENABLE
#include <openthread/tcp_ext.h>
#include "common/as_core_type.hpp"
#include "common/locator_getters.hpp"
#include "net/tcp6_ext.hpp"
using namespace ot;
void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, void *aDataBuffer, size_t aCapacity)
{
AsCoreType(aSendBuffer).Initialize(aDataBuffer, aCapacity);
}
otError otTcpCircularSendBufferWrite(otTcpEndpoint * aEndpoint,
otTcpCircularSendBuffer *aSendBuffer,
const void * aData,
size_t aLength,
size_t * aWritten,
uint32_t aFlags)
{
AssertPointerIsNotNull(aWritten);
return AsCoreType(aSendBuffer).Write(AsCoreType(aEndpoint), aData, aLength, *aWritten, aFlags);
}
void otTcpCircularSendBufferHandleForwardProgress(otTcpCircularSendBuffer *aSendBuffer, size_t aInSendBuffer)
{
AsCoreType(aSendBuffer).HandleForwardProgress(aInSendBuffer);
}
size_t otTcpCircularSendBufferGetFreeSpace(const otTcpCircularSendBuffer *aSendBuffer)
{
return AsCoreType(aSendBuffer).GetFreeSpace();
}
void otTcpCircularSendBufferForceDiscardAll(otTcpCircularSendBuffer *aSendBuffer)
{
AsCoreType(aSendBuffer).ForceDiscardAll();
}
otError otTcpCircularSendBufferDeinitialize(otTcpCircularSendBuffer *aSendBuffer)
{
return AsCoreType(aSendBuffer).Deinitialize();
}
#endif // OPENTHREAD_CONFIG_TCP_ENABLE
+231
View File
@@ -0,0 +1,231 @@
/*
* Copyright (c) 2022, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements TCP/IPv6 socket extensions.
*/
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_TCP_ENABLE
#include "tcp6_ext.hpp"
#include "common/code_utils.hpp"
#include "common/error.hpp"
#include "common/locator_getters.hpp"
#include "common/log.hpp"
namespace ot {
namespace Ip6 {
RegisterLogModule("TcpExt");
void TcpCircularSendBuffer::Initialize(void *aDataBuffer, size_t aCapacity)
{
mDataBuffer = static_cast<uint8_t *>(aDataBuffer);
mCapacity = aCapacity;
ForceDiscardAll();
}
Error TcpCircularSendBuffer::Write(Tcp::Endpoint &aEndpoint,
const void * aData,
size_t aLength,
size_t & aWritten,
uint32_t aFlags)
{
Error error = kErrorNone;
size_t bytesFree = GetFreeSpace();
size_t writeIndex;
uint32_t flags = 0;
size_t bytesUntilWrap;
/*
* Handle the case where we don't have enough space to accommodate all of the
* provided data.
*/
aLength = Min(aLength, bytesFree);
VerifyOrExit(aLength != 0);
/*
* This is a "simplifying" if statement the removes an edge case from the logic
* below. It guarantees that a write to an empty buffer will never wrap.
*/
if (mCapacityUsed == 0)
{
mStartIndex = 0;
}
writeIndex = GetIndex(mStartIndex, mCapacityUsed);
if ((aFlags & OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME) != 0 && aLength < bytesFree)
{
flags |= OT_TCP_SEND_MORE_TO_COME;
}
bytesUntilWrap = mCapacity - writeIndex;
if (aLength <= bytesUntilWrap)
{
memcpy(&mDataBuffer[writeIndex], aData, aLength);
if (writeIndex == 0)
{
/*
* mCapacityUsed == 0 corresponds to the case where we're writing
* to an empty buffer. mCapacityUsed != 0 && writeIndex == 0
* corresponds to the case where the buffer is not empty and this is
* writing the first bytes that wrap.
*/
uint8_t linkIndex;
if (mCapacityUsed == 0)
{
linkIndex = mFirstSendLinkIndex;
}
else
{
linkIndex = 1 - mFirstSendLinkIndex;
}
{
otLinkedBuffer &dataSendLink = mSendLinks[linkIndex];
dataSendLink.mNext = nullptr;
dataSendLink.mData = &mDataBuffer[writeIndex];
dataSendLink.mLength = aLength;
LogDebg("Appending link %u (points to index %u, length %u)", static_cast<unsigned>(linkIndex),
static_cast<unsigned>(writeIndex), static_cast<unsigned>(aLength));
error = aEndpoint.SendByReference(dataSendLink, flags);
}
}
else
{
LogDebg("Extending tail link by length %u", static_cast<unsigned>(aLength));
error = aEndpoint.SendByExtension(aLength, flags);
}
VerifyOrExit(error == kErrorNone, aLength = 0);
}
else
{
const uint8_t *dataIndexable = static_cast<const uint8_t *>(aData);
size_t bytesWrapped = aLength - bytesUntilWrap;
memcpy(&mDataBuffer[writeIndex], &dataIndexable[0], bytesUntilWrap);
memcpy(&mDataBuffer[0], &dataIndexable[bytesUntilWrap], bytesWrapped);
/*
* Because of the "simplifying" if statement at the top, we don't
* have to worry about starting from an empty buffer in this case.
*/
LogDebg("Extending tail link by length %u (wrapping)", static_cast<unsigned>(bytesUntilWrap));
error = aEndpoint.SendByExtension(bytesUntilWrap, flags | OT_TCP_SEND_MORE_TO_COME);
VerifyOrExit(error == kErrorNone, aLength = 0);
{
otLinkedBuffer &wrappedDataSendLink = mSendLinks[1 - mFirstSendLinkIndex];
wrappedDataSendLink.mNext = nullptr;
wrappedDataSendLink.mData = &mDataBuffer[0];
wrappedDataSendLink.mLength = bytesWrapped;
LogDebg("Appending link %u (wrapping)", static_cast<unsigned>(1 - mFirstSendLinkIndex));
error = aEndpoint.SendByReference(wrappedDataSendLink, flags);
VerifyOrExit(error == kErrorNone, aLength = bytesUntilWrap);
}
}
exit:
mCapacityUsed += aLength;
aWritten = aLength;
return error;
}
void TcpCircularSendBuffer::HandleForwardProgress(size_t aInSendBuffer)
{
size_t bytesRemoved;
size_t bytesUntilWrap;
OT_ASSERT(aInSendBuffer <= mCapacityUsed);
LogDebg("Forward progress: %u bytes in send buffer\n", aInSendBuffer);
bytesRemoved = mCapacityUsed - aInSendBuffer;
bytesUntilWrap = mCapacity - mStartIndex;
if (bytesRemoved < bytesUntilWrap)
{
mStartIndex += bytesRemoved;
}
else
{
mStartIndex = bytesRemoved - bytesUntilWrap;
/* The otLinkedBuffer for the pre-wrap data is now empty. */
LogDebg("Pre-wrap linked buffer now empty: switching first link index from %u to %u\n",
static_cast<unsigned>(mFirstSendLinkIndex), static_cast<unsigned>(1 - mFirstSendLinkIndex));
mFirstSendLinkIndex = 1 - mFirstSendLinkIndex;
}
mCapacityUsed = aInSendBuffer;
}
size_t TcpCircularSendBuffer::GetFreeSpace(void) const
{
return mCapacity - mCapacityUsed;
}
void TcpCircularSendBuffer::ForceDiscardAll(void)
{
mStartIndex = 0;
mCapacityUsed = 0;
mFirstSendLinkIndex = 0;
}
Error TcpCircularSendBuffer::Deinitialize(void)
{
return (mCapacityUsed != 0) ? kErrorBusy : kErrorNone;
}
size_t TcpCircularSendBuffer::GetIndex(size_t aStart, size_t aOffsetFromStart) const
{
size_t bytesUntilWrap;
size_t index;
OT_ASSERT(aStart < mCapacity);
bytesUntilWrap = mCapacity - aStart;
if (aOffsetFromStart < bytesUntilWrap)
{
index = aStart + aOffsetFromStart;
}
else
{
index = aOffsetFromStart - bytesUntilWrap;
}
return index;
}
} // namespace Ip6
} // namespace ot
#endif // OPENTHREAD_CONFIG_TCP_ENABLE
+136
View File
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2022, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for TCP/IPv6 socket extensions.
*/
#ifndef TCP6_EXT_HPP_
#define TCP6_EXT_HPP_
#include "openthread-core-config.h"
#include <openthread/tcp_ext.h>
#include "net/tcp6.hpp"
namespace ot {
namespace Ip6 {
/**
* @addtogroup core-tcp-ext
*
* @brief
* This module includes definitions for TCP/IPv6 socket extensions.
*
* @{
*
*/
/**
* This class represents a TCP circular send buffer.
*
*/
class TcpCircularSendBuffer : public otTcpCircularSendBuffer
{
public:
/**
* Initializes a TCP circular send buffer.
*
* @sa otTcpCircularSendBufferInitialize
*
* @param[in] aDataBuffer A pointer to memory to use to store data in the TCP circular send buffer.
* @param[in] aCapacity The capacity, in bytes, of the TCP circular send buffer, which must equal the size
* of the memory pointed to by @p aDataBuffer .
*/
void Initialize(void *aDataBuffer, size_t aCapacity);
/**
* Sends out data on a TCP endpoint, using this TCP circular send buffer to manage buffering.
*
* @sa otTcpCircularSendBufferWrite, particularly for guidance on how @p aEndpoint must be chosen.
*
* @param[in] aEndpoint The TCP endpoint on which to send out data.
* @param[in] aData A pointer to data to copy into the TCP circular send buffer.
* @param[in] aLength The length of the data pointed to by @p aData to copy into the TCP circular send buffer.
* @param[out] aWritten Populated with the amount of data copied into the send buffer, which might be less than
* @p aLength if the send buffer reaches capacity.
* @param[in] aFlags Flags specifying options for this operation.
*
* @retval kErrorNone on success, or the error returned by the TCP endpoint on failure.
*/
Error Write(Tcp::Endpoint &aEndpoint, const void *aData, size_t aLength, size_t &aWritten, uint32_t aFlags);
/**
* Performs circular-send-buffer-specific handling in the otTcpForwardProgress callback.
*
* @sa otTcpCircularSendBufferHandleForwardProgress
*
* @param[in] aInSendBuffer Value of @p aInSendBuffer passed to the otTcpForwardProgress() callback.
*/
void HandleForwardProgress(size_t aInSendBuffer);
/**
* Returns the amount of free space in this TCP circular send buffer.
*
* @sa otTcpCircularSendBufferFreeSpace
*
* @return The amount of free space in the send buffer.
*/
size_t GetFreeSpace(void) const;
/**
* Forcibly discards all data in this TCP circular send buffer.
*
* @sa otTcpCircularSendBufferForceDiscardAll
*
*/
void ForceDiscardAll(void);
/**
* Deinitializes this TCP circular send buffer.
*
* @sa otTcpCircularSendBufferDeinitialize
*
* @retval kErrorNone Successfully deinitialized this TCP circular send buffer.
* @retval kErrorFailed Failed to deinitialize the TCP circular send buffer.
*/
Error Deinitialize(void);
private:
size_t GetIndex(size_t aStart, size_t aOffsetFromStart) const;
};
} // namespace Ip6
DefineCoreType(otTcpCircularSendBuffer, Ip6::TcpCircularSendBuffer);
} // namespace ot
#endif // TCP6_HPP_
+1
View File
@@ -55,6 +55,7 @@ void lbuf_append(struct lbufhead* buffer, otLinkedBuffer* newentry) {
void lbuf_extend(struct lbufhead* buffer, size_t numbytes) {
buffer->tail->mLength += numbytes;
buffer->length += numbytes;
}
size_t lbuf_pop(struct lbufhead* buffer, size_t numbytes, uint32_t* ntraversed) {