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@cs.ubc.ca>
This commit is contained in:
Keir Fraser committed 2009-07-01 10:53:15 +01:00
1 parent 756b7b81f9
commit 286423408d
8 files changed
+117 -87

No files matched your search

+6 -6
View File
@@ -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'
+2 -1
View File
@@ -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,
+46 -34
View File
@@ -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')
@@ -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)
@@ -60,11 +59,6 @@ class BlktapController(BlkifController):
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
@@ -238,34 +238,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()
+1 -1
View File
@@ -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'
+4 -2
View File
@@ -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')
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.