[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v3 15/31] libxl_qmp_ev: Implement fd callback and read data



First step into taking care of the input from QEMU's QMP socket. For
now, we read data and store them in buffers.

Parsing of the data will be done in the following patches.

Signed-off-by: Anthony PERARD <anthony.perard@xxxxxxxxxx>
---
 tools/libxl/libxl_qmp.c | 113 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)

diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c
index 077cac9c8a..48dc376307 100644
--- a/tools/libxl/libxl_qmp.c
+++ b/tools/libxl/libxl_qmp.c
@@ -75,6 +75,12 @@
 #  define DEBUG_REPORT_RECEIVED(dom, buf, len) ((void)0)
 #endif
 
+#ifdef DEBUG_QMP_CLIENT
+#  define LOG_QMP(f, ...) LOGD(DEBUG, qmp->domid, f, ##__VA_ARGS__)
+#else
+#  define LOG_QMP(f, ...)
+#endif
+
 /*
  * QMP types & constant
  */
@@ -1353,15 +1359,115 @@ int libxl__qmp_initializations(libxl__gc *gc, uint32_t 
domid,
 
 /* ------------ Implementation of libxl__ev_qmp ---------------- */
 
+typedef struct libxl__qmp_rx_buf libxl__qmp_rx_buf;
+struct libxl__qmp_rx_buf {
+    LIBXL_TAILQ_ENTRY(libxl__qmp_rx_buf) entry;
+    /* How much data there is in buf */
+    int used;
+    /* How much have been parsed */
+    size_t consumed;
+    char buf[QMP_RECEIVE_BUFFER_SIZE];
+};
+
 struct libxl__ev_qmp_state {
     libxl__carefd *cfd;
     libxl__ev_fd efd;
     uint32_t domid;
+
+    LIBXL_TAILQ_HEAD(libxl__qmp_bufs, libxl__qmp_rx_buf) bufs;
 };
 
+
+static int ev_qmp_callback_readable(libxl__egc *egc, libxl__ev_qmp_state *qmp,
+                                    int fd)
+{
+    EGC_GC;
+    ssize_t r;
+    libxl__qmp_rx_buf *buf;
+
+    /* Check if last buffer still have space, or alloc a new one */
+    buf = LIBXL_TAILQ_LAST(&qmp->bufs, libxl__qmp_bufs);
+    if (buf) {
+        /* The -1 is because there is always space for a NUL character */
+        if (buf->used == sizeof(buf->buf) - 1) {
+            buf = NULL;
+        }
+    }
+    if (!buf) {
+        buf = libxl__malloc(NOGC, sizeof(*buf));
+        buf->used = 0;
+        buf->consumed = 0;
+        LIBXL_TAILQ_INSERT_TAIL(&qmp->bufs, buf, entry);
+    }
+
+    for (;;) {
+        /* The -1 is because there is always space for a NUL character */
+        r = read(fd, buf->buf + buf->used, sizeof(buf->buf) - buf->used - 1);
+        if (r < 0) {
+            if (errno == EINTR) continue;
+            assert(errno);
+            if (errno == EWOULDBLOCK) {
+                return 0;
+            }
+            LOGED(ERROR, qmp->domid, "error reading QMP socket");
+            return ERROR_FAIL;
+        }
+        break;
+    }
+
+    if (r == 0) {
+        LOGD(ERROR, qmp->domid, "No data read on QMP socket");
+        return 0;
+    }
+
+    LOG_QMP("received %ldB: '%.*s'", r, (int)r, buf->buf + buf->used);
+
+    buf->used += r;
+    assert(buf->used < sizeof(buf->buf));
+
+    return 0;
+}
+
+/* When the QMP client reach the conclusion that the QMP connection doesn't
+ * work anymore, this function can be called to propagate the error to every
+ * callback registered. And stop the client. */
+static void ev_qmp_callback_error(libxl__egc *egc, libxl__ev_qmp_state *qmp)
+{
+    EGC_GC;
+
+    LOGD(ERROR, qmp->domid, "Error happend with the QMP connection to QEMU");
+    libxl__ev_qmp_stop(gc, qmp);
+}
+
 static void ev_qmp_fd_callback(libxl__egc *egc, libxl__ev_fd *ev_fd,
                                int fd, short events, short revents)
 {
+    EGC_GC;
+    int rc;
+
+    libxl__ev_qmp_state *qmp = CONTAINER_OF(ev_fd, *qmp, efd);
+
+    if (revents & (POLLHUP)) {
+        LOGD(DEBUG, qmp->domid, "received POLLHUP from QMP socket");
+        ev_qmp_callback_error(egc, qmp);
+        return;
+    }
+    if (revents & ~(POLLIN|POLLOUT)) {
+        LOGD(ERROR, qmp->domid,
+             "unexpected poll event 0x%x on QMP socket (expected POLLIN "
+             "and/or POLLOUT)",
+            revents);
+        ev_qmp_callback_error(egc, qmp);
+        return;
+    }
+
+    if (revents & POLLIN) {
+        rc = ev_qmp_callback_readable(egc, qmp, fd);
+        if (rc) {
+            ev_qmp_callback_error(egc, qmp);
+            return;
+        }
+    }
 }
 
 static void libxl__ev_qmp_state_init(libxl__ev_qmp_state *qmp)
@@ -1369,6 +1475,7 @@ static void libxl__ev_qmp_state_init(libxl__ev_qmp_state 
*qmp)
     qmp->domid = INVALID_DOMID;
     qmp->cfd = NULL;
     libxl__ev_fd_init(&qmp->efd);
+    LIBXL_TAILQ_INIT(&qmp->bufs);
 }
 
 libxl__ev_qmp_state *libxl__ev_qmp_start(libxl__gc *gc, uint32_t domid)
@@ -1444,6 +1551,8 @@ out:
 
 void libxl__ev_qmp_stop(libxl__gc *gc, libxl__ev_qmp_state *qmp)
 {
+    libxl__qmp_rx_buf *buf, *tbuf;
+
     if (!qmp)
         return;
 
@@ -1451,6 +1560,10 @@ void libxl__ev_qmp_stop(libxl__gc *gc, 
libxl__ev_qmp_state *qmp)
 
     LOGD(DEBUG, qmp->domid, "Stopping QMP handler");
 
+    LIBXL_TAILQ_FOREACH_SAFE(buf, &qmp->bufs, entry, tbuf)
+        free(buf);
+    LIBXL_TAILQ_INIT(&qmp->bufs);
+
     libxl__ev_fd_deregister(gc, &qmp->efd);
     libxl__carefd_close(qmp->cfd);
     qmp->cfd = NULL;
-- 
Anthony PERARD


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.