Skip to content

Commit 0a245d7

Browse files
Ticonderoga2017Mivik
authored andcommitted
[mm] support huge page for map alloc (oscomp#49)
* support huge page alloc: unmmap * support huge page alloc: area_* -> unmap_* in unmap
1 parent 76bf692 commit 0a245d7

8 files changed

Lines changed: 352 additions & 111 deletions

File tree

‎modules/axdma/src/dma.rs‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use core::{alloc::Layout, ptr::NonNull};
22

33
use allocator::{AllocError, AllocResult, BaseAllocator, ByteAllocator};
44
use axalloc::{DefaultByteAllocator, global_allocator};
5-
use axhal::{mem::virt_to_phys, paging::MappingFlags};
5+
use axhal::{
6+
mem::virt_to_phys,
7+
paging::{MappingFlags, PageSize},
8+
};
69
use kspin::SpinNoIrq;
710
use log::{debug, error};
811
use memory_addr::{PAGE_SIZE_4K, VirtAddr, va};
@@ -94,7 +97,7 @@ impl DmaAllocator {
9497
let expand_size = num_pages * PAGE_SIZE_4K;
9598
axmm::kernel_aspace()
9699
.lock()
97-
.protect(vaddr, expand_size, flags)
100+
.protect(vaddr, expand_size, flags, PageSize::Size4K)
98101
.map_err(|e| {
99102
error!("change table flag fail: {e:?}");
100103
AllocError::NoMemory

‎modules/axmm/src/aspace.rs‎

Lines changed: 125 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ use axerrno::{AxError, AxResult, ax_err};
55
use axhal::mem::phys_to_virt;
66
use axhal::paging::{MappingFlags, PageTable, PagingError};
77
use memory_addr::{
8-
MemoryAddr, PAGE_SIZE_4K, PageIter4K, PhysAddr, VirtAddr, VirtAddrRange, is_aligned_4k,
8+
MemoryAddr, PAGE_SIZE_4K, PageIter4K, PhysAddr, VirtAddr, VirtAddrRange, is_aligned,
99
};
1010
use memory_set::{MemoryArea, MemorySet};
11+
use page_table_multiarch::PageSize;
1112

12-
use crate::backend::{Backend, SharedPages};
13+
use crate::backend::{Backend, PageIterWrapper, SharedPages};
1314
use crate::mapping_err_to_ax_err;
1415

1516
/// The virtual memory address space.
@@ -91,28 +92,84 @@ impl AddrSpace {
9192
self.pt.clear_copy_range(range.start, range.size());
9293
}
9394

94-
fn validate_region(&self, start: VirtAddr, size: usize) -> AxResult {
95+
/// The page table hardware can only map address ranges that are page-aligned.
96+
/// During the memory region validation in AddrSpace,
97+
/// the system enforces address alignment,
98+
/// ensuring that all memory operations comply with page boundary requirements.
99+
fn validate_region(&self, start: VirtAddr, size: usize, align: PageSize) -> AxResult {
95100
if !self.contains_range(start, size) {
96101
return ax_err!(InvalidInput, "address out of range");
97102
}
98-
if !start.is_aligned_4k() || !is_aligned_4k(size) {
103+
if !start.is_aligned(align) || !is_aligned(size, align.into()) {
99104
return ax_err!(InvalidInput, "address not aligned");
100105
}
101106
Ok(())
102107
}
103108

104-
/// Finds a free area that can accommodate the given size.
109+
/// Searches for a contiguous free region in the virtual address space
105110
///
106-
/// The search starts from the given hint address, and the area should be within the given limit range.
111+
/// This function searches for available virtual address space within a specified address range,
112+
/// based on the current memory region layout, that satisfies the size and alignment requirements.
107113
///
108-
/// Returns the start address of the free area. Returns None if no such area is found.
114+
/// # Parameters
115+
/// - `hint`: Suggested starting address for the search (may be adjusted due to alignment or overlapping regions)
116+
/// - `size`: Size of the contiguous address space to allocate (in bytes)
117+
/// - `limit`: Boundary of the allowed address range (inclusive of start and end addresses)
118+
/// - `align`: Address alignment requirement (e.g., page alignment like 4KB/2MB)
119+
///
120+
/// # Return Value
121+
/// - `Some(VirtAddr)`: A starting virtual address that meets all requirements was found
122+
/// - `None`: No sufficient space was found within the specified range
123+
///
124+
/// # Implementation Logic
125+
/// 1. Initialize `last_end` to the maximum aligned value between the hint and the start of the limit range
126+
/// 2. First pass: handle regions before the hint to determine the initial search position
127+
/// 3. Second pass: check gaps between regions:
128+
/// - Skip overlapping and already occupied regions
129+
/// - Check whether the gap between regions satisfies the `size + alignment` requirement
130+
/// 4. Finally, verify that the found address is within the specified `limit` range
131+
///
132+
/// # Notes
133+
/// - Alignment is strictly enforced on candidate addresses (ensured via `align_up`)
134+
/// - The region must be fully contained within the `limit` range (`end <= limit.end`)
135+
/// - The search may ignore the `hint` if a better space is found in later regions
109136
pub fn find_free_area(
110137
&self,
111138
hint: VirtAddr,
112139
size: usize,
113140
limit: VirtAddrRange,
141+
align: PageSize,
114142
) -> Option<VirtAddr> {
115-
self.areas.find_free_area(hint, size, limit)
143+
let mut last_end = hint.max(limit.start).align_up(align);
144+
for area in self.areas.iter() {
145+
if area.end() <= last_end {
146+
last_end = last_end.max(area.end().align_up(align));
147+
} else {
148+
break;
149+
}
150+
}
151+
for area in self.areas.iter() {
152+
let area_start = area.start();
153+
if area_start < last_end {
154+
continue;
155+
}
156+
if last_end
157+
.checked_add(size)
158+
.is_some_and(|end| end <= area_start)
159+
{
160+
return Some(last_end);
161+
}
162+
last_end = area.end().align_up(align);
163+
}
164+
165+
if last_end
166+
.checked_add(size)
167+
.is_some_and(|end| end <= limit.end)
168+
{
169+
Some(last_end)
170+
} else {
171+
None
172+
}
116173
}
117174

118175
/// Add a new linear mapping.
@@ -129,14 +186,16 @@ impl AddrSpace {
129186
start_paddr: PhysAddr,
130187
size: usize,
131188
flags: MappingFlags,
189+
align: PageSize,
132190
) -> AxResult {
133-
self.validate_region(start_vaddr, size)?;
134-
if !start_paddr.is_aligned_4k() {
191+
self.validate_region(start_vaddr, size, align)?;
192+
193+
if !start_paddr.is_aligned(align) {
135194
return ax_err!(InvalidInput, "address not aligned");
136195
}
137196

138197
let offset = start_vaddr.as_usize() - start_paddr.as_usize();
139-
let area = MemoryArea::new(start_vaddr, size, flags, Backend::new_linear(offset));
198+
let area = MemoryArea::new(start_vaddr, size, flags, Backend::new_linear(offset, align));
140199
self.areas
141200
.map(area, &mut self.pt, false)
142201
.map_err(mapping_err_to_ax_err)?;
@@ -157,10 +216,11 @@ impl AddrSpace {
157216
size: usize,
158217
flags: MappingFlags,
159218
populate: bool,
219+
align: PageSize,
160220
) -> AxResult {
161-
self.validate_region(start, size)?;
221+
self.validate_region(start, size, align)?;
162222

163-
let area = MemoryArea::new(start, size, flags, Backend::new_alloc(populate));
223+
let area = MemoryArea::new(start, size, flags, Backend::new_alloc(populate, align));
164224
self.areas
165225
.map(area, &mut self.pt, false)
166226
.map_err(mapping_err_to_ax_err)?;
@@ -181,17 +241,18 @@ impl AddrSpace {
181241
size: usize,
182242
flags: MappingFlags,
183243
source: Option<Arc<SharedPages>>,
244+
align: PageSize,
184245
) -> AxResult<Arc<SharedPages>> {
185-
self.validate_region(start, size)?;
246+
self.validate_region(start, size, align)?;
186247

187248
let area = MemoryArea::new(
188249
start,
189250
size,
190251
flags,
191-
Backend::new_shared(size / PAGE_SIZE_4K, source),
252+
Backend::new_shared(start, size, source, align).ok_or(AxError::InvalidInput)?,
192253
);
193254
let result = match area.backend() {
194-
Backend::Shared { pages } => pages.clone(),
255+
Backend::Shared { pages, .. } => pages.clone(),
195256
_ => unreachable!(),
196257
};
197258
self.areas
@@ -203,15 +264,15 @@ impl AddrSpace {
203264

204265
/// Populates the area with physical frames, returning false if the area
205266
/// contains unmapped area.
206-
pub fn populate_area(&mut self, mut start: VirtAddr, size: usize) -> AxResult {
207-
self.validate_region(start, size)?;
267+
pub fn populate_area(&mut self, mut start: VirtAddr, size: usize, align: PageSize) -> AxResult {
268+
self.validate_region(start, size, align)?;
208269
let end = start + size;
209270

210271
while let Some(area) = self.areas.find(start) {
211272
let backend = area.backend();
212-
if let Backend::Alloc { populate, .. } = backend {
213-
if !*populate {
214-
for addr in PageIter4K::new(start, area.end().min(end)).unwrap() {
273+
if let Backend::Alloc { populate, align } = *backend {
274+
if !populate {
275+
for addr in PageIterWrapper::new(start, area.end().min(end), align).unwrap() {
215276
match self.pt.query(addr) {
216277
Ok(_) => {}
217278
// If the page is not mapped, try map it.
@@ -226,7 +287,7 @@ impl AddrSpace {
226287
}
227288
}
228289
start = area.end();
229-
assert!(start.is_aligned_4k());
290+
assert!(start.is_aligned(align));
230291
if start >= end {
231292
break;
232293
}
@@ -245,7 +306,27 @@ impl AddrSpace {
245306
/// Returns an error if the address range is out of the address space or not
246307
/// aligned.
247308
pub fn unmap(&mut self, start: VirtAddr, size: usize) -> AxResult {
248-
self.validate_region(start, size)?;
309+
self.validate_region(start, size, PageSize::Size4K)?;
310+
311+
let end = start + size;
312+
for area in self
313+
.areas
314+
.iter()
315+
.skip_while(move |a| a.end() <= start)
316+
.take_while(move |a| a.start() < end)
317+
{
318+
let area_align = match area.backend() {
319+
Backend::Alloc { align, .. } => *align,
320+
Backend::Linear { align, .. } => *align,
321+
Backend::Shared { pages } => pages.align,
322+
};
323+
324+
let unmap_start = start.max(area.start());
325+
let unmap_size = end.min(area.end()) - unmap_start;
326+
if !unmap_start.is_aligned(area_align) || !is_aligned(unmap_size, area_align.into()) {
327+
return ax_err!(InvalidInput, "address not aligned");
328+
}
329+
}
249330

250331
self.areas
251332
.unmap(start, size, &mut self.pt)
@@ -255,16 +336,6 @@ impl AddrSpace {
255336

256337
/// To remove user area mappings from address space.
257338
pub fn unmap_user_areas(&mut self) -> AxResult {
258-
for area in self.areas.iter() {
259-
assert!(area.start().is_aligned_4k());
260-
assert!(area.size() % PAGE_SIZE_4K == 0);
261-
assert!(area.flags().contains(MappingFlags::USER));
262-
assert!(
263-
self.va_range
264-
.contains_range(VirtAddrRange::from_start_size(area.start(), area.size())),
265-
"MemorySet contains out-of-va-range area"
266-
);
267-
}
268339
self.areas.clear(&mut self.pt).unwrap();
269340
Ok(())
270341
}
@@ -281,18 +352,19 @@ impl AddrSpace {
281352
///
282353
/// # Notes
283354
/// The caller must ensure that the permission of the operation is allowed.
284-
fn process_area_data<F>(&self, start: VirtAddr, size: usize, f: F) -> AxResult
355+
fn process_area_data<F>(&self, start: VirtAddr, size: usize, align: PageSize, f: F) -> AxResult
285356
where
286357
F: FnMut(VirtAddr, usize, usize),
287358
{
288-
Self::process_area_data_with_page_table(&self.pt, &self.va_range, start, size, f)
359+
Self::process_area_data_with_page_table(&self.pt, &self.va_range, start, size, align, f)
289360
}
290361

291362
fn process_area_data_with_page_table<F>(
292363
pt: &PageTable,
293364
va_range: &VirtAddrRange,
294365
start: VirtAddr,
295366
size: usize,
367+
align: PageSize,
296368
mut f: F,
297369
) -> AxResult
298370
where
@@ -303,8 +375,9 @@ impl AddrSpace {
303375
}
304376
let mut cnt = 0;
305377
// If start is aligned to 4K, start_align_down will be equal to start_align_up.
306-
let end_align_up = (start + size).align_up_4k();
307-
for vaddr in PageIter4K::new(start.align_down_4k(), end_align_up)
378+
let end_align_up = (start + size).align_up(align);
379+
let start_addr = start.align_down(align);
380+
for vaddr in PageIterWrapper::new(start_addr, end_align_up, align)
308381
.expect("Failed to create page iterator")
309382
{
310383
let (mut paddr, _, _) = pt.query(vaddr).map_err(|_| AxError::BadAddress)?;
@@ -314,9 +387,9 @@ impl AddrSpace {
314387
if copy_size == 0 {
315388
break;
316389
}
317-
if vaddr == start.align_down_4k() && start.align_offset_4k() != 0 {
318-
let align_offset = start.align_offset_4k();
319-
copy_size = copy_size.min(PAGE_SIZE_4K - align_offset);
390+
if vaddr == start.align_down(align) && start.align_offset(align) != 0 {
391+
let align_offset = start.align_offset(align);
392+
copy_size = copy_size.min(align as usize - align_offset);
320393
paddr += align_offset;
321394
}
322395
f(phys_to_virt(paddr), cnt, copy_size);
@@ -331,8 +404,8 @@ impl AddrSpace {
331404
///
332405
/// * `start` - The start virtual address to read.
333406
/// * `buf` - The buffer to store the data.
334-
pub fn read(&self, start: VirtAddr, buf: &mut [u8]) -> AxResult {
335-
self.process_area_data(start, buf.len(), |src, offset, read_size| unsafe {
407+
pub fn read(&self, start: VirtAddr, align: PageSize, buf: &mut [u8]) -> AxResult {
408+
self.process_area_data(start, buf.len(), align, |src, offset, read_size| unsafe {
336409
core::ptr::copy_nonoverlapping(src.as_ptr(), buf.as_mut_ptr().add(offset), read_size);
337410
})
338411
}
@@ -343,8 +416,8 @@ impl AddrSpace {
343416
///
344417
/// * `start_vaddr` - The start virtual address to write.
345418
/// * `buf` - The buffer to write to the address space.
346-
pub fn write(&self, start: VirtAddr, buf: &[u8]) -> AxResult {
347-
self.process_area_data(start, buf.len(), |dst, offset, write_size| unsafe {
419+
pub fn write(&self, start: VirtAddr, align: PageSize, buf: &[u8]) -> AxResult {
420+
self.process_area_data(start, buf.len(), align, |dst, offset, write_size| unsafe {
348421
core::ptr::copy_nonoverlapping(buf.as_ptr().add(offset), dst.as_mut_ptr(), write_size);
349422
})
350423
}
@@ -353,9 +426,15 @@ impl AddrSpace {
353426
///
354427
/// Returns an error if the address range is out of the address space or not
355428
/// aligned.
356-
pub fn protect(&mut self, start: VirtAddr, size: usize, flags: MappingFlags) -> AxResult {
429+
pub fn protect(
430+
&mut self,
431+
start: VirtAddr,
432+
size: usize,
433+
flags: MappingFlags,
434+
align: PageSize,
435+
) -> AxResult {
357436
// Populate the area first, which also checks the address range for us.
358-
self.populate_area(start, size)?;
437+
self.populate_area(start, size, align)?;
359438

360439
self.areas
361440
.protect(start, size, |_| Some(flags), &mut self.pt)

0 commit comments

Comments
 (0)