blob: f9ce2945ddeae0d1a389fc7492eff04fe09535e8 [file] [log] [blame]
/* Copyright (c) 2015 Google Inc.
* Ron Minnich <rminnich@google.com>
* Barret Rhoden <brho@cs.berkeley.edu>
*
* Trivial thread-safe ID pool for small sets of things (< 64K)
* implemented as a stack.
*/
#include <smallidpool.h>
#include <kmalloc.h>
#include <atomic.h>
#include <stdio.h>
#include <assert.h>
struct u16_pool *create_u16_pool(unsigned int size)
{
struct u16_pool *id;
/* We could have size be a u16, but this might catch bugs where users
* tried to ask for more than 2^16 and had it succeed. */
if (size > MAX_U16_POOL_SZ)
return NULL;
/* ids and check are alloced and aligned right after the id struct */
id = kmalloc(sizeof(*id) + sizeof(uint16_t) * size + size, MEM_WAIT);
spinlock_init_irqsave(&id->lock);
id->size = size;
id->ids = (void *)&id[1];
id->check = (void *)&id->ids[id->size];
for (int i = 0; i < id->size; i++) {
id->ids[i] = i;
// fe rhymes with "free"
id->check[i] = 0xfe;
}
id->tos = 0;
return id;
}
/* Returns an unused u16, or -1 on failure (pool full or corruption).
*
* The invariant is that the stackpointer (TOS) will always point to the next
* slot that can be popped, if there are any. All free slots will be below the
* TOS, ranging from indexes [0, TOS), where if TOS == 0, then there are no free
* slots to push. The last valid slot is when TOS == size - 1. */
int get_u16(struct u16_pool *id)
{
uint16_t v;
spin_lock_irqsave(&id->lock);
if (id->tos == id->size) {
spin_unlock_irqsave(&id->lock);
return -1;
}
v = id->ids[id->tos++];
spin_unlock_irqsave(&id->lock);
/* v is ours, we can freely read and write its check field */
if (id->check[v] != 0xfe) {
printk("BAD! %d is already allocated (0x%x)\n", v,
id->check[v]);
return -1;
}
id->check[v] = 0x5a;
return v;
}
void put_u16(struct u16_pool *id, int v)
{
/* we could check for if v is in range before dereferencing. */
if (id->check[v] != 0x5a) {
printk("BAD! freeing non-allocated: %d(0x%x)\n", v,
id->check[v]);
return;
}
id->check[v] = 0xfe;
spin_lock_irqsave(&id->lock);
id->ids[--id->tos] = v;
spin_unlock_irqsave(&id->lock);
}