Skip to content

Commit e98a620

Browse files
committed
Mark partially purged arena chunks as non-hugepage.
Add the pages_[no]huge() functions, which toggle huge page state via madvise(..., MADV_[NO]HUGEPAGE) calls. The first time a page run is purged from within an arena chunk, call pages_nohuge() to tell the kernel to make no further attempts to back the chunk with huge pages. Upon arena chunk deletion, restore the associated virtual memory to its original state via pages_huge(). This resolves jemalloc#243.
1 parent fc11f3c commit e98a620

File tree

9 files changed

+110
-5
lines changed

9 files changed

+110
-5
lines changed

Makefile.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ TESTS_UNIT := \
167167
$(srcroot)test/unit/mq.c \
168168
$(srcroot)test/unit/mtx.c \
169169
$(srcroot)test/unit/pack.c \
170+
$(srcroot)test/unit/pages.c \
170171
$(srcroot)test/unit/ph.c \
171172
$(srcroot)test/unit/prng.c \
172173
$(srcroot)test/unit/prof_accum.c \

configure.ac

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,6 +1612,8 @@ JE_COMPILABLE([madvise(2)], [
16121612
madvise((void *)0, 0, 0);
16131613
], [je_cv_madvise])
16141614
if test "x${je_cv_madvise}" = "xyes" ; then
1615+
AC_DEFINE([JEMALLOC_HAVE_MADVISE], [ ])
1616+
16151617
dnl Check for madvise(..., MADV_FREE).
16161618
JE_COMPILABLE([madvise(..., MADV_FREE)], [
16171619
#include <sys/mman.h>
@@ -1632,9 +1634,15 @@ if test "x${je_cv_madvise}" = "xyes" ; then
16321634
AC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED], [ ])
16331635
fi
16341636

1635-
if test "x${je_cv_madv_free}" = "xyes" \
1636-
-o "x${je_cv_madv_dontneed}" = "xyes" ; then
1637-
AC_DEFINE([JEMALLOC_HAVE_MADVISE], [ ])
1637+
dnl Check for madvise(..., MADV_[NO]HUGEPAGE).
1638+
JE_COMPILABLE([madvise(..., MADV_[[NO]]HUGEPAGE)], [
1639+
#include <sys/mman.h>
1640+
], [
1641+
madvise((void *)0, 0, MADV_HUGEPAGE);
1642+
madvise((void *)0, 0, MADV_NOHUGEPAGE);
1643+
], [je_cv_thp])
1644+
if test "x${je_cv_thp}" = "xyes" ; then
1645+
AC_DEFINE([JEMALLOC_THP], [ ])
16381646
fi
16391647
fi
16401648

include/jemalloc/internal/arena.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,14 @@ struct arena_chunk_s {
190190
*/
191191
extent_node_t node;
192192

193+
/*
194+
* True if memory could be backed by transparent huge pages. This is
195+
* only directly relevant to Linux, since it is the only supported
196+
* platform on which jemalloc interacts with explicit transparent huge
197+
* page controls.
198+
*/
199+
bool hugepage;
200+
193201
/*
194202
* Map of pages within chunk that keeps track of free/large/small. The
195203
* first map_bias entries are omitted, since the chunk header does not

include/jemalloc/internal/jemalloc_internal_defs.h.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,12 @@
265265
#undef JEMALLOC_PURGE_MADVISE_FREE
266266
#undef JEMALLOC_PURGE_MADVISE_DONTNEED
267267

268+
/*
269+
* Defined if transparent huge pages are supported via the MADV_[NO]HUGEPAGE
270+
* arguments to madvise(2).
271+
*/
272+
#undef JEMALLOC_THP
273+
268274
/* Define if operating system has alloca.h header. */
269275
#undef JEMALLOC_HAS_ALLOCA_H
270276

include/jemalloc/internal/pages.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ void *pages_trim(void *addr, size_t alloc_size, size_t leadsize,
1616
bool pages_commit(void *addr, size_t size);
1717
bool pages_decommit(void *addr, size_t size);
1818
bool pages_purge(void *addr, size_t size);
19+
bool pages_huge(void *addr, size_t size);
20+
bool pages_nohuge(void *addr, size_t size);
1921
void pages_boot(void);
2022

2123
#endif /* JEMALLOC_H_EXTERNS */

include/jemalloc/internal/private_symbols.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,9 @@ p2rz
397397
pages_boot
398398
pages_commit
399399
pages_decommit
400+
pages_huge
400401
pages_map
402+
pages_nohuge
401403
pages_purge
402404
pages_trim
403405
pages_unmap

src/arena.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,8 @@ arena_chunk_init_hard(tsdn_t *tsdn, arena_t *arena)
664664
if (chunk == NULL)
665665
return (NULL);
666666

667+
chunk->hugepage = true;
668+
667669
/*
668670
* Initialize the map to contain one maximal free untouched run. Mark
669671
* the pages as zeroed if arena_chunk_alloc_internal() returned a zeroed
@@ -727,13 +729,14 @@ arena_chunk_alloc(tsdn_t *tsdn, arena_t *arena)
727729
static void
728730
arena_chunk_discard(tsdn_t *tsdn, arena_t *arena, arena_chunk_t *chunk)
729731
{
730-
size_t sn;
732+
size_t sn, hugepage;
731733
bool committed;
732734
chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER;
733735

734736
chunk_deregister(chunk, &chunk->node);
735737

736738
sn = extent_node_sn_get(&chunk->node);
739+
hugepage = chunk->hugepage;
737740
committed = (arena_mapbits_decommitted_get(chunk, map_bias) == 0);
738741
if (!committed) {
739742
/*
@@ -746,6 +749,14 @@ arena_chunk_discard(tsdn_t *tsdn, arena_t *arena, arena_chunk_t *chunk)
746749
chunk_hooks.decommit(chunk, chunksize, 0, map_bias << LG_PAGE,
747750
arena->ind);
748751
}
752+
if (!hugepage) {
753+
/*
754+
* Convert chunk back to the default state, so that all
755+
* subsequent chunk allocations start out with chunks that can
756+
* be backed by transparent huge pages.
757+
*/
758+
pages_huge(chunk, chunksize);
759+
}
749760

750761
chunk_dalloc_cache(tsdn, arena, &chunk_hooks, (void *)chunk, chunksize,
751762
sn, committed);
@@ -1682,6 +1693,17 @@ arena_purge_stashed(tsdn_t *tsdn, arena_t *arena, chunk_hooks_t *chunk_hooks,
16821693
run_size = arena_mapbits_large_size_get(chunk, pageind);
16831694
npages = run_size >> LG_PAGE;
16841695

1696+
/*
1697+
* If this is the first run purged within chunk, mark
1698+
* the chunk as non-huge. This will prevent all use of
1699+
* transparent huge pages for this chunk until the chunk
1700+
* as a whole is deallocated.
1701+
*/
1702+
if (chunk->hugepage) {
1703+
pages_nohuge(chunk, chunksize);
1704+
chunk->hugepage = false;
1705+
}
1706+
16851707
assert(pageind + npages <= chunk_npages);
16861708
assert(!arena_mapbits_decommitted_get(chunk, pageind));
16871709
assert(!arena_mapbits_decommitted_get(chunk,

src/pages.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ pages_purge(void *addr, size_t size)
170170
#ifdef _WIN32
171171
VirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE);
172172
unzeroed = true;
173-
#elif defined(JEMALLOC_HAVE_MADVISE)
173+
#elif (defined(JEMALLOC_PURGE_MADVISE_FREE) || \
174+
defined(JEMALLOC_PURGE_MADVISE_FREE))
174175
# if defined(JEMALLOC_PURGE_MADVISE_FREE)
175176
# define JEMALLOC_MADV_PURGE MADV_FREE
176177
# define JEMALLOC_MADV_ZEROS false
@@ -191,6 +192,34 @@ pages_purge(void *addr, size_t size)
191192
return (unzeroed);
192193
}
193194

195+
bool
196+
pages_huge(void *addr, size_t size)
197+
{
198+
199+
assert(PAGE_ADDR2BASE(addr) == addr);
200+
assert(PAGE_CEILING(size) == size);
201+
202+
#ifdef JEMALLOC_THP
203+
return (madvise(addr, size, MADV_HUGEPAGE) != 0);
204+
#else
205+
return (false);
206+
#endif
207+
}
208+
209+
bool
210+
pages_nohuge(void *addr, size_t size)
211+
{
212+
213+
assert(PAGE_ADDR2BASE(addr) == addr);
214+
assert(PAGE_CEILING(size) == size);
215+
216+
#ifdef JEMALLOC_THP
217+
return (madvise(addr, size, MADV_NOHUGEPAGE) != 0);
218+
#else
219+
return (false);
220+
#endif
221+
}
222+
194223
#ifdef JEMALLOC_SYSCTL_VM_OVERCOMMIT
195224
static bool
196225
os_overcommits_sysctl(void)

test/unit/pages.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include "test/jemalloc_test.h"
2+
3+
TEST_BEGIN(test_pages_huge)
4+
{
5+
bool commit;
6+
void *pages;
7+
8+
commit = true;
9+
pages = pages_map(NULL, PAGE, &commit);
10+
assert_ptr_not_null(pages, "Unexpected pages_map() error");
11+
12+
assert_false(pages_huge(pages, PAGE),
13+
"Unexpected pages_huge() result");
14+
assert_false(pages_nohuge(pages, PAGE),
15+
"Unexpected pages_nohuge() result");
16+
17+
pages_unmap(pages, PAGE);
18+
}
19+
TEST_END
20+
21+
int
22+
main(void)
23+
{
24+
25+
return (test(
26+
test_pages_huge));
27+
}

0 commit comments

Comments
 (0)