Commit ed63bb1d authored by Greg Hackmann's avatar Greg Hackmann Committed by Sumit Semwal
Browse files

dma-buf: give each buffer a full-fledged inode



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: default avatarGreg Hackmann <ghackmann@google.com>
Signed-off-by: default avatarChenbo Feng <fengc@google.com>
Signed-off-by: default avatarSumit Semwal <sumit.semwal@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20190613223408.139221-2-fengc@google.com
parent 51e857af
Loading
Loading
Loading
Loading
+57 −6
Original line number Diff line number Diff line
@@ -34,8 +34,10 @@
#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);
+1 −0
Original line number Diff line number Diff line
@@ -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__ */