From df25afd2cf5527fe1bb542bb146fef1be8d9a489 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 9 Sep 2023 14:46:32 +0200 Subject: [PATCH 1/3] core: add new "PollLimit" settings to .socket units This adds a new "PollLimit" pair of settings to .socket units, very similar to existing "TriggerLimit" logic. The differences are: * PollLimit focusses on the polling on the sockets, and pauses that temporarily if a ratelimit on that is reached. TriggerLimit otoh focusses on the triggering effect of socket units, and stops triggering once the ratelimit is hit. * While the trigger limit being hit is an action that causes the socket unit to fail the polling limit being reached will just temporarily disable polling on the socket fd, and it is resumed once the ratelimit interval is over. * When a socket unit operates on multiple socket fds (e,g, ListenStream= on both some ipv6 and an ipv4 address or so). Then the PollLimit will be specific to each fd, while the trigger limit is specific to the whole unit. Implementation-wise this is mostly a wrapper around sd-event's sd_event_source_set_ratelimit(), which exposes the desired behaviour directly. Usecase for all of this: socket services which when overloaded with connections should just slow down reception of it, but not fail persistently. (cherry picked from commit 2bec84e7a5bf3687ae65205753ba3d8067cf2f0e) --- man/org.freedesktop.systemd1.xml | 12 ++++++++++ src/core/dbus-socket.c | 8 +++++++ src/core/load-fragment-gperf.gperf.in | 2 ++ src/core/socket.c | 32 +++++++++++++++++++-------- src/core/socket.h | 2 ++ src/shared/bus-unit-util.c | 10 +++++---- 6 files changed, 53 insertions(+), 13 deletions(-) diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 56906e2f3b..0557dc2379 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -4727,6 +4727,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { readonly t TriggerLimitIntervalUSec = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly u TriggerLimitBurst = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t PollLimitIntervalUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u PollLimitBurst = ...; readonly u UID = ...; readonly u GID = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates") @@ -5961,6 +5965,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + + + @@ -6497,6 +6505,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + PollLimitIntervalUSec/PollLimitBurst properties configure the + polling limit for the socket unit. Expects a time in µs, resp. an unsigned integer. If either is set to + zero the limiting feature is turned off. + Properties diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 09a3a9502b..04552b7c60 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -129,6 +129,8 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PollLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, poll_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PollLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, poll_limit_burst), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), @@ -248,6 +250,9 @@ static int bus_socket_set_transient_property( if (streq(name, "TriggerLimitBurst")) return bus_set_transient_unsigned(u, name, &s->trigger_limit.burst, message, flags, error); + if (streq(name, "PollLimitBurst")) + return bus_set_transient_unsigned(u, name, &s->poll_limit_burst, message, flags, error); + if (streq(name, "SocketMode")) return bus_set_transient_mode_t(u, name, &s->socket_mode, message, flags, error); @@ -275,6 +280,9 @@ static int bus_socket_set_transient_property( if (streq(name, "TriggerLimitIntervalUSec")) return bus_set_transient_usec(u, name, &s->trigger_limit.interval, message, flags, error); + if (streq(name, "PollLimitIntervalUSec")) + return bus_set_transient_usec(u, name, &s->poll_limit_interval, message, flags, error); + if (streq(name, "SmackLabel")) return bus_set_transient_string(u, name, &s->smack, message, flags, error); diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index b66adf2811..0d1ee9c231 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -507,6 +507,8 @@ Socket.FileDescriptorName, config_parse_fdname, Socket.Service, config_parse_socket_service, 0, 0 Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval) Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst) +Socket.PollLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, poll_limit_interval) +Socket.PollLimitBurst, config_parse_unsigned, 0, offsetof(Socket, poll_limit_burst) {% if ENABLE_SMACK %} Socket.SmackLabel, config_parse_unit_string_printf, 0, offsetof(Socket, smack) Socket.SmackLabelIPIn, config_parse_unit_string_printf, 0, offsetof(Socket, smack_ip_in) diff --git a/src/core/socket.c b/src/core/socket.c index 75034ac357..dc18744f54 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -101,6 +101,9 @@ static void socket_init(Unit *u) { s->trigger_limit.interval = USEC_INFINITY; s->trigger_limit.burst = UINT_MAX; + + s->poll_limit_interval = USEC_INFINITY; + s->poll_limit_burst = UINT_MAX; } static void socket_unwatch_control_pid(Socket *s) { @@ -310,17 +313,20 @@ static int socket_add_extras(Socket *s) { * off the queues, which it might not necessarily do. Moreover, while Accept=no services are supposed to * process whatever is queued in one go, and thus should normally never have to be started frequently. This is * different for Accept=yes where each connection is processed by a new service instance, and thus frequent - * service starts are typical. */ + * service starts are typical. + * + * For the poll limit we follow a similar rule, but use 3/4th of the trigger limit parameters, to + * trigger this earlier. */ if (s->trigger_limit.interval == USEC_INFINITY) s->trigger_limit.interval = 2 * USEC_PER_SEC; + if (s->trigger_limit.burst == UINT_MAX) + s->trigger_limit.burst = s->accept ? 200 : 20; - if (s->trigger_limit.burst == UINT_MAX) { - if (s->accept) - s->trigger_limit.burst = 200; - else - s->trigger_limit.burst = 20; - } + if (s->poll_limit_interval == USEC_INFINITY) + s->poll_limit_interval = 2 * USEC_PER_SEC; + if (s->poll_limit_burst == UINT_MAX) + s->poll_limit_burst = s->accept ? 150 : 15; if (have_non_accept_socket(s)) { @@ -770,9 +776,13 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sTriggerLimitIntervalSec: %s\n" - "%sTriggerLimitBurst: %u\n", + "%sTriggerLimitBurst: %u\n" + "%sPollLimitIntervalSec: %s\n" + "%sPollLimitBurst: %u\n", prefix, FORMAT_TIMESPAN(s->trigger_limit.interval, USEC_PER_SEC), - prefix, s->trigger_limit.burst); + prefix, s->trigger_limit.burst, + prefix, FORMAT_TIMESPAN(s->poll_limit_interval, USEC_PER_SEC), + prefix, s->poll_limit_burst); str = ip_protocol_to_name(s->socket_protocol); if (str) @@ -1765,6 +1775,10 @@ static int socket_watch_fds(Socket *s) { (void) sd_event_source_set_description(p->event_source, "socket-port-io"); } + + r = sd_event_source_set_ratelimit(p->event_source, s->poll_limit_interval, s->poll_limit_burst); + if (r < 0) + log_unit_debug_errno(UNIT(s), r, "Failed to set poll limit on I/O event source, ignoring: %m"); } return 0; diff --git a/src/core/socket.h b/src/core/socket.h index 191d27f46d..b03a291e4a 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -158,6 +158,8 @@ struct Socket { char *fdname; RateLimit trigger_limit; + usec_t poll_limit_interval; + unsigned poll_limit_burst; }; SocketPeer *socket_peer_ref(SocketPeer *p); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index e7b44cc39b..9f0f37488d 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -2170,10 +2170,10 @@ static int bus_append_path_property(sd_bus_message *m, const char *field, const return 1; } - if (streq(field, "TriggerLimitBurst")) + if (STR_IN_SET(field, "TriggerLimitBurst", "PollLimitBurst")) return bus_append_safe_atou(m, field, eq); - if (streq(field, "TriggerLimitIntervalSec")) + if (STR_IN_SET(field, "TriggerLimitIntervalSec", "PollLimitIntervalSec")) return bus_append_parse_sec_rename(m, field, eq); return 0; @@ -2382,7 +2382,8 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", - "TriggerLimitBurst")) + "TriggerLimitBurst", + "PollLimitBurst")) return bus_append_safe_atou(m, field, eq); if (STR_IN_SET(field, "SocketMode", @@ -2397,7 +2398,8 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", - "TriggerLimitIntervalSec")) + "TriggerLimitIntervalSec", + "PollLimitIntervalSec")) return bus_append_parse_sec_rename(m, field, eq); if (STR_IN_SET(field, "ReceiveBuffer",