/* Hey Emacs, this is -*-C-*- 
 ******************************************************************************
 * raw.c -- Measure the raw performance.
 * 
 * Author          : Peter Bosch
 * Created On      : Thu Mar  4 13:41:31 1999
 * Last Modified By: Peter Bosch
 * Last Modified On: Sun Dec 12 12:54:20 1999
 * Status          : Unknown, Use with caution!
 * 
 * Unless other notices are present in any part of this file
 * explicitly claiming copyrights for other people and/or 
 * organizations, the contents of this file is fully copyright 
 * (C) 1999 Peter Bosch, all rights reserved.
 * 
 ******************************************************************************
 */

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include <asm/page.h>

#include <cwif.h>

#include <sched.h>

#define _IO(a, b)	(((a) << 8)|(b))
#define BLKGETSIZE	_IO(0x12,96)	/* return device size */
#define K 1024

static char *progname;

static int bsize = 128 * K;
static int nruns = 100;
static int bandwidth;
static int align;

static Time_t
now()
{
  struct timeval tv;

  if (gettimeofday(&tv, NULL) < 0) {
    fprintf(stderr, "%s: Cannot get time of day %s\n", progname, 
	    strerror(errno));
    exit(1);
  }
  return S(tv.tv_sec) + US(tv.tv_usec);
}

static void
_wait(Time_t until)
{
  Time_t t;
  struct timeval tv;

  t = now();
  if (until < t) return;

  tv.tv_sec  = (int)((until - t) / S(1));
  tv.tv_usec = (int)((until - t) % S(1)) / US(1);

  if (select(0, NULL, NULL, NULL, &tv) < 0) {
    fprintf(stderr, "%s: Cannot wait: %s\n", progname, strerror(errno));
    exit(1);
  }
}

static void
usage()
{
  fprintf(stderr, "Usage: %s [-a] [-B bw] [-b bsize] [-n nruns] filename\n",
	  progname);
  exit(1);
}

int
main(int argc, char **argv)
{
  struct sched_param schedpars;
  int fd, opt, n;
  char *fname;
  uint8_t *buf;
  uint32_t fsize;
  uint64_t offset;
  Time_t ibd, lastblock;

  progname = argv[0];
  while ((opt = getopt(argc, argv, "aB:b:n:?")) != EOF) {
    switch (opt) {
    case 'a':
      align = 1;
      break;
    case 'b':
      bsize = strtol(optarg, (char **)NULL, 0);
      break;
    case 'n':
      nruns = strtol(optarg, (char **)NULL, 0);
      break;
    case 'B':
      bandwidth = strtol(optarg, (char **)NULL, 0);
      break;
    case '?':
      usage();
    }
  }
  if (optind != argc - 1) usage();
  fname = argv[optind++];

  fd = open(fname, O_RDONLY);
  if (fd < 0) {
    fprintf(stderr, "%s: Cannot open %s: %s\n",
	    progname, fname, strerror(errno));
    exit(1);
  }

  if (bandwidth) {
    cwqos_t qos;

    qos.qos_bw = bandwidth;
    qos.qos_bs = bsize;

    if (ioctl(fd, CW_QOS, &qos) < 0) {
      fprintf(stderr, "%s: Cannot allocate bandwidth for %s: %s\n",
	      progname, fname, strerror(errno));
      exit(1);
    }

    ibd = S(bsize) / bandwidth;
  }

  if (0 && ioctl(fd, BLKGETSIZE, &fsize) < 0) {
    fprintf(stderr, "%s: Cannot get device size from %s: %s\n",
	    progname, fname, strerror(errno));
    exit(1);
  }
  fsize <<= 9;

  buf = (uint8_t *)malloc(bsize + PAGE_SIZE - 1);
  if (buf == NULL) {
    fprintf(stderr, "%s: Cannot allocate %d bytes\n", progname, bsize);
    exit(1);
  }
  if (align)
    buf = (uint8_t *)(((uint32_t)buf + PAGE_SIZE - 1) & PAGE_MASK);
  printf("%s: buf x%08X\n", progname, buf);

  if (mlock(buf, bsize) < 0) {
    fprintf(stderr, "%s: Cannot lock memory: %s\n", progname, strerror(errno));
    exit(1);
  }

  if ((schedpars.sched_priority = sched_get_priority_max(SCHED_FIFO)) < 0) {
    fprintf(stderr, "%s: Cannot get max schedule prio: %s\n",
	    progname, strerror(errno));
    exit(1);
  }

  schedpars.sched_priority--;
  if (0 && sched_setscheduler(0, SCHED_FIFO, &schedpars) < 0) {
    fprintf(stderr, "%s: Cannot set schedule: %s\n",
	    progname, strerror(errno));
    exit(1);
  }

  lastblock = now();
  for (n = 0; n != nruns; n++) {
    Time_t delta;

    if (lseek(fd, 0, SEEK_SET) < 0) {
      fprintf(stderr, "%s: Cannot reposition %s: %s\n",
	      progname, fname, strerror(errno));
      exit(1);
    }
    offset = 0;

    while (offset + bsize <= fsize) {
      int rv;
      
      if (bandwidth)
	_wait(lastblock + ibd);

      lastblock = delta = now();
      rv = read(fd, buf, bsize);
      delta = now() - delta;

      if (rv < 0) {
	fprintf(stderr, "%s: Cannot read from %s: %s\n",
		progname, fname, strerror(errno));
	exit(1);
      }

      if (rv != bsize) {
	fprintf(stderr, "%s: Requested %d, got %d from %s\n",
		progname, bsize, rv, fname);
	exit(1);
      }

      offset += bsize;
      printf("%d bytes took %d.%09d\n",
	     bsize, 
	     (int)(delta / S(1)),
	     (int)(delta % S(1)));
      break;
    }
  }
  exit(0);
}

  
      
