Add sched_xetaffinity() system calls in Occlum
1. LibOS support sched_getaffinity() and sched_setaffinity() system calls 2. Add sched_cpu_affinity test cases in occlum/test/sched
This commit is contained in:
		
							parent
							
								
									03c19ae2c6
								
							
						
					
					
						commit
						cff0de1c39
					
				| @ -9,7 +9,7 @@ enclave { | |||||||
|     trusted { |     trusted { | ||||||
|         /* define ECALLs here. */ |         /* define ECALLs here. */ | ||||||
|         public int libos_boot([in, string] const char* executable_path, [user_check] const char** argv); |         public int libos_boot([in, string] const char* executable_path, [user_check] const char** argv); | ||||||
|         public int libos_run(void); |         public int libos_run(int host_tid); | ||||||
|         /* This is only for debug usage */ |         /* This is only for debug usage */ | ||||||
|         public int dummy_ecall(void); |         public int dummy_ecall(void); | ||||||
|     }; |     }; | ||||||
| @ -20,5 +20,7 @@ enclave { | |||||||
|         void ocall_gettimeofday([out] long* sec, [out] long* us); |         void ocall_gettimeofday([out] long* sec, [out] long* us); | ||||||
|         void ocall_clock_gettime(int clockid, [out] long* sec, [out] long* ns); |         void ocall_clock_gettime(int clockid, [out] long* sec, [out] long* ns); | ||||||
|         void ocall_sync(void); |         void ocall_sync(void); | ||||||
|  |         int ocall_sched_getaffinity([out] int *error, int pid, size_t cpusize, [out, size=cpusize] unsigned char* buf); | ||||||
|  |         int ocall_sched_setaffinity([out] int *error, int pid, size_t cpusize, [in, size=cpusize] const unsigned char* buf); | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| use super::*; | use super::*; | ||||||
|  | use process::pid_t; | ||||||
| use std::ffi::{CStr, CString, OsString}; | use std::ffi::{CStr, CString, OsString}; | ||||||
| use std::path::Path; | use std::path::Path; | ||||||
| use util::mem_util::from_untrusted::*; | use util::mem_util::from_untrusted::*; | ||||||
| @ -32,10 +33,10 @@ pub extern "C" fn libos_boot(path_buf: *const c_char, argv: *const *const c_char | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub extern "C" fn libos_run() -> i32 { | pub extern "C" fn libos_run(host_tid: i32) -> i32 { | ||||||
|     let _ = backtrace::enable_backtrace("libocclum.signed.so", PrintFormat::Short); |     let _ = backtrace::enable_backtrace("libocclum.signed.so", PrintFormat::Short); | ||||||
|     panic::catch_unwind(|| { |     panic::catch_unwind(|| { | ||||||
|         backtrace::__rust_begin_short_backtrace(|| match do_run() { |         backtrace::__rust_begin_short_backtrace(|| match do_run(host_tid as pid_t) { | ||||||
|             Ok(exit_status) => exit_status, |             Ok(exit_status) => exit_status, | ||||||
|             Err(err) => EXIT_STATUS_INTERNAL_ERROR, |             Err(err) => EXIT_STATUS_INTERNAL_ERROR, | ||||||
|         }) |         }) | ||||||
| @ -91,8 +92,8 @@ fn do_boot(path_str: &str, argv: &Vec<CString>) -> Result<(), Error> { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO: make sure do_run() cannot be called after do_boot()
 | // TODO: make sure do_run() cannot be called after do_boot()
 | ||||||
| fn do_run() -> Result<i32, Error> { | fn do_run(host_tid: pid_t) -> Result<i32, Error> { | ||||||
|     let exit_status = process::run_task()?; |     let exit_status = process::run_task(host_tid)?; | ||||||
| 
 | 
 | ||||||
|     // sync file system
 |     // sync file system
 | ||||||
|     // TODO: only sync when all processes exit
 |     // TODO: only sync when all processes exit
 | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ pub use self::spawn::{do_spawn, FileAction}; | |||||||
| pub use self::task::{current_pid, get_current, run_task}; | pub use self::task::{current_pid, get_current, run_task}; | ||||||
| pub use self::thread::{do_clone, do_set_tid_address, CloneFlags, ThreadGroup}; | pub use self::thread::{do_clone, do_set_tid_address, CloneFlags, ThreadGroup}; | ||||||
| pub use self::wait::{WaitQueue, Waiter}; | pub use self::wait::{WaitQueue, Waiter}; | ||||||
|  | pub use self::sched::{CpuSet, do_sched_getaffinity, do_sched_setaffinity}; | ||||||
| 
 | 
 | ||||||
| #[allow(non_camel_case_types)] | #[allow(non_camel_case_types)] | ||||||
| pub type pid_t = u32; | pub type pid_t = u32; | ||||||
| @ -18,6 +19,7 @@ pub struct Process { | |||||||
|     pid: pid_t, |     pid: pid_t, | ||||||
|     pgid: pid_t, |     pgid: pid_t, | ||||||
|     tgid: pid_t, |     tgid: pid_t, | ||||||
|  |     host_tid: pid_t, | ||||||
|     exit_status: i32, |     exit_status: i32, | ||||||
|     // TODO: move cwd, root_inode into a FileSystem structure
 |     // TODO: move cwd, root_inode into a FileSystem structure
 | ||||||
|     // TODO: should cwd be a String or INode?
 |     // TODO: should cwd be a String or INode?
 | ||||||
| @ -73,6 +75,7 @@ mod spawn; | |||||||
| mod task; | mod task; | ||||||
| mod thread; | mod thread; | ||||||
| mod wait; | mod wait; | ||||||
|  | mod sched; | ||||||
| 
 | 
 | ||||||
| use self::task::Task; | use self::task::Task; | ||||||
| use super::*; | use super::*; | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ lazy_static! { | |||||||
|             pid: 0, |             pid: 0, | ||||||
|             pgid: 1, |             pgid: 1, | ||||||
|             tgid: 0, |             tgid: 0, | ||||||
|  |             host_tid: 0, | ||||||
|             exit_status: 0, |             exit_status: 0, | ||||||
|             cwd: "/".to_owned(), |             cwd: "/".to_owned(), | ||||||
|             clear_child_tid: None, |             clear_child_tid: None, | ||||||
| @ -40,6 +41,7 @@ impl Process { | |||||||
|             pid: new_pid, |             pid: new_pid, | ||||||
|             pgid: 1, // TODO: implement pgid
 |             pgid: 1, // TODO: implement pgid
 | ||||||
|             tgid: new_pid, |             tgid: new_pid, | ||||||
|  |             host_tid: 0, | ||||||
|             cwd: cwd.to_owned(), |             cwd: cwd.to_owned(), | ||||||
|             clear_child_tid: None, |             clear_child_tid: None, | ||||||
|             exit_status: 0, |             exit_status: 0, | ||||||
| @ -70,6 +72,12 @@ impl Process { | |||||||
|     pub fn get_pgid(&self) -> pid_t { |     pub fn get_pgid(&self) -> pid_t { | ||||||
|         self.pgid |         self.pgid | ||||||
|     } |     } | ||||||
|  |     pub fn get_host_tid(&self) -> pid_t { | ||||||
|  |         self.host_tid | ||||||
|  |     } | ||||||
|  |     pub fn set_host_tid(&mut self, host_tid: pid_t) { | ||||||
|  |         self.host_tid = host_tid; | ||||||
|  |     } | ||||||
|     pub fn get_status(&self) -> Status { |     pub fn get_status(&self) -> Status { | ||||||
|         self.status |         self.status | ||||||
|     } |     } | ||||||
|  | |||||||
							
								
								
									
										112
									
								
								src/libos/src/process/sched.rs
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										112
									
								
								src/libos/src/process/sched.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | |||||||
|  | use super::*; | ||||||
|  | 
 | ||||||
|  | extern "C" { | ||||||
|  |     fn ocall_sched_getaffinity(ret: *mut i32, errno: *mut i32, pid: i32, cpusetsize: size_t, mask: *mut c_uchar) -> sgx_status_t; | ||||||
|  |     fn ocall_sched_setaffinity(ret: *mut i32, errno: *mut i32, pid: i32, cpusetsize: size_t, mask: *const c_uchar) -> sgx_status_t; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct CpuSet { | ||||||
|  |     vec: Vec<u8>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl CpuSet { | ||||||
|  |     pub fn new(len: usize) -> CpuSet { | ||||||
|  |         let mut cpuset = CpuSet { vec: Vec::with_capacity(len) }; | ||||||
|  |         cpuset.vec.resize(len, 0); | ||||||
|  |         cpuset | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn from_raw_buf(ptr: *const u8, cpusize: usize) -> CpuSet { | ||||||
|  |         let mut cpuset = CpuSet { vec: Vec::with_capacity(cpusize) }; | ||||||
|  |         let buf_slice = unsafe { std::slice::from_raw_parts(ptr, cpusize) }; | ||||||
|  |         cpuset.vec.extend_from_slice(buf_slice); | ||||||
|  |         cpuset | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn as_mut_ptr(&mut self) -> *mut u8 { | ||||||
|  |         self.vec.as_mut_ptr() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn as_ptr(&self) -> *const u8 { | ||||||
|  |         self.vec.as_ptr() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn as_mut_slice(&mut self) -> &mut [u8] { | ||||||
|  |         self.vec.as_mut_slice() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn as_slice(&self) -> &[u8] { | ||||||
|  |         self.vec.as_slice() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn len(&self) -> usize { | ||||||
|  |         self.vec.len() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl std::fmt::LowerHex for CpuSet { | ||||||
|  |     fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { | ||||||
|  |         for byte in &(self.vec) { | ||||||
|  |             try!(fmtr.write_fmt(format_args!("{:02x}", byte))); | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl std::fmt::UpperHex for CpuSet { | ||||||
|  |     fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { | ||||||
|  |         for byte in &(self.vec) { | ||||||
|  |             try!(fmtr.write_fmt(format_args!("{:02X}", byte))); | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn find_host_tid(pid: pid_t) -> Result<pid_t, Error> { | ||||||
|  |     let process_ref = if pid == 0 { | ||||||
|  |         get_current() | ||||||
|  |     } else { | ||||||
|  |         get(pid)? | ||||||
|  |     }; | ||||||
|  |     let mut process = process_ref.lock().unwrap(); | ||||||
|  |     let host_tid = process.get_host_tid(); | ||||||
|  |     Ok(host_tid) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn do_sched_getaffinity(pid: pid_t, cpu_set: &mut CpuSet) -> Result<i32, Error> { | ||||||
|  |     let host_tid = match pid { | ||||||
|  |         0 => 0, | ||||||
|  |         _ => find_host_tid(pid)?, | ||||||
|  |     }; | ||||||
|  |     let buf = cpu_set.as_mut_ptr(); | ||||||
|  |     let cpusize = cpu_set.len(); | ||||||
|  |     let mut ret = 0; | ||||||
|  |     let mut error = 0; | ||||||
|  |     unsafe { | ||||||
|  |         ocall_sched_getaffinity(&mut ret, &mut error, host_tid as i32, cpusize, buf); | ||||||
|  |     } | ||||||
|  |     if (ret < 0) { | ||||||
|  |         let errno = Errno::from_errno(error); | ||||||
|  |         return errno!(errno, "ocall_sched_getaffinity failed"); | ||||||
|  |     } | ||||||
|  |     Ok(ret) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn do_sched_setaffinity(pid: pid_t, cpu_set: &CpuSet) -> Result<i32, Error> { | ||||||
|  |     let host_tid = match pid { | ||||||
|  |         0 => 0, | ||||||
|  |         _ => find_host_tid(pid)?, | ||||||
|  |     }; | ||||||
|  |     let buf = cpu_set.as_ptr(); | ||||||
|  |     let cpusize = cpu_set.len(); | ||||||
|  |     let mut ret = 0; | ||||||
|  |     let mut error = 0; | ||||||
|  |     unsafe { | ||||||
|  |         ocall_sched_setaffinity(&mut ret, &mut error, host_tid as i32, cpusize, buf); | ||||||
|  |     } | ||||||
|  |     if (ret < 0) { | ||||||
|  |         let errno = Errno::from_errno(error); | ||||||
|  |         return errno!(errno, "ocall_sched_setaffinity failed"); | ||||||
|  |     } | ||||||
|  |     Ok(ret) | ||||||
|  | } | ||||||
| @ -72,13 +72,14 @@ fn dequeue_task() -> Option<ProcessRef> { | |||||||
|     NEW_PROCESS_QUEUE.lock().unwrap().pop_front() |     NEW_PROCESS_QUEUE.lock().unwrap().pop_front() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn run_task() -> Result<i32, Error> { | pub fn run_task(host_tid: pid_t) -> Result<i32, Error> { | ||||||
|     let new_process: ProcessRef = |     let new_process: ProcessRef = | ||||||
|         dequeue_task().ok_or_else(|| (Errno::EAGAIN, "No new processes to run"))?; |         dequeue_task().ok_or_else(|| (Errno::EAGAIN, "No new processes to run"))?; | ||||||
|     set_current(&new_process); |     set_current(&new_process); | ||||||
| 
 | 
 | ||||||
|     let (pid, task) = { |     let (pid, task) = { | ||||||
|         let mut process = new_process.lock().unwrap(); |         let mut process = new_process.lock().unwrap(); | ||||||
|  |         process.set_host_tid(host_tid); | ||||||
|         let pid = process.get_pid(); |         let pid = process.get_pid(); | ||||||
|         let task = process.get_task_mut() as *mut Task; |         let task = process.get_task_mut() as *mut Task; | ||||||
|         (pid, task) |         (pid, task) | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ | |||||||
| use fs::*; | use fs::*; | ||||||
| use misc::{resource_t, rlimit_t, utsname_t}; | use misc::{resource_t, rlimit_t, utsname_t}; | ||||||
| use prelude::*; | use prelude::*; | ||||||
| use process::{pid_t, ChildProcessFilter, CloneFlags, FileAction, FutexFlags, FutexOp}; | use process::{pid_t, ChildProcessFilter, CloneFlags, FileAction, FutexFlags, FutexOp, CpuSet}; | ||||||
| use std::ffi::{CStr, CString}; | use std::ffi::{CStr, CString}; | ||||||
| use std::ptr; | use std::ptr; | ||||||
| use time::{timeval_t, clockid_t, timespec_t}; | use time::{timeval_t, clockid_t, timespec_t}; | ||||||
| @ -175,6 +175,8 @@ pub extern "C" fn dispatch_syscall( | |||||||
|         ), |         ), | ||||||
|         SYS_ARCH_PRCTL => do_arch_prctl(arg0 as u32, arg1 as *mut usize), |         SYS_ARCH_PRCTL => do_arch_prctl(arg0 as u32, arg1 as *mut usize), | ||||||
|         SYS_SET_TID_ADDRESS => do_set_tid_address(arg0 as *mut pid_t), |         SYS_SET_TID_ADDRESS => do_set_tid_address(arg0 as *mut pid_t), | ||||||
|  |         SYS_SCHED_GETAFFINITY => do_sched_getaffinity(arg0 as pid_t, arg1 as size_t, arg2 as *mut c_uchar), | ||||||
|  |         SYS_SCHED_SETAFFINITY => do_sched_setaffinity(arg0 as pid_t, arg1 as size_t, arg2 as *const c_uchar), | ||||||
| 
 | 
 | ||||||
|         // memory
 |         // memory
 | ||||||
|         SYS_MMAP => do_mmap( |         SYS_MMAP => do_mmap( | ||||||
| @ -960,6 +962,45 @@ fn do_set_tid_address(tidptr: *mut pid_t) -> Result<isize, Error> { | |||||||
|     process::do_set_tid_address(tidptr).map(|tid| tid as isize) |     process::do_set_tid_address(tidptr).map(|tid| tid as isize) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn do_sched_getaffinity(pid: pid_t, cpusize: size_t, buf: *mut c_uchar) ->  Result<isize, Error> { | ||||||
|  |     // Construct safe Rust types
 | ||||||
|  |     let mut buf_slice = { | ||||||
|  |         check_mut_array(buf, cpusize)?; | ||||||
|  |         if cpusize == 0 { | ||||||
|  |             return errno!(EINVAL, "cpuset size must be greater than zero"); | ||||||
|  |         } | ||||||
|  |         if buf as *const _ == std::ptr::null() { | ||||||
|  |             return errno!(EFAULT, "cpuset mask must NOT be null"); | ||||||
|  |         } | ||||||
|  |         unsafe { std::slice::from_raw_parts_mut(buf, cpusize) } | ||||||
|  |     }; | ||||||
|  |     // Call the memory-safe do_sched_getaffinity
 | ||||||
|  |     let mut cpuset = CpuSet::new(cpusize); | ||||||
|  |     let ret = process::do_sched_getaffinity(pid, &mut cpuset)?; | ||||||
|  |     debug!("sched_getaffinity cpuset: {:#x}", cpuset); | ||||||
|  |     // Copy from Rust types to C types
 | ||||||
|  |     buf_slice.copy_from_slice(cpuset.as_slice()); | ||||||
|  |     Ok(ret as isize) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn do_sched_setaffinity(pid: pid_t, cpusize: size_t, buf: *const c_uchar) ->  Result<isize, Error> { | ||||||
|  |     // Convert unsafe C types into safe Rust types
 | ||||||
|  |     let cpuset = { | ||||||
|  |         check_array(buf, cpusize)?; | ||||||
|  |         if cpusize == 0 { | ||||||
|  |             return errno!(EINVAL, "cpuset size must be greater than zero"); | ||||||
|  |         } | ||||||
|  |         if buf as *const _ == std::ptr::null() { | ||||||
|  |             return errno!(EFAULT, "cpuset mask must NOT be null"); | ||||||
|  |         } | ||||||
|  |         CpuSet::from_raw_buf(buf, cpusize) | ||||||
|  |     }; | ||||||
|  |     debug!("sched_setaffinity cpuset: {:#x}", cpuset); | ||||||
|  |     // Call the memory-safe do_sched_setaffinity
 | ||||||
|  |     let ret = process::do_sched_setaffinity(pid, &cpuset)?; | ||||||
|  |     Ok(ret as isize) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| fn do_socket(domain: c_int, socket_type: c_int, protocol: c_int) -> Result<isize, Error> { | fn do_socket(domain: c_int, socket_type: c_int, protocol: c_int) -> Result<isize, Error> { | ||||||
|     info!( |     info!( | ||||||
|         "socket: domain: {}, socket_type: {}, protocol: {}", |         "socket: domain: {}, socket_type: {}, protocol: {}", | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ | |||||||
| #include <pwd.h> | #include <pwd.h> | ||||||
| #include <sys/time.h> | #include <sys/time.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
|  | #include <sys/syscall.h> | ||||||
|  | #include <errno.h> | ||||||
| 
 | 
 | ||||||
| #define MAX_PATH FILENAME_MAX | #define MAX_PATH FILENAME_MAX | ||||||
| 
 | 
 | ||||||
| @ -213,6 +215,22 @@ void ocall_clock_gettime(int clockid, time_t* sec, long* ns) { | |||||||
|     *ns = ts.tv_nsec; |     *ns = ts.tv_nsec; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int ocall_sched_getaffinity(int* error, int pid, size_t cpusize, unsigned char* buf) { | ||||||
|  |     int ret = syscall(__NR_sched_getaffinity, pid, cpusize, buf); | ||||||
|  |     if (error) { | ||||||
|  |         *error = (ret == -1) ? errno : 0; | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ocall_sched_setaffinity(int* error, int pid, size_t cpusize, const unsigned char* buf) { | ||||||
|  |     int ret = syscall(__NR_sched_setaffinity, pid, cpusize, buf); | ||||||
|  |     if (error) { | ||||||
|  |         *error = (ret == -1) ? errno : 0; | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ocall_sync(void) { | void ocall_sync(void) { | ||||||
|     sync(); |     sync(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,11 +1,16 @@ | |||||||
| #include <pthread.h> | #include <pthread.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/syscall.h> | ||||||
| #include "atomic.h" | #include "atomic.h" | ||||||
| #include "futex.h" | #include "futex.h" | ||||||
| #include "sgx_urts.h" | #include "sgx_urts.h" | ||||||
| #include "Enclave_u.h" | #include "Enclave_u.h" | ||||||
| 
 | 
 | ||||||
|  | int syscall(); | ||||||
|  | #define gettid() syscall(__NR_gettid) | ||||||
|  | 
 | ||||||
| static volatile int num_tasks = 0; | static volatile int num_tasks = 0; | ||||||
| static volatile int main_task_status = 0; | static volatile int main_task_status = 0; | ||||||
| static volatile int any_fatal_error = 0; | static volatile int any_fatal_error = 0; | ||||||
| @ -29,7 +34,7 @@ static void* __run_task_thread(void* _data) { | |||||||
|     int status = 0; |     int status = 0; | ||||||
|     struct task_thread_data* data = _data; |     struct task_thread_data* data = _data; | ||||||
| 
 | 
 | ||||||
|     sgx_status_t sgx_ret = libos_run(data->eid, &status); |     sgx_status_t sgx_ret = libos_run(data->eid, &status, gettid()); | ||||||
|     if(sgx_ret != SGX_SUCCESS) { |     if(sgx_ret != SGX_SUCCESS) { | ||||||
|         // TODO: deal with ECALL error
 |         // TODO: deal with ECALL error
 | ||||||
|         printf("ERROR: ECall libos_run failed\n"); |         printf("ERROR: ECall libos_run failed\n"); | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ PROJECT_DIR := $(realpath $(CUR_DIR)/../) | |||||||
| # Dependencies: need to be compiled but not to run by any Makefile target
 | # Dependencies: need to be compiled but not to run by any Makefile target
 | ||||||
| TEST_DEPS := dev_null | TEST_DEPS := dev_null | ||||||
| # Tests: need to be compiled and run by test-% target
 | # Tests: need to be compiled and run by test-% target
 | ||||||
| TESTS := empty env hello_world malloc mmap file getpid spawn pipe time \
 | TESTS := empty env hello_world malloc mmap file getpid spawn sched pipe time \
 | ||||||
| 	truncate readdir mkdir link tls pthread uname rlimit client server \
 | 	truncate readdir mkdir link tls pthread uname rlimit client server \
 | ||||||
| 	server_epoll unix_socket cout hostfs cpuid rdtsc device | 	server_epoll unix_socket cout hostfs cpuid rdtsc device | ||||||
| # Benchmarks: need to be compiled and run by bench-% target
 | # Benchmarks: need to be compiled and run by bench-% target
 | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								test/sched/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										5
									
								
								test/sched/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | include ../test_common.mk | ||||||
|  | 
 | ||||||
|  | EXTRA_C_FLAGS := | ||||||
|  | EXTRA_LINK_FLAGS := | ||||||
|  | BIN_ARGS := | ||||||
							
								
								
									
										175
									
								
								test/sched/main.c
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										175
									
								
								test/sched/main.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,175 @@ | |||||||
|  | #define _GNU_SOURCE | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <assert.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <sched.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <spawn.h> | ||||||
|  | #include <sys/syscall.h> | ||||||
|  | #include <sys/wait.h> | ||||||
|  | #include "test.h" | ||||||
|  | 
 | ||||||
|  | // ============================================================================
 | ||||||
|  | // Test cases for sched_cpu_affinity
 | ||||||
|  | // ============================================================================
 | ||||||
|  | 
 | ||||||
|  | static int test_sched_getaffinity_with_self_pid() { | ||||||
|  |     cpu_set_t mask; | ||||||
|  |     if (sched_getaffinity(0, sizeof(cpu_set_t), &mask) < 0) { | ||||||
|  |         throw_error("failed to call sched_getaffinity"); | ||||||
|  |     } | ||||||
|  |     if (CPU_COUNT(&mask) <= 0) { | ||||||
|  |         throw_error("failed to get cpuset mask"); | ||||||
|  |     } | ||||||
|  |     if (sysconf(_SC_NPROCESSORS_ONLN) != CPU_COUNT(&mask)) { | ||||||
|  |         throw_error("cpuset num wrong"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int test_sched_setaffinity_with_self_pid() { | ||||||
|  |     int nproc = sysconf(_SC_NPROCESSORS_ONLN); | ||||||
|  |     cpu_set_t mask_old; | ||||||
|  |     for (int i = 0; i < nproc; ++i) { | ||||||
|  |         CPU_SET(i, &mask_old); | ||||||
|  |     } | ||||||
|  |     cpu_set_t mask; | ||||||
|  |     CPU_ZERO(&mask); | ||||||
|  |     CPU_SET(0, &mask); | ||||||
|  |     if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) < 0) { | ||||||
|  |         throw_error("failed to call sched_setaffinity \n"); | ||||||
|  |     } | ||||||
|  |     cpu_set_t mask2; | ||||||
|  |     if (sched_getaffinity(0, sizeof(cpu_set_t), &mask2) < 0) { | ||||||
|  |         throw_error("failed to call sched_getaffinity"); | ||||||
|  |     } | ||||||
|  |     if (!CPU_EQUAL(&mask, &mask2)) { | ||||||
|  |         throw_error("cpuset is wrong after get"); | ||||||
|  |     } | ||||||
|  |     if (sched_setaffinity(0, sizeof(cpu_set_t), &mask_old) < 0) { | ||||||
|  |         throw_error("recover cpuset error"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int test_sched_xetaffinity_with_child_pid() { | ||||||
|  |     int status, child_pid; | ||||||
|  |     int num = sysconf(_SC_NPROCESSORS_CONF); | ||||||
|  |     if (num <= 0) { | ||||||
|  |         throw_error("failed to get cpu number"); | ||||||
|  |     } | ||||||
|  |     cpu_set_t mask; | ||||||
|  |     CPU_ZERO(&mask); | ||||||
|  |     CPU_SET(num - 1 , &mask); | ||||||
|  |     int ret = posix_spawn(&child_pid, "getpid", NULL, NULL, NULL, NULL); | ||||||
|  |     if (ret < 0 ) { | ||||||
|  |         throw_error("spawn process error"); | ||||||
|  |     } | ||||||
|  |     printf("Spawn a child process with pid=%d\n", child_pid); | ||||||
|  |     if (sched_setaffinity(child_pid, sizeof(cpu_set_t), &mask) < 0) { | ||||||
|  |         throw_error("failed to set child affinity"); | ||||||
|  |     } | ||||||
|  |     cpu_set_t mask2; | ||||||
|  |     if (sched_getaffinity(child_pid, sizeof(cpu_set_t), &mask2) < 0) { | ||||||
|  |         throw_error("failed to get child affinity"); | ||||||
|  |     } | ||||||
|  |     if (!CPU_EQUAL(&mask, &mask2)) { | ||||||
|  |         throw_error("cpuset is wrong in child"); | ||||||
|  |     } | ||||||
|  |     ret = wait4(-1, &status, 0, NULL); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         throw_error("failed to wait4 the child proces"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define CPU_SET_SIZE_LIMIT (1024) | ||||||
|  | 
 | ||||||
|  | static int test_sched_getaffinity_via_explicit_syscall() { | ||||||
|  |     unsigned char buf[CPU_SET_SIZE_LIMIT] = { 0 }; | ||||||
|  |     int ret = syscall(__NR_sched_getaffinity, 0, CPU_SET_SIZE_LIMIT, buf); | ||||||
|  |     if (ret <= 0) { | ||||||
|  |         throw_error("failed to call __NR_sched_getaffinity"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int test_sched_setaffinity_via_explicit_syscall() { | ||||||
|  |     int nproc = sysconf(_SC_NPROCESSORS_ONLN); | ||||||
|  |     cpu_set_t mask_old; | ||||||
|  |     for (int i = 0; i < nproc; ++i) { | ||||||
|  |         CPU_SET(i, &mask_old); | ||||||
|  |     } | ||||||
|  |     cpu_set_t mask; | ||||||
|  |     CPU_ZERO(&mask); | ||||||
|  |     CPU_SET(0, &mask); | ||||||
|  |     if (syscall(__NR_sched_setaffinity, 0, sizeof(cpu_set_t), &mask) < 0) { | ||||||
|  |         throw_error("failed to call __NR_sched_setaffinity"); | ||||||
|  |     } | ||||||
|  |     cpu_set_t mask2; | ||||||
|  |     int ret_nproc = syscall(__NR_sched_getaffinity, 0, sizeof(cpu_set_t), &mask2); | ||||||
|  |     if (ret_nproc <= 0) { | ||||||
|  |         throw_error("failed to call __NR_sched_getaffinity"); | ||||||
|  |     } | ||||||
|  |     if (!CPU_EQUAL(&mask, &mask2)) { | ||||||
|  |         throw_error("explicit syscall cpuset is wrong"); | ||||||
|  |     } | ||||||
|  |     if (syscall(__NR_sched_setaffinity, 0, sizeof(cpu_set_t), &mask_old) < 0) { | ||||||
|  |         throw_error("recover cpuset error"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int test_sched_getaffinity_with_zero_cpusetsize() { | ||||||
|  |     cpu_set_t mask; | ||||||
|  |     if (sched_getaffinity(0, 0, &mask) != -1) { | ||||||
|  |         throw_error("check invalid cpusetsize(0) fail"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int test_sched_setaffinity_with_zero_cpusetsize() { | ||||||
|  |     cpu_set_t mask; | ||||||
|  |     if (sched_setaffinity(0, 0, &mask) != -1) { | ||||||
|  |         throw_error("check invalid cpusetsize(0) fail"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int test_sched_getaffinity_with_null_buffer() { | ||||||
|  |     unsigned char *buf = NULL; | ||||||
|  |     if (sched_getaffinity(0, sizeof(cpu_set_t), (cpu_set_t*)buf) != -1) { | ||||||
|  |         throw_error("check invalid buffer pointer(NULL) fail"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int test_sched_setaffinity_with_null_buffer() { | ||||||
|  |     unsigned char *buf = NULL; | ||||||
|  |     if (sched_setaffinity(0, sizeof(cpu_set_t), (cpu_set_t*)buf) != -1) { | ||||||
|  |         throw_error("check invalid buffer pointer(NULL) fail"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ============================================================================
 | ||||||
|  | // Test suite main
 | ||||||
|  | // ============================================================================
 | ||||||
|  | 
 | ||||||
|  | static test_case_t test_cases[] = { | ||||||
|  |     TEST_CASE(test_sched_xetaffinity_with_child_pid), | ||||||
|  |     TEST_CASE(test_sched_getaffinity_with_self_pid), | ||||||
|  |     TEST_CASE(test_sched_setaffinity_with_self_pid), | ||||||
|  |     TEST_CASE(test_sched_getaffinity_via_explicit_syscall), | ||||||
|  |     TEST_CASE(test_sched_setaffinity_via_explicit_syscall), | ||||||
|  |     TEST_CASE(test_sched_getaffinity_with_zero_cpusetsize), | ||||||
|  |     TEST_CASE(test_sched_setaffinity_with_zero_cpusetsize), | ||||||
|  |     TEST_CASE(test_sched_getaffinity_with_null_buffer), | ||||||
|  |     TEST_CASE(test_sched_setaffinity_with_null_buffer), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int main() { | ||||||
|  |     return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user