/************************************************************
 *
 * COPYRIGHT (C) HITACHI,LTD. 2002 ALL RIGHTS RESERVED.
 * WRITTEN BY HITACHI SYSTEMS DEVELOPMENT LABORATORY,
 *            HITACHI CENTRAL RESEARCH LABORATORY.
 *
 * Created by M. Hiramatsu <hiramatu@sdl.hitachi.co.jp>
 * 
 * lightweight logging (without disabling irq)
 * !NOTE!: Using carefully, Don't jump/link to current buffer.
 *         If jump to current buffer in interrupts, 
 *         consistency of recorded events may be broken.
 *
 ************************************************************/

#include<linux/version.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include <linux/sched.h>
#include<linux/lkst_private.h>

static int	evhandler_id = LKST_EVHANDLER_ID_VOID;

static int	lkst_mod_init(void);
static void	lkst_mod_cleanup(void);

module_init(lkst_mod_init);
module_exit(lkst_mod_cleanup);

MODULE_AUTHOR("M. Hiramatsu <hiramatu@sdl.hitachi.co.jp>");
MODULE_DESCRIPTION("LKST module event record handler without irq-disabling.");
MODULE_LICENSE("GPL");

MODULE_PARM(evhandler_id, "i");
MODULE_PARM_DESC(evhandler_id, "id of event-hanlder to regist.");

static void lkst_evhandler_lightweight(void *phookrec, int event_type,
				       lkst_arg_t arg1, lkst_arg_t arg2,
				       lkst_arg_t arg3, lkst_arg_t arg4)
{
	struct lkst_event_buffer *buffer;
	int pos;
	int baseid;
	if (! LKST_CURRENT_BUFFER_LOAD(buffer)) return ;

	/* get a entry position */ {
		baseid = local_atomic_read_and_add(1, &buffer->baseid);
		pos = baseid - buffer->write_offset;
	}
	/* adjust the entry position */
	if ( pos >= buffer->size ) {
		pos %= buffer->size;
		/* Check buffer wrapping. */
		if ( pos == 0 ) {
			buffer->write_offset += buffer->size;
			/* Wrapped. */
			/* Process buffer-over-flow event. */
			lkst_entry_log(NULL, 
				       LKST_ETYPE_LKST_BUFF_OVFLOW,
				       smp_processor_id(),
				       buffer->next,
				       event_type,
				       LKST_ARGP(buffer));
		}
	}
	/* logging */ {
		register struct lkst_event_record * event_entry_p = 
			&(LKST_BUFFER_BODY(buffer)[pos]);
		event_entry_p->log_recid = baseid;
		event_entry_p->log_time = lkst_evhandlerprim_mc();
		event_entry_p->log_event_type = event_type;
		event_entry_p->log_pid = current->pid; /* current pid */ ;
		event_entry_p->log_arg1 = arg1;
		event_entry_p->log_arg2 = arg2;
		event_entry_p->log_arg3 = arg3;
		event_entry_p->log_arg4 = arg4;
	}
}

static int lkst_mod_init()
{
	int	retval;

	/* Register an event handler for light logging. */
	retval = lkst_evhandler_register(evhandler_id,
					 "LOG_LIGHTWEIGHT",
					 &lkst_evhandler_lightweight,
					 NULL);
	if (retval < 0) {
		printk(KERN_ERR "cannot register event-handler function!\n");
		return retval;
	}
	evhandler_id = (lkst_evhandler_id) retval;

	return 0;
}

static void lkst_mod_cleanup()
{
	/* deregister the event handler. */
	if (evhandler_id != LKST_EVHANDLER_ID_VOID)
		lkst_evhandler_unregister((lkst_evhandler_id)evhandler_id);

	return;
}

