Initial commit
[authen-passphrase-scrypt.git] / scrypt-1.2.1 / lib / util / memlimit.c
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
66 static int
67 memlimit_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
120 static int
121 memlimit_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
151 static int
152 memlimit_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
206 static int
207 memlimit_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
248 int
249 memtouse(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.030666 seconds and 4 git commands to generate.