Index: lib/libc/gen/sched_getaffinity.c =================================================================== --- lib/libc/gen/sched_getaffinity.c +++ lib/libc/gen/sched_getaffinity.c @@ -33,24 +33,15 @@ int sched_getaffinity(pid_t pid, size_t cpusetsz, cpuset_t *cpuset) { - /* - * Be more Linux-compatible: - * - return EINVAL in passed size is less than size of cpuset_t - * in advance, instead of ERANGE from the syscall - * - if passed size is larger than the size of cpuset_t, be - * permissive by claming it back to sizeof(cpuset_t) and - * zeroing the rest. - */ - if (cpusetsz < sizeof(cpuset_t)) { + int error; + + error = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, + pid == 0 ? -1 : pid, cpusetsz, cpuset); + if (error == -1 && errno == ERANGE) errno = EINVAL; - return (-1); - } - if (cpusetsz > sizeof(cpuset_t)) { - memset((char *)cpuset + sizeof(cpuset_t), 0, - cpusetsz - sizeof(cpuset_t)); - cpusetsz = sizeof(cpuset_t); - } + if (error == 0) + return (cpusetsz < sizeof(cpuset_t) ? cpusetsz : + sizeof(cpuset_t)); - return (cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, - pid == 0 ? -1 : pid, cpusetsz, cpuset)); + return (error); } Index: lib/libc/gen/sched_setaffinity.c =================================================================== --- lib/libc/gen/sched_setaffinity.c +++ lib/libc/gen/sched_setaffinity.c @@ -33,18 +33,10 @@ int sched_setaffinity(pid_t pid, size_t cpusetsz, const cpuset_t *cpuset) { - cpuset_t c; int error; - if (cpusetsz > sizeof(cpuset_t)) { - errno = EINVAL; - return (-1); - } else { - memset(&c, 0, sizeof(c)); - memcpy(&c, cpuset, cpusetsz); - } error = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, - pid == 0 ? -1 : pid, sizeof(cpuset_t), &c); + pid == 0 ? -1 : pid, cpusetsz, cpuset); if (error == -1 && errno == EDEADLK) errno = EINVAL; Index: lib/libc/sys/cpuset_getaffinity.2 =================================================================== --- lib/libc/sys/cpuset_getaffinity.2 +++ lib/libc/sys/cpuset_getaffinity.2 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 23, 2017 +.Dd April 26, 2022 .Dt CPUSET_GETAFFINITY 2 .Os .Sh NAME @@ -71,14 +71,15 @@ are composed using the .Dv CPU_SET macros. -The kernel tolerates large sets as long as all CPUs specified -in the set exist. -Sets smaller than the kernel uses generate an error on calls to +If the user-supplied mask is not large enough to fit all of CPUs +that are currently physically on the system .Fn cpuset_getaffinity -even if the result set would fit within the user supplied set. +fails with +.Er ERANGE . Calls to .Fn cpuset_setaffinity -tolerate small sets with no restrictions. +tolerate masks of any size with no restrictions. The kernel uses a +significant part of the mask, no large than the size of the kernel cpuset_t. .Pp The supplied mask should have a size of .Fa setsize @@ -144,7 +145,7 @@ .It Bq Er ERANGE The .Fa cpusetsize -was either preposterously large or smaller than the kernel set size. +was smaller than needed to fit all active CPUs. .It Bq Er EPERM The calling process did not have the credentials required to complete the operation. Index: share/man/man3/pthread_attr_affinity_np.3 =================================================================== --- share/man/man3/pthread_attr_affinity_np.3 +++ share/man/man3/pthread_attr_affinity_np.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 12, 2021 +.Dd April 26, 2022 .Dt PTHREAD_ATTR_AFFINITY_NP 3 .Os .Sh NAME @@ -51,14 +51,15 @@ are composed using the .Dv CPU_SET macros. -The kernel tolerates large sets as long as all CPUs specified -in the set exist. -Sets smaller than the kernel uses generate an error on calls to -.Fn pthread_attr_getaffinity_np -even if the result set would fit within the user supplied set. +If the user-supplied mask is not large enough to fit all of CPUs +that are currently physically on the system +.Fn cpuset_getaffinity +fails with +.Er ERANGE . Calls to -.Fn pthread_attr_setaffinity_np -tolerate small sets with no restrictions. +.Fn cpuset_setaffinity +tolerate masks of any size with no restrictions. The kernel uses a +significant part of the mask, no large than the size of the kernel cpuset_t. .Pp The supplied mask should have a size of .Fa cpusetsize Index: sys/kern/kern_cpuset.c =================================================================== --- sys/kern/kern_cpuset.c +++ sys/kern/kern_cpuset.c @@ -1896,13 +1896,13 @@ int error; size_t size; - if (cpusetsize < sizeof(cpuset_t) || cpusetsize > CPU_MAXSIZE / NBBY) + if (cpusetsize < howmany(smp_cpus, NBBY)) return (ERANGE); error = cpuset_check_capabilities(td, level, which, id); if (error != 0) return (error); - size = cpusetsize; - mask = malloc(size, M_TEMP, M_WAITOK | M_ZERO); + size = min(cpusetsize, sizeof(cpuset_t)); + mask = malloc(sizeof(cpuset_t), M_TEMP, M_WAITOK | M_ZERO); error = cpuset_which(which, id, &p, &ttd, &set); if (error) goto out; @@ -2006,26 +2006,27 @@ struct proc *p; cpuset_t *mask; int error; + size_t loadsize, factsize; - if (cpusetsize < sizeof(cpuset_t) || cpusetsize > CPU_MAXSIZE / NBBY) - return (ERANGE); error = cpuset_check_capabilities(td, level, which, id); if (error != 0) return (error); - mask = malloc(cpusetsize, M_TEMP, M_WAITOK | M_ZERO); - error = copyin(maskp, mask, cpusetsize); + loadsize = min(cpusetsize, sizeof(cpuset_t)); + mask = malloc(sizeof(cpuset_t), M_TEMP, M_WAITOK | M_ZERO); + error = copyin(maskp, mask, loadsize); if (error) goto out; + factsize = howmany(smp_cpus, NBBY); /* * Verify that no high bits are set. */ - if (cpusetsize > sizeof(cpuset_t)) { + if (loadsize > factsize) { char *end; char *cp; end = cp = (char *)&mask->__bits; - end += cpusetsize; - cp += sizeof(cpuset_t); + end += loadsize; + cp += factsize; while (cp != end) if (*cp++ != 0) { error = EINVAL; Index: tests/sys/kern/Makefile =================================================================== --- tests/sys/kern/Makefile +++ tests/sys/kern/Makefile @@ -26,6 +26,7 @@ ATF_TESTS_C+= ptrace_test TEST_METADATA.ptrace_test+= timeout="15" ATF_TESTS_C+= reaper +ATF_TESTS_C+= sched_affinity ATF_TESTS_C+= sigaltstack ATF_TESTS_C+= sigwait .if ${MACHINE_ARCH} != "i386" && ${MACHINE_ARCH:Mpowerpc*} == "" Index: tests/sys/kern/sched_affinity.c =================================================================== --- /dev/null +++ tests/sys/kern/sched_affinity.c @@ -0,0 +1,222 @@ +/*- + * Copyright (c) 2022 Dmitry Chagin + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include + +#include + + +static int +support_sysconf(int name) +{ + int rv; + + rv = sysconf(name); + ATF_REQUIRE(rv != -1); + return (rv); +} + +ATF_TC_WITHOUT_HEAD(test_setinvalidcpu); +ATF_TC_BODY(test_setinvalidcpu, tc) +{ + size_t cpusetsize; + cpuset_t *set; + ssize_t cpus; + + cpus = support_sysconf(_SC_NPROCESSORS_CONF); + + set = CPU_ALLOC(cpus + 1); + ATF_REQUIRE(set != NULL); + cpusetsize = CPU_ALLOC_SIZE(cpus + 1); + CPU_ZERO(set); + CPU_SET(cpus, set); + ATF_REQUIRE(sched_setaffinity(0, cpusetsize, set) == -1); + ATF_REQUIRE_EQ(errno, EINVAL); + CPU_FREE(set); +} + +ATF_TC_WITHOUT_HEAD(test_setvalidcpu); +ATF_TC_BODY(test_setvalidcpu, tc) +{ + size_t cpusetsize; + cpuset_t *set; + ssize_t cpus; + int cpu; + + cpus = support_sysconf(_SC_NPROCESSORS_CONF); + cpu = cpus > 1 ? cpus - 1 : 0; + + set = CPU_ALLOC(cpus); + ATF_REQUIRE(set != NULL); + cpusetsize = CPU_ALLOC_SIZE(cpus); + CPU_ZERO(set); + CPU_SET(cpu, set); + ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, + -1, cpusetsize, set) == 0); + ATF_REQUIRE_EQ(cpu, sched_getcpu()); + CPU_FREE(set); +} + +ATF_TC_WITHOUT_HEAD(test_setzeroset1); +ATF_TC_BODY(test_setzeroset1, tc) +{ + size_t cpusetsize; + cpuset_t *set; + ssize_t cpus; + + cpus = support_sysconf(_SC_NPROCESSORS_CONF); + + set = CPU_ALLOC(cpus); + ATF_REQUIRE(set != NULL); + cpusetsize = CPU_ALLOC_SIZE(cpus); + CPU_ZERO(set); + ATF_REQUIRE(sched_setaffinity(0, cpusetsize, set) == -1); + ATF_REQUIRE_EQ(errno, EINVAL); + CPU_FREE(set); +} + +ATF_TC_WITHOUT_HEAD(test_setzeroset2); +ATF_TC_BODY(test_setzeroset2, tc) +{ + size_t cpusetsize; + cpuset_t *set; + ssize_t cpus; + + cpus = support_sysconf(_SC_NPROCESSORS_CONF); + + set = CPU_ALLOC(cpus); + ATF_REQUIRE(set != NULL); + cpusetsize = CPU_ALLOC_SIZE(cpus); + CPU_ZERO(set); + ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, + -1, cpusetsize, set) == -1); + ATF_REQUIRE_EQ(errno, EDEADLK); + CPU_FREE(set); +} + +ATF_TC_WITHOUT_HEAD(test_setmaxsetsize); +ATF_TC_BODY(test_setmaxsetsize, tc) +{ + size_t cpusetsize; + cpuset_t *set; + ssize_t cpus = INT32_MAX; + + set = CPU_ALLOC(cpus); + ATF_REQUIRE(set != NULL); + cpusetsize = CPU_ALLOC_SIZE(cpus); + CPU_ZERO(set); + CPU_SET(0, set); + ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, + -1, cpusetsize, set) == 0); + CPU_FREE(set); +} + +ATF_TC_WITHOUT_HEAD(test_setminsetsize); +ATF_TC_BODY(test_setminsetsize, tc) +{ + size_t cpusetsize = 1; + int8_t set; + ssize_t cpus; + + cpus = support_sysconf(_SC_NPROCESSORS_CONF); + if (cpus <= 8) + return; + + set = 1; + ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, + -1, cpusetsize, (const cpuset_t *)&set) == 0); + set = 0; + ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, + -1, cpusetsize, (const cpuset_t *)&set) == -1); + ATF_REQUIRE_EQ(errno, EDEADLK); +} + +ATF_TC_WITHOUT_HEAD(test_getminsetsize); +ATF_TC_BODY(test_getminsetsize, tc) +{ + size_t cpusetsize = 1; + int8_t set = 0; + ssize_t cpus; + + cpus = support_sysconf(_SC_NPROCESSORS_CONF); + if (cpus <= 8) { + /* Can't check ERANGE if cpus <= 8 */ + ATF_REQUIRE(cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, + -1, cpusetsize, (cpuset_t *)&set) == 0); + } else { + ATF_REQUIRE(cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, + -1, cpusetsize, (cpuset_t *)&set) == -1); + ATF_REQUIRE_EQ(errno, ERANGE); + } +} + +ATF_TC_WITHOUT_HEAD(test_getsetsize); +ATF_TC_BODY(test_getsetsize, tc) +{ + size_t cpusetsize; + cpuset_t *set; + ssize_t cpus; + + cpus = support_sysconf(_SC_NPROCESSORS_CONF); + + set = CPU_ALLOC(cpus); + ATF_REQUIRE(set != NULL); + cpusetsize = CPU_ALLOC_SIZE(cpus); + CPU_ZERO(set); + ATF_REQUIRE(cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, + -1, cpusetsize, set) == 0); + CPU_FREE(set); +} + +ATF_TC_WITHOUT_HEAD(test_schedgetsetsize); +ATF_TC_BODY(test_schedgetsetsize, tc) +{ + int cpusetsize; + cpuset_t *set; + ssize_t cpus; + + cpus = support_sysconf(_SC_NPROCESSORS_CONF); + + set = CPU_ALLOC(cpus); + ATF_REQUIRE(set != NULL); + cpusetsize = CPU_ALLOC_SIZE(cpus); + CPU_ZERO(set); + ATF_REQUIRE(sched_getaffinity(0, cpusetsize, set) == cpusetsize); + CPU_FREE(set); + + set = CPU_ALLOC(CPU_SETSIZE); + ATF_REQUIRE(set != NULL); + cpusetsize = CPU_ALLOC_SIZE(CPU_SETSIZE); + CPU_ZERO(set); + ATF_REQUIRE(sched_getaffinity(0, cpusetsize, set) == cpusetsize); + CPU_FREE(set); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, test_setinvalidcpu); + ATF_TP_ADD_TC(tp, test_setvalidcpu); + ATF_TP_ADD_TC(tp, test_setzeroset1); + ATF_TP_ADD_TC(tp, test_setzeroset2); + + ATF_TP_ADD_TC(tp, test_setminsetsize); + ATF_TP_ADD_TC(tp, test_setmaxsetsize); + + ATF_TP_ADD_TC(tp, test_getminsetsize); + ATF_TP_ADD_TC(tp, test_getsetsize); + + ATF_TP_ADD_TC(tp, test_schedgetsetsize); + + return (atf_no_error()); +}