HugePages.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*
  2. * Copyright 2012-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <folly/experimental/io/HugePages.h>
  17. #include <fcntl.h>
  18. #include <sys/stat.h>
  19. #include <sys/types.h>
  20. #include <cctype>
  21. #include <cstring>
  22. #include <algorithm>
  23. #include <stdexcept>
  24. #include <system_error>
  25. #include <boost/regex.hpp>
  26. #include <folly/Conv.h>
  27. #include <folly/CppAttributes.h>
  28. #include <folly/Format.h>
  29. #include <folly/Range.h>
  30. #include <folly/String.h>
  31. #include <folly/gen/Base.h>
  32. #include <folly/gen/File.h>
  33. #include <folly/gen/String.h>
  34. namespace folly {
  35. namespace {
  36. // Get the default huge page size
  37. size_t getDefaultHugePageSize() {
  38. // We need to parse /proc/meminfo
  39. static const boost::regex regex(R"!(Hugepagesize:\s*(\d+)\s*kB)!");
  40. size_t pageSize = 0;
  41. boost::cmatch match;
  42. bool error = gen::byLine("/proc/meminfo") | [&](StringPiece line) -> bool {
  43. if (boost::regex_match(line.begin(), line.end(), match, regex)) {
  44. StringPiece numStr(
  45. line.begin() + match.position(1), size_t(match.length(1)));
  46. pageSize = to<size_t>(numStr) * 1024; // in KiB
  47. return false; // stop
  48. }
  49. return true;
  50. };
  51. if (error) {
  52. throw std::runtime_error("Can't find default huge page size");
  53. }
  54. return pageSize;
  55. }
  56. // Get raw huge page sizes (without mount points, they'll be filled later)
  57. HugePageSizeVec readRawHugePageSizes() {
  58. // We need to parse file names from /sys/kernel/mm/hugepages
  59. static const boost::regex regex(R"!(hugepages-(\d+)kB)!");
  60. boost::smatch match;
  61. HugePageSizeVec vec;
  62. fs::path path("/sys/kernel/mm/hugepages");
  63. for (fs::directory_iterator it(path); it != fs::directory_iterator(); ++it) {
  64. std::string filename(it->path().filename().string());
  65. if (boost::regex_match(filename, match, regex)) {
  66. StringPiece numStr(
  67. filename.data() + match.position(1), size_t(match.length(1)));
  68. vec.emplace_back(to<size_t>(numStr) * 1024);
  69. }
  70. }
  71. return vec;
  72. }
  73. // Parse the value of a pagesize mount option
  74. // Format: number, optional K/M/G/T suffix, trailing junk allowed
  75. size_t parsePageSizeValue(StringPiece value) {
  76. static const boost::regex regex(R"!((\d+)([kmgt])?.*)!", boost::regex::icase);
  77. boost::cmatch match;
  78. if (!boost::regex_match(value.begin(), value.end(), match, regex)) {
  79. throw std::runtime_error("Invalid pagesize option");
  80. }
  81. char c = '\0';
  82. if (match.length(2) != 0) {
  83. c = char(tolower(value[size_t(match.position(2))]));
  84. }
  85. StringPiece numStr(value.data() + match.position(1), size_t(match.length(1)));
  86. auto const size = to<size_t>(numStr);
  87. auto const mult = [c] {
  88. switch (c) {
  89. case 't':
  90. return 1ull << 40;
  91. case 'g':
  92. return 1ull << 30;
  93. case 'm':
  94. return 1ull << 20;
  95. case 'k':
  96. return 1ull << 10;
  97. default:
  98. return 1ull << 0;
  99. }
  100. }();
  101. return size * mult;
  102. }
  103. /**
  104. * Get list of supported huge page sizes and their mount points, if
  105. * hugetlbfs file systems are mounted for those sizes.
  106. */
  107. HugePageSizeVec readHugePageSizes() {
  108. HugePageSizeVec sizeVec = readRawHugePageSizes();
  109. if (sizeVec.empty()) {
  110. return sizeVec; // nothing to do
  111. }
  112. std::sort(sizeVec.begin(), sizeVec.end());
  113. size_t defaultHugePageSize = getDefaultHugePageSize();
  114. struct PageSizeLess {
  115. bool operator()(const HugePageSize& a, size_t b) const {
  116. return a.size < b;
  117. }
  118. bool operator()(size_t a, const HugePageSize& b) const {
  119. return a < b.size;
  120. }
  121. };
  122. // Read and parse /proc/mounts
  123. std::vector<StringPiece> parts;
  124. std::vector<StringPiece> options;
  125. gen::byLine("/proc/mounts") | gen::eachAs<StringPiece>() |
  126. [&](StringPiece line) {
  127. parts.clear();
  128. split(" ", line, parts);
  129. // device path fstype options uid gid
  130. if (parts.size() != 6) {
  131. throw std::runtime_error("Invalid /proc/mounts line");
  132. }
  133. if (parts[2] != "hugetlbfs") {
  134. return; // we only care about hugetlbfs
  135. }
  136. options.clear();
  137. split(",", parts[3], options);
  138. size_t pageSize = defaultHugePageSize;
  139. // Search for the "pagesize" option, which must have a value
  140. for (auto& option : options) {
  141. // key=value
  142. const char* p = static_cast<const char*>(
  143. memchr(option.data(), '=', option.size()));
  144. if (!p) {
  145. continue;
  146. }
  147. if (StringPiece(option.data(), p) != "pagesize") {
  148. continue;
  149. }
  150. pageSize = parsePageSizeValue(StringPiece(p + 1, option.end()));
  151. break;
  152. }
  153. auto pos = std::lower_bound(
  154. sizeVec.begin(), sizeVec.end(), pageSize, PageSizeLess());
  155. if (pos == sizeVec.end() || pos->size != pageSize) {
  156. throw std::runtime_error("Mount page size not found");
  157. }
  158. if (!pos->mountPoint.empty()) {
  159. // Only one mount point per page size is allowed
  160. return;
  161. }
  162. // Store mount point
  163. fs::path path(parts[1].begin(), parts[1].end());
  164. struct stat st;
  165. const int ret = stat(path.string().c_str(), &st);
  166. if (ret == -1 && errno == ENOENT) {
  167. return;
  168. }
  169. checkUnixError(ret, "stat hugepage mountpoint failed");
  170. pos->mountPoint = fs::canonical(path);
  171. pos->device = st.st_dev;
  172. };
  173. return sizeVec;
  174. }
  175. } // namespace
  176. const HugePageSizeVec& getHugePageSizes() {
  177. static HugePageSizeVec sizes = readHugePageSizes();
  178. return sizes;
  179. }
  180. const HugePageSize* getHugePageSize(size_t size) {
  181. // Linear search is just fine.
  182. for (auto& p : getHugePageSizes()) {
  183. if (p.mountPoint.empty()) {
  184. continue;
  185. }
  186. if (size == 0 || size == p.size) {
  187. return &p;
  188. }
  189. }
  190. return nullptr;
  191. }
  192. const HugePageSize* getHugePageSizeForDevice(dev_t device) {
  193. // Linear search is just fine.
  194. for (auto& p : getHugePageSizes()) {
  195. if (p.mountPoint.empty()) {
  196. continue;
  197. }
  198. if (device == p.device) {
  199. return &p;
  200. }
  201. }
  202. return nullptr;
  203. }
  204. } // namespace folly