summaryrefslogtreecommitdiffstats
path: root/misc-modules/jit.c
diff options
context:
space:
mode:
Diffstat (limited to 'misc-modules/jit.c')
-rw-r--r--misc-modules/jit.c292
1 files changed, 292 insertions, 0 deletions
diff --git a/misc-modules/jit.c b/misc-modules/jit.c
new file mode 100644
index 0000000..62978b0
--- /dev/null
+++ b/misc-modules/jit.c
@@ -0,0 +1,292 @@
+/*
+ * jit.c -- the just-in-time module
+ *
+ * Copyright (C) 2001,2003 Alessandro Rubini and Jonathan Corbet
+ * Copyright (C) 2001,2003 O'Reilly & Associates
+ *
+ * The source code in this file can be freely used, adapted,
+ * and redistributed in source or binary form, so long as an
+ * acknowledgment appears in derived source files. The citation
+ * should list that the code comes from the book "Linux Device
+ * Drivers" by Alessandro Rubini and Jonathan Corbet, published
+ * by O'Reilly & Associates. No warranty is attached;
+ * we cannot take responsibility for errors or fitness for use.
+ *
+ * $Id: jit.c,v 1.16 2004/09/26 07:02:43 gregkh Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+
+#include <asm/hardirq.h>
+/*
+ * This module is a silly one: it only embeds short code fragments
+ * that show how time delays can be handled in the kernel.
+ */
+
+int delay = HZ; /* the default delay, expressed in jiffies */
+
+module_param(delay, int, 0);
+
+MODULE_AUTHOR("Alessandro Rubini");
+MODULE_LICENSE("Dual BSD/GPL");
+
+/* use these as data pointers, to implement four files in one function */
+enum jit_files {
+ JIT_BUSY,
+ JIT_SCHED,
+ JIT_QUEUE,
+ JIT_SCHEDTO
+};
+
+/*
+ * This function prints one line of data, after sleeping one second.
+ * It can sleep in different ways, according to the data pointer
+ */
+int jit_fn(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ unsigned long j0, j1; /* jiffies */
+ wait_queue_head_t wait;
+
+ init_waitqueue_head (&wait);
+ j0 = jiffies;
+ j1 = j0 + delay;
+
+ switch((long)data) {
+ case JIT_BUSY:
+ while (time_before(jiffies, j1))
+ cpu_relax();
+ break;
+ case JIT_SCHED:
+ while (time_before(jiffies, j1)) {
+ schedule();
+ }
+ break;
+ case JIT_QUEUE:
+ wait_event_interruptible_timeout(wait, 0, delay);
+ break;
+ case JIT_SCHEDTO:
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout (delay);
+ break;
+ }
+ j1 = jiffies; /* actual value after we delayed */
+
+ len = sprintf(buf, "%9li %9li\n", j0, j1);
+ *start = buf;
+ return len;
+}
+
+/*
+ * This file, on the other hand, returns the current time forever
+ */
+int jit_currentime(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ struct timeval tv1;
+ struct timespec tv2;
+ unsigned long j1;
+ u64 j2;
+
+ /* get them four */
+ j1 = jiffies;
+ j2 = get_jiffies_64();
+ do_gettimeofday(&tv1);
+ tv2 = current_kernel_time();
+
+ /* print */
+ len=0;
+ len += sprintf(buf,"0x%08lx 0x%016Lx %10i.%06i\n"
+ "%40i.%09i\n",
+ j1, j2,
+ (int) tv1.tv_sec, (int) tv1.tv_usec,
+ (int) tv2.tv_sec, (int) tv2.tv_nsec);
+ *start = buf;
+ return len;
+}
+
+/*
+ * The timer example follows
+ */
+
+int tdelay = 10;
+module_param(tdelay, int, 0);
+
+/* This data structure used as "data" for the timer and tasklet functions */
+struct jit_data {
+ struct timer_list timer;
+ struct tasklet_struct tlet;
+ int hi; /* tasklet or tasklet_hi */
+ wait_queue_head_t wait;
+ unsigned long prevjiffies;
+ unsigned char *buf;
+ int loops;
+};
+#define JIT_ASYNC_LOOPS 5
+
+void jit_timer_fn(unsigned long arg)
+{
+ struct jit_data *data = (struct jit_data *)arg;
+ unsigned long j = jiffies;
+ data->buf += sprintf(data->buf, "%9li %3li %i %6i %i %s\n",
+ j, j - data->prevjiffies, in_interrupt() ? 1 : 0,
+ current->pid, smp_processor_id(), current->comm);
+
+ if (--data->loops) {
+ data->timer.expires += tdelay;
+ data->prevjiffies = j;
+ add_timer(&data->timer);
+ } else {
+ wake_up_interruptible(&data->wait);
+ }
+}
+
+/* the /proc function: allocate everything to allow concurrency */
+int jit_timer(char *buf, char **start, off_t offset,
+ int len, int *eof, void *unused_data)
+{
+ struct jit_data *data;
+ char *buf2 = buf;
+ unsigned long j = jiffies;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ init_timer(&data->timer);
+ init_waitqueue_head (&data->wait);
+
+ /* write the first lines in the buffer */
+ buf2 += sprintf(buf2, " time delta inirq pid cpu command\n");
+ buf2 += sprintf(buf2, "%9li %3li %i %6i %i %s\n",
+ j, 0L, in_interrupt() ? 1 : 0,
+ current->pid, smp_processor_id(), current->comm);
+
+ /* fill the data for our timer function */
+ data->prevjiffies = j;
+ data->buf = buf2;
+ data->loops = JIT_ASYNC_LOOPS;
+
+ /* register the timer */
+ data->timer.data = (unsigned long)data;
+ data->timer.function = jit_timer_fn;
+ data->timer.expires = j + tdelay; /* parameter */
+ add_timer(&data->timer);
+
+ /* wait for the buffer to fill */
+ wait_event_interruptible(data->wait, !data->loops);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ buf2 = data->buf;
+ kfree(data);
+ *eof = 1;
+ return buf2 - buf;
+}
+
+void jit_tasklet_fn(unsigned long arg)
+{
+ struct jit_data *data = (struct jit_data *)arg;
+ unsigned long j = jiffies;
+ data->buf += sprintf(data->buf, "%9li %3li %i %6i %i %s\n",
+ j, j - data->prevjiffies, in_interrupt() ? 1 : 0,
+ current->pid, smp_processor_id(), current->comm);
+
+ if (--data->loops) {
+ data->prevjiffies = j;
+ if (data->hi)
+ tasklet_hi_schedule(&data->tlet);
+ else
+ tasklet_schedule(&data->tlet);
+ } else {
+ wake_up_interruptible(&data->wait);
+ }
+}
+
+/* the /proc function: allocate everything to allow concurrency */
+int jit_tasklet(char *buf, char **start, off_t offset,
+ int len, int *eof, void *arg)
+{
+ struct jit_data *data;
+ char *buf2 = buf;
+ unsigned long j = jiffies;
+ long hi = (long)arg;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ init_waitqueue_head (&data->wait);
+
+ /* write the first lines in the buffer */
+ buf2 += sprintf(buf2, " time delta inirq pid cpu command\n");
+ buf2 += sprintf(buf2, "%9li %3li %i %6i %i %s\n",
+ j, 0L, in_interrupt() ? 1 : 0,
+ current->pid, smp_processor_id(), current->comm);
+
+ /* fill the data for our tasklet function */
+ data->prevjiffies = j;
+ data->buf = buf2;
+ data->loops = JIT_ASYNC_LOOPS;
+
+ /* register the tasklet */
+ tasklet_init(&data->tlet, jit_tasklet_fn, (unsigned long)data);
+ data->hi = hi;
+ if (hi)
+ tasklet_hi_schedule(&data->tlet);
+ else
+ tasklet_schedule(&data->tlet);
+
+ /* wait for the buffer to fill */
+ wait_event_interruptible(data->wait, !data->loops);
+
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ buf2 = data->buf;
+ kfree(data);
+ *eof = 1;
+ return buf2 - buf;
+}
+
+
+
+int __init jit_init(void)
+{
+ create_proc_read_entry("currentime", 0, NULL, jit_currentime, NULL);
+ create_proc_read_entry("jitbusy", 0, NULL, jit_fn, (void *)JIT_BUSY);
+ create_proc_read_entry("jitsched",0, NULL, jit_fn, (void *)JIT_SCHED);
+ create_proc_read_entry("jitqueue",0, NULL, jit_fn, (void *)JIT_QUEUE);
+ create_proc_read_entry("jitschedto", 0, NULL, jit_fn, (void *)JIT_SCHEDTO);
+
+ create_proc_read_entry("jitimer", 0, NULL, jit_timer, NULL);
+ create_proc_read_entry("jitasklet", 0, NULL, jit_tasklet, NULL);
+ create_proc_read_entry("jitasklethi", 0, NULL, jit_tasklet, (void *)1);
+
+ return 0; /* success */
+}
+
+void __exit jit_cleanup(void)
+{
+ remove_proc_entry("currentime", NULL);
+ remove_proc_entry("jitbusy", NULL);
+ remove_proc_entry("jitsched", NULL);
+ remove_proc_entry("jitqueue", NULL);
+ remove_proc_entry("jitschedto", NULL);
+
+ remove_proc_entry("jitimer", NULL);
+ remove_proc_entry("jitasklet", NULL);
+ remove_proc_entry("jitasklethi", NULL);
+}
+
+module_init(jit_init);
+module_exit(jit_cleanup);