/*
    Copyright 2001 (c) by Claudio Jeker,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public 
    Licence. A full copy of the GNU GPL is provided in the file COPYING 
    found in the development root of Topsy.
*/
/*
	
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/VMemory/Attic/vm_hash.c,v $
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2922 $
    Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $  by: $Author: baderma $
 
*/
/*
	 vm_hash: This is a global hash tree where vm_prefs can be stored by object 
	 and offset. The hash tree can be rehashed if neccessary and can be used 
	 within different vm object managers.
 */
#include "vm.h"
#include "vm_alloc.h"
#include "vm_page.h"
#include "vm_hash.h"
#include "tailq.h"

TAILQ_HEAD(pref_list, vm_hash_pref) boot_bucket;

struct vm_hash
{
  /* reordered to gain faster access to often used vars */
  int hashmask;
  struct pref_list *hash;
  int hashsize;
  /* XXX lock */
}
vmhash;

struct vm_hash_pref
{
  struct vm_pref pref;
  TAILQ_ENTRY(vm_hash_pref) prefq;
};

static void vm_hash_unlock(void);
static void vm_hash_lock(void);

/* XXX PAGE_SHIFT defined */
#define PAGE_SHIFT 12
#define ATOP(x) (((unsigned long)(x)) >> PAGE_SHIFT)
#define VM_PAGEHASH(obj,off) \
	(((unsigned long)obj+ATOP(off)) & (vmhash.hashmask))

/* init the hash list so that we can start, as soon as the vm is running
	 vm_hash_rehash should be called to achive a better access time. */
void vm_hash_init(void)
{
  /* one bucket for the start */
  TAILQ_INIT(&boot_bucket);
  vmhash.hash = &boot_bucket;
  vmhash.hashsize = 1;
  vmhash.hashmask = 0;
  /* XXX init lock */
}

struct vm_pref* vm_hash_lookup(address_t obj, offset_t off)
      /* XXX interrupt handler */
{
  struct vm_hash_pref* t;
  struct pref_list *h;

  h = &vmhash.hash[VM_PAGEHASH(obj,off)];

  vm_hash_lock();
  TAILQ_FOREACH(h, t, prefq)
  {
    if ( VM_PREF(t)->offset == off &&
         VM_PREF(t)->object == obj )
    {
      vm_hash_unlock();
      return VM_PREF(t);
    }
  }
  vm_hash_unlock();
  return NULL;
}

int vm_hash_add(address_t obj, struct vm_pref* pref)
{
  struct pref_list *h;

  h = &vmhash.hash[VM_PAGEHASH(obj,pref->offset)];

  vm_hash_lock();
  TAILQ_ADDTAIL(h, (struct vm_hash_pref*)pref, prefq);
  vm_hash_unlock();

  return VM_OK;
}


struct vm_pref* vm_hash_remove(address_t obj, offset_t off)
{
  struct vm_hash_pref* t;
  struct pref_list *h;

  h = &vmhash.hash[VM_PAGEHASH(obj,off)];

  vm_hash_lock();
  TAILQ_FOREACH(h, t, prefq)
  {
    if ( VM_PREF(t)->offset == off &&
         VM_PREF(t)->object == obj )
    {
      TAILQ_REMOVE(h, t, prefq);
      vm_hash_unlock();
      return VM_PREF(t);
    }
  }
  vm_hash_unlock();
  return NULL;
}


int vm_hash_removepage(address_t obj, struct vm_pref* pref)
{
  struct pref_list *h;

  h = &vmhash.hash[VM_PAGEHASH(obj,pref->offset)];

  vm_hash_lock();
  TAILQ_REMOVE(h, (struct vm_hash_pref*)pref, prefq);
  vm_hash_unlock();

  return VM_OK;
}


void vm_hash_rehash(void)
{
  struct pref_list *hash;
  struct pref_list *oldhash;
  struct vm_hash_pref* t;
  struct pref_list *h;
  int hashsize;
  int oldhashsize;
  int i;

  /* the idea is to allocate a hashlist big enough to have only a few (<<10)
  	 pages per hashkey so lookups are fast but we waste some ram, but we have
  	 enough to waste...
  	 (Topsy uses less than 1MB of it the other 31MB are free, this is not a 
  	 glibc jumbo system ...)
   */
  /* 1. find out how many entries we need. Let's start with one entry per page */
  for ( hashsize = 1; hashsize <= vm->stat.npages; hashsize<<=1 )
    ;

  /* 2. allocate #entires*sizeof(struct pref_list) memory */
  hash = (struct pref_list*) vm_alloc_desc(hashsize * sizeof(struct pref_list) );

  /* 3. init all queueheads (TAILQ_INIT) */
  for ( i = 0; i < hashsize; i++ )
    TAILQ_INIT(&hash[i]);

  /* 4. lock the old hash list */
  oldhashsize = vmhash.hashsize;
  oldhash = vmhash.hash;

  vm_hash_lock();
  /* 5. vmhash.page_hash = newhash; */
  /* after the lock it is safe to do that */
  vmhash.hash = hash;
  vmhash.hashsize = hashsize;
  vmhash.hashmask = hashsize - 1;

  /* 5. walk through the hashlist (remove from the old add to the new) */
  for ( i = 0; i < oldhashsize; i++ )
    TAILQ_FOREACH(&oldhash[i], t, prefq)
  {
    TAILQ_REMOVE(&oldhash[i], t, prefq); /* removed form the old table */

    h = &vmhash.hash[VM_PAGEHASH(VM_PREF(t)->object,VM_PREF(t)->offset)];
    TAILQ_ADDTAIL(h, t, prefq);
  }
  /* 7. unlock hashlist */
  vm_hash_unlock();

  /* 8. free oldhash list, but only if it's not the static boot_bucket */
  if ( oldhash != &boot_bucket )
    vm_free_desc(oldhash);
  /* 9. done */

}

static void vm_hash_lock(void)
{}

static void vm_hash_unlock(void)
{}

void vm_hash_print(void)
{}
