Skip to content

Commit 1245799

Browse files
authored
fix(tty): Enhance TTY driver and device management (#1462)
* fix(tty): Enhance TTY driver and device management - Updated the TTY driver to handle both master and slave types more effectively during close operations, ensuring proper cleanup of device entries in /dev/pts. - Improved the handling of controlling TTY detachment for processes, adding support for the TIOCNOTTY command. - Refactored the PTY device initialization to ensure correct metadata settings and device registration. - Added a symlink for /dev/ptmx to point to the internal devpts node, preventing ENOENT errors during early access. These changes enhance the robustness and compatibility of the TTY subsystem with Linux semantics. Signed-off-by: longjin <[email protected]> * refactor(tty): Improve PTY device management and cleanup logic - Enhanced the `PtyDevPtsLink` structure to manage the lifecycle of PTY devices more effectively, including precise unlinking of directory entries and freeing of indices upon closure. - Refactored the close operation in the TTY driver to utilize the new management logic, ensuring proper cleanup of master and slave PTY devices. - Removed redundant code related to device entry removal, streamlining the cleanup process and aligning with Linux semantics. Signed-off-by: longjin <[email protected]> --------- Signed-off-by: longjin <[email protected]>
1 parent 23ab2f3 commit 1245799

File tree

18 files changed

+429
-126
lines changed

18 files changed

+429
-126
lines changed

kernel/src/arch/x86_64/filesystem/stat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl TryFrom<KStat> for PosixStat {
5555
tmp.st_uid = kstat.uid;
5656
tmp.st_gid = kstat.gid;
5757

58-
tmp.st_rdev = kstat.rdev.data() as usize;
58+
tmp.st_rdev = kstat.rdev.new_encode_dev() as usize;
5959
tmp.st_size = kstat.size as isize;
6060

6161
tmp.st_atime = kstat.atime.tv_sec as usize;

kernel/src/driver/tty/pty/mod.rs

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
use alloc::{
2-
string::{String, ToString},
3-
sync::Arc,
4-
};
1+
use alloc::sync::Arc;
52
use system_error::SystemError;
63
use unified_init::macros::unified_init;
74

85
use crate::{
9-
driver::base::device::{
10-
device_number::{DeviceNumber, Major},
11-
device_register, IdTable,
12-
},
13-
filesystem::devfs::devfs_register,
6+
driver::base::device::device_number::Major,
147
init::initcall::INITCALL_DEVICE,
158
libs::lazy_init::Lazy,
169
mm::VirtAddr,
@@ -22,7 +15,6 @@ use self::unix98pty::{Unix98PtyDriverInner, NR_UNIX98_PTY_MAX};
2215
use super::{
2316
termios::{ControlMode, InputMode, LocalMode, OutputMode, TTY_STD_TERMIOS},
2417
tty_core::{TtyCore, TtyCoreData, TtyFlag, TtyPacketStatus},
25-
tty_device::{TtyDevice, TtyType},
2618
tty_driver::{TtyDriver, TtyDriverManager, TtyDriverSubType, TtyDriverType, TTY_DRIVERS},
2719
tty_port::{DefaultTtyPort, TtyPort},
2820
};
@@ -165,20 +157,6 @@ impl PtyCommon {
165157
ptm_driver.set_other_pty_driver(Arc::downgrade(&pts_driver));
166158
pts_driver.set_other_pty_driver(Arc::downgrade(&ptm_driver));
167159

168-
let idt = IdTable::new(
169-
String::from("ptmx"),
170-
Some(DeviceNumber::new(Major::TTYAUX_MAJOR, 2)),
171-
);
172-
let ptmx_dev = TtyDevice::new(
173-
"ptmx".to_string(),
174-
idt.clone(),
175-
TtyType::Pty(super::tty_device::PtyType::Ptm),
176-
);
177-
178-
ptmx_dev.inner_write().metadata_mut().raw_dev = idt.device_number();
179-
device_register(ptmx_dev.clone())?;
180-
devfs_register("ptmx", ptmx_dev)?;
181-
182160
TTY_DRIVERS.lock().push(ptm_driver);
183161
TTY_DRIVERS.lock().push(pts_driver);
184162

kernel/src/driver/tty/pty/unix98pty.rs

Lines changed: 128 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,110 @@
1-
use alloc::{string::ToString, sync::Arc};
1+
use alloc::{
2+
string::ToString,
3+
sync::{Arc, Weak},
4+
};
5+
use core::sync::atomic::{AtomicBool, Ordering};
26
use system_error::SystemError;
37

48
use crate::{
59
driver::tty::{
610
termios::{ControlCharIndex, ControlMode, InputMode, LocalMode, Termios},
711
tty_core::{TtyCore, TtyCoreData, TtyFlag, TtyIoctlCmd, TtyPacketStatus},
8-
tty_device::TtyFilePrivateData,
12+
tty_device::{TtyDevice, TtyFilePrivateData},
913
tty_driver::{TtyDriver, TtyDriverPrivateData, TtyDriverSubType, TtyOperation},
1014
},
1115
filesystem::{
1216
devpts::DevPtsFs,
1317
epoll::{event_poll::EventPoll, EPollEventType},
14-
vfs::{
15-
file::FileFlags, FilePrivateData, FileType, InodeMode, MountFS,
16-
VFS_MAX_FOLLOW_SYMLINK_TIMES,
17-
},
18+
vfs::{file::FileFlags, FilePrivateData, FileSystem, FileType, IndexNode, InodeMode},
1819
},
19-
libs::spinlock::SpinLockGuard,
20+
libs::{casting::DowncastArc, spinlock::SpinLockGuard},
2021
mm::VirtAddr,
21-
process::ProcessManager,
2222
syscall::user_access::UserBufferWriter,
2323
};
2424

2525
use super::{ptm_driver, pts_driver, PtyCommon};
2626

2727
pub const NR_UNIX98_PTY_MAX: u32 = 128;
2828

29+
#[derive(Debug)]
30+
struct PtyDevPtsLink {
31+
/// devpts 挂载点根目录(/dev/pts 的 inode),用于精确 unlink 目录项
32+
pts_root: Weak<dyn IndexNode>,
33+
/// devpts 文件系统本体,用于精确回收索引(避免再去 downcast/全局路径查找)
34+
devpts: Weak<DevPtsFs>,
35+
index: usize,
36+
/// master 侧(ptmx)最后一个 fd 已关闭
37+
master_closed: AtomicBool,
38+
/// slave 侧(/dev/pts/N)最后一个 fd 已关闭
39+
slave_closed: AtomicBool,
40+
/// 目录项是否已经 unlink(通常在 master close 时执行)
41+
unlinked: AtomicBool,
42+
/// 索引是否已经归还(仅在 master+slave 都关闭后才允许归还)
43+
index_freed: AtomicBool,
44+
}
45+
46+
impl crate::driver::tty::tty_driver::TtyCorePrivateField for PtyDevPtsLink {
47+
fn as_any(&self) -> &dyn core::any::Any {
48+
self
49+
}
50+
}
51+
52+
impl PtyDevPtsLink {
53+
fn new(pts_root: Weak<dyn IndexNode>, devpts: Weak<DevPtsFs>, index: usize) -> Self {
54+
Self {
55+
pts_root,
56+
devpts,
57+
index,
58+
master_closed: AtomicBool::new(false),
59+
slave_closed: AtomicBool::new(false),
60+
unlinked: AtomicBool::new(false),
61+
index_freed: AtomicBool::new(false),
62+
}
63+
}
64+
65+
fn on_close(&self, subtype: TtyDriverSubType) {
66+
match subtype {
67+
TtyDriverSubType::PtyMaster => {
68+
self.master_closed.store(true, Ordering::SeqCst);
69+
// Linux 语义:master 关闭后,/dev/pts/N 目录项应从 devpts 中消失;
70+
// 但索引不能立即复用(slave 可能仍持有打开的 fd),因此 unlink 与 free_index 分离。
71+
self.try_unlink_once();
72+
}
73+
TtyDriverSubType::PtySlave => {
74+
self.slave_closed.store(true, Ordering::SeqCst);
75+
}
76+
_ => {}
77+
}
78+
79+
self.try_free_index_when_fully_closed();
80+
}
81+
82+
fn try_unlink_once(&self) {
83+
if self.unlinked.swap(true, Ordering::SeqCst) {
84+
return;
85+
}
86+
if let Some(root) = self.pts_root.upgrade() {
87+
let _ = root.unlink(&self.index.to_string());
88+
}
89+
}
90+
91+
fn try_free_index_when_fully_closed(&self) {
92+
if !(self.master_closed.load(Ordering::SeqCst) && self.slave_closed.load(Ordering::SeqCst))
93+
{
94+
return;
95+
}
96+
if self.index_freed.swap(true, Ordering::SeqCst) {
97+
return;
98+
}
99+
100+
// 兜底:如果 master 未触发 unlink(异常路径),在最终回收时再尝试一次。
101+
self.try_unlink_once();
102+
if let Some(devpts) = self.devpts.upgrade() {
103+
devpts.free_index(self.index);
104+
}
105+
}
106+
}
107+
29108
#[derive(Debug)]
30109
pub struct Unix98PtyDriverInner;
31110

@@ -220,12 +299,15 @@ impl TtyOperation for Unix98PtyDriverInner {
220299

221300
fn close(&self, tty: Arc<TtyCore>) -> Result<(), SystemError> {
222301
let driver = tty.core().driver();
223-
let root_inode = ProcessManager::current_mntns().root_inode();
224-
if tty.core().driver().tty_driver_sub_type() == TtyDriverSubType::PtySlave {
302+
// 通过 hook 精确管理 devpts 目录项与索引生命周期
303+
if let Some(hook_arc) = tty.private_fields() {
304+
if let Some(hook) = hook_arc.as_any().downcast_ref::<PtyDevPtsLink>() {
305+
hook.on_close(driver.tty_driver_sub_type());
306+
}
307+
}
308+
309+
if driver.tty_driver_sub_type() == TtyDriverSubType::PtySlave {
225310
driver.ttys().remove(&tty.core().index());
226-
let pts_root_inode =
227-
root_inode.lookup_follow_symlink("/dev/pts", VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
228-
let _ = pts_root_inode.unlink(&tty.core().index().to_string());
229311
if let Some(link) = tty.core().link() {
230312
let link_core = link.core();
231313
// set OTHER_CLOSED flag to tell master side that the slave side is closed
@@ -237,6 +319,17 @@ impl TtyOperation for Unix98PtyDriverInner {
237319
let epitems = link_core.epitems();
238320
let _ = EventPoll::wakeup_epoll(epitems, EPollEventType::EPOLLHUP);
239321
}
322+
} else if driver.tty_driver_sub_type() == TtyDriverSubType::PtyMaster {
323+
// master 侧最后关闭:从 driver 表移除自身(避免泄漏);devpts 的释放由 hook 统一处理
324+
driver.ttys().remove(&tty.core().index());
325+
if let Some(link) = tty.core().link() {
326+
let link_core = link.core();
327+
link_core.flags_write().insert(TtyFlag::OTHER_CLOSED);
328+
link_core.read_wq().wakeup_all();
329+
link_core.write_wq().wakeup_all();
330+
let epitems = link_core.epitems();
331+
let _ = EventPoll::wakeup_epoll(epitems, EPollEventType::EPOLLHUP);
332+
}
240333
}
241334

242335
Ok(())
@@ -262,6 +355,7 @@ impl TtyOperation for Unix98PtyDriverInner {
262355
}
263356

264357
pub fn ptmx_open(
358+
this: &TtyDevice,
265359
mut data: SpinLockGuard<FilePrivateData>,
266360
flags: &FileFlags,
267361
) -> Result<(), SystemError> {
@@ -271,17 +365,14 @@ pub fn ptmx_open(
271365
tty.core().add_count();
272366
return Ok(());
273367
}
274-
let root_inode = ProcessManager::current_mntns().root_inode();
275-
let pts_root_inode =
276-
root_inode.lookup_follow_symlink("/dev/pts", VFS_MAX_FOLLOW_SYMLINK_TIMES)?;
277-
278-
let fs = pts_root_inode
279-
.fs()
280-
.as_any_ref()
281-
.downcast_ref::<MountFS>()
282-
.unwrap()
283-
.inner_filesystem();
284-
let fsinfo = fs.as_any_ref().downcast_ref::<DevPtsFs>().unwrap();
368+
// 根据当前节点所属的文件系统决定 devpts 根
369+
let (pts_root_inode, fsinfo) =
370+
if let Some(devpts) = this.fs().clone().downcast_arc::<DevPtsFs>() {
371+
let root_inode = devpts.root_inode();
372+
(root_inode, devpts)
373+
} else {
374+
return Err(SystemError::ENODEV);
375+
};
285376

286377
let index = fsinfo.alloc_index()?;
287378

@@ -302,6 +393,19 @@ pub fn ptmx_open(
302393
InodeMode::from_bits_truncate(0x666),
303394
)?;
304395

396+
// 在 master/slave 两端记录 devpts 根目录与 fs,用于精确清理:
397+
// - master close: unlink /dev/pts/N
398+
// - master+slave 都 close: free_index(N)
399+
let hook = Arc::new(PtyDevPtsLink::new(
400+
Arc::downgrade(&pts_root_inode),
401+
Arc::downgrade(&fsinfo),
402+
index,
403+
));
404+
tty.set_private_fields(hook.clone());
405+
if let Some(slave) = tty.core().link() {
406+
slave.set_private_fields(hook);
407+
}
408+
305409
ptm_driver().driver_funcs().open(core)?;
306410

307411
Ok(())

kernel/src/driver/tty/tty_core.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -652,9 +652,10 @@ impl TtyOperation for TtyCore {
652652
self.core().dec_count();
653653
let cnt = self.core().count();
654654
// TODO: fix pty slave close issue
655-
let is_pty_slave_last =
656-
cnt == 1 && tty.core().driver().tty_driver_sub_type() == TtyDriverSubType::PtySlave;
657-
if !self.core().count_valid() || is_pty_slave_last {
655+
let subtype = tty.core().driver().tty_driver_sub_type();
656+
let is_pty_slave_last = cnt == 1 && subtype == TtyDriverSubType::PtySlave;
657+
let is_pty_master_last = cnt == 1 && subtype == TtyDriverSubType::PtyMaster;
658+
if !self.core().count_valid() || is_pty_slave_last || is_pty_master_last {
658659
// log::debug!(
659660
// "TtyCore close: ref count: {}, tty: {}",
660661
// cnt,

kernel/src/driver/tty/tty_device.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,16 @@ impl InnerTtyDevice {
7979
inode: None,
8080
driver: None,
8181
can_match: false,
82-
metadata: Metadata::new(FileType::CharDevice, InodeMode::from_bits_truncate(0o755)),
82+
metadata: {
83+
let mut md =
84+
Metadata::new(FileType::CharDevice, InodeMode::from_bits_truncate(0o755));
85+
// 对于字符设备,选择与 Linux devpts/devfs 一致的首选 I/O 块大小。
86+
// 1024 满足测试期望 (允许 1024 或 4096)。
87+
md.blk_size = 1024;
88+
md
89+
},
8390
}
8491
}
85-
86-
pub fn metadata_mut(&mut self) -> &mut Metadata {
87-
&mut self.metadata
88-
}
8992
}
9093

9194
#[derive(Debug, PartialEq)]
@@ -225,15 +228,28 @@ impl IndexNode for TtyDevice {
225228
mode: &crate::filesystem::vfs::file::FileFlags,
226229
) -> Result<(), SystemError> {
227230
if self.tty_type == TtyType::Pty(PtyType::Ptm) {
228-
return ptmx_open(data, mode);
231+
return ptmx_open(self, data, mode);
229232
}
230233
let dev_num = self.metadata()?.raw_dev;
234+
// /dev/tty 仅在已有控制终端时才能打开;否则返回 ENXIO
235+
let mut tty = if dev_num == DeviceNumber::new(Major::TTYAUX_MAJOR, 0) {
236+
if let Some(current) = self.open_current_tty(dev_num, &mut data) {
237+
Some(current)
238+
} else {
239+
return Err(SystemError::ENXIO);
240+
}
241+
} else {
242+
None
243+
};
244+
231245
// log::debug!(
232246
// "TtyDevice::open: dev_num: {}, current pid: {}",
233247
// dev_num,
234248
// ProcessManager::current_pid()
235249
// );
236-
let mut tty = self.open_current_tty(dev_num, &mut data);
250+
if tty.is_none() {
251+
tty = self.open_current_tty(dev_num, &mut data);
252+
}
237253
if tty.is_none() {
238254
let (index, driver) =
239255
TtyDriverManager::lookup_tty_driver(dev_num).ok_or(SystemError::ENODEV)?;
@@ -260,10 +276,8 @@ impl IndexNode for TtyDevice {
260276
}
261277

262278
let driver = tty.core().driver();
263-
// 考虑noctty(当前tty)
279+
// 考虑 O_NOCTTY:显式指定则不设置控制终端;pty master 也不会成为控制终端。
264280
if !(mode.contains(FileFlags::O_NOCTTY)
265-
&& dev_num == DeviceNumber::new(Major::TTY_MAJOR, 0)
266-
|| dev_num == DeviceNumber::new(Major::TTYAUX_MAJOR, 1)
267281
|| (driver.tty_driver_type() == TtyDriverType::Pty
268282
&& driver.tty_driver_sub_type() == TtyDriverSubType::PtyMaster))
269283
{

kernel/src/driver/tty/tty_driver.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use core::fmt::Debug;
1+
use core::{any::Any, fmt::Debug};
22

33
use alloc::{
44
string::{String, ToString},
@@ -102,7 +102,9 @@ impl TtyDriverManager {
102102

103103
/// tty 驱动程序的与设备相关的数据
104104
pub trait TtyDriverPrivateField: Debug + Send + Sync {}
105-
pub trait TtyCorePrivateField: Debug + Send + Sync {}
105+
pub trait TtyCorePrivateField: Debug + Send + Sync + Any {
106+
fn as_any(&self) -> &dyn Any;
107+
}
106108

107109
#[allow(dead_code)]
108110
#[derive(Debug)]
@@ -336,8 +338,13 @@ impl TtyDriver {
336338
// log::debug!("init_tty_device: to ldisc_setup");
337339
TtyLdiscManager::ldisc_setup(tty.clone(), tty.core().link())?;
338340

339-
// 在devfs创建对应的文件
341+
// 对 PTY 来说,用户可见的设备节点由 devpts 挂载点下的动态节点提供,
342+
// 不应再向全局 devfs 注册(否则在新实例复用索引时会因已有的 ptm/ptsX 节点返回 EEXIST)。
343+
if self.tty_driver_type == TtyDriverType::Pty {
344+
return Ok(tty);
345+
}
340346

347+
// 在devfs创建对应的文件
341348
// log::debug!("init_tty_device: to new tty device");
342349
let device = TtyDevice::new(
343350
core.name().clone(),

0 commit comments

Comments
 (0)