--- /dev/null
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <stdexcept>
+#include <system_error>
+
+#include <gflags/gflags.h>
+
+#include "folly/experimental/io/HugePages.h"
+#include "folly/Format.h"
+#include "folly/Range.h"
+#include "folly/ScopeGuard.h"
+
+DEFINE_bool(cp, false, "Copy file");
+
+using namespace folly;
+
+namespace {
+
+void usage(const char* name) __attribute__((noreturn));
+
+void usage(const char* name) {
+ std::cerr << folly::format(
+ "Usage: {0}\n"
+ " list all huge page sizes and their mount points\n"
+ " {0} -cp <src_file> <dest_nameprefix>\n"
+ " copy src_file to a huge page file\n",
+ name);
+ exit(1);
+}
+
+void copy(const char* srcFile, const char* destPrefix) {
+ int srcfd = open(srcFile, O_RDONLY);
+ if (srcfd == -1) {
+ throw std::system_error(errno, std::system_category(), "open failed");
+ }
+ SCOPE_EXIT {
+ close(srcfd);
+ };
+ struct stat st;
+ if (fstat(srcfd, &st) == -1) {
+ throw std::system_error(errno, std::system_category(), "fstat failed");
+ }
+
+ void* start = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, srcfd, 0);
+ if (start == MAP_FAILED) {
+ throw std::system_error(errno, std::system_category(), "mmap failed");
+ }
+
+ SCOPE_EXIT {
+ munmap(start, st.st_size);
+ };
+
+ HugePages hp;
+ auto f = hp.create(ByteRange(static_cast<const unsigned char*>(start),
+ st.st_size),
+ destPrefix);
+ std::cout << f.path << "\n";
+}
+
+void list() {
+ HugePages hp;
+ for (auto& p : hp.sizes()) {
+ std::cout << p.first << " " << p.second << "\n";
+ }
+}
+
+} // namespace
+
+
+int main(int argc, char *argv[]) {
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_cp) {
+ if (argc != 3) usage(argv[0]);
+ copy(argv[1], argv[2]);
+ } else {
+ if (argc != 1) usage(argv[0]);
+ list();
+ }
+ return 0;
+}
+
--- /dev/null
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "folly/experimental/io/HugePages.h"
+
+#include <sys/mman.h>
+
+#include <cctype>
+#include <cstring>
+
+#include <algorithm>
+#include <stdexcept>
+#include <system_error>
+
+#include <boost/filesystem.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/regex.hpp>
+
+#include <glog/logging.h>
+
+#include "folly/Conv.h"
+#include "folly/Format.h"
+#include "folly/Range.h"
+#include "folly/ScopeGuard.h"
+#include "folly/String.h"
+#include "folly/experimental/io/Stream.h"
+
+namespace fs = ::boost::filesystem;
+
+namespace folly {
+
+namespace {
+
+// Get the default huge page size
+size_t getDefaultHugePageSize() {
+ // We need to parse /proc/meminfo
+ static const boost::regex regex(R"!(Hugepagesize:\s*(\d+)\s*kB)!");
+ boost::cmatch match;
+ for (auto& byteLine : byLine("/proc/meminfo")) {
+ StringPiece line(byteLine);
+ if (boost::regex_match(line.begin(), line.end(), match, regex)) {
+ StringPiece numStr(line.begin() + match.position(1), match.length(1));
+ return to<size_t>(numStr) * 1024; // in KiB
+ }
+ }
+ throw std::runtime_error("Can't find default huge page size");
+}
+
+// Get raw huge page sizes (without mount points, they'll be filled later)
+HugePageSizeVec getRawHugePageSizes() {
+ // We need to parse file names from /sys/kernel/mm/hugepages
+ static const boost::regex regex(R"!(hugepages-(\d+)kB)!");
+ boost::smatch match;
+ HugePageSizeVec vec;
+ fs::path path("/sys/kernel/mm/hugepages");
+ for (fs::directory_iterator it(path); it != fs::directory_iterator(); ++it) {
+ std::string filename(it->path().filename().native());
+ if (boost::regex_match(filename, match, regex)) {
+ StringPiece numStr(filename.data() + match.position(1), match.length(1));
+ vec.emplace_back(to<size_t>(numStr) * 1024, "");
+ }
+ }
+ return vec;
+}
+
+// Parse the value of a pagesize mount option
+// Format: number, optional K/M/G/T suffix, trailing junk allowed
+size_t parsePageSizeValue(StringPiece value) {
+ static const boost::regex regex(R"!((\d+)([kmgt])?.*)!", boost::regex::icase);
+ boost::cmatch match;
+ if (!boost::regex_match(value.begin(), value.end(), match, regex)) {
+ throw std::runtime_error("Invalid pagesize option");
+ }
+ char c = '\0';
+ if (match.length(2) != 0) {
+ c = tolower(value[match.position(2)]);
+ }
+ StringPiece numStr(value.data() + match.position(1), match.length(1));
+ size_t size = to<size_t>(numStr);
+ switch (c) {
+ case 't': size *= 1024;
+ case 'g': size *= 1024;
+ case 'm': size *= 1024;
+ case 'k': size *= 1024;
+ }
+ return size;
+}
+
+/**
+ * Get list of supported huge page sizes and their mount points, if
+ * hugetlbfs file systems are mounted for those sizes.
+ */
+HugePageSizeVec getHugePageSizes() {
+ HugePageSizeVec sizeVec = getRawHugePageSizes();
+ if (sizeVec.empty()) {
+ return sizeVec; // nothing to do
+ }
+ std::sort(sizeVec.begin(), sizeVec.end());
+
+ size_t defaultHugePageSize = getDefaultHugePageSize();
+
+ struct PageSizeLess {
+ bool operator()(const std::pair<size_t, std::string>& a, size_t b) const {
+ return a.first < b;
+ }
+ bool operator()(size_t a, const std::pair<size_t, std::string>& b) const {
+ return a < b.first;
+ }
+ };
+
+ // Read and parse /proc/mounts
+ std::vector<StringPiece> parts;
+ std::vector<StringPiece> options;
+ for (auto& byteLine : byLine("/proc/mounts")) {
+ StringPiece line(byteLine);
+ parts.clear();
+ split(" ", line, parts);
+ // device path fstype options uid gid
+ if (parts.size() != 6) {
+ throw std::runtime_error("Invalid /proc/mounts line");
+ }
+ if (parts[2] != "hugetlbfs") {
+ continue; // we only care about hugetlbfs
+ }
+
+ options.clear();
+ split(",", parts[3], options);
+ size_t pageSize = defaultHugePageSize;
+ // Search for the "pagesize" option, which must have a value
+ for (auto& option : options) {
+ // key=value
+ const char* p = static_cast<const char*>(
+ memchr(option.data(), '=', option.size()));
+ if (!p) {
+ continue;
+ }
+ if (StringPiece(option.data(), p) != "pagesize") {
+ continue;
+ }
+ pageSize = parsePageSizeValue(StringPiece(p + 1, option.end()));
+ break;
+ }
+
+ auto pos = std::lower_bound(sizeVec.begin(), sizeVec.end(), pageSize,
+ PageSizeLess());
+ if (pos == sizeVec.end() || pos->first != pageSize) {
+ throw std::runtime_error("Mount page size not found");
+ }
+ if (pos->second.empty()) {
+ // Store mount point
+ pos->second.assign(parts[1].data(), parts[1].size());
+ }
+ }
+
+ return sizeVec;
+}
+
+// RAII wrapper around an open file, closes on exit unless you call release()
+class ScopedFd : private boost::noncopyable {
+ public:
+ explicit ScopedFd(int fd) : fd_(fd) { }
+ int fd() const { return fd_; }
+
+ void release() {
+ fd_ = -1;
+ }
+
+ void close() {
+ if (fd_ == -1) {
+ return;
+ }
+ int r = ::close(fd_);
+ fd_ = -1;
+ if (r == -1) {
+ throw std::system_error(errno, std::system_category(), "close failed");
+ }
+ }
+
+ ~ScopedFd() {
+ try {
+ close();
+ } catch (...) {
+ PLOG(ERROR) << "close failed!";
+ }
+ }
+
+ private:
+ int fd_;
+};
+
+// RAII wrapper that deletes a file upon destruction unless you call release()
+class ScopedDeleter : private boost::noncopyable {
+ public:
+ explicit ScopedDeleter(std::string name) : name_(std::move(name)) { }
+ void release() {
+ name_.clear();
+ }
+
+ ~ScopedDeleter() {
+ if (name_.empty()) {
+ return;
+ }
+ int r = ::unlink(name_.c_str());
+ if (r == -1) {
+ PLOG(ERROR) << "unlink failed";
+ }
+ }
+ private:
+ std::string name_;
+};
+
+// RAII wrapper around a mmap mapping, munmaps upon destruction unless you
+// call release()
+class ScopedMmap : private boost::noncopyable {
+ public:
+ ScopedMmap(void* start, size_t size) : start_(start), size_(size) { }
+
+ void* start() const { return start_; }
+ size_t size() const { return size_; }
+
+ void release() {
+ start_ = MAP_FAILED;
+ }
+
+ void munmap() {
+ if (start_ == MAP_FAILED) {
+ return;
+ }
+ int r = ::munmap(start_, size_);
+ start_ = MAP_FAILED;
+ if (r == -1) {
+ throw std::system_error(errno, std::system_category(), "munmap failed");
+ }
+ }
+
+ ~ScopedMmap() {
+ try {
+ munmap();
+ } catch (...) {
+ PLOG(ERROR) << "munmap failed!";
+ }
+ }
+ private:
+ void* start_;
+ size_t size_;
+};
+
+} // namespace
+
+HugePages::HugePages() : sizes_(getHugePageSizes()) { }
+
+HugePages::File HugePages::create(ByteRange data,
+ StringPiece baseName,
+ size_t hugePageSize,
+ mode_t mode) const {
+ // Pick an appropriate size.
+ StringPiece mountPath;
+ if (hugePageSize == 0) {
+ for (auto& p : sizes_) {
+ if (p.second.empty()) {
+ continue; // not mounted
+ }
+ hugePageSize = p.first;
+ mountPath = StringPiece(p.second);
+ break;
+ }
+ if (hugePageSize == 0) {
+ throw std::runtime_error("No huge page filesystem mounted");
+ }
+ } else {
+ // Linear search is just fine
+ for (auto& p : sizes_) {
+ if (p.first == hugePageSize) {
+ if (p.second.empty()) {
+ throw std::runtime_error(
+ "No huge page filesystem mounted with requested page size");
+ }
+ mountPath = StringPiece(p.second);
+ }
+ }
+ if (mountPath.empty()) {
+ throw std::runtime_error("Requested huge page size not found");
+ }
+ }
+
+ // Round size up
+ File file;
+ file.size = data.size() / hugePageSize * hugePageSize;
+ if (file.size != data.size()) {
+ file.size += hugePageSize;
+ }
+
+ file.path = folly::format("{}/{}", mountPath, baseName).str();
+ ScopedFd fd(open(file.path.c_str(), O_RDWR | O_CREAT | O_TRUNC, mode));
+ if (fd.fd() == -1) {
+ throw std::system_error(errno, std::system_category(), "open failed");
+ }
+
+ ScopedDeleter deleter(file.path);
+
+ ScopedMmap map(mmap(nullptr, file.size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd.fd(), 0),
+ file.size);
+ if (map.start() == MAP_FAILED) {
+ throw std::system_error(errno, std::system_category(), "mmap failed");
+ }
+
+ memcpy(map.start(), data.data(), data.size());
+
+ map.munmap();
+ deleter.release();
+ fd.close();
+
+ return file;
+}
+
+} // namespace folly
+
--- /dev/null
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FOLLY_IO_HUGEPAGES_H_
+#define FOLLY_IO_HUGEPAGES_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <cstddef>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "folly/Range.h"
+
+namespace folly {
+
+/**
+ * Vector of (huge_page_size, mount_point), sorted by huge_page_size.
+ * mount_point might be empty if no hugetlbfs file system is mounted for
+ * that size.
+ */
+typedef std::vector<std::pair<size_t, std::string>> HugePageSizeVec;
+
+/**
+ * Class to interface with Linux huge pages (hugetlbfs).
+ */
+class HugePages {
+ public:
+ HugePages();
+
+ /**
+ * Get list of supported huge page sizes and their mount points, if
+ * hugetlbfs file systems are mounted for those sizes.
+ */
+ const HugePageSizeVec& sizes() const { return sizes_; }
+
+ /**
+ * Create a file on a huge page filesystem containing a copy of the data
+ * from data. If multiple huge page sizes are allowed, we
+ * pick the smallest huge page size available, unless you request one
+ * explicitly with the hugePageSize argument.
+ *
+ * We return a struct File structure containing the full path and size
+ * (rounded up to a multiple of the huge page size)
+ */
+ struct File {
+ std::string path;
+ size_t size;
+ };
+ File create(
+ ByteRange data, StringPiece baseName, size_t hugePageSize = 0,
+ mode_t mode = 0644) const;
+
+ private:
+ HugePageSizeVec sizes_;
+};
+
+} // namespace folly
+
+#endif /* FOLLY_IO_HUGEPAGES_H_ */
+