1/**
2 * \file
3 * \brief Slot management for the memory allocator.
4 */
5
6/*
7 * Copyright (c) 2007, 2008, 2016, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <barrelfish/barrelfish.h>
16#include <mm/mm.h>
17#include <mm/slot_alloc.h>
18#include <stdio.h>
19
20static errval_t rootcn_alloc(void *st, uint8_t reqbits, struct capref *ret)
21{
22    return mm_alloc(st, reqbits, ret, NULL);
23}
24
25/// Allocate a new cnode if needed
26errval_t slot_prealloc_refill(struct slot_prealloc *this)
27{
28    uint8_t refill = !this->current;
29    errval_t err;
30
31    if (this->meta[refill].free == L2_CNODE_SLOTS) {
32        return SYS_ERR_OK; // Nop
33    }
34
35    // Allocate a ram cap
36    struct capref ram_cap;
37    err = mm_alloc(this->mm, L2_CNODE_BITS + OBJBITS_CTE, &ram_cap, NULL);
38    if (err_is_fail(err)) {
39        return err_push(err, MM_ERR_SLOT_MM_ALLOC);
40    }
41
42    // Retype to and build the next cnode
43    struct capref cnode_cap;
44    err = slot_alloc_root(&cnode_cap);
45    if (err_no(err) == LIB_ERR_SLOT_ALLOC_NO_SPACE) {
46        // resize root slot allocator (and rootcn)
47        err = root_slot_allocator_refill(rootcn_alloc, this->mm);
48        if (err_is_fail(err)) {
49            return err_push(err, LIB_ERR_ROOTSA_RESIZE);
50        }
51        // retry slot_alloc_root
52        err = slot_alloc_root(&cnode_cap);
53    }
54    if (err_is_fail(err)) {
55        return err_push(err, LIB_ERR_SLOT_ALLOC);
56    }
57
58    err = cnode_create_from_mem(cnode_cap, ram_cap, ObjType_L2CNode,
59            &this->meta[refill].cap.cnode, L2_CNODE_SLOTS);
60    if (err_is_fail(err)) {
61        return err_push(err, LIB_ERR_CNODE_CREATE);
62    }
63
64    // Set the metadata
65    this->meta[refill].cap.slot  = 0;
66    this->meta[refill].free      = L2_CNODE_SLOTS;
67
68    return SYS_ERR_OK;
69}
70
71errval_t slot_alloc_prealloc(void *inst, uint64_t nslots, struct capref *ret)
72{
73    struct slot_prealloc *this = inst;
74    assert(nslots <= (1UL << this->maxslotbits));
75
76    /* Check if enough space */
77    if (this->meta[this->current].free < nslots) {
78        // Allocate from next cnode
79        this->current = !this->current;
80    }
81
82    if (this->meta[this->current].free < nslots) {
83        return MM_ERR_SLOT_NOSLOTS;
84    }
85
86    /* Return next slot and update */
87    *ret = this->meta[this->current].cap;
88    this->meta[this->current].cap.slot += nslots;
89    this->meta[this->current].free -= nslots;
90
91    return SYS_ERR_OK;
92}
93
94/**
95 * \brief Initialise preallocating slot allocator instance
96 *
97 * \param this Pointer to area for instance data
98 * \param maxslotbits Maximum size of each allocation (in bits)
99 * \param initial_cnode First cap in an empty cnode to start allocating from
100 * \param initial_space Number of slots free in initial cnode
101 * \param ram_mm Memory allocator to use for RAM caps when creating new CNodes
102 */
103errval_t slot_prealloc_init(struct slot_prealloc *this, uint8_t maxslotbits,
104                            struct capref initial_cnode, uint64_t initial_space,
105                            struct mm *ram_mm)
106{
107    this->maxslotbits = maxslotbits;
108    this->mm = ram_mm;
109
110    assert(initial_space == L2_CNODE_SLOTS);
111    if (initial_space != L2_CNODE_SLOTS) {
112        debug_printf("Initial CNode for 2 level preallocating slot allocator needs to be 16kB");
113        return LIB_ERR_SLOT_ALLOC_INIT;
114    }
115
116    this->current = 0;
117    this->meta[0].cap       = initial_cnode;
118    this->meta[0].free      = initial_space;
119    this->meta[1].free      = 0;
120
121    return SYS_ERR_OK;
122}
123
124errval_t slot_alloc_basecn_init(struct slot_alloc_basecn *this)
125{
126    // Use ROOTCN_SLOT_SLOT_ALLOC0 as CNode fore basecn allocator
127    this->cap.cnode.croot = CPTR_ROOTCN;
128    this->cap.cnode.cnode = ROOTCN_SLOT_ADDR(ROOTCN_SLOT_SLOT_ALLOC0);
129    this->cap.cnode.level = CNODE_TYPE_OTHER;
130    this->cap.slot = 0;
131    this->free = L2_CNODE_SLOTS;
132
133    return SYS_ERR_OK;
134}
135
136errval_t slot_alloc_basecn(void *inst, uint64_t nslots, struct capref *ret)
137{
138    struct slot_alloc_basecn *this = inst;
139    errval_t err;
140
141    if (nslots > this->free) {
142        /* XXX: Special case for init, need to get memory from basecn */
143        struct capref ram;
144        err = ram_alloc(&ram, L2_CNODE_BITS + OBJBITS_CTE);
145        if (err_is_fail(err)) {
146            DEBUG_ERR(err, "ram_alloc in slot_alloc_basecn cannot allocate L2 "
147                           "CNode-sized ram cap");
148            return err_push(err, LIB_ERR_RAM_ALLOC);
149        }
150
151        /* to conform with 2 level cspace: put new cnode into rootcn */
152        struct capref cnode;
153        err = slot_alloc_root(&cnode);
154        if (err_is_fail(err)) {
155            DEBUG_ERR(err, "allocating root cnode slot");
156            return err_push(err, LIB_ERR_SLOT_ALLOC);
157        }
158
159        err = cnode_create_from_mem(cnode, ram, ObjType_L2CNode,
160                                    &this->cap.cnode, L2_CNODE_SLOTS);
161        if (err_is_fail(err)) {
162            return err_push(err, LIB_ERR_CNODE_CREATE);
163        }
164
165        this->cap.slot = 0;
166        this->free = L2_CNODE_SLOTS;
167    }
168
169    assert(nslots <= this->free);
170    *ret = this->cap;
171    this->cap.slot += nslots;
172    this->free -= nslots;
173
174    return SYS_ERR_OK;
175}
176
177/// Requires an instance of range_slot_allocator
178errval_t slot_alloc_dynamic(void *alloc, uint64_t nslots, struct capref *ret)
179{
180    return range_slot_alloc(alloc, nslots, ret);
181}
182
183errval_t slot_refill_dynamic(void *alloc)
184{
185    return range_slot_alloc_refill(alloc, L2_CNODE_SLOTS);
186}
187