Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add semaphores syscalls #156

Merged
merged 8 commits into from
Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions linux-loader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,9 @@ mod tests {
async fn test_random() {
assert_eq!(test("/bin/testrandom").await, 0);
}

#[async_std::test]
async fn test_sem() {
assert_eq!(test("/bin/testsem1").await, 0);
}
}
3 changes: 3 additions & 0 deletions linux-object/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ pub enum LxError {
ENOTEMPTY = 39,
/// Too many symbolic links encountered
ELOOP = 40,
/// Identifier removed
EIDRM = 43,
/// Socket operation on non-socket
ENOTSOCK = 88,
/// Protocol not available
Expand Down Expand Up @@ -158,6 +160,7 @@ impl fmt::Display for LxError {
ENOSYS => "Function not implemented",
ENOTEMPTY => "Directory not empty",
ELOOP => "Too many symbolic links encountered",
EIDRM => "Identifier removed",
ENOTSOCK => "Socket operation on non-socket",
ENOPROTOOPT => "Protocol not available",
EPFNOSUPPORT => "Protocol family not supported",
Expand Down
82 changes: 82 additions & 0 deletions linux-object/src/ipc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//! Linux Inter-Process Communication
#![deny(missing_docs)]
mod semary;

pub use self::semary::*;
use alloc::collections::BTreeMap;
use alloc::sync::Arc;

/// Semaphore table in a process
#[derive(Default)]
pub struct SemProc {
/// Semaphore arrays
arrays: BTreeMap<SemId, Arc<SemArray>>,
/// Undo operations when process terminates
undos: BTreeMap<(SemId, SemNum), SemOp>,
}

/// Semaphore set identifier (in a process)
type SemId = usize;

/// Semaphore number (in an array)
type SemNum = u16;

/// Semaphore operation value
type SemOp = i16;

impl SemProc {
/// Insert the `array` and return its ID
pub fn add(&mut self, array: Arc<SemArray>) -> SemId {
let id = self.get_free_id();
self.arrays.insert(id, array);
id
}

/// Remove an `array` by ID
pub fn remove(&mut self, id: SemId) {
self.arrays.remove(&id);
}

/// Get a free ID
fn get_free_id(&self) -> SemId {
(0..).find(|i| self.arrays.get(i).is_none()).unwrap()
}

/// Get an semaphore set by `id`
pub fn get(&self, id: SemId) -> Option<Arc<SemArray>> {
self.arrays.get(&id).cloned()
}

/// Add an undo operation
pub fn add_undo(&mut self, id: SemId, num: SemNum, op: SemOp) {
let old_val = *self.undos.get(&(id, num)).unwrap_or(&0);
let new_val = old_val - op;
self.undos.insert((id, num), new_val);
}
}

/// Fork the semaphore table. Clear undo info.
impl Clone for SemProc {
fn clone(&self) -> Self {
SemProc {
arrays: self.arrays.clone(),
undos: BTreeMap::default(),
}
}
}

/// Auto perform semaphores undo on drop
impl Drop for SemProc {
fn drop(&mut self) {
for (&(id, num), &op) in self.undos.iter() {
debug!("semundo: id: {}, num: {}, op: {}", id, num, op);
let sem_array = self.arrays[&id].clone();
let sem = &sem_array[num as usize];
match op {
1 => sem.release(),
0 => {}
_ => unimplemented!("Semaphore: semundo.(Not 1)"),
}
}
}
}
167 changes: 167 additions & 0 deletions linux-object/src/ipc/semary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
//! Linux semaphore ipc
use crate::error::LxError;
use crate::sync::Semaphore;
use crate::time::*;
use alloc::{collections::BTreeMap, sync::Arc, sync::Weak, vec::Vec};
use bitflags::*;
use core::ops::Index;
use lazy_static::*;
use spin::Mutex;
use spin::RwLock;

bitflags! {
struct SemGetFlag: usize {
const CREAT = 1 << 9;
const EXCLUSIVE = 1 << 10;
const NO_WAIT = 1 << 11;
}
}

/// structure specifies the access permissions on the semaphore set
///
/// struct ipc_perm
#[repr(C)]
#[derive(Clone, Copy)]
pub struct IpcPerm {
/// Key supplied to semget(2)
pub key: u32,
/// Effective UID of owner
pub uid: u32,
/// Effective GID of owner
pub gid: u32,
/// Effective UID of creator
pub cuid: u32,
/// Effective GID of creator
pub cgid: u32,
/// Permissions
pub mode: u32,
/// Sequence number
pub __seq: u32,
/// pad1
pub __pad1: usize,
/// pad2
pub __pad2: usize,
}

/// semid data structure
///
/// struct semid_ds
#[repr(C)]
#[derive(Clone, Copy)]
pub struct SemidDs {
/// Ownership and permissions
pub perm: IpcPerm,
/// Last semop time
pub otime: usize,
__pad1: usize,
/// Last change time
pub ctime: usize,
__pad2: usize,
/// number of semaphores in set
pub nsems: usize,
}

/// A System V semaphore set
pub struct SemArray {
/// semid data structure
pub semid_ds: Mutex<SemidDs>,
sems: Vec<Semaphore>,
}

impl Index<usize> for SemArray {
type Output = Semaphore;
fn index(&self, idx: usize) -> &Semaphore {
&self.sems[idx]
}
}

lazy_static! {
static ref KEY2SEM: RwLock<BTreeMap<u32, Weak<SemArray>>> = RwLock::new(BTreeMap::new());
}

impl SemArray {
/// remove semaphores
pub fn remove(&self) {
let mut key2sem = KEY2SEM.write();
let key = self.semid_ds.lock().perm.key;
key2sem.remove(&key);
for sem in self.sems.iter() {
sem.remove();
}
}

/// set last semop time
pub fn otime(&self) {
self.semid_ds.lock().otime = TimeSpec::now().sec;
}

/// set last change time
pub fn ctime(&self) {
self.semid_ds.lock().ctime = TimeSpec::now().sec;
}

/// for IPC_SET
/// see man semctl(2)
pub fn set(&self, new: &SemidDs) {
let mut lock = self.semid_ds.lock();
lock.perm.uid = new.perm.uid;
lock.perm.gid = new.perm.gid;
lock.perm.mode = new.perm.mode & 0x1ff;
}

/// Get the semaphore array with `key`.
/// If not exist, create a new one with `nsems` elements.
pub fn get_or_create(mut key: u32, nsems: usize, flags: usize) -> Result<Arc<Self>, LxError> {
let mut key2sem = KEY2SEM.write();
let flag = SemGetFlag::from_bits_truncate(flags);

if key == 0 {
// IPC_PRIVATE
// find an empty key slot
key = (1u32..).find(|i| key2sem.get(i).is_none()).unwrap();
} else {
// check existence
if let Some(weak_array) = key2sem.get(&key) {
if let Some(array) = weak_array.upgrade() {
if flag.contains(SemGetFlag::CREAT) && flag.contains(SemGetFlag::EXCLUSIVE) {
// exclusive
return Err(LxError::EEXIST);
}
return Ok(array);
}
}
}

// not found, create one
let mut semaphores = Vec::new();
for _ in 0..nsems {
semaphores.push(Semaphore::new(0));
}

// insert to global map
let array = Arc::new(SemArray {
semid_ds: Mutex::new(SemidDs {
perm: IpcPerm {
key,
uid: 0,
gid: 0,
cuid: 0,
cgid: 0,
// least significant 9 bits
mode: (flags as u32) & 0x1ff,
__seq: 0,
__pad1: 0,
__pad2: 0,
},
otime: 0,
ctime: TimeSpec::now().sec,
nsems,
__pad1: 0,
__pad2: 0,
}),
sems: semaphores,
});
key2sem.insert(key, Arc::downgrade(&array));
Ok(array)
}
}
4 changes: 3 additions & 1 deletion linux-object/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Linux kernel objects

#![no_std]
#![deny(warnings, unsafe_code)]
#![deny(warnings, unsafe_code, missing_docs)]
#![feature(bool_to_option)]

extern crate alloc;
Expand All @@ -16,8 +16,10 @@ pub mod error;
pub mod fs;

// layer 2
pub mod ipc;
pub mod loader;
pub mod process;
pub mod signal;
pub mod sync;
pub mod thread;
pub mod time;
23 changes: 23 additions & 0 deletions linux-object/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::error::*;
use crate::fs::*;
use crate::ipc::*;
use crate::signal::{Signal as LinuxSignal, SignalAction};
use alloc::vec::Vec;
use alloc::{
Expand Down Expand Up @@ -149,6 +150,8 @@ struct LinuxProcessInner {
file_limit: RLimit,
/// Opened files
files: HashMap<FileDesc, Arc<dyn FileLike>>,
/// Semaphore
semaphores: SemProc,
/// Futexes
futexes: HashMap<VirtAddr, Arc<Futex>>,
/// Child processes
Expand Down Expand Up @@ -361,6 +364,26 @@ impl LinuxProcess {
pub fn set_signal_action(&self, signal: LinuxSignal, action: SignalAction) {
self.inner.lock().signal_actions.table[signal as u8 as usize] = action;
}

/// Insert a `SemArray` and return its ID
pub fn semaphores_add(&self, array: Arc<SemArray>) -> usize {
self.inner.lock().semaphores.add(array)
}

/// Get an semaphore set by `id`
pub fn semaphores_get(&self, id: usize) -> Option<Arc<SemArray>> {
self.inner.lock().semaphores.get(id)
}

/// Add an undo operation
pub fn semaphores_add_undo(&self, id: usize, num: u16, op: i16) {
self.inner.lock().semaphores.add_undo(id, num, op)
}

/// Remove an `SemArray` by ID
pub fn semaphores_remove(&self, id: usize) {
self.inner.lock().semaphores.remove(id)
}
}

impl LinuxProcessInner {
Expand Down
5 changes: 3 additions & 2 deletions linux-object/src/signal/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ pub const SIG_ERR: usize = usize::max_value() - 1;
pub const SIG_DFL: usize = 0;
pub const SIG_IGN: usize = 1;

// yet there's a bug because of mismatching bits: https://sourceware.org/bugzilla/show_bug.cgi?id=25657
// just support 64bits size sigset
/// Linux struct sigset_t
///
/// yet there's a bug because of mismatching bits: https://sourceware.org/bugzilla/show_bug.cgi?id=25657
/// just support 64bits size sigset
#[derive(Default, Clone, Copy, Debug)]
#[repr(C)]
pub struct Sigset(u64);
Expand Down
13 changes: 10 additions & 3 deletions linux-object/src/signal/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Linux signals
#![allow(missing_docs)]
use bitflags::*;
use numeric_enum_macro::numeric_enum;

Expand Down Expand Up @@ -125,6 +127,7 @@ impl Signal {
}

/// See musl struct __ucontext
///
/// Not exactly the same for now
#[repr(C)]
#[derive(Clone)]
Expand All @@ -139,10 +142,14 @@ pub struct SignalUserContext {
#[repr(C)]
#[derive(Clone)]
pub struct SignalFrame {
pub ret_code_addr: usize, // point to ret_code
/// point to ret_code
pub ret_code_addr: usize,
/// Signal Frame info
pub info: SigInfo,
pub ucontext: SignalUserContext, // adapt interface, a little bit waste
pub ret_code: [u8; 7], // call sys_sigreturn
/// adapt interface, a little bit waste
pub ucontext: SignalUserContext,
/// call sys_sigreturn
pub ret_code: [u8; 7],
}

bitflags! {
Expand Down
2 changes: 1 addition & 1 deletion linux-object/src/sync/event_bus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ bitflags! {

/// Semaphore: is removed
const SEMAPHORE_REMOVED = 1 << 20;
/// Semaphore: can acquired
/// Semaphore: can acquired a resource of this semaphore
const SEMAPHORE_CAN_ACQUIRE = 1 << 21;
}
}
Expand Down
Loading