/* kprobebio.c
   this is a simple module to get informatoin about block io operations
   Will Cohen
*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/sched.h>
#include <linux/kallsyms.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/hash.h>

#define LOG_2_BINS 8
#define BINS (1<<LOG_2_BINS)

static int count_generic_make_request = 0;
static unsigned long long sectors_transferred = 0;

static struct block_device *bin_bdev[BINS];
static dev_t bin_bd_dev[BINS];
static int bin_count[BINS];
static unsigned long long bin_sectors[BINS];

void inst_generic_make_request(struct bio *bio)
{
  unsigned long b=hash_ptr(bio->bi_bdev, LOG_2_BINS);

  ++count_generic_make_request;
  sectors_transferred += bio_sectors(bio);

  if( bin_bdev[b] == NULL || bin_bdev[b] == bio->bi_bdev) {
	  bin_bdev[b] = bio->bi_bdev;
	  bin_bd_dev[b] = bio->bi_bdev->bd_dev;
	  ++bin_count[b];
	  bin_sectors[b] += bio_sectors(bio);
  }
  jprobe_return();
}

static struct jprobe my_jprobe = {
	.entry = (kprobe_opcode_t *) inst_generic_make_request
};

int init_module(void)
{
	my_jprobe.kp.addr = (kprobe_opcode_t *) generic_make_request;
	if (!my_jprobe.kp.addr) {
		printk("couldn't find %s to plant jprobe\n", "do_fork");
		return -1;
	}

	/* now we are all set to register the probe */
	register_jprobe(&my_jprobe);
	printk("plant jprobe at %p, handler addr %p\n",
	       my_jprobe.kp.addr, my_jprobe.entry);
	return 0;
}

void cleanup_module(void)
{
  int b;

  unregister_jprobe(&my_jprobe);
  printk("jprobe unregistered\n");

  /* print out information */
  printk("generic_make_request() called %d times for %lld sectors.\n",
	 count_generic_make_request, sectors_transferred);
  for (b=0; b<BINS; ++b){
    if (bin_bdev[b] != NULL) 
      printk("bdev 0x%x (%d,%d) %d %lld sectors.\n", bin_bdev[b],
	     MAJOR(bin_bd_dev[b]), MINOR(bin_bd_dev[b]),
	     bin_count[b], bin_sectors[b]);
  }
}

MODULE_LICENSE("GPL");
