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

Re: [Xen-devel] [PATCH v8 3/7] Qemu-Xen-vTPM: Xen frontend driver infrastructure



On Sun, 17 May 2015, Quan Xu wrote:
> 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 <quan.xu@xxxxxxxxx>
> ---
>  hw/xen/Makefile.objs         |   2 +-
>  hw/xen/xen_frontend.c        | 345 
> +++++++++++++++++++++++++++++++++++++++++++
>  include/hw/xen/xen_backend.h |   8 +
>  3 files changed, 354 insertions(+), 1 deletion(-)
>  create mode 100644 hw/xen/xen_frontend.c
> 
> diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs
> index 9ac9f7c..95eb9d0 100644
> --- a/hw/xen/Makefile.objs
> +++ b/hw/xen/Makefile.objs
> @@ -1,5 +1,5 @@
>  # xen backend driver support
> -common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o xen_pvdev.o
> +common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o 
> xen_frontend.o xen_pvdev.o
>  
>  obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
>  obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o 
> xen_pt_msi.o
> diff --git a/hw/xen/xen_frontend.c b/hw/xen/xen_frontend.c
> new file mode 100644
> index 0000000..55af45a
> --- /dev/null
> +++ b/hw/xen/xen_frontend.c
> @@ -0,0 +1,345 @@
> +/*
> + * Xen frontend driver infrastructure
> + *
> + *  Copyright (c) 2015 Intel Corporation
> + *  Authors:
> + *    Quan Xu <quan.xu@xxxxxxxxx>
> + *
> + * 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
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see 
> <http://www.gnu.org/licenses/>
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdarg.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <inttypes.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +#include <sys/signal.h>
> +
> +#include "hw/hw.h"
> +#include "sysemu/char.h"
> +#include "qemu/log.h"
> +#include "hw/xen/xen_backend.h"
> +#include <xen/grant_table.h>
> +
> +int xenstore_dev;
> +
> +/* private */
> +static int debug;
> +
> +static void xen_fe_evtchn_event(void *opaque)
> +{
> +    struct XenDevice *xendev = opaque;
> +    evtchn_port_t port;
> +
> +    port = xc_evtchn_pending(xendev->evtchndev);
> +    if (port != xendev->local_port) {
> +        return;
> +    }
> +    xc_evtchn_unmask(xendev->evtchndev, port);
> +
> +    if (xendev->ops->event) {
> +        xendev->ops->event(xendev);
> +    }
> +}

This looks very similar to xen_be_evtchn_event. I think we should share
the same function between frontends and backends.


> +/* ------------------------------------------------------------- */
> +
> +int xen_fe_alloc_unbound(struct XenDevice *xendev, int dom, int remote_dom)
> +{
> +    xendev->local_port = xc_evtchn_bind_unbound_port(xendev->evtchndev,
> +                                                     remote_dom);
> +    if (xendev->local_port == -1) {
> +        xen_fe_printf(xendev, 0, "xc_evtchn_alloc_unbound failed\n");
> +        return -1;
> +    }
> +    xen_fe_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port);
> +    qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev),
> +                        xen_fe_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_fe_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;
> +}

Given that this is xen_frontend.c, shouldn't this function be called
xenbus_fe_switch_state? Also why is it so different from
xen_be_set_state?  I think it should be the same except for:

xenstore_write_fe_int(xendev->fe, "state", state);


> +/*
> + * 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_fe_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_fe_printf(xendev, 2, "frontend not ready, ignoring\n");
> +        } else {
> +            xen_fe_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_fe_printf(xendev, 0, "xendev %s initialise error\n",
> +                      xendev->name);
> +        goto err;
> +    }
> +    xen_fe_try_connected(xendev);
> +
> +    return rc;
> +
> +err:
> +    xen_del_xendev(domid, handle);
> +    return -1;
> +}

This should be called xen_fe_try_initialise


> +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;
> +        }

This is an uncommon xenstore protocol. I don't recognize "frontend" and
"domain". Usually:

/local/domain/$DOMID_BACKEND/backend/$TYPE/$DOMID_FRONTEND/$VDEV/frontend

contains:

/local/domain/$DOMID_FRONTEND/device/$TYPE/$VDEV

Vice versa:

/local/domain/$FRONTEND_DOMID/device/$TYPE/$VDEV/backend

contains:

/local/domain/$BACKEND_DOMID/backend/$TYPE/$DOMID_FRONTEND/$VDEV

Am I getting it wrong? Is there a reason to diverge from this?


> +        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;
> +
> +    /* ops .init check, xendev is NOT initialized */
> +    rc = xen_fe_try_init(ops);
> +    if (rc != 0) {
> +        return -1;
> +    }

The init function should only be called when the corresponding xenstore
entries are present. Here it looks like you are initializing the
frontend unconditionally, even if /local/domain/0/device/$TYPE is missing.


> +    /* Get /local/domain/0/${type}/{} directory */
> +    snprintf(path, sizeof(path), "frontend/%s", type);

Why "frontend"?  The string for frontends is usually "device".


> +    dev = xs_directory(xenstore, 0, path, &cdev);
> +    if (dev == NULL) {
> +        return 0;
> +    }

It might actually be easier to scan /local/domain/$DOMID/backend instead
of /local/domain/0/$TYPE, as I expect it to contain only one entry.
Of course you would still need to double check that

/local/domain/$DOMID_BACKEND/backend/$TYPE/$DOMID_FRONTEND

contains the domain id of the domain QEMU is running on.


> +    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_fe_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_fe_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_fe_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);
> +}
> +
> +/*
> + * msg_level:
> + *  0 == errors (stderr + logfile).
> + *  1 == informative debug messages (logfile only).
> + *  2 == noisy debug messages (logfile only).
> + *  3 == will flood your log (logfile only).
> + */
> +void xen_fe_printf(struct XenDevice *xendev, int msg_level,
> +                   const char *fmt, ...)
> +{
> +    va_list args;
> +
> +    if (xendev) {
> +        if (msg_level > xendev->debug) {
> +            return;
> +        }
> +        qemu_log("xen fe: %s: ", xendev->name);
> +        if (msg_level == 0) {
> +            fprintf(stderr, "xen fe: %s: ", xendev->name);
> +        }
> +    } else {
> +        if (msg_level > debug) {
> +            return;
> +        }
> +        qemu_log("xen fe core: ");
> +        if (msg_level == 0) {
> +            fprintf(stderr, "xen fe core: ");
> +        }
> +    }
> +    va_start(args, fmt);
> +    qemu_log_vprintf(fmt, args);
> +    va_end(args);
> +    if (msg_level == 0) {
> +        va_start(args, fmt);
> +        vfprintf(stderr, fmt, args);
> +        va_end(args);
> +    }
> +    qemu_log_flush();
> +}

As I wrote before, I would like to avoid having two separe xen printf
functions. Please share the same function between frontends and
backends.

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

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