mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-17 12:47:16 +00:00
Replace Mutex with Spinlock in tty to avoid deadlock
This commit is contained in:
parent
2b59a406a6
commit
2985cdced6
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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(())
|
||||
|
@ -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()
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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};
|
||||
|
Loading…
x
Reference in New Issue
Block a user