2.6.23 fix - include linux/fs.h for definition of file_operations
[apc.git] / mod / itc-mod.c
blob2f2e45718335666eee56356acc9a258125600e51
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>
21 #include <asm/system.h>
22 #include <asm/uaccess.h>
24 #ifdef CONFIG_6xx
25 #include <asm/machdep.h>
26 #define pm_idle ppc_md.power_save
27 #endif
29 #if !(defined CONFIG_X86 || defined CONFIG_6xx)
30 #error Support for this architecture is not written yet
31 #endif
33 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
34 #include <linux/wrapper.h>
35 #include <linux/smp_lock.h>
36 #ifdef CONFIG_SMP
37 #error Support for SMP on 2.4 series of kernels is not written yet
38 #else
39 #define num_present_cpus() 1
40 #define num_online_cpus() 1
41 /* #define cpu_online(n) 1 */
42 #define cpu_present(n) 1
43 #endif
45 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20)
46 #define iminor(inode) MINOR((inode)->i_rdev)
47 #else
48 #define iminor(inode) minor((inode)->i_rdev)
49 #endif
50 #endif
52 #ifdef CONFIG_PREEMPT
53 #define itc_enter_bkl() do { \
54 preempt_disable (); \
55 lock_kernel (); \
56 } while (0)
57 #define itc_leave_bkl() do { \
58 unlock_kernel (); \
59 preempt_enable (); \
60 } while (0)
61 #else
62 #define itc_enter_bkl lock_kernel
63 #define itc_leave_bkl unlock_kernel
64 #ifdef ITC_PREEMPT_HACK
65 #error Attempt to enable ITC_PREEMPT_HACK on non preemptible kernel
66 #endif
67 #endif
69 MODULE_DESCRIPTION ("Idle time collector");
71 #ifdef CONFIG_X86
72 static void (*fidle_func) (void);
73 static long idle_func;
74 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
75 MODULE_PARM (idle_func, "l");
76 #else
77 module_param (idle_func, long, 0644);
78 #endif
79 MODULE_PARM_DESC (idle_func, "address of default idle function");
80 #endif
82 #define DEVNAME "itc"
83 static spinlock_t lock = SPIN_LOCK_UNLOCKED;
85 static void (*orig_pm_idle) (void);
86 static unsigned int itc_major;
88 struct itc
90 struct timeval cumm_sleep_time;
91 struct timeval sleep_started;
92 int sleeping;
95 static int in_use;
96 static struct itc global_itc[NR_CPUS];
98 /**********************************************************************
100 * Utility functions
102 **********************************************************************/
103 static void
104 cpeamb (struct timeval *c, struct timeval *a, struct timeval *b)
106 __typeof (c->tv_sec) sec = a->tv_sec - b->tv_sec;
107 __typeof (c->tv_usec) usec = a->tv_usec - b->tv_usec;
109 if (usec < 0)
111 sec -= 1;
112 usec = 1000000 + usec;
114 c->tv_usec += usec;
115 if (c->tv_usec > 1000000)
117 c->tv_sec += sec + 1;
118 c->tv_usec -= 1000000;
120 else
122 c->tv_sec += sec;
126 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
127 /* XXX: 2.4 */
128 /**********************************************************************
130 * Dummy to make sure we are unloaded properly
132 **********************************************************************/
133 static void
134 dummy_wakeup (void *unused)
136 printk (KERN_DEBUG "waking up %d\n", smp_processor_id ());
137 /* needed? safe? */
138 set_need_resched ();
140 #endif
142 /**********************************************************************
144 * idle
146 **********************************************************************/
147 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
148 #define QUIRK
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 struct miscdevice itc_misc_dev =
250 .minor = MISC_DYNAMIC_MINOR,
251 .name = "itc",
252 .fops = &itc_fops
255 static int
256 itc_release (struct inode * inode, struct file * filp)
258 itc_enter_bkl ();
259 pm_idle = orig_pm_idle;
260 in_use = 0;
261 itc_leave_bkl ();
262 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0)
263 /* XXX: 2.4 */
264 on_each_cpu (dummy_wakeup, NULL, 0, 1);
265 #endif
266 return 0;
269 static int
270 itc_open (struct inode * inode, struct file * filp)
272 int ret = 0;
273 const struct file_operations *old_fops = filp->f_op;
274 unsigned int minor = iminor (inode);
276 if (itc_major)
278 if (minor != 0)
280 return -ENODEV;
284 if (in_use)
286 return -EALREADY;
289 /* old_fops = filp->f_op; */
290 filp->f_op = fops_get (&itc_fops);
291 fops_put (old_fops);
293 itc_enter_bkl ();
294 if (pm_idle != itc_idle)
296 orig_pm_idle = pm_idle;
298 pm_idle = itc_idle;
299 in_use = 1;
300 itc_leave_bkl ();
302 return ret;
305 static ssize_t
306 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
308 int i;
309 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
310 ssize_t retval = 0;
311 unsigned long flags;
312 struct itc *itc = &global_itc[0];
313 struct timeval tmp[NR_CPUS], *tmpp;
315 tmpp = tmp;
316 if (count < itemsize * num_present_cpus ())
318 printk (KERN_ERR
319 "attempt to read something funny %d expected %d(%d,%d)\n",
320 count, itemsize * num_present_cpus (),
321 itemsize, num_present_cpus ());
322 return -EINVAL;
325 spin_lock_irqsave (&lock, flags);
326 for (i = 0; i < NR_CPUS; ++i, ++itc)
328 if (cpu_present (i))
330 if (itc->sleeping)
332 struct timeval tv;
334 itc_monotonic (&tv);
335 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
336 itc->sleep_started.tv_sec = tv.tv_sec;
337 itc->sleep_started.tv_usec = tv.tv_usec;
340 *tmpp++ = itc->cumm_sleep_time;
341 retval += itemsize;
344 spin_unlock_irqrestore (&lock, flags);
346 if (copy_to_user (buf, tmp, retval))
348 printk (KERN_ERR "failed to write %zu bytes to %p\n",
349 retval, buf);
350 retval = -EFAULT;
352 return retval;
355 /**********************************************************************
357 * ioctl handler
359 **********************************************************************/
360 static int
361 itc_ioctl (struct inode * inode, struct file * filp,
362 unsigned int cmd, unsigned long arg)
364 return -EINVAL;
367 /**********************************************************************
369 * Module constructor
371 **********************************************************************/
372 static __init int
373 init (void)
375 int err;
377 #ifdef CONFIG_X86
378 fidle_func = (void (*) (void)) idle_func;
379 #endif
381 #ifdef QUIRK
382 if (!pm_idle
383 #ifdef CONFIG_X86
384 && !fidle_func
385 #endif
388 printk
389 (KERN_ERR
390 "itc: no idle function\n"
391 "itc: boot kernel with idle=halt option\n"
392 "itc: or specify idle_func (modprobe itc idle_func=<address>)\n");
393 return -ENODEV;
395 #endif
397 if (itc_major)
399 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
400 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
402 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
403 itc_major, err);
404 return -ENODEV;
407 if (!itc_major)
409 itc_major = err;
412 else
414 err = misc_register (&itc_misc_dev);
415 if (err < 0)
417 printk (KERN_ERR "itc: misc_register failed err=%d\n", err);
418 return err;
422 orig_pm_idle = pm_idle;
423 printk
424 (KERN_DEBUG
425 "itc: driver loaded pm_idle=%p default_idle=%p"
426 #ifdef CONFIG_X86
427 ", idle_func=%p"
428 #endif
429 "\n",
430 pm_idle
431 #ifdef QUIRK
432 , NULL
433 #else
434 , default_idle
435 #endif
436 #ifdef CONFIG_X86
437 , fidle_func
438 #endif
440 printk (KERN_DEBUG "itc: CPUs(%d present=%d online=%d)"
441 #ifdef QUIRK
442 " Q"
443 #endif
444 #ifdef CONFIG_APM
445 " A"
446 #endif
447 #ifdef CONFIG_SMP
448 " S"
449 #endif
450 #ifdef CONFIG_PREEMPT
451 " P"
452 #endif
453 "\n",
454 NR_CPUS, num_present_cpus (), num_online_cpus ());
455 return 0;
458 /**********************************************************************
460 * Module destructor
462 **********************************************************************/
463 static __exit void
464 fini (void)
466 printk (KERN_DEBUG "itc: unloading (resetting pm_idle to %p)\n",
467 orig_pm_idle);
468 if (itc_major)
470 unregister_chrdev (itc_major, DEVNAME);
472 else
474 misc_deregister (&itc_misc_dev);
476 printk (KERN_DEBUG "itc: unloaded\n");
479 module_init (init);
480 module_exit (fini);