Skip to content

Commit 3ca57f6

Browse files
committed
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 idea is to do it "natively" especially those above mostly focus on linux whereas more platforms are cpu affinity capable. For now at least, focusing on simple cases, one core id or a range.
1 parent 6422cf6 commit 3ca57f6

File tree

6 files changed

+129
-0
lines changed

6 files changed

+129
-0
lines changed

configure.ac

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ pty.h \
403403
pwd.h \
404404
resolv.h \
405405
strings.h \
406+
sched.h \
406407
syslog.h \
407408
sysexits.h \
408409
sys/ioctl.h \
@@ -605,6 +606,7 @@ poll \
605606
pthread_jit_write_protect_np \
606607
putenv \
607608
scandir \
609+
sched_setaffinity \
608610
setitimer \
609611
setenv \
610612
shutdown \

sapi/fpm/fpm/fpm_conf.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ static struct ini_value_parser_s ini_fpm_global_options[] = {
9898
{ "process_control_timeout", &fpm_conf_set_time, GO(process_control_timeout) },
9999
{ "process.max", &fpm_conf_set_integer, GO(process_max) },
100100
{ "process.priority", &fpm_conf_set_integer, GO(process_priority) },
101+
#if HAVE_FPM_CPUAFFINITY
102+
{ "process.cpumask", &fpm_conf_set_string, GO(process_cpumask) },
103+
#endif
101104
{ "daemonize", &fpm_conf_set_boolean, GO(daemonize) },
102105
{ "rlimit_files", &fpm_conf_set_integer, GO(rlimit_files) },
103106
{ "rlimit_core", &fpm_conf_set_rlimit_core, GO(rlimit_core) },
@@ -130,6 +133,9 @@ static struct ini_value_parser_s ini_fpm_pool_options[] = {
130133
#endif
131134
{ "process.priority", &fpm_conf_set_integer, WPO(process_priority) },
132135
{ "process.dumpable", &fpm_conf_set_boolean, WPO(process_dumpable) },
136+
#if HAVE_FPM_CPUAFFINITY
137+
{ "process.cpumask", &fpm_conf_set_string, WPO(process_cpumask) },
138+
#endif
133139
{ "pm", &fpm_conf_set_pm, WPO(pm) },
134140
{ "pm.max_children", &fpm_conf_set_integer, WPO(pm_max_children) },
135141
{ "pm.start_servers", &fpm_conf_set_integer, WPO(pm_start_servers) },
@@ -621,6 +627,9 @@ static void *fpm_worker_pool_config_alloc(void)
621627
wp->config->pm_process_idle_timeout = 10; /* 10s by default */
622628
wp->config->process_priority = 64; /* 64 means unset */
623629
wp->config->process_dumpable = 0;
630+
#if HAVE_FPM_CPUAFFINITY
631+
wp->config->process_cpumask = NULL;
632+
#endif
624633
wp->config->clear_env = 1;
625634
wp->config->decorate_workers_output = 1;
626635
#ifdef SO_SETFIB

sapi/fpm/fpm/fpm_conf.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ struct fpm_global_config_s {
4444
int systemd_watchdog;
4545
int systemd_interval;
4646
#endif
47+
#if HAVE_FPM_CPUAFFINITY
48+
char *process_cpumask;
49+
#endif
4750
};
4851

4952
extern struct fpm_global_config_s fpm_global_config;
@@ -107,6 +110,9 @@ struct fpm_worker_pool_config_s {
107110
#ifdef SO_SETFIB
108111
int listen_setfib;
109112
#endif
113+
#if HAVE_FPM_CPUAFFINITY
114+
char *process_cpumask;
115+
#endif
110116
};
111117

112118
struct ini_value_parser_s {

sapi/fpm/fpm/fpm_config.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,16 @@
7575
#else
7676
# define HAVE_FPM_LQ 0
7777
#endif
78+
79+
#if defined(HAVE_SCHED_SETAFFINITY)
80+
/*
81+
* to expand to other platforms capable of this granularity (some BSD, solaris, ...).
82+
* macOS is a specific case with an api working fine on intel architecture
83+
* whereas on arm the api and semantic behind is different, since it is about
84+
* Quality Of Service, i.e. binding to a group of cores for high performance
85+
* vs cores for low energy consumption.
86+
*/
87+
# define HAVE_FPM_CPUAFFINITY 1
88+
#else
89+
# define HAVE_FPM_CPUAFFINITY 0
90+
#endif

sapi/fpm/fpm/fpm_unix.c

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
#include <selinux/selinux.h>
3636
#endif
3737

38+
#ifdef HAVE_SCHED_H
39+
#include <sched.h>
40+
#endif
41+
3842
#include "fpm.h"
3943
#include "fpm_conf.h"
4044
#include "fpm_cleanup.h"
@@ -349,6 +353,62 @@ static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
349353
}
350354
/* }}} */
351355

356+
#if HAVE_FPM_CPUAFFINITY
357+
static long fpm_cpumax(void)
358+
{
359+
static long cpuid = LONG_MIN;
360+
if (cpuid == LONG_MIN) {
361+
cpuid = sysconf(_SC_NPROCESSORS_ONLN);
362+
}
363+
364+
return cpuid;
365+
}
366+
367+
static int fpm_cpusrange(char *cpumask, int *min, int *max) {
368+
char *cpumasksep;
369+
long cpumax;
370+
cpumax = fpm_cpumax();
371+
if (0 >= cpumax) {
372+
return -1;
373+
}
374+
*min = strtol(cpumask, &cpumasksep, 0);
375+
if (errno || *min < 0 || *min >= cpumax) {
376+
return -1;
377+
}
378+
379+
*max = *min;
380+
if (*cpumasksep == '-') {
381+
if (strlen(cpumasksep) > 1) {
382+
char *err;
383+
*max = strtol(cpumasksep + 1, &err, 0);
384+
if (errno || *err != '\0' || *max <= *min || *max >= cpumax) {
385+
return -1;
386+
}
387+
} else {
388+
return -1;
389+
}
390+
}
391+
return 0;
392+
}
393+
394+
static int fpm_setcpuaffinity(int min, int max)
395+
{
396+
#ifdef HAVE_SCHED_SETAFFINITY
397+
cpu_set_t cset;
398+
int i;
399+
400+
CPU_ZERO(&cset);
401+
for (i = min; i <= max; i ++) {
402+
if (!CPU_ISSET(i, &cset)) {
403+
CPU_SET(i, &cset);
404+
}
405+
}
406+
407+
return sched_setaffinity(0, sizeof(cset), &cset);
408+
#endif
409+
}
410+
#endif
411+
352412
int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
353413
{
354414
int is_root = !geteuid();
@@ -402,6 +462,20 @@ int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
402462
}
403463
}
404464

465+
#if HAVE_FPM_CPUAFFINITY
466+
if (wp->config->process_cpumask) {
467+
int fcpu, lcpu;
468+
469+
if (0 > fpm_cpusrange(wp->config->process_cpumask, &fcpu, &lcpu)) {
470+
zlog(ZLOG_SYSERROR, "[pool %s] failed to fpm_cpusrange(%s)", wp->config->name, wp->config->process_cpumask);
471+
return -1;
472+
}
473+
if (0 > fpm_setcpuaffinity(fcpu, lcpu)) {
474+
zlog(ZLOG_SYSERROR, "[pool %s] failed to fpm_setcpuaffinity(%s)", wp->config->name, wp->config->process_cpumask);
475+
return -1;
476+
}
477+
}
478+
#endif
405479
if (wp->set_gid) {
406480
if (0 > setgid(wp->set_gid)) {
407481
zlog(ZLOG_SYSERROR, "[pool %s] failed to setgid(%d)", wp->config->name, wp->set_gid);
@@ -619,6 +693,25 @@ int fpm_unix_init_main(void)
619693
}
620694
}
621695

696+
#if HAVE_FPM_CPUAFFINITY
697+
if (fpm_global_config.process_cpumask) {
698+
if (is_root) {
699+
int fcpu, lcpu;
700+
701+
if (0 > fpm_cpusrange(fpm_global_config.process_cpumask, &fcpu, &lcpu)) {
702+
zlog(ZLOG_SYSERROR, "failed to fpm_cpurange(%s)", fpm_global_config.process_cpumask);
703+
return -1;
704+
}
705+
if (0 > fpm_setcpuaffinity(fcpu, lcpu)) {
706+
zlog(ZLOG_SYSERROR, "failed to fpm_setcpuaffinity(%s)", fpm_global_config.process_cpumask);
707+
return -1;
708+
}
709+
} else {
710+
zlog(ZLOG_NOTICE, "'process.cpumask' directive is ignored when FPM is not running as root");
711+
}
712+
}
713+
#endif
714+
622715
fpm_globals.parent_pid = getpid();
623716
for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
624717
if (0 > fpm_unix_conf_wp(wp)) {

sapi/fpm/php-fpm.conf.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@
125125
; Default value: 10
126126
;systemd_interval = 10
127127

128+
; Bind the master process to a cpu set.
129+
; The value can be one cpu id or a range.
130+
;
131+
; Default Value: not set
132+
; process.cpumask = 0-3
133+
128134
;;;;;;;;;;;;;;;;;;;;
129135
; Pool Definitions ;
130136
;;;;;;;;;;;;;;;;;;;;

0 commit comments

Comments
 (0)