Spdk/test/vhost/irqs.sh
Michal Berger 360190f9e2 test/vhost: Add helper functions for extracting IRQ data
Signed-off-by: Michal Berger <michal.berger@intel.com>
Change-Id: If75aeca0c44667ef02b72f2e4a9141da4057d291
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15459
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Karol Latecki <karol.latecki@intel.com>
Reviewed-by: Shuhei Matsumoto <smatsumoto@nvidia.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Kamil Godzwon <kamilx.godzwon@intel.com>
2023-05-09 17:58:11 +08:00

258 lines
6.2 KiB
Bash

# SPDX-License-Identifier: BSD-3-Clause
# Copyright (C) 2022 Intel Corporation.
# All rights reserved.
source "$rootdir/test/scheduler/common.sh"
declare -a irqs_counters=()
declare -a irqs_devices=()
declare -a irqs_types=()
declare -a cpus=()
_get_cpus() {
local state=${1:-online}
_get_override_cpus || "_get_${state}_cpus"
}
_get_override_cpus() {
((${#cpus_override[@]} > 0)) || return 1
fold_list_onto_array cpus "${cpus_override[@]}"
}
_get_online_cpus() {
fold_list_onto_array cpus $(get_online_cpus)
}
_get_offline_cpus() {
fold_list_onto_array cpus $(get_offline_cpus)
}
get_all_irqs() {
local irqs=() s_irqs=()
irqs=(/proc/irq/+([0-9]))
irqs=("${irqs[@]##*/}")
# Sort it
fold_list_onto_array s_irqs "${irqs[@]}"
printf '%u\n' "${s_irqs[@]}"
}
check_this_irq() {
local irq=${1:-0}
((${#irqs_to_lookup[@]} > 0)) || return 0
[[ -n ${irqs_to_lookup[irq]} ]]
}
update_irqs_sysfs() {
local sysroot=${1:-}
local input=$sysroot/sys/kernel/irq
[[ -e $input ]] || return 1
# per_cpu_count holds a list of all cpus, regardless of their state
_get_cpus online
_get_cpus offline
local irq_path
for irq_path in "$input/"*; do
irq=${irq_path##*/}
check_this_irq "$irq" || continue
IFS="," read -ra cpu_counters < "$irq_path/per_cpu_count"
for cpu in "${cpus[@]}"; do
eval "_irq${irq}_cpu${cpu}+=(${cpu_counters[cpu]})"
eval "_irq${irq}_counter[cpu]=_irq${irq}_cpu${cpu}[@]"
done
irqs_counters[irq]="_irq${irq}_counter[@]"
irqs_devices[irq]=$(< "$irq_path/actions")
irqs_types[irq]="$(< "$irq_path/chip_name") $(< "$irq_path/hwirq")-$(< "$irq_path/name")"
done
}
update_irqs_procfs() {
local input=${1:-/proc/interrupts}
[[ -e $input ]] || return 1
# /proc/interrupts shows only online CPUs. Use get_cpus() to get readings for
# proper CPUs rather than parsing the actual header of the file.
_get_cpus online
local counter_idx
while read -ra irqs; do
irq=${irqs[0]%:*}
[[ $irq == +([0-9]) ]] || continue
check_this_irq "$irq" || continue
cpu_counters=("${irqs[@]:1:${#cpus[@]}}") counter_idx=0
for cpu in "${cpus[@]}"; do
eval "_irq${irq}_cpu${cpu}+=(${cpu_counters[counter_idx++]})"
eval "_irq${irq}_counter[cpu]=_irq${irq}_cpu${cpu}[@]"
done
irqs_counters[irq]="_irq${irq}_counter[@]"
irqs_devices[irq]=${irqs[*]:${#cpus[@]}+1:2}
irqs_types[irq]=${irqs[*]:${#cpus[@]}+3}
done < "$input"
}
update_irqs() {
local irqs irq
local cpu cpu_counters=()
local irqs_to_lookup=()
fold_list_onto_array irqs_to_lookup "$@"
update_irqs_sysfs || update_irqs_procfs
}
get_irqs() {
local irqs=("$@") irq cpu
local _counters counters counter delta total
# If cpus[@] are not init, update was not run so nothing to check
((${#cpus[@]} > 0)) || return 1
if ((${#irqs[@]} == 0)); then
irqs=($(get_all_irqs))
fi
for irq in "${irqs[@]}"; do
for cpu in "${cpus[@]}"; do
[[ -v "_irq${irq}_cpu${cpu}[@]" ]] || continue
local -n counters="_irq${irq}_cpu${cpu}"
# keep a separate copy to not touch the main _irq*[]
_counters=("${counters[@]}") total=0
if ((${#counters[@]} > 1)); then
# Enhance output with calculating deltas between each reading
for ((counter = 0; counter < ${#counters[@]} - 1; counter++)); do
delta=$((counters[counter + 1] - counters[counter]))
_counters[counter + 1]="${counters[counter + 1]} (+$delta)"
: $((total += delta))
done
fi
_counters+=("==$total")
# Ignore idle irqs unless request from the env tells otherwise
if [[ -n $SHOW_ALL_IRQS ]] || ((total > 0)); then
echo "irq$irq->$(get_irq_type "$irq")->$(get_irq_device "$irq")@cpu$cpu:"
printf ' %s\n' "${_counters[@]}"
fi
done
done
}
get_irq_type() {
[[ -n ${irqs_types[$1]} ]] || return 1
echo "${irqs_types[$1]}"
}
get_irq_device() {
[[ -n ${irqs_devices[$1]} ]] || return 1
echo "${irqs_devices[$1]}"
}
reset_irqs() {
irqs_counters=() irqs_devices=() irqs_types=() cpus=()
unset -v "${!_irq@}"
}
read_irq_cpu_mask() {
local irq=$1 mask=$2
[[ -n $irq && -e /proc/irq/$irq || -n $mask ]] || return 1
# smp_affinity holds a string of comma-separated 32-bit values. Iterate
# over each dWORD and extract cpus bit by bit. Iterate from the end of
# the array as that's where the first dWORD is located.
local smp_affinity
local bit dword dword_l=32 dword_idx
local cpus=()
if [[ -n $mask ]]; then
IFS="," read -ra smp_affinity <<< "$mask"
else
IFS="," read -ra smp_affinity < "/proc/irq/$irq/smp_affinity"
fi
smp_affinity=("${smp_affinity[@]/#/0x}")
for ((dword = ${#smp_affinity[@]} - 1, dword_idx = 0; dword >= 0; dword--, dword_idx++)); do
bit=-1
while ((++bit < dword_l)); do
if ((smp_affinity[dword] & 1 << bit)); then
cpus[bit + dword_l * dword_idx]=$bit
fi
done
done
printf '%u\n' "${!cpus[@]}"
}
read_irq_cpu_list() {
local irq=$1 effective=${2:-0}
[[ -n $irq && -e /proc/irq/$irq ]] || return 1
if ((effective)); then
parse_cpu_list "/proc/irq/$irq/effective_affinity_list"
else
parse_cpu_list "/proc/irq/$irq/smp_affinity_list"
fi
}
build_irq_cpu_mask() {
local cpu dword_l=32 dword_idx dword idxs
local _mask=() mask=""
for cpu; do
dword_idx=$((cpu / dword_l))
((_mask[dword_idx] |= 1 << (cpu - dword_l * dword_idx)))
done
# Store sorted list of dword indexes that we got
idxs=("${!_mask[@]}")
# Fill out all dWORDs starting from the highest (last) dword index
for ((dword = idxs[-1]; dword >= 0; dword--)); do
_mask[dword]=$(printf '%08x' "${_mask[dword]}")
mask=${mask:+$mask,}${_mask[dword]}
done
echo "$mask"
}
squash_irq_cpu_mask() {
local mask
mask=$(build_irq_cpu_mask "$@")
# E.g.: 1,32,64,65,77,88,127 -> 0x80000000010020030000000100000002
# Valid under DPDK
echo "0x${mask//,/}"
}
unsquash_irq_cpu_mask() {
# E.g.: 0x80000000010020030000000100000002 -> 80000000,01002003,00000001,00000002
# 8 is a max number of chars in a dWORD represented in hex.
local smask=$1 _smask="" smask_l _smask_l
smask=${smask/0x/} smask_l=$((${#smask} / 8)) _smask_l=$smask_l
((smask_l == 0)) && echo "$smask" && return 0
# Put comma at a right index
while ((_smask_l)); do
_smask+=,${smask:${#smask}-_smask_l--*8:8}
done
# Add remaining chars if any
_smask=${smask::${#smask}-8*smask_l}${_smask}
# If there were no chars left, drop the ',' from the beginning of the string
echo "${_smask#,}"
}