New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
liburing zero copy #1273
liburing zero copy #1273
Conversation
By pre-registering the send buffers we can make use of zero copy in order to avoid the kernel memcpy-ing the buffers. Send buffers are allocated by us and will be set as available once liburing notifies us about it.
This is crashing in CI: https://github.com/versatica/mediasoup/actions/runs/7264289536/job/19791498040?pr=1273 |
NOTE for me: Current CI errors on ubuntu 22.04:
|
This is related to the memory available for the process. From The buffers associated with the iovecs will be locked in memory and charged against the user's RLIMIT_MEMLOCK resource limit We are allocating (4 * 1024) buffers of 1500B each, meaning ~= 5MB of memory for sending buffers. That is fairly low for a server. This error is happening in ubuntu-22.04 only, and not in previous versions so I wonder if they've changed the RLIMIT_MEMLOCK values for the image :-/ |
Are you sure this is about the image? You should be able to change those limits if you need to. But either way zero copy is not magic, memory for in-flight messages still has to be allocated somewhere. Whether 2% difference is worth the complexity and trickiness I'm not sure. |
We are already allocating such memory for liburing usage as it's all about sending multiple packets in batches. This PR is just telling liburing to keep the buffers untill put in the network card queue instead of coying them, nothing else. We can just disable ZC if the iovecs cannot be registered, and use the feature otherwise. |
This feature should be ready for those errors and react of them. Then we can document that rlimits must be managed and so on for proper usage. Can we try/carch the "no memory" specific error and, if so, skip io-uring usage and fallback to default behavior? |
Yes, we can disable zero copy if this error happens upon buffer registering. The rest of io-uring does not depend on rlmits. |
So this is wrapped in err = io_uring_register_buffers(std::addressof(this->ring), this->iovecs, DepLibUring::QueueDepth);
if (err < 0)
{
MS_THROW_ERROR("io_uring_register_buffers() failed: %s", std::strerror(-err));
} This means that this is throwing and the caller side doesn't silence the error, which is good. So we should add proper try/catch (I mean check So we should have some flag and public getter telling whether zero copy is enabled or not and check that flag in the related code, right? |
BTW the "Cannot allocate memory" is not a int io_uring_register_buffers(struct io_uring *ring, const struct iovec *iovecs,
unsigned nr_iovecs)
{
return do_register(ring, IORING_REGISTER_BUFFERS, iovecs, nr_iovecs);
} static inline int do_register(struct io_uring *ring, unsigned int opcode,
const void *arg, unsigned int nr_args)
{
int fd;
if (ring->int_flags & INT_FLAG_REG_REG_RING) {
opcode |= IORING_REGISTER_USE_REGISTERED_RING;
fd = ring->enter_ring_fd;
} else {
fd = ring->ring_fd;
}
return __sys_io_uring_register(fd, opcode, arg, nr_args);
} static inline int __sys_io_uring_register(unsigned int fd, unsigned int opcode,
const void *arg, unsigned int nr_args)
{
int ret;
ret = syscall(__NR_io_uring_register, fd, opcode, arg, nr_args);
return (ret < 0) ? -errno : ret;
} Let's see what the hell is #ifndef __NR_io_uring_register
#define __NR_io_uring_register 537
#endif
#elif defined __mips__
#ifndef __NR_io_uring_setup
#define __NR_io_uring_setup (__NR_Linux + 425)
#endif
#ifndef __NR_io_uring_enter
#define __NR_io_uring_enter (__NR_Linux + 426)
#endif
#ifndef __NR_io_uring_register
#define __NR_io_uring_register (__NR_Linux + 427)
#endif
#else /* !__alpha__ and !__mips__ */
#ifndef __NR_io_uring_setup
#define __NR_io_uring_setup 425
#endif
#ifndef __NR_io_uring_enter
#define __NR_io_uring_enter 426
#endif
#ifndef __NR_io_uring_register
#define __NR_io_uring_register 427
#endif Hehe. So our only chance is that https://en.cppreference.com/w/cpp/header/cerrno BTW cool mini library here that prints macro name of a given |
Or should we just log an error if |
Correct.
Just in case the error is code is ENOMEM. NOTE: I need to check the error is exactly this. |
Probably. Just wondering if some other legit errors could happen in some legit scenarios (VMs, AWS, Docker, etc), no idea but may be |
Let's see the CI output... |
# Conflicts: # CHANGELOG.md
Tests are now passing so your ENOMEM check is working fine. I've updated CHANGELOG and made a cosmetic change. |
I've merged v3 in this branch with updates ot NPM deps. So please run |
Wow:
Also wiht this change:
|
worker/src/DepLibUring.cpp
Outdated
@@ -313,15 +313,15 @@ DepLibUring::LibUring::LibUring() | |||
|
|||
if (err < 0) | |||
{ | |||
int errno = -err; | |||
int errno = -1 * err; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here seems the variable name. It's not a good idea using errno
as a local variable as the compiler threats it specially (see the compiling errors.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this file is using -err
in other places for logging purposes, so this change is generating an inconsistency across the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes done to make it consistent.
By pre-registering the send buffers into io_uring we can make use of zero copy in order to avoid the kernel memcpy-ing the buffers.
Send buffers are allocated by us and will be set as available for another use once liburing notifies us about it (once the buffers are in the network cards queue).
In my testing environment this provides a ~=2% CPU improvement (1 Router, 1 A/V Producer, 40 A/V Consumers).
NOTE: This is NOT applied to TCP on purpose as the sending buffer release in TCP depends on the reception of ACK from the client, and we don't want our buffer usage to depend on client's delay.
NOTE: Discussion in liburing about the CI issue.