v0.96
[apc.git] / mod / itc-mod.c
blob0676771414be8ff4db720e9e083b8bc5720317e9
1 #include <linux/signal.h>
2 #include <linux/sched.h>
3 #include <linux/interrupt.h>
5 #include <linux/version.h>
6 #include <linux/vmalloc.h>
7 #include <linux/module.h>
8 #include <linux/delay.h>
9 #include <linux/init.h>
10 #include <linux/slab.h>
11 #include <linux/smp.h>
12 #include <linux/mm.h>
13 #include <linux/pm.h>
15 #include <asm/system.h>
16 #include <asm/uaccess.h>
18 #ifdef CONFIG_6xx
19 #include <asm/machdep.h>
20 #define pm_idle ppc_md.power_save
21 #endif
23 #if !(defined CONFIG_X86 || defined CONFIG_6xx)
24 #error Support for this architecture is not written yet
25 #endif
27 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
28 #include <linux/wrapper.h>
29 #ifdef CONFIG_SMP
30 #define NB_CPUS weight (phys_cpu_present_map)
31 #else
32 #define NB_CPUS 1
33 #endif
35 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20)
36 #define iminor(inode) MINOR((inode)->i_rdev)
37 #else
38 #define iminor(inode) minor((inode)->i_rdev)
39 #endif
40 #else
41 #define NB_CPUS num_present_cpus ()
42 #endif
45 MODULE_DESCRIPTION ("Idle time collector");
47 static void (*idle_func) (void);
49 #ifdef CONFIG_X86
50 /* there are many ways to prevent gcc from complaining about module_param
51 and function pointer vs long, but let's not */
52 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
53 MODULE_PARM (idle_func, "l");
54 #else
55 module_param (idle_func, long, 0777);
56 #endif
57 MODULE_PARM_DESC (idle_func, "address of default idle function");
58 #endif
60 #define DEVNAME "itc"
61 static spinlock_t lock = SPIN_LOCK_UNLOCKED;
63 static void (*orig_pm_idle) (void);
64 static unsigned int itc_major;
66 struct itc
68 struct timeval cumm_sleep_time;
69 struct timeval sleep_started;
70 int sleeping;
73 static struct itc global_itc[NR_CPUS];
75 /**********************************************************************
77 * Utility functions
79 **********************************************************************/
80 static void
81 cpeamb (struct timeval *c, struct timeval *a, struct timeval *b)
83 __typeof (c->tv_sec) sec = a->tv_sec - b->tv_sec;
84 __typeof (c->tv_usec) usec = a->tv_usec - b->tv_usec;
86 if (usec < 0)
88 sec -= 1;
89 usec = 1000000 + usec;
91 c->tv_usec += usec;
92 if (c->tv_usec > 1000000)
94 c->tv_sec += sec + 1;
95 c->tv_usec -= 1000000;
97 else
99 c->tv_sec += sec;
103 /**********************************************************************
105 * File operations
107 **********************************************************************/
108 static int
109 itc_open (struct inode * inode, struct file * file);
111 static int
112 itc_release (struct inode * inode, struct file * file);
114 static int
115 itc_ioctl (struct inode * inode, struct file * file,
116 unsigned int cmd, unsigned long arg);
118 static ssize_t
119 itc_read (struct file * file, char * buf, size_t count, loff_t * ppos);
121 static struct file_operations itc_fops =
123 .owner = THIS_MODULE,
124 .open = itc_open,
125 .release = itc_release,
126 .ioctl = itc_ioctl,
127 .llseek = no_llseek,
128 .read = itc_read,
132 static int
133 itc_release (struct inode * inode, struct file * filp)
135 return 0;
138 static int
139 itc_open (struct inode * inode, struct file * filp)
141 int ret = 0;
142 const struct file_operations *old_fops = filp->f_op;
143 unsigned int minor = iminor (inode);
145 if (minor != 0)
146 return -ENODEV;
148 /* old_fops = filp->f_op; */
149 filp->f_op = fops_get (&itc_fops);
150 fops_put (old_fops);
151 return ret;
154 static ssize_t
155 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
157 int i;
158 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
159 ssize_t retval = 0;
160 unsigned long flags;
161 struct itc *itc = &global_itc[0];
163 /* printk ("itemsize=%d cpus=%d count=%d\n", itemsize, NR_CPUS, count); */
164 if (count < itemsize * NB_CPUS)
166 printk (KERN_ERR
167 "attempt to read something funny %d expected %d(%d,%d)\n",
168 count, itemsize * NB_CPUS, itemsize, NB_CPUS);
169 return -EINVAL;
172 spin_lock_irqsave (&lock, flags);
173 for (i = 0; i < NB_CPUS; ++i, ++itc)
175 if (itc->sleeping)
177 struct timeval tv;
179 do_gettimeofday (&tv);
180 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
181 itc->sleep_started.tv_sec = tv.tv_sec;
182 itc->sleep_started.tv_usec = tv.tv_usec;
185 if (copy_to_user (buf, &itc->cumm_sleep_time, itemsize))
187 printk (KERN_ERR "failed to write %zu bytes to %p\n", itemsize, buf);
188 retval = -EFAULT;
189 break;
191 retval += itemsize;
192 buf += itemsize;
195 spin_unlock_irqrestore (&lock, flags);
196 return retval;
199 /**********************************************************************
201 * ioctl handler
203 **********************************************************************/
204 static int
205 itc_ioctl (struct inode * inode, struct file * filp,
206 unsigned int cmd, unsigned long arg)
208 return 0;
211 /**********************************************************************
213 * idle
215 **********************************************************************/
216 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
217 #ifndef CONFIG_APM
218 #define QUIRK
219 #endif
220 #else
221 void default_idle (void);
222 #endif
224 static void
225 itc_idle (void)
227 struct itc *itc;
228 struct timeval tv;
229 unsigned long flags;
231 /* printk ("idle in\n"); */
232 spin_lock_irqsave (&lock, flags);
233 itc = &global_itc[smp_processor_id ()];
234 do_gettimeofday (&itc->sleep_started);
235 spin_unlock_irqrestore (&lock, flags);
237 #ifdef QUIRK
238 if (orig_pm_idle)
240 orig_pm_idle ();
242 else
244 idle_func ();
246 #else
247 if (orig_pm_idle)
249 orig_pm_idle ();
251 else
253 if (idle_func)
255 idle_func ();
257 else
259 default_idle ();
262 #endif
264 spin_lock_irqsave (&lock, flags);
265 do_gettimeofday (&tv);
266 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
267 itc->sleeping = 0;
268 spin_unlock_irqrestore (&lock, flags);
269 /* printk ("idle out\n"); */
272 /**********************************************************************
274 * Module constructor
276 **********************************************************************/
277 static __init int
278 init (void)
280 int err;
282 #ifdef QUIRK
283 if (!pm_idle && !idle_func)
285 printk
286 (KERN_ERR
287 "itc: no idle function\n"
288 "itc: boot kernel with idle=halt option\n"
289 "itc: or specify idle_func (modprobe itc idle_func=<address>)\n");
290 return -ENODEV;
292 #endif
294 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
295 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
297 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
298 itc_major, err);
299 return -ENODEV;
302 if (!itc_major)
304 itc_major = err;
307 printk
308 (KERN_DEBUG
309 "itc: driver loaded pm_idle=%p default_idle=%p, idle_func=%p\n",
310 pm_idle,
311 #ifdef QUIRK
312 NULL,
313 #else
314 default_idle,
315 #endif
316 idle_func
319 orig_pm_idle = pm_idle;
320 pm_idle = itc_idle;
321 return 0;
324 /**********************************************************************
326 * Module destructor
328 **********************************************************************/
329 static __exit void
330 fini (void)
332 printk (KERN_DEBUG "itc: unloading\n");
334 unregister_chrdev (itc_major, DEVNAME);
335 printk (KERN_DEBUG "itc: unloaded\n");
337 pm_idle = orig_pm_idle;
340 module_init (init);
341 module_exit (fini);