Remove /unsubscribe from htaccess config
[blerg.git] / builddeps / scrypt-1.1.6 / 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 #include "scrypt_platform.h"
30
31 #include <sys/types.h>
32 #include <sys/resource.h>
33
34 #ifdef HAVE_SYS_PARAM_H
35 #include <sys/param.h>
36 #endif
37 #ifdef HAVE_SYSCTL_HW_USERMEM
38 #include <sys/sysctl.h>
39 #endif
40 #ifdef HAVE_SYS_SYSINFO_H
41 #include <sys/sysinfo.h>
42 #endif
43
44 #include <errno.h>
45 #include <stddef.h>
46 #include <stdint.h>
47 #include <unistd.h>
48
49 #ifdef DEBUG
50 #include <stdio.h>
51 #endif
52
53 #include "memlimit.h"
54
55 #ifdef HAVE_SYSCTL_HW_USERMEM
56 static int
57 memlimit_sysctl_hw_usermem(size_t * memlimit)
58 {
59         int mib[2];
60         uint8_t usermembuf[8];
61         size_t usermemlen = 8;
62         uint64_t usermem;
63
64         /* Ask the kernel how much RAM we have. */
65         mib[0] = CTL_HW;
66         mib[1] = HW_USERMEM;
67         if (sysctl(mib, 2, usermembuf, &usermemlen, NULL, 0))
68                 return (1);
69
70         /*
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
75          * parsing them.
76          */
77         if (usermemlen == sizeof(uint64_t))
78                 usermem = *(uint64_t *)usermembuf;
79         else if (usermemlen == sizeof(uint32_t))
80                 usermem = *(uint32_t *)usermembuf;
81         else
82                 return (1);
83
84         /* Return the sysctl value, but clamp to SIZE_MAX if necessary. */
85 #if UINT64_MAX > SIZE_MAX
86         if (usermem > SIZE_MAX)
87                 *memlimit = SIZE_MAX;
88         else
89                 *memlimit = usermem;
90 #else
91         *memlimit = usermem;
92 #endif
93
94         /* Success! */
95         return (0);
96 }
97 #endif
98
99 /* If we don't HAVE_STRUCT_SYSINFO, we can't use sysinfo. */
100 #ifndef HAVE_STRUCT_SYSINFO
101 #undef HAVE_SYSINFO
102 #endif
103
104 /* If we don't HAVE_STRUCT_SYSINFO_TOTALRAM, we can't use sysinfo. */
105 #ifndef HAVE_STRUCT_SYSINFO_TOTALRAM
106 #undef HAVE_SYSINFO
107 #endif
108
109 #ifdef HAVE_SYSINFO
110 static int
111 memlimit_sysinfo(size_t * memlimit)
112 {
113         struct sysinfo info;
114         uint64_t totalmem;
115
116         /* Get information from the kernel. */
117         if (sysinfo(&info))
118                 return (1);
119         totalmem = info.totalram;
120
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;
124 #endif
125
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;
130         else
131                 *memlimit = totalmem;
132 #else
133         *memlimit = totalmem;
134 #endif
135
136         /* Success! */
137         return (0);
138 }
139 #endif /* HAVE_SYSINFO */
140
141 static int
142 memlimit_rlimit(size_t * memlimit)
143 {
144         struct rlimit rl;
145         uint64_t memrlimit;
146
147         /* Find the least of... */
148         memrlimit = (uint64_t)(-1);
149
150         /* ... RLIMIT_AS... */
151 #ifdef RLIMIT_AS
152         if (getrlimit(RLIMIT_AS, &rl))
153                 return (1);
154         if ((rl.rlim_cur != RLIM_INFINITY) &&
155             ((uint64_t)rl.rlim_cur < memrlimit))
156                 memrlimit = rl.rlim_cur;
157 #endif
158
159         /* ... RLIMIT_DATA... */
160         if (getrlimit(RLIMIT_DATA, &rl))
161                 return (1);
162         if ((rl.rlim_cur != RLIM_INFINITY) &&
163             ((uint64_t)rl.rlim_cur < memrlimit))
164                 memrlimit = rl.rlim_cur;
165
166         /* ... and RLIMIT_RSS. */
167 #ifdef RLIMIT_RSS
168         if (getrlimit(RLIMIT_RSS, &rl))
169                 return (1);
170         if ((rl.rlim_cur != RLIM_INFINITY) &&
171             ((uint64_t)rl.rlim_cur < memrlimit))
172                 memrlimit = rl.rlim_cur;
173 #endif
174
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;
179         else
180                 *memlimit = memrlimit;
181 #else
182         *memlimit = memrlimit;
183 #endif
184
185         /* Success! */
186         return (0);
187 }
188
189 #ifdef _SC_PHYS_PAGES
190
191 /* Some systems define _SC_PAGESIZE instead of _SC_PAGE_SIZE. */
192 #ifndef _SC_PAGE_SIZE
193 #define _SC_PAGE_SIZE _SC_PAGESIZE
194 #endif
195
196 static int
197 memlimit_sysconf(size_t * memlimit)
198 {
199         long pagesize;
200         long physpages;
201         uint64_t totalmem;
202
203         /* Set errno to 0 in order to distinguish "no limit" from "error". */
204         errno = 0;
205
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? */
210                 if (errno != 0)
211                         return (1);
212
213                 /* If not, there is no limit. */
214                 totalmem = (uint64_t)(-1);
215         } else {
216                 /* Compute the limit. */
217                 totalmem = (uint64_t)(pagesize) * (uint64_t)(physpages);
218         }
219
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;
224         else
225                 *memlimit = totalmem;
226 #else
227         *memlimit = totalmem;
228 #endif
229
230         /* Success! */
231         return (0);
232 }
233 #endif
234
235 int
236 memtouse(size_t maxmem, double maxmemfrac, size_t * memlimit)
237 {
238         size_t sysctl_memlimit, sysinfo_memlimit, rlimit_memlimit;
239         size_t sysconf_memlimit;
240         size_t memlimit_min;
241         size_t memavail;
242
243         /* Get memory limits. */
244 #ifdef HAVE_SYSCTL_HW_USERMEM
245         if (memlimit_sysctl_hw_usermem(&sysctl_memlimit))
246                 return (1);
247 #else
248         sysctl_memlimit = (size_t)(-1);
249 #endif
250 #ifdef HAVE_SYSINFO
251         if (memlimit_sysinfo(&sysinfo_memlimit))
252                 return (1);
253 #else
254         sysinfo_memlimit = (size_t)(-1);
255 #endif
256         if (memlimit_rlimit(&rlimit_memlimit))
257                 return (1);
258 #ifdef _SC_PHYS_PAGES
259         if (memlimit_sysconf(&sysconf_memlimit))
260                 return (1);
261 #else
262         sysconf_memlimit = (size_t)(-1);
263 #endif
264
265 #ifdef DEBUG
266         fprintf(stderr, "Memory limits are %zu %zu %zu %zu\n",
267             sysctl_memlimit, sysinfo_memlimit, rlimit_memlimit,
268             sysconf_memlimit);
269 #endif
270
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;
281
282         /* Only use the specified fraction of the available memory. */
283         if ((maxmemfrac > 0.5) || (maxmemfrac == 0.0))
284                 maxmemfrac = 0.5;
285         memavail = maxmemfrac * memlimit_min;
286
287         /* Don't use more than the specified maximum. */
288         if ((maxmem > 0) && (memavail > maxmem))
289                 memavail = maxmem;
290
291         /* But always allow at least 1 MiB. */
292         if (memavail < 1048576)
293                 memavail = 1048576;
294
295 #ifdef DEBUG
296         fprintf(stderr, "Allowing up to %zu memory to be used\n", memavail);
297 #endif
298
299         /* Return limit via the provided pointer. */
300         *memlimit = memavail;
301         return (0);
302 }