Files
asterinas/kernel/src/syscall/clock_gettime.rs
2024-11-13 21:44:37 +08:00

156 lines
5.4 KiB
Rust

// SPDX-License-Identifier: MPL-2.0
use core::time::Duration;
use int_to_c_enum::TryFromInt;
use super::SyscallReturn;
use crate::{
prelude::*,
process::{
posix_thread::{thread_table, AsPosixThread},
process_table,
},
time::{
clockid_t,
clocks::{
BootTimeClock, MonotonicClock, MonotonicCoarseClock, MonotonicRawClock, RealTimeClock,
RealTimeCoarseClock,
},
timespec_t, Clock,
},
};
pub fn sys_clock_gettime(
clockid: clockid_t,
timespec_addr: Vaddr,
ctx: &Context,
) -> Result<SyscallReturn> {
debug!("clockid = {:?}", clockid);
let time_duration = read_clock(clockid, ctx)?;
let timespec = timespec_t::from(time_duration);
ctx.get_user_space().write_val(timespec_addr, &timespec)?;
Ok(SyscallReturn::Return(0))
}
// The hard-coded clock IDs.
#[derive(Debug, Copy, Clone, TryFromInt, PartialEq)]
#[repr(i32)]
#[allow(non_camel_case_types)]
pub enum ClockId {
CLOCK_REALTIME = 0,
CLOCK_MONOTONIC = 1,
CLOCK_PROCESS_CPUTIME_ID = 2,
CLOCK_THREAD_CPUTIME_ID = 3,
CLOCK_MONOTONIC_RAW = 4,
CLOCK_REALTIME_COARSE = 5,
CLOCK_MONOTONIC_COARSE = 6,
CLOCK_BOOTTIME = 7,
}
/// The information decoded from a dynamic clock ID.
///
/// Dynamic clocks are the clocks operates on certain
/// character devices, processes or threads. Their IDs will
/// be generated by encoding the file descriptor, PID or TID.
/// Here we follow the rules in Linux:
///
/// The dynamic clock ID is a 32 bit integer.
/// - The most significant 29 bits hold either a PID or a file descriptor.
/// - Bit 2 indicates whether a cpu clock refers to a thread or a process.
/// - Bits 1 and 0 give the type: PROF=0, VIRT=1, SCHED=2, or FD=3.
/// - A clock ID is invalid if bits 2, 1, and 0 are all set.
///
/// Ref: https://github.com/torvalds/linux/blob/master/include/linux/posix-timers_types.h
pub enum DynamicClockIdInfo {
Pid(u32, DynamicClockType),
Tid(u32, DynamicClockType),
#[allow(dead_code)]
Fd(u32),
}
impl TryFrom<clockid_t> for DynamicClockIdInfo {
type Error = crate::Error;
fn try_from(value: clockid_t) -> core::prelude::v1::Result<Self, Self::Error> {
const CPU_CLOCK_TYPE_MASK: i32 = 0b11;
const ID_TYPE_MASK: i32 = 0b100;
const INVALID_MASK: i32 = CPU_CLOCK_TYPE_MASK | ID_TYPE_MASK;
if (value & INVALID_MASK) == INVALID_MASK {
return_errno_with_message!(Errno::EINVAL, "invalid clock ID");
}
let id = !(value >> 3);
let cpu_clock_type = DynamicClockType::try_from(CPU_CLOCK_TYPE_MASK & value)?;
if let DynamicClockType::FD = cpu_clock_type {
return Ok(DynamicClockIdInfo::Fd(id as u32));
}
if ID_TYPE_MASK & value > 0 {
Ok(DynamicClockIdInfo::Tid(id as u32, cpu_clock_type))
} else {
Ok(DynamicClockIdInfo::Pid(id as u32, cpu_clock_type))
}
}
}
#[derive(Debug, Copy, Clone, TryFromInt, PartialEq)]
#[repr(i32)]
pub enum DynamicClockType {
Profiling = 0,
Virtual = 1,
Scheduling = 2,
FD = 3,
}
/// Reads the time of a clock specified by the input clock ID.
///
/// If the clock ID does not support, this function will return `Err`.
pub fn read_clock(clockid: clockid_t, ctx: &Context) -> Result<Duration> {
if clockid >= 0 {
let clock_id = ClockId::try_from(clockid)?;
match clock_id {
ClockId::CLOCK_REALTIME => Ok(RealTimeClock::get().read_time()),
ClockId::CLOCK_MONOTONIC => Ok(MonotonicClock::get().read_time()),
ClockId::CLOCK_MONOTONIC_RAW => Ok(MonotonicRawClock::get().read_time()),
ClockId::CLOCK_REALTIME_COARSE => Ok(RealTimeCoarseClock::get().read_time()),
ClockId::CLOCK_MONOTONIC_COARSE => Ok(MonotonicCoarseClock::get().read_time()),
ClockId::CLOCK_BOOTTIME => Ok(BootTimeClock::get().read_time()),
ClockId::CLOCK_PROCESS_CPUTIME_ID => Ok(ctx.process.prof_clock().read_time()),
ClockId::CLOCK_THREAD_CPUTIME_ID => Ok(ctx.posix_thread.prof_clock().read_time()),
}
} else {
let dynamic_clockid_info = DynamicClockIdInfo::try_from(clockid)?;
match dynamic_clockid_info {
DynamicClockIdInfo::Pid(pid, clock_type) => {
let process = process_table::get_process(pid)
.ok_or_else(|| crate::Error::with_message(Errno::EINVAL, "invalid clock ID"))?;
match clock_type {
DynamicClockType::Profiling => Ok(process.prof_clock().read_time()),
DynamicClockType::Virtual => Ok(process.prof_clock().user_clock().read_time()),
// TODO: support scheduling clock and fd clock.
_ => unimplemented!(),
}
}
DynamicClockIdInfo::Tid(tid, clock_type) => {
let thread = thread_table::get_thread(tid)
.ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid clock ID"))?;
let posix_thread = thread.as_posix_thread().unwrap();
match clock_type {
DynamicClockType::Profiling => Ok(posix_thread.prof_clock().read_time()),
DynamicClockType::Virtual => {
Ok(posix_thread.prof_clock().user_clock().read_time())
}
_ => unimplemented!(),
}
}
DynamicClockIdInfo::Fd(_) => unimplemented!(),
}
}
}