diff options
author | Jason Ekstrand <[email protected]> | 2019-10-28 14:49:38 -0500 |
---|---|---|
committer | Jason Ekstrand <[email protected]> | 2019-10-31 13:46:09 +0000 |
commit | e4f01eca3b3cd1701f21cacbb8d29fe688ba42bb (patch) | |
tree | bb7d512820fa680e601702362c38b9c5f3d7cada /src/util/sparse_array.c | |
parent | 0a6d2593b8b63d2429e79eed900848c5c9a522c9 (diff) |
util: Add a free list structure for use with util_sparse_array
Reviewed-by: Lionel Landwerlin <[email protected]>
Diffstat (limited to 'src/util/sparse_array.c')
-rw-r--r-- | src/util/sparse_array.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/src/util/sparse_array.c b/src/util/sparse_array.c index d73b129ea26..06816401309 100644 --- a/src/util/sparse_array.c +++ b/src/util/sparse_array.c @@ -192,3 +192,88 @@ util_sparse_array_validate(struct util_sparse_array *arr) { validate_node_level(arr, arr->root, arr->root->level); } + +void +util_sparse_array_free_list_init(struct util_sparse_array_free_list *fl, + struct util_sparse_array *arr, + uint32_t sentinel, + uint32_t next_offset) +{ + fl->head = sentinel; + fl->arr = arr; + fl->sentinel = sentinel; + fl->next_offset = next_offset; +} + +static uint64_t +free_list_head(uint64_t old, uint32_t next) +{ + return ((old & 0xffffffff00000000ull) + 0x100000000ull) | next; +} + +void +util_sparse_array_free_list_push(struct util_sparse_array_free_list *fl, + uint32_t *items, unsigned num_items) +{ + assert(num_items > 0); + assert(items[0] != fl->sentinel); + void *last_elem = util_sparse_array_get(fl->arr, items[0]); + uint32_t *last_next = (uint32_t *)((char *)last_elem + fl->next_offset); + for (unsigned i = 1; i < num_items; i++) { + *last_next = items[i]; + assert(items[i] != fl->sentinel); + last_elem = util_sparse_array_get(fl->arr, items[i]); + last_next = (uint32_t *)((char *)last_elem + fl->next_offset); + } + + uint64_t current_head, old_head; + old_head = p_atomic_read(&fl->head); + do { + current_head = old_head; + *last_next = current_head; /* Index is the bottom 32 bits */ + uint64_t new_head = free_list_head(current_head, items[0]); + old_head = p_atomic_cmpxchg(&fl->head, current_head, new_head); + } while (old_head != current_head); +} + +uint32_t +util_sparse_array_free_list_pop_idx(struct util_sparse_array_free_list *fl) +{ + uint64_t current_head; + + current_head = p_atomic_read(&fl->head); + while (1) { + if ((uint32_t)current_head == fl->sentinel) + return fl->sentinel; + + uint32_t head_idx = current_head; /* Index is the bottom 32 bits */ + void *head_elem = util_sparse_array_get(fl->arr, head_idx); + uint32_t *head_next = (uint32_t *)((char *)head_elem + fl->next_offset); + uint32_t new_head = free_list_head(current_head, *head_next); + uint64_t old_head = p_atomic_cmpxchg(&fl->head, current_head, new_head); + if (old_head == current_head) + return head_idx; + current_head = old_head; + } +} + +void * +util_sparse_array_free_list_pop_elem(struct util_sparse_array_free_list *fl) +{ + uint64_t current_head; + + current_head = p_atomic_read(&fl->head); + while (1) { + if ((uint32_t)current_head == fl->sentinel) + return NULL; + + uint32_t head_idx = current_head; /* Index is the bottom 32 bits */ + void *head_elem = util_sparse_array_get(fl->arr, head_idx); + uint32_t *head_next = (uint32_t *)((char *)head_elem + fl->next_offset); + uint32_t new_head = free_list_head(current_head, *head_next); + uint64_t old_head = p_atomic_cmpxchg(&fl->head, current_head, new_head); + if (old_head == current_head) + return head_elem; + current_head = old_head; + } +} |