Skip to content

Commit acf7355

Browse files
Vitaly WoolKernel Patches Daemon
authored andcommitted
rust: add support for NUMA ids in allocations
Add a new type to support specifying NUMA identifiers in Rust allocators and extend the allocators to have NUMA id as a parameter. Thus, modify ReallocFunc to use the new extended realloc primitives from the C side of the kernel (i. e. k[v]realloc_node_align/vrealloc_node_align) and add the new function alloc_node to the Allocator trait while keeping the existing one (alloc) for backward compatibility. This will allow to specify node to use for allocation of e. g. {KV}Box, as well as for future NUMA aware users of the API. Signed-off-by: Vitaly Wool <[email protected]>
1 parent c43492f commit acf7355

File tree

6 files changed

+86
-28
lines changed

6 files changed

+86
-28
lines changed

rust/helpers/slab.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
#include <linux/slab.h>
44

55
void * __must_check __realloc_size(2)
6-
rust_helper_krealloc(const void *objp, size_t new_size, gfp_t flags)
6+
rust_helper_krealloc_node(const void *objp, size_t new_size, gfp_t flags, int node)
77
{
8-
return krealloc(objp, new_size, flags);
8+
return krealloc_node(objp, new_size, flags, node);
99
}
1010

1111
void * __must_check __realloc_size(2)
12-
rust_helper_kvrealloc(const void *p, size_t size, gfp_t flags)
12+
rust_helper_kvrealloc_node(const void *p, size_t size, gfp_t flags, int node)
1313
{
14-
return kvrealloc(p, size, flags);
14+
return kvrealloc_node(p, size, flags, node);
1515
}

rust/helpers/vmalloc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include <linux/vmalloc.h>
44

55
void * __must_check __realloc_size(2)
6-
rust_helper_vrealloc(const void *p, size_t size, gfp_t flags)
6+
rust_helper_vrealloc_node(const void *p, size_t size, gfp_t flags, int node)
77
{
8-
return vrealloc(p, size, flags);
8+
return vrealloc_node(p, size, flags, node);
99
}

rust/kernel/alloc.rs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub use self::kvec::Vec;
2828
/// Indicates an allocation error.
2929
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
3030
pub struct AllocError;
31+
32+
use crate::error::{code::EINVAL, Result};
3133
use core::{alloc::Layout, ptr::NonNull};
3234

3335
/// Flags to be used when allocating memory.
@@ -115,6 +117,29 @@ pub mod flags {
115117
pub const __GFP_NOWARN: Flags = Flags(bindings::__GFP_NOWARN);
116118
}
117119

120+
/// Non Uniform Memory Access (NUMA) node identifier
121+
#[derive(Clone, Copy, PartialEq)]
122+
pub struct NumaNode(i32);
123+
124+
impl NumaNode {
125+
/// create a new NUMA node identifer (non-negative integer)
126+
/// returns EINVAL if a negative id or an id exceeding MAX_NUMNODES is specified
127+
pub fn new(node: i32) -> Result<Self> {
128+
// SAFETY: MAX_NUMNODES never exceeds 2**10 because NODES_SHIFT is 0..10
129+
if node < 0 || node >= bindings::MAX_NUMNODES as i32 {
130+
return Err(EINVAL);
131+
}
132+
Ok(Self(node))
133+
}
134+
}
135+
136+
/// Specify necessary constant to pass the information to Allocator that the caller doesn't care
137+
/// about the NUMA node to allocate memory from.
138+
impl NumaNode {
139+
/// No node preference.
140+
pub const NO_NODE: NumaNode = NumaNode(bindings::NUMA_NO_NODE);
141+
}
142+
118143
/// The kernel's [`Allocator`] trait.
119144
///
120145
/// An implementation of [`Allocator`] can allocate, re-allocate and free memory buffers described
@@ -137,7 +162,7 @@ pub mod flags {
137162
/// - Implementers must ensure that all trait functions abide by the guarantees documented in the
138163
/// `# Guarantees` sections.
139164
pub unsafe trait Allocator {
140-
/// Allocate memory based on `layout` and `flags`.
165+
/// Allocate memory based on `layout`, `flags` and `nid`.
141166
///
142167
/// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout
143168
/// constraints (i.e. minimum size and alignment as specified by `layout`).
@@ -153,13 +178,21 @@ pub unsafe trait Allocator {
153178
///
154179
/// Additionally, `Flags` are honored as documented in
155180
/// <https://docs.kernel.org/core-api/mm-api.html#mm-api-gfp-flags>.
156-
fn alloc(layout: Layout, flags: Flags) -> Result<NonNull<[u8]>, AllocError> {
181+
fn alloc(layout: Layout, flags: Flags, nid: NumaNode) -> Result<NonNull<[u8]>, AllocError> {
157182
// SAFETY: Passing `None` to `realloc` is valid by its safety requirements and asks for a
158183
// new memory allocation.
159-
unsafe { Self::realloc(None, layout, Layout::new::<()>(), flags) }
184+
unsafe { Self::realloc(None, layout, Layout::new::<()>(), flags, nid) }
160185
}
161186

162-
/// Re-allocate an existing memory allocation to satisfy the requested `layout`.
187+
/// Re-allocate an existing memory allocation to satisfy the requested `layout` and
188+
/// a specific NUMA node request to allocate the memory for.
189+
///
190+
/// Systems employing a Non Uniform Memory Access (NUMA) architecture contain collections of
191+
/// hardware resources including processors, memory, and I/O buses, that comprise what is
192+
/// commonly known as a NUMA node.
193+
///
194+
/// `nid` stands for NUMA id, i. e. NUMA node identifier, which is a non-negative integer
195+
/// if a node needs to be specified, or [`NumaNode::NO_NODE`] if the caller doesn't care.
163196
///
164197
/// If the requested size is zero, `realloc` behaves equivalent to `free`.
165198
///
@@ -196,6 +229,7 @@ pub unsafe trait Allocator {
196229
layout: Layout,
197230
old_layout: Layout,
198231
flags: Flags,
232+
nid: NumaNode,
199233
) -> Result<NonNull<[u8]>, AllocError>;
200234

201235
/// Free an existing memory allocation.
@@ -211,7 +245,15 @@ pub unsafe trait Allocator {
211245
// SAFETY: The caller guarantees that `ptr` points at a valid allocation created by this
212246
// allocator. We are passing a `Layout` with the smallest possible alignment, so it is
213247
// smaller than or equal to the alignment previously used with this allocation.
214-
let _ = unsafe { Self::realloc(Some(ptr), Layout::new::<()>(), layout, Flags(0)) };
248+
let _ = unsafe {
249+
Self::realloc(
250+
Some(ptr),
251+
Layout::new::<()>(),
252+
layout,
253+
Flags(0),
254+
NumaNode::NO_NODE,
255+
)
256+
};
215257
}
216258
}
217259

rust/kernel/alloc/allocator.rs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use core::alloc::Layout;
1313
use core::ptr;
1414
use core::ptr::NonNull;
1515

16-
use crate::alloc::{AllocError, Allocator};
16+
use crate::alloc::{AllocError, Allocator, NumaNode};
1717
use crate::bindings;
1818
use crate::pr_warn;
1919

@@ -56,20 +56,25 @@ fn aligned_size(new_layout: Layout) -> usize {
5656

5757
/// # Invariants
5858
///
59-
/// One of the following: `krealloc`, `vrealloc`, `kvrealloc`.
59+
/// One of the following: `krealloc_node`, `vrealloc_node`, `kvrealloc_node`.
6060
struct ReallocFunc(
61-
unsafe extern "C" fn(*const crate::ffi::c_void, usize, u32) -> *mut crate::ffi::c_void,
61+
unsafe extern "C" fn(
62+
*const crate::ffi::c_void,
63+
usize,
64+
u32,
65+
crate::ffi::c_int,
66+
) -> *mut crate::ffi::c_void,
6267
);
6368

6469
impl ReallocFunc {
65-
// INVARIANT: `krealloc` satisfies the type invariants.
66-
const KREALLOC: Self = Self(bindings::krealloc);
70+
// INVARIANT: `krealloc_node` satisfies the type invariants.
71+
const KREALLOC: Self = Self(bindings::krealloc_node);
6772

68-
// INVARIANT: `vrealloc` satisfies the type invariants.
69-
const VREALLOC: Self = Self(bindings::vrealloc);
73+
// INVARIANT: `vrealloc_node` satisfies the type invariants.
74+
const VREALLOC: Self = Self(bindings::vrealloc_node);
7075

71-
// INVARIANT: `kvrealloc` satisfies the type invariants.
72-
const KVREALLOC: Self = Self(bindings::kvrealloc);
76+
// INVARIANT: `kvrealloc_node` satisfies the type invariants.
77+
const KVREALLOC: Self = Self(bindings::kvrealloc_node);
7378

7479
/// # Safety
7580
///
@@ -87,6 +92,7 @@ impl ReallocFunc {
8792
layout: Layout,
8893
old_layout: Layout,
8994
flags: Flags,
95+
nid: NumaNode,
9096
) -> Result<NonNull<[u8]>, AllocError> {
9197
let size = aligned_size(layout);
9298
let ptr = match ptr {
@@ -110,7 +116,7 @@ impl ReallocFunc {
110116
// - Those functions provide the guarantees of this function.
111117
let raw_ptr = unsafe {
112118
// If `size == 0` and `ptr != NULL` the memory behind the pointer is freed.
113-
self.0(ptr.cast(), size, flags.0).cast()
119+
self.0(ptr.cast(), size, flags.0, nid.0).cast()
114120
};
115121

116122
let ptr = if size == 0 {
@@ -134,9 +140,10 @@ unsafe impl Allocator for Kmalloc {
134140
layout: Layout,
135141
old_layout: Layout,
136142
flags: Flags,
143+
nid: NumaNode,
137144
) -> Result<NonNull<[u8]>, AllocError> {
138145
// SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`.
139-
unsafe { ReallocFunc::KREALLOC.call(ptr, layout, old_layout, flags) }
146+
unsafe { ReallocFunc::KREALLOC.call(ptr, layout, old_layout, flags, nid) }
140147
}
141148
}
142149

@@ -151,6 +158,7 @@ unsafe impl Allocator for Vmalloc {
151158
layout: Layout,
152159
old_layout: Layout,
153160
flags: Flags,
161+
nid: NumaNode,
154162
) -> Result<NonNull<[u8]>, AllocError> {
155163
// TODO: Support alignments larger than PAGE_SIZE.
156164
if layout.align() > bindings::PAGE_SIZE {
@@ -160,7 +168,7 @@ unsafe impl Allocator for Vmalloc {
160168

161169
// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
162170
// allocated with this `Allocator`.
163-
unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags) }
171+
unsafe { ReallocFunc::VREALLOC.call(ptr, layout, old_layout, flags, nid) }
164172
}
165173
}
166174

@@ -175,6 +183,7 @@ unsafe impl Allocator for KVmalloc {
175183
layout: Layout,
176184
old_layout: Layout,
177185
flags: Flags,
186+
nid: NumaNode,
178187
) -> Result<NonNull<[u8]>, AllocError> {
179188
// TODO: Support alignments larger than PAGE_SIZE.
180189
if layout.align() > bindings::PAGE_SIZE {
@@ -184,6 +193,6 @@ unsafe impl Allocator for KVmalloc {
184193

185194
// SAFETY: If not `None`, `ptr` is guaranteed to point to valid memory, which was previously
186195
// allocated with this `Allocator`.
187-
unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) }
196+
unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags, nid) }
188197
}
189198
}

rust/kernel/alloc/kbox.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
#[allow(unused_imports)] // Used in doc comments.
66
use super::allocator::{KVmalloc, Kmalloc, Vmalloc};
7-
use super::{AllocError, Allocator, Flags};
7+
use super::{AllocError, Allocator, Flags, NumaNode};
88
use core::alloc::Layout;
99
use core::fmt;
1010
use core::marker::PhantomData;
@@ -271,7 +271,7 @@ where
271271
/// ```
272272
pub fn new_uninit(flags: Flags) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
273273
let layout = Layout::new::<MaybeUninit<T>>();
274-
let ptr = A::alloc(layout, flags)?;
274+
let ptr = A::alloc(layout, flags, NumaNode::NO_NODE)?;
275275

276276
// INVARIANT: `ptr` is either a dangling pointer or points to memory allocated with `A`,
277277
// which is sufficient in size and alignment for storing a `T`.

rust/kernel/alloc/kvec.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use super::{
66
allocator::{KVmalloc, Kmalloc, Vmalloc},
77
layout::ArrayLayout,
8-
AllocError, Allocator, Box, Flags,
8+
AllocError, Allocator, Box, Flags, NumaNode,
99
};
1010
use core::{
1111
fmt,
@@ -633,6 +633,7 @@ where
633633
layout.into(),
634634
self.layout.into(),
635635
flags,
636+
NumaNode::NO_NODE,
636637
)?
637638
};
638639

@@ -1058,7 +1059,13 @@ where
10581059
// the type invariant to be smaller than `cap`. Depending on `realloc` this operation
10591060
// may shrink the buffer or leave it as it is.
10601061
ptr = match unsafe {
1061-
A::realloc(Some(buf.cast()), layout.into(), old_layout.into(), flags)
1062+
A::realloc(
1063+
Some(buf.cast()),
1064+
layout.into(),
1065+
old_layout.into(),
1066+
flags,
1067+
NumaNode::NO_NODE,
1068+
)
10621069
} {
10631070
// If we fail to shrink, which likely can't even happen, continue with the existing
10641071
// buffer.

0 commit comments

Comments
 (0)