* [PATCH 1/7] scsi: sg and st: account the command buffer memory to memcg
2026-11-06 13:18 [PATCH 0/7] memcg: account the remaining uncharged kernel allocations Tao Cui
@ 2026-11-06 13:18 ` Tao Cui
2026-11-06 13:18 ` [PATCH 2/7] dma-buf/udmabuf: account the page array metadata " Tao Cui
` (5 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Tao Cui @ 2026-11-06 13:18 UTC (permalink / raw)
To: akpm, dgilbert, mkp, loberman, jmeneghi, kraxel, vivek.kasireddy,
mst, jasowangio, eperezma, stefanha, sgarzare, rostedt,
vdonnefort, mhiramat, jgg, kevin.tian, axboe
Cc: linux-mm, James.Bottomley, sumit.semwal, christian.koenig,
phasta, joro, will, linux-scsi, dri-devel, linux-media,
linaro-mm-sig, kvm, virtualization, linux-trace-kernel, iommu,
io-uring, linux-kernel, bobbyeshleman, mathieu.desnoyers,
robin.murphy, mason, aesteve, tjmercier, harry, vbabka, cui.tao,
cuitao
From: Tao Cui <cuitao@kylinos.cn>
The sg data buffers are unaccounted: the per-fd reserved buffer, the
scatter-gather page array, and the order-0..N pages behind it, plus the
per-device tape buffer in st. All of it is user controllable. The
reserved size is capped only by the device max_sectors, SG_MAX_QUEUE
(16) commands can be in flight per fd, and the fd count is bounded by
RLIMIT_NOFILE. Nothing is charged to the memcg of the process driving
it. The September 2026 fixes in sg_build_indirect reshaped the element
sizing and left the accounting alone.
Mark these allocations __GFP_ACCOUNT so the driving process pays for
the memory it induces.
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
drivers/scsi/sg.c | 23 ++++++++++++++++++++---
drivers/scsi/st.c | 7 ++++---
2 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 6551f1284d44..9c1414479b21 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -911,6 +911,19 @@ static int put_compat_request_table(struct compat_sg_req_info __user *o,
}
#endif
+/*
+ * The sg passthrough of the blktrace ioctls has the same exposure as the
+ * block device path: holding the node is the only requirement today, and
+ * a created trace outlives the fd. Must match the gate in
+ * blk_trace_ioctl().
+ */
+static bool sg_is_blktrace_cmd(unsigned int cmd)
+{
+ return cmd == BLKTRACESETUP || cmd == BLKTRACESETUP2 ||
+ cmd == BLKTRACESTART || cmd == BLKTRACESTOP ||
+ cmd == BLKTRACETEARDOWN;
+}
+
static long
sg_ioctl_common(struct file *filp, Sg_device *sdp, Sg_fd *sfp,
unsigned int cmd_in, void __user *p)
@@ -920,6 +933,9 @@ sg_ioctl_common(struct file *filp, Sg_device *sdp, Sg_fd *sfp,
Sg_request *srp;
unsigned long iflags;
+ if (sg_is_blktrace_cmd(cmd_in) && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
"sg_ioctl: cmd=0x%x\n", (int) cmd_in));
read_only = (O_RDWR != (filp->f_flags & O_ACCMODE));
@@ -1829,7 +1845,7 @@ static int
sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize)
{
int sg_bufflen = tablesize * sizeof(struct page *);
- gfp_t gfp_flags = GFP_ATOMIC | __GFP_NOWARN;
+ gfp_t gfp_flags = GFP_ATOMIC | __GFP_NOWARN | __GFP_ACCOUNT;
schp->pages = kzalloc(sg_bufflen, gfp_flags);
if (!schp->pages)
@@ -1844,7 +1860,8 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size)
int ret_sz = 0, i, k, rem_sz, num, mx_sc_elems;
int sg_tablesize = sfp->parentdp->sg_tablesize;
int blk_size = buff_size, order;
- gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN | __GFP_ZERO;
+ gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN |
+ __GFP_ACCOUNT | __GFP_ZERO;
if (blk_size < 0)
return -EFAULT;
@@ -2134,7 +2151,7 @@ sg_add_sfp(Sg_device * sdp)
unsigned long iflags;
int bufflen;
- sfp = kzalloc_obj(*sfp, GFP_ATOMIC | __GFP_NOWARN);
+ sfp = kzalloc_obj(*sfp, GFP_ATOMIC | __GFP_NOWARN | __GFP_ACCOUNT);
if (!sfp)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index e9d41e3cc2dd..b92633ab2757 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -4074,7 +4074,7 @@ static struct st_buffer *new_tape_buffer(int max_sg)
{
struct st_buffer *tb;
- tb = kzalloc_obj(struct st_buffer);
+ tb = kzalloc_obj(struct st_buffer, GFP_KERNEL_ACCOUNT);
if (!tb) {
printk(KERN_NOTICE "st: Can't allocate new tape buffer.\n");
return NULL;
@@ -4083,7 +4083,8 @@ static struct st_buffer *new_tape_buffer(int max_sg)
tb->use_sg = max_sg;
tb->buffer_size = 0;
- tb->reserved_pages = kzalloc_objs(struct page *, max_sg);
+ tb->reserved_pages = kzalloc_objs(struct page *, max_sg,
+ GFP_KERNEL_ACCOUNT);
if (!tb->reserved_pages) {
kfree(tb);
return NULL;
@@ -4109,7 +4110,7 @@ static int enlarge_buffer(struct st_buffer * STbuffer, int new_size)
max_segs = STbuffer->use_sg;
- priority = GFP_KERNEL | __GFP_NOWARN;
+ priority = GFP_KERNEL | __GFP_NOWARN | __GFP_ACCOUNT;
if (STbuffer->cleared)
priority |= __GFP_ZERO;
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH 2/7] dma-buf/udmabuf: account the page array metadata to memcg
2026-11-06 13:18 [PATCH 0/7] memcg: account the remaining uncharged kernel allocations Tao Cui
2026-11-06 13:18 ` [PATCH 1/7] scsi: sg and st: account the command buffer memory to memcg Tao Cui
@ 2026-11-06 13:18 ` Tao Cui
2026-11-06 13:23 ` Christian König
2026-11-06 13:18 ` [PATCH 3/7] vhost: account the per-device kernel allocations " Tao Cui
` (4 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Tao Cui @ 2026-11-06 13:18 UTC (permalink / raw)
To: akpm, dgilbert, mkp, loberman, jmeneghi, kraxel, vivek.kasireddy,
mst, jasowangio, eperezma, stefanha, sgarzare, rostedt,
vdonnefort, mhiramat, jgg, kevin.tian, axboe
Cc: linux-mm, James.Bottomley, sumit.semwal, christian.koenig,
phasta, joro, will, linux-scsi, dri-devel, linux-media,
linaro-mm-sig, kvm, virtualization, linux-trace-kernel, iommu,
io-uring, linux-kernel, bobbyeshleman, mathieu.desnoyers,
robin.murphy, mason, aesteve, tjmercier, harry, vbabka, cui.tao,
cuitao
From: Tao Cui <cuitao@kylinos.cn>
udmabuf pins the pages of a memfd and hands them out as a dma-buf. The
data pages keep the accounting of the memfd they were instantiated in,
but the bookkeeping around them does not: the page pointer array and
the pinned_folios array, both sized by the buffer page count, plus the
transient folios array sized by the memfd folio count, are plain
GFP_KERNEL. A 1GB buffer costs about 4MB of uncharged SUnreclaim
memory, and size_limit_mb defaults to INT_MAX.
Charge the three kvmalloc allocations so the creator pays for the
metadata it induces. The system heap in the same subsystem already
accounts its buffer allocation; the data pages here stay with the
memfd.
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
drivers/dma-buf/udmabuf.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
index df6dd0046242..40182c82df6b 100644
--- a/drivers/dma-buf/udmabuf.c
+++ b/drivers/dma-buf/udmabuf.c
@@ -190,11 +190,12 @@ static void unpin_all_folios(struct udmabuf *ubuf)
static __always_inline int init_udmabuf(struct udmabuf *ubuf, pgoff_t pgcnt)
{
- ubuf->pages = kvmalloc_objs(*ubuf->pages, pgcnt);
+ ubuf->pages = kvmalloc_objs(*ubuf->pages, pgcnt, GFP_KERNEL_ACCOUNT);
if (!ubuf->pages)
return -ENOMEM;
- ubuf->pinned_folios = kvmalloc_objs(*ubuf->pinned_folios, pgcnt);
+ ubuf->pinned_folios = kvmalloc_objs(*ubuf->pinned_folios, pgcnt,
+ GFP_KERNEL_ACCOUNT);
if (!ubuf->pinned_folios)
return -ENOMEM;
@@ -387,7 +388,7 @@ static long udmabuf_create(struct miscdevice *device,
if (ret)
goto err;
- folios = kvmalloc_objs(*folios, max_nr_folios);
+ folios = kvmalloc_objs(*folios, max_nr_folios, GFP_KERNEL_ACCOUNT);
if (!folios) {
ret = -ENOMEM;
goto err;
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH 2/7] dma-buf/udmabuf: account the page array metadata to memcg
2026-11-06 13:18 ` [PATCH 2/7] dma-buf/udmabuf: account the page array metadata " Tao Cui
@ 2026-11-06 13:23 ` Christian König
2026-11-06 17:25 ` T.J. Mercier
0 siblings, 1 reply; 10+ messages in thread
From: Christian König @ 2026-11-06 13:23 UTC (permalink / raw)
To: Tao Cui, akpm, dgilbert, mkp, loberman, jmeneghi, kraxel,
vivek.kasireddy, mst, jasowangio, eperezma, stefanha, sgarzare,
rostedt, vdonnefort, mhiramat, jgg, kevin.tian, axboe,
T.J. Mercier
Cc: linux-mm, James.Bottomley, sumit.semwal, phasta, joro, will,
linux-scsi, dri-devel, linux-media, linaro-mm-sig, kvm,
virtualization, linux-trace-kernel, iommu, io-uring,
linux-kernel, bobbyeshleman, mathieu.desnoyers, robin.murphy,
mason, aesteve, tjmercier, harry, vbabka, cuitao
On 11/6/26 14:18, Tao Cui wrote:
> From: Tao Cui <cuitao@kylinos.cn>
>
> udmabuf pins the pages of a memfd and hands them out as a dma-buf. The
> data pages keep the accounting of the memfd they were instantiated in,
> but the bookkeeping around them does not: the page pointer array and
> the pinned_folios array, both sized by the buffer page count, plus the
> transient folios array sized by the memfd folio count, are plain
> GFP_KERNEL. A 1GB buffer costs about 4MB of uncharged SUnreclaim
> memory, and size_limit_mb defaults to INT_MAX.
>
> Charge the three kvmalloc allocations so the creator pays for the
> metadata it induces. The system heap in the same subsystem already
> accounts its buffer allocation; the data pages here stay with the
> memfd.
T.J. needs to take a look here, but I fear that this might break uAPI.
A lot of the DMA-buf resources are intentionally not charged against memcg.
Regards,
Christian.
>
> Signed-off-by: Tao Cui <cuitao@kylinos.cn>
> ---
> drivers/dma-buf/udmabuf.c | 7 ++++---
> 1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
> index df6dd0046242..40182c82df6b 100644
> --- a/drivers/dma-buf/udmabuf.c
> +++ b/drivers/dma-buf/udmabuf.c
> @@ -190,11 +190,12 @@ static void unpin_all_folios(struct udmabuf *ubuf)
>
> static __always_inline int init_udmabuf(struct udmabuf *ubuf, pgoff_t pgcnt)
> {
> - ubuf->pages = kvmalloc_objs(*ubuf->pages, pgcnt);
> + ubuf->pages = kvmalloc_objs(*ubuf->pages, pgcnt, GFP_KERNEL_ACCOUNT);
> if (!ubuf->pages)
> return -ENOMEM;
>
> - ubuf->pinned_folios = kvmalloc_objs(*ubuf->pinned_folios, pgcnt);
> + ubuf->pinned_folios = kvmalloc_objs(*ubuf->pinned_folios, pgcnt,
> + GFP_KERNEL_ACCOUNT);
> if (!ubuf->pinned_folios)
> return -ENOMEM;
>
> @@ -387,7 +388,7 @@ static long udmabuf_create(struct miscdevice *device,
> if (ret)
> goto err;
>
> - folios = kvmalloc_objs(*folios, max_nr_folios);
> + folios = kvmalloc_objs(*folios, max_nr_folios, GFP_KERNEL_ACCOUNT);
> if (!folios) {
> ret = -ENOMEM;
> goto err;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH 2/7] dma-buf/udmabuf: account the page array metadata to memcg
2026-11-06 13:23 ` Christian König
@ 2026-11-06 17:25 ` T.J. Mercier
0 siblings, 0 replies; 10+ messages in thread
From: T.J. Mercier @ 2026-11-06 17:25 UTC (permalink / raw)
To: Christian König
Cc: Tao Cui, akpm, dgilbert, mkp, loberman, jmeneghi, kraxel,
vivek.kasireddy, mst, jasowangio, eperezma, stefanha, sgarzare,
rostedt, vdonnefort, mhiramat, jgg, kevin.tian, axboe, linux-mm,
James.Bottomley, sumit.semwal, phasta, joro, will, linux-scsi,
dri-devel, linux-media, linaro-mm-sig, kvm, virtualization,
linux-trace-kernel, iommu, io-uring, linux-kernel, bobbyeshleman,
mathieu.desnoyers, robin.murphy, mason, aesteve, harry, vbabka,
cuitao
On Fri, Nov 6, 2026 at 5:23 AM Christian König <christian.koenig@amd.com> wrote:
>
> On 11/6/26 14:18, Tao Cui wrote:
> > From: Tao Cui <cuitao@kylinos.cn>
> >
> > udmabuf pins the pages of a memfd and hands them out as a dma-buf. The
> > data pages keep the accounting of the memfd they were instantiated in,
> > but the bookkeeping around them does not: the page pointer array and
> > the pinned_folios array, both sized by the buffer page count, plus the
> > transient folios array sized by the memfd folio count, are plain
> > GFP_KERNEL. A 1GB buffer costs about 4MB of uncharged SUnreclaim
> > memory, and size_limit_mb defaults to INT_MAX.
> >
> > Charge the three kvmalloc allocations so the creator pays for the
> > metadata it induces. The system heap in the same subsystem already
> > accounts its buffer allocation;
Only if the mem_accounting param is set to true. We (Android)
currently depend on it being disabled because system heap buffers
don't have a single owner for their entire lifetime (ownership often
changes immediately after they are allocated), and the kernel doesn't
have the ability to switch who is charged for the buffer memory quite
yet. Albert is working hard on it at the moment.
Could you update the message to note the mem_accounting param pls?
> > the data pages here stay with the
> > memfd.
memfd / udmabuf is a little special because it uses shmem for the
backing store, which is already tracked by memcg under all conditions
(except hugetlb). We don't currently use udmabuf, so the lack of memcg
charge transfer for udmabuf is not an issue. But once we get charge
transfer working for the system heap it'd be good to follow up with
udmabuf.
> T.J. needs to take a look here, but I fear that this might break uAPI.
Thanks for looping me in Christian. The buffer allocations cost
gigabytes, but these metadata arrays are only 4MB per 1GB of buffer,
so I don't expect charging them to break anyone. Leaving them
uncharged is an accounting gap though, since someone could spam many
udmabufs from the same memfd and only pay for the memfd pages once. So
this change is ok with me.
> A lot of the DMA-buf resources are intentionally not charged against memcg.
>
> Regards,
> Christian.
>
> >
> > Signed-off-by: Tao Cui <cuitao@kylinos.cn>
With the commit message updated to mention the mem_accounting param:
Reviewed-by: T.J. Mercier <tjmercier@google.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 3/7] vhost: account the per-device kernel allocations to memcg
2026-11-06 13:18 [PATCH 0/7] memcg: account the remaining uncharged kernel allocations Tao Cui
2026-11-06 13:18 ` [PATCH 1/7] scsi: sg and st: account the command buffer memory to memcg Tao Cui
2026-11-06 13:18 ` [PATCH 2/7] dma-buf/udmabuf: account the page array metadata " Tao Cui
@ 2026-11-06 13:18 ` Tao Cui
2026-11-06 13:18 ` [PATCH 4/7] tracing: account ring buffer pages and trace_array metadata " Tao Cui
` (3 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Tao Cui @ 2026-11-06 13:18 UTC (permalink / raw)
To: akpm, dgilbert, mkp, loberman, jmeneghi, kraxel, vivek.kasireddy,
mst, jasowangio, eperezma, stefanha, sgarzare, rostedt,
vdonnefort, mhiramat, jgg, kevin.tian, axboe
Cc: linux-mm, James.Bottomley, sumit.semwal, christian.koenig,
phasta, joro, will, linux-scsi, dri-devel, linux-media,
linaro-mm-sig, kvm, virtualization, linux-trace-kernel, iommu,
io-uring, linux-kernel, bobbyeshleman, mathieu.desnoyers,
robin.murphy, mason, aesteve, tjmercier, harry, vbabka, cui.tao,
cuitao
From: Tao Cui <cuitao@kylinos.cn>
Everything except the worker struct is allocated with plain GFP_KERNEL:
the per-vq iovec and log arrays at VHOST_SET_OWNER, the memory table at
VHOST_SET_MEM_TABLE whose size follows the user supplied region count,
the pending message nodes, and the vhost_net/vhost_vsock device structs
with their fallback to vmalloc. The worker has been accounted since the
vhost_task conversion.
A container that can open /dev/vhost-net or /dev/vhost-vsock, which
device plugins hand out with passthrough, holds roughly 150KB of
resident kernel memory per device after SET_OWNER. Charge the rest the
same way the worker already is.
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
drivers/vhost/net.c | 14 ++++++++------
drivers/vhost/vhost.c | 16 ++++++++++------
drivers/vhost/vsock.c | 5 +++--
3 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 4570a4ffbab2..bab6597c696d 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -249,7 +249,7 @@ vhost_net_ubuf_alloc(struct vhost_virtqueue *vq, bool zcopy)
/* No zero copy backend? Nothing to count. */
if (!zcopy)
return NULL;
- ubufs = kmalloc_obj(*ubufs);
+ ubufs = kmalloc_obj(*ubufs, GFP_KERNEL_ACCOUNT);
if (!ubufs)
return ERR_PTR(-ENOMEM);
atomic_set(&ubufs->refcount, 1);
@@ -302,7 +302,8 @@ static int vhost_net_set_ubuf_info(struct vhost_net *n)
if (!zcopy)
continue;
n->vqs[i].ubuf_info =
- kmalloc_objs(*n->vqs[i].ubuf_info, UIO_MAXIOV);
+ kmalloc_objs(*n->vqs[i].ubuf_info, UIO_MAXIOV,
+ GFP_KERNEL_ACCOUNT);
if (!n->vqs[i].ubuf_info)
goto err;
}
@@ -1344,17 +1345,18 @@ static int vhost_net_open(struct inode *inode, struct file *f)
struct xdp_buff *xdp;
int i;
- n = kvmalloc_obj(*n, GFP_KERNEL | __GFP_RETRY_MAYFAIL);
+ n = kvmalloc_obj(*n, GFP_KERNEL | __GFP_RETRY_MAYFAIL |
+ __GFP_ACCOUNT);
if (!n)
return -ENOMEM;
- vqs = kmalloc_objs(*vqs, VHOST_NET_VQ_MAX);
+ vqs = kmalloc_objs(*vqs, VHOST_NET_VQ_MAX, GFP_KERNEL_ACCOUNT);
if (!vqs) {
kvfree(n);
return -ENOMEM;
}
queue = kmalloc_array(VHOST_NET_BATCH, sizeof(void *),
- GFP_KERNEL);
+ GFP_KERNEL_ACCOUNT);
if (!queue) {
kfree(vqs);
kvfree(n);
@@ -1362,7 +1364,7 @@ static int vhost_net_open(struct inode *inode, struct file *f)
}
n->vqs[VHOST_NET_VQ_RX].rxq.queue = queue;
- xdp = kmalloc_objs(*xdp, VHOST_NET_BATCH);
+ xdp = kmalloc_objs(*xdp, VHOST_NET_BATCH, GFP_KERNEL_ACCOUNT);
if (!xdp) {
kfree(vqs);
kvfree(n);
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index ea127af58ada..d1274106ad5d 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -526,11 +526,14 @@ static long vhost_dev_alloc_iovecs(struct vhost_dev *dev)
for (i = 0; i < dev->nvqs; ++i) {
vq = dev->vqs[i];
- vq->indirect = kmalloc_objs(*vq->indirect, UIO_MAXIOV);
- vq->log = kmalloc_objs(*vq->log, dev->iov_limit);
- vq->heads = kmalloc_objs(*vq->heads, dev->iov_limit);
+ vq->indirect = kmalloc_objs(*vq->indirect, UIO_MAXIOV,
+ GFP_KERNEL_ACCOUNT);
+ vq->log = kmalloc_objs(*vq->log, dev->iov_limit,
+ GFP_KERNEL_ACCOUNT);
+ vq->heads = kmalloc_objs(*vq->heads, dev->iov_limit,
+ GFP_KERNEL_ACCOUNT);
vq->nheads = kmalloc_array(dev->iov_limit, sizeof(*vq->nheads),
- GFP_KERNEL);
+ GFP_KERNEL_ACCOUNT);
if (!vq->indirect || !vq->log || !vq->heads || !vq->nheads)
goto err_nomem;
}
@@ -2053,7 +2056,8 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
return -E2BIG;
if (max_iotlb_entries <= 0)
return -EINVAL;
- newmem = kvzalloc_flex(*newmem, regions, mem.nregions);
+ newmem = kvzalloc_flex(*newmem, regions, mem.nregions,
+ GFP_KERNEL_ACCOUNT);
if (!newmem)
return -ENOMEM;
@@ -3405,7 +3409,7 @@ EXPORT_SYMBOL_GPL(vhost_disable_notify);
struct vhost_msg_node *vhost_new_msg(struct vhost_virtqueue *vq, int type)
{
/* Make sure all padding within the structure is initialized. */
- struct vhost_msg_node *node = kzalloc_obj(*node);
+ struct vhost_msg_node *node = kzalloc_obj(*node, GFP_KERNEL_ACCOUNT);
if (!node)
return NULL;
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index 0cb87971d749..600daeb05e7b 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -721,11 +721,12 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
/* This struct is large and allocation could fail, fall back to vmalloc
* if there is no other way.
*/
- vsock = kvmalloc_obj(*vsock, GFP_KERNEL | __GFP_RETRY_MAYFAIL);
+ vsock = kvmalloc_obj(*vsock, GFP_KERNEL | __GFP_RETRY_MAYFAIL |
+ __GFP_ACCOUNT);
if (!vsock)
return -ENOMEM;
- vqs = kmalloc_objs(*vqs, ARRAY_SIZE(vsock->vqs));
+ vqs = kmalloc_objs(*vqs, ARRAY_SIZE(vsock->vqs), GFP_KERNEL_ACCOUNT);
if (!vqs) {
ret = -ENOMEM;
goto out;
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH 4/7] tracing: account ring buffer pages and trace_array metadata to memcg
2026-11-06 13:18 [PATCH 0/7] memcg: account the remaining uncharged kernel allocations Tao Cui
` (2 preceding siblings ...)
2026-11-06 13:18 ` [PATCH 3/7] vhost: account the per-device kernel allocations " Tao Cui
@ 2026-11-06 13:18 ` Tao Cui
2026-11-06 13:18 ` [PATCH 5/7] iommufd: account the hw_queue page array " Tao Cui
` (2 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Tao Cui @ 2026-11-06 13:18 UTC (permalink / raw)
To: akpm, dgilbert, mkp, loberman, jmeneghi, kraxel, vivek.kasireddy,
mst, jasowangio, eperezma, stefanha, sgarzare, rostedt,
vdonnefort, mhiramat, jgg, kevin.tian, axboe
Cc: linux-mm, James.Bottomley, sumit.semwal, christian.koenig,
phasta, joro, will, linux-scsi, dri-devel, linux-media,
linaro-mm-sig, kvm, virtualization, linux-trace-kernel, iommu,
io-uring, linux-kernel, bobbyeshleman, mathieu.desnoyers,
robin.murphy, mason, aesteve, tjmercier, harry, vbabka, cui.tao,
cuitao
From: Tao Cui <cuitao@kylinos.cn>
Creating an instance immediately allocates two full sets of per-cpu
ring buffers, array buffer plus snapshot, about 1.375MB per cpu per set
on the default config, plus the trace_array and its name. None of it
is charged: neither trace.c nor ring_buffer.c passes __GFP_ACCOUNT,
while user_events in the same directory accounts everything it
allocates.
Instance creation is currently gated by tracefs file permissions
alone, so a
rootful container with the host tracefs bound in can create instances
until the host runs out of memory. Charge the ring buffer pages and
the trace_array metadata to the creating cgroup.
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
kernel/trace/ring_buffer.c | 8 +++++++-
kernel/trace/trace.c | 6 +++---
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 1fd28a72e6ee..676c059c112c 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -441,7 +441,13 @@ static struct buffer_data_page *alloc_cpu_data(int cpu, int order)
* gracefully without invoking oom-killer and the system is not
* destabilized.
*/
- mflags = GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_COMP | __GFP_ZERO;
+ /*
+ * The subbuffers are charged to the memcg of whoever caused the
+ * allocation: the creator of a trace instance, or root at boot for
+ * the global trace.
+ */
+ mflags = GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_COMP | __GFP_ZERO |
+ __GFP_ACCOUNT;
page = alloc_pages_node(cpu_to_node(cpu), mflags, order);
if (!page)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index e8c4efa8107b..04b60b9cc4f5 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -8693,11 +8693,11 @@ trace_array_create_systems(const char *name, const char *systems,
int ret;
ret = -ENOMEM;
- tr = kzalloc_obj(*tr);
+ tr = kzalloc_obj(*tr, GFP_KERNEL_ACCOUNT);
if (!tr)
return ERR_PTR(ret);
- tr->name = kstrdup(name, GFP_KERNEL);
+ tr->name = kstrdup(name, GFP_KERNEL_ACCOUNT);
if (!tr->name)
goto out_free_tr;
@@ -8708,7 +8708,7 @@ trace_array_create_systems(const char *name, const char *systems,
goto out_free_tr;
if (systems) {
- tr->system_names = kstrdup_const(systems, GFP_KERNEL);
+ tr->system_names = kstrdup_const(systems, GFP_KERNEL_ACCOUNT);
if (!tr->system_names)
goto out_free_tr;
}
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH 5/7] iommufd: account the hw_queue page array to memcg
2026-11-06 13:18 [PATCH 0/7] memcg: account the remaining uncharged kernel allocations Tao Cui
` (3 preceding siblings ...)
2026-11-06 13:18 ` [PATCH 4/7] tracing: account ring buffer pages and trace_array metadata " Tao Cui
@ 2026-11-06 13:18 ` Tao Cui
2026-11-06 13:18 ` [PATCH 6/7] io_uring: account the ring context and SQPOLL thread data " Tao Cui
2026-11-06 13:18 ` [PATCH 7/7] io_uring: account the remaining per-object allocations " Tao Cui
6 siblings, 0 replies; 10+ messages in thread
From: Tao Cui @ 2026-11-06 13:18 UTC (permalink / raw)
To: akpm, dgilbert, mkp, loberman, jmeneghi, kraxel, vivek.kasireddy,
mst, jasowangio, eperezma, stefanha, sgarzare, rostedt,
vdonnefort, mhiramat, jgg, kevin.tian, axboe
Cc: linux-mm, James.Bottomley, sumit.semwal, christian.koenig,
phasta, joro, will, linux-scsi, dri-devel, linux-media,
linaro-mm-sig, kvm, virtualization, linux-trace-kernel, iommu,
io-uring, linux-kernel, bobbyeshleman, mathieu.desnoyers,
robin.murphy, mason, aesteve, tjmercier, harry, vbabka, cui.tao,
cuitao
From: Tao Cui <cuitao@kylinos.cn>
IOMMUFD_CMD_HW_QUEUE_ALLOC pins up to max_npages pages just to check
that a user supplied queue region is physically contiguous, and the
page array for that check is plain GFP_KERNEL. The size comes from
cmd->length, a u64 that is only overflow checked, and the array is
freed again as soon as the contiguity check fails, so the allocation
can be repeated concurrently: a 1GB region costs about 2MB of
uncharged memory per attempt.
Every other iommufd allocation that scales with user input is already
charged, and the pinned pages charge the mapper at map time. Charge
this array like the rest.
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
drivers/iommu/iommufd/viommu.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c
index f7951057a1e5..ab17e190da49 100644
--- a/drivers/iommu/iommufd/viommu.c
+++ b/drivers/iommu/iommufd/viommu.c
@@ -330,7 +330,8 @@ iommufd_hw_queue_alloc_phys(struct iommu_hw_queue_alloc *cmd,
* Use kvcalloc() to avoid memory fragmentation for a large page array.
* Set __GFP_NOWARN to avoid syzkaller blowups
*/
- pages = kvzalloc_objs(*pages, max_npages, GFP_KERNEL | __GFP_NOWARN);
+ pages = kvzalloc_objs(*pages, max_npages,
+ GFP_KERNEL | __GFP_NOWARN | __GFP_ACCOUNT);
if (!pages)
return ERR_PTR(-ENOMEM);
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH 6/7] io_uring: account the ring context and SQPOLL thread data to memcg
2026-11-06 13:18 [PATCH 0/7] memcg: account the remaining uncharged kernel allocations Tao Cui
` (4 preceding siblings ...)
2026-11-06 13:18 ` [PATCH 5/7] iommufd: account the hw_queue page array " Tao Cui
@ 2026-11-06 13:18 ` Tao Cui
2026-11-06 13:18 ` [PATCH 7/7] io_uring: account the remaining per-object allocations " Tao Cui
6 siblings, 0 replies; 10+ messages in thread
From: Tao Cui @ 2026-11-06 13:18 UTC (permalink / raw)
To: akpm, dgilbert, mkp, loberman, jmeneghi, kraxel, vivek.kasireddy,
mst, jasowangio, eperezma, stefanha, sgarzare, rostedt,
vdonnefort, mhiramat, jgg, kevin.tian, axboe
Cc: linux-mm, James.Bottomley, sumit.semwal, christian.koenig,
phasta, joro, will, linux-scsi, dri-devel, linux-media,
linaro-mm-sig, kvm, virtualization, linux-trace-kernel, iommu,
io-uring, linux-kernel, bobbyeshleman, mathieu.desnoyers,
robin.murphy, mason, aesteve, tjmercier, harry, vbabka, cui.tao,
cuitao
From: Tao Cui <cuitao@kylinos.cn>
The io_ring_ctx and the SQPOLL io_sqd are the last per-ring allocations
made with plain GFP_KERNEL. The larger surfaces around them are
already charged: io_mapped_ubuf and io_rsrc_node since commit
075d5837bafc ("io_uring/rsrc: charge buffer arrays and nodes to memcg"),
the tctx node and its XArrays since commit 0b34e7b20a48 ("io_uring:
charge tctx nodes and personalities").
Charge the context and the SQPOLL thread data the same way.
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
io_uring/io_uring.c | 2 +-
io_uring/rsrc.c | 3 ++-
io_uring/sqpoll.c | 2 +-
io_uring/tctx.c | 4 ++--
4 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 03724419c4e1..d39cdde49a9f 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -229,7 +229,7 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
int hash_bits;
bool ret;
- ctx = kzalloc_obj(*ctx);
+ ctx = kzalloc_obj(*ctx, GFP_KERNEL_ACCOUNT);
if (!ctx)
return NULL;
diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c
index e0345c3e764e..9a971c983686 100644
--- a/io_uring/rsrc.c
+++ b/io_uring/rsrc.c
@@ -825,7 +825,8 @@ static bool io_coalesce_buffer(struct page ***pages, int *nr_pages,
unsigned i, j;
/* Store head pages only*/
- new_array = kvmalloc_objs(struct page *, nr_folios);
+ new_array = kvmalloc_objs(struct page *, nr_folios,
+ GFP_KERNEL_ACCOUNT);
if (!new_array)
return false;
diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c
index d387488db2de..b82d3a285337 100644
--- a/io_uring/sqpoll.c
+++ b/io_uring/sqpoll.c
@@ -172,7 +172,7 @@ static struct io_sq_data *io_get_sq_data(struct io_uring_params *p,
return sqd;
}
- sqd = kzalloc_obj(*sqd);
+ sqd = kzalloc_obj(*sqd, GFP_KERNEL_ACCOUNT);
if (!sqd)
return ERR_PTR(-ENOMEM);
diff --git a/io_uring/tctx.c b/io_uring/tctx.c
index b4fecea3ea47..6673393100ec 100644
--- a/io_uring/tctx.c
+++ b/io_uring/tctx.c
@@ -23,7 +23,7 @@ static struct io_wq *io_init_wq_offload(struct io_ring_ctx *ctx,
mutex_lock(&ctx->uring_lock);
hash = ctx->hash_map;
if (!hash) {
- hash = kzalloc_obj(*hash);
+ hash = kzalloc_obj(*hash, GFP_KERNEL_ACCOUNT);
if (!hash) {
mutex_unlock(&ctx->uring_lock);
return ERR_PTR(-ENOMEM);
@@ -85,7 +85,7 @@ __cold struct io_uring_task *io_uring_alloc_task_context(struct task_struct *tas
struct io_uring_task *tctx;
int ret;
- tctx = kzalloc_obj(*tctx);
+ tctx = kzalloc_obj(*tctx, GFP_KERNEL_ACCOUNT);
if (unlikely(!tctx))
return ERR_PTR(-ENOMEM);
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH 7/7] io_uring: account the remaining per-object allocations to memcg
2026-11-06 13:18 [PATCH 0/7] memcg: account the remaining uncharged kernel allocations Tao Cui
` (5 preceding siblings ...)
2026-11-06 13:18 ` [PATCH 6/7] io_uring: account the ring context and SQPOLL thread data " Tao Cui
@ 2026-11-06 13:18 ` Tao Cui
6 siblings, 0 replies; 10+ messages in thread
From: Tao Cui @ 2026-11-06 13:18 UTC (permalink / raw)
To: akpm, dgilbert, mkp, loberman, jmeneghi, kraxel, vivek.kasireddy,
mst, jasowangio, eperezma, stefanha, sgarzare, rostedt,
vdonnefort, mhiramat, jgg, kevin.tian, axboe
Cc: linux-mm, James.Bottomley, sumit.semwal, christian.koenig,
phasta, joro, will, linux-scsi, dri-devel, linux-media,
linaro-mm-sig, kvm, virtualization, linux-trace-kernel, iommu,
io-uring, linux-kernel, bobbyeshleman, mathieu.desnoyers,
robin.murphy, mason, aesteve, tjmercier, harry, vbabka, cui.tao,
cuitao
From: Tao Cui <cuitao@kylinos.cn>
A handful of io_uring allocations still scale with user controlled
counts on plain GFP_KERNEL: the per-ring io-wq and its workers, the
alloc cache entry arrays, the provided buffer peek and import iovec
arrays, and the registered eventfd contexts. The tctx side is covered
by commit 0b34e7b20a48 ("io_uring: charge tctx nodes and
personalities").
Charge what is left.
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
io_uring/alloc_cache.c | 3 ++-
io_uring/eventfd.c | 2 +-
io_uring/io-wq.c | 4 ++--
io_uring/kbuf.c | 2 +-
4 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/io_uring/alloc_cache.c b/io_uring/alloc_cache.c
index 58423888b736..f41df1878633 100644
--- a/io_uring/alloc_cache.c
+++ b/io_uring/alloc_cache.c
@@ -22,7 +22,8 @@ bool io_alloc_cache_init(struct io_alloc_cache *cache,
unsigned max_nr, unsigned int size,
unsigned int init_bytes)
{
- cache->entries = kvmalloc_array(max_nr, sizeof(void *), GFP_KERNEL);
+ cache->entries = kvmalloc_array(max_nr, sizeof(void *),
+ GFP_KERNEL_ACCOUNT);
if (!cache->entries)
return true;
diff --git a/io_uring/eventfd.c b/io_uring/eventfd.c
index 63fe6e5d79ba..52f5d5b85f30 100644
--- a/io_uring/eventfd.c
+++ b/io_uring/eventfd.c
@@ -132,7 +132,7 @@ int io_eventfd_register(struct io_ring_ctx *ctx, void __user *arg,
if (copy_from_user(&fd, fds, sizeof(*fds)))
return -EFAULT;
- ev_fd = kmalloc_obj(*ev_fd);
+ ev_fd = kmalloc_obj(*ev_fd, GFP_KERNEL_ACCOUNT);
if (!ev_fd)
return -ENOMEM;
diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
index 92df54c6f50c..98f3aa378baf 100644
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -981,7 +981,7 @@ static bool create_io_worker(struct io_wq *wq, struct io_wq_acct *acct)
__set_current_state(TASK_RUNNING);
- worker = kzalloc_obj(*worker);
+ worker = kzalloc_obj(*worker, GFP_KERNEL_ACCOUNT);
if (!worker) {
fail:
atomic_dec(&acct->nr_running);
@@ -1358,7 +1358,7 @@ struct io_wq *io_wq_create(unsigned bounded, struct io_wq_data *data)
if (WARN_ON_ONCE(!bounded))
return ERR_PTR(-EINVAL);
- wq = kzalloc_obj(struct io_wq);
+ wq = kzalloc_obj(struct io_wq, GFP_KERNEL_ACCOUNT);
if (!wq)
return ERR_PTR(-ENOMEM);
diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c
index abf44f5dba26..bc6e552a91dd 100644
--- a/io_uring/kbuf.c
+++ b/io_uring/kbuf.c
@@ -288,7 +288,7 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg,
* a speculative peek operation.
*/
if (arg->mode & KBUF_MODE_EXPAND && nr_avail > nr_iovs && arg->max_len) {
- iov = kmalloc_objs(struct iovec, nr_avail);
+ iov = kmalloc_objs(struct iovec, nr_avail, GFP_KERNEL_ACCOUNT);
if (unlikely(!iov))
return -ENOMEM;
arg->iovs = iov;
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread