#!/bin/bash ############################################################################ # # Script for running those SCST regression tests that can be run automatically. # # Copyright (C) 2008-2009 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, runs # 'make debug2release' and again compiles the SCST source code. # - Checks whether the specified kernel version is present # in the directory specified through option -c. # - If the source code of the specified kernel version 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 ] [-d ] [-f] [-h] [-j ] [-p ] [-q] ..." echo " -c - cache directory for Linux kernel tarballs." echo " -d - directory for temporary regression test files." echo " -h - display this help information." echo " -j - number of jobs that 'make' should run simultaneously." echo " -k - remove temporary files before exiting." echo " -q - download kernel sources silently." echo " ... - kernel versions to test." } # First three components of the kernel version number. function kernel_version { echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\.[0-9]*\).*$/\1/p' } # Last component of the kernel version, or the empty string if $1 has only # three components. function patchlevel { echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\.[0-9]*\)[.-]\(.*\)$/\2/p' } # 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" || return $? if [ "$1" = "2.6.29" -o "$1" = "2.6.29.1" -o "$1" = "2.6.29.2" -o "$1" = "2.6.29.3" ] then cd "../linux-$1" || return $? patch -f -s -p1 <<'EOF' Make sure that branch profiling does not trigger sparse warnings. See also http://bugzilla.kernel.org/show_bug.cgi?id=12925 --- See also http://lkml.org/lkml/2009/4/5/120 --- orig/linux-2.6.29/include/linux/compiler.h 2009-03-23 19:12:14.000000000 -0400 +++ linux-2.6.29/include/linux/compiler.h 2009-03-24 08:46:46.000000000 -0400 @@ -75,7 +75,8 @@ struct ftrace_branch_data { * Note: DISABLE_BRANCH_PROFILING can be used by special lowlevel code * to disable branch tracing on a per file basis. */ -#if defined(CONFIG_TRACE_BRANCH_PROFILING) && !defined(DISABLE_BRANCH_PROFILING) +#if defined(CONFIG_TRACE_BRANCH_PROFILING) \ + && !defined(DISABLE_BRANCH_PROFILING) && !defined(__CHECKER__) void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect); #define likely_notrace(x) __builtin_expect(!!(x), 1) EOF fi if [ "$1" = "2.6.31" -o "${1#2.6.31.}" != "$1" ] then cd "../linux-$1" || return $? patch -f -s -p1 <<'EOF' Checking a 2.6.31.1 kernel configured with allyesconfig/allmodconfig with sparse (make C=2) triggers a sparse warning on code that uses the kmemcheck_annotate_bitfield() macro. An example of such a warning: include/net/inet_sock.h:208:17: warning: do-while statement is not a compound statement Signed-off-by: Bart Van Assche Cc: Vegard Nossum Cc: Andrew Morton --- See also http://lkml.org/lkml/2009/9/26/51 --- linux-2.6.31.1/include/linux/kmemcheck-orig.h 2009-09-26 13:53:44.000000000 +0200 +++ linux-2.6.31.1/include/linux/kmemcheck.h 2009-09-26 13:53:56.000000000 +0200 @@ -137,13 +137,13 @@ static inline void kmemcheck_mark_initia int name##_end[0]; #define kmemcheck_annotate_bitfield(ptr, name) \ - do if (ptr) { \ + do { if (ptr) { \ int _n = (long) &((ptr)->name##_end) \ - (long) &((ptr)->name##_begin); \ BUILD_BUG_ON(_n < 0); \ \ kmemcheck_mark_initialized(&((ptr)->name##_begin), _n); \ - } while (0) + } } while (0) #define kmemcheck_annotate_variable(var) \ do { \ EOF fi ) 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 { local rc=0 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 if ! patch -p0 -f --dry-run -s <$p &>/dev/null; then echo "ERROR: patch $p does not apply cleanly." rc=1 fi done if [ "${rc}" = 0 ]; then echo "OK" fi } # Copy the entire SCST source code tree from "$1" into the current directory. # Only copy those files which are administered by Subversion. function duplicate_scst_source_tree { if [ -e "$1/AskingQuestions" ]; then ( cd "$1" && svn status -v | grep -v '^[D?]' | cut -c3- | awk '{print $4}' \ | while read f; do [ ! -d "$f" ] && echo "$f"; done ) \ | tar -C "$1" --files-from=- -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 echo "OK" 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-$1.txt" local workingdirectory="${outputdir}/scst-$1" echo "Testing whether the $1 SCST tree compiles fine ..." ( if mkdir -p "${workingdirectory}" \ && cd "${workingdirectory}" \ && duplicate_scst_source_tree "${scst}" \ && make -s "$1" \ && (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 echo "OK" 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 if [ "${quiet_download}" = "false" ]; then echo "Downloading $1 ..." fi 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 # and with generate-kernel-patch options $2. function generate_kernel_patch { local scst_dir="${PWD}" local kver="$(kernel_version $1)" local patchfile="${outputdir}/scst-$1-kernel.patch" local patchfile_m="${outputdir}/scst-$1-kernel-matching-line-numbers.patch" local driver_options="" driver_options="$([ "${scst_local}" = "true" ] && echo "-l") \ $([ "${mpt_scst}" = "true" ] && echo "-m") \ $([ "${qla2x00t}" = "true" ] && echo "-q")" scripts/generate-kernel-patch ${driver_options} $2 $1 > "${patchfile}" scripts/generate-kernel-patch ${driver_options} -n $2 $1 > "${patchfile_m}" scripts/generate-kernel-patch ${driver_options} -p "${outputdir}/${patchdir}" $2 $1 } # 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" local patchfile="${outputdir}/scst-$1-kernel.patch" if [ -e "${outputdir}/linux-$1/scripts/checkpatch.pl" ]; then if [ "${multiple_patches}" = "false" ]; then echo "Running checkpatch on the SCST kernel patch ..." ( cd "${outputdir}/linux-$1" \ && scripts/checkpatch.pl --no-tree --no-signoff - < "${patchfile}" &> "${outputfile}") else echo "Running checkpatch on the SCST kernel patches ..." rm -f "${outputfile}" ( cd "${outputdir}/linux-$1" \ && for p in "${outputdir}/${patchdir}"/* do echo "==== $p" >>"${outputfile}" scripts/checkpatch.pl --no-tree --no-signoff - < "$p" >> "${outputfile}" 2>&1 done ) fi local errors=$(grep -c '^ERROR' "${outputfile}") local warnings=$(grep -c '^WARNING' "${outputfile}") echo "${errors} errors / ${warnings} warnings." grep -E '^WARNING|^ERROR' "${outputfile}" | sort | uniq -c else echo "Skipping checkpatch step for kernel $1." fi return 0 } function patch_and_configure_kernel { local patchfile="${outputdir}/scst-$1-kernel-matching-line-numbers.patch" local patchoutput="${outputdir}/patch-command-output-$1.txt" echo "Patching and configuring kernel ..." ( cd "${outputdir}/linux-$1" \ && if [ "${multiple_patches}" = "false" ]; then patch -p1 -f -s <"${patchfile}" >"${patchoutput}" else rm -f "${patchoutput}" for p in "${outputdir}/${patchdir}"/* do echo "==== $p" >>"${patchoutput}" patch -p1 -f -s <"${p}" >>"${patchoutput}" 2>&1 done fi \ && make -s allmodconfig &>"${outputdir}/make-config-output.txt" \ && sed -i.tmp 's/^CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y$/CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=n/' .config \ && make -s oldconfig &>/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 bzImage modules ) ) &> "${outputfile}" echo "See also ${outputfile}." return 0 } # Compile subdirectory $2 of the patched kernel tree linux-$1. function compile_kernel { local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" local outputfile="${outputdir}/compilation-$1-output.txt" local subdir="$2" echo "Compiling the patched kernel ..." ( cd "${outputdir}/linux-$1" \ && make -s prepare \ && make -s scripts \ && LC_ALL=C make -k M="${subdir}" ) &> "${outputfile}" local errors=$(grep -c ' error:' "${outputfile}") local warnings=$(grep -c ' warning:' "${outputfile}") echo "${errors} errors / ${warnings} warnings." cat "${outputfile}" | grep -E 'warning:|error:' | sort | uniq -c 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 k="$1" local kver="$(kernel_version $1)" local plevel="$(patchlevel $1)" local outputfile="${outputdir}/sparse-$1-output.txt" local subdir="$2" shift shift echo "Running sparse on the patched kernel in ${subdir} $@ ..." ( cd "${outputdir}/linux-$k" \ && make -s prepare \ && make -s scripts \ && if grep -q '^CONFIG_PPC=y$' .config; then LC_ALL=C make -k M=arch/powerpc/lib; fi \ && LC_ALL=C make -k C=2 CF=-D__CHECK_ENDIAN__ M="${subdir}" "$@" ) &> "${outputfile}" local errors=$(grep -c ' error:' "${outputfile}") local warnings=$(grep -c ' warning:' "${outputfile}") echo "${errors} errors / ${warnings} warnings." cat "${outputfile}" \ | grep -E 'warning:|error:' \ | sed -e 's/^[^ ]*:[^ ]*:[^ ]*: //' \ -e "s/context imbalance in '[^']*':/context imbalance in :/g" \ -e "s/context problem in '[^']*': '[^']*'/context problem in : /g" \ -e "s/function '[^']*'/function/g" \ -e "s/symbol '[^']*'/symbol/g" \ | sort \ | uniq -c 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}" local errors=$(grep -c '^[^ ]' "${outputfile}") echo "${errors} errors." grep '^[^ ]' "${outputfile}" | sed 's/.*: //' | sort | uniq -c 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 # ######################### export LC_ALL=C 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. Must be # an absolute path. outputdir="${PWD}/regression-test-output-$(date +%Y-%m-%d_%Hh%Mm%Ss)" # Driver configuration. mpt_scst="false" multiple_patches="false" qla2x00t="false" remove_temporary_files_at_end="false" run_local_compilation="true" scst_local="true" quiet_download="false" set -- $(/usr/bin/getopt "c:d:j:hklpq" "$@") while [ "$1" != "${1#-}" ] do case "$1" in '-c') kernel_sources="$2"; shift; shift;; '-d') outputdir="$2"; shift; shift;; '-h') usage; exit 1;; '-j') export MAKEFLAGS="-j$2"; shift; shift;; '-k') remove_temporary_files_at_end="true"; shift;; '-l') run_local_compilation="false"; shift;; '-p') multiple_patches="true"; shift;; '-q') quiet_download="true"; shift;; '--') shift;; *) usage; exit 1;; esac done kernel_versions="$*" # RHEL 4.x / CentOS 4.x has a kernel based on version 2.6.9. # RHEL 5.x / CentOS 5.x has a kernel based on version 2.6.18. # Ubuntu 8.04 (Hardy Heron) has a kernel based on version 2.6.24. # Ubuntu 8.10 (Intrepid Ibex) has a kernel based on version 2.6.27. # openSUSE 11.0 has a kernel based on version 2.6.25. # openSUSE 11.1 has a kernel based on version 2.6.27. #kernel_versions="2.6.23.17 2.6.24.7 2.6.25.20 2.6.26.8 2.6.27.21 2.6.28.9 2.6.29.1" if [ "${kernel_versions}" = "" ]; then usage exit 1 fi #################### # Regression tests # #################### if [ "$(type -p sparse)" = "" ]; then echo "Error: sparse has not yet been installed." echo "See also http://www.kernel.org/pub/software/devel/sparse/." fi if ! mkdir -p "${outputdir}"; then if [ -e "${outputdir}" ]; then echo "Error: directory ${outputdir} already exists." else echo "Error: could not create directory ${outputdir}." fi fi test_scst_tree_patches || exit $? if [ "${run_local_compilation}" = "true" ]; then compile_scst_unpatched || exit $? compile_scst_patched debug2release || exit $? compile_scst_patched debug2perf || exit $? compile_scst_patched enable_proc || exit $? fi first_iteration="true" for kv in ${kernel_versions} do echo "==========================" printf "= kernel %-15s =\n" "${kv}" echo "==========================" full_check="false" generate_kernel_patch_options="" ibmvio="false" run_checkpatch="true" run_sparse="true" global_multiple_patches="${multiple_patches}" while [ "${kv%-?}" != "${kv}" -o "${kv%-??}" != "${kv}" ]; do kv_without_opt="${kv%-?}" if [ "${kv_without_opt}" = "${kv}" ]; then kv_without_opt="${kv%-??}" fi kopt="${kv#${kv_without_opt}}" case "${kopt}" in '-f') full_check="true";; '-i') ibmvio="true";; '-nc') run_checkpatch="false";; '-ns') run_sparse="false";; '-u') generate_kernel_patch_options="-u";; '-p') multiple_patches="true";; *) echo "Error: unknown option ${kopt}."; exit 1;; esac kv="${kv_without_opt}" done patchdir="patchdir-${kv}" k="${kv}" download_kernel $k || continue generate_kernel_patch $k "${generate_kernel_patch_options}" || continue ( cd "${outputdir}" && extract_kernel_tree $k ) || continue if [ "${run_checkpatch}" = "true" ]; then run_checkpatch $k fi patch_and_configure_kernel $k if [ "${run_sparse}" = "true" ]; then run_sparse $k drivers/scst mv ${outputdir}/sparse-$k-output.txt ${outputdir}/sparse-$k-scst-output.txt if [ "${ibmvio}" = "true" ]; then run_sparse $k drivers/scsi libsrp.ko scsi_sysfs.ko \ scsi_transport_fc.ko scsi_transport_srp.ko mv ${outputdir}/sparse-$k-output.txt \ ${outputdir}/sparse-$k-scsi-output.txt if [ $(uname -m) = "ppc32" -o $(uname -m) = "ppc64" ]; then run_sparse $k drivers/scsi/ibmvscsi ibmvstgt.ko mv ${outputdir}/sparse-$k-output.txt \ ${outputdir}/sparse-$k-ibmvstgt-output.txt fi fi fi compile_kernel $k drivers/scst if [ "${full_check}" = "true" ]; then run_headers_check $k compile_patched_kernel $k run_checkstack $k run_namespacecheck $k run_make_htmldocs $k fi if [ "${remove_temporary_files_at_end}" = "true" ]; then rm -rf "${outputdir}" mkdir -p "${outputdir}" fi multiple_patches="${global_multiple_patches}" done