summaryrefslogtreecommitdiff
path: root/drivers/dma-buf/dma-buf.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma-buf/dma-buf.c')
-rw-r--r--drivers/dma-buf/dma-buf.c143
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(&copy->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);