memlimit.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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. #include <sys/types.h>
  31. #include <sys/resource.h>
  32. #ifdef HAVE_SYSCTL_HW_USERMEM
  33. #include <sys/sysctl.h>
  34. #endif
  35. #ifdef HAVE_SYS_SYSINFO_H
  36. #include <sys/sysinfo.h>
  37. #endif
  38. #include <stddef.h>
  39. #include <stdint.h>
  40. #ifdef DEBUG
  41. #include <stdio.h>
  42. #endif
  43. #include "memlimit.h"
  44. #ifdef HAVE_SYSCTL_HW_USERMEM
  45. static int
  46. memlimit_sysctl_hw_usermem(size_t * memlimit)
  47. {
  48. uint8_t usermembuf[8];
  49. size_t usermemlen = 8;
  50. uint64_t usermem;
  51. /* Ask the kernel how much RAM we have. */
  52. if (sysctlbyname("hw.usermem", usermembuf, &usermemlen, NULL, 0))
  53. return (1);
  54. /*
  55. * Parse as either a uint64_t or a uint32_t based on the length of
  56. * output the kernel reports having copied out. It appears that all
  57. * systems providing a sysctl interface for reading integers copy
  58. * them out as system-endian values, so we don't need to worry about
  59. * parsing them.
  60. */
  61. if (usermemlen == sizeof(uint64_t))
  62. usermem = *(uint64_t *)usermembuf;
  63. else if (usermemlen == sizeof(uint32_t))
  64. usermem = *(uint32_t *)usermembuf;
  65. else
  66. return (1);
  67. /* Return the sysctl value, but clamp to SIZE_MAX if necessary. */
  68. #if UINT64_MAX > SIZE_MAX
  69. if (usermem > SIZE_MAX)
  70. *memlimit = SIZE_MAX;
  71. else
  72. *memlimit = usermem;
  73. #else
  74. *memlimit = usermem;
  75. #endif
  76. /* Success! */
  77. return (0);
  78. }
  79. #endif
  80. /* If we don't HAVE_STRUCT_SYSINFO, we can't use sysinfo. */
  81. #ifndef HAVE_STRUCT_SYSINFO
  82. #undef HAVE_SYSINFO
  83. #endif
  84. #ifdef HAVE_SYSINFO
  85. static int
  86. memlimit_sysinfo(size_t * memlimit)
  87. {
  88. struct sysinfo info;
  89. uint64_t totalmem;
  90. /* Get information from the kernel. */
  91. if (sysinfo(&info))
  92. return (1);
  93. totalmem = info.totalram;
  94. /* If we're on a modern kernel, adjust based on mem_unit. */
  95. #ifdef HAVE_STRUCT_SYSINFO_MEM_UNIT
  96. totalmem = totalmem * info.mem_unit;
  97. #endif
  98. /* Return the value, but clamp to SIZE_MAX if necessary. */
  99. #if UINT64_MAX > SIZE_MAX
  100. if (totalmem > SIZE_MAX)
  101. *memlimit = SIZE_MAX;
  102. else
  103. *memlimit = totalmem;
  104. #else
  105. *memlimit = totalmem;
  106. #endif
  107. /* Success! */
  108. return (0);
  109. }
  110. #endif /* HAVE_SYSINFO */
  111. static int
  112. memlimit_rlimit(size_t * memlimit)
  113. {
  114. struct rlimit rl;
  115. uint64_t memrlimit;
  116. /* Find the least of... */
  117. memrlimit = (uint64_t)(-1);
  118. /* ... RLIMIT_AS... */
  119. if (getrlimit(RLIMIT_AS, &rl))
  120. return (1);
  121. if ((rl.rlim_cur != RLIM_INFINITY) &&
  122. ((uint64_t)rl.rlim_cur < memrlimit))
  123. memrlimit = rl.rlim_cur;
  124. /* ... RLIMIT_DATA... */
  125. if (getrlimit(RLIMIT_DATA, &rl))
  126. return (1);
  127. if ((rl.rlim_cur != RLIM_INFINITY) &&
  128. ((uint64_t)rl.rlim_cur < memrlimit))
  129. memrlimit = rl.rlim_cur;
  130. /* ... and RLIMIT_RSS. */
  131. #ifdef RLIMIT_RSS
  132. if (getrlimit(RLIMIT_RSS, &rl))
  133. return (1);
  134. if ((rl.rlim_cur != RLIM_INFINITY) &&
  135. ((uint64_t)rl.rlim_cur < memrlimit))
  136. memrlimit = rl.rlim_cur;
  137. #endif
  138. /* Return the value, but clamp to SIZE_MAX if necessary. */
  139. #if UINT64_MAX > SIZE_MAX
  140. if (memrlimit > SIZE_MAX)
  141. *memlimit = SIZE_MAX;
  142. else
  143. *memlimit = memrlimit;
  144. #else
  145. *memlimit = memrlimit;
  146. #endif
  147. /* Success! */
  148. return (0);
  149. }
  150. int
  151. memtouse(size_t maxmem, double maxmemfrac, size_t * memlimit)
  152. {
  153. size_t sysctl_memlimit, sysinfo_memlimit, rlimit_memlimit;
  154. size_t memlimit_min;
  155. size_t memavail;
  156. /* Get memory limits. */
  157. #ifdef HAVE_SYSCTL_HW_USERMEM
  158. if (memlimit_sysctl_hw_usermem(&sysctl_memlimit))
  159. return (1);
  160. #else
  161. sysctl_memlimit = (size_t)(-1);
  162. #endif
  163. #ifdef HAVE_SYSINFO
  164. if (memlimit_sysinfo(&sysinfo_memlimit))
  165. return (1);
  166. #else
  167. sysinfo_memlimit = (size_t)(-1);
  168. #endif
  169. if (memlimit_rlimit(&rlimit_memlimit))
  170. return (1);
  171. #ifdef DEBUG
  172. fprintf(stderr, "Memory limits are %zu %zu %zu\n",
  173. sysctl_memlimit, sysinfo_memlimit, rlimit_memlimit);
  174. #endif
  175. /* Find the smallest of them. */
  176. memlimit_min = (size_t)(-1);
  177. if (memlimit_min > sysctl_memlimit)
  178. memlimit_min = sysctl_memlimit;
  179. if (memlimit_min > sysinfo_memlimit)
  180. memlimit_min = sysinfo_memlimit;
  181. if (memlimit_min > rlimit_memlimit)
  182. memlimit_min = rlimit_memlimit;
  183. /* Only use the specified fraction of the available memory. */
  184. if ((maxmemfrac > 0.5) || (maxmemfrac == 0.0))
  185. maxmemfrac = 0.5;
  186. memavail = maxmemfrac * memlimit_min;
  187. /* Don't use more than the specified maximum. */
  188. if ((maxmem > 0) && (memavail > maxmem))
  189. memavail = maxmem;
  190. /* But always allow at least 1 MiB. */
  191. if (memavail < 1048576)
  192. memavail = 1048576;
  193. #ifdef DEBUG
  194. fprintf(stderr, "Allowing up to %zu memory to be used\n", memavail);
  195. #endif
  196. /* Return limit via the provided pointer. */
  197. *memlimit = memavail;
  198. return (0);
  199. }