summaryrefslogtreecommitdiffstats
path: root/misc-modules/jiq.c
diff options
context:
space:
mode:
Diffstat (limited to 'misc-modules/jiq.c')
-rw-r--r--misc-modules/jiq.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/misc-modules/jiq.c b/misc-modules/jiq.c
new file mode 100644
index 0000000..dc7da28
--- /dev/null
+++ b/misc-modules/jiq.c
@@ -0,0 +1,264 @@
+/*
+ * jiq.c -- the just-in-queue module
+ *
+ * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
+ * Copyright (C) 2001 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: jiq.c,v 1.7 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/sched.h>
+#include <linux/kernel.h>
+#include <linux/fs.h> /* everything... */
+#include <linux/proc_fs.h>
+#include <linux/errno.h> /* error codes */
+#include <linux/workqueue.h>
+#include <linux/preempt.h>
+#include <linux/interrupt.h> /* tasklets */
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * The delay for the delayed workqueue timer file.
+ */
+static long delay = 1;
+module_param(delay, long, 0);
+
+
+/*
+ * This module is a silly one: it only embeds short code fragments
+ * that show how enqueued tasks `feel' the environment
+ */
+
+#define LIMIT (PAGE_SIZE-128) /* don't print any more after this size */
+
+/*
+ * Print information about the current environment. This is called from
+ * within the task queues. If the limit is reched, awake the reading
+ * process.
+ */
+static DECLARE_WAIT_QUEUE_HEAD (jiq_wait);
+
+
+static struct work_struct jiq_work;
+
+
+
+/*
+ * Keep track of info we need between task queue runs.
+ */
+static struct clientdata {
+ int len;
+ char *buf;
+ unsigned long jiffies;
+ long delay;
+} jiq_data;
+
+#define SCHEDULER_QUEUE ((task_queue *) 1)
+
+
+
+static void jiq_print_tasklet(unsigned long);
+static DECLARE_TASKLET(jiq_tasklet, jiq_print_tasklet, (unsigned long)&jiq_data);
+
+
+/*
+ * Do the printing; return non-zero if the task should be rescheduled.
+ */
+static int jiq_print(void *ptr)
+{
+ struct clientdata *data = ptr;
+ int len = data->len;
+ char *buf = data->buf;
+ unsigned long j = jiffies;
+
+ if (len > LIMIT) {
+ wake_up_interruptible(&jiq_wait);
+ return 0;
+ }
+
+ if (len == 0)
+ len = sprintf(buf," time delta preempt pid cpu command\n");
+ else
+ len =0;
+
+ /* intr_count is only exported since 1.3.5, but 1.99.4 is needed anyways */
+ len += sprintf(buf+len, "%9li %4li %3i %5i %3i %s\n",
+ j, j - data->jiffies,
+ preempt_count(), current->pid, smp_processor_id(),
+ current->comm);
+
+ data->len += len;
+ data->buf += len;
+ data->jiffies = j;
+ return 1;
+}
+
+
+/*
+ * Call jiq_print from a work queue
+ */
+static void jiq_print_wq(void *ptr)
+{
+ struct clientdata *data = (struct clientdata *) ptr;
+
+ if (! jiq_print (ptr))
+ return;
+
+ if (data->delay)
+ schedule_delayed_work(&jiq_work, data->delay);
+ else
+ schedule_work(&jiq_work);
+}
+
+
+
+static int jiq_read_wq(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ DEFINE_WAIT(wait);
+
+ jiq_data.len = 0; /* nothing printed, yet */
+ jiq_data.buf = buf; /* print in this place */
+ jiq_data.jiffies = jiffies; /* initial time */
+ jiq_data.delay = 0;
+
+ prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE);
+ schedule_work(&jiq_work);
+ schedule();
+ finish_wait(&jiq_wait, &wait);
+
+ *eof = 1;
+ return jiq_data.len;
+}
+
+
+static int jiq_read_wq_delayed(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ DEFINE_WAIT(wait);
+
+ jiq_data.len = 0; /* nothing printed, yet */
+ jiq_data.buf = buf; /* print in this place */
+ jiq_data.jiffies = jiffies; /* initial time */
+ jiq_data.delay = delay;
+
+ prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE);
+ schedule_delayed_work(&jiq_work, delay);
+ schedule();
+ finish_wait(&jiq_wait, &wait);
+
+ *eof = 1;
+ return jiq_data.len;
+}
+
+
+
+
+/*
+ * Call jiq_print from a tasklet
+ */
+static void jiq_print_tasklet(unsigned long ptr)
+{
+ if (jiq_print ((void *) ptr))
+ tasklet_schedule (&jiq_tasklet);
+}
+
+
+
+static int jiq_read_tasklet(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ jiq_data.len = 0; /* nothing printed, yet */
+ jiq_data.buf = buf; /* print in this place */
+ jiq_data.jiffies = jiffies; /* initial time */
+
+ tasklet_schedule(&jiq_tasklet);
+ interruptible_sleep_on(&jiq_wait); /* sleep till completion */
+
+ *eof = 1;
+ return jiq_data.len;
+}
+
+
+
+
+/*
+ * This one, instead, tests out the timers.
+ */
+
+static struct timer_list jiq_timer;
+
+static void jiq_timedout(unsigned long ptr)
+{
+ jiq_print((void *)ptr); /* print a line */
+ wake_up_interruptible(&jiq_wait); /* awake the process */
+}
+
+
+static int jiq_read_run_timer(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+
+ jiq_data.len = 0; /* prepare the argument for jiq_print() */
+ jiq_data.buf = buf;
+ jiq_data.jiffies = jiffies;
+
+ init_timer(&jiq_timer); /* init the timer structure */
+ jiq_timer.function = jiq_timedout;
+ jiq_timer.data = (unsigned long)&jiq_data;
+ jiq_timer.expires = jiffies + HZ; /* one second */
+
+ jiq_print(&jiq_data); /* print and go to sleep */
+ add_timer(&jiq_timer);
+ interruptible_sleep_on(&jiq_wait); /* RACE */
+ del_timer_sync(&jiq_timer); /* in case a signal woke us up */
+
+ *eof = 1;
+ return jiq_data.len;
+}
+
+
+
+/*
+ * the init/clean material
+ */
+
+static int jiq_init(void)
+{
+
+ /* this line is in jiq_init() */
+ INIT_WORK(&jiq_work, jiq_print_wq, &jiq_data);
+
+ create_proc_read_entry("jiqwq", 0, NULL, jiq_read_wq, NULL);
+ create_proc_read_entry("jiqwqdelay", 0, NULL, jiq_read_wq_delayed, NULL);
+ create_proc_read_entry("jitimer", 0, NULL, jiq_read_run_timer, NULL);
+ create_proc_read_entry("jiqtasklet", 0, NULL, jiq_read_tasklet, NULL);
+
+ return 0; /* succeed */
+}
+
+static void jiq_cleanup(void)
+{
+ remove_proc_entry("jiqwq", NULL);
+ remove_proc_entry("jiqwqdelay", NULL);
+ remove_proc_entry("jitimer", NULL);
+ remove_proc_entry("jiqtasklet", NULL);
+}
+
+
+module_init(jiq_init);
+module_exit(jiq_cleanup);