[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH] libvchan: interdomain communications library
Signed-off-by: Daniel De Graaf <dgdegra@xxxxxxxxxxxxx> --- This version includes a local copy of gntalloc.h and gntdev.h to allow it to compile when the installed kernel headers do not include gntalloc and to support GNTDEV_SET_UNMAP_NOTIFY when the running kernel does not match the headers. --- tools/Makefile | 1 + tools/libvchan/Makefile | 57 ++++++ tools/libvchan/gntalloc.h | 82 ++++++++ tools/libvchan/gntdev.h | 150 ++++++++++++++ tools/libvchan/init.c | 456 ++++++++++++++++++++++++++++++++++++++++++ tools/libvchan/io.c | 270 +++++++++++++++++++++++++ tools/libvchan/libvchan.h | 141 +++++++++++++ tools/libvchan/node-select.c | 161 +++++++++++++++ tools/libvchan/node.c | 169 ++++++++++++++++ 9 files changed, 1487 insertions(+), 0 deletions(-) create mode 100644 tools/libvchan/Makefile create mode 100644 tools/libvchan/gntalloc.h create mode 100644 tools/libvchan/gntdev.h create mode 100644 tools/libvchan/init.c create mode 100644 tools/libvchan/io.c create mode 100644 tools/libvchan/libvchan.h create mode 100644 tools/libvchan/node-select.c create mode 100644 tools/libvchan/node.c diff --git a/tools/Makefile b/tools/Makefile index df6270c..9389e1f 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -27,6 +27,7 @@ SUBDIRS-$(CONFIG_NetBSD) += blktap2 SUBDIRS-$(CONFIG_NetBSD) += xenbackendd SUBDIRS-y += libfsimage SUBDIRS-$(LIBXENAPI_BINDINGS) += libxen +SUBDIRS-y += libvchan # do not recurse in to a dir we are about to delete ifneq "$(MAKECMDGOALS)" "distclean" diff --git a/tools/libvchan/Makefile b/tools/libvchan/Makefile new file mode 100644 index 0000000..9195f6e --- /dev/null +++ b/tools/libvchan/Makefile @@ -0,0 +1,57 @@ +# +# tools/libvchan/Makefile +# + +XEN_ROOT = $(CURDIR)/../.. +include $(XEN_ROOT)/tools/Rules.mk + +LIBVCHAN_OBJS = init.o io.o +NODE_OBJS = node.o +NODE2_OBJS = node-select.o + +LIBVCHAN_LIBS = $(LDLIBS_libxenstore) +LIBVCHAN_OBJS: CFLAGS += $(CFLAGS_libxenstore) + +MAJOR = 1.0 +MINOR = 0 + +CFLAGS += -I. -fPIC + +.PHONY: all +all: libvchan.so vchan-node1 vchan-node2 libvchan.a + +libvchan.so: libvchan.so.$(MAJOR) + ln -sf $< $@ + +libvchan.so.$(MAJOR): libvchan.so.$(MAJOR).$(MINOR) + ln -sf $< $@ + +libvchan.so.$(MAJOR).$(MINOR): $(LIBVCHAN_OBJS) + $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libvchan.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LIBVCHAN_LIBS) + +libvchan.a: $(LIBVCHAN_OBJS) + $(AR) rcs libvchan.a $^ + +vchan-node1: $(NODE_OBJS) libvchan.so + $(CC) $(LDFLAGS) -o $@ $(NODE_OBJS) libvchan.so $(LDLIBS_libvchan) + +vchan-node2: $(NODE2_OBJS) libvchan.so + $(CC) $(LDFLAGS) -o $@ $(NODE2_OBJS) libvchan.so $(LDLIBS_libvchan) + +.PHONY: install +install: all + $(INSTALL_DIR) $(DESTDIR)$(LIBDIR) + $(INSTALL_DIR) $(DESTDIR)$(INCLUDEDIR) + $(INSTALL_PROG) libvchan.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR) + ln -sf libvchan.so.$(MAJOR).$(MINOR) $(DESTDIR)$(LIBDIR)/libvchan.so.$(MAJOR) + ln -sf libvchan.so.$(MAJOR) $(DESTDIR)$(LIBDIR)/libvchan.so + $(INSTALL_DATA) libvchan.a $(DESTDIR)$(LIBDIR) + $(INSTALL_DATA) libvchan.h $(DESTDIR)$(INCLUDEDIR) + +.PHONY: clean +clean: + $(RM) -f *.o *.so* *.a vchan-node1 vchan-node2 $(DEPS) + +distclean: clean + +-include $(DEPS) diff --git a/tools/libvchan/gntalloc.h b/tools/libvchan/gntalloc.h new file mode 100644 index 0000000..76bd580 --- /dev/null +++ b/tools/libvchan/gntalloc.h @@ -0,0 +1,82 @@ +/****************************************************************************** + * gntalloc.h + * + * Interface to /dev/xen/gntalloc. + * + * Author: Daniel De Graaf <dgdegra@xxxxxxxxxxxxx> + * + * This file is in the public domain. + */ + +#ifndef __LINUX_PUBLIC_GNTALLOC_H__ +#define __LINUX_PUBLIC_GNTALLOC_H__ + +/* + * Allocates a new page and creates a new grant reference. + */ +#define IOCTL_GNTALLOC_ALLOC_GREF \ +_IOC(_IOC_NONE, 'G', 5, sizeof(struct ioctl_gntalloc_alloc_gref)) +struct ioctl_gntalloc_alloc_gref { + /* IN parameters */ + /* The ID of the domain to be given access to the grants. */ + uint16_t domid; + /* Flags for this mapping */ + uint16_t flags; + /* Number of pages to map */ + uint32_t count; + /* OUT parameters */ + /* The offset to be used on a subsequent call to mmap(). */ + uint64_t index; + /* The grant references of the newly created grant, one per page */ + /* Variable size, depending on count */ + uint32_t gref_ids[1]; +}; + +#define GNTALLOC_FLAG_WRITABLE 1 + +/* + * Deallocates the grant reference, allowing the associated page to be freed if + * no other domains are using it. + */ +#define IOCTL_GNTALLOC_DEALLOC_GREF \ +_IOC(_IOC_NONE, 'G', 6, sizeof(struct ioctl_gntalloc_dealloc_gref)) +struct ioctl_gntalloc_dealloc_gref { + /* IN parameters */ + /* The offset returned in the map operation */ + uint64_t index; + /* Number of references to unmap */ + uint32_t count; +}; + +/* + * Sets up an unmap notification within the page, so that the other side can do + * cleanup if this side crashes. Required to implement cross-domain robust + * mutexes or close notification on communication channels. + * + * Each mapped page only supports one notification; multiple calls referring to + * the same page overwrite the previous notification. You must clear the + * notification prior to the IOCTL_GNTALLOC_DEALLOC_GREF if you do not want it + * to occur. + */ +#define IOCTL_GNTALLOC_SET_UNMAP_NOTIFY \ +_IOC(_IOC_NONE, 'G', 7, sizeof(struct ioctl_gntalloc_unmap_notify)) +struct ioctl_gntalloc_unmap_notify { + /* IN parameters */ + /* Offset in the file descriptor for a byte within the page (same as + * used in mmap). If using UNMAP_NOTIFY_CLEAR_BYTE, this is the byte to + * be cleared. Otherwise, it can be any byte in the page whose + * notification we are adjusting. + */ + uint64_t index; + /* Action(s) to take on unmap */ + uint32_t action; + /* Event channel to notify */ + uint32_t event_channel_port; +}; + +/* Clear (set to zero) the byte specified by index */ +#define UNMAP_NOTIFY_CLEAR_BYTE 0x1 +/* Send an interrupt on the indicated event channel */ +#define UNMAP_NOTIFY_SEND_EVENT 0x2 + +#endif /* __LINUX_PUBLIC_GNTALLOC_H__ */ diff --git a/tools/libvchan/gntdev.h b/tools/libvchan/gntdev.h new file mode 100644 index 0000000..5304bd3 --- /dev/null +++ b/tools/libvchan/gntdev.h @@ -0,0 +1,150 @@ +/****************************************************************************** + * gntdev.h + * + * Interface to /dev/xen/gntdev. + * + * Copyright (c) 2007, D G Murray + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __LINUX_PUBLIC_GNTDEV_H__ +#define __LINUX_PUBLIC_GNTDEV_H__ + +struct ioctl_gntdev_grant_ref { + /* The domain ID of the grant to be mapped. */ + uint32_t domid; + /* The grant reference of the grant to be mapped. */ + uint32_t ref; +}; + +/* + * Inserts the grant references into the mapping table of an instance + * of gntdev. N.B. This does not perform the mapping, which is deferred + * until mmap() is called with @index as the offset. + */ +#define IOCTL_GNTDEV_MAP_GRANT_REF \ +_IOC(_IOC_NONE, 'G', 0, sizeof(struct ioctl_gntdev_map_grant_ref)) +struct ioctl_gntdev_map_grant_ref { + /* IN parameters */ + /* The number of grants to be mapped. */ + uint32_t count; + uint32_t pad; + /* OUT parameters */ + /* The offset to be used on a subsequent call to mmap(). */ + uint64_t index; + /* Variable IN parameter. */ + /* Array of grant references, of size @count. */ + struct ioctl_gntdev_grant_ref refs[1]; +}; + +/* + * Removes the grant references from the mapping table of an instance of + * of gntdev. N.B. munmap() must be called on the relevant virtual address(es) + * before this ioctl is called, or an error will result. + */ +#define IOCTL_GNTDEV_UNMAP_GRANT_REF \ +_IOC(_IOC_NONE, 'G', 1, sizeof(struct ioctl_gntdev_unmap_grant_ref)) +struct ioctl_gntdev_unmap_grant_ref { + /* IN parameters */ + /* The offset was returned by the corresponding map operation. */ + uint64_t index; + /* The number of pages to be unmapped. */ + uint32_t count; + uint32_t pad; +}; + +/* + * Returns the offset in the driver's address space that corresponds + * to @vaddr. This can be used to perform a munmap(), followed by an + * UNMAP_GRANT_REF ioctl, where no state about the offset is retained by + * the caller. The number of pages that were allocated at the same time as + * @vaddr is returned in @count. + * + * N.B. Where more than one page has been mapped into a contiguous range, the + * supplied @vaddr must correspond to the start of the range; otherwise + * an error will result. It is only possible to munmap() the entire + * contiguously-allocated range at once, and not any subrange thereof. + */ +#define IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR \ +_IOC(_IOC_NONE, 'G', 2, sizeof(struct ioctl_gntdev_get_offset_for_vaddr)) +struct ioctl_gntdev_get_offset_for_vaddr { + /* IN parameters */ + /* The virtual address of the first mapped page in a range. */ + uint64_t vaddr; + /* OUT parameters */ + /* The offset that was used in the initial mmap() operation. */ + uint64_t offset; + /* The number of pages mapped in the VM area that begins at @vaddr. */ + uint32_t count; + uint32_t pad; +}; + +/* + * Sets the maximum number of grants that may mapped at once by this gntdev + * instance. + * + * N.B. This must be called before any other ioctl is performed on the device. + */ +#define IOCTL_GNTDEV_SET_MAX_GRANTS \ +_IOC(_IOC_NONE, 'G', 3, sizeof(struct ioctl_gntdev_set_max_grants)) +struct ioctl_gntdev_set_max_grants { + /* IN parameter */ + /* The maximum number of grants that may be mapped at once. */ + uint32_t count; +}; + +/* + * Sets up an unmap notification within the page, so that the other side can do + * cleanup if this side crashes. Required to implement cross-domain robust + * mutexes or close notification on communication channels. + * + * Each mapped page only supports one notification; multiple calls referring to + * the same page overwrite the previous notification. You must clear the + * notification prior to the IOCTL_GNTALLOC_DEALLOC_GREF if you do not want it + * to occur. + */ +#define IOCTL_GNTDEV_SET_UNMAP_NOTIFY \ +_IOC(_IOC_NONE, 'G', 7, sizeof(struct ioctl_gntdev_unmap_notify)) +struct ioctl_gntdev_unmap_notify { + /* IN parameters */ + /* Offset in the file descriptor for a byte within the page (same as + * used in mmap). If using UNMAP_NOTIFY_CLEAR_BYTE, this is the byte to + * be cleared. Otherwise, it can be any byte in the page whose + * notification we are adjusting. + */ + uint64_t index; + /* Action(s) to take on unmap */ + uint32_t action; + /* Event channel to notify */ + uint32_t event_channel_port; +}; + +/* Clear (set to zero) the byte specified by index */ +#define UNMAP_NOTIFY_CLEAR_BYTE 0x1 +/* Send an interrupt on the indicated event channel */ +#define UNMAP_NOTIFY_SEND_EVENT 0x2 + +#endif /* __LINUX_PUBLIC_GNTDEV_H__ */ diff --git a/tools/libvchan/init.c b/tools/libvchan/init.c new file mode 100644 index 0000000..0cf00e2 --- /dev/null +++ b/tools/libvchan/init.c @@ -0,0 +1,456 @@ +/** + * @file + * @section AUTHORS + * + * Copyright (C) 2010 Rafal Wojtczuk <rafal@xxxxxxxxxxxxxxxxxxxxxx> + * + * Authors: + * Rafal Wojtczuk <rafal@xxxxxxxxxxxxxxxxxxxxxx> + * Daniel De Graaf <dgdegra@xxxxxxxxxxxxx> + * + * @section LICENSE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * @section DESCRIPTION + * + * This file contains the setup code used to establish the ring buffer. + */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/ioctl.h> +#include <sys/user.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include <xs.h> +#include <xen/sys/evtchn.h> + +#include "libvchan.h" +#include "gntalloc.h" +#include "gntdev.h" + +#ifndef PAGE_SHIFT +#define PAGE_SHIFT 12 +#endif + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +#define max(a,b) ((a > b) ? a : b) + +static int init_gnt_srv(struct libvchan *ctrl) +{ + int pages_left = ctrl->read.order >= PAGE_SHIFT ? 1 << (ctrl->read.order - PAGE_SHIFT) : 0; + int pages_right = ctrl->write.order >= PAGE_SHIFT ? 1 << (ctrl->write.order - PAGE_SHIFT) : 0; + + int ring_fd = open("/dev/xen/gntalloc", O_RDWR); + int ring_ref = -1; + if (ring_fd < 0) + return -1; + struct ioctl_gntalloc_alloc_gref *gref_info = malloc( + sizeof(struct ioctl_gntalloc_alloc_gref) + max(pages_left, pages_right)*sizeof(uint32_t)); + + gref_info->domid = ctrl->other_domain_id; + gref_info->flags = GNTALLOC_FLAG_WRITABLE; + gref_info->count = 1; + + int err = ioctl(ring_fd, IOCTL_GNTALLOC_ALLOC_GREF, gref_info); + if (err) + goto out; + + void* ring = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, ring_fd, gref_info->index); + + if (ring == MAP_FAILED) + goto out; + + ctrl->ring = ring; + ring_ref = gref_info->gref_ids[0]; + + memset(ring, 0, PAGE_SIZE); + + ctrl->read.shr = &ctrl->ring->left; + ctrl->write.shr = &ctrl->ring->right; + ctrl->ring->left_order = ctrl->read.order; + ctrl->ring->right_order = ctrl->write.order; + ctrl->ring->cli_live = 2; + ctrl->ring->srv_live = 1; + ctrl->ring->debug = 0xabcd; + +#ifdef IOCTL_GNTALLOC_SET_UNMAP_NOTIFY + { + struct ioctl_gntalloc_unmap_notify arg; + arg.index = gref_info->index + offsetof(struct vchan_interface, srv_live); + arg.action = UNMAP_NOTIFY_CLEAR_BYTE | UNMAP_NOTIFY_SEND_EVENT; + arg.event_channel_port = ctrl->event_port; + ioctl(ring_fd, IOCTL_GNTALLOC_SET_UNMAP_NOTIFY, &arg); + } +#endif + + if (ctrl->read.order == 10) { + ctrl->read.buffer = ((void*)ctrl->ring) + 1024; + } else if (ctrl->read.order == 11) { + ctrl->read.buffer = ((void*)ctrl->ring) + 2048; + } else { + gref_info->count = pages_left; + err = ioctl(ring_fd, IOCTL_GNTALLOC_ALLOC_GREF, gref_info); + if (err) + goto out_ring; + void* area = mmap(NULL, pages_left * PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, ring_fd, gref_info->index); + if (area == MAP_FAILED) + goto out_ring; + ctrl->read.buffer = area; + memcpy(ctrl->ring->grants, gref_info->gref_ids, pages_left * sizeof(uint32_t)); + } + + if (ctrl->write.order == 10) { + ctrl->write.buffer = ((void*)ctrl->ring) + 1024; + } else if (ctrl->write.order == 11) { + ctrl->write.buffer = ((void*)ctrl->ring) + 2048; + } else { + gref_info->count = pages_right; + err = ioctl(ring_fd, IOCTL_GNTALLOC_ALLOC_GREF, gref_info); + if (err) + goto out_unmap_left; + void* area = mmap(NULL, pages_right * PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, ring_fd, gref_info->index); + if (area == MAP_FAILED) + goto out_unmap_left; + ctrl->write.buffer = area; + memcpy(ctrl->ring->grants + (pages_left * sizeof(uint32_t)), + gref_info->gref_ids, pages_right * sizeof(uint32_t)); + } + +out: + close(ring_fd); + return ring_ref; +out_unmap_left: + munmap(ctrl->read.buffer, pages_left * PAGE_SIZE); +out_ring: + munmap(ring, PAGE_SIZE); + ring_ref = -1; + ctrl->ring = NULL; + ctrl->write.order = ctrl->read.order = 0; + goto out; +} + +static void* do_gnt_map(int fd, int domid, uint32_t* pages, size_t npages, uint64_t *index) +{ + int i, rv; + void* area = NULL; + struct ioctl_gntdev_map_grant_ref *gref_info; + gref_info = malloc(sizeof(*gref_info) + npages*sizeof(gref_info->refs[0])); + gref_info->count = npages; + for(i=0; i < npages; i++) { + gref_info->refs[i].domid = domid; + gref_info->refs[i].ref = pages[i]; + } + + rv = ioctl(fd, IOCTL_GNTDEV_MAP_GRANT_REF, gref_info); + if (rv) + goto out; + if (index) + *index = gref_info->index; + area = mmap(NULL, PAGE_SIZE * npages, PROT_READ | PROT_WRITE, MAP_SHARED, fd, gref_info->index); + if (area == MAP_FAILED) { + struct ioctl_gntdev_unmap_grant_ref undo = { + .index = gref_info->index, + .count = gref_info->count + }; + ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &undo); + area = NULL; + } + out: + free(gref_info); + return area; +} + +static int init_gnt_cli(struct libvchan *ctrl, uint32_t ring_ref) +{ + int ring_fd = open("/dev/xen/gntdev", O_RDWR); + int rv = -1; + uint64_t ring_index; + if (ring_fd < 0) + return -1; + + ctrl->ring = do_gnt_map(ring_fd, ctrl->other_domain_id, &ring_ref, 1, &ring_index); + + if (!ctrl->ring) + goto out; + + ctrl->write.order = ctrl->ring->left_order; + ctrl->read.order = ctrl->ring->right_order; + ctrl->write.shr = &ctrl->ring->left; + ctrl->read.shr = &ctrl->ring->right; + if (ctrl->write.order < 10 || ctrl->write.order > 24) + goto out_unmap_ring; + if (ctrl->read.order < 10 || ctrl->read.order > 24) + goto out_unmap_ring; + if (ctrl->read.order == ctrl->write.order && ctrl->read.order < 12) + goto out_unmap_ring; + + uint32_t* grants = ctrl->ring->grants; + + if (ctrl->write.order == 10) { + ctrl->write.buffer = ((void*)ctrl->ring) + 1024; + } else if (ctrl->write.order == 11) { + ctrl->write.buffer = ((void*)ctrl->ring) + 2048; + } else { + int pages_left = 1 << (ctrl->write.order - PAGE_SHIFT); + ctrl->write.buffer = do_gnt_map(ring_fd, ctrl->other_domain_id, grants, pages_left, NULL); + if (!ctrl->write.buffer) + goto out_unmap_ring; + grants += pages_left; + } + + if (ctrl->read.order == 10) { + ctrl->read.buffer = ((void*)ctrl->ring) + 1024; + } else if (ctrl->read.order == 11) { + ctrl->read.buffer = ((void*)ctrl->ring) + 2048; + } else { + int pages_right = 1 << (ctrl->read.order - PAGE_SHIFT); + ctrl->read.buffer = do_gnt_map(ring_fd, ctrl->other_domain_id, grants, pages_right, NULL); + if (!ctrl->read.buffer) + goto out_unmap_left; + } + +#ifdef IOCTL_GNTDEV_SET_UNMAP_NOTIFY + { + struct ioctl_gntdev_unmap_notify arg; + arg.index = ring_index + offsetof(struct vchan_interface, cli_live); + arg.action = UNMAP_NOTIFY_CLEAR_BYTE | UNMAP_NOTIFY_SEND_EVENT; + arg.event_channel_port = ctrl->event_port; + ioctl(ring_fd, IOCTL_GNTDEV_SET_UNMAP_NOTIFY, &arg); + } +#endif + + rv = 0; + out: + close(ring_fd); + return rv; + out_unmap_left: + if (ctrl->write.order >= PAGE_SHIFT) + munmap(ctrl->write.buffer, 1 << ctrl->write.order); + out_unmap_ring: + munmap(ctrl->ring, PAGE_SIZE); + ctrl->ring = 0; + ctrl->write.order = ctrl->read.order = 0; + rv = -1; + goto out; +} + +static int init_evt_srv(struct libvchan *ctrl) +{ + struct ioctl_evtchn_bind_unbound_port bind; + ctrl->event_fd = open("/dev/xen/evtchn", O_RDWR); + if (ctrl->event_fd < 0) + return -1; + bind.remote_domain = ctrl->other_domain_id; + ctrl->event_port = ioctl(ctrl->event_fd, IOCTL_EVTCHN_BIND_UNBOUND_PORT, &bind); + if (ctrl->event_port < 0) + return -1; + write(ctrl->event_fd, &ctrl->event_port, sizeof(ctrl->event_port)); + return 0; +} + +static int init_xs_srv(struct libvchan *ctrl, int ring_ref) +{ + int ret = -1; + struct xs_handle *xs; + struct xs_permissions perms[2]; + char buf[64]; + char ref[16]; + char* domid_str = NULL; + xs = xs_domain_open(); + if (!xs) + goto fail; + domid_str = xs_read(xs, 0, "domid", NULL); + if (!domid_str) + goto fail_xs_open; + + // owner domain is us + perms[0].id = atoi(domid_str); + // permissions for domains not listed = none + perms[0].perms = XS_PERM_NONE; + // other domains + perms[1].id = ctrl->other_domain_id; + perms[1].perms = XS_PERM_READ; + + snprintf(ref, sizeof ref, "%d", ring_ref); + snprintf(buf, sizeof buf, "data/vchan/%d/ring-ref", ctrl->device_number); + if (!xs_write(xs, 0, buf, ref, strlen(ref))) + goto fail_xs_open; + if (!xs_set_permissions(xs, 0, buf, perms, 2)) + goto fail_xs_open; + + snprintf(ref, sizeof ref, "%d", ctrl->event_port); + snprintf(buf, sizeof buf, "data/vchan/%d/event-channel", ctrl->device_number); + if (!xs_write(xs, 0, buf, ref, strlen(ref))) + goto fail_xs_open; + if (!xs_set_permissions(xs, 0, buf, perms, 2)) + goto fail_xs_open; + + ret = 0; + fail_xs_open: + free(domid_str); + xs_daemon_close(xs); + fail: + return ret; +} + +static int min_order(size_t siz) +{ + int rv = PAGE_SHIFT; + while (siz > (1 << rv)) + rv++; + return rv; +} + +struct libvchan *libvchan_server_init(int domain, int devno, size_t left_min, size_t right_min) +{ + // if you go over this size, you'll have too many grants to fit in the shared page. + size_t MAX_RING_SIZE = 256 * PAGE_SIZE; + if (left_min > MAX_RING_SIZE || right_min > MAX_RING_SIZE) + return 0; + + struct libvchan *ctrl = malloc(sizeof(struct libvchan)); + if (!ctrl) + return 0; + + ctrl->other_domain_id = domain; + ctrl->device_number = devno; + ctrl->ring = NULL; + ctrl->event_fd = -1; + ctrl->is_server = 1; + ctrl->server_persist = 0; + + ctrl->read.order = min_order(left_min); + ctrl->write.order = min_order(right_min); + + // if we can avoid allocating extra pages by using in-page rings, do so +#define MAX_SMALL_RING 1024 +#define MAX_LARGE_RING 2048 + if (left_min <= MAX_SMALL_RING && right_min <= MAX_LARGE_RING) { + ctrl->read.order = 10; + ctrl->write.order = 11; + } else if (left_min <= MAX_LARGE_RING && right_min <= MAX_SMALL_RING) { + ctrl->read.order = 11; + ctrl->write.order = 10; + } else if (left_min <= MAX_LARGE_RING) { + ctrl->read.order = 11; + } else if (right_min <= MAX_LARGE_RING) { + ctrl->write.order = 11; + } + if (init_evt_srv(ctrl)) + goto out; + int ring_ref = init_gnt_srv(ctrl); + if (ring_ref < 0) + goto out; + if (init_xs_srv(ctrl, ring_ref)) + goto out; + return ctrl; +out: + libvchan_close(ctrl); + return 0; +} + +static int init_evt_cli(struct libvchan *ctrl) +{ + struct ioctl_evtchn_bind_interdomain bind; + ctrl->event_fd = open("/dev/xen/evtchn", O_RDWR); + if (ctrl->event_fd < 0) + return -1; + + bind.remote_domain = ctrl->other_domain_id; + bind.remote_port = ctrl->event_port; + ctrl->event_port = ioctl(ctrl->event_fd, IOCTL_EVTCHN_BIND_INTERDOMAIN, &bind); + if (ctrl->event_port < 0) + return -1; + return 0; +} + + +struct libvchan *libvchan_client_init(int domain, int devno) +{ + struct libvchan *ctrl = malloc(sizeof(struct libvchan)); + struct xs_handle *xs = NULL; + char buf[64]; + char *ref; + int ring_ref; + unsigned int len; + if (!ctrl) + return 0; + ctrl->other_domain_id = domain; + ctrl->device_number = devno; + ctrl->ring = NULL; + ctrl->event_fd = -1; + ctrl->write.order = ctrl->read.order = 0; + ctrl->is_server = 0; + + xs = xs_daemon_open(); + if (!xs) + xs = xs_domain_open(); + if (!xs) + goto fail; + +// find xenstore entry + snprintf(buf, sizeof buf, "/local/domain/%d/data/vchan/%d/ring-ref", + ctrl->other_domain_id, ctrl->device_number); + ref = xs_read(xs, 0, buf, &len); + if (!ref) + goto fail; + ring_ref = atoi(ref); + free(ref); + if (!ring_ref) + goto fail; + snprintf(buf, sizeof buf, "/local/domain/%d/data/vchan/%d/event-channel", + ctrl->other_domain_id, ctrl->device_number); + ref = xs_read(xs, 0, buf, &len); + if (!ref) + goto fail; + ctrl->event_port = atoi(ref); + free(ref); + if (!ctrl->event_port) + goto fail; + +// set up event channel + if (init_evt_cli(ctrl)) + goto fail; + +// set up shared page(s) + if (init_gnt_cli(ctrl, ring_ref)) + goto fail; + + ctrl->ring->cli_live = 1; + ctrl->ring->debug = 0xabce; + + out: + if (xs) + xs_daemon_close(xs); + return ctrl; + fail: + libvchan_close(ctrl); + ctrl = NULL; + goto out; +} diff --git a/tools/libvchan/io.c b/tools/libvchan/io.c new file mode 100644 index 0000000..f95c7b0 --- /dev/null +++ b/tools/libvchan/io.c @@ -0,0 +1,270 @@ +/** + * @file + * @section AUTHORS + * + * Copyright (C) 2010 Rafal Wojtczuk <rafal@xxxxxxxxxxxxxxxxxxxxxx> + * + * Authors: + * Rafal Wojtczuk <rafal@xxxxxxxxxxxxxxxxxxxxxx> + * Daniel De Graaf <dgdegra@xxxxxxxxxxxxx> + * + * @section LICENSE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * @section DESCRIPTION + * + * This file contains the communications interface built on the ring buffer. + */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/ioctl.h> +#include <sys/uio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <xenctrl.h> + +#include "libvchan.h" + +#ifndef PAGE_SHIFT +#define PAGE_SHIFT 12 +#endif + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +// allow vchan data to be easily observed in strace by doing a +// writev() to FD -1 with the data being read/written. +#ifndef VCHAN_DEBUG +#define VCHAN_DEBUG 0 +#endif + +static uint32_t rd_prod(struct libvchan *ctrl) +{ + return ctrl->read.shr->prod; +} + +static uint32_t* _rd_cons(struct libvchan *ctrl) +{ + return &ctrl->read.shr->cons; +} +#define rd_cons(x) (*_rd_cons(x)) + +static uint32_t* _wr_prod(struct libvchan *ctrl) +{ + return &ctrl->write.shr->prod; +} +#define wr_prod(x) (*_wr_prod(x)) + +static uint32_t wr_cons(struct libvchan *ctrl) +{ + return ctrl->write.shr->cons; +} + +static const void* rd_ring(struct libvchan *ctrl) +{ + return ctrl->read.buffer; +} + +static void* wr_ring(struct libvchan *ctrl) +{ + return ctrl->write.buffer; +} + +static uint32_t wr_ring_size(struct libvchan *ctrl) +{ + return (1 << ctrl->write.order); +} + +static uint32_t rd_ring_size(struct libvchan *ctrl) +{ + return (1 << ctrl->read.order); +} + +int libvchan_data_ready(struct libvchan *ctrl) +{ + return rd_prod(ctrl) - rd_cons(ctrl); +} + +int libvchan_buffer_space(struct libvchan *ctrl) +{ + return wr_ring_size(ctrl) - (wr_prod(ctrl) - wr_cons(ctrl)); +} + +static int do_notify(struct libvchan *ctrl) +{ + struct ioctl_evtchn_notify notify; + notify.port = ctrl->event_port; + return ioctl(ctrl->event_fd, IOCTL_EVTCHN_NOTIFY, ¬ify); +} + +int libvchan_wait(struct libvchan *ctrl) +{ + int ret; + uint32_t dummy; + ret = read(ctrl->event_fd, &dummy, sizeof(dummy)); + if (ret == -1) + return -1; + write(ctrl->event_fd, &dummy, sizeof(dummy)); + return 0; +} + +/** + * returns -1 on error, or size on success + */ +static int do_send(struct libvchan *ctrl, const void *data, size_t size) +{ + int real_idx = wr_prod(ctrl) & (wr_ring_size(ctrl) - 1); + int avail_contig = wr_ring_size(ctrl) - real_idx; + if (VCHAN_DEBUG) { + char metainfo[32]; + struct iovec iov[2]; + iov[0].iov_base = metainfo; + iov[0].iov_len = snprintf(metainfo, 32, "vchan wr %d/%d", ctrl->other_domain_id, ctrl->device_number); + iov[1].iov_base = (void *)data; + iov[1].iov_len = size; + writev(-1, iov, 2); + } + if (avail_contig > size) + avail_contig = size; + memcpy(wr_ring(ctrl) + real_idx, data, avail_contig); + if (avail_contig < size) + { + // we rolled across the end of the ring + memcpy(wr_ring(ctrl), data + avail_contig, size - avail_contig); + } + wr_prod(ctrl) += size; + if (do_notify(ctrl) < 0) + return -1; + return size; +} + +/** + * returns 0 if no buffer space is available, -1 on error, or size on success + */ +int libvchan_send(struct libvchan *ctrl, const void *data, size_t size) +{ + int avail = libvchan_buffer_space(ctrl); + if (!libvchan_is_open(ctrl)) + return -1; + if (size > avail) + return 0; + return do_send(ctrl, data, size); +} + +int libvchan_write(struct libvchan *ctrl, const void *data, size_t size) +{ + int avail = libvchan_buffer_space(ctrl); + if (!libvchan_is_open(ctrl)) + return -1; + if (size > avail) + size = avail; + return do_send(ctrl, data, size); +} + +static int do_recv(struct libvchan *ctrl, void *data, size_t size) +{ + int real_idx = rd_cons(ctrl) & (rd_ring_size(ctrl) - 1); + int avail_contig = rd_ring_size(ctrl) - real_idx; + if (avail_contig > size) + avail_contig = size; + memcpy(data, rd_ring(ctrl) + real_idx, avail_contig); + if (avail_contig < size) + { + // we rolled across the end of the ring + memcpy(data + avail_contig, rd_ring(ctrl), size - avail_contig); + } + rd_cons(ctrl) += size; + if (VCHAN_DEBUG) { + char metainfo[32]; + struct iovec iov[2]; + iov[0].iov_base = metainfo; + iov[0].iov_len = snprintf(metainfo, 32, "vchan rd %d/%d", ctrl->other_domain_id, ctrl->device_number); + iov[1].iov_base = data; + iov[1].iov_len = size; + writev(-1, iov, 2); + } + if (do_notify(ctrl) < 0) + return -1; + return size; +} + +/** + * reads exactly size bytes from the vchan. + * returns 0 if insufficient data is available, -1 on error, or size on success + */ +int libvchan_recv(struct libvchan *ctrl, void *data, size_t size) +{ + int avail = libvchan_data_ready(ctrl); + if (size <= avail) + return do_recv(ctrl, data, size); + else if (libvchan_is_open(ctrl)) + return 0; + else + return -1; +} + +int libvchan_read(struct libvchan *ctrl, void *data, size_t size) +{ + int avail = libvchan_data_ready(ctrl); + if (size > avail) + size = avail; + if (avail) + return do_recv(ctrl, data, size); + else if (libvchan_is_open(ctrl)) + return 0; + else + return -1; +} + +int libvchan_is_open(struct libvchan* ctrl) +{ + if (ctrl->is_server) + return ctrl->server_persist || ctrl->ring->cli_live; + else + return ctrl->ring->srv_live; +} + +/// The fd to use for select() set +int libvchan_fd_for_select(struct libvchan *ctrl) +{ + return ctrl->event_fd; +} + +void libvchan_close(struct libvchan *ctrl) +{ + if (!ctrl) + return; + if (ctrl->ring) { + if (ctrl->is_server) + ctrl->ring->srv_live = 0; + else + ctrl->ring->cli_live = 0; + munmap(ctrl->ring, PAGE_SIZE); + } + if (ctrl->event_fd != -1) { + if (ctrl->event_port > 0 && ctrl->ring) + do_notify(ctrl); + close(ctrl->event_fd); + } + if (ctrl->read.order >= PAGE_SHIFT) + munmap(ctrl->read.buffer, 1 << ctrl->read.order); + if (ctrl->write.order >= PAGE_SHIFT) + munmap(ctrl->write.buffer, 1 << ctrl->write.order); + free(ctrl); +} diff --git a/tools/libvchan/libvchan.h b/tools/libvchan/libvchan.h new file mode 100644 index 0000000..a6c08f4 --- /dev/null +++ b/tools/libvchan/libvchan.h @@ -0,0 +1,141 @@ +/** + * @file + * @section AUTHORS + * + * Copyright (C) 2010 Rafal Wojtczuk <rafal@xxxxxxxxxxxxxxxxxxxxxx> + * + * Authors: + * Rafal Wojtczuk <rafal@xxxxxxxxxxxxxxxxxxxxxx> + * Daniel De Graaf <dgdegra@xxxxxxxxxxxxx> + * + * @section LICENSE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * @section DESCRIPTION + * + * Originally borrowed from the Qubes OS Project, http://www.qubes-os.org, + * this code has been substantially rewritten to use the gntdev and gntalloc + * devices instead of raw MFN's and map_foreign_range. + * + * This is a library for inter-domain communication. A standard Xen ring + * buffer is used, with a datagram-based interface built on top. The grant + * reference is expected to be shared through some out-of-band mechanism + * such as XenStore. + */ + +#include <stdint.h> +#include <sys/types.h> +#include <xen/sys/evtchn.h> + +struct ring_shared { + uint32_t cons, prod; +}; + +/// struct vchan_interface is placed in memory shared between domains +struct vchan_interface { + // standard consumer/producer interface, one pair per buffer + // left is client write, server read + // right is client read, server write + struct ring_shared left, right; + // size of the rings, which determines their location + // 10 - at offset 1024 in ring's page + // 11 - at offset 2048 in ring's page + // 12+ - uses 2^(N-12) grants to describe the multi-page ring + // These should remain constant once the page is shared. + // Only one of the two orders can be 10 (or 11). + uint16_t left_order, right_order; + // Shutdown detection + uint8_t cli_live, srv_live; + uint16_t debug; + // Grant list: ordering is left, right. Must not extend into actual ring + // or grow beyond the end of the initial shared page. + // These should remain constant once the page is shared, to allow + // for possible remapping by a client that restarts. + uint32_t grants[0]; +}; + +struct libvchan_ring { + // Pointer into the shared page. Offsets into buffer + struct ring_shared* shr; + // ring data + void* buffer; + // size of the ring is (1 << order). + // This is used to constrain offsets to the buffer. + // (we can't trust the order in the shared page to remain constant) + int order; +}; + +/// struct libvchan is a control structure, passed to all library calls +struct libvchan { + // person we communicate with + int other_domain_id; + // "port" we communicate on (allows multiple vchans to exist in xenstore) + int device_number; + // Shared ring page, mapped using gntdev or gntalloc + // Note that the FD for gntdev or gntalloc has already been closed. + struct vchan_interface *ring; + // event channel interface (needs port for API) + int event_fd; + uint32_t event_port; + // informative flag + int is_server:1; + int server_persist:1; + struct libvchan_ring read, write; +}; + +/** + * Set up a vchan, including granting pages + * @param domain The peer domain that will be connecting + * @param devno A device number, used to identify this vchan in xenstore + * @param send_min The minimum size (in bytes) of the send ring (left) + * @param recv_min The minimum size (in bytes) of the receive ring (right) + * @return The structure, or NULL in case of an error + */ +struct libvchan *libvchan_server_init(int domain, int devno, size_t read_min, size_t write_min); +/** + * Connect to an existing vchan. Note: you can reconnect to an existing vchan + * safely, however no locking is performed, so you must prevent multiple clients + * from connecting to a single server. + * + * @param domain The peer domain to connect to + * @param devno A device number, used to identify this vchan in xenstore + * @return The structure, or NULL in case of an error + */ +struct libvchan *libvchan_client_init(int domain, int devno); +/** + * Close a vchan. This deallocates the vchan and attempts to free its + * resources. The other side is notified of the close, but can still read any + * data pending prior to the close. + */ +void libvchan_close(struct libvchan *ctrl); + +// reads exactly size or aborts +int libvchan_recv(struct libvchan *ctrl, void *data, size_t size); +// reads up to size bytes (including zero) without blocking +int libvchan_read(struct libvchan *ctrl, void *data, size_t size); +// sends entire buffer or aborts +int libvchan_send(struct libvchan *ctrl, const void *data, size_t size); +// sends as much data as possible without blocking +int libvchan_write(struct libvchan *ctrl, const void *data, size_t size); +// waits for reads or writes to unblock, or for a close +int libvchan_wait(struct libvchan *ctrl); +// (only) when this FD is readable, libvchan_wait() will not block +int libvchan_fd_for_select(struct libvchan *ctrl); +// return 0 when one side has called libvchan_close() or crashed +// return 1 when both sides are open +// return 2 [server only] when no client has yet connected +int libvchan_is_open(struct libvchan* ctrl); +int libvchan_data_ready(struct libvchan *ctrl); +int libvchan_buffer_space(struct libvchan *ctrl); diff --git a/tools/libvchan/node-select.c b/tools/libvchan/node-select.c new file mode 100644 index 0000000..a7c614b --- /dev/null +++ b/tools/libvchan/node-select.c @@ -0,0 +1,161 @@ +/** + * @file + * @section AUTHORS + * + * Copyright (C) 2010 Rafal Wojtczuk <rafal@xxxxxxxxxxxxxxxxxxxxxx> + * + * Authors: + * Rafal Wojtczuk <rafal@xxxxxxxxxxxxxxxxxxxxxx> + * Daniel De Graaf <dgdegra@xxxxxxxxxxxxx> + * + * @section LICENSE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * @section DESCRIPTION + * + * This is a test program for libvchan. Communications are bidirectional, + * with either server (grant offeror) or client able to read and write. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include "libvchan.h" + +void usage(char** argv) +{ + fprintf(stderr, "usage:\n" + "\t%s [client|server] domainid nodeid [rbufsiz wbufsiz]\n", + argv[0]); + exit(1); +} + +#define BUFSIZE 5000 +char inbuf[BUFSIZE]; +char outbuf[BUFSIZE]; +int insiz = 0; +int outsiz = 0; +struct libvchan *ctrl = 0; + +void vchan_wr() { + if (!insiz) + return; + int ret = libvchan_write(ctrl, inbuf, insiz); + if (ret < 0) { + fprintf(stderr, "vchan write failed\n"); + exit(1); + } + if (ret > 0) { + insiz -= ret; + memmove(inbuf, inbuf + ret, insiz); + } +} + +void stdout_wr() { + if (!outsiz) + return; + int ret = write(1, outbuf, outsiz); + if (ret < 0 && errno != EAGAIN) + exit(1); + if (ret > 0) { + outsiz -= ret; + memmove(outbuf, outbuf + ret, outsiz); + } +} + +/** + Simple libvchan application, both client and server. + Both sides may write and read, both from the libvchan and from + stdin/stdout (just like netcat). +*/ + +int main(int argc, char **argv) +{ + int ret; + int libvchan_fd; + if (argc < 4) + usage(argv); + if (!strcmp(argv[1], "server")) { + int rsiz = argc > 4 ? atoi(argv[4]) : 0; + int wsiz = argc > 5 ? atoi(argv[5]) : 0; + ctrl = libvchan_server_init(atoi(argv[2]), atoi(argv[3]), rsiz, wsiz); + } else if (!strcmp(argv[1], "client")) + ctrl = libvchan_client_init(atoi(argv[2]), atoi(argv[3])); + else + usage(argv); + if (!ctrl) { + perror("libvchan_*_init"); + exit(1); + } + + fcntl(0, F_SETFL, O_NONBLOCK); + fcntl(1, F_SETFL, O_NONBLOCK); + + libvchan_fd = libvchan_fd_for_select(ctrl); + for (;;) { + fd_set rfds; + fd_set wfds; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + if (insiz != BUFSIZE) + FD_SET(0, &rfds); + if (outsiz) + FD_SET(1, &wfds); + FD_SET(libvchan_fd, &rfds); + ret = select(libvchan_fd + 1, &rfds, &wfds, NULL, NULL); + if (ret < 0) { + perror("select"); + exit(1); + } + if (FD_ISSET(0, &rfds)) { + ret = read(0, inbuf + insiz, BUFSIZE - insiz); + if (ret < 0 && errno != EAGAIN) + exit(1); + if (ret == 0) { + while (insiz) { + vchan_wr(); + libvchan_wait(ctrl); + } + return 0; + } + if (ret) + insiz += ret; + vchan_wr(); + } + if (FD_ISSET(libvchan_fd, &rfds)) { + libvchan_wait(ctrl); + vchan_wr(); + } + if (FD_ISSET(1, &wfds)) + stdout_wr(); + while (libvchan_data_ready(ctrl) && outsiz < BUFSIZE) { + ret = libvchan_read(ctrl, outbuf + outsiz, BUFSIZE - outsiz); + if (ret < 0) + exit(1); + outsiz += ret; + stdout_wr(); + } + if (!libvchan_is_open(ctrl)) { + fcntl(1, F_SETFL, 0); + while (outsiz) + stdout_wr(); + return 0; + } + } +} diff --git a/tools/libvchan/node.c b/tools/libvchan/node.c new file mode 100644 index 0000000..d00a50f --- /dev/null +++ b/tools/libvchan/node.c @@ -0,0 +1,169 @@ +/** + * @file + * @section AUTHORS + * + * Copyright (C) 2010 Rafal Wojtczuk <rafal@xxxxxxxxxxxxxxxxxxxxxx> + * + * Authors: + * Rafal Wojtczuk <rafal@xxxxxxxxxxxxxxxxxxxxxx> + * Daniel De Graaf <dgdegra@xxxxxxxxxxxxx> + * + * @section LICENSE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * @section DESCRIPTION + * + * This is a test program for libvchan. Communications are in one direction, + * either server (grant offeror) to client or vice versa. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <time.h> + +#include "libvchan.h" + +int libvchan_write_all(struct libvchan *ctrl, char *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = libvchan_write(ctrl, buf + written, size - written); + if (ret <= 0) { + perror("write"); + exit(1); + } + written += ret; + } + return size; +} + +int write_all(int fd, char *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = write(fd, buf + written, size - written); + if (ret <= 0) { + perror("write"); + exit(1); + } + written += ret; + } + return size; +} + +void usage(char** argv) +{ + fprintf(stderr, "usage:\n" + "%s [client|server] [read|write] domid nodeid\n", argv[0]); + exit(1); +} + +#define BUFSIZE 5000 +char buf[BUFSIZE]; +void reader(struct libvchan *ctrl) +{ + int size; + for (;;) { + size = rand() % (BUFSIZE - 1) + 1; + size = libvchan_read(ctrl, buf, size); + fprintf(stderr, "#"); + if (size < 0) { + perror("read vchan"); + libvchan_close(ctrl); + exit(1); + } + if (size == 0) + break; + size = write_all(1, buf, size); + if (size < 0) { + perror("stdout write"); + exit(1); + } + if (size == 0) { + perror("write size=0?\n"); + exit(1); + } + } +} + +void writer(struct libvchan *ctrl) +{ + int size; + for (;;) { + size = rand() % (BUFSIZE - 1) + 1; + size = read(0, buf, size); + if (size < 0) { + perror("read stdin"); + libvchan_close(ctrl); + exit(1); + } + if (size == 0) + break; + size = libvchan_write_all(ctrl, buf, size); + fprintf(stderr, "#"); + if (size < 0) { + perror("vchan write"); + exit(1); + } + if (size == 0) { + perror("write size=0?\n"); + exit(1); + } + } +} + + +/** + Simple libvchan application, both client and server. + One side does writing, the other side does reading; both from + standard input/output fds. +*/ +int main(int argc, char **argv) +{ + int seed = time(0); + struct libvchan *ctrl = 0; + int wr; + if (argc < 4) + usage(argv); + if (!strcmp(argv[2], "read")) + wr = 0; + else if (!strcmp(argv[2], "write")) + wr = 1; + else + usage(argv); + if (!strcmp(argv[1], "server")) + ctrl = libvchan_server_init(atoi(argv[3]), atoi(argv[4]), 0, 0); + else if (!strcmp(argv[1], "client")) + ctrl = libvchan_client_init(atoi(argv[3]), atoi(argv[4])); + else + usage(argv); + if (!ctrl) { + perror("libvchan_*_init"); + exit(1); + } + + srand(seed); + fprintf(stderr, "seed=%d\n", seed); + if (wr) + writer(ctrl); + else + reader(ctrl); + libvchan_close(ctrl); + return 0; +} -- 1.7.6 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |