diff options
| author | Daniel De Graaf <dgdegra@tycho.nsa.gov> | 2011-02-09 15:12:00 -0500 |
|---|---|---|
| committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2011-02-14 14:16:21 -0500 |
| commit | b57c18694ea1641b691fa05ed8af0ce339fa430b (patch) | |
| tree | 3fb3b050ef993092db69463fa56507f03eb31cf8 | |
| parent | 84e4075d60fc8f1c0b937765620bc784dd0c3d39 (diff) | |
xen-gntdev: Avoid unmapping ranges twice
In paravirtualized domains, mn_invl_page or mn_invl_range_start can
unmap a segment of a mapped region without unmapping all pages. When
the region is later released, the pages will be unmapped twice, leading
to an incorrect -EINVAL return.
Signed-off-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
| -rw-r--r-- | drivers/xen/gntdev.c | 33 |
1 files changed, 31 insertions, 2 deletions
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 4ca4262d3d19..4687cd557c97 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c | |||
| @@ -282,7 +282,7 @@ static int map_grant_pages(struct grant_map *map) | |||
| 282 | return err; | 282 | return err; |
| 283 | } | 283 | } |
| 284 | 284 | ||
| 285 | static int unmap_grant_pages(struct grant_map *map, int offset, int pages) | 285 | static int __unmap_grant_pages(struct grant_map *map, int offset, int pages) |
| 286 | { | 286 | { |
| 287 | int i, err = 0; | 287 | int i, err = 0; |
| 288 | 288 | ||
| @@ -301,7 +301,6 @@ static int unmap_grant_pages(struct grant_map *map, int offset, int pages) | |||
| 301 | } | 301 | } |
| 302 | } | 302 | } |
| 303 | 303 | ||
| 304 | pr_debug("map %d+%d [%d+%d]\n", map->index, map->count, offset, pages); | ||
| 305 | err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset, pages); | 304 | err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset, pages); |
| 306 | if (err) | 305 | if (err) |
| 307 | return err; | 306 | return err; |
| @@ -314,6 +313,36 @@ static int unmap_grant_pages(struct grant_map *map, int offset, int pages) | |||
| 314 | return err; | 313 | return err; |
| 315 | } | 314 | } |
| 316 | 315 | ||
| 316 | static int unmap_grant_pages(struct grant_map *map, int offset, int pages) | ||
| 317 | { | ||
| 318 | int range, err = 0; | ||
| 319 | |||
| 320 | pr_debug("unmap %d+%d [%d+%d]\n", map->index, map->count, offset, pages); | ||
| 321 | |||
| 322 | /* It is possible the requested range will have a "hole" where we | ||
| 323 | * already unmapped some of the grants. Only unmap valid ranges. | ||
| 324 | */ | ||
| 325 | while (pages && !err) { | ||
| 326 | while (pages && !map->unmap_ops[offset].handle) { | ||
| 327 | offset++; | ||
| 328 | pages--; | ||
| 329 | } | ||
| 330 | range = 0; | ||
| 331 | while (range < pages) { | ||
| 332 | if (!map->unmap_ops[offset+range].handle) { | ||
| 333 | range--; | ||
| 334 | break; | ||
| 335 | } | ||
| 336 | range++; | ||
| 337 | } | ||
| 338 | err = __unmap_grant_pages(map, offset, range); | ||
| 339 | offset += range; | ||
| 340 | pages -= range; | ||
| 341 | } | ||
| 342 | |||
| 343 | return err; | ||
| 344 | } | ||
| 345 | |||
| 317 | /* ------------------------------------------------------------------ */ | 346 | /* ------------------------------------------------------------------ */ |
| 318 | 347 | ||
| 319 | static void gntdev_vma_close(struct vm_area_struct *vma) | 348 | static void gntdev_vma_close(struct vm_area_struct *vma) |
