Merge tag 'drm-next-2019-07-16' of git://anongit.freedesktop.org/drm/drm
[linux-2.6-microblaze.git] / drivers / dma-buf / dma-buf.c
index bf4d4c8..dc4b2c5 100644 (file)
 #include <linux/poll.h>
 #include <linux/reservation.h>
 #include <linux/mm.h>
+#include <linux/mount.h>
 
 #include <uapi/linux/dma-buf.h>
+#include <uapi/linux/magic.h>
 
 static inline int is_dma_buf_file(struct file *);
 
@@ -35,6 +37,41 @@ struct dma_buf_list {
 
 static struct dma_buf_list db_list;
 
+static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen)
+{
+       struct dma_buf *dmabuf;
+       char name[DMA_BUF_NAME_LEN];
+       size_t ret = 0;
+
+       dmabuf = dentry->d_fsdata;
+       mutex_lock(&dmabuf->lock);
+       if (dmabuf->name)
+               ret = strlcpy(name, dmabuf->name, DMA_BUF_NAME_LEN);
+       mutex_unlock(&dmabuf->lock);
+
+       return dynamic_dname(dentry, buffer, buflen, "/%s:%s",
+                            dentry->d_name.name, ret > 0 ? name : "");
+}
+
+static const struct dentry_operations dma_buf_dentry_ops = {
+       .d_dname = dmabuffs_dname,
+};
+
+static struct vfsmount *dma_buf_mnt;
+
+static struct dentry *dma_buf_fs_mount(struct file_system_type *fs_type,
+               int flags, const char *name, void *data)
+{
+       return mount_pseudo(fs_type, "dmabuf:", NULL, &dma_buf_dentry_ops,
+                       DMA_BUF_MAGIC);
+}
+
+static struct file_system_type dma_buf_fs_type = {
+       .name = "dmabuf",
+       .mount = dma_buf_fs_mount,
+       .kill_sb = kill_anon_super,
+};
+
 static int dma_buf_release(struct inode *inode, struct file *file)
 {
        struct dma_buf *dmabuf;
@@ -79,6 +116,10 @@ static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
 
        dmabuf = file->private_data;
 
+       /* check if buffer supports mmap */
+       if (!dmabuf->ops->mmap)
+               return -EINVAL;
+
        /* check for overflowing the buffer's size */
        if (vma->vm_pgoff + vma_pages(vma) >
            dmabuf->size >> PAGE_SHIFT)
@@ -265,6 +306,43 @@ out:
        return events;
 }
 
+/**
+ * dma_buf_set_name - Set a name to a specific dma_buf to track the usage.
+ * The name of the dma-buf buffer can only be set when the dma-buf is not
+ * attached to any devices. It could theoritically support changing the
+ * name of the dma-buf if the same piece of memory is used for multiple
+ * purpose between different devices.
+ *
+ * @dmabuf [in]     dmabuf buffer that will be renamed.
+ * @buf:   [in]     A piece of userspace memory that contains the name of
+ *                  the dma-buf.
+ *
+ * Returns 0 on success. If the dma-buf buffer is already attached to
+ * devices, return -EBUSY.
+ *
+ */
+static long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf)
+{
+       char *name = strndup_user(buf, DMA_BUF_NAME_LEN);
+       long ret = 0;
+
+       if (IS_ERR(name))
+               return PTR_ERR(name);
+
+       mutex_lock(&dmabuf->lock);
+       if (!list_empty(&dmabuf->attachments)) {
+               ret = -EBUSY;
+               kfree(name);
+               goto out_unlock;
+       }
+       kfree(dmabuf->name);
+       dmabuf->name = name;
+
+out_unlock:
+       mutex_unlock(&dmabuf->lock);
+       return ret;
+}
+
 static long dma_buf_ioctl(struct file *file,
                          unsigned int cmd, unsigned long arg)
 {
@@ -303,11 +381,29 @@ static long dma_buf_ioctl(struct file *file,
                        ret = dma_buf_begin_cpu_access(dmabuf, direction);
 
                return ret;
+
+       case DMA_BUF_SET_NAME:
+               return dma_buf_set_name(dmabuf, (const char __user *)arg);
+
        default:
                return -ENOTTY;
        }
 }
 
+static void dma_buf_show_fdinfo(struct seq_file *m, struct file *file)
+{
+       struct dma_buf *dmabuf = file->private_data;
+
+       seq_printf(m, "size:\t%zu\n", dmabuf->size);
+       /* Don't count the temporary reference taken inside procfs seq_show */
+       seq_printf(m, "count:\t%ld\n", file_count(dmabuf->file) - 1);
+       seq_printf(m, "exp_name:\t%s\n", dmabuf->exp_name);
+       mutex_lock(&dmabuf->lock);
+       if (dmabuf->name)
+               seq_printf(m, "name:\t%s\n", dmabuf->name);
+       mutex_unlock(&dmabuf->lock);
+}
+
 static const struct file_operations dma_buf_fops = {
        .release        = dma_buf_release,
        .mmap           = dma_buf_mmap_internal,
@@ -317,6 +413,7 @@ static const struct file_operations dma_buf_fops = {
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = dma_buf_ioctl,
 #endif
+       .show_fdinfo    = dma_buf_show_fdinfo,
 };
 
 /*
@@ -327,6 +424,32 @@ static inline int is_dma_buf_file(struct file *file)
        return file->f_op == &dma_buf_fops;
 }
 
+static struct file *dma_buf_getfile(struct dma_buf *dmabuf, int flags)
+{
+       struct file *file;
+       struct inode *inode = alloc_anon_inode(dma_buf_mnt->mnt_sb);
+
+       if (IS_ERR(inode))
+               return ERR_CAST(inode);
+
+       inode->i_size = dmabuf->size;
+       inode_set_bytes(inode, dmabuf->size);
+
+       file = alloc_file_pseudo(inode, dma_buf_mnt, "dmabuf",
+                                flags, &dma_buf_fops);
+       if (IS_ERR(file))
+               goto err_alloc_file;
+       file->f_flags = flags & (O_ACCMODE | O_NONBLOCK);
+       file->private_data = dmabuf;
+       file->f_path.dentry->d_fsdata = dmabuf;
+
+       return file;
+
+err_alloc_file:
+       iput(inode);
+       return file;
+}
+
 /**
  * DOC: dma buf device access
  *
@@ -393,8 +516,7 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
                          || !exp_info->ops
                          || !exp_info->ops->map_dma_buf
                          || !exp_info->ops->unmap_dma_buf
-                         || !exp_info->ops->release
-                         || !exp_info->ops->mmap)) {
+                         || !exp_info->ops->release)) {
                return ERR_PTR(-EINVAL);
        }
 
@@ -422,8 +544,7 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
        }
        dmabuf->resv = resv;
 
-       file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf,
-                                       exp_info->flags);
+       file = dma_buf_getfile(dmabuf, exp_info->flags);
        if (IS_ERR(file)) {
                ret = PTR_ERR(file);
                goto err_dmabuf;
@@ -562,6 +683,7 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
        list_add(&attach->node, &dmabuf->attachments);
 
        mutex_unlock(&dmabuf->lock);
+
        return attach;
 
 err_attach:
@@ -584,6 +706,9 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
        if (WARN_ON(!dmabuf || !attach))
                return;
 
+       if (attach->sgt)
+               dmabuf->ops->unmap_dma_buf(attach, attach->sgt, attach->dir);
+
        mutex_lock(&dmabuf->lock);
        list_del(&attach->node);
        if (dmabuf->ops->detach)
@@ -619,10 +744,27 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
        if (WARN_ON(!attach || !attach->dmabuf))
                return ERR_PTR(-EINVAL);
 
+       if (attach->sgt) {
+               /*
+                * Two mappings with different directions for the same
+                * attachment are not allowed.
+                */
+               if (attach->dir != direction &&
+                   attach->dir != DMA_BIDIRECTIONAL)
+                       return ERR_PTR(-EBUSY);
+
+               return attach->sgt;
+       }
+
        sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction);
        if (!sg_table)
                sg_table = ERR_PTR(-ENOMEM);
 
+       if (!IS_ERR(sg_table) && attach->dmabuf->ops->cache_sgt_mapping) {
+               attach->sgt = sg_table;
+               attach->dir = direction;
+       }
+
        return sg_table;
 }
 EXPORT_SYMBOL_GPL(dma_buf_map_attachment);
@@ -646,8 +788,10 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
        if (WARN_ON(!attach || !attach->dmabuf || !sg_table))
                return;
 
-       attach->dmabuf->ops->unmap_dma_buf(attach, sg_table,
-                                               direction);
+       if (attach->sgt == sg_table)
+               return;
+
+       attach->dmabuf->ops->unmap_dma_buf(attach, sg_table, direction);
 }
 EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
 
@@ -895,6 +1039,10 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma,
        if (WARN_ON(!dmabuf || !vma))
                return -EINVAL;
 
+       /* check if buffer supports mmap */
+       if (!dmabuf->ops->mmap)
+               return -EINVAL;
+
        /* check for offset overflow */
        if (pgoff + vma_pages(vma) < pgoff)
                return -EOVERFLOW;
@@ -1014,8 +1162,8 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
                return ret;
 
        seq_puts(s, "\nDma-buf Objects:\n");
-       seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\n",
-                  "size", "flags", "mode", "count");
+       seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\n",
+                  "size", "flags", "mode", "count", "ino");
 
        list_for_each_entry(buf_obj, &db_list.head, list_node) {
                ret = mutex_lock_interruptible(&buf_obj->lock);
@@ -1026,11 +1174,13 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
                        continue;
                }
 
-               seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%s\n",
+               seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%s\t%08lu\t%s\n",
                                buf_obj->size,
                                buf_obj->file->f_flags, buf_obj->file->f_mode,
                                file_count(buf_obj->file),
-                               buf_obj->exp_name);
+                               buf_obj->exp_name,
+                               file_inode(buf_obj->file)->i_ino,
+                               buf_obj->name ?: "");
 
                robj = buf_obj->resv;
                while (true) {
@@ -1057,6 +1207,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
                                   fence->ops->get_driver_name(fence),
                                   fence->ops->get_timeline_name(fence),
                                   dma_fence_is_signaled(fence) ? "" : "un");
+                       dma_fence_put(fence);
                }
                rcu_read_unlock();
 
@@ -1125,6 +1276,10 @@ static inline void dma_buf_uninit_debugfs(void)
 
 static int __init dma_buf_init(void)
 {
+       dma_buf_mnt = kern_mount(&dma_buf_fs_type);
+       if (IS_ERR(dma_buf_mnt))
+               return PTR_ERR(dma_buf_mnt);
+
        mutex_init(&db_list.lock);
        INIT_LIST_HEAD(&db_list.head);
        dma_buf_init_debugfs();
@@ -1135,5 +1290,6 @@ subsys_initcall(dma_buf_init);
 static void __exit dma_buf_deinit(void)
 {
        dma_buf_uninit_debugfs();
+       kern_unmount(dma_buf_mnt);
 }
 __exitcall(dma_buf_deinit);