diff options
Diffstat (limited to 'drivers/xen/gntdev.c')
-rw-r--r-- | drivers/xen/gntdev.c | 34 |
1 files changed, 32 insertions, 2 deletions
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index afca14d9042e..99d8151c824a 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) |
@@ -312,7 +314,8 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages) | |||
312 | } | 314 | } |
313 | } | 315 | } |
314 | 316 | ||
315 | err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset, pages); | 317 | err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset, |
318 | pages, true); | ||
316 | if (err) | 319 | if (err) |
317 | return err; | 320 | return err; |
318 | 321 | ||
@@ -599,6 +602,8 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) | |||
599 | struct ioctl_gntdev_unmap_notify op; | 602 | struct ioctl_gntdev_unmap_notify op; |
600 | struct grant_map *map; | 603 | struct grant_map *map; |
601 | int rc; | 604 | int rc; |
605 | int out_flags; | ||
606 | unsigned int out_event; | ||
602 | 607 | ||
603 | if (copy_from_user(&op, u, sizeof(op))) | 608 | if (copy_from_user(&op, u, sizeof(op))) |
604 | return -EFAULT; | 609 | return -EFAULT; |
@@ -606,6 +611,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)) | 611 | if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) |
607 | return -EINVAL; | 612 | return -EINVAL; |
608 | 613 | ||
614 | /* We need to grab a reference to the event channel we are going to use | ||
615 | * to send the notify before releasing the reference we may already have | ||
616 | * (if someone has called this ioctl twice). This is required so that | ||
617 | * it is possible to change the clear_byte part of the notification | ||
618 | * without disturbing the event channel part, which may now be the last | ||
619 | * reference to that event channel. | ||
620 | */ | ||
621 | if (op.action & UNMAP_NOTIFY_SEND_EVENT) { | ||
622 | if (evtchn_get(op.event_channel_port)) | ||
623 | return -EINVAL; | ||
624 | } | ||
625 | |||
626 | out_flags = op.action; | ||
627 | out_event = op.event_channel_port; | ||
628 | |||
609 | spin_lock(&priv->lock); | 629 | spin_lock(&priv->lock); |
610 | 630 | ||
611 | list_for_each_entry(map, &priv->maps, next) { | 631 | list_for_each_entry(map, &priv->maps, next) { |
@@ -624,12 +644,22 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) | |||
624 | goto unlock_out; | 644 | goto unlock_out; |
625 | } | 645 | } |
626 | 646 | ||
647 | out_flags = map->notify.flags; | ||
648 | out_event = map->notify.event; | ||
649 | |||
627 | map->notify.flags = op.action; | 650 | map->notify.flags = op.action; |
628 | map->notify.addr = op.index - (map->index << PAGE_SHIFT); | 651 | map->notify.addr = op.index - (map->index << PAGE_SHIFT); |
629 | map->notify.event = op.event_channel_port; | 652 | map->notify.event = op.event_channel_port; |
653 | |||
630 | rc = 0; | 654 | rc = 0; |
655 | |||
631 | unlock_out: | 656 | unlock_out: |
632 | spin_unlock(&priv->lock); | 657 | spin_unlock(&priv->lock); |
658 | |||
659 | /* Drop the reference to the event channel we did not save in the map */ | ||
660 | if (out_flags & UNMAP_NOTIFY_SEND_EVENT) | ||
661 | evtchn_put(out_event); | ||
662 | |||
633 | return rc; | 663 | return rc; |
634 | } | 664 | } |
635 | 665 | ||