/*************************************************** */
/* Rule Set Based Access Control                     */
/* Author and (c) 1999-2001: Amon Ott <ao@rsbac.org> */
/* (a lot copied from mm/slab.c, with other          */
/*  copyrights)                                      */
/* Memory allocation functions for all parts         */
/* Last modified: 07/Dec/2001                        */
/*************************************************** */

#include <rsbac/types.h>
#include <rsbac/rkmem.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
/* Size description struct for general RSBAC caches. */
typedef struct rsbac_cache_sizes {
	size_t		 cs_size;
	kmem_cache_t	*cs_cachep;
} rsbac_cache_sizes_t;

static rsbac_cache_sizes_t rsbac_cache_sizes[] = {
#if PAGE_SIZE == 4096
	{    32,	NULL},
#endif
	{    64,	NULL},
#if PAGE_SIZE == 4096
	{    96,	NULL},
#endif
	{   128,	NULL},
	{   192,	NULL},
	{   256,	NULL},
	{   512,	NULL},
	{  1024,	NULL},
	{  2048,	NULL},
	{  4096,	NULL},
	{  8192,	NULL},
//	{ 16384,	NULL},
//	{ 32768,	NULL},
//	{ 65536,	NULL},
//	{131072,	NULL},
	{     0,	NULL}
};


/* Initialisation - setup caches.
 */
void __init rsbac_kmem_cache_sizes_init(void)
{
	rsbac_cache_sizes_t *sizes = rsbac_cache_sizes;
	char name[21];

	do {
		/* For performance, all the general caches are L1 aligned.
		 * This should be particularly beneficial on SMP boxes, as it
		 * eliminates "false sharing".
		 * Note for systems short on memory removing the alignment will
		 * allow tighter packing of the smaller caches. */
		sprintf(name,"rsbac-%Zd",sizes->cs_size);
		if (!(sizes->cs_cachep =
			kmem_cache_create(name, sizes->cs_size,
					0, SLAB_HWCACHE_ALIGN, NULL, NULL))) {
			BUG();
		}
		sizes++;
	} while (sizes->cs_size);
}
#endif

/**
 * rsbac_kmalloc - allocate memory
 * @size: how many bytes of memory are required.
 *
 * rsbac_kmalloc is the normal method of allocating memory for RSBAC
 * in the kernel. It will always be of type GFP_KERNEL.
 *
 * rsbac_kmalloc'd memory is freed by rsbac_kfree
 */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_kmalloc);
#endif
void * rsbac_kmalloc (size_t size)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
	rsbac_cache_sizes_t *csizep = rsbac_cache_sizes;

        if(!size)
          return NULL;
	for (; csizep->cs_size; csizep++) {
		if (size > csizep->cs_size)
			continue;
		return kmem_cache_alloc(csizep->cs_cachep, GFP_KERNEL);
	}
	printk(KERN_WARNING
	       "rsbac_kmalloc: size %u requested, max size is %u!\n",
	       size, RSBAC_MAX_KMALLOC);
	BUG();
	return NULL;
#else
	return kmalloc(size, GFP_KERNEL);
#endif
}

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_vkmalloc);
#endif
void * rsbac_vkmalloc (size_t size, boolean * vmalloc_used_p)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
	rsbac_cache_sizes_t *csizep = rsbac_cache_sizes;

        if(!size)
          return NULL;
	for (; csizep->cs_size; csizep++) {
		if (size > csizep->cs_size)
			continue;
		if(vmalloc_used_p)
		  *vmalloc_used_p = FALSE;
		return kmem_cache_alloc(csizep->cs_cachep, GFP_KERNEL);
	}
	if(vmalloc_used_p)
	  *vmalloc_used_p = TRUE;
	return vmalloc(size);
#else
	if(size > RSBAC_MAX_KMALLOC)
	  {
	    if(vmalloc_used_p)
	      *vmalloc_used_p = TRUE;
	    return vmalloc(size);
	  }
	else
	  {
	    if(vmalloc_used_p)
	      *vmalloc_used_p = FALSE;
	    return kmalloc(size, GFP_KERNEL);
	  }
#endif
}

/**
 * rsbac_kfree - free previously allocated memory
 * @objp: pointer returned by kmalloc.
 *
 * Don't free memory not originally allocated by rsbac_kmalloc()
 * or you will run into trouble.
 *
 * We simply call general kfree, which does everything
 */
#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_kfree);
#endif
void rsbac_kfree (const void *objp)
  {
    kfree(objp);
  }

#if defined(CONFIG_RSBAC_REG) || defined(CONFIG_RSBAC_REG_MAINT)
EXPORT_SYMBOL(rsbac_vkfree);
#endif
void rsbac_vkfree(void *objp, boolean vmalloc_used)
  {
    if(vmalloc_used)
      vfree(objp);
    else
      kfree(objp);
  }

/* end of rkmem.c */
