Skip to main content

solana_core/
resource_limits.rs

1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum ResourceLimitError {
5    #[cfg(unix)]
6    #[error(
7        "unable to increase the nofile limit to {desired} from {current}; setrlimit() error: \
8         {error}"
9    )]
10    Nofile {
11        desired: libc::rlim_t,
12        current: libc::rlim_t,
13        error: libc::c_int,
14    },
15}
16
17#[cfg(not(unix))]
18pub fn adjust_nofile_limit(_enforce_nofile_limit: bool) -> Result<(), ResourceLimitError> {
19    Ok(())
20}
21
22#[cfg(unix)]
23pub fn adjust_nofile_limit(enforce_nofile_limit: bool) -> Result<(), ResourceLimitError> {
24    // AccountsDB and RocksDB both may have many files open so bump the limit
25    // to ensure each database will be able to function properly
26    //
27    // This should be kept in sync with published validator instructions:
28    // https://docs.anza.xyz/operations/guides/validator-start#system-tuning
29    let desired_nofile = 1_000_000;
30
31    fn get_nofile() -> libc::rlimit {
32        let mut nofile = libc::rlimit {
33            rlim_cur: 0,
34            rlim_max: 0,
35        };
36        if unsafe { libc::getrlimit(libc::RLIMIT_NOFILE, &mut nofile) } != 0 {
37            warn!("getrlimit(RLIMIT_NOFILE) failed");
38        }
39        nofile
40    }
41
42    let mut nofile = get_nofile();
43    let current = nofile.rlim_cur;
44    if current < desired_nofile {
45        nofile.rlim_cur = desired_nofile;
46        let return_value = unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &nofile) };
47        if return_value != 0 {
48            let error = ResourceLimitError::Nofile {
49                desired: desired_nofile,
50                current,
51                error: return_value,
52            };
53
54            if cfg!(target_os = "macos") {
55                error!(
56                    "{error}. On macOS you may need to run |sudo launchctl limit maxfiles \
57                     {desired_nofile} {desired_nofile}| first",
58                );
59            } else {
60                error!("{error}");
61            };
62
63            if enforce_nofile_limit {
64                return Err(error);
65            }
66        }
67
68        nofile = get_nofile();
69    }
70    info!("Maximum open file descriptors: {}", nofile.rlim_cur);
71    Ok(())
72}
73
74/// Check kernel memory lock limit and tires to increase it if necessary.
75///
76/// Returns `false` when current limit is below `min_required` and cannot be increased.
77#[cfg(target_os = "linux")]
78fn try_adjust_ulimit_memlock(min_required: usize) -> bool {
79    fn get_memlock() -> libc::rlimit {
80        let mut memlock = libc::rlimit {
81            rlim_cur: 0,
82            rlim_max: 0,
83        };
84        if unsafe { libc::getrlimit(libc::RLIMIT_MEMLOCK, &mut memlock) } != 0 {
85            log::warn!("getrlimit(RLIMIT_MEMLOCK) failed");
86        }
87        memlock
88    }
89
90    let mut memlock = get_memlock();
91    let current = memlock.rlim_cur as usize;
92    if current < min_required {
93        memlock.rlim_cur = min_required as u64;
94        memlock.rlim_max = min_required as u64;
95        if unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &memlock) } != 0 {
96            log::warn!(
97                "Unable to increase the maximum memory lock limit to {min_required} from {current}"
98            );
99
100            if cfg!(target_os = "macos") {
101                log::warn!(
102                    "On mac OS you may need to run |sudo launchctl limit memlock {min_required} \
103                     {min_required}| first"
104                );
105            }
106            return false;
107        }
108
109        memlock = get_memlock();
110        log::info!("Bumped maximum memory lock limit: {}", memlock.rlim_cur);
111    }
112    true
113}
114
115pub fn check_memlock_limit_for_disk_io(required_size: usize) -> bool {
116    #[cfg(target_os = "linux")]
117    {
118        // memory locked requirement is only necessary on linux where io_uring is used
119        try_adjust_ulimit_memlock(required_size)
120    }
121    #[cfg(not(target_os = "linux"))]
122    {
123        let _ = required_size;
124        false
125    }
126}