From b1305becac3eaabaca799ce8737bebb541eda8c7 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Fri, 19 Jan 2024 12:25:56 -0600 Subject: [PATCH] scx: Add infeasible weights test Signed-off-by: David Vernet --- tools/testing/selftests/scx/Makefile | 6 +- tools/testing/selftests/scx/highpri.c | 106 ++++++++++++++++++++++ tools/testing/selftests/scx/infeasible.sh | 99 ++++++++++++++++++++ 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/scx/highpri.c create mode 100755 tools/testing/selftests/scx/infeasible.sh diff --git a/tools/testing/selftests/scx/Makefile b/tools/testing/selftests/scx/Makefile index 66130b00e6d1c..0bd0cd4a52904 100644 --- a/tools/testing/selftests/scx/Makefile +++ b/tools/testing/selftests/scx/Makefile @@ -153,6 +153,7 @@ override define CLEAN rm -f *.o *.bpf.o *.bpf.skel.h *.bpf.subskel.h rm -f $(TEST_GEN_PROGS) rm -f runner + rm -f highpri endef auto-test-targets := \ @@ -193,7 +194,10 @@ runner: $(SCXOBJ_DIR)/runner.o $(BPFOBJ) $(testcase-targets) TEST_GEN_PROGS := runner -all: runner +highpri: highpri.c infeasible.sh + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + +all: runner highpri .PHONY: all clean help diff --git a/tools/testing/selftests/scx/highpri.c b/tools/testing/selftests/scx/highpri.c new file mode 100644 index 0000000000000..394ac1d59cbba --- /dev/null +++ b/tools/testing/selftests/scx/highpri.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MIN_NICENESS -20 +#define MAX_NICENESS 19 +static int niceness = MIN_NICENESS; + +#define MAX_THREADS 1024 + +static const char help_fmt[] = +"A helper for creating infeasible weight conditions.\n" +"\n" +"Usage: %s [-n NICENESS] [-t NTASKS] [-h]\n" +"\n" +" -n NICENESS The niceness value to use for each thread. Must be in [-20, 19].\n" +" -t NTASKS The number of tasks to create. Must not exceed 1024.\n" +" -h Display this help and exit\n"; + + +static volatile int done = 0; + +static void handle_sigint(int sig) +{ + done = 1; +} + +static void *busy_spin(void *ctx) +{ + uint64_t value = 0; + pid_t pid = getpid(); + pthread_t tid = pthread_self(); + + if (!nice(niceness) && errno != 0) { + fprintf(stderr, "Failure: (%s)\n", strerror(errno)); + done = 1; + return NULL; + } + + while (!done) { + value++; + if (value % 10000 == 0) { + printf("%d[%lu] nice= %d\n", + pid, tid, nice(0)); + } + } + + return NULL; +} + +int main(int argc, char **argv) +{ + unsigned num_threads = 1, i; + pthread_t tids[MAX_THREADS]; + int opt; + + signal(SIGINT, handle_sigint); + signal(SIGTERM, handle_sigint); + + while ((opt = getopt(argc, argv, "n:t:h")) != -1) { + switch (opt) { + case 'n': + niceness = strtol(optarg, NULL, 10); + break; + case 't': + num_threads = strtol(optarg, NULL, 10); + break; + default: + fprintf(stderr, help_fmt, basename(argv[0])); + return opt != 'h'; + } + } + + if (niceness < MIN_NICENESS || niceness > MAX_NICENESS) { + fprintf(stderr, "Invalid niceness %d, must be in [%d, %d]\n", + niceness, MIN_NICENESS, MAX_NICENESS); + fprintf(stderr, help_fmt, basename(argv[0])); + return 1; + } + + if (num_threads >= MAX_THREADS) { + fprintf(stderr, "%u exceeds max threads %u\n", num_threads, + MAX_THREADS); + fprintf(stderr, help_fmt, basename(argv[0])); + return 1; + } + + for (i = 0; i < num_threads; i++) { + if (pthread_create(&tids[i], NULL, busy_spin, NULL)) { + fprintf(stderr, "Failed to create thread %u\n", i); + return 1; + } + } + + for (i = 0; i < num_threads; i++) + pthread_join(tids[i], NULL); + + return 0; +} diff --git a/tools/testing/selftests/scx/infeasible.sh b/tools/testing/selftests/scx/infeasible.sh new file mode 100755 index 0000000000000..9f20e8982f56c --- /dev/null +++ b/tools/testing/selftests/scx/infeasible.sh @@ -0,0 +1,99 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2024 David Vernet +# Copyright (C) 2024 Meta Platforms, Inc. and affiliates. + +# Script to make a cgroup with conditions that should cause the infeasible +# weight problem to manifest + +INFEASIBLE_CGRP_ROOT="/sys/fs/cgroup/infeasible.scope" + +function cgrp_n_tasks() { + return "${curr_pids}" +} + +# create_cgrp() - Create the root infeasible cgroup +function create_cgrp() { + local weight=10000 + + if [[ ! -d "${INFEASIBLE_CGRP_ROOT}" ]]; then + mkdir "${INFEASIBLE_CGRP_ROOT}" + echo "created root infeasible group ${INFEASIBLE_CGRP_ROOT}" + else + echo "root infeasible group ${INFEASIBLE_CGRP_ROOT} already existed" + local curr_pids=$(cat "${INFEASIBLE_CGRP_ROOT}/pids.current") + if [ "${curr_pids}" != "0" ]; then + echo "ERR: tasks found in ${INFEASIBLE_CGRP_ROOT}" + exit 1 + fi + fi + + echo "setting ${INFEASIBLE_CGRP_ROOT} weight to ${weight}" + echo ${weight} > "${INFEASIBLE_CGRP_ROOT}/cpu.weight" + echo "set ${INFEASIBLE_CGRP_ROOT} weight to ${weight}" +} + +# spawn_tasks(ntasks) - Spawn tasks with low niceness and return the pid +function spawn_tasks() { + local ntasks=$1 + + if [ -z $ntasks ]; then + ntasks=$(($(nproc)/4)) + fi + echo "spawning ${ntasks} tasks" + ./highpri -t ${ntasks} > /tmp/highpri.out 2> /tmp/highpri.err & + local status=$? + echo "status was: ${status}" + pid=$! + echo "pid is ${pid}" + echo "spawned highpri task ${pid}" + if [ ${pid} -le 0 ]; then + echo "ERR: failed to spawned highpri tasks" + exit 1 + fi +} + +# Move the tasks into the infeasible cgroup +function move_into_cgrp() { + local pid=$1 + local procsfile="${INFEASIBLE_CGRP_ROOT}/cgroup.procs" + local pidsfile="${INFEASIBLE_CGRP_ROOT}/pids.current" + + echo "moving ${pid} into ${procsfile}" + echo " procs in before: $(cat ${pidsfile})" + echo "${pid}" >> "${procsfile}" + local status=$? + echo "status was: ${status}" + if [ ${status} -ne 0 ]; then + echo "ERR: failed to move ${pid} into ${procsfile}" + echo "ERR: destroying ${pid}" + kill -9 ${pid} + echo "ERR: killed ${pid}. waiting for exit..." + wait + echo "ERR: ${pid} exited" + exit 1 + fi + echo " procs in after: $(cat ${pidsfile})" +} + +function cleanup() { + local curr_pids=$(cat "${INFEASIBLE_CGRP_ROOT}/pids.current") + if [ "${curr_pids}" != "0" ]; then + echo "WARN: ${curr_pids} tasks remaining in ${INFEASIBLE_CGRP_ROOT}" + fi +} + +function _term() { + echo "Caught SIGINT signal" + kill -s SIGINT "${pid}" +} + +create_cgrp +pid="" +trap _term SIGINT +spawn_tasks +move_into_cgrp ${pid} +echo "success: waiting for ${pid} to finish" +wait ${pid} +echo "${pid} exited. cleaning up..." +cleanup