@@ -364,6 +364,18 @@ struct libxl__ev_child {
LIBXL_LIST_ENTRY(struct libxl__ev_child) entry;
};
+struct libxl__ev_devlock {
+ /* filled by user */
+ libxl__ao *ao;
+ libxl_domid domid;
+ void (*callback)(libxl__egc *, libxl__ev_devlock *, int rc);
+ /* private to libxl__ev_devlock* */
+ libxl__ev_child child;
+ char *path; /* path of the lock file itself */
+ int fd;
+ bool held;
+};
+
/*
* QMP asynchronous calls
*
@@ -428,6 +440,8 @@ _hidden void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev);
typedef enum {
/* initial state */
qmp_state_disconnected = 1,
+ /* waiting for lock */
+ qmp_state_waiting_lock,
/* connected to QMP socket, waiting for greeting message */
qmp_state_connecting,
/* qmp_capabilities command sent, waiting for reply */
@@ -461,6 +475,7 @@ struct libxl__ev_qmp {
libxl__carefd *cfd;
libxl__ev_fd efd;
libxl__qmp_state state;
+ libxl__ev_qmplock lock;
int id;
int next_id; /* next id to use */
/* receive buffer */
@@ -4686,6 +4701,9 @@ static inline const char *libxl__qemu_qmp_path(libxl__gc *gc, int domid)
* which may take a significant amount time.
* It is to be acquired by an ao event callback.
*
+ * If libxl__ev_devlock is needed, it should be acquired while every
+ * libxl__ev_qmp are Idle for the current domain.
+ *
* It is to be acquired when adding/removing devices or making changes
* to them when this is a slow operation and json_lock isn't appropriate.
*
@@ -4711,17 +4729,6 @@ static inline const char *libxl__qemu_qmp_path(libxl__gc *gc, int domid)
* callback: When called: Active -> LockAcquired (on error: Idle)
* The callback is only called once.
*/
-struct libxl__ev_devlock {
- /* filled by user */
- libxl__ao *ao;
- libxl_domid domid;
- void (*callback)(libxl__egc *, libxl__ev_devlock *, int rc);
- /* private to libxl__ev_devlock* */
- libxl__ev_child child;
- char *path; /* path of the lock file itself */
- int fd;
- bool held;
-};
_hidden void libxl__ev_devlock_init(libxl__ev_devlock *);
_hidden void libxl__ev_devlock_lock(libxl__egc *, libxl__ev_devlock *);
_hidden void libxl__ev_devlock_unlock(libxl__gc *, libxl__ev_devlock *);
@@ -1084,6 +1084,7 @@ static void dm_state_saved(libxl__egc *egc, libxl__ev_qmp *ev,
*
* qmp_state External cfd efd id rx_buf* tx_buf* msg*
* disconnected Idle NULL Idle reset free free free
+ * waiting_lock Active open Idle reset used free set
* connecting Active open IN reset used free set
* cap.neg Active open IN|OUT sent used cap_neg set
* cap.neg Active open IN sent used free set
@@ -1153,6 +1154,10 @@ static void qmp_ev_ensure_reading_writing(libxl__gc *gc, libxl__ev_qmp *ev)
{
short events = POLLIN;
+ if (ev->state == qmp_state_waiting_lock)
+ /* We can't modifie the efd yet, as it isn't registered. */
+ return;
+
if (ev->tx_buf)
events |= POLLOUT;
else if ((ev->state == qmp_state_waiting_reply) && ev->msg)
@@ -1168,9 +1173,13 @@ static void qmp_ev_set_state(libxl__gc *gc, libxl__ev_qmp *ev,
switch (new_state) {
case qmp_state_disconnected:
break;
- case qmp_state_connecting:
+ case qmp_state_waiting_lock:
assert(ev->state == qmp_state_disconnected);
break;
+ case qmp_state_connecting:
+ assert(ev->state == qmp_state_disconnected ||
+ ev->state == qmp_state_waiting_lock);
+ break;
case qmp_state_capability_negotiation:
assert(ev->state == qmp_state_connecting);
break;
@@ -1231,20 +1240,20 @@ static int qmp_error_class_to_libxl_error_code(libxl__gc *gc,
/* Setup connection */
-static int qmp_ev_connect(libxl__gc *gc, libxl__ev_qmp *ev)
+static void qmp_ev_lock_aquired(libxl__egc *, libxl__ev_qmplock *, int rc);
+
+static int qmp_ev_connect(libxl__egc *egc, libxl__ev_qmp *ev)
/* disconnected -> connecting but with `msg` free
* on error: broken */
{
+ EGC_GC;
int fd;
- int rc, r;
- struct sockaddr_un un;
- const char *qmp_socket_path;
-
- assert(ev->state == qmp_state_disconnected);
+ int rc;
- qmp_socket_path = libxl__qemu_qmp_path(gc, ev->domid);
+ /* Convenience aliases */
+ libxl__ev_qmplock *lock = &ev->lock;
- LOGD(DEBUG, ev->domid, "Connecting to %s", qmp_socket_path);
+ assert(ev->state == qmp_state_disconnected);
libxl__carefd_begin();
fd = socket(AF_UNIX, SOCK_STREAM, 0);
@@ -1258,6 +1267,35 @@ static int qmp_ev_connect(libxl__gc *gc, libxl__ev_qmp *ev)
if (rc)
goto out;
+ qmp_ev_set_state(gc, ev, qmp_state_waiting_lock);
+
+ lock->ao = ev->ao;
+ lock->domid = ev->domid;
+ lock->callback = qmp_ev_lock_aquired;
+ libxl__ev_qmplock_lock(egc, &ev->lock);
+
+ return 0;
+
+out:
+ return rc;
+}
+
+static void qmp_ev_lock_aquired(libxl__egc *egc, libxl__ev_qmplock *lock,
+ int rc)
+{
+ libxl__ev_qmp *ev = CONTAINER_OF(lock, *ev, lock);
+ EGC_GC;
+ const char *qmp_socket_path;
+ struct sockaddr_un un;
+ int r;
+
+ if (rc)
+ goto out;
+
+ qmp_socket_path = libxl__qemu_qmp_path(gc, ev->domid);
+
+ LOGD(DEBUG, ev->domid, "Connecting to %s", qmp_socket_path);
+
rc = libxl__prepare_sockaddr_un(gc, &un, qmp_socket_path,
"QMP socket");
if (rc)
@@ -1279,10 +1317,10 @@ static int qmp_ev_connect(libxl__gc *gc, libxl__ev_qmp *ev)
qmp_ev_set_state(gc, ev, qmp_state_connecting);
- return 0;
+ return;
out:
- return rc;
+ LOGD(ERROR, ev->domid, "connect failed");
}
/* QMP FD callbacks */
@@ -1779,6 +1817,8 @@ void libxl__ev_qmp_init(libxl__ev_qmp *ev)
ev->qemu_version.major = -1;
ev->qemu_version.minor = -1;
ev->qemu_version.micro = -1;
+
+ libxl__ev_qmplock_init(&ev->lock);
}
int libxl__ev_qmp_send(libxl__egc *egc, libxl__ev_qmp *ev,
@@ -1798,7 +1838,7 @@ int libxl__ev_qmp_send(libxl__egc *egc, libxl__ev_qmp *ev,
/* Connect to QEMU if not already connected */
if (ev->state == qmp_state_disconnected) {
- rc = qmp_ev_connect(gc, ev);
+ rc = qmp_ev_connect(egc, ev);
if (rc)
goto error;
}
@@ -1830,6 +1870,7 @@ void libxl__ev_qmp_dispose(libxl__gc *gc, libxl__ev_qmp *ev)
libxl__ev_fd_deregister(gc, &ev->efd);
libxl__carefd_close(ev->cfd);
+ libxl__ev_qmplock_dispose(gc, &ev->lock);
libxl__ev_qmp_init(ev);
}
FIXME: The case where something failed when trying to acquired the lock isn't handled yet. This patch workaround the fact that it's not possible to connect multiple time to a single QMP socket. QEMU listen on the socket with a backlog value of 1, which mean that on Linux when concurrent thread call connect() on the socket, they get EAGAIN. To work around this, we use a new lock. Signed-off-by: Anthony PERARD <anthony.perard@citrix.com> --- tools/libxl/libxl_internal.h | 29 ++++++++++------ tools/libxl/libxl_qmp.c | 65 +++++++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 23 deletions(-)