From 5736658bee0eede05caa55d66b9ad337e3b77266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Sat, 18 Apr 2020 13:58:19 +0200 Subject: [PATCH] udp: add flag to enable recvmmsg(2) explicitly Instead of implicitly enabling it by checking the supplied buffer size to alloc_cb, have a dedicated flag that must be set on `uv_udp_init_ex`. Fixes: https://github.com/libuv/libuv/issues/2791 Closes: https://github.com/libuv/libuv/pull/2792 PR-URL: https://github.com/libuv/libuv/pull/2799 Reviewed-By: Ben Noordhuis Reviewed-By: Colin Ihrig --- docs/src/udp.rst | 24 ++++++++++++++++++------ include/uv.h | 7 ++++++- src/unix/udp.c | 16 ++++++++++------ src/uv-common.h | 1 + 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/docs/src/udp.rst b/docs/src/udp.rst index 786a28b0..6be20345 100644 --- a/docs/src/udp.rst +++ b/docs/src/udp.rst @@ -41,12 +41,16 @@ Data types * (provided they all set the flag) but only the last one to bind will receive * any traffic, in effect "stealing" the port from the previous listener. */ - UV_UDP_REUSEADDR = 4 + UV_UDP_REUSEADDR = 4, /* * Indicates that the message was received by recvmmsg, so the buffer provided * must not be freed by the recv_cb callback. */ - UV_UDP_MMSG_CHUNK = 8 + UV_UDP_MMSG_CHUNK = 8, + /* + * Indicates that recvmmsg should be used, if available. + */ + UV_UDP_RECVMMSG = 256 }; .. c:type:: void (*uv_udp_send_cb)(uv_udp_send_t* req, int status) @@ -125,12 +129,17 @@ API .. c:function:: int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) - Initialize the handle with the specified flags. At the moment the lower 8 bits - of the `flags` parameter are used as the socket domain. A socket will be created - for the given domain. If the specified domain is ``AF_UNSPEC`` no socket is created, - just like :c:func:`uv_udp_init`. + Initialize the handle with the specified flags. The lower 8 bits of the `flags` + parameter are used as the socket domain. A socket will be created for the given domain. + If the specified domain is ``AF_UNSPEC`` no socket is created, just like :c:func:`uv_udp_init`. + + The remaining bits can be used to set one of these flags: + + * `UV_UDP_RECVMMSG`: if set, and the platform supports it, :man:`recvmmsg(2)` will + be used. .. versionadded:: 1.7.0 + .. versionchanged:: 1.37.0 added the `UV_UDP_RECVMMSG` flag. .. c:function:: int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) @@ -379,6 +388,9 @@ API .. versionchanged:: 1.35.0 added support for :man:`recvmmsg(2)` on supported platforms). The use of this feature requires a buffer larger than 2 * 64KB to be passed to `alloc_cb`. + .. versionchanged:: 1.37.0 :man:`recvmmsg(2)` support is no longer enabled implicitly, + it must be explicitly requested by passing the `UV_UDP_RECVMMSG` flag to + :c:func:`uv_udp_init_ex`. .. c:function:: int uv_udp_recv_stop(uv_udp_t* handle) diff --git a/include/uv.h b/include/uv.h index 2e8072fd..a3a770db 100644 --- a/include/uv.h +++ b/include/uv.h @@ -610,7 +610,12 @@ enum uv_udp_flags { * Indicates that the message was received by recvmmsg, so the buffer provided * must not be freed by the recv_cb callback. */ - UV_UDP_MMSG_CHUNK = 8 + UV_UDP_MMSG_CHUNK = 8, + + /* + * Indicates that recvmmsg should be used, if available. + */ + UV_UDP_RECVMMSG = 256 }; typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status); diff --git a/src/unix/udp.c b/src/unix/udp.c index f2fcae17..3aa11c5b 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -262,11 +262,9 @@ static void uv__udp_recvmsg(uv_udp_t* handle) { assert(buf.base != NULL); #if HAVE_MMSG - uv_once(&once, uv__udp_mmsg_init); - if (uv__recvmmsg_avail) { - /* Returned space for more than 1 datagram, use it to receive - * multiple datagrams. */ - if (buf.len >= 2 * UV__UDP_DGRAM_MAXSIZE) { + if (handle->flags & UV_HANDLE_UDP_RECVMMSG) { + uv_once(&once, uv__udp_mmsg_init); + if (uv__recvmmsg_avail) { nread = uv__udp_recvmmsg(handle, &buf); if (nread > 0) count -= nread; @@ -949,6 +947,7 @@ static int uv__udp_set_source_membership6(uv_udp_t* handle, int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { int domain; int err; + int extra_flags; int fd; /* Use the lower 8 bits for the domain */ @@ -956,7 +955,9 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) return UV_EINVAL; - if (flags & ~0xFF) + /* Use the higher bits for extra flags */ + extra_flags = flags & ~0xFF; + if (extra_flags & ~UV_UDP_RECVMMSG) return UV_EINVAL; if (domain != AF_UNSPEC) { @@ -977,6 +978,9 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { QUEUE_INIT(&handle->write_queue); QUEUE_INIT(&handle->write_completed_queue); + if (extra_flags & UV_UDP_RECVMMSG) + handle->flags |= UV_HANDLE_UDP_RECVMMSG; + return 0; } diff --git a/src/uv-common.h b/src/uv-common.h index 13192c1e..f08fb8ae 100644 --- a/src/uv-common.h +++ b/src/uv-common.h @@ -104,6 +104,7 @@ enum { /* Only used by uv_udp_t handles. */ UV_HANDLE_UDP_PROCESSING = 0x01000000, UV_HANDLE_UDP_CONNECTED = 0x02000000, + UV_HANDLE_UDP_RECVMMSG = 0x04000000, /* Only used by uv_pipe_t handles. */ UV_HANDLE_NON_OVERLAPPED_PIPE = 0x01000000,