#include "container_seglists.h"
#include "ctree.h"
#include "out.h"
#include "sys_util.h"
#include "util.h"
#include "valgrind_internal.h"
#include "queue.h"
struct seglist_entry {
struct memory_block m;
STAILQ_ENTRY(seglist_entry) entry;
};
#define SEGLIST_BLOCK_LISTS 64U
struct block_container_seglists {
struct block_container super;
STAILQ_HEAD(, seglist_entry) blocks[SEGLIST_BLOCK_LISTS];
uint64_t nonempty_lists;
};
static int
container_seglists_insert_block(struct block_container *bc,
const struct memory_block *m)
{
ASSERT(m->chunk_id < MAX_CHUNK);
ASSERT(m->zone_id < UINT16_MAX);
ASSERTne(m->size_idx, 0);
struct block_container_seglists *c =
(struct block_container_seglists *)bc;
ASSERT(m->size_idx <= SEGLIST_BLOCK_LISTS);
struct seglist_entry *e = m->m_ops->get_user_data(m);
VALGRIND_DO_MAKE_MEM_DEFINED(e, sizeof(*e));
struct seglist_entry **last = c->blocks[m->size_idx - 1].stqh_last;
VALGRIND_ADD_TO_TX(e, sizeof(*e));
VALGRIND_ADD_TO_TX(last, sizeof(*last));
e->m = *m;
STAILQ_INSERT_TAIL(&c->blocks[m->size_idx - 1], e, entry);
VALGRIND_SET_CLEAN(last, sizeof(*last));
VALGRIND_SET_CLEAN(e, sizeof(*e));
VALGRIND_REMOVE_FROM_TX(last, sizeof(*last));
VALGRIND_REMOVE_FROM_TX(e, sizeof(*e));
c->nonempty_lists |= 1ULL << (m->size_idx - 1);
return 0;
}
static int
container_seglists_get_rm_block_bestfit(struct block_container *bc,
struct memory_block *m)
{
struct block_container_seglists *c =
(struct block_container_seglists *)bc;
ASSERT(m->size_idx <= SEGLIST_BLOCK_LISTS);
uint32_t i = 0;
uint64_t size_mask = (1ULL << (m->size_idx - 1)) - 1;
uint64_t v = c->nonempty_lists & ~size_mask;
if (v == 0)
return ENOMEM;
i = util_lssb_index64(v);
struct seglist_entry *e = STAILQ_FIRST(&c->blocks[i]);
VALGRIND_ADD_TO_TX(e, sizeof(*e));
STAILQ_REMOVE_HEAD(&c->blocks[i], entry);
if (STAILQ_EMPTY(&c->blocks[i]))
c->nonempty_lists &= ~(1ULL << (i));
VALGRIND_SET_CLEAN(e, sizeof(*e));
VALGRIND_REMOVE_FROM_TX(e, sizeof(*e));
*m = e->m;
return 0;
}
static int
container_seglists_is_empty(struct block_container *bc)
{
struct block_container_seglists *c =
(struct block_container_seglists *)bc;
return c->nonempty_lists == 0;
}
static void
container_seglists_rm_all(struct block_container *bc)
{
struct block_container_seglists *c =
(struct block_container_seglists *)bc;
for (unsigned i = 0; i < SEGLIST_BLOCK_LISTS; ++i) {
while (!STAILQ_EMPTY(&c->blocks[i])) {
struct seglist_entry *e = STAILQ_FIRST(&c->blocks[i]);
VALGRIND_ADD_TO_TX(e, sizeof(*e));
STAILQ_REMOVE_HEAD(&c->blocks[i], entry);
VALGRIND_SET_CLEAN(e, sizeof(*e));
VALGRIND_REMOVE_FROM_TX(e, sizeof(*e));
}
}
c->nonempty_lists = 0;
}
static void
container_seglists_destroy(struct block_container *bc)
{
struct block_container_seglists *c =
(struct block_container_seglists *)bc;
Free(c);
}
static struct block_container_ops container_seglists_ops = {
.insert = container_seglists_insert_block,
.get_rm_exact = NULL,
.get_rm_bestfit = container_seglists_get_rm_block_bestfit,
.get_exact = NULL,
.is_empty = container_seglists_is_empty,
.rm_all = container_seglists_rm_all,
.destroy = container_seglists_destroy,
};
struct block_container *
container_new_seglists(struct palloc_heap *heap)
{
struct block_container_seglists *bc = Malloc(sizeof(*bc));
if (bc == NULL)
goto error_container_malloc;
bc->super.heap = heap;
bc->super.c_ops = &container_seglists_ops;
for (unsigned i = 0; i < SEGLIST_BLOCK_LISTS; ++i)
STAILQ_INIT(&bc->blocks[i]);
bc->nonempty_lists = 0;
return (struct block_container *)&bc->super;
error_container_malloc:
return NULL;
}