diff options
Diffstat (limited to 'drivers/dma-buf/dma-buf.c')
| -rw-r--r-- | drivers/dma-buf/dma-buf.c | 143 |
1 files changed, 107 insertions, 36 deletions
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index edaa9e4ee4ae..77555096e4c7 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -33,7 +33,31 @@ #include <uapi/linux/dma-buf.h> #include <uapi/linux/magic.h> -#include "dma-buf-sysfs-stats.h" +#define CREATE_TRACE_POINTS +#include <trace/events/dma_buf.h> + +/* + * dmabuf->name must be accessed with holding dmabuf->name_lock. + * we need to take the lock around the tracepoint call itself where + * it is called in the code. + * + * Note: FUNC##_enabled() is a static branch that will only + * be set when the trace event is enabled. + */ +#define DMA_BUF_TRACE(FUNC, ...) \ + do { \ + /* Always expose lock if lockdep is enabled */ \ + if (IS_ENABLED(CONFIG_LOCKDEP) || FUNC##_enabled()) { \ + guard(spinlock)(&dmabuf->name_lock); \ + FUNC(__VA_ARGS__); \ + } \ + } while (0) + +/* Wrapper to hide the sg_table page link from the importer */ +struct dma_buf_sg_table_wrapper { + struct sg_table *original; + struct sg_table wrapper; +}; static inline int is_dma_buf_file(struct file *); @@ -156,7 +180,6 @@ static void dma_buf_release(struct dentry *dentry) */ BUG_ON(dmabuf->cb_in.active || dmabuf->cb_out.active); - dma_buf_stats_teardown(dmabuf); dmabuf->ops->release(dmabuf); if (dmabuf->resv == (struct dma_resv *)&dmabuf[1]) @@ -220,6 +243,8 @@ static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma) dmabuf->size >> PAGE_SHIFT) return -EINVAL; + DMA_BUF_TRACE(trace_dma_buf_mmap_internal, dmabuf); + return dmabuf->ops->mmap(dmabuf, vma); } @@ -735,22 +760,16 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) dmabuf->resv = resv; } - ret = dma_buf_stats_setup(dmabuf, file); - if (ret) - goto err_dmabuf; - file->private_data = dmabuf; file->f_path.dentry->d_fsdata = dmabuf; dmabuf->file = file; __dma_buf_list_add(dmabuf); + DMA_BUF_TRACE(trace_dma_buf_export, dmabuf); + return dmabuf; -err_dmabuf: - if (!resv) - dma_resv_fini(dmabuf->resv); - kfree(dmabuf); err_file: fput(file); err_module: @@ -768,10 +787,15 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_export, "DMA_BUF"); */ int dma_buf_fd(struct dma_buf *dmabuf, int flags) { + int fd; + if (!dmabuf || !dmabuf->file) return -EINVAL; - return FD_ADD(flags, dmabuf->file); + fd = FD_ADD(flags, dmabuf->file); + DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd); + + return fd; } EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF"); @@ -786,6 +810,7 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF"); struct dma_buf *dma_buf_get(int fd) { struct file *file; + struct dma_buf *dmabuf; file = fget(fd); @@ -797,7 +822,11 @@ struct dma_buf *dma_buf_get(int fd) return ERR_PTR(-EINVAL); } - return file->private_data; + dmabuf = file->private_data; + + DMA_BUF_TRACE(trace_dma_buf_get, dmabuf, fd); + + return dmabuf; } EXPORT_SYMBOL_NS_GPL(dma_buf_get, "DMA_BUF"); @@ -817,24 +846,64 @@ void dma_buf_put(struct dma_buf *dmabuf) return; fput(dmabuf->file); + + DMA_BUF_TRACE(trace_dma_buf_put, dmabuf); } EXPORT_SYMBOL_NS_GPL(dma_buf_put, "DMA_BUF"); -static void mangle_sg_table(struct sg_table *sg_table) +static int dma_buf_wrap_sg_table(struct sg_table **sg_table) { -#ifdef CONFIG_DMABUF_DEBUG - int i; - struct scatterlist *sg; - - /* To catch abuse of the underlying struct page by importers mix - * up the bits, but take care to preserve the low SG_ bits to - * not corrupt the sgt. The mixing is undone on unmap - * before passing the sgt back to the exporter. + struct scatterlist *to_sg, *from_sg; + struct sg_table *from = *sg_table; + struct dma_buf_sg_table_wrapper *to; + int i, ret; + + if (!IS_ENABLED(CONFIG_DMABUF_DEBUG)) + return 0; + + /* + * To catch abuse of the underlying struct page by importers copy the + * sg_table without copying the page_link and give only the copy back to + * the importer. */ - for_each_sgtable_sg(sg_table, sg, i) - sg->page_link ^= ~0xffUL; -#endif + to = kzalloc(sizeof(*to), GFP_KERNEL); + if (!to) + return -ENOMEM; + + ret = sg_alloc_table(&to->wrapper, from->nents, GFP_KERNEL); + if (ret) + goto free_to; + + to_sg = to->wrapper.sgl; + for_each_sgtable_dma_sg(from, from_sg, i) { + to_sg->offset = 0; + to_sg->length = 0; + sg_assign_page(to_sg, NULL); + sg_dma_address(to_sg) = sg_dma_address(from_sg); + sg_dma_len(to_sg) = sg_dma_len(from_sg); + to_sg = sg_next(to_sg); + } + + to->original = from; + *sg_table = &to->wrapper; + return 0; +free_to: + kfree(to); + return ret; +} + +static void dma_buf_unwrap_sg_table(struct sg_table **sg_table) +{ + struct dma_buf_sg_table_wrapper *copy; + + if (!IS_ENABLED(CONFIG_DMABUF_DEBUG)) + return; + + copy = container_of(*sg_table, typeof(*copy), wrapper); + *sg_table = copy->original; + sg_free_table(©->wrapper); + kfree(copy); } static inline bool @@ -971,6 +1040,9 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev, list_add(&attach->node, &dmabuf->attachments); dma_resv_unlock(dmabuf->resv); + DMA_BUF_TRACE(trace_dma_buf_dynamic_attach, dmabuf, attach, + dma_buf_attachment_is_dynamic(attach), dev); + return attach; err_attach: @@ -1015,6 +1087,9 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) if (dmabuf->ops->detach) dmabuf->ops->detach(dmabuf, attach); + DMA_BUF_TRACE(trace_dma_buf_detach, dmabuf, attach, + dma_buf_attachment_is_dynamic(attach), attach->dev); + kfree(attach); } EXPORT_SYMBOL_NS_GPL(dma_buf_detach, "DMA_BUF"); @@ -1131,10 +1206,11 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, if (ret < 0) goto error_unmap; } - mangle_sg_table(sg_table); + ret = dma_buf_wrap_sg_table(&sg_table); + if (ret) + goto error_unmap; -#ifdef CONFIG_DMA_API_DEBUG - { + if (IS_ENABLED(CONFIG_DMA_API_DEBUG)) { struct scatterlist *sg; u64 addr; int len; @@ -1146,10 +1222,10 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, if (!PAGE_ALIGNED(addr) || !PAGE_ALIGNED(len)) { pr_debug("%s: addr %llx or len %x is not page aligned!\n", __func__, addr, len); + break; } } } -#endif /* CONFIG_DMA_API_DEBUG */ return sg_table; error_unmap: @@ -1213,7 +1289,7 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, dma_resv_assert_held(attach->dmabuf->resv); - mangle_sg_table(sg_table); + dma_buf_unwrap_sg_table(&sg_table); attach->dmabuf->ops->unmap_dma_buf(attach, sg_table, direction); if (dma_buf_pin_on_map(attach)) @@ -1480,6 +1556,8 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, vma_set_file(vma, dmabuf->file); vma->vm_pgoff = pgoff; + DMA_BUF_TRACE(trace_dma_buf_mmap, dmabuf); + return dmabuf->ops->mmap(dmabuf, vma); } EXPORT_SYMBOL_NS_GPL(dma_buf_mmap, "DMA_BUF"); @@ -1710,12 +1788,6 @@ static inline void dma_buf_uninit_debugfs(void) static int __init dma_buf_init(void) { - int ret; - - ret = dma_buf_init_sysfs_statistics(); - if (ret) - return ret; - dma_buf_mnt = kern_mount(&dma_buf_fs_type); if (IS_ERR(dma_buf_mnt)) return PTR_ERR(dma_buf_mnt); @@ -1729,6 +1801,5 @@ static void __exit dma_buf_deinit(void) { dma_buf_uninit_debugfs(); kern_unmount(dma_buf_mnt); - dma_buf_uninit_sysfs_statistics(); } __exitcall(dma_buf_deinit); |
