starnix_uapi/
resource_limits.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::errors::{Errno, error};
6use crate::{
7    RLIM_INFINITY, RLIMIT_AS, RLIMIT_CORE, RLIMIT_CPU, RLIMIT_DATA, RLIMIT_FSIZE, RLIMIT_LOCKS,
8    RLIMIT_MEMLOCK, RLIMIT_MSGQUEUE, RLIMIT_NICE, RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_RSS,
9    RLIMIT_RTPRIO, RLIMIT_RTTIME, RLIMIT_SIGPENDING, RLIMIT_STACK, rlimit, uapi,
10};
11use std::collections::HashMap;
12
13/// A description of a resource.
14pub struct ResourceDesc {
15    /// The name of the resource.
16    pub name: &'static str,
17
18    /// The units in which limits on the resource are expressed.
19    pub unit: &'static str,
20}
21
22impl ResourceDesc {
23    fn new(name: &'static str, unit: &'static str) -> ResourceDesc {
24        ResourceDesc { name, unit }
25    }
26}
27
28#[allow(clippy::upper_case_acronyms)]
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub enum Resource {
31    CPU,
32    FSIZE,
33    DATA,
34    STACK,
35    CORE,
36    RSS,
37    NPROC,
38    NOFILE,
39    MEMLOCK,
40    AS,
41    LOCKS,
42    SIGPENDING,
43    MSGQUEUE,
44    NICE,
45    RTPRIO,
46    RTTIME,
47}
48
49impl Resource {
50    pub const ALL: [Resource; 16] = [
51        Resource::CPU,
52        Resource::FSIZE,
53        Resource::DATA,
54        Resource::STACK,
55        Resource::CORE,
56        Resource::RSS,
57        Resource::NPROC,
58        Resource::NOFILE,
59        Resource::MEMLOCK,
60        Resource::AS,
61        Resource::LOCKS,
62        Resource::SIGPENDING,
63        Resource::MSGQUEUE,
64        Resource::NICE,
65        Resource::RTPRIO,
66        Resource::RTTIME,
67    ];
68
69    pub fn from_raw(raw: u32) -> Result<Resource, Errno> {
70        Ok(match raw {
71            RLIMIT_CPU => Resource::CPU,
72            RLIMIT_FSIZE => Resource::FSIZE,
73            RLIMIT_DATA => Resource::DATA,
74            RLIMIT_STACK => Resource::STACK,
75            RLIMIT_CORE => Resource::CORE,
76            RLIMIT_RSS => Resource::RSS,
77            RLIMIT_NPROC => Resource::NPROC,
78            RLIMIT_NOFILE => Resource::NOFILE,
79            RLIMIT_MEMLOCK => Resource::MEMLOCK,
80            RLIMIT_AS => Resource::AS,
81            RLIMIT_LOCKS => Resource::LOCKS,
82            RLIMIT_SIGPENDING => Resource::SIGPENDING,
83            RLIMIT_MSGQUEUE => Resource::MSGQUEUE,
84            RLIMIT_NICE => Resource::NICE,
85            RLIMIT_RTPRIO => Resource::RTPRIO,
86            RLIMIT_RTTIME => Resource::RTTIME,
87            _ => return error!(EINVAL),
88        })
89    }
90
91    pub fn desc(&self) -> ResourceDesc {
92        match self {
93            Resource::CPU => ResourceDesc::new("Max cpu time", "seconds"),
94            Resource::FSIZE => ResourceDesc::new("Max file size", "bytes"),
95            Resource::DATA => ResourceDesc::new("Max data size", "bytes"),
96            Resource::STACK => ResourceDesc::new("Max stack size", "bytes"),
97            Resource::CORE => ResourceDesc::new("Max core file size", "bytes"),
98            Resource::RSS => ResourceDesc::new("Max resident set", "bytes"),
99            Resource::NPROC => ResourceDesc::new("Max processes", "processes"),
100            Resource::NOFILE => ResourceDesc::new("Max open files", "files"),
101            Resource::MEMLOCK => ResourceDesc::new("Max locked memory", "bytes"),
102            Resource::AS => ResourceDesc::new("Max address space", "bytes"),
103            Resource::LOCKS => ResourceDesc::new("Max file locks", "bytes"),
104            Resource::SIGPENDING => ResourceDesc::new("Max pending signals", "signals"),
105            Resource::MSGQUEUE => ResourceDesc::new("Max msgqueue size", "bytes"),
106            Resource::NICE => ResourceDesc::new("Max nice priority", ""),
107            Resource::RTPRIO => ResourceDesc::new("Max realtime priority", ""),
108            Resource::RTTIME => ResourceDesc::new("Max realtime timeout", "us"),
109        }
110    }
111}
112
113#[derive(Debug, Clone)]
114pub struct ResourceLimits {
115    values: HashMap<Resource, rlimit>,
116}
117
118const INFINITE_LIMIT: rlimit =
119    rlimit { rlim_cur: RLIM_INFINITY as u64, rlim_max: RLIM_INFINITY as u64 };
120
121// Most default limit values are the same that are used in GVisor, see
122// https://github.com/google/gvisor/blob/master/pkg/abi/linux/limits.go .
123
124const NPROC_LIMIT: u64 = 0x1FFFFFFF;
125
126// GVisor sets defaults for `SIGPENDING` to 0, but that's incorrect since it would block all
127// real-time signals. Set it to `max_threads / 2` (same as `NPROC_LIMIT`).
128const SIGPENDING_LIMIT: u64 = 0x1FFFFFFF;
129
130const DEFAULT_LIMITS: [(Resource, rlimit); 9] = [
131    (Resource::STACK, rlimit { rlim_cur: uapi::_STK_LIM as u64, rlim_max: RLIM_INFINITY as u64 }),
132    (Resource::CORE, rlimit { rlim_cur: 0, rlim_max: uapi::RLIM_INFINITY as u64 }),
133    (Resource::NPROC, rlimit { rlim_cur: NPROC_LIMIT, rlim_max: NPROC_LIMIT }),
134    (
135        Resource::NOFILE,
136        rlimit { rlim_cur: uapi::INR_OPEN_CUR as u64, rlim_max: uapi::INR_OPEN_MAX as u64 },
137    ),
138    (
139        Resource::MEMLOCK,
140        rlimit { rlim_cur: uapi::MLOCK_LIMIT as u64, rlim_max: uapi::MLOCK_LIMIT as u64 },
141    ),
142    (Resource::SIGPENDING, rlimit { rlim_cur: SIGPENDING_LIMIT, rlim_max: SIGPENDING_LIMIT }),
143    (
144        Resource::MSGQUEUE,
145        rlimit { rlim_cur: uapi::MQ_BYTES_MAX as u64, rlim_max: uapi::MQ_BYTES_MAX as u64 },
146    ),
147    (Resource::NICE, rlimit { rlim_cur: 0, rlim_max: 0 }),
148    (Resource::RTPRIO, rlimit { rlim_cur: 0, rlim_max: 0 }),
149];
150
151impl Default for ResourceLimits {
152    fn default() -> Self {
153        let mut limits = Self { values: Default::default() };
154        for (resource, value) in DEFAULT_LIMITS.iter() {
155            limits.set(*resource, *value);
156        }
157        limits
158    }
159}
160
161// NB: ResourceLimits objects are accessed using a lock.  To avoid the risk of
162// deadlock, we try to avoid lock acquisition in ResourceLimits.
163impl ResourceLimits {
164    pub fn get(&self, resource: Resource) -> rlimit {
165        *self.values.get(&resource).unwrap_or(&INFINITE_LIMIT)
166    }
167
168    pub fn set(&mut self, resource: Resource, value: rlimit) {
169        self.values.insert(resource, value);
170    }
171}