diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-28 22:43:13 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-28 22:43:13 -0500 |
| commit | 5e04f4b4290e03deb91b074087ae8d7c169d947d (patch) | |
| tree | 07802848a6afce236de7c3185be06db8567b38f0 | |
| parent | b6669737d3db7df79fad07180837c23dbe581db5 (diff) | |
| parent | 495c10cc1c0c359871d5bef32dd173252fc17995 (diff) | |
Merge tag 'tag-for-linus-3.9' of git://git.linaro.org/people/sumitsemwal/linux-dma-buf
Pull dma-buf framework updates from Sumit Semwal:
"Refcounting implemented for vmap in core dma-buf"
* tag 'tag-for-linus-3.9' of git://git.linaro.org/people/sumitsemwal/linux-dma-buf:
CHROMIUM: dma-buf: restore args on failure of dma_buf_mmap
dma-buf: implement vmap refcounting in the interface logic
| -rw-r--r-- | Documentation/dma-buf-sharing.txt | 6 | ||||
| -rw-r--r-- | drivers/base/dma-buf.c | 66 | ||||
| -rw-r--r-- | include/linux/dma-buf.h | 4 |
3 files changed, 63 insertions, 13 deletions
diff --git a/Documentation/dma-buf-sharing.txt b/Documentation/dma-buf-sharing.txt index 0188903bc9e1..4966b1be42ac 100644 --- a/Documentation/dma-buf-sharing.txt +++ b/Documentation/dma-buf-sharing.txt | |||
| @@ -302,7 +302,11 @@ Access to a dma_buf from the kernel context involves three steps: | |||
| 302 | void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) | 302 | void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) |
| 303 | 303 | ||
| 304 | The vmap call can fail if there is no vmap support in the exporter, or if it | 304 | The vmap call can fail if there is no vmap support in the exporter, or if it |
| 305 | runs out of vmalloc space. Fallback to kmap should be implemented. | 305 | runs out of vmalloc space. Fallback to kmap should be implemented. Note that |
| 306 | the dma-buf layer keeps a reference count for all vmap access and calls down | ||
| 307 | into the exporter's vmap function only when no vmapping exists, and only | ||
| 308 | unmaps it once. Protection against concurrent vmap/vunmap calls is provided | ||
| 309 | by taking the dma_buf->lock mutex. | ||
| 306 | 310 | ||
| 307 | 3. Finish access | 311 | 3. Finish access |
| 308 | 312 | ||
diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index ff5b745c4705..2a7cb0df176b 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c | |||
| @@ -39,6 +39,8 @@ static int dma_buf_release(struct inode *inode, struct file *file) | |||
| 39 | 39 | ||
| 40 | dmabuf = file->private_data; | 40 | dmabuf = file->private_data; |
| 41 | 41 | ||
| 42 | BUG_ON(dmabuf->vmapping_counter); | ||
| 43 | |||
| 42 | dmabuf->ops->release(dmabuf); | 44 | dmabuf->ops->release(dmabuf); |
| 43 | kfree(dmabuf); | 45 | kfree(dmabuf); |
| 44 | return 0; | 46 | return 0; |
| @@ -445,6 +447,9 @@ EXPORT_SYMBOL_GPL(dma_buf_kunmap); | |||
| 445 | int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, | 447 | int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, |
| 446 | unsigned long pgoff) | 448 | unsigned long pgoff) |
| 447 | { | 449 | { |
| 450 | struct file *oldfile; | ||
| 451 | int ret; | ||
| 452 | |||
| 448 | if (WARN_ON(!dmabuf || !vma)) | 453 | if (WARN_ON(!dmabuf || !vma)) |
| 449 | return -EINVAL; | 454 | return -EINVAL; |
| 450 | 455 | ||
| @@ -458,14 +463,22 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, | |||
| 458 | return -EINVAL; | 463 | return -EINVAL; |
| 459 | 464 | ||
| 460 | /* readjust the vma */ | 465 | /* readjust the vma */ |
| 461 | if (vma->vm_file) | 466 | get_file(dmabuf->file); |
| 462 | fput(vma->vm_file); | 467 | oldfile = vma->vm_file; |
| 463 | 468 | vma->vm_file = dmabuf->file; | |
| 464 | vma->vm_file = get_file(dmabuf->file); | ||
| 465 | |||
| 466 | vma->vm_pgoff = pgoff; | 469 | vma->vm_pgoff = pgoff; |
| 467 | 470 | ||
| 468 | return dmabuf->ops->mmap(dmabuf, vma); | 471 | ret = dmabuf->ops->mmap(dmabuf, vma); |
| 472 | if (ret) { | ||
| 473 | /* restore old parameters on failure */ | ||
| 474 | vma->vm_file = oldfile; | ||
| 475 | fput(dmabuf->file); | ||
| 476 | } else { | ||
| 477 | if (oldfile) | ||
| 478 | fput(oldfile); | ||
| 479 | } | ||
| 480 | return ret; | ||
| 481 | |||
| 469 | } | 482 | } |
| 470 | EXPORT_SYMBOL_GPL(dma_buf_mmap); | 483 | EXPORT_SYMBOL_GPL(dma_buf_mmap); |
| 471 | 484 | ||
| @@ -481,12 +494,34 @@ EXPORT_SYMBOL_GPL(dma_buf_mmap); | |||
| 481 | */ | 494 | */ |
| 482 | void *dma_buf_vmap(struct dma_buf *dmabuf) | 495 | void *dma_buf_vmap(struct dma_buf *dmabuf) |
| 483 | { | 496 | { |
| 497 | void *ptr; | ||
| 498 | |||
| 484 | if (WARN_ON(!dmabuf)) | 499 | if (WARN_ON(!dmabuf)) |
| 485 | return NULL; | 500 | return NULL; |
| 486 | 501 | ||
| 487 | if (dmabuf->ops->vmap) | 502 | if (!dmabuf->ops->vmap) |
| 488 | return dmabuf->ops->vmap(dmabuf); | 503 | return NULL; |
| 489 | return NULL; | 504 | |
| 505 | mutex_lock(&dmabuf->lock); | ||
| 506 | if (dmabuf->vmapping_counter) { | ||
| 507 | dmabuf->vmapping_counter++; | ||
| 508 | BUG_ON(!dmabuf->vmap_ptr); | ||
| 509 | ptr = dmabuf->vmap_ptr; | ||
| 510 | goto out_unlock; | ||
| 511 | } | ||
| 512 | |||
| 513 | BUG_ON(dmabuf->vmap_ptr); | ||
| 514 | |||
| 515 | ptr = dmabuf->ops->vmap(dmabuf); | ||
| 516 | if (IS_ERR_OR_NULL(ptr)) | ||
| 517 | goto out_unlock; | ||
| 518 | |||
| 519 | dmabuf->vmap_ptr = ptr; | ||
| 520 | dmabuf->vmapping_counter = 1; | ||
| 521 | |||
| 522 | out_unlock: | ||
| 523 | mutex_unlock(&dmabuf->lock); | ||
| 524 | return ptr; | ||
| 490 | } | 525 | } |
| 491 | EXPORT_SYMBOL_GPL(dma_buf_vmap); | 526 | EXPORT_SYMBOL_GPL(dma_buf_vmap); |
| 492 | 527 | ||
| @@ -500,7 +535,16 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) | |||
| 500 | if (WARN_ON(!dmabuf)) | 535 | if (WARN_ON(!dmabuf)) |
| 501 | return; | 536 | return; |
| 502 | 537 | ||
| 503 | if (dmabuf->ops->vunmap) | 538 | BUG_ON(!dmabuf->vmap_ptr); |
| 504 | dmabuf->ops->vunmap(dmabuf, vaddr); | 539 | BUG_ON(dmabuf->vmapping_counter == 0); |
| 540 | BUG_ON(dmabuf->vmap_ptr != vaddr); | ||
| 541 | |||
| 542 | mutex_lock(&dmabuf->lock); | ||
| 543 | if (--dmabuf->vmapping_counter == 0) { | ||
| 544 | if (dmabuf->ops->vunmap) | ||
| 545 | dmabuf->ops->vunmap(dmabuf, vaddr); | ||
| 546 | dmabuf->vmap_ptr = NULL; | ||
| 547 | } | ||
| 548 | mutex_unlock(&dmabuf->lock); | ||
| 505 | } | 549 | } |
| 506 | EXPORT_SYMBOL_GPL(dma_buf_vunmap); | 550 | EXPORT_SYMBOL_GPL(dma_buf_vunmap); |
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 3d754a394e92..9978b614a1aa 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h | |||
| @@ -119,8 +119,10 @@ struct dma_buf { | |||
| 119 | struct file *file; | 119 | struct file *file; |
| 120 | struct list_head attachments; | 120 | struct list_head attachments; |
| 121 | const struct dma_buf_ops *ops; | 121 | const struct dma_buf_ops *ops; |
| 122 | /* mutex to serialize list manipulation and attach/detach */ | 122 | /* mutex to serialize list manipulation, attach/detach and vmap/unmap */ |
| 123 | struct mutex lock; | 123 | struct mutex lock; |
| 124 | unsigned vmapping_counter; | ||
| 125 | void *vmap_ptr; | ||
| 124 | void *priv; | 126 | void *priv; |
| 125 | }; | 127 | }; |
| 126 | 128 | ||
