429 lines
11 KiB
Plaintext
Raw Normal View History

#!/bin/bash
## Manage linux driver patches for netmap.
## usage (from the dir containing the Makefile):
##
## scripts/np <action> [args...]
##
## where <action> is any of the functions below.
##
[ -f scripts/conf ] && source scripts/conf
## The following enviroment variables must be set:
##
## GITDIR: the absolute path of the netmap linux
## git repository, containing all the required netmap-*
## branches.
[ -n "$GITDIR" -a -d "$GITDIR/.git" ] || {
echo "GITDIR not set or not valid" >&2
exit 1
}
NETMAP_BRANCH=${NETMAP_BRANCH:-master}
function error {
echo "$@" >&2
exit 1
}
function get-params {
local params=$1; shift
err_msg="$PROGNAME $COMMAND $(echo $params| perl -pe 's/\S+/<$&>/g')"
local param
for param in $params; do
[[ -z "$@" ]] && error "$err_msg"
pname=$(echo -n $param | perl -pe 's/\W/_/g')
eval $pname="$1"
shift
done
[[ -n "$@" ]] && error "$err_msg"
}
##
## LINUX_SOURCES: the absolute path of a
## directory used to store all required linux-* source trees
## (The script will extract linux-x.y.z from GITDIR if it needs
## it and $LINUX_SOURCES does not already contain it).
##
## LINUX_CONFIGS: the absolute path of a
## directory containing the configuration files for
## the linux kernel. The file for version x must be named
## config-x. config-all can be used as a default.
##
## The configuration variables can be put in scripts/conf.
##
##
## Available actions:
##
##
## driver-path <driver> <version>
## retrieves the path of <driver> in the linux sources
## for version <version>. The path is output to stdout.
## It uses a local cache to minimize the expensive
## file system search.
function driver-path()
{
get-params "driver version" "$@"
cat cache/$version/$driver/path 2>/dev/null && return
local kern=$(get-kernel $version)
mkdir -p cache/$version/$driver
(
cd $kern
find drivers/net -name $driver
) | tee cache/$version/$driver/path
}
##
## get-patch [-c] <driver> <version>
## extract the netmap patch for the given <driver> and the
## given kernel <version>. The patch is stored in tmp-patches
## and the name of the patch is output to stdout.
## If a patch with the same name already exists in tmp-patches
## it is overwritten, unless the -c option is used,
## in which case the existing patch is kept (the patch name is still output).
function get-patch()
{
local use_cache
[ "$1" = -c ] && { use_cache=1; shift; }
get-params "driver version" "$@"
# convert kernel version to fixed notation
local v1=$(scripts/vers $version -c)
# compute next kernel version (in fixed notation)
local v2=$(scripts/vers $version -i -c)
local patchname=diff--$driver--$v1--$v2
local out=tmp-patches/$patchname
[ -n "$use_cache" -a -s $out ] && { echo $out; return; }
local drvpath=$(driver-path $driver $version)
[ -n "$drvpath" ] || return
local drvdir=$(dirname $drvpath)
(
cd $GITDIR
git diff --relative=$drvdir v$version..netmap-$version -- $drvpath
) > $out
# an empty patch means no netmap support for this driver
[ -s $out ] || { rm $out; return 1; }
echo $out
return 0;
}
##
## get-range <driver> <version1> <version2>
## extracts the netmap patches for the given <driver> for
## all the kernel versions from <version1> (included) to
## <version2> (excluded). All patches are stored in tmp-patches
## and their names are output to stdout.
function get-range()
{
get-params "driver version1 version2" "$@"
local v=$version1
# while version is less than $version2
while scripts/vers -b $v $version2 -L; do
get-patch $driver $v
# compute next version
v=$(scripts/vers $v -i)
done
}
##
## get-src <driver> <version> <dest>
## copies the original sources of the given <driver>,
## from the given kernel <version> to the given <dest>
## directory.
## It uses a local cache to minimize the expensive
## checkouts in GITDIR.
function get-src()
{
get-params "driver version dest" "$@"
local kern=$(get-kernel $version)
local src=$(driver-path $driver $version)
cp -r $kern/$src $dest
}
##
## extend <patch> <version>
## checks wether the range of applicability of the
## given <patch> can be extented to include <version>.
## It returns 0 on success and 1 on failure.
function extend()
{
get-params "patch version" "$@"
local _patch=$(realpath $patch)
# extract the driver name from the patch name
local driver=$(scripts/vers $_patch -s -p -p)
local tmpdir1=$(mktemp -d)
local tmpdir2=$(mktemp -d)
trap "rm -rf $tmpdir1 $tmpdir2" 0
# we get the driver sources for the given <version> and
# we apply two patches separately:
# i) the given <patch>;
# ii) the proper patch from GITDIR.
# We declare <patch> to be extendable if
# - it is still applicable AND
# - we obtain the same files from i) and ii) (ignoring whitespace)
get-src $driver $version $tmpdir1
get-src $driver $version $tmpdir2
(
cd $tmpdir1
patch --no-backup-if-mismatch -p1 < $_patch >/dev/null 2>&1
) || return 1
local patch2=$(get-patch -c $driver $version)
patch2=$(realpath $patch2)
(
cd $tmpdir2
patch -p1 < $patch2 >/dev/null 2>&1
) # this will certainly apply
diff -qbBr $tmpdir1 $tmpdir2 >/dev/null || return 1
return 0
}
##
## minimize <driver>
## tries to minimize the number of patch files for the given
## <driver>. It uses the patches currently found in tmp-patches
## and stores the resulting patches in final-patches.
## If final-patches already contained patches for <driver>,
## they are deleted first.
function minimize()
{
get-params "driver" "$@"
mkdir -p final-patches
local drv=$(basename $driver)
local patches=$(ls tmp-patches/diff--$drv--* 2>/dev/null)
[ -n "$patches" ] || return 1
# put the patch names in $1, $2, ...
set $patches
rm -f final-patches/diff--$drv--*
# the original patches (in tmp-patches) are ordered by version number.
# We consider one patch in turn (the 'pivot') and try
# to extend its range to cover the range of the next
# patch. If this succedes, the merged patch is the new
# pivot, otherwise the current pivot is output and the
# next patch becomes the new pivot. The process
# is repeated until there are no more patches to consider.
local pivot=$1
[ -n "$pivot" -a -e "$pivot" ] || return 1
# extract the left end and right end of the pivot's range
local ple=$(scripts/vers $pivot -s -p -C)
local pre=$(scripts/vers $pivot -s -C)
while [ -n "$pivot" ]; do
shift
if [ -n "$1" ]; then
# extract the left end and right end of the next patch
local nle=$(scripts/vers $1 -s -p -C)
local nre=$(scripts/vers $1 -s -C)
# we admit no gaps in the range
if [ $pre = $nle ] && extend $pivot $nle; then
pre=$nre
continue
fi
fi
# either out of patches or failed merge.
# Compute the file name of the current pivot and store
# the patch in its final location
out=$(scripts/vers diff $drv $ple -c $pre -c -S4)
cp $pivot final-patches/$out
# the new pivot becames the next patch (if any)
pivot=$1
pre=$nre
ple=$nle
done
return 0
}
##
## infty <driver> <version>
## if final-patches contains a patch for <driver> with a range
## ending in <version>, extend it to infinity.
## Do nothing otherwise.
function infty()
{
get-params "driver version" "$@"
local drv=$(basename $driver)
# convert kernel version to fixed notation
local v=$(scripts/vers $version -c)
local last=$(ls final-patches/diff--$drv--*--$v 2>/dev/null|tail -n1)
[ -n "$last" ] || return 1
mv -n $last $(scripts/vers $last -s -p 99999 -S4) 2>/dev/null
}
function get-kernel()
{
get-params "version" "$@"
local dst="$(realpath $LINUX_SOURCES)/linux-$version"
[ -d $dst ] && { echo $dst; return; }
mkdir -p $dst
(
cd $GITDIR
git archive v$v | tar xf - -C $dst
)
echo $dst
}
##
## build-prep <version>
## prepare the linux tree for <version> to be ready
## for external modules compilation.
## The tree is put in $LINUX_SOURCES/linux-<version> and the
## configuration is obtained from $LINUX_CONFIGS/config-<version>
## (or $LINUX_CONFIGS/config-all by default).
## Errors are logged to $LINUX_CONFIGS/linux-<version>.log.
## If $LINUX_SOURCES/linux-<version> already exists,
## nothing is done.
## In all cases, the absolute path of linux-<version> is
## output.
function build-prep()
{
get-params "version" "$@"
local dst=$(get-kernel $version)
exec 3>&1 4>&2 >$dst.log 2>&1
cp $LINUX_CONFIGS/config-$v $dst/.config 2>/dev/null ||
cp $LINUX_CONFIGS/config-all $dst/.config
(
cd $dst
yes '' | make oldconfig
make modules_prepare
)
exec 1>&3 2>&4
echo $dst
}
##
## check-patch <patch>
## check that the given <patch> applies and compiles without
## error for all its declared range of applicability.
## Errors are logged to log/<patch>.
function check-patch()
{
get-params "patch" "$@"
# extract the left version
local v1=$(scripts/vers $patch -s -p -C)
# extract the right version
local v2=$(scripts/vers $patch -s -C)
# extract the driver name
local driver=$(scripts/vers $patch -s -p -p)
local p=$(realpath $patch)
mkdir -p log
local log="$(realpath log)/$(basename $patch)"
local nmcommit=$(cd ..; git show-ref -s heads/$NETMAP_BRANCH)
echo -n $patch...
while scripts/vers -b $v1 $v2 -L; do
# cache lookup
local cache=cache/$v1/$driver
local cpatch=$cache/patch
local cnmcommit=$cache/nmcommit
local cstatus=$cache/status
local clog=$cache/log
if [ -f $cpatch ] &&
cmp -s $cpatch $patch &&
[ "$nmcommit" = "$(cat $cnmcommit)" ]; then
cp $clog $log
ok=$(cat $cstatus)
else
# update cache
cp $patch $cpatch
echo $nmcommit > $cnmcommit
local ksrc=$(build-prep $v1)
local tmpdir=$(mktemp -d)
trap "rm -rf $tmpdir" 0
(cd ..; git archive $NETMAP_BRANCH | tar xf - -C $tmpdir )
pushd $tmpdir/LINUX >/dev/null
mkdir single-patch
rm patches
ln -s single-patch patches
cp $p single-patch
ok=false
make KSRC=$ksrc >$log 2>&1 && ok=true
popd >/dev/null
cp $log $clog
fi
[ $ok = true ] || { echo FAILED; echo false > $cstatus; return 1; }
echo true > $cstatus
rm -rf $tmpdir
# compute next version
v1=$(scripts/vers $v1 -i)
done
echo OK
}
##
## build-check <driver>
## do a check-patch for all the patches of <driver> that are
## currently in tmp-patches. Patches that fail the check
## are moved to failed-patches.
function build-check()
{
get-params "driver" "$@"
mkdir -p failed-patches
local drv=$(basename $driver)
local patches=$(ls tmp-patches/diff--$drv--* 2>/dev/null)
local p
for p in $patches; do
check-patch $p || mv $p failed-patches
done
}
##
## forall <cmd> [args...]
## exec <cmd> <driver> [args...] for all known drivers.
function forall()
{
local cmd=$1
shift
# we obtain the value of DRIVER_SRC from the makefile
# (the +% target is defined in our Makefile and prints
# the contents of variable %)
local driver_srcs=$(make +DRIVER_SRCS)
local driver
for driver in $driver_srcs; do
$cmd $(basename $driver) "$@"
done
}
mkdir -p tmp-patches
PROGNAME=$0
[ -n "$1" ] || {
scripts/help $PROGNAME;
exit 1
}
COMMAND=$1; shift
case $COMMAND in
*-all)
forall ${COMMAND%-all} "$@"
;;
-[hH]|--help|-help|help)
scripts/help $PROGNAME
;;
*)
$COMMAND "$@"
;;
esac