mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
xend: Reduce xenstore transactions when listing domains
In summary, this allows a xenstore transaction object to be passed around the various device controllers, so that they don't have to do lots of singleton transactions. Transactions have very heavy I/O impact from xenstored so reducing their number is important. When running 3 guests, this patch reduces the impact of 'xm list --long' from 176 transactions, scaling O(n) with guests, to 26 transactions with O(1) scaling. I have previously attempted to also address the same issue with 'xm create' but that's much harder since the device front/back handshake requires that XenD use a number of small transactions. So i've not changed anything here. Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
1 parent
e33d4a7316
commit
27d2e52b7c
10 files changed
+155
-56
No files matched your search
@@ -28,6 +28,7 @@ from xen.xend.XendError import VmError
|
||||
from xen.xend.XendDevices import XendDevices
|
||||
from xen.xend.PrettyPrint import prettyprintstring
|
||||
from xen.xend.XendConstants import DOM_STATE_HALTED
|
||||
from xen.xend.xenstore.xstransact import xstransact
|
||||
from xen.xend.server.BlktapController import blktap_disk_types
|
||||
from xen.xend.server.netif import randomMAC
|
||||
from xen.util.blkif import blkdev_name_to_number
|
||||
@@ -941,36 +942,43 @@ class XendConfig(dict):
|
||||
|
||||
# Marshall devices (running or from configuration)
|
||||
if not ignore_devices:
|
||||
for cls in XendDevices.valid_devices():
|
||||
found = False
|
||||
txn = xstransact()
|
||||
try:
|
||||
for cls in XendDevices.valid_devices():
|
||||
found = False
|
||||
|
||||
# figure if there is a dev controller is valid and running
|
||||
if domain and domain.getDomid() != None:
|
||||
try:
|
||||
controller = domain.getDeviceController(cls)
|
||||
configs = controller.configurations()
|
||||
for config in configs:
|
||||
if sxp.name(config) in ('vbd', 'tap'):
|
||||
# The bootable flag is never written to the
|
||||
# store as part of the device config.
|
||||
dev_uuid = sxp.child_value(config, 'uuid')
|
||||
dev_type, dev_cfg = self['devices'][dev_uuid]
|
||||
is_bootable = dev_cfg.get('bootable', 0)
|
||||
config.append(['bootable', int(is_bootable)])
|
||||
# figure if there is a dev controller is valid and running
|
||||
if domain and domain.getDomid() != None:
|
||||
try:
|
||||
controller = domain.getDeviceController(cls)
|
||||
configs = controller.configurations(txn)
|
||||
for config in configs:
|
||||
if sxp.name(config) in ('vbd', 'tap'):
|
||||
# The bootable flag is never written to the
|
||||
# store as part of the device config.
|
||||
dev_uuid = sxp.child_value(config, 'uuid')
|
||||
dev_type, dev_cfg = self['devices'][dev_uuid]
|
||||
is_bootable = dev_cfg.get('bootable', 0)
|
||||
config.append(['bootable', int(is_bootable)])
|
||||
|
||||
sxpr.append(['device', config])
|
||||
sxpr.append(['device', config])
|
||||
|
||||
found = True
|
||||
except:
|
||||
log.exception("dumping sxp from device controllers")
|
||||
pass
|
||||
found = True
|
||||
except:
|
||||
log.exception("dumping sxp from device controllers")
|
||||
pass
|
||||
|
||||
# if we didn't find that device, check the existing config
|
||||
# for a device in the same class
|
||||
if not found:
|
||||
for dev_type, dev_info in self.all_devices_sxpr():
|
||||
if dev_type == cls:
|
||||
sxpr.append(['device', dev_info])
|
||||
# if we didn't find that device, check the existing config
|
||||
# for a device in the same class
|
||||
if not found:
|
||||
for dev_type, dev_info in self.all_devices_sxpr():
|
||||
if dev_type == cls:
|
||||
sxpr.append(['device', dev_info])
|
||||
|
||||
txn.commit()
|
||||
except:
|
||||
txn.abort()
|
||||
raise
|
||||
|
||||
return sxpr
|
||||
|
||||
|
||||
@@ -393,13 +393,22 @@ class XendDomain:
|
||||
@rtype: None
|
||||
"""
|
||||
|
||||
txn = xstransact()
|
||||
try:
|
||||
self._refreshTxn(txn, refresh_shutdown)
|
||||
txn.commit()
|
||||
except:
|
||||
txn.abort()
|
||||
raise
|
||||
|
||||
def _refreshTxn(self, transaction, refresh_shutdown):
|
||||
running = self._running_domains()
|
||||
# Add domains that are not already tracked but running in Xen,
|
||||
# and update domain state for those that are running and tracked.
|
||||
for dom in running:
|
||||
domid = dom['domid']
|
||||
if domid in self.domains:
|
||||
self.domains[domid].update(dom, refresh_shutdown)
|
||||
self.domains[domid].update(dom, refresh_shutdown, transaction)
|
||||
elif domid not in self.domains and dom['dying'] != 1:
|
||||
try:
|
||||
new_dom = XendDomainInfo.recreate(dom, False)
|
||||
|
||||
@@ -819,12 +819,15 @@ class XendDomainInfo:
|
||||
|
||||
self._update_consoles()
|
||||
|
||||
def _update_consoles(self):
|
||||
def _update_consoles(self, transaction = None):
|
||||
if self.domid == None or self.domid == 0:
|
||||
return
|
||||
|
||||
# Update VT100 port if it exists
|
||||
self.console_port = self.readDom('console/port')
|
||||
if transaction is None:
|
||||
self.console_port = self.readDom('console/port')
|
||||
else:
|
||||
self.console_port = self.readDomTxn(transaction, 'console/port')
|
||||
if self.console_port is not None:
|
||||
serial_consoles = self.info.console_get_all('vt100')
|
||||
if not serial_consoles:
|
||||
@@ -837,7 +840,10 @@ class XendDomainInfo:
|
||||
|
||||
|
||||
# Update VNC port if it exists and write to xenstore
|
||||
vnc_port = self.readDom('console/vnc-port')
|
||||
if transaction is None:
|
||||
vnc_port = self.readDom('console/vnc-port')
|
||||
else:
|
||||
vnc_port = self.readDomTxn(transaction, 'console/vnc-port')
|
||||
if vnc_port is not None:
|
||||
for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
|
||||
if dev_type == 'vfb':
|
||||
@@ -872,6 +878,27 @@ class XendDomainInfo:
|
||||
def storeVm(self, *args):
|
||||
return xstransact.Store(self.vmpath, *args)
|
||||
|
||||
|
||||
def _readVmTxn(self, transaction, *args):
|
||||
paths = map(lambda x: self.vmpath + "/" + x, args)
|
||||
return transaction.read(*paths)
|
||||
|
||||
def _writeVmTxn(self, transaction, *args):
|
||||
paths = map(lambda x: self.vmpath + "/" + x, args)
|
||||
return transaction.write(*paths)
|
||||
|
||||
def _removeVmTxn(self, transaction, *args):
|
||||
paths = map(lambda x: self.vmpath + "/" + x, args)
|
||||
return transaction.remove(*paths)
|
||||
|
||||
def _gatherVmTxn(self, transaction, *args):
|
||||
paths = map(lambda x: self.vmpath + "/" + x, args)
|
||||
return transaction.gather(paths)
|
||||
|
||||
def storeVmTxn(self, transaction, *args):
|
||||
paths = map(lambda x: self.vmpath + "/" + x, args)
|
||||
return transaction.store(*paths)
|
||||
|
||||
#
|
||||
# Function to update xenstore /dom/*
|
||||
#
|
||||
@@ -891,6 +918,28 @@ class XendDomainInfo:
|
||||
def storeDom(self, *args):
|
||||
return xstransact.Store(self.dompath, *args)
|
||||
|
||||
|
||||
def readDomTxn(self, transaction, *args):
|
||||
paths = map(lambda x: self.vmpath + "/" + x, args)
|
||||
return transaction.read(*paths)
|
||||
|
||||
def gatherDomTxn(self, transaction, *args):
|
||||
paths = map(lambda x: self.vmpath + "/" + x, args)
|
||||
return transaction.gather(*paths)
|
||||
|
||||
def _writeDomTxn(self, transaction, *args):
|
||||
paths = map(lambda x: self.vmpath + "/" + x, args)
|
||||
return transaction.write(*paths)
|
||||
|
||||
def _removeDomTxn(self, transaction, *args):
|
||||
paths = map(lambda x: self.vmpath + "/" + x, args)
|
||||
return transaction.remove(*paths)
|
||||
|
||||
def storeDomTxn(self, transaction, *args):
|
||||
paths = map(lambda x: self.vmpath + "/" + x, args)
|
||||
return transaction.store(*paths)
|
||||
|
||||
|
||||
def _recreateDom(self):
|
||||
complete(self.dompath, lambda t: self._recreateDomFunc(t))
|
||||
|
||||
@@ -2223,7 +2272,7 @@ class XendDomainInfo:
|
||||
(" as domain %s" % str(dom.domid)) or ""))
|
||||
|
||||
|
||||
def update(self, info = None, refresh = True):
|
||||
def update(self, info = None, refresh = True, transaction = None):
|
||||
"""Update with info from xc.domain_getinfo().
|
||||
"""
|
||||
log.trace("XendDomainInfo.update(%s) on domain %s", info,
|
||||
@@ -2246,7 +2295,7 @@ class XendDomainInfo:
|
||||
# TODO: we should eventually get rid of old_dom_states
|
||||
|
||||
self.info.update_config(info)
|
||||
self._update_consoles()
|
||||
self._update_consoles(transaction)
|
||||
|
||||
if refresh:
|
||||
self.refreshShutdown(info)
|
||||
|
||||
@@ -19,9 +19,12 @@ class ConsoleController(DevController):
|
||||
return (self.allocateDeviceID(), back, {})
|
||||
|
||||
|
||||
def getDeviceConfiguration(self, devid):
|
||||
result = DevController.getDeviceConfiguration(self, devid)
|
||||
devinfo = self.readBackend(devid, *self.valid_cfg)
|
||||
def getDeviceConfiguration(self, devid, transaction = None):
|
||||
result = DevController.getDeviceConfiguration(self, devid, transaction)
|
||||
if transaction is None:
|
||||
devinfo = self.readBackend(devid, *self.valid_cfg)
|
||||
else:
|
||||
devinfo = self.readBackendTxn(transaction, devid, *self.valid_cfg)
|
||||
config = dict(zip(self.valid_cfg, devinfo))
|
||||
config = dict([(key, val) for key, val in config.items()
|
||||
if val != None])
|
||||
|
||||
@@ -239,15 +239,15 @@ class DevController:
|
||||
|
||||
self.vm._removeVm("device/%s/%d" % (self.deviceClass, dev))
|
||||
|
||||
def configurations(self):
|
||||
return map(self.configuration, self.deviceIDs())
|
||||
def configurations(self, transaction = None):
|
||||
return map(lambda x: self.configuration(x, transaction), self.deviceIDs(transaction))
|
||||
|
||||
|
||||
def configuration(self, devid):
|
||||
def configuration(self, devid, transaction = None):
|
||||
"""@return an s-expression giving the current configuration of the
|
||||
specified device. This would be suitable for giving to {@link
|
||||
#createDevice} in order to recreate that device."""
|
||||
configDict = self.getDeviceConfiguration(devid)
|
||||
configDict = self.getDeviceConfiguration(devid, transaction)
|
||||
sxpr = [self.deviceClass]
|
||||
for key, val in configDict.items():
|
||||
if isinstance(val, (types.ListType, types.TupleType)):
|
||||
@@ -273,13 +273,16 @@ class DevController:
|
||||
'id', devid]]
|
||||
|
||||
|
||||
def getDeviceConfiguration(self, devid):
|
||||
def getDeviceConfiguration(self, devid, transaction = None):
|
||||
"""Returns the configuration of a device.
|
||||
|
||||
@note: Similar to L{configuration} except it returns a dict.
|
||||
@return: dict
|
||||
"""
|
||||
backdomid = xstransact.Read(self.frontendPath(devid), "backend-id")
|
||||
if transaction is None:
|
||||
backdomid = xstransact.Read(self.frontendPath(devid), "backend-id")
|
||||
else:
|
||||
backdomid = transaction.read(self.frontendPath(devid) + "/backend-id")
|
||||
if backdomid is None:
|
||||
raise VmError("Device %s not connected" % devid)
|
||||
|
||||
@@ -416,14 +419,28 @@ class DevController:
|
||||
else:
|
||||
raise VmError("Device %s not connected" % devid)
|
||||
|
||||
def readBackendTxn(self, transaction, devid, *args):
|
||||
frontpath = self.frontendPath(devid)
|
||||
backpath = transaction.read(frontpath + "/backend")
|
||||
if backpath:
|
||||
paths = map(lambda x: backpath + "/" + x, args)
|
||||
return transaction.read(*paths)
|
||||
else:
|
||||
raise VmError("Device %s not connected" % devid)
|
||||
|
||||
def readFrontend(self, devid, *args):
|
||||
return xstransact.Read(self.frontendPath(devid), *args)
|
||||
|
||||
def readFrontendTxn(self, transaction, devid, *args):
|
||||
paths = map(lambda x: self.frontendPath(devid) + "/" + x, args)
|
||||
return transaction.read(*paths)
|
||||
|
||||
def deviceIDs(self, transaction = None):
|
||||
"""@return The IDs of each of the devices currently configured for
|
||||
this instance's deviceClass.
|
||||
"""
|
||||
fe = self.backendRoot()
|
||||
|
||||
if transaction:
|
||||
return map(lambda x: int(x.split('/')[-1]), transaction.list(fe))
|
||||
else:
|
||||
|
||||
@@ -124,19 +124,26 @@ class BlkifController(DevController):
|
||||
(self.deviceClass, devid, config))
|
||||
|
||||
|
||||
def getDeviceConfiguration(self, devid):
|
||||
def getDeviceConfiguration(self, devid, transaction = None):
|
||||
"""Returns the configuration of a device.
|
||||
|
||||
@note: Similar to L{configuration} except it returns a dict.
|
||||
@return: dict
|
||||
"""
|
||||
config = DevController.getDeviceConfiguration(self, devid)
|
||||
devinfo = self.readBackend(devid, 'dev', 'type', 'params', 'mode',
|
||||
'uuid')
|
||||
config = DevController.getDeviceConfiguration(self, devid, transaction)
|
||||
if transaction is None:
|
||||
devinfo = self.readBackend(devid, 'dev', 'type', 'params', 'mode',
|
||||
'uuid')
|
||||
else:
|
||||
devinfo = self.readBackendTxn(transaction, devid,
|
||||
'dev', 'type', 'params', 'mode', 'uuid')
|
||||
dev, typ, params, mode, uuid = devinfo
|
||||
|
||||
if dev:
|
||||
dev_type = self.readFrontend(devid, 'device-type')
|
||||
if transaction is None:
|
||||
dev_type = self.readFrontend(devid, 'device-type')
|
||||
else:
|
||||
dev_type = self.readFrontendTxn(transaction, devid, 'device-type')
|
||||
if dev_type:
|
||||
dev += ':' + dev_type
|
||||
config['dev'] = dev
|
||||
|
||||
@@ -183,17 +183,20 @@ class NetifController(DevController):
|
||||
"network device")
|
||||
|
||||
|
||||
def getDeviceConfiguration(self, devid):
|
||||
def getDeviceConfiguration(self, devid, transaction = None):
|
||||
"""@see DevController.configuration"""
|
||||
|
||||
result = DevController.getDeviceConfiguration(self, devid)
|
||||
result = DevController.getDeviceConfiguration(self, devid, transaction)
|
||||
|
||||
config_path = "device/%s/%d/" % (self.deviceClass, devid)
|
||||
devinfo = ()
|
||||
for x in ( 'script', 'ip', 'bridge', 'mac',
|
||||
'type', 'vifname', 'rate', 'uuid', 'model', 'accel',
|
||||
'security_label'):
|
||||
y = self.vm._readVm(config_path + x)
|
||||
if transaction is None:
|
||||
y = self.vm._readVm(config_path + x)
|
||||
else:
|
||||
y = self.vm._readVmTxn(transaction, config_path + x)
|
||||
devinfo += (y,)
|
||||
(script, ip, bridge, mac, typ, vifname, rate, uuid,
|
||||
model, accel, security_label) = devinfo
|
||||
|
||||
@@ -78,8 +78,8 @@ class PciController(DevController):
|
||||
back['uuid'] = config.get('uuid','')
|
||||
return (0, back, {})
|
||||
|
||||
def getDeviceConfiguration(self, devid):
|
||||
result = DevController.getDeviceConfiguration(self, devid)
|
||||
def getDeviceConfiguration(self, devid, transaction = None):
|
||||
result = DevController.getDeviceConfiguration(self, devid, transaction)
|
||||
num_devs = self.readBackend(devid, 'num_devs')
|
||||
pci_devs = []
|
||||
|
||||
|
||||
@@ -75,9 +75,9 @@ class TPMifController(DevController):
|
||||
|
||||
return (devid, back, front)
|
||||
|
||||
def getDeviceConfiguration(self, devid):
|
||||
def getDeviceConfiguration(self, devid, transaction = None):
|
||||
"""Returns the configuration of a device"""
|
||||
result = DevController.getDeviceConfiguration(self, devid)
|
||||
result = DevController.getDeviceConfiguration(self, devid, transaction)
|
||||
|
||||
(instance, uuid, type) = \
|
||||
self.readBackend(devid, 'instance',
|
||||
|
||||
@@ -27,10 +27,13 @@ class VfbifController(DevController):
|
||||
return (devid, back, {})
|
||||
|
||||
|
||||
def getDeviceConfiguration(self, devid):
|
||||
result = DevController.getDeviceConfiguration(self, devid)
|
||||
def getDeviceConfiguration(self, devid, transaction = None):
|
||||
result = DevController.getDeviceConfiguration(self, devid, transaction)
|
||||
|
||||
devinfo = self.readBackend(devid, *CONFIG_ENTRIES)
|
||||
if transaction is None:
|
||||
devinfo = self.readBackend(devid, *CONFIG_ENTRIES)
|
||||
else:
|
||||
devinfo = self.readBackendTxn(transaction, devid, *CONFIG_ENTRIES)
|
||||
return dict([(CONFIG_ENTRIES[i], devinfo[i])
|
||||
for i in range(len(CONFIG_ENTRIES))
|
||||
if devinfo[i] is not None])
|
||||
|
||||