#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include "logbuf.h"
#include "ringbuf.h"

/** Per-CPU data **/
static struct ringbuf kmemprof_ringbuf[NR_CPUS];

static int logbuf_size = 256*PAGE_SIZE;
MODULE_PARM(logbuf_size, "i");

static struct file_operations logbuf_fops; 

extern struct proc_dir_entry * kmemprof_trace_dir;

int kmemprof_logbuf_create(int num_cpus)
{
	int ret, i;
	struct proc_dir_entry * entry;
	char name[100];

#ifdef DEBUG
	BUG_ON(num_cpus > NR_CPUS);
#endif
	for (i = 0; i < num_cpus; i++) {
		struct ringbuf * ringbuf = &kmemprof_ringbuf[i];

		if ((ret = ringbuf_init(ringbuf, logbuf_size)) < 0) {
			goto fail;
		}
		sprintf(name, "logbuf-%d", i);
		if (!(entry = create_proc_entry(name, 0, kmemprof_trace_dir))) {
			ret = -ENOMEM;
			goto fail;
		}
		entry->data = ringbuf;
		entry->proc_fops = &logbuf_fops;
	}

	return 0;

fail:
	while (--i >=0) {
		struct ringbuf * ringbuf = &kmemprof_ringbuf[i];

		sprintf(name, "logbuf-%d", i);
		remove_proc_entry(name, kmemprof_trace_dir);
		ringbuf_destroy(ringbuf);
	}

	return ret;
}
extern void kmemprof_logbuf_remove()
{
	int i;
	char name[100];

	for (i = 0; i < NR_CPUS; i++) {
		struct ringbuf * ringbuf = &kmemprof_ringbuf[i];

		sprintf(name, "logbuf-%d", i);
		remove_proc_entry(name, kmemprof_trace_dir);
		ringbuf_destroy(ringbuf);
	}
}

int kmemprof_logbuf_write (void * buf, int count)
{
	struct ringbuf * ringbuf = &kmemprof_ringbuf[smp_processor_id()];

	return (!ringbuf->buf_size) ? 0 : ringbuf_write(ringbuf, buf, count);
}

static int logbuf_open (struct inode * inode, struct file * file)
{
	struct proc_dir_entry * de = inode->u.generic_ip;
	struct ringbuf * desc = de->data;

	file->private_data = desc;

	return 0;
}

static ssize_t logbuf_read (struct file * file, char *buf, size_t count,
		loff_t * pos)
{
	int ret = 0;
	int cpuid;
	struct ringbuf * desc = file->private_data;
	unsigned long nent = count / sizeof(struct ringbuf_entry);

	cpuid = (kmemprof_ringbuf - desc)/sizeof(struct ringbuf);

	while (smp_processor_id() != cpuid) {
		if (signal_pending(current))
			return ret;
		yield();
	}

	ret = ringbuf_read_user(desc, buf, nent);
	ret *= sizeof(struct ringbuf_entry);
	*pos += ret;

	return ret;
}

static ssize_t logbuf_write (struct file * file, const char *buf,
		size_t count, loff_t * pos)
{
	struct ringbuf * desc = file->private_data;

	ringbuf_reset(desc);

	return count;
}

static int logbuf_release (struct inode * inode, struct file * file)
{
	return 0;
}

static unsigned int logbuf_poll (struct file * file,
		struct poll_table_struct * table)
{
	return 0;
}

static struct file_operations logbuf_fops = {
	.open = logbuf_open,
	.read = logbuf_read,
	.write = logbuf_write,
	.release = logbuf_release,
	.poll = logbuf_poll
};

