From 7ecf5c56d00a79a43ae68faec75e1db66b80e2b0 Mon Sep 17 00:00:00 2001 From: Shi Lei Date: Thu, 25 Jun 2026 00:33:21 +0000 Subject: [PATCH 1/4] fix(starry-kernel): annotate cloned aspace lock nesting --- os/StarryOS/kernel/src/mm/aspace/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/os/StarryOS/kernel/src/mm/aspace/mod.rs b/os/StarryOS/kernel/src/mm/aspace/mod.rs index 6d5fcc380e..212466e842 100644 --- a/os/StarryOS/kernel/src/mm/aspace/mod.rs +++ b/os/StarryOS/kernel/src/mm/aspace/mod.rs @@ -28,6 +28,7 @@ pub use self::{ }; type MovedPage = (VirtAddr, VirtAddr, PhysAddr, MappingFlags, PageSize, bool); +const CLONED_ADDR_SPACE_LOCK_SUBCLASS: u32 = 1; fn rollback_moved_pages(cursor: &mut PageTableCursor, moved_pages: &[MovedPage]) { for &(src_va, dst_va, paddr, flags, page_size, dst_newly_mapped) in moved_pages.iter().rev() { @@ -582,7 +583,10 @@ impl AddrSpace { let new_aspace = Arc::new(Mutex::new(Self::new_empty(self.base(), self.size())?)); let new_aspace_clone = new_aspace.clone(); - let mut guard = new_aspace.lock_nested(1); + // The caller holds the source AddrSpace lock while this fresh AddrSpace + // is being populated. The new lock is not published yet, so this is a + // structured source -> cloned-address-space nesting. + let mut guard = new_aspace.lock_nested(CLONED_ADDR_SPACE_LOCK_SUBCLASS); let child_rss = guard.rss() as *const MemoryAccounting; let child_acct = unsafe { &*child_rss }; let parent_acct = &self.rss; From 37220a78338f10f301a86bf619475e03bcf80ef0 Mon Sep 17 00:00:00 2001 From: Shi Lei Date: Thu, 25 Jun 2026 01:02:21 +0000 Subject: [PATCH 2/4] fix(starry-kernel): annotate nested futex wait queues --- os/StarryOS/kernel/src/task/futex.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/os/StarryOS/kernel/src/task/futex.rs b/os/StarryOS/kernel/src/task/futex.rs index f39919dcae..a46d283fb2 100644 --- a/os/StarryOS/kernel/src/task/futex.rs +++ b/os/StarryOS/kernel/src/task/futex.rs @@ -17,7 +17,7 @@ use core::{ use ax_errno::AxResult; use ax_memory_addr::VirtAddr; -use ax_sync::Mutex; +use ax_sync::{LockdepMutexExt, Mutex}; use ax_task::{ current, future::{self, block_on, interruptible}, @@ -29,6 +29,8 @@ use crate::{ task::{AsThread, ProcessData}, }; +const NESTED_WAIT_QUEUE_LOCK_SUBCLASS: u32 = 1; + /// Wait queue used by futex. #[derive(Default)] pub struct WaitQueue { @@ -240,7 +242,7 @@ impl WaitQueue { match core::ptr::from_ref(self).cmp(&core::ptr::from_ref(target)) { Ordering::Less => { let mut src = self.inner.lock(); - let mut dst = target.inner.lock(); + let mut dst = target.inner.lock_nested(NESTED_WAIT_QUEUE_LOCK_SUBCLASS); let wake_second = condition.take().expect("condition used once")()?; Self::wake_locked(&mut src.queue, wake_count, u32::MAX, &mut wakers); if wake_second { @@ -249,7 +251,7 @@ impl WaitQueue { } Ordering::Greater => { let mut dst = target.inner.lock(); - let mut src = self.inner.lock(); + let mut src = self.inner.lock_nested(NESTED_WAIT_QUEUE_LOCK_SUBCLASS); let wake_second = condition.take().expect("condition used once")()?; Self::wake_locked(&mut src.queue, wake_count, u32::MAX, &mut wakers); if wake_second { @@ -330,7 +332,7 @@ impl WaitQueue { let count = match core::ptr::from_ref(self).cmp(&core::ptr::from_ref(target)) { Ordering::Less => { let mut src = self.inner.lock(); - let mut dst = target.inner.lock(); + let mut dst = target.inner.lock_nested(NESTED_WAIT_QUEUE_LOCK_SUBCLASS); if !condition.take().expect("condition used once")()? { return Ok(None); } @@ -346,7 +348,7 @@ impl WaitQueue { } Ordering::Greater => { let mut dst = target.inner.lock(); - let mut src = self.inner.lock(); + let mut src = self.inner.lock_nested(NESTED_WAIT_QUEUE_LOCK_SUBCLASS); if !condition.take().expect("condition used once")()? { return Ok(None); } From 97b1e0bb4a3b19bf5363ec5274b7e1fcd51a1ad5 Mon Sep 17 00:00:00 2001 From: Shi Lei Date: Thu, 25 Jun 2026 02:47:31 +0000 Subject: [PATCH 3/4] fix(ax-fs-ng): avoid cached read lock inversion --- os/arceos/modules/axfs-ng/src/file/cache.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/os/arceos/modules/axfs-ng/src/file/cache.rs b/os/arceos/modules/axfs-ng/src/file/cache.rs index 1cd4ab7f79..dc7f352a9f 100644 --- a/os/arceos/modules/axfs-ng/src/file/cache.rs +++ b/os/arceos/modules/axfs-ng/src/file/cache.rs @@ -731,7 +731,6 @@ impl CachedFile { /// Reads data from the file at `offset` into `dst`. pub fn read_at(&self, mut dst: impl Write + IoBufMut, offset: u64) -> VfsResult { - let _io = self.shared.io_lock.lock(); let len = self.shared.len(); let end = offset.saturating_add(dst.remaining_mut() as u64).min(len); if end <= offset { @@ -749,12 +748,16 @@ impl CachedFile { let chunk_len = (end - page_start).min(PAGE_SIZE as u64) as usize - page_offset; { + let _io = self.shared.io_lock.lock(); let mut guard = self.shared.page_cache.lock(); let page = self.page_or_insert(file, &mut guard, pn, true)?.0; scratch.data()[..chunk_len] .copy_from_slice(&page.data()[page_offset..page_offset + chunk_len]); } + // `dst` may point at user memory. Copy after releasing cached-file + // locks so a user page fault can take AddrSpace without creating a + // cached-I/O -> AddrSpace lock order. dst.write_all(&scratch.data()[..chunk_len])?; read += chunk_len; current += chunk_len as u64; From efe9fd63017e342b5219a9eff2c14dc4c82d516e Mon Sep 17 00:00:00 2001 From: Shi Lei Date: Thu, 25 Jun 2026 03:44:14 +0000 Subject: [PATCH 4/4] fix(ax-fs-ng): drop pending lock before repoll --- .../axfs-ng/src/block_runtime/device.rs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/os/arceos/modules/axfs-ng/src/block_runtime/device.rs b/os/arceos/modules/axfs-ng/src/block_runtime/device.rs index 2a96494838..b6b28c5647 100644 --- a/os/arceos/modules/axfs-ng/src/block_runtime/device.rs +++ b/os/arceos/modules/axfs-ng/src/block_runtime/device.rs @@ -707,17 +707,20 @@ impl BlockDeviceHandle { ) -> Option { loop { match result { - Ok(RequestStatus::Pending) => match self.pending.lock().finish_pending_poll(key) { - PollProgress::Pending | PollProgress::Complete => return Some(false), - PollProgress::Repoll => { - let submitted = self - .pending - .lock() - .request(key) - .map(|request| request.submitted_request())?; - result = self.poll_request(submitted.queue_id, submitted.request_id); + Ok(RequestStatus::Pending) => { + let progress = self.pending.lock().finish_pending_poll(key); + match progress { + PollProgress::Pending | PollProgress::Complete => return Some(false), + PollProgress::Repoll => { + let submitted = self + .pending + .lock() + .request(key) + .map(|request| request.submitted_request())?; + result = self.poll_request(submitted.queue_id, submitted.request_id); + } } - }, + } Ok(RequestStatus::Complete) => { let task_id = self.pending.lock().complete(key, Ok(())); self.wake_completed_request(key, task_id);