Skip to content

Commit d903746

Browse files
committed
Add support for 16k pages
With 16384 pages: | size class | worst case internal fragmentation | slab slots | slab size | internal fragmentation for slabs | | - | - | - | - | - | | 16 | 93.75% | 256 | 16384 | 75.0% | | 32 | 46.88% | 256 | 16384 | 50.0% | | 48 | 31.25% | 256 | 16384 | 25.0% | | 64 | 23.44% | 256 | 16384 | 0.0% | | 80 | 18.75% | 204 | 16384 | 0.390625% | | 96 | 15.62% | 170 | 16384 | 0.390625% | | 112 | 13.39% | 146 | 16384 | 0.1953125% | | 128 | 11.72% | 256 | 32768 | 0.0% | | 160 | 19.38% | 204 | 32768 | 0.390625% | | 192 | 16.15% | 256 | 49152 | 0.0% | | 224 | 13.84% | 219 | 49152 | 0.1953125% | | 256 | 12.11% | 256 | 65536 | 0.0% | | 320 | 19.69% | 256 | 81920 | 0.0% | | 384 | 16.41% | 256 | 98304 | 0.0% | | 448 | 14.06% | 256 | 114688 | 0.0% | | 512 | 12.3% | 256 | 131072 | 0.0% | | 640 | 19.84% | 256 | 163840 | 0.0% | | 768 | 16.54% | 256 | 196608 | 0.0% | | 896 | 14.17% | 256 | 229376 | 0.0% | | 1024 | 12.4% | 256 | 262144 | 0.0% | | 1280 | 19.92% | 192 | 245760 | 0.0% | | 1536 | 16.6% | 160 | 245760 | 0.0% | | 1792 | 14.23% | 128 | 229376 | 0.0% | | 2048 | 12.45% | 128 | 262144 | 0.0% | | 2560 | 19.96% | 96 | 245760 | 0.0% | | 3072 | 16.63% | 80 | 245760 | 0.0% | | 3584 | 14.26% | 64 | 229376 | 0.0% | | 4096 | 12.48% | 64 | 262144 | 0.0% | | 5120 | 19.98% | 48 | 245760 | 0.0% | | 6144 | 16.65% | 40 | 245760 | 0.0% | | 7168 | 14.27% | 32 | 229376 | 0.0% | | 8192 | 12.49% | 32 | 262144 | 0.0% | | 10240 | 19.99% | 24 | 245760 | 0.0% | | 12288 | 16.66% | 20 | 245760 | 0.0% | | 14336 | 14.28% | 16 | 229376 | 0.0% | | 16384 | 12.49% | 16 | 262144 | 0.0% | | 20480 | 20.0% | 12 | 245760 | 0.0% | | 24576 | 16.66% | 10 | 245760 | 0.0% | | 28672 | 14.28% | 8 | 229376 | 0.0% | | 32768 | 12.5% | 8 | 262144 | 0.0% | | 40960 | 20.0% | 6 | 245760 | 0.0% | | 49152 | 16.66% | 5 | 245760 | 0.0% | | 57344 | 14.28% | 4 | 229376 | 0.0% | | 65536 | 12.5% | 4 | 262144 | 0.0% | | 81920 | 20.0% | 3 | 245760 | 0.0% | | 98304 | 16.67% | 2 | 196608 | 0.0% | | 114688 | 14.28% | 2 | 229376 | 0.0% | | 131072 | 12.5% | 2 | 262144 | 0.0% | maximum bitmap size is 256-bit maximum page span size is 16 (262144) with 4k to compare: | size class | worst case internal fragmentation | slab slots | slab size | internal fragmentation for slabs | | - | - | - | - | - | | 16 | 93.75% | 256 | 4096 | 0.0% | | 32 | 46.88% | 128 | 4096 | 0.0% | | 48 | 31.25% | 85 | 4096 | 0.390625% | | 64 | 23.44% | 64 | 4096 | 0.0% | | 80 | 18.75% | 51 | 4096 | 0.390625% | | 96 | 15.62% | 42 | 4096 | 1.5625% | | 112 | 13.39% | 36 | 4096 | 1.5625% | | 128 | 11.72% | 64 | 8192 | 0.0% | | 160 | 19.38% | 51 | 8192 | 0.390625% | | 192 | 16.15% | 64 | 12288 | 0.0% | | 224 | 13.84% | 54 | 12288 | 1.5625% | | 256 | 12.11% | 64 | 16384 | 0.0% | | 320 | 19.69% | 64 | 20480 | 0.0% | | 384 | 16.41% | 64 | 24576 | 0.0% | | 448 | 14.06% | 64 | 28672 | 0.0% | | 512 | 12.3% | 64 | 32768 | 0.0% | | 640 | 19.84% | 64 | 40960 | 0.0% | | 768 | 16.54% | 64 | 49152 | 0.0% | | 896 | 14.17% | 64 | 57344 | 0.0% | | 1024 | 12.4% | 64 | 65536 | 0.0% | | 1280 | 19.92% | 16 | 20480 | 0.0% | | 1536 | 16.6% | 16 | 24576 | 0.0% | | 1792 | 14.23% | 16 | 28672 | 0.0% | | 2048 | 12.45% | 16 | 32768 | 0.0% | | 2560 | 19.96% | 8 | 20480 | 0.0% | | 3072 | 16.63% | 8 | 24576 | 0.0% | | 3584 | 14.26% | 8 | 28672 | 0.0% | | 4096 | 12.48% | 8 | 32768 | 0.0% | | 5120 | 19.98% | 8 | 40960 | 0.0% | | 6144 | 16.65% | 8 | 49152 | 0.0% | | 7168 | 14.27% | 8 | 57344 | 0.0% | | 8192 | 12.49% | 8 | 65536 | 0.0% | | 10240 | 19.99% | 6 | 61440 | 0.0% | | 12288 | 16.66% | 5 | 61440 | 0.0% | | 14336 | 14.28% | 4 | 57344 | 0.0% | | 16384 | 12.49% | 4 | 65536 | 0.0% | | 20480 | 20.0% | 2 | 40960 | 0.0% | | 24576 | 16.66% | 2 | 49152 | 0.0% | | 28672 | 14.28% | 2 | 57344 | 0.0% | | 32768 | 12.5% | 2 | 65536 | 0.0% | | 40960 | 20.0% | 1 | 40960 | 0.0% | | 49152 | 16.66% | 1 | 49152 | 0.0% | | 57344 | 14.28% | 1 | 57344 | 0.0% | | 65536 | 12.5% | 1 | 65536 | 0.0% | | 81920 | 20.0% | 1 | 81920 | 0.0% | | 98304 | 16.67% | 1 | 98304 | 0.0% | | 114688 | 14.28% | 1 | 114688 | 0.0% | | 131072 | 12.5% | 1 | 131072 | 0.0% | maximum bitmap size is 256-bit maximum page span size is 16 (65536)
1 parent eea522b commit d903746

8 files changed

Lines changed: 111 additions & 28 deletions

File tree

Android.bp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ common_cflags = [
2828
"-DN_ARENA=1",
2929
"-DCONFIG_STATS=true",
3030
"-DCONFIG_SELF_INIT=false",
31+
"-DCONFIG_PAGE_SIZE=4096",
3132
]
3233

3334
cc_defaults {

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ endif
5353

5454
OBJECTS := $(addprefix $(OUT)/,$(OBJECTS))
5555

56+
ifeq (,$(filter $(CONFIG_PAGE_SIZE),4096 16384))
57+
$(error CONFIG_PAGE_SIZE must be 4096 or 16384)
58+
endif
59+
5660
ifeq (,$(filter $(CONFIG_SEAL_METADATA),true false))
5761
$(error CONFIG_SEAL_METADATA must be true or false)
5862
endif
@@ -108,7 +112,8 @@ CPPFLAGS += \
108112
-DCONFIG_CLASS_REGION_SIZE=$(CONFIG_CLASS_REGION_SIZE) \
109113
-DN_ARENA=$(CONFIG_N_ARENA) \
110114
-DCONFIG_STATS=$(CONFIG_STATS) \
111-
-DCONFIG_SELF_INIT=$(CONFIG_SELF_INIT)
115+
-DCONFIG_SELF_INIT=$(CONFIG_SELF_INIT) \
116+
-DCONFIG_PAGE_SIZE=$(CONFIG_PAGE_SIZE)
112117

113118
$(OUT)/libhardened_malloc$(SUFFIX).so: $(OBJECTS) | $(OUT)
114119
$(CC) $(CFLAGS) $(LDFLAGS) -shared $^ $(LDLIBS) -o $@

README.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,9 @@ large number of guard pages created by hardened\_malloc. As an example, in
180180
This is unnecessary if you set `CONFIG_GUARD_SLABS_INTERVAL` to a very large
181181
value in the build configuration.
182182

183-
On arm64, make sure your kernel is configured to use 4k pages since we haven't
184-
yet added support for 16k and 64k pages. The kernel also has to be configured
185-
to use 4 level page tables for the full 48 bit address space instead of only
186-
having a 39 bit address space for the default hardened\_malloc configuration.
183+
On arm64, the kernel also has to be configured to use 4 level page tables for
184+
the full 48 bit address space instead of only having a 39 bit address space
185+
for the default hardened\_malloc configuration.
187186
It's possible to reduce the class region size substantially to make a 39 bit
188187
address space workable but the defaults won't work.
189188

@@ -334,6 +333,14 @@ The following integer configuration options are available:
334333
granularity. See the [section on size classes](#size-classes) below for
335334
details.
336335

336+
* `CONFIG_PAGE_SIZE`: `4096` (default) to set the page size used by the
337+
allocator. Supported values are `4096` and `16384`. This must match the page
338+
size of the kernel the library will run on. On arm64, kernels may be
339+
configured for 4k or 16k pages. The allocator verifies at runtime that the
340+
compile-time page size matches the kernel page size and will abort if they
341+
differ. The slab slot counts are tuned per page size to minimize internal
342+
fragmentation for slabs.
343+
337344
There will be more control over enabled features in the future along with
338345
control over fairly arbitrarily chosen values like the size of empty slab
339346
caches (making them smaller improves security and reduces memory usage while
@@ -537,11 +544,11 @@ classes for each doubling in size.
537544

538545
The slot counts tied to the size classes are specific to this allocator rather
539546
than being taken from jemalloc. Slabs are always a span of pages so the slot
540-
count needs to be tuned to minimize waste due to rounding to the page size. For
541-
now, this allocator is set up only for 4096 byte pages as a small page size is
542-
desirable for finer-grained memory protection and randomization. It could be
543-
ported to larger page sizes in the future. The current slot counts are only a
544-
preliminary set of values.
547+
count needs to be tuned to minimize waste due to rounding to the page size.
548+
Tuned slot counts are provided for 4096 and 16384 byte page sizes, selectable
549+
via `CONFIG_PAGE_SIZE`. A smaller page size is desirable for
550+
finer-grained memory protection and randomization. The tables below show the
551+
default slot counts for 4096 byte pages.
545552

546553
| size class | worst case internal fragmentation | slab slots | slab size | internal fragmentation for slabs |
547554
| - | - | - | - | - |
@@ -584,7 +591,7 @@ preliminary set of values.
584591

585592
The slab allocation size classes end at 16384 since that's the final size for
586593
2048 byte spacing and the next spacing class matches the page size of 4096
587-
bytes on the target platforms. This is the minimum set of small size classes
594+
bytes when using 4k pages. This is the minimum set of small size classes
588595
required to avoid substantial waste from rounding.
589596

590597
The `CONFIG_EXTENDED_SIZE_CLASSES` option extends the size classes up to
@@ -620,8 +627,8 @@ the same size class scheme providing 4 size classes for every doubling of size.
620627
It increases virtual memory consumption but drastically improves performance
621628
where realloc is used without proper growth factors, which is fairly common and
622629
destroys performance in some commonly used programs. If large size classes are
623-
disabled, the granularity is instead the page size, which is currently always
624-
4096 bytes on supported platforms.
630+
disabled, the granularity is instead the page size (4096 or 16384 bytes
631+
depending on `CONFIG_PAGE_SIZE`).
625632

626633
## Scalability
627634

calculate-waste

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
#!/usr/bin/env python3
22

3-
from sys import argv
3+
import argparse
4+
5+
parser = argparse.ArgumentParser()
6+
parser.add_argument("--pagesize", default=4096, type=int)
7+
args = parser.parse_args()
8+
page_size = args.pagesize
49

510
size_classes = [
611
16, 32, 48, 64, 80, 96, 112, 128,
@@ -16,7 +21,7 @@ size_classes = [
1621
81920, 98304, 114688, 131072,
1722
]
1823

19-
size_class_slots = [
24+
size_class_slots_4k = [
2025
256, 128, 85, 64, 51, 42, 36, 64,
2126
51, 64, 54, 64,
2227
64, 64, 64, 64,
@@ -30,46 +35,84 @@ size_class_slots = [
3035
1, 1, 1, 1,
3136
]
3237

38+
size_class_slots_16k = [
39+
256, 256, 256, 256,
40+
204, 170, 146, 256,
41+
204, 256, 219, 256,
42+
256, 256, 256, 256,
43+
256, 256, 256, 256,
44+
192, 160, 128, 128,
45+
96, 80, 64, 64,
46+
48, 40, 32, 32,
47+
24, 20, 16, 16,
48+
12, 10, 8, 8,
49+
6, 5, 4, 4,
50+
3, 2, 2, 2,
51+
]
52+
53+
if page_size == 16384:
54+
size_class_slots = size_class_slots_16k
55+
else:
56+
size_class_slots = size_class_slots_4k
57+
3358
fragmentation = [100 - 1 / 16 * 100]
3459

3560
for i in range(len(size_classes) - 1):
3661
size_class = size_classes[i + 1]
3762
worst_case = size_classes[i] + 1
3863
used = worst_case / size_class
39-
fragmentation.append(100 - used * 100);
64+
fragmentation.append(100 - used * 100)
65+
4066

4167
def page_align(size):
42-
return (size + 4095) & ~4095
68+
mask = page_size - 1
69+
return (size + mask) & ~mask
70+
4371

72+
print(f"Page size: {page_size}")
73+
print()
74+
print("| ", end="")
75+
print(
76+
"size class",
77+
"worst case internal fragmentation",
78+
"slab slots",
79+
"slab size",
80+
"internal fragmentation for slabs",
81+
sep=" | ",
82+
end=" |\n",
83+
)
4484
print("| ", end="")
45-
print("size class", "worst case internal fragmentation", "slab slots", "slab size", "internal fragmentation for slabs", sep=" | ", end=" |\n")
46-
print("| ", end='')
4785
print("-", "-", "-", "-", "-", sep=" | ", end=" |\n")
4886
for size, slots, fragmentation in zip(size_classes, size_class_slots, fragmentation):
4987
used = size * slots
5088
real = page_align(used)
51-
print("| ", end='')
52-
print(size, f"{fragmentation:.4}%", slots, real, str(100 - used / real * 100) + "%", sep=" | ", end=" |\n")
53-
54-
if len(argv) < 2:
55-
exit()
89+
print("| ", end="")
90+
print(
91+
size,
92+
f"{fragmentation:.4}%",
93+
slots,
94+
real,
95+
str(100 - used / real * 100) + "%",
96+
sep=" | ",
97+
end=" |\n",
98+
)
5699

57100
max_bits = 256
58101
max_page_span = 16
59102

60103
print()
61104

62105
print("maximum bitmap size is {}-bit".format(max_bits))
63-
print("maximum page span size is {} ({})".format(max_page_span, max_page_span * 4096))
106+
print( "maximum page span size is {} ({})".format(max_page_span, max_page_span * page_size))
64107

65108
for size_class in size_classes:
66109
choices = []
67110
for bits in range(1, max_bits + 1):
68111
used = size_class * bits
69112
real = page_align(used)
70-
if real > 65536:
113+
if real > max_page_span * page_size:
71114
continue
72-
pages = real / 4096
115+
pages = real / page_size
73116
efficiency = used / real * 100
74117
choices.append((bits, used, real, pages, efficiency))
75118

config/default.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ CONFIG_CLASS_REGION_SIZE := 34359738368 # 32GiB
2121
CONFIG_N_ARENA := 4
2222
CONFIG_STATS := false
2323
CONFIG_SELF_INIT := true
24+
CONFIG_PAGE_SIZE := 4096

config/light.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ CONFIG_CLASS_REGION_SIZE := 34359738368 # 32GiB
2121
CONFIG_N_ARENA := 4
2222
CONFIG_STATS := false
2323
CONFIG_SELF_INIT := true
24+
CONFIG_PAGE_SIZE := 4096

h_malloc.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,22 @@ static const u32 size_classes[] = {
178178
};
179179

180180
static const u16 size_class_slots[] = {
181+
#if CONFIG_PAGE_SIZE == 16384
182+
/* 0 */ 256,
183+
/* 16 */ 256, 256, 256, 256, 204, 170, 146, 256,
184+
/* 32 */ 204, 256, 219, 256,
185+
/* 64 */ 256, 256, 256, 256,
186+
/* 128 */ 256, 256, 256, 256,
187+
/* 256 */ 192, 160, 128, 128,
188+
/* 512 */ 96, 80, 64, 64,
189+
/* 1024 */ 48, 40, 32, 32,
190+
/* 2048 */ 24, 20, 16, 16,
191+
#if CONFIG_EXTENDED_SIZE_CLASSES
192+
/* 4096 */ 12, 10, 8, 8,
193+
/* 8192 */ 6, 5, 4, 4,
194+
/* 16384 */ 3, 2, 2, 2,
195+
#endif
196+
#else /* 4k pages */
181197
/* 0 */ 256,
182198
/* 16 */ 256, 128, 85, 64, 51, 42, 36, 64,
183199
/* 32 */ 51, 64, 54, 64,
@@ -192,6 +208,7 @@ static const u16 size_class_slots[] = {
192208
/* 8192 */ 1, 1, 1, 1,
193209
/* 16384 */ 1, 1, 1, 1,
194210
#endif
211+
#endif
195212
};
196213

197214
static size_t get_slots(unsigned class) {
@@ -321,7 +338,8 @@ struct __attribute__((aligned(CACHELINE_SIZE))) size_class {
321338
#define REAL_CLASS_REGION_SIZE (CLASS_REGION_SIZE * 2)
322339
#define ARENA_SIZE (REAL_CLASS_REGION_SIZE * N_SIZE_CLASSES)
323340
static const size_t slab_region_size = ARENA_SIZE * N_ARENA;
324-
static_assert(PAGE_SIZE == 4096, "bitmap handling will need adjustment for other page sizes");
341+
static_assert(PAGE_SIZE == 4096 || PAGE_SIZE == 16384,
342+
"page size must be 4096 or 16384");
325343

326344
static void *get_slab(const struct size_class *c, size_t slab_size, const struct slab_metadata *metadata) {
327345
size_t index = metadata - c->slab_info;

pages.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@
77

88
#include "util.h"
99

10+
#ifndef PAGE_SHIFT
11+
#if CONFIG_PAGE_SIZE == 16384
12+
#define PAGE_SHIFT 14
13+
#else
1014
#define PAGE_SHIFT 12
15+
#endif
16+
#endif
17+
1118
#ifndef PAGE_SIZE
1219
#define PAGE_SIZE ((size_t)1 << PAGE_SHIFT)
1320
#endif

0 commit comments

Comments
 (0)