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; }
/** * 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; }