From 4830f3eb2ccc1be39d47479e8d9920f54ea2b59a Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Mon, 5 Apr 2021 18:18:48 +0800 Subject: [PATCH] kernel: server: implement DestroyServer(sid) This implements a syscall to shut down a server. Servers may not be shut down if they have outstanding messages that they have seen but not responded to. Signed-off-by: Sean Cross --- kernel/src/server.rs | 260 +++++++++++++++++++++++++++++++++++------ kernel/src/services.rs | 73 ++++++++++-- kernel/src/syscall.rs | 11 +- 3 files changed, 297 insertions(+), 47 deletions(-) diff --git a/kernel/src/server.rs b/kernel/src/server.rs index f05e638dc..021837184 100644 --- a/kernel/src/server.rs +++ b/kernel/src/server.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 pub use crate::arch::process::Thread; +use crate::{mem::MemoryManager, services::SystemServices}; use core::mem; use xous_kernel::{MemoryAddress, MemoryRange, MemorySize, Message, MessageSender, PID, SID, TID}; @@ -111,7 +112,7 @@ enum QueuedMessage { u16, /* client PID */ u8, /* client TID */ u8, /* message index */ - usize, /* server return address */ + usize, /* reserved */ usize, /* id */ usize, /* arg1 */ usize, /* arg2 */ @@ -122,7 +123,7 @@ enum QueuedMessage { u16, /* client PID */ u8, /* client TID */ u8, /* message index */ - usize, /* server return address */ + usize, /* reserved */ usize, /* id */ usize, /* arg1 */ usize, /* arg2 */ @@ -133,9 +134,9 @@ enum QueuedMessage { u16, /* client PID */ u8, /* client TID */ u8, /* message index */ - usize, /* reserved */ + usize, /* client memory address */ usize, /* id */ - usize, /* buf */ + usize, /* server memory address */ usize, /* buf_size */ usize, /* offset */ usize, /* valid */ @@ -144,9 +145,9 @@ enum QueuedMessage { u16, /* client PID */ u8, /* client TID */ u8, /* message index */ - usize, /* address of memory base in server */ + usize, /* client memory address */ usize, /* id */ - usize, /* buf */ + usize, /* server memory address */ usize, /* buf_size */ usize, /* offset */ usize, /* valid */ @@ -155,9 +156,9 @@ enum QueuedMessage { u16, /* client PID */ u8, /* client TID */ u8, /* message index */ - usize, /* address of memory base in server */ + usize, /* client memory address */ usize, /* id */ - usize, /* buf */ + usize, /* server memory address */ usize, /* buf_size */ usize, /* offset */ usize, /* valid */ @@ -169,9 +170,9 @@ enum QueuedMessage { u16, /* client PID */ u8, /* client TID */ u8, /* message index */ - usize, /* address of memory base in server */ + usize, /* client memory address */ usize, /* id */ - usize, /* buf */ + usize, /* server memory address */ usize, /* buf_size */ usize, /* offset */ usize, /* valid */ @@ -183,9 +184,9 @@ enum QueuedMessage { u16, /* client PID */ u8, /* client TID */ u8, /* message index */ - usize, /* address of memory base in server */ + usize, /* client memory address */ usize, /* id */ - usize, /* buf */ + usize, /* server memory address */ usize, /* buf_size */ usize, /* offset */ usize, /* valid */ @@ -197,7 +198,7 @@ enum QueuedMessage { u16, /* client PID */ u8, /* client TID */ u8, /* message index */ - usize, /* server return address */ + usize, /* reserved */ usize, /* id */ usize, /* arg1 */ usize, /* arg2 */ @@ -239,6 +240,22 @@ enum QueuedMessage { ), } +impl QueuedMessage { + /// Return `true` if this Queued Message is sitting inside of the Server, and + /// is therefore waiting to be returned. + /// This only indicates messages that have been seen by the Server and have + /// not yet been responded to -- Messages that have not yet been seen by the + /// Server will return `false`. + fn is_in_server(&self) -> bool { + match self { + &QueuedMessage::WaitingForget(_, _, _, _, _, _) + | &QueuedMessage::WaitingReturnMemory(_, _, _, _, _, _) + | &QueuedMessage::WaitingReturnScalar(_, _, _, _) => true, + _ => false, + } + } +} + impl Server { /// Initialize a server in the given option array. This function is /// designed to be called with `new` pointing to an entry in a vec. @@ -260,7 +277,7 @@ impl Server { let queue = unsafe { core::slice::from_raw_parts_mut( _backing.as_mut_ptr() as *mut QueuedMessage, - _backing.len() / mem::size_of::(), + _backing.len() / mem::size_of::(), ) }; @@ -289,12 +306,182 @@ impl Server { } /// Take a current slot and replace it with `None`, clearing out the contents of the queue. - pub fn destroy(current: &mut Option) -> Result<(), xous_kernel::Error> { - if let Some(mut server) = current.take() { - server.queue_head = 0; - server.queue_tail = 0; - server.ready_threads = 0; + /// Returns an error if the queue has any waiting elements. + /// Returns a list of threads that should be readied. + pub fn destroy( + mut self, + ss: &mut SystemServices, + ) -> Result<(), Self> { + + // First determine if the server has any Waiting messages. That is, any messages + // that are currently sitting in the memory space of the Server. These cannot + // safely be handled, and will cause an error if we try to mess with them. + for queue_entry in self.queue.iter() { + if queue_entry.is_in_server() { + return Err(self); + } } + + // We now know there will be no problems in shutting down this server. Look + // through the queue and respond to each message in turn. + for entry in self.queue.iter_mut() { + match *entry { + // If there are `Waiting` messages, then something is seriously wrong because + // we already determined above that this wouldn't happen. + QueuedMessage::WaitingForget(_, _, _, _, _, _) + | QueuedMessage::WaitingReturnMemory(_, _, _, _, _, _) + | QueuedMessage::WaitingReturnScalar(_, _, _, _) => panic!("message was waiting"), + + // For `Empty` and `Scalar` messages, all we have to do is ignore them. + // The sending process will not be blocked. These messages will be dropped, + // and the server will never see them. + QueuedMessage::Empty | QueuedMessage::ScalarMessage(_, _, _, _, _, _, _, _, _) => { + () + } + + // For `Send` messages, the Server has not yet seen these messages. Simply + // prevent this memory from getting mapped into the Server and free it. + QueuedMessage::MemoryMessageSend( + _pid, + _tid, + _idx, + _client_memory_addr, + _id, + server_memory_addr, + memory_length, + _offset, + _valid, + ) => { + MemoryManager::with_mut(|mm| { + let mut result = Ok(xous_kernel::Result::Ok); + let virt = server_memory_addr; + let size = memory_length; + if virt & 0xfff != 0 { + return Err(xous_kernel::Error::BadAlignment); + } + for addr in (virt..(virt + size)).step_by(crate::mem::PAGE_SIZE) { + if let Err(e) = mm.unmap_page(addr as *mut usize) { + if result.is_ok() { + result = Err(e); + } + } + } + result + }) + .unwrap(); + } + + // For BlockingScalar messages, the client is waiting for a response. + // Unblock the client and return an error indicating the server does + // not exist. + QueuedMessage::BlockingScalarTerminated(pid, tid, _, _, _, _, _, _, _) + | QueuedMessage::BlockingScalarMessage(pid, tid, _, _, _, _, _, _, _) => { + let pid = PID::new(pid as _).unwrap(); + let tid = tid as _; + + // Set the return value of the specified thread. + ss.set_thread_result( + pid, + tid, + xous_kernel::Result::Error(xous_kernel::Error::ServerNotFound), + ) + .unwrap(); + + // Mark it as ready to run. + ss.ready_thread(pid, tid).unwrap(); + } + + QueuedMessage::MemoryMessageROLend( + client_pid, + client_tid, + _idx, + client_addr, + _id, + server_addr, + buf_size, + _, + _, + ) + | QueuedMessage::MemoryMessageRWLend( + client_pid, + client_tid, + _idx, + client_addr, + _id, + server_addr, + buf_size, + _, + _, + ) + | QueuedMessage::MemoryMessageROLendTerminated( + client_pid, + client_tid, + _idx, + client_addr, + _id, + server_addr, + buf_size, + _, + _, + ) + | QueuedMessage::MemoryMessageRWLendTerminated( + client_pid, + client_tid, + _idx, + client_addr, + _id, + server_addr, + buf_size, + _, + _, + ) => { + let client_pid = PID::new(client_pid as _).unwrap(); + let client_tid = client_tid as _; + // Return the memory to the calling process + ss.return_memory( + server_addr as *mut u8, + client_pid, + client_tid, + client_addr as _, + buf_size, + ) + .unwrap(); + ss.ready_thread(client_pid, client_tid).unwrap(); + ss.set_thread_result( + client_pid, + client_tid, + xous_kernel::Result::Error(xous_kernel::Error::ServerNotFound), + ) + .unwrap(); + } + } + *entry = QueuedMessage::Empty; + } + + let server_pid = ss.current_pid(); + + // Finally, wake up all threads that are waiting on this Server. + while let Some(server_tid) = self.take_available_thread() { + ss.ready_thread(server_pid, server_tid).unwrap(); + ss.set_thread_result( + server_pid, + server_tid, + xous_kernel::Result::Error(xous_kernel::Error::ServerNotFound), + ) + .unwrap(); + } + + // Release the backing memory + #[cfg(baremetal)] + MemoryManager::with_mut(|mm| { + let virt = self.queue.as_mut_ptr() as usize; + let size = self.queue.len(); + for addr in (virt..(virt + size)).step_by(crate::arch::mem::PAGE_SIZE) { + mm.unmap_page(addr as *mut usize).unwrap(); + } + }); + + // The server should now be destroyed. Ok(()) } @@ -392,7 +579,8 @@ impl Server { .get_mut(message_index) .ok_or(xous_kernel::Error::BadAddress)?; // klog!("memory in queue[{}]: {:?}", message_index, current_val); - let (pid, tid, _idx, server_addr, client_addr, len, forget, is_memory) = match *current_val { + let (pid, tid, _idx, server_addr, client_addr, len, forget, is_memory) = match *current_val + { QueuedMessage::WaitingReturnMemory(pid, tid, idx, server_addr, client_addr, len) => { (pid, tid, idx, server_addr, client_addr, len, false, true) } @@ -509,7 +697,7 @@ impl Server { idx, client_addr, id, - buf, + server_addr, buf_size, offset, valid, @@ -520,7 +708,7 @@ impl Server { sender: sender.into(), body: xous_kernel::Message::Borrow(xous_kernel::MemoryMessage { id, - buf: MemoryRange::new(buf, buf_size).ok()?, + buf: MemoryRange::new(server_addr, buf_size).ok()?, offset: MemorySize::new(offset), valid: MemorySize::new(valid), }), @@ -529,7 +717,7 @@ impl Server { pid, tid, idx, - buf, + server_addr, client_addr, buf_size, ), @@ -541,7 +729,7 @@ impl Server { idx, client_addr, id, - buf, + server_addr, buf_size, offset, valid, @@ -552,7 +740,7 @@ impl Server { sender: sender.into(), body: xous_kernel::Message::MutableBorrow(xous_kernel::MemoryMessage { id, - buf: MemoryRange::new(buf, buf_size).ok()?, + buf: MemoryRange::new(server_addr, buf_size).ok()?, offset: MemorySize::new(offset), valid: MemorySize::new(valid), }), @@ -561,7 +749,7 @@ impl Server { pid, tid, idx, - buf, + server_addr, client_addr, buf_size, ), @@ -573,7 +761,7 @@ impl Server { idx, client_addr, id, - buf, + server_addr, buf_size, offset, valid, @@ -584,7 +772,7 @@ impl Server { sender: sender.into(), body: xous_kernel::Message::Borrow(xous_kernel::MemoryMessage { id, - buf: MemoryRange::new(buf, buf_size).ok()?, + buf: MemoryRange::new(server_addr, buf_size).ok()?, offset: MemorySize::new(offset), valid: MemorySize::new(valid), }), @@ -593,7 +781,7 @@ impl Server { pid, tid, idx, - buf, + server_addr, client_addr, buf_size, ), @@ -605,7 +793,7 @@ impl Server { idx, client_addr, id, - buf, + server_addr, buf_size, offset, valid, @@ -616,7 +804,7 @@ impl Server { sender: sender.into(), body: xous_kernel::Message::MutableBorrow(xous_kernel::MemoryMessage { id, - buf: MemoryRange::new(buf, buf_size).ok()?, + buf: MemoryRange::new(server_addr, buf_size).ok()?, offset: MemorySize::new(offset), valid: MemorySize::new(valid), }), @@ -625,7 +813,7 @@ impl Server { pid, tid, idx, - buf, + server_addr, client_addr, buf_size, ), @@ -666,7 +854,7 @@ impl Server { idx, _reserved, id, - buf, + server_addr, buf_size, offset, valid, @@ -676,7 +864,7 @@ impl Server { sender: sender.into(), body: xous_kernel::Message::Move(xous_kernel::MemoryMessage { id, - buf: MemoryRange::new(buf, buf_size).ok()?, + buf: MemoryRange::new(server_addr, buf_size).ok()?, offset: MemorySize::new(offset), valid: MemorySize::new(valid), }), @@ -832,7 +1020,7 @@ impl Server { pid.get() as _, tid as _, self.tail_generation, - original_address.map(|x| x.get()).unwrap_or(0), + 0, msg.id, msg.arg1, msg.arg2, @@ -843,7 +1031,7 @@ impl Server { pid.get() as _, tid as _, self.tail_generation, - original_address.map(|x| x.get()).unwrap_or(0), + 0, msg.id, msg.arg1, msg.arg2, diff --git a/kernel/src/services.rs b/kernel/src/services.rs index 4aa30c7ec..97e402d03 100644 --- a/kernel/src/services.rs +++ b/kernel/src/services.rs @@ -1047,6 +1047,12 @@ impl SystemServices { } let current_pid = self.current_pid(); + + // If the dest and src PID is the same, do nothing. + if current_pid == dest_pid { + return Ok(src_virt); + } + let src_mapping = self.get_process(current_pid)?.mapping; let dest_mapping = self.get_process(dest_pid)?.mapping; crate::mem::MemoryManager::with_mut(|mm| { @@ -1219,12 +1225,10 @@ impl SystemServices { pub fn return_memory( &mut self, src_virt: *mut u8, - _src_tid: TID, dest_pid: PID, _dest_tid: TID, dest_virt: *mut u8, len: usize, - _buf: MemoryRange, ) -> Result<*mut u8, xous_kernel::Error> { // klog!( // "Returning from {}:{} to {}:{}", @@ -1251,6 +1255,10 @@ impl SystemServices { } let current_pid = self.current_pid(); + // If it's within the same process, ignore the operation. + if current_pid == dest_pid { + return Ok(src_virt); + } let src_mapping = self.get_process(current_pid)?.mapping; let dest_mapping = self.get_process(dest_pid)?.mapping; use crate::mem::MemoryManager; @@ -1287,13 +1295,13 @@ impl SystemServices { pub fn return_memory( &mut self, src_virt: *mut u8, - _src_tid: TID, dest_pid: PID, dest_tid: TID, _dest_virt: *mut u8, - _len: usize, - buf: MemoryRange, + len: usize, + // buf: MemoryRange, ) -> Result<*mut u8, xous_kernel::Error> { + let buf = MemoryRange::new(src_virt as usize, len)?; let buf = unsafe { core::slice::from_raw_parts(buf.as_ptr(), buf.len()) }; let current_pid = self.current_pid(); { @@ -1447,6 +1455,55 @@ impl SystemServices { Ok(sid) } + /// Destroy the provided server ID and disconnect any processes that are + /// connected. + pub fn destroy_server(&mut self, pid: PID, sid: SID) -> Result<(), xous_kernel::Error> { + let mut idx_to_destroy = None; + // Look through the server list for a server that matches this SID + for (idx, entry) in self.servers.iter().enumerate() { + if let Some(server) = entry { + if server.sid == sid && server.pid == pid { + idx_to_destroy = Some(idx); + break; + } + } + } + + let server_idx = idx_to_destroy.ok_or(xous_kernel::Error::ServerNotFound)?; + let server = self.servers[server_idx].take().unwrap(); + // Try to destroy the server. This will fail if the server + // has any outstanding memory requests. + server.destroy(self).map_err(|server| { + self.servers[server_idx] = Some(server); + xous_kernel::Error::ServerQueueFull + })?; + + let pid = crate::arch::process::current_pid(); + // println!("KERNEL({}): Server table: {:?}", _pid.get(), self.servers); + // Disconnect this server from all processes. + for process in self.processes.iter_mut() { + if !process.free() { + process.activate().unwrap(); + ArchProcess::with_inner_mut(|process_inner| { + // Look through the connection map for (1) a free slot, and (2) an + // existing connection + for server_idx_opt in process_inner.connection_map.iter_mut() { + if let Some(client_server_idx) = server_idx_opt { + if client_server_idx.get() == (server_idx + 2) as _ { + *server_idx_opt = None; + continue; + } + } + } + }); + } + } + + // Switch back to the primary process. + self.get_process(pid).unwrap().activate().unwrap(); + Ok(()) + } + /// Connect to a server on behalf of another process. pub fn connect_process_to_server( &mut self, @@ -1767,9 +1824,9 @@ impl SystemServices { /// Calls the provided function with the current inner process state. pub fn shutdown(&mut self) -> Result<(), xous_kernel::Error> { // Destroy all servers. This will cause all queued messages to be lost. - for server in &mut self.servers { - if server.is_some() { - Server::destroy(server).unwrap(); + for server_idx in 0..self.servers.len() { + if let Some(server) = self.servers[server_idx].take() { + server.destroy(self).unwrap(); } } diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index d14c3ae65..ad82d9be2 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -323,16 +323,18 @@ fn return_memory( // client_pid, // client_tid // ); + #[cfg(baremetal)] + let src_virt = server_addr.get() as _; + #[cfg(not(baremetal))] + let src_virt = buf.addr.get() as _; // Return the memory to the calling process ss.return_memory( - server_addr.get() as _, - tid, + src_virt, client_pid, client_tid, client_addr.get() as _, len.get(), - buf, )?; // Unblock the client context to allow it to continue. @@ -844,6 +846,9 @@ pub fn handle_inner(pid: PID, tid: TID, in_irq: bool, call: SysCall) -> SysCallR ss.disconnect_from_server(cid) .and(Ok(xous_kernel::Result::Ok)) }), + SysCall::DestroyServer(sid) => SystemServices::with_mut(|ss| { + ss.destroy_server(pid, sid).and(Ok(xous_kernel::Result::Ok)) + }), _ => Err(xous_kernel::Error::UnhandledSyscall), } }