Initial commit
[authen-passphrase-scrypt.git] / scrypt-1.2.1 / lib / util / memlimit.c
CommitLineData
0c1f3509
MG
1/*-
2 * Copyright 2009 Colin Percival
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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
24 * SUCH DAMAGE.
25 *
26 * This file was originally written by Colin Percival as part of the Tarsnap
27 * online backup system.
28 */
29
30/* We use non-POSIX functionality in this file. */
31#undef _POSIX_C_SOURCE
32
33#include "scrypt_platform.h"
34
35#include <sys/types.h>
36#include <sys/resource.h>
37
38#ifdef HAVE_SYS_PARAM_H
39#include <sys/param.h>
40#endif
41#ifdef HAVE_SYS_SYSCTL_H
42#include <sys/sysctl.h>
43#endif
44#ifdef HAVE_SYS_SYSINFO_H
45#include <sys/sysinfo.h>
46#endif
47
48#include <errno.h>
49#include <stddef.h>
50#include <stdint.h>
51#include <string.h>
52#include <unistd.h>
53
54#ifdef DEBUG
55#include <stdio.h>
56#endif
57
58#include "memlimit.h"
59
60/* If we don't have CTL_HW, we can't use HW_USERMEM. */
61#ifndef CTL_HW
62#undef HW_USERMEM
63#endif
64
65#ifdef CTL_HW
66static int
67memlimit_sysctl_hw(size_t * memlimit, int mibleaf)
68{
69 int mib[2];
70 uint8_t sysctlbuf[8];
71 size_t sysctlbuflen = 8;
72 uint64_t sysctlval;
73
74 /* Ask the kernel how much RAM we have. */
75 mib[0] = CTL_HW;
76 mib[1] = mibleaf;
77 if (sysctl(mib, 2, sysctlbuf, &sysctlbuflen, NULL, 0))
78 return (1);
79
80 /*
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.
86 */
87 if (sysctlbuflen == sizeof(uint64_t))
88 memcpy(&sysctlval, sysctlbuf, sizeof(uint64_t));
89 else if (sysctlbuflen == sizeof(uint32_t))
90 sysctlval = SIZE_MAX;
91 else
92 return (1);
93
94 /* Return the sysctl value, but clamp to SIZE_MAX if necessary. */
95#if UINT64_MAX > SIZE_MAX
96 if (sysctlval > SIZE_MAX)
97 *memlimit = SIZE_MAX;
98 else
99 *memlimit = (size_t)sysctlval;
100#else
101 *memlimit = sysctlval;
102#endif
103
104 /* Success! */
105 return (0);
106}
107#endif
108
109/* If we don't HAVE_STRUCT_SYSINFO, we can't use sysinfo. */
110#ifndef HAVE_STRUCT_SYSINFO
111#undef HAVE_SYSINFO
112#endif
113
114/* If we don't HAVE_STRUCT_SYSINFO_TOTALRAM, we can't use sysinfo. */
115#ifndef HAVE_STRUCT_SYSINFO_TOTALRAM
116#undef HAVE_SYSINFO
117#endif
118
119#ifdef HAVE_SYSINFO
120static int
121memlimit_sysinfo(size_t * memlimit)
122{
123 struct sysinfo info;
124 uint64_t totalmem;
125
126 /* Get information from the kernel. */
127 if (sysinfo(&info))
128 return (1);
129 totalmem = info.totalram;
130
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;
134#endif
135
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;
140 else
141 *memlimit = (size_t)totalmem;
142#else
143 *memlimit = totalmem;
144#endif
145
146 /* Success! */
147 return (0);
148}
149#endif /* HAVE_SYSINFO */
150
151static int
152memlimit_rlimit(size_t * memlimit)
153{
154 struct rlimit rl;
155 uint64_t memrlimit;
156
157 /* Find the least of... */
158 memrlimit = (uint64_t)(-1);
159
160 /* ... RLIMIT_AS... */
161#ifdef RLIMIT_AS
162 if (getrlimit(RLIMIT_AS, &rl))
163 return (1);
164 if ((rl.rlim_cur != RLIM_INFINITY) &&
165 ((uint64_t)rl.rlim_cur < memrlimit))
166 memrlimit = (uint64_t)rl.rlim_cur;
167#endif
168
169 /* ... RLIMIT_DATA... */
170 if (getrlimit(RLIMIT_DATA, &rl))
171 return (1);
172 if ((rl.rlim_cur != RLIM_INFINITY) &&
173 ((uint64_t)rl.rlim_cur < memrlimit))
174 memrlimit = (uint64_t)rl.rlim_cur;
175
176 /* ... and RLIMIT_RSS. */
177#ifdef RLIMIT_RSS
178 if (getrlimit(RLIMIT_RSS, &rl))
179 return (1);
180 if ((rl.rlim_cur != RLIM_INFINITY) &&
181 ((uint64_t)rl.rlim_cur < memrlimit))
182 memrlimit = (uint64_t)rl.rlim_cur;
183#endif
184
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;
189 else
190 *memlimit = (size_t)memrlimit;
191#else
192 *memlimit = memrlimit;
193#endif
194
195 /* Success! */
196 return (0);
197}
198
199#ifdef _SC_PHYS_PAGES
200
201/* Some systems define _SC_PAGESIZE instead of _SC_PAGE_SIZE. */
202#ifndef _SC_PAGE_SIZE
203#define _SC_PAGE_SIZE _SC_PAGESIZE
204#endif
205
206static int
207memlimit_sysconf(size_t * memlimit)
208{
209 long pagesize;
210 long physpages;
211 uint64_t totalmem;
212
213 /* Set errno to 0 in order to distinguish "no limit" from "error". */
214 errno = 0;
215
216 /* Read the two limits. */
217 if (((pagesize = sysconf(_SC_PAGE_SIZE)) == -1) ||
218 ((physpages = sysconf(_SC_PHYS_PAGES)) == -1)) {
219 /*
220 * Did an error occur? OS X may return EINVAL due to not
221 * supporting _SC_PHYS_PAGES in spite of defining it.
222 */
223 if (errno != 0 && errno != EINVAL)
224 return (1);
225
226 /* If not, there is no limit. */
227 totalmem = (uint64_t)(-1);
228 } else {
229 /* Compute the limit. */
230 totalmem = (uint64_t)(pagesize) * (uint64_t)(physpages);
231 }
232
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;
237 else
238 *memlimit = (size_t)totalmem;
239#else
240 *memlimit = totalmem;
241#endif
242
243 /* Success! */
244 return (0);
245}
246#endif
247
248int
249memtouse(size_t maxmem, double maxmemfrac, size_t * memlimit)
250{
251 size_t usermem_memlimit, memsize_memlimit;
252 size_t sysinfo_memlimit, rlimit_memlimit;
253 size_t sysconf_memlimit;
254 size_t memlimit_min;
255 size_t memavail;
256
257 /* Get memory limits. */
258#ifdef HW_USERMEM
259 if (memlimit_sysctl_hw(&usermem_memlimit, HW_USERMEM))
260 return (1);
261#else
262 usermem_memlimit = SIZE_MAX;
263#endif
264#ifdef HW_MEMSIZE
265 if (memlimit_sysctl_hw(&memsize_memlimit, HW_MEMSIZE))
266 return (1);
267#else
268 memsize_memlimit = SIZE_MAX;
269#endif
270#ifdef HAVE_SYSINFO
271 if (memlimit_sysinfo(&sysinfo_memlimit))
272 return (1);
273#else
274 sysinfo_memlimit = SIZE_MAX;
275#endif
276 if (memlimit_rlimit(&rlimit_memlimit))
277 return (1);
278#ifdef _SC_PHYS_PAGES
279 if (memlimit_sysconf(&sysconf_memlimit))
280 return (1);
281#else
282 sysconf_memlimit = SIZE_MAX;
283#endif
284
285#ifdef DEBUG
286 fprintf(stderr, "Memory limits are %zu %zu %zu %zu %zu\n",
287 usermem_memlimit, memsize_memlimit,
288 sysinfo_memlimit, rlimit_memlimit,
289 sysconf_memlimit);
290#endif
291
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;
304
305 /* Only use the specified fraction of the available memory. */
306 if ((maxmemfrac > 0.5) || (maxmemfrac == 0.0))
307 maxmemfrac = 0.5;
308 memavail = (size_t)(maxmemfrac * memlimit_min);
309
310 /* Don't use more than the specified maximum. */
311 if ((maxmem > 0) && (memavail > maxmem))
312 memavail = maxmem;
313
314 /* But always allow at least 1 MiB. */
315 if (memavail < 1048576)
316 memavail = 1048576;
317
318#ifdef DEBUG
319 fprintf(stderr, "Allowing up to %zu memory to be used\n", memavail);
320#endif
321
322 /* Return limit via the provided pointer. */
323 *memlimit = memavail;
324 return (0);
325}
This page took 0.027896 seconds and 4 git commands to generate.