Erm...
[apc.git] / mod / itc-mod.c
blob63ccf3deffefedb00ceb28b7d9e9d6e9dfc8856e
1 #ifdef CONFIG_PREEMPT_RT
2 #error Preempt RT kernels are not supported
3 #endif
5 #ifdef CONFIG_PREEMPT
6 #define ITC_PREEMPT_HACK
7 #endif
9 #include <linux/signal.h>
10 #include <linux/sched.h>
11 #include <linux/interrupt.h>
13 #include <linux/fs.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>
21 #include <linux/mm.h>
22 #include <linux/pm.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>
28 #endif
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)
35 #define ACCOUNT_IRQ
36 #endif
37 #endif
39 #if !(defined CONFIG_X86 || defined CONFIG_6xx || defined CONFIG_PPC64)
40 #error Support for this architecture is nto written yet
41 #endif
43 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
44 #include <linux/wrapper.h>
45 #include <linux/smp_lock.h>
46 #ifdef CONFIG_SMP
47 #error Support for SMP on 2.4 series of kernels is not written yet
48 #else
49 #define num_present_cpus() 1
50 #define num_online_cpus() 1
51 /* #define cpu_online(n) 1 */
52 #define cpu_present(n) 1
53 #endif
55 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20)
56 #define iminor(inode) MINOR((inode)->i_rdev)
57 #else
58 #define iminor(inode) minor((inode)->i_rdev)
59 #endif
60 #endif
62 #ifdef CONFIG_PREEMPT
63 #define itc_enter_bkl() do { \
64 preempt_disable (); \
65 } while (0)
66 #define itc_leave_bkl() do { \
67 preempt_enable (); \
68 } while (0)
69 #else
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
74 #endif
75 #endif
77 MODULE_DESCRIPTION ("Idle time collector");
78 MODULE_LICENSE ("public domain");
80 #ifdef CONFIG_X86
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");
85 #else
86 module_param (idle_func, long, 0644);
87 #endif
88 MODULE_PARM_DESC (idle_func, "address of default idle function");
89 #endif
91 #define DEVNAME "itc"
92 static DEFINE_SPINLOCK (lock);
94 static void (*orig_pm_idle) (void);
95 static unsigned int itc_major;
97 struct itc
99 struct timeval cumm_sleep_time;
100 struct timeval sleep_started;
101 int sleeping;
104 static int in_use;
105 static struct itc global_itc[NR_CPUS];
107 /**********************************************************************
109 * Utility functions
111 **********************************************************************/
112 static void
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;
118 if (usec < 0)
120 sec -= 1;
121 usec = 1000000 + usec;
123 c->tv_usec += usec;
124 if (c->tv_usec > 1000000)
126 c->tv_sec += sec + 1;
127 c->tv_usec -= 1000000;
129 else
131 c->tv_sec += sec;
135 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
136 /* XXX: 2.4 */
137 /**********************************************************************
139 * Dummy to make sure we are unloaded properly
141 **********************************************************************/
142 static void
143 dummy_wakeup (void *unused)
145 printk (KERN_DEBUG "waking up %d\n", smp_processor_id ());
146 /* needed? safe? */
147 /* set_need_resched (); */
149 #endif
151 /**********************************************************************
153 * idle
155 **********************************************************************/
156 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
157 #define QUIRK
158 #else
159 void default_idle (void);
160 #endif
162 static void
163 itc_monotonic (struct timeval *tv)
165 do_gettimeofday (tv);
168 #ifdef ACCOUNT_IRQ
169 static cputime64_t
170 itc_irq_time (void)
172 struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat;
173 return cpustat->irq;
175 #endif
177 static void
178 itc_idle (void)
180 struct itc *itc;
181 struct timeval tv;
182 unsigned long flags;
183 #ifdef ACCOUNT_IRQ
184 struct timeval tv_irq_before, tv_irq_after;
185 cputime64_t irq_time_before, irq_time_after;
186 #endif
188 #ifdef ITC_PREEMPT_HACK
189 preempt_disable ();
190 #endif
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);
196 itc->sleeping = 1;
197 #ifdef ACCOUNT_IRQ
198 irq_time_before = itc_irq_time ();
199 #endif
200 spin_unlock_irqrestore (&lock, flags);
202 #ifdef QUIRK
203 if (orig_pm_idle)
205 orig_pm_idle ();
207 #ifdef CONFIG_X86
208 else
210 fidle_func ();
212 #endif
213 #else
214 if (orig_pm_idle)
216 orig_pm_idle ();
218 else
220 #ifdef CONFIG_X86
221 if (fidle_func)
223 fidle_func ();
225 else
226 #endif
228 default_idle ();
231 #endif
233 spin_lock_irqsave (&lock, flags);
234 itc_monotonic (&tv);
236 #ifdef ACCOUNT_IRQ
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);
244 #else
245 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
246 #endif
248 itc->sleeping = 0;
249 spin_unlock_irqrestore (&lock, flags);
250 /* printk ("idle out %d\n", smp_processor_id ()); */
252 #ifdef ITC_PREEMPT_HACK
253 preempt_enable ();
254 #endif
257 /**********************************************************************
259 * File operations
261 **********************************************************************/
262 static int
263 itc_open (struct inode * inode, struct file * file);
265 static int
266 itc_release (struct inode * inode, struct file * file);
268 static ssize_t
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,
274 .open = itc_open,
275 .release = itc_release,
276 .llseek = no_llseek,
277 .read = itc_read,
280 static struct miscdevice itc_misc_dev =
282 .minor = MISC_DYNAMIC_MINOR,
283 .name = "itc",
284 .fops = &itc_fops
287 static int
288 itc_release (struct inode * inode, struct file * filp)
290 itc_enter_bkl ();
291 pm_idle = orig_pm_idle;
292 in_use = 0;
293 itc_leave_bkl ();
294 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
295 /* XXX: 2.4 */
296 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 26)
297 /* 15c8b6c1aaaf1c4edd67e2f02e4d8e1bd1a51c0d */
298 on_each_cpu (dummy_wakeup, NULL, 1);
299 #else
300 on_each_cpu (dummy_wakeup, NULL, 0, 1);
301 #endif
302 #endif
303 return 0;
306 static int
307 itc_open (struct inode * inode, struct file * filp)
309 int ret = 0;
310 unsigned int minor = iminor (inode);
312 if (itc_major)
314 if (minor != 0)
316 return -ENODEV;
320 if (in_use)
322 return -EALREADY;
325 filp->f_op = &itc_fops;
327 itc_enter_bkl ();
328 if (pm_idle != itc_idle)
330 orig_pm_idle = pm_idle;
332 pm_idle = itc_idle;
333 in_use = 1;
334 itc_leave_bkl ();
336 return ret;
339 static ssize_t
340 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
342 int i;
343 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
344 ssize_t retval = 0;
345 unsigned long flags;
346 struct itc *itc = &global_itc[0];
347 struct timeval tmp[NR_CPUS], *tmpp;
349 tmpp = tmp;
350 if (count < itemsize * num_present_cpus ())
352 printk (KERN_ERR
353 "attempt to read something funny %zu expected %zu(%zu,%u)\n",
354 count, itemsize * num_present_cpus (),
355 itemsize, num_present_cpus ());
356 return -EINVAL;
359 spin_lock_irqsave (&lock, flags);
360 for (i = 0; i < NR_CPUS; ++i, ++itc)
362 if (cpu_present (i))
364 if (itc->sleeping)
366 struct timeval tv;
368 itc_monotonic (&tv);
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;
375 retval += itemsize;
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",
383 retval, buf);
384 retval = -EFAULT;
386 return retval;
389 /**********************************************************************
391 * Module constructor
393 **********************************************************************/
394 static __init int
395 init (void)
397 int err;
399 #ifdef CONFIG_X86
400 fidle_func = (void (*) (void)) idle_func;
401 #endif
403 #ifdef QUIRK
404 if (!pm_idle
405 #ifdef CONFIG_X86
406 && !fidle_func
407 #endif
410 printk
411 (KERN_ERR
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");
415 return -ENODEV;
417 #endif
419 if (itc_major)
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",
425 itc_major, err);
426 return -ENODEV;
429 if (!itc_major)
431 itc_major = err;
434 else
436 err = misc_register (&itc_misc_dev);
437 if (err < 0)
439 printk (KERN_ERR "itc: misc_register failed err=%d\n", err);
440 return err;
444 orig_pm_idle = pm_idle;
445 printk
446 (KERN_DEBUG
447 "itc: driver loaded pm_idle=%p default_idle=%p"
448 #ifdef CONFIG_X86
449 ", idle_func=%p"
450 #endif
451 "\n",
452 pm_idle
453 #ifdef QUIRK
454 , NULL
455 #else
456 , default_idle
457 #endif
458 #ifdef CONFIG_X86
459 , fidle_func
460 #endif
462 printk (KERN_DEBUG "itc: CPUs(%d present=%d online=%d)"
463 #ifdef QUIRK
464 " Q"
465 #endif
466 #ifdef CONFIG_APM
467 " A"
468 #endif
469 #ifdef CONFIG_SMP
470 " S"
471 #endif
472 #ifdef CONFIG_PREEMPT
473 " P"
474 #endif
475 "\n",
476 NR_CPUS, num_present_cpus (), num_online_cpus ());
477 return 0;
480 /**********************************************************************
482 * Module destructor
484 **********************************************************************/
485 static __exit void
486 fini (void)
488 printk (KERN_DEBUG "itc: unloading (resetting pm_idle to %p)\n",
489 orig_pm_idle);
490 if (itc_major)
492 unregister_chrdev (itc_major, DEVNAME);
494 else
496 misc_deregister (&itc_misc_dev);
498 printk (KERN_DEBUG "itc: unloaded\n");
501 module_init (init);
502 module_exit (fini);