Files
xen/tools/hotplug/Linux/block-tap
T
Jason Andryuk 76a484193d hotplug: Update block-tap
Implement a sharing check like the regular block script.

Checking tapback inside block-tap is too late since it needs to be
running to transition the backend to InitWait before block-tap is run.

tap-ctl check will be removed when the requirement for the blktap kernel
driver is removed.  Remove it now as it is of limited use.

find_device() needs to be non-fatal allow a sharing check.

Only write physical-device-path because that is all that tapback needs.
Also write_dev doesn't handled files and would incorrectly store
physical-device as 0:0 which would confuse the minor inside tapback

Signed-off-by: Jason Andryuk <jandryuk@gmail.com>
Signed-off-by: Jason Andryuk <jason.andryuk@amd.com>
Reviewed-by: Anthony PERARD <anthony.perard@citrix.com>
2024-04-25 09:46:56 +02:00

269 lines
6.6 KiB
Bash
Executable File

#!/bin/bash -e
#
# tapdisk Xen block device hotplug script
#
# Author George Dunlap <george.dunlap@eu.citrix.com>
#
# Based on block-iscsi by Roger Pau Monné <roger.pau@citrix.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation; version 2.1 only. with the special
# exception on linking described in file 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 Lesser General Public License for more details.
#
# Usage:
#
# Disks should be specified using the following syntax:
#
# vdev=xvda,backendtype=tap,format=vhd,target=/srv/target.vhd
#
# format is either "aio" (for raw files), or "vhd"
dir=$(dirname "$0")
. "$dir/block-common.sh"
remove_label()
{
echo $1 | sed "s/^\("$2"\)//"
}
check_tools()
{
if ! command -v tap-ctl > /dev/null 2>&1; then
fatal "Unable to find tap-ctl tool"
fi
}
# Sets the following global variables based on the params field passed in as
# a parameter: type file
parse_target()
{
params=($(echo "$1" | tr ":" "\n"))
type=${params[0]}
file=${params[1]}
if [ -z "$type" ] || [ -z "$file" ]; then
fatal "Cannot parse required parameters"
fi
}
# Sets $pid and $minor to point to the device associated with the target
find_device()
{
local info
local param
if [ -z "$type" ] || [ -z "$file" ]; then
fatal "required parameters not set"
fi
info=$(tap-ctl list -t $type -f $file)
for param in $(echo "$info" | tr "," "\n")
do
case $param in
pid=*)
pid=$(remove_label $param "pid=")
;;
minor=*)
minor=$(remove_label $param "minor=")
;;
esac
done
if [ -z "$pid" ] || [ -z "$minor" ]; then
return 1
fi
return 0
}
count_using()
{
local file="$1"
local dom
local dev
local f
local i=0
local base_path="$XENBUS_BASE_PATH/$XENBUS_TYPE"
for dom in $(xenstore-list "$base_path")
do
for dev in $(xenstore-list "$base_path/$dom")
do
f=$(xenstore_read_default "$base_path/$dom/$dev/params" "")
f=$(echo "$f" | cut -d ":" -f 2)
if [ -n "$f" ] && [ "$file" = $f ] ; then
i=$(( i + 1 ))
fi
done
done
echo "$i"
}
# tap_shared is used to determine if a shared tap can be closed
# Since a stubdom and a guest both use the same tap, it can only
# be freed when there is a single one left.
tap_shared() {
[ $( count_using "$file" ) -gt 1 ]
}
check_tap_sharing()
{
local file="$1"
local mode="$2"
local dom
local dev
local base_path="$XENBUS_BASE_PATH/$XENBUS_TYPE"
for dom in $(xenstore-list "$base_path") ; do
for dev in $(xenstore-list "$base_path/$dom") ; do
local f=$(xenstore_read_default "$base_path/$dom/$dev/params" "")
f=$(echo "$f" | cut -d ":" -f 2)
if [ -n "$f" ] && [ "$file" = "$f" ] ; then
if [ "$mode" = 'w' ] ; then
if ! same_vm $dom ; then
echo "guest $f"
return
fi
else
local m=$(xenstore_read_default "$base_path/$dom/$dev/mode"
"")
m=$(canonicalise_mode "$m")
if [ "$m" = 'w' ] ; then
if ! same_vm $dom ; then
echo "guest $f"
return
fi
fi
fi
fi
done
done
echo 'ok'
}
tap_create()
{
if ! minor=$( tap-ctl allocate ) ; then
fatal "Could not allocate minor"
fi
# Handle with or without kernel blktap
minor=${minor#/run/blktap-control/tapdisk/tapdisk-}
minor=${minor#/dev/xen/blktap-2/tapdev}
# tap-ctl is spawning tapdisk which would hold the _lockfd open.
# Ensure it is closed before running tap-ctl spawn, which needs to be
# done in a subshell to continue holding the lock in the parent.
if ! pid=$( ( eval "exec $_lockfd>&-" ; tap-ctl spawn ) ) ; then
tap-ctl free -m "$minor"
fatal "Could not spawn tapdisk for $minor"
fi
if ! tap-ctl attach -p "$pid" -m "$minor" ; then
tap-ctl free -m "$minor"
fatal "Could not attach $pid and $minor"
fi
if ! tap-ctl open -p "$pid" -m "$minor" -a "$target" ; then
tap-ctl detach -p "$pid" -m "$minor"
tap-ctl free -m "$minor"
fatal "Could not open \"$target\""
fi
}
# Attaches the device and writes xenstore backend entries to connect
# the device
add()
{
local result
claim_lock "block"
if find_device; then
result=$( check_tap_sharing "$file" "$mode" )
if [ "$result" != "ok" ] ; then
do_ebusy "tap $type file $file in use " "$mode" "${result%% *}"
fi
else
tap_create
fi
# Create nbd unix path. find_device/tap_create set pid & minor
dev=$( printf "/run/blktap-control/nbd%ld.%d" "$pid" "$minor" )
xenstore_write "$XENBUS_PATH/pid" "$pid"
xenstore_write "$XENBUS_PATH/minor" "$minor"
# dev, as a unix socket, would end up with major:minor 0:0 in
# physical-device if write_dev were used. tapback would be thrown off by
# that incorrect minor, so just skip writing physical-device.
xenstore_write "$XENBUS_PATH/physical-device-path" "$dev"
success
release_lock "block"
}
# Disconnects the device
remove()
{
local minor
local pid
claim_lock "block"
if tap_shared ; then
return
fi
minor=$( xenstore_read "$XENBUS_PATH/minor" )
pid=$( xenstore_read "$XENBUS_PATH/pid" )
[ -n "$minor" ] || fatal "minor missing"
[ -n "$pid" ] || fatal "pid missing"
do_or_die tap-ctl destroy -p "$pid" -m "$minor" > /dev/null
release_lock "block"
}
command=$1
target=$(xenstore-read $XENBUS_PATH/params || true)
if [ -z "$target" ]; then
fatal "No information about the target"
fi
parse_target "$target"
check_tools || exit 1
mode=$( xenstore_read $XENBUS_PATH/mode )
mode=$( canonicalise_mode $mode )
# needed for same_vm
FRONTEND_ID=$(xenstore_read "$XENBUS_PATH/frontend-id")
FRONTEND_UUID=$(xenstore_read_default \
"/local/domain/$FRONTEND_ID/vm" 'unknown')
case $command in
add)
add
;;
remove)
remove
;;
*)
exit 1
;;
esac