diff options
Diffstat (limited to 'drivers/xen/gntdev.c')
-rw-r--r-- | drivers/xen/gntdev.c | 54 |
1 files changed, 42 insertions, 12 deletions
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index c866a62f766d..b0b02a501167 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -479,18 +479,32 @@ static const struct vm_operations_struct gntdev_vmops = { /* ------------------------------------------------------------------ */ -static void unmap_if_in_range(struct gntdev_grant_map *map, +static bool in_range(struct gntdev_grant_map *map, unsigned long start, unsigned long end) { - unsigned long mstart, mend; - int err; - if (!map->vma) - return; + return false; if (map->vma->vm_start >= end) - return; + return false; if (map->vma->vm_end <= start) - return; + return false; + + return true; +} + +static int unmap_if_in_range(struct gntdev_grant_map *map, + unsigned long start, unsigned long end, + bool blockable) +{ + unsigned long mstart, mend; + int err; + + if (!in_range(map, start, end)) + return 0; + + if (!blockable) + return -EAGAIN; + mstart = max(start, map->vma->vm_start); mend = min(end, map->vma->vm_end); pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n", @@ -501,23 +515,39 @@ static void unmap_if_in_range(struct gntdev_grant_map *map, (mstart - map->vma->vm_start) >> PAGE_SHIFT, (mend - mstart) >> PAGE_SHIFT); WARN_ON(err); + + return 0; } -static void mn_invl_range_start(struct mmu_notifier *mn, +static int mn_invl_range_start(struct mmu_notifier *mn, struct mm_struct *mm, - unsigned long start, unsigned long end) + unsigned long start, unsigned long end, + bool blockable) { struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); struct gntdev_grant_map *map; + int ret = 0; + + if (blockable) + mutex_lock(&priv->lock); + else if (!mutex_trylock(&priv->lock)) + return -EAGAIN; - mutex_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { - unmap_if_in_range(map, start, end); + ret = unmap_if_in_range(map, start, end, blockable); + if (ret) + goto out_unlock; } list_for_each_entry(map, &priv->freeable_maps, next) { - unmap_if_in_range(map, start, end); + ret = unmap_if_in_range(map, start, end, blockable); + if (ret) + goto out_unlock; } + +out_unlock: mutex_unlock(&priv->lock); + + return ret; } static void mn_release(struct mmu_notifier *mn, |