Commit f8a085a2 authored by Michal Berger's avatar Michal Berger Committed by Jim Harris
Browse files

test/vhost: Add helper functions for extracting IRQ data

parent 78df9be4
Loading
Loading
Loading
Loading

test/vhost/irqs.sh

0 → 100644
+257 −0
Original line number Diff line number Diff line
#  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#,}"
}