v0.97a
[apc.git] / mod / itc-mod.c
blob8135ee78bc7704a3ed79844d7052fafb06036b0e
1 /* #define ITC_PREEMPT_HACK */
2 /* uncomment the above if APC blatantly lies and PREEMPTION is enabled */
3 #include <linux/signal.h>
4 #include <linux/sched.h>
5 #include <linux/interrupt.h>
7 #include <linux/version.h>
8 #include <linux/vmalloc.h>
9 #include <linux/module.h>
10 #include <linux/delay.h>
11 #include <linux/init.h>
12 #include <linux/slab.h>
13 #include <linux/smp.h>
14 #include <linux/mm.h>
15 #include <linux/pm.h>
17 #include <asm/system.h>
18 #include <asm/uaccess.h>
20 #ifdef CONFIG_6xx
21 #include <asm/machdep.h>
22 #define pm_idle ppc_md.power_save
23 #endif
25 #if !(defined CONFIG_X86 || defined CONFIG_6xx)
26 #error Support for this architecture is not written yet
27 #endif
29 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
30 #include <linux/wrapper.h>
31 #include <linux/smp_lock.h>
32 #ifdef CONFIG_SMP
33 #error Support for SMP on 2.4 series of kernels is not written yet
34 #else
35 #define num_present_cpus() 1
36 #define num_online_cpus() 1
37 /* #define cpu_online(n) 1 */
38 #define cpu_present(n) 1
39 #endif
41 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20)
42 #define iminor(inode) MINOR((inode)->i_rdev)
43 #else
44 #define iminor(inode) minor((inode)->i_rdev)
45 #endif
46 #endif
48 #ifdef CONFIG_PREEMPT
49 #define itc_enter_bkl() do { \
50 preempt_disable (); \
51 lock_kernel (); \
52 } while (0)
53 #define itc_leave_bkl() do { \
54 unlock_kernel (); \
55 preempt_enable (); \
56 } while (0)
57 #else
58 #define itc_enter_bkl lock_kernel
59 #define itc_leave_bkl unlock_kernel
60 #ifdef ITC_PREEMPT_HACK
61 #error Attempt to enable ITC_PREEMPT_HACK on non preemptible kernel
62 #endif
63 #endif
65 MODULE_DESCRIPTION ("Idle time collector");
67 static void (*idle_func) (void);
69 #ifdef CONFIG_X86
70 /* there are many ways to prevent gcc from complaining about module_param
71 and function pointer vs long, but let's not */
72 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
73 MODULE_PARM (idle_func, "l");
74 #else
75 module_param (idle_func, long, 0777);
76 #endif
77 MODULE_PARM_DESC (idle_func, "address of default idle function");
78 #endif
80 #define DEVNAME "itc"
81 static spinlock_t lock = SPIN_LOCK_UNLOCKED;
83 static void (*orig_pm_idle) (void);
84 static unsigned int itc_major;
86 struct itc
88 struct timeval cumm_sleep_time;
89 struct timeval sleep_started;
90 int sleeping;
93 static struct itc global_itc[NR_CPUS];
95 /**********************************************************************
97 * Utility functions
99 **********************************************************************/
100 static void
101 cpeamb (struct timeval *c, struct timeval *a, struct timeval *b)
103 __typeof (c->tv_sec) sec = a->tv_sec - b->tv_sec;
104 __typeof (c->tv_usec) usec = a->tv_usec - b->tv_usec;
106 if (usec < 0)
108 sec -= 1;
109 usec = 1000000 + usec;
111 c->tv_usec += usec;
112 if (c->tv_usec > 1000000)
114 c->tv_sec += sec + 1;
115 c->tv_usec -= 1000000;
117 else
119 c->tv_sec += sec;
123 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
124 /* XXX: 2.4 */
125 /**********************************************************************
127 * Dummy to make sure we are unloaded properly
129 **********************************************************************/
130 static void
131 dummy_wakeup (void *unused)
133 printk (KERN_DEBUG "waking up %d\n", smp_processor_id ());
134 /* needed? safe? */
135 set_need_resched ();
137 #endif
139 /**********************************************************************
141 * idle
143 **********************************************************************/
144 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
145 #ifndef CONFIG_APM
146 #define QUIRK
147 #endif
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 else
183 idle_func ();
185 #else
186 if (orig_pm_idle)
188 orig_pm_idle ();
190 else
192 if (idle_func)
194 idle_func ();
196 else
198 default_idle ();
201 #endif
203 spin_lock_irqsave (&lock, flags);
204 itc_monotonic (&tv);
205 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
206 itc->sleeping = 0;
207 spin_unlock_irqrestore (&lock, flags);
208 /* printk ("idle out %d\n", smp_processor_id ()); */
210 #ifdef ITC_PREEMPT_HACK
211 preempt_enable ();
212 #endif
215 /**********************************************************************
217 * File operations
219 **********************************************************************/
220 static int
221 itc_open (struct inode * inode, struct file * file);
223 static int
224 itc_release (struct inode * inode, struct file * file);
226 static int
227 itc_ioctl (struct inode * inode, struct file * file,
228 unsigned int cmd, unsigned long arg);
230 static ssize_t
231 itc_read (struct file * file, char * buf, size_t count, loff_t * ppos);
233 static struct file_operations itc_fops =
235 .owner = THIS_MODULE,
236 .open = itc_open,
237 .release = itc_release,
238 .ioctl = itc_ioctl,
239 .llseek = no_llseek,
240 .read = itc_read,
243 static int
244 itc_release (struct inode * inode, struct file * filp)
246 itc_enter_bkl ();
247 pm_idle = orig_pm_idle;
248 itc_leave_bkl ();
249 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
250 /* XXX: 2.4 */
251 on_each_cpu (dummy_wakeup, NULL, 0, 1);
252 #endif
253 return 0;
256 static int
257 itc_open (struct inode * inode, struct file * filp)
259 int ret = 0;
260 const struct file_operations *old_fops = filp->f_op;
261 unsigned int minor = iminor (inode);
263 if (minor != 0)
265 return -ENODEV;
268 /* old_fops = filp->f_op; */
269 filp->f_op = fops_get (&itc_fops);
270 fops_put (old_fops);
272 itc_enter_bkl ();
273 if (pm_idle != itc_idle)
275 orig_pm_idle = pm_idle;
277 pm_idle = itc_idle;
278 itc_leave_bkl ();
280 return ret;
283 static ssize_t
284 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
286 int i;
287 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
288 ssize_t retval = 0;
289 unsigned long flags;
290 struct itc *itc = &global_itc[0];
292 if (count < itemsize * num_present_cpus ())
294 printk (KERN_ERR
295 "attempt to read something funny %d expected %d(%d,%d)\n",
296 count, itemsize * num_present_cpus (),
297 itemsize, num_present_cpus ());
298 return -EINVAL;
301 spin_lock_irqsave (&lock, flags);
302 for (i = 0; i < NR_CPUS; ++i, ++itc)
304 if (cpu_present (i))
306 if (itc->sleeping)
308 struct timeval tv;
310 itc_monotonic (&tv);
311 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
312 itc->sleep_started.tv_sec = tv.tv_sec;
313 itc->sleep_started.tv_usec = tv.tv_usec;
316 if (copy_to_user (buf, &itc->cumm_sleep_time, itemsize))
318 printk (KERN_ERR "failed to write %zu bytes to %p\n",
319 itemsize, buf);
320 retval = -EFAULT;
321 break;
323 retval += itemsize;
324 buf += itemsize;
327 spin_unlock_irqrestore (&lock, flags);
329 return retval;
332 /**********************************************************************
334 * ioctl handler
336 **********************************************************************/
337 static int
338 itc_ioctl (struct inode * inode, struct file * filp,
339 unsigned int cmd, unsigned long arg)
341 return -EINVAL;
344 /**********************************************************************
346 * Module constructor
348 **********************************************************************/
349 static __init int
350 init (void)
352 int err;
354 #ifdef QUIRK
355 if (!pm_idle && !idle_func)
357 printk
358 (KERN_ERR
359 "itc: no idle function\n"
360 "itc: boot kernel with idle=halt option\n"
361 "itc: or specify idle_func (modprobe itc idle_func=<address>)\n");
362 return -ENODEV;
364 #endif
366 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
367 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
369 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
370 itc_major, err);
371 return -ENODEV;
374 if (!itc_major)
376 itc_major = err;
379 orig_pm_idle = pm_idle;
380 printk
381 (KERN_DEBUG
382 "itc: driver loaded pm_idle=%p default_idle=%p, idle_func=%p\n",
383 pm_idle,
384 #ifdef QUIRK
385 NULL,
386 #else
387 default_idle,
388 #endif
389 idle_func
391 printk (KERN_DEBUG "itc: CPUs(%d present=%d online=%d)"
392 #ifdef QUIRK
393 " Q"
394 #endif
395 #ifdef CONFIG_APM
396 " A"
397 #endif
398 #ifdef CONFIG_SMP
399 " S"
400 #endif
401 #ifdef CONFIG_PREEMPT
402 " P"
403 #endif
404 "\n",
405 NR_CPUS, num_present_cpus (), num_online_cpus ());
406 return 0;
409 /**********************************************************************
411 * Module destructor
413 **********************************************************************/
414 static __exit void
415 fini (void)
417 printk (KERN_DEBUG "itc: unloading (resetting pm_idle to %p)\n",
418 orig_pm_idle);
419 unregister_chrdev (itc_major, DEVNAME);
420 printk (KERN_DEBUG "itc: unloaded\n");
423 module_init (init);
424 module_exit (fini);