]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: haproxy: move limits handlers to limits
authorValentine Krasnobaeva <vkrasnobaeva@haproxy.com>
Wed, 10 Jul 2024 10:53:29 +0000 (12:53 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 10 Jul 2024 16:05:48 +0000 (18:05 +0200)
This patch moves handlers to compute process related limits in 'limits'
compilation unit.

src/haproxy.c
src/limits.c

index ea53f9130e9ca845eeafe8fa248617047658c3ce..c009475834b76fa9491f3c31e2854117e16e2814 100644 (file)
@@ -1438,202 +1438,6 @@ static void ha_random_boot(char *const *argv)
        ha_random_seed(boot_seed, sizeof(boot_seed));
 }
 
-/* considers splicing proxies' maxconn, computes the ideal global.maxpipes
- * setting, and returns it. It may return -1 meaning "unlimited" if some
- * unlimited proxies have been found and the global.maxconn value is not yet
- * set. It may also return a value greater than maxconn if it's not yet set.
- * Note that a value of zero means there is no need for pipes. -1 is never
- * returned if global.maxconn is valid.
- */
-int compute_ideal_maxpipes()
-{
-       struct proxy *cur;
-       int nbfe = 0, nbbe = 0;
-       int unlimited = 0;
-       int pipes;
-       int max;
-
-       for (cur = proxies_list; cur; cur = cur->next) {
-               if (cur->options2 & (PR_O2_SPLIC_ANY)) {
-                       if (cur->cap & PR_CAP_FE) {
-                               max = cur->maxconn;
-                               nbfe += max;
-                               if (!max) {
-                                       unlimited = 1;
-                                       break;
-                               }
-                       }
-                       if (cur->cap & PR_CAP_BE) {
-                               max = cur->fullconn ? cur->fullconn : global.maxconn;
-                               nbbe += max;
-                               if (!max) {
-                                       unlimited = 1;
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       pipes = MAX(nbfe, nbbe);
-       if (global.maxconn) {
-               if (pipes > global.maxconn || unlimited)
-                       pipes = global.maxconn;
-       } else if (unlimited) {
-               pipes = -1;
-       }
-
-       return pipes >= 4 ? pipes / 4 : pipes;
-}
-
-/* considers global.maxsocks, global.maxpipes, async engines, SSL frontends and
- * rlimits and computes an ideal maxconn. It's meant to be called only when
- * maxsock contains the sum of listening FDs, before it is updated based on
- * maxconn and pipes. If there are not enough FDs left, DEFAULT_MAXCONN (by
- * default 100) is returned as it is expected that it will even run on tight
- * environments, and will maintain compatibility with previous packages that
- * used to rely on this value as the default one. The system will emit a
- * warning indicating how many FDs are missing anyway if needed.
- */
-int compute_ideal_maxconn()
-{
-       int ssl_sides = !!global.ssl_used_frontend + !!global.ssl_used_backend;
-       int engine_fds = global.ssl_used_async_engines * ssl_sides;
-       int pipes = compute_ideal_maxpipes();
-       int remain = MAX(rlim_fd_cur_at_boot, rlim_fd_max_at_boot);
-       int maxconn;
-
-       /* we have to take into account these elements :
-        *   - number of engine_fds, which inflates the number of FD needed per
-        *     connection by this number.
-        *   - number of pipes per connection on average : for the unlimited
-        *     case, this is 0.5 pipe FDs per connection, otherwise it's a
-        *     fixed value of 2*pipes.
-        *   - two FDs per connection
-        */
-
-       /* on some modern distros for archs like amd64 fs.nr_open (kernel max)
-        * could be in order of 1 billion. Systemd since the version 256~rc3-3
-        * bumped fs.nr_open as the hard RLIMIT_NOFILE (rlim_fd_max_at_boot).
-        * If we are started without any limits, we risk to finish with computed
-        * maxconn = ~500000000, maxsock = ~2*maxconn. So, fdtab will be
-        * extremely large and watchdog will kill the process, when it will try
-        * to loop over the fdtab (see fd_reregister_all). Please note, that
-        * fd_hard_limit is taken in account implicitly via 'ideal_maxconn'
-        * value in all global.maxconn adjustements, when global.rlimit_memmax
-        * is set:
-        *
-        *   MIN(global.maxconn, capped by global.rlimit_memmax, ideal_maxconn);
-        *
-        * It also caps global.rlimit_nofile, if it couldn't be set as rlim_cur
-        * and as rlim_max. So, fd_hard_limitit is a good parameter to serve as
-        * a safeguard, when no haproxy-specific limits are set, i.e.
-        * rlimit_memmax, maxconn, rlimit_nofile. But it must be kept as a zero,
-        * if only one of these ha-specific limits is presented in config or in
-        * the cmdline.
-        */
-       if (!global.fd_hard_limit && !global.maxconn && !global.rlimit_nofile
-           && !global.rlimit_memmax)
-               global.fd_hard_limit = DEFAULT_MAXFD;
-
-       if (remain > global.fd_hard_limit)
-               remain = global.fd_hard_limit;
-
-       /* subtract listeners and checks */
-       remain -= global.maxsock;
-
-       /* one epoll_fd/kqueue_fd per thread */
-       remain -= global.nbthread;
-
-       /* one wake-up pipe (2 fd) per thread */
-       remain -= 2 * global.nbthread;
-
-       /* Fixed pipes values : we only subtract them if they're not larger
-        * than the remaining FDs because pipes are optional.
-        */
-       if (pipes >= 0 && pipes * 2 < remain)
-               remain -= pipes * 2;
-
-       if (pipes < 0) {
-               /* maxsock = maxconn * 2 + maxconn/4 * 2 + maxconn * engine_fds.
-                *         = maxconn * (2 + 0.5 + engine_fds)
-                *         = maxconn * (4 + 1 + 2*engine_fds) / 2
-                */
-               maxconn = 2 * remain / (5 + 2 * engine_fds);
-       } else {
-               /* maxsock = maxconn * 2 + maxconn * engine_fds.
-                *         = maxconn * (2 + engine_fds)
-                */
-               maxconn = remain / (2 + engine_fds);
-       }
-
-       return MAX(maxconn, DEFAULT_MAXCONN);
-}
-
-/* computes the estimated maxsock value for the given maxconn based on the
- * possibly set global.maxpipes and existing partial global.maxsock. It may
- * temporarily change global.maxconn for the time needed to propagate the
- * computations, and will reset it.
- */
-int compute_ideal_maxsock(int maxconn)
-{
-       int maxpipes = global.maxpipes;
-       int maxsock  = global.maxsock;
-
-
-       if (!maxpipes) {
-               int old_maxconn = global.maxconn;
-
-               global.maxconn = maxconn;
-               maxpipes = compute_ideal_maxpipes();
-               global.maxconn = old_maxconn;
-       }
-
-       maxsock += maxconn * 2;         /* each connection needs two sockets */
-       maxsock += maxpipes * 2;        /* each pipe needs two FDs */
-       maxsock += global.nbthread;     /* one epoll_fd/kqueue_fd per thread */
-       maxsock += 2 * global.nbthread; /* one wake-up pipe (2 fd) per thread */
-
-       /* compute fd used by async engines */
-       if (global.ssl_used_async_engines) {
-               int sides = !!global.ssl_used_frontend + !!global.ssl_used_backend;
-
-               maxsock += maxconn * sides * global.ssl_used_async_engines;
-       }
-       return maxsock;
-}
-
-/* Tests if it is possible to set the current process's RLIMIT_NOFILE to
- * <maxsock>, then sets it back to the previous value. Returns non-zero if the
- * value is accepted, non-zero otherwise. This is used to determine if an
- * automatic limit may be applied or not. When it is not, the caller knows that
- * the highest we can do is the rlim_max at boot. In case of error, we return
- * that the setting is possible, so that we defer the error processing to the
- * final stage in charge of enforcing this.
- */
-int check_if_maxsock_permitted(int maxsock)
-{
-       struct rlimit orig_limit, test_limit;
-       int ret;
-
-       if (global.fd_hard_limit && maxsock > global.fd_hard_limit)
-               return 0;
-
-       if (getrlimit(RLIMIT_NOFILE, &orig_limit) != 0)
-               return 1;
-
-       /* don't go further if we can't even set to what we have */
-       if (raise_rlim_nofile(NULL, &orig_limit) != 0)
-               return 1;
-
-       test_limit.rlim_max = MAX(maxsock, orig_limit.rlim_max);
-       test_limit.rlim_cur = test_limit.rlim_max;
-       ret = raise_rlim_nofile(NULL, &test_limit);
-
-       if (raise_rlim_nofile(NULL, &orig_limit) != 0)
-               return 1;
-
-       return ret == 0;
-}
 
 /* Evaluates a condition provided within a conditional block of the
  * configuration. Makes process to exit with 0, if the condition is true, with
index badeeb089559a70e6b17f5aa0a388667dafb4522..60c07d2061d439068389c212e4c55a203ba8070d 100644 (file)
@@ -40,3 +40,200 @@ int raise_rlim_nofile(struct rlimit *old_limit, struct rlimit *new_limit)
 
        return ret;
 }
+
+/* considers splicing proxies' maxconn, computes the ideal global.maxpipes
+ * setting, and returns it. It may return -1 meaning "unlimited" if some
+ * unlimited proxies have been found and the global.maxconn value is not yet
+ * set. It may also return a value greater than maxconn if it's not yet set.
+ * Note that a value of zero means there is no need for pipes. -1 is never
+ * returned if global.maxconn is valid.
+ */
+int compute_ideal_maxpipes()
+{
+       struct proxy *cur;
+       int nbfe = 0, nbbe = 0;
+       int unlimited = 0;
+       int pipes;
+       int max;
+
+       for (cur = proxies_list; cur; cur = cur->next) {
+               if (cur->options2 & (PR_O2_SPLIC_ANY)) {
+                       if (cur->cap & PR_CAP_FE) {
+                               max = cur->maxconn;
+                               nbfe += max;
+                               if (!max) {
+                                       unlimited = 1;
+                                       break;
+                               }
+                       }
+                       if (cur->cap & PR_CAP_BE) {
+                               max = cur->fullconn ? cur->fullconn : global.maxconn;
+                               nbbe += max;
+                               if (!max) {
+                                       unlimited = 1;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       pipes = MAX(nbfe, nbbe);
+       if (global.maxconn) {
+               if (pipes > global.maxconn || unlimited)
+                       pipes = global.maxconn;
+       } else if (unlimited) {
+               pipes = -1;
+       }
+
+       return pipes >= 4 ? pipes / 4 : pipes;
+}
+
+/* considers global.maxsocks, global.maxpipes, async engines, SSL frontends and
+ * rlimits and computes an ideal maxconn. It's meant to be called only when
+ * maxsock contains the sum of listening FDs, before it is updated based on
+ * maxconn and pipes. If there are not enough FDs left, DEFAULT_MAXCONN (by
+ * default 100) is returned as it is expected that it will even run on tight
+ * environments, and will maintain compatibility with previous packages that
+ * used to rely on this value as the default one. The system will emit a
+ * warning indicating how many FDs are missing anyway if needed.
+ */
+int compute_ideal_maxconn()
+{
+       int ssl_sides = !!global.ssl_used_frontend + !!global.ssl_used_backend;
+       int engine_fds = global.ssl_used_async_engines * ssl_sides;
+       int pipes = compute_ideal_maxpipes();
+       int remain = MAX(rlim_fd_cur_at_boot, rlim_fd_max_at_boot);
+       int maxconn;
+
+       /* we have to take into account these elements :
+        *   - number of engine_fds, which inflates the number of FD needed per
+        *     connection by this number.
+        *   - number of pipes per connection on average : for the unlimited
+        *     case, this is 0.5 pipe FDs per connection, otherwise it's a
+        *     fixed value of 2*pipes.
+        *   - two FDs per connection
+        */
+
+       /* on some modern distros for archs like amd64 fs.nr_open (kernel max)
+        * could be in order of 1 billion. Systemd since the version 256~rc3-3
+        * bumped fs.nr_open as the hard RLIMIT_NOFILE (rlim_fd_max_at_boot).
+        * If we are started without any limits, we risk to finish with computed
+        * maxconn = ~500000000, maxsock = ~2*maxconn. So, fdtab will be
+        * extremely large and watchdog will kill the process, when it will try
+        * to loop over the fdtab (see fd_reregister_all). Please note, that
+        * fd_hard_limit is taken in account implicitly via 'ideal_maxconn'
+        * value in all global.maxconn adjustements, when global.rlimit_memmax
+        * is set:
+        *
+        *   MIN(global.maxconn, capped by global.rlimit_memmax, ideal_maxconn);
+        *
+        * It also caps global.rlimit_nofile, if it couldn't be set as rlim_cur
+        * and as rlim_max. So, fd_hard_limitit is a good parameter to serve as
+        * a safeguard, when no haproxy-specific limits are set, i.e.
+        * rlimit_memmax, maxconn, rlimit_nofile. But it must be kept as a zero,
+        * if only one of these ha-specific limits is presented in config or in
+        * the cmdline.
+        */
+       if (!global.fd_hard_limit && !global.maxconn && !global.rlimit_nofile
+           && !global.rlimit_memmax)
+               global.fd_hard_limit = DEFAULT_MAXFD;
+
+       if (remain > global.fd_hard_limit)
+               remain = global.fd_hard_limit;
+
+       /* subtract listeners and checks */
+       remain -= global.maxsock;
+
+       /* one epoll_fd/kqueue_fd per thread */
+       remain -= global.nbthread;
+
+       /* one wake-up pipe (2 fd) per thread */
+       remain -= 2 * global.nbthread;
+
+       /* Fixed pipes values : we only subtract them if they're not larger
+        * than the remaining FDs because pipes are optional.
+        */
+       if (pipes >= 0 && pipes * 2 < remain)
+               remain -= pipes * 2;
+
+       if (pipes < 0) {
+               /* maxsock = maxconn * 2 + maxconn/4 * 2 + maxconn * engine_fds.
+                *         = maxconn * (2 + 0.5 + engine_fds)
+                *         = maxconn * (4 + 1 + 2*engine_fds) / 2
+                */
+               maxconn = 2 * remain / (5 + 2 * engine_fds);
+       } else {
+               /* maxsock = maxconn * 2 + maxconn * engine_fds.
+                *         = maxconn * (2 + engine_fds)
+                */
+               maxconn = remain / (2 + engine_fds);
+       }
+
+       return MAX(maxconn, DEFAULT_MAXCONN);
+}
+
+/* computes the estimated maxsock value for the given maxconn based on the
+ * possibly set global.maxpipes and existing partial global.maxsock. It may
+ * temporarily change global.maxconn for the time needed to propagate the
+ * computations, and will reset it.
+ */
+int compute_ideal_maxsock(int maxconn)
+{
+       int maxpipes = global.maxpipes;
+       int maxsock  = global.maxsock;
+
+
+       if (!maxpipes) {
+               int old_maxconn = global.maxconn;
+
+               global.maxconn = maxconn;
+               maxpipes = compute_ideal_maxpipes();
+               global.maxconn = old_maxconn;
+       }
+
+       maxsock += maxconn * 2;         /* each connection needs two sockets */
+       maxsock += maxpipes * 2;        /* each pipe needs two FDs */
+       maxsock += global.nbthread;     /* one epoll_fd/kqueue_fd per thread */
+       maxsock += 2 * global.nbthread; /* one wake-up pipe (2 fd) per thread */
+
+       /* compute fd used by async engines */
+       if (global.ssl_used_async_engines) {
+               int sides = !!global.ssl_used_frontend + !!global.ssl_used_backend;
+
+               maxsock += maxconn * sides * global.ssl_used_async_engines;
+       }
+       return maxsock;
+}
+
+/* Tests if it is possible to set the current process's RLIMIT_NOFILE to
+ * <maxsock>, then sets it back to the previous value. Returns non-zero if the
+ * value is accepted, non-zero otherwise. This is used to determine if an
+ * automatic limit may be applied or not. When it is not, the caller knows that
+ * the highest we can do is the rlim_max at boot. In case of error, we return
+ * that the setting is possible, so that we defer the error processing to the
+ * final stage in charge of enforcing this.
+ */
+int check_if_maxsock_permitted(int maxsock)
+{
+       struct rlimit orig_limit, test_limit;
+       int ret;
+
+       if (global.fd_hard_limit && maxsock > global.fd_hard_limit)
+               return 0;
+
+       if (getrlimit(RLIMIT_NOFILE, &orig_limit) != 0)
+               return 1;
+
+       /* don't go further if we can't even set to what we have */
+       if (raise_rlim_nofile(NULL, &orig_limit) != 0)
+               return 1;
+
+       test_limit.rlim_max = MAX(maxsock, orig_limit.rlim_max);
+       test_limit.rlim_cur = test_limit.rlim_max;
+       ret = raise_rlim_nofile(NULL, &test_limit);
+
+       if (raise_rlim_nofile(NULL, &orig_limit) != 0)
+               return 1;
+
+       return ret == 0;
+}