#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define BLKHASHPROF	_IOR(0x12,108,sizeof(struct bio_hash_stats))
#define BLKHASHCLEAR	_IO(0x12,109)


#define MAX_PROFILE_BUCKETS	64
struct bio_hash_stats {
	unsigned long nr_lookups;
	unsigned long nr_hits;
	unsigned long nr_inserts;
	unsigned long max_bucket_size;
	unsigned long bucket_size[MAX_PROFILE_BUCKETS + 1];

	unsigned long q_nr_back_lookups;
	unsigned long q_nr_back_hits;
	unsigned long q_nr_back_merges;
	unsigned long q_nr_front_lookups;
	unsigned long q_nr_front_hits;
	unsigned long q_nr_front_merges;
};

int main(int argc, char *argv[])
{
	struct bio_hash_stats *stat;
	int fd, i, c, reset = 0;
	float hitrate;

	if (argc < 2) {
		fprintf(stderr, "need device as argument\n");
		return 1;
	}

	fprintf(stdout, "Using device %s\n", argv[1]);
	fd = open(argv[1], O_RDONLY);
	if (fd == -1) {
		perror("open");
		return 2;
	}

	for (;;) {
		c = getopt(argc, argv, "r");
		if (c == -1)
			break;
		switch (c) {
			case 'r': reset = 1; break;
			default: break;
		}
	}

	if (reset) {
		fprintf(stdout, "Clearing hash statistics\n");
		if (ioctl(fd, BLKHASHCLEAR) == -1) {
			perror("ioctl");
			return 3;
		}
		return 0;
	}

	stat = malloc(sizeof(struct bio_hash_stats));

	if (ioctl(fd, BLKHASHPROF, stat) == -1) {
		perror("ioctl");
		return 3;
	}

	fprintf(stdout, "Nr lookups\t\t%lu\n", stat->nr_lookups);
	fprintf(stdout, "Nr hits\t\t\t%lu\n", stat->nr_hits);
	fprintf(stdout, "Nr inserts\t\t%lu\n\n", stat->nr_inserts);

	fprintf(stdout, "Nr back lookups\t\t%lu\n", stat->q_nr_back_lookups);
	fprintf(stdout, "Nr back hits\t\t%lu\n", stat->q_nr_back_hits);
	fprintf(stdout, "Nr back merges\t\t%lu\n", stat->q_nr_back_merges);
	hitrate = 100.0;
	if (stat->q_nr_back_lookups)
		hitrate = (stat->q_nr_back_merges * 100.0) / stat->q_nr_back_lookups;
	fprintf(stdout, "Hit rate\t\t%.02f%%\n\n",  hitrate);

	fprintf(stdout, "Nr front lookups\t%lu\n", stat->q_nr_front_lookups);
	fprintf(stdout, "Nr front hits\t\t%lu\n", stat->q_nr_front_hits);
	fprintf(stdout, "Nr front merges\t\t%lu\n", stat->q_nr_front_merges);
	hitrate = 100.0;
	if (stat->q_nr_front_lookups)
		hitrate = (stat->q_nr_front_merges * 100.0) / stat->q_nr_front_lookups;
	fprintf(stdout, "Hit rate\t\t%.02f%%\n\n",  hitrate);

	fprintf(stdout, "Max fill\t\t%lu\n", stat->max_bucket_size + 1);
	fprintf(stdout, "Fill distribution over time:\n");
	for (i = 0; i <= MAX_PROFILE_BUCKETS && i <= stat->max_bucket_size; i++)
		fprintf(stdout, "%02d: %lu\n", i + 1, stat->bucket_size[i]);

	return 0;
}
