/* 
 * 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.
 */
/*
 * Road map of net reserve:
 *
 *	1. create a net reserve and counting a total number of bytes sent through it
 *	2. perform accounting based on C/T model
 *	3. introduce the enforcement for net reserves
 */
#include <rk/rk_linux.h>
#include <rk/rk_error.h>
#include <rk/rk.h>

#include <rk/timespec.h>	/* this is needed by the priority_list
				   functions, to compare timespec structures
				 */
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <linux/time.h>
#include <asm/processor.h>
#include <net/sock.h>
#ifdef THREADED_NET
#include <linux/smp_lock.h>
#endif

#ifdef	RK_NET_RSV

static int net_reserve_destroy (rk_reserve_t);
static void net_reserve_replenish(rk_reserve_t, cpu_tick_t, cpu_tick_t dummy);
static void net_reserve_enforce (rk_reserve_t);
static void net_reserve_attach (rk_reserve_t, struct rs_proc_list *);
static void net_reserve_detach (rk_reserve_t, struct rs_proc_list *);
static void net_reserve_sleep_on (rk_reserve_t);
static int net_reserve_read_proc (rk_reserve_t, char *);

struct rk_reserve_ops net_reserve_ops = {
  net_reserve_destroy,
  NULL,				/* start_account */
  NULL,				/* stop_account */
  net_reserve_replenish,
  net_reserve_enforce,
  net_reserve_attach,
  net_reserve_detach,
#ifdef	linux
  net_reserve_sleep_on,
  net_reserve_read_proc,
#endif
};

#define SUPER_CHARGED			1
#ifdef SUPER_CHARGED
#define RATE_MONOTONIC			0
#define DEADLINE_MONOTONIC 		1
#define DEFAULT_DEV       "eth0"
#define MAX_QUEUES       61

static int net_reserves_scheduling_policy = DEADLINE_MONOTONIC;
#endif /* SUPER_CHARGED */

/*
 * Internal data structure to keep track of cpu capacity usage
 *	99.99% is represented as 9999
 */
/* typedef	unsigned long		net_capacity_t;
typedef	unsigned long long	net_capacity_quad_t;
*/

#define	CAPACITY_INT(x)		((x)/100)
#define	CAPACITY_FRAC(x)	((x)-(x/100)*100)
#define	INT2CAPACITY(x)		((x)*10000)
#define	CAPACITY2INT(x)		((x)/10000)
#define	PERCENT2CAPACITY(x)	((x)*100)
#define	CAPACITY_OF(c,t)	(INT2CAPACITY(c)/(t))

/*
 *	66% ... is the maximum available capacity for reserves.
 */
#define	NET_CAPACITY_MAX	PERCENT2CAPACITY(66)
#define	LINK_SPEED_10MBPS	(10*1000*1000/8)
#define BYTE_TRANSMISSION_TIME  (8*100)	// in ns
#define TOTAL_BUFFER_SPACE      1500*60*10
static int net_reserve_available_buffer_pool;
static int net_reserve_queues;

#ifdef EVAL_RK_NET

#define	MAX_COUNT_NET	250
extern unsigned long volatile jiffies;
struct net_eval_data
{
  unsigned long t_jiffies;	/* jiffies when recorded */
  unsigned long c_used;		/* bytes used in period */
};
#endif


/*
 * Capacity must be administrated for each interface
 */
struct list_head net_reserve_head;
net_capacity_t net_reserve_current_capacity;	/* for admission control */

#ifdef NET_MEAS
cpu_tick_data_t sock_start = 0;
cpu_tick_data_t sock_end = 0;
cpu_tick_data_t sock_proc = 0;
cpu_tick_data_t devstart = 0;
cpu_tick_data_t devend = 0;
cpu_tick_data_t dev_proc = 0;
cpu_tick_data_t qdisc_wak = 0;
cpu_tick_data_t qdisc_nq = 0;
#endif


struct queue_set
{

  net_reserve_t rsv;
  net_capacity_quad_t qt;

};

typedef struct queue_set queue_set_t;

queue_set_t net_reserve_queue_set[MAX_QUEUES + 3];	// list of queues.

static int link_speed=80*1000*1000; //default is 10Mbps

static int byt_tx_time;
unsigned char hard_net_reserve_active = 0;

/*	
		Priority list stuff... */
#define net_reserve_entry(list) list_entry((list), struct net_reserve, net_link)
static int efficient_timespec_ceil (struct timespec dividend,
				    struct timespec divider);
static int ceiling (unsigned long dividend, unsigned long divider);
static int timespec_ceiling (struct timespec dividend,
			     struct timespec divider);
static struct timespec timespec_mult (struct timespec multiplicand,
				      int multiplier);
static struct timespec timespec_mult (struct timespec, int);
int admit_reserve_request (net_reserve_t net);
void priority_list_add (net_reserve_t net, struct list_head *head);
int priority_list_remove (net_reserve_t net, struct list_head *head);



//extern void destroy_sock (struct sock *sk);
/*
 *
 */
#include <linux/netdevice.h>
#include <net/pkt_sched.h>
void net_reserve_activate (struct device *dev);
void
net_reserve_init (void)
{
#ifdef	linux
//      extern struct Qdisc     *net_reserve_qdisc;
  struct device *netdev;
 
#endif
  INIT_LIST_HEAD (&net_reserve_head);
  net_reserve_current_capacity = 0;

  net_reserve_available_buffer_pool = TOTAL_BUFFER_SPACE;

  byt_tx_time = 1000*1000*1000/link_speed; // in ns
//      net_reserve_qdisc = NULL;
  //      net_reserve_queue_set[0].rsv = NULL;


/*	
 *     Let's activate net_reserve immediatly!!!
 */
  /* We have to check the devices that are active from the list */
#if 0
  netdev = dev_base->next;
  if (netdev == NULL)
    {
      printk ("net_reserve_init: invalid device name(%s)\n", ifr.ifr_name);
      return;
    }
#endif
  netdev = dev_base;
do {
    if (netdev != NULL) {
      printk("netdev %s device found\n", netdev->name);
      net_reserve_activate(netdev);
      netdev = netdev->next;
    }
    
    if (netdev == dev_base || netdev == NULL)
      break;
    
  } while(netdev != dev_base || netdev != NULL);
  
 
  
}

/*rk_reserve_t
net_reserve_create(rk_resource_set_t rs,
		   size_t b, struct timespec *t,
		   rk_reserve_param_t p,
		   void *data) */
/* 	
		Changed the prototype in order to compile
rk_reserve_t
net_reserve_create(rk_resource_set_t rs, net_reserve_attr_t net_attr)
*/
rk_reserve_t
net_reserve_create (rk_resource_set_t rs,
		    net_reserve_attr_t net_attr, void *data)
{
  rk_reserve_t rsv=NULL;
  net_reserve_t netrsv;
  net_capacity_t capacity;
  net_capacity_t new_capacity;
  net_capacity_quad_t qc, qt, qd;
  unsigned long flags;
  /*
#ifdef	linux
  struct device *netdev = data;
#endif
  */

  if (rs == NULL)
    return NULL_RESERVE;

  rk_spin_lock (flags);
  /* check for validity of "rs" first */
  if (!rk_valid_rset (rs))
    goto leave;

  if (rs->rs_net)
    {
      /* pre-existing cpu reserve */
      goto leave;
    }

  /* admission control */
  /*  
     now the amount is in net_attr
     qc = b;
   */
  qc = net_attr->amount;

  net_attr->net_time.tv_sec =
    (net_attr->amount * byt_tx_time) / (1000 * 1000 * 1000);

  net_attr->net_time.tv_nsec = net_attr->amount * byt_tx_time -
    net_attr->net_time.tv_sec * 1000 * 1000 * 1000;

  /*                 items for the period
     qt = t->tv_sec; qt *= NANOSEC; qt += t->tv_nsec;
   */
  qt = net_attr->period.tv_sec;
  qt *= NANOSEC;
  qt += net_attr->period.tv_nsec;

  qd = net_attr->deadline.tv_sec;
  qd *= NANOSEC;
  qd += net_attr->deadline.tv_nsec;

  if (!qt)
    {
      printk ("net_reserve_create: null time period\n");
      return NULL_RESERVE;
    }

  if (!net_attr->amount)
    {
      printk ("net_reserve_create: null amount time\n");
      return NULL_RESERVE;
    }

  /* Check deadline */
  if (timespec_gt (net_attr->deadline, net_attr->period))
    {
      printk ("net_reserve_create: deadline greater than period\n");
      return NULL_RESERVE;
    }

  /* Check for the start_time value */
  if (net_attr->start_time.tv_nsec >= NANOSEC)
    {
      printk ("net_reserve_create: start_time greater than NANOSEC\n");
      return NULL_RESERVE;
    }

  capacity = CAPACITY_OF (qc, (qt * link_speed) / NANOSEC);
     new_capacity = capacity + net_reserve_current_capacity;
  /*
     if (new_capacity > NET_CAPACITY_MAX) {
     return NULL_RESERVE;
     }
   */
  net_reserve_current_capacity = new_capacity;
  /* create net reserve object and set static data */
  netrsv = malloc (sizeof (struct net_reserve));
  bzero (netrsv, sizeof (struct net_reserve));
  memcpy (&(netrsv->net_res_attr), net_attr,
	  sizeof (net_reserve_attr_data_t));

  /*  
     as above
     netrsv->period = *t;
     netrsv->amount = b;
   */

  netrsv->period = net_attr->period;
  netrsv->amount = net_attr->amount;
  nanosec2tick (&qt, &netrsv->period_ticks);
  netrsv->capacity = capacity;
  /*  
     We don't neet this list, now we use 
     net_reserve_head for the priority list
     list_add(&netrsv->net_link, &net_reserve_head);
   */

  /* init per period data */
  netrsv->available_amount = netrsv->amount;

  if (net_attr->buffer_space < net_reserve_available_buffer_pool)
    {
	/** This is for downward compatibiliy */
      if (net_attr->buffer_space != 0)
	{
	  netrsv->buffer_space = netrsv->net_res_attr.buffer_space =
	    net_attr->buffer_space;
	}
      else
	{
	  netrsv->buffer_space = netrsv->net_res_attr.buffer_space =
	    10 * 1500;
	}
      net_reserve_available_buffer_pool -= netrsv->buffer_space;
    }
  else
    {
	/**** XXX; is this correct?? */
      if (net_reserve_available_buffer_pool > 10 * 1500)
	{
	  netrsv->buffer_space = netrsv->net_res_attr.buffer_space =
	    10 * 1500;
	}
      else
	{
	  netrsv->buffer_space = netrsv->net_res_attr.buffer_space =
	    net_reserve_available_buffer_pool;
	}
      net_reserve_available_buffer_pool -= netrsv->buffer_space;
    }

  printk ("buffer space %d\n", netrsv->buffer_space);

  if (net_reserve_available_buffer_pool <= 0)
    {
      printk ("net_reserve_create: Warning!!: buffer pool is finished\n");
    }

/* Luca: Why priority_list_remove is protected and priority_list_add is not? */
  priority_list_add (netrsv, &net_reserve_head);

  if (!admit_reserve_request (netrsv))
    {
      //rk_disable();
      priority_list_remove (netrsv, &net_reserve_head);
      free (netrsv);

      printk ("net_reserve_create: admission control failed\n");
      return NULL_RESERVE;
    }

  /*Increment the number of active real-time packet queues */
  net_reserve_queues++;

#ifdef EVAL_RK_NET
  {
    int size = MAX_COUNT_NET;	/* 3min */

    netrsv->net_eval_data_count = 0;
    netrsv->net_eval_datap = malloc (sizeof (struct net_eval_data) * size);

    bzero (netrsv->net_eval_datap, sizeof (struct net_eval_data) * size);
  }
#endif

  /* create generic reserve object */
  rsv = rk_reserve_create (rs, RSV_NET);

  rsv->rsv_state = RSV_IS_NULL;
  rsv->rsv_rsv = netrsv;
  rsv->rsv_ops = &net_reserve_ops;
  netrsv->rsv = rsv;
  /*    
     Now the reserve param are in net_attr
     rsv->rsv_reserve_param = *p;
   */
  rsv->rsv_reserve_param = net_attr->reserve_type;

  init_waitqueue (&netrsv->depleted_wait);

  /* create a timer for it */
  rk_replenish_timer_create (rsv, net_attr->start_time);



  /* attach reserve to resource set */
#if 0				/* Now, attachment is performed by the timer.. */
  if (rk_resource_set_attach_reserve (rs, rsv) != RK_SUCCESS)
    {
      printk ("Cannot Attach...\n");
      return NULL_RESERVE;
    }
#endif
leave:
  rk_spin_unlock (flags);

  return rsv;			/* success */
}

static int
net_reserve_ctl (rk_reserve_t rsv, net_reserve_attr_t net_attr)
{
  net_reserve_t net;
  net_reserve_attr_data_t old_local_attr;
  net_capacity_t capacity;
  net_capacity_t new_capacity, old_capacity;
  net_capacity_quad_t qc, qt, qd;
  int admit_ok = FALSE;
  int pos_adjust = FALSE;

  if ((rsv == NULL) || (!rk_valid_rsv (rsv)))
    {
      return -EINVAL;
    }

  if ((void *) (net = rsv->rsv_rsv) == NULL_RESERVE)
    {
      return -EINVAL;
    }

  /*check for zero reservation request */
  if (!net_attr->amount)
    {
      printk ("net_reserve_create: null amount time\n");
      return -EINVAL;
    }

  /* check deadline */
  if (timespec_gt (net_attr->deadline, net_attr->period))
    {
      /* deadline cannot be greater than the period */
      return -EINVAL;
    }

  /* calculate a new capacity */
  qc = net_attr->amount;

  qt = net_attr->period.tv_sec;
  qt *= NANOSEC;
  qt += net_attr->period.tv_nsec;

  qd = net_attr->deadline.tv_sec;
  qd *= NANOSEC;
  qd += net_attr->deadline.tv_nsec;

  net_attr->net_time.tv_sec =
    (net_attr->amount * byt_tx_time) / (1000 * 1000 * 1000);

  net_attr->net_time.tv_nsec = net_attr->amount * byt_tx_time -
    net_attr->net_time.tv_sec * 1000 * 1000 * 1000;

  capacity = CAPACITY_OF (qc, qt);

  /* save the old parameters */
  memcpy (&old_local_attr, &(net->net_res_attr),
	  sizeof (net_reserve_attr_data_t));

  old_capacity = net->capacity;

  /* load the new parameters */
  memcpy (&(net->net_res_attr), net_attr, sizeof (net_reserve_attr_data_t));
  net->capacity = capacity;
  {
    int adjust = FALSE;
    switch (net_reserves_scheduling_policy)
      {
      case RATE_MONOTONIC:
	if (timespec_ne (old_local_attr.period, net_attr->period))
	  {
	    adjust = TRUE;
	    break;
	  }
	if ((timespec_eq (old_local_attr.deadline, net_attr->deadline))
	    &&
	    (timespec_ge
	     (old_local_attr.blocking_time, net_attr->blocking_time))
	    && (timespec_ge (old_local_attr.net_time, net_attr->net_time)))
	  {
	    /* same period, same deadline, less compute time: must be ok */
	    admit_ok = TRUE;
	  }
	break;
      case DEADLINE_MONOTONIC:
	if (timespec_ne (old_local_attr.deadline, net_attr->deadline))
	  {
	    adjust = TRUE;
	    break;
	  }
	if ((timespec_eq (old_local_attr.period, net_attr->period)) &&
	    (timespec_ge (old_local_attr.blocking_time,
			  net_attr->blocking_time)) &&
	    (timespec_ge (old_local_attr.net_time, net_attr->net_time)))
	  {
	    /* same period, same deadline, less compute time: must be ok */
	    admit_ok = TRUE;
	  }
	break;

      }

    if (adjust)
      {
	pos_adjust = TRUE;	/* need this for possible resetting later */
	priority_list_remove (net, &net_reserve_head);
	priority_list_add (net, &net_reserve_head);
      }
  }


  /* do a new admission control test */
  if ((!admit_ok) && (!admit_reserve_request (net)))
    {
      /* admission control failed: return to original values */
      memcpy (&(net->net_res_attr), &old_local_attr,
	      sizeof (net_reserve_attr_data_t));
      net->capacity = old_capacity;

      if (pos_adjust)
	{
	  /* remove and add from list again */
	  priority_list_remove (net, &net_reserve_head);
	  priority_list_add (net, &net_reserve_head);
	}
      return RK_ERROR;		/* error since change failed */
    }

  /* Admission control test for buffer management */

  if (net_reserve_available_buffer_pool + old_local_attr.buffer_space -
      net->buffer_space < 0)
    {
      /* admission control failed: return to original values */
      memcpy (&(net->net_res_attr), &old_local_attr,
	      sizeof (net_reserve_attr_data_t));
      net->capacity = old_capacity;

      if (pos_adjust)
	{
	  /* remove and add from list again */
	  priority_list_remove (net, &net_reserve_head);
	  priority_list_add (net, &net_reserve_head);
	}

      return RK_ERROR;		/* error since change failed */
    }

  net_reserve_available_buffer_pool +=
    old_local_attr.buffer_space - net->buffer_space;

  /* available utilization: is this really needed? */
  new_capacity = capacity + net_reserve_current_capacity - net->capacity;

  net_reserve_current_capacity = new_capacity;

  return RK_SUCCESS;		/* success */
}

/*
 *
 */

/*

queue_set : contains the list of network reserves, and their corresponding 
time periods (T)
The queue number for the reserve is same as the corresponding index inthe queue_set array 

*/

/*     
   I changed them with the priority list, from cpu_reserve.c
   I modified the priority_list_add and priority_list_remove
   functions to work with net reserves...
*/

/*
 * Add specified reservation into reservation list in priority order.
 * Adjust reservation_priority index of list members accordingly.
 */
void
priority_list_add (net_reserve_t net, struct list_head *head)
{
  net_reserve_t r;
  int i, done = FALSE;
  struct list_head *ptr;

  if (head->next == head)
    {

      net->queue_number = 0;
      list_add (&(net->net_link), head);
      return;
    }

  for (ptr = head->next; ptr != head; ptr = ptr->next)
    {
      r = net_reserve_entry (ptr);
      i = r->queue_number + 1;

#ifdef SUPER_CHARGED
      switch (net_reserves_scheduling_policy)
	{
	case RATE_MONOTONIC:
	  if (timespec_lt (net->net_res_attr.period, r->net_res_attr.period))
	    {
	      done = TRUE;
	    }
	  break;
	case DEADLINE_MONOTONIC:
	  if (timespec_lt
	      (net->net_res_attr.deadline, r->net_res_attr.deadline))
	    {
	      done = TRUE;
	    }
	  break;
	}
#else /* SUPER_CHARGED */
      if (timespec_lt (net->net_res_attr.period, r->net_res_attr.period))
	{
	  done = TRUE;
	}
#endif /* SUPER_CHARGED */

      if ((done) || (ptr == head))
	{
	  /* insert reservation here: subsequent indices will be fine as is! */

	  net->queue_number = i;
	  list_add (&(net->net_link), ptr->prev);
	  break;
	}
      else
	{
	  /* this increments the priority index of this entry */

	  r->queue_number = i;
	}
    }

  if (!done)
    list_add (&net->net_link, ptr->prev);
}

/*
 * Remove specified reservation from reservation list.
 * Adjust reservation_priority index of remaining list members accordingly.
 */
int
priority_list_remove (net_reserve_t net, struct list_head *head)
{
  net_reserve_t r;
  int i;
  struct list_head *ptr;

  if (head->next == head)
    {
      /* empty list */
      return -1;
    }
  if (head->next == &(net->net_link))
    {
      /* remove item to make empty list */
      list_del (&(net->net_link));
      return 0;
    }


  /* search through list to find item */
  for (ptr = head->next; ptr != head; ptr = ptr->next)
    {
      r = net_reserve_entry (ptr);

      i = r->queue_number - 1;

      if (r == net)
	{
	  /* delete reservation */
	  list_del (&(net->net_link));

	  /* we are done; subsequent entries already have valid priority indices */
	  return 0;
	}
      else
	{
	  /* this decrements the priority index of this entry */

	  r->queue_number = i;
	}
    }

  /* we should not reach here, but just in case, we set the priority 
   * index correctly.
   */
  for (ptr = head->prev, i = 0; ptr != head; ptr = ptr->prev, i++)
    {
      r = net_reserve_entry (ptr);

      r->queue_number = i;
    }

  return (-1);
}



/*
 *
 *
 */

static int
net_reserve_destroy (rk_reserve_t rsv)
{
  void empty_skb_list (int queue_number);
  net_reserve_t netrsv = rsv->rsv_rsv;

  empty_skb_list (netrsv->queue_number);

  /* destroy timer */
  rk_replenish_timer_destroy (rsv);
  /* return capacity */
  net_reserve_current_capacity -= netrsv->capacity;
  if ((int) net_reserve_current_capacity < 0)
    {
      net_reserve_current_capacity = 0;
    }

  /* Release the buffer space */
  net_reserve_available_buffer_pool += netrsv->buffer_space;

  /* Release the queue */

  priority_list_remove (netrsv, &net_reserve_head);
  /*Decrement the number of active real-time queues */
  net_reserve_queues--;

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

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

static void
net_reserve_replenish(rk_reserve_t rsv, cpu_tick_t period, cpu_tick_t dummy)
{
  net_reserve_t netrsv = rsv->rsv_rsv;
  net_capacity_t c;
  long long signed int amount;

#ifdef EVAL_RK_NET
  unsigned long count;
  struct net_eval_data *datap;
#endif

  *period = netrsv->period_ticks;
//*period = 1000000000; Probably this eliminate the crashes?
  //      c = CAPACITY_OF(netrsv->used_amount, netrsv->available_amount);

  c = CAPACITY_OF (netrsv->used_amount, netrsv->period_ticks);

  if (netrsv->max_utilization < c)
    {
      netrsv->max_utilization = c;
    }
  else if (netrsv->min_utilization > c || netrsv->max_utilization == 0)
    {
      netrsv->min_utilization = c;
    }

  netrsv->average.total_utils += c;
  netrsv->average.total_count++;

  netrsv->prev_used_amount = netrsv->used_amount;


  netrsv->net_period_buff_used_bw[(netrsv->average.total_count)%(RK_HISTORY_SIZE)].amount = netrsv->prev_used_amount;

  netrsv->net_period_buff_used_bw[(netrsv->average.total_count)%(RK_HISTORY_SIZE)].count = netrsv->average.total_count;

  /* refill the capacity */

  netrsv->used_amount = 0;
  switch (rsv->rsv_reserve_param.rep_mode)
    {
    case RSV_SOFT:
      netrsv->available_amount = netrsv->amount;
      break;

    case RSV_FIRM:
      amount = netrsv->used_amount - netrsv->amount;

      if (amount > 0)
	{
	  size_t next;
	  next = netrsv->amount - amount;
	  if (next >= 0)
	    {
	      netrsv->available_amount = next;
	    }
	  else
	    {
	      netrsv->available_amount = 0;
	    }
	}
      else
	{
	  netrsv->available_amount = netrsv->amount;
	}
      break;

    case RSV_HARD:
      amount = netrsv->used_amount - netrsv->amount;
      if (netrsv->used_amount > netrsv->amount)
	{
	  size_t next;
	  next = netrsv->amount - amount;
	  if (next >= 0)
	    {
	      netrsv->available_amount = next;
	    }
	  else
	    {
	      netrsv->available_amount = 0;
	    }
	}
      else
	{
	  netrsv->available_amount = netrsv->amount;
	}
      break;
    }

  if (hard_net_reserve_active == 1)
    {
      hard_net_reserve_active = 0;
      mark_bh(NET_BH);
	    
    }

}



static void
net_reserve_enforce (rk_reserve_t rsv)
{
  /*
    net_reserve_t netrsv = rsv->rsv_rsv;
  */
}

static void
net_reserve_attach (rk_reserve_t rsv, struct rs_proc_list *rs_proc)
{
  net_reserve_t netrsv;
  struct rs_sock_list *rs_sock;
  struct sock *sk;
  struct list_head *sock_list;

  printk("ATTACH\n");

  netrsv = rsv->rsv_rsv;
  sock_list = &rs_proc->rs_proc_task->rs_sock_list;

  for (sock_list = sock_list->next;
       sock_list != &rs_proc->rs_proc_task->rs_sock_list;
       sock_list = sock_list->next)
    {

      rs_sock = list_entry (sock_list, struct rs_sock_list, rs_sock_list);

      sk = rs_sock->sk;

      if (sk != NULL)
	{
	  printk("adding sock\n");
	  sk->rk_net_reserve = rsv;
	}

      printk("hmm\n");
    }


  printk("departing from sock\n");
}

static void
net_reserve_detach (rk_reserve_t rsv, struct rs_proc_list *rs_proc)
{
  net_reserve_t netrsv;
  struct rs_sock_list *rs_sock;
  struct sock *sk;
  struct list_head *sock_list;

  netrsv = rsv->rsv_rsv;
  sock_list = &rs_proc->rs_proc_task->rs_sock_list;

  for (sock_list = sock_list->next;
       sock_list != &rs_proc->rs_proc_task->rs_sock_list;
       sock_list = sock_list->next)
    {

      rs_sock = list_entry (sock_list, struct rs_sock_list, rs_sock_list);

      sk = rs_sock->sk;

      if (sk != NULL)
	sk->rk_net_reserve = NULL;

    }
}

static void
net_reserve_sleep_on (rk_reserve_t rsv)
{
  net_reserve_t netrsv = rsv->rsv_rsv;
  sleep_on (&netrsv->depleted_wait);
}


inline void net_reserve_get_history(rk_reserve_t rsv, rk_net_reserve_history_t data)
{
  unsigned long flags;
  
  net_reserve_t net = rsv->rsv_rsv;

  rk_spin_lock(flags);

  
  data->amount = net->net_period_buff_used_bw[(data->count)%RK_HISTORY_SIZE].amount;
  data->count = net->net_period_buff_used_bw[(data->count)%RK_HISTORY_SIZE].count;
  //printk("amount %lu\n", data->amount);
  
  rk_spin_unlock(flags);

}


int
admit_reserve_request (net_reserve_t net)
{
  struct timespec completion_time, prev_completion_time;
  struct timespec result;
  net_reserve_t curr, r;
  int ceil_value;
  net_reserve_attr_t attr = &(net->net_res_attr);
  struct list_head *ptr, *p2;

#ifdef DEBUG_RK
  printk ("admit_reserve_request: ");
#endif /* DEBUG_RK */

  /* the admission control policy for a reserve is as follows.
   * We check the completion times of the incoming reserve and ALL
   * lower priority reserves assuming that the reserve is admitted.
   * If the completion times of all these reserves are less than their
   * respective deadlines, then the incoming reserve can be admitted.
   */

#ifdef LOG_RESERVES
  if (log_messages)
    {
      log_start = clock_ttime (clockrt);
    }
#endif /* LOG_RESERVES */

  /* do basic checks quickly */
  if (timespec_gt (attr->net_time, attr->period) ||
      timespec_gt (attr->net_time, attr->deadline))
    {
#ifdef DEBUG_RK
      printk ("false (0)\n");
#endif /* DEBUG_RK */
      return FALSE;
    }

  /* perform the "completion time test" */
  for (ptr = net_reserve_head.next; ptr != &net_reserve_head; ptr = ptr->next)
    {
      net_reserve_attr_t curr_attr;

      curr = net_reserve_entry (ptr);
      curr_attr = &(curr->net_res_attr);

      /* repeat for each reserve in the current set of reserves */

      /* Optimization: skip reserves with shorter deadlines for
       *   deadline-monotonic scheduling policy or with shorter periods for
       *   rate-monotonic scheduling.
       */
#ifdef SUPER_CHARGED
      {
	int cont = FALSE;
	switch (net_reserves_scheduling_policy)
	  {
	  case RATE_MONOTONIC:
	    if (timespec_lt (curr_attr->period, attr->period))
	      {
		/* shorter period reservation: must have been tested earlier */
		cont = TRUE;
	      }
	    break;
	  case DEADLINE_MONOTONIC:
	    if (timespec_lt (curr_attr->deadline, attr->deadline))
	      {
		/* shorter deadline reservation: must have been tested earlier */
		cont = TRUE;
	      }
	    break;
	  default:


	    break;
	  }
	if (cont)
	  continue;
      }
#else /* SUPER_CHARGED */
      if (timespec_lt (curr_attr->period, attr->period))
	{
	  /* shorter period reservation: must have been tested earlier */
	  continue;
	}
#endif /* SUPER_CHARGED */

      timespec_zero (prev_completion_time);
      timespec_set (completion_time, curr_attr->net_time);
#if 0
      timespec_add (completion_time, curr_attr->blocking_time);
#endif
      /* calculate the worst-case completion time */
      while (timespec_le (prev_completion_time, curr_attr->deadline) &&
	     timespec_lt (prev_completion_time, completion_time))
	{
	  timespec_set (prev_completion_time, completion_time);
	  timespec_zero (completion_time);

	  for (p2 = net_reserve_head.next;
	       p2 != &net_reserve_head; p2 = p2->next)
	    {
	      net_reserve_attr_t r_attr;

	      r = net_reserve_entry (p2);

	      /* We compute preemption time from the HIGHER priority reserves and
	       * any EQUAL priority reserves.  This is necessary because the
	       * deadlines can be different for reserves with the same period.
	       */
	      if (r == curr)
		{
		  /* self */
		  continue;
		}

	      r_attr = &(r->net_res_attr);

#ifdef SUPER_CHARGED
	      {
		int done = FALSE;
		switch (net_reserves_scheduling_policy)
		  {
		  case RATE_MONOTONIC:
		    if (timespec_gt (r_attr->period, curr_attr->period))
		      {
			done = TRUE;
		      }
		    break;
		  case DEADLINE_MONOTONIC:
		    if (timespec_gt (r_attr->deadline, curr_attr->deadline))
		      {
			/* we have found a reserve with a longer period/deadline and
			 * therefore a lower priority, break from loop */
			done = TRUE;
		      }
		    break;
		  }
		if (done)
		  break;
	      }
#else /* SUPER_CHARGED */
	      if (timespec_gt (r_attr->period, curr_attr->period))
		break;
#endif /* SUPER_CHARGED */

	      ceil_value = efficient_timespec_ceil (prev_completion_time,
						    r_attr->period);
	      result = timespec_mult (r_attr->net_time, ceil_value);
	      timespec_add (completion_time, result);
	    }

	  /* add computation time and blocking time of self */
	  timespec_add (completion_time, curr_attr->net_time);
	  //   timespec_add(completion_time, curr_attr->blocking_time);
	}			/* end while */

      if (timespec_gt (completion_time, curr_attr->period) ||
	  timespec_gt (completion_time, curr_attr->deadline))
	{
	  /* deadline already missed; return failure */
#ifdef DEBUG_RK
	  printk ("false\n");
#endif /* DEBUG_RK */
	  return FALSE;
	}
    }

#ifdef LOG_RESERVES
  if (log_messages)
    {
      log_end = clock_ttime (clockrt);
      timespec_sub (log_end, log_start);
      sprintf (LINEA, " admission control %d %d \n", log_end.seconds,
	       log_end.nanoseconds);
      kern_msg_log (LINEA);
    }
#endif /* LOG_RESERVES */

  /* everything seems fine */
#ifdef DEBUG_RK
  printk ("true\n");
#endif /* DEBUG_RK */

  return TRUE;
}



asmlinkage int sys_rk_net_reserve_get_history(rk_reserve_t rsv, rk_net_reserve_history_t udata)
{
  rk_net_reserve_history_data_t data;

  //printk("h\n");
#if 0
  if (!rk_valid_rsv(rsv))
    {printk("invalid rsv %p\n", rsv);
    return -EINVAL;
    }
#endif
  copy_from_user_ret(&data, udata, sizeof(rk_net_reserve_history_data_t), -EFAULT);

  net_reserve_get_history(rsv, &data);

  copy_to_user(udata, &data,sizeof(rk_reserve_history_data_t));

  return RK_SUCCESS;


}

/*
 * Linux specific
 */
#ifdef	linux
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
#include <linux/init.h>
#include <net/sock.h>
#include <net/pkt_sched.h>
/*
 * Linux Packet Scheduler
 *
 *	enqueue: classifies a packet (sk_buff) 
 *		 and puts it in an appropriate queue in qdisc
 *	dequeue: removes the highest priority packet from a queue
 *	requeue: enqueues a packet again (when its transmission is failed)
 *		 always succeeds (enqueue may fail because of too long queue)
 *	init:	 initializes queues
 *	reset:	 removes all packets from queues and clears everything
 *
 */

/*
 *
 */
struct Qdisc *net_reserve_qdisc;
struct Qdisc *old_qdisc;

void
empty_skb_list (int queue_number)
{
  struct sk_buff_head *list;
  struct sk_buff *skb;

/* First of all, we have to empty the associated queue... */
  list = ((struct sk_buff_head *) net_reserve_qdisc->data) + queue_number;
  while (!skb_queue_empty (list))
    {
      /* Warning!!! We also need to free the sockbuff!!! */
      skb = __skb_dequeue (list);
      kfree_skb (skb);
    }
}



/* 
 * Net-reserve extends "3-band FIFO queue" in sch_generic.c by adding a 
 * RT queue.  There must be a RT queue for each priority level, but there is
 * only a single queue now to make it different from best effort queues.
 */

static const u8 prio2band[TC_PRIO_MAX + 1] =
  { 62, 63, 63, 63, 62, 63, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62 };

/*	{ 2, 3, 3, 3, 2, 3, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2 }; from Shui */
/* was	{ 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 };*/

static int
net_reserve_enqueue (struct sk_buff *skb, struct Qdisc *qdisc)
{
  struct sk_buff_head *list;
  struct sock *sk = skb->sk;
  struct net_reserve *net_rsv;

  /****XXX: Hack to prevent crashing by ARP packets ******/



  if (sk != NULL && skb->protocol == __constant_htons (ETH_P_IP))
    {

      /* if sk is associated with a net reserve */
      if (sk->rk_net_reserve != NULL)
	{

	  /* obtain a real reserve */
	  net_rsv = sk->rk_net_reserve->rsv_rsv;

	  /* choose RT queue */
	  list =
	    ((struct sk_buff_head *) qdisc->data) + (MAX_QUEUES -
						     net_rsv->queue_number -
						     1);
	  /* enqueue now */
	  if (net_rsv->consumed_buffer + skb->len < net_rsv->buffer_space)
	    {
	      __skb_queue_tail (list, skb);
	      qdisc->q.qlen++;
	      net_rsv->consumed_buffer += skb->len;
	      return 1;
	    }
	  else
	    {
	      qdisc->stats.drops++;
	      kfree_skb (skb);
	      return 0;
	    }
	}
    }

  /* No net reserve */
  list = ((struct sk_buff_head *) qdisc->data) +
    prio2band[skb->priority & TC_PRIO_MAX];

  if (list->qlen <= skb->dev->tx_queue_len)
    {
      __skb_queue_tail (list, skb);
      qdisc->q.qlen++;
      return 1;
    }
  qdisc->stats.drops++;
  kfree_skb (skb);
  return 0;
}

static struct sk_buff *
net_reserve_dequeue (struct Qdisc *qdisc)
{
  int prio;
  struct sk_buff_head *list = ((struct sk_buff_head *) qdisc->data);
  struct sk_buff *skb;
  struct sk_buff *skb_head;
  struct sock *sk;
  struct sk_buff_head *active_list = NULL;
  struct net_reserve *net_rsv;
  rk_reserve_t rsv;
  unsigned char local_hard_reserve_active = 0; /*local variable to check if a hard reserve is active;*/

  for (prio = 0; prio < MAX_QUEUES + 3; prio++, list++)
    {
      if (prio < MAX_QUEUES && !skb_queue_empty (list))
	{
	  skb_head = ((struct sk_buff *) list)->next;	//This is the first buffer in the circular queue after "list"

	  sk = skb_head->sk;
	  if (skb_head->protocol == __constant_htons (ETH_P_IP))
	    {
	      if (sk->rk_net_reserve != NULL)
		{
		  net_rsv = sk->rk_net_reserve->rsv_rsv;
		  if (net_rsv->used_amount + skb_head->len <=
		      net_rsv->available_amount)
		    {
		      net_rsv->used_amount += skb_head->len;
		      net_rsv->consumed_buffer -= skb_head->len;
		    }
		  else
		    {
		      switch (sk->rk_net_reserve->rsv_reserve_param.enf_mode)
			{
			case RSV_HARD:
			  local_hard_reserve_active = 1;
			  break;

			case RSV_FIRM:
			  if (active_list == NULL)
			    active_list = list;
			  break;

			case RSV_SOFT:
			  if (active_list == NULL)
			    active_list = list;
			  break;

			}
		      continue;
		    }
		}
	    }
	}

      skb = __skb_dequeue (list);
      if (skb)
	{

	  qdisc->q.qlen--;
	  return skb;
	}
    }

  if (active_list != NULL)
    {
      local_hard_reserve_active = 0;
      skb = __skb_dequeue (active_list);
      qdisc->q.qlen--;

      sk = skb->sk;
      net_rsv = sk->rk_net_reserve->rsv_rsv;

      net_rsv->used_amount += skb->len;

      net_rsv->consumed_buffer -= skb->len;
      active_list = NULL;

      return skb;
    }

  if (local_hard_reserve_active == 1)
    hard_net_reserve_active = 1;

  return NULL;
}


static int
net_reserve_requeue (struct sk_buff *skb, struct Qdisc *qdisc)
{
  struct sk_buff_head *list;
  struct sock *sk = skb->sk;
  struct net_reserve *net_rsv;

  /* if sk is associated with a net reserve */
  if (skb->protocol == __constant_htons (ETH_P_IP))
    {
      if (sk->rk_net_reserve != NULL)
	{
	  /* choose RT queue */
	  list = ((struct sk_buff_head *) qdisc->data);
	  net_rsv = sk->rk_net_reserve->rsv_rsv;
	  net_rsv->consumed_buffer += skb->len;
	}


      else
	{
	  list = ((struct sk_buff_head *) qdisc->data) +
	    prio2band[skb->priority & TC_PRIO_MAX];
	}
    }
  else
    {
      list = ((struct sk_buff_head *) qdisc->data) +
	prio2band[skb->priority & TC_PRIO_MAX];

    }
  __skb_queue_head (list, skb);
  qdisc->q.qlen++;
  return 1;
}

static int
net_reserve_queue_init (struct Qdisc *qdisc, struct rtattr *opt)
{
  int i;
  struct sk_buff_head *list;

  list = ((struct sk_buff_head *) qdisc->data);
  /* Number of active real-time queues : Max:64 */
  net_reserve_queues = 0;

  for (i = 0; i < (MAX_QUEUES + 3); i++)
    {
      skb_queue_head_init (list + i);
      net_reserve_queue_set[i].rsv = NULL;
      net_reserve_queue_set[i].qt = 0;
    }
  return 0;
}

static void
net_reserve_queue_reset (struct Qdisc *qdisc)
{
  int prio;
  struct sk_buff_head *list = ((struct sk_buff_head *) qdisc->data);

  for (prio = 0; prio < (MAX_QUEUES + 3); prio++)
    skb_queue_purge (list + prio);
  qdisc->q.qlen = 0;
  /* Number of active real-time queues */
  net_reserve_queues = 0;
}

//static struct Qdisc_ops net_reserve_queue_ops =
struct Qdisc_ops net_reserve_queue_ops = {
  NULL,
  NULL,
  "net_reserve",		/* must be less than IFNAMSIZ (16) characters */
  (MAX_QUEUES + 3) * sizeof (struct sk_buff_head),
  /* 3-band FIFO queue + 61 for RT (for now) */
  net_reserve_enqueue,
  net_reserve_dequeue,
  net_reserve_requeue,
  NULL,

  net_reserve_queue_init,
  net_reserve_queue_reset,
};

int qdisc_graft (struct device *dev, struct Qdisc *parent, u32 classid,
		 struct Qdisc *new, struct Qdisc **old);

void
net_reserve_activate (struct device *dev)
{
  net_reserve_qdisc = qdisc_create_dflt (dev, &net_reserve_queue_ops);

  if (net_reserve_qdisc->dev != dev)
    {
      printk
	("net_reserve_activate: multiple devices not supported (yet).\n");
    }

  /*      
   *              I try to activate it in this way...
   */

  qdisc_graft (dev, NULL, 0, net_reserve_qdisc, &old_qdisc);

}

extern rk_resource_set_t rs_net;

void
net_reserve_cleanup (void)
{

  struct device *netdev;

  /* correct device name? */
#if 0
  strcpy (ifr.ifr_name, DEFAULT_DEV);

  dev = dev_get (ifr.ifr_name);
  if (dev == NULL)
    {
      printk ("sys_net_reserve_create: invalid device name(%s)\n",
	      ifr.ifr_name);
      return;
    }
#endif
  netdev = dev_base;
do {
    if (netdev != NULL) {
      printk("netdev %s device found\n", netdev->name);
      // net_reserve_activate(netdev);

      qdisc_graft (netdev, NULL, 0, old_qdisc, &net_reserve_qdisc);
      netdev = netdev->next;
    }
    
    if (netdev == dev_base || netdev == NULL)
      break;
    
  } while(netdev != dev_base || netdev != NULL);


}

/*
 *	struct sock *sk;		... include/net/sock.h
 *	struct socket *sock;		... include/linux/net.h
 *	struct sk_buff *skb;		... include/linux/skbuff.h
 *	struct device *netdev		... include/linux/netdevice.h
 *
 *	sock = sk->socket;
 *	sk = sock->sk;
 *	sk = skb->sk;
 *	netdev = skb->dev;
 *
 *	packet classifier uses "struct sock"
 */

/*asmlinkage rk_reserve_t
sys_net_reserve_create(rk_resource_set_t rs,
		       size_t ub, struct timespec *ut,
		       struct rk_reserve_param *up,

		       int socket_fd, struct ifreq *uifr) */

asmlinkage rk_reserve_t
sys_net_reserve_create (rk_resource_set_t rs, net_reserve_attr_t uattr)
{
  net_reserve_attr_data_t attr;
  rk_reserve_t rsv;
  net_reserve_t netrsv;
  struct timespec t;
  struct rk_reserve_param p;
  struct device *netdev;
  struct ifreq ifr;
  int err, T;

  copy_from_user (&attr, uattr, sizeof(net_reserve_attr_data_t));

  /* correct device name? */

  strcpy (ifr.ifr_name, attr.dev_name);

  netdev = dev_get (ifr.ifr_name);
  if (netdev == NULL)
    {
      printk ("sys_net_reserve_create: invalid device name(%s): using default device eth0\n",
	      ifr.ifr_name);
      
      netdev = dev_base->next;
      //return NULL_RESERVE;
    }

  /*      
     Now, we must use a net_reserve_attr structure!!!
     rsv = net_reserve_create(rs, ub, &t, &p, netdev);
   */

  rsv = net_reserve_create (rs, &attr, netdev);

  printk("rsv %p\n", rsv);
  if (rsv)
    {
      /* */
      netrsv = rsv->rsv_rsv;
      netrsv->netdev = netdev;

      /*
      sk->ip_tos =
	(int) (attr.period.tv_sec * 1000 + attr.period.tv_nsec / MICROSEC);
      */
      /* XXX: must be examined later */
      //netrsv->buffer_space = 60 * 1500;
      //sk->ip_tos = 1;
    }

  /* net_reserve_qdisc active ? */
  if (net_reserve_qdisc == NULL)
    {
      /* activate net reserve Qdisc */
      net_reserve_activate (netdev);
    }

  return rsv;
}

asmlinkage int
sys_net_reserve_ctl (rk_reserve_t rsv, net_reserve_attr_t net_attr)
{
  net_reserve_attr_data_t local_attr;

  if (copy_from_user
      (&local_attr, net_attr, sizeof (net_reserve_attr_data_t)))
    {
      return -EFAULT;
    }

  return net_reserve_ctl (rsv, &local_attr);
}

/* this ceiling computation works faster by assuming that the seconds
 * field is less than 1000 seconds, and by truncating the nanoseconds field.
 * If the seconds field is greater than 1000 seconds, a valid but VERY 
 * inefficient response will result!
 */
int
efficient_timespec_ceil (struct timespec dividend, struct timespec divider)
{
  unsigned long dividend_microsecs, divider_microsecs;

  if ((divider.tv_sec == 0) && (divider.tv_nsec == 0))
    {
      return -1;
    }
  if ((dividend.tv_sec >= 1000) || (divider.tv_sec >= 1000))
    {
      /* be ready to pay a BIG penalty now! */
      return (timespec_ceiling (dividend, divider));
    }
#define    MICROSECS_PER_SECOND          1000000

  /* truncate nanoseconds */
  dividend_microsecs = (dividend.tv_sec % 1000) * MICROSECS_PER_SECOND +
    (dividend.tv_nsec / 1000);
  divider_microsecs = (divider.tv_sec % 1000) * MICROSECS_PER_SECOND +
    (divider.tv_nsec / 1000);
  return (ceiling (dividend_microsecs, divider_microsecs));
}


int
ceiling (unsigned long dividend, unsigned long divider)
{
  int quotient;
  quotient = dividend / divider;

  if (divider * quotient == dividend)
    return (quotient);
  else
    return (++quotient);
}

int
timespec_ceiling (struct timespec dividend, struct timespec divider)
{
  int quotient = 1;
  struct timespec divider1;

  divider1 = divider;
  while (timespec_gt (dividend, divider1))
    {
      timespec_add (divider1, divider);
      quotient++;
    }
  return (quotient);
}

struct timespec
timespec_mult (struct timespec multiplicand, int multiplier)
{
  struct timespec result;

  result.tv_sec = 0;
  result.tv_nsec = 0;

  do
    {
      timespec_add (result, multiplicand);
    }
  while (--multiplier);

  return (result);
}


/*
 * Linux proc file system interface
 */

static int
net_reserve_read_proc (rk_reserve_t rsv, char *buf)
{
  char *p = buf;
  net_reserve_t netrsv;
  net_capacity_t ave;

  netrsv = rsv->rsv_rsv;
  if (netrsv->average.total_count)
    {
      ave = netrsv->average.total_utils / netrsv->average.total_count;
    }
  else
    {
      if (netrsv->available_amount)
	{
	  ave = CAPACITY_OF (netrsv->used_amount, netrsv->period_ticks);
	}
      else
	{
	  ave = CAPACITY_OF (netrsv->used_amount, 1);
	}
    }


  p += sprintf (p,
		"utilization (ave max min): %lu.%02lu %lu.%02lu %lu.%02lu\n",
		CAPACITY_INT (ave), CAPACITY_FRAC (ave),
		CAPACITY_INT (netrsv->max_utilization),
		CAPACITY_FRAC (netrsv->max_utilization),
		CAPACITY_INT (netrsv->min_utilization),
		CAPACITY_FRAC (netrsv->min_utilization));
  p +=
    sprintf (p, "last used |used | available: %d | %d |%d\n",
	     netrsv->prev_used_amount, netrsv->used_amount,
	     netrsv->available_amount);
  p += sprintf (p, "count: %lu\n", netrsv->average.total_count);
  p += sprintf (p, "reserved: %lu.%02lu\n",
		CAPACITY_INT (netrsv->capacity),
		CAPACITY_FRAC (netrsv->capacity));

  return (p - buf);
}

int
net_reserve_status_proc (char *buf)
{
  char *p = buf;

  p += sprintf (p, "net_reserve_current_capacity: %lu.%02lu\n",
		CAPACITY_INT (net_reserve_current_capacity),
		CAPACITY_FRAC (net_reserve_current_capacity));

#ifdef NET_MEAS

  p += sprintf (p, "sock_processing %lu\n", sock_proc);
  p += sprintf (p, "sockstart %lu\n", sock_start);
  p += sprintf (p, "sock %lu\n", sock_end);
  p += sprintf (p, "qdisc_nq %lu\n", qdisc_nq);
  p += sprintf (p, "qdisc wake %lu\n", qdisc_wak);
  p += sprintf (p, "dest %lu\n", devstart);
  p += sprintf (p, "dend %lu\n", devend);
  p += sprintf (p, "dev_processing %lu\n", dev_proc);
#endif

  return (p - buf);
}
#endif /* linux */

#else /* RK_NET_RSV */
#ifdef	linux
asmlinkage rk_reserve_t
sys_net_reserve_create (rk_resource_set_t rs,
			size_t ub, struct timespec *ut,
			struct rk_reserve_param *up,
			int socket_fd, void *uifr)
{
  return NULL;
}

asmlinkage int
sys_net_reserve_ctl (rk_resource_set_t rs, rk_reserve_t rsv,
		     size_t ub, struct timespec *ut)
{
  return RK_ERROR;
}

#endif /* linux */
#endif /* RK_NET_RSV */
