char/pipe: implement pipes
This commit is contained in:
parent
2efa5b8ff4
commit
6848f6c56d
@ -13,6 +13,7 @@ kernel-util = { path = "../kernel-util" }
|
|||||||
ygg_driver_block = { path = "../../driver/block/core" }
|
ygg_driver_block = { path = "../../driver/block/core" }
|
||||||
|
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
|
futures-util = { version = "0.3.28", default-features = false, features = ["alloc", "async-await"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hosted-tests = { path = "../hosted-tests" }
|
hosted-tests = { path = "../hosted-tests" }
|
||||||
|
@ -16,11 +16,13 @@ use crate::{
|
|||||||
use self::{
|
use self::{
|
||||||
device::{BlockFile, CharFile},
|
device::{BlockFile, CharFile},
|
||||||
directory::DirectoryFile,
|
directory::DirectoryFile,
|
||||||
|
pipe::PipeEnd,
|
||||||
regular::RegularFile,
|
regular::RegularFile,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod device;
|
mod device;
|
||||||
mod directory;
|
mod directory;
|
||||||
|
mod pipe;
|
||||||
mod regular;
|
mod regular;
|
||||||
|
|
||||||
/// Per-file optional instance data created when a regular file is opened
|
/// Per-file optional instance data created when a regular file is opened
|
||||||
@ -45,9 +47,19 @@ pub enum File {
|
|||||||
Regular(RegularFile),
|
Regular(RegularFile),
|
||||||
Block(BlockFile),
|
Block(BlockFile),
|
||||||
Char(CharFile),
|
Char(CharFile),
|
||||||
|
AnonymousPipe(PipeEnd),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
|
/// Constructs a pipe pair, returning its `(read, write)` ends
|
||||||
|
pub fn new_pipe_pair(capacity: usize) -> (Arc<Self>, Arc<Self>) {
|
||||||
|
let (read, write) = PipeEnd::new_pair(capacity);
|
||||||
|
(
|
||||||
|
Arc::new(Self::AnonymousPipe(read)),
|
||||||
|
Arc::new(Self::AnonymousPipe(write)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn directory(node: NodeRef, position: DirectoryOpenPosition) -> Arc<Self> {
|
pub(crate) fn directory(node: NodeRef, position: DirectoryOpenPosition) -> Arc<Self> {
|
||||||
let position = IrqSafeSpinlock::new(position.into());
|
let position = IrqSafeSpinlock::new(position.into());
|
||||||
Arc::new(Self::Directory(DirectoryFile { node, position }))
|
Arc::new(Self::Directory(DirectoryFile { node, position }))
|
||||||
@ -133,6 +145,7 @@ impl File {
|
|||||||
Self::Regular(file) => Some(&file.node),
|
Self::Regular(file) => Some(&file.node),
|
||||||
Self::Block(file) => Some(&file.node),
|
Self::Block(file) => Some(&file.node),
|
||||||
Self::Char(file) => Some(&file.node),
|
Self::Char(file) => Some(&file.node),
|
||||||
|
Self::AnonymousPipe(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,6 +156,7 @@ impl Read for File {
|
|||||||
Self::Regular(file) => file.read(buf),
|
Self::Regular(file) => file.read(buf),
|
||||||
Self::Block(file) => file.read(buf),
|
Self::Block(file) => file.read(buf),
|
||||||
Self::Char(file) => file.read(buf),
|
Self::Char(file) => file.read(buf),
|
||||||
|
Self::AnonymousPipe(pipe) => pipe.read(buf),
|
||||||
Self::Directory(_) => Err(Error::IsADirectory),
|
Self::Directory(_) => Err(Error::IsADirectory),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,6 +168,7 @@ impl Write for File {
|
|||||||
Self::Regular(file) => file.write(buf),
|
Self::Regular(file) => file.write(buf),
|
||||||
Self::Block(file) => file.write(buf),
|
Self::Block(file) => file.write(buf),
|
||||||
Self::Char(file) => file.write(buf),
|
Self::Char(file) => file.write(buf),
|
||||||
|
Self::AnonymousPipe(pipe) => pipe.write(buf),
|
||||||
Self::Directory(_) => Err(Error::IsADirectory),
|
Self::Directory(_) => Err(Error::IsADirectory),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,7 +179,7 @@ impl Seek for File {
|
|||||||
match self {
|
match self {
|
||||||
Self::Regular(file) => Ok(*file.position.lock()),
|
Self::Regular(file) => Ok(*file.position.lock()),
|
||||||
Self::Block(file) => Ok(*file.position.lock()),
|
Self::Block(file) => Ok(*file.position.lock()),
|
||||||
Self::Char(_) => Err(Error::InvalidOperation),
|
Self::Char(_) | Self::AnonymousPipe(_) => Err(Error::InvalidOperation),
|
||||||
Self::Directory(_) => Err(Error::IsADirectory),
|
Self::Directory(_) => Err(Error::IsADirectory),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,7 +188,7 @@ impl Seek for File {
|
|||||||
match self {
|
match self {
|
||||||
Self::Regular(file) => file.seek(from),
|
Self::Regular(file) => file.seek(from),
|
||||||
Self::Block(file) => file.seek(from),
|
Self::Block(file) => file.seek(from),
|
||||||
Self::Char(_) => Err(Error::InvalidOperation),
|
Self::Char(_) | Self::AnonymousPipe(_) => Err(Error::InvalidOperation),
|
||||||
Self::Directory(_) => Err(Error::IsADirectory),
|
Self::Directory(_) => Err(Error::IsADirectory),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,6 +215,7 @@ impl fmt::Debug for File {
|
|||||||
.field("write", &file.write)
|
.field("write", &file.write)
|
||||||
.finish_non_exhaustive(),
|
.finish_non_exhaustive(),
|
||||||
Self::Directory(_) => f.debug_struct("DirectoryFile").finish_non_exhaustive(),
|
Self::Directory(_) => f.debug_struct("DirectoryFile").finish_non_exhaustive(),
|
||||||
|
Self::AnonymousPipe(_) => f.debug_struct("AnonymousPipe").finish_non_exhaustive(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,6 +226,7 @@ mod tests {
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use kernel_util::sync::IrqSafeSpinlock;
|
use kernel_util::sync::IrqSafeSpinlock;
|
||||||
|
use ygg_driver_block::BlockDevice;
|
||||||
use yggdrasil_abi::{
|
use yggdrasil_abi::{
|
||||||
error::Error,
|
error::Error,
|
||||||
io::{DirectoryEntry, FileType, OpenOptions, SeekFrom},
|
io::{DirectoryEntry, FileType, OpenOptions, SeekFrom},
|
||||||
@ -217,7 +234,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
device::{BlockDevice, CharDevice},
|
device::CharDevice,
|
||||||
file::DirectoryOpenPosition,
|
file::DirectoryOpenPosition,
|
||||||
impls::const_value_node,
|
impls::const_value_node,
|
||||||
node::{AccessToken, CommonImpl, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl},
|
node::{AccessToken, CommonImpl, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl},
|
||||||
|
226
lib/vfs/src/file/pipe.rs
Normal file
226
lib/vfs/src/file/pipe.rs
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
use core::{
|
||||||
|
pin::Pin,
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
|
||||||
|
use alloc::{sync::Arc, vec, vec::Vec};
|
||||||
|
use futures_util::{task::AtomicWaker, Future};
|
||||||
|
use kernel_util::{block, sync::IrqSafeSpinlock};
|
||||||
|
use yggdrasil_abi::error::Error;
|
||||||
|
|
||||||
|
struct PipeInner {
|
||||||
|
data: Vec<u8>,
|
||||||
|
capacity: usize,
|
||||||
|
rd: usize,
|
||||||
|
wr: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Pipe {
|
||||||
|
inner: IrqSafeSpinlock<PipeInner>,
|
||||||
|
shutdown: AtomicBool,
|
||||||
|
read_notify: AtomicWaker,
|
||||||
|
write_notify: AtomicWaker,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PipeEnd {
|
||||||
|
Read(Arc<Pipe>),
|
||||||
|
Write(Arc<Pipe>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PipeInner {
|
||||||
|
pub fn new(capacity: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
data: vec![0; capacity],
|
||||||
|
capacity,
|
||||||
|
rd: 0,
|
||||||
|
wr: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_write(&self) -> bool {
|
||||||
|
(self.wr + 1) % self.capacity != self.rd
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_read(&self) -> bool {
|
||||||
|
self.rd != self.wr
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn write(&mut self, val: u8) {
|
||||||
|
self.data[self.wr] = val;
|
||||||
|
self.wr = (self.wr + 1) % self.capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn read(&mut self) -> u8 {
|
||||||
|
let val = self.data[self.rd];
|
||||||
|
self.rd = (self.rd + 1) % self.capacity;
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_write(&mut self, val: u8) -> bool {
|
||||||
|
if self.can_write() {
|
||||||
|
unsafe {
|
||||||
|
self.write(val);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_read(&mut self) -> Option<u8> {
|
||||||
|
if self.can_read() {
|
||||||
|
Some(unsafe { self.read() })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pipe {
|
||||||
|
pub fn new(capacity: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: IrqSafeSpinlock::new(PipeInner::new(capacity)),
|
||||||
|
shutdown: AtomicBool::new(false),
|
||||||
|
read_notify: AtomicWaker::new(),
|
||||||
|
write_notify: AtomicWaker::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown(&self) {
|
||||||
|
self.shutdown.store(true, Ordering::Release);
|
||||||
|
self.read_notify.wake();
|
||||||
|
self.write_notify.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blocking_write(&self, val: u8) -> impl Future<Output = Result<(), Error>> + '_ {
|
||||||
|
struct F<'a> {
|
||||||
|
pipe: &'a Pipe,
|
||||||
|
val: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Future for F<'a> {
|
||||||
|
type Output = Result<(), Error>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut lock = self.pipe.inner.lock();
|
||||||
|
|
||||||
|
// Try fast path before acquiring write notify to avoid unnecessary contention
|
||||||
|
if self.pipe.shutdown.load(Ordering::Acquire) {
|
||||||
|
// TODO BrokenPipe
|
||||||
|
return Poll::Ready(Err(Error::ReadOnly));
|
||||||
|
} else if lock.try_write(self.val) {
|
||||||
|
self.pipe.read_notify.wake();
|
||||||
|
return Poll::Ready(Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pipe.write_notify.register(cx.waker());
|
||||||
|
|
||||||
|
if self.pipe.shutdown.load(Ordering::Acquire) {
|
||||||
|
Poll::Ready(Err(Error::ReadOnly))
|
||||||
|
} else if lock.try_write(self.val) {
|
||||||
|
self.pipe.read_notify.wake();
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
F { pipe: self, val }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blocking_read(&self) -> impl Future<Output = Option<u8>> + '_ {
|
||||||
|
struct F<'a> {
|
||||||
|
pipe: &'a Pipe,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Future for F<'a> {
|
||||||
|
type Output = Option<u8>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut lock = self.pipe.inner.lock();
|
||||||
|
|
||||||
|
if let Some(val) = lock.try_read() {
|
||||||
|
self.pipe.write_notify.wake();
|
||||||
|
return Poll::Ready(Some(val));
|
||||||
|
} else if self.pipe.shutdown.load(Ordering::Acquire) {
|
||||||
|
return Poll::Ready(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pipe.read_notify.register(cx.waker());
|
||||||
|
|
||||||
|
if let Some(val) = lock.try_read() {
|
||||||
|
Poll::Ready(Some(val))
|
||||||
|
} else if self.pipe.shutdown.load(Ordering::Acquire) {
|
||||||
|
Poll::Ready(None)
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
F { pipe: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PipeEnd {
|
||||||
|
pub fn new_pair(capacity: usize) -> (PipeEnd, PipeEnd) {
|
||||||
|
let pipe = Arc::new(Pipe::new(capacity));
|
||||||
|
let read = PipeEnd::Read(pipe.clone());
|
||||||
|
let write = PipeEnd::Write(pipe);
|
||||||
|
|
||||||
|
(read, write)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||||
|
let PipeEnd::Read(read) = self else {
|
||||||
|
return Err(Error::InvalidOperation);
|
||||||
|
};
|
||||||
|
|
||||||
|
block! {
|
||||||
|
let mut pos = 0;
|
||||||
|
let mut rem = buf.len();
|
||||||
|
|
||||||
|
while rem != 0 {
|
||||||
|
if let Some(val) = read.blocking_read().await {
|
||||||
|
buf[pos] = val;
|
||||||
|
pos += 1;
|
||||||
|
rem -= 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(pos)
|
||||||
|
}?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self, buf: &[u8]) -> Result<usize, Error> {
|
||||||
|
let PipeEnd::Write(write) = self else {
|
||||||
|
return Err(Error::InvalidOperation);
|
||||||
|
};
|
||||||
|
|
||||||
|
block! {
|
||||||
|
let mut pos = 0;
|
||||||
|
let mut rem = buf.len();
|
||||||
|
|
||||||
|
while rem != 0 {
|
||||||
|
write.blocking_write(buf[pos]).await?;
|
||||||
|
pos += 1;
|
||||||
|
rem -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(pos)
|
||||||
|
}?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for PipeEnd {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
match self {
|
||||||
|
Self::Read(read) => read.shutdown(),
|
||||||
|
Self::Write(write) => write.shutdown(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -62,12 +62,20 @@ impl ProcessIo {
|
|||||||
/// Removes and closes a [FileRef] from the struct
|
/// Removes and closes a [FileRef] from the struct
|
||||||
pub fn close_file(&mut self, fd: RawFd) -> Result<(), Error> {
|
pub fn close_file(&mut self, fd: RawFd) -> Result<(), Error> {
|
||||||
// Do nothing, file will be dropped and closed
|
// Do nothing, file will be dropped and closed
|
||||||
self.files.remove(&fd).ok_or(Error::InvalidFile)?;
|
if self.files.remove(&fd).is_some() {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidFile)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all [FileRef]s from the struct which do not pass the `predicate` check
|
/// Removes all [FileRef]s from the struct which do not pass the `predicate` check
|
||||||
pub fn retain<F: Fn(&RawFd, &mut FileRef) -> bool>(&mut self, predicate: F) {
|
pub fn retain<F: Fn(&RawFd, &mut FileRef) -> bool>(&mut self, predicate: F) {
|
||||||
self.files.retain(predicate);
|
self.files.retain(predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles process exit by closing all of its files
|
||||||
|
pub fn handle_exit(&mut self) {
|
||||||
|
self.files.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ use abi::{
|
|||||||
};
|
};
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use kernel_util::{block, runtime, sync::IrqSafeSpinlockGuard};
|
use kernel_util::{block, runtime, sync::IrqSafeSpinlockGuard};
|
||||||
use vfs::{IoContext, NodeRef, Read, Seek, Write};
|
use vfs::{File, IoContext, NodeRef, Read, Seek, Write};
|
||||||
use yggdrasil_abi::{error::SyscallResult, io::MountOptions};
|
use yggdrasil_abi::{error::SyscallResult, io::MountOptions};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -188,7 +188,17 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
|
|||||||
let fd = RawFd(args[0] as u32);
|
let fd = RawFd(args[0] as u32);
|
||||||
|
|
||||||
run_with_io(process, |mut io| {
|
run_with_io(process, |mut io| {
|
||||||
io.close_file(fd)?;
|
let res = io.close_file(fd);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Err(Error::InvalidFile) => {
|
||||||
|
warnln!("Double close of fd {:?} in process {}", fd, process.id());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
res?;
|
||||||
|
|
||||||
Ok(0)
|
Ok(0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -262,6 +272,24 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
|
|||||||
SyscallFunction::RemoveDirectory => {
|
SyscallFunction::RemoveDirectory => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
SyscallFunction::CreatePipe => {
|
||||||
|
let ends: &mut [MaybeUninit<RawFd>; 2] = arg_user_mut(args[0] as usize)?;
|
||||||
|
|
||||||
|
run_with_io(process, |mut io| {
|
||||||
|
let (read, write) = File::new_pipe_pair(256);
|
||||||
|
|
||||||
|
let read_fd = io.place_file(read)?;
|
||||||
|
let write_fd = io.place_file(write)?;
|
||||||
|
|
||||||
|
infoln!("Read end: {:?}", read_fd);
|
||||||
|
infoln!("Write end: {:?}", write_fd);
|
||||||
|
|
||||||
|
ends[0].write(read_fd);
|
||||||
|
ends[1].write(write_fd);
|
||||||
|
|
||||||
|
Ok(0)
|
||||||
|
})
|
||||||
|
}
|
||||||
// Process management
|
// Process management
|
||||||
SyscallFunction::SpawnProcess => {
|
SyscallFunction::SpawnProcess => {
|
||||||
let options = arg_user_ref::<SpawnOptions>(args[0] as usize)?;
|
let options = arg_user_ref::<SpawnOptions>(args[0] as usize)?;
|
||||||
|
@ -346,7 +346,7 @@ impl Process {
|
|||||||
inner.state = ProcessState::Terminated(code);
|
inner.state = ProcessState::Terminated(code);
|
||||||
|
|
||||||
// XXX
|
// XXX
|
||||||
// self.io.lock().handle_exit();
|
self.io.lock().handle_exit();
|
||||||
|
|
||||||
drop(inner);
|
drop(inner);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user