Update build.ml
[apc.git] / mod / itc-mod.c
blob0b60e909dad896356e85bb9428b907af012ea6cd
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/version.h>
10 #include <linux/vmalloc.h>
11 #include <linux/module.h>
12 #include <linux/delay.h>
13 #include <linux/init.h>
14 #include <linux/slab.h>
15 #include <linux/smp.h>
16 #include <linux/mm.h>
17 #include <linux/pm.h>
18 #include <linux/miscdevice.h>
20 #include <asm/system.h>
21 #include <asm/uaccess.h>
23 #ifdef CONFIG_6xx
24 #include <asm/machdep.h>
25 #define pm_idle ppc_md.power_save
26 #endif
28 #if !(defined CONFIG_X86 || defined CONFIG_6xx)
29 #error Support for this architecture is not written yet
30 #endif
32 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
33 #include <linux/wrapper.h>
34 #include <linux/smp_lock.h>
35 #ifdef CONFIG_SMP
36 #error Support for SMP on 2.4 series of kernels is not written yet
37 #else
38 #define num_present_cpus() 1
39 #define num_online_cpus() 1
40 /* #define cpu_online(n) 1 */
41 #define cpu_present(n) 1
42 #endif
44 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20)
45 #define iminor(inode) MINOR((inode)->i_rdev)
46 #else
47 #define iminor(inode) minor((inode)->i_rdev)
48 #endif
49 #endif
51 #ifdef CONFIG_PREEMPT
52 #define itc_enter_bkl() do { \
53 preempt_disable (); \
54 lock_kernel (); \
55 } while (0)
56 #define itc_leave_bkl() do { \
57 unlock_kernel (); \
58 preempt_enable (); \
59 } while (0)
60 #else
61 #define itc_enter_bkl lock_kernel
62 #define itc_leave_bkl unlock_kernel
63 #ifdef ITC_PREEMPT_HACK
64 #error Attempt to enable ITC_PREEMPT_HACK on non preemptible kernel
65 #endif
66 #endif
68 MODULE_DESCRIPTION ("Idle time collector");
70 #ifdef CONFIG_X86
71 static void (*fidle_func) (void);
72 static long idle_func;
73 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
74 MODULE_PARM (idle_func, "l");
75 #else
76 module_param (idle_func, long, 0644);
77 #endif
78 MODULE_PARM_DESC (idle_func, "address of default idle function");
79 #endif
81 #define DEVNAME "itc"
82 static spinlock_t lock = SPIN_LOCK_UNLOCKED;
84 static void (*orig_pm_idle) (void);
85 static unsigned int itc_major;
87 struct itc
89 struct timeval cumm_sleep_time;
90 struct timeval sleep_started;
91 int sleeping;
94 static int in_use;
95 static struct itc global_itc[NR_CPUS];
97 /**********************************************************************
99 * Utility functions
101 **********************************************************************/
102 static void
103 cpeamb (struct timeval *c, struct timeval *a, struct timeval *b)
105 __typeof (c->tv_sec) sec = a->tv_sec - b->tv_sec;
106 __typeof (c->tv_usec) usec = a->tv_usec - b->tv_usec;
108 if (usec < 0)
110 sec -= 1;
111 usec = 1000000 + usec;
113 c->tv_usec += usec;
114 if (c->tv_usec > 1000000)
116 c->tv_sec += sec + 1;
117 c->tv_usec -= 1000000;
119 else
121 c->tv_sec += sec;
125 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
126 /* XXX: 2.4 */
127 /**********************************************************************
129 * Dummy to make sure we are unloaded properly
131 **********************************************************************/
132 static void
133 dummy_wakeup (void *unused)
135 printk (KERN_DEBUG "waking up %d\n", smp_processor_id ());
136 /* needed? safe? */
137 set_need_resched ();
139 #endif
141 /**********************************************************************
143 * idle
145 **********************************************************************/
146 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
147 #define QUIRK
148 #else
149 void default_idle (void);
150 #endif
152 static void
153 itc_monotonic (struct timeval *tv)
155 do_gettimeofday (tv);
158 static void
159 itc_idle (void)
161 struct itc *itc;
162 struct timeval tv;
163 unsigned long flags;
165 #ifdef ITC_PREEMPT_HACK
166 preempt_disable ();
167 #endif
169 /* printk ("idle in %d\n", smp_processor_id ()); */
170 spin_lock_irqsave (&lock, flags);
171 itc = &global_itc[smp_processor_id ()];
172 itc_monotonic (&itc->sleep_started);
173 itc->sleeping = 1;
174 spin_unlock_irqrestore (&lock, flags);
176 #ifdef QUIRK
177 if (orig_pm_idle)
179 orig_pm_idle ();
181 #ifdef CONFIG_X86
182 else
184 fidle_func ();
186 #endif
187 #else
188 if (orig_pm_idle)
190 orig_pm_idle ();
192 else
194 #ifdef CONFIG_X86
195 if (fidle_func)
197 fidle_func ();
199 else
200 #endif
202 default_idle ();
205 #endif
207 spin_lock_irqsave (&lock, flags);
208 itc_monotonic (&tv);
209 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
210 itc->sleeping = 0;
211 spin_unlock_irqrestore (&lock, flags);
212 /* printk ("idle out %d\n", smp_processor_id ()); */
214 #ifdef ITC_PREEMPT_HACK
215 preempt_enable ();
216 #endif
219 /**********************************************************************
221 * File operations
223 **********************************************************************/
224 static int
225 itc_open (struct inode * inode, struct file * file);
227 static int
228 itc_release (struct inode * inode, struct file * file);
230 static int
231 itc_ioctl (struct inode * inode, struct file * file,
232 unsigned int cmd, unsigned long arg);
234 static ssize_t
235 itc_read (struct file * file, char * buf, size_t count, loff_t * ppos);
237 static struct file_operations itc_fops =
239 .owner = THIS_MODULE,
240 .open = itc_open,
241 .release = itc_release,
242 .ioctl = itc_ioctl,
243 .llseek = no_llseek,
244 .read = itc_read,
247 static struct miscdevice itc_misc_dev =
249 .minor = MISC_DYNAMIC_MINOR,
250 .name = "itc",
251 .fops = &itc_fops
254 static int
255 itc_release (struct inode * inode, struct file * filp)
257 itc_enter_bkl ();
258 pm_idle = orig_pm_idle;
259 in_use = 0;
260 itc_leave_bkl ();
261 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
262 /* XXX: 2.4 */
263 on_each_cpu (dummy_wakeup, NULL, 0, 1);
264 #endif
265 return 0;
268 static int
269 itc_open (struct inode * inode, struct file * filp)
271 int ret = 0;
272 const struct file_operations *old_fops = filp->f_op;
273 unsigned int minor = iminor (inode);
275 if (itc_major)
277 if (minor != 0)
279 return -ENODEV;
283 if (in_use)
285 return -EALREADY;
288 /* old_fops = filp->f_op; */
289 filp->f_op = fops_get (&itc_fops);
290 fops_put (old_fops);
292 itc_enter_bkl ();
293 if (pm_idle != itc_idle)
295 orig_pm_idle = pm_idle;
297 pm_idle = itc_idle;
298 in_use = 1;
299 itc_leave_bkl ();
301 return ret;
304 static ssize_t
305 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
307 int i;
308 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
309 ssize_t retval = 0;
310 unsigned long flags;
311 struct itc *itc = &global_itc[0];
312 struct timeval tmp[NR_CPUS], *tmpp;
314 tmpp = tmp;
315 if (count < itemsize * num_present_cpus ())
317 printk (KERN_ERR
318 "attempt to read something funny %d expected %d(%d,%d)\n",
319 count, itemsize * num_present_cpus (),
320 itemsize, num_present_cpus ());
321 return -EINVAL;
324 spin_lock_irqsave (&lock, flags);
325 for (i = 0; i < NR_CPUS; ++i, ++itc)
327 if (cpu_present (i))
329 if (itc->sleeping)
331 struct timeval tv;
333 itc_monotonic (&tv);
334 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
335 itc->sleep_started.tv_sec = tv.tv_sec;
336 itc->sleep_started.tv_usec = tv.tv_usec;
339 *tmpp++ = itc->cumm_sleep_time;
340 retval += itemsize;
343 spin_unlock_irqrestore (&lock, flags);
345 if (copy_to_user (buf, tmp, retval))
347 printk (KERN_ERR "failed to write %zu bytes to %p\n",
348 retval, buf);
349 retval = -EFAULT;
351 return retval;
354 /**********************************************************************
356 * ioctl handler
358 **********************************************************************/
359 static int
360 itc_ioctl (struct inode * inode, struct file * filp,
361 unsigned int cmd, unsigned long arg)
363 return -EINVAL;
366 /**********************************************************************
368 * Module constructor
370 **********************************************************************/
371 static __init int
372 init (void)
374 int err;
376 #ifdef CONFIG_X86
377 fidle_func = (void (*) (void)) idle_func;
378 #endif
380 #ifdef QUIRK
381 if (!pm_idle
382 #ifdef CONFIG_X86
383 && !fidle_func
384 #endif
387 printk
388 (KERN_ERR
389 "itc: no idle function\n"
390 "itc: boot kernel with idle=halt option\n"
391 "itc: or specify idle_func (modprobe itc idle_func=<address>)\n");
392 return -ENODEV;
394 #endif
396 if (itc_major)
398 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
399 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
401 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
402 itc_major, err);
403 return -ENODEV;
406 if (!itc_major)
408 itc_major = err;
411 else
413 err = misc_register (&itc_misc_dev);
414 if (err < 0)
416 printk (KERN_ERR "itc: misc_register failed err=%d\n", err);
417 return err;
421 orig_pm_idle = pm_idle;
422 printk
423 (KERN_DEBUG
424 "itc: driver loaded pm_idle=%p default_idle=%p"
425 #ifdef CONFIG_X86
426 ", idle_func=%p"
427 #endif
428 "\n",
429 pm_idle
430 #ifdef QUIRK
431 , NULL
432 #else
433 , default_idle
434 #endif
435 #ifdef CONFIG_X86
436 , fidle_func
437 #endif
439 printk (KERN_DEBUG "itc: CPUs(%d present=%d online=%d)"
440 #ifdef QUIRK
441 " Q"
442 #endif
443 #ifdef CONFIG_APM
444 " A"
445 #endif
446 #ifdef CONFIG_SMP
447 " S"
448 #endif
449 #ifdef CONFIG_PREEMPT
450 " P"
451 #endif
452 "\n",
453 NR_CPUS, num_present_cpus (), num_online_cpus ());
454 return 0;
457 /**********************************************************************
459 * Module destructor
461 **********************************************************************/
462 static __exit void
463 fini (void)
465 printk (KERN_DEBUG "itc: unloading (resetting pm_idle to %p)\n",
466 orig_pm_idle);
467 if (itc_major)
469 unregister_chrdev (itc_major, DEVNAME);
471 else
473 misc_deregister (&itc_misc_dev);
475 printk (KERN_DEBUG "itc: unloaded\n");
478 module_init (init);
479 module_exit (fini);