[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v6] Userspace grant communication



Changes since v5:
  - Added a tested xen version to workaround in #4
  - Cleaned up variable names & structures
  - Clarified some of the cleanup in gntalloc
  - Removed copyright statement from public-domain files

[PATCH 1/6] xen-gntdev: Change page limit to be global instead of per-open
[PATCH 2/6] xen-gntdev: Use find_vma rather than iterating our vma list manually
[PATCH 3/6] xen-gntdev: Add reference counting to maps
[PATCH 4/6] xen-gntdev: Support mapping in HVM domains
[PATCH 5/6] xen-gntalloc: Userspace grant allocation driver
[PATCH 6/6] xen/gntalloc,gntdev: Add unmap notify ioctl

Test/Demo code (also updated):

#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/mman.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;
};

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

#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];
};
#define GNTDEV_MAP_WRITABLE 0x1

#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;
};

/*
 * 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 */
        /* Index of a byte in the page */
        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

/*
 * 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 */
        /* Index of a byte in the page */
        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

#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif


int a_fd;
int d_fd;

struct shr_page {
        uint64_t id;
        char buffer[64];
        uint8_t notifies[8];
};

struct data {
        struct shr_page* mem;
        int handle;
} items[128];

void sa(int id)
{
        struct ioctl_gntalloc_alloc_gref arg = {
                .domid = id,
                .flags = GNTALLOC_FLAG_WRITABLE,
                .count = 1
        };
        int rv = ioctl(a_fd, IOCTL_GNTALLOC_ALLOC_GREF, &arg);
        if (rv) {
                printf("src-add error: %s (rv=%d)\n", strerror(errno), rv);
                return;
        }
        int i=0;
        while (items[i].mem) i++;
        items[i].mem = mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, a_fd, 
arg.index);
        if (items[i].mem == MAP_FAILED) {
                items[i].mem = 0;
                printf("mmap failed: SHOULD NOT HAPPEN\n");
                return;
        }
        items[i].handle = arg.index;
        printf("Created shared page with domain %d, grant #%d. Mapped locally 
at %d=%p\n",
                id, arg.gref_ids[0], arg.index, items[i].mem);

        items[i].mem->id = rand() | ((long)(getpid()) << 32);
        items[i].mem->notifies[0] = 1;
        struct ioctl_gntalloc_unmap_notify uarg = {
                .index = arg.index + offsetof(struct shr_page, notifies[0]),
                .action = UNMAP_NOTIFY_CLEAR_BYTE
        };
        rv = ioctl(a_fd, IOCTL_GNTALLOC_SET_UNMAP_NOTIFY, &uarg);
        if (rv)
                printf("gntalloc unmap notify error: %s (rv=%d)\n", 
strerror(errno), rv);
}

void sd(int ref) {
        struct ioctl_gntalloc_dealloc_gref arg = {
                .index = ref,
                .count = 1
        };

        int rv = ioctl(a_fd, IOCTL_GNTALLOC_DEALLOC_GREF, &arg);
        if (rv)
                printf("src-del error: %s (rv=%d)\n", strerror(errno), rv);
        else
                printf("Stopped offering grant at offset %d\n", ref);
}

void mm(int domid, int refid) {
        struct ioctl_gntdev_map_grant_ref arg = {
                .count = 1,
                .refs[0].domid = domid,
                .refs[0].ref = refid,
        };
        int rv = ioctl(d_fd, IOCTL_GNTDEV_MAP_GRANT_REF, &arg);
        if (rv) {
                printf("Could not map grant %d.%d: %s (rv=%d)\n", domid, refid, 
strerror(errno), rv);
                return;
        }
        int i=0,j=1;
        while (items[i].mem) i++;
        items[i].mem = mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, d_fd, 
arg.index);
        if (items[i].mem == MAP_FAILED) {
                items[i].mem = 0;
                printf("Could not map grant %d.%d: %s (map failed)\n", domid, 
refid, strerror(errno), rv);
                return;
        }
        items[i].handle = arg.index;
        printf("Mapped grant %d.%d as %d=%p\n", domid, refid, arg.index, 
items[i].mem);

        while (items[i].mem->notifies[j]) j++;
        items[i].mem->notifies[j] = 1;
        struct ioctl_gntalloc_unmap_notify uarg = {
                .index = arg.index + offsetof(struct shr_page, notifies[j]),
                .action = UNMAP_NOTIFY_CLEAR_BYTE
        };
        rv = ioctl(d_fd, IOCTL_GNTDEV_SET_UNMAP_NOTIFY, &uarg);
        if (rv)
                printf("gntdev unmap notify error: %s (rv=%d)\n", 
strerror(errno), rv);
}

void gu(int index) {
        struct ioctl_gntdev_unmap_grant_ref arg = {
                .index = index,
                .count = 1,
        };
        int rv = ioctl(d_fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &arg);
        if (rv)
                printf("gu error: %s (rv=%d)\n", strerror(errno), rv);
        else
                printf("Unhooked mapped grant at offset %d\n", index);
}

void mu(void* addr) {
        int i = 0;
        munmap(addr, 4096);
        while (i < 128)
        {
                if (items[i].mem == addr)
                        items[i].mem = 0;
                i++;
        }
        printf("Unmapped page at %p\n", addr);
}

void show(char* word) {
        int i;
        int wlen = strlen(word);
        for(i=0; i < 128; i++) {
                if (!items[i].mem)
                        continue;
                memmove(items[i].mem->buffer + wlen, items[i].mem->buffer, 63 - 
wlen);
                memcpy(items[i].mem->buffer, word, wlen);
                printf("%02d(%ld,%d): id %16lx n=%d%d%d%d%d%d%d%d b=%s\n",
                        i, items[i].mem, items[i].handle, items[i].mem->id,
                        items[i].mem->notifies[0], items[i].mem->notifies[1], 
items[i].mem->notifies[2], items[i].mem->notifies[3], 
                        items[i].mem->notifies[4], items[i].mem->notifies[5], 
items[i].mem->notifies[6], items[i].mem->notifies[7], 
                        items[i].mem->buffer);
        }
        printf("END\n");
}

int main(int argc, char** argv) {
        a_fd = open("/dev/xen/gntalloc", O_RDWR);
        d_fd = open("/dev/xen/gntdev", O_RDWR);
        printf(
                "add <domid>           return gntref, address\n"
                "map <domid> <ref>     return index, address\n"
                "adel <gntref>         delete <add> internal\n"
                "ddel <index>          delete <map> internal\n"
                "unmap <address>       unmap memory\n"
                "show                  show all pages\n"
                "<word>                append word to all mapped pages, show\n"
                " PID %x\n", getpid()
        );
        while (1) {
                char line[80];
                char word[80];
                long a, b;
                printf("\n> ");
                fflush(stdout);
                fgets(line, 80, stdin);
                sscanf(line, "%s %ld %ld", word, &a, &b);
                if (!strcmp(word, "add")) {
                        sa(a);
                } else if (!strcmp(word, "map")) {
                        mm(a, b);
                } else if (!strcmp(word, "adel")) {
                        sd(a);
                } else if (!strcmp(word, "ddel")) {
                        gu(a);
                } else if (!strcmp(word, "unmap")) {
                        mu((void*)a);
                } else if (!strcmp(word, "show")) {
                        show("");
                } else {
                        show(word);
                }
        }
}

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.