-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
kernel: add interface registry / views
- Loading branch information
Showing
14 changed files
with
661 additions
and
94 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
//! Globally unique identifier allocator. | ||
use core::sync::atomic::{AtomicU64, Ordering}; | ||
|
||
/// Static, system-wide monotonically increasing resource counter. | ||
/// | ||
/// Used for a variety of resources; all resources in the system are guaranteed | ||
/// to have a unique ID, even across resource types. | ||
static COUNTER: AtomicU64 = AtomicU64::new(1); | ||
|
||
/// Allocates a new globally unique identifier. | ||
/// | ||
/// Guaranteed to be unique across all cores in the system, | ||
/// and monotonically increasing. This function is lock-free. | ||
/// | ||
/// Guaranteed never to return 0. | ||
#[inline] | ||
pub fn allocate() -> u64 { | ||
COUNTER.fetch_add(1, Ordering::Relaxed) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
//! Types and traits for Oro interfaces, exposed by modules and the kernel. | ||
use core::{ | ||
cell::UnsafeCell, | ||
mem::MaybeUninit, | ||
sync::atomic::{ | ||
AtomicU8, | ||
Ordering::{Acquire, Relaxed, Release}, | ||
}, | ||
}; | ||
|
||
use oro_mem::alloc::sync::Arc; | ||
|
||
/// Implements an interface, which is a flat namespace of `(index, flay_keyvals)`. | ||
/// | ||
/// Typically, these are interacted with via system calls. Modules (and the kernel) | ||
/// can consume or provide interfaces to the ring in order for applications to | ||
/// interact with them. | ||
/// | ||
/// Interfaces can be backed by a number of mechanisms; notably, the kernel implements | ||
/// them as direct interfaces to kernel data structures and machinery. They can also | ||
/// be implemented by userspace applications, which can then be interacted with by | ||
/// system calls or ports (or perhaps other interfaces). | ||
pub trait Interface: Send + Sync { | ||
/// Returns the type ID of the interface. Note that IDs with all high 32 bits | ||
/// cleared are reserved for kernel usage. | ||
fn type_id(&self) -> u64; | ||
/// Handles a system call request to this interface. | ||
/// | ||
/// System call handling must be quick and non-blocking. Either it can be | ||
/// serviced immediately, or can be processed "offline", returning a handle | ||
/// that can be polled for completion. | ||
fn handle(&self, request: SystemCallRequest) -> InterfaceResponse; | ||
} | ||
|
||
/// Response from an interface after handling a system call. | ||
/// | ||
/// When performing a [`Interface::handle`] operation, the interface can either | ||
/// respond immediately, or defer the response to a later time. In the latter case, | ||
/// a pollable handle is returned. | ||
pub enum InterfaceResponse { | ||
/// The interface has handled the request and the response is ready. | ||
Immediate(SystemCallResponse), | ||
/// The interface has received the request, but the response is not ready yet. | ||
Pending(InFlightSystemCallHandle), | ||
} | ||
|
||
/// The producer side of an in-flight system call. | ||
/// | ||
/// Interfaces should hold onto this handle when they defer a system call response, | ||
/// allowing to submit the response at a later time. | ||
#[repr(transparent)] | ||
pub struct InFlightSystemCall(Arc<InFlightSystemCallInner>); | ||
|
||
/// Inner shared state for an in-flight system call. | ||
struct InFlightSystemCallInner { | ||
/// The response is ready. Used as an atomic rather than an `Option`. | ||
state: AtomicU8, | ||
/// The response data; only valid if `state` is [`InFlightState::Ready`]. | ||
response: UnsafeCell<MaybeUninit<SystemCallResponse>>, | ||
} | ||
|
||
// SAFETY: We strictly control access to the inner state and control exactly | ||
// SAFETY: how it is used, and can guarantee that it is safe to share across | ||
// SAFETY: threads. Any misbehavior is a bug. | ||
unsafe impl Sync for InFlightSystemCallInner {} | ||
// SAFETY: The inner state is valid across threads, despite using an unsafe cell. | ||
unsafe impl Send for InFlightSystemCallInner {} | ||
|
||
/// Indicates the state of the in-flight system call. | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
#[repr(u8)] | ||
pub enum InFlightState { | ||
/// Pending | ||
Pending, | ||
/// The response is ready. | ||
Ready, | ||
/// The caller has canceled the system call. | ||
CallerCanceled, | ||
/// The interface has canceled the system call. | ||
InterfaceCanceled, | ||
/// The interface serviced the response, and the caller has | ||
/// already taken the response. This is considered an error. | ||
Finished, | ||
} | ||
|
||
/// The caller side of an in-flight system call. | ||
pub struct InFlightSystemCallHandle(Arc<InFlightSystemCallInner>); | ||
|
||
impl InFlightSystemCall { | ||
/// Creates a new in-flight system call interface/caller pair. | ||
/// | ||
/// The interface should call this, hold on to the first element | ||
/// (the interface side), and return the second element (the caller side) | ||
/// via [`InterfaceResponse::Pending`]. | ||
/// | ||
/// Once the interface has a response, it can submit it via [`Self::submit`], | ||
/// which consumes this handle and notifies the caller. | ||
#[must_use] | ||
pub fn new() -> (Self, InFlightSystemCallHandle) { | ||
let inner = Arc::new(InFlightSystemCallInner { | ||
state: AtomicU8::new(InFlightState::Pending as u8), | ||
response: UnsafeCell::new(MaybeUninit::uninit()), | ||
}); | ||
|
||
(Self(inner.clone()), InFlightSystemCallHandle(inner)) | ||
} | ||
|
||
/// Checks if the caller canceled the system call. | ||
/// | ||
/// There's no need to check this before submitting a response; however, | ||
/// if the caller is doing something long-running, it may be useful to | ||
/// check, as if this returns `true`, the response will be dropped immediately. | ||
#[inline] | ||
#[must_use] | ||
pub fn canceled(&self) -> bool { | ||
self.0.state.load(Relaxed) == (InFlightState::CallerCanceled as u8) | ||
} | ||
|
||
/// Submits a response to the in-flight system call, consuming the | ||
/// interface side of the handle. | ||
/// | ||
/// **Note:** there is no guarantee that the response will be taken by the | ||
/// caller; if the caller canceled the system call, the response will be | ||
/// dropped. Further, the caller may cancel the system call after this | ||
/// method is called, but before the response is taken. | ||
/// | ||
/// If the interface's processing of this sytem call request is long-winded, | ||
/// and side effects are not a concern, it may be useful to check if the | ||
/// system call was canceled before submitting the response via [`Self::canceled`]. | ||
pub fn submit(self, response: SystemCallResponse) { | ||
// SAFETY: We are the only ones that can write to the response, | ||
// SAFETY: and it can only happen here, once. | ||
unsafe { self.0.response.get().write(MaybeUninit::new(response)) }; | ||
// NOTE(qix-): Even if the caller canceled the system call, this is | ||
// NOTE(qix-): innocuous; it's going to get dropped anyway, and we | ||
// NOTE(qix-): save a branch. | ||
self.0.state.store(InFlightState::Ready as u8, Release); | ||
} | ||
} | ||
|
||
impl Drop for InFlightSystemCall { | ||
fn drop(&mut self) { | ||
self.0 | ||
.state | ||
.compare_exchange( | ||
InFlightState::Pending as u8, | ||
InFlightState::InterfaceCanceled as u8, | ||
Relaxed, | ||
Relaxed, | ||
) | ||
.ok(); | ||
} | ||
} | ||
|
||
impl InFlightSystemCallHandle { | ||
/// Get the current state of the in-flight system call. | ||
#[inline] | ||
#[must_use] | ||
pub fn state(&self) -> InFlightState { | ||
// SAFETY: We control the value entirely; we can transmute it back. | ||
unsafe { ::core::mem::transmute::<u8, InFlightState>(self.0.state.load(Acquire)) } | ||
} | ||
|
||
/// Try to take the response from the in-flight system call. | ||
/// | ||
/// - If the response is ready, it is returned as `Ok(Some(response))`. | ||
/// - If the response is not ready, `Ok(None)` is returned. | ||
/// - If the system call was canceled by the interface, `Err(InFlightState::InterfaceCanceled)` | ||
/// is returned. | ||
/// - Attempts to take the response after it has been taken by the caller will return | ||
/// `Err(InFlightState::Finished)`. | ||
#[inline] | ||
pub fn try_take_response(&self) -> Result<Option<SystemCallResponse>, InFlightState> { | ||
match self.state() { | ||
InFlightState::Pending => Ok(None), | ||
InFlightState::CallerCanceled => unreachable!(), | ||
InFlightState::Ready => { | ||
// SAFETY: We are the only ones that can write to the response, | ||
// SAFETY: and it can only happen once. | ||
self.0.state.store(InFlightState::Finished as u8, Release); | ||
Ok(Some(unsafe { self.0.response.get().read().assume_init() })) | ||
} | ||
status @ (InFlightState::Finished | InFlightState::InterfaceCanceled) => Err(status), | ||
} | ||
} | ||
} | ||
|
||
impl Drop for InFlightSystemCallHandle { | ||
fn drop(&mut self) { | ||
self.0 | ||
.state | ||
.compare_exchange( | ||
InFlightState::Pending as u8, | ||
InFlightState::CallerCanceled as u8, | ||
Relaxed, | ||
Relaxed, | ||
) | ||
.ok(); | ||
} | ||
} | ||
|
||
/// System call request data. | ||
#[derive(Debug, Clone)] | ||
pub struct SystemCallRequest { | ||
/// The opcode. | ||
pub opcode: oro_sysabi::syscall::Opcode, | ||
/// The first argument. | ||
pub arg1: u64, | ||
/// The second argument. | ||
pub arg2: u64, | ||
/// The third argument. | ||
pub arg3: u64, | ||
/// The fourth argument. | ||
pub arg4: u64, | ||
} | ||
|
||
/// System call response data. | ||
#[derive(Debug, Clone, Copy)] | ||
pub struct SystemCallResponse { | ||
/// The error code. | ||
pub error: oro_sysabi::syscall::Error, | ||
/// The return value. | ||
pub ret: u64, | ||
} | ||
|
||
/// Response action from the registry after dispatching a system call. | ||
#[derive(Debug)] | ||
pub enum SystemCallAction { | ||
/// The system call has been processed and the thread should be resumed. | ||
RespondImmediate(SystemCallResponse), | ||
/// The system call has been processed or is in-flight and the thread should be paused. | ||
Pause, | ||
} |
Oops, something went wrong.