Exemplo n.º 1
0
int
tapback_device_scanf_otherend(vbd_t * const device, xs_transaction_t xst,
        const char * const path, const char * const fmt, ...)
{
    va_list ap;
    int n = 0;
    char *s = NULL;

    ASSERT(device);
    ASSERT(path);

    if (!(s = tapback_device_read_otherend(device, xst, path)))
        return -1;
    va_start(ap, fmt);
    n = vsscanf(s, fmt, ap);
    free(s);
    va_end(ap);

    return n;
}
Exemplo n.º 2
0
/**
 * Core functions that instructs the tapdisk to connect to the shared ring (if
 * not already connected).
 *
 * If the tapdisk is not already connected, all the necessary information is
 * read from XenStore and the tapdisk gets connected using this information.
 * This function is idempotent: if the tapback daemon gets restarted this
 * function will be called again but it won't really do anything.
 *
 * @param device the VBD the tapdisk should connect to
 * @returns (a) 0 on success, (b) ESRCH if the tapdisk is not available, and
 * (c) an error code otherwise
 */
static inline int
connect_tap(vbd_t * const device)
{
    evtchn_port_t port = 0;
    grant_ref_t *gref = NULL;
    int err = 0;
    char *proto_str = NULL;
    char *persistent_grants_str = NULL;
    int nr_pages = 0, proto = 0, order = 0;
    bool persistent_grants = false;

    ASSERT(device);

    /*
     * FIXME disconnect if already connected and then reconnect, this is how
     * blkback does.
     * FIXME If we're already connected, why did we end up here in the first
     * place?
     */
    ASSERT(!device->connected);

    /*
     * The physical-device XenStore key has not been written yet.
     */
    if (!device->tap) {
        DBG(device, "no tapdisk yet\n");
        err = ESRCH;
        goto out;
    }
    /*
     * TODO How can we make sure we're not missing a node written by the
     * front-end? Use xs_directory?
     */

    if (1 != tapback_device_scanf_otherend(device, XBT_NULL, RING_PAGE_ORDER,
                "%d", &order))
        order = 0;

     nr_pages = 1 << order;

    if (!(gref = calloc(nr_pages, sizeof(grant_ref_t)))) {
        WARN(device, "failed to allocate memory for grant refs.\n");
        err = ENOMEM;
        goto out;
    }

    /*
     * Read the grant references.
     */
    if (order) {
        int i = 0;
        /*
         * +10 is for INT_MAX, +1 for NULL termination
         */

        /*
         * TODO include domid/devid in the error messages
         */
        static const size_t len = sizeof(RING_REF) + 10 + 1;
        char ring_ref[len];
        for (i = 0; i < nr_pages; i++) {
            if (snprintf(ring_ref, len, "%s%d", RING_REF, i) >= (int)len) {
                DBG(device, "error printing to buffer\n");
                err = EINVAL;
                goto out;
            }
            if (1 != tapback_device_scanf_otherend(device, XBT_NULL, ring_ref,
                        "%u", &gref[i])) {
                WARN(device, "failed to read grant ref 0x%x\n", i);
                err = ENOENT;
                goto out;
            }
        }
    } else {
        if (1 != tapback_device_scanf_otherend(device, XBT_NULL, RING_REF,
                    "%u", &gref[0])) {
            WARN(device, "failed to read grant ref\n");
            err = ENOENT;
            goto out;
        }
    }

    /*
     * Read the event channel.
     */
    if (1 != tapback_device_scanf_otherend(device, XBT_NULL, EVENT_CHANNEL,
                "%u", &port)) {
        WARN(device, "failed to read event channel\n");
        err = ENOENT;
        goto out;
    }

    /*
     * Read the guest VM's ABI.
     */
    if (!(proto_str = tapback_device_read_otherend(device, XBT_NULL, PROTO)))
        proto = BLKIF_PROTOCOL_X86_32;
    else if (!strcmp(proto_str, XEN_IO_PROTO_ABI_NATIVE))
        proto = BLKIF_PROTOCOL_NATIVE;
    else if (!strcmp(proto_str, XEN_IO_PROTO_ABI_X86_32))
        proto = BLKIF_PROTOCOL_X86_32;
    else if (!strcmp(proto_str, XEN_IO_PROTO_ABI_X86_64))
        proto = BLKIF_PROTOCOL_X86_64;
    else {
        WARN(device, "unsupported protocol %s\n", proto_str);
        err = EINVAL;
        goto out;
    }

    DBG(device, "protocol=%d\n", proto);

    /*
     * Does the front-end support persistent grants?
     */
    persistent_grants_str = tapback_device_read_otherend(device, XBT_NULL,
            FEAT_PERSIST);
    if (persistent_grants_str) {
        if (!strcmp(persistent_grants_str, "0"))
            persistent_grants = false;
        else if (!strcmp(persistent_grants_str, "1"))
            persistent_grants = true;
        else {
            WARN(device, "invalid %s value: %s\n", FEAT_PERSIST,
                    persistent_grants_str);
            err = EINVAL;
            goto out;
        }
    }
    else
        DBG(device, "front-end doesn't support persistent grants\n");

    /*
     * persistent grants are not yet supported
     */
    if (persistent_grants)
        WARN(device, "front-end supports persistent grants but we don't\n");

    /*
     * Create the shared ring and ask the tapdisk to connect to it.
     */
    if ((err = -tap_ctl_connect_xenblkif(device->tap->pid, device->domid,
                    device->devid, gref, order, port, proto, NULL,
                    device->minor))) {
        /*
         * This happens if the tapback dameon gets restarted while there are
         * active VBDs.
         */
        if (err == EALREADY) {
            INFO(device, "tapdisk[%d] minor=%d already connected to the "
					"shared ring\n", device->tap->pid, device->tap->minor);
            err = 0;
        } else {
            WARN(device, "tapdisk[%d] failed to connect to the shared "
                    "ring: %s\n", device->tap->pid, strerror(-err));
            goto out;
        }
    }

    device->connected = true;

    DBG(device, "tapdisk[%d] connected to shared ring\n", device->tap->pid);

out:
    if (err && device->connected) {
        const int err2 = -tap_ctl_disconnect_xenblkif(device->tap->pid,
                device->domid, device->devid, NULL);
        if (err2) {
            WARN(device, "error disconnecting tapdisk[%d] from the shared "
                    "ring (error ignored): %s\n", device->tap->pid,
                    strerror(err2));
        }

        device->connected = false;
    }

    free(gref);
    free(proto_str);
    free(persistent_grants_str);

    return err;
}