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 |