]> Pileus Git - ~andy/linux/blobdiff - drivers/xen/gntdev.c
Merge branches 'perf-urgent-for-linus', 'x86-urgent-for-linus' and 'sched-urgent...
[~andy/linux] / drivers / xen / gntdev.c
index afca14d9042e6cd2ae03f238143ac8188aaf35fb..1ffd03bf8e10dcb6b015e3e0b94a2a460f0d0dcd 100644 (file)
@@ -193,8 +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)
+       if (map->notify.flags & UNMAP_NOTIFY_SEND_EVENT) {
                notify_remote_via_evtchn(map->notify.event);
+               evtchn_put(map->notify.event);
+       }
 
        if (map->pages) {
                if (!use_ptemod)
@@ -312,7 +314,8 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages)
                }
        }
 
-       err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset, pages);
+       err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset,
+                               pages, true);
        if (err)
                return err;
 
@@ -599,6 +602,8 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u)
        struct ioctl_gntdev_unmap_notify op;
        struct grant_map *map;
        int rc;
+       int out_flags;
+       unsigned int out_event;
 
        if (copy_from_user(&op, u, sizeof(op)))
                return -EFAULT;
@@ -606,6 +611,21 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u)
        if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT))
                return -EINVAL;
 
+       /* We need to grab a reference to the event channel we are going to use
+        * to send the notify before releasing the reference we may already have
+        * (if someone has called this ioctl twice). This is required so that
+        * it is possible to change the clear_byte part of the notification
+        * without disturbing the event channel part, which may now be the last
+        * reference to that event channel.
+        */
+       if (op.action & UNMAP_NOTIFY_SEND_EVENT) {
+               if (evtchn_get(op.event_channel_port))
+                       return -EINVAL;
+       }
+
+       out_flags = op.action;
+       out_event = op.event_channel_port;
+
        spin_lock(&priv->lock);
 
        list_for_each_entry(map, &priv->maps, next) {
@@ -624,12 +644,22 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u)
                goto unlock_out;
        }
 
+       out_flags = map->notify.flags;
+       out_event = map->notify.event;
+
        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);
+
+       /* Drop the reference to the event channel we did not save in the map */
+       if (out_flags & UNMAP_NOTIFY_SEND_EVENT)
+               evtchn_put(out_event);
+
        return rc;
 }
 
@@ -692,7 +722,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma)
        vma->vm_flags |= VM_RESERVED|VM_DONTEXPAND;
 
        if (use_ptemod)
-               vma->vm_flags |= VM_DONTCOPY|VM_PFNMAP;
+               vma->vm_flags |= VM_DONTCOPY;
 
        vma->vm_private_data = map;