diff options
| author | Tim Chen <tim.c.chen@linux.intel.com> | 2026-09-21 17:37:22 -0700 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2026-09-22 10:49:53 +0200 |
| commit | 0d6526f82c3cdefcca47f73f5fc08dc6f335eac6 (patch) | |
| tree | 176dc2d685debcfb082c151ee94f2425ac96b364 /tools/testing/selftests/splice | |
| download | linux-stable-0d6526f82c3cdefcca47f73f5fc08dc6f335eac6.tar.gz linux-stable-0d6526f82c3cdefcca47f73f5fc08dc6f335eac6.zip | |
sched/cache: Keep nr_pref_llc_running in the runnable domain, to fix LLC mis-scheduling buggrafted
alb_break_llc() decides whether to break LLC preference during active
load balance. It does so by testing that every runnable fair task on the
source rq prefers its LLC:
env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable
But the two counters cover different sets. nr_pref_llc_running is updated
in account_llc_enqueue()/account_llc_dequeue(), next to cfs_rq->nr_queued,
so it follows queued tasks. h_nr_runnable is updated in set_delayed()/
clear_delayed() and drops delay-dequeued tasks.
So under DELAY_DEQUEUE, a preferring task that goes to sleep stays counted
in nr_pref_llc_running while h_nr_runnable falls. The equality then breaks,
alb_break_llc() returns false, and active balance is free to pull a task
off its preferred LLC. Active balance only moves runnable tasks, and this
is the only LLC check it consults: once the stopper runs, LBF_ACTIVE_LB
skips the per-task test in can_migrate_task(). The runnable set is the one
we want.
Fix it on the counter side. A task should be counted in
nr_pref_llc_running exactly while it is both queued on its preferred LLC
(pref_llc_queued) and runnable (!sched_delayed). Define that membership
once in task_pref_llc_runnable(), and adjust the counter only through
pref_llc_running_inc()/pref_llc_running_dec() from the four sites that
change either input: account_llc_enqueue(), account_llc_dequeue(),
set_delayed() and clear_delayed(). Gating every update on the same
predicate keeps the delay, wake and dequeue paths from double-counting
or underflowing; see the comments at those sites for the ordering.
nr_llc_running and sd->llc_counts are not touched and stay on queued
semantics.
Fixes: 714059f79ff0 ("sched/cache: Handle moving single tasks to/from their preferred LLC")
Closes: https://lore.kernel.org/lkml/20260827135000.735138-1-zhanxusheng@xiaomi.com/
Reported-by: Zhan Xusheng <zhanxusheng@xiaomi.com>
Suggested-by: Chen Yu <yu.c.chen@intel.com>
Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Kayra Cizmeci <kayracizmeci@gmail.com>
Cc: <stable@kernel.org> # v7.2.x
Link: https://patch.msgid.link/06af61afedac32e6477f57feb4d658f6c411c3af.1790035273.git.tim.c.chen@linux.intel.com
Diffstat (limited to 'tools/testing/selftests/splice')
| -rw-r--r-- | tools/testing/selftests/splice/.gitignore | 3 | ||||
| -rw-r--r-- | tools/testing/selftests/splice/Makefile | 5 | ||||
| -rw-r--r-- | tools/testing/selftests/splice/config | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/splice/default_file_splice_read.c | 9 | ||||
| -rwxr-xr-x | tools/testing/selftests/splice/default_file_splice_read.sh | 8 | ||||
| -rw-r--r-- | tools/testing/selftests/splice/settings | 1 | ||||
| -rwxr-xr-x | tools/testing/selftests/splice/short_splice_read.sh | 133 | ||||
| -rw-r--r-- | tools/testing/selftests/splice/splice_read.c | 57 |
8 files changed, 217 insertions, 0 deletions
diff --git a/tools/testing/selftests/splice/.gitignore b/tools/testing/selftests/splice/.gitignore new file mode 100644 index 000000000..be8266f5d --- /dev/null +++ b/tools/testing/selftests/splice/.gitignore @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +default_file_splice_read +splice_read diff --git a/tools/testing/selftests/splice/Makefile b/tools/testing/selftests/splice/Makefile new file mode 100644 index 000000000..541cd826d --- /dev/null +++ b/tools/testing/selftests/splice/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +TEST_PROGS := default_file_splice_read.sh short_splice_read.sh +TEST_GEN_PROGS_EXTENDED := default_file_splice_read splice_read + +include ../lib.mk diff --git a/tools/testing/selftests/splice/config b/tools/testing/selftests/splice/config new file mode 100644 index 000000000..058c92836 --- /dev/null +++ b/tools/testing/selftests/splice/config @@ -0,0 +1 @@ +CONFIG_TEST_LKM=m diff --git a/tools/testing/selftests/splice/default_file_splice_read.c b/tools/testing/selftests/splice/default_file_splice_read.c new file mode 100644 index 000000000..a3c6e5672 --- /dev/null +++ b/tools/testing/selftests/splice/default_file_splice_read.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include <fcntl.h> + +int main(int argc, char **argv) +{ + splice(0, 0, 1, 0, 1<<30, 0); + return 0; +} diff --git a/tools/testing/selftests/splice/default_file_splice_read.sh b/tools/testing/selftests/splice/default_file_splice_read.sh new file mode 100755 index 000000000..490db5a2e --- /dev/null +++ b/tools/testing/selftests/splice/default_file_splice_read.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +n=`./default_file_splice_read </dev/null | wc -c` + +test "$n" = 0 && exit 0 + +echo "default_file_splice_read broken: leaked $n" +exit 1 diff --git a/tools/testing/selftests/splice/settings b/tools/testing/selftests/splice/settings new file mode 100644 index 000000000..89cedfc0d --- /dev/null +++ b/tools/testing/selftests/splice/settings @@ -0,0 +1 @@ +timeout=5 diff --git a/tools/testing/selftests/splice/short_splice_read.sh b/tools/testing/selftests/splice/short_splice_read.sh new file mode 100755 index 000000000..4710e09f4 --- /dev/null +++ b/tools/testing/selftests/splice/short_splice_read.sh @@ -0,0 +1,133 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Test for mishandling of splice() on pseudofilesystems, which should catch +# bugs like 11990a5bd7e5 ("module: Correctly truncate sysfs sections output") +# +# Since splice fallback was removed as part of the set_fs() rework, many of these +# tests expect to fail now. See https://lore.kernel.org/lkml/202009181443.C2179FB@keescook/ +set -e + +DIR=$(dirname "$0") + +ret=0 + +expect_success() +{ + title="$1" + shift + + echo "" >&2 + echo "$title ..." >&2 + + set +e + "$@" + rc=$? + set -e + + case "$rc" in + 0) + echo "ok: $title succeeded" >&2 + ;; + 1) + echo "FAIL: $title should work" >&2 + ret=$(( ret + 1 )) + ;; + *) + echo "FAIL: something else went wrong" >&2 + ret=$(( ret + 1 )) + ;; + esac +} + +expect_failure() +{ + title="$1" + shift + + echo "" >&2 + echo "$title ..." >&2 + + set +e + "$@" + rc=$? + set -e + + case "$rc" in + 0) + echo "FAIL: $title unexpectedly worked" >&2 + ret=$(( ret + 1 )) + ;; + 1) + echo "ok: $title correctly failed" >&2 + ;; + *) + echo "FAIL: something else went wrong" >&2 + ret=$(( ret + 1 )) + ;; + esac +} + +do_splice() +{ + filename="$1" + bytes="$2" + expected="$3" + report="$4" + + out=$("$DIR"/splice_read "$filename" "$bytes" | cat) + if [ "$out" = "$expected" ] ; then + echo " matched $report" >&2 + return 0 + else + echo " no match: '$out' vs $report" >&2 + return 1 + fi +} + +test_splice() +{ + filename="$1" + + echo " checking $filename ..." >&2 + + full=$(cat "$filename") + rc=$? + if [ $rc -ne 0 ] ; then + return 2 + fi + + two=$(echo "$full" | grep -m1 . | cut -c-2) + + # Make sure full splice has the same contents as a standard read. + echo " splicing 4096 bytes ..." >&2 + if ! do_splice "$filename" 4096 "$full" "full read" ; then + return 1 + fi + + # Make sure a partial splice see the first two characters. + echo " splicing 2 bytes ..." >&2 + if ! do_splice "$filename" 2 "$two" "'$two'" ; then + return 1 + fi + + return 0 +} + +### /proc/$pid/ has no splice interface; these should all fail. +expect_failure "proc_single_open(), seq_read() splice" test_splice /proc/$$/limits +expect_failure "special open(), seq_read() splice" test_splice /proc/$$/comm + +### /proc/sys/ has a splice interface; these should all succeed. +expect_success "proc_handler: proc_dointvec_minmax() splice" test_splice /proc/sys/fs/nr_open +expect_success "proc_handler: proc_dostring() splice" test_splice /proc/sys/kernel/modprobe +expect_success "proc_handler: special read splice" test_splice /proc/sys/kernel/version + +### /sys/ has no splice interface; these should all fail. +if ! [ -d /sys/module/test_module/sections ] ; then + expect_success "test_module kernel module load" modprobe test_module +fi +expect_success "kernfs attr splice" test_splice /sys/module/test_module/coresize +expect_success "kernfs binattr splice" test_splice /sys/module/test_module/sections/.init.text + +exit $ret diff --git a/tools/testing/selftests/splice/splice_read.c b/tools/testing/selftests/splice/splice_read.c new file mode 100644 index 000000000..46dae6a25 --- /dev/null +++ b/tools/testing/selftests/splice/splice_read.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +int main(int argc, char *argv[]) +{ + int fd; + size_t size; + ssize_t spliced; + + if (argc < 2) { + fprintf(stderr, "Usage: %s INPUT [BYTES]\n", argv[0]); + return EXIT_FAILURE; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + perror(argv[1]); + return EXIT_FAILURE; + } + + if (argc == 3) + size = atol(argv[2]); + else { + struct stat statbuf; + + if (fstat(fd, &statbuf) < 0) { + perror(argv[1]); + return EXIT_FAILURE; + } + + if (statbuf.st_size > INT_MAX) { + fprintf(stderr, "%s: Too big\n", argv[1]); + return EXIT_FAILURE; + } + + size = statbuf.st_size; + } + + /* splice(2) file to stdout. */ + spliced = splice(fd, NULL, STDOUT_FILENO, NULL, + size, SPLICE_F_MOVE); + if (spliced < 0) { + perror("splice"); + return EXIT_FAILURE; + } + + close(fd); + return EXIT_SUCCESS; +} |
