Skip to content

Commit ba812cc

Browse files
committed
ext/pcntl: cpu affinity improvements, targetting linux.
recent kernels are capable of supporting over 1024 number of cpus thus we no longer use static bitsets and move towards dynamic ones.
1 parent de5a582 commit ba812cc

1 file changed

Lines changed: 98 additions & 30 deletions

File tree

ext/pcntl/pcntl.c

Lines changed: 98 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -57,53 +57,76 @@
5757
#include <errno.h>
5858
#if defined(HAVE_UNSHARE) || defined(HAVE_SCHED_SETAFFINITY) || defined(HAVE_SCHED_GETCPU)
5959
#include <sched.h>
60+
#if defined(__linux__) && defined(CPU_ALLOC)
61+
#define PCNTL_CPUSET_DYNAMIC 1
62+
typedef cpu_set_t *PCNTL_CPUSET_T;
63+
#define PCNTL_CPUSET(mask) (mask)
64+
#define PCNTL_CPUSET_SIZE(mask, sz) (sz)
65+
#define PCNTL_CPU_ISSET(i, mask, sz) CPU_ISSET_S(i, sz, mask)
66+
#define PCNTL_CPU_SET(i, mask, sz) CPU_SET_S(i, sz, mask)
67+
#define PCNTL_CPU_ALLOC(mask, sz, maxcpus) \
68+
do { \
69+
(sz) = MAX(CPU_ALLOC_SIZE(maxcpus), sizeof(cpu_set_t)); \
70+
(mask) = ecalloc(1, (sz)); \
71+
} while (0)
72+
#define PCNTL_CPU_DESTROY(mask) efree(mask)
73+
#else
6074
#if defined(__FreeBSD__)
6175
#include <sys/types.h>
6276
#include <sys/cpuset.h>
6377
typedef cpuset_t cpu_set_t;
6478
#endif
65-
#define PCNTL_CPUSET(mask) &mask
66-
#define PCNTL_CPUSET_SIZE(mask) sizeof(mask)
67-
#define PCNTL_CPU_ISSET(i, mask) CPU_ISSET(i, &mask)
68-
#define PCNTL_CPU_SET(i, mask) CPU_SET(i, &mask)
69-
#define PCNTL_CPU_ZERO(mask) CPU_ZERO(&mask)
79+
typedef cpu_set_t PCNTL_CPUSET_T;
80+
#define PCNTL_CPUSET(mask) (&(mask))
81+
#define PCNTL_CPUSET_SIZE(mask, sz) (sz)
82+
#define PCNTL_CPU_ISSET(i, mask, sz) CPU_ISSET(i, &(mask))
83+
#define PCNTL_CPU_SET(i, mask, sz) CPU_SET(i, &(mask))
84+
#define PCNTL_CPU_ALLOC(mask, sz, maxcpus) \
85+
do { \
86+
CPU_ZERO(&(mask)); \
87+
(sz) = sizeof(mask); \
88+
} while (0)
7089
#define PCNTL_CPU_DESTROY(mask) ((void)0)
90+
#endif
7191
#elif defined(__NetBSD__)
7292
#include <sys/syscall.h>
7393
#include <sched.h>
74-
typedef cpuset_t *cpu_set_t;
94+
typedef cpuset_t *PCNTL_CPUSET_T;
7595
#define sched_getaffinity(p, c, m) syscall(SYS__sched_getaffinity, p, 0, c, m)
7696
#define sched_setaffinity(p, c, m) syscall(SYS__sched_setaffinity, p, 0, c, m)
77-
#define PCNTL_CPUSET(mask) mask
78-
#define PCNTL_CPUSET_SIZE(mask) cpuset_size(mask)
79-
#define PCNTL_CPU_ISSET(i, mask) cpuset_isset((cpuid_t)i, mask)
80-
#define PCNTL_CPU_SET(i, mask) cpuset_set((cpuid_t)i, mask)
81-
#define PCNTL_CPU_ZERO(mask) \
97+
#define PCNTL_CPUSET(mask) (mask)
98+
#define PCNTL_CPUSET_SIZE(mask, sz) (sz)
99+
#define PCNTL_CPU_ISSET(i, mask, sz) cpuset_isset((cpuid_t)i, mask)
100+
#define PCNTL_CPU_SET(i, mask, sz) cpuset_set((cpuid_t)i, mask)
101+
#define PCNTL_CPU_ALLOC(mask, sz, maxcpus) \
82102
do { \
83103
mask = cpuset_create(); \
84104
if (UNEXPECTED(!mask)) { \
85105
php_error_docref(NULL, E_WARNING, "cpuset_create: Insufficient memory"); \
86106
RETURN_FALSE; \
87107
} \
88108
cpuset_zero(mask); \
109+
(sz) = cpuset_size(mask); \
89110
} while(0)
90111
#define PCNTL_CPU_DESTROY(mask) cpuset_destroy(mask)
91112
#define HAVE_SCHED_SETAFFINITY 1
92113
#elif defined(HAVE_PSET_BIND)
93114
#include <sys/pset.h>
94-
typedef psetid_t cpu_set_t;
95-
#define sched_getaffinity(p, c, m) pset_bind(PS_QUERY, P_PID, p, &m)
96-
#define sched_setaffinity(p, c, m) pset_bind(m, P_PID, (p <= 0 ? getpid() : p), NULL)
115+
typedef psetid_t PCNTL_CPUSET_T;
116+
#define sched_getaffinity(p, c, m) ((void)(c), pset_bind(PS_QUERY, P_PID, p, &m))
117+
#define sched_setaffinity(p, c, m) ((void)(c), pset_bind(m, P_PID, (p <= 0 ? getpid() : p), NULL))
97118
#define PCNTL_CPUSET(mask) mask
98-
#define PCNTL_CPU_ISSET(i, mask) (pset_assign(PS_QUERY, (processorid_t)i, &query) == 0 && query == mask)
99-
#define PCNTL_CPU_SET(i, mask) pset_assign(mask, (processorid_t)i, NULL)
100-
#define PCNTL_CPU_ZERO(mask) \
119+
#define PCNTL_CPUSET_SIZE(mask, sz) (sz)
120+
#define PCNTL_CPU_ISSET(i, mask, sz) (pset_assign(PS_QUERY, (processorid_t)i, &query) == 0 && query == mask)
121+
#define PCNTL_CPU_SET(i, mask, sz) pset_assign(mask, (processorid_t)i, NULL)
122+
#define PCNTL_CPU_ALLOC(mask, sz, maxcpus) \
101123
psetid_t query; \
102124
do { \
103125
if (UNEXPECTED(pset_create(&mask) != 0)) { \
104126
php_error_docref(NULL, E_WARNING, "pset_create: %s", strerror(errno)); \
105127
RETURN_FALSE; \
106128
} \
129+
(sz) = sizeof(mask); \
107130
} while (0)
108131
#define PCNTL_CPU_DESTROY(mask) \
109132
do { \
@@ -1678,11 +1701,46 @@ PHP_FUNCTION(pcntl_setns)
16781701
#endif
16791702

16801703
#ifdef HAVE_SCHED_SETAFFINITY
1704+
static zend_long pcntl_cpu_count(void)
1705+
{
1706+
errno = 0;
1707+
zend_long maxcpus = sysconf(_SC_NPROCESSORS_CONF);
1708+
if (UNEXPECTED(maxcpus < 0)) {
1709+
PCNTL_G(last_error) = errno ? errno : EINVAL;
1710+
php_error_docref(NULL, E_WARNING, "Cannot determine the number of cpus");
1711+
}
1712+
1713+
return maxcpus;
1714+
}
1715+
1716+
#ifdef PCNTL_CPUSET_DYNAMIC
1717+
#define PCNTL_CPUSET_MAX_SIZE (1024 * 1024)
1718+
1719+
static bool pcntl_getaffinity(pid_t pid, PCNTL_CPUSET_T *mask, size_t *sz)
1720+
{
1721+
while (sched_getaffinity(pid, *sz, *mask) != 0) {
1722+
if (errno != EINVAL || *sz >= PCNTL_CPUSET_MAX_SIZE) {
1723+
return false;
1724+
}
1725+
1726+
efree(*mask);
1727+
*sz *= 2;
1728+
*mask = ecalloc(1, *sz);
1729+
}
1730+
1731+
return true;
1732+
}
1733+
#else
1734+
# define pcntl_getaffinity(pid, mask, sz) \
1735+
(sched_getaffinity(pid, PCNTL_CPUSET_SIZE(*(mask), *(sz)), PCNTL_CPUSET(*(mask))) == 0)
1736+
#endif
1737+
16811738
PHP_FUNCTION(pcntl_getcpuaffinity)
16821739
{
16831740
zend_long pid;
16841741
bool pid_is_null = 1;
1685-
cpu_set_t mask;
1742+
PCNTL_CPUSET_T mask;
1743+
size_t sz;
16861744

16871745
ZEND_PARSE_PARAMETERS_START(0, 1)
16881746
Z_PARAM_OPTIONAL
@@ -1691,10 +1749,15 @@ PHP_FUNCTION(pcntl_getcpuaffinity)
16911749

16921750
// 0 == getpid in this context, we're just saving a syscall
16931751
pid = pid_is_null ? 0 : pid;
1752+
zend_long maxcpus = pcntl_cpu_count();
16941753

1695-
PCNTL_CPU_ZERO(mask);
1754+
if (UNEXPECTED(maxcpus < 0)) {
1755+
RETURN_FALSE;
1756+
}
16961757

1697-
if (sched_getaffinity(pid, PCNTL_CPUSET_SIZE(mask), PCNTL_CPUSET(mask)) != 0) {
1758+
PCNTL_CPU_ALLOC(mask, sz, maxcpus);
1759+
1760+
if (!pcntl_getaffinity(pid, &mask, &sz)) {
16981761
PCNTL_CPU_DESTROY(mask);
16991762
PCNTL_G(last_error) = errno;
17001763
switch (errno) {
@@ -1714,12 +1777,11 @@ PHP_FUNCTION(pcntl_getcpuaffinity)
17141777
RETURN_FALSE;
17151778
}
17161779

1717-
zend_ulong maxcpus = (zend_ulong)sysconf(_SC_NPROCESSORS_CONF);
17181780
array_init(return_value);
17191781
zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
17201782

1721-
for (zend_ulong i = 0; i < maxcpus; i ++) {
1722-
if (PCNTL_CPU_ISSET(i, mask)) {
1783+
for (zend_long i = 0; i < maxcpus; i ++) {
1784+
if (PCNTL_CPU_ISSET(i, mask, sz)) {
17231785
add_next_index_long(return_value, i);
17241786
}
17251787
}
@@ -1730,8 +1792,9 @@ PHP_FUNCTION(pcntl_setcpuaffinity)
17301792
{
17311793
zend_long pid;
17321794
bool pid_is_null = 1;
1733-
cpu_set_t mask;
1795+
PCNTL_CPUSET_T mask;
17341796
zval *hmask = NULL, *ncpu;
1797+
size_t sz;
17351798

17361799
ZEND_PARSE_PARAMETERS_START(0, 2)
17371800
Z_PARAM_OPTIONAL
@@ -1747,8 +1810,13 @@ PHP_FUNCTION(pcntl_setcpuaffinity)
17471810

17481811
// 0 == getpid in this context, we're just saving a syscall
17491812
pid = pid_is_null ? 0 : pid;
1750-
zend_long maxcpus = sysconf(_SC_NPROCESSORS_CONF);
1751-
PCNTL_CPU_ZERO(mask);
1813+
zend_long maxcpus = pcntl_cpu_count();
1814+
1815+
if (UNEXPECTED(maxcpus < 0)) {
1816+
RETURN_FALSE;
1817+
}
1818+
1819+
PCNTL_CPU_ALLOC(mask, sz, maxcpus);
17521820

17531821
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(hmask), ncpu) {
17541822
ZVAL_DEREF(ncpu);
@@ -1773,17 +1841,17 @@ PHP_FUNCTION(pcntl_setcpuaffinity)
17731841
}
17741842

17751843
if (cpu < 0 || cpu >= maxcpus) {
1776-
zend_argument_value_error(2, "cpu id must be between 0 and " ZEND_ULONG_FMT " (" ZEND_LONG_FMT ")", maxcpus, cpu);
1844+
zend_argument_value_error(2, "cpu id must be between 0 and " ZEND_LONG_FMT " (" ZEND_LONG_FMT ")", maxcpus, cpu);
17771845
PCNTL_CPU_DESTROY(mask);
17781846
RETURN_THROWS();
17791847
}
17801848

1781-
if (!PCNTL_CPU_ISSET(cpu, mask)) {
1782-
PCNTL_CPU_SET(cpu, mask);
1849+
if (!PCNTL_CPU_ISSET(cpu, mask, sz)) {
1850+
PCNTL_CPU_SET(cpu, mask, sz);
17831851
}
17841852
} ZEND_HASH_FOREACH_END();
17851853

1786-
if (sched_setaffinity(pid, PCNTL_CPUSET_SIZE(mask), PCNTL_CPUSET(mask)) != 0) {
1854+
if (sched_setaffinity(pid, PCNTL_CPUSET_SIZE(mask, sz), PCNTL_CPUSET(mask)) != 0) {
17871855
PCNTL_CPU_DESTROY(mask);
17881856
PCNTL_G(last_error) = errno;
17891857
switch (errno) {

0 commit comments

Comments
 (0)