...
1#!/usr/bin/env bash
2# Test the current package under a different kernel.
3# Requires virtme and qemu to be installed.
4# Examples:
5# Run all tests on a 5.4 kernel
6# $ ./run-tests.sh 5.4
7# Run a subset of tests:
8# $ ./run-tests.sh 5.4 ./link
9
10set -euo pipefail
11
12script="$(realpath "$0")"
13readonly script
14
15# This script is a bit like a Matryoshka doll since it keeps re-executing itself
16# in various different contexts:
17#
18# 1. invoked by the user like run-tests.sh 5.4
19# 2. invoked by go test like run-tests.sh --exec-vm
20# 3. invoked by init in the vm like run-tests.sh --exec-test
21#
22# This allows us to use all available CPU on the host machine to compile our
23# code, and then only use the VM to execute the test. This is because the VM
24# is usually slower at compiling than the host.
25if [[ "${1:-}" = "--exec-vm" ]]; then
26 shift
27
28 input="$1"
29 shift
30
31 # Use sudo if /dev/kvm isn't accessible by the current user.
32 sudo=""
33 if [[ ! -r /dev/kvm || ! -w /dev/kvm ]]; then
34 sudo="sudo"
35 fi
36 readonly sudo
37
38 testdir="$(dirname "$1")"
39 output="$(mktemp -d)"
40 printf -v cmd "%q " "$@"
41
42 if [[ "$(stat -c '%t:%T' -L /proc/$$/fd/0)" == "1:3" ]]; then
43 # stdin is /dev/null, which doesn't play well with qemu. Use a fifo as a
44 # blocking substitute.
45 mkfifo "${output}/fake-stdin"
46 # Open for reading and writing to avoid blocking.
47 exec 0<> "${output}/fake-stdin"
48 rm "${output}/fake-stdin"
49 fi
50
51 for ((i = 0; i < 3; i++)); do
52 if ! $sudo virtme-run --kimg "${input}/bzImage" --memory 768M --pwd \
53 --rwdir="${testdir}=${testdir}" \
54 --rodir=/run/input="${input}" \
55 --rwdir=/run/output="${output}" \
56 --script-sh "PATH=\"$PATH\" CI_MAX_KERNEL_VERSION="${CI_MAX_KERNEL_VERSION:-}" \"$script\" --exec-test $cmd" \
57 --kopt possible_cpus=2; then # need at least two CPUs for some tests
58 exit 23
59 fi
60
61 if [[ -e "${output}/status" ]]; then
62 break
63 fi
64
65 if [[ -v CI ]]; then
66 echo "Retrying test run due to qemu crash"
67 continue
68 fi
69
70 exit 42
71 done
72
73 rc=$(<"${output}/status")
74 $sudo rm -r "$output"
75 exit $rc
76elif [[ "${1:-}" = "--exec-test" ]]; then
77 shift
78
79 mount -t bpf bpf /sys/fs/bpf
80 mount -t tracefs tracefs /sys/kernel/debug/tracing
81
82 if [[ -d "/run/input/bpf" ]]; then
83 export KERNEL_SELFTESTS="/run/input/bpf"
84 fi
85
86 if [[ -f "/run/input/bpf/bpf_testmod/bpf_testmod.ko" ]]; then
87 insmod "/run/input/bpf/bpf_testmod/bpf_testmod.ko"
88 fi
89
90 dmesg --clear
91 rc=0
92 "$@" || rc=$?
93 dmesg
94 echo $rc > "/run/output/status"
95 exit $rc # this return code is "swallowed" by qemu
96fi
97
98readonly kernel_version="${1:-}"
99if [[ -z "${kernel_version}" ]]; then
100 echo "Expecting kernel version as first argument"
101 exit 1
102fi
103shift
104
105readonly kernel="linux-${kernel_version}.bz"
106readonly selftests="linux-${kernel_version}-selftests-bpf.tgz"
107readonly input="$(mktemp -d)"
108readonly tmp_dir="${TMPDIR:-/tmp}"
109readonly branch="${BRANCH:-master}"
110
111fetch() {
112 echo Fetching "${1}"
113 pushd "${tmp_dir}" > /dev/null
114 curl -s -L -O --fail --etag-compare "${1}.etag" --etag-save "${1}.etag" "https://github.com/cilium/ci-kernels/raw/${branch}/${1}"
115 local ret=$?
116 popd > /dev/null
117 return $ret
118}
119
120fetch "${kernel}"
121cp "${tmp_dir}/${kernel}" "${input}/bzImage"
122
123if fetch "${selftests}"; then
124 echo "Decompressing selftests"
125 mkdir "${input}/bpf"
126 tar --strip-components=4 -xf "${tmp_dir}/${selftests}" -C "${input}/bpf"
127else
128 echo "No selftests found, disabling"
129fi
130
131args=(-short -coverpkg=./... -coverprofile=coverage.out -count 1 ./...)
132if (( $# > 0 )); then
133 args=("$@")
134fi
135
136export GOFLAGS=-mod=readonly
137export CGO_ENABLED=0
138# LINUX_VERSION_CODE test compares this to discovered value.
139export KERNEL_VERSION="${kernel_version}"
140
141echo Testing on "${kernel_version}"
142go test -exec "$script --exec-vm $input" "${args[@]}"
143echo "Test successful on ${kernel_version}"
144
145rm -r "${input}"
View as plain text