TBS based build system
[apc.git] / mod / itc-mod.c
blobf43999af50fab582ff5a67689f4480c3e1672dbb
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 #ifndef CONFIG_APM
148 #define QUIRK
149 #endif
150 #else
151 void default_idle (void);
152 #endif
154 static void
155 itc_monotonic (struct timeval *tv)
157 do_gettimeofday (tv);
160 static void
161 itc_idle (void)
163 struct itc *itc;
164 struct timeval tv;
165 unsigned long flags;
167 #ifdef ITC_PREEMPT_HACK
168 preempt_disable ();
169 #endif
171 /* printk ("idle in %d\n", smp_processor_id ()); */
172 spin_lock_irqsave (&lock, flags);
173 itc = &global_itc[smp_processor_id ()];
174 itc_monotonic (&itc->sleep_started);
175 itc->sleeping = 1;
176 spin_unlock_irqrestore (&lock, flags);
178 #ifdef QUIRK
179 if (orig_pm_idle)
181 orig_pm_idle ();
183 #ifdef CONFIG_X86
184 else
186 fidle_func ();
188 #endif
189 #else
190 if (orig_pm_idle)
192 orig_pm_idle ();
194 else
196 #ifdef CONFIG_X86
197 if (fidle_func)
199 fidle_func ();
201 else
202 #endif
204 default_idle ();
207 #endif
209 spin_lock_irqsave (&lock, flags);
210 itc_monotonic (&tv);
211 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
212 itc->sleeping = 0;
213 spin_unlock_irqrestore (&lock, flags);
214 /* printk ("idle out %d\n", smp_processor_id ()); */
216 #ifdef ITC_PREEMPT_HACK
217 preempt_enable ();
218 #endif
221 /**********************************************************************
223 * File operations
225 **********************************************************************/
226 static int
227 itc_open (struct inode * inode, struct file * file);
229 static int
230 itc_release (struct inode * inode, struct file * file);
232 static int
233 itc_ioctl (struct inode * inode, struct file * file,
234 unsigned int cmd, unsigned long arg);
236 static ssize_t
237 itc_read (struct file * file, char * buf, size_t count, loff_t * ppos);
239 static struct file_operations itc_fops =
241 .owner = THIS_MODULE,
242 .open = itc_open,
243 .release = itc_release,
244 .ioctl = itc_ioctl,
245 .llseek = no_llseek,
246 .read = itc_read,
249 static struct miscdevice itc_misc_dev =
251 .minor = MISC_DYNAMIC_MINOR,
252 .name = "itc",
253 .fops = &itc_fops
256 static int
257 itc_release (struct inode * inode, struct file * filp)
259 itc_enter_bkl ();
260 pm_idle = orig_pm_idle;
261 in_use = 0;
262 itc_leave_bkl ();
263 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
264 /* XXX: 2.4 */
265 on_each_cpu (dummy_wakeup, NULL, 0, 1);
266 #endif
267 return 0;
270 static int
271 itc_open (struct inode * inode, struct file * filp)
273 int ret = 0;
274 const struct file_operations *old_fops = filp->f_op;
275 unsigned int minor = iminor (inode);
277 if (itc_major)
279 if (minor != 0)
281 return -ENODEV;
285 if (in_use)
287 return -EALREADY;
290 /* old_fops = filp->f_op; */
291 filp->f_op = fops_get (&itc_fops);
292 fops_put (old_fops);
294 itc_enter_bkl ();
295 if (pm_idle != itc_idle)
297 orig_pm_idle = pm_idle;
299 pm_idle = itc_idle;
300 in_use = 1;
301 itc_leave_bkl ();
303 return ret;
306 static ssize_t
307 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
309 int i;
310 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
311 ssize_t retval = 0;
312 unsigned long flags;
313 struct itc *itc = &global_itc[0];
314 struct timeval tmp[NR_CPUS], *tmpp;
316 tmpp = tmp;
317 if (count < itemsize * num_present_cpus ())
319 printk (KERN_ERR
320 "attempt to read something funny %d expected %d(%d,%d)\n",
321 count, itemsize * num_present_cpus (),
322 itemsize, num_present_cpus ());
323 return -EINVAL;
326 spin_lock_irqsave (&lock, flags);
327 for (i = 0; i < NR_CPUS; ++i, ++itc)
329 if (cpu_present (i))
331 if (itc->sleeping)
333 struct timeval tv;
335 itc_monotonic (&tv);
336 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
337 itc->sleep_started.tv_sec = tv.tv_sec;
338 itc->sleep_started.tv_usec = tv.tv_usec;
341 *tmpp++ = itc->cumm_sleep_time;
342 retval += itemsize;
345 spin_unlock_irqrestore (&lock, flags);
347 if (copy_to_user (buf, tmp, retval))
349 printk (KERN_ERR "failed to write %zu bytes to %p\n",
350 retval, buf);
351 retval = -EFAULT;
353 return retval;
356 /**********************************************************************
358 * ioctl handler
360 **********************************************************************/
361 static int
362 itc_ioctl (struct inode * inode, struct file * filp,
363 unsigned int cmd, unsigned long arg)
365 return -EINVAL;
368 /**********************************************************************
370 * Module constructor
372 **********************************************************************/
373 static __init int
374 init (void)
376 int err;
378 #ifdef CONFIG_X86
379 fidle_func = (void (*) (void)) idle_func;
380 #endif
382 #ifdef QUIRK
383 if (!pm_idle
384 #ifdef CONFIG_X86
385 && !fidle_func
386 #endif
389 printk
390 (KERN_ERR
391 "itc: no idle function\n"
392 "itc: boot kernel with idle=halt option\n"
393 "itc: or specify idle_func (modprobe itc idle_func=<address>)\n");
394 return -ENODEV;
396 #endif
398 if (itc_major)
400 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
401 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
403 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
404 itc_major, err);
405 return -ENODEV;
408 if (!itc_major)
410 itc_major = err;
413 else
415 err = misc_register (&itc_misc_dev);
416 if (err < 0)
418 printk (KERN_ERR "itc: misc_register failed err=%d\n", err);
419 return err;
423 orig_pm_idle = pm_idle;
424 printk
425 (KERN_DEBUG
426 "itc: driver loaded pm_idle=%p default_idle=%p"
427 #ifdef CONFIG_X86
428 ", idle_func=%p"
429 #endif
430 "\n",
431 pm_idle
432 #ifdef QUIRK
433 , NULL
434 #else
435 , default_idle
436 #endif
437 #ifdef CONFIG_X86
438 , fidle_func
439 #endif
441 printk (KERN_DEBUG "itc: CPUs(%d present=%d online=%d)"
442 #ifdef QUIRK
443 " Q"
444 #endif
445 #ifdef CONFIG_APM
446 " A"
447 #endif
448 #ifdef CONFIG_SMP
449 " S"
450 #endif
451 #ifdef CONFIG_PREEMPT
452 " P"
453 #endif
454 "\n",
455 NR_CPUS, num_present_cpus (), num_online_cpus ());
456 return 0;
459 /**********************************************************************
461 * Module destructor
463 **********************************************************************/
464 static __exit void
465 fini (void)
467 printk (KERN_DEBUG "itc: unloading (resetting pm_idle to %p)\n",
468 orig_pm_idle);
469 if (itc_major)
471 unregister_chrdev (itc_major, DEVNAME);
473 else
475 misc_deregister (&itc_misc_dev);
477 printk (KERN_DEBUG "itc: unloaded\n");
480 module_init (init);
481 module_exit (fini);