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 6/6] xen/gntalloc, gntdev: Add unmap notify ioctl

This ioctl allows the users of a shared page to be notified when
the other end exits abnormally.

Signed-off-by: Daniel De Graaf <dgdegra@xxxxxxxxxxxxx>
---
 drivers/xen/gntalloc.c |   59 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/xen/gntdev.c   |   61 +++++++++++++++++++++++++++++++++++++++++++++++-
 include/xen/gntalloc.h |   28 ++++++++++++++++++++++
 include/xen/gntdev.h   |   27 +++++++++++++++++++++
 4 files changed, 174 insertions(+), 1 deletions(-)

diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c
index d06bf2b..a7ffdfe 100644
--- a/drivers/xen/gntalloc.c
+++ b/drivers/xen/gntalloc.c
@@ -60,11 +60,13 @@
 #include <linux/uaccess.h>
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/highmem.h>
 
 #include <xen/xen.h>
 #include <xen/page.h>
 #include <xen/grant_table.h>
 #include <xen/gntalloc.h>
+#include <xen/events.h>
 
 static int limit = 1024;
 module_param(limit, int, 0644);
@@ -75,6 +77,12 @@ static LIST_HEAD(gref_list);
 static DEFINE_SPINLOCK(gref_lock);
 static int gref_size;
 
+struct notify_info {
+       uint16_t pgoff:12;    /* Bits 0-11: Offset of the byte to clear */
+       uint16_t flags:2;     /* Bits 12-13: Unmap notification flags */
+       int event;            /* Port (event channel) to notify */
+};
+
 /* Metadata on a grant reference. */
 struct gntalloc_gref {
        struct list_head next_gref;  /* list entry gref_list */
@@ -83,6 +91,7 @@ struct gntalloc_gref {
        uint64_t file_index;         /* File offset for mmap() */
        unsigned int users;          /* Use count - when zero, waiting on Xen */
        grant_ref_t gref_id;         /* The grant reference number */
+       struct notify_info notify;   /* Unmap notification */
 };
 
 struct gntalloc_file_private_data {
@@ -164,6 +173,16 @@ undo:
 
 static void __del_gref(struct gntalloc_gref *gref)
 {
+       if (gref->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) {
+               uint8_t *tmp = kmap(gref->page);
+               tmp[gref->notify.pgoff] = 0;
+               kunmap(gref->page);
+       }
+       if (gref->notify.flags & UNMAP_NOTIFY_SEND_EVENT)
+               notify_remote_via_evtchn(gref->notify.event);
+
+       gref->notify.flags = 0;
+
        if (gref->gref_id > 0) {
                if (gnttab_query_foreign_access(gref->gref_id))
                        return;
@@ -349,6 +368,43 @@ dealloc_grant_out:
        return rc;
 }
 
+static long gntalloc_ioctl_unmap_notify(struct gntalloc_file_private_data 
*priv,
+               void __user *arg)
+{
+       struct ioctl_gntalloc_unmap_notify op;
+       struct gntalloc_gref *gref;
+       uint64_t index;
+       int pgoff;
+       int rc;
+
+       if (copy_from_user(&op, arg, sizeof(op)))
+               return -EFAULT;
+
+       index = op.index & ~(PAGE_SIZE - 1);
+       pgoff = op.index & (PAGE_SIZE - 1);
+
+       spin_lock(&gref_lock);
+
+       gref = find_grefs(priv, index, 1);
+       if (!gref) {
+               rc = -ENOENT;
+               goto unlock_out;
+       }
+
+       if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) {
+               rc = -EINVAL;
+               goto unlock_out;
+       }
+
+       gref->notify.flags = op.action;
+       gref->notify.pgoff = pgoff;
+       gref->notify.event = op.event_channel_port;
+       rc = 0;
+ unlock_out:
+       spin_unlock(&gref_lock);
+       return rc;
+}
+
 static long gntalloc_ioctl(struct file *filp, unsigned int cmd,
                unsigned long arg)
 {
@@ -361,6 +417,9 @@ static long gntalloc_ioctl(struct file *filp, unsigned int 
cmd,
        case IOCTL_GNTALLOC_DEALLOC_GREF:
                return gntalloc_ioctl_dealloc(priv, (void __user *)arg);
 
+       case IOCTL_GNTALLOC_SET_UNMAP_NOTIFY:
+               return gntalloc_ioctl_unmap_notify(priv, (void __user *)arg);
+
        default:
                return -ENOIOCTLCMD;
        }
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 58fddf3..91706c2 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -37,6 +37,7 @@
 #include <xen/xen.h>
 #include <xen/grant_table.h>
 #include <xen/gntdev.h>
+#include <xen/events.h>
 #include <asm/xen/hypervisor.h>
 #include <asm/xen/hypercall.h>
 #include <asm/xen/page.h>
@@ -63,6 +64,13 @@ struct gntdev_priv {
        struct mmu_notifier mn;
 };
 
+struct unmap_notify {
+       int flags;
+       /* Address relative to the start of the grant_map */
+       int addr;
+       int event;
+};
+
 struct grant_map {
        struct list_head next;
        struct vm_area_struct *vma;
@@ -71,6 +79,7 @@ struct grant_map {
        int flags;
        int is_mapped;
        atomic_t users;
+       struct unmap_notify notify;
        struct ioctl_gntdev_grant_ref *grants;
        struct gnttab_map_grant_ref   *map_ops;
        struct gnttab_unmap_grant_ref *unmap_ops;
@@ -165,7 +174,7 @@ static struct grant_map *gntdev_find_map_index(struct 
gntdev_priv *priv,
        list_for_each_entry(map, &priv->maps, next) {
                if (map->index != index)
                        continue;
-               if (map->count != count)
+               if (count && map->count != count)
                        continue;
                return map;
        }
@@ -184,6 +193,10 @@ static void gntdev_put_map(struct grant_map *map)
 
        atomic_sub(map->count, &pages_mapped);
 
+       if (map->notify.flags & UNMAP_NOTIFY_SEND_EVENT) {
+               notify_remote_via_evtchn(map->notify.event);
+       }
+
        if (map->pages) {
                if (!use_ptemod)
                        unmap_grant_pages(map, 0, map->count);
@@ -273,6 +286,16 @@ static int unmap_grant_pages(struct grant_map *map, int 
offset, int pages)
 {
        int i, err = 0;
 
+       if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) {
+               int pgno = (map->notify.addr >> PAGE_SHIFT);
+               if (pgno >= offset && pgno < offset + pages) {
+                       uint8_t *tmp = kmap(map->pages[pgno]);
+                       tmp[map->notify.addr & (PAGE_SIZE-1)] = 0;
+                       kunmap(map->pages[pgno]);
+                       map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE;
+               }
+       }
+
        pr_debug("map %d+%d [%d+%d]\n", map->index, map->count, offset, pages);
        err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages, pages);
        if (err)
@@ -518,6 +541,39 @@ static long gntdev_ioctl_get_offset_for_vaddr(struct 
gntdev_priv *priv,
        return 0;
 }
 
+static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u)
+{
+       struct ioctl_gntdev_unmap_notify op;
+       struct grant_map *map;
+       int rc;
+
+       if (copy_from_user(&op, u, sizeof(op)))
+               return -EFAULT;
+
+       if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT))
+               return -EINVAL;
+
+       spin_lock(&priv->lock);
+
+       list_for_each_entry(map, &priv->maps, next) {
+               uint64_t begin = map->index << PAGE_SHIFT;
+               uint64_t end = (map->index + map->count) << PAGE_SHIFT;
+               if (op.index >= begin && op.index < end)
+                       goto found;
+       }
+       rc = -ENOENT;
+       goto unlock_out;
+
+ found:
+       map->notify.flags = op.action;
+       map->notify.addr = op.index - (map->index << PAGE_SHIFT);
+       map->notify.event = op.event_channel_port;
+       rc = 0;
+ unlock_out:
+       spin_unlock(&priv->lock);
+       return rc;
+}
+
 static long gntdev_ioctl(struct file *flip,
                         unsigned int cmd, unsigned long arg)
 {
@@ -534,6 +590,9 @@ static long gntdev_ioctl(struct file *flip,
        case IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR:
                return gntdev_ioctl_get_offset_for_vaddr(priv, ptr);
 
+       case IOCTL_GNTDEV_SET_UNMAP_NOTIFY:
+               return gntdev_ioctl_notify(priv, ptr);
+
        default:
                pr_debug("priv %p, unknown cmd %x\n", priv, cmd);
                return -ENOIOCTLCMD;
diff --git a/include/xen/gntalloc.h b/include/xen/gntalloc.h
index bc3b85e..257cc8d 100644
--- a/include/xen/gntalloc.h
+++ b/include/xen/gntalloc.h
@@ -47,4 +47,32 @@ struct ioctl_gntalloc_dealloc_gref {
        /* 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 */
+       /* 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
+
 #endif /* __LINUX_PUBLIC_GNTALLOC_H__ */
diff --git a/include/xen/gntdev.h b/include/xen/gntdev.h
index eb23f41..5d9b9b4 100644
--- a/include/xen/gntdev.h
+++ b/include/xen/gntdev.h
@@ -116,4 +116,31 @@ struct ioctl_gntdev_set_max_grants {
        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 */
+       /* 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
+
 #endif /* __LINUX_PUBLIC_GNTDEV_H__ */
-- 
1.7.3.4


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