xref: /freebsd/contrib/jemalloc/src/base.c (revision 273c26a3c3bea87a241d6879abd4f991db180bf0)
1 #define	JEMALLOC_BASE_C_
2 #include "jemalloc/internal/jemalloc_internal.h"
3 
4 /******************************************************************************/
5 /* Data. */
6 
7 static malloc_mutex_t	base_mtx;
8 static extent_tree_t	base_avail_szad;
9 static extent_node_t	*base_nodes;
10 static size_t		base_allocated;
11 static size_t		base_resident;
12 static size_t		base_mapped;
13 
14 /******************************************************************************/
15 
16 static extent_node_t *
17 base_node_try_alloc(tsdn_t *tsdn)
18 {
19 	extent_node_t *node;
20 
21 	malloc_mutex_assert_owner(tsdn, &base_mtx);
22 
23 	if (base_nodes == NULL)
24 		return (NULL);
25 	node = base_nodes;
26 	base_nodes = *(extent_node_t **)node;
27 	JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(node, sizeof(extent_node_t));
28 	return (node);
29 }
30 
31 static void
32 base_node_dalloc(tsdn_t *tsdn, extent_node_t *node)
33 {
34 
35 	malloc_mutex_assert_owner(tsdn, &base_mtx);
36 
37 	JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(node, sizeof(extent_node_t));
38 	*(extent_node_t **)node = base_nodes;
39 	base_nodes = node;
40 }
41 
42 static extent_node_t *
43 base_chunk_alloc(tsdn_t *tsdn, size_t minsize)
44 {
45 	extent_node_t *node;
46 	size_t csize, nsize;
47 	void *addr;
48 
49 	malloc_mutex_assert_owner(tsdn, &base_mtx);
50 	assert(minsize != 0);
51 	node = base_node_try_alloc(tsdn);
52 	/* Allocate enough space to also carve a node out if necessary. */
53 	nsize = (node == NULL) ? CACHELINE_CEILING(sizeof(extent_node_t)) : 0;
54 	csize = CHUNK_CEILING(minsize + nsize);
55 	addr = chunk_alloc_base(csize);
56 	if (addr == NULL) {
57 		if (node != NULL)
58 			base_node_dalloc(tsdn, node);
59 		return (NULL);
60 	}
61 	base_mapped += csize;
62 	if (node == NULL) {
63 		node = (extent_node_t *)addr;
64 		addr = (void *)((uintptr_t)addr + nsize);
65 		csize -= nsize;
66 		if (config_stats) {
67 			base_allocated += nsize;
68 			base_resident += PAGE_CEILING(nsize);
69 		}
70 	}
71 	extent_node_init(node, NULL, addr, csize, true, true);
72 	return (node);
73 }
74 
75 /*
76  * base_alloc() guarantees demand-zeroed memory, in order to make multi-page
77  * sparse data structures such as radix tree nodes efficient with respect to
78  * physical memory usage.
79  */
80 void *
81 base_alloc(tsdn_t *tsdn, size_t size)
82 {
83 	void *ret;
84 	size_t csize, usize;
85 	extent_node_t *node;
86 	extent_node_t key;
87 
88 	/*
89 	 * Round size up to nearest multiple of the cacheline size, so that
90 	 * there is no chance of false cache line sharing.
91 	 */
92 	csize = CACHELINE_CEILING(size);
93 
94 	usize = s2u(csize);
95 	extent_node_init(&key, NULL, NULL, usize, false, false);
96 	malloc_mutex_lock(tsdn, &base_mtx);
97 	node = extent_tree_szad_nsearch(&base_avail_szad, &key);
98 	if (node != NULL) {
99 		/* Use existing space. */
100 		extent_tree_szad_remove(&base_avail_szad, node);
101 	} else {
102 		/* Try to allocate more space. */
103 		node = base_chunk_alloc(tsdn, csize);
104 	}
105 	if (node == NULL) {
106 		ret = NULL;
107 		goto label_return;
108 	}
109 
110 	ret = extent_node_addr_get(node);
111 	if (extent_node_size_get(node) > csize) {
112 		extent_node_addr_set(node, (void *)((uintptr_t)ret + csize));
113 		extent_node_size_set(node, extent_node_size_get(node) - csize);
114 		extent_tree_szad_insert(&base_avail_szad, node);
115 	} else
116 		base_node_dalloc(tsdn, node);
117 	if (config_stats) {
118 		base_allocated += csize;
119 		/*
120 		 * Add one PAGE to base_resident for every page boundary that is
121 		 * crossed by the new allocation.
122 		 */
123 		base_resident += PAGE_CEILING((uintptr_t)ret + csize) -
124 		    PAGE_CEILING((uintptr_t)ret);
125 	}
126 	JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ret, csize);
127 label_return:
128 	malloc_mutex_unlock(tsdn, &base_mtx);
129 	return (ret);
130 }
131 
132 void
133 base_stats_get(tsdn_t *tsdn, size_t *allocated, size_t *resident,
134     size_t *mapped)
135 {
136 
137 	malloc_mutex_lock(tsdn, &base_mtx);
138 	assert(base_allocated <= base_resident);
139 	assert(base_resident <= base_mapped);
140 	*allocated = base_allocated;
141 	*resident = base_resident;
142 	*mapped = base_mapped;
143 	malloc_mutex_unlock(tsdn, &base_mtx);
144 }
145 
146 bool
147 base_boot(void)
148 {
149 
150 	if (malloc_mutex_init(&base_mtx, "base", WITNESS_RANK_BASE))
151 		return (true);
152 	extent_tree_szad_new(&base_avail_szad);
153 	base_nodes = NULL;
154 
155 	return (false);
156 }
157 
158 void
159 base_prefork(tsdn_t *tsdn)
160 {
161 
162 	malloc_mutex_prefork(tsdn, &base_mtx);
163 }
164 
165 void
166 base_postfork_parent(tsdn_t *tsdn)
167 {
168 
169 	malloc_mutex_postfork_parent(tsdn, &base_mtx);
170 }
171 
172 void
173 base_postfork_child(tsdn_t *tsdn)
174 {
175 
176 	malloc_mutex_postfork_child(tsdn, &base_mtx);
177 }
178