#!/bin/bash ############################################################################ # # Script for running those SCST regression tests that can be run automatically. # # Copyright (C) 2008 Bart Van Assche # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # ############################################################################ ############################################################################ # This script performs the following actions: # - Creates a temporary directory for storing the output of the regression # tests. No existing files are modified by this script. # - Verifies whether the top-level *.patch files apply cleanly to the SCST # tree. # - Duplicates the entire source tree to the temporary directory and # compiles the SCST source code. # - Duplicates the entire source tree to the temporary directory, applies # the full-perf patches and again compiles the SCST source code. # - Checks whether the kernel version specified through option -k is present # in the directory specified through option -c. # - If the source code of the kernel specified through -k is not present, # download it. # - Convert the SCST source code into a kernel patch. # - Extract the kernel sources. # - Run checkpatch on the SCST kernel patch. # - Apply the SCST kernel patch to the kernel tree. # - Run 'make allmodconfig'. # - Run the sparse source code checker on the SCST directory. # - Run 'make headers_check'. # - Compile the kernel tree. # - Run 'make checkstack'. # - Run 'make namespacecheck'. # - Run 'make htmldocs'. # # Note: the results of the individual steps are not verified by this script # -- the output generated by the individual steps has to be verified by # reviewing the output files written into the temporary directory. ############################################################################ ######################## # Function definitions # ######################## function usage { echo "Usage: $0 [-c] [-h] [-k ] [-k ] ..." echo " -c - cache directory for Linux kernel tarballs." echo " -h - display this help information." echo " -k - kernel version to use during test." } # First three components of the kernel version number. function kernel_version { if [ "${1#[0-9]*.[0-9]*.[0-9]*.[0-9]*}" != "$1" ]; then echo "${1%.[0-9]*}" else echo "$1" fi } # Last component of the kernel version, or the empty string if $1 has only # three components. function patchlevel { if [ "${1#[0-9]*.[0-9]*.[0-9]*.}" = "$1" ]; then echo "" else echo "${1#[0-9]*.[0-9]*.[0-9]*.}" fi } # Create a linux-$1 tree in the current directory, where $1 is a kernel # version number with either three or four components. function extract_kernel_tree { local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" local tmpdir=kernel-tree-tmp-$$ rm -rf "linux-$1" "${tmpdir}" mkdir "${tmpdir}" || return $? ( cd "${tmpdir}" || return $? tar xjf "${kernel_sources}/linux-${kver}.tar.bz2" || return $? cd "linux-${kver}" || return $? if [ "${plevel}" != "" ]; then bzip2 -cd "${kernel_sources}/patch-$1.bz2" | patch -p1 -f -s || return $? fi cd .. mv "linux-${kver}" "../linux-$1" ) rmdir "${tmpdir}" } # Test whether the *.patch files in the SCST top-level directory apply cleanly # to the SCST tree. Does not modify any files nor produce any output files. function test_scst_tree_patches { echo "Testing whether the SCST patches apply cleanly to the SCST tree ..." for p in *.patch srpt/patches/scst_increase_max_tgt_cmds.patch do patch -p0 -f --dry-run -s <$p >&/dev/null \ || echo "ERROR: patch $p does not apply cleanly." done } # Copy the entire SCST source code tree except the regression-* directories # from "$1" into the current directory. function duplicate_scst_source_tree { if [ -e "$1/AskingQuestions" ]; then tar -C "$1" --exclude=regression-* -c -f - . | tar -x -f - else return 1 fi } # Compile the unpatched SCST source code. function compile_scst_unpatched { local scst="$PWD" local outputfile="${outputdir}/compilation-output-unpatched.txt" local workingdirectory="${outputdir}/scst-unpatched" echo "Testing whether the SCST tree compiles fine ..." ( if mkdir -p "${workingdirectory}" \ && cd "${workingdirectory}" \ && duplicate_scst_source_tree "${scst}" \ && (make -s clean \ && make -s scst iscsi-scst \ && if "${scst_local}" = "true" ; then make -C scst_local clean; fi \ && if "${scst_local}" = "true" ; then make -C scst_local -s ; fi \ && if "${mpt_scst}" = "true" ; then make -C mpt clean; fi \ && if "${mpt_scst}" = "true" ; then make -C mpt -s ; fi \ && make -C srpt -s clean \ && make -C srpt -s ) \ >& "${outputfile}" then true else echo "FAILED" fi ) } # Test out-of-tree compilation agains the kernel header files in # /lib/modules/$(uname -r)/build. function compile_scst_patched { local scst="$PWD" local outputfile="${outputdir}/compilation-output-patched.txt" local workingdirectory="${outputdir}/scst-patched" echo "Testing whether the full-perf SCST tree compiles fine ..." ( if mkdir -p "${workingdirectory}" \ && cd "${workingdirectory}" \ && duplicate_scst_source_tree "${scst}" \ && patch -p0 -f -s <"${scst}/iscsi-full_perf.patch" \ && patch -p0 -f -s <"${scst}/qla2x00t-full_perf.patch" \ && patch -p0 -f -s <"${scst}/scst-full_perf.patch" \ && (make -s clean \ && make -s scst iscsi-scst \ && if "${scst_local}" = "true" ; then make -C scst_local clean; fi \ && if "${scst_local}" = "true" ; then make -C scst_local -s ; fi \ && if "${mpt_scst}" = "true" ; then make -C mpt clean; fi \ && if "${mpt_scst}" = "true" ; then make -C mpt -s ; fi \ && make -C srpt -s clean \ && make -C srpt -s ) \ >& "${outputfile}" then true else echo "FAILED" fi ) } # Download the file from URL $1 and save it in the current directory. function download_file { if [ ! -e "$(basename "$1")" ]; then echo "Downloading $1 ..." if ! wget -q -nc "$1"; then echo "Downloading $1 failed." return 1 fi fi } # Make sure the kernel tarball and patch file are present in directory # ${kernel_sources}. Download any missing files from ${kernel_mirror}. function download_kernel { local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" mkdir -p "${kernel_sources}" || return $? test -w "${kernel_sources}" || return $? ( cd "${kernel_sources}" || return $? download_file "${kernel_mirror}/linux-$(kernel_version $1).tar.bz2" \ || return $? if [ "${plevel}" != "" ]; then download_file "${kernel_mirror}/patch-$1.bz2" || return $? fi ) } # Generate a kernel patch from the SCST source tree for kernel version $1. function generate_kernel_patch { scripts/generate-kernel-patch \ $([ "${scst_local}" = "true" ] && echo -- "-l") \ $([ "${mpt_scst}" = "true" ] && echo -- "-m") \ $([ "${qla2x00t}" = "true" ] && echo -- "-q") \ $1 } # Generate a kernel patch through scripts/generate-kernel-patch and test # whether it applies cleanly to kernel version $1. Leaves a vanilla kernel # in directory "${outputdir}/linux-$1" at exit. function test_if_patch_applies_cleanly { local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" local outputfile="${outputdir}/kernel-$1-patch-output.txt" local rc=0 echo "Testing whether the generated kernel patch applies cleanly to $1 ..." ( cd "${outputdir}" && extract_kernel_tree "$1" ) generate_kernel_patch $1 \ | (cd "${outputdir}/linux-$1" && patch -p1 --dry-run -f >& "${outputfile}") if [ $? != 0 ]; then echo "FAILED" rc=1 fi return $rc } # Run checkpatch on the generated kernel patch. Assumes that there is a # vanilla kernel tree present in directory "${outputdir}/linux-$1", and leaves # this kernel tree clean. function run_checkpatch { local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" local outputfile="${outputdir}/checkpatch-$1-output.txt" echo "Running checkpatch version $1 on the SCST kernel patch ..." generate_kernel_patch $1 \ | (cd "${outputdir}/linux-$1" && scripts/checkpatch.pl - >& "${outputfile}") # For now, only display checkpatch errors. local errors=$(grep -c '^ERROR' "${outputfile}") local warnings=$(grep -c '^WARNING' "${outputfile}") echo "${errors} errors / ${warnings} warnings." return 0 } function patch_and_configure_kernel { local patchfile="${outputdir}/scst-$1-kernel.patch" echo "Patching and configuring kernel $1 ..." generate_kernel_patch "$1" > "${patchfile}" ( cd "${outputdir}/linux-$1" \ && patch -p1 -f -s <"${patchfile}" \ >"${outputdir}/patch-command-output.txt" \ && make -s allmodconfig &>/dev/null ) } # Patches and compiles a kernel tree. Assumes that there is a vanilla kernel # tree present in directory "${outputdir}/linux-$1". function compile_patched_kernel { local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" local outputfile="${outputdir}/kernel-$1-compilation-output.txt" echo "Compiling kernel $1 ..." ( ( cd "${outputdir}/linux-$1" \ && LC_ALL=C make -s -k -j3 bzImage modules ) ) >& "${outputfile}" echo "See also ${outputfile}." return 0 } # Run the source code verification tool 'sparse' on the SCST code. Assumes that # there is a patched kernel tree present in directory "${outputdir}/linux-$1". # For more information about endianness annotations, see also # http://lwn.net/Articles/205624/. function run_sparse { local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" local outputfile="${outputdir}/sparse-$1-output.txt" echo "Running sparse on the patched $1 kernel ..." ( cd "${outputdir}/linux-$1" \ && make -s prepare \ && make -s scripts \ && LC_ALL=C make -k C=2 M=drivers/scst # CF=-D__CHECK_ENDIAN__ ) >& "${outputfile}" echo "See also ${outputfile}." return 0 } function run_checkstack { local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" local outputfile="${outputdir}/checkstack-$1-output.txt" echo "Running checkstack on the patched $1 kernel ..." ( cd "${outputdir}/linux-$1" \ && make -s prepare \ && make -s scripts \ && LC_ALL=C make -k checkstack ) >& "${outputfile}" echo "See also ${outputfile}." return 0 } function run_namespacecheck { local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" local outputfile="${outputdir}/namespacecheck-$1-output.txt" echo "Running namespacecheck on the patched $1 kernel ..." ( cd "${outputdir}/linux-$1" \ && make -s prepare \ && make -s scripts \ && LC_ALL=C make -k namespacecheck ) >& "${outputfile}" echo "See also ${outputfile}." return 0 } function run_headers_check { local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" local outputfile="${outputdir}/headers_check-$1-output.txt" echo "Running headers check on the patched $1 kernel ..." ( cd "${outputdir}/linux-$1" \ && make -s prepare \ && make -s scripts \ && LC_ALL=C make -k headers_check ) >& "${outputfile}" echo "See also ${outputfile}." return 0 } function run_make_htmldocs { local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" local outputfile="${outputdir}/htmldocs-$1-output.txt" echo "Generating HTML documentation for the patched $1 kernel ..." ( cd "${outputdir}/linux-$1" \ && make -s prepare \ && make -s scripts \ && LC_ALL=C make -k htmldocs ) >& "${outputfile}" echo "See also ${outputfile}." return 0 } ######################### # Argument verification # ######################### if [ ! -e scst -o ! -e iscsi-scst -o ! -e srpt ]; then echo "Please run this script from inside the SCST subversion source tree." exit 1 fi # Where to store persistenly downloaded kernel tarballs and kernel patches. kernel_sources="$HOME/software/downloads" # URL for downloading kernel tarballs and kernel patches. kernel_mirror="ftp://ftp.eu.kernel.org/pub/linux/kernel/v2.6" kernel_versions="" # Directory in which the regression test output files will be stored. outputdir=$PWD/regression-test-output-$(date +%Y-%m-%d_%Hh%Mm%Ss) # Driver configuration. mpt_scst="false" qla2x00t="false" scst_local="true" set -- $(/usr/bin/getopt "c:hk:" "$@") while [ "$1" != "${1#-}" ] do case "$1" in '-c') kernel_sources="$2"; shift; shift;; '-h') usage; exit 1;; '-k') kernel_versions="${kernel_versions} $2"; shift; shift;; '--') shift;; *) usage; exit 1;; esac done if [ $# != 0 ]; then usage exit 1 fi # Default kernel versions to use for the test. if [ "${kernel_versions}" = "" ]; then #kernel_versions="2.6.24.7 2.6.25.20 2.6.26.8 2.6.27.11 2.6.28" kernel_versions="2.6.28" fi #################### # Regression tests # #################### rm -rf "${outputdir}" mkdir -p "${outputdir}" || exit $? test_scst_tree_patches || exit $? compile_scst_unpatched || exit $? compile_scst_patched || exit $? for k in ${kernel_versions} do if download_kernel $k && test_if_patch_applies_cleanly $k; then run_checkpatch $k patch_and_configure_kernel $k run_sparse $k run_headers_check $k compile_patched_kernel $k run_checkstack $k run_namespacecheck $k run_make_htmldocs $k else echo "FAILED for kernel $k" fi done