mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/2] rust_binder: charge allocated pages to memcg
@ 2026-10-16 13:30 Alice Ryhl
  2026-10-16 13:30 ` [PATCH 1/2] rust: page: add Page::alloc_page_memcg() Alice Ryhl
  2026-10-16 13:30 ` [PATCH 2/2] rust_binder: charge allocated pages to memcg Alice Ryhl
  0 siblings, 2 replies; 5+ messages in thread
From: Alice Ryhl @ 2026-10-16 13:30 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Carlos Llamas, Lorenzo Stoakes,
	Liam R. Howlett, T.J. Mercier
  Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan, linux-mm, rust-for-linux, linux-kernel,
	Alice Ryhl

Pages allocated by the Rust Binder driver and mapped into a process's
VMA are currently not accounted to any memory cgroup. Add
`Page::alloc_page_memcg()` (using `put_page()` on drop so folios are
uncharged when freed), and use it to charge newly allocated binder pages
to the target process's `mm_struct`.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
Alice Ryhl (2):
      rust: page: add Page::alloc_page_memcg()
      rust_binder: charge allocated pages to memcg

 drivers/android/binder/page_range.rs | 13 ++++++++--
 rust/helpers/page.c                  | 17 ++++++++++++
 rust/kernel/page.rs                  | 50 +++++++++++++++++++++++++++++++++++-
 3 files changed, 77 insertions(+), 3 deletions(-)
---
base-commit: 24a19b953e2a3d0ea5a8c4951a0ab8f32720c907
change-id: 20261014-binder-memcg-b42cb9f1098d

Best regards,
-- 
Alice Ryhl <aliceryhl@google.com>


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] rust: page: add Page::alloc_page_memcg()
  2026-10-16 13:30 [PATCH 0/2] rust_binder: charge allocated pages to memcg Alice Ryhl
@ 2026-10-16 13:30 ` Alice Ryhl
  2026-10-16 18:41   ` T.J. Mercier
  2026-10-16 13:30 ` [PATCH 2/2] rust_binder: charge allocated pages to memcg Alice Ryhl
  1 sibling, 1 reply; 5+ messages in thread
From: Alice Ryhl @ 2026-10-16 13:30 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Carlos Llamas, Lorenzo Stoakes,
	Liam R. Howlett, T.J. Mercier
  Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan, linux-mm, rust-for-linux, linux-kernel,
	Alice Ryhl

Pages allocated on behalf of a userspace process and mapped into its
address space should be accounted to the process's memory cgroup. Thus,
add `Page::alloc_page_memcg()` to charge newly allocated pages using
`mem_cgroup_charge()`, and switch `Page::drop()` from `__free_pages()`
to `put_page()` so that charged folios are uncharged when freed.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 rust/helpers/page.c | 17 +++++++++++++++++
 rust/kernel/page.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/rust/helpers/page.c b/rust/helpers/page.c
index f8463fbed2a2..145758a8a954 100644
--- a/rust/helpers/page.c
+++ b/rust/helpers/page.c
@@ -2,6 +2,7 @@
 
 #include <linux/gfp.h>
 #include <linux/highmem.h>
+#include <linux/memcontrol.h>
 #include <linux/mm.h>
 
 __rust_helper struct page *rust_helper_alloc_pages(gfp_t gfp_mask,
@@ -10,6 +11,22 @@ __rust_helper struct page *rust_helper_alloc_pages(gfp_t gfp_mask,
 	return alloc_pages(gfp_mask, order);
 }
 
+__rust_helper void rust_helper_put_page(struct page *page)
+{
+	put_page(page);
+}
+
+__rust_helper struct folio *rust_helper_page_folio(struct page *page)
+{
+	return page_folio(page);
+}
+
+__rust_helper int rust_helper_mem_cgroup_charge(struct folio *folio,
+						struct mm_struct *mm, gfp_t gfp)
+{
+	return mem_cgroup_charge(folio, mm, gfp);
+}
+
 __rust_helper void *rust_helper_kmap_local_page(struct page *page)
 {
 	return kmap_local_page(page);
diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
index 1c0796ea229f..7a36b7dd6c9c 100644
--- a/rust/kernel/page.rs
+++ b/rust/kernel/page.rs
@@ -10,8 +10,10 @@
     bindings,
     error::{
         code::*,
+        Error,
         Result, //
     },
+    mm::Mm,
     uaccess::UserSliceReader, //
 };
 use core::{
@@ -195,6 +197,28 @@ pub fn alloc_page(flags: Flags) -> Result<Self, AllocError> {
         Ok(Self { page })
     }
 
+    /// Allocates a new page and charges it to the memory cgroup of `mm`.
+    #[inline]
+    pub fn alloc_page_memcg(flags: Flags, mm: &Mm) -> Result<Self, MemcgAllocError> {
+        let page = Self::alloc_page(flags)?;
+        // Note: Charging the folio with `mem_cgroup_charge()` only works because `Page::drop` uses
+        // `put_page()` (which uncharges the folio before freeing it) rather than `__free_pages()`.
+        //
+        // SAFETY: `page` was just allocated, so we hold the only reference to it and it is not
+        // yet charged to any memcg. `mm.as_raw()` is a valid pointer to an `mm_struct`.
+        let ret = unsafe {
+            bindings::mem_cgroup_charge(
+                bindings::page_folio(page.as_ptr()),
+                mm.as_raw(),
+                flags.as_raw(),
+            )
+        };
+        if ret != 0 {
+            return Err(MemcgAllocError::Memcg);
+        }
+        Ok(page)
+    }
+
     /// Returns a raw pointer to the page.
     pub fn as_ptr(&self) -> *mut bindings::page {
         self.page.as_ptr()
@@ -376,7 +400,31 @@ pub unsafe fn copy_from_user_slice_raw(
 impl Drop for Page {
     #[inline]
     fn drop(&mut self) {
+        // We need to use `put_page()` instead of `__free_pages()` to properly handle memcg
+        // accounting.
+        //
         // SAFETY: By the type invariants, we have ownership of the page and can free it.
-        unsafe { bindings::__free_pages(self.page.as_ptr(), 0) };
+        unsafe { bindings::put_page(self.page.as_ptr()) };
+    }
+}
+
+/// Error returned by [`Page::alloc_page_memcg`].
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum MemcgAllocError {
+    /// Allocating the page failed.
+    Alloc,
+    /// Charging the page to the memory cgroup failed.
+    Memcg,
+}
+
+impl From<AllocError> for MemcgAllocError {
+    fn from(_: AllocError) -> Self {
+        Self::Alloc
+    }
+}
+
+impl From<MemcgAllocError> for Error {
+    fn from(_: MemcgAllocError) -> Error {
+        ENOMEM
     }
 }

-- 
2.56.0.212.g129ede20d9-goog


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2/2] rust_binder: charge allocated pages to memcg
  2026-10-16 13:30 [PATCH 0/2] rust_binder: charge allocated pages to memcg Alice Ryhl
  2026-10-16 13:30 ` [PATCH 1/2] rust: page: add Page::alloc_page_memcg() Alice Ryhl
@ 2026-10-16 13:30 ` Alice Ryhl
  2026-10-16 18:41   ` T.J. Mercier
  1 sibling, 1 reply; 5+ messages in thread
From: Alice Ryhl @ 2026-10-16 13:30 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Carlos Llamas, Lorenzo Stoakes,
	Liam R. Howlett, T.J. Mercier
  Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
	Benno Lossin, Andreas Hindborg, Trevor Gross, Danilo Krummrich,
	Daniel Almeida, Tamir Duberstein, Alexandre Courbot,
	Onur Özkan, linux-mm, rust-for-linux, linux-kernel,
	Alice Ryhl

Pages allocated for a binder VMA are currently not charged to any memory
cgroup. Thus, use `Page::alloc_page_memcg()` to charge them to the
owning process's `mm_struct`.

Suggested-by: T.J. Mercier <tjmercier@google.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 drivers/android/binder/page_range.rs | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
index 52ffbf3504e7..57da47243967 100644
--- a/drivers/android/binder/page_range.rs
+++ b/drivers/android/binder/page_range.rs
@@ -27,7 +27,7 @@
     ffi::{c_ulong, c_void},
     mm::{virt, Mm, MmWithUser},
     new_mutex, new_spinlock,
-    page::{Page, PAGE_SHIFT, PAGE_SIZE},
+    page::{MemcgAllocError, Page, PAGE_SHIFT, PAGE_SIZE},
     prelude::*,
     str::CStr,
     sync::{aref::ARef, Mutex, SpinLock},
@@ -399,7 +399,16 @@ pub(crate) fn use_range(&self, start: usize, end: usize) -> Result<()> {
     /// Assumes that `i` is in bounds.
     #[cold]
     unsafe fn use_page_slow(&self, i: usize) -> Result<()> {
-        let new_page = Page::alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO)?;
+        let new_page = Page::alloc_page_memcg(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, &self.mm)
+            .inspect_err(|err| {
+                if *err == MemcgAllocError::Memcg {
+                    binder_debug!(
+                        FailedTransaction,
+                        "page allocation for message to {} was blocked by memory cgroup limits",
+                        self.pid,
+                    );
+                }
+            })?;
 
         let mm_mutex = self.mm_lock.lock();
         let inner = self.lock.lock();

-- 
2.56.0.212.g129ede20d9-goog


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] rust: page: add Page::alloc_page_memcg()
  2026-10-16 13:30 ` [PATCH 1/2] rust: page: add Page::alloc_page_memcg() Alice Ryhl
@ 2026-10-16 18:41   ` T.J. Mercier
  0 siblings, 0 replies; 5+ messages in thread
From: T.J. Mercier @ 2026-10-16 18:41 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Greg Kroah-Hartman, Carlos Llamas, Lorenzo Stoakes,
	Liam R. Howlett, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, linux-mm, rust-for-linux,
	linux-kernel, Johannes Weiner, Shakeel Butt

On Fri, Oct 16, 2026 at 6:30 AM Alice Ryhl <aliceryhl@google.com> wrote:
>
> Pages allocated on behalf of a userspace process and mapped into its
> address space should be accounted to the process's memory cgroup. Thus,
> add `Page::alloc_page_memcg()` to charge newly allocated pages using
> `mem_cgroup_charge()`, and switch `Page::drop()` from `__free_pages()`
> to `put_page()` so that charged folios are uncharged when freed.
>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
>  rust/helpers/page.c | 17 +++++++++++++++++
>  rust/kernel/page.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 66 insertions(+), 1 deletion(-)
>
> diff --git a/rust/helpers/page.c b/rust/helpers/page.c
> index f8463fbed2a2..145758a8a954 100644
> --- a/rust/helpers/page.c
> +++ b/rust/helpers/page.c
> @@ -2,6 +2,7 @@
>
>  #include <linux/gfp.h>
>  #include <linux/highmem.h>
> +#include <linux/memcontrol.h>
>  #include <linux/mm.h>
>
>  __rust_helper struct page *rust_helper_alloc_pages(gfp_t gfp_mask,
> @@ -10,6 +11,22 @@ __rust_helper struct page *rust_helper_alloc_pages(gfp_t gfp_mask,
>         return alloc_pages(gfp_mask, order);
>  }
>
> +__rust_helper void rust_helper_put_page(struct page *page)
> +{
> +       put_page(page);
> +}
> +
> +__rust_helper struct folio *rust_helper_page_folio(struct page *page)
> +{
> +       return page_folio(page);
> +}
> +
> +__rust_helper int rust_helper_mem_cgroup_charge(struct folio *folio,
> +                                               struct mm_struct *mm, gfp_t gfp)
> +{
> +       return mem_cgroup_charge(folio, mm, gfp);
> +}
> +
>  __rust_helper void *rust_helper_kmap_local_page(struct page *page)
>  {
>         return kmap_local_page(page);
> diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs
> index 1c0796ea229f..7a36b7dd6c9c 100644
> --- a/rust/kernel/page.rs
> +++ b/rust/kernel/page.rs
> @@ -10,8 +10,10 @@
>      bindings,
>      error::{
>          code::*,
> +        Error,
>          Result, //
>      },
> +    mm::Mm,
>      uaccess::UserSliceReader, //
>  };
>  use core::{
> @@ -195,6 +197,28 @@ pub fn alloc_page(flags: Flags) -> Result<Self, AllocError> {
>          Ok(Self { page })
>      }
>
> +    /// Allocates a new page and charges it to the memory cgroup of `mm`.
> +    #[inline]
> +    pub fn alloc_page_memcg(flags: Flags, mm: &Mm) -> Result<Self, MemcgAllocError> {
> +        let page = Self::alloc_page(flags)?;
> +        // Note: Charging the folio with `mem_cgroup_charge()` only works because `Page::drop` uses
> +        // `put_page()` (which uncharges the folio before freeing it) rather than `__free_pages()`.
> +        //
> +        // SAFETY: `page` was just allocated, so we hold the only reference to it and it is not
> +        // yet charged to any memcg. `mm.as_raw()` is a valid pointer to an `mm_struct`.
> +        let ret = unsafe {
> +            bindings::mem_cgroup_charge(
> +                bindings::page_folio(page.as_ptr()),
> +                mm.as_raw(),
> +                flags.as_raw(),

I wonder if we should we mask out __GFP_ACCOUNT from flags? It's not
provided by the new caller in the next patch, but it could be included
inadvertently in a future call elsewhere. The VM_BUG_ON_FOLIO that
checks if the page would be double charged only happens when
CONFIG_DEBUG_VM=y. Without it on production builds, folio->memcg_data
just gets overwritten. We'd probably catch that eventually in a
CONFIG_DEBUG_VM build, but not sure if we should rely on that. I don't
feel strongly, just


> +            )
> +        };
> +        if ret != 0 {
> +            return Err(MemcgAllocError::Memcg);
> +        }
> +        Ok(page)
> +    }
> +
>      /// Returns a raw pointer to the page.
>      pub fn as_ptr(&self) -> *mut bindings::page {
>          self.page.as_ptr()
> @@ -376,7 +400,31 @@ pub unsafe fn copy_from_user_slice_raw(
>  impl Drop for Page {
>      #[inline]
>      fn drop(&mut self) {
> +        // We need to use `put_page()` instead of `__free_pages()` to properly handle memcg
> +        // accounting.
> +        //
>          // SAFETY: By the type invariants, we have ownership of the page and can free it.
> -        unsafe { bindings::__free_pages(self.page.as_ptr(), 0) };
> +        unsafe { bindings::put_page(self.page.as_ptr()) };
> +    }
> +}
> +
> +/// Error returned by [`Page::alloc_page_memcg`].
> +#[derive(Copy, Clone, PartialEq, Eq, Debug)]
> +pub enum MemcgAllocError {
> +    /// Allocating the page failed.
> +    Alloc,
> +    /// Charging the page to the memory cgroup failed.
> +    Memcg,
> +}
> +
> +impl From<AllocError> for MemcgAllocError {
> +    fn from(_: AllocError) -> Self {
> +        Self::Alloc
> +    }
> +}
> +
> +impl From<MemcgAllocError> for Error {
> +    fn from(_: MemcgAllocError) -> Error {
> +        ENOMEM
>      }
>  }
>
> --
> 2.56.0.212.g129ede20d9-goog
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] rust_binder: charge allocated pages to memcg
  2026-10-16 13:30 ` [PATCH 2/2] rust_binder: charge allocated pages to memcg Alice Ryhl
@ 2026-10-16 18:41   ` T.J. Mercier
  0 siblings, 0 replies; 5+ messages in thread
From: T.J. Mercier @ 2026-10-16 18:41 UTC (permalink / raw)
  To: Alice Ryhl
  Cc: Greg Kroah-Hartman, Carlos Llamas, Lorenzo Stoakes,
	Liam R. Howlett, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
	Alexandre Courbot, Onur Özkan, linux-mm, rust-for-linux,
	linux-kernel, Johannes Weiner, Shakeel Butt

On Fri, Oct 16, 2026 at 6:30 AM Alice Ryhl <aliceryhl@google.com> wrote:
>
> Pages allocated for a binder VMA are currently not charged to any memory
> cgroup. Thus, use `Page::alloc_page_memcg()` to charge them to the
> owning process's `mm_struct`.
>
> Suggested-by: T.J. Mercier <tjmercier@google.com>
> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
> ---
>  drivers/android/binder/page_range.rs | 13 +++++++++++--
>  1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
> index 52ffbf3504e7..57da47243967 100644
> --- a/drivers/android/binder/page_range.rs
> +++ b/drivers/android/binder/page_range.rs
> @@ -27,7 +27,7 @@
>      ffi::{c_ulong, c_void},
>      mm::{virt, Mm, MmWithUser},
>      new_mutex, new_spinlock,
> -    page::{Page, PAGE_SHIFT, PAGE_SIZE},
> +    page::{MemcgAllocError, Page, PAGE_SHIFT, PAGE_SIZE},
>      prelude::*,
>      str::CStr,
>      sync::{aref::ARef, Mutex, SpinLock},
> @@ -399,7 +399,16 @@ pub(crate) fn use_range(&self, start: usize, end: usize) -> Result<()> {
>      /// Assumes that `i` is in bounds.
>      #[cold]
>      unsafe fn use_page_slow(&self, i: usize) -> Result<()> {
> -        let new_page = Page::alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO)?;
> +        let new_page = Page::alloc_page_memcg(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, &self.mm)

Not related to this change, but I think it might be better to ditch
GFP_KERNEL | __GFP_HIGHMEM and use GFP_HIGHUSER. We're already mapping
this into userspace (and now charging the memcg for it).

Is current the sender here? If memory.high is exceeded, current gets
blamed for it in try_charge_memcg()'s done_restock section. So if it's
the sender, that's not exactly what we want since the sender would be
throttled for it. So I think this depends on
https://lore.kernel.org/all/20261010210957.1350874-4-shakeel.butt@linux.dev/.



> +            .inspect_err(|err| {
> +                if *err == MemcgAllocError::Memcg {
> +                    binder_debug!(
> +                        FailedTransaction,
> +                        "page allocation for message to {} was blocked by memory cgroup limits",
> +                        self.pid,
> +                    );
> +                }
> +            })?;
>
>          let mm_mutex = self.mm_lock.lock();
>          let inner = self.lock.lock();
>
> --
> 2.56.0.212.g129ede20d9-goog
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-16 18:42 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-16 13:30 [PATCH 0/2] rust_binder: charge allocated pages to memcg Alice Ryhl
2026-10-16 13:30 ` [PATCH 1/2] rust: page: add Page::alloc_page_memcg() Alice Ryhl
2026-10-16 18:41   ` T.J. Mercier
2026-10-16 13:30 ` [PATCH 2/2] rust_binder: charge allocated pages to memcg Alice Ryhl
2026-10-16 18:41   ` T.J. Mercier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®