From: Valentine Krasnobaeva Date: Wed, 10 Jul 2024 10:53:29 +0000 (+0200) Subject: REORG: haproxy: move limits handlers to limits X-Git-Tag: v3.1-dev4~108 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1f8addfdc2c98b4462629e1b07a00592995e5ea3;p=thirdparty%2Fhaproxy.git REORG: haproxy: move limits handlers to limits This patch moves handlers to compute process related limits in 'limits' compilation unit. --- diff --git a/src/haproxy.c b/src/haproxy.c index ea53f9130e..c009475834 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -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 - * , 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 diff --git a/src/limits.c b/src/limits.c index badeeb0895..60c07d2061 100644 --- a/src/limits.c +++ b/src/limits.c @@ -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 + * , 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; +}