ia64/xen-unstable

changeset 13874:6524e02edbeb

[blktap] Allow HVM booting from blktap device(s)

Signed-off-by: wim colgate <wim@xensource.com>
author wim@xen-wim.site
date Wed Feb 07 16:53:01 2007 -0800 (2007-02-07)
parents 780f097b54c5
children 3fbe12560ffe
files tools/ioemu/xenstore.c tools/python/xen/xend/XendConfig.py tools/python/xen/xend/XendDomainInfo.py tools/python/xen/xend/server/BlktapController.py tools/python/xen/xend/server/DevController.py
line diff
     1.1 --- a/tools/ioemu/xenstore.c	Wed Feb 07 17:29:52 2007 +0000
     1.2 +++ b/tools/ioemu/xenstore.c	Wed Feb 07 16:53:01 2007 -0800
     1.3 @@ -10,6 +10,7 @@
     1.4  
     1.5  #include "vl.h"
     1.6  #include "block_int.h"
     1.7 +#include <unistd.h>
     1.8  
     1.9  static struct xs_handle *xsh = NULL;
    1.10  static char *hd_filename[MAX_DISKS];
    1.11 @@ -52,11 +53,40 @@ void xenstore_check_new_media_present(in
    1.12      qemu_mod_timer(insert_timer, qemu_get_clock(rt_clock) + timeout);
    1.13  }
    1.14  
    1.15 +static int waitForDevice(char *path, char *field, char *desired)
    1.16 +{ 
    1.17 +    char *buf = NULL, *stat = NULL;
    1.18 +    unsigned int len;
    1.19 +    int val = 1;
    1.20 +
    1.21 +    /* loop until we find a value in xenstore, return 
    1.22 +     * if it was what we wanted, or not
    1.23 +     */
    1.24 +    while (1) {
    1.25 +        if (pasprintf(&buf, "%s/%s", path, field) == -1)
    1.26 +            goto done;
    1.27 +        free(stat);
    1.28 +        stat = xs_read(xsh, XBT_NULL, buf, &len);
    1.29 +        if (stat == NULL) {
    1.30 +            usleep(100000); /* 1/10th second, no path found */
    1.31 +        } else {
    1.32 +            val = strcmp(stat, desired);
    1.33 +            goto done;
    1.34 +        }
    1.35 +    }
    1.36 +
    1.37 +done:
    1.38 +    free(stat);
    1.39 +    free(buf);
    1.40 +    return val;
    1.41 +}
    1.42 +
    1.43  void xenstore_parse_domain_config(int domid)
    1.44  {
    1.45      char **e = NULL;
    1.46      char *buf = NULL, *path;
    1.47 -    char *bpath = NULL, *dev = NULL, *params = NULL, *type = NULL;
    1.48 +    char *fpath = NULL, *bpath = NULL,
    1.49 +         *dev = NULL, *params = NULL, *type = NULL;
    1.50      int i;
    1.51      unsigned int len, num, hd_index;
    1.52  
    1.53 @@ -120,7 +150,35 @@ void xenstore_parse_domain_config(int do
    1.54  	    hd_filename[hd_index] = params;	/* strdup() */
    1.55  	    params = NULL;		/* don't free params on re-use */
    1.56  	}
    1.57 +        /* 
    1.58 +         * check if device has a phantom vbd; the phantom is hooked
    1.59 +         * to the frontend device (for ease of cleanup), so lookup 
    1.60 +         * the frontend device, and see if there is a phantom_vbd
    1.61 +         * if there is, we will use resolution as the filename
    1.62 +         */
    1.63 +	if (pasprintf(&buf, "%s/device/vbd/%s/phantom_vbd", path, e[i]) == -1)
    1.64 +	    continue;
    1.65 +	free(fpath);
    1.66 +        fpath = xs_read(xsh, XBT_NULL, buf, &len);
    1.67 +	if (fpath != NULL) {
    1.68 +
    1.69 +            if (waitForDevice(fpath, "hotplug-status", "connected")) {
    1.70 +               continue;
    1.71 +            }
    1.72 +
    1.73 +	    if (pasprintf(&buf, "%s/dev", fpath) == -1)
    1.74 +	        continue;
    1.75 +            params = xs_read(xsh, XBT_NULL, buf , &len);
    1.76 +	    if (params != NULL) {
    1.77 +                free(hd_filename[hd_index]);
    1.78 +                hd_filename[hd_index] = params;
    1.79 +                params = NULL;              /* don't free params on re-use */
    1.80 +            }
    1.81 +        }
    1.82  	bs_table[hd_index] = bdrv_new(dev);
    1.83 +        /* re-establish buf */
    1.84 +	if (pasprintf(&buf, "%s/params", bpath) == -1)
    1.85 +	    continue;
    1.86  	/* check if it is a cdrom */
    1.87  	if (type && !strcmp(type, "cdrom")) {
    1.88  	    bdrv_set_type_hint(bs_table[hd_index], BDRV_TYPE_CDROM);
     2.1 --- a/tools/python/xen/xend/XendConfig.py	Wed Feb 07 17:29:52 2007 +0000
     2.2 +++ b/tools/python/xen/xend/XendConfig.py	Wed Feb 07 16:53:01 2007 -0800
     2.3 @@ -1148,6 +1148,47 @@ class XendConfig(dict):
     2.4          # no valid device to add
     2.5          return ''
     2.6  
     2.7 +    def phantom_device_add(self, dev_type, cfg_xenapi = None,
     2.8 +                   target = None):
     2.9 +        """Add a phantom tap device configuration in XenAPI struct format.
    2.10 +        """
    2.11 +
    2.12 +        if target == None:
    2.13 +            target = self
    2.14 +        
    2.15 +        if dev_type not in XendDevices.valid_devices() and \
    2.16 +           dev_type not in XendDevices.pseudo_devices():        
    2.17 +            raise XendConfigError("XendConfig: %s not a valid device type" %
    2.18 +                            dev_type)
    2.19 +
    2.20 +        if cfg_xenapi == None:
    2.21 +            raise XendConfigError("XendConfig: device_add requires some "
    2.22 +                                  "config.")
    2.23 +
    2.24 +        if cfg_xenapi:
    2.25 +            log.debug("XendConfig.phantom_device_add: %s" % str(cfg_xenapi))
    2.26 + 
    2.27 +        if cfg_xenapi:
    2.28 +            dev_info = {}            
    2.29 +            if dev_type in ('vbd', 'tap'):
    2.30 +                if dev_type == 'vbd':
    2.31 +                    dev_info['uname'] = cfg_xenapi.get('image', '')
    2.32 +                    dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
    2.33 +                elif dev_type == 'tap':
    2.34 +                    if cfg_xenapi.get('image').find('tap:') == -1:
    2.35 +                        dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
    2.36 +                    dev_info['dev'] =  '/dev/%s' % cfg_xenapi.get('device')
    2.37 +                    dev_info['uname'] = cfg_xenapi.get('image')
    2.38 +                dev_info['mode'] = cfg_xenapi.get('mode')
    2.39 +                dev_info['backend'] = '0'
    2.40 +                dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
    2.41 +                dev_info['uuid'] = dev_uuid
    2.42 +                self['devices'][dev_uuid] = (dev_type, dev_info)
    2.43 +                self['vbd_refs'].append(dev_uuid)
    2.44 +                return dev_uuid
    2.45 +
    2.46 +        return ''
    2.47 +
    2.48      def console_add(self, protocol, location, other_config = {}):
    2.49          dev_uuid = uuid.createString()
    2.50          if protocol == 'vt100':
     3.1 --- a/tools/python/xen/xend/XendDomainInfo.py	Wed Feb 07 17:29:52 2007 +0000
     3.2 +++ b/tools/python/xen/xend/XendDomainInfo.py	Wed Feb 07 16:53:01 2007 -0800
     3.3 @@ -1565,19 +1565,54 @@ class XendDomainInfo:
     3.4      # VM Destroy
     3.5      # 
     3.6  
     3.7 +    def _prepare_phantom_paths(self):
     3.8 +        # get associated devices to destroy
     3.9 +        # build list of phantom devices to be removed after normal devices
    3.10 +        plist = []
    3.11 +        from xen.xend.xenstore.xstransact import xstransact
    3.12 +        t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
    3.13 +        for dev in t.list():
    3.14 +            backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
    3.15 +                                  % (self.dompath, dev))
    3.16 +            if backend_phantom_vbd is not None:
    3.17 +                frontend_phantom_vbd =  xstransact.Read("%s/frontend" \
    3.18 +                                  % backend_phantom_vbd)
    3.19 +                plist.append(backend_phantom_vbd)
    3.20 +                plist.append(frontend_phantom_vbd)
    3.21 +        return plist
    3.22 +
    3.23 +    def _cleanup_phantom_devs(self, plist):
    3.24 +        # remove phantom devices
    3.25 +        if not plist == []:
    3.26 +            time.sleep(2)
    3.27 +        for paths in plist:
    3.28 +            if paths.find('backend') != -1:
    3.29 +                from xen.xend.server import DevController
    3.30 +                # Modify online status /before/ updating state (latter is watched by
    3.31 +                # drivers, so this ordering avoids a race).
    3.32 +                xstransact.Write(paths, 'online', "0")
    3.33 +                xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
    3.34 +            # force
    3.35 +            xstransact.Remove(paths)
    3.36 +
    3.37      def destroy(self):
    3.38          """Cleanup VM and destroy domain.  Nothrow guarantee."""
    3.39  
    3.40          log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
    3.41  
    3.42 +        paths = self._prepare_phantom_paths()
    3.43 +
    3.44          self._cleanupVm()
    3.45          if self.dompath is not None:
    3.46              self.destroyDomain()
    3.47  
    3.48 +        self._cleanup_phantom_devs(paths)
    3.49  
    3.50      def destroyDomain(self):
    3.51          log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
    3.52  
    3.53 +        paths = self._prepare_phantom_paths()
    3.54 +
    3.55          try:
    3.56              if self.domid is not None:
    3.57                  xc.domain_destroy(self.domid)
    3.58 @@ -1591,7 +1626,7 @@ class XendDomainInfo:
    3.59          XendDomain.instance().remove_domain(self)
    3.60  
    3.61          self.cleanupDomain()
    3.62 -
    3.63 +        self._cleanup_phantom_devs(paths)
    3.64  
    3.65      def resumeDomain(self):
    3.66          log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
    3.67 @@ -2211,6 +2246,25 @@ class XendDomainInfo:
    3.68  
    3.69          return dev_uuid
    3.70  
    3.71 +    def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
    3.72 +        """Create a VBD using a VDI from XendStorageRepository.
    3.73 +
    3.74 +        @param xenapi_vbd: vbd struct from the Xen API
    3.75 +        @param vdi_image_path: VDI UUID
    3.76 +        @rtype: string
    3.77 +        @return: uuid of the device
    3.78 +        """
    3.79 +        xenapi_vbd['image'] = vdi_image_path
    3.80 +        dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
    3.81 +        if not dev_uuid:
    3.82 +            raise XendError('Failed to create device')
    3.83 +
    3.84 +        if self.state == XEN_API_VM_POWER_STATE_RUNNING:
    3.85 +            _, config = self.info['devices'][dev_uuid]
    3.86 +            config['devid'] = self.getDeviceController('tap').createDevice(config)
    3.87 +
    3.88 +        return config['devid']
    3.89 +
    3.90      def create_vif(self, xenapi_vif):
    3.91          """Create VIF device from the passed struct in Xen API format.
    3.92  
     4.1 --- a/tools/python/xen/xend/server/BlktapController.py	Wed Feb 07 17:29:52 2007 +0000
     4.2 +++ b/tools/python/xen/xend/server/BlktapController.py	Wed Feb 07 16:53:01 2007 -0800
     4.3 @@ -2,7 +2,10 @@
     4.4  
     4.5  
     4.6  from xen.xend.server.blkif import BlkifController
     4.7 +from xen.xend.XendLogging import log
     4.8  
     4.9 +phantomDev = 0;
    4.10 +phantomId = 0;
    4.11  
    4.12  class BlktapController(BlkifController):
    4.13      def __init__(self, vm):
    4.14 @@ -12,3 +15,62 @@ class BlktapController(BlkifController):
    4.15          """@see DevController#frontendRoot"""
    4.16          
    4.17          return "%s/device/vbd" % self.vm.getDomainPath()
    4.18 +
    4.19 +    def getDeviceDetails(self, config):
    4.20 +        (devid, back, front) = BlkifController.getDeviceDetails(self, config)
    4.21 +
    4.22 +        phantomDevid = 0
    4.23 +        wrapped = False
    4.24 +
    4.25 +        try:
    4.26 +            imagetype = self.vm.info['image']['type']
    4.27 +        except:
    4.28 +            imagetype = ""
    4.29 +
    4.30 +        if imagetype == 'hvm':
    4.31 +            tdevname = back['dev']
    4.32 +            index = ['c', 'd', 'e', 'f', 'g', 'h', 'i', \
    4.33 +                     'j', 'l', 'm', 'n', 'o', 'p']
    4.34 +            while True:
    4.35 +                global phantomDev
    4.36 +                global phantomId
    4.37 +                import os, stat
    4.38 +
    4.39 +                phantomId = phantomId + 1
    4.40 +                if phantomId == 16:
    4.41 +                    if index[phantomDev] == index[-1]:
    4.42 +                        if wrapped:
    4.43 +                            raise VmError(" No loopback block \
    4.44 +                                       devices are available. ")
    4.45 +                        wrapped = True
    4.46 +                        phantomDev = 0
    4.47 +                    else:
    4.48 +                        phantomDev = phantomDev + 1
    4.49 +                    phantomId = 1
    4.50 +                devname = 'xvd%s%d' % (index[phantomDev], phantomId)
    4.51 +                try:
    4.52 +                    info = os.stat('/dev/%s' % devname)
    4.53 +                except:
    4.54 +                    break
    4.55 +
    4.56 +            vbd = { 'mode': 'w', 'device': devname }
    4.57 +            fn = 'tap:%s' % back['params']
    4.58 +
    4.59 +            # recurse ... by creating the vbd, then fallthrough
    4.60 +            # and finish creating the original device
    4.61 +
    4.62 +            from xen.xend import XendDomain
    4.63 +            dom0 = XendDomain.instance().privilegedDomain()
    4.64 +            phantomDevid = dom0.create_phantom_vbd_with_vdi(vbd, fn)
    4.65 +            # we need to wait for this device at a higher level
    4.66 +            # the vbd that gets created will have a link to us
    4.67 +            # and will let them do it there
    4.68 +
    4.69 +        # add a hook to point to the phantom device,
    4.70 +        # root path is always the same (dom0 tap)
    4.71 +        if phantomDevid != 0:
    4.72 +            front['phantom_vbd'] = '/local/domain/0/backend/tap/0/%s' \
    4.73 +                                   % str(phantomDevid)
    4.74 +
    4.75 +        return (devid, back, front)
    4.76 +
     5.1 --- a/tools/python/xen/xend/server/DevController.py	Wed Feb 07 17:29:52 2007 +0000
     5.2 +++ b/tools/python/xen/xend/server/DevController.py	Wed Feb 07 16:53:01 2007 -0800
     5.3 @@ -473,6 +473,19 @@ class DevController:
     5.4      def waitForBackend(self, devid):
     5.5  
     5.6          frontpath = self.frontendPath(devid)
     5.7 +        # lookup a phantom 
     5.8 +        phantomPath = xstransact.Read(frontpath, 'phantom_vbd')
     5.9 +        if phantomPath is not None:
    5.10 +            log.debug("Waiting for %s's phantom %s.", devid, phantomPath)
    5.11 +            statusPath = phantomPath + '/' + HOTPLUG_STATUS_NODE
    5.12 +            ev = Event()
    5.13 +            result = { 'status': Timeout }
    5.14 +            xswatch(statusPath, hotplugStatusCallback, ev, result)
    5.15 +            ev.wait(DEVICE_CREATE_TIMEOUT)
    5.16 +            err = xstransact.Read(statusPath, HOTPLUG_ERROR_NODE)
    5.17 +            if result['status'] != 'Connected':
    5.18 +                return (result['status'], err)
    5.19 +            
    5.20          backpath = xstransact.Read(frontpath, "backend")
    5.21  
    5.22