From: Albert Esteve <aesteve@redhat.com>
To: Tejun Heo <tj@kernel.org>, Johannes Weiner <hannes@cmpxchg.org>,
Michal Koutný <mkoutny@suse.com>,
Jonathan Corbet <corbet@lwn.net>,
Shuah Khan <skhan@linuxfoundation.org>,
Sumit Semwal <sumit.semwal@linaro.org>,
Christian König <christian.koenig@amd.com>,
Michal Hocko <mhocko@kernel.org>,
Roman Gushchin <roman.gushchin@linux.dev>,
Shakeel Butt <shakeel.butt@linux.dev>,
Muchun Song <muchun.song@linux.dev>,
Andrew Morton <akpm@linux-foundation.org>,
Benjamin Gaignard <benjamin.gaignard@collabora.com>,
Brian Starkey <Brian.Starkey@arm.com>,
John Stultz <jstultz@google.com>,
"T.J. Mercier" <tjmercier@google.com>,
Christian Brauner <brauner@kernel.org>,
Paul Moore <paul@paul-moore.com>,
James Morris <jmorris@namei.org>,
"Serge E. Hallyn" <serge@hallyn.com>,
Stephen Smalley <stephen.smalley.work@gmail.com>,
Ondrej Mosnacek <omosnace@redhat.com>,
Shuah Khan <shuah@kernel.org>
Cc: cgroups@vger.kernel.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-media@vger.kernel.org,
dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org,
linux-mm@kvack.org, linux-security-module@vger.kernel.org,
selinux@vger.kernel.org, linux-kselftest@vger.kernel.org,
Albert Esteve <aesteve@redhat.com>,
mripard@kernel.org, echanude@redhat.com
Subject: [PATCH RFC 1/5] memcg: Track exported dma-buffers
Date: Tue, 12 May 2026 11:10:43 +0200 [thread overview]
Message-ID: <20260512-v2_20230123_tjmercier_google_com-v1-1-6326701c3691@redhat.com> (raw)
In-Reply-To: <20260512-v2_20230123_tjmercier_google_com-v1-0-6326701c3691@redhat.com>
From: "T.J. Mercier" <tjmercier@google.com>
When a buffer is exported to userspace, use memcg to attribute the
buffer to the allocating cgroup until all buffer references are
released.
Unlike the dmabuf sysfs stats implementation, this memcg accounting
avoids contention over the kernfs_rwsem incurred when creating or
removing nodes.
Signed-off-by: T.J. Mercier <tjmercier@google.com>
Signed-off-by: Albert Esteve <aesteve@redhat.com>
---
Documentation/admin-guide/cgroup-v2.rst | 4 ++++
drivers/dma-buf/dma-buf.c | 13 ++++++++++++
include/linux/dma-buf.h | 4 ++++
include/linux/memcontrol.h | 37 +++++++++++++++++++++++++++++++++
mm/memcontrol.c | 19 +++++++++++++++++
5 files changed, 77 insertions(+)
diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst
index 6efd0095ed995..8bdbc2e866430 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -1635,6 +1635,10 @@ The following nested keys are defined.
Amount of memory used for storing in-kernel data
structures.
+ dmabuf (npn)
+ Amount of memory used for exported DMA buffers allocated by the cgroup.
+ Stays with the allocating cgroup regardless of how the buffer is shared.
+
workingset_refault_anon
Number of refaults of previously evicted anonymous pages.
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 71f37544a5c61..ce02377f48908 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -14,6 +14,7 @@
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/dma-buf.h>
+#include <linux/memcontrol.h>
#include <linux/dma-fence.h>
#include <linux/dma-fence-unwrap.h>
#include <linux/anon_inodes.h>
@@ -180,6 +181,9 @@ static void dma_buf_release(struct dentry *dentry)
*/
BUG_ON(dmabuf->cb_in.active || dmabuf->cb_out.active);
+ mem_cgroup_uncharge_dmabuf(dmabuf->memcg, PAGE_ALIGN(dmabuf->size) / PAGE_SIZE);
+ mem_cgroup_put(dmabuf->memcg);
+
dmabuf->ops->release(dmabuf);
if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
@@ -760,6 +764,13 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
dmabuf->resv = resv;
}
+ dmabuf->memcg = get_mem_cgroup_from_mm(current->mm);
+ if (!mem_cgroup_charge_dmabuf(dmabuf->memcg, PAGE_ALIGN(dmabuf->size) / PAGE_SIZE,
+ GFP_KERNEL)) {
+ ret = -ENOMEM;
+ goto err_memcg;
+ }
+
file->private_data = dmabuf;
file->f_path.dentry->d_fsdata = dmabuf;
dmabuf->file = file;
@@ -770,6 +781,8 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
return dmabuf;
+err_memcg:
+ mem_cgroup_put(dmabuf->memcg);
err_file:
fput(file);
err_module:
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index d1203da56fc5f..d9f1ccb51c60e 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -27,6 +27,7 @@
struct device;
struct dma_buf;
struct dma_buf_attachment;
+struct mem_cgroup;
/**
* struct dma_buf_ops - operations possible on struct dma_buf
@@ -429,6 +430,9 @@ struct dma_buf {
__poll_t active;
} cb_in, cb_out;
+
+ /** @memcg: the cgroup to which this buffer is currently attributed */
+ struct mem_cgroup *memcg;
};
/**
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index dc3fa687759b4..10068a833ad9e 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -39,6 +39,7 @@ enum memcg_stat_item {
MEMCG_ZSWAP_B,
MEMCG_ZSWAPPED,
MEMCG_ZSWAP_INCOMP,
+ MEMCG_DMABUF,
MEMCG_NR_STAT,
};
@@ -649,6 +650,24 @@ int mem_cgroup_charge_hugetlb(struct folio* folio, gfp_t gfp);
int mem_cgroup_swapin_charge_folio(struct folio *folio, struct mm_struct *mm,
gfp_t gfp, swp_entry_t entry);
+/**
+ * mem_cgroup_charge_dmabuf - Charge dma-buf memory to a cgroup and update stat counter
+ * @memcg: memcg to charge
+ * @nr_pages: number of pages to charge
+ * @gfp_mask: reclaim mode
+ *
+ * Charges @nr_pages to @memcg. Returns %true if the charge fit within
+ * @memcg's configured limit, %false if it doesn't.
+ */
+bool __mem_cgroup_charge_dmabuf(struct mem_cgroup *memcg, unsigned int nr_pages, gfp_t gfp_mask);
+static inline bool mem_cgroup_charge_dmabuf(struct mem_cgroup *memcg, unsigned int nr_pages,
+ gfp_t gfp_mask)
+{
+ if (mem_cgroup_disabled())
+ return true;
+ return __mem_cgroup_charge_dmabuf(memcg, nr_pages, gfp_mask);
+}
+
void __mem_cgroup_uncharge(struct folio *folio);
/**
@@ -664,6 +683,14 @@ static inline void mem_cgroup_uncharge(struct folio *folio)
__mem_cgroup_uncharge(folio);
}
+void __mem_cgroup_uncharge_dmabuf(struct mem_cgroup *memcg, unsigned int nr_pages);
+static inline void mem_cgroup_uncharge_dmabuf(struct mem_cgroup *memcg, unsigned int nr_pages)
+{
+ if (mem_cgroup_disabled())
+ return;
+ __mem_cgroup_uncharge_dmabuf(memcg, nr_pages);
+}
+
void __mem_cgroup_uncharge_folios(struct folio_batch *folios);
static inline void mem_cgroup_uncharge_folios(struct folio_batch *folios)
{
@@ -1142,10 +1169,20 @@ static inline int mem_cgroup_swapin_charge_folio(struct folio *folio,
return 0;
}
+static inline bool mem_cgroup_charge_dmabuf(struct mem_cgroup *memcg, unsigned int nr_pages,
+ gfp_t gfp_mask)
+{
+ return true;
+}
+
static inline void mem_cgroup_uncharge(struct folio *folio)
{
}
+static inline void mem_cgroup_uncharge_dmabuf(struct mem_cgroup *memcg, unsigned int nr_pages)
+{
+}
+
static inline void mem_cgroup_uncharge_folios(struct folio_batch *folios)
{
}
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index c03d4787d4668..15cee13d3ccd6 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -433,6 +433,7 @@ static const unsigned int memcg_stat_items[] = {
MEMCG_ZSWAP_B,
MEMCG_ZSWAPPED,
MEMCG_ZSWAP_INCOMP,
+ MEMCG_DMABUF,
};
#define NR_MEMCG_NODE_STAT_ITEMS ARRAY_SIZE(memcg_node_stat_items)
@@ -1580,6 +1581,7 @@ static const struct memory_stat memory_stats[] = {
#ifdef CONFIG_HUGETLB_PAGE
{ "hugetlb", NR_HUGETLB },
#endif
+ { "dmabuf", MEMCG_DMABUF },
/* The memory events */
{ "workingset_refault_anon", WORKINGSET_REFAULT_ANON },
@@ -5399,6 +5401,23 @@ void mem_cgroup_flush_workqueue(void)
flush_workqueue(memcg_wq);
}
+bool __mem_cgroup_charge_dmabuf(struct mem_cgroup *memcg, unsigned int nr_pages, gfp_t gfp_mask)
+{
+ if (try_charge(memcg, gfp_mask, nr_pages) == 0) {
+ mod_memcg_state(memcg, MEMCG_DMABUF, nr_pages);
+ return true;
+ }
+
+ return false;
+}
+
+void __mem_cgroup_uncharge_dmabuf(struct mem_cgroup *memcg, unsigned int nr_pages)
+{
+ mod_memcg_state(memcg, MEMCG_DMABUF, -nr_pages);
+ if (!mem_cgroup_is_root(memcg))
+ refill_stock(memcg, nr_pages);
+}
+
static int __init cgroup_memory(char *s)
{
char *token;
--
2.53.0
next prev parent reply other threads:[~2026-05-12 9:11 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-12 9:10 [PATCH RFC 0/5] memcg: dma-buf per-cgroup accounting via pid_fd Albert Esteve
2026-05-12 9:10 ` Albert Esteve [this message]
2026-05-16 3:56 ` Claude review: memcg: Track exported dma-buffers Claude Code Review Bot
2026-05-12 9:10 ` [PATCH RFC 2/5] dma-heap: charge dma-buf memory via explicit memcg Albert Esteve
2026-05-12 10:14 ` Christian König
2026-05-12 18:53 ` T.J. Mercier
2026-05-13 11:39 ` Albert Esteve
2026-05-13 16:35 ` T.J. Mercier
2026-05-13 12:41 ` Albert Esteve
2026-05-13 16:39 ` T.J. Mercier
2026-05-13 18:39 ` Albert Esteve
2026-05-15 13:53 ` Christian Brauner
2026-05-15 17:06 ` T.J. Mercier
2026-05-16 3:56 ` Claude review: " Claude Code Review Bot
2026-05-12 9:10 ` [PATCH RFC 3/5] security: dma-heap: Add dma_heap_alloc LSM hook Albert Esteve
2026-05-16 3:56 ` Claude review: " Claude Code Review Bot
2026-05-12 9:10 ` [PATCH RFC 4/5] selinux: Restrict cross-cgroup dma-heap charging Albert Esteve
2026-05-14 20:44 ` Paul Moore
2026-05-16 3:56 ` Claude review: " Claude Code Review Bot
2026-05-12 9:10 ` [PATCH RFC 5/5] selftests/dmabuf-heaps: Add dma-buf memcg accounting tests Albert Esteve
2026-05-16 3:56 ` Claude review: " Claude Code Review Bot
2026-05-16 3:56 ` Claude review: memcg: dma-buf per-cgroup accounting via pid_fd Claude Code Review Bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260512-v2_20230123_tjmercier_google_com-v1-1-6326701c3691@redhat.com \
--to=aesteve@redhat.com \
--cc=Brian.Starkey@arm.com \
--cc=akpm@linux-foundation.org \
--cc=benjamin.gaignard@collabora.com \
--cc=brauner@kernel.org \
--cc=cgroups@vger.kernel.org \
--cc=christian.koenig@amd.com \
--cc=corbet@lwn.net \
--cc=dri-devel@lists.freedesktop.org \
--cc=echanude@redhat.com \
--cc=hannes@cmpxchg.org \
--cc=jmorris@namei.org \
--cc=jstultz@google.com \
--cc=linaro-mm-sig@lists.linaro.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-security-module@vger.kernel.org \
--cc=mhocko@kernel.org \
--cc=mkoutny@suse.com \
--cc=mripard@kernel.org \
--cc=muchun.song@linux.dev \
--cc=omosnace@redhat.com \
--cc=paul@paul-moore.com \
--cc=roman.gushchin@linux.dev \
--cc=selinux@vger.kernel.org \
--cc=serge@hallyn.com \
--cc=shakeel.butt@linux.dev \
--cc=shuah@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=stephen.smalley.work@gmail.com \
--cc=sumit.semwal@linaro.org \
--cc=tj@kernel.org \
--cc=tjmercier@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox