ia64/xen-unstable

changeset 19333:e2de0e760a0d

Fix qemu spawn for Solaris

On Solaris, xend runs in a 'process contract' such that all children
are killed when the service is restarted. Spawn qemu processes in a
new contract to avoid this.

Signed-off-by: John Levon <john.levon@sun.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Mar 12 11:07:00 2009 +0000 (2009-03-12)
parents 99771e02b38a
children 1ad8216abda5
files tools/python/setup.py tools/python/xen/lowlevel/process/process.c tools/python/xen/xend/image.py tools/python/xen/xend/osdep.py
line diff
     1.1 --- a/tools/python/setup.py	Thu Mar 12 11:01:25 2009 +0000
     1.2 +++ b/tools/python/setup.py	Thu Mar 12 11:07:00 2009 +0000
     1.3 @@ -38,6 +38,13 @@ scf = Extension("scf",
     1.4                 libraries          = libraries,
     1.5                 sources            = [ "xen/lowlevel/scf/scf.c" ])
     1.6               
     1.7 +process = Extension("process",
     1.8 +               extra_compile_args = extra_compile_args,
     1.9 +               include_dirs       = include_dirs + [ "xen/lowlevel/process" ],
    1.10 +               library_dirs       = library_dirs,
    1.11 +               libraries          = libraries + [ "contract" ],
    1.12 +               sources            = [ "xen/lowlevel/process/process.c" ])
    1.13 +
    1.14  acm = Extension("acm",
    1.15                 extra_compile_args = extra_compile_args,
    1.16                 include_dirs       = include_dirs + [ "xen/lowlevel/acm" ],
    1.17 @@ -63,6 +70,7 @@ ptsname = Extension("ptsname",
    1.18  modules = [ xc, xs, ptsname, acm, flask ]
    1.19  if os.uname()[0] == 'SunOS':
    1.20      modules.append(scf)
    1.21 +    modules.append(process)
    1.22  
    1.23  setup(name            = 'xen',
    1.24        version         = '3.0',
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/tools/python/xen/lowlevel/process/process.c	Thu Mar 12 11:07:00 2009 +0000
     2.3 @@ -0,0 +1,164 @@
     2.4 +/*
     2.5 + * Permission is hereby granted, free of charge, to any person obtaining a copy
     2.6 + * of this software and associated documentation files (the "Software"), to
     2.7 + * deal in the Software without restriction, including without limitation the
     2.8 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
     2.9 + * sell copies of the Software, and to permit persons to whom the Software is
    2.10 + * furnished to do so, subject to the following conditions:
    2.11 + *
    2.12 + * The above copyright notice and this permission notice shall be included in
    2.13 + * all copies or substantial portions of the Software.
    2.14 + *
    2.15 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    2.16 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    2.17 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    2.18 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    2.19 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    2.20 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    2.21 + * DEALINGS IN THE SOFTWARE.
    2.22 + *
    2.23 + * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
    2.24 + * Use is subject to license terms.
    2.25 + */
    2.26 +
    2.27 +#include <Python.h>
    2.28 +
    2.29 +#include <libcontract.h>
    2.30 +#include <sys/contract/process.h>
    2.31 +#include <fcntl.h>
    2.32 +#include <stdio.h>
    2.33 +
    2.34 +/*
    2.35 + * On Solaris, xend runs under a contract as an smf(5) service.  As a
    2.36 + * result, when spawning long-running children such as a domain's
    2.37 + * qemu-dm instantiation, we have to make sure it's in a separate
    2.38 + * contract. Before we fork, we must activate a separate process
    2.39 + * contract template to place the child processes in a new contract.
    2.40 + */
    2.41 +
    2.42 +static PyObject *
    2.43 +pyprocess_activate(PyObject *o, PyObject *args, PyObject *kwargs)
    2.44 +{
    2.45 +	static char *kwlist[] = { "name", NULL };
    2.46 +	char *name = NULL;
    2.47 +	int flags;
    2.48 +	int cfd;
    2.49 +
    2.50 +	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", kwlist, &name))
    2.51 +		return (NULL);
    2.52 +
    2.53 +	cfd = open64("/system/contract/process/template", O_RDWR);
    2.54 +
    2.55 +	if (cfd == -1)
    2.56 +		goto err;
    2.57 +
    2.58 +	if ((flags = fcntl(cfd, F_GETFD, 0)) == -1)
    2.59 +		goto err;
    2.60 +	
    2.61 +	if (fcntl(cfd, F_SETFD, flags | FD_CLOEXEC) == -1)
    2.62 +		goto err;
    2.63 +
    2.64 +	if (name != NULL)
    2.65 +		ct_pr_tmpl_set_svc_aux(cfd, name);
    2.66 +
    2.67 +	if (ct_tmpl_activate(cfd))
    2.68 +		goto err;
    2.69 +
    2.70 +	return (PyInt_FromLong((long)cfd));
    2.71 +
    2.72 +err:
    2.73 +	if (cfd != -1)
    2.74 +		close(cfd);
    2.75 +	PyErr_SetFromErrno(PyExc_OSError);
    2.76 +	return (NULL);
    2.77 +}
    2.78 +
    2.79 +static PyObject *
    2.80 +pyprocess_clear(PyObject *o, PyObject *args, PyObject *kwargs)
    2.81 +{
    2.82 +	static char *kwlist[] = { "contract", NULL };
    2.83 +	int cfd;
    2.84 +
    2.85 +	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &cfd))
    2.86 +		return (NULL);
    2.87 +
    2.88 +	if (ct_tmpl_clear(cfd) != 0) {
    2.89 +		PyErr_SetFromErrno(PyExc_OSError);
    2.90 +		return (NULL);
    2.91 +	}
    2.92 +
    2.93 +	close(cfd);
    2.94 +
    2.95 +	Py_INCREF(Py_None);
    2.96 +	return (Py_None);
    2.97 +}
    2.98 +
    2.99 +static PyObject *
   2.100 +pyprocess_abandon_latest(PyObject *o, PyObject *args, PyObject *kwargs)
   2.101 +{
   2.102 +	static char *kwlist[] = { NULL };
   2.103 +	static char path[PATH_MAX];
   2.104 +	ct_stathdl_t st;
   2.105 +	ctid_t latest;
   2.106 +	int cfd;
   2.107 +
   2.108 +	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
   2.109 +		return (NULL);
   2.110 +
   2.111 +	cfd = open64("/system/contract/process/latest", O_RDONLY);
   2.112 +	if (cfd == -1)
   2.113 +		goto err;
   2.114 +
   2.115 +	ct_status_read(cfd, CTD_COMMON, &st);
   2.116 +	latest = ct_status_get_id(st);
   2.117 +	ct_status_free(st);
   2.118 +	close(cfd);
   2.119 +
   2.120 +	snprintf(path, PATH_MAX, "/system/contract/process/%ld/ctl",
   2.121 +	    (long)latest);
   2.122 +
   2.123 +	if ((cfd = open64(path, O_WRONLY)) < 0) 
   2.124 +		goto err;
   2.125 +	if (ct_ctl_abandon(cfd))
   2.126 +		goto err;
   2.127 +	close(cfd);
   2.128 +
   2.129 +	Py_INCREF(Py_None);
   2.130 +	return (Py_None);
   2.131 +err:
   2.132 +	PyErr_SetFromErrno(PyExc_OSError);
   2.133 +	return (NULL);
   2.134 +}
   2.135 +
   2.136 +PyDoc_STRVAR(pyprocess_activate__doc__,
   2.137 +    "activate(name)\n"
   2.138 +    "\n"
   2.139 +    "Activate a new process contract template. If name is given,\n"
   2.140 +    "it is used as the template's auxiliary value.\n"
   2.141 +    "Returns the new contract template.\n");
   2.142 + 
   2.143 +PyDoc_STRVAR(pyprocess_clear__doc__,
   2.144 +    "clear(contract)\n"
   2.145 +    "\n"
   2.146 +    "Clear and close the given contract template.\n");
   2.147 +
   2.148 +PyDoc_STRVAR(pyprocess_abandon_latest__doc__,
   2.149 +    "abandon_latest()\n"
   2.150 +    "\n"
   2.151 +    "Abandon the latest contract created by this thread.\n");
   2.152 +
   2.153 +static struct PyMethodDef pyprocess_module_methods[] = {
   2.154 +    { "activate", (PyCFunction) pyprocess_activate,
   2.155 +      METH_VARARGS|METH_KEYWORDS, pyprocess_activate__doc__ },
   2.156 +    { "clear", (PyCFunction) pyprocess_clear,
   2.157 +      METH_VARARGS|METH_KEYWORDS, pyprocess_clear__doc__ },
   2.158 +    { "abandon_latest", (PyCFunction) pyprocess_abandon_latest,
   2.159 +      METH_VARARGS|METH_KEYWORDS, pyprocess_abandon_latest__doc__ },
   2.160 +    { NULL, NULL, 0, NULL }	
   2.161 +};
   2.162 +
   2.163 +PyMODINIT_FUNC
   2.164 +initprocess(void)
   2.165 +{
   2.166 +	Py_InitModule("process", pyprocess_module_methods);
   2.167 +}
     3.1 --- a/tools/python/xen/xend/image.py	Thu Mar 12 11:01:25 2009 +0000
     3.2 +++ b/tools/python/xen/xend/image.py	Thu Mar 12 11:07:00 2009 +0000
     3.3 @@ -40,6 +40,7 @@ from xen.xend import arch
     3.4  from xen.xend import XendOptions
     3.5  from xen.util import oshelp
     3.6  from xen.util import utils
     3.7 +from xen.xend import osdep
     3.8  
     3.9  xc = xen.lowlevel.xc.xc()
    3.10  
    3.11 @@ -407,9 +408,12 @@ class ImageHandler:
    3.12          logfd = os.open(self.logfile, logfile_mode)
    3.13          
    3.14          sys.stderr.flush()
    3.15 +        contract = osdep.prefork("%s:%d" %
    3.16 +                                 (self.vm.getName(), self.vm.getDomid()))
    3.17          pid = os.fork()
    3.18          if pid == 0: #child
    3.19              try:
    3.20 +                osdep.postfork(contract)
    3.21                  os.dup2(null, 0)
    3.22                  os.dup2(logfd, 1)
    3.23                  os.dup2(logfd, 2)
    3.24 @@ -426,6 +430,7 @@ class ImageHandler:
    3.25              except:
    3.26                  os._exit(127)
    3.27          else:
    3.28 +            osdep.postfork(contract, abandon=True)
    3.29              self.pid = pid
    3.30              os.close(null)
    3.31              os.close(logfd)
     4.1 --- a/tools/python/xen/xend/osdep.py	Thu Mar 12 11:01:25 2009 +0000
     4.2 +++ b/tools/python/xen/xend/osdep.py	Thu Mar 12 11:07:00 2009 +0000
     4.3 @@ -192,6 +192,32 @@ def _solaris_get_cpuinfo():
     4.4      "SunOS": _solaris_get_cpuinfo
     4.5  }
     4.6  
     4.7 +def _default_prefork(name):
     4.8 +    pass
     4.9 +
    4.10 +def _default_postfork(ct, abandon=False):
    4.11 +    pass
    4.12 +
    4.13 +# call this for long-running processes that should survive a xend
    4.14 +# restart
    4.15 +def _solaris_prefork(name):
    4.16 +    from xen.lowlevel import process
    4.17 +    return process.activate(name)
    4.18 +
    4.19 +def _solaris_postfork(ct, abandon=False):
    4.20 +    from xen.lowlevel import process
    4.21 +    process.clear(ct)
    4.22 +    if abandon:
    4.23 +        process.abandon_latest()
    4.24 +
    4.25 +_get_prefork = {
    4.26 +    "SunOS": _solaris_prefork
    4.27 +}
    4.28 +
    4.29 +_get_postfork = {
    4.30 +    "SunOS": _solaris_postfork
    4.31 +}
    4.32 +
    4.33  def _get(var, default=None):
    4.34      return var.get(os.uname()[0], default)
    4.35  
    4.36 @@ -201,3 +227,5 @@ pygrub_path = _get(_pygrub_path, "/usr/b
    4.37  vif_script = _get(_vif_script, "vif-bridge")
    4.38  lookup_balloon_stat = _get(_balloon_stat, _linux_balloon_stat)
    4.39  get_cpuinfo = _get(_get_cpuinfo, _linux_get_cpuinfo)
    4.40 +prefork = _get(_get_prefork, _default_prefork)
    4.41 +postfork = _get(_get_postfork, _default_postfork)