1/*
2 * \brief Domain internals for the process manager.
3 *
4 * Copyright (c) 2017, ETH Zurich.
5 * All rights reserved.
6 *
7 * This file is distributed under the terms in the attached LICENSE file.
8 * If you do not find this file, copies can be found by writing to:
9 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
10 */
11
12#include <barrelfish/barrelfish.h>
13#include <collections/hash_table.h>
14#include <collections/list.h>
15#include <if/spawn_defs.h>
16
17#include "domain.h"
18#include "spawnd_state.h"
19
20#define HASH_INDEX_BUCKETS 6151
21static collections_hash_table* domain_table = NULL;
22
23#define DOMAIN_CAP_REFILL_COUNT L2_CNODE_SLOTS//1
24static struct domain_cap_node *domain_cap_list = NULL;
25static uint32_t free_domain_caps = 0;
26static domainid_t domain_alloc = 0;
27
28inline bool domain_should_refill_caps(void) {
29    return free_domain_caps == 0;
30}
31
32/**
33 * \brief Allocates a new L2 cnode and fills it with domain capabilities.
34 */
35errval_t domain_prealloc_caps(void)
36{
37    struct capref new_cnode_cap;
38    struct cnoderef new_cnode;
39    errval_t err = cnode_create_l2(&new_cnode_cap, &new_cnode);
40    if (err_is_fail(err)) {
41        DEBUG_ERR(err, "cnode_create_l2");
42        return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
43    }
44
45    struct capref cap_iter = {
46        .cnode = new_cnode,
47        .slot = 0
48    };
49    err = cap_retype(cap_iter, cap_procmng, 0, ObjType_Domain, 0,
50                     DOMAIN_CAP_REFILL_COUNT);
51    if (err_is_fail(err)) {
52        DEBUG_ERR(err, "cap_retype");
53        return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
54    }
55
56    for (cap_iter.slot = 0; cap_iter.slot < DOMAIN_CAP_REFILL_COUNT; ++cap_iter.slot) {
57        struct domain_cap_node *node = (struct domain_cap_node*) malloc(
58                sizeof(struct domain_cap_node));
59        node->domain_cap = cap_iter;
60
61        err = domain_cap_hash(node->domain_cap, &node->hash);
62        if (err_is_fail(err)) {
63            DEBUG_ERR(err, "domain_cap_hash");
64            return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
65        }
66
67        node->next = domain_cap_list;
68        domain_cap_list = node;
69        ++free_domain_caps;
70    }
71
72    return SYS_ERR_OK;
73}
74
75/**
76 * \brief Returns the next node in the list of available domain caps.
77 */
78struct domain_cap_node *next_cap_node(void)
79{
80    assert(domain_cap_list != NULL);
81    assert(free_domain_caps > 0);
82
83    struct domain_cap_node *tmp = domain_cap_list;
84    domain_cap_list = domain_cap_list->next;
85    --free_domain_caps;
86
87    return tmp;
88}
89
90/**
91 * \brief Creates and returns a new domain entry.
92 *
93 * \param cap_node  preallocated domain cap node.
94 * \param ret_entry returned domain entry, must be passed in non-NULL.
95 */
96errval_t domain_new(struct domain_cap_node *cap_node, const char* argbuf,
97                    size_t argbytes, struct domain_entry **ret_entry)
98{
99    assert(ret_entry != NULL);
100
101    struct domain_entry *entry = (struct domain_entry*) malloc(
102            sizeof(struct domain_entry));
103    if (entry == NULL) {
104        return LIB_ERR_MALLOC_FAIL;
105    }
106
107    entry->cap_node = cap_node;
108    entry->status = DOMAIN_STATUS_NIL;
109    memset(entry->spawnds, 0, sizeof(entry->spawnds));
110    entry->num_spawnds_running = 0;
111    entry->num_spawnds_resources = 0;
112    entry->waiters = NULL;
113
114    entry->domainid = domain_alloc;
115    domain_alloc++;
116
117    entry->argbuf = memdup(argbuf, argbytes);
118    entry->argbytes = argbytes;
119
120    if (domain_table == NULL) {
121        collections_hash_create_with_buckets(&domain_table, HASH_INDEX_BUCKETS,
122                                             NULL);
123        if (domain_table == NULL) {
124            return PROC_MGMT_ERR_CREATE_DOMAIN_TABLE;
125        }
126    }
127
128    collections_hash_insert(domain_table, cap_node->hash, entry);
129
130    *ret_entry = entry;
131
132    return SYS_ERR_OK;
133}
134
135/**
136 * \brief Returns the domain entry associated with the given domain cap.
137 *
138 * \param domain_cap identifying cap for which to look up the domain entry.
139 * \param returned domain entry, must be passed in non-NULL.
140 */
141errval_t domain_get_by_cap(struct capref domain_cap,
142                           struct domain_entry **ret_entry)
143{
144    assert(ret_entry != NULL);
145
146    uint64_t key;
147    errval_t err = domain_cap_hash(domain_cap, &key);
148    if (err_is_fail(err)) {
149        return err;
150    }
151
152    void *table_entry = collections_hash_find(domain_table, key);
153    if (table_entry == NULL) {
154        return PROC_MGMT_ERR_DOMAIN_TABLE_FIND;
155    }
156    *ret_entry = (struct domain_entry*) table_entry;
157
158    return SYS_ERR_OK;
159}
160
161errval_t domain_get_by_id(domainid_t domain_id,
162                          struct domain_entry **ret_entry)
163{
164    // XXX slow since we traverse the whole hash map
165    collections_hash_traverse_start(domain_table);
166
167    uint64_t key;
168    void* ele = collections_hash_traverse_next(domain_table, &key);
169
170    // get all domain ids and store in list
171    // XXX traverse whole hash table since it seems to not
172    // reset the internal lists when resetting the traversal of the hash table
173    while (ele != NULL) {
174        struct domain_entry *entry = (struct domain_entry*) ele;
175
176        if (entry->domainid == domain_id) {
177            *ret_entry = entry;
178        }
179
180        ele = collections_hash_traverse_next(domain_table, &key);
181    }
182
183    collections_hash_traverse_end(domain_table);
184    if (ret_entry == NULL) {
185        return PROC_MGMT_ERR_DOMAIN_TABLE_FIND;
186    } else {
187        return SYS_ERR_OK;
188    }
189}
190
191/**
192 * \brief Adds a new core to the list of cores where the given domain runs.
193 *
194 * \param entry   domain entry to add a new core for.
195 * \param core_id new core running a dispatcher for the domain.
196 */
197void domain_run_on_core(struct domain_entry *entry, coreid_t core_id)
198{
199    assert(entry != NULL);
200    assert(core_id < MAX_COREID);
201    assert(entry->status == DOMAIN_STATUS_NIL ||
202           entry->status == DOMAIN_STATUS_RUNNING);
203
204    entry->status = DOMAIN_STATUS_RUNNING;
205
206    entry->spawnds[core_id] = spawnd_state_get(core_id);
207    ++entry->num_spawnds_running;
208    ++entry->num_spawnds_resources;
209}
210
211
212/**
213 * \brief Creates a new domain entry for the given cap node and core.
214 *
215 * \param cap_node preallocated capability node for the new domain.
216 * \param core_id  core that runs the new domain.
217 */
218errval_t domain_spawn(struct domain_cap_node *cap_node, coreid_t core_id,
219                      const char* argbuf, size_t argbytes)
220{
221    struct domain_entry *entry = NULL;
222    errval_t err = domain_new(cap_node, argbuf, argbytes, &entry);
223    if (err_is_fail(err)) {
224        if (entry != NULL) {
225            free(entry);
226        }
227        return err;
228    }
229
230    domain_run_on_core(entry, core_id);
231
232    return SYS_ERR_OK;
233}
234
235/**
236 * \brief Marks that the domain identified by the given cap spans a new core.
237 *
238 * \param domain_cap identifying capability for the spanning domain.
239 * \param core_id    new core which the domain spans.
240 */
241errval_t domain_span(struct capref domain_cap, coreid_t core_id)
242{
243    struct domain_entry *entry = NULL;
244    errval_t err = domain_get_by_cap(domain_cap, &entry);
245    if (err_is_fail(err)) {
246        return err;
247    }
248    assert(entry != NULL);
249
250    domain_run_on_core(entry, core_id);
251
252    return SYS_ERR_OK;
253}
254
255void domain_get_all_ids(domainid_t** domains, size_t* len)
256{
257    if (domain_table == NULL) {
258        *len = 0;
259        return;
260    }
261
262    collections_hash_traverse_start(domain_table);
263
264    collections_listnode* start;
265    collections_list_create(&start, NULL);
266
267    uint64_t key;
268    void* ele = collections_hash_traverse_next(domain_table, &key);
269
270    // get all domain ids and store in list
271    while (ele != NULL) {
272        struct domain_entry *entry = (struct domain_entry*) ele;
273
274        if (entry->status != DOMAIN_STATUS_CLEANED) {
275            collections_list_insert(start, &entry->domainid);
276        }
277
278        ele = collections_hash_traverse_next(domain_table, &key);
279    }
280
281    domainid_t* doms = (domainid_t*) calloc(1, sizeof(domainid_t)*
282                                             collections_list_size(start));
283
284    *len = collections_list_size(start);
285    // copy domain ids
286    for (int i = 0; i < collections_list_size(start); i++) {
287        doms[i] = *((domainid_t*) collections_list_get_ith_item(start, i));
288    }
289
290    collections_list_release(start);
291    *domains = doms;
292    collections_hash_traverse_end(domain_table);
293}
294