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.
use crate::arch::x86::device::io_port::{IoPort, ReadWriteAccess, WriteOnlyAccess};
use crate::sync::SpinLock;
use crate::trap::IrqAllocateHandle;
use alloc::{sync::Arc, vec::Vec};
use log::debug;
use spin::{Mutex, Once};
use spin::Once;
use trapframe::TrapFrame;
bitflags::bitflags! {
@ -24,9 +25,9 @@ static SERIAL_MODEM_CTRL: IoPort<u8, WriteOnlyAccess> =
unsafe { IoPort::new(SERIAL_DATA_PORT + 4) };
static SERIAL_LINE_STS: IoPort<u8, ReadWriteAccess> = unsafe { IoPort::new(SERIAL_DATA_PORT + 5) };
static CONSOLE_IRQ_CALLBACK: Once<Mutex<IrqAllocateHandle>> = Once::new();
static SERIAL_INPUT_CALLBACKS: Mutex<Vec<Arc<dyn Fn(u8) + Send + Sync + 'static>>> =
Mutex::new(Vec::new());
static CONSOLE_IRQ_CALLBACK: Once<SpinLock<IrqAllocateHandle>> = Once::new();
static SERIAL_INPUT_CALLBACKS: SpinLock<Vec<Arc<dyn Fn(u8) + Send + Sync + 'static>>> =
SpinLock::new(Vec::new());
/// Initializes the serial port.
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() {
let mut irq = crate::arch::x86::kernel::pic::allocate_irq(4).unwrap();
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)
@ -72,10 +73,11 @@ where
fn handle_serial_input(trap_frame: &TrapFrame) {
// 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;
}
let lock = SERIAL_INPUT_CALLBACKS.lock();
};
let received_char = receive_char().unwrap();
debug!("receive char = {:?}", received_char);
for callback in lock.iter() {

View File

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

View File

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

View File

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

View File

@ -15,13 +15,13 @@ const BUFFER_CAPACITY: usize = 4096;
pub struct LineDiscipline {
/// current line
current_line: RwLock<CurrentLine>,
current_line: SpinLock<CurrentLine>,
/// The read buffer
read_buffer: Mutex<StaticRb<u8, BUFFER_CAPACITY>>,
read_buffer: SpinLock<StaticRb<u8, BUFFER_CAPACITY>>,
/// The foreground process group
foreground: RwLock<Option<Pgid>>,
foreground: SpinLock<Option<Pgid>>,
/// termios
termios: RwLock<KernelTermios>,
termios: SpinLock<KernelTermios>,
/// Pollee
pollee: Pollee,
}
@ -65,17 +65,17 @@ impl LineDiscipline {
/// create a new line discipline
pub fn new() -> Self {
Self {
current_line: RwLock::new(CurrentLine::new()),
read_buffer: Mutex::new(StaticRb::default()),
foreground: RwLock::new(None),
termios: RwLock::new(KernelTermios::default()),
current_line: SpinLock::new(CurrentLine::new()),
read_buffer: SpinLock::new(StaticRb::default()),
foreground: SpinLock::new(None),
termios: SpinLock::new(KernelTermios::default()),
pollee: Pollee::new(IoEvents::empty()),
}
}
/// push char to line discipline. This function should be called in input interrupt handler.
pub fn push_char(&self, mut item: u8) {
let termios = self.termios.read();
let termios = self.termios.lock();
if termios.contains_icrnl() {
if item == b'\r' {
item = b'\n'
@ -85,7 +85,7 @@ impl LineDiscipline {
if item == *termios.get_special_char(CC_C_CHAR::VINTR) {
// type Ctrl + C, signal SIGINT
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 fg_group = process_table::pgid_to_process_group(fg).unwrap();
fg_group.kernel_signal(kernel_signal);
@ -94,7 +94,7 @@ impl LineDiscipline {
} else if item == *termios.get_special_char(CC_C_CHAR::VQUIT) {
// type Ctrl + \, signal SIGQUIT
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 fg_group = process_table::pgid_to_process_group(fg).unwrap();
fg_group.kernel_signal(kernel_signal);
@ -102,17 +102,17 @@ impl LineDiscipline {
}
} else if item == *termios.get_special_char(CC_C_CHAR::VKILL) {
// erase current line
self.current_line.write().drain();
self.current_line.lock().drain();
} else if item == *termios.get_special_char(CC_C_CHAR::VERASE) {
// type backspace
let mut current_line = self.current_line.write();
let mut current_line = self.current_line.lock();
if !current_line.is_empty() {
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.
// 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);
let current_line_chars = current_line.drain();
for char in current_line_chars {
@ -120,7 +120,7 @@ impl LineDiscipline {
}
} else if item >= 0x20 && item < 0x7f {
// printable character
self.current_line.write().push_char(item);
self.current_line.lock().push_char(item);
}
} else {
// raw mode
@ -148,7 +148,7 @@ impl LineDiscipline {
let ch = char::from(item);
print!("{}", ch);
}
let termios = self.termios.read();
let termios = self.termios.lock();
if item == *termios.get_special_char(CC_C_CHAR::VERASE) {
// write a space to overwrite current character
let bytes: [u8; 3] = [b'\x08', b' ', b'\x08'];
@ -202,7 +202,7 @@ impl LineDiscipline {
}
let (vmin, vtime) = {
let termios = self.termios.read();
let termios = self.termios.lock();
let vmin = *termios.get_special_char(CC_C_CHAR::VMIN);
let vtime = *termios.get_special_char(CC_C_CHAR::VTIME);
(vmin, vtime)
@ -246,10 +246,11 @@ impl LineDiscipline {
let mut read_len = 0;
for i in 0..max_read_len {
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
if meet_new_line(next_char, &self.termios.read()) {
if !should_not_be_read(next_char, &self.termios.read()) {
if meet_new_line(next_char, &termios) {
if !should_not_be_read(next_char, &termios) {
dst[i] = next_char;
read_len += 1;
}
@ -291,7 +292,7 @@ impl LineDiscipline {
/// whether the current process belongs to foreground process group
fn current_belongs_to_foreground(&self) -> bool {
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 process_group.contains_process(current.pid()) {
return true;
@ -304,7 +305,7 @@ impl LineDiscipline {
/// set foreground process group
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.
if self.is_readable() {
self.pollee.add_events(IoEvents::IN);
@ -312,8 +313,8 @@ impl LineDiscipline {
}
/// get foreground process group id
pub fn get_fg(&self) -> Option<Pgid> {
*self.foreground.read()
pub fn fg_pgid(&self) -> Option<Pgid> {
*self.foreground.lock()
}
/// whether there is buffered data
@ -321,12 +322,12 @@ impl LineDiscipline {
self.read_buffer.lock().len() == 0
}
pub fn get_termios(&self) -> KernelTermios {
*self.termios.read()
pub fn termios(&self) -> KernelTermios {
*self.termios.lock()
}
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 super::*;
use crate::driver::tty::TtyDriver;
use crate::fs::utils::{IoEvents, IoctlCmd, Poller};
use crate::prelude::*;
use crate::process::Pgid;
use crate::util::{read_val_from_user, write_val_to_user};
pub mod driver;
pub mod line_discipline;
pub mod termio;
lazy_static! {
static ref N_TTY: Arc<Tty> = {
let name = CString::new("console").unwrap();
Arc::new(Tty::new(name))
};
static N_TTY: Once<Arc<Tty>> = Once::new();
pub(super) fn init() {
let name = CString::new("console").unwrap();
let tty = Arc::new(Tty::new(name));
N_TTY.call_once(|| tty);
driver::init();
}
pub struct Tty {
@ -22,7 +27,7 @@ pub struct Tty {
/// line discipline
ldisc: LineDiscipline,
/// driver
driver: Mutex<Weak<TtyDriver>>,
driver: SpinLock<Weak<TtyDriver>>,
}
impl Tty {
@ -30,7 +35,7 @@ impl Tty {
Tty {
name,
ldisc: LineDiscipline::new(),
driver: Mutex::new(Weak::new()),
driver: SpinLock::new(Weak::new()),
}
}
@ -79,14 +84,14 @@ impl Device for Tty {
match cmd {
IoctlCmd::TCGETS => {
// Get terminal attributes
let termios = self.ldisc.get_termios();
let termios = self.ldisc.termios();
trace!("get termios = {:?}", termios);
write_val_to_user(arg, &termios)?;
Ok(0)
}
IoctlCmd::TIOCGPGRP => {
// 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 {
None => return_errno_with_message!(Errno::ENOENT, "No fg process group"),
Some(fg_pgid) => {
@ -120,5 +125,5 @@ impl Device for Tty {
/// FIXME: should we maintain a static console?
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 log::info;
@ -8,7 +6,6 @@ pub fn init() {
for comp in INPUT_COMPONENT.get().unwrap().get_input_device() {
info!("input device name:{}", comp.name());
}
tty::init();
}
#[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 jinux_frame::config::PAGE_SIZE;
// 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::{print, println};
pub(crate) use log::{debug, error, info, trace, warn};