diff --git a/test/vhost/irqs.sh b/test/vhost/irqs.sh new file mode 100644 index 000000000..d8ca3a6b3 --- /dev/null +++ b/test/vhost/irqs.sh @@ -0,0 +1,257 @@ +# 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#,}" +}