Replace Mutex with Spinlock in tty to avoid deadlock

This commit is contained in:
Jianfeng Jiang
2023-05-31 16:45:44 +08:00
committed by Tate, Hongliang Tian
parent 2b59a406a6
commit 2985cdced6
8 changed files with 76 additions and 69 deletions

View File

@ -1,10 +1,11 @@
//! A port-mapped UART. Copied from uart_16550. //! A port-mapped UART. Copied from uart_16550.
use crate::arch::x86::device::io_port::{IoPort, ReadWriteAccess, WriteOnlyAccess}; use crate::arch::x86::device::io_port::{IoPort, ReadWriteAccess, WriteOnlyAccess};
use crate::sync::SpinLock;
use crate::trap::IrqAllocateHandle; use crate::trap::IrqAllocateHandle;
use alloc::{sync::Arc, vec::Vec}; use alloc::{sync::Arc, vec::Vec};
use log::debug; use log::debug;
use spin::{Mutex, Once}; use spin::Once;
use trapframe::TrapFrame; use trapframe::TrapFrame;
bitflags::bitflags! { bitflags::bitflags! {
@ -24,9 +25,9 @@ static SERIAL_MODEM_CTRL: IoPort<u8, WriteOnlyAccess> =
unsafe { IoPort::new(SERIAL_DATA_PORT + 4) }; unsafe { IoPort::new(SERIAL_DATA_PORT + 4) };
static SERIAL_LINE_STS: IoPort<u8, ReadWriteAccess> = unsafe { IoPort::new(SERIAL_DATA_PORT + 5) }; static SERIAL_LINE_STS: IoPort<u8, ReadWriteAccess> = unsafe { IoPort::new(SERIAL_DATA_PORT + 5) };
static CONSOLE_IRQ_CALLBACK: Once<Mutex<IrqAllocateHandle>> = Once::new(); static CONSOLE_IRQ_CALLBACK: Once<SpinLock<IrqAllocateHandle>> = Once::new();
static SERIAL_INPUT_CALLBACKS: Mutex<Vec<Arc<dyn Fn(u8) + Send + Sync + 'static>>> = static SERIAL_INPUT_CALLBACKS: SpinLock<Vec<Arc<dyn Fn(u8) + Send + Sync + 'static>>> =
Mutex::new(Vec::new()); SpinLock::new(Vec::new());
/// Initializes the serial port. /// Initializes the serial port.
pub(crate) fn init() { pub(crate) fn init() {
@ -56,7 +57,7 @@ pub fn register_serial_input_callback(f: impl Fn(u8) + Send + Sync + 'static) {
pub(crate) fn callback_init() { pub(crate) fn callback_init() {
let mut irq = crate::arch::x86::kernel::pic::allocate_irq(4).unwrap(); let mut irq = crate::arch::x86::kernel::pic::allocate_irq(4).unwrap();
irq.on_active(handle_serial_input); irq.on_active(handle_serial_input);
CONSOLE_IRQ_CALLBACK.call_once(|| Mutex::new(irq)); CONSOLE_IRQ_CALLBACK.call_once(|| SpinLock::new(irq));
} }
pub(crate) fn register_serial_input_irq_handler<F>(callback: F) pub(crate) fn register_serial_input_irq_handler<F>(callback: F)
@ -72,10 +73,11 @@ where
fn handle_serial_input(trap_frame: &TrapFrame) { fn handle_serial_input(trap_frame: &TrapFrame) {
// debug!("keyboard interrupt was met"); // debug!("keyboard interrupt was met");
if SERIAL_INPUT_CALLBACKS.is_locked() { let lock = if let Some(lock) = SERIAL_INPUT_CALLBACKS.try_lock() {
lock
} else {
return; return;
} };
let lock = SERIAL_INPUT_CALLBACKS.lock();
let received_char = receive_char().unwrap(); let received_char = receive_char().unwrap();
debug!("receive char = {:?}", received_char); debug!("receive char = {:?}", received_char);
for callback in lock.iter() { for callback in lock.iter() {

View File

@ -12,8 +12,10 @@ use core::{
ops::{Index, IndexMut}, ops::{Index, IndexMut},
}; };
use font8x8::UnicodeFonts; use font8x8::UnicodeFonts;
use jinux_frame::{config::PAGE_SIZE, mmio::Mmio, vm::VmIo, LimineFramebufferRequest}; use jinux_frame::{
use spin::{Mutex, Once}; config::PAGE_SIZE, mmio::Mmio, sync::SpinLock, vm::VmIo, LimineFramebufferRequest,
};
use spin::Once;
#[init_component] #[init_component]
fn framebuffer_init() -> Result<(), ComponentInitError> { fn framebuffer_init() -> Result<(), ComponentInitError> {
@ -21,7 +23,7 @@ fn framebuffer_init() -> Result<(), ComponentInitError> {
Ok(()) Ok(())
} }
pub(crate) static WRITER: Once<Mutex<Writer>> = Once::new(); pub(crate) static WRITER: Once<SpinLock<Writer>> = Once::new();
static FRAMEBUFFER_REUEST: LimineFramebufferRequest = LimineFramebufferRequest::new(0); static FRAMEBUFFER_REUEST: LimineFramebufferRequest = LimineFramebufferRequest::new(0);
pub(crate) fn init() { pub(crate) fn init() {
@ -64,7 +66,7 @@ pub(crate) fn init() {
}; };
writer.clear(); writer.clear();
WRITER.call_once(|| Mutex::new(writer)); WRITER.call_once(|| SpinLock::new(writer));
} }
pub(crate) struct Writer { pub(crate) struct Writer {

View File

@ -11,6 +11,7 @@ pub fn init() -> Result<()> {
add_node(null, "null")?; add_node(null, "null")?;
let zero = Arc::new(zero::Zero); let zero = Arc::new(zero::Zero);
add_node(zero, "zero")?; add_node(zero, "zero")?;
tty::init();
let tty = tty::get_n_tty().clone(); let tty = tty::get_n_tty().clone();
add_node(tty, "tty")?; add_node(tty, "tty")?;
Ok(()) Ok(())

View File

@ -1,28 +1,30 @@
pub use jinux_frame::arch::x86::device::serial::register_serial_input_callback; pub use jinux_frame::arch::x86::device::serial::register_serial_input_callback;
use spin::Once;
use crate::{ use crate::{
device::tty::{get_n_tty, Tty}, device::tty::{get_n_tty, Tty},
prelude::*, prelude::*,
}; };
lazy_static! { pub static TTY_DRIVER: Once<Arc<TtyDriver>> = Once::new();
pub static ref TTY_DRIVER: Arc<TtyDriver> = {
pub(super) fn init() {
register_serial_input_callback(serial_input_callback);
let tty_driver = Arc::new(TtyDriver::new()); let tty_driver = Arc::new(TtyDriver::new());
// FIXME: install n_tty into tty_driver? // FIXME: install n_tty into tty_driver?
let n_tty = get_n_tty(); let n_tty = get_n_tty();
tty_driver.install(n_tty.clone()); tty_driver.install(n_tty.clone());
tty_driver TTY_DRIVER.call_once(|| tty_driver);
};
} }
pub struct TtyDriver { pub struct TtyDriver {
ttys: Mutex<Vec<Arc<Tty>>>, ttys: SpinLock<Vec<Arc<Tty>>>,
} }
impl TtyDriver { impl TtyDriver {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
ttys: Mutex::new(Vec::new()), ttys: SpinLock::new(Vec::new()),
} }
} }
@ -70,9 +72,5 @@ fn serial_input_callback(item: u8) {
} }
fn get_tty_driver() -> &'static TtyDriver { fn get_tty_driver() -> &'static TtyDriver {
&TTY_DRIVER TTY_DRIVER.get().unwrap()
}
pub fn init() {
register_serial_input_callback(serial_input_callback);
} }

View File

@ -15,13 +15,13 @@ const BUFFER_CAPACITY: usize = 4096;
pub struct LineDiscipline { pub struct LineDiscipline {
/// current line /// current line
current_line: RwLock<CurrentLine>, current_line: SpinLock<CurrentLine>,
/// The read buffer /// The read buffer
read_buffer: Mutex<StaticRb<u8, BUFFER_CAPACITY>>, read_buffer: SpinLock<StaticRb<u8, BUFFER_CAPACITY>>,
/// The foreground process group /// The foreground process group
foreground: RwLock<Option<Pgid>>, foreground: SpinLock<Option<Pgid>>,
/// termios /// termios
termios: RwLock<KernelTermios>, termios: SpinLock<KernelTermios>,
/// Pollee /// Pollee
pollee: Pollee, pollee: Pollee,
} }
@ -65,17 +65,17 @@ impl LineDiscipline {
/// create a new line discipline /// create a new line discipline
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
current_line: RwLock::new(CurrentLine::new()), current_line: SpinLock::new(CurrentLine::new()),
read_buffer: Mutex::new(StaticRb::default()), read_buffer: SpinLock::new(StaticRb::default()),
foreground: RwLock::new(None), foreground: SpinLock::new(None),
termios: RwLock::new(KernelTermios::default()), termios: SpinLock::new(KernelTermios::default()),
pollee: Pollee::new(IoEvents::empty()), pollee: Pollee::new(IoEvents::empty()),
} }
} }
/// push char to line discipline. This function should be called in input interrupt handler. /// push char to line discipline. This function should be called in input interrupt handler.
pub fn push_char(&self, mut item: u8) { pub fn push_char(&self, mut item: u8) {
let termios = self.termios.read(); let termios = self.termios.lock();
if termios.contains_icrnl() { if termios.contains_icrnl() {
if item == b'\r' { if item == b'\r' {
item = b'\n' item = b'\n'
@ -85,7 +85,7 @@ impl LineDiscipline {
if item == *termios.get_special_char(CC_C_CHAR::VINTR) { if item == *termios.get_special_char(CC_C_CHAR::VINTR) {
// type Ctrl + C, signal SIGINT // type Ctrl + C, signal SIGINT
if termios.contains_isig() { if termios.contains_isig() {
if let Some(fg) = *self.foreground.read() { if let Some(fg) = *self.foreground.lock() {
let kernel_signal = KernelSignal::new(SIGINT); let kernel_signal = KernelSignal::new(SIGINT);
let fg_group = process_table::pgid_to_process_group(fg).unwrap(); let fg_group = process_table::pgid_to_process_group(fg).unwrap();
fg_group.kernel_signal(kernel_signal); fg_group.kernel_signal(kernel_signal);
@ -94,7 +94,7 @@ impl LineDiscipline {
} else if item == *termios.get_special_char(CC_C_CHAR::VQUIT) { } else if item == *termios.get_special_char(CC_C_CHAR::VQUIT) {
// type Ctrl + \, signal SIGQUIT // type Ctrl + \, signal SIGQUIT
if termios.contains_isig() { if termios.contains_isig() {
if let Some(fg) = *self.foreground.read() { if let Some(fg) = *self.foreground.lock() {
let kernel_signal = KernelSignal::new(SIGQUIT); let kernel_signal = KernelSignal::new(SIGQUIT);
let fg_group = process_table::pgid_to_process_group(fg).unwrap(); let fg_group = process_table::pgid_to_process_group(fg).unwrap();
fg_group.kernel_signal(kernel_signal); fg_group.kernel_signal(kernel_signal);
@ -102,17 +102,17 @@ impl LineDiscipline {
} }
} else if item == *termios.get_special_char(CC_C_CHAR::VKILL) { } else if item == *termios.get_special_char(CC_C_CHAR::VKILL) {
// erase current line // erase current line
self.current_line.write().drain(); self.current_line.lock().drain();
} else if item == *termios.get_special_char(CC_C_CHAR::VERASE) { } else if item == *termios.get_special_char(CC_C_CHAR::VERASE) {
// type backspace // type backspace
let mut current_line = self.current_line.write(); let mut current_line = self.current_line.lock();
if !current_line.is_empty() { if !current_line.is_empty() {
current_line.backspace(); current_line.backspace();
} }
} else if meet_new_line(item, &self.get_termios()) { } else if meet_new_line(item, &self.termios()) {
// a new line was met. We currently add the item to buffer. // a new line was met. We currently add the item to buffer.
// when we read content, the item should be skipped if it's EOF. // when we read content, the item should be skipped if it's EOF.
let mut current_line = self.current_line.write(); let mut current_line = self.current_line.lock();
current_line.push_char(item); current_line.push_char(item);
let current_line_chars = current_line.drain(); let current_line_chars = current_line.drain();
for char in current_line_chars { for char in current_line_chars {
@ -120,7 +120,7 @@ impl LineDiscipline {
} }
} else if item >= 0x20 && item < 0x7f { } else if item >= 0x20 && item < 0x7f {
// printable character // printable character
self.current_line.write().push_char(item); self.current_line.lock().push_char(item);
} }
} else { } else {
// raw mode // raw mode
@ -148,7 +148,7 @@ impl LineDiscipline {
let ch = char::from(item); let ch = char::from(item);
print!("{}", ch); print!("{}", ch);
} }
let termios = self.termios.read(); let termios = self.termios.lock();
if item == *termios.get_special_char(CC_C_CHAR::VERASE) { if item == *termios.get_special_char(CC_C_CHAR::VERASE) {
// write a space to overwrite current character // write a space to overwrite current character
let bytes: [u8; 3] = [b'\x08', b' ', b'\x08']; let bytes: [u8; 3] = [b'\x08', b' ', b'\x08'];
@ -202,7 +202,7 @@ impl LineDiscipline {
} }
let (vmin, vtime) = { let (vmin, vtime) = {
let termios = self.termios.read(); let termios = self.termios.lock();
let vmin = *termios.get_special_char(CC_C_CHAR::VMIN); let vmin = *termios.get_special_char(CC_C_CHAR::VMIN);
let vtime = *termios.get_special_char(CC_C_CHAR::VTIME); let vtime = *termios.get_special_char(CC_C_CHAR::VTIME);
(vmin, vtime) (vmin, vtime)
@ -246,10 +246,11 @@ impl LineDiscipline {
let mut read_len = 0; let mut read_len = 0;
for i in 0..max_read_len { for i in 0..max_read_len {
if let Some(next_char) = buffer.pop() { if let Some(next_char) = buffer.pop() {
if self.termios.read().is_canonical_mode() { let termios = self.termios.lock();
if termios.is_canonical_mode() {
// canonical mode, read until meet new line // canonical mode, read until meet new line
if meet_new_line(next_char, &self.termios.read()) { if meet_new_line(next_char, &termios) {
if !should_not_be_read(next_char, &self.termios.read()) { if !should_not_be_read(next_char, &termios) {
dst[i] = next_char; dst[i] = next_char;
read_len += 1; read_len += 1;
} }
@ -291,7 +292,7 @@ impl LineDiscipline {
/// whether the current process belongs to foreground process group /// whether the current process belongs to foreground process group
fn current_belongs_to_foreground(&self) -> bool { fn current_belongs_to_foreground(&self) -> bool {
let current = current!(); let current = current!();
if let Some(fg_pgid) = *self.foreground.read() { if let Some(fg_pgid) = *self.foreground.lock() {
if let Some(process_group) = process_table::pgid_to_process_group(fg_pgid) { if let Some(process_group) = process_table::pgid_to_process_group(fg_pgid) {
if process_group.contains_process(current.pid()) { if process_group.contains_process(current.pid()) {
return true; return true;
@ -304,7 +305,7 @@ impl LineDiscipline {
/// set foreground process group /// set foreground process group
pub fn set_fg(&self, fg_pgid: Pgid) { pub fn set_fg(&self, fg_pgid: Pgid) {
*self.foreground.write() = Some(fg_pgid); *self.foreground.lock() = Some(fg_pgid);
// Some background processes may be waiting on the wait queue, when set_fg, the background processes may be able to read. // Some background processes may be waiting on the wait queue, when set_fg, the background processes may be able to read.
if self.is_readable() { if self.is_readable() {
self.pollee.add_events(IoEvents::IN); self.pollee.add_events(IoEvents::IN);
@ -312,8 +313,8 @@ impl LineDiscipline {
} }
/// get foreground process group id /// get foreground process group id
pub fn get_fg(&self) -> Option<Pgid> { pub fn fg_pgid(&self) -> Option<Pgid> {
*self.foreground.read() *self.foreground.lock()
} }
/// whether there is buffered data /// whether there is buffered data
@ -321,12 +322,12 @@ impl LineDiscipline {
self.read_buffer.lock().len() == 0 self.read_buffer.lock().len() == 0
} }
pub fn get_termios(&self) -> KernelTermios { pub fn termios(&self) -> KernelTermios {
*self.termios.read() *self.termios.lock()
} }
pub fn set_termios(&self, termios: KernelTermios) { pub fn set_termios(&self, termios: KernelTermios) {
*self.termios.write() = termios; *self.termios.lock() = termios;
} }
} }

View File

@ -1,19 +1,24 @@
use spin::Once;
use self::driver::TtyDriver;
use self::line_discipline::LineDiscipline; use self::line_discipline::LineDiscipline;
use super::*; use super::*;
use crate::driver::tty::TtyDriver;
use crate::fs::utils::{IoEvents, IoctlCmd, Poller}; use crate::fs::utils::{IoEvents, IoctlCmd, Poller};
use crate::prelude::*; use crate::prelude::*;
use crate::process::Pgid; use crate::process::Pgid;
use crate::util::{read_val_from_user, write_val_to_user}; use crate::util::{read_val_from_user, write_val_to_user};
pub mod driver;
pub mod line_discipline; pub mod line_discipline;
pub mod termio; pub mod termio;
lazy_static! { static N_TTY: Once<Arc<Tty>> = Once::new();
static ref N_TTY: Arc<Tty> = {
pub(super) fn init() {
let name = CString::new("console").unwrap(); let name = CString::new("console").unwrap();
Arc::new(Tty::new(name)) let tty = Arc::new(Tty::new(name));
}; N_TTY.call_once(|| tty);
driver::init();
} }
pub struct Tty { pub struct Tty {
@ -22,7 +27,7 @@ pub struct Tty {
/// line discipline /// line discipline
ldisc: LineDiscipline, ldisc: LineDiscipline,
/// driver /// driver
driver: Mutex<Weak<TtyDriver>>, driver: SpinLock<Weak<TtyDriver>>,
} }
impl Tty { impl Tty {
@ -30,7 +35,7 @@ impl Tty {
Tty { Tty {
name, name,
ldisc: LineDiscipline::new(), ldisc: LineDiscipline::new(),
driver: Mutex::new(Weak::new()), driver: SpinLock::new(Weak::new()),
} }
} }
@ -79,14 +84,14 @@ impl Device for Tty {
match cmd { match cmd {
IoctlCmd::TCGETS => { IoctlCmd::TCGETS => {
// Get terminal attributes // Get terminal attributes
let termios = self.ldisc.get_termios(); let termios = self.ldisc.termios();
trace!("get termios = {:?}", termios); trace!("get termios = {:?}", termios);
write_val_to_user(arg, &termios)?; write_val_to_user(arg, &termios)?;
Ok(0) Ok(0)
} }
IoctlCmd::TIOCGPGRP => { IoctlCmd::TIOCGPGRP => {
// FIXME: Get the process group ID of the foreground process group on this terminal. // FIXME: Get the process group ID of the foreground process group on this terminal.
let fg_pgid = self.ldisc.get_fg(); let fg_pgid = self.ldisc.fg_pgid();
match fg_pgid { match fg_pgid {
None => return_errno_with_message!(Errno::ENOENT, "No fg process group"), None => return_errno_with_message!(Errno::ENOENT, "No fg process group"),
Some(fg_pgid) => { Some(fg_pgid) => {
@ -120,5 +125,5 @@ impl Device for Tty {
/// FIXME: should we maintain a static console? /// FIXME: should we maintain a static console?
pub fn get_n_tty() -> &'static Arc<Tty> { pub fn get_n_tty() -> &'static Arc<Tty> {
&N_TTY N_TTY.get().unwrap()
} }

View File

@ -1,5 +1,3 @@
pub mod tty;
use jinux_input::INPUT_COMPONENT; use jinux_input::INPUT_COMPONENT;
use log::info; use log::info;
@ -8,7 +6,6 @@ pub fn init() {
for comp in INPUT_COMPONENT.get().unwrap().get_input_device() { for comp in INPUT_COMPONENT.get().unwrap().get_input_device() {
info!("input device name:{}", comp.name()); info!("input device name:{}", comp.name());
} }
tty::init();
} }
#[allow(unused)] #[allow(unused)]

View File

@ -18,6 +18,7 @@ pub(crate) use core::ffi::CStr;
pub(crate) use int_to_c_enum::TryFromInt; pub(crate) use int_to_c_enum::TryFromInt;
pub(crate) use jinux_frame::config::PAGE_SIZE; pub(crate) use jinux_frame::config::PAGE_SIZE;
// pub(crate) use jinux_frame::sync::{Mutex, MutexGuard}; // pub(crate) use jinux_frame::sync::{Mutex, MutexGuard};
pub(crate) use jinux_frame::sync::{SpinLock, SpinLockGuard};
pub(crate) use jinux_frame::vm::Vaddr; pub(crate) use jinux_frame::vm::Vaddr;
pub(crate) use jinux_frame::{print, println}; pub(crate) use jinux_frame::{print, println};
pub(crate) use log::{debug, error, info, trace, warn}; pub(crate) use log::{debug, error, info, trace, warn};