[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 7/9] vdevice tool for manipulating vdevice bus
Subject: vdevice tool for manipulating vdevice bus This creates a simple C tool for list, adding and removing vdevices. This is a demonstration (although, in my opinion, and important one, because it shows how simple this is). This functionality should also be added to the python tools. diff -r 570d423310b5 .hgignore --- a/.hgignore Fri Jun 2 07:05:28 2006 +++ b/.hgignore Mon Jun 5 14:26:28 2006 @@ -146,6 +146,7 @@ ^tools/security/secpol_tool$ ^tools/security/xen/.*$ ^tools/tests/test_x86_emulator$ +^tools/vdevice/vdevice$ ^tools/vnet/gc$ ^tools/vnet/gc.*/.*$ ^tools/vnet/vnet-module/.*\.ko$ diff -r 570d423310b5 tools/Makefile --- a/tools/Makefile Fri Jun 2 07:05:28 2006 +++ b/tools/Makefile Mon Jun 5 14:26:28 2006 @@ -13,6 +13,7 @@ SUBDIRS += console SUBDIRS += xenmon SUBDIRS += guest-headers +SUBDIRS += vdevice ifeq ($(VTPM_TOOLS),y) SUBDIRS += vtpm_manager SUBDIRS += vtpm diff -r 570d423310b5 tools/vdevice/Makefile --- /dev/null Fri Jun 2 07:05:28 2006 +++ b/tools/vdevice/Makefile Mon Jun 5 14:26:28 2006 @@ -0,0 +1,34 @@ +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +INSTALL = install +INSTALL_DATA = $(INSTALL) -m0644 +INSTALL_PROG = $(INSTALL) -m0755 +INSTALL_DIR = $(INSTALL) -d -m0755 + +PROFILE=#-pg +BASECFLAGS=-Wall -g -Werror +# Make gcc generate dependencies. +BASECFLAGS += -Wp,-MD,.$(@F).d +PROG_DEP = .*.d +BASECFLAGS+= -O3 $(PROFILE) +BASECFLAGS+= -I$(XEN_ROOT)/tools/libxc +BASECFLAGS+= -I$(XEN_ROOT)/tools/xenstore +BASECFLAGS+= -I. + +CFLAGS += $(BASECFLAGS) +LDFLAGS += $(PROFILE) -L$(XEN_LIBXC) -L$(XEN_XENSTORE) + +all: vdevice + +clean: + rm -f vdevice *.o .*.d + +vdevice: vdevice.o + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -lxenctrl -lxenstore -o $@ + +install: vdevice + $(INSTALL_DIR) -p $(DESTDIR)/usr/sbin + $(INSTALL_PROG) vdevice $(DESTDIR)/usr/sbin + +-include $(PROG_DEP) diff -r 570d423310b5 tools/vdevice/vdevice.c --- /dev/null Fri Jun 2 07:05:28 2006 +++ b/tools/vdevice/vdevice.c Mon Jun 5 14:26:28 2006 @@ -0,0 +1,571 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <err.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdint.h> +#include <stdbool.h> +#include <assert.h> +#include <errno.h> +#include <stdint.h> +#include <sys/ioctl.h> +#include <net/ethernet.h> + +#include <xs.h> + +#include <xen/xen.h> +#include <xen/share.h> +#include <xen/linux/xenshare.h> +#include <xen/event_channel.h> +#include <xen/linux/privcmd.h> +#include <xen/io/vdevice.h> + +#include <xc_private.h> + +#define PROGRAM_NAME "vdevice" + +static int xc_fd; + +#define __unused __attribute__((unused)) + +/* FIXME: Move to xenctrl library */ +static int HYPERVISOR_share(int cmd, int arg1, int arg2, int arg3, int arg4) +{ + privcmd_hypercall_t privcmd; + + privcmd.op = __HYPERVISOR_share_op; + privcmd.arg[0] = cmd; + privcmd.arg[1] = arg1; + privcmd.arg[2] = arg2; + privcmd.arg[3] = arg3; + privcmd.arg[4] = arg4; + + return do_xen_hypercall(xc_fd, &privcmd); +} + +/* FIXME: Move to xenctrl library */ +static int add_grant(share_ref_t ref, domid_t dom) +{ + dom0_op_t op = { .cmd = DOM0_GRANTSHAREDPAGES, + .interface_version = DOM0_INTERFACE_VERSION, + .u.grantsharedpages.share_ref = ref, + .u.grantsharedpages.domain = dom }; + + /* FIXME: Skip domain 0 as it will always have access */ + if (dom == 0) + return 0; + + return do_dom0_op(xc_fd, &op); +} + +static void *map_pages(share_ref_t share_ref, unsigned int num_pages, + unsigned int *peer_id) +{ + struct xenshare_get_share shareget; + int shareiofd, ret; + void *sharepage; + + shareiofd = open("/dev/xenshare", O_RDWR); + if (shareiofd < 0) + err(1, "Could not open '%s'", "/dev/xenshare"); + + shareget.share_ref = share_ref; + shareget.num_pages = num_pages; + ret = ioctl(shareiofd, IOCTL_XENSHARE_GET_SHARE, &shareget); + if (ret < 0) + err(1, "Getting shared pages gave %i", ret); + *peer_id = ret; + + /* Map shared page */ + sharepage = mmap(NULL, num_pages*getpagesize(), PROT_READ|PROT_WRITE, + MAP_SHARED, shareiofd, + XENSHARE_MAP_SHARE_PAGE * getpagesize()); + if (sharepage == MAP_FAILED) + err(1, "Failed to map shared page"); + + return sharepage; +} + +/* Munmap addr, let the xenshare interface clean up evtchns etc */ +static int unmap_pages(void *addr) +{ + int err; + + err = munmap((void *)addr, PAGE_SIZE); + if (err < 0) { + fprintf(stderr, "Failed to munmap() (%i,%i)\n", err, -errno); + return -errno; + } + + return 0; +} + +/* FIXME: Move to xenctrl library */ +static share_ref_t create_shared_pages(int num_pages, unsigned int *peer_id) +{ + share_ref_t share_ref; + int err; + void *addr; + + dom0_op_t op = { .cmd = DOM0_CREATESHAREDPAGES, + .interface_version = DOM0_INTERFACE_VERSION, + .u.createsharedpages.num = num_pages }; + + err = do_dom0_op(xc_fd, &op); + if (err < 0) + return 0; + + printf("Create page returned 0x%x\n", err); + + /* Save the share_ref */ + share_ref = err; + + /* Clear the page */ + addr = map_pages(share_ref, num_pages, peer_id); + memset(addr, 0, num_pages * getpagesize()); + unmap_pages(addr); + + return share_ref; +} + +static uint64_t get_domain_shared_ref(struct xs_handle *h, domid_t domid) +{ + unsigned int len; + unsigned long long share_ref; + char key[512]; + char *val, *endp; + + sprintf(key, "/local/domain/%i/vdevice-share", domid); + val = xs_read(h, 0, key, &len); + + if (val == NULL) + return DOMID_FIRST_RESERVED; + share_ref = strtoull(val, &endp, 0); + if (endp == val || *endp) { + errno = EINVAL; + free(val); + return DOMID_FIRST_RESERVED; + } + free(val); + return share_ref; +} + +/* Get dom0 vdevice_share from /sys/bus/vdevice/share_ref */ +static uint64_t get_dom0_shared_ref(void) +{ + FILE *f; + unsigned long long share_ref; + + f = fopen("/sys/bus/vdevice/share_ref", "r"); + if (!f) { + return DOMID_FIRST_RESERVED; + } + if (fscanf(f, "%llx", &share_ref) != 1) { + errno = EINVAL; + return DOMID_FIRST_RESERVED; + } + fclose(f); + return share_ref; +} + +struct vdevice_type +{ + /* Name of this device */ + const char *name; + + /* Number of pages to create for it. */ + unsigned int num_pages; + + /* Type number of this device. */ + uint32_t type; + + /* Features when creating a new one of these */ + uint32_t features; + + /* --create. Returns num args consumed. */ + int (*create)(struct vdevice_type *, + share_ref_t ref, void *map, int argc, char *argv[]); + + /* List info about this vdevice. */ + void (*list)(struct vdevice_type *, const struct vdevice_desc *vdesc); +}; + +/* Volatile is important: someone else changes it. */ +static uint32_t get_status(volatile struct vdevice_desc *vdevice) +{ + return vdevice->status; +} + +/* Returns the vdevice reference for this domain. */ +static share_ref_t vdevice_ref_for_domain(domid_t domid) +{ + share_ref_t vdevice_ref; + + if (domid == 0) + vdevice_ref = get_dom0_shared_ref(); + else { + int saved_errno; + struct xs_handle *xsh = xs_daemon_open(); + if (!xsh) { + warn("Could not talk to xenstored"); + return DOMID_FIRST_RESERVED; + } + vdevice_ref = get_domain_shared_ref(xsh, domid); + saved_errno = errno; + xs_daemon_close(xsh); + errno = saved_errno; + } + return vdevice_ref; +} + +static bool add_vdevice_entry(const char *domain, + uint32_t type, uint32_t features, + unsigned int num_pages, share_ref_t share_ref, + uint32_t status_flags) +{ + struct vdevice_desc *vdevices; + unsigned int i, peer_id; + uint32_t status; + share_ref_t vdevice_ref; + long domid; + char *endp; + + domid = strtol(domain, &endp, 0); + if (domid >= DOMID_FIRST_RESERVED || endp == domain || *endp != '\0') { + warn("Invalid domain id '%s'", domain); + return false; + } + + vdevice_ref = vdevice_ref_for_domain(domid); + if (vdevice_ref == DOMID_FIRST_RESERVED) { + warnx("Could not find vdevice page for domain %li", domid); + return false; + } + + /* There is always excatly 1 page for vdevices */ + vdevices = map_pages(vdevice_ref, 1, &peer_id); + if (!vdevices) { + warn("Could not access vdevice page %#llx for domain %li", + (long long)vdevice_ref, domid); + return false; + } + + for (i = 0; vdevices[i].id.type; i++) { + if (i == (PAGE_SIZE / sizeof(struct vdevice_desc)) - 1) { + warnx("Vdevice page for domain %li is full", domid); + unmap_pages(vdevices); + return false; + } + } + + if (add_grant(share_ref, domid) != 0) { + warn("Could not grant domain %li access to device", domid); + unmap_pages(vdevices); + return false; + } + + vdevices[i].id.type = type; + vdevices[i].id.features = features; + vdevices[i].nr_pages = num_pages; + vdevices[i].shared_ref = share_ref; + vdevices[i].status = 0; + + /* FIXME: magic "1" */ + HYPERVISOR_share(XEN_SHARE_trigger, vdevice_ref, 1, 0, 0); + + /* FIXME: Use /dev/xenshare, rather than spinning. Timeout. */ + do { + status = get_status(&vdevices[i]); + sleep(1); + } while ((status & (VDEVICE_S_FAILED|status_flags)) == 0); + + if (status & VDEVICE_S_FAILED) { + warnx("Adding device %i to domain %li failed: status %#08x", + i, domid, status); + /* if add_device filed the shared page is destroyed */ + vdevices[i].id.type = 0; + unmap_pages(vdevices); + return false; + } + unmap_pages(vdevices); + return true; +} + +static void remove_vdevice_entry(share_ref_t vdevice_ref, + struct vdevice_type *type, + share_ref_t share_ref) +{ + struct vdevice_desc *vdevices; + unsigned int i, peer_id; + + vdevices = map_pages(vdevice_ref, 1, &peer_id); + if (!vdevices) { + warn("Could not access vdevice page"); + return; + } + + for (i = 0; vdevices[i].shared_ref != share_ref; i++) { + if (i == (PAGE_SIZE / sizeof(struct vdevice_desc)) - 1) { + warnx("Could not find device %s (%li) in vdevice page", + type->name, share_ref); + return; + } + } + + /* FIXME: report the domid we're talking about! */ + if (vdevices[i].id.type != type->type) { + warnx("Vdevice %i using shared ref %li" + " has wrong type: %i", + i, share_ref, vdevices[i].id.type); + return; + } + memset(&vdevices[i], 0, sizeof(vdevices[i])); + + HYPERVISOR_share(XEN_SHARE_trigger, vdevice_ref, 1, 0, 0); + /* FIXME: wait for ack! */ +} + +/* FIXME: some callers need to recover, not exit if this fails... */ +static share_ref_t domid_arg(const char *arg) +{ + unsigned long domain; + char *endp; + share_ref_t vdevice_ref; + + domain = strtol(arg, &endp, 0); + if (strlen(arg) == 0 || *endp != '\0') + errx(1, "Invalid domain id '%s'", arg); + + vdevice_ref = vdevice_ref_for_domain(domain); + if (vdevice_ref == DOMID_FIRST_RESERVED) + err(1, "Cannot find vdevice page for domain '%s'", arg); + return vdevice_ref; +} + +static struct vdevice_type types[] = { +}; + +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +static struct vdevice_type *find_type(const char *type) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(types); i++) + if (!strcmp(types[i].name, type)) + return &types[i]; + return NULL; +} +static struct vdevice_type *find_type_err(const char *type) +{ + if (!find_type(type)) + errx(1, "unknown type '%s'", type); + return find_type(type); +} +static struct vdevice_type *find_type_number(unsigned int num) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(types); i++) + if (num == types[i].type) + return &types[i]; + return NULL; +} + +static void usage(void) +{ + unsigned int i; + fprintf(stderr, "Usage:\n" + "\t%s --create <type> ...\n" + "\t%s --add <type> <share_ref> <domid>\n" + "\t%s --remove <type> <share_ref> <domid>\n" + "\t%s --delete <type> <share_ref> ...\n" + "\t%s --list <domid>\n", + PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, + PROGRAM_NAME); + fprintf(stderr, "Available types:"); + for (i = 0; i < ARRAY_SIZE(types); i++) + fprintf(stderr, " %s", types[i].name); + fprintf(stderr, "\n"); + exit(1); +} + +static void list_devices(share_ref_t vdevice_ref) +{ + unsigned int i, peer_id; + struct vdevice_desc *vdevices; + + vdevices = map_pages(vdevice_ref, 1, &peer_id); + if (!vdevices) + err(1, "Could not access vdevice page"); + + for (i = 0; i < PAGE_SIZE / sizeof(struct vdevice_desc); i++) { + struct vdevice_type *type; + + if (!vdevices[i].id.type) + continue; + + type = find_type_number(vdevices[i].id.type); + printf("Device %i: %s %#x share=%#llx", i, + type ? type->name : "(unknown)", + vdevices[i].status, + (unsigned long long)vdevices[i].shared_ref); + if (type) + type->list(type, &vdevices[i]); + printf("\n"); + } +} + +static void destroy_share(share_ref_t share_ref) +{ + int olderr = errno; + dom0_op_t op = { .cmd = DOM0_DESTROYSHAREDPAGES, + .interface_version = DOM0_INTERFACE_VERSION, + .u.destroysharedpages.share_ref = share_ref }; + if (do_dom0_op(xc_fd, &op) != 0) + warn("Failed to destroy share"); + errno = olderr; +} + +/* Steal the number of pages from the command line if specified, + * otherwise use the default from the type defn. */ +static int get_num_pages(int *argc, char **argv, int num_pages) +{ + int i, j; + + for(i=0;i<*argc;i++) { + if (strcmp(argv[i], "--num_pages") == 0) { + if (i == *argc-1) + errx(1, "Specified num_pages at end of args"); + + num_pages = atoi(argv[i+1]); + if (num_pages <= 0) + errx(1, "%s is an invalid number of pages", + argv[i+1]); + + for(j=0;j+i+2<*argc;j++) { + argv[i+j] = argv[i+j+2]; + } + argv[(*argc)-1] = NULL; + argv[(*argc)-2] = NULL; + *argc -= 2; + break; + } + } + + return num_pages; +} + +static void create_device(struct vdevice_type *type, int argc, char *argv[]) +{ + unsigned int peer_id; + int argoff; + share_ref_t share_ref; + void *map; + int num_pages = get_num_pages(&argc, argv, type->num_pages); + + share_ref = create_shared_pages(num_pages, &peer_id); + if (share_ref == 0) + err(1, "Failed to create a new shared page!"); + + map = xc_map_foreign_range(xc_fd, DOMID_SELF, + PAGE_SIZE * num_pages, + PROT_READ|PROT_WRITE, share_ref); + if (!map) { + destroy_share(share_ref); + err(1, "Failed to map share %li", share_ref); + } + argoff = type->create(type, share_ref, map, argc, argv); + if (argoff < 0) { + destroy_share(share_ref); + exit(1); + } + argc -= argoff; + argv += argoff; + + while (argv[0]) { + add_vdevice_entry(argv[0], type->type, type->features, + num_pages, share_ref, VDEVICE_S_ACKNOWLEDGE); + argv++; + } +} + +static void add_device(struct vdevice_type *type, + share_ref_t share_ref, + const char *domain) +{ + /* FIXME: get nr_pages from vdesc? */ + if (!add_vdevice_entry(domain, type->type, type->features, + type->num_pages, share_ref, + VDEVICE_S_ACKNOWLEDGE)) + exit(1); +} + +static void delete_device(struct vdevice_type *type, share_ref_t share_ref, + int argc, char *argv[]) +{ + /* Remove domains, then destroy share. */ + while (argv[0]) { + remove_vdevice_entry(domid_arg(argv[0]), type, share_ref); + argv++; + } + + destroy_share(share_ref); +} + +static void remove_device(struct vdevice_type *type, + share_ref_t share_ref, + share_ref_t vdevices_ref) +{ + remove_vdevice_entry(vdevices_ref, type, share_ref); +} + +static uint64_t share_ref_arg(const char *arg) +{ + char *endp; + uint64_t share_ref = strtoull(arg, &endp, 0); + + if (*endp || endp == arg) + errx(1, "Invalid shared reference %s", arg); + return share_ref; +} + + +/* FIXME: Locking! what prevents 2 (or more) userspace apps clobbering each + * others memory? */ +int main(int argc, char *argv[]) +{ + if (argc < 2) + usage(); + + xc_fd = xc_interface_open(); + if (xc_fd < 0) + err(1, "Failed to open xc interface"); + + if (!strcmp(argv[1], "--list")) { + if (argc != 3) + usage(); + list_devices(domid_arg(argv[2])); + } else if (!strcmp(argv[1], "--create")) { + if (argc < 3) + usage(); + create_device(find_type_err(argv[2]), argc-3, argv+3); + } else if (!strcmp(argv[1], "--add")) { + if (argc != 5) + usage(); + add_device(find_type_err(argv[2]), share_ref_arg(argv[3]), + argv[4]); + } else if (!strcmp(argv[1], "--delete")) { + if (argc < 4) + usage(); + delete_device(find_type_err(argv[2]), share_ref_arg(argv[3]), + argc-4, argv+4); + } else if (!strcmp(argv[1], "--remove")) { + if (argc != 5) + usage(); + remove_device(find_type_err(argv[2]), share_ref_arg(argv[3]), + domid_arg(argv[4])); + } else + usage(); + return 0; +} -- ccontrol: http://ccontrol.ozlabs.org _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |