diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst
index 2fa69f710e2a..757dd47ab679 100644
--- a/Documentation/filesystems/proc.rst
+++ b/Documentation/filesystems/proc.rst
@@ -47,6 +47,7 @@ fixes/update part 1.1 Stefani Seibold <stefani@seibold.net> June 9 2009
3.10 /proc/<pid>/timerslack_ns - Task timerslack value
3.11 /proc/<pid>/patch_state - Livepatch patch operation state
3.12 /proc/<pid>/arch_status - Task architecture specific information
+ 3.13 /proc/<pid>/task/<tid>/dmabuf_fds - DMA buffers referenced by an FD
4 Configuring procfs
4.1 Mount options
@@ -2131,6 +2132,35 @@ AVX512_elapsed_ms
the task is unlikely an AVX512 user, but depends on the workload and the
scheduling scenario, it also could be a false negative mentioned above.
+3.13 /proc/<pid>/task/<tid>/dmabuf_fds - DMA buffers referenced by an FD
+-------------------------------------------------------------------------
+This file exposes a list of the inode numbers for every DMA buffer
+FD that the task has.
+
+The same inode number can appear more than once, indicating the total
+FD references for the associated DMA buffer.
+
+The inode number can be used to lookup the DMA buffer information in
+the sysfs interface /sys/kernel/dmabuf/buffers/<inode-no>/.
+
+Example Output
+~~~~~~~~~~~~~~
+$ cat /proc/612/task/612/dmabuf_fds
+30972 30973 45678 49326
+
+Permission to access this file is governed by a ptrace access mode
+PTRACE_MODE_READ_FSCREDS.
+
+Threads can have different files when created without specifying
+the CLONE_FILES flag. For this reason the interface is presented as
+/proc/<pid>/task/<tid>/dmabuf_fds and not /proc/<pid>/dmabuf_fds.
+This simplifies kernel code and aggregation can be handled in
+userspace.
+
+If a thread has the same files as its group leader, then its dmabuf_fds
+file will be empty as these dmabufs are already reported by the
+group leader.
+
Chapter 4: Configuring procfs
=============================
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 9ad6397aaa97..0660c06be4c6 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -29,8 +29,6 @@
#include <uapi/linux/dma-buf.h>
#include <uapi/linux/magic.h>
-static inline int is_dma_buf_file(struct file *);
-
struct dma_buf_list {
struct list_head head;
struct mutex lock;@@ -434,10 +432,7 @@ static const struct file_operations dma_buf_fops = {
.show_fdinfo = dma_buf_show_fdinfo,
};
-/*
- * is_dma_buf_file - Check if struct file* is associated with dma_buf
- */
-static inline int is_dma_buf_file(struct file *file)
+int is_dma_buf_file(struct file *file)
{
return file->f_op == &dma_buf_fops;
}diff --git a/fs/proc/Makefile b/fs/proc/Makefile
index bd08616ed8ba..91a67f43ddf4 100644
--- a/fs/proc/Makefile
+++ b/fs/proc/Makefile
@@ -16,6 +16,7 @@ proc-y += cmdline.o
proc-y += consoles.o
proc-y += cpuinfo.o
proc-y += devices.o
+proc-y += dma_bufs.o
proc-y += interrupts.o
proc-y += loadavg.o
proc-y += meminfo.o
diff --git a/fs/proc/base.c b/fs/proc/base.c
index b3422cda2a91..af15a60b9831 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -3598,6 +3598,7 @@ static const struct pid_entry tid_base_stuff[] = {
#ifdef CONFIG_SECCOMP_CACHE_DEBUG
ONE("seccomp_cache", S_IRUSR, proc_pid_seccomp_cache),
#endif
+ REG("dmabuf_fds", 0444, proc_tid_dmabuf_fds_operations),
};
static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx)diff --git a/fs/proc/dma_bufs.c b/fs/proc/dma_bufs.c
new file mode 100644
index 000000000000..46ea9cf968ed
--- /dev/null
+++ b/fs/proc/dma_bufs.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Per-process DMA-BUF Stats
+ *
+ * Copyright (C) 2021 Google LLC.
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/fdtable.h>
+#include <linux/ptrace.h>
+#include <linux/seq_file.h>
+
+#include "internal.h"
+
+struct dmabuf_fds_private {
+ struct inode *inode;
+ struct task_struct *task;
+ struct file *dmabuf_file;
+};
+
+static loff_t *next_dmabuf(struct dmabuf_fds_private *priv,
+ loff_t *pos)
+{
+ struct fdtable *fdt;
+ struct file *file;
+
+ rcu_read_lock();
+ fdt = files_fdtable(priv->task->files);
+ for (; *pos < fdt->max_fds; ++*pos) {
+ file = files_lookup_fd_rcu(priv->task->files, (unsigned int) *pos);
+ if (file && is_dma_buf_file(file) && get_file_rcu(file)) {
+ priv->dmabuf_file = file;
+ break;
+ }
+ }
+ if (*pos >= fdt->max_fds)
+ pos = NULL;
+ rcu_read_unlock();
+
+ return pos;
+}
+
+static void *dmabuf_fds_seq_start(struct seq_file *s, loff_t *pos)
+{
+ struct dmabuf_fds_private *priv = s->private;
+ struct files_struct *group_leader_files;
+
+ priv->task = get_proc_task(priv->inode);
+
+ if (!priv->task)
+ return ERR_PTR(-ESRCH);
+
+ /* Hold task lock for duration that files need to be stable */
+ task_lock(priv->task);
+
+ /*
+ * If this task is not the group leader but shares the same files, leave file empty.
+ * These dmabufs are already reported in the group leader's dmabuf_fds.
+ */
+ group_leader_files = priv->task->group_leader->files;
+ if (priv->task != priv->task->group_leader && priv->task->files == group_leader_files) {
+ task_unlock(priv->task);
+ put_task_struct(priv->task);
+ priv->task = NULL;
+ return NULL;
+ }
+
+ return next_dmabuf(priv, pos);
+}
+
+static void *dmabuf_fds_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ ++*pos;
+ return next_dmabuf(s->private, pos);
+}
+
+static void dmabuf_fds_seq_stop(struct seq_file *s, void *v)
+{
+ struct dmabuf_fds_private *priv = s->private;
+
+ if (priv->task) {
+ task_unlock(priv->task);
+ put_task_struct(priv->task);
+
+ }
+ if (priv->dmabuf_file)
+ fput(priv->dmabuf_file);
+}
+
+static int dmabuf_fds_seq_show(struct seq_file *s, void *v)
+{
+ struct dmabuf_fds_private *priv = s->private;
+ struct file *file = priv->dmabuf_file;
+ struct dma_buf *dmabuf = file->private_data;
+
+ if (!dmabuf)
+ return -ESRCH;
+
+ seq_printf(s, "%8lu ", file_inode(file)->i_ino);
+
+ fput(priv->dmabuf_file);
+ priv->dmabuf_file = NULL;
+
+ return 0;
+}
+
+static const struct seq_operations proc_tid_dmabuf_fds_seq_ops = {
+ .start = dmabuf_fds_seq_start,
+ .next = dmabuf_fds_seq_next,
+ .stop = dmabuf_fds_seq_stop,
+ .show = dmabuf_fds_seq_show
+};
+
+static int proc_dmabuf_fds_open(struct inode *inode, struct file *file,
+ const struct seq_operations *ops)
+{
+ struct dmabuf_fds_private *priv;
+ struct task_struct *task;
+ bool allowed = false;
+
+ task = get_proc_task(inode);
+ if (!task)
+ return -ESRCH;
+
+ allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
+ put_task_struct(task);
+
+ if (!allowed)
+ return -EACCES;
+
+ priv = __seq_open_private(file, ops, sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+
+ priv->inode = inode;
+ priv->task = NULL;
+ priv->dmabuf_file = NULL;
+
+ return 0;
+}
+
+static int proc_dmabuf_fds_release(struct inode *inode, struct file *file)
+{
+ return seq_release_private(inode, file);
+}
+
+static int tid_dmabuf_fds_open(struct inode *inode, struct file *file)
+{
+ return proc_dmabuf_fds_open(inode, file,
+ &proc_tid_dmabuf_fds_seq_ops);
+}
+
+const struct file_operations proc_tid_dmabuf_fds_operations = {
+ .open = tid_dmabuf_fds_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = proc_dmabuf_fds_release,
+};
+diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index f60b379dcdc7..4ca74220db9c 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -303,6 +303,7 @@ extern const struct file_operations proc_pid_smaps_operations;
extern const struct file_operations proc_pid_smaps_rollup_operations;
extern const struct file_operations proc_clear_refs_operations;
extern const struct file_operations proc_pagemap_operations;
+extern const struct file_operations proc_tid_dmabuf_fds_operations;
extern unsigned long task_vsize(struct mm_struct *);
extern unsigned long task_statm(struct mm_struct *,
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index cf72699cb2bc..087e11f7f193 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -27,6 +27,11 @@ struct device;
struct dma_buf;
struct dma_buf_attachment;
+/**
+ * Check if struct file* is associated with dma_buf.
+ */
+int is_dma_buf_file(struct file *file);
+
/**
* struct dma_buf_ops - operations possible on struct dma_buf
* @vmap: [optional] creates a virtual mapping for the buffer into kernel
--
2.30.0.280.ga3ce27912f-goog