WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH 7/9] vdevice tool for manipulating vdevice bus

To: Xen Mailing List <xen-devel@xxxxxxxxxxxxxxxxxxx>
Subject: [Xen-devel] [PATCH 7/9] vdevice tool for manipulating vdevice bus
From: Rusty Russell <rusty@xxxxxxxxxxxxxxx>
Date: Tue, 06 Jun 2006 15:55:51 +1000
Delivery-date: Mon, 05 Jun 2006 22:56:26 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
In-reply-to: <1149572143.5183.25.camel@xxxxxxxxxxxxxxxxxxxxx>
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
References: <1149572143.5183.25.camel@xxxxxxxxxxxxxxxxxxxxx>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
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

<Prev in Thread] Current Thread [Next in Thread>