Add IRQ accounting but only on PPC
[apc.git] / mod / itc-mod.c
blobfc86698c229d45533b303640820999e14c305343
1 #ifdef CONFIG_PREEMPT
2 #define ITC_PREEMPT_HACK
3 #endif
5 #include <linux/signal.h>
6 #include <linux/sched.h>
7 #include <linux/interrupt.h>
9 #include <linux/fs.h>
10 #include <linux/version.h>
11 #include <linux/vmalloc.h>
12 #include <linux/module.h>
13 #include <linux/delay.h>
14 #include <linux/init.h>
15 #include <linux/slab.h>
16 #include <linux/smp.h>
17 #include <linux/mm.h>
18 #include <linux/pm.h>
19 #include <linux/miscdevice.h>
20 #include <linux/kernel_stat.h>
22 #include <asm/system.h>
23 #include <asm/uaccess.h>
25 #ifdef CONFIG_6xx
26 #include <asm/machdep.h>
27 #define pm_idle ppc_md.power_save
28 #define ACCOUNT_IRQ
29 #endif
31 #if !(defined CONFIG_X86 || defined CONFIG_6xx)
32 #error Support for this architecture is not written yet
33 #endif
35 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
36 #include <linux/wrapper.h>
37 #include <linux/smp_lock.h>
38 #ifdef CONFIG_SMP
39 #error Support for SMP on 2.4 series of kernels is not written yet
40 #else
41 #define num_present_cpus() 1
42 #define num_online_cpus() 1
43 /* #define cpu_online(n) 1 */
44 #define cpu_present(n) 1
45 #endif
47 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20)
48 #define iminor(inode) MINOR((inode)->i_rdev)
49 #else
50 #define iminor(inode) minor((inode)->i_rdev)
51 #endif
52 #endif
54 #ifdef CONFIG_PREEMPT
55 #define itc_enter_bkl() do { \
56 preempt_disable (); \
57 lock_kernel (); \
58 } while (0)
59 #define itc_leave_bkl() do { \
60 unlock_kernel (); \
61 preempt_enable (); \
62 } while (0)
63 #else
64 #define itc_enter_bkl lock_kernel
65 #define itc_leave_bkl unlock_kernel
66 #ifdef ITC_PREEMPT_HACK
67 #error Attempt to enable ITC_PREEMPT_HACK on non preemptible kernel
68 #endif
69 #endif
71 MODULE_DESCRIPTION ("Idle time collector");
73 #ifdef CONFIG_X86
74 static void (*fidle_func) (void);
75 static long idle_func;
76 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
77 MODULE_PARM (idle_func, "l");
78 #else
79 module_param (idle_func, long, 0644);
80 #endif
81 MODULE_PARM_DESC (idle_func, "address of default idle function");
82 #endif
84 #define DEVNAME "itc"
85 static spinlock_t lock = SPIN_LOCK_UNLOCKED;
87 static void (*orig_pm_idle) (void);
88 static unsigned int itc_major;
90 struct itc
92 struct timeval cumm_sleep_time;
93 struct timeval sleep_started;
94 int sleeping;
97 static int in_use;
98 static struct itc global_itc[NR_CPUS];
100 /**********************************************************************
102 * Utility functions
104 **********************************************************************/
105 static void
106 cpeamb (struct timeval *c, struct timeval *a, struct timeval *b)
108 __typeof (c->tv_sec) sec = a->tv_sec - b->tv_sec;
109 __typeof (c->tv_usec) usec = a->tv_usec - b->tv_usec;
111 if (usec < 0)
113 sec -= 1;
114 usec = 1000000 + usec;
116 c->tv_usec += usec;
117 if (c->tv_usec > 1000000)
119 c->tv_sec += sec + 1;
120 c->tv_usec -= 1000000;
122 else
124 c->tv_sec += sec;
128 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
129 /* XXX: 2.4 */
130 /**********************************************************************
132 * Dummy to make sure we are unloaded properly
134 **********************************************************************/
135 static void
136 dummy_wakeup (void *unused)
138 printk (KERN_DEBUG "waking up %d\n", smp_processor_id ());
139 /* needed? safe? */
140 set_need_resched ();
142 #endif
144 /**********************************************************************
146 * idle
148 **********************************************************************/
149 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
150 #define QUIRK
151 #else
152 void default_idle (void);
153 #endif
155 static void
156 itc_monotonic (struct timeval *tv)
158 do_gettimeofday (tv);
161 #ifdef ACCOUNT_IRQ
162 static cputime64_t
163 itc_irq_time (void)
165 struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat;
166 return cpustat->irq;
168 #endif
170 static void
171 itc_idle (void)
173 struct itc *itc;
174 struct timeval tv;
175 unsigned long flags;
176 #ifdef ACCOUNT_IRQ
177 struct timeval tv_irq_before, tv_irq_after;
178 cputime64_t irq_time_before, irq_time_after;
179 #endif
181 #ifdef ITC_PREEMPT_HACK
182 preempt_disable ();
183 #endif
185 /* printk ("idle in %d\n", smp_processor_id ()); */
186 spin_lock_irqsave (&lock, flags);
187 itc = &global_itc[smp_processor_id ()];
188 itc_monotonic (&itc->sleep_started);
189 itc->sleeping = 1;
190 #ifdef ACCOUNT_IRQ
191 irq_time_before = itc_irq_time ();
192 #endif
193 spin_unlock_irqrestore (&lock, flags);
195 #ifdef QUIRK
196 if (orig_pm_idle)
198 orig_pm_idle ();
200 #ifdef CONFIG_X86
201 else
203 fidle_func ();
205 #endif
206 #else
207 if (orig_pm_idle)
209 orig_pm_idle ();
211 else
213 #ifdef CONFIG_X86
214 if (fidle_func)
216 fidle_func ();
218 else
219 #endif
221 default_idle ();
224 #endif
226 spin_lock_irqsave (&lock, flags);
227 itc_monotonic (&tv);
229 #ifdef ACCOUNT_IRQ
230 irq_time_after = itc_irq_time ();
232 cputime_to_timeval (irq_time_before, &tv_irq_before);
233 cputime_to_timeval (irq_time_after, &tv_irq_after);
235 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
236 cpeamb (&itc->cumm_sleep_time, &tv_irq_before, &tv_irq_after);
237 #else
238 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
239 #endif
241 itc->sleeping = 0;
242 spin_unlock_irqrestore (&lock, flags);
243 /* printk ("idle out %d\n", smp_processor_id ()); */
245 #ifdef ITC_PREEMPT_HACK
246 preempt_enable ();
247 #endif
250 /**********************************************************************
252 * File operations
254 **********************************************************************/
255 static int
256 itc_open (struct inode * inode, struct file * file);
258 static int
259 itc_release (struct inode * inode, struct file * file);
261 static int
262 itc_ioctl (struct inode * inode, struct file * file,
263 unsigned int cmd, unsigned long arg);
265 static ssize_t
266 itc_read (struct file * file, char * buf, size_t count, loff_t * ppos);
268 static struct file_operations itc_fops =
270 .owner = THIS_MODULE,
271 .open = itc_open,
272 .release = itc_release,
273 .ioctl = itc_ioctl,
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 on_each_cpu (dummy_wakeup, NULL, 0, 1);
295 #endif
296 return 0;
299 static int
300 itc_open (struct inode * inode, struct file * filp)
302 int ret = 0;
303 const struct file_operations *old_fops = filp->f_op;
304 unsigned int minor = iminor (inode);
306 if (itc_major)
308 if (minor != 0)
310 return -ENODEV;
314 if (in_use)
316 return -EALREADY;
319 /* old_fops = filp->f_op; */
320 filp->f_op = fops_get (&itc_fops);
321 fops_put (old_fops);
323 itc_enter_bkl ();
324 if (pm_idle != itc_idle)
326 orig_pm_idle = pm_idle;
328 pm_idle = itc_idle;
329 in_use = 1;
330 itc_leave_bkl ();
332 return ret;
335 static ssize_t
336 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
338 int i;
339 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
340 ssize_t retval = 0;
341 unsigned long flags;
342 struct itc *itc = &global_itc[0];
343 struct timeval tmp[NR_CPUS], *tmpp;
345 tmpp = tmp;
346 if (count < itemsize * num_present_cpus ())
348 printk (KERN_ERR
349 "attempt to read something funny %d expected %d(%d,%d)\n",
350 count, itemsize * num_present_cpus (),
351 itemsize, num_present_cpus ());
352 return -EINVAL;
355 spin_lock_irqsave (&lock, flags);
356 for (i = 0; i < NR_CPUS; ++i, ++itc)
358 if (cpu_present (i))
360 if (itc->sleeping)
362 struct timeval tv;
364 itc_monotonic (&tv);
365 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
366 itc->sleep_started.tv_sec = tv.tv_sec;
367 itc->sleep_started.tv_usec = tv.tv_usec;
370 *tmpp++ = itc->cumm_sleep_time;
371 retval += itemsize;
374 spin_unlock_irqrestore (&lock, flags);
376 if (copy_to_user (buf, tmp, retval))
378 printk (KERN_ERR "failed to write %zu bytes to %p\n",
379 retval, buf);
380 retval = -EFAULT;
382 return retval;
385 /**********************************************************************
387 * ioctl handler
389 **********************************************************************/
390 static int
391 itc_ioctl (struct inode * inode, struct file * filp,
392 unsigned int cmd, unsigned long arg)
394 return -EINVAL;
397 /**********************************************************************
399 * Module constructor
401 **********************************************************************/
402 static __init int
403 init (void)
405 int err;
407 #ifdef CONFIG_X86
408 fidle_func = (void (*) (void)) idle_func;
409 #endif
411 #ifdef QUIRK
412 if (!pm_idle
413 #ifdef CONFIG_X86
414 && !fidle_func
415 #endif
418 printk
419 (KERN_ERR
420 "itc: no idle function\n"
421 "itc: boot kernel with idle=halt option\n"
422 "itc: or specify idle_func (modprobe itc idle_func=<address>)\n");
423 return -ENODEV;
425 #endif
427 if (itc_major)
429 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
430 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
432 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
433 itc_major, err);
434 return -ENODEV;
437 if (!itc_major)
439 itc_major = err;
442 else
444 err = misc_register (&itc_misc_dev);
445 if (err < 0)
447 printk (KERN_ERR "itc: misc_register failed err=%d\n", err);
448 return err;
452 orig_pm_idle = pm_idle;
453 printk
454 (KERN_DEBUG
455 "itc: driver loaded pm_idle=%p default_idle=%p"
456 #ifdef CONFIG_X86
457 ", idle_func=%p"
458 #endif
459 "\n",
460 pm_idle
461 #ifdef QUIRK
462 , NULL
463 #else
464 , default_idle
465 #endif
466 #ifdef CONFIG_X86
467 , fidle_func
468 #endif
470 printk (KERN_DEBUG "itc: CPUs(%d present=%d online=%d)"
471 #ifdef QUIRK
472 " Q"
473 #endif
474 #ifdef CONFIG_APM
475 " A"
476 #endif
477 #ifdef CONFIG_SMP
478 " S"
479 #endif
480 #ifdef CONFIG_PREEMPT
481 " P"
482 #endif
483 "\n",
484 NR_CPUS, num_present_cpus (), num_online_cpus ());
485 return 0;
488 /**********************************************************************
490 * Module destructor
492 **********************************************************************/
493 static __exit void
494 fini (void)
496 printk (KERN_DEBUG "itc: unloading (resetting pm_idle to %p)\n",
497 orig_pm_idle);
498 if (itc_major)
500 unregister_chrdev (itc_major, DEVNAME);
502 else
504 misc_deregister (&itc_misc_dev);
506 printk (KERN_DEBUG "itc: unloaded\n");
509 module_init (init);
510 module_exit (fini);