base/sys/linux/
mod.rs

1// Copyright 2017 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Small system utility modules for usage by other modules.
6
7#[cfg(target_os = "android")]
8mod android;
9#[cfg(target_os = "android")]
10use android as target_os;
11#[cfg(target_os = "linux")]
12#[allow(clippy::module_inception)]
13mod linux;
14#[cfg(target_os = "linux")]
15use linux as target_os;
16use log::warn;
17#[macro_use]
18pub mod ioctl;
19#[macro_use]
20pub mod syslog;
21mod capabilities;
22mod descriptor;
23mod event;
24mod file;
25mod file_traits;
26mod mmap;
27mod net;
28mod notifiers;
29pub mod platform_timer_resolution;
30mod poll;
31mod priority;
32mod sched;
33mod shm;
34pub mod signal;
35mod signalfd;
36mod terminal;
37mod timer;
38pub mod vsock;
39mod write_zeroes;
40
41use std::ffi::CString;
42use std::fs::remove_file;
43use std::fs::File;
44use std::fs::OpenOptions;
45use std::mem;
46use std::mem::MaybeUninit;
47use std::ops::Deref;
48use std::os::fd::AsRawFd;
49use std::os::fd::BorrowedFd;
50use std::os::unix::io::FromRawFd;
51use std::os::unix::io::RawFd;
52use std::os::unix::net::UnixDatagram;
53use std::os::unix::net::UnixListener;
54use std::os::unix::process::ExitStatusExt;
55use std::path::Path;
56use std::path::PathBuf;
57use std::process::ExitStatus;
58use std::ptr;
59use std::sync::OnceLock;
60use std::time::Duration;
61
62pub use capabilities::drop_capabilities;
63pub use event::EventExt;
64pub(crate) use event::PlatformEvent;
65pub use file::find_next_data;
66pub use file::FileDataIterator;
67pub(crate) use file_traits::lib::*;
68pub use ioctl::*;
69use libc::c_int;
70use libc::c_long;
71use libc::fcntl;
72use libc::pipe2;
73use libc::prctl;
74use libc::syscall;
75use libc::waitpid;
76use libc::SYS_getpid;
77use libc::SYS_getppid;
78use libc::SYS_gettid;
79use libc::EINVAL;
80use libc::O_CLOEXEC;
81use libc::PR_SET_NAME;
82use libc::SIGKILL;
83use libc::WNOHANG;
84pub use mmap::*;
85pub(in crate::sys) use net::sendmsg_nosignal as sendmsg;
86pub(in crate::sys) use net::sockaddr_un;
87pub(in crate::sys) use net::sockaddrv4_to_lib_c;
88pub(in crate::sys) use net::sockaddrv6_to_lib_c;
89pub use poll::EventContext;
90pub use priority::*;
91pub use sched::*;
92pub use shm::MemfdSeals;
93pub use shm::SharedMemoryLinux;
94pub use signal::*;
95pub use signalfd::Error as SignalFdError;
96pub use signalfd::*;
97pub use terminal::*;
98pub(crate) use write_zeroes::file_punch_hole;
99pub(crate) use write_zeroes::file_write_zeroes_at;
100
101use crate::descriptor::FromRawDescriptor;
102use crate::descriptor::SafeDescriptor;
103pub use crate::errno::Error;
104pub use crate::errno::Result;
105pub use crate::errno::*;
106use crate::number_of_logical_cores;
107use crate::round_up_to_page_size;
108pub use crate::sys::unix::descriptor::*;
109use crate::syscall;
110use crate::AsRawDescriptor;
111use crate::IoBuf;
112use crate::IoBufMut;
113use crate::Pid;
114
115/// Re-export libc types that are part of the API.
116pub type Uid = libc::uid_t;
117pub type Gid = libc::gid_t;
118pub type Mode = libc::mode_t;
119
120// Directory that holds cpu sysinfo files.
121const CPU_DIR: &str = "/sys/devices/system/cpu";
122
123/// Safe wrapper for PR_SET_NAME(2const)
124#[inline(always)]
125pub fn set_thread_name(name: &str) -> Result<()> {
126    let name = CString::new(name).or(Err(Error::new(EINVAL)))?;
127    // SAFETY: prctl copies name and doesn't expect it to outlive this function.
128    let ret = unsafe { prctl(PR_SET_NAME, name.as_c_str()) };
129    if ret == 0 {
130        Ok(())
131    } else {
132        errno_result()
133    }
134}
135
136/// This bypasses `libc`'s caching `getpid(2)` wrapper which can be invalid if a raw clone was used
137/// elsewhere.
138#[inline(always)]
139pub fn getpid() -> Pid {
140    // SAFETY:
141    // Safe because this syscall can never fail and we give it a valid syscall number.
142    unsafe { syscall(SYS_getpid as c_long) as Pid }
143}
144
145/// Safe wrapper for the geppid Linux systemcall.
146#[inline(always)]
147pub fn getppid() -> Pid {
148    // SAFETY:
149    // Safe because this syscall can never fail and we give it a valid syscall number.
150    unsafe { syscall(SYS_getppid as c_long) as Pid }
151}
152
153/// Safe wrapper for the gettid Linux systemcall.
154pub fn gettid() -> Pid {
155    // SAFETY:
156    // Calling the gettid() sycall is always safe.
157    unsafe { syscall(SYS_gettid as c_long) as Pid }
158}
159
160/// Safe wrapper for `geteuid(2)`.
161#[inline(always)]
162pub fn geteuid() -> Uid {
163    // SAFETY:
164    // trivially safe
165    unsafe { libc::geteuid() }
166}
167
168/// Safe wrapper for `getegid(2)`.
169#[inline(always)]
170pub fn getegid() -> Gid {
171    // SAFETY:
172    // trivially safe
173    unsafe { libc::getegid() }
174}
175
176/// The operation to perform with `flock`.
177pub enum FlockOperation {
178    LockShared,
179    LockExclusive,
180    Unlock,
181}
182
183/// Safe wrapper for flock(2) with the operation `op` and optionally `nonblocking`. The lock will be
184/// dropped automatically when `file` is dropped.
185#[inline(always)]
186pub fn flock<F: AsRawDescriptor>(file: &F, op: FlockOperation, nonblocking: bool) -> Result<()> {
187    let mut operation = match op {
188        FlockOperation::LockShared => libc::LOCK_SH,
189        FlockOperation::LockExclusive => libc::LOCK_EX,
190        FlockOperation::Unlock => libc::LOCK_UN,
191    };
192
193    if nonblocking {
194        operation |= libc::LOCK_NB;
195    }
196
197    // SAFETY:
198    // Safe since we pass in a valid fd and flock operation, and check the return value.
199    syscall!(unsafe { libc::flock(file.as_raw_descriptor(), operation) }).map(|_| ())
200}
201
202/// The operation to perform with `fallocate`.
203pub enum FallocateMode {
204    PunchHole,
205    ZeroRange,
206    Allocate,
207}
208
209impl From<FallocateMode> for i32 {
210    fn from(value: FallocateMode) -> Self {
211        match value {
212            FallocateMode::Allocate => libc::FALLOC_FL_KEEP_SIZE,
213            FallocateMode::PunchHole => libc::FALLOC_FL_PUNCH_HOLE | libc::FALLOC_FL_KEEP_SIZE,
214            FallocateMode::ZeroRange => libc::FALLOC_FL_ZERO_RANGE | libc::FALLOC_FL_KEEP_SIZE,
215        }
216    }
217}
218
219impl From<FallocateMode> for u32 {
220    fn from(value: FallocateMode) -> Self {
221        Into::<i32>::into(value) as u32
222    }
223}
224
225/// Safe wrapper for `fallocate()`.
226pub fn fallocate<F: AsRawDescriptor>(
227    file: &F,
228    mode: FallocateMode,
229    offset: u64,
230    len: u64,
231) -> Result<()> {
232    let offset = if offset > libc::off64_t::MAX as u64 {
233        return Err(Error::new(libc::EINVAL));
234    } else {
235        offset as libc::off64_t
236    };
237
238    let len = if len > libc::off64_t::MAX as u64 {
239        return Err(Error::new(libc::EINVAL));
240    } else {
241        len as libc::off64_t
242    };
243
244    // SAFETY:
245    // Safe since we pass in a valid fd and fallocate mode, validate offset and len,
246    // and check the return value.
247    syscall!(unsafe { libc::fallocate64(file.as_raw_descriptor(), mode.into(), offset, len) })
248        .map(|_| ())
249}
250
251/// Arguments for how `openat2(2)` should open the target path.
252#[repr(C)]
253#[derive(Default, Debug, Copy, Clone)]
254pub struct open_how {
255    /// Flags for the open operation (e.g. `O_RDONLY`).
256    pub flags: u64,
257    /// Mode for the created file (if `O_CREAT` is set).
258    pub mode: u64,
259    /// Resolution flags (e.g. `RESOLVE_IN_ROOT`).
260    pub resolve: u64,
261}
262
263/// Safe wrapper for `openat2(2)`.
264pub fn openat2<D: AsRawDescriptor>(dir: &D, name: &std::ffi::CStr, how: &open_how) -> Result<File> {
265    // SAFETY:
266    // Safe because the syscall is provided with valid arguments and its return value is checked.
267    syscall!(unsafe {
268        libc::syscall(
269            libc::SYS_openat2,
270            dir.as_raw_descriptor(),
271            name.as_ptr(),
272            how as *const open_how,
273            std::mem::size_of::<open_how>() as libc::size_t,
274        )
275    })
276    .map(|fd| unsafe { File::from_raw_descriptor(fd as RawFd) })
277}
278
279/// Safe wrapper for `fstat()`.
280pub fn fstat<F: AsRawDescriptor>(f: &F) -> Result<libc::stat64> {
281    let mut st = MaybeUninit::<libc::stat64>::zeroed();
282
283    // SAFETY:
284    // Safe because the kernel will only write data in `st` and we check the return
285    // value.
286    syscall!(unsafe { libc::fstat64(f.as_raw_descriptor(), st.as_mut_ptr()) })?;
287
288    // SAFETY:
289    // Safe because the kernel guarantees that the struct is now fully initialized.
290    Ok(unsafe { st.assume_init() })
291}
292
293/// Checks whether a file is a block device fie or not.
294pub fn is_block_file<F: AsRawDescriptor>(file: &F) -> Result<bool> {
295    let stat = fstat(file)?;
296    Ok((stat.st_mode & libc::S_IFMT) == libc::S_IFBLK)
297}
298
299const BLOCK_IO_TYPE: u32 = 0x12;
300ioctl_io_nr!(BLKDISCARD, BLOCK_IO_TYPE, 119);
301
302/// Discards the given range of a block file.
303pub fn discard_block<F: AsRawDescriptor>(file: &F, offset: u64, len: u64) -> Result<()> {
304    let range: [u64; 2] = [offset, len];
305    // SAFETY:
306    // Safe because
307    // - we check the return value.
308    // - ioctl(BLKDISCARD) does not hold the descriptor after the call.
309    // - ioctl(BLKDISCARD) does not break the file descriptor.
310    // - ioctl(BLKDISCARD) does not modify the given range.
311    syscall!(unsafe { libc::ioctl(file.as_raw_descriptor(), BLKDISCARD, &range) }).map(|_| ())
312}
313
314/// A trait used to abstract types that provide a process id that can be operated on.
315pub trait AsRawPid {
316    fn as_raw_pid(&self) -> Pid;
317}
318
319impl AsRawPid for Pid {
320    fn as_raw_pid(&self) -> Pid {
321        *self
322    }
323}
324
325impl AsRawPid for std::process::Child {
326    fn as_raw_pid(&self) -> Pid {
327        self.id() as Pid
328    }
329}
330
331/// A safe wrapper around waitpid.
332///
333/// On success if a process was reaped, it will be returned as the first value.
334/// The second returned value is the ExitStatus from the libc::waitpid() call.
335///
336/// Note: this can block if libc::WNOHANG is not set and EINTR is not handled internally.
337pub fn wait_for_pid<A: AsRawPid>(pid: A, options: c_int) -> Result<(Option<Pid>, ExitStatus)> {
338    let pid = pid.as_raw_pid();
339    let mut status: c_int = 1;
340    // SAFETY:
341    // Safe because status is owned and the error is checked.
342    let ret = unsafe { libc::waitpid(pid, &mut status, options) };
343    if ret < 0 {
344        return errno_result();
345    }
346    Ok((
347        if ret == 0 { None } else { Some(ret) },
348        ExitStatus::from_raw(status),
349    ))
350}
351
352/// Reaps a child process that has terminated.
353///
354/// Returns `Ok(pid)` where `pid` is the process that was reaped or `Ok(0)` if none of the children
355/// have terminated. An `Error` is with `errno == ECHILD` if there are no children left to reap.
356///
357/// # Examples
358///
359/// Reaps all child processes until there are no terminated children to reap.
360///
361/// ```
362/// fn reap_children() {
363///     loop {
364///         match base::linux::reap_child() {
365///             Ok(0) => println!("no children ready to reap"),
366///             Ok(pid) => {
367///                 println!("reaped {}", pid);
368///                 continue
369///             },
370///             Err(e) if e.errno() == libc::ECHILD => println!("no children left"),
371///             Err(e) => println!("error reaping children: {}", e),
372///         }
373///         break
374///     }
375/// }
376/// ```
377pub fn reap_child() -> Result<Pid> {
378    // SAFETY:
379    // Safe because we pass in no memory, prevent blocking with WNOHANG, and check for error.
380    let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
381    if ret == -1 {
382        errno_result()
383    } else {
384        Ok(ret)
385    }
386}
387
388/// Kill all processes in the current process group.
389///
390/// On success, this kills all processes in the current process group, including the current
391/// process, meaning this will not return. This is equivalent to a call to `kill(0, SIGKILL)`.
392pub fn kill_process_group() -> Result<()> {
393    // SAFETY: Safe because pid is 'self group' and return value doesn't matter.
394    unsafe { kill(0, SIGKILL) }?;
395    // Kill succeeded, so this process never reaches here.
396    unreachable!();
397}
398
399/// Spawns a pipe pair where the first pipe is the read end and the second pipe is the write end.
400///
401/// The `O_CLOEXEC` flag will be set during pipe creation.
402pub fn pipe() -> Result<(File, File)> {
403    let mut pipe_fds = [-1; 2];
404    // SAFETY:
405    // Safe because pipe2 will only write 2 element array of i32 to the given pointer, and we check
406    // for error.
407    let ret = unsafe { pipe2(&mut pipe_fds[0], O_CLOEXEC) };
408    if ret == -1 {
409        errno_result()
410    } else {
411        // SAFETY:
412        // Safe because both fds must be valid for pipe2 to have returned sucessfully and we have
413        // exclusive ownership of them.
414        Ok(unsafe {
415            (
416                File::from_raw_fd(pipe_fds[0]),
417                File::from_raw_fd(pipe_fds[1]),
418            )
419        })
420    }
421}
422
423/// Sets the pipe signified with fd to `size`.
424///
425/// Returns the new size of the pipe or an error if the OS fails to set the pipe size.
426pub fn set_pipe_size(fd: RawFd, size: usize) -> Result<usize> {
427    // SAFETY:
428    // Safe because fcntl with the `F_SETPIPE_SZ` arg doesn't touch memory.
429    syscall!(unsafe { fcntl(fd, libc::F_SETPIPE_SZ, size as c_int) }).map(|ret| ret as usize)
430}
431
432/// Test-only function used to create a pipe that is full. The pipe is created, has its size set to
433/// the minimum and then has that much data written to it. Use `new_pipe_full` to test handling of
434/// blocking `write` calls in unit tests.
435pub fn new_pipe_full() -> Result<(File, File)> {
436    use std::io::Write;
437
438    let (rx, mut tx) = pipe()?;
439    // The smallest allowed size of a pipe is the system page size on linux.
440    let page_size = set_pipe_size(tx.as_raw_descriptor(), round_up_to_page_size(1))?;
441
442    // Fill the pipe with page_size zeros so the next write call will block.
443    let buf = vec![0u8; page_size];
444    tx.write_all(&buf)?;
445
446    Ok((rx, tx))
447}
448
449/// Used to attempt to clean up a named pipe after it is no longer used.
450pub struct UnlinkUnixDatagram(pub UnixDatagram);
451impl AsRef<UnixDatagram> for UnlinkUnixDatagram {
452    fn as_ref(&self) -> &UnixDatagram {
453        &self.0
454    }
455}
456impl Drop for UnlinkUnixDatagram {
457    fn drop(&mut self) {
458        if let Ok(addr) = self.0.local_addr() {
459            if let Some(path) = addr.as_pathname() {
460                if let Err(e) = remove_file(path) {
461                    warn!("failed to remove control socket file: {}", e);
462                }
463            }
464        }
465    }
466}
467
468/// Used to attempt to clean up a named pipe after it is no longer used.
469pub struct UnlinkUnixListener(pub UnixListener);
470
471impl AsRef<UnixListener> for UnlinkUnixListener {
472    fn as_ref(&self) -> &UnixListener {
473        &self.0
474    }
475}
476
477impl Deref for UnlinkUnixListener {
478    type Target = UnixListener;
479
480    fn deref(&self) -> &UnixListener {
481        &self.0
482    }
483}
484
485impl Drop for UnlinkUnixListener {
486    fn drop(&mut self) {
487        if let Ok(addr) = self.0.local_addr() {
488            if let Some(path) = addr.as_pathname() {
489                if let Err(e) = remove_file(path) {
490                    warn!("failed to remove control socket file: {}", e);
491                }
492            }
493        }
494    }
495}
496
497/// Verifies that |raw_descriptor| is actually owned by this process and duplicates it
498/// to ensure that we have a unique handle to it.
499pub fn validate_raw_descriptor(raw_descriptor: RawDescriptor) -> Result<RawDescriptor> {
500    validate_raw_fd(&raw_descriptor)
501}
502
503/// Verifies that |raw_fd| is actually owned by this process and duplicates it to ensure that
504/// we have a unique handle to it.
505pub fn validate_raw_fd(raw_fd: &RawFd) -> Result<RawFd> {
506    // Checking that close-on-exec isn't set helps filter out FDs that were opened by
507    // crosvm as all crosvm FDs are close on exec.
508    // SAFETY:
509    // Safe because this doesn't modify any memory and we check the return value.
510    let flags = unsafe { libc::fcntl(*raw_fd, libc::F_GETFD) };
511    if flags < 0 || (flags & libc::FD_CLOEXEC) != 0 {
512        return Err(Error::new(libc::EBADF));
513    }
514
515    // SAFETY:
516    // Duplicate the fd to ensure that we don't accidentally close an fd previously
517    // opened by another subsystem.  Safe because this doesn't modify any memory and
518    // we check the return value.
519    let dup_fd = unsafe { libc::fcntl(*raw_fd, libc::F_DUPFD_CLOEXEC, 0) };
520    if dup_fd < 0 {
521        return Err(Error::last());
522    }
523    Ok(dup_fd as RawFd)
524}
525
526/// Utility function that returns true if the given FD is readable without blocking.
527///
528/// On an error, such as an invalid or incompatible FD, this will return false, which can not be
529/// distinguished from a non-ready to read FD.
530pub fn poll_in<F: AsRawDescriptor>(fd: &F) -> bool {
531    let mut fds = libc::pollfd {
532        fd: fd.as_raw_descriptor(),
533        events: libc::POLLIN,
534        revents: 0,
535    };
536    // SAFETY:
537    // Safe because we give a valid pointer to a list (of 1) FD and check the return value.
538    let ret = unsafe { libc::poll(&mut fds, 1, 0) };
539    // An error probably indicates an invalid FD, or an FD that can't be polled. Returning false in
540    // that case is probably correct as such an FD is unlikely to be readable, although there are
541    // probably corner cases in which that is wrong.
542    if ret == -1 {
543        return false;
544    }
545    fds.revents & libc::POLLIN != 0
546}
547
548/// Return the maximum Duration that can be used with libc::timespec.
549pub fn max_timeout() -> Duration {
550    Duration::new(libc::time_t::MAX as u64, 999999999)
551}
552
553/// If the given path is of the form /proc/self/fd/N for some N, returns `Ok(Some(N))`. Otherwise
554/// returns `Ok(None)`.
555pub fn safe_descriptor_from_path<P: AsRef<Path>>(path: P) -> Result<Option<SafeDescriptor>> {
556    let path = path.as_ref();
557    if path.parent() == Some(Path::new("/proc/self/fd")) {
558        let raw_descriptor = path
559            .file_name()
560            .and_then(|fd_osstr| fd_osstr.to_str())
561            .and_then(|fd_str| fd_str.parse::<RawFd>().ok())
562            .ok_or_else(|| Error::new(EINVAL))?;
563        let validated_fd = validate_raw_fd(&raw_descriptor)?;
564        Ok(Some(
565            // SAFETY:
566            // Safe because nothing else has access to validated_fd after this call.
567            unsafe { SafeDescriptor::from_raw_descriptor(validated_fd) },
568        ))
569    } else {
570        Ok(None)
571    }
572}
573
574/// Check FD is not opened by crosvm and returns a FD that is freshly DUPFD_CLOEXEC's.
575/// A SafeDescriptor is created from the duplicated fd. It does not take ownership of
576/// fd passed by argument.
577pub fn safe_descriptor_from_cmdline_fd(fd: &RawFd) -> Result<SafeDescriptor> {
578    let validated_fd = validate_raw_fd(fd)?;
579    Ok(
580        // SAFETY:
581        // Safe because nothing else has access to validated_fd after this call.
582        unsafe { SafeDescriptor::from_raw_descriptor(validated_fd) },
583    )
584}
585
586/// Open the file with the given path, or if it is of the form `/proc/self/fd/N` then just use the
587/// file descriptor.
588///
589/// Note that this will not work properly if the same `/proc/self/fd/N` path is used twice in
590/// different places, as the metadata (including the offset) will be shared between both file
591/// descriptors.
592pub fn open_file_or_duplicate<P: AsRef<Path>>(path: P, options: &OpenOptions) -> Result<File> {
593    let path = path.as_ref();
594    // Special case '/proc/self/fd/*' paths. The FD is already open, just use it.
595    Ok(if let Some(fd) = safe_descriptor_from_path(path)? {
596        fd.into()
597    } else {
598        options.open(path)?
599    })
600}
601
602/// Get the soft and hard limits of max number of open files allowed by the environment.
603pub fn max_open_files() -> Result<libc::rlimit64> {
604    let mut buf = mem::MaybeUninit::<libc::rlimit64>::zeroed();
605
606    // SAFETY:
607    // Safe because this will only modify `buf` and we check the return value.
608    let res = unsafe { libc::prlimit64(0, libc::RLIMIT_NOFILE, ptr::null(), buf.as_mut_ptr()) };
609    if res == 0 {
610        // SAFETY:
611        // Safe because the kernel guarantees that the struct is fully initialized.
612        let limit = unsafe { buf.assume_init() };
613        Ok(limit)
614    } else {
615        errno_result()
616    }
617}
618
619/// Executes the given callback with extended soft limit of max number of open files. After the
620/// callback executed, restore the limit.
621pub fn call_with_extended_max_files<T, E>(
622    callback: impl FnOnce() -> std::result::Result<T, E>,
623) -> Result<std::result::Result<T, E>> {
624    let cur_limit = max_open_files()?;
625    let new_limit = libc::rlimit64 {
626        rlim_cur: cur_limit.rlim_max,
627        ..cur_limit
628    };
629    let needs_extension = cur_limit.rlim_cur < new_limit.rlim_cur;
630    if needs_extension {
631        set_max_open_files(new_limit)?;
632    }
633
634    let r = callback();
635
636    // Restore the soft limit.
637    if needs_extension {
638        set_max_open_files(cur_limit)?;
639    }
640
641    Ok(r)
642}
643
644/// Set the soft and hard limits of max number of open files to the given value.
645fn set_max_open_files(limit: libc::rlimit64) -> Result<()> {
646    // SAFETY: RLIMIT_NOFILE is known only to read a buffer of size rlimit64, and we have always
647    // rlimit64 allocated.
648    let res = unsafe { libc::setrlimit64(libc::RLIMIT_NOFILE, &limit) };
649    if res == 0 {
650        Ok(())
651    } else {
652        errno_result()
653    }
654}
655
656/// Moves the requested PID/TID to a particular cgroup
657pub fn move_to_cgroup(cgroup_path: PathBuf, id_to_write: Pid, cgroup_file: &str) -> Result<()> {
658    use std::io::Write;
659
660    let gpu_cgroup_file = cgroup_path.join(cgroup_file);
661    let mut f = File::create(gpu_cgroup_file)?;
662    f.write_all(id_to_write.to_string().as_bytes())?;
663    Ok(())
664}
665
666pub fn move_task_to_cgroup(cgroup_path: PathBuf, thread_id: Pid) -> Result<()> {
667    move_to_cgroup(cgroup_path, thread_id, "tasks")
668}
669
670pub fn move_proc_to_cgroup(cgroup_path: PathBuf, process_id: Pid) -> Result<()> {
671    move_to_cgroup(cgroup_path, process_id, "cgroup.procs")
672}
673
674fn read_sysfs_cpu_info_in_dir(cpu_dir: &str, cpu_id: usize, property: &str) -> Result<String> {
675    let path = Path::new(cpu_dir)
676        .join(format!("cpu{cpu_id}"))
677        .join(property);
678
679    std::fs::read_to_string(path).map_err(|e| e.into())
680}
681
682/// Queries the property of a specified CPU sysfs node.
683fn parse_sysfs_cpu_info_vec(cpu_id: usize, property: &str) -> Result<Vec<u32>> {
684    parse_sysfs_cpu_info_vec_in_dir(CPU_DIR, cpu_id, property)
685}
686
687fn parse_sysfs_cpu_info_vec_in_dir(
688    cpu_dir: &str,
689    cpu_id: usize,
690    property: &str,
691) -> Result<Vec<u32>> {
692    read_sysfs_cpu_info_in_dir(cpu_dir, cpu_id, property)?
693        .split_whitespace()
694        .map(|x| x.parse().map_err(|_| Error::new(libc::EINVAL)))
695        .collect()
696}
697
698/// Returns a list of supported frequencies in kHz for a given logical core.
699pub fn logical_core_frequencies_khz(cpu_id: usize) -> Result<Vec<u32>> {
700    parse_sysfs_cpu_info_vec(cpu_id, "cpufreq/scaling_available_frequencies")
701}
702
703/// Queries the property of a specified CPU sysfs node.
704fn parse_sysfs_cpu_info(cpu_id: usize, property: &str) -> Result<u32> {
705    parse_sysfs_cpu_info_in_dir(CPU_DIR, cpu_id, property)
706}
707
708fn parse_sysfs_cpu_info_in_dir(cpu_dir: &str, cpu_id: usize, property: &str) -> Result<u32> {
709    read_sysfs_cpu_info_in_dir(cpu_dir, cpu_id, property)?
710        .trim()
711        .parse()
712        .map_err(|_| Error::new(libc::EINVAL))
713}
714
715/// Returns the capacity (measure of performance) of a given logical core.
716pub fn logical_core_capacity(cpu_id: usize) -> Result<u32> {
717    static CPU_MAX_FREQS: OnceLock<Option<Vec<u32>>> = OnceLock::new();
718
719    let cpu_capacity = parse_sysfs_cpu_info(cpu_id, "cpu_capacity")?;
720
721    // Collect and cache the maximum frequencies of all cores. We need to know
722    // the largest maximum frequency between all cores to reverse normalization,
723    // so collect all the values once on the first call to this function.
724    let cpu_max_freqs = CPU_MAX_FREQS.get_or_init(|| {
725        (0..number_of_logical_cores().ok()?)
726            .map(|cpu_id| logical_core_max_freq_khz(cpu_id).ok())
727            .collect()
728    });
729
730    if let Some(cpu_max_freqs) = cpu_max_freqs {
731        let largest_max_freq = *cpu_max_freqs.iter().max().ok_or(Error::new(EINVAL))?;
732        let cpu_max_freq = *cpu_max_freqs.get(cpu_id).ok_or(Error::new(EINVAL))?;
733        let normalized_cpu_capacity = (u64::from(cpu_capacity) * u64::from(largest_max_freq))
734            .checked_div(u64::from(cpu_max_freq))
735            .ok_or(Error::new(EINVAL))?;
736        normalized_cpu_capacity
737            .try_into()
738            .map_err(|_| Error::new(EINVAL))
739    } else {
740        // cpu-freq is not enabled. Fall back to using the normalized capacity.
741        Ok(cpu_capacity)
742    }
743}
744
745/// Returns the cluster ID of a given logical core.
746pub fn logical_core_cluster_id(cpu_id: usize) -> Result<u32> {
747    parse_sysfs_cpu_info(cpu_id, "topology/physical_package_id")
748}
749
750/// Returns the maximum frequency (in kHz) of a given logical core.
751pub fn logical_core_max_freq_khz(cpu_id: usize) -> Result<u32> {
752    parse_sysfs_cpu_info(cpu_id, "cpufreq/cpuinfo_max_freq")
753}
754
755/// Parses a string of comma separated CPU ranges, e.g. "0-2,4,6-8" into a BTreeSet of CPU IDs.
756fn parse_online_cpu_range(content: &str) -> std::collections::BTreeSet<usize> {
757    let mut cpus = std::collections::BTreeSet::new();
758    for part in content.trim().split(',') {
759        let part = part.trim();
760        if part.is_empty() {
761            continue;
762        }
763        if let Some((start_str, end_str)) = part.split_once('-') {
764            if let (Ok(start), Ok(end)) = (start_str.parse::<usize>(), end_str.parse::<usize>()) {
765                for i in start..=end {
766                    cpus.insert(i);
767                }
768            }
769        } else if let Ok(cpu) = part.parse::<usize>() {
770            cpus.insert(cpu);
771        }
772    }
773    cpus
774}
775
776/// Returns a bool if the CPU is online. The online status is cached on the first call.
777pub fn is_cpu_online(cpu_id: usize) -> bool {
778    static ONLINE_CPUS: OnceLock<std::collections::BTreeSet<usize>> = OnceLock::new();
779
780    let online_cpus = ONLINE_CPUS.get_or_init(|| {
781        let mut cpus = std::collections::BTreeSet::new();
782        let path = Path::new(CPU_DIR).join("online");
783        match std::fs::read_to_string(&path) {
784            Ok(content) => {
785                cpus = parse_online_cpu_range(&content);
786            }
787            Err(_) => {
788                // If we hit an error trying to access cpuX/online files, assume the CPU is online.
789                // This prevents permission/EACCES errors on individual files from crashing crosvm.
790                if let Ok(total_cores) = crate::number_of_logical_cores() {
791                    for id in 0..total_cores {
792                        match parse_sysfs_cpu_info(id, "online") {
793                            Ok(1) => {
794                                cpus.insert(id);
795                            }
796                            Ok(_) => {}
797                            Err(e) => {
798                                // Assume online on error to avoid crashes.
799                                warn!(
800                                    "Assuming CPU {} is online because we couldn't read the sys file: {}",
801                                    id, e
802                                );
803                                cpus.insert(id);
804                            }
805                        }
806                    }
807                }
808            }
809        }
810        cpus
811    });
812
813    online_cpus.contains(&cpu_id)
814}
815
816/// Wrapper around the `preadv2` syscall for standard I/O slices.
817///
818/// We invoke the Linux syscall directly using `libc::syscall` instead of `libc::preadv2`
819/// because some host build environments (e.g. older glibc sysroots like glibc 2.17 used in
820/// Android prebuilts for Cuttlefish) do not export `preadv2` in libc.
821pub fn preadv2(
822    fd: BorrowedFd<'_>,
823    iovs: &mut [IoBufMut],
824    offset: libc::off_t,
825    flags: libc::c_int,
826) -> libc::ssize_t {
827    if iovs.is_empty() {
828        return 0;
829    }
830    let pos_l = offset as libc::c_ulong;
831    let pos_h = 0;
832    // SAFETY:
833    // Safe because `IoBufMut` is ABI-compatible with `libc::iovec`, the buffers referenced by
834    // `iovs` are valid for writes for the duration of the call, and the file descriptor is valid.
835    unsafe {
836        libc::syscall(
837            libc::SYS_preadv2,
838            fd.as_raw_fd(),
839            iovs.as_mut_ptr() as *mut libc::iovec,
840            iovs.len(),
841            pos_l,
842            pos_h,
843            flags,
844        ) as libc::ssize_t
845    }
846}
847
848/// Wrapper around the `pwritev2` syscall for standard I/O slices.
849///
850/// We invoke the Linux syscall directly using `libc::syscall` instead of `libc::pwritev2`
851/// because some host build environments (e.g. older glibc sysroots like glibc 2.17 used in
852/// Android prebuilts for Cuttlefish) do not export `pwritev2` in libc.
853pub fn pwritev2(
854    fd: BorrowedFd<'_>,
855    iovs: &[IoBuf],
856    offset: libc::off_t,
857    flags: libc::c_int,
858) -> libc::ssize_t {
859    if iovs.is_empty() {
860        return 0;
861    }
862    let pos_l = offset as libc::c_ulong;
863    let pos_h = 0;
864    // SAFETY:
865    // Safe because `IoBuf` is ABI-compatible with `libc::iovec`, the buffers referenced by
866    // `iovs` are valid for reads for the duration of the call, and the file descriptor is valid.
867    unsafe {
868        libc::syscall(
869            libc::SYS_pwritev2,
870            fd.as_raw_fd(),
871            iovs.as_ptr(),
872            iovs.len(),
873            pos_l,
874            pos_h,
875            flags,
876        ) as libc::ssize_t
877    }
878}
879
880#[repr(C)]
881pub struct sched_attr {
882    pub size: u32,
883
884    pub sched_policy: u32,
885    pub sched_flags: u64,
886    pub sched_nice: i32,
887
888    pub sched_priority: u32,
889
890    pub sched_runtime: u64,
891    pub sched_deadline: u64,
892    pub sched_period: u64,
893
894    pub sched_util_min: u32,
895    pub sched_util_max: u32,
896}
897
898impl Default for sched_attr {
899    fn default() -> Self {
900        Self {
901            size: std::mem::size_of::<sched_attr>() as u32,
902            sched_policy: 0,
903            sched_flags: 0,
904            sched_nice: 0,
905            sched_priority: 0,
906            sched_runtime: 0,
907            sched_deadline: 0,
908            sched_period: 0,
909            sched_util_min: 0,
910            sched_util_max: 0,
911        }
912    }
913}
914
915pub fn sched_setattr(pid: Pid, attr: &mut sched_attr, flags: u32) -> Result<()> {
916    // SAFETY: Safe becuase all the args are valid and the return valud is checked.
917    let ret = unsafe {
918        libc::syscall(
919            libc::SYS_sched_setattr,
920            pid as usize,
921            attr as *mut sched_attr as usize,
922            flags as usize,
923        )
924    };
925
926    if ret < 0 {
927        return Err(Error::last());
928    }
929    Ok(())
930}
931
932#[cfg(test)]
933mod tests {
934    use std::fs::create_dir_all;
935    use std::fs::File;
936    use std::io::Write;
937    use std::os::fd::AsRawFd;
938
939    use tempfile::TempDir;
940
941    use super::*;
942    use crate::unix::add_fd_flags;
943
944    fn create_temp_file(path: &Path, content: &str) {
945        if let Some(parent) = path.parent() {
946            create_dir_all(parent).unwrap();
947        }
948        let mut file = File::create(path).unwrap();
949        file.write_all(content.as_bytes()).unwrap();
950    }
951
952    #[test]
953    fn test_parse_online_cpu_range() {
954        let set = parse_online_cpu_range("0-3,5-7");
955        assert_eq!(set.len(), 7);
956        assert!(set.contains(&0));
957        assert!(set.contains(&1));
958        assert!(set.contains(&2));
959        assert!(set.contains(&3));
960        assert!(!set.contains(&4));
961        assert!(set.contains(&5));
962        assert!(set.contains(&6));
963        assert!(set.contains(&7));
964
965        let set = parse_online_cpu_range("0");
966        assert_eq!(set.len(), 1);
967        assert!(set.contains(&0));
968
969        let set = parse_online_cpu_range("0,2,4");
970        assert_eq!(set.len(), 3);
971        assert!(set.contains(&0));
972        assert!(set.contains(&2));
973        assert!(set.contains(&4));
974
975        let set = parse_online_cpu_range("  0-1,  3  ");
976        assert_eq!(set.len(), 3);
977        assert!(set.contains(&0));
978        assert!(set.contains(&1));
979        assert!(set.contains(&3));
980
981        let set = parse_online_cpu_range("");
982        assert!(set.is_empty());
983    }
984
985    #[test]
986    fn pipe_size_and_fill() {
987        let (_rx, mut tx) = new_pipe_full().expect("Failed to pipe");
988
989        // To  check that setting the size worked, set the descriptor to non blocking and check that
990        // write returns an error.
991        add_fd_flags(tx.as_raw_fd(), libc::O_NONBLOCK).expect("Failed to set tx non blocking");
992        tx.write(&[0u8; 8])
993            .expect_err("Write after fill didn't fail");
994    }
995
996    #[test]
997    fn test_parse_sysfs_cpu_info() {
998        let temp_dir = TempDir::new().unwrap();
999        let root = temp_dir.path();
1000        let cpu_dir = root.join("sys/devices/system/cpu");
1001        let cpu = 0;
1002        let property = "cpufreq/cpuinfo_max_freq";
1003        create_temp_file(
1004            &root.join("sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"),
1005            "1000",
1006        );
1007
1008        assert_eq!(
1009            parse_sysfs_cpu_info_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap(),
1010            1000
1011        );
1012    }
1013
1014    #[test]
1015    fn test_parse_sysfs_cpu_info_error() {
1016        let temp_dir = TempDir::new().unwrap();
1017        let root = temp_dir.path();
1018        let cpu_dir = root.join("sys/devices/system/cpu");
1019        let cpu = 0;
1020        let property = "cpufreq/cpuinfo_max_freq";
1021        // Not creating the sysinfo file should result in an error trying to read from it.
1022
1023        let err =
1024            parse_sysfs_cpu_info_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap_err();
1025        assert_eq!(err, Error::new(libc::ENOENT));
1026    }
1027
1028    #[test]
1029    fn test_parse_sysfs_cpu_info_vec() {
1030        let temp_dir = TempDir::new().unwrap();
1031        let root = temp_dir.path();
1032        let cpu_dir = root.join("sys/devices/system/cpu");
1033        let cpu = 0;
1034        let property = "cpufreq/scaling_available_frequencies";
1035        create_temp_file(
1036            &root.join("sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies"),
1037            "1000 2000",
1038        );
1039
1040        assert_eq!(
1041            parse_sysfs_cpu_info_vec_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap(),
1042            vec![1000, 2000]
1043        );
1044    }
1045
1046    #[test]
1047    fn test_parse_sysfs_cpu_info_vec_error() {
1048        let temp_dir = TempDir::new().unwrap();
1049        let root = temp_dir.path();
1050        let cpu_dir = root.join("sys/devices/system/cpu");
1051        let cpu = 0;
1052        let property = "cpufreq/scaling_available_frequencies";
1053        // Not creating the sysinfo file should result in an error trying to read from it.
1054
1055        let err =
1056            parse_sysfs_cpu_info_vec_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap_err();
1057        assert_eq!(err, Error::new(libc::ENOENT));
1058    }
1059}