From db3e622dd8f9d511e45ad87197fc216da3865a9b Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 9 Dec 2022 19:30:55 +0000 Subject: [PATCH 1/4] fpm binding master and children processes to specific core(s). rational is there is git snippets and similar floating around about how to achieve this via command line. So the idea is to do it "natively" especially those above mostly focus on linux whereas more platforms are cpu affinity "capable". --- sapi/fpm/fpm/fpm_conf.c | 15 ++++ sapi/fpm/fpm/fpm_conf.h | 6 ++ sapi/fpm/fpm/fpm_config.h | 13 +++ sapi/fpm/fpm/fpm_unix.c | 123 ++++++++++++++++++++++++++ sapi/fpm/php-fpm.conf.in | 12 +++ sapi/fpm/tests/cpuaffinity-fail.phpt | 43 +++++++++ sapi/fpm/tests/cpuaffinity-range.phpt | 55 ++++++++++++ sapi/fpm/tests/cpuaffinity.phpt | 55 ++++++++++++ sapi/fpm/www.conf.in | 12 +++ 9 files changed, 334 insertions(+) create mode 100644 sapi/fpm/tests/cpuaffinity-fail.phpt create mode 100644 sapi/fpm/tests/cpuaffinity-range.phpt create mode 100644 sapi/fpm/tests/cpuaffinity.phpt diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c index 7743d28f448dd..cfd5c7869218f 100644 --- a/sapi/fpm/fpm/fpm_conf.c +++ b/sapi/fpm/fpm/fpm_conf.c @@ -97,6 +97,9 @@ static const struct ini_value_parser_s ini_fpm_global_options[] = { { "process_control_timeout", &fpm_conf_set_time, GO(process_control_timeout) }, { "process.max", &fpm_conf_set_integer, GO(process_max) }, { "process.priority", &fpm_conf_set_integer, GO(process_priority) }, +#if HAVE_FPM_CPUAFFINITY + { "process.cpu_list", &fpm_conf_set_string, GO(process_cpu_list) }, +#endif { "daemonize", &fpm_conf_set_boolean, GO(daemonize) }, { "rlimit_files", &fpm_conf_set_integer, GO(rlimit_files) }, { "rlimit_core", &fpm_conf_set_rlimit_core, GO(rlimit_core) }, @@ -129,6 +132,9 @@ static const struct ini_value_parser_s ini_fpm_pool_options[] = { #endif { "process.priority", &fpm_conf_set_integer, WPO(process_priority) }, { "process.dumpable", &fpm_conf_set_boolean, WPO(process_dumpable) }, +#if HAVE_FPM_CPUAFFINITY + { "process.cpu_list", &fpm_conf_set_string, WPO(process_cpu_list) }, +#endif { "pm", &fpm_conf_set_pm, WPO(pm) }, { "pm.max_children", &fpm_conf_set_integer, WPO(pm_max_children) }, { "pm.start_servers", &fpm_conf_set_integer, WPO(pm_start_servers) }, @@ -634,6 +640,9 @@ static void *fpm_worker_pool_config_alloc(void) wp->config->pm_process_idle_timeout = 10; /* 10s by default */ wp->config->process_priority = 64; /* 64 means unset */ wp->config->process_dumpable = 0; +#if HAVE_FPM_CPUAFFINITY + wp->config->process_cpu_list = NULL; +#endif wp->config->clear_env = 1; wp->config->decorate_workers_output = 1; #ifdef SO_SETFIB @@ -1730,6 +1739,9 @@ static void fpm_conf_dump(void) } else { zlog(ZLOG_NOTICE, "\tprocess.priority = %d", fpm_global_config.process_priority); } +#if HAVE_FPM_CPUAFFINITY + zlog(ZLOG_NOTICE, "\tprocess.cpu_list = %s", STR2STR(fpm_global_config.process_cpu_list)); +#endif zlog(ZLOG_NOTICE, "\tdaemonize = %s", BOOL2STR(fpm_global_config.daemonize)); zlog(ZLOG_NOTICE, "\trlimit_files = %d", fpm_global_config.rlimit_files); zlog(ZLOG_NOTICE, "\trlimit_core = %d", fpm_global_config.rlimit_core); @@ -1769,6 +1781,9 @@ static void fpm_conf_dump(void) zlog(ZLOG_NOTICE, "\tprocess.priority = %d", wp->config->process_priority); } zlog(ZLOG_NOTICE, "\tprocess.dumpable = %s", BOOL2STR(wp->config->process_dumpable)); +#if HAVE_FPM_CPUAFFINITY + zlog(ZLOG_NOTICE, "\tprocess.cpu_list = %s", STR2STR(wp->config->process_cpu_list)); +#endif zlog(ZLOG_NOTICE, "\tpm = %s", PM2STR(wp->config->pm)); zlog(ZLOG_NOTICE, "\tpm.max_children = %d", wp->config->pm_max_children); zlog(ZLOG_NOTICE, "\tpm.start_servers = %d", wp->config->pm_start_servers); diff --git a/sapi/fpm/fpm/fpm_conf.h b/sapi/fpm/fpm/fpm_conf.h index 5b354a9bdecef..88d6ff8a49d36 100644 --- a/sapi/fpm/fpm/fpm_conf.h +++ b/sapi/fpm/fpm/fpm_conf.h @@ -44,6 +44,9 @@ struct fpm_global_config_s { int systemd_watchdog; int systemd_interval; #endif +#if HAVE_FPM_CPUAFFINITY + char *process_cpu_list; +#endif }; extern struct fpm_global_config_s fpm_global_config; @@ -107,6 +110,9 @@ struct fpm_worker_pool_config_s { #ifdef SO_SETFIB int listen_setfib; #endif +#if HAVE_FPM_CPUAFFINITY + char *process_cpu_list; +#endif }; struct ini_value_parser_s { diff --git a/sapi/fpm/fpm/fpm_config.h b/sapi/fpm/fpm/fpm_config.h index a637326ed767a..0c8832e4d939b 100644 --- a/sapi/fpm/fpm/fpm_config.h +++ b/sapi/fpm/fpm/fpm_config.h @@ -85,3 +85,16 @@ #else # define HAVE_FPM_LQ 0 #endif + +#if defined(HAVE_SCHED_SETAFFINITY) || defined(HAVE_CPUSET_SETAFFINITY) +/* + * to expand to other platforms capable of this granularity (some BSD, solaris, ...). + * macOS is a specific case with an api working fine on intel architecture + * whereas on arm the api and semantic behind is different, since it is about + * Quality Of Service, i.e. binding to a group of cores for high performance + * vs cores for low energy consumption. + */ +# define HAVE_FPM_CPUAFFINITY 1 +#else +# define HAVE_FPM_CPUAFFINITY 0 +#endif diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c index b2f0e71d83314..5f0d61bab6499 100644 --- a/sapi/fpm/fpm/fpm_unix.c +++ b/sapi/fpm/fpm/fpm_unix.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,15 @@ #include #endif +#ifdef HAVE_SCHED_H +#include +#endif + +#ifdef HAVE_SYS_CPUSET_H +#include +typedef cpuset_t cpu_set_t; +#endif + #include "fpm.h" #include "fpm_conf.h" #include "fpm_cleanup.h" @@ -421,6 +431,101 @@ static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */ } /* }}} */ +#if HAVE_FPM_CPUAFFINITY +struct fpm_cpuaffinity_conf { + cpu_set_t cset; + long min; + long max; +}; + +static long fpm_cpumax(void) +{ + static long cpuid = LONG_MIN; + if (cpuid == LONG_MIN) { + cpu_set_t cset; +#if defined(HAVE_SCHED_SETAFFINITY) + if (sched_getaffinity(0, sizeof(cset), &cset) == 0) { +#elif defined(HAVE_CPUSET_SETAFFINITY) + if (cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(cset), &cset) == 0) { +#endif + cpuid = CPU_COUNT(&cset); + } else { + cpuid = -1; + } + } + + return cpuid; +} + +static void fpm_cpuaffinity_init(struct fpm_cpuaffinity_conf *c) +{ + CPU_ZERO(&c->cset); +} + +static void fpm_cpuaffinity_add(struct fpm_cpuaffinity_conf *c) +{ +#if defined(HAVE_FPM_CPUAFFINITY) + int i; + + for (i = c->min; i <= c->max; i ++) { + if (!CPU_ISSET(i, &c->cset)) { + CPU_SET(i, &c->cset); + } + } +#endif +} + +static int fpm_cpuaffinity_set(struct fpm_cpuaffinity_conf *c) +{ +#if defined(HAVE_SCHED_SETAFFINITY) + return sched_setaffinity(0, sizeof(c->cset), &c->cset); +#elif defined(HAVE_CPUSET_SETAFFINITY) + return cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(c->cset), &c->cset); +#endif +} + +static int fpm_setcpuaffinity(char *cpu_list) +{ + char *token, *buf; + struct fpm_cpuaffinity_conf fconf; + int r, cpumax; + + r = -1; + cpumax = fpm_cpumax(); + + fpm_cpuaffinity_init(&fconf); + token = php_strtok_r(cpu_list, ",", &buf); + + while (token) { + char *cpu_listsep; + + fconf.min = strtol(token, &cpu_listsep, 0); + if (errno || fconf.min < 0 || fconf.min > cpumax) { + return -1; + } + fconf.max = fconf.min; + if (*cpu_listsep == '-') { + if (strlen(cpu_listsep) > 1) { + char *err; + fconf.max = strtol(cpu_listsep + 1, &err, 0); + if (errno || *err != '\0' || fconf.max < fconf.min || fconf.max > cpumax) { + return -1; + } + } else { + return -1; + } + } + + fpm_cpuaffinity_add(&fconf); + + token = php_strtok_r(NULL, ";", &buf); + } + + r = fpm_cpuaffinity_set(&fconf); + return r; +} +#endif + int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ { int is_root = !geteuid(); @@ -445,6 +550,15 @@ int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ zlog(ZLOG_SYSERROR, "[pool %s] failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_core. setrlimit(RLIMIT_CORE, %d)", wp->config->name, wp->config->rlimit_core); } } +#if HAVE_FPM_CPUAFFINITY + if (wp->config->process_cpu_list) { + + if (0 > fpm_setcpuaffinity(wp->config->process_cpu_list)) { + zlog(ZLOG_SYSERROR, "[pool %s] failed to fpm_setcpuaffinity(%s)", wp->config->name, wp->config->process_cpu_list); + return -1; + } + } +#endif if (is_root && wp->config->chroot && *wp->config->chroot) { if (0 > chroot(wp->config->chroot)) { @@ -692,6 +806,15 @@ int fpm_unix_init_main(void) } } +#if HAVE_FPM_CPUAFFINITY + if (fpm_global_config.process_cpu_list) { + if (0 > fpm_setcpuaffinity(fpm_global_config.process_cpu_list)) { + zlog(ZLOG_SYSERROR, "failed to fpm_setcpuaffinity(%s)", fpm_global_config.process_cpu_list); + return -1; + } + } +#endif + fpm_globals.parent_pid = getpid(); for (wp = fpm_worker_all_pools; wp; wp = wp->next) { if (0 > fpm_unix_conf_wp(wp)) { diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in index f1b48adca08f2..c6f3cfee4f6c9 100644 --- a/sapi/fpm/php-fpm.conf.in +++ b/sapi/fpm/php-fpm.conf.in @@ -124,6 +124,18 @@ ; Default value: 10 ;systemd_interval = 10 +; Bind the master process to a cpu set. +; The value can be one cpu id, a range or a list thereof. +; +; Default Value: not set +; Valid syntaxes are: +; process.cpu_list = "cpu id" - to bind to a single core +; process.cpu_list = "min cpu id-max cpu id" - to bind to a core range + from minimum cpu id to max +; process.cpu_list = "[min cpu id-max cpu id],[min cpu id-max cpu id],..." + - to bind to multiple ranges + separated by a comma + ;;;;;;;;;;;;;;;;;;;; ; Pool Definitions ; ;;;;;;;;;;;;;;;;;;;; diff --git a/sapi/fpm/tests/cpuaffinity-fail.phpt b/sapi/fpm/tests/cpuaffinity-fail.phpt new file mode 100644 index 0000000000000..d9bb4a6fffa06 --- /dev/null +++ b/sapi/fpm/tests/cpuaffinity-fail.phpt @@ -0,0 +1,43 @@ +--TEST-- +FPM: cpu affinity test +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogError("failed to fpm_setcpuaffinity\(\d+\): Inappropriate ioctl for device \(\d+\)"); +$tester->expectLogError("FPM initialization failed"); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/cpuaffinity-range.phpt b/sapi/fpm/tests/cpuaffinity-range.phpt new file mode 100644 index 0000000000000..89c1b2db6d39c --- /dev/null +++ b/sapi/fpm/tests/cpuaffinity-range.phpt @@ -0,0 +1,55 @@ +--TEST-- +FPM: cpu affinity test, range +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/cpuaffinity.phpt b/sapi/fpm/tests/cpuaffinity.phpt new file mode 100644 index 0000000000000..620d26f70affc --- /dev/null +++ b/sapi/fpm/tests/cpuaffinity.phpt @@ -0,0 +1,55 @@ +--TEST-- +FPM: cpu affinity test +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/www.conf.in b/sapi/fpm/www.conf.in index 69df3e6630047..90809b8c1c574 100644 --- a/sapi/fpm/www.conf.in +++ b/sapi/fpm/www.conf.in @@ -80,6 +80,18 @@ listen = 127.0.0.1:9000 ; Default Value: no set ; process.priority = -19 +; Bind the pool processes to a cpu set. +; The value can be one cpu id, a range or a list thereof. +; +; Default Value: inherits master's cpu affinity +; Valid syntaxes are: +; process.cpu_list = "cpu id" - to bind to a single core +; process.cpu_list = "min cpu id-max cpu id" - to bind to a core range + from minimum cpu id to max +; process.cpu_list = "[min cpu id-max cpu id],[min cpu id-max cpu id],..." + - to bind to multiple ranges + separated by a comma + ; Set the process dumpable flag (PR_SET_DUMPABLE prctl for Linux or ; PROC_TRACE_CTL procctl for FreeBSD) even if the process user ; or group is different than the master process user. It allows to create process From ab0bc745c4d57ed56316f9dc1d51a1f9c2ad6f97 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 6 Feb 2023 16:30:54 +0000 Subject: [PATCH 2/4] build --- sapi/fpm/fpm/fpm_unix.c | 47 +++++++++++---------------- sapi/fpm/php-fpm.conf.in | 11 ++++--- sapi/fpm/tests/cpuaffinity-range.phpt | 11 ++----- sapi/fpm/tests/cpuaffinity.phpt | 9 +---- sapi/fpm/tests/tester.inc | 13 ++++++++ sapi/fpm/www.conf.in | 13 ++++---- 6 files changed, 48 insertions(+), 56 deletions(-) diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c index 5f0d61bab6499..1a4590789fc44 100644 --- a/sapi/fpm/fpm/fpm_unix.c +++ b/sapi/fpm/fpm/fpm_unix.c @@ -432,12 +432,6 @@ static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */ /* }}} */ #if HAVE_FPM_CPUAFFINITY -struct fpm_cpuaffinity_conf { - cpu_set_t cset; - long min; - long max; -}; - static long fpm_cpumax(void) { static long cpuid = LONG_MIN; @@ -457,58 +451,56 @@ static long fpm_cpumax(void) return cpuid; } -static void fpm_cpuaffinity_init(struct fpm_cpuaffinity_conf *c) +static void fpm_cpuaffinity_init(cpu_set_t *c) { - CPU_ZERO(&c->cset); + CPU_ZERO(c); } -static void fpm_cpuaffinity_add(struct fpm_cpuaffinity_conf *c) +static void fpm_cpuaffinity_add(cpu_set_t *c, int min, int max) { -#if defined(HAVE_FPM_CPUAFFINITY) int i; - for (i = c->min; i <= c->max; i ++) { - if (!CPU_ISSET(i, &c->cset)) { - CPU_SET(i, &c->cset); + for (i = min; i <= max; i ++) { + if (!CPU_ISSET(i, c)) { + CPU_SET(i, c); } } -#endif } -static int fpm_cpuaffinity_set(struct fpm_cpuaffinity_conf *c) +static int fpm_cpuaffinity_set(cpu_set_t *c) { #if defined(HAVE_SCHED_SETAFFINITY) - return sched_setaffinity(0, sizeof(c->cset), &c->cset); + return sched_setaffinity(0, sizeof(c), c); #elif defined(HAVE_CPUSET_SETAFFINITY) - return cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(c->cset), &c->cset); + return cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(c), c); #endif } static int fpm_setcpuaffinity(char *cpu_list) { char *token, *buf; - struct fpm_cpuaffinity_conf fconf; - int r, cpumax; + cpu_set_t c; + int r, cpumax, min, max; r = -1; cpumax = fpm_cpumax(); - fpm_cpuaffinity_init(&fconf); + fpm_cpuaffinity_init(&c); token = php_strtok_r(cpu_list, ",", &buf); while (token) { char *cpu_listsep; - fconf.min = strtol(token, &cpu_listsep, 0); - if (errno || fconf.min < 0 || fconf.min > cpumax) { + min = strtol(token, &cpu_listsep, 0); + if (errno || min < 0 || min > cpumax) { return -1; } - fconf.max = fconf.min; + max = min; if (*cpu_listsep == '-') { if (strlen(cpu_listsep) > 1) { char *err; - fconf.max = strtol(cpu_listsep + 1, &err, 0); - if (errno || *err != '\0' || fconf.max < fconf.min || fconf.max > cpumax) { + max = strtol(cpu_listsep + 1, &err, 0); + if (errno || *err != '\0' || max < min || max > cpumax) { return -1; } } else { @@ -516,12 +508,12 @@ static int fpm_setcpuaffinity(char *cpu_list) } } - fpm_cpuaffinity_add(&fconf); + fpm_cpuaffinity_add(&c, min, max); token = php_strtok_r(NULL, ";", &buf); } - r = fpm_cpuaffinity_set(&fconf); + r = fpm_cpuaffinity_set(&c); return r; } #endif @@ -552,7 +544,6 @@ int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ } #if HAVE_FPM_CPUAFFINITY if (wp->config->process_cpu_list) { - if (0 > fpm_setcpuaffinity(wp->config->process_cpu_list)) { zlog(ZLOG_SYSERROR, "[pool %s] failed to fpm_setcpuaffinity(%s)", wp->config->name, wp->config->process_cpu_list); return -1; diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in index c6f3cfee4f6c9..f9522e7d39f22 100644 --- a/sapi/fpm/php-fpm.conf.in +++ b/sapi/fpm/php-fpm.conf.in @@ -129,11 +129,12 @@ ; ; Default Value: not set ; Valid syntaxes are: -; process.cpu_list = "cpu id" - to bind to a single core -; process.cpu_list = "min cpu id-max cpu id" - to bind to a core range - from minimum cpu id to max -; process.cpu_list = "[min cpu id-max cpu id],[min cpu id-max cpu id],..." - - to bind to multiple ranges +; process.cpu_list = "cpu id" - bind master process to cpu id +; process.cpu_list = "[min cpu id]-[max cpu id]" + - bind master process from min cpu id + to max cpu id +; process.cpu_list = "[[min cpu id]-[max cpu id],[min cpu id-max cpu id],...]" + - bind master process to cpu id ranges separated by a comma ;;;;;;;;;;;;;;;;;;;; diff --git a/sapi/fpm/tests/cpuaffinity-range.phpt b/sapi/fpm/tests/cpuaffinity-range.phpt index 89c1b2db6d39c..e08db71cc5bc6 100644 --- a/sapi/fpm/tests/cpuaffinity-range.phpt +++ b/sapi/fpm/tests/cpuaffinity-range.phpt @@ -7,14 +7,7 @@ if (!str_contains(PHP_OS, 'Linux') && !str_contains(PHP_OS, 'FreeBSD')) { die('skipped supported only on Linux and FreeBSD'); } -if (str_contains(PHP_OS, 'Linux')) { - $cmd = 'nproc'; -} else { - $cmd = 'sysctl hw.ncpus'; -} - -$nproc = intval(exec($cmd)); -if ($nproc < 4) { +if (FPM\Tester::getCores() < 4) { die('skipped supported only on 4 cores or more environments'); } ?> @@ -34,7 +27,7 @@ pm.max_children = 1 pm.start_servers = 1 pm.min_spare_servers = 1 pm.max_spare_servers = 1 -process.cpu_list = 0-1,2-3 +process.cpu_list = 0,2-3 EOT; $tester = new FPM\Tester($cfg); diff --git a/sapi/fpm/tests/cpuaffinity.phpt b/sapi/fpm/tests/cpuaffinity.phpt index 620d26f70affc..ca17e885a700c 100644 --- a/sapi/fpm/tests/cpuaffinity.phpt +++ b/sapi/fpm/tests/cpuaffinity.phpt @@ -7,14 +7,7 @@ if (!str_contains(PHP_OS, 'Linux') && !str_contains(PHP_OS, 'FreeBSD')) { die('skipped supported only on Linux and FreeBSD'); } -if (str_contains(PHP_OS, 'Linux')) { - $cmd = 'nproc'; -} else { - $cmd = 'sysctl hw.ncpus'; -} - -$nproc = intval(exec($cmd)); -if ($nproc < 2) { +if (FPM\Tester::getCores() < 4) { die('skipped supported only on multicores environments'); } ?> diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc index f486309085e6d..528b26c10d558 100644 --- a/sapi/fpm/tests/tester.inc +++ b/sapi/fpm/tests/tester.inc @@ -203,6 +203,19 @@ class Tester } } + static public function getCores() + { + if (str_contains(PHP_OS, 'Linux')) { + $cmd = 'nproc'; + } else if (str_contains(PHP_OS, 'FreeBSD') || str_contains(PHP_OS, 'Darwin')) { + $cmd = 'sysctl hw.ncpus'; + } else { + return 0; + } + + return intval(exec($cmd)); + } + /** * @param int $backTraceIndex * diff --git a/sapi/fpm/www.conf.in b/sapi/fpm/www.conf.in index 90809b8c1c574..df125c4e5f38f 100644 --- a/sapi/fpm/www.conf.in +++ b/sapi/fpm/www.conf.in @@ -85,12 +85,13 @@ listen = 127.0.0.1:9000 ; ; Default Value: inherits master's cpu affinity ; Valid syntaxes are: -; process.cpu_list = "cpu id" - to bind to a single core -; process.cpu_list = "min cpu id-max cpu id" - to bind to a core range - from minimum cpu id to max -; process.cpu_list = "[min cpu id-max cpu id],[min cpu id-max cpu id],..." - - to bind to multiple ranges - separated by a comma +; process.cpu_list = "cpu id" - bind pool process to cpu id +; process.cpu_list = "[min cpu id]-[max cpu id]" + - bind pool process from + min cpu id to max cpu id +; process.cpu_list = "[[min cpu id]-[max cpu id],[min cpu id-max cpu id],...]" + - bind pool process to cpu id + ranges separated by a comma ; Set the process dumpable flag (PR_SET_DUMPABLE prctl for Linux or ; PROC_TRACE_CTL procctl for FreeBSD) even if the process user From 02e261713f268aaca89139b857245cc6ce2019e7 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 21 May 2023 13:00:24 +0100 Subject: [PATCH 3/4] changes from feedback --- sapi/fpm/fpm/fpm_unix.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c index 1a4590789fc44..9734020e44744 100644 --- a/sapi/fpm/fpm/fpm_unix.c +++ b/sapi/fpm/fpm/fpm_unix.c @@ -37,11 +37,9 @@ #include #endif -#ifdef HAVE_SCHED_H +#if defined(HAVE_SCHED_H) #include -#endif - -#ifdef HAVE_SYS_CPUSET_H +#elif defined(HAVE_SYS_CPUSET_H) #include typedef cpuset_t cpu_set_t; #endif @@ -478,7 +476,7 @@ static int fpm_cpuaffinity_set(cpu_set_t *c) static int fpm_setcpuaffinity(char *cpu_list) { - char *token, *buf; + char *token, *buf, *ptr; cpu_set_t c; int r, cpumax, min, max; @@ -486,13 +484,19 @@ static int fpm_setcpuaffinity(char *cpu_list) cpumax = fpm_cpumax(); fpm_cpuaffinity_init(&c); - token = php_strtok_r(cpu_list, ",", &buf); + ptr = estrdup(cpu_list); + token = php_strtok_r(ptr, ",", &buf); while (token) { char *cpu_listsep; + if (!isdigit(*token)) { + return -1; + } + min = strtol(token, &cpu_listsep, 0); - if (errno || min < 0 || min > cpumax) { + if (errno || (*cpu_listsep != '\0' && *cpu_listsep != '-') || min < 0 || min > cpumax) { + efree(ptr); return -1; } max = min; @@ -501,19 +505,21 @@ static int fpm_setcpuaffinity(char *cpu_list) char *err; max = strtol(cpu_listsep + 1, &err, 0); if (errno || *err != '\0' || max < min || max > cpumax) { + efree(ptr); return -1; } } else { + efree(ptr); return -1; } } fpm_cpuaffinity_add(&c, min, max); - - token = php_strtok_r(NULL, ";", &buf); + token = php_strtok_r(NULL, ",", &buf); } r = fpm_cpuaffinity_set(&c); + efree(ptr); return r; } #endif From 6540f9467560626746356c4f747062f8cf3ca50e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 21 May 2023 13:00:39 +0100 Subject: [PATCH 4/4] adding new tests --- sapi/fpm/config.m4 | 2 ++ sapi/fpm/fpm/fpm_unix.c | 4 +-- sapi/fpm/tests/cpuaffinity-fail2.phpt | 42 +++++++++++++++++++++++ sapi/fpm/tests/cpuaffinity-fail3.phpt | 42 +++++++++++++++++++++++ sapi/fpm/tests/cpuaffinity-multi.phpt | 48 +++++++++++++++++++++++++++ 5 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 sapi/fpm/tests/cpuaffinity-fail2.phpt create mode 100644 sapi/fpm/tests/cpuaffinity-fail3.phpt create mode 100644 sapi/fpm/tests/cpuaffinity-multi.phpt diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4 index 4d4952eee86e7..3fe25ca163021 100644 --- a/sapi/fpm/config.m4 +++ b/sapi/fpm/config.m4 @@ -356,6 +356,8 @@ if test "$PHP_FPM" != "no"; then AC_CHECK_HEADER([priv.h], [AC_CHECK_FUNCS([setpflags])]) AC_CHECK_HEADER([sys/times.h], [AC_CHECK_FUNCS([times])]) AC_CHECK_HEADER([port.h], [AC_CHECK_FUNCS([port_create])]) + AC_CHECK_HEADER([sched.h], [AC_CHECK_FUNCS([sched_setaffinity])]) + AC_CHECK_HEADER([sys/cpuset.h], [AC_CHECK_FUNCS([cpuset_setaffinity])]) PHP_ARG_WITH([fpm-user],, [AS_HELP_STRING([[--with-fpm-user[=USER]]], diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c index 9734020e44744..1d564c4cdc62a 100644 --- a/sapi/fpm/fpm/fpm_unix.c +++ b/sapi/fpm/fpm/fpm_unix.c @@ -37,9 +37,9 @@ #include #endif -#if defined(HAVE_SCHED_H) +#if defined(HAVE_SCHED_SETAFFINITY) #include -#elif defined(HAVE_SYS_CPUSET_H) +#elif defined(HAVE_CPUSETAFFINITY) #include typedef cpuset_t cpu_set_t; #endif diff --git a/sapi/fpm/tests/cpuaffinity-fail2.phpt b/sapi/fpm/tests/cpuaffinity-fail2.phpt new file mode 100644 index 0000000000000..5b29cc5094c54 --- /dev/null +++ b/sapi/fpm/tests/cpuaffinity-fail2.phpt @@ -0,0 +1,42 @@ +--TEST-- +FPM: cpu affinity test +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogError("FPM initialization failed"); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/cpuaffinity-fail3.phpt b/sapi/fpm/tests/cpuaffinity-fail3.phpt new file mode 100644 index 0000000000000..10c6fb5fcc682 --- /dev/null +++ b/sapi/fpm/tests/cpuaffinity-fail3.phpt @@ -0,0 +1,42 @@ +--TEST-- +FPM: cpu affinity test +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogError("FPM initialization failed"); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/cpuaffinity-multi.phpt b/sapi/fpm/tests/cpuaffinity-multi.phpt new file mode 100644 index 0000000000000..3b8031940f6dd --- /dev/null +++ b/sapi/fpm/tests/cpuaffinity-multi.phpt @@ -0,0 +1,48 @@ +--TEST-- +FPM: cpu affinity test +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- +