diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index fb12659857..4b3a4f206c 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -617,40 +617,62 @@ bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b return false; } -int fd_inc_sndbuf(int fd, size_t n) { +int fd_set_sndbuf(int fd, size_t n, bool increase) { int r, value; socklen_t l = sizeof(value); + if (n > INT_MAX) + return -ERANGE; + r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) + if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) return 0; - /* If we have the privileges we will ignore the kernel limit. */ + /* First, try to set the buffer size with SO_SNDBUF. */ + r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n); + if (r < 0) + return r; - if (setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n) < 0) { - r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n); - if (r < 0) - return r; - } + /* SO_SNDBUF above may set to the kernel limit, instead of the requested size. + * So, we need to check the actual buffer size here. */ + r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); + if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) + return 1; + + /* If we have the privileges we will ignore the kernel limit. */ + r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n); + if (r < 0) + return r; return 1; } -int fd_inc_rcvbuf(int fd, size_t n) { +int fd_set_rcvbuf(int fd, size_t n, bool increase) { int r, value; socklen_t l = sizeof(value); + if (n > INT_MAX) + return -ERANGE; + r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) + if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) return 0; - /* If we have the privileges we will ignore the kernel limit. */ + /* First, try to set the buffer size with SO_RCVBUF. */ + r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n); + if (r < 0) + return r; - if (setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n) < 0) { - r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n); - if (r < 0) - return r; - } + /* SO_RCVBUF above may set to the kernel limit, instead of the requested size. + * So, we need to check the actual buffer size here. */ + r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); + if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2) + return 1; + + /* If we have the privileges we will ignore the kernel limit. */ + r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n); + if (r < 0) + return r; return 1; } diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 5610a99e53..6a320a976f 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -118,8 +118,14 @@ int netlink_family_from_string(const char *s) _pure_; bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b); -int fd_inc_sndbuf(int fd, size_t n); -int fd_inc_rcvbuf(int fd, size_t n); +int fd_set_sndbuf(int fd, size_t n, bool increase); +static inline int fd_inc_sndbuf(int fd, size_t n) { + return fd_set_sndbuf(fd, n, true); +} +int fd_set_rcvbuf(int fd, size_t n, bool increase); +static inline int fd_inc_rcvbuf(int fd, size_t n) { + return fd_set_rcvbuf(fd, n, true); +} int ip_tos_to_string_alloc(int i, char **s); int ip_tos_from_string(const char *s); diff --git a/src/core/socket.c b/src/core/socket.c index 855b354dd8..a21584ea48 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1058,20 +1058,15 @@ static void socket_apply_socket_options(Socket *s, int fd) { } if (s->receive_buffer > 0) { - /* We first try with SO_RCVBUFFORCE, in case we have the perms for that */ - if (setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, s->receive_buffer) < 0) { - r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, s->receive_buffer); - if (r < 0) - log_unit_warning_errno(UNIT(s), r, "SO_RCVBUF failed: %m"); - } + r = fd_set_rcvbuf(fd, s->receive_buffer, false); + if (r < 0) + log_unit_warning_errno(UNIT(s), r, "SO_RCVBUF/SO_RCVBUFFORCE failed: %m"); } if (s->send_buffer > 0) { - if (setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, s->send_buffer) < 0) { - r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, s->send_buffer); - if (r < 0) - log_unit_warning_errno(UNIT(s), r, "SO_SNDBUF failed: %m"); - } + r = fd_set_sndbuf(fd, s->receive_buffer, false); + if (r < 0) + log_unit_warning_errno(UNIT(s), r, "SO_SNDBUF/SO_SNDBUFFORCE failed: %m"); } if (s->mark >= 0) { diff --git a/src/libsystemd/sd-device/device-monitor.c b/src/libsystemd/sd-device/device-monitor.c index cb6f1a70f8..43646e1880 100644 --- a/src/libsystemd/sd-device/device-monitor.c +++ b/src/libsystemd/sd-device/device-monitor.c @@ -91,18 +91,9 @@ int device_monitor_allow_unicast_sender(sd_device_monitor *m, sd_device_monitor } _public_ int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size) { - int r, n = (int) size; - assert_return(m, -EINVAL); - assert_return((size_t) n == size, -EINVAL); - if (setsockopt_int(m->sock, SOL_SOCKET, SO_RCVBUFFORCE, n) < 0) { - r = setsockopt_int(m->sock, SOL_SOCKET, SO_RCVBUF, n); - if (r < 0) - return r; - } - - return 0; + return fd_set_rcvbuf(m->sock, size, false); } int device_monitor_disconnect(sd_device_monitor *m) { diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 6187434466..344fc08c5b 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -36,11 +36,11 @@ #include "path-util.h" #include "set.h" #include "signal-util.h" +#include "stat-util.h" #include "strv.h" #include "sysctl-util.h" #include "tmpfile-util.h" #include "udev-util.h" -#include "virt.h" /* use 128 MB for receive socket kernel queue. */ #define RCVBUF_SIZE (128*1024*1024) @@ -261,16 +261,19 @@ static int manager_udev_process_link(sd_device_monitor *monitor, sd_device *devi static int manager_connect_udev(Manager *m) { int r; - /* udev does not initialize devices inside containers, - * so we rely on them being already initialized before - * entering the container */ - if (detect_container() > 0) + /* udev does not initialize devices inside containers, so we rely on them being already + * initialized before entering the container. */ + if (path_is_read_only_fs("/sys") > 0) return 0; r = sd_device_monitor_new(&m->device_monitor); if (r < 0) return log_error_errno(r, "Failed to initialize device monitor: %m"); + r = sd_device_monitor_set_receive_buffer_size(m->device_monitor, RCVBUF_SIZE); + if (r < 0) + log_warning_errno(r, "Failed to increase buffer size for device monitor, ignoring: %m"); + r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "net", NULL); if (r < 0) return log_error_errno(r, "Could not add device monitor filter: %m"); @@ -1347,7 +1350,7 @@ static int manager_connect_genl(Manager *m) { r = sd_netlink_inc_rcvbuf(m->genl, RCVBUF_SIZE); if (r < 0) - return r; + log_warning_errno(r, "Failed to increase receive buffer size for general netlink socket, ignoring: %m"); r = sd_netlink_attach_event(m->genl, m->event, 0); if (r < 0) @@ -1369,9 +1372,14 @@ static int manager_connect_rtnl(Manager *m) { if (r < 0) return r; - r = sd_netlink_inc_rcvbuf(m->rtnl, RCVBUF_SIZE); - if (r < 0) - return r; + /* Bump receiver buffer, but only if we are not called via socket activation, as in that + * case systemd sets the receive buffer size for us, and the value in the .socket unit + * should take full effect. */ + if (fd < 0) { + r = sd_netlink_inc_rcvbuf(m->rtnl, RCVBUF_SIZE); + if (r < 0) + log_warning_errno(r, "Failed to increase receive buffer size for rtnl socket, ignoring: %m"); + } r = sd_netlink_attach_event(m->rtnl, m->event, 0); if (r < 0) diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 48a7774fc9..22dad2acaf 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -1686,8 +1686,11 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg /* Bump receiver buffer, but only if we are not called via socket activation, as in that * case systemd sets the receive buffer size for us, and the value in the .socket unit * should take full effect. */ - if (fd_uevent < 0) - (void) sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024); + if (fd_uevent < 0) { + r = sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024); + if (r < 0) + log_warning_errno(r, "Failed to set receive buffer size for device monitor, ignoring: %m"); + } r = device_monitor_enable_receiving(manager->monitor); if (r < 0)