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 |