Blame view

drivers/xen/gntdev.c 19.6 KB
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Suppo...
35
  #include <linux/highmem.h>
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
36
37
38
  
  #include <xen/xen.h>
  #include <xen/grant_table.h>
ca47ceaa2   Daniel De Graaf   xen-gntdev: Use b...
39
  #include <xen/balloon.h>
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
40
  #include <xen/gntdev.h>
bdc612dc6   Daniel De Graaf   xen/gntalloc,gntd...
41
  #include <xen/events.h>
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Chang...
50
  static int limit = 1024*1024;
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
51
  module_param(limit, int, 0644);
ef91082e9   Daniel De Graaf   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   Gerd Hoffmann   xen/gntdev: allow...
56

aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
57
  static int use_ptemod;
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
58
59
  struct gntdev_priv {
  	struct list_head maps;
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   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   Gerd Hoffmann   xen/gntdev: allow...
71
72
  struct grant_map {
  	struct list_head next;
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
73
74
75
76
  	struct vm_area_struct *vma;
  	int index;
  	int count;
  	int flags;
68b025c81   Daniel De Graaf   xen-gntdev: Add r...
77
  	atomic_t users;
bdc612dc6   Daniel De Graaf   xen/gntalloc,gntd...
78
  	struct unmap_notify notify;
ab31523c2   Gerd Hoffmann   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   Stefano Stabellini   xen: modify kerne...
82
  	struct gnttab_map_grant_ref   *kmap_ops;
a12b4eb34   Stefano Stabellini   xen gntdev: use g...
83
  	struct page **pages;
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
84
  };
aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
85
  static int unmap_grant_pages(struct grant_map *map, int offset, int pages);
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Chang...
93
94
  	pr_debug("%s: maps list (priv %p)
  ", __func__, priv);
ab31523c2   Gerd Hoffmann   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   Stefano Stabellini   xen gntdev: use g...
106
  	int i;
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
107
108
109
110
  
  	add = kzalloc(sizeof(struct grant_map), GFP_KERNEL);
  	if (NULL == add)
  		return NULL;
fc6e0c3b9   Dan Carpenter   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   Stefano Stabellini   xen gntdev: use g...
116
117
118
  	if (NULL == add->grants    ||
  	    NULL == add->map_ops   ||
  	    NULL == add->unmap_ops ||
0930bba67   Stefano Stabellini   xen: modify kerne...
119
  	    NULL == add->kmap_ops  ||
a12b4eb34   Stefano Stabellini   xen gntdev: use g...
120
  	    NULL == add->pages)
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
121
  		goto err;
693394b8c   Stefano Stabellini   xen: add an "high...
122
  	if (alloc_xenballooned_pages(count, add->pages, false /* lowmem */))
ca47ceaa2   Daniel De Graaf   xen-gntdev: Use b...
123
  		goto err;
a12b4eb34   Stefano Stabellini   xen gntdev: use g...
124
  	for (i = 0; i < count; i++) {
77c35acb7   Daniel De Graaf   xen-gntdev: Fix i...
125
126
  		add->map_ops[i].handle = -1;
  		add->unmap_ops[i].handle = -1;
0930bba67   Stefano Stabellini   xen: modify kerne...
127
  		add->kmap_ops[i].handle = -1;
a12b4eb34   Stefano Stabellini   xen gntdev: use g...
128
  	}
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
129
130
  	add->index = 0;
  	add->count = count;
68b025c81   Daniel De Graaf   xen-gntdev: Add r...
131
  	atomic_set(&add->users, 1);
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
132

ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
133
134
135
  	return add;
  
  err:
a12b4eb34   Stefano Stabellini   xen gntdev: use g...
136
  	kfree(add->pages);
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
137
138
139
  	kfree(add->grants);
  	kfree(add->map_ops);
  	kfree(add->unmap_ops);
0930bba67   Stefano Stabellini   xen: modify kerne...
140
  	kfree(add->kmap_ops);
ab31523c2   Gerd Hoffmann   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   Gerd Hoffmann   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   Daniel De Graaf   xen/gntalloc,gntd...
170
  		if (count && map->count != count)
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
171
172
173
174
175
  			continue;
  		return map;
  	}
  	return NULL;
  }
68b025c81   Daniel De Graaf   xen-gntdev: Add r...
176
  static void gntdev_put_map(struct grant_map *map)
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
177
178
179
  {
  	if (!map)
  		return;
a12b4eb34   Stefano Stabellini   xen gntdev: use g...
180

68b025c81   Daniel De Graaf   xen-gntdev: Add r...
181
182
183
184
  	if (!atomic_dec_and_test(&map->users))
  		return;
  
  	atomic_sub(map->count, &pages_mapped);
0cc678f85   Daniel De Graaf   xen/gnt{dev,alloc...
185
  	if (map->notify.flags & UNMAP_NOTIFY_SEND_EVENT) {
bdc612dc6   Daniel De Graaf   xen/gntalloc,gntd...
186
  		notify_remote_via_evtchn(map->notify.event);
0cc678f85   Daniel De Graaf   xen/gnt{dev,alloc...
187
188
  		evtchn_put(map->notify.event);
  	}
bdc612dc6   Daniel De Graaf   xen/gntalloc,gntd...
189

aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
190
191
192
  	if (map->pages) {
  		if (!use_ptemod)
  			unmap_grant_pages(map, 0, map->count);
ca47ceaa2   Daniel De Graaf   xen-gntdev: Use b...
193
  		free_xenballooned_pages(map->count, map->pages);
aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
194
  	}
a12b4eb34   Stefano Stabellini   xen gntdev: use g...
195
  	kfree(map->pages);
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Suppo...
209
  	int flags = map->flags | GNTMAP_application_map | GNTMAP_contains_pte;
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
210
211
212
  	u64 pte_maddr;
  
  	BUG_ON(pgnr >= map->count);
ba5d10122   Jeremy Fitzhardinge   xen/gntdev: stop ...
213
  	pte_maddr = arbitrary_virt_to_machine(pte).maddr;
aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
214
  	gnttab_set_map_op(&map->map_ops[pgnr], pte_maddr, flags,
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
215
216
  			  map->grants[pgnr].ref,
  			  map->grants[pgnr].domid);
aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
217
  	gnttab_set_unmap_op(&map->unmap_ops[pgnr], pte_maddr, flags,
77c35acb7   Daniel De Graaf   xen-gntdev: Fix i...
218
  			    -1 /* handle */);
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Suppo...
225
226
  
  	if (!use_ptemod) {
12996fc38   Daniel De Graaf   xen-gntdev: Avoid...
227
  		/* Note: it could already be mapped */
77c35acb7   Daniel De Graaf   xen-gntdev: Fix i...
228
  		if (map->map_ops[0].handle != -1)
12996fc38   Daniel De Graaf   xen-gntdev: Avoid...
229
  			return 0;
aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
230
  		for (i = 0; i < map->count; i++) {
38eaeb0fd   Ian Campbell   xen: gntdev: fix ...
231
  			unsigned long addr = (unsigned long)
aab8f11a6   Daniel De Graaf   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   Daniel De Graaf   xen-gntdev: Fix i...
237
  				map->flags, -1 /* handle */);
aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
238
  		}
0930bba67   Stefano Stabellini   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   Daniel De Graaf   xen-gntdev: Suppo...
263
  	}
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
264
265
266
  
  	pr_debug("map %d+%d
  ", map->index, map->count);
0930bba67   Stefano Stabellini   xen: modify kerne...
267
268
  	err = gnttab_map_refs(map->map_ops, use_ptemod ? map->kmap_ops : NULL,
  			map->pages, map->count);
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   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   Gerd Hoffmann   xen/gntdev: allow...
281
282
283
  	}
  	return err;
  }
b57c18694   Daniel De Graaf   xen-gntdev: Avoid...
284
  static int __unmap_grant_pages(struct grant_map *map, int offset, int pages)
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
285
286
  {
  	int i, err = 0;
bdc612dc6   Daniel De Graaf   xen/gntalloc,gntd...
287
288
  	if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) {
  		int pgno = (map->notify.addr >> PAGE_SHIFT);
0ea22f072   Daniel De Graaf   xen-gntdev: Fix u...
289
  		if (pgno >= offset && pgno < offset + pages && use_ptemod) {
f4ee4af44   Daniel De Graaf   xen-gntdev: Add c...
290
291
  			void __user *tmp = (void __user *)
  				map->vma->vm_start + map->notify.addr;
9960be970   Daniel De Graaf   xen-gntdev: preve...
292
293
  			err = copy_to_user(tmp, &err, 1);
  			if (err)
12f0258d5   Dan Carpenter   xen-gntdev: retur...
294
  				return -EFAULT;
0ea22f072   Daniel De Graaf   xen-gntdev: Fix u...
295
296
  			map->notify.flags &= ~UNMAP_NOTIFY_CLEAR_BYTE;
  		} else if (pgno >= offset && pgno < offset + pages) {
bdc612dc6   Daniel De Graaf   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   Daniel De Graaf   xen/grant-table: ...
303
304
  	err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset,
  				pages, true);
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   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   Gerd Hoffmann   xen/gntdev: allow...
316
317
318
  	}
  	return err;
  }
b57c18694   Daniel De Graaf   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   Daniel De Graaf   xen-gntdev: Fix i...
330
  		while (pages && map->unmap_ops[offset].handle == -1) {
b57c18694   Daniel De Graaf   xen-gntdev: Avoid...
331
332
333
334
335
  			offset++;
  			pages--;
  		}
  		range = 0;
  		while (range < pages) {
77c35acb7   Daniel De Graaf   xen-gntdev: Fix i...
336
  			if (map->unmap_ops[offset+range].handle == -1) {
b57c18694   Daniel De Graaf   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   Gerd Hoffmann   xen/gntdev: allow...
349
  /* ------------------------------------------------------------------ */
d79647aea   Daniel De Graaf   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   Gerd Hoffmann   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   Daniel De Graaf   xen/gntdev,gntall...
361
362
  	pr_debug("gntdev_vma_close %p
  ", vma);
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
363
364
  	map->vma = NULL;
  	vma->vm_private_data = NULL;
68b025c81   Daniel De Graaf   xen-gntdev: Add r...
365
  	gntdev_put_map(map);
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
366
  }
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
367
  static struct vm_operations_struct gntdev_vmops = {
d79647aea   Daniel De Graaf   xen/gntdev,gntall...
368
  	.open = gntdev_vma_open,
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
369
  	.close = gntdev_vma_close,
ab31523c2   Gerd Hoffmann   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   Gerd Hoffmann   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   Gerd Hoffmann   xen/gntdev: allow...
453

aab8f11a6   Daniel De Graaf   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   Gerd Hoffmann   xen/gntdev: allow...
463
  	}
ab31523c2   Gerd Hoffmann   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   Gerd Hoffmann   xen/gntdev: allow...
481
482
483
  
  	pr_debug("priv %p
  ", priv);
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
484
485
  	while (!list_empty(&priv->maps)) {
  		map = list_entry(priv->maps.next, struct grant_map, next);
68b025c81   Daniel De Graaf   xen-gntdev: Add r...
486
487
  		list_del(&map->next);
  		gntdev_put_map(map);
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
488
  	}
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
489

aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
490
491
  	if (use_ptemod)
  		mmu_notifier_unregister(&priv->mn, priv->mm);
ab31523c2   Gerd Hoffmann   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   Gerd Hoffmann   xen/gntdev: allow...
509
510
511
512
513
  
  	err = -ENOMEM;
  	map = gntdev_alloc_map(priv, op.count);
  	if (!map)
  		return err;
ef91082e9   Daniel De Graaf   xen-gntdev: Chang...
514

68b025c81   Daniel De Graaf   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   Gerd Hoffmann   xen/gntdev: allow...
519
520
  		return err;
  	}
68b025c81   Daniel De Graaf   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   Daniel De Graaf   xen-gntdev: Chang...
524
525
  		return err;
  	}
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Add r...
530
531
  	if (copy_to_user(u, &op, sizeof(op)) != 0)
  		return -EFAULT;
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Add r...
549
550
  	if (map) {
  		list_del(&map->next);
68b025c81   Daniel De Graaf   xen-gntdev: Add r...
551
552
  		err = 0;
  	}
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
553
  	spin_unlock(&priv->lock);
1f1503ba0   Daniel De Graaf   xen/gntdev: Fix s...
554
555
  	if (map)
  		gntdev_put_map(map);
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Use f...
563
  	struct vm_area_struct *vma;
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Use f...
570
571
  	vma = find_vma(current->mm, op.vaddr);
  	if (!vma || vma->vm_ops != &gntdev_vmops)
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
572
  		return -EINVAL;
a879211bf   Daniel De Graaf   xen-gntdev: Use f...
573
574
575
576
  
  	map = vma->vm_private_data;
  	if (!map)
  		return -EINVAL;
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
577
578
  	op.offset = map->index << PAGE_SHIFT;
  	op.count = map->count;
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
579
580
581
582
583
  
  	if (copy_to_user(u, &op, sizeof(op)) != 0)
  		return -EFAULT;
  	return 0;
  }
bdc612dc6   Daniel De Graaf   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   Daniel De Graaf   xen/gnt{dev,alloc...
589
590
  	int out_flags;
  	unsigned int out_event;
bdc612dc6   Daniel De Graaf   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   Daniel De Graaf   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   Daniel De Graaf   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   Daniel De Graaf   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   Daniel De Graaf   xen/gnt{dev,alloc...
628
629
  	out_flags = map->notify.flags;
  	out_event = map->notify.event;
bdc612dc6   Daniel De Graaf   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   Daniel De Graaf   xen/gnt{dev,alloc...
633

bdc612dc6   Daniel De Graaf   xen/gntalloc,gntd...
634
  	rc = 0;
0cc678f85   Daniel De Graaf   xen/gnt{dev,alloc...
635

bdc612dc6   Daniel De Graaf   xen/gntalloc,gntd...
636
637
   unlock_out:
  	spin_unlock(&priv->lock);
0cc678f85   Daniel De Graaf   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   Daniel De Graaf   xen/gntalloc,gntd...
642
643
  	return rc;
  }
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen/gntalloc,gntd...
659
660
  	case IOCTL_GNTDEV_SET_UNMAP_NOTIFY:
  		return gntdev_ioctl_notify(priv, ptr);
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Suppo...
676
  	int i, err = -EINVAL;
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Suppo...
689
  	if (use_ptemod && map->vma)
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
690
  		goto unlock_out;
aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
691
  	if (use_ptemod && priv->mm != vma->vm_mm) {
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
692
693
694
695
  		printk(KERN_WARNING "Huh? Other mm?
  ");
  		goto unlock_out;
  	}
68b025c81   Daniel De Graaf   xen-gntdev: Add r...
696
  	atomic_inc(&map->users);
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
697
  	vma->vm_ops = &gntdev_vmops;
d79647aea   Daniel De Graaf   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   Gerd Hoffmann   xen/gntdev: allow...
702
703
  
  	vma->vm_private_data = map;
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
704

aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
705
706
  	if (use_ptemod)
  		map->vma = vma;
12996fc38   Daniel De Graaf   xen-gntdev: Avoid...
707
708
709
  	if (map->flags) {
  		if ((vma->vm_flags & VM_WRITE) &&
  				(map->flags & GNTMAP_readonly))
a93e20a83   Dan Carpenter   xen-gntdev: unloc...
710
  			goto out_unlock_put;
12996fc38   Daniel De Graaf   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   Gerd Hoffmann   xen/gntdev: allow...
716

f0a70c882   Daniel De Graaf   xen/gntdev: Fix c...
717
  	spin_unlock(&priv->lock);
aab8f11a6   Daniel De Graaf   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   Daniel De Graaf   xen-gntdev: Fix m...
725
  			goto out_put_map;
aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
726
  		}
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
727
728
729
  	}
  
  	err = map_grant_pages(map);
90b6f3054   Daniel De Graaf   xen-gntdev: Fix m...
730
731
  	if (err)
  		goto out_put_map;
f0a70c882   Daniel De Graaf   xen/gntdev: Fix c...
732

aab8f11a6   Daniel De Graaf   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   Daniel De Graaf   xen-gntdev: Fix m...
738
  				goto out_put_map;
aab8f11a6   Daniel De Graaf   xen-gntdev: Suppo...
739
740
  		}
  	}
f0a70c882   Daniel De Graaf   xen/gntdev: Fix c...
741
  	return 0;
ab31523c2   Gerd Hoffmann   xen/gntdev: allow...
742
743
744
  unlock_out:
  	spin_unlock(&priv->lock);
  	return err;
90b6f3054   Daniel De Graaf   xen-gntdev: Fix m...
745

a93e20a83   Dan Carpenter   xen-gntdev: unloc...
746
747
  out_unlock_put:
  	spin_unlock(&priv->lock);
90b6f3054   Daniel De Graaf   xen-gntdev: Fix m...
748
  out_put_map:
84e4075d6   Daniel De Graaf   xen-gntdev: Use m...
749
750
  	if (use_ptemod)
  		map->vma = NULL;
90b6f3054   Daniel De Graaf   xen-gntdev: Fix m...
751
752
  	gntdev_put_map(map);
  	return err;
ab31523c2   Gerd Hoffmann   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   Daniel De Graaf   xen-gntdev: Suppo...
777
  	use_ptemod = xen_pv_domain();
ab31523c2   Gerd Hoffmann   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);
  
  /* ------------------------------------------------------------------ */