2 * Eddie Kohler, Yandong Mao, Robert Morris
3 * Copyright (c) 2012-2014 President and Fellows of Harvard College
4 * Copyright (c) 2012-2014 Massachusetts Institute of Technology
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, subject to the conditions
9 * listed in the Masstree LICENSE file. These conditions include: you must
10 * preserve this copyright notice, and you cannot mention the copyright
11 * holders in advertising related to the Software without their permission.
12 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
13 * notice is a summary of the Masstree LICENSE file; the license in that file
16 #include "kvthread.hh"
22 #if HAVE_SUPERPAGE && !NOSUPERPAGE
23 #include <sys/types.h>
27 threadinfo *threadinfo::allthreads;
28 pthread_key_t threadinfo::key;
30 int threadinfo::no_pool_value;
34 void memdebug::landmark(char* buf, size_t size) const {
35 if (this->magic != magic_value && this->magic != magic_free_value)
36 snprintf(buf, size, "???");
38 snprintf(buf, size, "%s:%d", this->file, this->line);
40 snprintf(buf, size, "%d", this->line);
42 snprintf(buf, size, "0");
46 memdebug::hard_free_checks(const memdebug *m, size_t size, int freetype,
47 int after_rcu, const char *op) {
49 m->landmark(buf, sizeof(buf));
50 if (m->magic == magic_free_value)
51 fprintf(stderr, "%s(%p): double free, was @%s\n",
53 else if (m->magic != magic_value)
54 fprintf(stderr, "%s(%p): freeing unallocated pointer (%x)\n",
56 assert(m->magic == magic_value);
57 if (freetype && m->freetype != freetype)
58 fprintf(stderr, "%s(%p): expected type %x, saw %x, "
59 "allocated %s\n", op, m + 1, freetype, m->freetype, buf);
60 if (!after_rcu && m->size != size)
61 fprintf(stderr, "%s(%p): expected size %lu, saw %lu, "
62 "allocated %s\n", op, m + 1,
63 (unsigned long) size, (unsigned long) m->size, buf);
64 if (m->after_rcu != after_rcu)
65 fprintf(stderr, "%s(%p): double free after rcu, allocated @%s\n",
68 assert(m->freetype == freetype);
70 assert(m->size == size);
71 assert(m->after_rcu == after_rcu);
75 memdebug::hard_assert_use(const void *ptr, memtag tag1, memtag tag2) {
76 const memdebug *m = reinterpret_cast<const memdebug *>(ptr) - 1;
77 char tagbuf[40], buf[256];
78 m->landmark(buf, sizeof(buf));
79 if (tag2 == (memtag) -1)
80 sprintf(buf, "%x", tag1);
82 sprintf(buf, "%x/%x", tag1, tag2);
83 if (m->magic == magic_free_value)
84 fprintf(stderr, "%p: use tag %s after free, allocated %s\n",
86 else if (m->magic != magic_value)
87 fprintf(stderr, "%p: pointer is unallocated, not tag %s\n",
89 assert(m->magic == magic_value);
90 if (tag1 != 0 && (m->freetype >> 8) != tag1 && (m->freetype >> 8) != tag2)
91 fprintf(stderr, "%p: expected tag %s, got tag %x, allocated %s\n",
92 m + 1, tagbuf, m->freetype >> 8, buf);
94 assert((m->freetype >> 8) == tag1 || (m->freetype >> 8) == tag2);
98 threadinfo *threadinfo::make(int purpose, int index) {
99 static int threads_initialized;
101 threadinfo *ti = (threadinfo *) malloc(8192);
102 memset(ti, 0, sizeof(*ti));
103 ti->next_ = allthreads;
104 ti->purpose_ = purpose;
108 void *limbo_space = ti->allocate(sizeof(limbo_group), memtag_limbo);
109 ti->mark(tc_limbo_slots, limbo_group::capacity);
110 ti->limbo_head_ = ti->limbo_tail_ = new(limbo_space) limbo_group;
112 if (!threads_initialized) {
113 #if ENABLE_ASSERTIONS
114 const char* s = getenv("_");
115 no_pool_value = s && strstr(s, "valgrind") != 0;
117 threads_initialized = 1;
123 void threadinfo::refill_rcu()
125 if (limbo_head_ == limbo_tail_ && !limbo_tail_->next_
126 && limbo_tail_->head_ == limbo_tail_->tail_)
127 limbo_tail_->head_ = limbo_tail_->tail_ = 0;
128 else if (!limbo_tail_->next_) {
129 void *limbo_space = allocate(sizeof(limbo_group), memtag_limbo);
130 mark(tc_limbo_slots, limbo_group::capacity);
131 limbo_tail_->next_ = new(limbo_space) limbo_group;
132 limbo_tail_ = limbo_tail_->next_;
134 limbo_tail_ = limbo_tail_->next_;
137 void threadinfo::hard_rcu_quiesce()
139 uint64_t min_epoch = gc_epoch_;
140 for (threadinfo *ti = allthreads; ti; ti = ti->next()) {
141 prefetch((const void *) ti->next());
142 uint64_t epoch = ti->gc_epoch_;
143 if (epoch && (int64_t) (epoch - min_epoch) < 0)
147 limbo_group *lg = limbo_head_;
148 limbo_element *lb = &lg->e_[lg->head_];
149 limbo_element *le = &lg->e_[lg->tail_];
151 if (lb != le && (int64_t) (lb->epoch_ - min_epoch) < 0) {
153 free_rcu(lb->ptr_, lb->freetype_);
158 if (lb == le && lg == limbo_tail_) {
159 lg->head_ = lg->tail_;
161 } else if (lb == le) {
162 assert(lg->tail_ == lg->capacity && lg->next_);
163 lg->head_ = lg->tail_ = 0;
165 lb = &lg->e_[lg->head_];
166 le = &lg->e_[lg->tail_];
167 } else if (lb->epoch_ < min_epoch) {
168 lg->head_ = lb - lg->e_;
173 if (lg != limbo_head_) {
174 // shift nodes in [limbo_head_, limbo_tail_) to be after
176 limbo_group *old_head = limbo_head_;
178 limbo_group **last = &limbo_tail_->next_;
180 last = &(*last)->next_;
183 last = &(*last)->next_;
188 limbo_epoch_ = (lb == le ? 0 : lb->epoch_);
191 void threadinfo::report_rcu(void *ptr) const
193 for (limbo_group *lg = limbo_head_; lg; lg = lg->next_) {
195 for (int i = 0; i < lg->capacity; ++i) {
200 if (lg->e_[i].ptr_ == ptr)
201 fprintf(stderr, "thread %d: rcu %p@%d: %s as %x @%" PRIu64 "\n",
202 index_, lg, i, status ? "waiting" : "freed",
203 lg->e_[i].freetype_, lg->e_[i].epoch_);
208 void threadinfo::report_rcu_all(void *ptr)
210 for (threadinfo *ti = allthreads; ti; ti = ti->next())
215 #if HAVE_SUPERPAGE && !NOSUPERPAGE
216 static size_t read_superpage_size() {
217 if (DIR* d = opendir("/sys/kernel/mm/hugepages")) {
218 size_t n = (size_t) -1;
219 while (struct dirent* de = readdir(d))
220 if (de->d_type == DT_DIR
221 && strncmp(de->d_name, "hugepages-", 10) == 0
222 && de->d_name[10] >= '0' && de->d_name[10] <= '9') {
223 size_t x = strtol(&de->d_name[10], 0, 10) << 10;
232 static size_t superpage_size = 0;
235 static void initialize_pool(void* pool, size_t sz, size_t unit) {
236 char* p = reinterpret_cast<char*>(pool);
237 void** nextptr = reinterpret_cast<void**>(p);
238 for (size_t off = unit; off + unit <= sz; off += unit) {
240 nextptr = reinterpret_cast<void**>(p + off);
245 void threadinfo::refill_pool(int nl) {
246 assert(!pool_[nl - 1]);
249 pool_[nl - 1] = malloc(nl * CACHE_LINE_SIZE);
251 *reinterpret_cast<void**>(pool_[nl - 1]) = 0;
256 size_t pool_size = 0;
259 #if HAVE_SUPERPAGE && !NOSUPERPAGE
261 superpage_size = read_superpage_size();
262 if (superpage_size != (size_t) -1) {
263 pool_size = superpage_size;
265 if ((r = posix_memalign(&pool, pool_size, pool_size)) != 0) {
266 fprintf(stderr, "posix_memalign superpage: %s\n", strerror(r));
268 superpage_size = (size_t) -1;
269 } else if (madvise(pool, pool_size, MADV_HUGEPAGE) != 0) {
270 perror("madvise superpage");
271 superpage_size = (size_t) -1;
274 pool = mmap(0, pool_size, PROT_READ | PROT_WRITE,
275 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
276 if (pool == MAP_FAILED) {
277 perror("mmap superpage");
279 superpage_size = (size_t) -1;
282 superpage_size = (size_t) -1;
289 if ((r = posix_memalign(&pool, CACHE_LINE_SIZE, pool_size)) != 0) {
290 fprintf(stderr, "posix_memalign: %s\n", strerror(r));
295 initialize_pool(pool, pool_size, nl * CACHE_LINE_SIZE);
296 pool_[nl - 1] = pool;
299 void threadinfo::run() {
300 threadid_ = pthread_self();
301 pthread_setspecific(key, this);
304 void* threadinfo::thread_trampoline(void* argument) {
305 threadinfo* ti = static_cast<threadinfo*>(argument);
307 return ti->thread_func_(ti);
310 int threadinfo::run(void* (*thread_func)(threadinfo*), void* thread_data) {
311 assert(!thread_func_ && !threadid_);
312 thread_func_ = thread_func;
313 thread_data_ = thread_data;
314 return pthread_create(&threadid_, 0, thread_trampoline, this);