diff --git a/src/context/file.rs b/src/context/file.rs index 27603428f8..861852392d 100644 --- a/src/context/file.rs +++ b/src/context/file.rs @@ -26,7 +26,8 @@ pub struct FileDescription { bitflags! { #[derive(Clone, Copy, Debug)] pub struct InternalFlags: u32 { - const POSITIONED = 1; + const POSITIONED = 1 << 0; + const NOTIFY_ON_NEXT_DETACH = 1 << 1; } } impl FileDescription { @@ -82,9 +83,21 @@ impl FileDescription { impl FileDescriptor { pub fn close(self, token: &mut CleanLockToken) -> Result<()> { + { + let description = self.description.read(); + if description + .internal_flags + .contains(InternalFlags::NOTIFY_ON_NEXT_DETACH) + { + let scheme = scheme::get_scheme(token.token(), description.scheme)?; + scheme.detach(description.number, token)?; + } + } + if let Ok(file) = Arc::try_unwrap(self.description).map(RwLock::into_inner) { file.try_close(token)?; } + Ok(()) } } diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index a6203c4d38..2e1d2050e7 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -652,6 +652,9 @@ pub trait KernelScheme: Send + Sync + 'static { fn close(&self, id: usize, token: &mut CleanLockToken) -> Result<()> { Ok(()) } + fn detach(&self, id: usize, token: &mut CleanLockToken) -> Result<()> { + Ok(()) + } fn kcall( &self, id: usize, @@ -665,6 +668,7 @@ pub trait KernelScheme: Send + Sync + 'static { fn kstdfscall( &self, id: usize, + desc: Arc>, payload: UserSliceRw, flags: CallFlags, metadata: &[u64], diff --git a/src/scheme/user.rs b/src/scheme/user.rs index 2cf843fc47..25777da41a 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -68,7 +68,7 @@ enum State { #[derive(Debug)] enum Response { - Regular(Result, u8), + Regular(Result, u8, bool), Fd(Arc>), MultipleFds(Option>>>), } @@ -76,7 +76,7 @@ enum Response { impl Response { fn as_regular(self) -> Result { match self { - Response::Regular(res, _) => res, + Response::Regular(res, _, _) => res, Response::Fd(_) | Response::MultipleFds(_) => Err(Error::new(EIO)), } } @@ -116,6 +116,11 @@ enum ParsedCqe { base_addr: VirtualAddress, page_count: usize, }, + RespondAndNotifyOnDetach { + tag: u32, + res: Result, + extra0: u8, + }, } impl ParsedCqe { fn parse_cqe(cqe: &Cqe) -> Result { @@ -126,6 +131,11 @@ impl ParsedCqe { res: Error::demux(cqe.result as usize), extra0: cqe.extra_raw[0], }, + CqeOpcode::RespondAndNotifyOnDetach => Self::RespondAndNotifyOnDetach { + tag: cqe.tag, + res: Error::demux(cqe.result as usize), + extra0: cqe.extra_raw[0], + }, CqeOpcode::RespondWithFd => Self::ResponseWithFd { tag: cqe.tag, fd: cqe.result as usize, @@ -765,7 +775,10 @@ impl UserInner { fn handle_parsed(&self, cqe: &ParsedCqe, token: &mut CleanLockToken) -> Result<()> { match *cqe { ParsedCqe::RegularResponse { tag, res, extra0 } => { - self.respond(tag, Response::Regular(res, extra0), token)? + self.respond(tag, Response::Regular(res, extra0, false), token)? + } + ParsedCqe::RespondAndNotifyOnDetach { tag, res, extra0 } => { + self.respond(tag, Response::Regular(res, extra0, true), token)? } ParsedCqe::ResponseWithFd { tag, fd } => self.respond( tag, @@ -917,7 +930,7 @@ impl UserInner { } => { // Convert ECANCELED to EINTR if a request was being canceled (currently always // due to signals). - if let Response::Regular(ref mut res, _) = response + if let Response::Regular(ref mut res, _, _) = response && canceling && *res == Err(Error::new(ECANCELED)) { @@ -925,7 +938,7 @@ impl UserInner { } // TODO: Require ECANCELED? - if let Response::Regular(ref mut res, _) = response + if let Response::Regular(ref mut res, _, _) = response && !canceling && *res == Err(Error::new(EINTR)) { @@ -1362,7 +1375,7 @@ impl KernelScheme for UserScheme { address.release()?; match result? { - Response::Regular(res, fl) => Ok({ + Response::Regular(res, fl, _) => Ok({ let fd = res?; OpenResult::SchemeLocal( fd, @@ -1583,6 +1596,25 @@ impl KernelScheme for UserScheme { Ok(()) } + + fn detach(&self, id: usize, token: &mut CleanLockToken) -> Result<()> { + println!("detach(id={id}"); + let ctx = { context::current().read(token.token()).caller_ctx() }; + self.inner.todo.send( + Sqe { + opcode: Opcode::Detach as u8, + sqe_flags: SqeFlags::empty(), + _rsvd: 0, + tag: 0, + args: [id as u64, 0, 0, 0, 0, 0], + caller: ctx.pid as u64, + }, + token, + ); + event::trigger(self.inner.root_id, self.inner.scheme_id.get(), EVENT_READ); + Ok(()) + } + fn kdup( &self, file: usize, @@ -1604,7 +1636,7 @@ impl KernelScheme for UserScheme { address.release()?; match result? { - Response::Regular(res, fl) => Ok({ + Response::Regular(res, fl, _) => Ok({ let fd = res?; OpenResult::SchemeLocal( fd, @@ -1864,6 +1896,7 @@ impl KernelScheme for UserScheme { fn kstdfscall( &self, id: usize, + desc: Arc>, payload: UserSliceRw, _flags: CallFlags, metadata: &[u64], @@ -1894,10 +1927,18 @@ impl KernelScheme for UserScheme { let len = dst.len().min(metadata.len()); dst[..len].copy_from_slice(&metadata[..len]); } - inner - .call_inner(Vec::new(), sqe, address.span(), token)? - .as_regular() + + match inner.call_inner(Vec::new(), sqe, address.span(), token)? { + Response::Regular(res, _, notify_on_detach) => { + desc.write() + .internal_flags + .set(InternalFlags::NOTIFY_ON_NEXT_DETACH, notify_on_detach); + res + } + _ => Err(Error::new(EIO)), + } } + fn kfdwrite( &self, number: usize, @@ -1960,7 +2001,7 @@ impl KernelScheme for UserScheme { )?; let descriptions_opt = match res { - Response::Regular(res, _) => { + Response::Regular(res, _, _) => { return match res { Ok(_) => Err(Error::new(EIO)), Err(e) => Err(e),