1
// Copyright (c) 2007, Google Inc.
2
// All rights reserved.
4
// Redistribution and use in source and binary forms, with or without
5
// modification, are permitted provided that the following conditions are
8
// * Redistributions of source code must retain the above copyright
9
// notice, this list of conditions and the following disclaimer.
10
// * Redistributions in binary form must reproduce the above
11
// copyright notice, this list of conditions and the following disclaimer
12
// in the documentation and/or other materials provided with the
14
// * Neither the name of Google Inc. nor the names of its
15
// contributors may be used to endorse or promote products derived from
16
// this software without specific prior written permission.
18
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
// Author: Arun Sharma
33
// A tcmalloc system allocator that uses a memory based filesystem such as
36
// Since these only exist on linux, we only register this allocator there.
46
#include <sys/param.h>
47
#include <sys/types.h>
48
#include <sys/vfs.h> // for statfs
51
#include "base/basictypes.h"
52
#include "base/googleinit.h"
53
#include "base/logging.h"
54
#include "base/sysinfo.h"
55
#include "system-alloc.h"
59
DEFINE_string(memfs_malloc_path, EnvToString("TCMALLOC_MEMFS_MALLOC_PATH", ""),
60
"Path where hugetlbfs or tmpfs is mounted. The caller is "
61
"responsible for ensuring that the path is unique and does "
62
"not conflict with another process");
63
DEFINE_int64(memfs_malloc_limit_mb, 0, "Limit total allocation size to the "
64
"specified number of MiB. 0 == no limit.");
66
// Hugetlbfs based allocator for tcmalloc
67
class HugetlbSysAllocator: public SysAllocator {
69
HugetlbSysAllocator(int fd, int page_size)
70
: big_page_size_(page_size),
75
void* Alloc(size_t size, size_t *actual_size, size_t alignment);
77
void DumpStats(TCMalloc_Printer* printer);
81
int hugetlb_fd_; // file descriptor for hugetlb
85
void HugetlbSysAllocator::DumpStats(TCMalloc_Printer* printer) {
86
printer->printf("HugetlbSysAllocator: failed_=%d allocated=%"PRId64"\n",
87
failed_, static_cast<int64_t>(hugetlb_base_));
90
// No locking needed here since we assume that tcmalloc calls
91
// us with an internal lock held (see tcmalloc/system-alloc.cc).
92
void* HugetlbSysAllocator::Alloc(size_t size, size_t *actual_size,
95
// We don't respond to allocation requests smaller than big_page_size_ unless
96
// the caller is willing to take more than they asked for.
97
if (actual_size == NULL && size < big_page_size_) {
101
// Enforce huge page alignment. Be careful to deal with overflow.
102
if (alignment < big_page_size_) alignment = big_page_size_;
103
size_t aligned_size = ((size + alignment - 1) / alignment) * alignment;
104
if (aligned_size < size) {
109
// Ask for extra memory if alignment > pagesize
111
if (alignment > big_page_size_) {
112
extra = alignment - big_page_size_;
115
// Test if this allocation would put us over the limit.
116
off_t limit = FLAGS_memfs_malloc_limit_mb*1024*1024;
117
if (limit > 0 && hugetlb_base_ + size + extra > limit) {
118
// Disable the allocator when there's less than one page left.
119
if (limit - hugetlb_base_ < big_page_size_) {
125
// This is not needed for hugetlbfs, but needed for tmpfs. Annoyingly
126
// hugetlbfs returns EINVAL for ftruncate.
127
int ret = ftruncate(hugetlb_fd_, hugetlb_base_ + size + extra);
128
if (ret != 0 && errno != EINVAL) {
133
// Note: size + extra does not overflow since:
134
// size + alignment < (1<<NBITS).
135
// and extra <= alignment
136
// therefore size + extra < (1<<NBITS)
137
void *result = mmap(0, size + extra, PROT_WRITE|PROT_READ,
138
MAP_SHARED, hugetlb_fd_, hugetlb_base_);
139
if (result == reinterpret_cast<void*>(MAP_FAILED)) {
143
uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
145
// Adjust the return memory so it is aligned
147
if ((ptr & (alignment - 1)) != 0) {
148
adjust = alignment - (ptr & (alignment - 1));
151
hugetlb_base_ += (size + extra);
154
*actual_size = size + extra - adjust;
157
return reinterpret_cast<void*>(ptr);
160
static void InitSystemAllocator() {
161
if (FLAGS_memfs_malloc_path.length()) {
162
// Don't rely on the caller to ensure unique path name
163
char pid[64]; // pids are smaller than this!
164
int n = snprintf(pid, sizeof(pid), "%u", (unsigned int)(getpid()));
165
CHECK(n < sizeof(pid));
166
string hugetlbfs_path = FLAGS_memfs_malloc_path
169
int hugetlb_fd = open(hugetlbfs_path.c_str(), O_RDWR | O_CREAT | O_EXCL,
171
if (hugetlb_fd == -1) {
172
RAW_LOG(WARNING, "unable to create memfs_malloc_path file %s: %s",
173
hugetlbfs_path.c_str(), strerror(errno));
177
// Cleanup memory on process exit
178
CHECK_ERR(unlink(hugetlbfs_path.c_str()));
180
// Use fstatfs to figure out the default page size for memfs
182
CHECK_ERR(fstatfs(hugetlb_fd, &sfs));
183
int64 page_size = sfs.f_bsize;
185
SysAllocator *alloc = new HugetlbSysAllocator(hugetlb_fd, page_size);
186
// Register ourselves with tcmalloc
187
RegisterSystemAllocator(alloc, 0);
191
REGISTER_MODULE_INITIALIZER(memfs_malloc, { InitSystemAllocator(); });
193
#endif /* ifdef __linux */