Skip to content

Commit 4ce6e06

Browse files
authored
fix(fs): stabilize epoll and namespace lifecycles (#2165)
* fix(fs): stabilize epoll and namespace lifecycles Eliminate the epoll wait lock inversion by removing the unreachable shutdown state and keeping waiter registration entirely within the ready-state synchronization domain. Refresh proc task entries by PID object identity and thread-group ownership so recycled TIDs cannot resolve stale cached directories. Resolve proc namespace magic links to namespace-backed files and preserve their mount projection through anonymous, non-cached dentries, allowing bind mounts to outlive the source task without leaking wrapper-cache entries. Add concurrent epoll ctl/wait coverage and namespace bind-lifetime regression coverage, including checks that ordinary proc fd magic-link projection remains unchanged. Validation: - make fmt - make kernel - DragonOS proc PID/TID reuse, UTS namespace, epoll, and mount suites - CubeSandbox container create/exec/destroy, 5/5 on the final kernel Signed-off-by: longjin <longjin@dragonos.org> * fix(procfs): preserve namespace fd identities Carry a stable namespace dentry name with mount-projected magic-link targets so open namespace descriptors render type:[inode] instead of falling back to anon_inode. Keep bind-mounted namespace descriptors on the ordinary mount path and expose anonymous namespace roots in mountinfo without a leading slash, matching Linux d_path and nsfs semantics. Extend the namespace bind regression test to cover the original fd identity, the bind-mounted fd path, and the mountinfo root. Signed-off-by: longjin <longjin@dragonos.org> --------- Signed-off-by: longjin <longjin@dragonos.org>
1 parent 4a99bd4 commit 4ce6e06

10 files changed

Lines changed: 613 additions & 81 deletions

File tree

kernel/src/filesystem/epoll/event_poll.rs

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ use crate::{
1515
Duration, Instant, PosixTimeSpec,
1616
},
1717
};
18-
use core::{
19-
fmt::Debug,
20-
sync::atomic::{AtomicBool, Ordering},
21-
};
18+
use core::fmt::Debug;
2219

2320
use alloc::{
2421
collections::{BTreeSet, LinkedList},
@@ -77,8 +74,6 @@ pub struct EventPoll {
7774
ready_state: Arc<SpinLock<ReadyState>>,
7875
/// 监听本 epollfd 的 epitems(用于支持 epoll 嵌套:epollfd 被加入另一个 epoll)
7976
pub(super) poll_epitems: Arc<LockedEPItemLinkedList>,
80-
/// 是否已经关闭
81-
shutdown: AtomicBool,
8277
self_ref: Option<Weak<Mutex<EventPoll>>>,
8378
}
8479

@@ -96,20 +91,12 @@ impl EventPoll {
9691
epoll_wq: WaitQueue::default(),
9792
})),
9893
poll_epitems: Arc::new(LockedEPItemLinkedList::default()),
99-
shutdown: AtomicBool::new(false),
10094
self_ref: None,
10195
}
10296
}
10397

10498
/// 关闭epoll时,执行的逻辑
10599
pub(super) fn close(&mut self) -> Result<(), SystemError> {
106-
// 唤醒epoll上面等待的所有进程
107-
self.shutdown.store(true, Ordering::SeqCst);
108-
{
109-
let rs = self.ready_state.lock_irqsave();
110-
rs.epoll_wq.wakeup_all(None);
111-
}
112-
113100
let fds: Vec<i32> = self.ep_items.keys().cloned().collect::<Vec<_>>();
114101
// 清理红黑树里面的epitems
115102
for fd in fds {
@@ -455,11 +442,6 @@ impl EventPoll {
455442
continue;
456443
}
457444

458-
if epoll.0.lock().shutdown.load(Ordering::SeqCst) {
459-
// 如果已经关闭
460-
return Err(SystemError::EBADF);
461-
}
462-
463445
// 如果超时
464446
if timeout {
465447
return Ok(0);
@@ -524,9 +506,7 @@ impl EventPoll {
524506
{
525507
// 注册前再次检查,避免错过事件(仅需 SpinLock)
526508
let rs = rs_arc.lock_irqsave();
527-
if Self::ready_state_has_events(&rs)
528-
|| epoll.0.lock().shutdown.load(Ordering::SeqCst)
529-
{
509+
if Self::ready_state_has_events(&rs) {
530510
available = true;
531511
// 不注册,直接继续
532512
} else {
@@ -555,10 +535,6 @@ impl EventPoll {
555535
let rs = rs_arc.lock_irqsave();
556536
rs.epoll_wq.remove_waker(&waker);
557537
available = Self::ready_state_has_events(&rs);
558-
if epoll.0.lock().shutdown.load(Ordering::SeqCst) {
559-
// epoll 被关闭,直接退出
560-
return Err(SystemError::EINVAL);
561-
}
562538
}
563539

564540
if let Some(timer) = timer {

kernel/src/filesystem/procfs/pid/ns.rs

Lines changed: 99 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ use crate::{
77
filesystem::{
88
procfs::{
99
pid::ProcPidTarget,
10-
template::{Builder, DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps},
10+
template::{
11+
Builder, DirOps, FileOps, ProcDir, ProcDirBuilder, ProcFileBuilder, ProcSymBuilder,
12+
SymOps,
13+
},
1114
thread_self::NsFileType,
1215
},
1316
vfs::{
1417
file::{FilePrivateData, NamespaceFilePrivateData},
15-
IndexNode, InodeId, InodeMode,
18+
utils::DName,
19+
FileSystem, IndexNode, InodeId, InodeMode, SpecialNodeData,
1620
},
1721
},
1822
process::namespace::{nsproxy::NamespaceId, NamespaceOps},
@@ -25,42 +29,27 @@ use alloc::{
2529
use core::convert::TryFrom;
2630
use system_error::SystemError;
2731

28-
/// 获取指定进程的命名空间 ID
29-
fn get_ns_ino(target: &ProcPidTarget, ns_type: NsFileType) -> Result<usize, SystemError> {
30-
let pcb = target.task().ok_or(SystemError::ESRCH)?;
31-
let nsproxy = pcb.nsproxy();
32-
33-
let ino: NamespaceId = match ns_type {
34-
NsFileType::Ipc => nsproxy.ipc_ns.ns_common().nsid,
35-
NsFileType::Uts => nsproxy.uts_ns.ns_common().nsid,
36-
NsFileType::Mnt => nsproxy.mnt_ns.ns_common().nsid,
37-
NsFileType::Net => nsproxy.net_ns.ns_common().nsid,
38-
NsFileType::Pid => pcb.active_pid_ns().ns_common().nsid,
39-
NsFileType::PidForChildren => nsproxy.pid_ns_for_children.ns_common().nsid,
40-
NsFileType::Time | NsFileType::TimeForChildren => {
41-
// Time namespace 尚未实现
42-
NamespaceId::new(0)
43-
}
44-
NsFileType::User => pcb.cred().user_ns.ns_common().nsid,
45-
NsFileType::Cgroup => nsproxy.cgroup_ns.ns_common().nsid,
46-
};
47-
48-
Ok(ino.data())
32+
#[derive(Debug)]
33+
struct NamespaceSnapshot {
34+
data: NamespaceFilePrivateData,
35+
nsid: NamespaceId,
4936
}
5037

51-
fn namespace_private_data(
38+
fn namespace_snapshot(
5239
target: &ProcPidTarget,
5340
ns_type: NsFileType,
54-
) -> Result<NamespaceFilePrivateData, SystemError> {
41+
) -> Result<NamespaceSnapshot, SystemError> {
5542
let pcb = target.task().ok_or(SystemError::ESRCH)?;
5643
let nsproxy = pcb.nsproxy();
5744

58-
let ns_data = match ns_type {
45+
let data = match ns_type {
5946
NsFileType::Ipc => NamespaceFilePrivateData::Ipc(nsproxy.ipc_ns.clone()),
6047
NsFileType::Uts => NamespaceFilePrivateData::Uts(nsproxy.uts_ns.clone()),
6148
NsFileType::Mnt => NamespaceFilePrivateData::Mnt(nsproxy.mnt_ns.clone()),
6249
NsFileType::Net => NamespaceFilePrivateData::Net(nsproxy.net_ns.clone()),
63-
NsFileType::Pid => NamespaceFilePrivateData::Pid(pcb.active_pid_ns()),
50+
NsFileType::Pid => {
51+
NamespaceFilePrivateData::Pid(pcb.try_active_pid_ns().ok_or(SystemError::ESRCH)?)
52+
}
6453
NsFileType::PidForChildren => {
6554
NamespaceFilePrivateData::PidForChildren(nsproxy.pid_ns_for_children.clone())
6655
}
@@ -71,7 +60,19 @@ fn namespace_private_data(
7160
NsFileType::Cgroup => NamespaceFilePrivateData::Cgroup(nsproxy.cgroup_ns.clone()),
7261
};
7362

74-
Ok(ns_data)
63+
let nsid = match &data {
64+
NamespaceFilePrivateData::Ipc(ns) => ns.ns_common().nsid,
65+
NamespaceFilePrivateData::Uts(ns) => ns.ns_common().nsid,
66+
NamespaceFilePrivateData::Mnt(ns) => ns.ns_common().nsid,
67+
NamespaceFilePrivateData::Net(ns) => ns.ns_common().nsid,
68+
NamespaceFilePrivateData::Pid(ns) | NamespaceFilePrivateData::PidForChildren(ns) => {
69+
ns.ns_common().nsid
70+
}
71+
NamespaceFilePrivateData::User(ns) => ns.ns_common().nsid,
72+
NamespaceFilePrivateData::Cgroup(ns) => ns.ns_common().nsid,
73+
};
74+
75+
Ok(NamespaceSnapshot { data, nsid })
7576
}
7677

7778
/// /proc/[pid]/ns 目录的 DirOps 实现
@@ -110,7 +111,12 @@ impl DirOps for NsDirOps {
110111
}
111112

112113
// 创建命名空间符号链接
113-
let inode = NsSymOps::new_inode(self.target.clone(), ns_type, dir.self_ref_weak().clone());
114+
let inode = NsSymOps::new_inode(
115+
self.target.clone(),
116+
ns_type,
117+
Arc::downgrade(&dir.fs()),
118+
dir.self_ref_weak().clone(),
119+
);
114120
cached_children.insert(name.to_string(), inode.clone());
115121
Ok(inode)
116122
}
@@ -125,7 +131,12 @@ impl DirOps for NsDirOps {
125131
for name in NsFileType::ALL_NAMES {
126132
if let Ok(ns_type) = NsFileType::try_from(name) {
127133
cached_children.entry(name.to_string()).or_insert_with(|| {
128-
NsSymOps::new_inode(self.target.clone(), ns_type, dir.self_ref_weak().clone())
134+
NsSymOps::new_inode(
135+
self.target.clone(),
136+
ns_type,
137+
Arc::downgrade(&dir.fs()),
138+
dir.self_ref_weak().clone(),
139+
)
129140
});
130141
}
131142
}
@@ -137,43 +148,90 @@ impl DirOps for NsDirOps {
137148
pub struct NsSymOps {
138149
target: ProcPidTarget,
139150
ns_type: NsFileType,
151+
fs: Weak<dyn FileSystem>,
140152
}
141153

142154
impl NsSymOps {
143155
pub fn new_inode(
144156
target: ProcPidTarget,
145157
ns_type: NsFileType,
158+
fs: Weak<dyn FileSystem>,
146159
parent: Weak<dyn IndexNode>,
147160
) -> Arc<dyn IndexNode> {
148-
ProcSymBuilder::new(Self { target, ns_type }, InodeMode::S_IRWXUGO)
149-
.parent(parent)
150-
.build()
151-
.unwrap()
161+
ProcSymBuilder::new(
162+
Self {
163+
target,
164+
ns_type,
165+
fs,
166+
},
167+
InodeMode::S_IRWXUGO,
168+
)
169+
.parent(parent)
170+
.build()
171+
.unwrap()
172+
}
173+
}
174+
175+
#[derive(Debug)]
176+
struct NamespaceFileOps {
177+
snapshot: NamespaceSnapshot,
178+
}
179+
180+
impl FileOps for NamespaceFileOps {
181+
fn read_at(
182+
&self,
183+
_offset: usize,
184+
_len: usize,
185+
_buf: &mut [u8],
186+
_data: MutexGuard<FilePrivateData>,
187+
) -> Result<usize, SystemError> {
188+
Err(SystemError::EINVAL)
189+
}
190+
191+
fn open(&self, data: &mut MutexGuard<FilePrivateData>) -> Result<(), SystemError> {
192+
**data = FilePrivateData::Namespace(self.snapshot.data.clone());
193+
Ok(())
194+
}
195+
196+
fn dynamic_inode_id(&self) -> Option<InodeId> {
197+
Some(InodeId::new(self.snapshot.nsid.data()))
152198
}
153199
}
154200

155201
impl SymOps for NsSymOps {
156202
fn read_link(&self, buf: &mut [u8]) -> Result<usize, SystemError> {
157-
let ino = get_ns_ino(&self.target, self.ns_type)?;
203+
let ino = namespace_snapshot(&self.target, self.ns_type)?.nsid.data();
158204
let target = format!("{}:[{}]", self.ns_type.name(), ino);
159205
let len = target.len().min(buf.len());
160206
buf[..len].copy_from_slice(&target.as_bytes()[..len]);
161207
Ok(len)
162208
}
163209

164-
fn is_self_reference(&self) -> bool {
165-
// 命名空间符号链接是自引用的魔法链接
166-
true
210+
fn special_node(&self) -> Option<SpecialNodeData> {
211+
let snapshot = namespace_snapshot(&self.target, self.ns_type).ok()?;
212+
let dname = DName::from(format!(
213+
"{}:[{}]",
214+
self.ns_type.name(),
215+
snapshot.nsid.data()
216+
));
217+
let inode = ProcFileBuilder::new(NamespaceFileOps { snapshot }, InodeMode::S_IRUGO)
218+
.fs(self.fs.clone())
219+
.build()
220+
.ok()?;
221+
Some(SpecialNodeData::MountProjectedReference {
222+
target: inode,
223+
dname,
224+
})
167225
}
168226

169227
fn dynamic_inode_id(&self) -> Option<InodeId> {
170-
get_ns_ino(&self.target, self.ns_type)
228+
namespace_snapshot(&self.target, self.ns_type)
171229
.ok()
172-
.map(InodeId::new)
230+
.map(|snapshot| InodeId::new(snapshot.nsid.data()))
173231
}
174232

175233
fn open(&self, data: &mut MutexGuard<FilePrivateData>) -> Result<(), SystemError> {
176-
**data = FilePrivateData::Namespace(namespace_private_data(&self.target, self.ns_type)?);
234+
**data = FilePrivateData::Namespace(namespace_snapshot(&self.target, self.ns_type)?.data);
177235
Ok(())
178236
}
179237
}

0 commit comments

Comments
 (0)