# HG changeset patch
# User wim@xxxxxxxxxxxx
# Date 1170895981 28800
# Node ID 6524e02edbeb8aebe65aa5400af4b09dfccb8729
# Parent 780f097b54c5f9161f7c6cf3f86b2bb72cc43587
[blktap] Allow HVM booting from blktap device(s)
Signed-off-by: wim colgate <wim@xxxxxxxxxxxxx>
---
tools/ioemu/xenstore.c | 60 +++++++++++++++++++++-
tools/python/xen/xend/XendConfig.py | 41 +++++++++++++++
tools/python/xen/xend/XendDomainInfo.py | 56 ++++++++++++++++++++
tools/python/xen/xend/server/BlktapController.py | 62 +++++++++++++++++++++++
tools/python/xen/xend/server/DevController.py | 13 ++++
5 files changed, 230 insertions(+), 2 deletions(-)
diff -r 780f097b54c5 -r 6524e02edbeb tools/ioemu/xenstore.c
--- a/tools/ioemu/xenstore.c Wed Feb 07 17:29:52 2007 +0000
+++ b/tools/ioemu/xenstore.c Wed Feb 07 16:53:01 2007 -0800
@@ -10,6 +10,7 @@
#include "vl.h"
#include "block_int.h"
+#include <unistd.h>
static struct xs_handle *xsh = NULL;
static char *hd_filename[MAX_DISKS];
@@ -52,11 +53,40 @@ void xenstore_check_new_media_present(in
qemu_mod_timer(insert_timer, qemu_get_clock(rt_clock) + timeout);
}
+static int waitForDevice(char *path, char *field, char *desired)
+{
+ char *buf = NULL, *stat = NULL;
+ unsigned int len;
+ int val = 1;
+
+ /* loop until we find a value in xenstore, return
+ * if it was what we wanted, or not
+ */
+ while (1) {
+ if (pasprintf(&buf, "%s/%s", path, field) == -1)
+ goto done;
+ free(stat);
+ stat = xs_read(xsh, XBT_NULL, buf, &len);
+ if (stat == NULL) {
+ usleep(100000); /* 1/10th second, no path found */
+ } else {
+ val = strcmp(stat, desired);
+ goto done;
+ }
+ }
+
+done:
+ free(stat);
+ free(buf);
+ return val;
+}
+
void xenstore_parse_domain_config(int domid)
{
char **e = NULL;
char *buf = NULL, *path;
- char *bpath = NULL, *dev = NULL, *params = NULL, *type = NULL;
+ char *fpath = NULL, *bpath = NULL,
+ *dev = NULL, *params = NULL, *type = NULL;
int i;
unsigned int len, num, hd_index;
@@ -120,7 +150,35 @@ void xenstore_parse_domain_config(int do
hd_filename[hd_index] = params; /* strdup() */
params = NULL; /* don't free params on re-use */
}
+ /*
+ * check if device has a phantom vbd; the phantom is hooked
+ * to the frontend device (for ease of cleanup), so lookup
+ * the frontend device, and see if there is a phantom_vbd
+ * if there is, we will use resolution as the filename
+ */
+ if (pasprintf(&buf, "%s/device/vbd/%s/phantom_vbd", path, e[i]) == -1)
+ continue;
+ free(fpath);
+ fpath = xs_read(xsh, XBT_NULL, buf, &len);
+ if (fpath != NULL) {
+
+ if (waitForDevice(fpath, "hotplug-status", "connected")) {
+ continue;
+ }
+
+ if (pasprintf(&buf, "%s/dev", fpath) == -1)
+ continue;
+ params = xs_read(xsh, XBT_NULL, buf , &len);
+ if (params != NULL) {
+ free(hd_filename[hd_index]);
+ hd_filename[hd_index] = params;
+ params = NULL; /* don't free params on re-use */
+ }
+ }
bs_table[hd_index] = bdrv_new(dev);
+ /* re-establish buf */
+ if (pasprintf(&buf, "%s/params", bpath) == -1)
+ continue;
/* check if it is a cdrom */
if (type && !strcmp(type, "cdrom")) {
bdrv_set_type_hint(bs_table[hd_index], BDRV_TYPE_CDROM);
diff -r 780f097b54c5 -r 6524e02edbeb tools/python/xen/xend/XendConfig.py
--- a/tools/python/xen/xend/XendConfig.py Wed Feb 07 17:29:52 2007 +0000
+++ b/tools/python/xen/xend/XendConfig.py Wed Feb 07 16:53:01 2007 -0800
@@ -1148,6 +1148,47 @@ class XendConfig(dict):
# no valid device to add
return ''
+ def phantom_device_add(self, dev_type, cfg_xenapi = None,
+ target = None):
+ """Add a phantom tap device configuration in XenAPI struct format.
+ """
+
+ if target == None:
+ target = self
+
+ if dev_type not in XendDevices.valid_devices() and \
+ dev_type not in XendDevices.pseudo_devices():
+ raise XendConfigError("XendConfig: %s not a valid device type" %
+ dev_type)
+
+ if cfg_xenapi == None:
+ raise XendConfigError("XendConfig: device_add requires some "
+ "config.")
+
+ if cfg_xenapi:
+ log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
+
+ if cfg_xenapi:
+ dev_info = {}
+ if dev_type in ('vbd', 'tap'):
+ if dev_type == 'vbd':
+ dev_info['uname'] = cfg_xenapi.get('image', '')
+ dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
+ elif dev_type == 'tap':
+ if cfg_xenapi.get('image').find('tap:') == -1:
+ dev_info['uname'] = 'tap:qcow:%s' %
cfg_xenapi.get('image')
+ dev_info['dev'] = '/dev/%s' % cfg_xenapi.get('device')
+ dev_info['uname'] = cfg_xenapi.get('image')
+ dev_info['mode'] = cfg_xenapi.get('mode')
+ dev_info['backend'] = '0'
+ dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
+ dev_info['uuid'] = dev_uuid
+ self['devices'][dev_uuid] = (dev_type, dev_info)
+ self['vbd_refs'].append(dev_uuid)
+ return dev_uuid
+
+ return ''
+
def console_add(self, protocol, location, other_config = {}):
dev_uuid = uuid.createString()
if protocol == 'vt100':
diff -r 780f097b54c5 -r 6524e02edbeb tools/python/xen/xend/XendDomainInfo.py
--- a/tools/python/xen/xend/XendDomainInfo.py Wed Feb 07 17:29:52 2007 +0000
+++ b/tools/python/xen/xend/XendDomainInfo.py Wed Feb 07 16:53:01 2007 -0800
@@ -1565,18 +1565,53 @@ class XendDomainInfo:
# VM Destroy
#
+ def _prepare_phantom_paths(self):
+ # get associated devices to destroy
+ # build list of phantom devices to be removed after normal devices
+ plist = []
+ from xen.xend.xenstore.xstransact import xstransact
+ t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
+ for dev in t.list():
+ backend_phantom_vbd =
xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
+ % (self.dompath, dev))
+ if backend_phantom_vbd is not None:
+ frontend_phantom_vbd = xstransact.Read("%s/frontend" \
+ % backend_phantom_vbd)
+ plist.append(backend_phantom_vbd)
+ plist.append(frontend_phantom_vbd)
+ return plist
+
+ def _cleanup_phantom_devs(self, plist):
+ # remove phantom devices
+ if not plist == []:
+ time.sleep(2)
+ for paths in plist:
+ if paths.find('backend') != -1:
+ from xen.xend.server import DevController
+ # Modify online status /before/ updating state (latter is
watched by
+ # drivers, so this ordering avoids a race).
+ xstransact.Write(paths, 'online', "0")
+ xstransact.Write(paths, 'state',
str(DevController.xenbusState['Closing']))
+ # force
+ xstransact.Remove(paths)
+
def destroy(self):
"""Cleanup VM and destroy domain. Nothrow guarantee."""
log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
+
+ paths = self._prepare_phantom_paths()
self._cleanupVm()
if self.dompath is not None:
self.destroyDomain()
+ self._cleanup_phantom_devs(paths)
def destroyDomain(self):
log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
+
+ paths = self._prepare_phantom_paths()
try:
if self.domid is not None:
@@ -1591,7 +1626,7 @@ class XendDomainInfo:
XendDomain.instance().remove_domain(self)
self.cleanupDomain()
-
+ self._cleanup_phantom_devs(paths)
def resumeDomain(self):
log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
@@ -2211,6 +2246,25 @@ class XendDomainInfo:
return dev_uuid
+ def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
+ """Create a VBD using a VDI from XendStorageRepository.
+
+ @param xenapi_vbd: vbd struct from the Xen API
+ @param vdi_image_path: VDI UUID
+ @rtype: string
+ @return: uuid of the device
+ """
+ xenapi_vbd['image'] = vdi_image_path
+ dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
+ if not dev_uuid:
+ raise XendError('Failed to create device')
+
+ if self.state == XEN_API_VM_POWER_STATE_RUNNING:
+ _, config = self.info['devices'][dev_uuid]
+ config['devid'] =
self.getDeviceController('tap').createDevice(config)
+
+ return config['devid']
+
def create_vif(self, xenapi_vif):
"""Create VIF device from the passed struct in Xen API format.
diff -r 780f097b54c5 -r 6524e02edbeb
tools/python/xen/xend/server/BlktapController.py
--- a/tools/python/xen/xend/server/BlktapController.py Wed Feb 07 17:29:52
2007 +0000
+++ b/tools/python/xen/xend/server/BlktapController.py Wed Feb 07 16:53:01
2007 -0800
@@ -2,7 +2,10 @@
from xen.xend.server.blkif import BlkifController
+from xen.xend.XendLogging import log
+phantomDev = 0;
+phantomId = 0;
class BlktapController(BlkifController):
def __init__(self, vm):
@@ -12,3 +15,62 @@ class BlktapController(BlkifController):
"""@see DevController#frontendRoot"""
return "%s/device/vbd" % self.vm.getDomainPath()
+
+ def getDeviceDetails(self, config):
+ (devid, back, front) = BlkifController.getDeviceDetails(self, config)
+
+ phantomDevid = 0
+ wrapped = False
+
+ try:
+ imagetype = self.vm.info['image']['type']
+ except:
+ imagetype = ""
+
+ if imagetype == 'hvm':
+ tdevname = back['dev']
+ index = ['c', 'd', 'e', 'f', 'g', 'h', 'i', \
+ 'j', 'l', 'm', 'n', 'o', 'p']
+ while True:
+ global phantomDev
+ global phantomId
+ import os, stat
+
+ phantomId = phantomId + 1
+ if phantomId == 16:
+ if index[phantomDev] == index[-1]:
+ if wrapped:
+ raise VmError(" No loopback block \
+ devices are available. ")
+ wrapped = True
+ phantomDev = 0
+ else:
+ phantomDev = phantomDev + 1
+ phantomId = 1
+ devname = 'xvd%s%d' % (index[phantomDev], phantomId)
+ try:
+ info = os.stat('/dev/%s' % devname)
+ except:
+ break
+
+ vbd = { 'mode': 'w', 'device': devname }
+ fn = 'tap:%s' % back['params']
+
+ # recurse ... by creating the vbd, then fallthrough
+ # and finish creating the original device
+
+ from xen.xend import XendDomain
+ dom0 = XendDomain.instance().privilegedDomain()
+ phantomDevid = dom0.create_phantom_vbd_with_vdi(vbd, fn)
+ # we need to wait for this device at a higher level
+ # the vbd that gets created will have a link to us
+ # and will let them do it there
+
+ # add a hook to point to the phantom device,
+ # root path is always the same (dom0 tap)
+ if phantomDevid != 0:
+ front['phantom_vbd'] = '/local/domain/0/backend/tap/0/%s' \
+ % str(phantomDevid)
+
+ return (devid, back, front)
+
diff -r 780f097b54c5 -r 6524e02edbeb
tools/python/xen/xend/server/DevController.py
--- a/tools/python/xen/xend/server/DevController.py Wed Feb 07 17:29:52
2007 +0000
+++ b/tools/python/xen/xend/server/DevController.py Wed Feb 07 16:53:01
2007 -0800
@@ -473,6 +473,19 @@ class DevController:
def waitForBackend(self, devid):
frontpath = self.frontendPath(devid)
+ # lookup a phantom
+ phantomPath = xstransact.Read(frontpath, 'phantom_vbd')
+ if phantomPath is not None:
+ log.debug("Waiting for %s's phantom %s.", devid, phantomPath)
+ statusPath = phantomPath + '/' + HOTPLUG_STATUS_NODE
+ ev = Event()
+ result = { 'status': Timeout }
+ xswatch(statusPath, hotplugStatusCallback, ev, result)
+ ev.wait(DEVICE_CREATE_TIMEOUT)
+ err = xstransact.Read(statusPath, HOTPLUG_ERROR_NODE)
+ if result['status'] != 'Connected':
+ return (result['status'], err)
+
backpath = xstransact.Read(frontpath, "backend")
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|