IoUringAssmEngn: map StagingBuff w/mmap; reg w/io_uring; add eventFd
StagingBuffer: We now allocate memory with mmap(MAP_ANONYMOUS) so that we can be sure it can be pinned with io_uring_register_buffers(). This ensures that if DMA is possible, it should be usable. IoUringAssemblyEngine: We now register an eventfd with io_uring so that we can listen for CQEs with boost::asio.
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
#include <boostAsioLinkageFix.h>
|
#include <boostAsioLinkageFix.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/eventfd.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <boost/system/error_code.hpp>
|
#include <boost/system/error_code.hpp>
|
||||||
#include <livoxProto1/device.h>
|
#include <livoxProto1/device.h>
|
||||||
#include <livoxProto1/livoxProto1.h>
|
#include <livoxProto1/livoxProto1.h>
|
||||||
@@ -33,7 +36,7 @@ IoUringAssemblyEngine::IoUringAssemblyEngine(PcloudStimulusBuffer& parent_)
|
|||||||
: parent(parent_),
|
: parent(parent_),
|
||||||
frameAssemblyDesc(nullptr), ring{},
|
frameAssemblyDesc(nullptr), ring{},
|
||||||
isSetup(false),
|
isSetup(false),
|
||||||
stallTimer(parent_.device->componentThread->getIoService())
|
eventfdFd(-1), stallTimer(parent_.device->componentThread->getIoService())
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool IoUringAssemblyEngine::setup()
|
bool IoUringAssemblyEngine::setup()
|
||||||
@@ -61,17 +64,47 @@ bool IoUringAssemblyEngine::setup()
|
|||||||
if (udpFd < 0)
|
if (udpFd < 0)
|
||||||
{ return false; }
|
{ return false; }
|
||||||
|
|
||||||
|
// Declare iovec early to avoid goto crossing initialization
|
||||||
|
struct iovec iov;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/** EXPLANATION:
|
/** EXPLANATION:
|
||||||
* Initialize io_uring ring - allocate SQEs and CQEs for one frame assembly
|
* Initialize io_uring ring - allocate SQEs and CQEs for one frame assembly
|
||||||
* One SQE per slot (one datagram per slot)
|
* One SQE per slot (one datagram per slot)
|
||||||
*/
|
*/
|
||||||
int ret = io_uring_queue_init(
|
ret = io_uring_queue_init(
|
||||||
static_cast<unsigned int>(frameAssemblyDesc->numSlots), &ring, 0);
|
static_cast<unsigned int>(frameAssemblyDesc->numSlots), &ring, 0);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{ return false; }
|
{ goto cleanup; }
|
||||||
|
|
||||||
|
// Register staging buffer with io_uring for DMA-apt I/O
|
||||||
|
iov = parent.stagingBuffer.getIoUringRegisterIoVec();
|
||||||
|
ret = io_uring_register_buffers(&ring, &iov, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
{ goto cleanup_ring; }
|
||||||
|
|
||||||
|
// Create eventfd for CQE notifications (used with boost's unified loop)
|
||||||
|
eventfdFd = eventfd(0, EFD_NONBLOCK);
|
||||||
|
if (eventfdFd < 0)
|
||||||
|
{ goto cleanup_buffers; }
|
||||||
|
|
||||||
|
// Register eventfd with io_uring
|
||||||
|
ret = io_uring_register_eventfd(&ring, eventfdFd);
|
||||||
|
if (ret < 0)
|
||||||
|
{ goto cleanup_eventfd; }
|
||||||
|
|
||||||
isSetup = true;
|
isSetup = true;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
cleanup_eventfd:
|
||||||
|
close(eventfdFd);
|
||||||
|
eventfdFd = -1;
|
||||||
|
cleanup_buffers:
|
||||||
|
io_uring_unregister_buffers(&ring);
|
||||||
|
cleanup_ring:
|
||||||
|
io_uring_queue_exit(&ring);
|
||||||
|
cleanup:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IoUringAssemblyEngine::finalize()
|
void IoUringAssemblyEngine::finalize()
|
||||||
@@ -79,9 +112,16 @@ void IoUringAssemblyEngine::finalize()
|
|||||||
// Call stop() to cancel in-flight operations (stop() already cancels the timer)
|
// Call stop() to cancel in-flight operations (stop() already cancels the timer)
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
// Clean up io_uring ring if it was initialized
|
if (eventfdFd >= 0)
|
||||||
|
{
|
||||||
|
io_uring_unregister_eventfd(&ring);
|
||||||
|
close(eventfdFd);
|
||||||
|
eventfdFd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (isSetup)
|
if (isSetup)
|
||||||
{
|
{
|
||||||
|
io_uring_unregister_buffers(&ring);
|
||||||
io_uring_queue_exit(&ring);
|
io_uring_queue_exit(&ring);
|
||||||
isSetup = false;
|
isSetup = false;
|
||||||
}
|
}
|
||||||
@@ -109,6 +149,9 @@ void IoUringAssemblyEngine::stop()
|
|||||||
// 1. Cancel all pending SQEs using io_uring cancellation mechanisms
|
// 1. Cancel all pending SQEs using io_uring cancellation mechanisms
|
||||||
// 2. Cancel in-flight stall timeout timer via stallTimer.cancel()
|
// 2. Cancel in-flight stall timeout timer via stallTimer.cancel()
|
||||||
// 3. Set appropriate state flags
|
// 3. Set appropriate state flags
|
||||||
|
|
||||||
|
// Cancel in-flight stall timeout timer
|
||||||
|
stallTimer.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IoUringAssemblyEngine::cancelIncompleteAndFillDummies()
|
void IoUringAssemblyEngine::cancelIncompleteAndFillDummies()
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ private:
|
|||||||
struct io_uring ring;
|
struct io_uring ring;
|
||||||
bool isSetup;
|
bool isSetup;
|
||||||
|
|
||||||
|
// Eventfd for CQE notifications (used with boost's unified loop)
|
||||||
|
int eventfdFd;
|
||||||
// Point cloud data socket descriptor
|
// Point cloud data socket descriptor
|
||||||
std::shared_ptr<boost::asio::posix::stream_descriptor> pcloudDataFdDesc;
|
std::shared_ptr<boost::asio::posix::stream_descriptor> pcloudDataFdDesc;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "frameAssemblyDesc.h"
|
#include "frameAssemblyDesc.h"
|
||||||
|
|
||||||
@@ -96,6 +99,18 @@ public:
|
|||||||
void startAssembly() { assemblingFlag.store(true); }
|
void startAssembly() { assemblingFlag.store(true); }
|
||||||
void stopAssembly() { assemblingFlag.store(false); }
|
void stopAssembly() { assemblingFlag.store(false); }
|
||||||
|
|
||||||
|
/** EXPLANATION:
|
||||||
|
* Returns an iovec for io_uring registration.
|
||||||
|
* The buffer is mmap()-allocated and suitable for IORING_REGISTER_BUFFERS.
|
||||||
|
*/
|
||||||
|
struct iovec getIoUringRegisterIoVec() const
|
||||||
|
{
|
||||||
|
struct iovec iov;
|
||||||
|
iov.iov_base = buffer.get();
|
||||||
|
iov.iov_len = bufferNBytes;
|
||||||
|
return iov;
|
||||||
|
}
|
||||||
|
|
||||||
inline std::string stringify() const
|
inline std::string stringify() const
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
@@ -111,8 +126,25 @@ public:
|
|||||||
private:
|
private:
|
||||||
void computeSlotStrideAndBufferSize();
|
void computeSlotStrideAndBufferSize();
|
||||||
|
|
||||||
// Buffer data
|
// Custom deleter for mmap-allocated buffer
|
||||||
std::unique_ptr<uint8_t[]> buffer;
|
struct MmapDeleter
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
MmapDeleter(size_t s) : size(s) {}
|
||||||
|
|
||||||
|
void operator()(uint8_t* ptr) const
|
||||||
|
{
|
||||||
|
if (ptr != nullptr && size > 0)
|
||||||
|
{
|
||||||
|
munmap(ptr, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Buffer data - mmap-allocated for io_uring registration
|
||||||
|
// Using unique_ptr<uint8_t, MmapDeleter> instead of array syntax
|
||||||
|
// since we have a custom deleter that knows the size
|
||||||
|
std::unique_ptr<uint8_t, MmapDeleter> buffer;
|
||||||
size_t bufferNBytes;
|
size_t bufferNBytes;
|
||||||
|
|
||||||
// Layout/invariants
|
// Layout/invariants
|
||||||
@@ -135,7 +167,7 @@ inline StagingBuffer::StagingBuffer(
|
|||||||
const InputEngineConstraints& inputEngineConstraints_,
|
const InputEngineConstraints& inputEngineConstraints_,
|
||||||
const OutputEngineConstraints& /*outputEngineConstraints*/,
|
const OutputEngineConstraints& /*outputEngineConstraints*/,
|
||||||
size_t nDgramsPerFrame)
|
size_t nDgramsPerFrame)
|
||||||
: buffer(nullptr), bufferNBytes(0),
|
: buffer(nullptr, MmapDeleter(0)), bufferNBytes(0),
|
||||||
nDgramsPerFrame(nDgramsPerFrame), slotStrideNBytes(0),
|
nDgramsPerFrame(nDgramsPerFrame), slotStrideNBytes(0),
|
||||||
inputConstraints(inputEngineConstraints_),
|
inputConstraints(inputEngineConstraints_),
|
||||||
assemblingFlag(false)
|
assemblingFlag(false)
|
||||||
@@ -148,7 +180,23 @@ assemblingFlag(false)
|
|||||||
|
|
||||||
computeSlotStrideAndBufferSize();
|
computeSlotStrideAndBufferSize();
|
||||||
|
|
||||||
buffer = std::make_unique<uint8_t[]>(bufferNBytes);
|
/* Allocate buffer using mmap() for io_uring registration
|
||||||
|
* MAP_ANONYMOUS | MAP_PRIVATE creates anonymous, non-file-backed memory
|
||||||
|
*/
|
||||||
|
void* mmapped = mmap(
|
||||||
|
nullptr, bufferNBytes,
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_ANONYMOUS | MAP_PRIVATE,
|
||||||
|
-1, 0);
|
||||||
|
|
||||||
|
if (mmapped == MAP_FAILED)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string(__func__)
|
||||||
|
+ ": StagingBuffer: mmap() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = std::unique_ptr<uint8_t, MmapDeleter>(
|
||||||
|
static_cast<uint8_t*>(mmapped), MmapDeleter(bufferNBytes));
|
||||||
currentNBytes.store(0);
|
currentNBytes.store(0);
|
||||||
|
|
||||||
// Build FrameAssemblyDesc once
|
// Build FrameAssemblyDesc once
|
||||||
|
|||||||
@@ -39,3 +39,5 @@
|
|||||||
to the io_context of the thread it should post its callbacks to, and
|
to the io_context of the thread it should post its callbacks to, and
|
||||||
then post callbacks to those io_contexts when UDP cmd responses
|
then post callbacks to those io_contexts when UDP cmd responses
|
||||||
come in.
|
come in.
|
||||||
|
* Consider using MAP_HUGEPAGE with both PcloudStimBuff::StagingBuffer
|
||||||
|
and in the PcloudStimulusBuffer's ringbuff.
|
||||||
|
|||||||
Reference in New Issue
Block a user