[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] blktap2: add blktap2 device class and device controller



# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1246441995 -3600
# Node ID 72db35d854f3ab53a5e651db5b44de6885fb4914
# Parent  238a148b1447de993f40c97f6cd8f76e202cfa3f
blktap2: add blktap2 device class and device controller

blktap2 devices must be handled differently than blktap2
devices. blktap2 devices require a sysfs write to close the underlying
device, as well as extra sysfs writes when the domU is
paused/unpaused. The differences between blktap1 and blktap2 are great
enough to warrant the creation of a new device class, 'tap2', and
device controller for blktap2 devices.

  * add a new device controller (Blktap2Controller) and device class
    (tap2) for blktap2 devices
  * move blktap2 specific code from DevController to Blktap2Controller
  * if possible, check xenstore to determine block device class
  * use vmpath (/vm/<uuid>/) when releasing devices
  * modify linux hotplug cleanup script to handle blktap2 device
    removal

Signed-off-by: Ryan O'Connor <rjo@xxxxxxxxx>
---
 tools/hotplug/Linux/xen-hotplug-cleanup          |    7 ++
 tools/python/xen/xend/XendConfig.py              |   12 +--
 tools/python/xen/xend/XendDevices.py             |    3 
 tools/python/xen/xend/XendDomainInfo.py          |   80 +++++++++++++----------
 tools/python/xen/xend/server/BlktapController.py |   65 ++++++++++++++----
 tools/python/xen/xend/server/DevController.py    |   29 --------
 tools/python/xen/xm/create.py                    |    2 
 tools/python/xen/xm/main.py                      |    6 +
 8 files changed, 117 insertions(+), 87 deletions(-)

diff -r 238a148b1447 -r 72db35d854f3 tools/hotplug/Linux/xen-hotplug-cleanup
--- a/tools/hotplug/Linux/xen-hotplug-cleanup   Tue Jun 30 16:00:57 2009 +0100
+++ b/tools/hotplug/Linux/xen-hotplug-cleanup   Wed Jul 01 10:53:15 2009 +0100
@@ -18,6 +18,13 @@ vm=$(xenstore_read_default "/local/domai
 # construct /vm/UUID/device/DEVCLASS/DEVID
 if [ "$vm" != "" ]; then
   vm_dev="$vm/device/${path_array[1]}/${path_array[3]}"
+
+  # if the vm path does not exist and the device class is 'vbd' then we may 
have
+  # a tap2 device
+  if [ ! $(xenstore-read "vm_dev" 2>/dev/null) ] \
+       && [ "${path_array[1]}" = "vbd" ]; then
+    vm_dev="$vm/device/tap2/${path_array[3]}"
+  fi
 else
   vm_dev=
 fi
diff -r 238a148b1447 -r 72db35d854f3 tools/python/xen/xend/XendConfig.py
--- a/tools/python/xen/xend/XendConfig.py       Tue Jun 30 16:00:57 2009 +0100
+++ b/tools/python/xen/xend/XendConfig.py       Wed Jul 01 10:53:15 2009 +0100
@@ -1114,7 +1114,7 @@ class XendConfig(dict):
                             controller = domain.getDeviceController(cls)
                             configs = controller.configurations(txn)
                             for config in configs:
-                                if sxp.name(config) in ('vbd', 'tap'):
+                                if sxp.name(config) in ('vbd', 'tap', 'tap2'):
                                     dev_uuid = sxp.child_value(config, 'uuid')
                                     dev_type, dev_cfg = 
self['devices'][dev_uuid]
                                     if sxp.child_value(config, 'bootable', 
None) is None:
@@ -1179,7 +1179,7 @@ class XendConfig(dict):
     def device_duplicate_check(self, dev_type, dev_info, defined_config, 
config):
         defined_devices_sxpr = self.all_devices_sxpr(target = defined_config)
         
-        if dev_type == 'vbd' or dev_type == 'tap':
+        if dev_type == 'vbd' or dev_type == 'tap' or dev_type == 'tap2':
             dev_uname = dev_info.get('uname')
             blkdev_name = dev_info.get('dev')
             devid = self._blkdev_name_to_number(blkdev_name)
@@ -1187,7 +1187,7 @@ class XendConfig(dict):
                 return
             
             for o_dev_type, o_dev_info in defined_devices_sxpr:
-                if o_dev_type == 'vbd' or o_dev_type == 'tap':
+                if o_dev_type == 'vbd' or o_dev_type == 'tap' or o_dev_type == 
'tap2':
                     blkdev_file = blkdev_uname_to_file(dev_uname)
                     o_dev_uname = sxp.child_value(o_dev_info, 'uname')
                     if o_dev_uname != None:
@@ -1369,7 +1369,7 @@ class XendConfig(dict):
                 else:
                     dev_info['driver'] = 'paravirtualised'
 
-            if dev_type == 'tap':
+            if dev_type == 'tap' or dev_type == 'tap2':
                 if dev_info['uname'].split(':')[1] not in blktap_disk_types:
                     raise XendConfigError("tap:%s not a valid disk type" %
                                     dev_info['uname'].split(':')[1])
@@ -1406,7 +1406,7 @@ class XendConfig(dict):
                         # Compat hack -- mark first disk bootable
                         dev_info['bootable'] = int(not target[param])
                     target[param].append(dev_uuid)
-            elif dev_type == 'tap':
+            elif dev_type == 'tap' or dev_type == 'tap2':
                 if 'vbd_refs' not in target:
                     target['vbd_refs'] = []
                 if dev_uuid not in target['vbd_refs']:
@@ -1504,7 +1504,7 @@ class XendConfig(dict):
                 target['devices'][dev_uuid] = (dev_type, dev_info)
                 target['vif_refs'].append(dev_uuid)
             
-            elif dev_type in ('vbd', 'tap'):
+            elif dev_type in ('vbd', 'tap', 'tap2'):
                 dev_info['type'] = cfg_xenapi.get('type', 'Disk')
                 if dev_info['type'] == 'CD':
                     old_vbd_type = 'cdrom'
diff -r 238a148b1447 -r 72db35d854f3 tools/python/xen/xend/XendDevices.py
--- a/tools/python/xen/xend/XendDevices.py      Tue Jun 30 16:00:57 2009 +0100
+++ b/tools/python/xen/xend/XendDevices.py      Wed Jul 01 10:53:15 2009 +0100
@@ -20,7 +20,7 @@
 #
 
 from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, vfbif, 
vscsiif
-from xen.xend.server.BlktapController import BlktapController
+from xen.xend.server.BlktapController import BlktapController, 
Blktap2Controller
 from xen.xend.server.ConsoleController import ConsoleController
 
 
@@ -42,6 +42,7 @@ class XendDevices:
         'ioports': iopif.IOPortsController,
         'irq': irqif.IRQController,
         'tap': BlktapController,
+        'tap2': Blktap2Controller,
         'vfb': vfbif.VfbifController,
         'vkbd': vfbif.VkbdifController,
         'console': ConsoleController,
diff -r 238a148b1447 -r 72db35d854f3 tools/python/xen/xend/XendDomainInfo.py
--- a/tools/python/xen/xend/XendDomainInfo.py   Tue Jun 30 16:00:57 2009 +0100
+++ b/tools/python/xen/xend/XendDomainInfo.py   Wed Jul 01 10:53:15 2009 +0100
@@ -538,12 +538,11 @@ class XendDomainInfo:
         @raise XendError: Failed pausing a domain
         """
         try:
-            bepath="/local/domain/0/backend/"
             if(self.domid):
-                
-                dev =  xstransact.List(bepath + 'vbd' + "/%d" % (self.domid,))
+                # get all blktap2 devices
+                dev =  xstransact.List(self.vmpath + 'device/tap2')
                 for x in dev:
-                    path = self.getDeviceController('vbd').readBackend(x, 
'params')
+                    path = self.getDeviceController('tap2').readBackend(x, 
'params')
                     if path and path.startswith('/dev/xen/blktap-2'):
                         #Figure out the sysfs path.
                         pattern = re.compile('/dev/xen/blktap-2/tapdev(\d+)$')
@@ -569,11 +568,10 @@ class XendDomainInfo:
         @raise XendError: Failed unpausing a domain
         """
         try:
-            bepath="/local/domain/0/backend/"
             if(self.domid):
-                dev =  xstransact.List(bepath + "vbd" + "/%d" % (self.domid,))
+                dev =  xstransact.List(self.vmpath + 'device/tap2')
                 for x in dev:
-                    path = self.getDeviceController('vbd').readBackend(x, 
'params')
+                    path = self.getDeviceController('tap2').readBackend(x, 
'params')
                     if path and path.startswith('/dev/xen/blktap-2'):
                         #Figure out the sysfs path.
                         pattern = re.compile('/dev/xen/blktap-2/tapdev(\d+)$')
@@ -812,6 +810,11 @@ class XendDomainInfo:
             try:
                 dev_config_dict['devid'] = devid = \
                     self._createDevice(dev_type, dev_config_dict)
+                if dev_type == 'tap2':
+                    # createDevice may create a blktap1 device if blktap2 is 
not
+                    # installed or if the blktap driver is not supported in
+                    # blktap1
+                    dev_type = self.getBlockDeviceClass(devid)
                 self._waitForDevice(dev_type, devid)
             except VmError, ex:
                 del self.info['devices'][dev_uuid]
@@ -821,7 +824,7 @@ class XendDomainInfo:
                 elif dev_type == 'vscsi':
                     for dev in dev_config_dict['devs']:
                         XendAPIStore.deregister(dev['uuid'], 'DSCSI')
-                elif dev_type == 'tap':
+                elif dev_type == 'tap' or dev_type == 'tap2':
                     self.info['vbd_refs'].remove(dev_uuid)
                 else:
                     self.info['%s_refs' % dev_type].remove(dev_uuid)
@@ -1200,9 +1203,9 @@ class XendDomainInfo:
         if self.domid is not None:
             
             #new blktap implementation may need a sysfs write after everything 
is torn down.
-            dev = 
self.getDeviceController(deviceClass).convertToDeviceNumber(devid)
-            path = self.getDeviceController(deviceClass).readBackend(dev, 
'params')                
-            if path and path.startswith('/dev/xen/blktap-2'):
+            if deviceClass == 'tap2':
+                dev = 
self.getDeviceController(deviceClass).convertToDeviceNumber(devid)
+                path = self.getDeviceController(deviceClass).readBackend(dev, 
'params')
                 frontpath = 
self.getDeviceController(deviceClass).frontendPath(dev)
                 backpath = xstransact.Read(frontpath, "backend")
                 
thread.start_new_thread(self.getDeviceController(deviceClass).finishDeviceCleanup,
 (backpath, path))
@@ -1238,7 +1241,7 @@ class XendDomainInfo:
                     dev_info = self._getDeviceInfo_vif(mac)
                 else:
                     _, dev_info = sxprs[dev]
-            else:  # 'vbd' or 'tap'
+            else:  # 'vbd' or 'tap' or 'tap2'
                 dev_info = self._getDeviceInfo_vbd(dev)
                 # To remove the UUID of the device from refs,
                 # deviceClass must be always 'vbd'.
@@ -1267,7 +1270,7 @@ class XendDomainInfo:
             sxprs = []
             dev_num = 0
             for dev_type, dev_info in self.info.all_devices_sxpr():
-                if (deviceClass == 'vbd' and dev_type not in ['vbd', 'tap']) 
or \
+                if (deviceClass == 'vbd' and dev_type not in ['vbd', 'tap', 
'tap2']) or \
                    (deviceClass != 'vbd' and dev_type != deviceClass):
                     continue
 
@@ -1295,12 +1298,27 @@ class XendDomainInfo:
             return sxprs
 
     def getBlockDeviceClass(self, devid):
-        # To get a device number from the devid,
-        # we temporarily use the device controller of VBD.
-        dev = self.getDeviceController('vbd').convertToDeviceNumber(devid)
-        dev_info = self._getDeviceInfo_vbd(dev)
-        if dev_info:
-            return dev_info[0]
+        # if the domain is running we can get the device class from xenstore.
+        # This is more accurate, as blktap1 devices show up as blktap2 devices
+        # in the config.
+        if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED, 
DOM_STATE_CRASHED):
+            # All block devices have a vbd frontend, so we know the frontend 
path
+            dev = self.getDeviceController('vbd').convertToDeviceNumber(devid)
+            frontendPath = "%s/device/vbd/%s" % (self.dompath, dev)
+            for devclass in XendDevices.valid_devices():
+                for dev in xstransact.List("%s/device/%s" % (self.vmpath, 
devclass)):
+                    devFrontendPath = 
xstransact.Read("%s/device/%s/%s/frontend" % (self.vmpath, devclass, dev))
+                    if frontendPath == devFrontendPath:
+                        return devclass
+
+        else: # the domain is not active so we must get the device class
+              # from the config
+            # To get a device number from the devid,
+            # we temporarily use the device controller of VBD.
+            dev = self.getDeviceController('vbd').convertToDeviceNumber(devid)
+            dev_info = self._getDeviceInfo_vbd(dev)
+            if dev_info:
+                return dev_info[0]
 
     def _getDeviceInfo_vif(self, mac):
         for dev_type, dev_info in self.info.all_devices_sxpr():
@@ -1311,7 +1329,7 @@ class XendDomainInfo:
 
     def _getDeviceInfo_vbd(self, devid):
         for dev_type, dev_info in self.info.all_devices_sxpr():
-            if dev_type != 'vbd' and dev_type != 'tap':
+            if dev_type != 'vbd' and dev_type != 'tap' and dev_type != 'tap2':
                 continue
             dev = sxp.child_value(dev_info, 'dev')
             dev = dev.split(':')[0]
@@ -2256,26 +2274,19 @@ class XendDomainInfo:
             log.debug("No device model")
 
         log.debug("Releasing devices")
-        t = xstransact("%s/device" % self.dompath)
+        t = xstransact("%s/device" % self.vmpath)
         try:
             for devclass in XendDevices.valid_devices():
                 for dev in t.list(devclass):
                     try:
-                        true_devclass = devclass
-                        if devclass == 'vbd':
-                            # In the case of "vbd", the true device class
-                            # may possibly be "tap". Just in case, verify
-                            # device class.
-                            devid = dev.split('/')[-1]
-                            true_devclass = self.getBlockDeviceClass(devid)
                         log.debug("Removing %s", dev);
-                        self.destroyDevice(true_devclass, dev, False);
+                        self.destroyDevice(devclass, dev, False);
                     except:
                         # Log and swallow any exceptions in removal --
                         # there's nothing more we can do.
                         log.exception("Device release failed: %s; %s; %s",
                                       self.info['name_label'],
-                                      true_devclass, dev)
+                                      devclass, dev)
         finally:
             t.abort()
 
@@ -2948,7 +2959,7 @@ class XendDomainInfo:
 
             fn = blkdev_uname_to_file(disk)
             taptype = blkdev_uname_to_taptype(disk)
-            mounted = devtype == 'tap' and taptype != 'aio' and taptype != 
'sync' and not os.stat(fn).st_rdev
+            mounted = devtype in ['tap', 'tap2'] and taptype != 'aio' and 
taptype != 'sync' and not os.stat(fn).st_rdev
             if mounted:
                 # This is a file, not a device.  pygrub can cope with a
                 # file if it's raw, but if it's QCOW or other such formats
@@ -3052,7 +3063,8 @@ class XendDomainInfo:
             diff = time.time() - start
             vbds = self.getDeviceController('vbd').deviceIDs()
             taps = self.getDeviceController('tap').deviceIDs()
-            for i in vbds + taps:
+            tap2s = self.getDeviceController('tap2').deviceIDs()
+            for i in vbds + taps + tap2s:
                 test = 1
                 log.info("Dev %s still active, looping...", i)
                 time.sleep(0.1)
@@ -3635,7 +3647,7 @@ class XendDomainInfo:
         """
         xenapi_vbd['image'] = vdi_image_path
         if vdi_image_path.startswith('tap'):
-            dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
+            dev_uuid = self.info.device_add('tap2', cfg_xenapi = xenapi_vbd)
         else:
             dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
             
@@ -3647,7 +3659,7 @@ class XendDomainInfo:
             _, config = self.info['devices'][dev_uuid]
             
             if vdi_image_path.startswith('tap'):
-                dev_control = self.getDeviceController('tap')
+                dev_control = self.getDeviceController('tap2')
             else:
                 dev_control = self.getDeviceController('vbd')
 
diff -r 238a148b1447 -r 72db35d854f3 
tools/python/xen/xend/server/BlktapController.py
--- a/tools/python/xen/xend/server/BlktapController.py  Tue Jun 30 16:00:57 
2009 +0100
+++ b/tools/python/xen/xend/server/BlktapController.py  Wed Jul 01 10:53:15 
2009 +0100
@@ -24,7 +24,7 @@ blktap_disk_types = [
     'ioemu',
     'tapdisk',
     ]
- 
+
 def doexec(args, inputtext=None):
     """Execute a subprocess, then return its return code, stdout and stderr"""
     proc = popen2.Popen3(args, True)
@@ -49,8 +49,7 @@ def parseDeviceString(device):
 
     return minor, device, control
 
-
-
+# blktap1 device controller
 class BlktapController(BlkifController):
     def __init__(self, vm):
         BlkifController.__init__(self, vm)
@@ -59,11 +58,6 @@ class BlktapController(BlkifController):
         """@see DevController#frontendRoot"""
         
         return "%s/device/vbd" % self.vm.getDomainPath()
-
-    def devicePath(self, devid):
-        """@see DevController#devicePath"""
-        
-        return "%s/device/vbd/%s" % (self.vm.vmpath, devid)
 
     def getDeviceDetails(self, config):
         (devid, back, front) = BlkifController.getDeviceDetails(self, config)
@@ -123,6 +117,20 @@ class BlktapController(BlkifController):
 
         return (devid, back, front)
 
+class Blktap2Controller(BlktapController):
+    def __init__(self, vm):
+        BlktapController.__init__(self, vm)
+
+    def backendPath(self, backdom, devid):
+        if self.deviceClass == 'tap2':
+            deviceClass = 'vbd'
+        else:
+            deviceClass = 'tap'
+        return "%s/backend/%s/%s/%d" % (backdom.getDomainPath(),
+                                        deviceClass,
+                                        self.vm.getDomid(), devid)
+
+
     def createDevice(self, config):
 
         uname = config.get('required_uname', '')
@@ -140,12 +148,16 @@ class BlktapController(BlkifController):
         stderr.close();
         if( out.find("blktap2") >= 0 ):
             blktap2_installed=1;
-           
+
         if typ in ('tap'):
-            if subtyp in ('tapdisk'):                                          
+            if subtyp in ('tapdisk'):
                 if params in ('ioemu', 'qcow2', 'vmdk', 'sync') or not 
blktap2_installed:
-                    log.warn('WARNING: using deprecated blktap module');
-                    return BlkifController.createDevice(self, config);
+                    # pass this device off to BlktapController
+                    log.warn('WARNING: using deprecated blktap module')
+                    self.deviceClass = 'tap'
+                    devid = BlktapController.createDevice(self, config)
+                    self.deviceClass = 'tap2'
+                    return devid
 
         cmd = [ TAPDISK_BINARY, '-n', '%s:%s' % (params, file) ]
         (rc,stdout,stderr) = doexec(cmd)
@@ -165,7 +177,32 @@ class BlktapController(BlkifController):
         #device is configured.  Then continue to create the device
         config.update({'uname' : 'phy:' + device.rstrip()})
 
-        self.deviceClass='vbd'
         devid = BlkifController.createDevice(self, config)
-        self.deviceClass='tap'
         return devid
+
+    # The new blocktap implementation requires a sysfs signal to close
+    # out disks.  This function is called from a thread when the
+    # domain is detached from the disk.
+    def finishDeviceCleanup(self, backpath, path):
+        """Perform any device specific cleanup
+
+        @backpath backend xenstore path.
+        @path frontend device path
+
+        """
+
+        #Figure out what we're going to wait on.
+        self.waitForBackend_destroy(backpath)
+
+        #Figure out the sysfs path.
+        pattern = re.compile('/dev/xen/blktap-2/tapdev(\d+)$')
+        ctrlid = pattern.search(path)
+        ctrl = '/sys/class/blktap2/blktap' + ctrlid.group(1)
+
+        #Close out the disk
+        f = open(ctrl + '/remove', 'w')
+        f.write('remove');
+        f.close()
+
+        return
+
diff -r 238a148b1447 -r 72db35d854f3 
tools/python/xen/xend/server/DevController.py
--- a/tools/python/xen/xend/server/DevController.py     Tue Jun 30 16:00:57 
2009 +0100
+++ b/tools/python/xen/xend/server/DevController.py     Wed Jul 01 10:53:15 
2009 +0100
@@ -237,34 +237,6 @@ class DevController:
 
             # xstransact.Remove(self.devicePath()) ?? Below is the same ?
             self.vm._removeVm("device/%s/%d" % (self.deviceClass, dev))
-
-    # The new blocktap implementation requires a sysfs signal to close
-    # out disks.  This function is called from a thread when the
-    # domain is detached from the disk.
-    def finishDeviceCleanup(self, backpath, path):
-        """Perform any device specific cleanup
-
-        @backpath backend xenstore path.
-        @path frontend device path
-
-        """
-        
-        if path and path.startswith('/dev/xen/blktap-2'):
-            
-            #Figure out what we're going to wait on.
-            self.waitForBackend_destroy(backpath)            
-
-            #Figure out the sysfs path.
-            pattern = re.compile('/dev/xen/blktap-2/tapdev(\d+)$')
-            ctrlid = pattern.search(path)
-            ctrl = '/sys/class/blktap2/blktap' + ctrlid.group(1)
-            
-            #Close out the disk
-            f = open(ctrl + '/remove', 'w')
-            f.write('remove');
-            f.close()
-
-        return
 
     def configurations(self, transaction = None):
         return map(lambda x: self.configuration(x, transaction), 
self.deviceIDs(transaction))
@@ -575,7 +547,6 @@ class DevController:
             
         backpath = self.readVm(devid, "backend")
 
-
         if backpath:
             statusPath = backpath + '/' + HOTPLUG_STATUS_NODE
             ev = Event()
diff -r 238a148b1447 -r 72db35d854f3 tools/python/xen/xm/create.py
--- a/tools/python/xen/xm/create.py     Tue Jun 30 16:00:57 2009 +0100
+++ b/tools/python/xen/xm/create.py     Wed Jul 01 10:53:15 2009 +0100
@@ -718,7 +718,7 @@ def configure_disks(config_devs, vals):
     """
     for (uname, dev, mode, backend, protocol) in vals.disk:
         if uname.startswith('tap:'):
-            cls = 'tap'
+            cls = 'tap2'
         else:
             cls = 'vbd'
 
diff -r 238a148b1447 -r 72db35d854f3 tools/python/xen/xm/main.py
--- a/tools/python/xen/xm/main.py       Tue Jun 30 16:00:57 2009 +0100
+++ b/tools/python/xen/xm/main.py       Wed Jul 01 10:53:15 2009 +0100
@@ -2371,7 +2371,7 @@ def parse_block_configuration(args):
     dom = args[0]
 
     if args[1].startswith('tap:'):
-        cls = 'tap'
+        cls = 'tap2'
     else:
         cls = 'vbd'
 
@@ -2706,7 +2706,9 @@ def xm_block_detach(args):
         dom = args[0]
         dev = args[1]
         dc = server.xend.domain.getBlockDeviceClass(dom, dev)
-        if dc == "tap":
+        if dc == "tap2":
+            detach(args, 'tap2')
+        elif dc == "tap":
             detach(args, 'tap')
         else:
             detach(args, 'vbd')

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.