From 0bf76c5311039778424e741617a0dd14b77f1763 Mon Sep 17 00:00:00 2001 From: "T.J. Mercier" Date: Tue, 8 Jul 2025 22:58:14 +0000 Subject: [PATCH] ANDROID: Track per-process dmabuf PSS DMA buffers exist for sharing memory, so dividing a buffer's size by the number of processes with references to it to obtain proportional set size is a useful metric for understanding an individual process's share of system-wide dmabuf memory. Dmabuf memory is not guaranteed to be representable by struct pages, and a process may hold only file descriptor references to a buffer. So PSS cannot be calculated on a per-page basis, and PSS accounting is always performed in units of the full buffer size, and only once for each process regardless of the number and type of references a process has for a single buffer. The /proc//dmabuf_pss file in procfs now reports the sum of all buffer PSS values referenced by a process. The units are bytes. This allows userspace to obtain per-process dmabuf accounting information quickly compared to calculating it from multiple sources in procfs and sysfs. Note that a dmabuf can be backed by different types of memory such as system DRAM, GPU VRAM, or others. This patch makes no distinction between these different types of memory, so on systems with non-unified memory the reported values should be interpreted with this in mind. Bug: 424648392 Change-Id: I8ec370b0d7fd37e69f677c6f580940c89cc03a42 Signed-off-by: T.J. Mercier --- drivers/dma-buf/dma-buf.c | 8 ++++++++ fs/proc/base.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/dma-buf.h | 8 ++++++++ 3 files changed, 50 insertions(+) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 7c9ac163d115..cb91dadeb465 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -115,6 +115,9 @@ static void dma_buf_release(struct dentry *dentry) if (dmabuf->resv == (struct dma_resv *)&dmabuf[1]) dma_resv_fini(dmabuf->resv); + if (atomic64_read(&dmabuf->num_unique_refs)) + pr_err("destroying dmabuf with non-zero task refs\n"); + WARN_ON(!list_empty(&dmabuf->attachments)); module_put(dmabuf->owner); kfree(dmabuf->name); @@ -199,6 +202,8 @@ static int new_task_dmabuf_record(struct task_struct *task, struct dma_buf *dmab rec->refcnt = 1; list_add(&rec->node, &task->dmabuf_info->dmabufs); + atomic64_inc(&dmabuf->num_unique_refs); + return 0; } @@ -276,6 +281,7 @@ void dma_buf_unaccount_task(struct dma_buf *dmabuf, struct task_struct *task) list_del(&rec->node); kfree(rec); task->dmabuf_info->rss -= dmabuf->size; + atomic64_dec(&dmabuf->num_unique_refs); } err: spin_unlock(&task->dmabuf_info->lock); @@ -851,6 +857,8 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) dmabuf->resv = resv; } + atomic64_set(&dmabuf->num_unique_refs, 0); + file->private_data = dmabuf; file->f_path.dentry->d_fsdata = dmabuf; dmabuf->file = file; diff --git a/fs/proc/base.c b/fs/proc/base.c index 6b91ddcab7e2..2eee67e06ffe 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3397,6 +3397,39 @@ static const struct file_operations proc_dmabuf_rss_hwm_operations = { .llseek = seq_lseek, .release = single_release, }; + +static int proc_dmabuf_pss_show(struct seq_file *m, struct pid_namespace *ns, + struct pid *pid, struct task_struct *task) +{ + struct task_dma_buf_record *rec; + u64 pss = 0; + + if (!task->dmabuf_info) { + pr_err("%s dmabuf accounting record was not allocated\n", __func__); + return -ENOMEM; + } + + if (!(task->flags & PF_KTHREAD)) { + spin_lock(&task->dmabuf_info->lock); + list_for_each_entry(rec, &task->dmabuf_info->dmabufs, node) { + s64 refs = atomic64_read(&rec->dmabuf->num_unique_refs); + + if (refs <= 0) { + pr_err("dmabuf has <= refs %lld\n", refs); + continue; + } + + pss += rec->dmabuf->size / (size_t)refs; + } + spin_unlock(&task->dmabuf_info->lock); + + seq_printf(m, "%llu\n", pss); + } else { + seq_puts(m, "0\n"); + } + + return 0; +} #endif /* @@ -3525,6 +3558,7 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_DMA_SHARED_BUFFER ONE("dmabuf_rss", S_IRUGO, proc_dmabuf_rss_show), REG("dmabuf_rss_hwm", S_IRUGO|S_IWUSR, proc_dmabuf_rss_hwm_operations), + ONE("dmabuf_pss", S_IRUGO, proc_dmabuf_pss_show), #endif }; diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index a362c8ba7a21..267bf322272f 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -25,6 +25,7 @@ #include #include #ifndef __GENKSYMS__ +#include #include #endif @@ -534,6 +535,13 @@ struct dma_buf { } *sysfs_entry; #endif + /** + * @num_unique_refs: + * + * The number of tasks that reference this buffer. For calculating PSS. + */ + atomic64_t num_unique_refs; + ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); };