qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 15/19] Qemu-Xen-vTPM: Xen frontend driver infrastruc


From: Emil Condrea
Subject: [Qemu-devel] [PATCH 15/19] Qemu-Xen-vTPM: Xen frontend driver infrastructure
Date: Sun, 10 Jul 2016 14:47:46 +0300

This patch adds infrastructure for xen front drivers living in qemu,
so drivers don't need to implement common stuff on their own.  It's
mostly xenbus management stuff: some functions to access XenStore,
setting up XenStore watches, callbacks on device discovery and state
changes, and handle event channel between the virtual machines.

Call xen_fe_register() function to register XenDevOps, and make sure,
XenDevOps's flags is DEVOPS_FLAG_FE, which is flag bit to point out
the XenDevOps is Xen frontend.

Signed-off-by: Quan Xu <address@hidden>
Signed-off-by: Emil Condrea <address@hidden>

---
Changes in v9:
 * xenstore_dev should not be global, it will not work correctly with
multiple xen frontends living in qemu
 * reuse some common functions:
    - xen_fe_printf -> xen_pv_printf
    - xen_fe_evtchn_event -> xen_pv_evtchn_event
 * use libxenevtchn stable API instead of xc_* calls:
    - xc_evtchn_fd -> xenevtchn_fd
    - xc_evtchn_close -> xenevtchn_close
    - xc_evtchn_bind_unbound_port -> xenevtchn_bind_unbound_port
---
 hw/xen/xen_frontend.c         | 308 ++++++++++++++++++++++++++++++++++++++++++
 hw/xen/xen_pvdev.c            |  15 ++
 include/hw/xen/xen_frontend.h |   6 +
 include/hw/xen/xen_pvdev.h    |   1 +
 4 files changed, 330 insertions(+)

diff --git a/hw/xen/xen_frontend.c b/hw/xen/xen_frontend.c
index 6b92cf1..7b305ce 100644
--- a/hw/xen/xen_frontend.c
+++ b/hw/xen/xen_frontend.c
@@ -3,6 +3,10 @@
  *
  *  (c) 2008 Gerd Hoffmann <address@hidden>
  *
+ *  Copyright (c) 2015 Intel Corporation
+ *  Authors:
+ *    Quan Xu <address@hidden>
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
@@ -23,6 +27,8 @@
 #include "hw/xen/xen_frontend.h"
 #include "hw/xen/xen_backend.h"
 
+static int debug = 0;
+
 char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node)
 {
     return xenstore_read_str(xendev->fe, node);
@@ -86,3 +92,305 @@ void xenstore_update_fe(char *watch, struct XenDevice 
*xendev)
     xen_fe_frontend_changed(xendev, node);
     xen_be_check_state(xendev);
 }
+
+struct XenDevice *xen_fe_get_xendev(const char *type, int dom, int dev,
+                                    char *backend, struct XenDevOps *ops)
+{
+    struct XenDevice *xendev;
+
+    xendev = xen_pv_find_xendev(type, dom, dev);
+    if (xendev) {
+        return xendev;
+    }
+
+    /* init new xendev */
+    xendev = g_malloc0(ops->size);
+    xendev->type  = type;
+    xendev->dom   = dom;
+    xendev->dev   = dev;
+    xendev->ops   = ops;
+
+    /*return if the ops->flags is not DEVOPS_FLAG_FE*/
+    if (!(ops->flags & DEVOPS_FLAG_FE)) {
+        return NULL;
+    }
+
+    snprintf(xendev->be, sizeof(xendev->be), "%s", backend);
+    snprintf(xendev->name, sizeof(xendev->name), "%s-%d",
+             xendev->type, xendev->dev);
+
+    xendev->debug = debug;
+    xendev->local_port = -1;
+
+    xendev->evtchndev = xenevtchn_open(NULL, 0);
+    if (xendev->evtchndev == NULL) {
+        xen_pv_printf(NULL, 0, "can't open evtchn device\n");
+        g_free(xendev);
+        return NULL;
+    }
+    fcntl(xenevtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC);
+
+    if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) {
+        xendev->gnttabdev = xengnttab_open(NULL, 0);
+        if (xendev->gnttabdev == NULL) {
+            xen_pv_printf(NULL, 0, "can't open gnttab device\n");
+            xenevtchn_close(xendev->evtchndev);
+            g_free(xendev);
+            return NULL;
+        }
+    } else {
+        xendev->gnttabdev = NULL;
+    }
+
+    xen_pv_insert_xendev(xendev);
+
+    if (xendev->ops->alloc) {
+        xendev->ops->alloc(xendev);
+    }
+
+    return xendev;
+}
+
+int xen_fe_alloc_unbound(struct XenDevice *xendev, int dom, int remote_dom){
+    xendev->local_port = xenevtchn_bind_unbound_port(xendev->evtchndev,
+                                                     remote_dom);
+    if (xendev->local_port == -1) {
+        xen_pv_printf(xendev, 0, "xenevtchn_bind_unbound_port failed\n");
+        return -1;
+    }
+    xen_pv_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port);
+    qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev),
+                        xen_pv_evtchn_event, NULL, xendev);
+    return 0;
+}
+
+/*
+ * Make sure, initialize the 'xendev->fe' in xendev->ops->init() or
+ * xendev->ops->initialize()
+ */
+int xenbus_switch_state(struct XenDevice *xendev, enum xenbus_state xbus)
+{
+    xs_transaction_t xbt = XBT_NULL;
+
+    if (xendev->fe_state == xbus) {
+        return 0;
+    }
+
+    xendev->fe_state = xbus;
+    if (xendev->fe == NULL) {
+        xen_pv_printf(NULL, 0, "xendev->fe is NULL\n");
+        return -1;
+    }
+
+retry_transaction:
+    xbt = xs_transaction_start(xenstore);
+    if (xbt == XBT_NULL) {
+        goto abort_transaction;
+    }
+
+    if (xenstore_write_int(xendev->fe, "state", xbus)) {
+        goto abort_transaction;
+    }
+
+    if (!xs_transaction_end(xenstore, xbt, 0)) {
+        if (errno == EAGAIN) {
+            goto retry_transaction;
+        }
+    }
+
+    return 0;
+
+abort_transaction:
+    xs_transaction_end(xenstore, xbt, 1);
+    return -1;
+}
+
+/*
+ * Simplify QEMU side, a thread is running in Xen backend, which will
+ * connect frontend when the frontend is initialised. Call these initialised
+ * functions.
+ */
+static int xen_fe_try_init(void *opaque)
+{
+    struct XenDevOps *ops = opaque;
+    int rc = -1;
+
+    if (ops->init) {
+        rc = ops->init(NULL);
+    }
+
+    return rc;
+}
+
+static int xen_fe_try_initialise(struct XenDevice *xendev)
+{
+    int rc = 0, fe_state;
+
+    if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) {
+        fe_state = XenbusStateUnknown;
+    }
+    xendev->fe_state = fe_state;
+
+    if (xendev->ops->initialise) {
+        rc = xendev->ops->initialise(xendev);
+    }
+    if (rc != 0) {
+        xen_pv_printf(xendev, 0, "initialise() failed\n");
+        return rc;
+    }
+
+    xenbus_switch_state(xendev, XenbusStateInitialised);
+    return 0;
+}
+
+static void xen_fe_try_connected(struct XenDevice *xendev)
+{
+    if (!xendev->ops->connected) {
+        return;
+    }
+
+    if (xendev->fe_state != XenbusStateConnected) {
+        if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) {
+            xen_pv_printf(xendev, 2, "frontend not ready, ignoring\n");
+        } else {
+            xen_pv_printf(xendev, 2, "frontend not ready (yet)\n");
+            return;
+        }
+    }
+
+    xendev->ops->connected(xendev);
+}
+
+static int xen_fe_check(struct XenDevice *xendev, uint32_t domid,
+                        int handle)
+{
+    int rc = 0;
+
+    rc = xen_fe_try_initialise(xendev);
+    if (rc != 0) {
+        xen_pv_printf(xendev, 0, "xendev %s initialise error\n",
+                      xendev->name);
+        goto err;
+    }
+    xen_fe_try_connected(xendev);
+
+    return rc;
+
+err:
+    xen_pv_del_xendev(domid, handle);
+    return -1;
+}
+
+static char *xenstore_fe_get_backend(const char *type, int be_domid,
+                                     uint32_t domid, int *hdl)
+{
+    char *name, *str, *ret = NULL;
+    uint32_t i, cdev;
+    int handle = 0;
+    char path[XEN_BUFSIZE];
+    char **dev = NULL;
+
+    name = xenstore_get_domain_name(domid);
+    snprintf(path, sizeof(path), "frontend/%s/%d", type, be_domid);
+    dev = xs_directory(xenstore, 0, path, &cdev);
+    for (i = 0; i < cdev; i++) {
+        handle = i;
+        snprintf(path, sizeof(path), "frontend/%s/%d/%d",
+        type, be_domid, handle);
+        str = xenstore_read_str(path, "domain");
+        if (!strcmp(name, str)) {
+            break;
+        }
+
+        free(str);
+
+        /* Not the backend domain */
+        if (handle == (cdev - 1)) {
+            goto err;
+        }
+    }
+
+    snprintf(path, sizeof(path), "frontend/%s/%d/%d",
+    type, be_domid, handle);
+    str = xenstore_read_str(path, "backend");
+    if (str != NULL) {
+        ret = g_strdup(str);
+        free(str);
+    }
+
+    *hdl = handle;
+    free(dev);
+
+    return ret;
+err:
+    *hdl = -1;
+    free(dev);
+    return NULL;
+}
+
+static int xenstore_fe_scan(const char *type, uint32_t domid,
+                            struct XenDevOps *ops)
+{
+    struct XenDevice *xendev;
+    char path[XEN_BUFSIZE], token[XEN_BUFSIZE];
+    unsigned int cdev, j;
+    char *backend;
+    char **dev = NULL;
+    int rc;
+    int xenstore_dev;
+
+    /* ops .init check, xendev is NOT initialized */
+    rc = xen_fe_try_init(ops);
+    if (rc != 0) {
+        return -1;
+    }
+
+    /* Get /local/domain/0/${type}/{} directory */
+    snprintf(path, sizeof(path), "frontend/%s", type);
+    dev = xs_directory(xenstore, 0, path, &cdev);
+    if (dev == NULL) {
+        return 0;
+    }
+
+    for (j = 0; j < cdev; j++) {
+
+        /* Get backend via domain name */
+        backend = xenstore_fe_get_backend(type, atoi(dev[j]),
+                                          domid, &xenstore_dev);
+        if (backend == NULL) {
+            continue;
+        }
+
+        xendev = xen_fe_get_xendev(type, domid, xenstore_dev, backend, ops);
+        free(backend);
+        if (xendev == NULL) {
+            xen_pv_printf(xendev, 0, "xendev is NULL.\n");
+            continue;
+        }
+
+        /*
+         * Simplify QEMU side, a thread is running in Xen backend, which will
+         * connect frontend when the frontend is initialised.
+         */
+        if (xen_fe_check(xendev, domid, xenstore_dev) < 0) {
+            xen_pv_printf(xendev, 0, "xendev fe_check error.\n");
+            continue;
+        }
+
+        /* Setup watch */
+        snprintf(token, sizeof(token), "be:%p:%d:%p",
+                 type, domid, xendev->ops);
+        if (!xs_watch(xenstore, xendev->be, token)) {
+            xen_pv_printf(xendev, 0, "xs_watch failed.\n");
+            continue;
+        }
+    }
+
+    free(dev);
+    return 0;
+}
+
+int xen_fe_register(const char *type, struct XenDevOps *ops)
+{
+    return xenstore_fe_scan(type, xen_domid, ops);
+}
diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c
index 394ddc1..cb51e87 100644
--- a/hw/xen/xen_pvdev.c
+++ b/hw/xen/xen_pvdev.c
@@ -121,6 +121,21 @@ cleanup:
     free(vec);
 }
 
+char *xenstore_get_domain_name(uint32_t domid)
+{
+    char *dom_path, *str, *ret = NULL;
+
+    dom_path = xs_get_domain_path(xenstore, domid);
+    str = xenstore_read_str(dom_path, "name");
+    free(dom_path);
+    if (str != NULL) {
+        ret = g_strdup(str);
+        free(str);
+    }
+
+    return ret;
+}
+
 const char *xenbus_strstate(enum xenbus_state state)
 {
     static const char *const name[] = {
diff --git a/include/hw/xen/xen_frontend.h b/include/hw/xen/xen_frontend.h
index 5d03f4e..8fc0422 100644
--- a/include/hw/xen/xen_frontend.h
+++ b/include/hw/xen/xen_frontend.h
@@ -8,6 +8,12 @@ int xenstore_read_fe_int(struct XenDevice *xendev, const char 
*node, int *ival);
 int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node, 
uint64_t *uval);
 void xenstore_update_fe(char *watch, struct XenDevice *xendev);
 
+struct XenDevice *xen_fe_get_xendev(const char *type, int dom, int dev,
+                                    char *backend, struct XenDevOps *ops);
 void xen_fe_frontend_changed(struct XenDevice *xendev, const char *node);
+int xen_fe_register(const char *type, struct XenDevOps *ops);
+int xen_fe_alloc_unbound(struct XenDevice *xendev, int dom, int remote_dom);
+int xenbus_switch_state(struct XenDevice *xendev, enum xenbus_state xbus);
+
 
 #endif /* QEMU_HW_XEN_FRONTEND_H */
diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h
index c985a9d..11bab56 100644
--- a/include/hw/xen/xen_pvdev.h
+++ b/include/hw/xen/xen_pvdev.h
@@ -65,6 +65,7 @@ char *xenstore_read_str(const char *base, const char *node);
 int xenstore_read_int(const char *base, const char *node, int *ival);
 int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval);
 void xenstore_update(void *unused);
+char *xenstore_get_domain_name(uint32_t domid);
 
 const char *xenbus_strstate(enum xenbus_state state);
 
-- 
1.9.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]