/* Coloured Allocator
 * */

#include <linux/mm.h>
#include <asm/colours.h>

#define CACHE_SZ	1024 /* how many pages to keep in our cache */
#define MASK GFP_KERNEL

static spinlock_t coloured_lock;

/*  Basically our cache looks like this

[PTR ] [PTR ] [PTR ] [NULL] [NULL] [NULL] [NULL] [PTR ]
		       ^			   ^
		       |			   | 
		   global_index 		 local_index

global_index == local_index is the bad case.
global_index != local_index means just return the pointer local_index
points to */


static struct page * coloured_cache[NUM_COLOURS][CACHE_SZ/NUM_COLOURS];
static int local_index[NUM_COLOURS];
static int global_index;

#define INC(a) (a=(((a) == (CACHE_SZ/NUM_COLOURS)-1)? 0 : a+1))

struct page * __get_coloured_pages(unsigned long address, int gfp_mask,
	unsigned long order)
{
	struct page * ret;
	int col = addr_colour(address);

	if(order!=0)
		BUG();

	if(local_index[col] != global_index) {
retry:
		ret = coloured_cache[col][local_index[col]];
		coloured_cache[col][local_index[col]] = NULL;
		
		INC(local_index[col]);
	} else {
		/* bad case */
		int i;
		struct page * new_chunk;

		for(i=0; i<NUM_COLOURS; i++) {
			if(coloured_cache[i][global_index]) {
				free_page(coloured_cache[i][global_index]);
			}
		}

		new_chunk = alloc_pages(gfp_mask, LOG2_COLOURS);
		for(i=0; i<NUM_COLOURS; i++) {

			if (i != 0) /* alloc_pages did the first one */
				get_page(new_chunk);

			coloured_cache[i][global_index] = new_chunk;
			new_chunk++;
		}
			
		INC(global_index);

		goto retry;
	}

	return ret;
}
