/* 
 * 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.
 */

#include <rk/rk_linux.h>
#include <rk/rk_error.h>
#include <rk/rk.h>
#include <rk/timespec.h>
#include <linux/time.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <rk/css.h>

#define X_SLACK 50 /*  proportion of disk slack over the summation of bf+af slack (in percents)*/
static inline void tick2timespec(long,struct timespec *);


/* just for temporary */
static void rk_disk_parameter_load(disk_param_t disk_param) {
  unsigned long long driver_bf_overhead;
  unsigned long long driver_af_overhead;
  unsigned long long driver_io_overhead;
  cpu_tick_data_t           disk_driver_bf_overhead; 
  cpu_tick_data_t           disk_driver_af_overhead;
  cpu_tick_data_t           disk_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_param->invoke_ticks = 60000;
  disk_param->return_ticks = 5000;
  disk_param->read_ahead_ticks = 18000;
  disk_param->user_copy_ticks = 60000;
  disk_param->one_block_ticks = (disk_driver_bf_overhead+
				 disk_driver_af_overhead+
				 disk_driver_io_overhead);
  disk_param->blocksize=4096;
  disk_param->server_period_ticks = 20000000;
}

/* create the reservation for access a disk w/ cpu needed by the application
 * before and after accessing the disk 
 */
int 
css_disk_cpu_reserve_create(rk_resource_set_t rs,css_disk_cpu_reserve_attr_t css_attr)
{
  cpu_tick_data_t cpu_bf_ticks, cpu_af_ticks, disk_ticks;
  cpu_tick_data_t deadline_ticks;
  cpu_tick_data_t slack_ticks, ticks;
  unsigned long blocks;
  rk_reserve_t  disk_rsv, bf_cpu_rsv, af_cpu_rsv;
  disk_param_data_t disk_param;
  disk_reserve_attr_data_t disk_attr;
  cpu_reserve_attr_data_t cpu_bf_attr, cpu_af_attr;

  rk_disk_parameter_load(&disk_param);
  {
    cpu_capacity_quad_t	bf, af;
    bf = css_attr->compute_bf.tv_sec; bf*=NANOSEC;
         bf+= css_attr->compute_bf.tv_nsec;
    af = css_attr->compute_af.tv_sec; af*=NANOSEC;
         af+= css_attr->compute_af.tv_nsec;
    nanosec2tick(&bf, &cpu_bf_ticks);
    nanosec2tick(&af, &cpu_af_ticks);
#ifdef DEBUG_CSS
    printk("CSS bf_access = %lu ns, %lu ticks \n", (unsigned long )bf, (unsigned long) cpu_bf_ticks);
    printk("CSS af_access =  %lu ns, %lu ticks \n", (unsigned long )af, (unsigned long) cpu_af_ticks);
#endif DEBUG_CSS
  }
  cpu_bf_ticks += disk_param.invoke_ticks;
  cpu_af_ticks += disk_param.return_ticks;

  /* At this moment, we assign the slack time to cpu_reserve_bf
     and cpu_reserve_af under the constraints that about 50% of total 
     slack time goes to disk. The rest slack time is shared 
     proportionally  to the ratio of cpu_bf_ticks and cpu_af_ticks. 
     More flexible and adaptive slack time assignment should be 
     determined later*/

  /* find the number of blocks */
  if (css_attr->size%disk_param.blocksize) 
    blocks = (css_attr->size/disk_param.blocksize) +1;
  else blocks = css_attr->size/disk_param.blocksize;

#ifdef DEBUG_CSS
  printk("CSS disk_attr.size = %lu , blocks = %d \n", (unsigned long) css_attr->size, blocks);
#endif DEBUG_CSS
  /* Compute the least elapse time for disk to get data */
  disk_ticks = blocks * disk_param.one_block_ticks;
#ifdef DEBUG_CSS
  printk("disk_ticks = %lu (%d * %lu) \n",
	 (unsigned long) disk_ticks, blocks,
	 (unsigned long) disk_param.one_block_ticks);
#endif DEBUG_CSS
  /* Compute the least elapse time for user to get data from disk requests */
printk("old cpu_af_ticks = %lu \n", (unsigned long) cpu_af_ticks);
  cpu_af_ticks += blocks * disk_param.user_copy_ticks;
printk("cpu_af_ticks = %lu (%d * %lu) \n",
       (unsigned long) cpu_af_ticks, (int) blocks,
       (unsigned long) disk_param.user_copy_ticks);
  /* Compute the overhead to spawn read ahead requests */
printk("old cpu_bf_ticks = %lu \n", (unsigned long) cpu_bf_ticks);
  cpu_bf_ticks += blocks * disk_param.read_ahead_ticks;
printk("cpu_bf_ticks = %lu (%d * %lu) \n", 
       (unsigned long) cpu_bf_ticks, (int) blocks , 
       (unsigned long) disk_param.read_ahead_ticks);
  /* Compute the total slack time */
  {
    cpu_capacity_quad_t d;
    d = css_attr->deadline.tv_sec; d*=NANOSEC;
    d+= css_attr->deadline.tv_nsec;
    nanosec2tick(&d, &deadline_ticks);
#ifdef DEBUG_CSS
    printk("CSS deadline = (%lu,%lu)  %lu ns %lu ticks\n", 
	   (unsigned long) css_attr->deadline.tv_sec,
	   (unsigned long)css_attr->deadline.tv_nsec,
	   (unsigned long)d,
	   (unsigned long)deadline_ticks);
#endif DEBUG_CSS
  }
  slack_ticks = deadline_ticks-disk_ticks-cpu_bf_ticks-cpu_af_ticks;
#ifdef DEBUG_CSS
  printk("CSS total slack_ticks = %lu deadline_ticks = %lu disk_ticks = %lu bf = %lu af = %lu\n", (unsigned long) slack_ticks,
	 (unsigned long)deadline_ticks,
	 (unsigned long)disk_ticks,
	 (unsigned long)cpu_bf_ticks,
	 (unsigned long)cpu_af_ticks);
#endif DEBUG_CSS  
  /* Compute disk slack for disk and create the disk reservation */
  //  ticks = slack_ticks/2;
  ticks = (((disk_ticks + ((X_SLACK * slack_ticks)/100 ))/  disk_param.server_period_ticks ) *  disk_param.server_period_ticks  ) - disk_ticks;
#ifdef DEBUG_CSS
  printk("CSS disk slack = %lu X_SLACK = %d \%\n", (unsigned long) ticks, X_SLACK );
#endif DEBUG_CSS
  disk_attr.size = css_attr->size;
  disk_attr.period = css_attr->period;
  {
    cpu_tick_data_t t;
    t = ticks + disk_ticks;
    tick2timespec(t, &disk_attr.deadline);
  }
  disk_attr.blocking_time.tv_sec = 0;
  disk_attr.blocking_time.tv_nsec = 0;
  if ((css_attr->start_time.tv_sec == 0) &&
      (css_attr->start_time.tv_nsec == 0)) {
    struct timespec onesecond;
    rk_cur_time(&css_attr->start_time);
    /* add delay for 0.5second  */
    onesecond.tv_sec = 0;
    onesecond.tv_nsec = 500000000;
    timespec_add(css_attr->start_time,onesecond);
  }
  disk_attr.start_time = css_attr->start_time;
  disk_attr.reserve_type= css_attr->reserve_type;
#ifdef DEBUG_CSS
	printk("CSS reserve disk size = %lu bytes in period(%lu,%lu) deadline(%lu,%lu) \n", disk_attr.size, disk_attr.period.tv_sec, disk_attr.period.tv_nsec, disk_attr.deadline.tv_sec, disk_attr.deadline.tv_nsec);
#endif DEBUG_CSS
  
  /* Computer slack and create the CPU reservation for compute_bf */
  slack_ticks -= ticks;
#ifdef DEBUG_CSS
  printk("CSS slack left = %lu \n", (unsigned long) slack_ticks);
#endif DEBUG_CSS
  ticks = (slack_ticks * cpu_bf_ticks ) / (cpu_bf_ticks+cpu_af_ticks);
#ifdef DEBUG_CSS
  printk("CSS ticks for bf = %lu proportion bf=%lu , af=%lu\n", 
	 (unsigned long)ticks ,
	 (unsigned long)cpu_bf_ticks ,
	 (unsigned long)cpu_af_ticks);
#endif DEBUG_CSS
  tick2timespec(cpu_bf_ticks, &(cpu_bf_attr.compute_time));
  cpu_bf_attr.period.tv_sec = css_attr->period.tv_sec;
  cpu_bf_attr.period.tv_nsec = css_attr->period.tv_nsec;
  {
    cpu_tick_data_t t;
    t = ticks + cpu_bf_ticks;
    tick2timespec(t, &(cpu_bf_attr.deadline));
  }
  cpu_bf_attr.blocking_time.tv_sec = 0;
  cpu_bf_attr.blocking_time.tv_nsec = 0;
  /* the cpu start_time is in_sync with the application */
  cpu_bf_attr.start_time = css_attr->start_time;
  memcpy(&(cpu_bf_attr.reserve_type),&(css_attr->reserve_type),
	 sizeof(rk_reserve_param_data_t));
#ifdef DEBUG_CSS
  printk("CSS cpu_bf compute (%lu,%lu), period (%lu,%lu) , deadline(%lu,%lu) \n",
	cpu_bf_attr.compute_time.tv_sec, 
	 cpu_bf_attr.compute_time.tv_nsec,
	cpu_bf_attr.period.tv_sec, 
	 cpu_bf_attr.period.tv_nsec,
	cpu_bf_attr.deadline.tv_sec, 
	 cpu_bf_attr.deadline.tv_nsec);
#endif DEBUG_CSS
  
  /* Compute slack and create the CPU reservation for compute_af */
  slack_ticks -= ticks;
#ifdef DEBUG_CSS
  printk("CSS slack left = %lu \n", (unsigned long) slack_ticks);
#endif DEBUG_CSS
  /* the cpu start_time should offset to the expected time the disk access is completed */
  cpu_af_attr.start_time = css_attr->start_time;
  //timespec_add(cpu_af_attr.start_time, cpu_bf_attr.compute_time);
  timespec_add(cpu_af_attr.start_time, disk_attr.deadline);
  memcpy(&(cpu_af_attr.reserve_type),&(css_attr->reserve_type),
	 sizeof(rk_reserve_param_data_t));
  cpu_af_attr.period.tv_sec = css_attr->period.tv_sec;
  cpu_af_attr.period.tv_nsec = css_attr->period.tv_nsec;
  {
    cpu_tick_data_t t;
    t = slack_ticks+cpu_af_ticks;
    tick2timespec(t, &(cpu_af_attr.deadline));
#ifdef DEBUG_CSS
    printk("CSS cpu af slack = (%lu,%lu) \n", cpu_af_attr.deadline.tv_sec, cpu_af_attr.deadline.tv_nsec);
#endif DEBUG_CSS
  }
  tick2timespec(cpu_af_ticks, &(cpu_af_attr.compute_time));
  timespec_add(cpu_af_attr.deadline, disk_attr.deadline);
  //mespec_add(cpu_af_attr.deadline, cpu_bf_attr.deadline);
  //timespec_sub(cpu_af_attr.deadline, cpu_bf_attr.compute_time);
#ifdef DEBUG_CSS
    printk("CSS cpu af deadline = (%lu,%lu) disk_deadline = (%lu, %lu) \n", cpu_af_attr.deadline.tv_sec, cpu_af_attr.deadline.tv_nsec, disk_attr.deadline.tv_sec, disk_attr.deadline.tv_nsec);
#endif DEBUG_CSS

  cpu_af_attr.blocking_time.tv_sec = disk_attr.deadline.tv_sec;
  cpu_af_attr.blocking_time.tv_nsec = disk_attr.deadline.tv_nsec;
#ifdef DEBUG_CSS
  printk("CSS cpu_af compute (%lu,%lu), period (%lu,%lu) , deadline(%lu,%lu) blocking (%lu,%lu)\n",
	 (unsigned long)cpu_af_attr.compute_time.tv_sec, 
	 (unsigned long)cpu_af_attr.compute_time.tv_nsec,
	 (unsigned long)cpu_af_attr.period.tv_sec, 
	 (unsigned long)cpu_af_attr.period.tv_nsec,
	 (unsigned long)cpu_af_attr.deadline.tv_sec,
	 (unsigned long)cpu_af_attr.deadline.tv_nsec,
	 (unsigned long)cpu_af_attr.blocking_time.tv_sec,
	 (unsigned long)cpu_af_attr.blocking_time.tv_nsec);
#endif DEBUG_CSS


  {
    extern rk_reserve_t cpu_reserve_create(rk_resource_set_t, 
					   cpu_reserve_attr_t);
    printk("create cpu_reserve for compute_af \n");
    if ((bf_cpu_rsv = cpu_reserve_create(rs, &cpu_af_attr))==NULL_RESERVE) {
      printk("CSS cannont create cpu_reserve for compute_af \n");
      goto error;
    }
  }
  {
    extern rk_reserve_t cpu_reserve_create(rk_resource_set_t,
					   cpu_reserve_attr_t);
    printk("create cpu_reserve for compute_bf \n");
    if ((af_cpu_rsv = cpu_reserve_create(rs, &cpu_bf_attr))==NULL_RESERVE) {
      printk("CSS cannot create cpu_reserve for compute_bf \n");
      goto error;
    }
  }
  {
    extern rk_reserve_t disk_reserve_create(rk_resource_set_t,
					    disk_reserve_attr_t);
    printk("adjust css for new disk_reserve \n");
    if ((disk_rsv = disk_reserve_create(rs, &disk_attr))==NULL_RESERVE) {
      printk("CSS cannot create disk_reserve \n");
      goto error;
    }
  }
  

  return RK_SUCCESS;
   
 error:
  return RK_ERROR;
}
			    
asmlinkage int sys_css_disk_cpu_reserve_create(rk_resource_set_t rs, 
					       css_disk_cpu_reserve_attr_t css_attr) {
        css_disk_cpu_reserve_attr_data_t local_attr;

#ifdef DEBUG_RK
  printk("CSS css_disk_rsv_create: rs(0x%x)\n", (int)rs);
#endif DEBUG_RK

	/* check region before access */
	if ((!css_attr) || (verify_area(VERIFY_READ, css_attr, 
					sizeof(css_disk_cpu_reserve_attr_data_t)))) {
	  return RK_ERROR;
	}
	if (copy_from_user(&local_attr, css_attr, 
			   sizeof(css_disk_cpu_reserve_attr_data_t))) {
	  return RK_ERROR;
	}

	return css_disk_cpu_reserve_create(rs, &local_attr);

}

static inline void tick2timespec(long tick, struct timespec *time) {
  cpu_tick_data_t cpu_tick;
  cpu_tick_data_t us;
  
  cpu_tick = (cpu_tick_data_t) tick;
  tick2usec(&cpu_tick, &us);
  time->tv_sec = us/1000000;
  time->tv_nsec = (us-(time->tv_sec*1000000))*1000;
}
