solana_core/
resource_limits.rs

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