# HG changeset patch # User cegger # Date 1351254310 -7200 NetBSD gnttab support diff -r 0f9a695ef781 -r 7a5eb2d4ffeb tools/include/xen-sys/NetBSD/gntdev.h --- /dev/null +++ b/tools/include/xen-sys/NetBSD/gntdev.h @@ -0,0 +1,136 @@ +/****************************************************************************** + * 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 __NetBSD_PUBLIC_GNTDEV_H__ +#define __NetBSD_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 \ + _IOWR('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 \ + _IOW('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 \ + _IOWR('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 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 \ + _IOR('G', 3, sizeof(struct ioctl_gntdev_unmap_notify)) +struct ioctl_gntdev_unmap_notify { + /* IN parameters */ + /* Offset in the file descriptor for a byte within the page. This + * offset is the result of the IOCTL_GNTDEV_MAP_GRANT_REF and is the + * same as is used with mmap(). If using UNMAP_NOTIFY_CLEAR_BYTE, this + * is the byte within the page to be cleared. + */ + 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 /* __NetBSD_PUBLIC_GNTDEV_H__ */ diff -r 0f9a695ef781 -r 7a5eb2d4ffeb tools/libxc/xc_netbsd.c --- a/tools/libxc/xc_netbsd.c +++ b/tools/libxc/xc_netbsd.c @@ -21,6 +21,7 @@ #include "xc_private.h" #include <xen/sys/evtchn.h> +#include <xen/sys/gntdev.h> #include <unistd.h> #include <fcntl.h> #include <malloc.h> @@ -390,7 +391,174 @@ void *xc_memalign(xc_interface *xch, siz return valloc(size); } -static struct xc_osdep_ops *netbsd_osdep_init(xc_interface *xch, enum xc_osdep_type type) +#define DEVXEN "/dev/xen/" + +static xc_osdep_handle +netbsd_gnttab_open(xc_gnttab *xcg) +{ + int fd; + + fd = open(DEVXEN "gntdev", O_RDWR); + if (fd == -1) + return XC_OSDEP_OPEN_ERROR; + + return (xc_osdep_handle)fd; +} + +static int +netbsd_gnttab_close(xc_gnttab *xcg, xc_osdep_handle h) +{ + int fd = (int)h; + return close(fd); +} + +static void * +netbsd_gnttab_grant_map(xc_gnttab *xch, xc_osdep_handle h, + uint32_t count, int flags, int prot, + uint32_t *domids, uint32_t *refs, + uint32_t notify_offset, + evtchn_port_t notify_port) +{ + int fd = (int)h; + struct ioctl_gntdev_map_grant_ref *map; + void *addr = NULL; + int domids_stride = 1; + int i; + + if (flags & XC_GRANT_MAP_SINGLE_DOMAIN) + domids_stride = 0; + + map = malloc(sizeof(*map) + + (count - 1) * sizeof(struct ioctl_gntdev_map_grant_ref)); + if ( map == NULL ) + return NULL; + + for ( i = 0; i < count; i++ ) + { + map->refs[i].domid = domids[i * domids_stride]; + map->refs[i].ref = refs[i]; + } + + map->count = count; + + if ( ioctl(fd, IOCTL_GNTDEV_MAP_GRANT_REF, map) ) { + PERROR("netbsd_gnttab_map_grant_refs: ioctl MAP_GRANT_REF failed"); + goto out; + } + + retry: + addr = mmap(NULL, XC_PAGE_SIZE * count, prot, MAP_SHARED, fd, + map->index); + if (addr == MAP_FAILED && errno == EAGAIN) { + /* + * The grant hypercall can return EAGAIN if the granted page + * is swapped out. Since the paging daemon may be in the same + * domain, the hypercall cannot block without causing a + * deadlock. + * + * Because there are no notifications when the page is swapped + * in, wait a bit before retrying, and hope that the page will + * arrive eventually. + */ + usleep(1000); + goto retry; + } + + if (addr != MAP_FAILED) + { + int rv = 0; + struct ioctl_gntdev_unmap_notify notify; + + notify.index = map->index; + notify.action = 0; + if (notify_offset >= 0 && notify_offset < XC_PAGE_SIZE * count) { + notify.index += notify_offset; + notify.action |= UNMAP_NOTIFY_CLEAR_BYTE; + } + if (notify.action) + rv = ioctl(fd, IOCTL_GNTDEV_SET_UNMAP_NOTIFY, ¬ify); + if (rv) { + PERROR("netbsd_gnttab_grant_map: ioctl SET_UNMAP_NOTIFY failed"); + munmap(addr, count * XC_PAGE_SIZE); + addr = MAP_FAILED; + } + } + + if (addr == MAP_FAILED) + { + int saved_errno = errno; + struct ioctl_gntdev_unmap_grant_ref unmap_grant; + + /* Unmap the driver slots used to store the grant information. */ + PERROR("xc_gnttab_map_grant_refs: mmap failed"); + unmap_grant.index = map->index; + unmap_grant.count = count; + ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant); + errno = saved_errno; + addr = NULL; + } + + out: + free(map); + + return addr; +} + +static int +netbsd_gnttab_munmap(xc_gnttab *xcg, xc_osdep_handle h, + void *start_address, uint32_t count) +{ + int fd = (int)h; + struct ioctl_gntdev_get_offset_for_vaddr get_offset; + struct ioctl_gntdev_unmap_grant_ref unmap_grant; + int rc; + + if ( start_address == NULL ) + { + errno = EINVAL; + return -1; + } + + /* First, it is necessary to get the offset which was initially used to + * mmap() the pages. + */ + get_offset.vaddr = (unsigned long)start_address; + rc = ioctl(fd, IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR, &get_offset); + if ( rc ) + return rc; + + if ( get_offset.count != count ) + { + errno = EINVAL; + return -1; + } + + /* Next, unmap the memory. */ + rc = munmap(start_address, count * getpagesize()); + if ( rc ) + return rc; + + /* Finally, unmap the driver slots used to store the grant information. */ + unmap_grant.index = get_offset.offset; + unmap_grant.count = count; + rc = ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant); + if ( rc ) + return rc; + return 0; +} + +static struct xc_osdep_ops netbsd_gnttab_ops = { + .open = &netbsd_gnttab_open, + .close = &netbsd_gnttab_close, + + .u.gnttab = { + .grant_map = &netbsd_gnttab_grant_map, + .munmap = &netbsd_gnttab_munmap, + }, +}; + +static struct xc_osdep_ops * +netbsd_osdep_init(xc_interface *xch, enum xc_osdep_type type) { switch ( type ) { @@ -398,6 +566,8 @@ static struct xc_osdep_ops *netbsd_osdep return &netbsd_privcmd_ops; case XC_OSDEP_EVTCHN: return &netbsd_evtchn_ops; + case XC_OSDEP_GNTTAB: + return &netbsd_gnttab_ops; default: return NULL; }