1*19c3b8c2SApple OSS Distributions /*
2*19c3b8c2SApple OSS Distributions * Copyright (c) 2020-2022 Apple Inc. All rights reserved.
3*19c3b8c2SApple OSS Distributions *
4*19c3b8c2SApple OSS Distributions * @APPLE_LICENSE_HEADER_START@
5*19c3b8c2SApple OSS Distributions *
6*19c3b8c2SApple OSS Distributions * This file contains Original Code and/or Modifications of Original Code
7*19c3b8c2SApple OSS Distributions * as defined in and that are subject to the Apple Public Source License
8*19c3b8c2SApple OSS Distributions * Version 2.0 (the 'License'). You may not use this file except in
9*19c3b8c2SApple OSS Distributions * compliance with the License. Please obtain a copy of the License at
10*19c3b8c2SApple OSS Distributions * http://www.opensource.apple.com/apsl/ and read it before using this
11*19c3b8c2SApple OSS Distributions * file.
12*19c3b8c2SApple OSS Distributions *
13*19c3b8c2SApple OSS Distributions * The Original Code and all software distributed under the License are
14*19c3b8c2SApple OSS Distributions * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15*19c3b8c2SApple OSS Distributions * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16*19c3b8c2SApple OSS Distributions * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17*19c3b8c2SApple OSS Distributions * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18*19c3b8c2SApple OSS Distributions * Please see the License for the specific language governing rights and
19*19c3b8c2SApple OSS Distributions * limitations under the License.
20*19c3b8c2SApple OSS Distributions *
21*19c3b8c2SApple OSS Distributions * @APPLE_LICENSE_HEADER_END@
22*19c3b8c2SApple OSS Distributions */
23*19c3b8c2SApple OSS Distributions
24*19c3b8c2SApple OSS Distributions #include <stdbool.h>
25*19c3b8c2SApple OSS Distributions #include <stdint.h>
26*19c3b8c2SApple OSS Distributions #include <kern/assert.h>
27*19c3b8c2SApple OSS Distributions #include <kern/locks.h>
28*19c3b8c2SApple OSS Distributions #include <os/atomic_private.h>
29*19c3b8c2SApple OSS Distributions
30*19c3b8c2SApple OSS Distributions #include "log_mem.h"
31*19c3b8c2SApple OSS Distributions
32*19c3b8c2SApple OSS Distributions #define BLOCK_INVALID ((size_t)-1)
33*19c3b8c2SApple OSS Distributions #define BLOCK_LEVEL_BASE(level) ((1 << (level)) - 1)
34*19c3b8c2SApple OSS Distributions #define BLOCK_SIZE(level) (1 << (level))
35*19c3b8c2SApple OSS Distributions #define BLOCK_PARENT(b) (((b) % 2 == 0) ? ((b) >> 1) - 1 : ((b) >> 1))
36*19c3b8c2SApple OSS Distributions #define BLOCK_LCHILD(b) (((b) << 1) + 1)
37*19c3b8c2SApple OSS Distributions #define BLOCK_BUDDY(b) (((b) & 0x1) ? (b) + 1 : (b) - 1)
38*19c3b8c2SApple OSS Distributions #define BLOCK_INDEX(lm, l, a, s) \
39*19c3b8c2SApple OSS Distributions (BLOCK_LEVEL_BASE(l) + ((uintptr_t)(a) - (uintptr_t)(lm)->lm_mem) / (s))
40*19c3b8c2SApple OSS Distributions
41*19c3b8c2SApple OSS Distributions #define MAP_SIZE(size_order, min_order) \
42*19c3b8c2SApple OSS Distributions MAX(1, (1 << ((size_order) - (min_order) + 1)) / 8)
43*19c3b8c2SApple OSS Distributions
44*19c3b8c2SApple OSS Distributions #define BITMAP_BUCKET_SIZE (8 * sizeof(((logmem_t *)0)->lm_mem_map[0]))
45*19c3b8c2SApple OSS Distributions #define BITMAP_BUCKET(i) ((i) / BITMAP_BUCKET_SIZE)
46*19c3b8c2SApple OSS Distributions #define BITMAP_BIT(i) (uint8_t)(1 << (BITMAP_BUCKET_SIZE - ((i) % BITMAP_BUCKET_SIZE) - 1))
47*19c3b8c2SApple OSS Distributions
48*19c3b8c2SApple OSS Distributions #define logmem_lock(lock, lm) if ((lock)) lck_spin_lock(&(lm)->lm_lock)
49*19c3b8c2SApple OSS Distributions #define logmem_unlock(lock, lm) if ((lock)) lck_spin_unlock(&(lm)->lm_lock)
50*19c3b8c2SApple OSS Distributions
51*19c3b8c2SApple OSS Distributions static LCK_GRP_DECLARE(logmem_lck_grp, "logmem_lck_grp");
52*19c3b8c2SApple OSS Distributions
53*19c3b8c2SApple OSS Distributions
54*19c3b8c2SApple OSS Distributions static bool
bitmap_get(logmem_t * lm,size_t block)55*19c3b8c2SApple OSS Distributions bitmap_get(logmem_t *lm, size_t block)
56*19c3b8c2SApple OSS Distributions {
57*19c3b8c2SApple OSS Distributions return lm->lm_mem_map[BITMAP_BUCKET(block)] & BITMAP_BIT(block);
58*19c3b8c2SApple OSS Distributions }
59*19c3b8c2SApple OSS Distributions
60*19c3b8c2SApple OSS Distributions static void
bitmap_set(logmem_t * lm,size_t block)61*19c3b8c2SApple OSS Distributions bitmap_set(logmem_t *lm, size_t block)
62*19c3b8c2SApple OSS Distributions {
63*19c3b8c2SApple OSS Distributions lm->lm_mem_map[BITMAP_BUCKET(block)] |= BITMAP_BIT(block);
64*19c3b8c2SApple OSS Distributions }
65*19c3b8c2SApple OSS Distributions
66*19c3b8c2SApple OSS Distributions static void
bitmap_clear(logmem_t * lm,size_t block)67*19c3b8c2SApple OSS Distributions bitmap_clear(logmem_t *lm, size_t block)
68*19c3b8c2SApple OSS Distributions {
69*19c3b8c2SApple OSS Distributions lm->lm_mem_map[BITMAP_BUCKET(block)] &= ~BITMAP_BIT(block);
70*19c3b8c2SApple OSS Distributions }
71*19c3b8c2SApple OSS Distributions
72*19c3b8c2SApple OSS Distributions static void
bitmap_reserve_root(logmem_t * lm,size_t block)73*19c3b8c2SApple OSS Distributions bitmap_reserve_root(logmem_t *lm, size_t block)
74*19c3b8c2SApple OSS Distributions {
75*19c3b8c2SApple OSS Distributions const size_t top_block = BLOCK_LEVEL_BASE(lm->lm_cap_order - lm->lm_max_order);
76*19c3b8c2SApple OSS Distributions
77*19c3b8c2SApple OSS Distributions for (ssize_t next = BLOCK_PARENT(block); next >= top_block; next = BLOCK_PARENT(next)) {
78*19c3b8c2SApple OSS Distributions /*
79*19c3b8c2SApple OSS Distributions * If the rest of the root path is already marked as
80*19c3b8c2SApple OSS Distributions * allocated we are done.
81*19c3b8c2SApple OSS Distributions */
82*19c3b8c2SApple OSS Distributions if (bitmap_get(lm, next)) {
83*19c3b8c2SApple OSS Distributions break;
84*19c3b8c2SApple OSS Distributions }
85*19c3b8c2SApple OSS Distributions bitmap_set(lm, next);
86*19c3b8c2SApple OSS Distributions }
87*19c3b8c2SApple OSS Distributions }
88*19c3b8c2SApple OSS Distributions
89*19c3b8c2SApple OSS Distributions static void
bitmap_release_root(logmem_t * lm,size_t block)90*19c3b8c2SApple OSS Distributions bitmap_release_root(logmem_t *lm, size_t block)
91*19c3b8c2SApple OSS Distributions {
92*19c3b8c2SApple OSS Distributions const size_t top_block = BLOCK_LEVEL_BASE(lm->lm_cap_order - lm->lm_max_order);
93*19c3b8c2SApple OSS Distributions int buddy_allocated = 0;
94*19c3b8c2SApple OSS Distributions
95*19c3b8c2SApple OSS Distributions while (block > top_block) {
96*19c3b8c2SApple OSS Distributions buddy_allocated = bitmap_get(lm, BLOCK_BUDDY(block));
97*19c3b8c2SApple OSS Distributions block = BLOCK_PARENT(block);
98*19c3b8c2SApple OSS Distributions /*
99*19c3b8c2SApple OSS Distributions * If there is another allocation within the parent subtree
100*19c3b8c2SApple OSS Distributions * in place we cannot mark the rest of the root path as free.
101*19c3b8c2SApple OSS Distributions */
102*19c3b8c2SApple OSS Distributions if (buddy_allocated) {
103*19c3b8c2SApple OSS Distributions break;
104*19c3b8c2SApple OSS Distributions }
105*19c3b8c2SApple OSS Distributions bitmap_clear(lm, block);
106*19c3b8c2SApple OSS Distributions }
107*19c3b8c2SApple OSS Distributions }
108*19c3b8c2SApple OSS Distributions
109*19c3b8c2SApple OSS Distributions static void
bitmap_update_subtree(logmem_t * lm,size_t level,size_t block,void (* fun)(logmem_t *,size_t))110*19c3b8c2SApple OSS Distributions bitmap_update_subtree(logmem_t *lm, size_t level, size_t block, void (*fun)(logmem_t *, size_t))
111*19c3b8c2SApple OSS Distributions {
112*19c3b8c2SApple OSS Distributions const size_t lcount = lm->lm_cap_order - lm->lm_min_order - level + 1;
113*19c3b8c2SApple OSS Distributions
114*19c3b8c2SApple OSS Distributions for (size_t l = 0, n = 1; l < lcount; l++, n <<= 1) {
115*19c3b8c2SApple OSS Distributions for (int i = 0; i < n; i++) {
116*19c3b8c2SApple OSS Distributions fun(lm, block + i);
117*19c3b8c2SApple OSS Distributions }
118*19c3b8c2SApple OSS Distributions block = BLOCK_LCHILD(block);
119*19c3b8c2SApple OSS Distributions }
120*19c3b8c2SApple OSS Distributions }
121*19c3b8c2SApple OSS Distributions
122*19c3b8c2SApple OSS Distributions static void
bitmap_release_subtree(logmem_t * lm,size_t level,size_t block)123*19c3b8c2SApple OSS Distributions bitmap_release_subtree(logmem_t *lm, size_t level, size_t block)
124*19c3b8c2SApple OSS Distributions {
125*19c3b8c2SApple OSS Distributions bitmap_update_subtree(lm, level, block, bitmap_clear);
126*19c3b8c2SApple OSS Distributions }
127*19c3b8c2SApple OSS Distributions
128*19c3b8c2SApple OSS Distributions static void
bitmap_reserve_subtree(logmem_t * lm,size_t level,size_t block)129*19c3b8c2SApple OSS Distributions bitmap_reserve_subtree(logmem_t *lm, size_t level, size_t block)
130*19c3b8c2SApple OSS Distributions {
131*19c3b8c2SApple OSS Distributions bitmap_update_subtree(lm, level, block, bitmap_set);
132*19c3b8c2SApple OSS Distributions }
133*19c3b8c2SApple OSS Distributions
134*19c3b8c2SApple OSS Distributions static size_t
block_size_level(logmem_t * lm,size_t amount)135*19c3b8c2SApple OSS Distributions block_size_level(logmem_t *lm, size_t amount)
136*19c3b8c2SApple OSS Distributions {
137*19c3b8c2SApple OSS Distributions for (size_t l = lm->lm_min_order; l <= lm->lm_max_order; l++) {
138*19c3b8c2SApple OSS Distributions if (amount <= BLOCK_SIZE(l)) {
139*19c3b8c2SApple OSS Distributions return lm->lm_cap_order - l;
140*19c3b8c2SApple OSS Distributions }
141*19c3b8c2SApple OSS Distributions }
142*19c3b8c2SApple OSS Distributions return BLOCK_INVALID;
143*19c3b8c2SApple OSS Distributions }
144*19c3b8c2SApple OSS Distributions
145*19c3b8c2SApple OSS Distributions static size_t
block_locate(logmem_t * lm,void * addr,size_t amount,size_t * block)146*19c3b8c2SApple OSS Distributions block_locate(logmem_t *lm, void *addr, size_t amount, size_t *block)
147*19c3b8c2SApple OSS Distributions {
148*19c3b8c2SApple OSS Distributions size_t level = block_size_level(lm, amount);
149*19c3b8c2SApple OSS Distributions if (level != BLOCK_INVALID) {
150*19c3b8c2SApple OSS Distributions *block = BLOCK_INDEX(lm, level, addr, amount);
151*19c3b8c2SApple OSS Distributions }
152*19c3b8c2SApple OSS Distributions return level;
153*19c3b8c2SApple OSS Distributions }
154*19c3b8c2SApple OSS Distributions
155*19c3b8c2SApple OSS Distributions static size_t
block_reserve(logmem_t * lm,size_t level)156*19c3b8c2SApple OSS Distributions block_reserve(logmem_t *lm, size_t level)
157*19c3b8c2SApple OSS Distributions {
158*19c3b8c2SApple OSS Distributions assert(level != BLOCK_INVALID);
159*19c3b8c2SApple OSS Distributions
160*19c3b8c2SApple OSS Distributions const size_t base = BLOCK_LEVEL_BASE(level);
161*19c3b8c2SApple OSS Distributions const size_t end = base + BLOCK_SIZE(level);
162*19c3b8c2SApple OSS Distributions
163*19c3b8c2SApple OSS Distributions for (size_t block = base; block < end; block++) {
164*19c3b8c2SApple OSS Distributions if (!bitmap_get(lm, block)) {
165*19c3b8c2SApple OSS Distributions bitmap_reserve_root(lm, block);
166*19c3b8c2SApple OSS Distributions bitmap_reserve_subtree(lm, level, block);
167*19c3b8c2SApple OSS Distributions return block - base;
168*19c3b8c2SApple OSS Distributions }
169*19c3b8c2SApple OSS Distributions }
170*19c3b8c2SApple OSS Distributions
171*19c3b8c2SApple OSS Distributions return BLOCK_INVALID;
172*19c3b8c2SApple OSS Distributions }
173*19c3b8c2SApple OSS Distributions
174*19c3b8c2SApple OSS Distributions static void *
logmem_alloc_impl(logmem_t * lm,size_t * amount,bool lock_lm)175*19c3b8c2SApple OSS Distributions logmem_alloc_impl(logmem_t *lm, size_t *amount, bool lock_lm)
176*19c3b8c2SApple OSS Distributions {
177*19c3b8c2SApple OSS Distributions assert(amount);
178*19c3b8c2SApple OSS Distributions
179*19c3b8c2SApple OSS Distributions os_atomic_inc(&lm->lm_cnt_allocations, relaxed);
180*19c3b8c2SApple OSS Distributions
181*19c3b8c2SApple OSS Distributions if (!lm->lm_mem) {
182*19c3b8c2SApple OSS Distributions os_atomic_inc(&lm->lm_cnt_failed_lmoff, relaxed);
183*19c3b8c2SApple OSS Distributions return NULL;
184*19c3b8c2SApple OSS Distributions }
185*19c3b8c2SApple OSS Distributions
186*19c3b8c2SApple OSS Distributions if (*amount == 0 || *amount > BLOCK_SIZE(lm->lm_max_order)) {
187*19c3b8c2SApple OSS Distributions os_atomic_inc(&lm->lm_cnt_failed_size, relaxed);
188*19c3b8c2SApple OSS Distributions return NULL;
189*19c3b8c2SApple OSS Distributions }
190*19c3b8c2SApple OSS Distributions
191*19c3b8c2SApple OSS Distributions size_t level = block_size_level(lm, *amount);
192*19c3b8c2SApple OSS Distributions logmem_lock(lock_lm, lm);
193*19c3b8c2SApple OSS Distributions size_t block = block_reserve(lm, level);
194*19c3b8c2SApple OSS Distributions logmem_unlock(lock_lm, lm);
195*19c3b8c2SApple OSS Distributions
196*19c3b8c2SApple OSS Distributions if (block == BLOCK_INVALID) {
197*19c3b8c2SApple OSS Distributions os_atomic_inc(&lm->lm_cnt_failed_full, relaxed);
198*19c3b8c2SApple OSS Distributions return NULL;
199*19c3b8c2SApple OSS Distributions }
200*19c3b8c2SApple OSS Distributions
201*19c3b8c2SApple OSS Distributions *amount = BLOCK_SIZE(lm->lm_cap_order - level);
202*19c3b8c2SApple OSS Distributions os_atomic_sub(&lm->lm_cnt_free, (uint32_t)*amount, relaxed);
203*19c3b8c2SApple OSS Distributions
204*19c3b8c2SApple OSS Distributions return &lm->lm_mem[block * *amount];
205*19c3b8c2SApple OSS Distributions }
206*19c3b8c2SApple OSS Distributions
207*19c3b8c2SApple OSS Distributions void *
logmem_alloc_locked(logmem_t * lm,size_t * amount)208*19c3b8c2SApple OSS Distributions logmem_alloc_locked(logmem_t *lm, size_t *amount)
209*19c3b8c2SApple OSS Distributions {
210*19c3b8c2SApple OSS Distributions return logmem_alloc_impl(lm, amount, true);
211*19c3b8c2SApple OSS Distributions }
212*19c3b8c2SApple OSS Distributions
213*19c3b8c2SApple OSS Distributions void *
logmem_alloc(logmem_t * lm,size_t * amount)214*19c3b8c2SApple OSS Distributions logmem_alloc(logmem_t *lm, size_t *amount)
215*19c3b8c2SApple OSS Distributions {
216*19c3b8c2SApple OSS Distributions return logmem_alloc_impl(lm, amount, false);
217*19c3b8c2SApple OSS Distributions }
218*19c3b8c2SApple OSS Distributions
219*19c3b8c2SApple OSS Distributions static void
logmem_free_impl(logmem_t * lm,void * addr,size_t amount,bool lock_lm)220*19c3b8c2SApple OSS Distributions logmem_free_impl(logmem_t *lm, void *addr, size_t amount, bool lock_lm)
221*19c3b8c2SApple OSS Distributions {
222*19c3b8c2SApple OSS Distributions assert(addr);
223*19c3b8c2SApple OSS Distributions assert(amount > 0 && ((amount & (amount - 1)) == 0));
224*19c3b8c2SApple OSS Distributions
225*19c3b8c2SApple OSS Distributions size_t block = BLOCK_INVALID;
226*19c3b8c2SApple OSS Distributions size_t level = block_locate(lm, addr, amount, &block);
227*19c3b8c2SApple OSS Distributions assert(level != BLOCK_INVALID);
228*19c3b8c2SApple OSS Distributions assert(block != BLOCK_INVALID);
229*19c3b8c2SApple OSS Distributions
230*19c3b8c2SApple OSS Distributions assert(lm->lm_mem);
231*19c3b8c2SApple OSS Distributions
232*19c3b8c2SApple OSS Distributions logmem_lock(lock_lm, lm);
233*19c3b8c2SApple OSS Distributions bitmap_release_root(lm, block);
234*19c3b8c2SApple OSS Distributions bitmap_release_subtree(lm, level, block);
235*19c3b8c2SApple OSS Distributions logmem_unlock(lock_lm, lm);
236*19c3b8c2SApple OSS Distributions
237*19c3b8c2SApple OSS Distributions os_atomic_add(&lm->lm_cnt_free, (uint32_t)amount, relaxed);
238*19c3b8c2SApple OSS Distributions }
239*19c3b8c2SApple OSS Distributions
240*19c3b8c2SApple OSS Distributions void
logmem_free_locked(logmem_t * lm,void * addr,size_t amount)241*19c3b8c2SApple OSS Distributions logmem_free_locked(logmem_t *lm, void *addr, size_t amount)
242*19c3b8c2SApple OSS Distributions {
243*19c3b8c2SApple OSS Distributions logmem_free_impl(lm, addr, amount, true);
244*19c3b8c2SApple OSS Distributions }
245*19c3b8c2SApple OSS Distributions
246*19c3b8c2SApple OSS Distributions void
logmem_free(logmem_t * lm,void * addr,size_t amount)247*19c3b8c2SApple OSS Distributions logmem_free(logmem_t *lm, void *addr, size_t amount)
248*19c3b8c2SApple OSS Distributions {
249*19c3b8c2SApple OSS Distributions logmem_free_impl(lm, addr, amount, false);
250*19c3b8c2SApple OSS Distributions }
251*19c3b8c2SApple OSS Distributions
252*19c3b8c2SApple OSS Distributions size_t
logmem_required_size(size_t size_order,size_t min_order)253*19c3b8c2SApple OSS Distributions logmem_required_size(size_t size_order, size_t min_order)
254*19c3b8c2SApple OSS Distributions {
255*19c3b8c2SApple OSS Distributions return round_page(BLOCK_SIZE(size_order)) + round_page(MAP_SIZE(size_order, min_order));
256*19c3b8c2SApple OSS Distributions }
257*19c3b8c2SApple OSS Distributions
258*19c3b8c2SApple OSS Distributions size_t
logmem_max_size(const logmem_t * lm)259*19c3b8c2SApple OSS Distributions logmem_max_size(const logmem_t *lm)
260*19c3b8c2SApple OSS Distributions {
261*19c3b8c2SApple OSS Distributions return BLOCK_SIZE(lm->lm_max_order);
262*19c3b8c2SApple OSS Distributions }
263*19c3b8c2SApple OSS Distributions
264*19c3b8c2SApple OSS Distributions bool
logmem_empty(const logmem_t * lm)265*19c3b8c2SApple OSS Distributions logmem_empty(const logmem_t *lm)
266*19c3b8c2SApple OSS Distributions {
267*19c3b8c2SApple OSS Distributions return lm->lm_cnt_free == BLOCK_SIZE(lm->lm_cap_order);
268*19c3b8c2SApple OSS Distributions }
269*19c3b8c2SApple OSS Distributions
270*19c3b8c2SApple OSS Distributions bool
logmem_ready(const logmem_t * lm)271*19c3b8c2SApple OSS Distributions logmem_ready(const logmem_t *lm)
272*19c3b8c2SApple OSS Distributions {
273*19c3b8c2SApple OSS Distributions return lm->lm_mem != NULL;
274*19c3b8c2SApple OSS Distributions }
275*19c3b8c2SApple OSS Distributions
276*19c3b8c2SApple OSS Distributions void
logmem_init(logmem_t * lm,void * lm_mem,size_t lm_mem_size,size_t size_order,size_t min_order,size_t max_order)277*19c3b8c2SApple OSS Distributions logmem_init(logmem_t *lm, void *lm_mem, size_t lm_mem_size, size_t size_order, size_t min_order, size_t max_order)
278*19c3b8c2SApple OSS Distributions {
279*19c3b8c2SApple OSS Distributions assert(lm_mem_size >= logmem_required_size(size_order, min_order));
280*19c3b8c2SApple OSS Distributions assert(size_order >= max_order);
281*19c3b8c2SApple OSS Distributions assert(max_order >= min_order);
282*19c3b8c2SApple OSS Distributions
283*19c3b8c2SApple OSS Distributions bzero(lm, sizeof(*lm));
284*19c3b8c2SApple OSS Distributions bzero(lm_mem, lm_mem_size);
285*19c3b8c2SApple OSS Distributions
286*19c3b8c2SApple OSS Distributions lck_spin_init(&lm->lm_lock, &logmem_lck_grp, LCK_ATTR_NULL);
287*19c3b8c2SApple OSS Distributions lm->lm_mem = lm_mem;
288*19c3b8c2SApple OSS Distributions lm->lm_mem_map = (uint8_t *)((uintptr_t)lm_mem + round_page(BLOCK_SIZE(size_order)));
289*19c3b8c2SApple OSS Distributions lm->lm_mem_size = lm_mem_size;
290*19c3b8c2SApple OSS Distributions lm->lm_cap_order = size_order;
291*19c3b8c2SApple OSS Distributions lm->lm_min_order = min_order;
292*19c3b8c2SApple OSS Distributions lm->lm_max_order = max_order;
293*19c3b8c2SApple OSS Distributions lm->lm_cnt_free = BLOCK_SIZE(size_order);
294*19c3b8c2SApple OSS Distributions }
295