OLD | NEW |
| (Empty) |
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 } | |
OLD | NEW |