|  | /* 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, KMALLOC_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); | 
|  | } |