Commit 9a8df3eb authored by jan.koester's avatar jan.koester
Browse files

ppc cpucount

parent 54375fd5
Loading
Loading
Loading
Loading
+156 −8
Changes for build-cross.sh: 156 added lines, 8 removed lines.
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@
#  not in the default run:
#      packages    the FenrirOS specs as RPMs (host rpmbuild, cross toolchain)
#      repo        those RPMs as dnf5 repository $WORK/repo/<arch> (REPO_UPLOAD)
#      docker      container images fenriros, fenriros-devel of the repository
#                  (linux/ppc; tarballs in $WORK/docker)
#
# Usage:  ./build-cross.sh [step...]   (default: all steps in order)
#         ./build-cross.sh clean
@@ -77,6 +79,8 @@ LLVM_REF="${LLVM_REF:-llvmorg-23.1.2}"
if [[ -z ${LLVM_SRC:-} && -d $SYSTEMPP_SRC/build-chroot/llvm-project/compiler-rt ]]; then
    LLVM_SRC="$SYSTEMPP_SRC/build-chroot/llvm-project"
fi
# (else the x86 build's checkout, build-chroot.sh's LLVM_SRC)
[[ -n ${LLVM_SRC:-} || -d $SRCDIR/llvm-project ]] || LLVM_SRC="/build/$USER/systempp/build-chroot/llvm-project"
LLVM_SRC="$(realpath -m "${LLVM_SRC:-$SRCDIR/llvm-project}")"
FRESH_SRC="$(realpath -m "${FRESH_SRC:-$SYSTEMPP_SRC/../fresh}")"
SYSLITE_SRC="$(realpath -m "${SYSLITE_SRC:-$SYSTEMPP_SRC/../syslite}")"
@@ -95,7 +99,8 @@ CROSS_PACKAGES_SET="${CROSS_PACKAGES:+1}"
CROSS_PACKAGES="${CROSS_PACKAGES:-systempp fresh zlib zstd bzip2 libjson-c libbrotli libcmdplus libuuidp libnetplus libyaml libconfplus
    libhttppp libressl syslite sharedd adconnect storaged iscsid upsd firewalld tinyxml2 snmpdd ntpplus containerd webctl
    popt lua file libelf libsqlite3 rpm ca-certificates fenriros-release liblzma libxml2 libarchive glib2 curl
    libsolv librepo fmt toml11 libsmartcols dnf5 e2fsprogs dosfstools gptfdisk mtd-utils openssh}"
    libsolv librepo fmt toml11 libsmartcols dnf5 e2fsprogs dosfstools gptfdisk mtd-utils openssh
    llvm-project}"
# the board's kernel as a package (kernel-<board>.spec, e.g. the NAS ITX's
# writes its boot image into the NAND on installation)
if [[ -z ${CROSS_PACKAGES_SET:-} && -f $FENRIR_FILES/rpm/specs/kernel-$BOARD.spec ]]; then
@@ -103,6 +108,13 @@ if [[ -z ${CROSS_PACKAGES_SET:-} && -f $FENRIR_FILES/rpm/specs/kernel-$BOARD.spe
fi

PREFIX=/usr
# packages built and in the repository, but not in the board's root
# (dnf install them when needed): the toolchain
ROOTFS_EXCLUDE="${ROOTFS_EXCLUDE:-llvm-project clang lld lldb llvm-tools}"
# llvm-project: the x86 build's checkout (staged as the spec's Source0) and
# its native tablegen (LLVM_NATIVE_TOOL_DIR, build-chroot.sh's host-tblgen)
export LLVM_SRC   # (defined above: the builtins step uses it too)
LLVM_NATIVE_TOOL_DIR="${LLVM_NATIVE_TOOL_DIR:-/build/$USER/systempp/build-chroot/build/llvm-host/bin}"
INTERP="$PREFIX/lib/ld-linux-$PROC.so"
BUILD="$WORK/build"
SYSROOT="$WORK/sysroot"
@@ -118,6 +130,7 @@ BUILTINS="$SYSROOT$PREFIX/lib/libsystempp-builtins.a"
PATH="$PATH:/usr/sbin:/sbin"

msg()  { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33mWARNING:\033[0m %s\n' "$*" >&2; }
die()  { printf '\033[1;31mERROR:\033[0m %s\n' "$*" >&2; exit 1; }

# run <logname> <cmd...> -- run a command, output into $LOGS/<logname>.log
@@ -226,16 +239,21 @@ step_builtins() {
# gen_wrapper <out> <c|c++> -- a clang driver for the target, compiling
# against the sysroot's systempp headers and linking systempp's crt objects
# and libc.so (same logic as build-chroot.sh's gen_wrapper).
# WRAP_CLANG/WRAP_RESDIR/WRAP_ROOT/WRAP_TFLAGS/WRAP_BUILTINS: another clang,
# root and builtins than the host's and the sysroot (the native wrappers of
# the target's clang package, see native_wrappers)
gen_wrapper() {
    local out="$1" lang="$2" clang drv="" resdir
    clang="$(command -v "$HOST_CC")"
    local out="$1" lang="$2" clang drv="" resdir root tflags builtins
    clang="${WRAP_CLANG:-$(command -v "$HOST_CC")}"
    [[ $lang == c++ ]] && drv="--driver-mode=g++"
    resdir="$("$HOST_CC" -print-resource-dir)"
    local inc="$TFLAGS -fPIC -nostdinc -cxx-isystem $SYSROOT$PREFIX/include/cxx -isystem $SYSROOT$PREFIX/include -isystem $resdir/include"
    resdir="${WRAP_RESDIR:-$("$HOST_CC" -print-resource-dir)}"
    root="${WRAP_ROOT-$SYSROOT}" tflags="${WRAP_TFLAGS-$TFLAGS}" builtins="${WRAP_BUILTINS:-$BUILTINS}"
    local inc="$tflags -fPIC -nostdinc -cxx-isystem $root$PREFIX/include/cxx -isystem $root$PREFIX/include -isystem $resdir/include"
    local TFLAGS="$tflags" BUILTINS="$builtins"
    cat >"$out" <<EOF
#!/bin/sh
# generated by systempp build-cross.sh -- clang driver for systempp on $TRIPLE
LIB="$SYSROOT$PREFIX/lib"
LIB="$root$PREFIX/lib"
mode=link pie=1 shared=0 static=0 startfiles=1 libs=1
for a in "\$@"; do
    case "\$a" in
@@ -277,6 +295,31 @@ EOF
    chmod +x "$out"
}

# native_wrappers <out.tar.gz> <llvm-major> -- the target clang package's
# cc/c++ (systempp-cc/-c++, as build-chroot.sh's llvm_wrappers for x86_64):
# its own clang, headers and crt objects under /usr, the target's
# compiler-rt builtins as /usr/lib/libsystempp-builtins.a, the mandatory
# target flags (e500: soft-float, secure PLT) built in
native_wrappers() {
    local out="$1" major="$2" d="$BUILD/native-wrappers" l
    rm -rf --one-file-system "${d:?}"
    mkdir -p "$d$PREFIX/bin" "$d$PREFIX/lib"
    cp "$BUILTINS" "$d$PREFIX/lib/libsystempp-builtins.a"
    local flags="${TFLAGS#--target=$CLANG_TARGET }"
    WRAP_CLANG="$PREFIX/bin/clang-$major" WRAP_RESDIR="$PREFIX/lib/clang/$major" WRAP_ROOT="" \
        WRAP_TFLAGS="$flags" WRAP_BUILTINS="$PREFIX/lib/libsystempp-builtins.a" \
        gen_wrapper "$d$PREFIX/bin/systempp-cc" c
    WRAP_CLANG="$PREFIX/bin/clang-$major" WRAP_RESDIR="$PREFIX/lib/clang/$major" WRAP_ROOT="" \
        WRAP_TFLAGS="$flags" WRAP_BUILTINS="$PREFIX/lib/libsystempp-builtins.a" \
        gen_wrapper "$d$PREFIX/bin/systempp-c++" c++
    for l in cc clang; do ln -s systempp-cc "$d$PREFIX/bin/$l"; done
    for l in c++ clang++; do ln -s systempp-c++ "$d$PREFIX/bin/$l"; done
    for l in clang-cpp clang-cl; do ln -s "clang-$major" "$d$PREFIX/bin/$l"; done
    for l in ar ranlib nm strip objcopy objdump readelf; do ln -s "llvm-$l" "$d$PREFIX/bin/$l"; done
    tar -czf "$out" -C "$d" usr
    rm -rf --one-file-system "${d:?}"
}

step_toolchain() {
    [[ -f $SYSROOT$PREFIX/lib/libc.so && -f $BUILTINS ]] || die "sysroot incomplete -- run 'systempp' and 'builtins' first"
    msg "toolchain: $TRIPLE-cc/-c++ in $TOOLCHAIN"
@@ -525,7 +568,8 @@ step_rootfs() {
            ! -name '*-debuginfo-*' 2>/dev/null | while read -r r; do
                printf '%s %s %s\n' "$(rpm -qp --qf '%{NAME}' "$r" 2>/dev/null)" \
                    "$(rpm -qp --qf '%{VERSION}-%{RELEASE}' "$r" 2>/dev/null)" "$r"
            done | sort -k1,1 -k2,2V | awk '{ last[$1] = $3 } END { for (n in last) print last[n] }' |
            done | sort -k1,1 -k2,2V |
            awk -v ex=" $ROOTFS_EXCLUDE " '{ last[$1] = $3 } END { for (n in last) if (index(ex, " " n " ") == 0) print last[n] }' |
            sort >"$list"
        msg "rootfs: installing $(wc -l <"$list") RPMs"
        : >"$LOGS/rootfs.log"
@@ -676,6 +720,14 @@ EOF
cross_prebuild() {
    local pkg="$1" spec="$2"
    case "$pkg" in
    llvm-project)
        # the wrappers tarball build-chroot.sh staged is x86_64's: the
        # target's instead (its builtins, e500 flags)
        local major
        major="$(sed -n 's/^Version:[[:space:]]*\([0-9]*\).*/\1/p' "$spec" | head -n1)"
        native_wrappers "$WORK/rpmbuild/SOURCES/llvm-project-wrappers.tar.gz" "$major"
        echo "$spec"
        ;;
    libsqlite3)
        # build-chroot.sh's step_rpm: the amalgamation into /usr/local, which
        # libsqlite3.spec packages -- here under the sysroot
@@ -743,6 +795,7 @@ step_packages() {
               CMAKE_TOOLCHAIN_FILE="$pkgtc" \
               CMAKE_POLICY_VERSION_MINIMUM=3.5 \
               QEMU_LD_PREFIX="$SYSROOT" QEMU_CPU=e500v2 \
               LLVM_NATIVE_TOOL_DIR="$LLVM_NATIVE_TOOL_DIR" \
               PKG_CONFIG_SYSROOT_DIR="$SYSROOT" \
               PKG_CONFIG_LIBDIR="$SYSROOT$PREFIX/lib/pkgconfig:$SYSROOT$PREFIX/share/pkgconfig" \
               rpmbuild -bb --nodeps --target "$RPM_ARCH-linux" \
@@ -809,12 +862,107 @@ step_repo() {
    msg "repo ready: $repo/$arch"
}

# docker: container images of the repository's RPMs, as build-chroot.sh's
# 'docker' step: fenriros (DOCKER_MINIMAL and what it requires) and
# fenriros-devel (every package but the board's kernel and DOCKER_DEVEL_EXCLUDE),
# root tarballs in $WORK/docker and, with docker there, imported as
# <DOCKER_PREFIX>fenriros[-devel]:<DOCKER_TAG> for linux/$DOCKER_ARCH (what
# containerd's goArch() calls this machine: uname -m)
DOCKER="${DOCKER:-docker}"
DOCKER_PREFIX="${DOCKER_PREFIX:-}"
DOCKER_TAG="${DOCKER_TAG:-$(date -u +%Y%m%d)}"
DOCKER_ARCH="${DOCKER_ARCH:-ppc}"
DOCKER_MINIMAL="${DOCKER_MINIMAL:-systempp fresh fenriros-release rpm dnf5 ca-certificates}"
DOCKER_DEVEL_EXCLUDE="${DOCKER_DEVEL_EXCLUDE:-kernel-$BOARD fenriros-installer}"

# newest_rpms <dir> -- "<name> <file>" of the newest build of each package
newest_rpms() {
    local r
    for r in "$1"/*.rpm; do
        [[ -e $r ]] || continue
        printf '%s %s %s\n' "$(rpm -qp --qf '%{NAME}' "$r" 2>/dev/null)" \
            "$(rpm -qp --qf '%{EPOCHNUM}:%{VERSION}-%{RELEASE}' "$r" 2>/dev/null)" "$r"
    done | sort -k1,1 -k2,2V | awk '{ last[$1] = $3 } END { for (n in last) print n, last[n] }' | sort
}

# rpm_closure <name-file-list> <name>... -- the files of the named packages
# and everything they require (by provides and file names)
rpm_closure() {
    local list="$1"; shift
    local -A file prov want req
    local n f c p
    while read -r n f; do
        file[$n]=$f prov[$n]=$n
        while read -r c; do [[ -n $c && -z ${prov[$c]:-} ]] && prov[$c]=$n; done \
            < <(rpm -qp --qf '[%{PROVIDENAME}\n][%{FILENAMES}\n]' "$f" 2>/dev/null)
        req[$n]="$(rpm -qp --qf '[%{REQUIRENAME}\n]' "$f" 2>/dev/null)"
    done <"$list"
    local queue=("$@")
    while ((${#queue[@]})); do
        n="${queue[0]}"; queue=("${queue[@]:1}")
        [[ -z ${want[$n]:-} ]] || continue
        [[ -n ${file[$n]:-} ]] || die "docker: no $n RPM in the repository"
        want[$n]=1
        while IFS= read -r c; do
            [[ -n $c && $c != rpmlib\(* && $c != \(* ]] || continue
            p="${prov[$c]:-}"
            [[ -z $p || -n ${want[$p]:-} ]] || queue+=("$p")
            [[ -n $p ]] || warn "docker: $n requires $c, which no RPM provides"
        done <<<"${req[$n]:-}"
    done
    for n in "${!want[@]}"; do echo "${file[$n]}"; done | sort
}

# docker_image <name> <file-list> -- root, tarball, (docker) image
docker_image() {
    local name="$1" list="$2" root="$BUILD/docker-$1"
    msg "docker: $name: installing $(wc -l <"$list") RPMs"
    rm -rf --one-file-system "$root"
    mkdir -p "$root"
    unshare -r rpm --root "$root" --initdb
    # scriptlets run in the root through binfmt's qemu-$PROC
    if ! QEMU_CPU=e500v2 unshare -r rpm --root "$root" --ignorearch -ivh $(cat "$list") >>"$LOGS/docker-$name.log" 2>&1; then
        grep -E 'is needed by|conflicts with' "$LOGS/docker-$name.log" | sort -u | head -20 >&2
        die "docker: $name: RPM installation failed (log: $LOGS/docker-$name.log)"
    fi
    # no RPM owns these (as build-chroot.sh's docker_image)
    mkdir -p "$root"/{run,tmp,root,home,var/tmp,var/log}
    chmod 1777 "$root/tmp" "$root/var/tmp"
    mkdir -p "$WORK/docker"
    tar -C "$root" --numeric-owner --owner=0 --group=0 -cf "$WORK/docker/$name-$DOCKER_ARCH.tar" .
    msg "docker: $WORK/docker/$name-$DOCKER_ARCH.tar ($(du -h "$WORK/docker/$name-$DOCKER_ARCH.tar" | cut -f1))"
    if command -v "$DOCKER" >/dev/null; then
        run docker "$DOCKER" import --platform "linux/$DOCKER_ARCH" \
            --change "ENV PATH=/usr/bin:/usr/sbin:/bin:/sbin" \
            --change "ENV HOME=/root" \
            --change "WORKDIR /root" \
            --change "CMD [\"/usr/bin/fresh\"]" \
            --change "LABEL org.opencontainers.image.title=$name" \
            --change "LABEL org.opencontainers.image.version=$DOCKER_TAG" \
            "$WORK/docker/$name-$DOCKER_ARCH.tar" "${DOCKER_PREFIX}${name}:$DOCKER_TAG-$DOCKER_ARCH"
        msg "docker: ${DOCKER_PREFIX}${name}:$DOCKER_TAG-$DOCKER_ARCH (linux/$DOCKER_ARCH)"
    fi
}

step_docker() {
    local pk="$WORK/repo/$RPM_ARCH/Packages" all="$BUILD/docker-all"
    [[ -d $pk ]] || die "no repository -- run the 'repo' step first"
    mkdir -p "$LOGS"
    : >"$LOGS/docker-fenriros.log"; : >"$LOGS/docker-fenriros-devel.log"
    newest_rpms "$pk" >"$all"
    # shellcheck disable=SC2086
    rpm_closure "$all" $DOCKER_MINIMAL >"$BUILD/docker-minimal"
    docker_image fenriros "$BUILD/docker-minimal"
    awk -v ex=" $DOCKER_DEVEL_EXCLUDE " 'index(ex, " " $1 " ") == 0 { print $2 }' "$all" >"$BUILD/docker-devel"
    docker_image fenriros-devel "$BUILD/docker-devel"
}

steps=("$@")
((${#steps[@]})) || steps=("${ALL_STEPS[@]}")

for s in "${steps[@]}"; do
    case "$s" in
        headers|systempp|builtins|toolchain|fresh|syslite|kernel|rootfs|image|installer|packages|repo)
        headers|systempp|builtins|toolchain|fresh|syslite|kernel|rootfs|image|installer|packages|repo|docker)
            check_deps; mkdir -p "$WORK" "$BUILD"; "step_$s" ;;
        clean) step_clean ;;
        -h|--help) sed -n '2,/^$/s/^# \{0,1\}//p' "$0"; exit 0 ;;
+20 −0
Changes for src/linux/include/sched.h: 20 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -72,6 +72,8 @@ extern "C" {
    } cpu_set_t;

    int __sched_cpucount (size_t setsize, const cpu_set_t *setp);
    cpu_set_t *__sched_cpualloc (size_t count);
    void __sched_cpufree (cpu_set_t *set);

    int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t* mask);
    int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t* mask);
@@ -121,3 +123,21 @@ extern "C" {
#define CPU_ISSET(cpu, set) \
    ((((set)->__bits[(cpu) / __CPU_BITS_PER_WORD]) >> ((cpu) % __CPU_BITS_PER_WORD)) & 1UL)
#define CPU_COUNT(set) __sched_cpucount(sizeof(cpu_set_t), (set))

/* Dynamically sized sets (glibc): CPU_ALLOC(count) for `count` CPUs,
 * CPU_ALLOC_SIZE(count) bytes, and the *_S variants taking that size. */
#define CPU_ALLOC_SIZE(count) \
    ((((size_t)(count) + __CPU_BITS_PER_WORD - 1) / __CPU_BITS_PER_WORD) * sizeof(unsigned long))
#define CPU_ALLOC(count) __sched_cpualloc(count)
#define CPU_FREE(set) __sched_cpufree(set)
#define CPU_ZERO_S(size, set) __builtin_memset((set), 0, (size))
#define CPU_SET_S(cpu, size, set) \
    ((size_t)(cpu) / 8 < (size_t)(size) \
     ? ((set)->__bits[(cpu) / __CPU_BITS_PER_WORD] |= (1UL << ((cpu) % __CPU_BITS_PER_WORD))) : 0)
#define CPU_CLR_S(cpu, size, set) \
    ((size_t)(cpu) / 8 < (size_t)(size) \
     ? ((set)->__bits[(cpu) / __CPU_BITS_PER_WORD] &= ~(1UL << ((cpu) % __CPU_BITS_PER_WORD))) : 0)
#define CPU_ISSET_S(cpu, size, set) \
    ((size_t)(cpu) / 8 < (size_t)(size) \
     ? ((((set)->__bits[(cpu) / __CPU_BITS_PER_WORD]) >> ((cpu) % __CPU_BITS_PER_WORD)) & 1UL) : 0)
#define CPU_COUNT_S(size, set) __sched_cpucount((size), (set))
+20 −1
Changes for src/linux/sched.cpp: 20 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
 *******************************************************************************/

#include <sched.h>
#include <stdlib.h>

#include <bits/syscall.h>
#include <bits/sysbits.h>
@@ -44,6 +45,17 @@ extern "C" {
        return count;
    }

    /* CPU_ALLOC()/CPU_FREE(): sets for more than CPU_SETSIZE CPUs, sized
     * with CPU_ALLOC_SIZE() (as glibc: whole longs, zeroed). LLVM's
     * computeHostNumPhysicalCores() uses them on every Linux but x86. */
    cpu_set_t *__sched_cpualloc (size_t count){
        return static_cast<cpu_set_t *>(calloc(1, CPU_ALLOC_SIZE(count)));
    }

    void __sched_cpufree (cpu_set_t *set){
        free(set);
    }

    /* Real syscall wrapper -- was a stub that always returned -1,
     * meaning sched_yield() (and std::this_thread::yield() built on
     * it) never actually yielded at all. */
@@ -92,7 +104,14 @@ extern "C" {
        return syscall_ret(syscall3(__NR_sched_setaffinity, pid, cpusetsize, (unsigned long)mask));
    }

    /* The system call returns the bytes of the kernel's mask it copied;
     * the libc function returns 0 and zeroes the rest of the caller's set
     * (as glibc and musl) -- callers test == 0 (LLVM's core count). */
    int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t* mask){
        return syscall_ret(syscall3(__NR_sched_getaffinity, pid, cpusetsize, (unsigned long)mask));
        long r = syscall_ret(syscall3(__NR_sched_getaffinity, pid, cpusetsize, (unsigned long)mask));
        if (r < 0) return -1;
        if ((size_t)r < cpusetsize)
            __builtin_memset((char *)mask + r, 0, cpusetsize - (size_t)r);
        return 0;
    }
};
+20 −0
Changes for src/test/e2e/CMakeLists.txt: 20 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -3787,3 +3787,23 @@ target_link_libraries(e2e_to_string_wide PRIVATE
add_dependencies(e2e_to_string_wide crti Scrt1 crtbeginS crtn systempp)

add_test(NAME e2e_to_string_wide COMMAND e2e_to_string_wide)

add_executable(e2e_cpu_alloc cpu_alloc_test.cpp)
set_target_properties(e2e_cpu_alloc PROPERTIES OUTPUT_NAME "cpu_alloc_test")
target_compile_options(e2e_cpu_alloc PRIVATE -fPIE)
target_link_options(e2e_cpu_alloc PRIVATE
    -nostdlib -pie
    -Wl,--hash-style=sysv
    -Wl,--dynamic-linker,$<TARGET_FILE_DIR:systempp>/ld-linux-${CMAKE_SYSTEM_PROCESSOR}.so
    -Wl,-rpath,$<TARGET_FILE_DIR:systempp>
)
target_link_libraries(e2e_cpu_alloc PRIVATE
    $<TARGET_OBJECTS:crti>
    $<TARGET_OBJECTS:Scrt1>
    $<TARGET_OBJECTS:crtbeginS>
    $<TARGET_OBJECTS:crtn>
    systempp
)
add_dependencies(e2e_cpu_alloc crti Scrt1 crtbeginS crtn systempp)

add_test(NAME e2e_cpu_alloc COMMAND e2e_cpu_alloc)
+27 −0
Changes for src/test/e2e/cpu_alloc_test.cpp: 27 added lines, 0 removed lines.
Original line number Diff line number Diff line
/* CPU_ALLOC()/CPU_ALLOC_SIZE()/CPU_FREE() and the *_S macros (were missing:
 * LLVM's computeHostNumPhysicalCores() needs them off x86). */
#include <sched.h>
#include <stdio.h>

#define CHECK(c, m) do { if (!(c)) { printf("FAIL: %s\n", m); return 1; } } while (0)

int main() {
    CHECK(CPU_ALLOC_SIZE(2048) == 2048 / 8, "CPU_ALLOC_SIZE(2048) is 256 bytes");
    CHECK(CPU_ALLOC_SIZE(1) == sizeof(unsigned long), "CPU_ALLOC_SIZE(1) is one long");
    cpu_set_t *s = CPU_ALLOC(2048);
    CHECK(s, "CPU_ALLOC returns memory");
    size_t sz = CPU_ALLOC_SIZE(2048);
    CHECK(CPU_COUNT_S(sz, s) == 0, "CPU_ALLOC zeroes the set");
    CPU_SET_S(2000, sz, s);
    CPU_SET_S(3, sz, s);
    CPU_SET_S(4096, sz, s);  /* beyond the set: ignored */
    CHECK(CPU_ISSET_S(2000, sz, s) && CPU_ISSET_S(3, sz, s), "CPU_SET_S/CPU_ISSET_S");
    CHECK(!CPU_ISSET_S(4096, sz, s), "CPU_ISSET_S beyond the set is 0");
    CHECK(CPU_COUNT_S(sz, s) == 2, "CPU_COUNT_S counts the whole set");
    CHECK(sched_getaffinity(0, sz, s) == 0 && CPU_COUNT_S(sz, s) >= 1, "sched_getaffinity with a CPU_ALLOC set");
    CPU_ZERO_S(sz, s);
    CHECK(CPU_COUNT_S(sz, s) == 0, "CPU_ZERO_S");
    CPU_FREE(s);
    printf("PASS\n");
    return 0;
}