// 2012-09-10 09:28
// Benchmark based on dist-norm.c in ITM-20120109-preCleanup.tar.xz.

#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <asm/tsc.h>
#include "bench_constants.h"

#include <linux/errno.h>
#define EOBSCURE EPERM   /* Failed without explanation */

MODULE_LICENSE ("GPL and additional rights");
MODULE_AUTHOR ("David Flater");
MODULE_DESCRIPTION ("Kernelspace microbenchmark testing module");

// static?
int32_t bigGlobalCarray[bigLimit];

// 100 seems to be safe.
// 200 results in retries trying to read /proc/benchmark.
#define no_interrupt_repeats 100

static int benchmark_proc_show (struct seq_file *m, void *v) {
  static unsigned long snum = 0;
  uint32_t i, j;
  uint64_t before, after;
  unsigned long irqstate = 0;

  seq_printf (m, "BEGIN %lu\n", snum++);
  local_irq_save (irqstate);

  for (j=0; j<no_interrupt_repeats; ++j) {

    mb();                    // mfence
    before = get_cycles();   // rdtsc
    mb();                    // mfence

    for (i=0; i<bigLimit; ++i)
      bigGlobalCarray[i] = setVal;

    mb();                    // mfence
    after = get_cycles();    // rdtsc
    mb();                    // mfence

    // This is elapsed time in CPU cycles.  Overflow is possible; however,
    // the printed result should be correct even when the timer and the
    // subtraction wrap around... assuming that there's not an even larger
    // data type that the uint64s could be promoted to.
    seq_printf (m, "%20llu\n", after - before);
  }

  local_irq_restore (irqstate);
  return 0;
}

static int benchmark_proc_open (struct inode *inode, struct file *file) {
  return single_open (file, benchmark_proc_show, NULL);
}

static const struct file_operations benchmark_proc_fops = {
  .open	     = benchmark_proc_open,
  .read	     = seq_read,
  .llseek    = seq_lseek,
  .release   = single_release,
};

static int __init proc_benchmark_init (void) {
  // No umask security; accessible by anyone.
  if (!proc_create ("benchmark", 0444, NULL, &benchmark_proc_fops))
    return -EOBSCURE;
  return 0;
}

static void __exit proc_benchmark_exit (void) {
  remove_proc_entry ("benchmark", NULL);
}

module_init (proc_benchmark_init);
module_exit (proc_benchmark_exit);
