aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/gntdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/xen/gntdev.c')
-rw-r--r--drivers/xen/gntdev.c31
1 files changed, 30 insertions, 1 deletions
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 39871326afa2..a7308559a26a 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -193,8 +193,10 @@ static void gntdev_put_map(struct grant_map *map)
193 193
194 atomic_sub(map->count, &pages_mapped); 194 atomic_sub(map->count, &pages_mapped);
195 195
196 if (map->notify.flags & UNMAP_NOTIFY_SEND_EVENT) 196 if (map->notify.flags & UNMAP_NOTIFY_SEND_EVENT) {
197 notify_remote_via_evtchn(map->notify.event); 197 notify_remote_via_evtchn(map->notify.event);
198 evtchn_put(map->notify.event);
199 }
198 200
199 if (map->pages) { 201 if (map->pages) {
200 if (!use_ptemod) 202 if (!use_ptemod)
@@ -599,6 +601,8 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u)
599 struct ioctl_gntdev_unmap_notify op; 601 struct ioctl_gntdev_unmap_notify op;
600 struct grant_map *map; 602 struct grant_map *map;
601 int rc; 603 int rc;
604 int out_flags;
605 unsigned int out_event;
602 606
603 if (copy_from_user(&op, u, sizeof(op))) 607 if (copy_from_user(&op, u, sizeof(op)))
604 return -EFAULT; 608 return -EFAULT;
@@ -606,6 +610,21 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u)
606 if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) 610 if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT))
607 return -EINVAL; 611 return -EINVAL;
608 612
613 /* We need to grab a reference to the event channel we are going to use
614 * to send the notify before releasing the reference we may already have
615 * (if someone has called this ioctl twice). This is required so that
616 * it is possible to change the clear_byte part of the notification
617 * without disturbing the event channel part, which may now be the last
618 * reference to that event channel.
619 */
620 if (op.action & UNMAP_NOTIFY_SEND_EVENT) {
621 if (evtchn_get(op.event_channel_port))
622 return -EINVAL;
623 }
624
625 out_flags = op.action;
626 out_event = op.event_channel_port;
627
609 spin_lock(&priv->lock); 628 spin_lock(&priv->lock);
610 629
611 list_for_each_entry(map, &priv->maps, next) { 630 list_for_each_entry(map, &priv->maps, next) {
@@ -624,12 +643,22 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u)
624 goto unlock_out; 643 goto unlock_out;
625 } 644 }
626 645
646 out_flags = map->notify.flags;
647 out_event = map->notify.event;
648
627 map->notify.flags = op.action; 649 map->notify.flags = op.action;
628 map->notify.addr = op.index - (map->index << PAGE_SHIFT); 650 map->notify.addr = op.index - (map->index << PAGE_SHIFT);
629 map->notify.event = op.event_channel_port; 651 map->notify.event = op.event_channel_port;
652
630 rc = 0; 653 rc = 0;
654
631 unlock_out: 655 unlock_out:
632 spin_unlock(&priv->lock); 656 spin_unlock(&priv->lock);
657
658 /* Drop the reference to the event channel we did not save in the map */
659 if (out_flags & UNMAP_NOTIFY_SEND_EVENT)
660 evtchn_put(out_event);
661
633 return rc; 662 return rc;
634} 663}
635 664