429 lines
11 KiB
Plaintext
429 lines
11 KiB
Plaintext
|
#!/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
|