2addc463ee
Update copyright year in various files. Signed-off-by: Ajit Khaparde <ajit.khaparde@broadcom.com> Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com> Reviewed-by: Kalesh AP <kalesh-anakkur.purayil@broadcom.com>
472 lines
8.2 KiB
C
472 lines
8.2 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2019-2021 Broadcom
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "bitalloc.h"
|
|
|
|
#define BITALLOC_MAX_LEVELS 6
|
|
|
|
|
|
/* Finds the last bit set plus 1, equivalent to gcc __builtin_fls */
|
|
static int
|
|
ba_fls(bitalloc_word_t v)
|
|
{
|
|
int c = 32;
|
|
|
|
if (!v)
|
|
return 0;
|
|
|
|
if (!(v & 0xFFFF0000u)) {
|
|
v <<= 16;
|
|
c -= 16;
|
|
}
|
|
if (!(v & 0xFF000000u)) {
|
|
v <<= 8;
|
|
c -= 8;
|
|
}
|
|
if (!(v & 0xF0000000u)) {
|
|
v <<= 4;
|
|
c -= 4;
|
|
}
|
|
if (!(v & 0xC0000000u)) {
|
|
v <<= 2;
|
|
c -= 2;
|
|
}
|
|
if (!(v & 0x80000000u)) {
|
|
v <<= 1;
|
|
c -= 1;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
/* Finds the first bit set plus 1, equivalent to gcc __builtin_ffs */
|
|
static int
|
|
ba_ffs(bitalloc_word_t v)
|
|
{
|
|
int c; /* c will be the number of zero bits on the right plus 1 */
|
|
|
|
v &= -v;
|
|
c = v ? 32 : 0;
|
|
|
|
if (v & 0x0000FFFF)
|
|
c -= 16;
|
|
if (v & 0x00FF00FF)
|
|
c -= 8;
|
|
if (v & 0x0F0F0F0F)
|
|
c -= 4;
|
|
if (v & 0x33333333)
|
|
c -= 2;
|
|
if (v & 0x55555555)
|
|
c -= 1;
|
|
|
|
return c;
|
|
}
|
|
|
|
int
|
|
ba_init(struct bitalloc *pool, int size)
|
|
{
|
|
bitalloc_word_t *mem = (bitalloc_word_t *)pool;
|
|
int i;
|
|
|
|
/* Initialize */
|
|
pool->size = 0;
|
|
|
|
if (size < 1 || size > BITALLOC_MAX_SIZE)
|
|
return -1;
|
|
|
|
/* Zero structure */
|
|
for (i = 0;
|
|
i < (int)(BITALLOC_SIZEOF(size) / sizeof(bitalloc_word_t));
|
|
i++)
|
|
mem[i] = 0;
|
|
|
|
/* Initialize */
|
|
pool->size = size;
|
|
|
|
/* Embed number of words of next level, after each level */
|
|
int words[BITALLOC_MAX_LEVELS];
|
|
int lev = 0;
|
|
int offset = 0;
|
|
|
|
words[0] = (size + 31) / 32;
|
|
while (words[lev] > 1) {
|
|
lev++;
|
|
words[lev] = (words[lev - 1] + 31) / 32;
|
|
}
|
|
|
|
while (lev) {
|
|
offset += words[lev];
|
|
pool->storage[offset++] = words[--lev];
|
|
}
|
|
|
|
/* Free the entire pool */
|
|
for (i = 0; i < size; i++)
|
|
ba_free(pool, i);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ba_alloc_helper(struct bitalloc *pool,
|
|
int offset,
|
|
int words,
|
|
unsigned int size,
|
|
int index,
|
|
int *clear)
|
|
{
|
|
bitalloc_word_t *storage = &pool->storage[offset];
|
|
int loc = ba_ffs(storage[index]);
|
|
int r;
|
|
|
|
if (loc == 0)
|
|
return -1;
|
|
|
|
loc--;
|
|
|
|
if (pool->size > size) {
|
|
r = ba_alloc_helper(pool,
|
|
offset + words + 1,
|
|
storage[words],
|
|
size * 32,
|
|
index * 32 + loc,
|
|
clear);
|
|
} else {
|
|
r = index * 32 + loc;
|
|
*clear = 1;
|
|
pool->free_count--;
|
|
}
|
|
|
|
if (*clear) {
|
|
storage[index] &= ~(1 << loc);
|
|
*clear = (storage[index] == 0);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int
|
|
ba_alloc(struct bitalloc *pool)
|
|
{
|
|
int clear = 0;
|
|
|
|
return ba_alloc_helper(pool, 0, 1, 32, 0, &clear);
|
|
}
|
|
|
|
/**
|
|
* Help function to alloc entry from highest available index
|
|
*
|
|
* Searching the pool from highest index for the empty entry.
|
|
*
|
|
* [in] pool
|
|
* Pointer to the resource pool
|
|
*
|
|
* [in] offset
|
|
* Offset of the storage in the pool
|
|
*
|
|
* [in] words
|
|
* Number of words in this level
|
|
*
|
|
* [in] size
|
|
* Number of entries in this level
|
|
*
|
|
* [in] index
|
|
* Index of words that has the entry
|
|
*
|
|
* [in] clear
|
|
* Indicate if a bit needs to be clear due to the entry is allocated
|
|
*
|
|
* Returns:
|
|
* 0 - Success
|
|
* -1 - Failure
|
|
*/
|
|
static int
|
|
ba_alloc_reverse_helper(struct bitalloc *pool,
|
|
int offset,
|
|
int words,
|
|
unsigned int size,
|
|
int index,
|
|
int *clear)
|
|
{
|
|
bitalloc_word_t *storage = &pool->storage[offset];
|
|
int loc = ba_fls(storage[index]);
|
|
int r;
|
|
|
|
if (loc == 0)
|
|
return -1;
|
|
|
|
loc--;
|
|
|
|
if (pool->size > size) {
|
|
r = ba_alloc_reverse_helper(pool,
|
|
offset + words + 1,
|
|
storage[words],
|
|
size * 32,
|
|
index * 32 + loc,
|
|
clear);
|
|
} else {
|
|
r = index * 32 + loc;
|
|
*clear = 1;
|
|
pool->free_count--;
|
|
}
|
|
|
|
if (*clear) {
|
|
storage[index] &= ~(1 << loc);
|
|
*clear = (storage[index] == 0);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int
|
|
ba_alloc_reverse(struct bitalloc *pool)
|
|
{
|
|
int clear = 0;
|
|
|
|
return ba_alloc_reverse_helper(pool, 0, 1, 32, 0, &clear);
|
|
}
|
|
|
|
static int
|
|
ba_alloc_index_helper(struct bitalloc *pool,
|
|
int offset,
|
|
int words,
|
|
unsigned int size,
|
|
int *index,
|
|
int *clear)
|
|
{
|
|
bitalloc_word_t *storage = &pool->storage[offset];
|
|
int loc;
|
|
int r;
|
|
|
|
if (pool->size > size)
|
|
r = ba_alloc_index_helper(pool,
|
|
offset + words + 1,
|
|
storage[words],
|
|
size * 32,
|
|
index,
|
|
clear);
|
|
else
|
|
r = 1; /* Check if already allocated */
|
|
|
|
loc = (*index % 32);
|
|
*index = *index / 32;
|
|
|
|
if (r == 1) {
|
|
r = (storage[*index] & (1 << loc)) ? 0 : -1;
|
|
if (r == 0) {
|
|
*clear = 1;
|
|
pool->free_count--;
|
|
}
|
|
}
|
|
|
|
if (*clear) {
|
|
storage[*index] &= ~(1 << loc);
|
|
*clear = (storage[*index] == 0);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int
|
|
ba_alloc_index(struct bitalloc *pool, int index)
|
|
{
|
|
int clear = 0;
|
|
int index_copy = index;
|
|
|
|
if (index < 0 || index >= (int)pool->size)
|
|
return -1;
|
|
|
|
if (ba_alloc_index_helper(pool, 0, 1, 32, &index_copy, &clear) >= 0)
|
|
return index;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
ba_inuse_helper(struct bitalloc *pool,
|
|
int offset,
|
|
int words,
|
|
unsigned int size,
|
|
int *index)
|
|
{
|
|
bitalloc_word_t *storage = &pool->storage[offset];
|
|
int loc;
|
|
int r;
|
|
|
|
if (pool->size > size)
|
|
r = ba_inuse_helper(pool,
|
|
offset + words + 1,
|
|
storage[words],
|
|
size * 32,
|
|
index);
|
|
else
|
|
r = 1; /* Check if in use */
|
|
|
|
loc = (*index % 32);
|
|
*index = *index / 32;
|
|
|
|
if (r == 1)
|
|
r = (storage[*index] & (1 << loc)) ? -1 : 0;
|
|
|
|
return r;
|
|
}
|
|
|
|
int
|
|
ba_inuse(struct bitalloc *pool, int index)
|
|
{
|
|
if (index < 0 || index >= (int)pool->size)
|
|
return -1;
|
|
|
|
return ba_inuse_helper(pool, 0, 1, 32, &index) == 0;
|
|
}
|
|
|
|
static int
|
|
ba_free_helper(struct bitalloc *pool,
|
|
int offset,
|
|
int words,
|
|
unsigned int size,
|
|
int *index)
|
|
{
|
|
bitalloc_word_t *storage = &pool->storage[offset];
|
|
int loc;
|
|
int r;
|
|
|
|
if (pool->size > size)
|
|
r = ba_free_helper(pool,
|
|
offset + words + 1,
|
|
storage[words],
|
|
size * 32,
|
|
index);
|
|
else
|
|
r = 1; /* Check if already free */
|
|
|
|
loc = (*index % 32);
|
|
*index = *index / 32;
|
|
|
|
if (r == 1) {
|
|
r = (storage[*index] & (1 << loc)) ? -1 : 0;
|
|
if (r == 0)
|
|
pool->free_count++;
|
|
}
|
|
|
|
if (r == 0)
|
|
storage[*index] |= (1 << loc);
|
|
|
|
return r;
|
|
}
|
|
|
|
int
|
|
ba_free(struct bitalloc *pool, int index)
|
|
{
|
|
if (index < 0 || index >= (int)pool->size)
|
|
return -1;
|
|
|
|
return ba_free_helper(pool, 0, 1, 32, &index);
|
|
}
|
|
|
|
int
|
|
ba_inuse_free(struct bitalloc *pool, int index)
|
|
{
|
|
if (index < 0 || index >= (int)pool->size)
|
|
return -1;
|
|
|
|
return ba_free_helper(pool, 0, 1, 32, &index) + 1;
|
|
}
|
|
|
|
int
|
|
ba_free_count(struct bitalloc *pool)
|
|
{
|
|
return (int)pool->free_count;
|
|
}
|
|
|
|
int ba_inuse_count(struct bitalloc *pool)
|
|
{
|
|
return (int)(pool->size) - (int)(pool->free_count);
|
|
}
|
|
|
|
static int
|
|
ba_find_next_helper(struct bitalloc *pool,
|
|
int offset,
|
|
int words,
|
|
unsigned int size,
|
|
int *index,
|
|
int free)
|
|
{
|
|
bitalloc_word_t *storage = &pool->storage[offset];
|
|
int loc, r, bottom = 0;
|
|
|
|
if (pool->size > size)
|
|
r = ba_find_next_helper(pool,
|
|
offset + words + 1,
|
|
storage[words],
|
|
size * 32,
|
|
index,
|
|
free);
|
|
else
|
|
bottom = 1; /* Bottom of tree */
|
|
|
|
loc = (*index % 32);
|
|
*index = *index / 32;
|
|
|
|
if (bottom) {
|
|
int bit_index = *index * 32;
|
|
|
|
loc = ba_ffs(~storage[*index] & ((bitalloc_word_t)-1 << loc));
|
|
if (loc > 0) {
|
|
loc--;
|
|
r = (bit_index + loc);
|
|
if (r >= (int)pool->size)
|
|
r = -1;
|
|
} else {
|
|
/* Loop over array at bottom of tree */
|
|
r = -1;
|
|
bit_index += 32;
|
|
*index = *index + 1;
|
|
while ((int)pool->size > bit_index) {
|
|
loc = ba_ffs(~storage[*index]);
|
|
|
|
if (loc > 0) {
|
|
loc--;
|
|
r = (bit_index + loc);
|
|
if (r >= (int)pool->size)
|
|
r = -1;
|
|
break;
|
|
}
|
|
bit_index += 32;
|
|
*index = *index + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r >= 0 && (free)) {
|
|
if (bottom)
|
|
pool->free_count++;
|
|
storage[*index] |= (1 << loc);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int
|
|
ba_find_next_inuse(struct bitalloc *pool, int index)
|
|
{
|
|
if (index < 0 ||
|
|
index >= (int)pool->size ||
|
|
pool->free_count == pool->size)
|
|
return -1;
|
|
|
|
return ba_find_next_helper(pool, 0, 1, 32, &index, 0);
|
|
}
|
|
|
|
int
|
|
ba_find_next_inuse_free(struct bitalloc *pool, int index)
|
|
{
|
|
if (index < 0 ||
|
|
index >= (int)pool->size ||
|
|
pool->free_count == pool->size)
|
|
return -1;
|
|
|
|
return ba_find_next_helper(pool, 0, 1, 32, &index, 1);
|
|
}
|