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 /scripts/decodecode | |
| 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 'scripts/decodecode')
| -rwxr-xr-x | scripts/decodecode | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/scripts/decodecode b/scripts/decodecode new file mode 100755 index 000000000..01d25dc11 --- /dev/null +++ b/scripts/decodecode @@ -0,0 +1,251 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Disassemble the Code: line in Linux oopses +# usage: decodecode < oops.file +# +# options: set env. variable AFLAGS=options to pass options to "as"; +# e.g., to decode an i386 oops on an x86_64 system, use: +# AFLAGS=--32 decodecode < 386.oops +# PC=hex - the PC (program counter) the oops points to + +faultlinenum=1 + +cleanup() { + rm -f $T $T.s $T.o $T.oo $T.aa $T.dis +} + +die() { + echo "$@" + exit 1 +} + +trap cleanup EXIT + +T=`mktemp` || die "cannot create temp file" +code= +cont= + +while read i ; do + +case "$i" in +*Code:*) + code=$i + cont=yes + ;; +*) + [ -n "$cont" ] && { + xdump="$(echo $i | grep '^[[:xdigit:]<>[:space:]]\+$')" + if [ -n "$xdump" ]; then + code="$code $xdump" + else + cont= + fi + } + ;; +esac + +done + +if [ -z "$code" ]; then + rm $T + die "Code line not found" +fi + +echo $code +code=`echo $code | sed -e 's/.*Code: //'` + +width=`expr index "$code" ' '` +width=$((($width-1)/2)) +case $width in +1) type=byte ;; +2) type=2byte ;; +4) type=4byte ;; +esac + +if [ -z "$ARCH" ]; then + case `uname -m` in + aarch64*) ARCH=arm64 ;; + arm*) ARCH=arm ;; + loongarch*) ARCH=loongarch ;; + esac +fi + +# Params: (tmp_file, pc_sub) +disas() { + t=$1 + pc_sub=$2 + + ${CROSS_COMPILE}as $AFLAGS -o $t.o $t.s > /dev/null 2>&1 + + if [ "$ARCH" = "arm" ]; then + if [ $width -eq 2 ]; then + OBJDUMPFLAGS="-M force-thumb" + fi + + ${CROSS_COMPILE}strip $t.o + fi + + if [ "$ARCH" = "arm64" ]; then + if [ $width -eq 4 ]; then + type=inst + fi + + ${CROSS_COMPILE}strip $t.o + fi + + if [ "$ARCH" = "riscv" ]; then + OBJDUMPFLAGS="-M no-aliases --section=.text -D" + ${CROSS_COMPILE}strip $t.o + fi + + if [ "$ARCH" = "loongarch" ]; then + ${CROSS_COMPILE}strip $t.o + fi + + if [ $pc_sub -ne 0 ]; then + if [ $PC ]; then + adj_vma=$(( $PC - $pc_sub )) + OBJDUMPFLAGS="$OBJDUMPFLAGS --adjust-vma=$adj_vma" + fi + fi + + ${CROSS_COMPILE}objdump $OBJDUMPFLAGS -S $t.o | \ + grep -v "/tmp\|Disassembly\|\.text\|^$" > $t.dis 2>&1 +} + +# Match the maximum number of opcode bytes from @op_bytes contained within +# @opline +# +# Params: +# @op_bytes: The string of bytes from the Code: line +# @opline: The disassembled line coming from objdump +# +# Returns: +# The max number of opcode bytes from the beginning of @op_bytes which match +# the opcode bytes in the objdump line. +get_substr_opcode_bytes_num() +{ + local op_bytes=$1 + local opline=$2 + + local retval=0 + substr="" + + for opc in $op_bytes; + do + substr+="$opc" + + opcode="$substr" + if [ "$ARCH" = "riscv" ]; then + opcode=$(echo $opcode | tr ' ' '\n' | tac | tr -d '\n') + fi + + # return if opcode bytes do not match @opline anymore + if ! echo $opline | grep -q "$opcode"; + then + break + fi + + # add trailing space + substr+=" " + retval=$((retval+1)) + done + + return $retval +} + +# Return the line number in objdump output to where the IP marker in the Code: +# line points to +# +# Params: +# @all_code: code in bytes without the marker +# @dis_file: disassembled file +# @ip_byte: The byte to which the IP points to +get_faultlinenum() +{ + local all_code="$1" + local dis_file="$2" + + # num bytes including IP byte + local num_bytes_ip=$(( $3 + 1 * $width )) + + # Add the two header lines (we're counting from 1). + local retval=3 + + # remove marker + all_code=$(echo $all_code | sed -e 's/[<>()]//g') + + while read line + do + get_substr_opcode_bytes_num "$all_code" "$line" + ate_opcodes=$? + + if ! (( $ate_opcodes )); then + continue + fi + + num_bytes_ip=$((num_bytes_ip - ($ate_opcodes * $width) )) + if (( $num_bytes_ip <= 0 )); then + break + fi + + # Delete matched opcode bytes from all_code. For that, compute + # how many chars those opcodes are represented by and include + # trailing space. + # + # a byte is 2 chars, ate_opcodes is also the number of trailing + # spaces + del_chars=$(( ($ate_opcodes * $width * 2) + $ate_opcodes )) + + all_code=$(echo $all_code | sed -e "s!^.\{$del_chars\}!!") + + let "retval+=1" + + done < $dis_file + + return $retval +} + +marker=`expr index "$code" "\<"` +if [ $marker -eq 0 ]; then + marker=`expr index "$code" "\("` +fi + +touch $T.oo +if [ $marker -ne 0 ]; then + # How many bytes to subtract from the program counter + # in order to get to the beginning virtual address of the + # Code: + pc_sub=$(( (($marker - 1) / (2 * $width + 1)) * $width )) + echo All code >> $T.oo + echo ======== >> $T.oo + beforemark=`echo "$code"` + echo -n " .$type 0x" > $T.s + + echo $beforemark | sed -e 's/ /,0x/g; s/[<>()]//g' >> $T.s + + disas $T $pc_sub + + cat $T.dis >> $T.oo + + get_faultlinenum "$code" "$T.dis" $pc_sub + faultlinenum=$? + + # and fix code at-and-after marker + code=`echo "$code" | cut -c$((${marker} + 1))-` + + rm -f $T.o $T.s $T.dis +fi + +echo Code starting with the faulting instruction > $T.aa +echo =========================================== >> $T.aa +code=`echo $code | sed -e 's/\r//;s/ [<(]/ /;s/[>)] / /;s/ /,0x/g; s/[>)]$//'` +echo -n " .$type 0x" > $T.s +echo $code >> $T.s +disas $T 0 +cat $T.dis >> $T.aa + +cat $T.oo | sed -e "${faultlinenum}s/^\([^:]*:\)\(.*\)/\1\*\2\t\t<-- trapping instruction/" +echo +cat $T.aa +cleanup |
