Quoting Daniel P. Berrange (berrange(a)redhat.com):
The LXC controller 'main' method received the handshake FD
and invokes
lxcControllerRun(). This method does various setup tasks, in particular
the following:
....
if (lxcSetContainerResources(def) < 0)
goto cleanup;
...
...
if (lxcContainerSendContinue(handshakefd) < 0) {
virReportSystemError(errno, "%s",
_("error sending continue signal to parent"));
goto cleanup;
}
VIR_FORCE_CLOSE(handshakefd);
Thanks, Daniel. You're right! This is fixed in git, by the patch 'lxc:
controller: Improve container error reporting' (which does much more
than it says :). The following patch is how I had just fixed 0.9.2 this
morning. It'll be nicer if I can get the git commit cherrypicked. I
can't wait till I can upgrade!
thanks,
-serge
Description: Make lxc driver hold sem until controller is far enough
The lxc driver currently does not wait until the container has set
up its cgroups before dropping the driver mutex.
First, move the controller's accept of the monitor socket wait
until it has set up the cgroups.
Second, because a connect does not actually wait for an accept to
happen, force the driver to wait with a silly two-way read/write
handshake. Since the monitor socket is also used elsewhere, make
this handshake happen everywhere.
Author: Serge Hallyn <serge.hallyn(a)canonical.com>
Bug-Ubuntu:
https://bugs.launchpad.net/ubuntu/+source/nova/+bug/842845
Forwarded: not-needed
Index: libvirt-0.9.2/src/lxc/lxc_controller.c
===================================================================
--- libvirt-0.9.2.orig/src/lxc/lxc_controller.c 2011-10-03 13:10:31.098934902 -0500
+++ libvirt-0.9.2/src/lxc/lxc_controller.c 2011-10-03 13:10:53.823679619 -0500
@@ -432,6 +432,8 @@
numEvents = epoll_wait(epollFd, &epollEvent, 1, timeout);
if (numEvents > 0) {
if (epollEvent.data.fd == monitor) {
+ int ret;
+ char go[4];
int fd = accept(monitor, NULL, 0);
if (fd < 0) {
/* First reflex may be simply to declare accept failure
@@ -457,6 +459,17 @@
_("epoll_ctl(client) failed"));
goto cleanup;
}
+ ret = read(client, go, 3);
+ if (ret < 0) {
+ virReportSystemError(errno, "%s",
+ _("Failed to read 'go' from
driver"));
+ goto cleanup;
+ }
+ ret = write(client, "go", 3);
+ if (ret < 0) {
+ virReportSystemError(errno, "%s", _("Failed to write
'go' to container"));
+ goto cleanup;
+ }
} else if (client != -1 && epollEvent.data.fd == client) {
if (0 > epoll_ctl(epollFd, EPOLL_CTL_DEL, client, &epollEvent)) {
virReportSystemError(errno, "%s",
@@ -611,10 +624,9 @@
unsigned int nveths,
char **veths,
int monitor,
- int client,
int appPty)
{
- int rc = -1;
+ int ret, rc = -1;
int control[2] = { -1, -1};
int containerPty = -1;
char *containerPtyPath = NULL;
@@ -622,6 +634,8 @@
virDomainFSDefPtr root;
char *devpts = NULL;
char *devptmx = NULL;
+ char go[4];
+ int client;
if (socketpair(PF_UNIX, SOCK_STREAM, 0, control) < 0) {
virReportSystemError(errno, "%s",
@@ -631,8 +645,29 @@
root = virDomainGetRootFilesystem(def);
+ VIR_DEBUG("About to set resources and cgroups\n");
if (lxcSetContainerResources(def) < 0)
goto cleanup;
+ VIR_DEBUG("Done setting resources and cgroups\n");
+
+ /* Accept initial client which is the libvirtd daemon */
+ if ((client = accept(monitor, NULL, 0)) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Failed to accept a connection from driver"));
+ goto cleanup;
+ }
+ VIR_DEBUG("Accepted monitor fd from driver\n");
+ ret = read(client, go, 3);
+ if (ret < 0) {
+ virReportSystemError(errno, "%s",
+ _("Failed to read 'go' from driver"));
+ goto cleanup;
+ }
+ ret = write(client, "go", 3);
+ if (ret < 0) {
+ virReportSystemError(errno, "%s", _("Failed to write 'go'
to container"));
+ goto cleanup;
+ }
/*
* If doing a chroot style setup, we need to prepare
@@ -765,7 +800,6 @@
{
pid_t pid;
int rc = 1;
- int client;
char *name = NULL;
int nveths = 0;
char **veths = NULL;
@@ -922,14 +956,7 @@
/* Initialize logging */
virLogSetFromEnv();
- /* Accept initial client which is the libvirtd daemon */
- if ((client = accept(monitor, NULL, 0)) < 0) {
- virReportSystemError(errno, "%s",
- _("Failed to accept a connection from driver"));
- goto cleanup;
- }
-
- rc = lxcControllerRun(def, nveths, veths, monitor, client, appPty);
+ rc = lxcControllerRun(def, nveths, veths, monitor, appPty);
cleanup:
Index: libvirt-0.9.2/src/lxc/lxc_driver.c
===================================================================
--- libvirt-0.9.2.orig/src/lxc/lxc_driver.c 2011-10-03 13:10:27.608663571 -0500
+++ libvirt-0.9.2/src/lxc/lxc_driver.c 2011-10-03 13:10:53.823679619 -0500
@@ -1160,8 +1160,9 @@
virDomainObjPtr vm)
{
char *sockpath = NULL;
- int fd;
+ int fd, r;
struct sockaddr_un addr;
+ char go[4];
if (virAsprintf(&sockpath, "%s/%s.sock",
driver->stateDir, vm->def->name) < 0) {
@@ -1189,6 +1190,17 @@
goto error;
}
+ r = write(fd, "go", 3);
+ if (r < 0) {
+ virReportSystemError(errno, "%s", _("Failed to write 'go'
to container"));
+ goto error;
+ }
+ r = read(fd, go, 3);
+ if (r < 0) {
+ virReportSystemError(errno, "%s", _("Failed to read 'go'
from container"));
+ goto error;
+ }
+
VIR_FREE(sockpath);
return fd;
@@ -1491,6 +1503,7 @@
* pid file out to disk */
if ((priv->monitor = lxcMonitorClient(driver, vm)) < 0)
goto cleanup;
+ VIR_DEBUG("driver: got the monitor socket from client\n");
/* And get its pid */
if ((r = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid))
!= 0) {