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