293 lines
7.4 KiB
C
293 lines
7.4 KiB
C
|
/*
|
||
|
* 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);
|