Smp (#23)
* Fire up multiple processors * Use IPIs to wake up secondary processors * Much better exception information * Modifications to show more information on fault * WIP: Use real libstd * Add TLS (not complete) * Add random function, export getpid, cleanup * Do not spin APs until new context * Update rust * Update rust * Use rd/wrfsbase * Implement TLS * Implement compiler builtins and update rust * Update rust * Back to Redox libstd * Update rust
This commit is contained in:
parent
25dc44b348
commit
149b0297a4
54 changed files with 1121 additions and 380 deletions
|
@ -5,7 +5,7 @@ use spin::Mutex;
|
|||
|
||||
use arch;
|
||||
use context::file::File;
|
||||
use context::memory::{Grant, Memory, SharedMemory};
|
||||
use context::memory::{Grant, Memory, SharedMemory, Tls};
|
||||
use syscall::data::Event;
|
||||
use sync::{WaitCondition, WaitQueue};
|
||||
|
||||
|
@ -36,7 +36,7 @@ pub struct Context {
|
|||
/// Context running or not
|
||||
pub running: bool,
|
||||
/// CPU ID, if locked
|
||||
pub cpuid: Option<usize>,
|
||||
pub cpu_id: Option<usize>,
|
||||
/// Context is halting parent
|
||||
pub vfork: bool,
|
||||
/// Context is being waited on
|
||||
|
@ -55,6 +55,8 @@ pub struct Context {
|
|||
pub heap: Option<SharedMemory>,
|
||||
/// User stack
|
||||
pub stack: Option<Memory>,
|
||||
/// User Tls
|
||||
pub tls: Option<Tls>,
|
||||
/// User grants
|
||||
pub grants: Arc<Mutex<Vec<Grant>>>,
|
||||
/// The name of the context
|
||||
|
@ -81,7 +83,7 @@ impl Context {
|
|||
egid: 0,
|
||||
status: Status::Blocked,
|
||||
running: false,
|
||||
cpuid: None,
|
||||
cpu_id: None,
|
||||
vfork: false,
|
||||
waitpid: Arc::new(WaitCondition::new()),
|
||||
wake: None,
|
||||
|
@ -91,6 +93,7 @@ impl Context {
|
|||
image: Vec::new(),
|
||||
heap: None,
|
||||
stack: None,
|
||||
tls: None,
|
||||
grants: Arc::new(Mutex::new(Vec::new())),
|
||||
name: Arc::new(Mutex::new(Vec::new())),
|
||||
cwd: Arc::new(Mutex::new(Vec::new())),
|
||||
|
@ -153,6 +156,13 @@ impl Context {
|
|||
pub fn unblock(&mut self) -> bool {
|
||||
if self.status == Status::Blocked {
|
||||
self.status = Status::Runnable;
|
||||
if let Some(cpu_id) = self.cpu_id {
|
||||
if cpu_id != ::cpu_id() {
|
||||
// Send IPI if not on current CPU
|
||||
// TODO: Make this more architecture independent
|
||||
unsafe { arch::device::local_apic::LOCAL_APIC.ipi(cpu_id) };
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -332,3 +332,10 @@ impl Drop for Memory {
|
|||
self.unmap(true);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tls {
|
||||
pub master: VirtualAddress,
|
||||
pub file_size: usize,
|
||||
pub mem: Memory
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ pub fn init() {
|
|||
context.kfx = Some(fx);
|
||||
context.status = Status::Runnable;
|
||||
context.running = true;
|
||||
context.cpuid = Some(::cpu_id());
|
||||
context.cpu_id = Some(::cpu_id());
|
||||
CONTEXT_ID.store(context.id, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,27 +16,34 @@ pub unsafe fn switch() -> bool {
|
|||
arch::interrupt::pause();
|
||||
}
|
||||
|
||||
let cpu_id = ::cpu_id();
|
||||
|
||||
let from_ptr;
|
||||
let mut to_ptr = 0 as *mut Context;
|
||||
{
|
||||
let contexts = contexts();
|
||||
{
|
||||
let context_lock = contexts.current().expect("context::switch: Not inside of context");
|
||||
let context_lock = contexts.current().expect("context::switch: not inside of context");
|
||||
let mut context = context_lock.write();
|
||||
from_ptr = context.deref_mut() as *mut Context;
|
||||
}
|
||||
|
||||
let check_context = |context: &mut Context| -> bool {
|
||||
if context.cpuid == None || context.cpuid == Some(::cpu_id()) {
|
||||
if context.status == Status::Blocked && context.wake.is_some() {
|
||||
let wake = context.wake.expect("context::switch: wake not set");
|
||||
if context.cpu_id == None && cpu_id == 0 {
|
||||
context.cpu_id = Some(cpu_id);
|
||||
println!("{}: take {} {}", cpu_id, context.id, ::core::str::from_utf8_unchecked(&context.name.lock()));
|
||||
}
|
||||
|
||||
let current = arch::time::monotonic();
|
||||
if current.0 > wake.0 || (current.0 == wake.0 && current.1 >= wake.1) {
|
||||
context.unblock();
|
||||
}
|
||||
if context.status == Status::Blocked && context.wake.is_some() {
|
||||
let wake = context.wake.expect("context::switch: wake not set");
|
||||
|
||||
let current = arch::time::monotonic();
|
||||
if current.0 > wake.0 || (current.0 == wake.0 && current.1 >= wake.1) {
|
||||
context.unblock();
|
||||
}
|
||||
}
|
||||
|
||||
if context.cpu_id == Some(cpu_id) {
|
||||
if context.status == Status::Runnable && ! context.running {
|
||||
return true;
|
||||
}
|
||||
|
@ -74,8 +81,6 @@ pub unsafe fn switch() -> bool {
|
|||
return false;
|
||||
}
|
||||
|
||||
// println!("{}: Switch {} to {}", ::cpu_id(), (&*from_ptr).id, (&*to_ptr).id);
|
||||
|
||||
(&mut *from_ptr).running = false;
|
||||
(&mut *to_ptr).running = true;
|
||||
if let Some(ref stack) = (*to_ptr).kstack {
|
||||
|
@ -83,6 +88,9 @@ pub unsafe fn switch() -> bool {
|
|||
}
|
||||
CONTEXT_ID.store((&mut *to_ptr).id, Ordering::SeqCst);
|
||||
|
||||
// Unset global lock before switch, as arch is only usable by the current CPU at this time
|
||||
arch::context::CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst);
|
||||
|
||||
(&mut *from_ptr).arch.switch_to(&mut (&mut *to_ptr).arch);
|
||||
|
||||
true
|
||||
|
|
|
@ -152,6 +152,19 @@ pub extern fn userspace_init() {
|
|||
panic!("initfs:init returned")
|
||||
}
|
||||
|
||||
/// Allow exception handlers to send signal to arch-independant kernel
|
||||
#[no_mangle]
|
||||
pub extern fn ksignal(signal: usize) {
|
||||
println!("SIGNAL {}, CPU {}, PID {}", signal, cpu_id(), context::context_id());
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
if let Some(context_lock) = contexts.current() {
|
||||
let context = context_lock.read();
|
||||
println!("NAME {}", unsafe { ::core::str::from_utf8_unchecked(&context.name.lock()) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn kmain(cpus: usize) {
|
||||
CPU_ID.store(0, Ordering::SeqCst);
|
||||
|
@ -195,6 +208,14 @@ pub extern fn kmain_ap(id: usize) {
|
|||
println!("AP {}: {:?}", id, pid);
|
||||
|
||||
loop {
|
||||
unsafe { interrupt::enable_and_halt() }
|
||||
unsafe {
|
||||
interrupt::disable();
|
||||
if context::switch() {
|
||||
interrupt::enable_and_nop();
|
||||
} else {
|
||||
// Enable interrupts, then halt CPU (to save power) until the next interrupt is actually fired.
|
||||
interrupt::enable_and_halt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@ use context;
|
|||
use syscall::error::Result;
|
||||
|
||||
pub fn resource() -> Result<Vec<u8>> {
|
||||
let mut string = format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n",
|
||||
let mut string = format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n",
|
||||
"PID",
|
||||
"PPID",
|
||||
"UID",
|
||||
"GID",
|
||||
"STAT",
|
||||
"CPU",
|
||||
"MEM",
|
||||
"NAME");
|
||||
{
|
||||
|
@ -18,6 +19,35 @@ pub fn resource() -> Result<Vec<u8>> {
|
|||
for (_id, context_lock) in contexts.iter() {
|
||||
let context = context_lock.read();
|
||||
|
||||
let mut stat_string = String::new();
|
||||
if context.stack.is_some() {
|
||||
stat_string.push('U');
|
||||
} else {
|
||||
stat_string.push('K');
|
||||
}
|
||||
match context.status {
|
||||
context::Status::Runnable => {
|
||||
stat_string.push('R');
|
||||
},
|
||||
context::Status::Blocked => if context.wake.is_some() {
|
||||
stat_string.push('S');
|
||||
} else {
|
||||
stat_string.push('B');
|
||||
},
|
||||
context::Status::Exited(_status) => {
|
||||
stat_string.push('Z');
|
||||
}
|
||||
}
|
||||
if context.running {
|
||||
stat_string.push('+');
|
||||
}
|
||||
|
||||
let cpu_string = if let Some(cpu_id) = context.cpu_id {
|
||||
format!("{}", cpu_id)
|
||||
} else {
|
||||
format!("?")
|
||||
};
|
||||
|
||||
let mut memory = 0;
|
||||
if let Some(ref kfx) = context.kstack {
|
||||
memory += kfx.len();
|
||||
|
@ -49,38 +79,16 @@ pub fn resource() -> Result<Vec<u8>> {
|
|||
format!("{} B", memory)
|
||||
};
|
||||
|
||||
let mut stat_string = String::new();
|
||||
if context.stack.is_some() {
|
||||
stat_string.push('U');
|
||||
} else {
|
||||
stat_string.push('K');
|
||||
}
|
||||
match context.status {
|
||||
context::Status::Runnable => {
|
||||
stat_string.push('R');
|
||||
},
|
||||
context::Status::Blocked => if context.wake.is_some() {
|
||||
stat_string.push('S');
|
||||
} else {
|
||||
stat_string.push('B');
|
||||
},
|
||||
context::Status::Exited(_status) => {
|
||||
stat_string.push('Z');
|
||||
}
|
||||
}
|
||||
if context.running {
|
||||
stat_string.push('+');
|
||||
}
|
||||
|
||||
let name_bytes = context.name.lock();
|
||||
let name = str::from_utf8(&name_bytes).unwrap_or("");
|
||||
|
||||
string.push_str(&format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n",
|
||||
string.push_str(&format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n",
|
||||
context.id,
|
||||
context.ppid,
|
||||
context.euid,
|
||||
context.egid,
|
||||
stat_string,
|
||||
cpu_string,
|
||||
memory_string,
|
||||
name));
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
|
|||
let rgid;
|
||||
let euid;
|
||||
let egid;
|
||||
let mut cpu_id = None;
|
||||
let arch;
|
||||
let vfork;
|
||||
let mut kfx_option = None;
|
||||
|
@ -70,6 +71,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
|
|||
let mut image = vec![];
|
||||
let mut heap_option = None;
|
||||
let mut stack_option = None;
|
||||
let mut tls_option = None;
|
||||
let grants;
|
||||
let name;
|
||||
let cwd;
|
||||
|
@ -88,6 +90,10 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
|
|||
euid = context.euid;
|
||||
egid = context.egid;
|
||||
|
||||
if flags & CLONE_VM == CLONE_VM {
|
||||
cpu_id = context.cpu_id;
|
||||
}
|
||||
|
||||
arch = context.arch.clone();
|
||||
|
||||
if let Some(ref fx) = context.kfx {
|
||||
|
@ -181,6 +187,29 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
|
|||
stack_option = Some(new_stack);
|
||||
}
|
||||
|
||||
if let Some(ref tls) = context.tls {
|
||||
let mut new_tls = context::memory::Tls {
|
||||
master: tls.master,
|
||||
file_size: tls.file_size,
|
||||
mem: context::memory::Memory::new(
|
||||
VirtualAddress::new(arch::USER_TMP_TLS_OFFSET),
|
||||
tls.mem.size(),
|
||||
entry::PRESENT | entry::NO_EXECUTE | entry::WRITABLE,
|
||||
true,
|
||||
false
|
||||
)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
arch::externs::memcpy(new_tls.mem.start_address().get() as *mut u8,
|
||||
tls.master.get() as *const u8,
|
||||
tls.file_size);
|
||||
}
|
||||
|
||||
new_tls.mem.remap(tls.mem.flags(), true);
|
||||
tls_option = Some(new_tls);
|
||||
}
|
||||
|
||||
if flags & CLONE_VM == CLONE_VM {
|
||||
grants = context.grants.clone();
|
||||
} else {
|
||||
|
@ -277,6 +306,8 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
|
|||
context.euid = euid;
|
||||
context.egid = egid;
|
||||
|
||||
context.cpu_id = cpu_id;
|
||||
|
||||
context.status = context::Status::Runnable;
|
||||
|
||||
context.vfork = vfork;
|
||||
|
@ -393,6 +424,12 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
|
|||
context.stack = Some(stack);
|
||||
}
|
||||
|
||||
// Setup user TLS
|
||||
if let Some(mut tls) = tls_option {
|
||||
tls.mem.move_to(VirtualAddress::new(arch::USER_TLS_OFFSET), &mut new_table, &mut temporary_page, true);
|
||||
context.tls = Some(tls);
|
||||
}
|
||||
|
||||
context.name = name;
|
||||
|
||||
context.cwd = cwd;
|
||||
|
@ -411,6 +448,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<usize> {
|
|||
pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
|
||||
let entry;
|
||||
let mut sp = arch::USER_STACK_OFFSET + arch::USER_STACK_SIZE - 256;
|
||||
let fs = arch::USER_STACK_OFFSET;
|
||||
|
||||
{
|
||||
let mut args = Vec::new();
|
||||
|
@ -466,11 +504,12 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
|
|||
// Set name
|
||||
context.name = Arc::new(Mutex::new(canonical));
|
||||
|
||||
// Unmap previous image and stack
|
||||
// Unmap previous image, heap, grants, stack, and tls
|
||||
context.image.clear();
|
||||
drop(context.heap.take());
|
||||
drop(context.stack.take());
|
||||
context.grants = Arc::new(Mutex::new(Vec::new()));
|
||||
drop(context.stack.take());
|
||||
drop(context.tls.take());
|
||||
|
||||
if stat.st_mode & syscall::flag::MODE_SETUID == syscall::flag::MODE_SETUID {
|
||||
context.euid = stat.st_uid;
|
||||
|
@ -481,6 +520,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
|
|||
}
|
||||
|
||||
// Map and copy new segments
|
||||
let mut tls_option = None;
|
||||
for segment in elf.segments() {
|
||||
if segment.p_type == program_header::PT_LOAD {
|
||||
let mut memory = context::memory::Memory::new(
|
||||
|
@ -514,6 +554,12 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
|
|||
memory.remap(flags, true);
|
||||
|
||||
context.image.push(memory.to_shared());
|
||||
} else if segment.p_type == program_header::PT_TLS {
|
||||
tls_option = Some((
|
||||
VirtualAddress::new(segment.p_vaddr as usize),
|
||||
segment.p_filesz as usize,
|
||||
segment.p_memsz as usize
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -535,6 +581,35 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
|
|||
true
|
||||
));
|
||||
|
||||
// Map TLS
|
||||
if let Some((master, file_size, size)) = tls_option {
|
||||
let tls = context::memory::Tls {
|
||||
master: master,
|
||||
file_size: file_size,
|
||||
mem: context::memory::Memory::new(
|
||||
VirtualAddress::new(arch::USER_TLS_OFFSET),
|
||||
size,
|
||||
entry::NO_EXECUTE | entry::WRITABLE | entry::USER_ACCESSIBLE,
|
||||
true,
|
||||
true
|
||||
)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
// Copy file data
|
||||
memcpy(tls.mem.start_address().get() as *mut u8,
|
||||
master.get() as *const u8,
|
||||
file_size);
|
||||
}
|
||||
|
||||
// Set TLS pointer
|
||||
//TODO: Do not use stack to store TLS pointer, use a TCB structure instead
|
||||
unsafe { *(arch::USER_STACK_OFFSET as *mut usize) = tls.mem.start_address().get() + size; }
|
||||
|
||||
context.tls = Some(tls);
|
||||
}
|
||||
|
||||
// Push arguments
|
||||
let mut arg_size = 0;
|
||||
for arg in args.iter().rev() {
|
||||
sp -= mem::size_of::<usize>();
|
||||
|
@ -597,7 +672,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result<usize> {
|
|||
}
|
||||
|
||||
// Go to usermode
|
||||
unsafe { usermode(entry, sp); }
|
||||
unsafe { usermode(entry, sp, fs); }
|
||||
}
|
||||
|
||||
pub fn exit(status: usize) -> ! {
|
||||
|
@ -897,6 +972,7 @@ pub fn virttophys(virtual_address: usize) -> Result<usize> {
|
|||
pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result<usize> {
|
||||
loop {
|
||||
let mut exited = false;
|
||||
let mut running;
|
||||
let waitpid;
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
|
@ -909,10 +985,23 @@ pub fn waitpid(pid: usize, status_ptr: usize, flags: usize) -> Result<usize> {
|
|||
}
|
||||
exited = true;
|
||||
}
|
||||
running = context.running;
|
||||
waitpid = context.waitpid.clone();
|
||||
}
|
||||
|
||||
if exited {
|
||||
// Spin until not running
|
||||
while running {
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
running = context.running;
|
||||
}
|
||||
|
||||
arch::interrupt::pause();
|
||||
}
|
||||
|
||||
let mut contexts = context::contexts_mut();
|
||||
return contexts.remove(pid).ok_or(Error::new(ESRCH)).and(Ok(pid));
|
||||
} else if flags & WNOHANG == WNOHANG {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue