summaryrefslogtreecommitdiff
path: root/na/mm/slab.h
blob: 716ebe6e12a33a7ada85d778677f92f8e8bab5ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (C) 2026 Richard Qin */

#pragma once

/*
 * SLUB Allocator — module-private header
 *
 * SLUB = Simple List of Unfull Blocks.
 * Metadata lives in Frame descriptors (no per-page Slab header).
 * Free object list is embedded inside the objects themselves.
 * Per-CPU "active page" eliminates lock contention on the fast path.
 *
 * Reference: docs/specs/slab.md
 */

#include <lume/types.h>
#include <lume/config.h>
#include <lume/frame.h>
#include <lume/list.h>
#include <kernel/sync/spinlock.h>

/* Size classes: 8, 16, 32, 64, 128, 256, 512, 1024, 2048 */
inline constexpr int kNumSizeClasses = 9;
inline constexpr uint32 kSizeClasses[kNumSizeClasses] = {
    8, 16, 32, 64, 128, 256, 512, 1024, 2048
};

/* Per-CPU active slab page pointer */
struct PerCpuSlab {
    Frame* active;  // Currently used slab page (nullptr if none)
};

/*
 * KmemCache — manages a pool of fixed-size objects.
 *
 * Lock contract:
 *   lock_ protects partial_ list only.
 *   Per-CPU cpu_slab_ access requires interrupts disabled.
 *   PMM calls happen outside lock_ to avoid lock ordering issues.
 */
class KmemCache {
public:
    KmemCache() = default;

    // Explicit init (Placement New / BSS pattern)
    void init(const char* name, uint32 obj_size, uint32 obj_align);

    // Allocate one object. Returns nullptr on OOM.
    void* alloc();

    // Free one object back to its owning page.
    void free(void* obj);

    const char* name() const { return name_; }
    uint32 obj_size() const { return obj_size_; }

private:
    // Get a partially-filled page from the partial list (lock must be held)
    Frame* get_partial();

    // Put a page onto the partial list (lock must be held)
    void put_partial(Frame* f);

    // Remove a page from the partial list (lock must be held)
    void remove_partial(Frame* f);

    // Allocate a new page from PMM and format it as a slab
    Frame* new_slab();

    Spinlock lock_;                  // Protects partial_ only
    list_node partial_;              // Partially-filled slab pages

    PerCpuSlab cpu_slab_[kMaxCpus];  // Per-CPU active page

    const char* name_ = "uninit";
    uint32 obj_size_  = 0;           // Aligned object size (bytes)
    uint32 obj_align_ = 0;           // Alignment requirement
    uint32 objs_per_slab_ = 0;      // Objects per 4KB page
};

/* Global size-class caches */
extern KmemCache g_size_caches[kNumSizeClasses];