]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: add new "PollLimit" settings to .socket units
authorLennart Poettering <lennart@poettering.net>
Sat, 9 Sep 2023 12:46:32 +0000 (14:46 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 18 Sep 2023 16:55:19 +0000 (18:55 +0200)
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.

man/org.freedesktop.systemd1.xml
src/core/dbus-socket.c
src/core/load-fragment-gperf.gperf.in
src/core/socket.c
src/core/socket.h
src/shared/bus-unit-util.c

index 4ca0583d65522517d09c3ccaa61476dc988b407c..47d4b4828b43e5d9535f9b22815977bc3641b480 100644 (file)
@@ -4735,6 +4735,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")
@@ -5969,6 +5973,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TriggerLimitBurst"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="PollLimitIntervalUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PollLimitBurst"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="UID"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="GID"/>
@@ -6505,6 +6513,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--End of Autogenerated section-->
 
+    <para><varname>PollLimitIntervalUSec</varname>/<varname>PollLimitBurst</varname> 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.</para>
+
     <refsect2>
       <title>Properties</title>
 
index 09a3a9502b33f98ddafc13b9622c1483f11bd8cb..04552b7c60bd68bca1583cffee07513a93d36696 100644 (file)
@@ -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);
 
index b66adf281193617c56b70b462cee4ff29d8003c1..0d1ee9c231aa1c91eccb6dd63e1c6c8c0dcb5d77 100644 (file)
@@ -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)
index 9e7809721afd35fba6c18791f0f6e3cd5b51b349..3e25cc8d6effb3f8420dee88e4b25d5f223ac0cf 100644 (file)
@@ -102,6 +102,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) {
@@ -307,17 +310,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)) {
 
@@ -767,9 +773,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)
@@ -1761,6 +1771,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;
index 03b11c1a692dbcdbcdf6ee41145b8db933aba53b..0b82141659a081fe8c62662be4c51e52f4515da5 100644 (file)
@@ -159,6 +159,8 @@ struct Socket {
         char *fdname;
 
         RateLimit trigger_limit;
+        usec_t poll_limit_interval;
+        unsigned poll_limit_burst;
 };
 
 SocketPeer *socket_peer_ref(SocketPeer *p);
index ad1957d9d741fbce74a88da405db436c3abdd324..e2d6bfebf320fb335ad3f6dbbe33cfd3e101cef4 100644 (file)
@@ -2165,10 +2165,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;
@@ -2377,7 +2377,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",
@@ -2392,7 +2393,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",