Files
xen/tools/examples/block
T
rread@ubuntu.eng.hq.xensource.com b8ae09f01c The device sharing check races when more than one file backed vbd is
configured. Both of the files pick the same free loop device in the for
loop, and then one fails to losetup.  This patch will now retry if
losetup fails until it succeeds or no more devices are available.

Signed-off-by: Robert Read <robert@xensource.com>
2005-11-28 15:34:00 -07:00

283 lines
5.2 KiB
Bash

#!/bin/sh
set -x
dir=$(dirname "$0")
. "$dir/block-common.sh"
expand_dev() {
local dev
case $1 in
/*)
dev=$1
;;
*)
dev=/dev/$1
;;
esac
echo -n $dev
}
canonicalise_mode()
{
local mode="$1"
if ! expr index "$mode" 'w' >/dev/null
then
echo 'ro'
elif ! expr index "$mode" '!' >/dev/null
then
echo 'rw'
else
echo 'no'
fi
}
##
# check_sharing device device_major_minor mode
#
# Check whether the device requested is already in use. To use the device in
# read-only mode, it may be in use in read-only mode, but may not be in use in
# read-write anywhere at all. To use the device in read-write mode, it must
# not be in use anywhere at all.
#
check_sharing()
{
local dev="$1"
local mode="$2"
local devmm=$(device_major_minor "$dev")
local file
if [ "$mode" == 'rw' ]
then
toskip="^$"
else
toskip="^[^ ]* [^ ]* [^ ]* ro "
fi
for file in $(cat /proc/mounts | grep -v "$toskip" | cut -f 1 -d ' ')
do
if [ -e "$file" ]
then
local d=$(device_major_minor "$file")
if [ "$d" == "$devmm" ]
then
echo 'local'
return
fi
fi
done
for file in /sys/devices/xen-backend/*/physical_device
do
if [ -e "$file" ] # Cope with no devices, i.e. the * above did not expand.
then
local d=$(cat "$file")
if [ "$d" == "$devmm" ]
then
if [ "$mode" == 'rw' ]
then
echo 'guest'
return
else
local m=$(cat "${file/physical_device/mode}")
if expr index "$m" 'w' >/dev/null
then
echo 'guest'
return
fi
fi
fi
fi
done
echo 'ok'
}
check_device_sharing()
{
local dev="$1"
local mode=$(canonicalise_mode "$2")
local result
if [ "$mode" == 'no' ]
then
return 0
fi
result=$(check_sharing "$dev" "$mode")
if [ "$result" != 'ok' ]
then
do_ebusy "Device $dev is mounted " "$mode" "$result"
fi
}
check_file_sharing()
{
local file="$1"
local dev="$2"
local mode="$3"
result=$(check_sharing "$dev" "$mode")
if [ "$result" != 'ok' ]
then
do_ebusy "File $file is loopback-mounted through $dev,
which is mounted " "$mode" "$result"
fi
}
do_ebusy()
{
local prefix="$1"
local mode="$2"
local result="$3"
if [ "$result" == 'guest' ]
then
dom='a guest '
when='now'
else
dom='the privileged '
when='by a guest'
fi
if [ "$mode" == 'rw' ]
then
m1=''
m2=''
else
m1='read-write '
m2='read-only '
fi
ebusy \
"${prefix}${m1}in ${dom}domain,
and so cannot be mounted ${m2}${when}."
}
t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING')
case "$command" in
add)
phys=$(xenstore_read_default "$XENBUS_PATH/physical-device" 'MISSING')
if [ "$phys" != 'MISSING' ]
then
# Depending upon the hotplug configuration, it is possible for this
# script to be called twice, so just bail.
exit 0
fi
p=$(xenstore_read "$XENBUS_PATH/params")
mode=$(xenstore_read "$XENBUS_PATH/mode")
case $t in
phy)
dev=$(expand_dev $p)
check_device_sharing "$dev" "$mode"
write_dev "$dev"
exit 0
;;
file)
# Canonicalise the file, for sharing check comparison, and the mode
# for ease of use here.
file=$(readlink -f "$p")
mode=$(canonicalise_mode "$mode")
if [ "$mode" == 'rw' ] && ! stat "$file" -c %A | grep w >&/dev/null
then
ebusy \
"File $file is read-only, and so I will not
mount it read-write in a guest domain."
fi
while true
do
loopdev=''
for dev in /dev/loop*
do
if [ ! -b "$dev" ]
then
continue
fi
f=$(losetup "$dev" 2>/dev/null) || f='()'
f=$(echo "$f" | sed -e 's/.*(\(.*\)).*/\1/g')
log err "$file $f $dev"
if [ "$f" ]
then
# $dev is in use. Check sharing.
if [ "$mode" == 'no' ]
then
continue
fi
f=$(readlink -f "$f")
if [ "$f" == "$file" ]
then
check_file_sharing "$file" "$dev" "$mode"
fi
else
# $dev is not in use, so we'll remember it for use later; we want
# to finish the sharing check first.
if [ "$loopdev" == '' ]
then
loopdev="$dev"
fi
fi
done
if [ "$loopdev" == '' ]
then
fatal 'Failed to find an unused loop device'
fi
if losetup "$loopdev" "$file"
then
log err "mapped $file using $loopdev"
xenstore_write "$XENBUS_PATH/node" "$loopdev"
write_dev "$loopdev"
exit 0
else
log err "losetup $loopdev $file failed, retry"
fi
done
;;
esac
;;
remove)
case $t in
phy)
exit 0
;;
file)
node=$(xenstore_read "$XENBUS_PATH/node")
losetup -d "$node"
exit 0
;;
esac
;;
esac
# If we've reached here, $t is neither phy nor file, so fire a helper script.
[ -x /etc/xen/scripts/block-"$t" ] && \
/etc/xen/scripts/block-"$t" "$command" $node