2 * Copyright 2009 Colin Percival
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * This file was originally written by Colin Percival as part of the Tarsnap
27 * online backup system.
29 #include "scrypt_platform.h"
31 #include <sys/types.h>
32 #include <sys/resource.h>
34 #ifdef HAVE_SYS_PARAM_H
35 #include <sys/param.h>
37 #ifdef HAVE_SYSCTL_HW_USERMEM
38 #include <sys/sysctl.h>
40 #ifdef HAVE_SYS_SYSINFO_H
41 #include <sys/sysinfo.h>
55 #ifdef HAVE_SYSCTL_HW_USERMEM
57 memlimit_sysctl_hw_usermem(size_t * memlimit)
60 uint8_t usermembuf[8];
61 size_t usermemlen = 8;
64 /* Ask the kernel how much RAM we have. */
67 if (sysctl(mib, 2, usermembuf, &usermemlen, NULL, 0))
71 * Parse as either a uint64_t or a uint32_t based on the length of
72 * output the kernel reports having copied out. It appears that all
73 * systems providing a sysctl interface for reading integers copy
74 * them out as system-endian values, so we don't need to worry about
77 if (usermemlen == sizeof(uint64_t))
78 usermem = *(uint64_t *)usermembuf;
79 else if (usermemlen == sizeof(uint32_t))
80 usermem = *(uint32_t *)usermembuf;
84 /* Return the sysctl value, but clamp to SIZE_MAX if necessary. */
85 #if UINT64_MAX > SIZE_MAX
86 if (usermem > SIZE_MAX)
99 /* If we don't HAVE_STRUCT_SYSINFO, we can't use sysinfo. */
100 #ifndef HAVE_STRUCT_SYSINFO
104 /* If we don't HAVE_STRUCT_SYSINFO_TOTALRAM, we can't use sysinfo. */
105 #ifndef HAVE_STRUCT_SYSINFO_TOTALRAM
111 memlimit_sysinfo(size_t * memlimit)
116 /* Get information from the kernel. */
119 totalmem = info.totalram;
121 /* If we're on a modern kernel, adjust based on mem_unit. */
122 #ifdef HAVE_STRUCT_SYSINFO_MEM_UNIT
123 totalmem = totalmem * info.mem_unit;
126 /* Return the value, but clamp to SIZE_MAX if necessary. */
127 #if UINT64_MAX > SIZE_MAX
128 if (totalmem > SIZE_MAX)
129 *memlimit = SIZE_MAX;
131 *memlimit = totalmem;
133 *memlimit = totalmem;
139 #endif /* HAVE_SYSINFO */
142 memlimit_rlimit(size_t * memlimit)
147 /* Find the least of... */
148 memrlimit = (uint64_t)(-1);
150 /* ... RLIMIT_AS... */
152 if (getrlimit(RLIMIT_AS, &rl))
154 if ((rl.rlim_cur != RLIM_INFINITY) &&
155 ((uint64_t)rl.rlim_cur < memrlimit))
156 memrlimit = rl.rlim_cur;
159 /* ... RLIMIT_DATA... */
160 if (getrlimit(RLIMIT_DATA, &rl))
162 if ((rl.rlim_cur != RLIM_INFINITY) &&
163 ((uint64_t)rl.rlim_cur < memrlimit))
164 memrlimit = rl.rlim_cur;
166 /* ... and RLIMIT_RSS. */
168 if (getrlimit(RLIMIT_RSS, &rl))
170 if ((rl.rlim_cur != RLIM_INFINITY) &&
171 ((uint64_t)rl.rlim_cur < memrlimit))
172 memrlimit = rl.rlim_cur;
175 /* Return the value, but clamp to SIZE_MAX if necessary. */
176 #if UINT64_MAX > SIZE_MAX
177 if (memrlimit > SIZE_MAX)
178 *memlimit = SIZE_MAX;
180 *memlimit = memrlimit;
182 *memlimit = memrlimit;
189 #ifdef _SC_PHYS_PAGES
191 /* Some systems define _SC_PAGESIZE instead of _SC_PAGE_SIZE. */
192 #ifndef _SC_PAGE_SIZE
193 #define _SC_PAGE_SIZE _SC_PAGESIZE
197 memlimit_sysconf(size_t * memlimit)
203 /* Set errno to 0 in order to distinguish "no limit" from "error". */
206 /* Read the two limits. */
207 if (((pagesize = sysconf(_SC_PAGE_SIZE)) == -1) ||
208 ((physpages = sysconf(_SC_PHYS_PAGES)) == -1)) {
209 /* Did an error occur? */
213 /* If not, there is no limit. */
214 totalmem = (uint64_t)(-1);
216 /* Compute the limit. */
217 totalmem = (uint64_t)(pagesize) * (uint64_t)(physpages);
220 /* Return the value, but clamp to SIZE_MAX if necessary. */
221 #if UINT64_MAX > SIZE_MAX
222 if (totalmem > SIZE_MAX)
223 *memlimit = SIZE_MAX;
225 *memlimit = totalmem;
227 *memlimit = totalmem;
236 memtouse(size_t maxmem, double maxmemfrac, size_t * memlimit)
238 size_t sysctl_memlimit, sysinfo_memlimit, rlimit_memlimit;
239 size_t sysconf_memlimit;
243 /* Get memory limits. */
244 #ifdef HAVE_SYSCTL_HW_USERMEM
245 if (memlimit_sysctl_hw_usermem(&sysctl_memlimit))
248 sysctl_memlimit = (size_t)(-1);
251 if (memlimit_sysinfo(&sysinfo_memlimit))
254 sysinfo_memlimit = (size_t)(-1);
256 if (memlimit_rlimit(&rlimit_memlimit))
258 #ifdef _SC_PHYS_PAGES
259 if (memlimit_sysconf(&sysconf_memlimit))
262 sysconf_memlimit = (size_t)(-1);
266 fprintf(stderr, "Memory limits are %zu %zu %zu %zu\n",
267 sysctl_memlimit, sysinfo_memlimit, rlimit_memlimit,
271 /* Find the smallest of them. */
272 memlimit_min = (size_t)(-1);
273 if (memlimit_min > sysctl_memlimit)
274 memlimit_min = sysctl_memlimit;
275 if (memlimit_min > sysinfo_memlimit)
276 memlimit_min = sysinfo_memlimit;
277 if (memlimit_min > rlimit_memlimit)
278 memlimit_min = rlimit_memlimit;
279 if (memlimit_min > sysconf_memlimit)
280 memlimit_min = sysconf_memlimit;
282 /* Only use the specified fraction of the available memory. */
283 if ((maxmemfrac > 0.5) || (maxmemfrac == 0.0))
285 memavail = maxmemfrac * memlimit_min;
287 /* Don't use more than the specified maximum. */
288 if ((maxmem > 0) && (memavail > maxmem))
291 /* But always allow at least 1 MiB. */
292 if (memavail < 1048576)
296 fprintf(stderr, "Allowing up to %zu memory to be used\n", memavail);
299 /* Return limit via the provided pointer. */
300 *memlimit = memavail;