diff options
Diffstat (limited to 'drivers/xen/gntdev.c')
-rw-r--r-- | drivers/xen/gntdev.c | 31 |
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 | ||