feat: k_mem_slab fixed-size block allocator (#46a)#51
Open
swoisz wants to merge 2 commits into
Open
Conversation
Independent reimplementation of upstream Zephyr's k_mem_slab API over the Boreas substrate. The low-risk half of #46 (split from k_fifo). Design: the free-block count and blocking ride the owned, notification-backed k_sem (count = free blocks); a portMUX guards the intrusive free list and the usage counters. Reusing k_sem inherits its hardened wake protocol -- a give targets the highest-priority waiter without bumping the count, so a freed block is reserved for the woken allocator and a racing K_NO_WAIT alloc correctly gets -ENOMEM. This matches upstream's direct hand-off semantics with no new wait-queue code. - Free list: upstream's intrusive singly-linked scheme (next-pointer in each free block's first word, threaded back-to-front). block_size must be >= sizeof(void*) and word-aligned; alloc returns uninitialized memory (never zeroed). - API: k_mem_slab_init, k_mem_slab_alloc(timeout), k_mem_slab_free, num_used/num_free/max_used_get, K_MEM_SLAB_DEFINE[_STATIC]. - Return codes match upstream: 0 / -ENOMEM (K_NO_WAIT, none free) / -EAGAIN (timeout) / -EINVAL (bad params); k_sem_take's -EBUSY is mapped to -ENOMEM. - ISR-safe alloc(K_NO_WAIT) and free (IRAM/K_ISR_SAFE), like the other primitives. - K_MEM_SLAB_DEFINE rounds block_size and buffer alignment up to a pointer word (upstream WB_UP), so the same DEFINE compiles on 32- and 64-bit targets (the linux test host). The free list is threaded lazily on first use, since upstream's PRE_KERNEL SYS_INIT threading does not run on the linux target -- DEFINE'd slabs must therefore be first touched from thread context (documented). Divergence from upstream noted on the declarations; k_mem_slab_init keeps upstream's strict word-alignment -EINVAL contract (no rounding). Tests (7): init/accounting, -EINVAL on bad params, alloc distinctness/ in-bounds/no-overlap + exhaustion -ENOMEM + max_used high-water, blocking-alloc timeout -EAGAIN, K_MEM_SLAB_DEFINE lazy-init usable, blocking alloc woken by free (MT), N>blocks multi-waiter conservation (MT), and FromISR free wakes a blocked allocator (HW-gated). linux suite: 231/0 x3 (224 + 7; ISR test compiles out on linux). Refs #46 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s list Folded both review blockers (no correctness bugs were found by the adversarial trace -- all 7 interleavings verified safe; these are robustness/quality fixes): - The embedded `avail` sem is now compile-time-initialized via Z_SEM_INITIALIZER in the DEFINE macro (like K_TIMER_DEFINE's embedded sem), so k_sem_init no longer runs on the lazy first-use path. That path is now pure free-list threading (IRAM-safe pointer work), which makes the K_ISR_SAFE annotations honest and lets a DEFINE'd slab be first-touched from an ISR -- the "first use from thread context" caveat is dropped. - k_mem_slab_free now calls z_mem_slab_ensure_threaded too, so a DEFINE'd slab freed before any alloc (caller error) can't push onto an unthreaded list. - BUILD_ASSERT num_blocks >= 1 in the DEFINE macros. - ensure_threaded's under-lock re-check uses __atomic_load_n (TSAN cleanliness; it was already correct under the portMUX barrier). - Comment the intentional num_used transient-skew during the free-> woken-alloc hand-off. - Test: assert a freed-then-realloc'd block keeps its bytes (alloc returns uninitialized memory, never zeroed). linux suite: 231/0 x3. Refs #46 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports Zephyr's
k_mem_slabfixed-size block allocator — the low-risk half of #46 (split fromk_fifo/k_lifo, which carries the object-lifetime risk). Independent reimplementation over the Boreas substrate: the free-block count and blocking ride the owned, notification-backedk_sem, with aportMUXguarding the intrusive free list and usage counters.Design
*(char**)block), threaded back-to-front.block_sizemust be>= sizeof(void*)and word-aligned.allocreturns uninitialized memory (never zeroed), matching upstream.k_sem:alloc=k_sem_take(timeout)then pop under the lock;free= push thenk_sem_give. Reusingk_seminherits its hardened, targeted wake — a give with a waiter present wakes that waiter without bumping the count, so a freed block is reserved for the woken allocator and a racingK_NO_WAITalloc correctly gets-ENOMEM. This reproduces upstream's direct hand-off with no new wait-queue code.0/-ENOMEM(K_NO_WAIT, none free) /-EAGAIN(timeout) /-EINVAL(bad params).alloc(K_NO_WAIT)andfree(IRAM/K_ISR_SAFE, verified in the ELF).K_MEM_SLAB_DEFINEroundsblock_size/buffer-align up to a pointer word (upstreamWB_UP), so the same DEFINE compiles on 32- and 64-bit targets (the linux test host). The embedded sem is compile-time-initialized (likeK_TIMER_DEFINE's), so the only lazy first-use step is free-list threading — pure IRAM-safe pointer work, safe from any context including an ISR. (Upstream threads the list from a PRE_KERNELSYS_INIT, which doesn't run on the linux target.)API
k_mem_slab_init,k_mem_slab_alloc(timeout),k_mem_slab_free,k_mem_slab_num_used_get/num_free_get/max_used_get,K_MEM_SLAB_DEFINE[_STATIC].Review
Boreas-conformance + adversarial-trace fan-out. The adversarial trace found no correctness bugs — all 7 interleavings (count/list desync, the hand-off steal, lazy-init publication barriers, free-before-init, dual-core ISR-free) verified safe, including that free-before-init is a benign no-op (not a crash) because
sys_dlist_is_emptyon a zeroed list returns NULL rather than dereferencing. Two robustness blockers folded: the embedded sem is now compile-time-initialized (removing flash-residentk_sem_initfrom the lazy/ISR path) andfreealso threads the list defensively.Test plan
alloc/freeconfirmed IRAM-resident,initin flashTests: init/accounting,
-EINVALon bad params, alloc distinctness/in-bounds/no-overlap + exhaustion-ENOMEM+max_usedhigh-water, blocking-alloc timeout-EAGAIN,K_MEM_SLAB_DEFINElazy-init usable + not-zeroed-on-realloc, blocking alloc woken by free (MT), N>blocks multi-waiter conservation (MT), FromISR free wakes a blocked allocator (HW-gated).Refs #46 (the
k_fifo/k_lifohalf lands separately)🤖 Generated with Claude Code