/*
 * disk_reserve.c: code to support DISK reservations
 *
 * Copyright (C) 2000 TimeSys Corporation
 *
 * This is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * This file is derived from software distributed under the following terms:
 *
 * Real-time and Multimedia Systems Laboratory
 * Copyright (c) 2000 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Real-Time and Multimedia Systems Laboratory
 *  Attn: Prof. Raj Rajkumar
 *  Electrical and Computer Engineering, and Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 *  or via email to raj@ece.cmu.edu
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */

/* Note, I assume that the server period is less than one second */
#include <rk/rk_linux.h>
#include <rk/rk_error.h>
#include <rk/rk.h>
#include <rk/timespec.h>
#include <linux/time.h>
#include <asm/uaccess.h>
#include <rk/css.h>
#include <linux/fs.h>
#include <linux/blkdev.h>

#define LIMIT_READAHEAD
#define KB 1024
/*
 * Function prototypes
 */ 
static	int   disk_reserve_destroy(rk_reserve_t);
static	void  disk_reserve_replenish(rk_reserve_t, cpu_tick_t, cpu_tick_t);
static	void  disk_reserve_enforce(rk_reserve_t);
static  void  disk_reserve_update_account(rk_reserve_t, unsigned long);
static  void  disk_reserve_quota_query(rk_reserve_t, unsigned long *);

void (*driver_replenish_function)(rk_reserve_t, unsigned long);
/*static  int   disk_reserve_read_proc(rk_reserve_t, char *); */


struct rk_reserve_ops	disk_reserve_ops = {
	disk_reserve_destroy,
	NULL, /* start_account */
	NULL, /* stop_account */
	disk_reserve_replenish,
	disk_reserve_enforce,
	NULL, /* disk_reserve_attach */
	NULL, /* disk_reserve_detach */
#ifdef	linux
	NULL, /* sleep_on */
	NULL, /* cpu_reserve_read_proc */
#endif
	disk_reserve_update_account, /* update the resource account */
	disk_reserve_quota_query, /* ask for the available quota */
	NULL, /* wait_on_quota */
};

static inline unsigned long UPPER(unsigned long long A, unsigned long long B)
{
	if (A-((A/B) *B))
	return (unsigned long) ((A/B)+1);
	else return (unsigned long) (A/B);
}
//#define UPPER(A,B) (A-((A/B)*B))? (unsigned long) ((A/B)+1) : (unsigned long)(A/B)
#define disk_reserve_entry(list) list_entry((list), struct disk_reserve, disk_link)

/* Unable CSS Functinality for this moment*/
#define CSS_CONFIG 1
#define CPU_RSV_CTL 1

/*
 * Global variables
 */

#define EARLIEST_DEADLINE_FIRST   0
#define NUM_DISK_RESERVE_POLICIES  (EARLIEST_DEADLINE_FIRST+1)

static int disk_reserves_scheduling_policy = EARLIEST_DEADLINE_FIRST ;

struct list_head	         disk_reserve_head;
/* current disk utilization */
static disk_capacity_t           disk_current_capacity; 
/* the disk server period */
static cpu_tick_data_t           disk_server_period_ticks;
/* the disk server capcity (the number of tickets (blocks) to access ) */
static disk_block_t              disk_server_blocks;
/* CPU ticks computation  needed by driver before one block disk access */
static cpu_tick_data_t           disk_driver_bf_overhead; 
/* CPU ticks computation needed by driver after one block disk access */
static cpu_tick_data_t           disk_driver_af_overhead;
/* I/O time (in unit of cpu ticks ) for one block disk access */
static cpu_tick_data_t           disk_driver_io_overhead;
/* number of reserved blocks by default */
static int disk_default_reserved_blocks;
/* the server resource set */
static struct task_struct        *disk_server_tsk;
static rk_resource_set_t         disk_server_resource_set;
static rk_reserve_t              disk_cpu_rsv;
static cpu_reserve_attr_data_t   disk_cpu_attr;   
static disk_param_data_t       disk_parameters;
static int css_enable = 1;
void    (*rk_disk_reserve_hook)(struct task_struct *, struct buffer_head *);
int     (*rk_disk_readahead_max_hook)(struct inode *, struct task_struct *);
int     (*rk_disk_realtime_req_hook)(struct task_struct *);
#if 1
void (*rk_tick2nanosecond_hook)(long, long *);
#endif



#define ONE_BLOCK_DRIVER (disk_driver_bf_overhead+disk_driver_af_overhead)
#define ONE_BLOCK_IO (disk_driver_io_overhead)
#define BLKSIZE disk_parameters.blocksize

/*
 * More function prototypes 
 */

static disk_block_t server_capacity_computation_add(disk_reserve_attr_t);
static disk_block_t server_capacity_computation_del(disk_reserve_t);
static int disk_reserve_admit_request(disk_reserve_attr_t);
static int disk_reserve_adm_cancel_request(disk_reserve_t disk);
static void disk_size_to_blocks(unsigned long, disk_block_t *);
static int disk_capacity(unsigned long, cpu_tick_data_t);
void rk_disk_reserve_attach_bh(struct task_struct *, struct buffer_head *);
int rk_disk_readahead_max(struct inode *, struct task_struct *);
int rk_disk_realtime_req(struct task_struct *);
int disk_higher_prio(rk_reserve_t, rk_reserve_t);
#if 1
void rk_tick2nanosecond(long,long*);
#endif
static inline void tick2nanosecond(long,long*);
static rk_reserve_t rk_get_disk_reserve(struct task_struct *);
/*
 * Functions
 */


void
disk_reserve_init(void)
{
	INIT_LIST_HEAD(&disk_reserve_head);
	css_enable = 1;
	driver_replenish_function = NULL;
	disk_current_capacity = 0;
	/* set to default value */
	disk_parameters.invoke_ticks = 0; 
	disk_parameters.return_ticks = 0;
   /* for temporary */
#if 1

   disk_parameters.blocksize = 4096; /* 4 KB */
#define M 1024*1024
   disk_parameters.max_readahead = 200 *M;
   {
     unsigned long long server_period;

     server_period = 50000000; /* 50 ms */
     nanosec2tick(&server_period, &disk_parameters.server_period_ticks);
     printk("disk_server_period_ticks = %lu \n", (unsigned long) disk_parameters.server_period_ticks);
   }
   
   /* data from measurement 
    * At this moment we just hard-wired the parameters.
    * More elegant tool will be added here 
    */
   {
     //     unsigned long long server_period;
     unsigned long long driver_bf_overhead;
     unsigned long long driver_af_overhead;
     unsigned long long driver_io_overhead;
     
     driver_bf_overhead = 200000; /* 200 us */
     driver_af_overhead = 100000; /* 100 us */
     driver_io_overhead = 500000; /* 500 us */

     
     nanosec2tick(&driver_bf_overhead, &disk_driver_bf_overhead);
     nanosec2tick(&driver_af_overhead, &disk_driver_af_overhead);
     nanosec2tick(&driver_io_overhead, &disk_driver_io_overhead);
   }
   disk_server_period_ticks = disk_parameters.server_period_ticks;

#ifdef APPLE_DEBUG_RK
   printk("disk_server_period_ticks = %lu \n", (unsigned long) disk_server_period_ticks);
   printk("disk_driver_bf_overhead = %lu \n", (unsigned long) disk_driver_bf_overhead );
   printk("disk_driver_af_overhead = %lu \n", (unsigned long) disk_driver_af_overhead );
   printk("disk_driver_io_overhead  = %lu \n", (unsigned long) disk_driver_io_overhead );
   printk("ONE_BLOCK_DRIVER = %lu \n", (unsigned long ) ONE_BLOCK_DRIVER);
   printk("ONE_BLOCK_IO = %lu \n", (unsigned long )ONE_BLOCK_IO);
   printk("one block ticks = %lu \n",(unsigned long )ONE_BLOCK_DRIVER + ONE_BLOCK_IO);

#endif APPLE_DEBUG_RK

   disk_default_reserved_blocks = 1;
#endif
	disk_parameters.one_block_ticks = disk_driver_bf_overhead+
	     disk_driver_af_overhead+disk_driver_io_overhead;

	/* create default reservation (one block per server period)*/ 
	disk_server_blocks = disk_default_reserved_blocks;
	disk_server_resource_set = rk_resource_set_create("disk");
	disk_cpu_attr.start_time.tv_sec = 0;
	disk_cpu_attr.start_time.tv_nsec = 0;
	disk_cpu_attr.compute_time.tv_sec = 0;
	tick2nanosecond(ONE_BLOCK_DRIVER * disk_server_blocks, &disk_cpu_attr.compute_time.tv_nsec);
	disk_cpu_attr.period.tv_sec = 0;
	tick2nanosecond(disk_server_period_ticks, &disk_cpu_attr.period.tv_nsec);
	disk_cpu_attr.deadline.tv_sec = 0;
	disk_cpu_attr.deadline.tv_nsec = disk_cpu_attr.period.tv_nsec;

	disk_cpu_attr.blocking_time.tv_sec = 0;
	tick2nanosecond(ONE_BLOCK_IO * disk_server_blocks, &disk_cpu_attr.blocking_time.tv_nsec);
	disk_cpu_attr.reserve_type.enf_mode = RSV_SOFT;
	disk_cpu_attr.reserve_type.rep_mode = RSV_SOFT;
	disk_cpu_attr.reserve_type.sch_mode = RSV_SOFT;
	disk_cpu_attr.ticket_type = CONSTANT;
	/* one block for now */
	disk_cpu_attr.quota_tickets = disk_server_blocks; 
	/* Ticket ages one elapse time to access io */
	disk_cpu_attr.ticket_age = ONE_BLOCK_DRIVER+ONE_BLOCK_IO ;

	{
	  extern rk_reserve_t cpu_reserve_create(rk_resource_set_t,
						 cpu_reserve_attr_t);
	  disk_cpu_rsv = cpu_reserve_create(disk_server_resource_set, &disk_cpu_attr);
	  if (disk_cpu_rsv == NULL_RESERVE) {
	    /* error */
	    printk("Disk reserve initialization error\n");
	    return;
	  }
	}
	/* enable hook */
	{
	  extern void rk_enable_hook_disk_reserve(void);
	  rk_enable_hook_disk_reserve();
	}
}


/* Define the readahead blocks 
 * In Linux kernel, fs tries to readahead for whole size of request or 
 * max_readahead specified by device (default:MAX_READAHEAD defined in 
 * blkdev.h). 
 */
int
rk_disk_readahead_max(struct inode *inode, struct task_struct *tsk)
{
  unsigned long flags,rdahead;
  rk_spin_lock(flags);
  rdahead = MAX_READAHEAD;
  if ((rk_get_disk_reserve(tsk)!= NULL_RESERVE) &&
      (disk_parameters.max_readahead)) {
#ifdef DISK_OVH_MEASURE
    rdahead = 1;
#else
    rdahead = disk_parameters.max_readahead;
#endif
  }
  if (inode->i_dev && max_readahead[MAJOR(inode->i_dev)]) {
    if (rdahead > max_readahead[MAJOR(inode->i_dev)][MINOR(inode->i_dev)])
      rdahead =  max_readahead[MAJOR(inode->i_dev)][MINOR(inode->i_dev)];
  }
  rk_spin_unlock(flags);
#ifdef LIMIT_READAHEAD
  if (rdahead >= 80 * KB) rdahead = 80*KB;
#endif LIMIT_READAHEAD
  return rdahead;
}

/* Use disk_reserve that attaches to the current resource set of the task.
 * Otherwise, use the disk reserve of the highest priority resource set.
 */
static rk_reserve_t rk_get_disk_reserve(struct task_struct *tsk) {
  struct list_head *rs_list;
  struct resource_set_list *entry;
  rk_resource_set_t rs;

  if ( (rk_valid_rset(tsk->rk_resource_set)) &&
       (tsk->rk_resource_set->rs_disk)) {
    return tsk->rk_resource_set->rs_disk;
  } 

  /* no disk reserve in the current resource set, `search the rs_list */
  if (tsk->rs_list.resource_set_list.next == NULL) {
    /*  */
    //    INIT_LIST_HEAD(&(tsk->rs_list.resource_set_list));
    return NULL_RESERVE;
  }
  rs_list = &((&tsk->rs_list)->resource_set_list);
  for (rs_list = rs_list->next; 
       rs_list != &((&tsk->rs_list)->resource_set_list);
       rs_list = rs_list->next) {
    /* get an resource set */
    entry = list_entry(rs_list, struct resource_set_list, resource_set_list);
    rs = entry->rk_resource_set;
    if (rk_valid_rset(rs) && rs->rs_disk) {
      return rs->rs_disk;
    }
  }
  return NULL_RESERVE;
}

int rk_disk_realtime_req(struct task_struct *tsk) {
  unsigned long flags;
  int retval;
  rk_spin_lock(flags);
  if (rk_get_disk_reserve(tsk)!= NULL_RESERVE) 
    retval = 1;
  else retval = 0;
  rk_spin_unlock(flags);
  return retval;
}

/* If there is a resource set already attached to the buffer head,
 * use the resource set that has disk reserve w/ higher priority.
 * In case that there is no resource set attached to the buffer head,
 * attach the resource set that has disk reserve.
 * Otherwise attach the tsk current resource set to the buffer head
 */
void 
rk_disk_reserve_attach_bh(struct task_struct *tsk, struct buffer_head *bh)
{
  unsigned long flags;
  rk_reserve_t disk_rsv;

  rk_spin_lock(flags);
  disk_rsv = rk_get_disk_reserve(tsk);
  if ((rk_valid_rset(bh->rk_resource_set)) &&
      (bh->rk_resource_set->rs_disk)) { 
    if ((disk_rsv) &&
	(disk_higher_prio(disk_rsv, 
			bh->rk_resource_set->rs_disk))) {
      bh->rk_resource_set = disk_rsv->rsv_rs;
    } 
  }
  else {
    if (disk_rsv) 
      bh->rk_resource_set = disk_rsv->rsv_rs;
    else 
      bh->rk_resource_set = tsk->rk_resource_set;
  }
  rk_spin_unlock(flags);
}

/* Start sever */
int 
disk_server_start(struct task_struct *tsk)
{
  /* attach process w/ cpu_reserve */	
  if (disk_server_tsk) return RK_ERROR;
  disk_server_tsk = tsk;
  if (css_enable) 
    return resource_set_attach_process(disk_server_resource_set, disk_server_tsk);
  return RK_SUCCESS;
}

int 
disk_server_stop(struct task_struct *tsk)
{
  extern int __rk_resource_set_detach_process(rk_resource_set_t,
					      struct task_struct *);

  if (disk_server_tsk != tsk) return RK_ERROR;
  disk_server_tsk = NULL;
  if (css_enable) 
    return __rk_resource_set_detach_process(disk_server_resource_set, tsk);
  return RK_SUCCESS;
}


/* Get one ticket to run */
void 
disk_server_wait_for_ticket()
{
  if (css_enable)
    disk_cpu_rsv->rsv_ops->wait_on_quota(disk_cpu_rsv);
  else {}; /* allow disk to run w/ full speed */
}


/* register replenish function */
int 
disk_server_replenish_register(void (*f)(rk_reserve_t, unsigned long))
{
  if (driver_replenish_function) return RK_ERROR;
  driver_replenish_function = f;
  return RK_SUCCESS;
}

/* unregister replenish function */
int 
disk_server_replenish_unregister(void)
{
  driver_replenish_function = NULL;
  return RK_SUCCESS;
}

rk_reserve_t
disk_reserve_create(rk_resource_set_t rs, disk_reserve_attr_t disk_attr)
{
	rk_reserve_t	rsv;
	disk_reserve_t	disk;

	if (rs == NULL) return NULL_RESERVE;

	/* check for validity of "rs" first */
	if (!rk_valid_rset(rs)) return NULL_RESERVE;

	if (rs->rs_disk) {
	  /* pre-existing disk reserve; error */
	  return NULL_RESERVE;
	}

#ifdef DEBUG_DISK_RK
	printk("disk_reserve_create: rs(0x%x) size(%lu) t(%lu,%lu) d(%lu,%lu) b(%lu,%lu) mode(%d,%d,%d) \n",
	       (int)rs, (unsigned long) disk_attr->size,	      
	       (unsigned long)disk_attr->period.tv_sec, (unsigned long)disk_attr->period.tv_nsec,
	       (unsigned long)disk_attr->deadline.tv_sec, (unsigned long)disk_attr->deadline.tv_nsec,
	       (unsigned long)disk_attr->blocking_time.tv_sec, (unsigned long)disk_attr->blocking_time.tv_nsec,
	       disk_attr->reserve_type.enf_mode, 
	       disk_attr->reserve_type.rep_mode,
	       disk_attr->reserve_type.sch_mode
	       );
#endif DEBUG_DISK_RK

	/* check period if 0*/
	if ((disk_attr->period.tv_sec==0)&&(disk_attr->period.tv_nsec==0))
	  return NULL_RESERVE;

	/* check capcity if 0 */
	if (disk_attr->size ==0) 
	  return NULL_RESERVE;

	/* check deadline; cannot be greatter than the period at this moment */
	if (timespec_gt(disk_attr->deadline, disk_attr->period)) 
	  return NULL_RESERVE;
	
	/* check for sane start_time value */
	if (disk_attr->start_time.tv_nsec >= NANOSEC) 
	  return NULL_RESERVE;
	

	/* do an admission control test and 
	   update global state for example, disk_current_capacity */
	if (!disk_reserve_admit_request(disk_attr)) {
	  return NULL_RESERVE; 
	}
	
	/* error */

	/* create disk reserve object */
	disk = malloc(sizeof(struct disk_reserve));
	bzero(disk, sizeof(struct disk_reserve));
	memcpy(&(disk->disk_res_attr), disk_attr, 
	       sizeof(disk_reserve_attr_data_t));

	/* add reservation to reservation list */ 
	list_add(&(disk->disk_link), &disk_reserve_head);
	  
	/* calculate disk ticks per capacity */
	{
	  unsigned long long qt, qd;
	  qt = disk_attr->period.tv_sec; qt *= NANOSEC;
	  qt += disk_attr->period.tv_nsec;
	  qd = disk_attr->deadline.tv_sec; qd *= NANOSEC;
	  qd += disk_attr->deadline.tv_nsec;
	  nanosec2tick(&qt, &disk->disk_period_ticks);
	  nanosec2tick(&qd, &disk->disk_deadline_ticks);
	}
	
	disk_size_to_blocks(disk_attr->size,&disk->available_blocks);
	disk->used_blocks = 0;
	disk->prev_used_blocks = 0;
	disk->disk_capacity = disk_capacity(disk_attr->size, disk->disk_period_ticks);

	/* create generic reserve object */
	rsv = rk_reserve_create(rs, RSV_DISK);
	rsv->rsv_state = RSV_IS_NULL;
	rsv->rsv_rsv = disk;
	rsv->rsv_ops = &disk_reserve_ops;
	rsv->rsv_reserve_param = disk_attr->reserve_type;
	disk->rsv = rsv;


	/* create a timer for it */
	/* XXX: we must later have one timer per resource instance in set */
	rk_replenish_timer_create(rsv, disk_attr->start_time);

	return rsv;		/* success */
}

static int
disk_reserve_destroy(rk_reserve_t rsv)
{
	disk_reserve_t	disk = rsv->rsv_rsv;

#ifdef DEBUG_DISK_RK
	printk("disk_reserve_destroy: rsv(0x%x) disk(0x%x)\n",
	       (int)rsv, (int)disk);
#endif DEBUG_DISK_RK

	/* destroy timer */
	rk_replenish_timer_destroy(rsv);

	/* do update global state for admission control 
	   (disk_current_capacity) */
	disk_reserve_adm_cancel_request(disk);

	list_del(&disk->disk_link);
	INIT_LIST_HEAD(&disk->disk_link);		/* for sure */
	kfree(disk);
	rsv->rsv_rsv = NULL_RESERVE;

	rk_reserve_destroy(rsv);
	return RK_SUCCESS;	/* success */
}

/*
 * Replenish & Enforce a reserve
 */
static void
disk_reserve_replenish(rk_reserve_t rsv, cpu_tick_t period, cpu_tick_t start_ticks)
{
	unsigned long flags;
        disk_reserve_t	disk = rsv->rsv_rsv;
	*period = disk->disk_period_ticks;
	
	/* refill the capacity */
	switch (rsv->rsv_reserve_param.rep_mode) {
	case RSV_SOFT:
	case RSV_FIRM:
	  /* At this moment, treat RSV_SOFT and RSV_FIRM the same.
	     If the resource usage exceeds the reserved amount, the server  sets rsv_state
	     to RSV_IS_DEPLETED state and serves the rest of requests as best_effort requests. */
	  /* get full replenishment available_bocks = block(size)*/
	  break;
	case RSV_HARD:
	  /* If the resource usage exceeds the reserved amount, the server  sets rsv_state
	     to RSV_IS_DEPLETED state and drops the rest of requests. */
	  /* get full replenishment available_bocks = block(size)*/
	  break;
	  break;
	default:
	  printk("replenish: unknown rep mode!\n");
	  break;
	}

	/* reset the used blocks */
	rk_spin_lock(flags);
	disk->prev_used_blocks = disk->used_blocks;
	disk->used_blocks = 0;
	/* set up the next deadline  */
	disk->disk_current_deadline_ticks = *start_ticks + disk->disk_deadline_ticks;
	rk_spin_unlock(flags);

	/* clear the deplete state */
	if (rsv->rsv_state & RSV_IS_DEPLETED)
	  rsv->rsv_state &= ~RSV_IS_DEPLETED;
	
	/* if replenish function is installed */
	if (driver_replenish_function) {
	  disk_reserve_t disk = rsv->rsv_rsv;
	  unsigned long count = disk->available_blocks - disk->used_blocks;
	  if (count) 
	    driver_replenish_function(rsv, count);
	}
}

static void
disk_reserve_enforce(rk_reserve_t rsv)
{
	/* update state */
	if (!(rsv->rsv_state & RSV_IS_DEPLETED)) {
#ifdef DEBUG_DISK_RK
	  printk("enforce rsv(%0x) \n", (int) rsv);
#endif DEBUG_DISK_RK
	  rsv->rsv_state |= RSV_IS_DEPLETED;
	}
}

/* update the used blocks; need to convert size in KBytes to blocks */
static void
disk_reserve_update_account(rk_reserve_t rsv, unsigned long blocks) {
        disk_reserve_t	disk = rsv->rsv_rsv;
	unsigned long flags;

#ifdef _DEBUG_DISK_RK
	printk("update account rsv (%x) used (%d) new (%d)\n", (unsigned int) rsv, (int) disk->used_blocks, (int) blocks);
#endif _DEBUG_DISK_RK
	
	/* update the used blocks */
	rk_spin_lock(flags);
	disk->used_blocks += blocks;	  
	rk_spin_unlock(flags);	
}

/* ask for the available quota */
static void
disk_reserve_quota_query(rk_reserve_t rsv, unsigned long *blocks) {
        disk_reserve_t	disk = rsv->rsv_rsv;
	long tmp;
	unsigned long flags;
	rk_spin_lock(flags);
	tmp = disk->available_blocks - disk->used_blocks;
	rk_spin_unlock(flags);	

	if ( tmp > 0)
	  *blocks = (unsigned long) tmp;
	else *blocks =0;
}

/* check if it's higher priority */
int
disk_higher_prio(rk_reserve_t rsv1, rk_reserve_t rsv2)
{
  int retval;
  unsigned long flags;
  disk_reserve_t disk1 = rsv1->rsv_rsv;
  disk_reserve_t disk2 = rsv2->rsv_rsv;

  /* check validity of reserves */
  if (rsv1 == NULL_RESERVE) return FALSE;
  if (rsv2 == NULL_RESERVE) return TRUE;

  switch(disk_reserves_scheduling_policy) {
  case EARLIEST_DEADLINE_FIRST:
    rk_spin_lock(flags);
    if (disk1->disk_current_deadline_ticks < 
	disk2->disk_current_deadline_ticks) 
      retval = TRUE;
    else retval = FALSE;
    rk_spin_unlock(flags);
    return retval;

  default:
    break;
  }
  return FALSE; /* don't want to create preemption */
}

/*
 * admission control for DISK reserves
 */
static int disk_reserve_admit_request(disk_reserve_attr_t disk_attr)
{
  unsigned long long qt, qd;
  cpu_tick_data_t period_ticks, deadline_ticks;
  disk_capacity_t capacity;


  switch (disk_reserves_scheduling_policy) {
  case EARLIEST_DEADLINE_FIRST:
    qt = disk_attr->period.tv_sec; qt *= NANOSEC;
    qt += disk_attr->period.tv_nsec;
    qd = disk_attr->deadline.tv_sec; qd *= NANOSEC;
    qd += disk_attr->deadline.tv_nsec;
    nanosec2tick(&qt, &period_ticks);
    nanosec2tick(&qd, &deadline_ticks);
    /* Make sure that task deadline is larger than 3 times of server period */
    if (deadline_ticks <= 3*disk_server_period_ticks) {
      printk("disk_reserve_create: deadline is too short \n");
      return FALSE;
    }
    
    /* do the admission control */
#ifdef APPLE_DEBUG_RK
    printk("disk_capacity = %d size = %d period_ticks = %d \n", disk_capacity(disk_attr->size, period_ticks), disk_attr->size, period_ticks );
    printk("old_current_capacity = %d \n", disk_current_capacity );
    printk("admit request ..new current capacity = %d \n",  
	   disk_current_capacity +disk_capacity(disk_attr->size, period_ticks));
#endif APPLE_DEBUG_RK 

    capacity = disk_current_capacity +  
      disk_capacity(disk_attr->size, period_ticks);

    if (capacity > 100) {
      printk("disk_reserve_create: the system is overloaded \n");
      return FALSE;
    }
    
    break;
  default:
    printk("disk_reserve_create : scheduling policy is not set properly \n");
    return FALSE;
  }

  /* Compute the server capacity */
  /* I use an approximation that the number of blocks the disk
   * server needs to read is equal to the total disk utilzation 
   * times the server_period_ticks
   */
#ifdef CSS_CONFIG
  {
    disk_block_t tmp;
    

#if 0
    tmp = ((capacity * disk_server_period_ticks)/
	   (ONE_BLOCK_DRIVER+ONE_BLOCK_IO ));
    tmp = (tmp%100)? tmp/100+1 : tmp/100;             
#endif
    tmp = server_capacity_computation_add(disk_attr);

#ifdef APPLE_DEBUG_RK
    printk("compute how many blocks we need to read = %d \n", tmp);
#endif APPLE_DEBUG_RK

#ifdef DEBUG_DISK_RK
    printk("disk_reserve_admit_request: new server_blocks %d, old server_blocks %d \n", (int) tmp, (int) disk_server_blocks);
#endif DEBUG_DISK_RK
    if (tmp > disk_server_blocks) {
      /* Ask for more resource */
 	disk_cpu_attr.compute_time.tv_sec = 0;
	tick2nanosecond((ONE_BLOCK_DRIVER * tmp), 
			&disk_cpu_attr.compute_time.tv_nsec);
	disk_cpu_attr.blocking_time.tv_sec = 0;
	tick2nanosecond((ONE_BLOCK_IO * tmp)
			, &disk_cpu_attr.blocking_time.tv_nsec);
	
#ifdef APPLE_DEBUG_RK
	printk("new server_blocks = %d\n", tmp);
	printk("neeed compute time = %lu nsec, blocking time = %lu nsec \n",
	       (unsigned long) disk_cpu_attr.compute_time.tv_nsec,
	       (unsigned long) disk_cpu_attr.blocking_time.tv_nsec);
#endif APPLE_DEBUG_RK
	disk_cpu_attr.quota_tickets = tmp;
	disk_cpu_attr.ticket_age = (ONE_BLOCK_DRIVER+ONE_BLOCK_IO)*tmp;

#ifdef APPLE_DEBUG_RK
	printk("ticket age = %lu cpu_ticks\n", disk_cpu_attr.ticket_age);
#endif APPLE_DEBUG_RK

	{ /* Ask more resource */
#ifdef CPU_RSV_CTL
	  extern int cpu_reserve_ctl(rk_reserve_t, cpu_reserve_attr_t);
	  if (cpu_reserve_ctl(disk_cpu_rsv, &disk_cpu_attr)) {
	    printk("disk_reserve_create: error! not enough cpu is available \n");
	    return FALSE;
	  }
	  else {
	    printk("CSS Adjust SUCCESS rs = 0x%x SERVER_BLOCK = %d \n", 
		   (unsigned int) disk_server_resource_set,
		   (int) tmp);
	    disk_current_capacity = capacity;
	    disk_server_blocks = tmp;
#ifdef DEBUG_DISK_RK
	    printk("disk_reserve_create: completed , disk_current_capacity = %d, server_blocks = %d \n", (int) disk_current_capacity, (int) disk_server_blocks);
#endif DEBUG_DISK_RK
	    return TRUE;
	  }
#else /* CPU_RSV_CTL */
	  extern rk_reserve_t cpu_reserve_create(rk_resource_set_t,
						 cpu_reserve_attr_t);
	  rk_resource_set_t         tmp_disk_server_resource_set;
	  rk_reserve_t              tmp_disk_cpu_rsv;
	  
	  tmp_disk_server_resource_set = rk_resource_set_create("disktmp");
	  if (!(tmp_disk_cpu_rsv = cpu_reserve_create(tmp_disk_server_resource_set, &disk_cpu_attr))) {
	    /* new reservation is not granted */
	    printk("disk_reserve_create: error! not enough cpu is available \n");
	    rk_resource_set_destroy(tmp_disk_server_resource_set);
	    return FALSE;
	  } else {
	    /* attach new rs to the server */
	    int retval= 111;
	    if (disk_server_tsk && css_enable) 
	      retval = resource_set_attach_process(tmp_disk_server_resource_set, disk_server_tsk);
	    printk("NEW CSS RS = 0x%x disk_server_tsk = 0x%x attachret =%d\n", (unsigned int) tmp_disk_server_resource_set, (unsigned int) disk_server_tsk,retval);
	    /* destroy the old rs */
	    rk_resource_set_destroy(disk_server_resource_set);
	    printk("CSS DESTROY OLD CSS SUCCESS \n");
	    disk_current_capacity = capacity;
	    disk_server_blocks = tmp;
	    disk_cpu_rsv = tmp_disk_cpu_rsv;
	    disk_server_resource_set = tmp_disk_server_resource_set;
	    rk_resource_set_set_name(disk_server_resource_set,"disk");
#ifdef DEBUG_DISK_RK
	    printk("disk_reserve_create: completed , disk_current_capacity = %d, server_blocks = %d \n", (int) disk_current_capacity, (int) disk_server_blocks);
#endif DEBUG_DISK_RK
	    return TRUE;
	  }
#endif CPU_RSV_CTL
	} /* end of asking more resource */
    }
    else {
      printk("CSS use same server_blocks = %d \n", (int) disk_server_blocks);
      disk_current_capacity = capacity;
#ifdef DEBUG_DISK_RK
      printk("disk_reserve_create: completed , disk_current_capacity = %d, server_blocks = %d \n", (int) disk_current_capacity, (int) disk_server_blocks);
#endif DEBUG_DISK_RK
    }
  }
  
#endif CSS_CONFIG
  return TRUE;
}

/* 
 * update the state of the admission due to request cancellation
 */
static int disk_reserve_adm_cancel_request(disk_reserve_t disk)
{
  disk_reserve_attr_t disk_attr = &(disk->disk_res_attr);
  unsigned long long qt, qd;
  cpu_tick_data_t period_ticks, deadline_ticks;

  switch (disk_reserves_scheduling_policy) {
  case EARLIEST_DEADLINE_FIRST:
    qt = disk_attr->period.tv_sec; qt *= NANOSEC;
    qt += disk_attr->period.tv_nsec;
    qd = disk_attr->deadline.tv_sec; qd *= NANOSEC;
    qd += disk_attr->deadline.tv_nsec;
    nanosec2tick(&qt, &period_ticks);
    nanosec2tick(&qd, &deadline_ticks);
#ifdef DEBUG_DISK_RK
    printk("disk_reserve_destroy (EDF) : rsv_capacity to destroy  =%d old_current_capacity = %d \n", (int) disk_capacity(disk_attr->size, period_ticks), (int) disk_current_capacity);
#endif DEBUG_DISK_RK
    
    disk_current_capacity -= disk_capacity(disk_attr->size, period_ticks);
    break;
  default :
    /* shouldn't happen here */
#ifdef DEBUG_DISK_RK
    printk("disk_reserve_destroy : scheduling policy is not set properly \n");
#endif DEBUG_DISK_RK
    return FALSE;
  }
#ifdef CSS_CONFIG
  {
    disk_block_t tmp;
    
#if 0
    tmp = ((disk_current_capacity * disk_server_period_ticks)/
	   (ONE_BLOCK_DRIVER+ONE_BLOCK_IO));
    tmp = (tmp%100)? tmp/100+1 : tmp/100;             
#endif 
    tmp = server_capacity_computation_del(disk);
    printk("CSS delete compute how many blocks we need to read = %d \n", (int) tmp);

    if (tmp < disk_default_reserved_blocks) tmp = disk_default_reserved_blocks;
    if (tmp < disk_server_blocks) {
      /*  Return excess resources */
 	disk_cpu_attr.compute_time.tv_sec = 0;
	tick2nanosecond((ONE_BLOCK_DRIVER * tmp), 
			&disk_cpu_attr.compute_time.tv_nsec);
	disk_cpu_attr.blocking_time.tv_sec = 0;
	tick2nanosecond((ONE_BLOCK_IO * tmp)
			, &disk_cpu_attr.blocking_time.tv_nsec);
	disk_cpu_attr.quota_tickets = tmp;
	disk_cpu_attr.ticket_age = (ONE_BLOCK_DRIVER+ONE_BLOCK_IO)*tmp;
	{
	  /* return excess resources */
#ifdef CPU_RSV_CTL
	  extern int cpu_reserve_ctl(rk_reserve_t, cpu_reserve_attr_t);
	  if (cpu_reserve_ctl(disk_cpu_rsv, &disk_cpu_attr)) {
	    printk("disk_reserve_destroy: error! cannot return the resources \n");
	  }
	  else {
	    printk("CSS Adjust SUCCESS rs = 0x%x SERVER_BLOCK = %d \n", 
		   (unsigned int) disk_server_resource_set,
		   (int) tmp);
	    disk_server_blocks = tmp;
	  }
#else /* CPU_RSV_CTL */
	  extern rk_reserve_t cpu_reserve_create(rk_resource_set_t,
						 cpu_reserve_attr_t);
	  rk_resource_set_t         tmp_disk_server_resource_set;
	  rk_reserve_t              tmp_disk_cpu_rsv;
	  
	  tmp_disk_server_resource_set = rk_resource_set_create("disktmp");
	  if (!(tmp_disk_cpu_rsv = cpu_reserve_create(tmp_disk_server_resource_set, &disk_cpu_attr))) {
#ifdef DEBUG_DISK_RK
	    printk("disk_reserve_destroy: error! cannot return the resources \n");
#endif DEBUG_DISK_RK
	    rk_resource_set_destroy(tmp_disk_server_resource_set);
	  } else {
	    int retval=111;
	    /* attach new rs to the server */
	    if (disk_server_tsk && css_enable) 
	      retval =resource_set_attach_process(tmp_disk_server_resource_set, disk_server_tsk);
	    printk("NEW CSS RS = 0x%x disk_server_tsk = 0x%x attachret = %d\n", (unsigned int) tmp_disk_server_resource_set, (unsigned int) disk_server_tsk,retval);
	    /* destroy the old rsv */
	    rk_resource_set_destroy(disk_server_resource_set);
	    printk("CSS DESTROY OLD CSS SUCCESS \n");
	    disk_server_blocks = tmp;
	    disk_cpu_rsv = tmp_disk_cpu_rsv;
	    disk_server_resource_set = tmp_disk_server_resource_set;
	    rk_resource_set_set_name(disk_server_resource_set,"disk");
	  }
#endif CPU_RSV_CTL
	} /* end of returning excess resources */
    }
    else {
	printk("CSS use the same server_block = %d \n" , (int) disk_server_blocks);
    }
  }
#ifdef DEBUG_DISK_RK
  printk("disk_reserve_destroy: current capacity = %d, current disk_server_blocks = %d \n", 
	 (int) disk_current_capacity, (int)disk_server_blocks);
#endif DEBUG_DISK_RK
  
#endif CSS_CONFIG
  return disk_current_capacity;
}

/* 
 * compute the disk capcacity of the request 
 */
static int
disk_capacity(unsigned long size, cpu_tick_data_t period_ticks)
{
  disk_block_t blocks;
  cpu_tick_data_t one_block_ticks;

  disk_size_to_blocks(size, &blocks);
#ifdef APPLE_DEBUG_RK
  printk("disk_capacity convert size %lu bytes to %d blocks \n", (unsigned long) size, blocks);
#endif APPLE_DEBUG_RK
  /* elapse time to access one block (driver overhead + I/O)  */
  one_block_ticks = ONE_BLOCK_DRIVER + ONE_BLOCK_IO;
  return (blocks*one_block_ticks*100)/period_ticks + 1;
}

/* convert size in KB to number of blocks */
static void
disk_size_to_blocks(unsigned long size, disk_block_t *blocks)
{
  if (size % BLKSIZE) *blocks = size/BLKSIZE + 1;
  else *blocks = size/BLKSIZE;
}
/* 
 * compute number of bocks can read in the duration w/ full capacity 
 */
inline int 
max_disk_blocks(struct timespec time) 
{
  unsigned long long duration;
  cpu_tick_data_t one_block_ticks, duration_ticks;

  /* elapse time to access one block (driver overhead + I/O)  */
  one_block_ticks = ONE_BLOCK_DRIVER+ONE_BLOCK_IO;
  
  duration = time.tv_sec; duration *= NANOSEC;
  duration += time.tv_nsec;
  nanosec2tick(&duration, &duration_ticks);
    
  return (duration_ticks/one_block_ticks);
}

/*
 * Linux-specific
 */
#ifdef	linux

/*
 * Linux proc file system interface
 */

asmlinkage int 
sys_rk_disk_set_css(int on)
{
  extern int __rk_resource_set_detach_process(rk_resource_set_t,
					      struct task_struct *);
  if (on && !css_enable) { /* enable css */
    printk("enable css \n");
    css_enable = 1;
    if (disk_server_tsk) 
      return resource_set_attach_process(disk_server_resource_set, disk_server_tsk);
  }
  else if (!on & css_enable) { /* disable css */
    printk("disable css \n");
    css_enable = 0;
    if (disk_server_tsk) 
      return __rk_resource_set_detach_process(disk_server_resource_set, disk_server_tsk);
  }
  return RK_SUCCESS;
}

asmlinkage rk_reserve_t
sys_rk_disk_reserve_create(rk_resource_set_t rs, disk_reserve_attr_t disk_attr)
{
        disk_reserve_attr_data_t local_attr;

#ifdef DEBUG_DISK_RK
  printk("disk_rsv_create: rs(0x%x)\n", (int)rs);
#endif DEBUG_DISK_RK

	/* check region before access */
	if ((!disk_attr) || (verify_area(VERIFY_READ, disk_attr, 
					sizeof(disk_reserve_attr_data_t)))) {
	    return NULL_RESERVE;
	}
	if (copy_from_user(&local_attr, disk_attr, 
			   sizeof(disk_reserve_attr_data_t))) {
	  return NULL_RESERVE;
	}

	return disk_reserve_create(rs, &local_attr);
}

asmlinkage int
sys_rk_disk_reserve_delete(rk_resource_set_t rs)
{
        rk_reserve_t rsv;

#ifdef DEBUG_DISK_RK
  printk("disk_rsv_delete: rs(0x%x)\n", (int)rs);
#endif DEBUG_DISK_RK
	/* check validity of "rs" first */
	if ((rs == NULL) || (!rk_valid_rset(rs))) return -EINVAL; 
	rsv = rs->rs_disk;
	if (rsv == NULL_RESERVE) return -EINVAL;

	rs->rs_disk=NULL_RESERVE;
	return disk_reserve_destroy(rsv);
}

int disk_reserve_delete(rk_resource_set_t rs, rk_reserve_t rsv) 
{
  /* check validity of "rs" first */
  if ((rs == NULL) || (!rk_valid_rset(rs))) return  RK_ERROR; 
  if ((rsv == NULL_RESERVE) || (rs->rs_disk != rsv)) return  RK_ERROR;
  rs->rs_disk = NULL_RESERVE;
  return disk_reserve_destroy(rsv);
}

asmlinkage int
sys_rk_disk_reserve_get_attr(rk_reserve_t rsv, 
			      disk_reserve_attr_t disk_params)
{
  disk_reserve_t disk;

#ifdef DEBUG_RK
  printk("disk_rsv_get_attr: rsv(0x%x)\n", rsv);
#endif DEBUG_RK

  /* check region before access */
  disk = rsv->rsv_rsv;
  if ((!disk) || (verify_area(VERIFY_WRITE, disk_params, 
			     sizeof(disk_reserve_attr_data_t)))) {
    return -EFAULT;
  }
  if (copy_to_user(disk_params, &(disk->disk_res_attr),
		   sizeof(disk_reserve_attr_data_t))) {
    return -EINVAL;
  }

  return RK_SUCCESS;
}

#endif linux

/* The disk reserve for measurement only.
 * This is for default resoruce set.
 * No need to apply admission control to these reserves.
 * We apply the 100% capacity reserve.
 * The parameter duration is the measurement granularity we want
 * to keep track the cpu usage.
 */
rk_reserve_t disk_reserve_dummy_create(rk_resource_set_t rs, time_t duration)
{
  disk_reserve_t disk;
  disk_reserve_attr_t disk_attr;
  rk_reserve_t rsv;
  /* create disk reserve object */
  disk = malloc(sizeof(struct disk_reserve));
  bzero(disk, sizeof(struct disk_reserve));
  disk_attr = &(disk->disk_res_attr);
  disk_attr->period.tv_sec = duration;
  disk_attr->period.tv_nsec = 0;
  disk_attr->deadline.tv_sec = duration;
  disk_attr->deadline.tv_nsec = 0;
  disk_attr->blocking_time.tv_sec = 0;
  disk_attr->blocking_time.tv_nsec = 0;
  disk_attr->size = max_disk_blocks(disk_attr->period) * BLKSIZE;
  /* start it, whenever the system is ready */
  disk_attr->start_time.tv_sec = 0;
  disk_attr->start_time.tv_nsec = 10000000;
  disk_attr->reserve_type.sch_mode = RSV_SOFT;
  disk_attr->reserve_type.enf_mode = RSV_SOFT;
  disk_attr->reserve_type.rep_mode = RSV_SOFT;

  //disk_attr->reserve_type.sch_mode = RSV_HARD;
  //disk_attr->reserve_type.enf_mode = RSV_HARD;
  //disk_attr->reserve_type.rep_mode = RSV_HARD;

  /* create generic reserve object */
  rsv = rk_reserve_create(rs, RSV_DISK);
  rsv->rsv_state = RSV_IS_NULL;
  rsv->rsv_rsv = disk;
  rsv->rsv_ops = &disk_reserve_ops;
  rsv->rsv_reserve_param = disk_attr->reserve_type;
  disk->rsv = rsv;
  
  
  /* create a timer for it */
  /* XXX: we must later have one timer per resource instance in set */
  rk_replenish_timer_create(rsv, disk_attr->start_time);

  return rsv;
       
}


static inline void tick2nanosecond(long tick, long *ns) {
  cpu_tick_data_t cpu_tick;
  cpu_tick_data_t us;
  
  cpu_tick = (cpu_tick_data_t) tick;
  tick2usec(&cpu_tick, &us);
  *ns = (us) *1000;
}

#if 1
void rk_tick2nanosecond(long tick, long *ns) {
  cpu_tick_data_t cpu_tick;
  cpu_tick_data_t us;
  
  cpu_tick = (cpu_tick_data_t) tick;
  tick2usec(&cpu_tick, &us);
  *ns = (us) *1000;
}
#endif

void
rk_enable_hook_disk_reserve(void)
{
  if (!rk_disk_reserve_hook) 
    rk_disk_reserve_hook =  rk_disk_reserve_attach_bh;
  if (!rk_disk_readahead_max_hook)
      rk_disk_readahead_max_hook = rk_disk_readahead_max;
  if (!rk_disk_realtime_req_hook)
    rk_disk_realtime_req_hook = rk_disk_realtime_req;
#if 1
  if (!rk_tick2nanosecond_hook)
    rk_tick2nanosecond_hook = rk_tick2nanosecond;
#endif
}

void
rk_disable_hook_disk_reserve(void)
{
  if (rk_disk_reserve_hook) 
    rk_disk_reserve_hook = (void*)0;
  if (rk_disk_readahead_max_hook) 
    rk_disk_readahead_max_hook = (void*)0;
  if (rk_disk_realtime_req_hook)
    rk_disk_realtime_req_hook = (void*)0;
#if 1
  if (rk_tick2nanosecond_hook)
    rk_tick2nanosecond_hook = (void*)0;
#endif
	
}

static disk_block_t server_capacity_computation_add(disk_reserve_attr_t disk_attr)
{
  struct list_head *ptr1, *ptr2;
  disk_reserve_t r,i;
  disk_block_t max_blocks, newblocks;
  cpu_tick_data_t newdeadline,newperiod;
  unsigned long long qt, qd;
  disk_block_t b,blocks;
  /* perform the "completion time test" */
  /* for the EDF case; 
   * We can use the completion time test for DM to prove the schedulability
   * of EDF.
   * Therefore, the number of blocks the server needs to run
   * in each period equals 
   *        UPPER[ (sum of (BLi* UPPER[D/Ti]))* Ts/D] 
   * Where D is the deadline of targe task
   *       Ti is the period of task that has shorter deadline
   *       BLi is the number of blocks of task i 
   *       Ts is the serve period
   * Do this test to all target task that has deadline larger or equal to the new task.
   * return the maximum capacity 
   */
#ifdef CSS_DEBUG
  printk("CSS computation_add for deadline (%lu, %lu,%lu) begin \n", 
	 (unsigned long) disk_attr->size,
	 (unsigned long) disk_attr->deadline.tv_sec,
	 (unsigned long) disk_attr->deadline.tv_nsec);
#endif CSS_DEBUG
  max_blocks = 0;
  qt = disk_attr->period.tv_sec; qt *= NANOSEC;
  qt += disk_attr->period.tv_nsec;
  qd = disk_attr->deadline.tv_sec; qd *= NANOSEC;
  qd += disk_attr->deadline.tv_nsec;
  nanosec2tick(&qt, &newperiod);
  nanosec2tick(&qd, &newdeadline);
  disk_size_to_blocks(disk_attr->size,&newblocks);

  for (ptr1 = disk_reserve_head.next; 
       ptr1 != &disk_reserve_head; 
       ptr1 = ptr1->next) {
    r = disk_reserve_entry(ptr1);
    if (r->disk_deadline_ticks >= newdeadline) {
      b=0;
      for (ptr2 = disk_reserve_head.next; 
	   ptr2 != &disk_reserve_head; 
	   ptr2 = ptr2->next) {
	i = disk_reserve_entry(ptr2);
	if (i->disk_deadline_ticks <= r->disk_deadline_ticks) 
	  b += (i->available_blocks * UPPER( r->disk_deadline_ticks, i->disk_period_ticks));
      }
      /* include the new task */
      disk_size_to_blocks(disk_attr->size,&newblocks);
      b += (newblocks * UPPER(  r->disk_deadline_ticks, newperiod));
      blocks = UPPER( (b* disk_server_period_ticks) , r->disk_deadline_ticks);
      if (blocks > max_blocks) max_blocks = blocks;
    } 
  }
  /* test for the new task */
  {
    b = newblocks;
    for (ptr2 = disk_reserve_head.next; 
	 ptr2 != &disk_reserve_head; 
	 ptr2 = ptr2->next) {
      i = disk_reserve_entry(ptr2);
     if (i->disk_deadline_ticks <= newdeadline) 
	b += (i->available_blocks * UPPER( newdeadline, i->disk_period_ticks));
    }
    blocks = UPPER( b * disk_server_period_ticks,  newdeadline);
    if (blocks > max_blocks) max_blocks = blocks;
  }
#ifdef CSS_DEBUG
  printk("CSS computation_add end max_blocks = %lu \n", (unsigned long) max_blocks);
#endif CSS_DEBUG
  return max_blocks;
}

static disk_block_t server_capacity_computation_del(disk_reserve_t disk)
{
  struct list_head *ptr1, *ptr2;
  disk_reserve_t r,i;
  disk_block_t max_blocks;
  disk_block_t b,blocks;
  /* do the computation by ignore the data in disk_attr */
  /* look at server_capacity_computation_add for more detail */
  max_blocks = 0;
  for (ptr1 = disk_reserve_head.next; 
       ptr1 != &disk_reserve_head; 
       ptr1 = ptr1->next) {
    r = disk_reserve_entry(ptr1);
    
    if ((disk!=r) 
	/* &&(r->disk_deadline_ticks <= disk->disk_deadline_ticks) */) {
      b=0;
      for (ptr2 = disk_reserve_head.next; 
	   ptr2 != &disk_reserve_head; 
	   ptr2 = ptr2->next) {
	i = disk_reserve_entry(ptr2);
	if((i!=disk) && (i->disk_deadline_ticks <= r->disk_deadline_ticks)) {
	  b += (i->available_blocks * UPPER( r->disk_deadline_ticks, i->disk_period_ticks));
	}
      }
    
      blocks = UPPER( (b* disk_server_period_ticks) , r->disk_deadline_ticks);
      if (blocks > max_blocks) max_blocks = blocks;
    }
  }

  return max_blocks;
}
