An instrumentation patch which attempts to work out where all that idle time
is coming from.

cat /proc/sleepo

fs/select.c:255(schedule_timeout) 119 204415008 1717773 37026565
kernel/exit.c:1056(schedule) 10 47496358 4749635 23686014
fs/namei.c:1247(down) 1 53708 53708 53708
fs/namei.c:344(down) 4 67425 16856 33478
fs/jbd/journal.c:197(schedule) 5 96894718 19378943 44777091

Tells us that four down() attempts at fs/namei.c:344 blocked.  The total
amout of time spent blocked was 67425 microseconds.  The average was 16845 us
and the maximum was 33478 us.

Bear in mind that if 100 processes block at a certain point for one second,
this tool will claim that 100 seconds were spent blocked at that point.

Which is quite reasonable, but some care is needed when interpreting the
results.

The following operations are instrumented:

	down()
	down_read()
	down_write()
	schedule()
	schedule_timeout()
	io_schedule()
	io_schedule_timeout()


CONFIG_SLEEPOMETER is currently broken with CONFIG_PREEMPT due to #include
hell.

Reading /proc/sleepo clears all the current stats.


 arch/i386/Kconfig            |    7 +
 arch/i386/kernel/entry.S     |    8 +
 fs/proc/proc_misc.c          |   17 +++
 include/asm-i386/rwsem.h     |    0 
 include/asm-i386/semaphore.h |   36 +++++++
 include/linux/fs.h           |   11 --
 include/linux/preempt.h      |    2 
 include/linux/rwsem.h        |   34 +++++++
 include/linux/sched.h        |   14 ++-
 include/linux/sleepo.h       |   80 +++++++++++++++++
 kernel/Makefile              |    1 
 kernel/ksyms.c               |    8 +
 kernel/module.c              |    4 
 kernel/sched.c               |   16 +++
 kernel/sleepo.c              |  198 +++++++++++++++++++++++++++++++++++++++++++
 kernel/timer.c               |    4 
 16 files changed, 426 insertions(+), 14 deletions(-)

diff -puN arch/i386/Kconfig~sleepometer arch/i386/Kconfig
--- 25/arch/i386/Kconfig~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/arch/i386/Kconfig	2003-06-15 17:51:15.000000000 -0700
@@ -1760,6 +1760,13 @@ config X86_MPPARSE
 	depends on X86_LOCAL_APIC && !X86_VISWS
 	default y
 
+config SLEEPOMETER
+	bool "sleep instrumentation" if !PREEMPT
+	default n
+	help
+	  Creates /proc/sleepo, and puts things in there which identify
+	  where the kernel is blocking and for how long.
+
 endmenu
 
 source "security/Kconfig"
diff -puN arch/i386/kernel/entry.S~sleepometer arch/i386/kernel/entry.S
--- 25/arch/i386/kernel/entry.S~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/arch/i386/kernel/entry.S	2003-06-15 17:51:15.000000000 -0700
@@ -237,7 +237,11 @@ need_resched:
 	jz restore_all
 	movl $PREEMPT_ACTIVE,TI_PRE_COUNT(%ebp)
 	sti
+#ifdef CONFIG_SLEEPOMETER
+	call sleepo_schedule
+#else
 	call schedule
+#endif
 	movl $0,TI_PRE_COUNT(%ebp)
 	cli
 	jmp need_resched
@@ -332,7 +336,11 @@ work_pending:
 	testb $_TIF_NEED_RESCHED, %cl
 	jz work_notifysig
 work_resched:
+#ifdef CONFIG_SLEEPOMETER
+	call sleepo_schedule
+#else
 	call schedule
+#endif
 	cli				# make sure we don't miss an interrupt
 					# setting need_resched or sigpending
 					# between sampling and the iret
diff -puN fs/proc/proc_misc.c~sleepometer fs/proc/proc_misc.c
--- 25/fs/proc/proc_misc.c~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/fs/proc/proc_misc.c	2003-06-15 17:51:15.000000000 -0700
@@ -298,6 +298,20 @@ static struct file_operations proc_vmsta
 	.release	= seq_release,
 };
 
+#ifdef CONFIG_SLEEPOMETER
+extern struct seq_operations sleepo_op;
+static int sleepo_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &sleepo_op);
+}
+static struct file_operations proc_sleepo_file_operations = {
+	.open		= sleepo_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+#endif
+
 #ifdef CONFIG_PROC_HARDWARE
 static int hardware_read_proc(char *page, char **start, off_t off,
 				 int count, int *eof, void *data)
@@ -695,6 +709,9 @@ void __init proc_misc_init(void)
 	create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations);
 	create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations);
 	create_seq_entry("vmstat",S_IRUGO, &proc_vmstat_file_operations);
+#ifdef CONFIG_SLEEPOMETER
+	create_seq_entry("sleepo",S_IRUGO, &proc_sleepo_file_operations);
+#endif
 	create_seq_entry("diskstats", 0, &proc_diskstats_operations);
 #ifdef CONFIG_MODULES
 	create_seq_entry("modules", 0, &proc_modules_operations);
diff -puN include/asm-i386/semaphore.h~sleepometer include/asm-i386/semaphore.h
--- 25/include/asm-i386/semaphore.h~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/include/asm-i386/semaphore.h	2003-06-15 17:51:15.000000000 -0700
@@ -41,6 +41,10 @@
 #include <linux/wait.h>
 #include <linux/rwsem.h>
 
+#ifdef CONFIG_SLEEPOMETER
+#include <linux/sleepo.h>
+#endif
+
 struct semaphore {
 	atomic_t count;
 	int sleepers;
@@ -111,7 +115,11 @@ asmlinkage void __up(struct semaphore * 
  * "__down_failed" is a special asm handler that calls the C
  * routine that actually waits. See arch/i386/kernel/semaphore.c
  */
+#ifdef CONFIG_SLEEPOMETER
+static inline void sleepo_down(struct semaphore * sem)
+#else
 static inline void down(struct semaphore * sem)
+#endif
 {
 #ifdef WAITQUEUE_DEBUG
 	CHECK_MAGIC(sem->__magic);
@@ -131,11 +139,26 @@ static inline void down(struct semaphore
 		:"memory");
 }
 
+#ifdef CONFIG_SLEEPOMETER
+#define down(sem)							\
+	do {								\
+		static struct sleepo_data sd;				\
+									\
+		sleepo_begin(__FILE__, __LINE__, "down", &sd);		\
+		sleepo_down(sem);					\
+		sleepo_end(&sd);					\
+	} while (0)
+#endif
+
 /*
  * Interruptible try to acquire a semaphore.  If we obtained
  * it, return zero.  If we were interrupted, returns -EINTR
  */
+#ifdef CONFIG_SLEEPOMETER
+static inline int sleepo_down_interruptible(struct semaphore * sem)
+#else
 static inline int down_interruptible(struct semaphore * sem)
+#endif
 {
 	int result;
 
@@ -159,6 +182,19 @@ static inline int down_interruptible(str
 	return result;
 }
 
+#ifdef CONFIG_SLEEPOMETER
+#define down_interruptible(sem)						\
+	({								\
+		static struct sleepo_data sd;				\
+		int ret;						\
+									\
+		sleepo_begin(__FILE__, __LINE__, "down_interruptible", &sd);\
+		ret = sleepo_down_interruptible(sem);			\
+		sleepo_end(&sd);					\
+		ret;							\
+	})
+#endif
+
 /*
  * Non-blockingly attempt to down() a semaphore.
  * Returns zero if we acquired it
diff -puN include/linux/fs.h~sleepometer include/linux/fs.h
--- 25/include/linux/fs.h~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/include/linux/fs.h	2003-06-15 17:51:15.000000000 -0700
@@ -627,15 +627,8 @@ struct super_block {
 /*
  * Superblock locking.
  */
-static inline void lock_super(struct super_block * sb)
-{
-	down(&sb->s_lock);
-}
-
-static inline void unlock_super(struct super_block * sb)
-{
-	up(&sb->s_lock);
-}
+#define lock_super(sb)		down(&(sb)->s_lock)
+#define unlock_super(sb)	up(&(sb)->s_lock)
 
 /*
  * VFS helper functions..
diff -puN include/linux/preempt.h~sleepometer include/linux/preempt.h
--- 25/include/linux/preempt.h~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/include/linux/preempt.h	2003-06-15 17:51:15.000000000 -0700
@@ -22,7 +22,9 @@ do { \
 
 #ifdef CONFIG_PREEMPT
 
+#ifndef CONFIG_SLEEPOMETER
 extern void preempt_schedule(void);
+#endif
 
 #define preempt_disable() \
 do { \
diff -puN include/linux/sched.h~sleepometer include/linux/sched.h
--- 25/include/linux/sched.h~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/include/linux/sched.h	2003-06-15 17:51:15.000000000 -0700
@@ -158,8 +158,14 @@ extern void show_trace_task(task_t *tsk)
  */
 extern void show_stack(struct task_struct *task, unsigned long *sp);
 
+#ifdef CONFIG_SLEEPOMETER
+#include <linux/sleepo.h>
+#else
 void io_schedule(void);
 long io_schedule_timeout(long timeout);
+asmlinkage void schedule(void);
+extern signed long FASTCALL(schedule_timeout(signed long timeout));
+#endif
 
 extern void cpu_init (void);
 extern void trap_init(void);
@@ -171,8 +177,6 @@ extern unsigned long cache_decay_ticks;
 
 
 #define	MAX_SCHEDULE_TIMEOUT	LONG_MAX
-extern signed long FASTCALL(schedule_timeout(signed long timeout));
-asmlinkage void schedule(void);
 
 struct namespace;
 
@@ -341,6 +345,12 @@ struct task_struct {
 	unsigned long sleep_avg;
 	unsigned long last_run;
 
+#ifdef CONFIG_SLEEPOMETER
+	unsigned long long sleepo_start;	/* usecs */
+	struct sleepo_data *active_sleepo;
+	int sleepo_depth;
+#endif
+
 	unsigned long policy;
 	unsigned long cpus_allowed;
 	unsigned int time_slice, first_time_slice;
diff -puN kernel/ksyms.c~sleepometer kernel/ksyms.c
--- 25/kernel/ksyms.c~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/kernel/ksyms.c	2003-06-15 17:51:15.000000000 -0700
@@ -459,13 +459,15 @@ EXPORT_SYMBOL(sleep_on);
 EXPORT_SYMBOL(sleep_on_timeout);
 EXPORT_SYMBOL(interruptible_sleep_on);
 EXPORT_SYMBOL(interruptible_sleep_on_timeout);
+EXPORT_SYMBOL(yield);
+EXPORT_SYMBOL(__cond_resched);
+#ifndef CONFIG_SLEEPOMETER
 EXPORT_SYMBOL(schedule);
+EXPORT_SYMBOL(schedule_timeout);
 #ifdef CONFIG_PREEMPT
 EXPORT_SYMBOL(preempt_schedule);
 #endif
-EXPORT_SYMBOL(schedule_timeout);
-EXPORT_SYMBOL(yield);
-EXPORT_SYMBOL(__cond_resched);
+#endif
 EXPORT_SYMBOL(set_user_nice);
 EXPORT_SYMBOL(task_nice);
 EXPORT_SYMBOL_GPL(idle_cpu);
diff -puN kernel/Makefile~sleepometer kernel/Makefile
--- 25/kernel/Makefile~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/kernel/Makefile	2003-06-15 17:51:15.000000000 -0700
@@ -20,6 +20,7 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq.o
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
 obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o
 obj-$(CONFIG_COMPAT) += compat.o
+obj-$(CONFIG_SLEEPOMETER) += sleepo.o
 
 ifneq ($(CONFIG_IA64),y)
 # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
diff -puN kernel/sched.c~sleepometer kernel/sched.c
--- 25/kernel/sched.c~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/kernel/sched.c	2003-06-15 17:51:15.000000000 -0700
@@ -1241,7 +1241,11 @@ void scheduling_functions_start_here(voi
 /*
  * schedule() is the main scheduler function.
  */
+#ifdef CONFIG_SLEEPOMETER
+asmlinkage void sleepo_schedule(void)
+#else
 asmlinkage void schedule(void)
+#endif
 {
 	task_t *prev, *next;
 	runqueue_t *rq;
@@ -1344,7 +1348,11 @@ switch_tasks:
  * off of preempt_enable.  Kernel preemptions off return from interrupt
  * occur there and call schedule directly.
  */
+#ifdef CONFIG_SLEEPOMETER
+asmlinkage void sleepo_preempt_schedule(void)
+#else
 asmlinkage void preempt_schedule(void)
+#endif
 {
 	struct thread_info *ti = current_thread_info();
 
@@ -2020,7 +2028,11 @@ void yield(void)
  * But don't do that if it is a deliberate, throttling IO wait (this task
  * has set its backing_dev_info: the queue against which it should throttle)
  */
+#ifdef CONFIG_SLEEPOMETER
+void sleepo_io_schedule(void)
+#else
 void io_schedule(void)
+#endif
 {
 	struct runqueue *rq = this_rq();
 
@@ -2029,7 +2041,11 @@ void io_schedule(void)
 	atomic_dec(&rq->nr_iowait);
 }
 
+#ifdef CONFIG_SLEEPOMETER
+long sleepo_io_schedule_timeout(long timeout)
+#else
 long io_schedule_timeout(long timeout)
+#endif
 {
 	struct runqueue *rq = this_rq();
 	long ret;
diff -puN kernel/timer.c~sleepometer kernel/timer.c
--- 25/kernel/timer.c~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/kernel/timer.c	2003-06-15 17:51:15.000000000 -0700
@@ -985,7 +985,11 @@ static void process_timeout(unsigned lon
  *
  * In all cases the return value is guaranteed to be non-negative.
  */
+#ifdef CONFIG_SLEEPOMETER
+signed long sleepo_schedule_timeout(signed long timeout)
+#else
 signed long schedule_timeout(signed long timeout)
+#endif
 {
 	struct timer_list timer;
 	unsigned long expire;
diff -puN kernel/module.c~sleepometer kernel/module.c
--- 25/kernel/module.c~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/kernel/module.c	2003-06-15 17:51:15.000000000 -0700
@@ -647,6 +647,10 @@ sys_delete_module(const char __user *nam
 	char name[MODULE_NAME_LEN];
 	int ret, forced = 0;
 
+#ifdef CONFIG_SLEEPOMETER
+	return -EINVAL;		/* sleepo doesn't clean stuff up */
+#endif
+
 	if (!capable(CAP_SYS_MODULE))
 		return -EPERM;
 
diff -puN include/asm-i386/rwsem.h~sleepometer include/asm-i386/rwsem.h
diff -puN include/linux/rwsem.h~sleepometer include/linux/rwsem.h
--- 25/include/linux/rwsem.h~sleepometer	2003-06-15 17:51:15.000000000 -0700
+++ 25-akpm/include/linux/rwsem.h	2003-06-15 17:51:15.000000000 -0700
@@ -19,6 +19,10 @@
 #include <asm/system.h>
 #include <asm/atomic.h>
 
+#ifdef CONFIG_SLEEPOMETER
+#include <linux/sleepo.h>
+#endif
+
 struct rw_semaphore;
 
 #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
@@ -38,7 +42,11 @@ extern void FASTCALL(rwsemtrace(struct r
 /*
  * lock for reading
  */
+#ifdef CONFIG_SLEEPOMETER
+static inline void sleepo_down_read(struct rw_semaphore *sem)
+#else
 static inline void down_read(struct rw_semaphore *sem)
+#endif
 {
 	might_sleep();
 	rwsemtrace(sem,"Entering down_read");
@@ -46,6 +54,17 @@ static inline void down_read(struct rw_s
 	rwsemtrace(sem,"Leaving down_read");
 }
 
+#ifdef CONFIG_SLEEPOMETER
+#define down_read(sem)							\
+	do {								\
+		static struct sleepo_data sd;				\
+									\
+		sleepo_begin(__FILE__, __LINE__, "down_read", &sd);	\
+		sleepo_down_read(sem);					\
+		sleepo_end(&sd);					\
+	} while (0)
+#endif
+
 /*
  * trylock for reading -- returns 1 if successful, 0 if contention
  */
@@ -61,7 +80,11 @@ static inline int down_read_trylock(stru
 /*
  * lock for writing
  */
+#ifdef CONFIG_SLEEPOMETER
+static inline void sleepo_down_write(struct rw_semaphore *sem)
+#else
 static inline void down_write(struct rw_semaphore *sem)
+#endif
 {
 	might_sleep();
 	rwsemtrace(sem,"Entering down_write");
@@ -69,6 +92,17 @@ static inline void down_write(struct rw_
 	rwsemtrace(sem,"Leaving down_write");
 }
 
+#ifdef CONFIG_SLEEPOMETER
+#define down_write(sem)							\
+	do {								\
+		static struct sleepo_data sd;				\
+									\
+		sleepo_begin(__FILE__, __LINE__, "down_write", &sd);	\
+		sleepo_down_write(sem);					\
+		sleepo_end(&sd);					\
+	} while (0)
+#endif
+
 /*
  * trylock for writing -- returns 1 if successful, 0 if contention
  */
diff -puN /dev/null include/linux/sleepo.h
--- /dev/null	2002-08-30 16:31:37.000000000 -0700
+++ 25-akpm/include/linux/sleepo.h	2003-06-15 17:51:15.000000000 -0700
@@ -0,0 +1,80 @@
+
+#ifndef SLEEPOMETER_H
+#define SLEEPOMETER_H
+
+#include <linux/spinlock.h>
+#include <asm/linkage.h>
+
+struct sleepo_data {
+	spinlock_t lock;
+	unsigned long nr_sleeps;
+	unsigned long long total_usecs;
+	unsigned long long max_usecs;
+	const char *file;
+	const char *sleep_type;
+	int line;
+	struct sleepo_data *next;
+};
+
+void sleepo_io_schedule(void);
+long sleepo_io_schedule_timeout(long timeout);
+asmlinkage void sleepo_schedule(void);
+void sleepo_preempt_schedule(void);
+signed long sleepo_schedule_timeout(signed long timeout);
+void sleepo_begin(const char *file, int line,
+		const char *sleep_type, struct sleepo_data *sd);
+void sleepo_end(struct sleepo_data *sd);
+void sleepo_start(const char *file, int line,
+		const char *sleep_type, struct sleepo_data *sd);
+void sleepo_stop(struct sleepo_data *sd);
+
+#define schedule()							\
+	do {								\
+		static struct sleepo_data sd;				\
+									\
+		sleepo_start(__FILE__, __LINE__, "schedule", &sd);	\
+		sleepo_schedule();					\
+		sleepo_stop(&sd);					\
+	} while (0)
+
+#define preempt_schedule()						\
+	do {								\
+		static struct sleepo_data sd;				\
+									\
+		sleepo_start(__FILE__, __LINE__, "preempt_schedule" &sd);\
+		sleepo_preempt_schedule();				\
+		sleepo_stop(&sd);					\
+	} while (0)
+
+#define io_schedule()							\
+	do {								\
+		static struct sleepo_data sd;				\
+									\
+		sleepo_start(__FILE__, __LINE__, "io_schedule", &sd);	\
+		sleepo_io_schedule();					\
+		sleepo_stop(&sd);					\
+	} while (0)
+
+#define schedule_timeout(t)						\
+	({								\
+		static struct sleepo_data sd;				\
+		long ret;						\
+									\
+		sleepo_start(__FILE__, __LINE__, "schedule_timeout", &sd);\
+		ret = sleepo_schedule_timeout(t);			\
+		sleepo_stop(&sd);					\
+		ret;							\
+	})
+
+#define io_schedule_timeout(t)						\
+	({								\
+		static struct sleepo_data sd;				\
+		long ret;						\
+									\
+		sleepo_start(__FILE__, __LINE__, "io_schedule_timeout", &sd);\
+		ret = sleepo_io_schedule_timeout(t);			\
+		sleepo_stop(&sd);					\
+		ret;							\
+	})
+
+#endif		/* SLEEPOMETER_H */
diff -puN /dev/null kernel/sleepo.c
--- /dev/null	2002-08-30 16:31:37.000000000 -0700
+++ 25-akpm/kernel/sleepo.c	2003-06-15 17:51:00.000000000 -0700
@@ -0,0 +1,198 @@
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+
+#include <asm/div64.h>
+
+static spinlock_t sleepo_lock = SPIN_LOCK_UNLOCKED;
+
+static struct sleepo_data *sleepo_data;
+
+/*
+ * Return a suitable time, in microseconds.
+ */
+
+static unsigned long long grab_time(void)
+{
+#ifdef CONFIG_X86
+	/*
+	 * do_gettimeofday() goes backwards sometimes :(.  Usethe TSC
+	 */
+	unsigned long long ret;
+	extern unsigned long cpu_khz;
+
+	rdtscll(ret);
+	do_div(ret, cpu_khz / 1000);
+	return ret;
+#else
+	struct timeval now;
+	unsigned long long ret;
+
+	do_gettimeofday(&now);
+	ret = now.tv_sec;
+	ret *= 1000000;
+	ret += now.tv_usec;
+	return ret;
+#endif
+}
+
+static void sleepo_install(const char *file, int line,
+			const char *sleep_type, struct sleepo_data *sd)
+{
+	spin_lock(&sleepo_lock);
+	if (sd->file == NULL) {
+		sd->next = sleepo_data;
+		sleepo_data = sd;
+		sd->line = line;
+		sd->sleep_type = sleep_type;
+		spin_lock_init(&sd->lock);
+		sd->file = file;
+	}
+	spin_unlock(&sleepo_lock);
+}
+
+/*
+ * About to enter a sleep
+ */
+void sleepo_start(const char *file, int line,
+			const char *sleep_type, struct sleepo_data *sd)
+{
+	if (sd->file == NULL)
+		sleepo_install(file, line, sleep_type, sd);
+	if (current->sleepo_depth++ == 0)
+		current->sleepo_start = grab_time();
+}
+EXPORT_SYMBOL(sleepo_start);
+
+/*
+ * Finished a sleep
+ */
+void sleepo_stop(struct sleepo_data *sd)
+{
+	unsigned long long delta;
+
+	if (--current->sleepo_depth == 0) {
+		unsigned long long now = grab_time();
+
+		delta = now - current->sleepo_start;
+
+		if (current->active_sleepo)
+			sd = current->active_sleepo;
+
+		spin_lock(&sd->lock);
+		sd->nr_sleeps++;
+		sd->total_usecs += delta;
+		if (delta > sd->max_usecs)
+			sd->max_usecs = delta;
+		spin_unlock(&sd->lock);
+		current->sleepo_start = 0;
+	}
+}
+EXPORT_SYMBOL(sleepo_stop);
+
+/*
+ * About to call something which might sleep
+ */
+void sleepo_begin(const char *file, int line,
+		const char *sleep_type, struct sleepo_data *sd)
+{
+	if (sd->file == NULL)
+		sleepo_install(file, line, sleep_type, sd);
+	current->active_sleepo = sd;
+}
+EXPORT_SYMBOL(sleepo_begin);
+
+/*
+ * Finish might-sleep region
+ */
+void sleepo_end(struct sleepo_data *sd)
+{
+	current->active_sleepo = NULL;
+}
+EXPORT_SYMBOL(sleepo_end);
+
+
+
+
+static void sleepo_wipe(void)
+{
+	struct sleepo_data *sd;
+
+	spin_lock(&sleepo_lock);
+	sd = sleepo_data;
+	while (sd) {
+		spin_lock(&sd->lock);
+		sd->nr_sleeps = 0;
+		sd->total_usecs = 0;
+		sd->max_usecs = 0;
+		spin_unlock(&sd->lock);
+		sd = sd->next;
+	}
+	spin_unlock(&sleepo_lock);
+}
+
+
+/*
+ * /proc stuff
+ */
+
+
+static void *sleepo_seq_start(struct seq_file *m, loff_t *pos)
+{
+	spin_lock(&sleepo_lock);
+	return *pos ? NULL : sleepo_data;
+}
+
+static void *sleepo_seq_next(struct seq_file *m, void *arg, loff_t *pos)
+{
+	struct sleepo_data *sd = arg;
+
+	(*pos)++;
+	if (sd)
+		sd = sd->next;
+	return sd;
+}
+
+static int sleepo_seq_show(struct seq_file *m, void *arg)
+{
+	struct sleepo_data *sd = arg;
+
+	spin_lock(&sd->lock);
+	if (sd->nr_sleeps) {
+		unsigned long long mean_usecs;
+
+		mean_usecs = sd->total_usecs;
+		do_div(mean_usecs, sd->nr_sleeps);
+		seq_printf(m, "%s:%d(%s) %lu %llu %llu %llu\n",
+				sd->file, sd->line, sd->sleep_type,
+				sd->nr_sleeps, sd->total_usecs,
+				mean_usecs, sd->max_usecs);
+	}
+	sd->nr_sleeps = 0;
+	sd->total_usecs = 0;
+	sd->max_usecs = 0;
+	spin_unlock(&sd->lock);
+	return 0;
+}
+
+static void sleepo_seq_stop(struct seq_file *m, void *arg)
+{
+	spin_unlock(&sleepo_lock);
+	sleepo_wipe();
+}
+
+struct seq_operations sleepo_op = {
+	.start	= sleepo_seq_start,
+	.next	= sleepo_seq_next,
+	.stop	= sleepo_seq_stop,
+	.show	= sleepo_seq_show,
+};
+
+EXPORT_SYMBOL(sleepo_io_schedule);
+EXPORT_SYMBOL(sleepo_io_schedule_timeout);
+EXPORT_SYMBOL(sleepo_schedule);
+EXPORT_SYMBOL(sleepo_schedule_timeout);
+
+#ifdef CONFIG_PREEMPT
+EXPORT_SYMBOL(sleepo_preempt_schedule);
+#endif

_