1 #ifdef CONFIG_PREEMPT_RT
2 #error Preempt RT kernels are not supported
6 #define ITC_PREEMPT_HACK
9 #include <linux/signal.h>
10 #include <linux/sched.h>
11 #include <linux/interrupt.h>
14 #include <linux/version.h>
15 #include <linux/vmalloc.h>
16 #include <linux/module.h>
17 #include <linux/delay.h>
18 #include <linux/init.h>
19 #include <linux/slab.h>
20 #include <linux/smp.h>
23 #include <linux/miscdevice.h>
24 #include <linux/kernel_stat.h>
25 #if LINUX_VERSION_CODE < KERNEL_VERSION (3, 0, 0)
26 #include <asm/system.h>
27 #include <linux/smp_lock.h>
29 #include <asm/uaccess.h>
31 #if defined CONFIG_6xx || defined CONFIG_PPC64
32 #include <asm/machdep.h>
33 #define pm_idle ppc_md.power_save
34 #if LINUX_VERSION_CODE < KERNEL_VERSION (3, 0, 0)
39 #if !(defined CONFIG_X86 || defined CONFIG_6xx || defined CONFIG_PPC64)
40 #error Support for this architecture is nto written yet
43 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
44 #include <linux/wrapper.h>
45 #include <linux/smp_lock.h>
47 #error Support for SMP on 2.4 series of kernels is not written yet
49 #define num_present_cpus() 1
50 #define num_online_cpus() 1
51 /* #define cpu_online(n) 1 */
52 #define cpu_present(n) 1
55 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20)
56 #define iminor(inode) MINOR((inode)->i_rdev)
58 #define iminor(inode) minor((inode)->i_rdev)
63 #define itc_enter_bkl() do { \
66 #define itc_leave_bkl() do { \
70 #define itc_enter_bkl() for (;;) {smp_mb (); break;}
71 #define itc_leave_bkl() for (;;) {smp_mb (); break;}
72 #ifdef ITC_PREEMPT_HACK
73 #error Attempt to enable ITC_PREEMPT_HACK on non preemptible kernel
77 MODULE_DESCRIPTION ("Idle time collector");
78 MODULE_LICENSE ("public domain");
81 static void (*fidle_func
) (void);
82 static long idle_func
;
83 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
84 MODULE_PARM (idle_func
, "l");
86 module_param (idle_func
, long, 0644);
88 MODULE_PARM_DESC (idle_func
, "address of default idle function");
92 static DEFINE_SPINLOCK (lock
);
94 static void (*orig_pm_idle
) (void);
95 static unsigned int itc_major
;
99 struct timeval cumm_sleep_time
;
100 struct timeval sleep_started
;
105 static struct itc global_itc
[NR_CPUS
];
107 /**********************************************************************
111 **********************************************************************/
113 cpeamb (struct timeval
*c
, struct timeval
*a
, struct timeval
*b
)
115 __typeof (c
->tv_sec
) sec
= a
->tv_sec
- b
->tv_sec
;
116 __typeof (c
->tv_usec
) usec
= a
->tv_usec
- b
->tv_usec
;
121 usec
= 1000000 + usec
;
124 if (c
->tv_usec
> 1000000)
126 c
->tv_sec
+= sec
+ 1;
127 c
->tv_usec
-= 1000000;
135 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
137 /**********************************************************************
139 * Dummy to make sure we are unloaded properly
141 **********************************************************************/
143 dummy_wakeup (void *unused
)
145 printk (KERN_DEBUG
"waking up %d\n", smp_processor_id ());
147 /* set_need_resched (); */
151 /**********************************************************************
155 **********************************************************************/
156 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
159 void default_idle (void);
163 itc_monotonic (struct timeval
*tv
)
165 do_gettimeofday (tv
);
172 struct cpu_usage_stat
*cpustat
= &kstat_this_cpu
.cpustat
;
184 struct timeval tv_irq_before
, tv_irq_after
;
185 cputime64_t irq_time_before
, irq_time_after
;
188 #ifdef ITC_PREEMPT_HACK
192 /* printk ("idle in %d\n", smp_processor_id ()); */
193 spin_lock_irqsave (&lock
, flags
);
194 itc
= &global_itc
[smp_processor_id ()];
195 itc_monotonic (&itc
->sleep_started
);
198 irq_time_before
= itc_irq_time ();
200 spin_unlock_irqrestore (&lock
, flags
);
233 spin_lock_irqsave (&lock
, flags
);
237 irq_time_after
= itc_irq_time ();
239 cputime_to_timeval (irq_time_before
, &tv_irq_before
);
240 cputime_to_timeval (irq_time_after
, &tv_irq_after
);
242 cpeamb (&itc
->cumm_sleep_time
, &tv
, &itc
->sleep_started
);
243 cpeamb (&itc
->cumm_sleep_time
, &tv_irq_before
, &tv_irq_after
);
245 cpeamb (&itc
->cumm_sleep_time
, &tv
, &itc
->sleep_started
);
249 spin_unlock_irqrestore (&lock
, flags
);
250 /* printk ("idle out %d\n", smp_processor_id ()); */
252 #ifdef ITC_PREEMPT_HACK
257 /**********************************************************************
261 **********************************************************************/
263 itc_open (struct inode
* inode
, struct file
* file
);
266 itc_release (struct inode
* inode
, struct file
* file
);
269 itc_read (struct file
* file
, char * buf
, size_t count
, loff_t
* ppos
);
271 static struct file_operations itc_fops
=
273 .owner
= THIS_MODULE
,
275 .release
= itc_release
,
280 static struct miscdevice itc_misc_dev
=
282 .minor
= MISC_DYNAMIC_MINOR
,
288 itc_release (struct inode
* inode
, struct file
* filp
)
291 pm_idle
= orig_pm_idle
;
294 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
296 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 26)
297 /* 15c8b6c1aaaf1c4edd67e2f02e4d8e1bd1a51c0d */
298 on_each_cpu (dummy_wakeup
, NULL
, 1);
300 on_each_cpu (dummy_wakeup
, NULL
, 0, 1);
307 itc_open (struct inode
* inode
, struct file
* filp
)
310 unsigned int minor
= iminor (inode
);
325 filp
->f_op
= &itc_fops
;
328 if (pm_idle
!= itc_idle
)
330 orig_pm_idle
= pm_idle
;
340 itc_read (struct file
*file
, char * buf
, size_t count
, loff_t
* ppos
)
343 size_t itemsize
= sizeof (global_itc
[0].cumm_sleep_time
);
346 struct itc
*itc
= &global_itc
[0];
347 struct timeval tmp
[NR_CPUS
], *tmpp
;
350 if (count
< itemsize
* num_present_cpus ())
353 "attempt to read something funny %zu expected %zu(%zu,%u)\n",
354 count
, itemsize
* num_present_cpus (),
355 itemsize
, num_present_cpus ());
359 spin_lock_irqsave (&lock
, flags
);
360 for (i
= 0; i
< NR_CPUS
; ++i
, ++itc
)
369 cpeamb (&itc
->cumm_sleep_time
, &tv
, &itc
->sleep_started
);
370 itc
->sleep_started
.tv_sec
= tv
.tv_sec
;
371 itc
->sleep_started
.tv_usec
= tv
.tv_usec
;
374 *tmpp
++ = itc
->cumm_sleep_time
;
378 spin_unlock_irqrestore (&lock
, flags
);
380 if (copy_to_user (buf
, tmp
, retval
))
382 printk (KERN_ERR
"failed to write %zu bytes to %p\n",
389 /**********************************************************************
393 **********************************************************************/
400 fidle_func
= (void (*) (void)) idle_func
;
412 "itc: no idle function\n"
413 "itc: boot kernel with idle=halt option\n"
414 "itc: or specify idle_func (modprobe itc idle_func=<address>)\n");
421 err
= register_chrdev (itc_major
, DEVNAME
, &itc_fops
);
422 if (err
< 0 || ((itc_major
&& err
) || (!itc_major
&& !err
)))
424 printk (KERN_ERR
"itc: register_chrdev failed itc_major=%d err=%d\n",
436 err
= misc_register (&itc_misc_dev
);
439 printk (KERN_ERR
"itc: misc_register failed err=%d\n", err
);
444 orig_pm_idle
= pm_idle
;
447 "itc: driver loaded pm_idle=%p default_idle=%p"
462 printk (KERN_DEBUG
"itc: CPUs(%d present=%d online=%d)"
472 #ifdef CONFIG_PREEMPT
476 NR_CPUS
, num_present_cpus (), num_online_cpus ());
480 /**********************************************************************
484 **********************************************************************/
488 printk (KERN_DEBUG
"itc: unloading (resetting pm_idle to %p)\n",
492 unregister_chrdev (itc_major
, DEVNAME
);
496 misc_deregister (&itc_misc_dev
);
498 printk (KERN_DEBUG
"itc: unloaded\n");