diff --git a/kernel/libs/xarray/src/cursor.rs b/kernel/libs/xarray/src/cursor.rs index 523304a14..8c74e9f31 100644 --- a/kernel/libs/xarray/src/cursor.rs +++ b/kernel/libs/xarray/src/cursor.rs @@ -10,7 +10,7 @@ use ostd::{ }; use crate::{ - SLOT_SIZE, XArray, XLockGuard, + BITS_PER_LAYER, SLOT_SIZE, XArray, XLockGuard, entry::NodeEntryRef, mark::{NoneMark, XMark}, node::{Height, XNode}, @@ -226,6 +226,114 @@ impl<'a, P: NonNullPtr + Send + Sync, M> Cursor<'a, P, M> { self.state.move_to(current_node, self.index); self.continue_traverse_to_target(); } + + /// Moves the cursor to the first present item at or after the current index. + /// If found, updates the cursor's index and state, and returns the index. + /// If not found, returns None. + pub fn next_present(&mut self) -> Option { + loop { + self.traverse_to_target(); + + let state = core::mem::take(&mut self.state); + if let CursorState::AtNode { + node, + operation_offset, + } = state + { + if node.is_leaf() { + // Check current slot + if node.entry_with(self.guard, operation_offset).is_some() { + self.state = CursorState::AtNode { + node, + operation_offset, + }; + return Some(self.index); + } + + // Check subsequent slots in this leaf + let mut off = operation_offset + 1; + while off < SLOT_SIZE as u8 { + if node.entry_with(self.guard, off).is_some() { + self.index += (off - operation_offset) as u64; + self.state = CursorState::AtNode { + node, + operation_offset: off, + }; + return Some(self.index); + } + off += 1; + } + + // Move to next leaf + let remaining_in_leaf = SLOT_SIZE as u64 - operation_offset as u64; + self.index = self.index.checked_add(remaining_in_leaf)?; + self.reset(); + } else { + // Should not happen if traverse_to_target works as expected (it stops at leaf or inactive). + // But if it stops at internal node, it means missing child. + // We should treat it as Inactive logic. + self.reset(); + } + } else { + // Inactive. Current index is empty. + // Find next present from root. + let head = self.xa.head.read_with(self.guard)?; + let max_index = head.height().max_index(); + if self.index > max_index { + return None; + } + + if let Some(next_idx) = self.find_next_from_root(self.index) { + self.index = next_idx; + // Loop will continue and traverse_to_target will succeed + } else { + return None; + } + } + } + } + + fn find_next_from_root(&self, target: u64) -> Option { + let head = self.xa.head.read_with(self.guard)?; + self.find_next_in_node(&head, 0, target) + } + + fn find_next_in_node(&self, node: &XNode

, node_base: u64, target: u64) -> Option { + let height = node.height(); + let shift = (*height - 1) * BITS_PER_LAYER as u8; + + let start_offset = if target > node_base { + ((target - node_base) >> shift) as u8 + } else { + 0 + }; + + for off in start_offset..SLOT_SIZE as u8 { + let child_base = node_base + ((off as u64) << shift); + + if let Some(entry) = node.entry_with(self.guard, off) { + if *height == 1 { + // Leaf node. + if child_base >= target { + return Some(child_base); + } + } else { + // Internal node. + let child = entry.left().unwrap(); + let search_start = if off == start_offset { + target + } else { + child_base + }; + + if let Some(found) = self.find_next_in_node(&child, child_base, search_start) { + return Some(found); + } + } + } + } + None + } } impl> Cursor<'_, P, M> { diff --git a/kernel/src/vm/vmo/mod.rs b/kernel/src/vm/vmo/mod.rs index b42ad68e6..96c25f038 100644 --- a/kernel/src/vm/vmo/mod.rs +++ b/kernel/src/vm/vmo/mod.rs @@ -415,26 +415,38 @@ impl Vmo { let page_idx_range = get_page_idx_range(&range); let mut cursor = locked_pages.cursor_mut(page_idx_range.start as u64); - let Some(pager) = &self.pager else { - for _ in page_idx_range { - cursor.remove(); - cursor.next(); + // Ensure the cursor points to the first present item in the range + if cursor.load().is_none() { + if let Some(idx) = cursor.next_present() { + cursor.reset_to(idx); + } else { + return Ok(()); } - return Ok(()); - }; + } let mut removed_page_idx = Vec::new(); - for page_idx in page_idx_range { + while cursor.index() < page_idx_range.end as u64 { if cursor.remove().is_some() { - removed_page_idx.push(page_idx); + removed_page_idx.push(cursor.index() as usize); } + + // Advance to the next index, then skip forward to the next present item if this slot is empty cursor.next(); + if cursor.load().is_none() { + if let Some(idx) = cursor.next_present() { + cursor.reset_to(idx); + } else { + break; + } + } } drop(locked_pages); - for page_idx in removed_page_idx { - pager.decommit_page(page_idx)?; + if let Some(pager) = &self.pager { + for page_idx in removed_page_idx { + pager.decommit_page(page_idx)?; + } } Ok(())