mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
[HVM] save restore: frame work
Signed-off-by: Zhai Edwin <edwin.zhai@intel.com> frame work for HVM save restore in Control Panel
This commit is contained in:
1 parent
ccace2b704
commit
4a13cf1b70
10 files changed
+260
-17
No files matched your search
@@ -160,6 +160,20 @@ static PyObject *pyxc_domain_destroy(XcObject *self, PyObject *args)
|
||||
return dom_op(self, args, xc_domain_destroy);
|
||||
}
|
||||
|
||||
static PyObject *pyxc_domain_shutdown(XcObject *self, PyObject *args)
|
||||
{
|
||||
uint32_t dom, reason;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ii", &dom, &reason))
|
||||
return NULL;
|
||||
|
||||
if (xc_domain_shutdown(self->xc_handle, dom, reason) != 0)
|
||||
return pyxc_error_to_exception();
|
||||
|
||||
Py_INCREF(zero);
|
||||
return zero;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *pyxc_vcpu_setaffinity(XcObject *self,
|
||||
PyObject *args,
|
||||
@@ -1029,6 +1043,14 @@ static PyMethodDef pyxc_methods[] = {
|
||||
" dom [int]: Identifier of domain to be destroyed.\n\n"
|
||||
"Returns: [int] 0 on success; -1 on error.\n" },
|
||||
|
||||
{ "domain_shutdown",
|
||||
(PyCFunction)pyxc_domain_shutdown,
|
||||
METH_VARARGS, "\n"
|
||||
"Shutdown a domain.\n"
|
||||
" dom [int, 0]: Domain identifier to use.\n"
|
||||
" reason [int, 0]: Reason for shutdown.\n"
|
||||
"Returns: [int] 0 on success; -1 on error.\n" },
|
||||
|
||||
{ "vcpu_setaffinity",
|
||||
(PyCFunction)pyxc_vcpu_setaffinity,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
|
||||
@@ -22,11 +22,14 @@ from xen.xend.XendConfig import XendConfig
|
||||
from xen.xend.XendConstants import *
|
||||
|
||||
SIGNATURE = "LinuxGuestRecord"
|
||||
QEMU_SIGNATURE = "QemuDeviceModelRecord"
|
||||
dm_batch = 512
|
||||
XC_SAVE = "xc_save"
|
||||
XC_RESTORE = "xc_restore"
|
||||
|
||||
|
||||
sizeof_int = calcsize("i")
|
||||
sizeof_unsigned_int = calcsize("I")
|
||||
sizeof_unsigned_long = calcsize("L")
|
||||
|
||||
|
||||
@@ -69,6 +72,11 @@ def save(fd, dominfo, network, live, dst):
|
||||
"could not write guest state file: config len")
|
||||
write_exact(fd, config, "could not write guest state file: config")
|
||||
|
||||
image_cfg = dominfo.info.get('image', {})
|
||||
hvm = image_cfg.has_key('hvm')
|
||||
|
||||
if hvm:
|
||||
log.info("save hvm domain")
|
||||
# xc_save takes three customization parameters: maxit, max_f, and
|
||||
# flags the last controls whether or not save is 'live', while the
|
||||
# first two further customize behaviour when 'live' save is
|
||||
@@ -76,7 +84,7 @@ def save(fd, dominfo, network, live, dst):
|
||||
# libxenguest; see the comments and/or code in xc_linux_save() for
|
||||
# more information.
|
||||
cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd),
|
||||
str(dominfo.getDomid()), "0", "0", str(int(live)) ]
|
||||
str(dominfo.getDomid()), "0", "0", str(int(live) | (int(hvm) << 2)) ]
|
||||
log.debug("[xc_save]: %s", string.join(cmd))
|
||||
|
||||
def saveInputHandler(line, tochild):
|
||||
@@ -90,12 +98,29 @@ def save(fd, dominfo, network, live, dst):
|
||||
log.info("Domain %d suspended.", dominfo.getDomid())
|
||||
dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP3,
|
||||
domain_name)
|
||||
#send signal to device model for save
|
||||
if hvm:
|
||||
log.info("release_devices for hvm domain")
|
||||
dominfo._releaseDevices(True)
|
||||
tochild.write("done\n")
|
||||
tochild.flush()
|
||||
log.debug('Written done')
|
||||
|
||||
forkHelper(cmd, fd, saveInputHandler, False)
|
||||
|
||||
# put qemu device model state
|
||||
if hvm:
|
||||
write_exact(fd, QEMU_SIGNATURE, "could not write qemu signature")
|
||||
qemu_fd = os.open("/tmp/xen.qemu-dm.%d" % dominfo.getDomid(), os.O_RDONLY)
|
||||
while True:
|
||||
buf = os.read(qemu_fd, dm_batch)
|
||||
if len(buf):
|
||||
write_exact(fd, buf, "could not write device model state")
|
||||
else:
|
||||
break
|
||||
os.close(qemu_fd)
|
||||
os.remove("/tmp/xen.qemu-dm.%d" % dominfo.getDomid())
|
||||
|
||||
dominfo.destroyDomain()
|
||||
try:
|
||||
dominfo.setName(domain_name)
|
||||
@@ -149,19 +174,49 @@ def restore(xd, fd, dominfo = None, paused = False):
|
||||
|
||||
nr_pfns = (dominfo.getMemoryTarget() + 3) / 4
|
||||
|
||||
# if hvm, pass mem size to calculate the store_mfn
|
||||
hvm = 0
|
||||
apic = 0
|
||||
pae = 0
|
||||
image_cfg = dominfo.info.get('image', {})
|
||||
hvm = image_cfg.has_key('hvm')
|
||||
if hvm:
|
||||
# the 'memory' in config has been removed
|
||||
hvm = dominfo.info['memory_static_min']
|
||||
apic = dominfo.info['image']['hvm'].get('apic', 0)
|
||||
pae = dominfo.info['image']['hvm'].get('pae', 0)
|
||||
log.info("restore hvm domain %d, mem=%d, apic=%d, pae=%d",
|
||||
dominfo.domid, hvm, apic, pae)
|
||||
|
||||
try:
|
||||
l = read_exact(fd, sizeof_unsigned_long,
|
||||
"not a valid guest state file: pfn count read")
|
||||
if hvm:
|
||||
l = read_exact(fd, sizeof_unsigned_int,
|
||||
"not a valid hvm guest state file: pfn count read")
|
||||
nr_pfns = unpack("I", l)[0] # native sizeof int
|
||||
else:
|
||||
l = read_exact(fd, sizeof_unsigned_long,
|
||||
"not a valid guest state file: pfn count read")
|
||||
|
||||
max_pfn = unpack("L", l)[0] # native sizeof long
|
||||
if max_pfn > 16*1024*1024: # XXX
|
||||
raise XendError(
|
||||
"not a valid guest state file: pfn count out of range")
|
||||
|
||||
balloon.free(xc.pages_to_kib(nr_pfns))
|
||||
shadow = dominfo.info['shadow_memory']
|
||||
log.debug("restore:shadow=0x%x, _static_max=0x%x, _static_min=0x%x, "
|
||||
"nr_pfns=0x%x.", dominfo.info['shadow_memory'],
|
||||
dominfo.info['memory_static_max'],
|
||||
dominfo.info['memory_static_min'], nr_pfns)
|
||||
|
||||
|
||||
balloon.free(xc.pages_to_kib(nr_pfns) + shadow * 1024)
|
||||
|
||||
shadow_cur = xc.shadow_mem_control(dominfo.getDomid(), shadow)
|
||||
dominfo.info['shadow_memory'] = shadow_cur
|
||||
|
||||
cmd = map(str, [xen.util.auxbin.pathTo(XC_RESTORE),
|
||||
fd, dominfo.getDomid(), max_pfn,
|
||||
store_port, console_port])
|
||||
store_port, console_port, hvm, pae, apic])
|
||||
log.debug("[xc_restore]: %s", string.join(cmd))
|
||||
|
||||
handler = RestoreInputHandler()
|
||||
@@ -171,10 +226,30 @@ def restore(xd, fd, dominfo = None, paused = False):
|
||||
if handler.store_mfn is None or handler.console_mfn is None:
|
||||
raise XendError('Could not read store/console MFN')
|
||||
|
||||
os.read(fd, 1) # Wait for source to close connection
|
||||
dominfo.waitForDevices() # Wait for backends to set up
|
||||
if not paused:
|
||||
dominfo.unpause()
|
||||
|
||||
# get qemu state and create a tmp file for dm restore
|
||||
if hvm:
|
||||
qemu_signature = read_exact(fd, len(QEMU_SIGNATURE),
|
||||
"invalid device model signature read")
|
||||
if qemu_signature != QEMU_SIGNATURE:
|
||||
raise XendError("not a valid device model state: found '%s'" %
|
||||
qemu_signature)
|
||||
qemu_fd = os.open("/tmp/xen.qemu-dm.%d" % dominfo.getDomid(),
|
||||
os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
|
||||
while True:
|
||||
buf = os.read(fd, dm_batch)
|
||||
if len(buf):
|
||||
write_exact(qemu_fd, buf,
|
||||
"could not write dm state to tmp file")
|
||||
else:
|
||||
break
|
||||
os.close(qemu_fd)
|
||||
|
||||
|
||||
os.read(fd, 1) # Wait for source to close connection
|
||||
|
||||
dominfo.completeRestore(handler.store_mfn, handler.console_mfn)
|
||||
|
||||
|
||||
@@ -451,6 +451,16 @@ class XendDomainInfo:
|
||||
self._removeVm('xend/previous_restart_time')
|
||||
self.storeDom("control/shutdown", reason)
|
||||
|
||||
## shutdown hypercall for hvm domain desides xenstore write
|
||||
image_cfg = self.info.get('image', {})
|
||||
hvm = image_cfg.has_key('hvm')
|
||||
if hvm:
|
||||
for code in DOMAIN_SHUTDOWN_REASONS.keys():
|
||||
if DOMAIN_SHUTDOWN_REASONS[code] == reason:
|
||||
break
|
||||
xc.domain_shutdown(self.domid, code)
|
||||
|
||||
|
||||
def pause(self):
|
||||
"""Pause domain
|
||||
|
||||
@@ -1228,8 +1238,11 @@ class XendDomainInfo:
|
||||
if self.image:
|
||||
self.image.createDeviceModel()
|
||||
|
||||
def _releaseDevices(self):
|
||||
def _releaseDevices(self, suspend = False):
|
||||
"""Release all domain's devices. Nothrow guarantee."""
|
||||
if suspend and self.image:
|
||||
self.image.destroy(suspend)
|
||||
return
|
||||
|
||||
while True:
|
||||
t = xstransact("%s/device" % self.dompath)
|
||||
@@ -1395,6 +1408,7 @@ class XendDomainInfo:
|
||||
self.info['shadow_memory'] * 1024,
|
||||
self.info['memory_static_max'] * 1024)
|
||||
|
||||
log.debug("_initDomain:shadow_memory=0x%x, memory_static_max=0x%x, memory_static_min=0x%x.", self.info['shadow_memory'], self.info['memory_static_max'], self.info['memory_static_min'],)
|
||||
# Round shadow up to a multiple of a MiB, as shadow_mem_control
|
||||
# takes MiB and we must not round down and end up under-providing.
|
||||
shadow = ((shadow + 1023) / 1024) * 1024
|
||||
@@ -1494,6 +1508,16 @@ class XendDomainInfo:
|
||||
self.console_mfn = console_mfn
|
||||
|
||||
self._introduceDomain()
|
||||
image_cfg = self.info.get('image', {})
|
||||
hvm = image_cfg.has_key('hvm')
|
||||
if hvm:
|
||||
self.image = image.create(self,
|
||||
self.info,
|
||||
self.info['image'],
|
||||
self.info['devices'])
|
||||
if self.image:
|
||||
self.image.createDeviceModel(True)
|
||||
self.image.register_shutdown_watch()
|
||||
self._storeDomDetails()
|
||||
self._registerWatches()
|
||||
self.refreshShutdown()
|
||||
|
||||
@@ -173,7 +173,7 @@ class ImageHandler:
|
||||
"""Build the domain. Define in subclass."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def createDeviceModel(self):
|
||||
def createDeviceModel(self, restore = False):
|
||||
"""Create device model for the domain (define in subclass if needed)."""
|
||||
pass
|
||||
|
||||
@@ -478,7 +478,7 @@ class HVMImageHandler(ImageHandler):
|
||||
|
||||
return ret
|
||||
|
||||
def createDeviceModel(self):
|
||||
def createDeviceModel(self, restore = False):
|
||||
if self.pid:
|
||||
return
|
||||
# Execute device model.
|
||||
@@ -487,6 +487,8 @@ class HVMImageHandler(ImageHandler):
|
||||
args = args + ([ "-d", "%d" % self.vm.getDomid(),
|
||||
"-m", "%s" % (self.getRequiredInitialReservation() / 1024)])
|
||||
args = args + self.dmargs
|
||||
if restore:
|
||||
args = args + ([ "-loadvm", "/tmp/xen.qemu-dm.%d" % self.vm.getDomid() ])
|
||||
env = dict(os.environ)
|
||||
if self.display:
|
||||
env['DISPLAY'] = self.display
|
||||
@@ -505,12 +507,16 @@ class HVMImageHandler(ImageHandler):
|
||||
self.register_reboot_feature_watch()
|
||||
self.pid = self.vm.gatherDom(('image/device-model-pid', int))
|
||||
|
||||
def destroy(self):
|
||||
def destroy(self, suspend = False):
|
||||
self.unregister_shutdown_watch()
|
||||
self.unregister_reboot_feature_watch();
|
||||
if self.pid:
|
||||
try:
|
||||
os.kill(self.pid, signal.SIGKILL)
|
||||
sig = signal.SIGKILL
|
||||
if suspend:
|
||||
log.info("use sigusr1 to signal qemu %d", self.pid)
|
||||
sig = signal.SIGUSR1
|
||||
os.kill(self.pid, sig)
|
||||
except OSError, exn:
|
||||
log.exception(exn)
|
||||
try:
|
||||
|
||||