Add timeouts to more driver spin loops
This commit is contained in:
@@ -16,6 +16,8 @@ pub mod io;
|
||||
mod logger;
|
||||
/// The Scatter Gather List (SGL) API for drivers.
|
||||
pub mod sgl;
|
||||
/// Low latency timeout for driver loops
|
||||
pub mod timeout;
|
||||
|
||||
pub use logger::{output_level, file_level, setup_logging};
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
use std::{thread, time::{Duration, Instant}};
|
||||
|
||||
pub struct Timeout {
|
||||
instant: Instant,
|
||||
duration: Duration,
|
||||
}
|
||||
|
||||
impl Timeout {
|
||||
#[inline]
|
||||
pub fn new(duration: Duration) -> Self {
|
||||
Self {
|
||||
instant: Instant::now(),
|
||||
duration,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_micros(micros: u64) -> Self {
|
||||
Self::new(Duration::from_micros(micros))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_millis(millis: u64) -> Self {
|
||||
Self::new(Duration::from_millis(millis))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_secs(secs: u64) -> Self {
|
||||
Self::new(Duration::from_secs(secs))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn run(&self) -> Result<(), ()> {
|
||||
if self.instant.elapsed() < self.duration {
|
||||
// Sleeps in Redox are only evaluated on PIT ticks (a few ms), which is not
|
||||
// short enough for a reasonably responsive timeout. However, the clock is
|
||||
// highly accurate. So, we yield instead of sleep to reduce latency.
|
||||
//TODO: allow timeout that spins instead of yields?
|
||||
std::thread::yield_now();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use common::io::{Io, Pio, ReadOnly, WriteOnly};
|
||||
use common::{io::{Io, Pio, ReadOnly, WriteOnly}, timeout::Timeout};
|
||||
use log::{debug, error, info, trace};
|
||||
|
||||
use std::{
|
||||
@@ -103,32 +103,6 @@ const DEFAULT_TIMEOUT: u64 = 50_000;
|
||||
// Reset timeout in microseconds
|
||||
const RESET_TIMEOUT: u64 = 500_000;
|
||||
|
||||
struct Timeout {
|
||||
instant: Instant,
|
||||
duration: Duration,
|
||||
}
|
||||
|
||||
impl Timeout {
|
||||
fn new(micros: u64) -> Self {
|
||||
Self {
|
||||
instant: Instant::now(),
|
||||
duration: Duration::from_micros(micros),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&self) -> Result<(), ()> {
|
||||
if self.instant.elapsed() < self.duration {
|
||||
// Sleeps in Redox are only evaluated on PIT ticks (a few ms), which is not
|
||||
// short enough for a reasonably responsive timeout. However, the clock is
|
||||
// highly accurate. So, we yield instead of sleep to reduce latency.
|
||||
thread::yield_now();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Ps2 {
|
||||
data: Pio<u8>,
|
||||
status: ReadOnly<Pio<u8>>,
|
||||
@@ -149,7 +123,7 @@ impl Ps2 {
|
||||
}
|
||||
|
||||
fn wait_read(&mut self, micros: u64) -> Result<(), Error> {
|
||||
let timeout = Timeout::new(micros);
|
||||
let timeout = Timeout::from_micros(micros);
|
||||
loop {
|
||||
if self.status().contains(StatusFlags::OUTPUT_FULL) {
|
||||
return Ok(());
|
||||
@@ -159,7 +133,7 @@ impl Ps2 {
|
||||
}
|
||||
|
||||
fn wait_write(&mut self, micros: u64) -> Result<(), Error> {
|
||||
let timeout = Timeout::new(micros);
|
||||
let timeout = Timeout::from_micros(micros);
|
||||
loop {
|
||||
if !self.status().contains(StatusFlags::INPUT_FULL) {
|
||||
return Ok(());
|
||||
|
||||
@@ -6,6 +6,7 @@ use syscall::error::{Error, Result, EIO, EMSGSIZE};
|
||||
|
||||
use common::dma::Dma;
|
||||
use common::io::{Io, Mmio, ReadOnly};
|
||||
use common::timeout::Timeout;
|
||||
|
||||
const RX_BUFFER_SIZE: usize = 64 * 1024;
|
||||
|
||||
@@ -219,7 +220,7 @@ impl Rtl8139 {
|
||||
mac_address: [0; 6],
|
||||
};
|
||||
|
||||
module.init();
|
||||
module.init()?;
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
@@ -253,7 +254,7 @@ impl Rtl8139 {
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn init(&mut self) {
|
||||
pub unsafe fn init(&mut self) -> Result<()> {
|
||||
let mac_low = self.regs.mac[0].read();
|
||||
let mac_high = self.regs.mac[1].read();
|
||||
let mac = [
|
||||
@@ -276,10 +277,13 @@ impl Rtl8139 {
|
||||
self.mac_address = mac;
|
||||
|
||||
// Reset - this will disable tx and rx, reinitialize FIFOs, and set the system buffer pointer to the initial value
|
||||
log::debug!("Reset");
|
||||
self.regs.cr.writef(CR_RST, true);
|
||||
while self.regs.cr.readf(CR_RST) {
|
||||
core::hint::spin_loop();
|
||||
{
|
||||
log::debug!("Reset");
|
||||
let timeout = Timeout::from_secs(1);
|
||||
self.regs.cr.writef(CR_RST, true);
|
||||
while self.regs.cr.readf(CR_RST) {
|
||||
timeout.run().map_err(|()| Error::new(EIO))?;
|
||||
}
|
||||
}
|
||||
|
||||
// Set up rx buffer
|
||||
@@ -300,5 +304,6 @@ impl Rtl8139 {
|
||||
self.regs.cr.writef(CR_RE | CR_TE, true);
|
||||
|
||||
log::debug!("Complete!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::convert::TryInto;
|
||||
use std::mem;
|
||||
|
||||
use common::io::{Io, Mmio, ReadOnly};
|
||||
use driver_network::NetworkAdapter;
|
||||
use syscall::error::{Error, Result, EMSGSIZE};
|
||||
|
||||
use common::dma::Dma;
|
||||
use common::io::{Io, Mmio, ReadOnly};
|
||||
use common::timeout::Timeout;
|
||||
use driver_network::NetworkAdapter;
|
||||
use syscall::error::{Error, Result, EIO, EMSGSIZE};
|
||||
|
||||
#[repr(C, packed)]
|
||||
struct Regs {
|
||||
@@ -220,7 +220,7 @@ impl Rtl8168 {
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn init(&mut self) {
|
||||
pub unsafe fn init(&mut self) -> Result<()> {
|
||||
let mac_low = self.regs.mac[0].read();
|
||||
let mac_high = self.regs.mac[1].read();
|
||||
let mac = [
|
||||
@@ -243,10 +243,13 @@ impl Rtl8168 {
|
||||
self.mac_address = mac;
|
||||
|
||||
// Reset - this will disable tx and rx, reinitialize FIFOs, and set the system buffer pointer to the initial value
|
||||
log::debug!("Reset");
|
||||
self.regs.cmd.writef(1 << 4, true);
|
||||
while self.regs.cmd.readf(1 << 4) {
|
||||
core::hint::spin_loop();
|
||||
{
|
||||
log::debug!("Reset");
|
||||
let timeout = Timeout::from_secs(1);
|
||||
self.regs.cmd.writef(1 << 4, true);
|
||||
while self.regs.cmd.readf(1 << 4) {
|
||||
timeout.run().map_err(|()| Error::new(EIO))?;
|
||||
}
|
||||
}
|
||||
|
||||
// Set up rx buffers
|
||||
@@ -337,5 +340,6 @@ impl Rtl8168 {
|
||||
self.regs.cmd_9346.write(0);
|
||||
|
||||
log::debug!("Complete!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@ use std::ops::DerefMut;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{ptr, u32};
|
||||
|
||||
use common::dma::Dma;
|
||||
use common::io::{Io, Mmio};
|
||||
use common::timeout::Timeout;
|
||||
use syscall::error::{Error, Result, EIO};
|
||||
|
||||
use super::fis::{FisRegH2D, FisType};
|
||||
|
||||
use common::dma::Dma;
|
||||
|
||||
const ATA_CMD_READ_DMA_EXT: u8 = 0x25;
|
||||
const ATA_CMD_WRITE_DMA_EXT: u8 = 0x35;
|
||||
const ATA_CMD_IDENTIFY: u8 = 0xEC;
|
||||
@@ -80,13 +80,12 @@ impl HbaPort {
|
||||
}
|
||||
|
||||
pub fn start(&mut self) -> Result<()> {
|
||||
let timer = Instant::now();
|
||||
let timeout = Timeout::new(TIMEOUT);
|
||||
while self.cmd.readf(HBA_PORT_CMD_CR) {
|
||||
core::hint::spin_loop();
|
||||
if timer.elapsed() >= TIMEOUT {
|
||||
timeout.run().map_err(|()| {
|
||||
log::error!("HBA start timed out");
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
}
|
||||
|
||||
self.cmd.writef(HBA_PORT_CMD_FRE | HBA_PORT_CMD_ST, true);
|
||||
@@ -96,13 +95,12 @@ impl HbaPort {
|
||||
pub fn stop(&mut self) -> Result<()> {
|
||||
self.cmd.writef(HBA_PORT_CMD_ST, false);
|
||||
|
||||
let timer = Instant::now();
|
||||
let timeout = Timeout::new(TIMEOUT);
|
||||
while self.cmd.readf(HBA_PORT_CMD_FR | HBA_PORT_CMD_CR) {
|
||||
core::hint::spin_loop();
|
||||
if timer.elapsed() >= TIMEOUT {
|
||||
timeout.run().map_err(|()| {
|
||||
log::error!("HBA stop timed out");
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
}
|
||||
|
||||
self.cmd.writef(HBA_PORT_CMD_FRE, false);
|
||||
@@ -404,13 +402,12 @@ impl HbaPort {
|
||||
callback(cmdheader, cmdfis, prdt_entry, acmd)
|
||||
}
|
||||
|
||||
let timer = Instant::now();
|
||||
let timeout = Timeout::new(TIMEOUT);
|
||||
while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) {
|
||||
core::hint::spin_loop();
|
||||
if timer.elapsed() >= TIMEOUT {
|
||||
timeout.run().map_err(|()| {
|
||||
log::error!("HBA ata_start timeout");
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
}
|
||||
|
||||
self.ci.writef(1 << slot, true);
|
||||
@@ -426,13 +423,12 @@ impl HbaPort {
|
||||
}
|
||||
|
||||
pub fn ata_stop(&mut self, slot: u32) -> Result<()> {
|
||||
let timer = Instant::now();
|
||||
let timeout = Timeout::new(TIMEOUT);
|
||||
while self.ata_running(slot) {
|
||||
core::hint::spin_loop();
|
||||
if timer.elapsed() >= TIMEOUT {
|
||||
timeout.run().map_err(|()| {
|
||||
log::error!("HBA ata_stop timeout");
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
}
|
||||
|
||||
self.stop()?;
|
||||
|
||||
@@ -169,7 +169,7 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
let mut nvme = Nvme::new(address.as_ptr() as usize, interrupt_method, pcid_handle)
|
||||
.expect("nvmed: failed to allocate driver data");
|
||||
|
||||
unsafe { nvme.init() }
|
||||
unsafe { nvme.init().expect("nvmed: failed to init") }
|
||||
log::debug!("Finished base initialization");
|
||||
let nvme = Arc::new(nvme);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::sync::Arc;
|
||||
use parking_lot::{Mutex, ReentrantMutex, RwLock};
|
||||
|
||||
use common::io::{Io, Mmio};
|
||||
use common::timeout::Timeout;
|
||||
use syscall::error::{Error, Result, EIO};
|
||||
|
||||
use common::dma::Dma;
|
||||
@@ -190,7 +191,7 @@ impl Nvme {
|
||||
self.doorbell_write(2 * (qid as usize) + 1, u32::from(head));
|
||||
}
|
||||
|
||||
pub unsafe fn init(&mut self) {
|
||||
pub unsafe fn init(&mut self) -> Result<()> {
|
||||
let thread_ctxts = self.thread_ctxts.get_mut();
|
||||
{
|
||||
let regs = self.regs.read();
|
||||
@@ -204,14 +205,20 @@ impl Nvme {
|
||||
log::debug!("Disabling controller.");
|
||||
self.regs.get_mut().cc.writef(1, false);
|
||||
|
||||
log::trace!("Waiting for not ready.");
|
||||
loop {
|
||||
let csts = self.regs.get_mut().csts.read();
|
||||
log::trace!("CSTS: {:X}", csts);
|
||||
if csts & 1 == 1 {
|
||||
std::hint::spin_loop();
|
||||
} else {
|
||||
break;
|
||||
{
|
||||
log::trace!("Waiting for not ready.");
|
||||
let mut timeout = Timeout::from_secs(5);
|
||||
loop {
|
||||
let csts = self.regs.get_mut().csts.read();
|
||||
log::trace!("CSTS: {:X}", csts);
|
||||
if csts & 1 == 1 {
|
||||
timeout.run().map_err(|()| {
|
||||
log::error!("failed to wait for not ready");
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,16 +276,24 @@ impl Nvme {
|
||||
log::debug!("Enabling controller.");
|
||||
self.regs.get_mut().cc.writef(1, true);
|
||||
|
||||
log::debug!("Waiting for ready");
|
||||
loop {
|
||||
let csts = self.regs.get_mut().csts.read();
|
||||
log::debug!("CSTS: {:X}", csts);
|
||||
if csts & 1 == 0 {
|
||||
std::hint::spin_loop();
|
||||
} else {
|
||||
break;
|
||||
{
|
||||
log::debug!("Waiting for ready");
|
||||
let mut timeout = Timeout::from_secs(5);
|
||||
loop {
|
||||
let csts = self.regs.get_mut().csts.read();
|
||||
log::debug!("CSTS: {:X}", csts);
|
||||
if csts & 1 == 0 {
|
||||
timeout.run().map_err(|()| {
|
||||
log::error!("failed to wait for ready");
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Masks or unmasks multiple vectors.
|
||||
|
||||
Reference in New Issue
Block a user