aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/gntalloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/xen/gntalloc.c')
-rw-r--r--drivers/xen/gntalloc.c120
1 files changed, 86 insertions, 34 deletions
diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c
index e1c4c6e5b469..e2400c8963fa 100644
--- a/drivers/xen/gntalloc.c
+++ b/drivers/xen/gntalloc.c
@@ -74,7 +74,7 @@ MODULE_PARM_DESC(limit, "Maximum number of grants that may be allocated by "
74 "the gntalloc device"); 74 "the gntalloc device");
75 75
76static LIST_HEAD(gref_list); 76static LIST_HEAD(gref_list);
77static DEFINE_SPINLOCK(gref_lock); 77static DEFINE_MUTEX(gref_mutex);
78static int gref_size; 78static int gref_size;
79 79
80struct notify_info { 80struct notify_info {
@@ -99,6 +99,12 @@ struct gntalloc_file_private_data {
99 uint64_t index; 99 uint64_t index;
100}; 100};
101 101
102struct gntalloc_vma_private_data {
103 struct gntalloc_gref *gref;
104 int users;
105 int count;
106};
107
102static void __del_gref(struct gntalloc_gref *gref); 108static void __del_gref(struct gntalloc_gref *gref);
103 109
104static void do_cleanup(void) 110static void do_cleanup(void)
@@ -143,15 +149,15 @@ static int add_grefs(struct ioctl_gntalloc_alloc_gref *op,
143 } 149 }
144 150
145 /* Add to gref lists. */ 151 /* Add to gref lists. */
146 spin_lock(&gref_lock); 152 mutex_lock(&gref_mutex);
147 list_splice_tail(&queue_gref, &gref_list); 153 list_splice_tail(&queue_gref, &gref_list);
148 list_splice_tail(&queue_file, &priv->list); 154 list_splice_tail(&queue_file, &priv->list);
149 spin_unlock(&gref_lock); 155 mutex_unlock(&gref_mutex);
150 156
151 return 0; 157 return 0;
152 158
153undo: 159undo:
154 spin_lock(&gref_lock); 160 mutex_lock(&gref_mutex);
155 gref_size -= (op->count - i); 161 gref_size -= (op->count - i);
156 162
157 list_for_each_entry(gref, &queue_file, next_file) { 163 list_for_each_entry(gref, &queue_file, next_file) {
@@ -167,7 +173,7 @@ undo:
167 */ 173 */
168 if (unlikely(!list_empty(&queue_gref))) 174 if (unlikely(!list_empty(&queue_gref)))
169 list_splice_tail(&queue_gref, &gref_list); 175 list_splice_tail(&queue_gref, &gref_list);
170 spin_unlock(&gref_lock); 176 mutex_unlock(&gref_mutex);
171 return rc; 177 return rc;
172} 178}
173 179
@@ -178,8 +184,10 @@ static void __del_gref(struct gntalloc_gref *gref)
178 tmp[gref->notify.pgoff] = 0; 184 tmp[gref->notify.pgoff] = 0;
179 kunmap(gref->page); 185 kunmap(gref->page);
180 } 186 }
181 if (gref->notify.flags & UNMAP_NOTIFY_SEND_EVENT) 187 if (gref->notify.flags & UNMAP_NOTIFY_SEND_EVENT) {
182 notify_remote_via_evtchn(gref->notify.event); 188 notify_remote_via_evtchn(gref->notify.event);
189 evtchn_put(gref->notify.event);
190 }
183 191
184 gref->notify.flags = 0; 192 gref->notify.flags = 0;
185 193
@@ -189,6 +197,8 @@ static void __del_gref(struct gntalloc_gref *gref)
189 197
190 if (!gnttab_end_foreign_access_ref(gref->gref_id, 0)) 198 if (!gnttab_end_foreign_access_ref(gref->gref_id, 0))
191 return; 199 return;
200
201 gnttab_free_grant_reference(gref->gref_id);
192 } 202 }
193 203
194 gref_size--; 204 gref_size--;
@@ -251,7 +261,7 @@ static int gntalloc_release(struct inode *inode, struct file *filp)
251 261
252 pr_debug("%s: priv %p\n", __func__, priv); 262 pr_debug("%s: priv %p\n", __func__, priv);
253 263
254 spin_lock(&gref_lock); 264 mutex_lock(&gref_mutex);
255 while (!list_empty(&priv->list)) { 265 while (!list_empty(&priv->list)) {
256 gref = list_entry(priv->list.next, 266 gref = list_entry(priv->list.next,
257 struct gntalloc_gref, next_file); 267 struct gntalloc_gref, next_file);
@@ -261,7 +271,7 @@ static int gntalloc_release(struct inode *inode, struct file *filp)
261 __del_gref(gref); 271 __del_gref(gref);
262 } 272 }
263 kfree(priv); 273 kfree(priv);
264 spin_unlock(&gref_lock); 274 mutex_unlock(&gref_mutex);
265 275
266 return 0; 276 return 0;
267} 277}
@@ -286,21 +296,21 @@ static long gntalloc_ioctl_alloc(struct gntalloc_file_private_data *priv,
286 goto out; 296 goto out;
287 } 297 }
288 298
289 spin_lock(&gref_lock); 299 mutex_lock(&gref_mutex);
290 /* Clean up pages that were at zero (local) users but were still mapped 300 /* Clean up pages that were at zero (local) users but were still mapped
291 * by remote domains. Since those pages count towards the limit that we 301 * by remote domains. Since those pages count towards the limit that we
292 * are about to enforce, removing them here is a good idea. 302 * are about to enforce, removing them here is a good idea.
293 */ 303 */
294 do_cleanup(); 304 do_cleanup();
295 if (gref_size + op.count > limit) { 305 if (gref_size + op.count > limit) {
296 spin_unlock(&gref_lock); 306 mutex_unlock(&gref_mutex);
297 rc = -ENOSPC; 307 rc = -ENOSPC;
298 goto out_free; 308 goto out_free;
299 } 309 }
300 gref_size += op.count; 310 gref_size += op.count;
301 op.index = priv->index; 311 op.index = priv->index;
302 priv->index += op.count * PAGE_SIZE; 312 priv->index += op.count * PAGE_SIZE;
303 spin_unlock(&gref_lock); 313 mutex_unlock(&gref_mutex);
304 314
305 rc = add_grefs(&op, gref_ids, priv); 315 rc = add_grefs(&op, gref_ids, priv);
306 if (rc < 0) 316 if (rc < 0)
@@ -343,7 +353,7 @@ static long gntalloc_ioctl_dealloc(struct gntalloc_file_private_data *priv,
343 goto dealloc_grant_out; 353 goto dealloc_grant_out;
344 } 354 }
345 355
346 spin_lock(&gref_lock); 356 mutex_lock(&gref_mutex);
347 gref = find_grefs(priv, op.index, op.count); 357 gref = find_grefs(priv, op.index, op.count);
348 if (gref) { 358 if (gref) {
349 /* Remove from the file list only, and decrease reference count. 359 /* Remove from the file list only, and decrease reference count.
@@ -363,7 +373,7 @@ static long gntalloc_ioctl_dealloc(struct gntalloc_file_private_data *priv,
363 373
364 do_cleanup(); 374 do_cleanup();
365 375
366 spin_unlock(&gref_lock); 376 mutex_unlock(&gref_mutex);
367dealloc_grant_out: 377dealloc_grant_out:
368 return rc; 378 return rc;
369} 379}
@@ -383,7 +393,7 @@ static long gntalloc_ioctl_unmap_notify(struct gntalloc_file_private_data *priv,
383 index = op.index & ~(PAGE_SIZE - 1); 393 index = op.index & ~(PAGE_SIZE - 1);
384 pgoff = op.index & (PAGE_SIZE - 1); 394 pgoff = op.index & (PAGE_SIZE - 1);
385 395
386 spin_lock(&gref_lock); 396 mutex_lock(&gref_mutex);
387 397
388 gref = find_grefs(priv, index, 1); 398 gref = find_grefs(priv, index, 1);
389 if (!gref) { 399 if (!gref) {
@@ -396,12 +406,30 @@ static long gntalloc_ioctl_unmap_notify(struct gntalloc_file_private_data *priv,
396 goto unlock_out; 406 goto unlock_out;
397 } 407 }
398 408
409 /* We need to grab a reference to the event channel we are going to use
410 * to send the notify before releasing the reference we may already have
411 * (if someone has called this ioctl twice). This is required so that
412 * it is possible to change the clear_byte part of the notification
413 * without disturbing the event channel part, which may now be the last
414 * reference to that event channel.
415 */
416 if (op.action & UNMAP_NOTIFY_SEND_EVENT) {
417 if (evtchn_get(op.event_channel_port)) {
418 rc = -EINVAL;
419 goto unlock_out;
420 }
421 }
422
423 if (gref->notify.flags & UNMAP_NOTIFY_SEND_EVENT)
424 evtchn_put(gref->notify.event);
425
399 gref->notify.flags = op.action; 426 gref->notify.flags = op.action;
400 gref->notify.pgoff = pgoff; 427 gref->notify.pgoff = pgoff;
401 gref->notify.event = op.event_channel_port; 428 gref->notify.event = op.event_channel_port;
402 rc = 0; 429 rc = 0;
430
403 unlock_out: 431 unlock_out:
404 spin_unlock(&gref_lock); 432 mutex_unlock(&gref_mutex);
405 return rc; 433 return rc;
406} 434}
407 435
@@ -429,26 +457,40 @@ static long gntalloc_ioctl(struct file *filp, unsigned int cmd,
429 457
430static void gntalloc_vma_open(struct vm_area_struct *vma) 458static void gntalloc_vma_open(struct vm_area_struct *vma)
431{ 459{
432 struct gntalloc_gref *gref = vma->vm_private_data; 460 struct gntalloc_vma_private_data *priv = vma->vm_private_data;
433 if (!gref) 461
462 if (!priv)
434 return; 463 return;
435 464
436 spin_lock(&gref_lock); 465 mutex_lock(&gref_mutex);
437 gref->users++; 466 priv->users++;
438 spin_unlock(&gref_lock); 467 mutex_unlock(&gref_mutex);
439} 468}
440 469
441static void gntalloc_vma_close(struct vm_area_struct *vma) 470static void gntalloc_vma_close(struct vm_area_struct *vma)
442{ 471{
443 struct gntalloc_gref *gref = vma->vm_private_data; 472 struct gntalloc_vma_private_data *priv = vma->vm_private_data;
444 if (!gref) 473 struct gntalloc_gref *gref, *next;
474 int i;
475
476 if (!priv)
445 return; 477 return;
446 478
447 spin_lock(&gref_lock); 479 mutex_lock(&gref_mutex);
448 gref->users--; 480 priv->users--;
449 if (gref->users == 0) 481 if (priv->users == 0) {
450 __del_gref(gref); 482 gref = priv->gref;
451 spin_unlock(&gref_lock); 483 for (i = 0; i < priv->count; i++) {
484 gref->users--;
485 next = list_entry(gref->next_gref.next,
486 struct gntalloc_gref, next_gref);
487 if (gref->users == 0)
488 __del_gref(gref);
489 gref = next;
490 }
491 kfree(priv);
492 }
493 mutex_unlock(&gref_mutex);
452} 494}
453 495
454static struct vm_operations_struct gntalloc_vmops = { 496static struct vm_operations_struct gntalloc_vmops = {
@@ -459,19 +501,25 @@ static struct vm_operations_struct gntalloc_vmops = {
459static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma) 501static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma)
460{ 502{
461 struct gntalloc_file_private_data *priv = filp->private_data; 503 struct gntalloc_file_private_data *priv = filp->private_data;
504 struct gntalloc_vma_private_data *vm_priv;
462 struct gntalloc_gref *gref; 505 struct gntalloc_gref *gref;
463 int count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; 506 int count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
464 int rv, i; 507 int rv, i;
465 508
466 pr_debug("%s: priv %p, page %lu+%d\n", __func__,
467 priv, vma->vm_pgoff, count);
468
469 if (!(vma->vm_flags & VM_SHARED)) { 509 if (!(vma->vm_flags & VM_SHARED)) {
470 printk(KERN_ERR "%s: Mapping must be shared.\n", __func__); 510 printk(KERN_ERR "%s: Mapping must be shared.\n", __func__);
471 return -EINVAL; 511 return -EINVAL;
472 } 512 }
473 513
474 spin_lock(&gref_lock); 514 vm_priv = kmalloc(sizeof(*vm_priv), GFP_KERNEL);
515 if (!vm_priv)
516 return -ENOMEM;
517
518 mutex_lock(&gref_mutex);
519
520 pr_debug("%s: priv %p,%p, page %lu+%d\n", __func__,
521 priv, vm_priv, vma->vm_pgoff, count);
522
475 gref = find_grefs(priv, vma->vm_pgoff << PAGE_SHIFT, count); 523 gref = find_grefs(priv, vma->vm_pgoff << PAGE_SHIFT, count);
476 if (gref == NULL) { 524 if (gref == NULL) {
477 rv = -ENOENT; 525 rv = -ENOENT;
@@ -480,9 +528,13 @@ static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma)
480 goto out_unlock; 528 goto out_unlock;
481 } 529 }
482 530
483 vma->vm_private_data = gref; 531 vm_priv->gref = gref;
532 vm_priv->users = 1;
533 vm_priv->count = count;
534
535 vma->vm_private_data = vm_priv;
484 536
485 vma->vm_flags |= VM_RESERVED; 537 vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
486 538
487 vma->vm_ops = &gntalloc_vmops; 539 vma->vm_ops = &gntalloc_vmops;
488 540
@@ -499,7 +551,7 @@ static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma)
499 rv = 0; 551 rv = 0;
500 552
501out_unlock: 553out_unlock:
502 spin_unlock(&gref_lock); 554 mutex_unlock(&gref_mutex);
503 return rv; 555 return rv;
504} 556}
505 557