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

[Xen-devel] [PATCH 3 of 5 RFC] blktap3: Introduce xenio.c, core xenio daemon functionality



Contains the core xenio daemon functionality, where it monitors XenStore for
blkfront state changes and acts in response to them. There is considerable
clean up left to do in this file (uninitialised struct members used,
misleading function names, obscure/cryptic functionality, some functions should
go in separate files etc.).

The xenio daemon monitors XenStore and when a Virtual Block Device must be
created, it creates the shared ring, instructs the tapdisk to connect to it, and
finally communicates any necessary information to blkfront via XenStore.

diff -r 6918a76c96d8 -r 7126fda14249 tools/blktap3/xenio/xenio.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/blktap3/xenio/xenio.c       Wed Nov 28 14:18:45 2012 +0000
@@ -0,0 +1,1947 @@
+/*
+ * Copyright (C) 2012      Citrix Ltd.
+ *
+ * This program 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; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program 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.
+ *
+ * This file contains the xenio daemon. It is the user-space daemon responsible
+ * for instructing the tapdisk to connect to the shared ring when the block
+ * front-end becomes ready. It does so by monitoring XenStore for changes in
+ * blkfront state.
+ */
+
+/*
+ * TODO Some of these includes may be useless.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <xenstore.h>
+#include <xen/xen.h>
+#include <xen/io/xenbus.h>
+#include <xen/grant_table.h>
+#include <xen/event_channel.h>
+
+#include "blktap3.h"
+#include "tap-ctl.h"
+#include "tap-ctl-xen.h"
+#include "tap-ctl-info.h"
+#include "xenio-common.h"
+#include "xenio-private.h"
+
+/**
+ * TODO Removes calls to this macro and replace with graceful error handling.
+ */
+#define BUG()                  abort()
+
+#define BUG_ON(_cond)  if (unlikely(_cond)) BUG()
+
+#define ORDER_TO_PAGES(o)      (1 << (o))
+
+/*
+ * XenStore path components.
+ *
+ * TODO "xenio" is defined in the IDL, take it from there instead of
+ * hard-coding it here
+ */
+#define XENIO_BACKEND_NAME     "xenio"
+#define XENIO_BACKEND_PATH     "backend/"XENIO_BACKEND_NAME
+#define XENIO_BACKEND_TOKEN    "backend-"XENIO_BACKEND_NAME
+
+/*
+ * TODO The following structures are private to this file. Move them to a
+ * separate file for code simplification purposes?
+ */
+
+/**
+ * Additional state for the virtual block device (see struct xenio_device
+ * first).
+ *
+ * TODO Rename this struct, there's no blkback any more.
+ * 
+ * TODO Try to merge into xenio_device to simplify the code.
+ */
+typedef struct blkback_device {
+
+    /**
+     * FIXME This is used but isn't initialised anywhere!
+     */
+    dev_t dev;
+
+    /**
+     * TODO already in struct xenio_device
+     */
+    domid_t domid;
+
+    /**
+     * TODO The device ID. This is the same as xenio_device.devid, why do we
+     * keep two copies of it?
+     */
+    int devid;
+
+    /**
+     * The grant references as supplied from blkfront via XenStore. They are
+     * supplied to tapdisk.
+     *
+     * TODO Retrieving the grant references and supplying them to tapdisk is
+     * done in the same function, so we don't have to keep them around. The
+     * only _currently obvious_ reason for this member to exist is to detect
+     * whether the tapdisk is connected or not, and this can be achieved by
+     * using a dedicated member. Could there be another reason for which we
+     * need to keep them around?
+     */
+    grant_ref_t *gref;
+
+    /**
+     * TODO The only reason we're keeping this is for detecting whether
+     * tapdisk is connected.
+     */
+    evtchn_port_t port;
+
+    /**
+        * Descriptor of the tapdisk process serving this virtual block device. 
We
+     * need this until the end of the VBD's lifetime in order to disconnect
+     * the tapdisk.
+        */
+    tap_list_t tap;
+
+    /**
+        * Sector size, supplied by the tapdisk, communicated to blkfront.
+        */
+    unsigned int sector_size;
+
+    /**
+        * Number of sectors, supplied by the tapdisk, communicated to blkfront.
+        */
+    unsigned long long sectors;
+
+    /**
+     * FIXME This is used but isn't initialised anywhere! Maybe it's the
+     * useless "info" in blkback_probe_device?
+        */
+    unsigned int info;
+} blkback_device_t;
+
+/**
+ * TODO A block device in the guest.
+ */
+typedef struct xenio_device {
+
+    /**
+        * Device name, as retrieved from XenStore at probe-time.
+     *
+     * TODO What's the format of this string?
+     *
+     * TODO Duplicates blkback_device.devid.
+        */
+    char *name;
+
+    /**
+        * For linked lists.
+        */
+     TAILQ_ENTRY(xenio_device) backend_entry;
+
+    /**
+        * FIXME Supposed to solve some kind of race condition with libxl?
+        */
+    long long serial;
+
+    /**
+        * The domain ID this VBD belongs to.
+        */
+    domid_t domid;
+
+    /**
+     * The root directory in XenStore for this VBD. This is where all
+     * directories and key/value pairs related to this VBD are stored.
+     */
+    char *frontend_path;
+
+    /**
+     * XenStore path to the VBD's state.
+     *
+     * TODO Do we really have to keep this around? This is just
+     * xenio_device.xenio_device + "/state".
+     */
+    char *frontend_state_path;
+
+    /**
+     * Additional state for the VBD.
+     */
+    blkback_device_t *bdev;
+
+} xenio_device_t;
+
+TAILQ_HEAD(tqh_xenio_device, xenio_device);
+
+/**
+ * TODO Organising these operation in such a way is not currently necessary
+ * since we only use one set of them (blkback_probe_device, blkback_remove, and
+ * blkback_frontend_changed). I guess it would make sense if we plan to have
+ * more of these, but I don't see a reason for this. Discuss whether we may
+ * need more of them or remove this struct.
+ */
+struct xenio_backend_ops {
+
+    /**
+     * Initialises the device handle.
+     *
+     * TODO misleading name
+     */
+    int (*probe) (xenio_device_t *, domid_t, const char *);
+
+    /**
+     * Frees the device handle.
+     *
+     * TODO misleading name
+     */
+    void (*remove) (xenio_device_t *);
+
+    /**
+     * To be executed in response to block front-end state changes.
+     */
+    int (*frontend_changed) (xenio_device_t *, XenbusState);
+};
+
+/**
+ * The collection of all necessary handles and descriptors.
+ */
+static struct xenio_backend {
+
+    /**
+        * A handle to XenStore.
+        */
+    struct xs_handle *xs;
+
+    /**
+     * For executing transacted operations on XenStore.
+     */
+    xs_transaction_t xst;
+
+    /**
+        * The list of virtual block devices.
+        */
+    struct tqh_xenio_device devices;
+
+    /**
+        * For allocating serials.
+        */
+    long long serial;
+
+    const struct xenio_backend_ops *ops;
+
+    /**
+     * TODO From xen/include/public/io/blkif.h: "The maximum supported size of
+     * the request ring buffer" 
+     */
+    int max_ring_page_order;
+} backend;
+
+#define xenio_backend_for_each_device(_device, _next)  \
+       TAILQ_FOREACH_SAFE(_device, &backend.devices, backend_entry, _next)
+
+/*
+ * TODO Here comes a set of functions used for accessing XenStore. It would
+ * be better to isolate them in a separate file since their functionality has
+ * nothing really to do with the daemon's logic.
+ */
+
+/**
+ * Prints the supplied arguments to a buffer and returns it. The buffer must
+ * be deallocated by the user.
+ */
+static char *
+vmprintf(const char * const fmt, va_list ap)
+{
+    char *s;
+    int n;
+
+    n = vasprintf(&s, fmt, ap);
+    if (n < 0)
+        s = NULL;
+
+    return s;
+}
+
+/**
+ * Prints the supplied arguments to a buffer and returns it. The buffer must
+ * be deallocated by the user.
+ */
+__printf(1, 2)
+static char *
+mprintf(const char * const fmt, ...)
+{
+    va_list ap;
+    char *s;
+
+    va_start(ap, fmt);
+    s = vmprintf(fmt, ap);
+    va_end(ap);
+
+    return s;
+}
+
+/**
+ * Reads the specified XenStore path. The caller must free the returned buffer.
+ *
+ * @param xs handle to XenStore
+ * @param xst XenStore transaction (TODO Maybe NULL?)
+ * @param fmt TODO
+ * @param ap TODO
+ * @returns TODO
+ *
+ * TODO Why don't we return the data pointer?
+ */
+static char *
+xenio_xs_vread(struct xs_handle * const xs, xs_transaction_t xst,
+        const char * const fmt, va_list ap)
+{
+    char *path, *data, *s = NULL;
+    unsigned int len;
+
+    assert(xs);
+
+    path = vmprintf(fmt, ap);
+    data = xs_read(xs, xst, path, &len);
+    DBG("XS read %s -> %s \n", path, data);
+    free(path);
+
+    if (data) {
+        s = strndup(data, len);
+        free(data);
+    }
+
+    return s;
+}
+
+/**
+ * Reads the specified XenStore path. The caller must free the returned buffer.
+ *
+ * @param xs handle to XenStore
+ * @param xst XenStore transaction (TODO Maybe NULL?)
+ * @param fmt TODO
+ * @returns TODO
+ */
+__printf(3, 4)
+static char *
+xenio_xs_read(struct xs_handle * const xs, xs_transaction_t xst,
+        const char * const fmt, ...)
+{
+    va_list ap;
+    char *s;
+
+    assert(xs);
+
+    va_start(ap, fmt);
+    s = xenio_xs_vread(xs, xst, fmt, ap);
+    va_end(ap);
+
+    return s;
+}
+
+/**
+ * Retrieves the value of the specified of the device from XenStore,
+ * i.e. backend/xenio/<domid>/<devname>/@path
+ *
+ * @param device the VBD 
+ * @param path the XenStore key
+ * @returns a buffer containing the value, the caller must free it
+ */
+static char *
+xenio_device_read(const xenio_device_t * const device, const char * const path)
+{
+    assert(device);
+    assert(path);
+
+    return xenio_xs_read(backend.xs, backend.xst, "%s/%d/%s/%s",
+            XENIO_BACKEND_PATH, device->domid, device->name, path);
+}
+
+/**
+ * TODO Only called by xenio_device_scanf, merge into it?
+ */
+static inline int
+xenio_device_vscanf(xenio_device_t * const device, const char * const path,
+        const char * const fmt, va_list ap)
+{
+    char *s;
+    int n;
+
+    assert(device);
+    assert(path);
+
+    s = xenio_device_read(device, path);
+    if (!s)
+        return -1;
+
+    DBG("%s <- %s\n", path, s);
+    n = vsscanf(s, fmt, ap);
+    free(s);
+
+    return n;
+}
+
+/**
+ * TODO Only called by xenio_device_check_serial, merge into it?
+ */
+__scanf(3, 4)
+static int
+xenio_device_scanf(xenio_device_t * const device, const char * const path,
+        const char * const fmt, ...)
+{
+    va_list ap;
+    int n;
+
+    assert(device);
+    assert(path);
+
+    va_start(ap, fmt);
+    n = xenio_device_vscanf(device, path, fmt, ap);
+    va_end(ap);
+
+    return n;
+}
+
+/**
+ * Retrieves the XenStore value of the specified key of the VBD's front-end.
+ *
+ * @param device the VBD
+ * @param path key under the front-end directory
+ * @returns a buffer containing the value, the caller must free it
+ */
+static char *
+xenio_device_read_otherend(xenio_device_t * const device,
+        const char * const path)
+{
+    assert(device);
+    assert(path);
+
+    return xenio_xs_read(backend.xs, backend.xst, "%s/%s",
+            device->frontend_path, path);
+}
+
+/**
+ * Reads the specified XenStore path under the front-end directory in a
+ * scanf-like manner.
+ *
+ * TODO Only called by xenio_device_scanf_otherend, merge into it?
+ */
+static inline int
+xenio_device_vscanf_otherend(xenio_device_t * const device,
+        const char * const path, const char * const fmt, va_list ap)
+{
+    char *s;
+    int n;
+
+    assert(device);
+    assert(path);
+
+    s = xenio_device_read_otherend(device, path);
+    if (!s)
+        return -1;
+
+    n = vsscanf(s, fmt, ap);
+    free(s);
+
+    return n;
+}
+
+/**
+ * Reads the specified XenStore path under the front-end directory in a
+ * scanf-like manner.
+ *
+ * @param device the VBD
+ * @param path TODO
+ * @param fmt TODO
+ */
+__scanf(3, 4)
+static int
+xenio_device_scanf_otherend(xenio_device_t * const device,
+        const char * const path, const char * const fmt, ...)
+{
+    va_list ap;
+    int n;
+
+    assert(device);
+    assert(path);
+
+    va_start(ap, fmt);
+    n = xenio_device_vscanf_otherend(device, path, fmt, ap);
+    va_end(ap);
+
+    return n;
+}
+
+/**
+ * TODO Only called by xenio_device_printf.
+ *
+ * @returns 0 on success, an error code otherwise
+ */
+static inline int
+xenio_device_vprintf(xenio_device_t * const device, const char * const key,
+        const int mkread, const char * const fmt, va_list ap)
+{
+    char *path = NULL, *val = NULL;
+    bool nerr;
+    int err;
+
+    assert(device);
+    assert(key);
+
+    path = mprintf("%s/%d/%s/%s", XENIO_BACKEND_PATH, device->domid,
+            device->name, key);
+    if (!path) {
+        err = -errno;
+        goto fail;
+    }
+
+    val = vmprintf(fmt, ap);
+    if (!val) {
+        err = -errno;
+        goto fail;
+    }
+
+    DBG("%s -> %s\n", path, val);
+    nerr = xs_write(backend.xs, backend.xst, path, val, strlen(val));
+    if (!nerr) {
+        err = -errno;
+        goto fail;
+    }
+
+    if (mkread) {
+        struct xs_permissions perms = {
+            device->domid,
+            XS_PERM_READ
+        };
+
+        nerr = xs_set_permissions(backend.xs, backend.xst, path, &perms, 1);
+        if (!nerr) {
+            err = -errno;
+            goto fail;
+        }
+    }
+
+    err = 0;
+
+fail:
+    if (path)
+        free(path);
+    if (val)
+        free(val);
+
+    return err;
+}
+
+/**
+ * Writes to XenStore backened/xenio/<domid>/<devname>/@key = @fmt. If @mkread
+ * is non-zero TODO the xenstore key/value pair is read-only?
+ *
+ * @param device the VBD
+ * @param key the key to write to
+ * @param mkread TODO
+ * @param fmt TODO
+ * @returns 0 on success, an error code otherwise
+ */
+__printf(4, 5)
+static int
+xenio_device_printf(xenio_device_t * const device, const char * const key,
+        const int mkread, const char * const fmt, ...)
+{
+    va_list ap;
+    int err;
+
+    assert(device);
+    assert(key);
+
+    va_start(ap, fmt);
+    err = xenio_device_vprintf(device, key, mkread, fmt, ap);
+    va_end(ap);
+
+    return err;
+}
+
+/**
+ * TODO Only called by xenio_backend_probe_device
+ *
+ * Returns 0 if the device's serial hasn't changed.
+ */
+static inline long long
+xenio_device_check_serial(xenio_device_t * const device)
+{
+    long long serial;
+    int n, err;
+
+    assert(device);
+
+    /* read backend/xenio/<domid>/<device name>/xenio-serial from xenstore */
+    n = xenio_device_scanf(device, "xenio-serial", "%lld", &serial);
+    if (n != 1) {
+        err = -EEXIST;
+        goto fail;
+    }
+
+    if (serial != device->serial) {
+        err = -EXDEV;
+        goto fail;
+    }
+
+    err = 0;
+fail:
+    return err;
+}
+
+/**
+ * Removes the XenStore watch from the front-end.
+ *
+ * @param device the VBD whose front-end XenStore path should stop being
+ * watched
+ */
+static void
+xenio_device_unwatch_frontend_state(xenio_device_t * const device)
+{
+    assert(device);
+
+    if (device->frontend_state_path)
+        xs_unwatch(backend.xs, device->frontend_state_path, "otherend-state");
+
+    if (device->frontend_state_path) {
+        free(device->frontend_state_path);
+        device->frontend_state_path = NULL;
+    }
+}
+
+/**
+ * Watches the frontend path, using otherend-state as the token. I.e.
+ * /local/domain/<domid>/device/vbd/<devname>/state
+ *
+ * TODO Only called by xenio_backend_create_device
+ */
+static inline int
+xenio_device_watch_frontend_state(xenio_device_t * const device)
+{
+    bool nerr;
+    int err;
+
+    assert(device);
+
+    device->frontend_state_path = mprintf("%s/state", device->frontend_path);
+    if (!device->frontend_state_path) {
+        err = -errno;
+        goto fail;
+    }
+
+    DBG("watching %s\n", device->frontend_state_path);
+
+    nerr = xs_watch(backend.xs, device->frontend_state_path, "otherend-state");
+    if (!nerr) {
+        err = -errno;
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    xenio_device_unwatch_frontend_state(device);
+    return err;
+}
+
+/**
+ *
+ * TODO Only called by xenio_backend_handle_otherend_watch, merge into it?
+ */
+static int
+xenio_device_check_frontend_state(xenio_device_t * const device)
+{
+    int state, err;
+    char *s, *end;
+
+    assert(device);
+
+    s = xenio_xs_read(backend.xs, backend.xst, "%s",
+            device->frontend_state_path);
+    if (!s) {
+        err = -errno;
+        goto fail;
+    }
+
+    state = strtol(s, &end, 0);
+    if (*end != 0 || end == s) {
+        err = -EINVAL;
+        goto fail;
+    }
+
+    err = backend.ops->frontend_changed(device, state);
+       DBG("change from state %d: %d\n", state, err);
+
+  fail:
+    free(s);
+    return err;
+}
+
+/**
+ * Switches the back-end state of the device by writing to XenStore.
+ *
+ * @param device the VBD
+ * @param state the state to switch to
+ * @returns 0 on success, an error code otherwise
+ */
+static int
+xenio_device_switch_state(xenio_device_t * const device,
+        const XenbusState state)
+{
+    assert(device);
+
+    /*
+     * TODO Ensure @state contains a legitimate XenbusState value.
+     * TODO Should we check for valid state transitions?
+     */
+
+    return xenio_device_printf(device, "state", 0, "%u", state);
+}
+
+/**
+ * Destroys and deallocates the back-end part of a VBD.
+ *
+ * @param device the VBD to destroy
+ */
+static void
+xenio_backend_destroy_device(xenio_device_t * const device)
+{
+    assert(device);
+
+    TAILQ_REMOVE(&backend.devices, device, backend_entry);
+
+    xenio_device_unwatch_frontend_state(device);
+
+    if (device->frontend_path) {
+        free(device->frontend_path);
+        device->frontend_path = NULL;
+    }
+
+    if (device->name) {
+        free(device->name);
+        device->name = NULL;
+    }
+
+    free(device);
+}
+
+/**
+ * TODO Only called by xenio_backend_probe_device, merge into it?
+ */
+static void
+xenio_backend_remove_device(xenio_device_t * const device)
+{
+    backend.ops->remove(device);
+    xenio_backend_destroy_device(device);
+}
+
+/**
+ * Creates a device and adds it to the list of devices.
+ * Initiates a XenStore watch to the blkfront state.
+ *
+ * Creating the device implies initializing the handle and retrieving all the
+ * information of the tapdisk serving this VBD.
+ *
+ * @param domid the ID of the domain where the VBD is created
+ * @param name TODO device name?
+ * @returns 0 on success, an error code otherwise
+ */
+static inline int
+xenio_backend_create_device(const int domid, const char * const name)
+{
+    xenio_device_t *device;
+    int err;
+
+    assert(name);
+
+    DBG("creating device %d/%s\n", domid, name);
+
+    device = calloc(1, sizeof(*device));
+    if (!device) {
+        WARN("error allocating memory\n");
+        err = -errno;
+        goto fail;
+    }
+
+       /*
+        * TODO replace with alloc_serial() and check for overflow
+        */
+    device->serial = backend.serial++;
+    device->domid = domid;
+
+    TAILQ_INSERT_TAIL(&backend.devices, device, backend_entry);
+
+    device->name = strdup(name);
+    if (!device->name) {
+        err = -errno;
+        goto fail;
+    }
+
+    /*
+     * Get the frontend path in XenStore. We need this to talk to blkfront.
+     */
+    device->frontend_path = xenio_device_read(device, "frontend");
+    DBG("frontend = '%s' (%d)\n", device->frontend_path, errno);
+    if (!device->frontend_path) {
+        err = -errno;
+        goto fail;
+    }
+
+    /*
+     * Write to xenstore xenio-serial and max-ring-page-order.
+     */
+    /* FIXME What's xenio-serial? */
+    err = xenio_device_printf(device, "xenio-serial", 0, "%lld",
+            device->serial);
+    if (err)
+        goto fail;
+
+    /*
+     * TODO Who uses this?
+     */
+    if (backend.max_ring_page_order)
+        err = xenio_device_printf(device, "max-ring-page-order", 0, "%d",
+                backend.max_ring_page_order);
+    if (err)
+        goto fail;
+
+    /*
+     * Get the tapdisk that is serving this virtual block device, along with
+     * it's parameters.
+     */
+    err = backend.ops->probe(device, device->domid, name);
+    if (err)
+        goto fail;
+
+       /*
+        * Finally, watch the frontend path in XenStore for changes. Once the
+        * frontend state has changed, we can continue. TODO
+        */
+    err = xenio_device_watch_frontend_state(device);
+    if (err)
+        goto fail;
+
+    return 0;
+
+fail:
+    if (device) {
+        WARN("error creating device: domid=%d name=%s err=%d (%s)\n",
+             device->domid, device->name, err, strerror(-err));
+        xenio_backend_destroy_device(device);
+    }
+
+    return err;
+}
+
+/**
+ * TODO Only called by xenio_backend_device_exists, merge into it?
+ */
+__printf(3, 4)
+static inline bool
+xenio_xs_exists(struct xs_handle * const xs, xs_transaction_t xst,
+        const char * const fmt, ...)
+{
+    va_list ap;
+    char *s;
+
+    va_start(ap, fmt);
+    s = xenio_xs_vread(xs, xst, fmt, ap);
+    va_end(ap);
+    if (s)
+        free(s);
+
+    return s != NULL;
+}
+
+/**
+ * Tells whether the device exists on the domain by looking at xenstore.
+ *
+ * TODO Only called by xenio_backend_probe_device, merge into it?
+ */
+static inline bool
+xenio_backend_device_exists(const int domid, const char * const name)
+{
+    /* i.e. backend/xenio/<domid>/<device name> */
+    return xenio_xs_exists(backend.xs, backend.xst, "%s/%d/%s",
+            XENIO_BACKEND_PATH, domid, name);
+}
+
+/**
+ * Iterates over all devices and returns the one for which the condition is
+ * true.
+ */
+#define xenio_backend_find_device(_device, _cond)              \
+do {                                                                           
                        \
+       xenio_device_t *__next;                                                 
        \
+       int found = 0;                                                          
                \
+       xenio_backend_for_each_device(_device, __next) {        \
+               if (_cond) {                                                    
                \
+                       found = 1;                                              
                        \
+                       break;                                                  
                        \
+               }                                                               
                                \
+       }                                                                       
                                \
+       if (!found)                                                             
                        \
+               _device = NULL;                                                 
                \
+} while (0)
+
+/**
+ * Creates (removes) a device depending on the existence (non-existence) of the
+ * "backend/xenio/@domid/@devname" XenStore path.
+ *
+ * @param domid the ID of the domain where the VBD is created
+ * @param devname device name
+ * @returns 0 on success, an error code otherwise
+ *
+ * TODO Find out what that xenio-serial thing does.
+ */
+static int
+xenio_backend_probe_device(const int domid, const char * const devname)
+{
+    bool exists, create, remove;
+    xenio_device_t *device;
+    int err;
+
+    assert(devname);
+
+    DBG("probe device domid=%d name=%s\n", domid, devname);
+
+    /*
+     * Ask XenStore if the device _should_ exist.
+     */
+    exists = xenio_backend_device_exists(domid, devname);
+
+    /*
+     * Search the device list for this specific device.
+     */
+    xenio_backend_find_device(device,
+            device->domid == domid && !strcmp(device->name, devname));
+
+    /*
+        * If XenStore says that the device exists but it's not in our device 
list,
+     * we must create it. If it's the other way around, this is a removal. If
+     * XenStore says that the device exists and it's in our device list, TODO
+     * we must check serial.
+     */
+    remove = device && !exists;
+    create = exists && !device;
+
+    DBG("exists=%d device=%p remove=%d create=%d\n",
+        exists, device, remove, create);
+
+    if (device && exists) {
+        /*
+         * check the device serial, to sync with fast
+         * remove/re-create cycles.
+         */
+        remove = create = !!xenio_device_check_serial(device);
+
+        if (!create && !remove) {
+            DBG("neither create nor remove\n");
+        }
+    }
+
+       /*
+        * TODO is this possible?
+        */
+       assert(!(create && remove));
+
+    if (remove)
+        xenio_backend_remove_device(device);
+
+    if (create) {
+        err = xenio_backend_create_device(domid, devname);
+        if (err)
+            goto fail;
+    }
+
+    err = 0;
+fail:
+    return err;
+}
+
+/**
+ * TODO What does this function do? Seems like it scans for new devices.
+ *
+ * TODO Only called by xenio_backend_handle_backend_watch. Under which
+ * circumstances?
+ */
+static inline int
+xenio_backend_scan(void)
+{
+    xenio_device_t *device, *next;
+    unsigned int i, j, n, m;
+    char **dir;
+
+    /*
+     * scrap all non-existent devices
+     */
+
+    xenio_backend_for_each_device(device, next)
+        xenio_backend_probe_device(device->domid, device->name);
+
+    /*
+     * probe the new ones
+     */
+
+    dir = xs_directory(backend.xs, backend.xst, XENIO_BACKEND_PATH, &n);
+    if (!dir)
+        return 0;
+
+    /*
+     * TODO Looks like we're checking the XenStore paths of ALL domains, could
+     * there be a performance issue in the presence of many VMs?
+     */
+    for (i = 0; i < n; i++) {
+        char *path, **sub, *end;
+        int domid;
+
+        /*
+         * Get the domain ID.
+         */
+        domid = strtoul(dir[i], &end, 0);
+        if (*end != 0 || end == dir[i])
+            continue;
+
+        /*
+         * Read the devices of this domain.
+         */
+        path = mprintf("%s/%d", XENIO_BACKEND_PATH, domid);
+        assert(path != NULL);
+        sub = xs_directory(backend.xs, backend.xst, path, &m);
+        free(path);
+
+        /*
+         * Probe each device.
+         */
+        for (j = 0; j < m; j++)
+            xenio_backend_probe_device(domid, sub[j]);
+
+        free(sub);
+    }
+
+    free(dir);
+    return 0;
+}
+
+/**
+ * Act in response to a change in the front-end XenStore path.
+ *
+ * @param path the front-end's XenStore path that changed
+ * @returns 0 on success, an error code otherwise
+ *
+ * TODO Does blkfront write to this path?
+ *
+ * TODO Only called by xenio_backend_read_watch
+ */
+static inline int
+xenio_backend_handle_otherend_watch(const char * const path)
+{
+    xenio_device_t *device;
+    int err = 0;
+
+    assert(path);
+
+    /*
+     * Find the device that has the same frontend state path.
+     *
+     * TODO It seems that there should definitely be such a device in our list,
+     * otherwise this function would not have executed at all, since we would
+     * not be waiting on that xenstore path.  The xenstore patch we wait for
+     * is: /local/domain/<domid>/device/vbd/<devname>/state. In order to watch
+     * this path, it means that we have received a device create request, so
+     * the device will be there
+     */
+    xenio_backend_find_device(device,
+            !strcmp(device->frontend_state_path, path));
+    if (device) {
+        DBG("device: domid=%d name=%s\n", device->domid, device->name);
+        err = xenio_device_check_frontend_state(device);
+    } else {
+        WARN("XXX no device found!\n");
+        BUG();
+    }
+
+    return err;
+}
+
+/**
+ * Act in response to a change in the back-end XenStore path.
+ *
+ * If the path is "/backend" or "/backend/xenio", all devices are probed.
+ * Otherwise, the path should be "backend/xenio/<domid>/<device name>"
+ * (i.e. backend/xenio/1/51712), and in this case this specific device is
+ * probed.
+ *
+ * @param path the back-end's XenStore path that changed
+ * @returns 0 on success, an error code otherwise
+ *
+ * TODO Who modifies the back-end path in XenStore?
+ *
+ * TODO Only called by xenio_backend_read_watch.
+ */
+static inline int
+xenio_backend_handle_backend_watch(char * const path)
+{
+    char *s, *end, *name;
+    int domid;
+
+    assert(path);
+
+    /*
+        * ignore the backend/xenio part
+        */
+    s = strtok(path, "/");
+    assert(!strcmp(s, "backend"));
+    s = strtok(NULL, "/");
+    if (!s)
+        return xenio_backend_scan();
+
+    assert(!strcmp(s, XENIO_BACKEND_NAME));
+    s = strtok(NULL, "/");
+    if (!s)
+        return xenio_backend_scan();
+
+    /*
+        * get the domain ID
+        */
+    domid = strtoul(s, &end, 0);
+    if (*end != 0 || end == s)
+        return -EINVAL;
+
+    /*
+        * get the device name
+        */
+    name = strtok(NULL, "/");
+    if (!name)
+        return xenio_backend_scan();
+       else
+               /*
+                * Create or remove the device, TODO or check it's serial.
+                */
+               return xenio_backend_probe_device(domid, name);
+}
+
+/**
+ * Read changes that occurred on the "backend/xenio" XenStore path and act.
+ * Either the back-end path or the front-end path will have changed.
+ */
+static inline void
+xenio_backend_read_watch(void)
+{
+    char **watch, *path, *token;
+    unsigned int n;
+    int err, _abort;
+
+    /* read the change */
+    watch = xs_read_watch(backend.xs, &n);
+    path = watch[XS_WATCH_PATH];
+    token = watch[XS_WATCH_TOKEN];
+
+    DBG("--\n");
+    DBG("path=%s token=%s\n", path, token);
+
+    /*
+     * TODO Put the body of "again:" into a function instead of using goto.
+     */
+again:
+    backend.xst = xs_transaction_start(backend.xs);
+    if (!backend.xst) {
+        WARN("error starting transaction\n");
+        goto fail;
+    }
+
+    /*
+     * Token is either "otherend-state", indicating a change in the front-end,
+        * or "backend-xenio", indicating a change in the back-end.
+     * TODO Who sets the token?
+     */
+    switch (token[0]) {
+
+    case 'o':
+        if (!strcmp(token, "otherend-state")) {
+            err = xenio_backend_handle_otherend_watch(path);
+            break;
+        }
+        /* TODO gracefully fail? */
+        BUG();
+
+    case 'b':
+        /*
+         * TODO verify the following:
+         *
+         * When blkfront (TODO or libxl?) requests a new VBD, the path
+         * '/<domid>/<devname>' is appended to 'backend/xenio'. In response,
+         * xenio creates the VBD handle and initializes it (i.e. it finds which
+         * tapdisk serves this VBD).)
+         *
+         * TODO The XenStore watch may trigger for a not yet discovered reason.
+         * The result is xenio_backend_handle_backend_watch not doing anything
+         * interesting, it only checks the 'xenio-serial'.
+         */
+        if (!strcmp(token, XENIO_BACKEND_TOKEN)) {
+            err = xenio_backend_handle_backend_watch(path);
+            break;
+        }
+        /* TODO gracefully fail? */
+        BUG();
+
+    default:
+        /* TODO gracefully fail? */
+        BUG();
+    }
+
+    _abort = !!err;
+    if (_abort)
+        DBG("aborting transaction: %s\n", strerror(-err));
+
+    err = xs_transaction_end(backend.xs, backend.xst, _abort);
+    backend.xst = 0;
+    if (!err) {
+        err = -errno;
+        /*
+         * This is OK according to xs_transaction_end's semantics.
+         */
+        if (errno == EAGAIN) {
+            goto again;
+        }
+        DBG("xs_transaction_end failed: %s\n", strerror(err));
+    }
+
+fail:
+    if (watch)
+        free(watch);
+    return;
+}
+
+static void
+xenio_backend_destroy(void)
+{
+    if (backend.xs) {
+        xs_daemon_close(backend.xs);
+        backend.xs = NULL;
+    }
+}
+
+/**
+ * Initializes the back-end descriptor. There is one back-end per xenio
+ * process. Also, it initiates a watch to XenStore on back-end/xenio.
+ *
+ * @param ops back-end operations, see struct xenio_backend_ops
+ * @param max_ring_page_order TODO
+ * @returns 0 on success, an error code otherwise
+ *
+ * TODO What's the use of the token? Aren't we the only ones to watch this
+ * path? It would only make sense if we want to run multiple xenio daemons.
+ */
+static inline int
+xenio_backend_create(const struct xenio_backend_ops * const ops,
+        const int max_ring_page_order)
+{
+    bool nerr;
+    int err = -EINVAL;
+
+    if (!ops) {
+        WARN("no backend operations\n");
+        goto fail;
+    }
+
+    backend.max_ring_page_order = max_ring_page_order;
+    TAILQ_INIT(&backend.devices);
+    backend.xst = XBT_NULL;
+
+    backend.xs = xs_daemon_open();
+    if (!backend.xs) {
+        err = -EINVAL;
+        goto fail;
+    }
+
+    /*
+     * Set a watch on the back-end path using a token.
+     *
+     * TODO Do we really need to supply a token, given that this is the _only_
+     * watch on this specific path by this process?
+     */
+    nerr = xs_watch(backend.xs, XENIO_BACKEND_PATH, XENIO_BACKEND_TOKEN);
+    if (!nerr) {
+        err = -errno;
+        goto fail;
+    }
+
+    backend.ops = ops;
+
+    return 0;
+
+fail:
+       xenio_backend_destroy();
+
+    return -err;
+}
+
+/**
+ * Retrieves the tapdisk designated to serve this device, storing this
+ * information in the supplied VBD handle.
+ *
+ * @param bdev the VBD handle for whose tapdisk handle should be retrieved
+ * @returns 0 on success, an error code otherwise
+ *
+ * FIXME How does this thing work since bdev->dev is never initialised?
+ *
+ * TODO Only called by blkback_probe, merge into it?
+ */
+static inline int
+blkback_find_tapdisk(blkback_device_t * const bdev)
+{
+    struct tqh_tap_list list;
+    tap_list_t *tap;
+    int err = 0;
+    int found = 0;
+
+    assert(bdev);
+
+    err = tap_ctl_list(&list);
+    if (err) {
+        WARN("error listing tapdisks: %s\n", strerror(err));
+        return err;
+    }
+
+    if (!TAILQ_EMPTY(&list)) {
+        tap_list_for_each_entry(tap, &list)
+            if (tap->minor == minor(bdev->dev)) {
+                found = 1;
+                break;
+            }
+    } else {
+        WARN("no tapdisks\n");
+    }
+
+    /*
+     * FIXME Shouldn't we free the list? Memory leak!?
+     */
+
+    if (!found)
+        return -ENOENT;
+
+    memcpy(&bdev->tap, tap, sizeof(bdev->tap));
+
+    return 0;
+}
+
+/**
+ * TODO Only called by blkback_connect_tap, merge into it?
+ */
+static inline int
+blkback_read_otherend_proto(xenio_device_t * const xbdev)
+{
+    char *s;
+
+    assert(xbdev);
+
+    s = xenio_device_read_otherend(xbdev, "protocol");
+    if (!s)
+        return XENIO_BLKIF_PROTO_NATIVE;
+
+    switch (s[0]) {
+    case 'x':
+        if (!strcmp(s, "x86_32-abi"))
+            return XENIO_BLKIF_PROTO_X86_32;
+
+        if (!strcmp(s, "x86_64-abi"))
+            return XENIO_BLKIF_PROTO_X86_64;
+    }
+
+    free(s);
+    return -EINVAL;
+}
+
+/**
+ * Core functions that instructs the tapdisk to connect to the shared ring and
+ * communicates essential information to blkfront.
+ * TODO Elaborate more.
+ * TODO Verify that the XenStore values written in the "write_info:" section
+ * are for blkfront. If this is true, rename the function as its name is
+ * misleading.
+ *
+ * @param xbdev the VBD to which the tapdisk should connect
+ * @returns 0 on success, an error code otherwise
+ *
+ * TODO Only called by blkback_frontend_changed.
+ */
+static inline int
+blkback_connect_tap(xenio_device_t * const xbdev)
+{
+    blkback_device_t *bdev;
+
+    /*
+     * The event channel port to receive/provide notifications, dictated by
+     * blkfront.
+     */
+    evtchn_port_t port;
+
+    grant_ref_t *gref = NULL;
+    int n, proto, err = 0;
+    int order;
+    char *pool;
+    char ring_ref[12]; /* TODO Why 12? */
+
+    assert(xbdev);
+
+    bdev = xbdev->bdev;
+
+    /*
+     * TODO Use a clearer way to indicate whether a tapdisk is already
+     * connected?
+     */
+    if (bdev->gref) {
+        /*
+         * TODO Why should this function be called on an already connected
+         * VBD? Why re-write the sector size etc. in XenStore for an already
+         * connected VBD?
+         */
+        DBG("blkback already connected to tapdisk.\n");
+        goto write_info;
+    }
+
+    n = xenio_device_scanf_otherend(xbdev, "ring-page-order", "%d", &order);
+    if (n != 1)
+        order = 0;
+
+    gref = calloc(ORDER_TO_PAGES(order), sizeof(grant_ref_t));
+    if (!gref) {
+        DBG("Failed to allocate memory for grant refs.\n");
+        err = ENOMEM;
+        goto fail;
+    }
+
+    /*
+     * FIXME Figure out what the body of this if-else does.
+     */
+    if (order) {
+        int i;
+        for (i = 0; i < (1 << order); i++) {
+            sprintf(ring_ref, "ring-ref%d", i);
+            n = xenio_device_scanf_otherend(xbdev, ring_ref, "%u", &gref[i]);
+            if (n != 1) {
+                DBG("Failed to read grant ref #%d.\n", i);
+                err = ENOENT;
+                goto fail;
+            }
+            DBG("%s = %d\n", ring_ref, gref[i]);
+        }
+    } else {
+        /*
+         * TODO Does this mean there's only one page in the ring?
+         */
+        n = xenio_device_scanf_otherend(xbdev, "ring-ref", "%u", &gref[0]);
+        if (n != 1) {
+            DBG("Failed to read grant ref.\n");
+            err = ENOENT;
+            goto fail;
+        }
+
+        DBG("ring-ref = %d\n", gref[0]);
+    }
+
+    /*
+     * Retrieve the event channel, dictated by blkfront.
+     */
+    n = xenio_device_scanf_otherend(xbdev, "event-channel", "%u", &port);
+    if (n != 1) {
+        DBG("Failed to read event channel.\n");
+        err = ENOENT;
+        goto fail;
+    }
+
+    /*
+     * Read the guest VM's ABI.
+     */
+    proto = blkback_read_otherend_proto(xbdev);
+    if (proto < 0) {
+        DBG("Failed to read protocol.\n");
+        err = ENOENT;
+        goto fail;
+    }
+
+    /*
+     * TODO What's this? Who writes this?
+     */
+    pool = xenio_device_read(xbdev, "sm-data/frame-pool");
+
+    DBG("connecting vbd-%d-%d (order %d, evt %d, proto %d, pool %s)"
+        " to tapdisk %d minor %d\n",
+        bdev->domid, bdev->devid, order, port, proto, pool, bdev->tap.pid,
+        bdev->tap.minor);
+
+    /*
+     * Instruct the tapdisk to connect to the shared ring.
+     */
+    err = tap_ctl_connect_xenblkif(bdev->tap.pid, bdev->tap.minor, bdev->domid,
+            bdev->devid, gref, order, port, proto, pool);
+    DBG("err=%d errno=%d\n", err, errno);
+    if (err)
+        goto fail;
+
+    bdev->gref = gref;
+    gref = NULL;
+    bdev->port = port;
+
+    /*
+     * TODO This part is not implied by the function's name. Even if we rename
+     * this function, we should put the following section in a separate
+     * (static) function for code clarity and maintainability purpose.
+     */
+write_info:
+
+    /*
+     * TODO Write the sector size to XenStore so that blkfront knows
+     * the sector size of the block device it will create?
+     */
+    err = xenio_device_printf(xbdev, "sector-size", 1, "%u",
+            bdev->sector_size);
+    if (err) {
+        DBG("Failed to write sector-size.\n");
+        goto fail;
+    }
+
+    /*
+     * Write the number of sectors, sector size, and info to the back-end path
+     * in XenStore so that blkfront creates a VBD with the appropriate
+     * characteristics.
+     */
+    err = xenio_device_printf(xbdev, "sectors", 1, "%llu", bdev->sectors);
+    if (err) {
+        DBG("Failed to write sectors.\n");
+        goto fail;
+    }
+
+    err = xenio_device_printf(xbdev, "info", 1, "%u", bdev->info);
+    if (err) {
+        DBG("Failed to write info.\n");
+        goto fail;
+    }
+
+    err = xenio_device_switch_state(xbdev, XenbusStateConnected);
+    if (err) {
+        DBG("Failed to switch state %d\n", err);
+    }
+
+fail:
+    if (err) {
+        if (bdev->gref) {
+            tap_ctl_disconnect_xenblkif(bdev->tap.pid, bdev->tap.minor,
+                    bdev->domid, bdev->devid, NULL);
+
+            gref = bdev->gref;
+            bdev->gref = NULL;
+        }
+    }
+
+    if (gref)
+        free(gref);
+
+    return err;
+}
+
+/**
+ * Instructs the tapdisk to disconnect itself from the shared ring.
+ * TODO It also switches state, which isn't implied by the function's name,
+ * rename function?
+ *
+ * @param xbdev the VBD whose tapdisk should be disconnected
+ * @returns 0 on success, an error code otherwise
+ *
+ * TODO Only called by blkback_frontend_changed.
+ */
+static inline int
+blkback_disconnect_tap(xenio_device_t * const xbdev)
+{
+    blkback_device_t *bdev;
+    int err = 0;
+
+    assert(xbdev);
+
+    bdev = xbdev->bdev;
+    assert(bdev);
+
+    if (bdev->gref == NULL || bdev->port < 0)
+        return err;
+
+    DBG("disconnecting vbd-%d-%d from tapdisk %d minor %d\n",
+        bdev->domid, bdev->devid, bdev->tap.pid, bdev->tap.minor);
+
+    err = tap_ctl_disconnect_xenblkif(bdev->tap.pid, bdev->tap.minor,
+            bdev->domid, bdev->devid, NULL);
+    if (err && errno != -ESRCH)
+        goto fail;
+
+    free(bdev->gref);
+    bdev->gref = NULL;
+    bdev->port = -1;
+
+    err = xenio_device_switch_state(xbdev, XenbusStateClosed);
+fail:
+    return err;
+}
+
+/**
+ * Retrieves the VBD parameters from the tapdisk and initialises the device
+ * handle with them.
+ *
+ * @param xbdev the VBD whose tapdisk parameters should be retrieved
+ * @returns 0 on success, an error code otherwise
+ *
+ * TODO aren't these supplied by blkback_find_tapdisk/tap_ctl_list?
+ * TODO only called by blkback_probe.
+ * TODO Very misleading function name
+ */
+static inline int
+blkback_probe_device(xenio_device_t * const xbdev)
+{
+    blkback_device_t *bdev;
+    int err;
+    unsigned int info;
+
+    assert(xbdev);
+
+    bdev = xbdev->bdev;
+    assert(bdev);
+
+    /*
+     * TODO info is unused, maybe it's the info that should be set in
+     * blkback_device?
+     */
+    err = tap_ctl_info(bdev->tap.pid, bdev->tap.minor, &bdev->sectors,
+            &bdev->sector_size, &info);
+    if (err)
+        WARN("tap_ctl_info failed %d\n", err);
+    else
+        DBG("sectors=%llu sector-size=%d\n", bdev->sectors,
+            bdev->sector_size);
+
+    return err;
+}
+
+/**
+ * Deallocates a struct blkback_device.
+ */
+static void
+blkback_device_destroy(blkback_device_t * const bdev)
+{
+       assert(bdev);
+
+    if (bdev->gref)
+        free(bdev->gref);
+    free(bdev);
+}
+
+/**
+ * Initialises the VBD with information regarding the tapdisk that is serving
+ * it.
+ *
+ * @param xbdev the device to initialise
+ * @param domid the ID of the domain where the VBD is created
+ * @param name TODO the device name (as retrieved from blkfront via XenStore)
+ * @returns 0 on success, an error code otherwise
+ *
+ * TODO The function name is very misleading.
+ */
+static int
+blkback_probe(xenio_device_t * const xbdev, const domid_t domid,
+        const char * const name)
+{
+    blkback_device_t *bdev;
+    int err;
+    char *end;
+
+    assert(xbdev);
+    assert(name);
+
+    DBG("probe blkback %s-%d-%s\n", XENIO_BACKEND_NAME, xbdev->domid,
+        xbdev->name);
+
+    bdev = calloc(1, sizeof(*bdev));
+    if (!bdev) {
+        WARN("error allocating memory\n");
+        err = -errno;
+        goto fail;
+    }
+    xbdev->bdev = bdev;
+
+    bdev->domid = domid;
+    bdev->devid = strtoul(name, &end, 0);
+    if (*end != 0 || end == name) {
+        err = -EINVAL;
+        goto fail;
+    }
+
+    DBG("devid=%d\n", bdev->devid);
+    err = blkback_find_tapdisk(bdev);
+    if (err) {
+        WARN("error looking for tapdisk: %s", strerror(err));
+        goto fail;
+    }
+
+    /*
+     * get the VBD parameters from the tapdisk
+     */
+    blkback_probe_device(xbdev);
+    DBG("got %s-%d-%d with tapdev %d/%d\n", XENIO_BACKEND_NAME,
+        xbdev->domid, bdev->devid, bdev->tap.pid, bdev->tap.minor);
+
+    return 0;
+
+fail:
+    if (bdev)
+        blkback_device_destroy(bdev);
+
+    return err;
+}
+
+/**
+ * Deallocates the VBD.
+ *
+ * TODO misleading function name
+ */
+static void
+blkback_remove(xenio_device_t * const xbdev)
+{
+    blkback_device_t *bdev;
+
+    assert(xbdev);
+    bdev = xbdev->bdev;
+
+    DBG("remove %s-%d-%s\n", XENIO_BACKEND_NAME, xbdev->domid, xbdev->name);
+
+    blkback_device_destroy(bdev);
+}
+
+/**
+ * Acts on changes in the front-end state.
+ *
+ * @param xbdev the VBD whose front-end state changed
+ * @param state the new state
+ * @returns 0 on success, an error code otherwise
+ *
+ * TODO Only called by xenio_device_check_front-end_state.
+ * TODO Ensure that @state is a valid XenbusState.
+ * TODO Replace the switch statement with a despatch table.
+ * TODO Functions called here in response to a state change do not have
+ * consistent names with each other (e.g. xenio_device_switch_state vs.
+ * blkback_connect_tap).
+ */
+static inline int
+blkback_frontend_changed(xenio_device_t * const xbdev, const XenbusState state)
+{
+    int err = 0;
+
+    assert(xbdev);
+
+    DBG("frontend_changed %s-%d-%s state=%d\n", XENIO_BACKEND_NAME,
+            xbdev->domid, xbdev->name, state);
+
+    /*
+     * TODO replace with a despatch table
+     */
+    switch (state) {
+    case XenbusStateUnknown:
+        /* TODO wtf */
+        break;
+
+        /* 
+         * Switch our state from Initialising (1) to InitWait (2). Absolutely
+         * nothing else to do.
+         */
+    case XenbusStateInitialising:
+        err = xenio_device_switch_state(xbdev, XenbusStateInitWait);
+        break;
+
+        /*
+         * XXX 
+         */
+    case XenbusStateInitialised:
+    case XenbusStateConnected:
+        err = blkback_connect_tap(xbdev);
+        break;
+
+    case XenbusStateClosing:
+        err = xenio_device_switch_state(xbdev, XenbusStateClosing);
+        break;
+
+    case XenbusStateClosed:
+        err = blkback_disconnect_tap(xbdev);
+        break;
+
+    case XenbusStateReconfiguring:
+    case XenbusStateReconfigured:
+        /* wtf */
+        break;
+
+    case XenbusStateInitWait:
+        /* fatal */
+        break;
+    }
+
+    return err;
+}
+
+/**
+ * FIXME are we using any other operations?
+ */
+static struct xenio_backend_ops blkback_ops = {
+    .probe = blkback_probe,
+    .remove = blkback_remove,
+    .frontend_changed = blkback_frontend_changed,
+};
+
+/**
+ * Runs the daemon.
+ *
+ * It watches XenStore (backend/xenio) for changes and when it detects one, it
+ * creates or removes a VBD.
+ */
+static inline int
+xenio_backend_run(void)
+{
+    const int fd = xs_fileno(backend.xs);
+       int err;
+
+    do {
+        fd_set rfds;
+        int nfds;
+
+        FD_ZERO(&rfds);
+        FD_SET(fd, &rfds);
+
+        /* poll the fd for changes in the XenStore path we're interested in */
+        nfds = select(fd + 1, &rfds, NULL, NULL, NULL);
+        if (nfds < 0) {
+            perror("select");
+            err = -errno;
+            break;
+        }
+
+        if (FD_ISSET(fd, &rfds))
+            xenio_backend_read_watch();
+    } while (1);
+
+    return err;
+}
+
+static char *blkback_ident;
+
+static void
+blkback_vlog_fprintf(const int prio, const char * const fmt, va_list ap)
+{
+    const char *strprio[] = {
+        [LOG_DEBUG] = "DBG",
+        [LOG_INFO] = "INF",
+        [LOG_WARNING] = "WRN"
+    };
+
+    BUG_ON(prio < 0);
+    BUG_ON(prio > sizeof(strprio) / sizeof(strprio[0]));
+    BUG_ON(!strprio[prio]);
+
+    fprintf(stderr, "%s[%s] ", blkback_ident, strprio[prio]);
+    vfprintf(stderr, fmt, ap);
+}
+
+/**
+ * Print xenio's usage instructions.
+ */
+static void
+usage(FILE * const stream, const char * const prog)
+{
+    fprintf(stream,
+            "usage: %s\n"
+            "\t[-m|--max-ring-order <max ring page order>]\n"
+            "\t[-D|--debug]\n"
+                       "\t[-h|--help]\n", prog);
+}
+
+int main(int argc, char **argv)
+{
+    const char *prog;
+    int opt_debug, opt_max_ring_page_order;
+    int err;
+
+    prog = basename(argv[0]);
+
+    opt_debug = 0;
+    opt_max_ring_page_order = 0;
+
+    do {
+        const struct option longopts[] = {
+            {"help", 0, NULL, 'h'},
+            {"max-ring-order", 1, NULL, 'm'},
+            {"debug", 0, NULL, 'D'},
+        };
+        int c;
+
+        c = getopt_long(argc, argv, "hm:n:D", longopts, NULL);
+        if (c < 0)
+            break;
+
+        switch (c) {
+        case 'h':
+            usage(stdout, prog);
+            return 0;
+        case 'm':
+            opt_max_ring_page_order = atoi(optarg);
+            if (opt_max_ring_page_order < 0)
+                goto usage;
+            break;
+        case 'D':
+            opt_debug = 1;
+            break;
+        case '?':
+            goto usage;
+        }
+    } while (1);
+
+    blkback_ident = XENIO_BACKEND_TOKEN;
+    if (opt_debug)
+        xenio_vlog = blkback_vlog_fprintf;
+    else
+        openlog(blkback_ident, 0, LOG_DAEMON);
+
+    if (!opt_debug) {
+        err = daemon(0, 0);
+        if (err) {
+            err = -errno;
+            goto fail;
+        }
+    }
+
+       if ((err = xenio_backend_create(&blkback_ops, 
opt_max_ring_page_order))) {
+        WARN("error creating blkback: %s\n", strerror(err));
+        goto fail;
+    }
+
+    err = xenio_backend_run();
+
+    xenio_backend_destroy();
+
+fail:
+    return err ? -err : 0;
+
+usage:
+    usage(stderr, prog);
+    return 1;
+}

_______________________________________________
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®.