This is an automated email from the ASF dual-hosted git repository.
driazati pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new b18f6425f9 [Hexagon] [runtime] Manage RPC and runtime buffers
separately (#13028)
b18f6425f9 is described below
commit b18f6425f951f5e9e039f497898be7d3227ddc93
Author: Janet Schneider <[email protected]>
AuthorDate: Wed Oct 12 14:57:28 2022 -0400
[Hexagon] [runtime] Manage RPC and runtime buffers separately (#13028)
Creates HexagonPageAllocator to manage allocations for the RPC server
Adds new Device APIs for RPC buffer management
Allocations are tracked by two separate buffer managers:
- rpc_hexbuffs is used exclusively for RPC buffers
- runtime_hexbuffs is used exclusively for runtime buffers
This will fix the throw on shutdown in the simulator.
---
src/runtime/hexagon/hexagon_buffer_manager.h | 13 ++++++
src/runtime/hexagon/hexagon_device_api.cc | 48 ++++++++++++++++++----
src/runtime/hexagon/hexagon_device_api.h | 36 ++++++++++------
src/runtime/hexagon/rpc/hexagon/rpc_server.cc | 32 ++++++++++++++-
.../hexagon/hexagon_device_api_tests.cc | 27 ++++++++----
5 files changed, 127 insertions(+), 29 deletions(-)
diff --git a/src/runtime/hexagon/hexagon_buffer_manager.h
b/src/runtime/hexagon/hexagon_buffer_manager.h
index a698b0ecb1..eecf96a6db 100644
--- a/src/runtime/hexagon/hexagon_buffer_manager.h
+++ b/src/runtime/hexagon/hexagon_buffer_manager.h
@@ -25,6 +25,7 @@
#include <memory>
#include <unordered_map>
#include <utility>
+#include <vector>
#include "hexagon_buffer.h"
@@ -85,6 +86,18 @@ class HexagonBufferManager {
return hexagon_buffer_map_.empty();
}
+ //! \brief Returns a vector of currently allocated pointers, owned by the
manager.
+ // Note - this should only be used by the device API to keep track of what
+ // was in the manager when HexagonDeviceAPI::ReleaseResources is called.
+ std::vector<void*> current_allocations() {
+ std::vector<void*> allocated;
+ std::lock_guard<std::mutex> lock(map_mutex_);
+ for (const auto& [data_ptr, buffer] : hexagon_buffer_map_) {
+ allocated.push_back(data_ptr);
+ }
+ return allocated;
+ }
+
private:
//! \brief Contains the HexagonBuffer objects managed by this class.
std::unordered_map<void*, std::unique_ptr<HexagonBuffer>>
hexagon_buffer_map_;
diff --git a/src/runtime/hexagon/hexagon_device_api.cc
b/src/runtime/hexagon/hexagon_device_api.cc
index 7c251721b7..3574ab5018 100644
--- a/src/runtime/hexagon/hexagon_device_api.cc
+++ b/src/runtime/hexagon/hexagon_device_api.cc
@@ -90,18 +90,23 @@ void* HexagonDeviceAPI::AllocDataSpace(Device dev, int
ndim, const int64_t* shap
const size_t typesize = (dtype.bits / 8) * dtype.lanes;
+ CHECK(runtime_hexbuffs) << "Attempted to allocate Hexagon data with "
+ << "HexagonDeviceAPI::AllocDataSpace before
initializing resources. "
+ << "Please call HexagonDeviceAPI::AcquireResources";
+
if (ndim == 0) {
// Allocate storage for a single scalar value.
- return mgr->AllocateHexagonBuffer(typesize, kHexagonAllocAlignment,
mem_scope);
+ return runtime_hexbuffs->AllocateHexagonBuffer(typesize,
kHexagonAllocAlignment, mem_scope);
} else if (ndim == 1) {
// Allocate a single, contiguous memory region.
size_t nbytes = shape[0] * typesize;
- return mgr->AllocateHexagonBuffer(nbytes, kHexagonAllocAlignment,
mem_scope);
+ return runtime_hexbuffs->AllocateHexagonBuffer(nbytes,
kHexagonAllocAlignment, mem_scope);
} else if (ndim == 2) {
// Allocate the region(s) needed for Hexagon's indirect-tensor format.
size_t nallocs = shape[0];
size_t nbytes = shape[1] * typesize;
- return mgr->AllocateHexagonBuffer(nallocs, nbytes, kHexagonAllocAlignment,
mem_scope);
+ return runtime_hexbuffs->AllocateHexagonBuffer(nallocs, nbytes,
kHexagonAllocAlignment,
+ mem_scope);
} else {
return nullptr; // unreachable
}
@@ -115,13 +120,34 @@ void* HexagonDeviceAPI::AllocDataSpace(Device dev, size_t
nbytes, size_t alignme
if (alignment < kHexagonAllocAlignment) {
alignment = kHexagonAllocAlignment;
}
- return mgr->AllocateHexagonBuffer(nbytes, alignment, String("global"));
+ CHECK(runtime_hexbuffs) << "Attempted to allocate Hexagon data with "
+ << "HexagonDeviceAPI::AllocDataSpace before
initializing resources. "
+ << "Please call HexagonDeviceAPI::AcquireResources";
+ return runtime_hexbuffs->AllocateHexagonBuffer(nbytes, alignment,
String("global"));
}
void HexagonDeviceAPI::FreeDataSpace(Device dev, void* ptr) {
CHECK(ptr) << "buffer pointer is null";
CHECK(IsValidDevice(dev)) << "dev.device_type: " << dev.device_type;
- mgr->FreeHexagonBuffer(ptr);
+ if (runtime_hexbuffs) {
+ runtime_hexbuffs->FreeHexagonBuffer(ptr);
+ } else {
+ auto it = std::find(released_runtime_buffers.begin(),
released_runtime_buffers.end(), ptr);
+ CHECK(it != released_runtime_buffers.end()) << "Attempted to free Hexagon
data with "
+ <<
"HexagonDeviceAPI::FreeDataSpace that was not "
+ << "allocated during the
session.";
+ }
+}
+
+void* HexagonDeviceAPI::AllocRpcBuffer(size_t nbytes, size_t alignment) {
+ CHECK(nbytes) << "number of bytes is zero";
+ CHECK(alignment) << "alignment is zero";
+ return rpc_hexbuffs.AllocateHexagonBuffer(nbytes, alignment,
String("global"));
+}
+
+void HexagonDeviceAPI::FreeRpcBuffer(void* ptr) {
+ CHECK(ptr) << "buffer pointer is null";
+ rpc_hexbuffs.FreeHexagonBuffer(ptr);
}
// WorkSpace: runtime allocations for Hexagon
@@ -137,7 +163,10 @@ void* HexagonDeviceAPI::AllocWorkspace(Device dev, size_t
size, DLDataType type_
void HexagonDeviceAPI::FreeWorkspace(Device dev, void* data) {
CHECK(IsValidDevice(dev)) << "dev.device_type: " << dev.device_type;
- CHECK(mgr->count(data) != 0)
+ CHECK(runtime_hexbuffs) << "Attempted to free Hexagon workspace with "
+ << "HexagonDeviceAPI::FreeWorkspace outside of a
session. "
+ << "Please call HexagonDeviceAPI::AcquireResources";
+ CHECK(runtime_hexbuffs->count(data) != 0)
<< "Attempt made to free unknown or already freed workspace allocation";
dmlc::ThreadLocalStore<HexagonWorkspacePool>::Get()->FreeWorkspace(dev,
data);
}
@@ -160,8 +189,13 @@ void HexagonDeviceAPI::CopyDataFromTo(DLTensor* from,
DLTensor* to, TVMStreamHan
CHECK_EQ(from->byte_offset, 0);
CHECK_EQ(to->byte_offset, 0);
CHECK_EQ(GetDataSize(*from), GetDataSize(*to));
+ CHECK(runtime_hexbuffs) << "Attempted to copy Hexagon data with "
+ << "HexagonDeviceAPI::CopyDataFromTo before
initializing resources. "
+ << "Please call HexagonDeviceAPI::AcquireResources";
- auto lookup_hexagon_buffer = [this](void* ptr) -> HexagonBuffer* { return
mgr->find(ptr); };
+ auto lookup_hexagon_buffer = [this](void* ptr) -> HexagonBuffer* {
+ return runtime_hexbuffs->find(ptr);
+ };
HexagonBuffer* hex_from_buf = lookup_hexagon_buffer(from->data);
HexagonBuffer* hex_to_buf = lookup_hexagon_buffer(to->data);
diff --git a/src/runtime/hexagon/hexagon_device_api.h
b/src/runtime/hexagon/hexagon_device_api.h
index 1c802f3530..8d2795e7a0 100644
--- a/src/runtime/hexagon/hexagon_device_api.h
+++ b/src/runtime/hexagon/hexagon_device_api.h
@@ -48,7 +48,7 @@ class HexagonDeviceAPI final : public DeviceAPI {
static HexagonDeviceAPI* Global();
//! \brief Constructor
- HexagonDeviceAPI() { mgr = &hexbuffs; }
+ HexagonDeviceAPI() {}
//! \brief Destructor
~HexagonDeviceAPI() {}
@@ -60,7 +60,7 @@ class HexagonDeviceAPI final : public DeviceAPI {
CHECK_EQ(runtime_hexbuffs, nullptr);
runtime_hexbuffs = std::make_unique<HexagonBufferManager>();
- mgr = runtime_hexbuffs.get();
+ released_runtime_buffers.clear();
CHECK_EQ(runtime_threads, nullptr);
runtime_threads = std::make_unique<HexagonThreadManager>(threads,
stack_size, pipe_size);
@@ -78,10 +78,10 @@ class HexagonDeviceAPI final : public DeviceAPI {
runtime_threads.reset();
CHECK(runtime_hexbuffs) << "runtime_hexbuffs was not created in
AcquireResources";
- if (runtime_hexbuffs && !runtime_hexbuffs->empty()) {
- LOG(INFO) << "runtime_hexbuffs was not empty in ReleaseResources";
+ if (!runtime_hexbuffs->empty()) {
+ DLOG(INFO) << "runtime_hexbuffs was not empty in ReleaseResources";
+ released_runtime_buffers = runtime_hexbuffs->current_allocations();
}
- mgr = &hexbuffs;
runtime_hexbuffs.reset();
CHECK(runtime_vtcm) << "runtime_vtcm was not created in AcquireResources";
@@ -106,6 +106,12 @@ class HexagonDeviceAPI final : public DeviceAPI {
//! \brief Free the allocated HexagonBuffer.
void FreeDataSpace(Device dev, void* ptr) final;
+ //! \brief Hexagon-only interface to allocate buffers used for the RPC server
+ void* AllocRpcBuffer(size_t nbytes, size_t alignment);
+
+ //! \brief Hexagon-only interface to free buffers used for the RPC server
+ void FreeRpcBuffer(void* ptr);
+
/*! \brief Request a dynamically allocated HexagonBuffer from a workspace
pool.
* \returns The underlying allocation pointer.
*/
@@ -190,15 +196,21 @@ class HexagonDeviceAPI final : public DeviceAPI {
(DLDeviceType(dev.device_type) == kDLCPU);
}
- //! \brief Manages underlying HexagonBuffer allocations
- // runtime_hexbuffs is used for runtime allocations. It is created
- // with a call to AcquireResources, and destroyed on ReleaseResources.
- // hexbuffs is used for all allocations outside of the session lifetime.
- HexagonBufferManager hexbuffs;
+ //! \brief Manages RPC HexagonBuffer allocations
+ // rpc_hexbuffs is used only in Alloc/FreeRpcBuffer. It is static because
it lives for the
+ // lifetime of the static Device API.
+ HexagonBufferManager rpc_hexbuffs;
+
+ //! \brief Manages runtime HexagonBuffer allocations
+ // runtime_hexbuffs is used for runtime allocations, separate from
rpc_hexbuffs. It is created
+ // with a call to AcquireResources, and destroyed on ReleaseResources. The
buffers in this
+ // manager are scoped to the lifetime of a user application session.
std::unique_ptr<HexagonBufferManager> runtime_hexbuffs;
- //! \brief Current buffer manager
- HexagonBufferManager* mgr;
+ //! \brief Keeps a list of released runtime HexagonBuffer allocations
+ // ReleaseResources can be called when there are still buffers in
runtime_hexbuffs. This list
+ // stores the buffers that were released.
+ std::vector<void*> released_runtime_buffers;
//! \brief Thread manager
std::unique_ptr<HexagonThreadManager> runtime_threads;
diff --git a/src/runtime/hexagon/rpc/hexagon/rpc_server.cc
b/src/runtime/hexagon/rpc/hexagon/rpc_server.cc
index 22a54043cd..29c3a1bdfe 100644
--- a/src/runtime/hexagon/rpc/hexagon/rpc_server.cc
+++ b/src/runtime/hexagon/rpc/hexagon/rpc_server.cc
@@ -38,6 +38,7 @@ extern "C" {
#include "../../../library_module.h"
#include "../../../minrpc/minrpc_server.h"
#include "../../hexagon/hexagon_common.h"
+#include "../../hexagon/hexagon_device_api.h"
#include "hexagon_rpc.h"
namespace tvm {
@@ -145,6 +146,35 @@ class HexagonIOHandler {
uint32_t write_buffer_available_length_;
};
+// Internal allocator that redirects alloc to TVM's C API.
+template <typename TIOHandler>
+class HexagonPageAllocator {
+ public:
+ using ArenaPageHeader = tvm::support::ArenaPageHeader;
+
+ explicit HexagonPageAllocator(TIOHandler* io) : io_(io) {}
+
+ ArenaPageHeader* allocate(size_t min_size) {
+ size_t npages = ((min_size + kPageSize - 1) / kPageSize);
+ void* data;
+
+ data = HexagonDeviceAPI::Global()->AllocRpcBuffer(npages * kPageSize,
kPageAlign);
+
+ ArenaPageHeader* header = static_cast<ArenaPageHeader*>(data);
+ header->size = npages * kPageSize;
+ header->offset = sizeof(ArenaPageHeader);
+ return header;
+ }
+
+ void deallocate(ArenaPageHeader* page) {
HexagonDeviceAPI::Global()->FreeRpcBuffer(page); }
+
+ static const constexpr int kPageSize = 2 << 10;
+ static const constexpr int kPageAlign = 8;
+
+ private:
+ TIOHandler* io_;
+};
+
class HexagonRPCServer {
public:
explicit HexagonRPCServer(uint8_t* receive_buffer, size_t
receive_buffer_size_bytes)
@@ -185,7 +215,7 @@ class HexagonRPCServer {
private:
HexagonIOHandler io_;
- MinRPCServer<HexagonIOHandler> rpc_server_;
+ MinRPCServer<HexagonIOHandler, HexagonPageAllocator> rpc_server_;
};
} // namespace hexagon
diff --git a/tests/cpp-runtime/hexagon/hexagon_device_api_tests.cc
b/tests/cpp-runtime/hexagon/hexagon_device_api_tests.cc
index 2139aa78f7..e262a16ada 100644
--- a/tests/cpp-runtime/hexagon/hexagon_device_api_tests.cc
+++ b/tests/cpp-runtime/hexagon/hexagon_device_api_tests.cc
@@ -147,22 +147,31 @@ TEST_F(HexagonDeviceAPITest,
DISABLED_alloc_free_diff_dev) {
EXPECT_THROW(hexapi->FreeDataSpace(cpu_dev, buf), InternalError);
}
-// Alloc a non-runtime buffer
-// Alloc a runtime buffer
-// "Release" resources for runtime
-// Verify the runtime buffer cannot be freed, but the non-runtime buffer can
-// This test should be run last
-TEST_F(HexagonDeviceAPITest, leak_resources) {
+// Ensure runtime buffer manager is properly configured and destroyed
+// in Acquire/Release
+TEST_F(HexagonDeviceAPITest, runtime_buffer_manager) {
hexapi->ReleaseResources();
- void* pre_runtime_buf = hexapi->AllocDataSpace(hex_dev, nbytes, alignment,
int8);
- CHECK(pre_runtime_buf != nullptr);
+ EXPECT_THROW(hexapi->AllocDataSpace(hex_dev, nbytes, alignment, int8),
InternalError);
hexapi->AcquireResources();
void* runtime_buf = hexapi->AllocDataSpace(hex_dev, nbytes, alignment, int8);
CHECK(runtime_buf != nullptr);
hexapi->ReleaseResources();
+ hexapi->FreeDataSpace(hex_dev, runtime_buf);
+ hexapi->AcquireResources();
EXPECT_THROW(hexapi->FreeDataSpace(hex_dev, runtime_buf), InternalError);
- hexapi->FreeDataSpace(hex_dev, pre_runtime_buf);
+}
+
+// Ensure RPC buffer manager is always available
+TEST_F(HexagonDeviceAPITest, rpc_buffer_manager) {
+ void* rpc_buf;
+ rpc_buf = hexapi->AllocRpcBuffer(nbytes, alignment);
+ CHECK(rpc_buf != nullptr);
+ hexapi->ReleaseResources();
+ hexapi->FreeRpcBuffer(rpc_buf);
+ rpc_buf = hexapi->AllocRpcBuffer(nbytes, alignment);
+ CHECK(rpc_buf != nullptr);
hexapi->AcquireResources();
+ hexapi->FreeRpcBuffer(rpc_buf);
}
// Ensure thread manager is properly configured and destroyed