]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
tests: nproc: fix false failure on some systems
authorPádraig Brady <P@draigBrady.com>
Sun, 24 Aug 2025 10:41:01 +0000 (11:41 +0100)
committerPádraig Brady <P@draigBrady.com>
Sun, 24 Aug 2025 10:48:52 +0000 (11:48 +0100)
* tests/nproc/nproc-quota.sh: Also simulate sched_getscheduler()
as this will not be called on older or non linux, or
may return ENOSYS on Alpine.
Fixes https://bugs.gnu.org/79299

tests/nproc/nproc-quota.sh

index 236a1a83bdba2baa27c818a723a56aff52d92a5a..1c1bc1f51584c2b2f663150123535ff1519274db 100755 (executable)
 . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
 print_ver_ nproc
 require_root_
+require_gcc_shared_
+
+# Replace sched_getscheduler()
+cat > k.c <<'EOF' || framework_failure_
+#include <stdio.h>
+#include <errno.h>
+#define __USE_GNU  /* For SCHED_DEADLINE.  */
+#include <sched.h>
+
+int
+sched_getscheduler (pid_t pid)
+{
+  fclose (fopen ("preloaded","w")); /* marker for preloaded interception */
+  FILE* policyf = fopen ("/proc/self/sched", "r");
+  int policy;
+  #define fscanfmt fscanf  /* Avoid syntax check.  */
+  if (pid == 0 && fscanfmt (policyf, "policy : %d", &policy) == 1)
+  {
+     switch (policy)
+       {
+         case 0:  return SCHED_OTHER;
+         case 1:  return SCHED_FIFO;
+         case 2:  return SCHED_RR;
+         case 6:  return SCHED_DEADLINE;
+         case -1: errno = EINVAL; return -1;
+         default: return SCHED_OTHER;
+       }
+  }
+  else
+    {
+      errno = ENOSYS;
+      return -1;
+    }
+}
+EOF
+
+# compile/link the interception shared library:
+gcc_shared_ k.c k.so \
+  || skip_ 'failed to build sched_getscheduler shared library'
+
+(export LD_PRELOAD=$LD_PRELOAD:./k.so
+ nproc) || fail=1
+
+# Ensure our wrapper is in place and is called
+# I.e., this test is restricted to new enough Linux systems
+# as otherwise cpu_quota() will not call sched_getscheduler()
+test -e preloaded || skip_ 'LD_PRELOAD interception failed'
 
 # We could look for modifiable cgroup quotas on the current system,
 # but that would be dangerous to modify and restore robustly.
@@ -32,6 +79,7 @@ ROOT=cgroup
   mkdir -p $ROOT/sys/fs/cgroup/foo &&
   touch $ROOT/sys/fs/cgroup/cgroup.controllers &&
   echo '0::/foo' > $ROOT/proc/self/cgroup &&
+  echo 'policy   :   0' > $ROOT/proc/self/sched &&
   echo 'max 100000' > $ROOT/sys/fs/cgroup/foo/cpu.max  # ignored
 } || framework_failure_
 
@@ -39,7 +87,10 @@ nproc=$abs_top_builddir/src/nproc$EXEEXT
 cp --parents $(ldd $nproc | grep -o '/[^ ]*') $ROOT ||
   skip_ 'Failed to copy nproc libs to chroot'
 cp $nproc $ROOT || framework_failure_
-chroot $ROOT /nproc --version ||
+cp k.so $ROOT || framework_failure_
+
+NPROC() { LD_PRELOAD=$LD_PRELOAD:./k.so chroot $ROOT /nproc "$@"; }
+NPROC --version ||
   skip_ 'Failed to execute nproc in chroot'
 
 unset OMP_NUM_THEADS
@@ -52,34 +103,45 @@ ncpus=$(nproc) || fail=1
 # would then not match $ncpus
 if test "$ncpus" = "$(nproc --all)"; then
 echo 'max 100000' > $ROOT/sys/fs/cgroup/cpu.max &&
-test $(chroot $ROOT /nproc) -eq $ncpus || fail=1
+test $(NPROC) -eq $ncpus || fail=1
 # Note Linux won't allow values > 1,000,000 (i.e., micro seconds)
 # so no point testing above that threshold ( for e.g $((1<<53)) )
 echo "1000000 1" > $ROOT/sys/fs/cgroup/cpu.max &&
-test $(chroot $ROOT /nproc) -eq $ncpus || fail=1
+test $(NPROC) -eq $ncpus || fail=1
 # Can't happen in real cgroup, but ensure we avoid divide by zero
 echo '100000 0' > $ROOT/sys/fs/cgroup/cpu.max &&
-test $(chroot $ROOT /nproc) -eq $ncpus || fail=1
+test $(NPROC) -eq $ncpus || fail=1
+echo '100000 100000' > $ROOT/sys/fs/cgroup/cpu.max &&
+test $(OMP_NUM_THREADS=$ncpus NPROC) -eq $ncpus || fail=1
+
 echo '100000 100000' > $ROOT/sys/fs/cgroup/cpu.max &&
-test $(OMP_NUM_THREADS=$ncpus chroot $ROOT /nproc) -eq $ncpus || fail=1
+echo 'policy   :   1' > $ROOT/proc/self/sched && # No quota for SCHED_FIFO
+test $(NPROC) -eq $ncpus || fail=1
+echo 'policy   :   2' > $ROOT/proc/self/sched && # No quota for SCHED_RR
+test $(NPROC) -eq $ncpus || fail=1
+echo 'policy   :   6' > $ROOT/proc/self/sched && # No quota for SCHED_DEADLINE
+test $(NPROC) -eq $ncpus || fail=1
+echo 'policy   :  -1' > $ROOT/proc/self/sched && # Unsupported
+test $(NPROC) -eq $ncpus || fail=1
+echo 'policy   :   0' > $ROOT/proc/self/sched # reset to SCHED_OTHER
 fi
 
 echo '40000 100000' > $ROOT/sys/fs/cgroup/cpu.max &&
-test $(chroot $ROOT /nproc) -eq 1 || fail=1
+test $(NPROC) -eq 1 || fail=1
 echo '50000 100000' > $ROOT/sys/fs/cgroup/cpu.max &&
-test $(chroot $ROOT /nproc) -eq 1 || fail=1
+test $(NPROC) -eq 1 || fail=1
 echo '100000 100000' > $ROOT/sys/fs/cgroup/cpu.max &&
-test $(chroot $ROOT /nproc) -eq 1 || fail=1
+test $(NPROC) -eq 1 || fail=1
 echo '140000 100000' > $ROOT/sys/fs/cgroup/cpu.max &&
-test $(chroot $ROOT /nproc) -eq 1 || fail=1
+test $(NPROC) -eq 1 || fail=1
 echo '1 1000000' > $ROOT/sys/fs/cgroup/cpu.max &&
-test $(chroot $ROOT /nproc) -eq 1 || fail=1
+test $(NPROC) -eq 1 || fail=1
 
 if test $ncpus -gt 1; then
 echo '150000 100000' > $ROOT/sys/fs/cgroup/cpu.max &&
-test $(chroot $ROOT /nproc) -eq 2 || fail=1
+test $(NPROC) -eq 2 || fail=1
 echo '150000 100000' > $ROOT/sys/fs/cgroup/cpu.max &&
-test $(OMP_THREAD_LIMIT=10 chroot $ROOT /nproc) -eq 2 || fail=1
+test $(OMP_THREAD_LIMIT=10 NPROC) -eq 2 || fail=1
 fi
 
 Exit $fail