Blame view
drivers/xen/gntdev.c
19.6 KB
ab31523c2 xen/gntdev: allow... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
/****************************************************************************** * gntdev.c * * Device for accessing (in user-space) pages that have been granted by other * domains. * * Copyright (c) 2006-2007, D G Murray. * (c) 2009 Gerd Hoffmann <kraxel@redhat.com> * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #undef DEBUG #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/mmu_notifier.h> #include <linux/types.h> #include <linux/uaccess.h> #include <linux/sched.h> #include <linux/spinlock.h> #include <linux/slab.h> |
aab8f11a6 xen-gntdev: Suppo... |
35 |
#include <linux/highmem.h> |
ab31523c2 xen/gntdev: allow... |
36 37 38 |
#include <xen/xen.h> #include <xen/grant_table.h> |
ca47ceaa2 xen-gntdev: Use b... |
39 |
#include <xen/balloon.h> |
ab31523c2 xen/gntdev: allow... |
40 |
#include <xen/gntdev.h> |
bdc612dc6 xen/gntalloc,gntd... |
41 |
#include <xen/events.h> |
ab31523c2 xen/gntdev: allow... |
42 43 44 45 46 47 48 49 |
#include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> #include <asm/xen/page.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Derek G. Murray <Derek.Murray@cl.cam.ac.uk>, " "Gerd Hoffmann <kraxel@redhat.com>"); MODULE_DESCRIPTION("User-space granted page access driver"); |
ef91082e9 xen-gntdev: Chang... |
50 |
static int limit = 1024*1024; |
ab31523c2 xen/gntdev: allow... |
51 |
module_param(limit, int, 0644); |
ef91082e9 xen-gntdev: Chang... |
52 53 54 55 |
MODULE_PARM_DESC(limit, "Maximum number of grants that may be mapped by " "the gntdev device"); static atomic_t pages_mapped = ATOMIC_INIT(0); |
ab31523c2 xen/gntdev: allow... |
56 |
|
aab8f11a6 xen-gntdev: Suppo... |
57 |
static int use_ptemod; |
ab31523c2 xen/gntdev: allow... |
58 59 |
struct gntdev_priv { struct list_head maps; |
ab31523c2 xen/gntdev: allow... |
60 61 62 63 64 |
/* lock protects maps from concurrent changes */ spinlock_t lock; struct mm_struct *mm; struct mmu_notifier mn; }; |
bdc612dc6 xen/gntalloc,gntd... |
65 66 67 68 69 70 |
struct unmap_notify { int flags; /* Address relative to the start of the grant_map */ int addr; int event; }; |
ab31523c2 xen/gntdev: allow... |
71 72 |
struct grant_map { struct list_head next; |
ab31523c2 xen/gntdev: allow... |
73 74 75 76 |
struct vm_area_struct *vma; int index; int count; int flags; |
68b025c81 xen-gntdev: Add r... |
77 |
atomic_t users; |
bdc612dc6 xen/gntalloc,gntd... |
78 |
struct unmap_notify notify; |
ab31523c2 xen/gntdev: allow... |
79 80 81 |
struct ioctl_gntdev_grant_ref *grants; struct gnttab_map_grant_ref *map_ops; struct gnttab_unmap_grant_ref *unmap_ops; |
0930bba67 xen: modify kerne... |
82 |
struct gnttab_map_grant_ref *kmap_ops; |
a12b4eb34 xen gntdev: use g... |
83 |
struct page **pages; |
ab31523c2 xen/gntdev: allow... |
84 |
}; |
aab8f11a6 xen-gntdev: Suppo... |
85 |
static int unmap_grant_pages(struct grant_map *map, int offset, int pages); |
ab31523c2 xen/gntdev: allow... |
86 87 88 89 90 91 92 |
/* ------------------------------------------------------------------ */ static void gntdev_print_maps(struct gntdev_priv *priv, char *text, int text_index) { #ifdef DEBUG struct grant_map *map; |
ef91082e9 xen-gntdev: Chang... |
93 94 |
pr_debug("%s: maps list (priv %p) ", __func__, priv); |
ab31523c2 xen/gntdev: allow... |
95 96 97 98 99 100 101 102 103 104 105 |
list_for_each_entry(map, &priv->maps, next) pr_debug(" index %2d, count %2d %s ", map->index, map->count, map->index == text_index && text ? text : ""); #endif } static struct grant_map *gntdev_alloc_map(struct gntdev_priv *priv, int count) { struct grant_map *add; |
a12b4eb34 xen gntdev: use g... |
106 |
int i; |
ab31523c2 xen/gntdev: allow... |
107 108 109 110 |
add = kzalloc(sizeof(struct grant_map), GFP_KERNEL); if (NULL == add) return NULL; |
fc6e0c3b9 xen-gntdev: integ... |
111 112 113 114 115 |
add->grants = kcalloc(count, sizeof(add->grants[0]), GFP_KERNEL); add->map_ops = kcalloc(count, sizeof(add->map_ops[0]), GFP_KERNEL); add->unmap_ops = kcalloc(count, sizeof(add->unmap_ops[0]), GFP_KERNEL); add->kmap_ops = kcalloc(count, sizeof(add->kmap_ops[0]), GFP_KERNEL); add->pages = kcalloc(count, sizeof(add->pages[0]), GFP_KERNEL); |
a12b4eb34 xen gntdev: use g... |
116 117 118 |
if (NULL == add->grants || NULL == add->map_ops || NULL == add->unmap_ops || |
0930bba67 xen: modify kerne... |
119 |
NULL == add->kmap_ops || |
a12b4eb34 xen gntdev: use g... |
120 |
NULL == add->pages) |
ab31523c2 xen/gntdev: allow... |
121 |
goto err; |
693394b8c xen: add an "high... |
122 |
if (alloc_xenballooned_pages(count, add->pages, false /* lowmem */)) |
ca47ceaa2 xen-gntdev: Use b... |
123 |
goto err; |
a12b4eb34 xen gntdev: use g... |
124 |
for (i = 0; i < count; i++) { |
77c35acb7 xen-gntdev: Fix i... |
125 126 |
add->map_ops[i].handle = -1; add->unmap_ops[i].handle = -1; |
0930bba67 xen: modify kerne... |
127 |
add->kmap_ops[i].handle = -1; |
a12b4eb34 xen gntdev: use g... |
128 |
} |
ab31523c2 xen/gntdev: allow... |
129 130 |
add->index = 0; add->count = count; |
68b025c81 xen-gntdev: Add r... |
131 |
atomic_set(&add->users, 1); |
ab31523c2 xen/gntdev: allow... |
132 |
|
ab31523c2 xen/gntdev: allow... |
133 134 135 |
return add; err: |
a12b4eb34 xen gntdev: use g... |
136 |
kfree(add->pages); |
ab31523c2 xen/gntdev: allow... |
137 138 139 |
kfree(add->grants); kfree(add->map_ops); kfree(add->unmap_ops); |
0930bba67 xen: modify kerne... |
140 |
kfree(add->kmap_ops); |
ab31523c2 xen/gntdev: allow... |
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
kfree(add); return NULL; } static void gntdev_add_map(struct gntdev_priv *priv, struct grant_map *add) { struct grant_map *map; list_for_each_entry(map, &priv->maps, next) { if (add->index + add->count < map->index) { list_add_tail(&add->next, &map->next); goto done; } add->index = map->index + map->count; } list_add_tail(&add->next, &priv->maps); done: |
ab31523c2 xen/gntdev: allow... |
159 160 161 162 163 164 165 166 167 168 169 |
gntdev_print_maps(priv, "[new]", add->index); } static struct grant_map *gntdev_find_map_index(struct gntdev_priv *priv, int index, int count) { struct grant_map *map; list_for_each_entry(map, &priv->maps, next) { if (map->index != index) continue; |
bdc612dc6 xen/gntalloc,gntd... |
170 |
if (count && map->count != count) |
ab31523c2 xen/gntdev: allow... |
171 172 173 174 175 |
continue; return map; } return NULL; } |
68b025c81 xen-gntdev: Add r... |
176 |
static void gntdev_put_map(struct grant_map *map) |
ab31523c2 xen/gntdev: allow... |
177 178 179 |
{ if (!map) return; |
a12b4eb34 xen gntdev: use g... |
180 |
|
68b025c81 xen-gntdev: Add r... |
181 182 183 184 |
if (!atomic_dec_and_test(&map->users)) return; atomic_sub(map->count, &pages_mapped); |
0cc678f85 xen/gnt{dev,alloc... |
185 |
if (map->notify.flags & UNMAP_NOTIFY_SEND_EVENT) { |
bdc612dc6 xen/gntalloc,gntd... |
186 |
notify_remote_via_evtchn(map->notify.event); |
0cc678f85 xen/gnt{dev,alloc... |
187 188 |
evtchn_put(map->notify.event); } |
bdc612dc6 xen/gntalloc,gntd... |
189 |
|
aab8f11a6 xen-gntdev: Suppo... |
190 191 192 |
if (map->pages) { if (!use_ptemod) unmap_grant_pages(map, 0, map->count); |
ca47ceaa2 xen-gntdev: Use b... |
193 |
free_xenballooned_pages(map->count, map->pages); |
aab8f11a6 xen-gntdev: Suppo... |
194 |
} |
a12b4eb34 xen gntdev: use g... |
195 |
kfree(map->pages); |
ab31523c2 xen/gntdev: allow... |
196 197 198 199 200 201 202 203 204 205 206 207 208 |
kfree(map->grants); kfree(map->map_ops); kfree(map->unmap_ops); kfree(map); } /* ------------------------------------------------------------------ */ static int find_grant_ptes(pte_t *pte, pgtable_t token, unsigned long addr, void *data) { struct grant_map *map = data; unsigned int pgnr = (addr - map->vma->vm_start) >> PAGE_SHIFT; |
aab8f11a6 xen-gntdev: Suppo... |
209 |
int flags = map->flags | GNTMAP_application_map | GNTMAP_contains_pte; |
ab31523c2 xen/gntdev: allow... |
210 211 212 |
u64 pte_maddr; BUG_ON(pgnr >= map->count); |
ba5d10122 xen/gntdev: stop ... |
213 |
pte_maddr = arbitrary_virt_to_machine(pte).maddr; |
aab8f11a6 xen-gntdev: Suppo... |
214 |
gnttab_set_map_op(&map->map_ops[pgnr], pte_maddr, flags, |
ab31523c2 xen/gntdev: allow... |
215 216 |
map->grants[pgnr].ref, map->grants[pgnr].domid); |
aab8f11a6 xen-gntdev: Suppo... |
217 |
gnttab_set_unmap_op(&map->unmap_ops[pgnr], pte_maddr, flags, |
77c35acb7 xen-gntdev: Fix i... |
218 |
-1 /* handle */); |
ab31523c2 xen/gntdev: allow... |
219 220 221 222 223 224 |
return 0; } static int map_grant_pages(struct grant_map *map) { int i, err = 0; |
aab8f11a6 xen-gntdev: Suppo... |
225 226 |
if (!use_ptemod) { |
12996fc38 xen-gntdev: Avoid... |
227 |
/* Note: it could already be mapped */ |
77c35acb7 xen-gntdev: Fix i... |
228 |
if (map->map_ops[0].handle != -1) |
12996fc38 xen-gntdev: Avoid... |
229 |
return 0; |
aab8f11a6 xen-gntdev: Suppo... |
230 |
for (i = 0; i < map->count; i++) { |
38eaeb0fd xen: gntdev: fix ... |
231 |
unsigned long addr = (unsigned long) |
aab8f11a6 xen-gntdev: Suppo... |
232 233 234 235 236 |
pfn_to_kaddr(page_to_pfn(map->pages[i])); gnttab_set_map_op(&map->map_ops[i], addr, map->flags, map->grants[i].ref, map->grants[i].domid); gnttab_set_unmap_op(&map->unmap_ops[i], addr, |
77c35acb7 xen-gntdev: Fix i... |
237 |
map->flags, -1 /* handle */); |
aab8f11a6 xen-gntdev: Suppo... |
238 |
} |
0930bba67 xen: modify kerne... |
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
} else { /* * Setup the map_ops corresponding to the pte entries pointing * to the kernel linear addresses of the struct pages. * These ptes are completely different from the user ptes dealt * with find_grant_ptes. */ for (i = 0; i < map->count; i++) { unsigned level; unsigned long address = (unsigned long) pfn_to_kaddr(page_to_pfn(map->pages[i])); pte_t *ptep; u64 pte_maddr = 0; BUG_ON(PageHighMem(map->pages[i])); ptep = lookup_address(address, &level); pte_maddr = arbitrary_virt_to_machine(ptep).maddr; gnttab_set_map_op(&map->kmap_ops[i], pte_maddr, map->flags | GNTMAP_host_map | GNTMAP_contains_pte, map->grants[i].ref, map->grants[i].domid); } |
aab8f11a6 xen-gntdev: Suppo... |
263 |
} |
ab31523c2 xen/gntdev: allow... |
264 265 266 |
pr_debug("map %d+%d ", map->index, map->count); |
0930bba67 xen: modify kerne... |
267 268 |
err = gnttab_map_refs(map->map_ops, use_ptemod ? map->kmap_ops : NULL, map->pages, map->count); |
ab31523c2 xen/gntdev: allow... |
269 270 271 272 273 274 |
if (err) return err; for (i = 0; i < map->count; i++) { if (map->map_ops[i].status) err = -EINVAL; |
77c35acb7 xen-gntdev: Fix i... |
275 276 277 278 279 280 |
else { BUG_ON(map->map_ops[i].handle == -1); map->unmap_ops[i].handle = map->map_ops[i].handle; pr_debug("map handle=%d ", map->map_ops[i].handle); } |
ab31523c2 xen/gntdev: allow... |
281 282 283 |
} return err; } |
b57c18694 xen-gntdev: Avoid... |
284 |
static int __unmap_grant_pages(struct grant_map *map, int offset, int pages) |
ab31523c2 xen/gntdev: allow... |
285 286 |
{ int i, err = 0; |
bdc612dc6 xen/gntalloc,gntd... |
287 288 |
if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { int pgno = (map->notify.addr >> PAGE_SHIFT); |
0ea22f072 xen-gntdev: Fix u... |
289 |
if (pgno >= offset && pgno < offset + pages && use_ptemod) { |
f4ee4af44 xen-gntdev: Add c... |
290 291 |
void __user *tmp = (void __user *) map->vma->vm_start + map->notify.addr; |
9960be970 xen-gntdev: preve... |
292 293 |
err = copy_to_user(tmp, &err, 1); if (err) |
12f0258d5 xen-gntdev: retur... |
294 |
return -EFAULT; |
0ea22f072 xen-gntdev: Fix u... |
295 296 |
map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; } else if (pgno >= offset && pgno < offset + pages) { |
bdc612dc6 xen/gntalloc,gntd... |
297 298 299 300 301 302 |
uint8_t *tmp = kmap(map->pages[pgno]); tmp[map->notify.addr & (PAGE_SIZE-1)] = 0; kunmap(map->pages[pgno]); map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE; } } |
7d17e84bb xen/grant-table: ... |
303 304 |
err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset, pages, true); |
ab31523c2 xen/gntdev: allow... |
305 306 307 308 309 310 |
if (err) return err; for (i = 0; i < pages; i++) { if (map->unmap_ops[offset+i].status) err = -EINVAL; |
77c35acb7 xen-gntdev: Fix i... |
311 312 313 314 315 |
pr_debug("unmap handle=%d st=%d ", map->unmap_ops[offset+i].handle, map->unmap_ops[offset+i].status); map->unmap_ops[offset+i].handle = -1; |
ab31523c2 xen/gntdev: allow... |
316 317 318 |
} return err; } |
b57c18694 xen-gntdev: Avoid... |
319 320 321 322 323 324 325 326 327 328 329 |
static int unmap_grant_pages(struct grant_map *map, int offset, int pages) { int range, err = 0; pr_debug("unmap %d+%d [%d+%d] ", map->index, map->count, offset, pages); /* It is possible the requested range will have a "hole" where we * already unmapped some of the grants. Only unmap valid ranges. */ while (pages && !err) { |
77c35acb7 xen-gntdev: Fix i... |
330 |
while (pages && map->unmap_ops[offset].handle == -1) { |
b57c18694 xen-gntdev: Avoid... |
331 332 333 334 335 |
offset++; pages--; } range = 0; while (range < pages) { |
77c35acb7 xen-gntdev: Fix i... |
336 |
if (map->unmap_ops[offset+range].handle == -1) { |
b57c18694 xen-gntdev: Avoid... |
337 338 339 340 341 342 343 344 345 346 347 348 |
range--; break; } range++; } err = __unmap_grant_pages(map, offset, range); offset += range; pages -= range; } return err; } |
ab31523c2 xen/gntdev: allow... |
349 |
/* ------------------------------------------------------------------ */ |
d79647aea xen/gntdev,gntall... |
350 351 352 353 354 355 356 357 |
static void gntdev_vma_open(struct vm_area_struct *vma) { struct grant_map *map = vma->vm_private_data; pr_debug("gntdev_vma_open %p ", vma); atomic_inc(&map->users); } |
ab31523c2 xen/gntdev: allow... |
358 359 360 |
static void gntdev_vma_close(struct vm_area_struct *vma) { struct grant_map *map = vma->vm_private_data; |
d79647aea xen/gntdev,gntall... |
361 362 |
pr_debug("gntdev_vma_close %p ", vma); |
ab31523c2 xen/gntdev: allow... |
363 364 |
map->vma = NULL; vma->vm_private_data = NULL; |
68b025c81 xen-gntdev: Add r... |
365 |
gntdev_put_map(map); |
ab31523c2 xen/gntdev: allow... |
366 |
} |
ab31523c2 xen/gntdev: allow... |
367 |
static struct vm_operations_struct gntdev_vmops = { |
d79647aea xen/gntdev,gntall... |
368 |
.open = gntdev_vma_open, |
ab31523c2 xen/gntdev: allow... |
369 |
.close = gntdev_vma_close, |
ab31523c2 xen/gntdev: allow... |
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
}; /* ------------------------------------------------------------------ */ static void mn_invl_range_start(struct mmu_notifier *mn, struct mm_struct *mm, unsigned long start, unsigned long end) { struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); struct grant_map *map; unsigned long mstart, mend; int err; spin_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { if (!map->vma) continue; |
ab31523c2 xen/gntdev: allow... |
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
if (map->vma->vm_start >= end) continue; if (map->vma->vm_end <= start) continue; 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 ", map->index, map->count, map->vma->vm_start, map->vma->vm_end, start, end, mstart, mend); err = unmap_grant_pages(map, (mstart - map->vma->vm_start) >> PAGE_SHIFT, (mend - mstart) >> PAGE_SHIFT); WARN_ON(err); } spin_unlock(&priv->lock); } static void mn_invl_page(struct mmu_notifier *mn, struct mm_struct *mm, unsigned long address) { mn_invl_range_start(mn, mm, address, address + PAGE_SIZE); } static void mn_release(struct mmu_notifier *mn, struct mm_struct *mm) { struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); struct grant_map *map; int err; spin_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { if (!map->vma) continue; pr_debug("map %d+%d (%lx %lx) ", map->index, map->count, map->vma->vm_start, map->vma->vm_end); err = unmap_grant_pages(map, /* offset */ 0, map->count); WARN_ON(err); } spin_unlock(&priv->lock); } struct mmu_notifier_ops gntdev_mmu_ops = { .release = mn_release, .invalidate_page = mn_invl_page, .invalidate_range_start = mn_invl_range_start, }; /* ------------------------------------------------------------------ */ static int gntdev_open(struct inode *inode, struct file *flip) { struct gntdev_priv *priv; int ret = 0; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; INIT_LIST_HEAD(&priv->maps); spin_lock_init(&priv->lock); |
ab31523c2 xen/gntdev: allow... |
453 |
|
aab8f11a6 xen-gntdev: Suppo... |
454 455 456 457 458 459 460 461 462 |
if (use_ptemod) { priv->mm = get_task_mm(current); if (!priv->mm) { kfree(priv); return -ENOMEM; } priv->mn.ops = &gntdev_mmu_ops; ret = mmu_notifier_register(&priv->mn, priv->mm); mmput(priv->mm); |
ab31523c2 xen/gntdev: allow... |
463 |
} |
ab31523c2 xen/gntdev: allow... |
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 |
if (ret) { kfree(priv); return ret; } flip->private_data = priv; pr_debug("priv %p ", priv); return 0; } static int gntdev_release(struct inode *inode, struct file *flip) { struct gntdev_priv *priv = flip->private_data; struct grant_map *map; |
ab31523c2 xen/gntdev: allow... |
481 482 483 |
pr_debug("priv %p ", priv); |
ab31523c2 xen/gntdev: allow... |
484 485 |
while (!list_empty(&priv->maps)) { map = list_entry(priv->maps.next, struct grant_map, next); |
68b025c81 xen-gntdev: Add r... |
486 487 |
list_del(&map->next); gntdev_put_map(map); |
ab31523c2 xen/gntdev: allow... |
488 |
} |
ab31523c2 xen/gntdev: allow... |
489 |
|
aab8f11a6 xen-gntdev: Suppo... |
490 491 |
if (use_ptemod) mmu_notifier_unregister(&priv->mn, priv->mm); |
ab31523c2 xen/gntdev: allow... |
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 |
kfree(priv); return 0; } static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv, struct ioctl_gntdev_map_grant_ref __user *u) { struct ioctl_gntdev_map_grant_ref op; struct grant_map *map; int err; if (copy_from_user(&op, u, sizeof(op)) != 0) return -EFAULT; pr_debug("priv %p, add %d ", priv, op.count); if (unlikely(op.count <= 0)) return -EINVAL; |
ab31523c2 xen/gntdev: allow... |
509 510 511 512 513 |
err = -ENOMEM; map = gntdev_alloc_map(priv, op.count); if (!map) return err; |
ef91082e9 xen-gntdev: Chang... |
514 |
|
68b025c81 xen-gntdev: Add r... |
515 516 517 518 |
if (unlikely(atomic_add_return(op.count, &pages_mapped) > limit)) { pr_debug("can't map: over limit "); gntdev_put_map(map); |
ab31523c2 xen/gntdev: allow... |
519 520 |
return err; } |
68b025c81 xen-gntdev: Add r... |
521 522 523 |
if (copy_from_user(map->grants, &u->refs, sizeof(map->grants[0]) * op.count) != 0) { gntdev_put_map(map); |
ef91082e9 xen-gntdev: Chang... |
524 525 |
return err; } |
ab31523c2 xen/gntdev: allow... |
526 527 528 529 |
spin_lock(&priv->lock); gntdev_add_map(priv, map); op.index = map->index << PAGE_SHIFT; spin_unlock(&priv->lock); |
68b025c81 xen-gntdev: Add r... |
530 531 |
if (copy_to_user(u, &op, sizeof(op)) != 0) return -EFAULT; |
ab31523c2 xen/gntdev: allow... |
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
return 0; } static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv, struct ioctl_gntdev_unmap_grant_ref __user *u) { struct ioctl_gntdev_unmap_grant_ref op; struct grant_map *map; int err = -ENOENT; if (copy_from_user(&op, u, sizeof(op)) != 0) return -EFAULT; pr_debug("priv %p, del %d+%d ", priv, (int)op.index, (int)op.count); spin_lock(&priv->lock); map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count); |
68b025c81 xen-gntdev: Add r... |
549 550 |
if (map) { list_del(&map->next); |
68b025c81 xen-gntdev: Add r... |
551 552 |
err = 0; } |
ab31523c2 xen/gntdev: allow... |
553 |
spin_unlock(&priv->lock); |
1f1503ba0 xen/gntdev: Fix s... |
554 555 |
if (map) gntdev_put_map(map); |
ab31523c2 xen/gntdev: allow... |
556 557 558 559 560 561 562 |
return err; } static long gntdev_ioctl_get_offset_for_vaddr(struct gntdev_priv *priv, struct ioctl_gntdev_get_offset_for_vaddr __user *u) { struct ioctl_gntdev_get_offset_for_vaddr op; |
a879211bf xen-gntdev: Use f... |
563 |
struct vm_area_struct *vma; |
ab31523c2 xen/gntdev: allow... |
564 565 566 567 568 569 |
struct grant_map *map; if (copy_from_user(&op, u, sizeof(op)) != 0) return -EFAULT; pr_debug("priv %p, offset for vaddr %lx ", priv, (unsigned long)op.vaddr); |
a879211bf xen-gntdev: Use f... |
570 571 |
vma = find_vma(current->mm, op.vaddr); if (!vma || vma->vm_ops != &gntdev_vmops) |
ab31523c2 xen/gntdev: allow... |
572 |
return -EINVAL; |
a879211bf xen-gntdev: Use f... |
573 574 575 576 |
map = vma->vm_private_data; if (!map) return -EINVAL; |
ab31523c2 xen/gntdev: allow... |
577 578 |
op.offset = map->index << PAGE_SHIFT; op.count = map->count; |
ab31523c2 xen/gntdev: allow... |
579 580 581 582 583 |
if (copy_to_user(u, &op, sizeof(op)) != 0) return -EFAULT; return 0; } |
bdc612dc6 xen/gntalloc,gntd... |
584 585 586 587 588 |
static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) { struct ioctl_gntdev_unmap_notify op; struct grant_map *map; int rc; |
0cc678f85 xen/gnt{dev,alloc... |
589 590 |
int out_flags; unsigned int out_event; |
bdc612dc6 xen/gntalloc,gntd... |
591 592 593 594 595 596 |
if (copy_from_user(&op, u, sizeof(op))) return -EFAULT; if (op.action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) return -EINVAL; |
0cc678f85 xen/gnt{dev,alloc... |
597 598 599 600 601 602 603 604 605 606 607 608 609 610 |
/* We need to grab a reference to the event channel we are going to use * to send the notify before releasing the reference we may already have * (if someone has called this ioctl twice). This is required so that * it is possible to change the clear_byte part of the notification * without disturbing the event channel part, which may now be the last * reference to that event channel. */ if (op.action & UNMAP_NOTIFY_SEND_EVENT) { if (evtchn_get(op.event_channel_port)) return -EINVAL; } out_flags = op.action; out_event = op.event_channel_port; |
bdc612dc6 xen/gntalloc,gntd... |
611 612 613 614 615 616 617 618 619 620 621 622 |
spin_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { uint64_t begin = map->index << PAGE_SHIFT; uint64_t end = (map->index + map->count) << PAGE_SHIFT; if (op.index >= begin && op.index < end) goto found; } rc = -ENOENT; goto unlock_out; found: |
9960be970 xen-gntdev: preve... |
623 624 625 626 627 |
if ((op.action & UNMAP_NOTIFY_CLEAR_BYTE) && (map->flags & GNTMAP_readonly)) { rc = -EINVAL; goto unlock_out; } |
0cc678f85 xen/gnt{dev,alloc... |
628 629 |
out_flags = map->notify.flags; out_event = map->notify.event; |
bdc612dc6 xen/gntalloc,gntd... |
630 631 632 |
map->notify.flags = op.action; map->notify.addr = op.index - (map->index << PAGE_SHIFT); map->notify.event = op.event_channel_port; |
0cc678f85 xen/gnt{dev,alloc... |
633 |
|
bdc612dc6 xen/gntalloc,gntd... |
634 |
rc = 0; |
0cc678f85 xen/gnt{dev,alloc... |
635 |
|
bdc612dc6 xen/gntalloc,gntd... |
636 637 |
unlock_out: spin_unlock(&priv->lock); |
0cc678f85 xen/gnt{dev,alloc... |
638 639 640 641 |
/* Drop the reference to the event channel we did not save in the map */ if (out_flags & UNMAP_NOTIFY_SEND_EVENT) evtchn_put(out_event); |
bdc612dc6 xen/gntalloc,gntd... |
642 643 |
return rc; } |
ab31523c2 xen/gntdev: allow... |
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 |
static long gntdev_ioctl(struct file *flip, unsigned int cmd, unsigned long arg) { struct gntdev_priv *priv = flip->private_data; void __user *ptr = (void __user *)arg; switch (cmd) { case IOCTL_GNTDEV_MAP_GRANT_REF: return gntdev_ioctl_map_grant_ref(priv, ptr); case IOCTL_GNTDEV_UNMAP_GRANT_REF: return gntdev_ioctl_unmap_grant_ref(priv, ptr); case IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR: return gntdev_ioctl_get_offset_for_vaddr(priv, ptr); |
bdc612dc6 xen/gntalloc,gntd... |
659 660 |
case IOCTL_GNTDEV_SET_UNMAP_NOTIFY: return gntdev_ioctl_notify(priv, ptr); |
ab31523c2 xen/gntdev: allow... |
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 |
default: pr_debug("priv %p, unknown cmd %x ", priv, cmd); return -ENOIOCTLCMD; } return 0; } static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) { struct gntdev_priv *priv = flip->private_data; int index = vma->vm_pgoff; int count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; struct grant_map *map; |
aab8f11a6 xen-gntdev: Suppo... |
676 |
int i, err = -EINVAL; |
ab31523c2 xen/gntdev: allow... |
677 678 679 680 681 682 683 684 685 686 687 688 |
if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) return -EINVAL; pr_debug("map %d+%d at %lx (pgoff %lx) ", index, count, vma->vm_start, vma->vm_pgoff); spin_lock(&priv->lock); map = gntdev_find_map_index(priv, index, count); if (!map) goto unlock_out; |
aab8f11a6 xen-gntdev: Suppo... |
689 |
if (use_ptemod && map->vma) |
ab31523c2 xen/gntdev: allow... |
690 |
goto unlock_out; |
aab8f11a6 xen-gntdev: Suppo... |
691 |
if (use_ptemod && priv->mm != vma->vm_mm) { |
ab31523c2 xen/gntdev: allow... |
692 693 694 695 |
printk(KERN_WARNING "Huh? Other mm? "); goto unlock_out; } |
68b025c81 xen-gntdev: Add r... |
696 |
atomic_inc(&map->users); |
ab31523c2 xen/gntdev: allow... |
697 |
vma->vm_ops = &gntdev_vmops; |
d79647aea xen/gntdev,gntall... |
698 699 700 701 |
vma->vm_flags |= VM_RESERVED|VM_DONTEXPAND; if (use_ptemod) vma->vm_flags |= VM_DONTCOPY|VM_PFNMAP; |
ab31523c2 xen/gntdev: allow... |
702 703 |
vma->vm_private_data = map; |
ab31523c2 xen/gntdev: allow... |
704 |
|
aab8f11a6 xen-gntdev: Suppo... |
705 706 |
if (use_ptemod) map->vma = vma; |
12996fc38 xen-gntdev: Avoid... |
707 708 709 |
if (map->flags) { if ((vma->vm_flags & VM_WRITE) && (map->flags & GNTMAP_readonly)) |
a93e20a83 xen-gntdev: unloc... |
710 |
goto out_unlock_put; |
12996fc38 xen-gntdev: Avoid... |
711 712 713 714 715 |
} else { map->flags = GNTMAP_host_map; if (!(vma->vm_flags & VM_WRITE)) map->flags |= GNTMAP_readonly; } |
ab31523c2 xen/gntdev: allow... |
716 |
|
f0a70c882 xen/gntdev: Fix c... |
717 |
spin_unlock(&priv->lock); |
aab8f11a6 xen-gntdev: Suppo... |
718 719 720 721 722 723 724 |
if (use_ptemod) { err = apply_to_page_range(vma->vm_mm, vma->vm_start, vma->vm_end - vma->vm_start, find_grant_ptes, map); if (err) { printk(KERN_WARNING "find_grant_ptes() failure. "); |
90b6f3054 xen-gntdev: Fix m... |
725 |
goto out_put_map; |
aab8f11a6 xen-gntdev: Suppo... |
726 |
} |
ab31523c2 xen/gntdev: allow... |
727 728 729 |
} err = map_grant_pages(map); |
90b6f3054 xen-gntdev: Fix m... |
730 731 |
if (err) goto out_put_map; |
f0a70c882 xen/gntdev: Fix c... |
732 |
|
aab8f11a6 xen-gntdev: Suppo... |
733 734 735 736 737 |
if (!use_ptemod) { for (i = 0; i < count; i++) { err = vm_insert_page(vma, vma->vm_start + i*PAGE_SIZE, map->pages[i]); if (err) |
90b6f3054 xen-gntdev: Fix m... |
738 |
goto out_put_map; |
aab8f11a6 xen-gntdev: Suppo... |
739 740 |
} } |
f0a70c882 xen/gntdev: Fix c... |
741 |
return 0; |
ab31523c2 xen/gntdev: allow... |
742 743 744 |
unlock_out: spin_unlock(&priv->lock); return err; |
90b6f3054 xen-gntdev: Fix m... |
745 |
|
a93e20a83 xen-gntdev: unloc... |
746 747 |
out_unlock_put: spin_unlock(&priv->lock); |
90b6f3054 xen-gntdev: Fix m... |
748 |
out_put_map: |
84e4075d6 xen-gntdev: Use m... |
749 750 |
if (use_ptemod) map->vma = NULL; |
90b6f3054 xen-gntdev: Fix m... |
751 752 |
gntdev_put_map(map); return err; |
ab31523c2 xen/gntdev: allow... |
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 |
} static const struct file_operations gntdev_fops = { .owner = THIS_MODULE, .open = gntdev_open, .release = gntdev_release, .mmap = gntdev_mmap, .unlocked_ioctl = gntdev_ioctl }; static struct miscdevice gntdev_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = "xen/gntdev", .fops = &gntdev_fops, }; /* ------------------------------------------------------------------ */ static int __init gntdev_init(void) { int err; if (!xen_domain()) return -ENODEV; |
aab8f11a6 xen-gntdev: Suppo... |
777 |
use_ptemod = xen_pv_domain(); |
ab31523c2 xen/gntdev: allow... |
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 |
err = misc_register(&gntdev_miscdev); if (err != 0) { printk(KERN_ERR "Could not register gntdev device "); return err; } return 0; } static void __exit gntdev_exit(void) { misc_deregister(&gntdev_miscdev); } module_init(gntdev_init); module_exit(gntdev_exit); /* ------------------------------------------------------------------ */ |