dma-buf: give each buffer a full-fledged inode
authorGreg Hackmann <ghackmann@google.com>
Thu, 13 Jun 2019 22:34:06 +0000 (15:34 -0700)
committerSumit Semwal <sumit.semwal@linaro.org>
Fri, 14 Jun 2019 09:30:50 +0000 (15:00 +0530)
By traversing /proc/*/fd and /proc/*/map_files, processes with CAP_ADMIN
can get a lot of fine-grained data about how shmem buffers are shared
among processes.  stat(2) on each entry gives the caller a unique
ID (st_ino), the buffer's size (st_size), and even the number of pages
currently charged to the buffer (st_blocks / 512).

In contrast, all dma-bufs share the same anonymous inode.  So while we
can count how many dma-buf fds or mappings a process has, we can't get
the size of the backing buffers or tell if two entries point to the same
dma-buf.  On systems with debugfs, we can get a per-buffer breakdown of
size and reference count, but can't tell which processes are actually
holding the references to each buffer.

Replace the singleton inode with full-fledged inodes allocated by
alloc_anon_inode().  This involves creating and mounting a
mini-pseudo-filesystem for dma-buf, following the example in fs/aio.c.

Signed-off-by: Greg Hackmann <ghackmann@google.com>
Signed-off-by: Chenbo Feng <fengc@google.com>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20190613223408.139221-2-fengc@google.com
drivers/dma-buf/dma-buf.c
include/uapi/linux/magic.h

index f4104a21b06907ebdb13a3b768e640f0f5ee0b79..3612ccededd670103dde88cb14b5af479341a84e 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 *);
 
@@ -46,6 +48,25 @@ struct dma_buf_list {
 
 static struct dma_buf_list db_list;
 
+static const struct dentry_operations dma_buf_dentry_ops = {
+       .d_dname = simple_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;
@@ -342,6 +363,31 @@ 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;
+
+       return file;
+
+err_alloc_file:
+       iput(inode);
+       return file;
+}
+
 /**
  * DOC: dma buf device access
  *
@@ -436,8 +482,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;
@@ -1055,8 +1100,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);
@@ -1067,11 +1112,12 @@ 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\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);
 
                robj = buf_obj->resv;
                while (true) {
@@ -1167,6 +1213,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();
@@ -1177,5 +1227,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);
index f8c00045d537e4b810a768ace70211d0bc0e3686..665e18627f78ca9bb4e744f07a6a0e6b74190469 100644 (file)
@@ -91,5 +91,6 @@
 #define UDF_SUPER_MAGIC                0x15013346
 #define BALLOON_KVM_MAGIC      0x13661366
 #define ZSMALLOC_MAGIC         0x58295829
+#define DMA_BUF_MAGIC          0x444d4142      /* "DMAB" */
 
 #endif /* __LINUX_MAGIC_H__ */