v1.00
[apc.git] / mod / itc-mod.c
blobdb793710fe71e9c49e5d11f9e5e000fdcb856d33
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>
19 #include <asm/system.h>
20 #include <asm/uaccess.h>
22 #ifdef CONFIG_6xx
23 #include <asm/machdep.h>
24 #define pm_idle ppc_md.power_save
25 #endif
27 #if !(defined CONFIG_X86 || defined CONFIG_6xx)
28 #error Support for this architecture is not written yet
29 #endif
31 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
32 #include <linux/wrapper.h>
33 #include <linux/smp_lock.h>
34 #ifdef CONFIG_SMP
35 #error Support for SMP on 2.4 series of kernels is not written yet
36 #else
37 #define num_present_cpus() 1
38 #define num_online_cpus() 1
39 /* #define cpu_online(n) 1 */
40 #define cpu_present(n) 1
41 #endif
43 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20)
44 #define iminor(inode) MINOR((inode)->i_rdev)
45 #else
46 #define iminor(inode) minor((inode)->i_rdev)
47 #endif
48 #endif
50 #ifdef CONFIG_PREEMPT
51 #define itc_enter_bkl() do { \
52 preempt_disable (); \
53 lock_kernel (); \
54 } while (0)
55 #define itc_leave_bkl() do { \
56 unlock_kernel (); \
57 preempt_enable (); \
58 } while (0)
59 #else
60 #define itc_enter_bkl lock_kernel
61 #define itc_leave_bkl unlock_kernel
62 #ifdef ITC_PREEMPT_HACK
63 #error Attempt to enable ITC_PREEMPT_HACK on non preemptible kernel
64 #endif
65 #endif
67 MODULE_DESCRIPTION ("Idle time collector");
69 #ifdef CONFIG_X86
70 static void (*fidle_func) (void);
71 static long idle_func;
72 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
73 MODULE_PARM (idle_func, "l");
74 #else
75 module_param (idle_func, long, 0644);
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 int in_use;
94 static struct itc global_itc[NR_CPUS];
96 /**********************************************************************
98 * Utility functions
100 **********************************************************************/
101 static void
102 cpeamb (struct timeval *c, struct timeval *a, struct timeval *b)
104 __typeof (c->tv_sec) sec = a->tv_sec - b->tv_sec;
105 __typeof (c->tv_usec) usec = a->tv_usec - b->tv_usec;
107 if (usec < 0)
109 sec -= 1;
110 usec = 1000000 + usec;
112 c->tv_usec += usec;
113 if (c->tv_usec > 1000000)
115 c->tv_sec += sec + 1;
116 c->tv_usec -= 1000000;
118 else
120 c->tv_sec += sec;
124 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
125 /* XXX: 2.4 */
126 /**********************************************************************
128 * Dummy to make sure we are unloaded properly
130 **********************************************************************/
131 static void
132 dummy_wakeup (void *unused)
134 printk (KERN_DEBUG "waking up %d\n", smp_processor_id ());
135 /* needed? safe? */
136 set_need_resched ();
138 #endif
140 /**********************************************************************
142 * idle
144 **********************************************************************/
145 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
146 #ifndef CONFIG_APM
147 #define QUIRK
148 #endif
149 #else
150 void default_idle (void);
151 #endif
153 static void
154 itc_monotonic (struct timeval *tv)
156 do_gettimeofday (tv);
159 static void
160 itc_idle (void)
162 struct itc *itc;
163 struct timeval tv;
164 unsigned long flags;
166 #ifdef ITC_PREEMPT_HACK
167 preempt_disable ();
168 #endif
170 /* printk ("idle in %d\n", smp_processor_id ()); */
171 spin_lock_irqsave (&lock, flags);
172 itc = &global_itc[smp_processor_id ()];
173 itc_monotonic (&itc->sleep_started);
174 itc->sleeping = 1;
175 spin_unlock_irqrestore (&lock, flags);
177 #ifdef QUIRK
178 if (orig_pm_idle)
180 orig_pm_idle ();
182 #ifdef CONFIG_X86
183 else
185 fidle_func ();
187 #endif
188 #else
189 if (orig_pm_idle)
191 orig_pm_idle ();
193 else
195 #ifdef CONFIG_X86
196 if (fidle_func)
198 fidle_func ();
200 else
201 #endif
203 default_idle ();
206 #endif
208 spin_lock_irqsave (&lock, flags);
209 itc_monotonic (&tv);
210 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
211 itc->sleeping = 0;
212 spin_unlock_irqrestore (&lock, flags);
213 /* printk ("idle out %d\n", smp_processor_id ()); */
215 #ifdef ITC_PREEMPT_HACK
216 preempt_enable ();
217 #endif
220 /**********************************************************************
222 * File operations
224 **********************************************************************/
225 static int
226 itc_open (struct inode * inode, struct file * file);
228 static int
229 itc_release (struct inode * inode, struct file * file);
231 static int
232 itc_ioctl (struct inode * inode, struct file * file,
233 unsigned int cmd, unsigned long arg);
235 static ssize_t
236 itc_read (struct file * file, char * buf, size_t count, loff_t * ppos);
238 static struct file_operations itc_fops =
240 .owner = THIS_MODULE,
241 .open = itc_open,
242 .release = itc_release,
243 .ioctl = itc_ioctl,
244 .llseek = no_llseek,
245 .read = itc_read,
248 static int
249 itc_release (struct inode * inode, struct file * filp)
251 itc_enter_bkl ();
252 pm_idle = orig_pm_idle;
253 in_use = 0;
254 itc_leave_bkl ();
255 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
256 /* XXX: 2.4 */
257 on_each_cpu (dummy_wakeup, NULL, 0, 1);
258 #endif
259 return 0;
262 static int
263 itc_open (struct inode * inode, struct file * filp)
265 int ret = 0;
266 const struct file_operations *old_fops = filp->f_op;
267 unsigned int minor = iminor (inode);
269 if (minor != 0)
271 return -ENODEV;
274 if (in_use)
276 return -EALREADY;
279 /* old_fops = filp->f_op; */
280 filp->f_op = fops_get (&itc_fops);
281 fops_put (old_fops);
283 itc_enter_bkl ();
284 if (pm_idle != itc_idle)
286 orig_pm_idle = pm_idle;
288 pm_idle = itc_idle;
289 in_use = 1;
290 itc_leave_bkl ();
292 return ret;
295 static ssize_t
296 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
298 int i;
299 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
300 ssize_t retval = 0;
301 unsigned long flags;
302 struct itc *itc = &global_itc[0];
304 if (count < itemsize * num_present_cpus ())
306 printk (KERN_ERR
307 "attempt to read something funny %d expected %d(%d,%d)\n",
308 count, itemsize * num_present_cpus (),
309 itemsize, num_present_cpus ());
310 return -EINVAL;
313 spin_lock_irqsave (&lock, flags);
314 for (i = 0; i < NR_CPUS; ++i, ++itc)
316 if (cpu_present (i))
318 if (itc->sleeping)
320 struct timeval tv;
322 itc_monotonic (&tv);
323 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
324 itc->sleep_started.tv_sec = tv.tv_sec;
325 itc->sleep_started.tv_usec = tv.tv_usec;
328 if (copy_to_user (buf, &itc->cumm_sleep_time, itemsize))
330 printk (KERN_ERR "failed to write %zu bytes to %p\n",
331 itemsize, buf);
332 retval = -EFAULT;
333 break;
335 retval += itemsize;
336 buf += itemsize;
339 spin_unlock_irqrestore (&lock, flags);
341 return retval;
344 /**********************************************************************
346 * ioctl handler
348 **********************************************************************/
349 static int
350 itc_ioctl (struct inode * inode, struct file * filp,
351 unsigned int cmd, unsigned long arg)
353 return -EINVAL;
356 /**********************************************************************
358 * Module constructor
360 **********************************************************************/
361 static __init int
362 init (void)
364 int err;
366 #ifdef CONFIG_X86
367 fidle_func = (void (*) (void)) idle_func;
368 #endif
370 #ifdef QUIRK
371 if (!pm_idle
372 #ifdef CONFIG_X86
373 && !fidle_func
374 #endif
377 printk
378 (KERN_ERR
379 "itc: no idle function\n"
380 "itc: boot kernel with idle=halt option\n"
381 "itc: or specify idle_func (modprobe itc idle_func=<address>)\n");
382 return -ENODEV;
384 #endif
386 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
387 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
389 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
390 itc_major, err);
391 return -ENODEV;
394 if (!itc_major)
396 itc_major = err;
399 orig_pm_idle = pm_idle;
400 printk
401 (KERN_DEBUG
402 "itc: driver loaded pm_idle=%p default_idle=%p"
403 #ifdef CONFIG_X86
404 ", idle_func=%p"
405 #endif
406 "\n",
407 pm_idle
408 #ifdef QUIRK
409 , NULL
410 #else
411 , default_idle
412 #endif
413 #ifdef CONFIG_X86
414 , fidle_func
415 #endif
417 printk (KERN_DEBUG "itc: CPUs(%d present=%d online=%d)"
418 #ifdef QUIRK
419 " Q"
420 #endif
421 #ifdef CONFIG_APM
422 " A"
423 #endif
424 #ifdef CONFIG_SMP
425 " S"
426 #endif
427 #ifdef CONFIG_PREEMPT
428 " P"
429 #endif
430 "\n",
431 NR_CPUS, num_present_cpus (), num_online_cpus ());
432 return 0;
435 /**********************************************************************
437 * Module destructor
439 **********************************************************************/
440 static __exit void
441 fini (void)
443 printk (KERN_DEBUG "itc: unloading (resetting pm_idle to %p)\n",
444 orig_pm_idle);
445 unregister_chrdev (itc_major, DEVNAME);
446 printk (KERN_DEBUG "itc: unloaded\n");
449 module_init (init);
450 module_exit (fini);