]> Git Repo - linux.git/blob - tools/testing/selftests/bpf/vmtest.sh
scsi: zfcp: Trace when request remove fails after qdio send fails
[linux.git] / tools / testing / selftests / bpf / vmtest.sh
1 #!/bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3
4 set -u
5 set -e
6
7 # This script currently only works for x86_64 and s390x, as
8 # it is based on the VM image used by the BPF CI, which is
9 # available only for these architectures.
10 ARCH="$(uname -m)"
11 case "${ARCH}" in
12 s390x)
13         QEMU_BINARY=qemu-system-s390x
14         QEMU_CONSOLE="ttyS1"
15         QEMU_FLAGS=(-smp 2)
16         BZIMAGE="arch/s390/boot/compressed/vmlinux"
17         ;;
18 x86_64)
19         QEMU_BINARY=qemu-system-x86_64
20         QEMU_CONSOLE="ttyS0,115200"
21         QEMU_FLAGS=(-cpu host -smp 8)
22         BZIMAGE="arch/x86/boot/bzImage"
23         ;;
24 aarch64)
25         QEMU_BINARY=qemu-system-aarch64
26         QEMU_CONSOLE="ttyAMA0,115200"
27         QEMU_FLAGS=(-M virt,gic-version=3 -cpu host -smp 8)
28         BZIMAGE="arch/arm64/boot/Image"
29         ;;
30 *)
31         echo "Unsupported architecture"
32         exit 1
33         ;;
34 esac
35 DEFAULT_COMMAND="./test_progs"
36 MOUNT_DIR="mnt"
37 ROOTFS_IMAGE="root.img"
38 OUTPUT_DIR="$HOME/.bpf_selftests"
39 KCONFIG_REL_PATHS=("tools/testing/selftests/bpf/config" "tools/testing/selftests/bpf/config.${ARCH}")
40 INDEX_URL="https://raw.githubusercontent.com/libbpf/ci/master/INDEX"
41 NUM_COMPILE_JOBS="$(nproc)"
42 LOG_FILE_BASE="$(date +"bpf_selftests.%Y-%m-%d_%H-%M-%S")"
43 LOG_FILE="${LOG_FILE_BASE}.log"
44 EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status"
45
46 usage()
47 {
48         cat <<EOF
49 Usage: $0 [-i] [-s] [-d <output_dir>] -- [<command>]
50
51 <command> is the command you would normally run when you are in
52 tools/testing/selftests/bpf. e.g:
53
54         $0 -- ./test_progs -t test_lsm
55
56 If no command is specified and a debug shell (-s) is not requested,
57 "${DEFAULT_COMMAND}" will be run by default.
58
59 If you build your kernel using KBUILD_OUTPUT= or O= options, these
60 can be passed as environment variables to the script:
61
62   O=<kernel_build_path> $0 -- ./test_progs -t test_lsm
63
64 or
65
66   KBUILD_OUTPUT=<kernel_build_path> $0 -- ./test_progs -t test_lsm
67
68 Options:
69
70         -i)             Update the rootfs image with a newer version.
71         -d)             Update the output directory (default: ${OUTPUT_DIR})
72         -j)             Number of jobs for compilation, similar to -j in make
73                         (default: ${NUM_COMPILE_JOBS})
74         -s)             Instead of powering off the VM, start an interactive
75                         shell. If <command> is specified, the shell runs after
76                         the command finishes executing
77 EOF
78 }
79
80 unset URLS
81 populate_url_map()
82 {
83         if ! declare -p URLS &> /dev/null; then
84                 # URLS contain the mapping from file names to URLs where
85                 # those files can be downloaded from.
86                 declare -gA URLS
87                 while IFS=$'\t' read -r name url; do
88                         URLS["$name"]="$url"
89                 done < <(curl -Lsf ${INDEX_URL})
90         fi
91 }
92
93 download()
94 {
95         local file="$1"
96
97         if [[ ! -v URLS[$file] ]]; then
98                 echo "$file not found" >&2
99                 return 1
100         fi
101
102         echo "Downloading $file..." >&2
103         curl -Lsf "${URLS[$file]}" "${@:2}"
104 }
105
106 newest_rootfs_version()
107 {
108         {
109         for file in "${!URLS[@]}"; do
110                 if [[ $file =~ ^"${ARCH}"/libbpf-vmtest-rootfs-(.*)\.tar\.zst$ ]]; then
111                         echo "${BASH_REMATCH[1]}"
112                 fi
113         done
114         } | sort -rV | head -1
115 }
116
117 download_rootfs()
118 {
119         local rootfsversion="$1"
120         local dir="$2"
121
122         if ! which zstd &> /dev/null; then
123                 echo 'Could not find "zstd" on the system, please install zstd'
124                 exit 1
125         fi
126
127         download "${ARCH}/libbpf-vmtest-rootfs-$rootfsversion.tar.zst" |
128                 zstd -d | sudo tar -C "$dir" -x
129 }
130
131 recompile_kernel()
132 {
133         local kernel_checkout="$1"
134         local make_command="$2"
135
136         cd "${kernel_checkout}"
137
138         ${make_command} olddefconfig
139         ${make_command}
140 }
141
142 mount_image()
143 {
144         local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
145         local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
146
147         sudo mount -o loop "${rootfs_img}" "${mount_dir}"
148 }
149
150 unmount_image()
151 {
152         local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
153
154         sudo umount "${mount_dir}" &> /dev/null
155 }
156
157 update_selftests()
158 {
159         local kernel_checkout="$1"
160         local selftests_dir="${kernel_checkout}/tools/testing/selftests/bpf"
161
162         cd "${selftests_dir}"
163         ${make_command}
164
165         # Mount the image and copy the selftests to the image.
166         mount_image
167         sudo rm -rf "${mount_dir}/root/bpf"
168         sudo cp -r "${selftests_dir}" "${mount_dir}/root"
169         unmount_image
170 }
171
172 update_init_script()
173 {
174         local init_script_dir="${OUTPUT_DIR}/${MOUNT_DIR}/etc/rcS.d"
175         local init_script="${init_script_dir}/S50-startup"
176         local command="$1"
177         local exit_command="$2"
178
179         mount_image
180
181         if [[ ! -d "${init_script_dir}" ]]; then
182                 cat <<EOF
183 Could not find ${init_script_dir} in the mounted image.
184 This likely indicates a bad rootfs image, Please download
185 a new image by passing "-i" to the script
186 EOF
187                 exit 1
188
189         fi
190
191         sudo bash -c "echo '#!/bin/bash' > ${init_script}"
192
193         if [[ "${command}" != "" ]]; then
194                 sudo bash -c "cat >>${init_script}" <<EOF
195 # Have a default value in the exit status file
196 # incase the VM is forcefully stopped.
197 echo "130" > "/root/${EXIT_STATUS_FILE}"
198
199 {
200         cd /root/bpf
201         echo ${command}
202         stdbuf -oL -eL ${command}
203         echo "\$?" > "/root/${EXIT_STATUS_FILE}"
204 } 2>&1 | tee "/root/${LOG_FILE}"
205 # Ensure that the logs are written to disk
206 sync
207 EOF
208         fi
209
210         sudo bash -c "echo ${exit_command} >> ${init_script}"
211         sudo chmod a+x "${init_script}"
212         unmount_image
213 }
214
215 create_vm_image()
216 {
217         local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
218         local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
219
220         rm -rf "${rootfs_img}"
221         touch "${rootfs_img}"
222         chattr +C "${rootfs_img}" >/dev/null 2>&1 || true
223
224         truncate -s 2G "${rootfs_img}"
225         mkfs.ext4 -q "${rootfs_img}"
226
227         mount_image
228         download_rootfs "$(newest_rootfs_version)" "${mount_dir}"
229         unmount_image
230 }
231
232 run_vm()
233 {
234         local kernel_bzimage="$1"
235         local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
236
237         if ! which "${QEMU_BINARY}" &> /dev/null; then
238                 cat <<EOF
239 Could not find ${QEMU_BINARY}
240 Please install qemu or set the QEMU_BINARY environment variable.
241 EOF
242                 exit 1
243         fi
244
245         ${QEMU_BINARY} \
246                 -nodefaults \
247                 -display none \
248                 -serial mon:stdio \
249                 "${QEMU_FLAGS[@]}" \
250                 -enable-kvm \
251                 -m 4G \
252                 -drive file="${rootfs_img}",format=raw,index=1,media=disk,if=virtio,cache=none \
253                 -kernel "${kernel_bzimage}" \
254                 -append "root=/dev/vda rw console=${QEMU_CONSOLE}"
255 }
256
257 copy_logs()
258 {
259         local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
260         local log_file="${mount_dir}/root/${LOG_FILE}"
261         local exit_status_file="${mount_dir}/root/${EXIT_STATUS_FILE}"
262
263         mount_image
264         sudo cp ${log_file} "${OUTPUT_DIR}"
265         sudo cp ${exit_status_file} "${OUTPUT_DIR}"
266         sudo rm -f ${log_file}
267         unmount_image
268 }
269
270 is_rel_path()
271 {
272         local path="$1"
273
274         [[ ${path:0:1} != "/" ]]
275 }
276
277 do_update_kconfig()
278 {
279         local kernel_checkout="$1"
280         local kconfig_file="$2"
281
282         rm -f "$kconfig_file" 2> /dev/null
283
284         for config in "${KCONFIG_REL_PATHS[@]}"; do
285                 local kconfig_src="${kernel_checkout}/${config}"
286                 cat "$kconfig_src" >> "$kconfig_file"
287         done
288 }
289
290 update_kconfig()
291 {
292         local kernel_checkout="$1"
293         local kconfig_file="$2"
294
295         if [[ -f "${kconfig_file}" ]]; then
296                 local local_modified="$(stat -c %Y "${kconfig_file}")"
297
298                 for config in "${KCONFIG_REL_PATHS[@]}"; do
299                         local kconfig_src="${kernel_checkout}/${config}"
300                         local src_modified="$(stat -c %Y "${kconfig_src}")"
301                         # Only update the config if it has been updated after the
302                         # previously cached config was created. This avoids
303                         # unnecessarily compiling the kernel and selftests.
304                         if [[ "${src_modified}" -gt "${local_modified}" ]]; then
305                                 do_update_kconfig "$kernel_checkout" "$kconfig_file"
306                                 # Once we have found one outdated configuration
307                                 # there is no need to check other ones.
308                                 break
309                         fi
310                 done
311         else
312                 do_update_kconfig "$kernel_checkout" "$kconfig_file"
313         fi
314 }
315
316 catch()
317 {
318         local exit_code=$1
319         local exit_status_file="${OUTPUT_DIR}/${EXIT_STATUS_FILE}"
320         # This is just a cleanup and the directory may
321         # have already been unmounted. So, don't let this
322         # clobber the error code we intend to return.
323         unmount_image || true
324         if [[ -f "${exit_status_file}" ]]; then
325                 exit_code="$(cat ${exit_status_file})"
326         fi
327         exit ${exit_code}
328 }
329
330 main()
331 {
332         local script_dir="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
333         local kernel_checkout=$(realpath "${script_dir}"/../../../../)
334         # By default the script searches for the kernel in the checkout directory but
335         # it also obeys environment variables O= and KBUILD_OUTPUT=
336         local kernel_bzimage="${kernel_checkout}/${BZIMAGE}"
337         local command="${DEFAULT_COMMAND}"
338         local update_image="no"
339         local exit_command="poweroff -f"
340         local debug_shell="no"
341
342         while getopts ':hskid:j:' opt; do
343                 case ${opt} in
344                 i)
345                         update_image="yes"
346                         ;;
347                 d)
348                         OUTPUT_DIR="$OPTARG"
349                         ;;
350                 j)
351                         NUM_COMPILE_JOBS="$OPTARG"
352                         ;;
353                 s)
354                         command=""
355                         debug_shell="yes"
356                         exit_command="bash"
357                         ;;
358                 h)
359                         usage
360                         exit 0
361                         ;;
362                 \? )
363                         echo "Invalid Option: -$OPTARG"
364                         usage
365                         exit 1
366                         ;;
367                 : )
368                         echo "Invalid Option: -$OPTARG requires an argument"
369                         usage
370                         exit 1
371                         ;;
372                 esac
373         done
374         shift $((OPTIND -1))
375
376         trap 'catch "$?"' EXIT
377
378         if [[ $# -eq 0  && "${debug_shell}" == "no" ]]; then
379                 echo "No command specified, will run ${DEFAULT_COMMAND} in the vm"
380         else
381                 command="$@"
382         fi
383
384         local kconfig_file="${OUTPUT_DIR}/latest.config"
385         local make_command="make -j ${NUM_COMPILE_JOBS} KCONFIG_CONFIG=${kconfig_file}"
386
387         # Figure out where the kernel is being built.
388         # O takes precedence over KBUILD_OUTPUT.
389         if [[ "${O:=""}" != "" ]]; then
390                 if is_rel_path "${O}"; then
391                         O="$(realpath "${PWD}/${O}")"
392                 fi
393                 kernel_bzimage="${O}/${BZIMAGE}"
394                 make_command="${make_command} O=${O}"
395         elif [[ "${KBUILD_OUTPUT:=""}" != "" ]]; then
396                 if is_rel_path "${KBUILD_OUTPUT}"; then
397                         KBUILD_OUTPUT="$(realpath "${PWD}/${KBUILD_OUTPUT}")"
398                 fi
399                 kernel_bzimage="${KBUILD_OUTPUT}/${BZIMAGE}"
400                 make_command="${make_command} KBUILD_OUTPUT=${KBUILD_OUTPUT}"
401         fi
402
403         populate_url_map
404
405         local rootfs_img="${OUTPUT_DIR}/${ROOTFS_IMAGE}"
406         local mount_dir="${OUTPUT_DIR}/${MOUNT_DIR}"
407
408         echo "Output directory: ${OUTPUT_DIR}"
409
410         mkdir -p "${OUTPUT_DIR}"
411         mkdir -p "${mount_dir}"
412         update_kconfig "${kernel_checkout}" "${kconfig_file}"
413
414         recompile_kernel "${kernel_checkout}" "${make_command}"
415
416         if [[ "${update_image}" == "no" && ! -f "${rootfs_img}" ]]; then
417                 echo "rootfs image not found in ${rootfs_img}"
418                 update_image="yes"
419         fi
420
421         if [[ "${update_image}" == "yes" ]]; then
422                 create_vm_image
423         fi
424
425         update_selftests "${kernel_checkout}" "${make_command}"
426         update_init_script "${command}" "${exit_command}"
427         run_vm "${kernel_bzimage}"
428         if [[ "${command}" != "" ]]; then
429                 copy_logs
430                 echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}"
431         fi
432 }
433
434 main "$@"
This page took 0.058563 seconds and 4 git commands to generate.