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.
30 /* We use non-POSIX functionality in this file. */
31 #undef _POSIX_C_SOURCE
33 #include "scrypt_platform.h"
35 #include <sys/types.h>
36 #include <sys/resource.h>
38 #ifdef HAVE_SYS_PARAM_H
39 #include <sys/param.h>
41 #ifdef HAVE_SYS_SYSCTL_H
42 #include <sys/sysctl.h>
44 #ifdef HAVE_SYS_SYSINFO_H
45 #include <sys/sysinfo.h>
60 /* If we don't have CTL_HW, we can't use HW_USERMEM. */
67 memlimit_sysctl_hw(size_t * memlimit
, int mibleaf
)
71 size_t sysctlbuflen
= 8;
74 /* Ask the kernel how much RAM we have. */
77 if (sysctl(mib
, 2, sysctlbuf
, &sysctlbuflen
, NULL
, 0))
81 * If we read 8 bytes out, assume this is a system-endian uint64_t.
82 * If we only read 4 bytes out, the OS is trying to give us a
83 * uint32_t answer -- but given how many systems now have 4GB+ of RAM,
84 * it's probably truncating, and we really can't trust the value we
85 * have returned to us.
87 if (sysctlbuflen
== sizeof(uint64_t))
88 memcpy(&sysctlval
, sysctlbuf
, sizeof(uint64_t));
89 else if (sysctlbuflen
== sizeof(uint32_t))
94 /* Return the sysctl value, but clamp to SIZE_MAX if necessary. */
95 #if UINT64_MAX > SIZE_MAX
96 if (sysctlval
> SIZE_MAX
)
99 *memlimit
= (size_t)sysctlval
;
101 *memlimit
= sysctlval
;
109 /* If we don't HAVE_STRUCT_SYSINFO, we can't use sysinfo. */
110 #ifndef HAVE_STRUCT_SYSINFO
114 /* If we don't HAVE_STRUCT_SYSINFO_TOTALRAM, we can't use sysinfo. */
115 #ifndef HAVE_STRUCT_SYSINFO_TOTALRAM
121 memlimit_sysinfo(size_t * memlimit
)
126 /* Get information from the kernel. */
129 totalmem
= info
.totalram
;
131 /* If we're on a modern kernel, adjust based on mem_unit. */
132 #ifdef HAVE_STRUCT_SYSINFO_MEM_UNIT
133 totalmem
= totalmem
* info
.mem_unit
;
136 /* Return the value, but clamp to SIZE_MAX if necessary. */
137 #if UINT64_MAX > SIZE_MAX
138 if (totalmem
> SIZE_MAX
)
139 *memlimit
= SIZE_MAX
;
141 *memlimit
= (size_t)totalmem
;
143 *memlimit
= totalmem
;
149 #endif /* HAVE_SYSINFO */
152 memlimit_rlimit(size_t * memlimit
)
157 /* Find the least of... */
158 memrlimit
= (uint64_t)(-1);
160 /* ... RLIMIT_AS... */
162 if (getrlimit(RLIMIT_AS
, &rl
))
164 if ((rl
.rlim_cur
!= RLIM_INFINITY
) &&
165 ((uint64_t)rl
.rlim_cur
< memrlimit
))
166 memrlimit
= (uint64_t)rl
.rlim_cur
;
169 /* ... RLIMIT_DATA... */
170 if (getrlimit(RLIMIT_DATA
, &rl
))
172 if ((rl
.rlim_cur
!= RLIM_INFINITY
) &&
173 ((uint64_t)rl
.rlim_cur
< memrlimit
))
174 memrlimit
= (uint64_t)rl
.rlim_cur
;
176 /* ... and RLIMIT_RSS. */
178 if (getrlimit(RLIMIT_RSS
, &rl
))
180 if ((rl
.rlim_cur
!= RLIM_INFINITY
) &&
181 ((uint64_t)rl
.rlim_cur
< memrlimit
))
182 memrlimit
= (uint64_t)rl
.rlim_cur
;
185 /* Return the value, but clamp to SIZE_MAX if necessary. */
186 #if UINT64_MAX > SIZE_MAX
187 if (memrlimit
> SIZE_MAX
)
188 *memlimit
= SIZE_MAX
;
190 *memlimit
= (size_t)memrlimit
;
192 *memlimit
= memrlimit
;
199 #ifdef _SC_PHYS_PAGES
201 /* Some systems define _SC_PAGESIZE instead of _SC_PAGE_SIZE. */
202 #ifndef _SC_PAGE_SIZE
203 #define _SC_PAGE_SIZE _SC_PAGESIZE
207 memlimit_sysconf(size_t * memlimit
)
213 /* Set errno to 0 in order to distinguish "no limit" from "error". */
216 /* Read the two limits. */
217 if (((pagesize
= sysconf(_SC_PAGE_SIZE
)) == -1) ||
218 ((physpages
= sysconf(_SC_PHYS_PAGES
)) == -1)) {
220 * Did an error occur? OS X may return EINVAL due to not
221 * supporting _SC_PHYS_PAGES in spite of defining it.
223 if (errno
!= 0 && errno
!= EINVAL
)
226 /* If not, there is no limit. */
227 totalmem
= (uint64_t)(-1);
229 /* Compute the limit. */
230 totalmem
= (uint64_t)(pagesize
) * (uint64_t)(physpages
);
233 /* Return the value, but clamp to SIZE_MAX if necessary. */
234 #if UINT64_MAX > SIZE_MAX
235 if (totalmem
> SIZE_MAX
)
236 *memlimit
= SIZE_MAX
;
238 *memlimit
= (size_t)totalmem
;
240 *memlimit
= totalmem
;
249 memtouse(size_t maxmem
, double maxmemfrac
, size_t * memlimit
)
251 size_t usermem_memlimit
, memsize_memlimit
;
252 size_t sysinfo_memlimit
, rlimit_memlimit
;
253 size_t sysconf_memlimit
;
257 /* Get memory limits. */
259 if (memlimit_sysctl_hw(&usermem_memlimit
, HW_USERMEM
))
262 usermem_memlimit
= SIZE_MAX
;
265 if (memlimit_sysctl_hw(&memsize_memlimit
, HW_MEMSIZE
))
268 memsize_memlimit
= SIZE_MAX
;
271 if (memlimit_sysinfo(&sysinfo_memlimit
))
274 sysinfo_memlimit
= SIZE_MAX
;
276 if (memlimit_rlimit(&rlimit_memlimit
))
278 #ifdef _SC_PHYS_PAGES
279 if (memlimit_sysconf(&sysconf_memlimit
))
282 sysconf_memlimit
= SIZE_MAX
;
286 fprintf(stderr
, "Memory limits are %zu %zu %zu %zu %zu\n",
287 usermem_memlimit
, memsize_memlimit
,
288 sysinfo_memlimit
, rlimit_memlimit
,
292 /* Find the smallest of them. */
293 memlimit_min
= SIZE_MAX
;
294 if (memlimit_min
> usermem_memlimit
)
295 memlimit_min
= usermem_memlimit
;
296 if (memlimit_min
> memsize_memlimit
)
297 memlimit_min
= memsize_memlimit
;
298 if (memlimit_min
> sysinfo_memlimit
)
299 memlimit_min
= sysinfo_memlimit
;
300 if (memlimit_min
> rlimit_memlimit
)
301 memlimit_min
= rlimit_memlimit
;
302 if (memlimit_min
> sysconf_memlimit
)
303 memlimit_min
= sysconf_memlimit
;
305 /* Only use the specified fraction of the available memory. */
306 if ((maxmemfrac
> 0.5) || (maxmemfrac
== 0.0))
308 memavail
= (size_t)(maxmemfrac
* memlimit_min
);
310 /* Don't use more than the specified maximum. */
311 if ((maxmem
> 0) && (memavail
> maxmem
))
314 /* But always allow at least 1 MiB. */
315 if (memavail
< 1048576)
319 fprintf(stderr
, "Allowing up to %zu memory to be used\n", memavail
);
322 /* Return limit via the provided pointer. */
323 *memlimit
= memavail
;
This page took 0.030666 seconds and 4 git commands to generate.