Eh?
[apc.git] / mod / itc-mod.c
blob25e1cd849a2028013107996f6ab10de3be2a01bd
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 #include <linux/smp_lock.h>
27 #include <asm/system.h>
28 #include <asm/uaccess.h>
30 #if defined CONFIG_6xx || defined CONFIG_PPC64
31 #include <asm/machdep.h>
32 #define pm_idle ppc_md.power_save
33 #define ACCOUNT_IRQ
34 #endif
36 #if !(defined CONFIG_X86 || defined CONFIG_6xx || defined CONFIG_PPC64)
37 #error Support for this architecture is nto written yet
38 #endif
40 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
41 #include <linux/wrapper.h>
42 #include <linux/smp_lock.h>
43 #ifdef CONFIG_SMP
44 #error Support for SMP on 2.4 series of kernels is not written yet
45 #else
46 #define num_present_cpus() 1
47 #define num_online_cpus() 1
48 /* #define cpu_online(n) 1 */
49 #define cpu_present(n) 1
50 #endif
52 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20)
53 #define iminor(inode) MINOR((inode)->i_rdev)
54 #else
55 #define iminor(inode) minor((inode)->i_rdev)
56 #endif
57 #endif
59 #ifdef CONFIG_PREEMPT
60 #define itc_enter_bkl() do { \
61 preempt_disable (); \
62 lock_kernel (); \
63 } while (0)
64 #define itc_leave_bkl() do { \
65 unlock_kernel (); \
66 preempt_enable (); \
67 } while (0)
68 #else
69 #define itc_enter_bkl lock_kernel
70 #define itc_leave_bkl unlock_kernel
71 #ifdef ITC_PREEMPT_HACK
72 #error Attempt to enable ITC_PREEMPT_HACK on non preemptible kernel
73 #endif
74 #endif
76 MODULE_DESCRIPTION ("Idle time collector");
78 #ifdef CONFIG_X86
79 static void (*fidle_func) (void);
80 static long idle_func;
81 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
82 MODULE_PARM (idle_func, "l");
83 #else
84 module_param (idle_func, long, 0644);
85 #endif
86 MODULE_PARM_DESC (idle_func, "address of default idle function");
87 #endif
89 #define DEVNAME "itc"
90 static spinlock_t lock = SPIN_LOCK_UNLOCKED;
92 static void (*orig_pm_idle) (void);
93 static unsigned int itc_major;
95 struct itc
97 struct timeval cumm_sleep_time;
98 struct timeval sleep_started;
99 int sleeping;
102 static int in_use;
103 static struct itc global_itc[NR_CPUS];
105 /**********************************************************************
107 * Utility functions
109 **********************************************************************/
110 static void
111 cpeamb (struct timeval *c, struct timeval *a, struct timeval *b)
113 __typeof (c->tv_sec) sec = a->tv_sec - b->tv_sec;
114 __typeof (c->tv_usec) usec = a->tv_usec - b->tv_usec;
116 if (usec < 0)
118 sec -= 1;
119 usec = 1000000 + usec;
121 c->tv_usec += usec;
122 if (c->tv_usec > 1000000)
124 c->tv_sec += sec + 1;
125 c->tv_usec -= 1000000;
127 else
129 c->tv_sec += sec;
133 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
134 /* XXX: 2.4 */
135 /**********************************************************************
137 * Dummy to make sure we are unloaded properly
139 **********************************************************************/
140 static void
141 dummy_wakeup (void *unused)
143 printk (KERN_DEBUG "waking up %d\n", smp_processor_id ());
144 /* needed? safe? */
145 set_need_resched ();
147 #endif
149 /**********************************************************************
151 * idle
153 **********************************************************************/
154 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
155 #define QUIRK
156 #else
157 void default_idle (void);
158 #endif
160 static void
161 itc_monotonic (struct timeval *tv)
163 do_gettimeofday (tv);
166 #ifdef ACCOUNT_IRQ
167 static cputime64_t
168 itc_irq_time (void)
170 struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat;
171 return cpustat->irq;
173 #endif
175 static void
176 itc_idle (void)
178 struct itc *itc;
179 struct timeval tv;
180 unsigned long flags;
181 #ifdef ACCOUNT_IRQ
182 struct timeval tv_irq_before, tv_irq_after;
183 cputime64_t irq_time_before, irq_time_after;
184 #endif
186 #ifdef ITC_PREEMPT_HACK
187 preempt_disable ();
188 #endif
190 /* printk ("idle in %d\n", smp_processor_id ()); */
191 spin_lock_irqsave (&lock, flags);
192 itc = &global_itc[smp_processor_id ()];
193 itc_monotonic (&itc->sleep_started);
194 itc->sleeping = 1;
195 #ifdef ACCOUNT_IRQ
196 irq_time_before = itc_irq_time ();
197 #endif
198 spin_unlock_irqrestore (&lock, flags);
200 #ifdef QUIRK
201 if (orig_pm_idle)
203 orig_pm_idle ();
205 #ifdef CONFIG_X86
206 else
208 fidle_func ();
210 #endif
211 #else
212 if (orig_pm_idle)
214 orig_pm_idle ();
216 else
218 #ifdef CONFIG_X86
219 if (fidle_func)
221 fidle_func ();
223 else
224 #endif
226 default_idle ();
229 #endif
231 spin_lock_irqsave (&lock, flags);
232 itc_monotonic (&tv);
234 #ifdef ACCOUNT_IRQ
235 irq_time_after = itc_irq_time ();
237 cputime_to_timeval (irq_time_before, &tv_irq_before);
238 cputime_to_timeval (irq_time_after, &tv_irq_after);
240 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
241 cpeamb (&itc->cumm_sleep_time, &tv_irq_before, &tv_irq_after);
242 #else
243 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
244 #endif
246 itc->sleeping = 0;
247 spin_unlock_irqrestore (&lock, flags);
248 /* printk ("idle out %d\n", smp_processor_id ()); */
250 #ifdef ITC_PREEMPT_HACK
251 preempt_enable ();
252 #endif
255 /**********************************************************************
257 * File operations
259 **********************************************************************/
260 static int
261 itc_open (struct inode * inode, struct file * file);
263 static int
264 itc_release (struct inode * inode, struct file * file);
266 static ssize_t
267 itc_read (struct file * file, char * buf, size_t count, loff_t * ppos);
269 static struct file_operations itc_fops =
271 .owner = THIS_MODULE,
272 .open = itc_open,
273 .release = itc_release,
274 .llseek = no_llseek,
275 .read = itc_read,
278 static struct miscdevice itc_misc_dev =
280 .minor = MISC_DYNAMIC_MINOR,
281 .name = "itc",
282 .fops = &itc_fops
285 static int
286 itc_release (struct inode * inode, struct file * filp)
288 itc_enter_bkl ();
289 pm_idle = orig_pm_idle;
290 in_use = 0;
291 itc_leave_bkl ();
292 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
293 /* XXX: 2.4 */
294 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 26)
295 /* 15c8b6c1aaaf1c4edd67e2f02e4d8e1bd1a51c0d */
296 on_each_cpu (dummy_wakeup, NULL, 1);
297 #else
298 on_each_cpu (dummy_wakeup, NULL, 0, 1);
299 #endif
300 #endif
301 return 0;
304 static int
305 itc_open (struct inode * inode, struct file * filp)
307 int ret = 0;
308 const struct file_operations *old_fops = filp->f_op;
309 unsigned int minor = iminor (inode);
311 if (itc_major)
313 if (minor != 0)
315 return -ENODEV;
319 if (in_use)
321 return -EALREADY;
324 /* old_fops = filp->f_op; */
325 filp->f_op = fops_get (&itc_fops);
326 fops_put (old_fops);
328 itc_enter_bkl ();
329 if (pm_idle != itc_idle)
331 orig_pm_idle = pm_idle;
333 pm_idle = itc_idle;
334 in_use = 1;
335 itc_leave_bkl ();
337 return ret;
340 static ssize_t
341 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
343 int i;
344 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
345 ssize_t retval = 0;
346 unsigned long flags;
347 struct itc *itc = &global_itc[0];
348 struct timeval tmp[NR_CPUS], *tmpp;
350 tmpp = tmp;
351 if (count < itemsize * num_present_cpus ())
353 printk (KERN_ERR
354 "attempt to read something funny %zu expected %zu(%zu,%u)\n",
355 count, itemsize * num_present_cpus (),
356 itemsize, num_present_cpus ());
357 return -EINVAL;
360 spin_lock_irqsave (&lock, flags);
361 for (i = 0; i < NR_CPUS; ++i, ++itc)
363 if (cpu_present (i))
365 if (itc->sleeping)
367 struct timeval tv;
369 itc_monotonic (&tv);
370 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
371 itc->sleep_started.tv_sec = tv.tv_sec;
372 itc->sleep_started.tv_usec = tv.tv_usec;
375 *tmpp++ = itc->cumm_sleep_time;
376 retval += itemsize;
379 spin_unlock_irqrestore (&lock, flags);
381 if (copy_to_user (buf, tmp, retval))
383 printk (KERN_ERR "failed to write %zu bytes to %p\n",
384 retval, buf);
385 retval = -EFAULT;
387 return retval;
390 /**********************************************************************
392 * Module constructor
394 **********************************************************************/
395 static __init int
396 init (void)
398 int err;
400 #ifdef CONFIG_X86
401 fidle_func = (void (*) (void)) idle_func;
402 #endif
404 #ifdef QUIRK
405 if (!pm_idle
406 #ifdef CONFIG_X86
407 && !fidle_func
408 #endif
411 printk
412 (KERN_ERR
413 "itc: no idle function\n"
414 "itc: boot kernel with idle=halt option\n"
415 "itc: or specify idle_func (modprobe itc idle_func=<address>)\n");
416 return -ENODEV;
418 #endif
420 if (itc_major)
422 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
423 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
425 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
426 itc_major, err);
427 return -ENODEV;
430 if (!itc_major)
432 itc_major = err;
435 else
437 err = misc_register (&itc_misc_dev);
438 if (err < 0)
440 printk (KERN_ERR "itc: misc_register failed err=%d\n", err);
441 return err;
445 orig_pm_idle = pm_idle;
446 printk
447 (KERN_DEBUG
448 "itc: driver loaded pm_idle=%p default_idle=%p"
449 #ifdef CONFIG_X86
450 ", idle_func=%p"
451 #endif
452 "\n",
453 pm_idle
454 #ifdef QUIRK
455 , NULL
456 #else
457 , default_idle
458 #endif
459 #ifdef CONFIG_X86
460 , fidle_func
461 #endif
463 printk (KERN_DEBUG "itc: CPUs(%d present=%d online=%d)"
464 #ifdef QUIRK
465 " Q"
466 #endif
467 #ifdef CONFIG_APM
468 " A"
469 #endif
470 #ifdef CONFIG_SMP
471 " S"
472 #endif
473 #ifdef CONFIG_PREEMPT
474 " P"
475 #endif
476 "\n",
477 NR_CPUS, num_present_cpus (), num_online_cpus ());
478 return 0;
481 /**********************************************************************
483 * Module destructor
485 **********************************************************************/
486 static __exit void
487 fini (void)
489 printk (KERN_DEBUG "itc: unloading (resetting pm_idle to %p)\n",
490 orig_pm_idle);
491 if (itc_major)
493 unregister_chrdev (itc_major, DEVNAME);
495 else
497 misc_deregister (&itc_misc_dev);
499 printk (KERN_DEBUG "itc: unloaded\n");
502 module_init (init);
503 module_exit (fini);