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:
Keir Fraser committed 2007-10-30 09:19:43 +00:00
1 parent e33d4a7316
commit 27d2e52b7c
10 files changed
+155 -56

No files matched your search

+34 -26
View File
@@ -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
+10 -1
View File
@@ -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)
+54 -5
View File
@@ -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])
+23 -6
View File
@@ -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:
+12 -5
View File
@@ -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
+6 -3
View File
@@ -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
+2 -2
View File
@@ -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 = []
+2 -2
View File
@@ -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',
+6 -3
View File
@@ -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])
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.