From nobody Mon Feb 9 01:01:53 2026 Received: from mail.cock.li (unknown [37.120.193.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 17888318ECC; Sat, 31 Jan 2026 15:41:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=37.120.193.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769874087; cv=none; b=kMRI80g9OTgMOHrhkDMUDdMbBXD0hBLafYWOkIxZwToaj82Y8GuYd0osySNzouxM/fzNAwgjvuZdsdzJhOBbVTiHIYK8i4PqCWcLTE8n2nr8XDCGshfCKxDxvRhR23+sHptCz+hii8TxxCIv4A8uESaSPBlTTr+DoTsPMHyOy0g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769874087; c=relaxed/simple; bh=esEcNNEfTcgzbfPBBZyLIznR/0jROJnXakLmAGgUkA4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EZWeDhXq11hAhCkS0YsTZYv1RyAx2C7GPFxTq/7qSzppqypmy7yAKqyhbhCsANA3UniqwGb0s3TMFGZ2+FctydG0CyqaHKehAN4cQEGqcBQ1vz7Hg1exOiwjHQlJ+IOAxUBRhBDMSs3Efl5EQrUqTuH6Z9wOn8HkybJ3K31PALs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=cock.li; spf=pass smtp.mailfrom=cock.li; dkim=pass (2048-bit key) header.d=cock.li header.i=@cock.li header.b=R2Lv3Uc4; arc=none smtp.client-ip=37.120.193.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=cock.li Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cock.li Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cock.li header.i=@cock.li header.b="R2Lv3Uc4" From: Shivam Kalra DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=cock.li; s=mail; t=1769874082; bh=esEcNNEfTcgzbfPBBZyLIznR/0jROJnXakLmAGgUkA4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=R2Lv3Uc4LSXiR0EPJ/wFVLRCPzqjqunIO52HjjFnwwdOoGFyV/t3u29AQ81Kocgen gpJVVikVp+260FGW5pEzzcBOO2BP0rG9nz9NQQTHufcHpPwhZ+XuAhX6L75uLfPfoh eUjaVWqy8FQSFb3pSqGPQPMJLntf1a/Pgz+0NTtqUdx1eOsqXNqNhhhMmtxyPRW8iX 2hPz5IrmwhOlAXUpPPD3NBffl8KvWysZtSlejeGTP8VK+/5b/dVku82RIYsQ6oT/jB PJwJTxKyJtgsLwqLvHiPAx/LZpvapCoVSdZof9kDoeC2u8DPxx2hbM47iV6pDfQ+B6 R/elSjj6/o0Og== To: dakr@kernel.org, cmllamas@google.com, gregkh@linuxfoundation.org Cc: aliceryhl@google.com, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Shivam Kalra Subject: [PATCH v2 1/3] rust: alloc: Add shrink_to and shrink_to_fit methods to Vec Date: Sat, 31 Jan 2026 21:10:13 +0530 Message-ID: <20260131154016.270385-2-shivamklr@cock.li> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260131154016.270385-1-shivamklr@cock.li> References: <20260131154016.270385-1-shivamklr@cock.li> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add methods to shrink the capacity of a Vec to free excess memory. This is useful for drivers that experience variable workloads and want to reclaim memory after spikes. - `shrink_to(min_capacity, flags)`: Shrinks the capacity of the vector with a lower bound. The capacity will remain at least as large as both the length and the supplied value. - `shrink_to_fit(flags)`: Shrinks the capacity of the vector as much as possible. This implementation guarantees shrinking (unless already optimal), because the kernel allocators don't support in-place shrinking, a new allocation is always made. Suggested-by: Alice Ryhl Signed-off-by: Shivam Kalra --- rust/kernel/alloc/kvec.rs | 111 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index ac8d6f763ae81..9c02734ced88f 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -733,6 +733,117 @@ pub fn retain(&mut self, mut f: impl FnMut(&mut T) ->= bool) { } self.truncate(num_kept); } + + /// Shrinks the capacity of the vector with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// If the current capacity is less than the lower limit, this is a no= -op. + /// + /// This requires allocating a new buffer and copying the elements, th= en freeing + /// the old buffer. + /// + /// # Examples + /// + /// ``` + /// let mut v =3D KVec::with_capacity(100, GFP_KERNEL)?; + /// v.push(1, GFP_KERNEL)?; + /// v.push(2, GFP_KERNEL)?; + /// assert!(v.capacity() >=3D 100); + /// + /// v.shrink_to(50, GFP_KERNEL)?; + /// assert!(v.capacity() >=3D 50); + /// assert!(v.capacity() < 100); + /// + /// v.shrink_to(0, GFP_KERNEL)?; + /// assert!(v.capacity() >=3D 2); + /// # Ok::<(), Error>(()) + /// ``` + pub fn shrink_to(&mut self, min_capacity: usize, flags: Flags) -> Resu= lt<(), AllocError> { + // Calculate the target capacity: max(len, min_capacity). + let target_cap =3D core::cmp::max(self.len(), min_capacity); + + // If we're already within limits, nothing to do. + if self.capacity() <=3D target_cap { + return Ok(()); + } + + // ZSTs have no allocation to shrink. + if Self::is_zst() { + return Ok(()); + } + + // Handle empty vector or target capacity 0: just free the allocat= ion and reset. + if target_cap =3D=3D 0 { + // Only free if we actually have an allocation. + if !self.layout.is_empty() { + // SAFETY: + // - `self.ptr` was previously allocated with `A`. + // - `self.layout` matches the `ArrayLayout` of the preced= ing allocation. + unsafe { A::free(self.ptr.cast(), self.layout.into()) }; + } + self.ptr =3D NonNull::dangling(); + self.layout =3D ArrayLayout::empty(); + return Ok(()); + } + + // Create a new layout that exactly fits the target capacity. + // SAFETY: `target_cap` is guaranteed to be <=3D `self.capacity()`= , and the original + // capacity was validated, so `target_cap * size_of::() <=3D is= ize::MAX`. + let new_layout =3D unsafe { ArrayLayout::::new_unchecked(target= _cap) }; + + // Allocate a new, smaller buffer. + let new_ptr =3D A::alloc(new_layout.into(), flags, NumaNode::NO_NO= DE)?; + + // SAFETY: + // - `self.as_ptr()` is valid for reads of `self.len` elements by = the type invariant. + // - `new_ptr` is valid for writes of `self.len` elements (we just= allocated it). + // - The regions do not overlap (different allocations). + // - Both pointers are properly aligned. + unsafe { + ptr::copy_nonoverlapping(self.as_ptr(), new_ptr.as_ptr().cast:= :(), self.len); + } + + // Free the old buffer. + // SAFETY: + // - `self.ptr` was previously allocated with `A`. + // - `self.layout` matches the `ArrayLayout` of the preceding allo= cation. + unsafe { A::free(self.ptr.cast(), self.layout.into()) }; + + // SAFETY: `new_ptr.as_ptr()` is non-null because `A::alloc` succe= eded. + self.ptr =3D unsafe { NonNull::new_unchecked(new_ptr.as_ptr().cast= ::()) }; + self.layout =3D new_layout; + + Ok(()) + } + + /// Shrinks the capacity of the vector as much as possible. + /// + /// The capacity will be reduced to match the length, freeing any exce= ss memory. + /// This requires allocating a new buffer and copying the elements, th= en freeing + /// the old buffer. + /// + /// If the allocation of the new buffer fails, the vector is left unch= anged and + /// an error is returned. + /// + /// # Examples + /// + /// ``` + /// let mut v =3D KVec::with_capacity(100, GFP_KERNEL)?; + /// v.push(1, GFP_KERNEL)?; + /// v.push(2, GFP_KERNEL)?; + /// v.push(3, GFP_KERNEL)?; + /// assert!(v.capacity() >=3D 100); + /// + /// v.shrink_to_fit(GFP_KERNEL)?; + /// assert_eq!(v.capacity(), 3); + /// assert_eq!(&v, &[1, 2, 3]); + /// # Ok::<(), Error>(()) + /// ``` + pub fn shrink_to_fit(&mut self, flags: Flags) -> Result<(), AllocError= > { + self.shrink_to(0, flags) + } } =20 impl Vec { --=20 2.43.0 From nobody Mon Feb 9 01:01:53 2026 Received: from mail.cock.li (unknown [37.120.193.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EA1B8331206; Sat, 31 Jan 2026 15:41:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=37.120.193.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769874089; cv=none; b=sccKNrbxkR31Hf3vuWFcdM+xbL7F25J2rY6b5n1/y4rsI5Q56CiFlUYnDb7Z4hTp9vtoEfb+vkiQrfT9XoSIM1R3oHmrm7MILaDGqBO65/37GvaDv71qqQ7nc7/5OSN8iQLcZZPlUnetxmbWonPQmrstaHNN5rnzNtvboJHu22g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769874089; c=relaxed/simple; bh=ypJSZsd2gkd3H9jZrx749RhEDkv5ZqvN4gf27waZnjY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=n+pST5jHBfzSNnGC5s3B/2QAOdrAZMCAUyiyo/9fc5rth4flC7KcIOgcYgWWssFanI6rIGFQMmXz/2DGMyNrRKSxy9asdHE8Q7ihszhVEPd/TR7Qn0Ote4gd9PoHPUBenTjZdiSAGn8/Fy4VC5zUpzUcdizl76fNqr/y9tbbNfI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=cock.li; spf=pass smtp.mailfrom=cock.li; dkim=pass (2048-bit key) header.d=cock.li header.i=@cock.li header.b=YDCO8sdg; arc=none smtp.client-ip=37.120.193.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=cock.li Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cock.li Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cock.li header.i=@cock.li header.b="YDCO8sdg" From: Shivam Kalra DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=cock.li; s=mail; t=1769874085; bh=ypJSZsd2gkd3H9jZrx749RhEDkv5ZqvN4gf27waZnjY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YDCO8sdgoGcMq8UwjUMLjhkmoBn7Ou+JmXzk7pZLIsAxkiGnAh1tY3zuhs2xnQiQJ cXwLNg+X798hthqIqGwzq6l8lejdb4c0EQVW8qWAGrpSzyiShaeFnC+2Xo0uBA3R/e +Kf0g1akWzv790gDSExZ74EvAOBzgdVyq8/pwmzdYPlPkxS/Ez43UpK7Z6SWlaSuxW cYbBNv8vgNjgVYsh3v1xELYDIIuVokwGy8Uf+zyYGxjgSdOdLvfNPSF2XEgyllaZmD TAhp3bxpgOQLKbBggLllAtU4iRf2rj+3sZzPCLrr6PBsGs0ZdPUsLicMD5n+Iy4XSz 4sEnknIyWSr/g== To: dakr@kernel.org, cmllamas@google.com, gregkh@linuxfoundation.org Cc: aliceryhl@google.com, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Shivam Kalra Subject: [PATCH v2 2/3] rust: alloc: add KUnit tests for Vec shrinking Date: Sat, 31 Jan 2026 21:10:14 +0530 Message-ID: <20260131154016.270385-3-shivamklr@cock.li> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260131154016.270385-1-shivamklr@cock.li> References: <20260131154016.270385-1-shivamklr@cock.li> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add comprehensive KUnit tests for Vec::shrink_to() and Vec::shrink_to_fit() to verify that capacity is reduced correctly. The tests cover: - shrinking from excess capacity - correct handling of empty vectors - no-op behavior when capacity is already optimal - respecting the minimum capacity limit for shrink_to() - no growth when the requested minimum exceeds current capacity Testing: - KUnit tests pass locally (command above). - Built with CONFIG_RUST=3Dy on x86_64 (user-mode testing via --arch um). Signed-off-by: Shivam Kalra --- rust/kernel/alloc/kvec.rs | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index 9c02734ced88f..5ab78d6c03e40 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -1509,4 +1509,69 @@ fn add(value: &mut [bool]) { func.push_within_capacity(false).unwrap(); } } + + #[test] + fn test_shrink_to_fit() { + // Create a vector with excess capacity. + let mut v: KVec =3D KVec::with_capacity(100, GFP_KERNEL).unwr= ap(); + v.push(1, GFP_KERNEL).unwrap(); + v.push(2, GFP_KERNEL).unwrap(); + v.push(3, GFP_KERNEL).unwrap(); + + assert!(v.capacity() >=3D 100); + assert_eq!(v.len(), 3); + + // Shrink with 0 (equivalent to shrink_to_fit). + v.shrink_to(0, GFP_KERNEL).unwrap(); + + // Capacity should now equal length. + assert_eq!(v.capacity(), 3); + assert_eq!(v.len(), 3); + assert_eq!(&v, &[1, 2, 3]); + + // Shrink empty vector. + let mut v: KVec =3D KVec::with_capacity(50, GFP_KERNEL).unwra= p(); + v.shrink_to(0, GFP_KERNEL).unwrap(); + assert_eq!(v.capacity(), 0); + + // Shrink already optimal (no-op). + let mut v: KVec =3D KVec::new(); + v.push(1, GFP_KERNEL).unwrap(); + v.shrink_to(0, GFP_KERNEL).unwrap(); + assert!(v.capacity() >=3D 1); + } + + #[test] + fn test_shrink_to_min() { + let mut v: KVec =3D KVec::with_capacity(100, GFP_KERNEL).unwr= ap(); + for i in 0..10 { + v.push(i, GFP_KERNEL).unwrap(); + } + assert_eq!(v.len(), 10); + assert!(v.capacity() >=3D 100); + + // Shrink to a specific minimum higher than len. + v.shrink_to(50, GFP_KERNEL).unwrap(); + assert!(v.capacity() >=3D 50); + assert!(v.capacity() < 100); // Should have shrunk + assert_eq!(v.len(), 10); + assert_eq!(v[0], 0); + assert_eq!(v[9], 9); + + // Shrink to a minimum lower than len (should stop at len). + v.shrink_to(5, GFP_KERNEL).unwrap(); + assert_eq!(v.capacity(), 10); + assert_eq!(v.len(), 10); + } + + #[test] + fn test_shrink_to_no_growth() { + let mut v: KVec =3D KVec::with_capacity(10, GFP_KERNEL).unwra= p(); + v.push(1, GFP_KERNEL).unwrap(); + let cap =3D v.capacity(); + + // Requesting larger capacity should NOT grow the vector. + v.shrink_to(100, GFP_KERNEL).unwrap(); + assert_eq!(v.capacity(), cap); + } } --=20 2.43.0 From nobody Mon Feb 9 01:01:53 2026 Received: from mail.cock.li (unknown [37.120.193.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 92A851A58D; Sun, 1 Feb 2026 00:08:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=37.120.193.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769904529; cv=none; b=Gwd18VLBtQnS4LGhqff4WJO2yTku+FYRqs99bEfmXFTj/I1nIn17+/ldsbM9Unn2YhIr45ORN9bHi11ZQcyWkqLP86CdhfOcqkbOBCfqXFIgFK0Sm2ZZh9HNxIodU0oVbXXXngGkEDxINc/UEp5e5sJrvG3sZPN353avD7rbtog= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769904529; c=relaxed/simple; bh=PNZhCR+/pggdZ+eX2S14oEhix7kpixYkOW4FSi+tsAY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CAAwa+8uU1Q4k6k9kDTjylzmjQADKPoc5hGo52jPxj6oyI4wdwHQ+yKmICh6MnwTB61AtZFHyoiNZwvu5hk/DBiPbq1TfCwBvA2W/ZgF1e418VubfiG+ID9gB5R0M2I3jamyh2xRvq56IndH3dQYeZ/+huVkD8VnOQX9+/UBh8s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=cock.li; spf=pass smtp.mailfrom=cock.li; dkim=pass (2048-bit key) header.d=cock.li header.i=@cock.li header.b=kK1n3efU; arc=none smtp.client-ip=37.120.193.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=cock.li Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cock.li Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cock.li header.i=@cock.li header.b="kK1n3efU" From: Shivam Kalra DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=cock.li; s=mail; t=1769904515; bh=PNZhCR+/pggdZ+eX2S14oEhix7kpixYkOW4FSi+tsAY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kK1n3efUSVe5VFeKpkpYEFeKi3+oeNHTrlaTDRxr9JFVSXm6lrTqGQ1qUxQDSO78x 82slpxCkRa6Fy6AGCotoWFGAoREtR56AN4rppi8OzXlgZza9Oxr7XMLyFw4jOpzKnj 2oXNTbG8VGIqLuYKvHoTTJT1ONWzFRkuZoyxwKFQiiOR33LXdnSUEQI4pkr7DslUTQ 97zLhcWWMQsVZSGJaGAP3x1pcJBz84ZihsB+6XLo+p/AGN+eiHjXOp5CMAvz+FK6PF zu1D5X1Wtb5BIYtdI6FooisA9+rgConqOjHLqiqdf3a370nTvhU1fVYpYd1gnyk3DR xy7jUUlLtn36A== To: dakr@kernel.org, cmllamas@google.com, gregkh@linuxfoundation.org Cc: aliceryhl@google.com, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Shivam Kalra Subject: [PATCH v2 3/3] binder: context: shrink all_procs vector to reclaim memory Date: Sun, 1 Feb 2026 05:38:17 +0530 Message-ID: <20260201000817.275382-1-shivamklr@cock.li> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260131154016.270385-1-shivamklr@cock.li> References: <20260131154016.270385-1-shivamklr@cock.li> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" After removing a process from the all_procs list, shrink the vector if it has significant unused capacity. Specifically, if the capacity exceeds 128 and the length is less than half the capacity, shrink the vector to its current length. This prevents unbounded memory retention in long-running systems where the number of binder processes fluctuates significantly over time. Link: https://lore.kernel.org/rust-for-linux/aW4DWebqCg1c8Fgb@google.com/ Suggested-by: Alice Ryhl Signed-off-by: Shivam Kalra --- drivers/android/binder/context.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/android/binder/context.rs b/drivers/android/binder/con= text.rs index 9cf437c025a20..f2505fbf17403 100644 --- a/drivers/android/binder/context.rs +++ b/drivers/android/binder/context.rs @@ -94,6 +94,16 @@ pub(crate) fn deregister_process(self: &Arc, proc:= &Arc) { } let mut manager =3D self.manager.lock(); manager.all_procs.retain(|p| !Arc::ptr_eq(p, proc)); + + // Shrink the vector if it has significant unused capacity. + // Only shrink if capacity > 128 to avoid repeated reallocations f= or small vectors. + let len =3D manager.all_procs.len(); + let cap =3D manager.all_procs.capacity(); + if cap > 128 && len < cap / 2 { + // Shrink to current length. Ignore allocation failures since = this is just an + // optimization; the vector remains valid even if shrinking fa= ils. + let _ =3D manager.all_procs.shrink_to(len, GFP_KERNEL); + } } =20 pub(crate) fn set_manager_node(&self, node_ref: NodeRef) -> Result { --=20 2.43.0