v0.94
[apc.git] / mod / itc-mod.c
blobdebe22603cc517b5f04323f4f4e76d468cfb301f
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>
14 #include <asm/system.h>
15 #include <asm/uaccess.h>
17 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
18 #include <linux/wrapper.h>
19 #ifdef CONFIG_SMP
20 #define NB_CPUS weight (phys_cpu_present_map)
21 #else
22 #define NB_CPUS 1
23 #endif
25 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20)
26 #define iminor(inode) MINOR((inode)->i_rdev)
27 #else
28 #define iminor(inode) minor((inode)->i_rdev)
29 #endif
30 #else
31 #define NB_CPUS num_present_cpus ()
32 #endif
35 MODULE_DESCRIPTION ("Idle time collector");
37 /* there are many ways to prevent gcc from complaining about module_param
38 and function pointer vs long, but let's not */
39 static void (*idle_func) (void);
41 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
42 MODULE_PARM (idle_func, "l");
43 #else
44 module_param (idle_func, long, 0777);
45 #endif
46 MODULE_PARM_DESC (idle_func, "address of default idle function");
48 #define DEVNAME "itc"
49 static spinlock_t lock = SPIN_LOCK_UNLOCKED;
51 static void (*orig_pm_idle) (void);
52 static unsigned int itc_major;
54 struct itc
56 struct timeval cumm_sleep_time;
57 struct timeval sleep_started;
58 int sleeping;
61 static struct itc global_itc[NR_CPUS];
63 /**********************************************************************
65 * Utility functions
67 **********************************************************************/
68 static void
69 cpeamb (struct timeval *c, struct timeval *a, struct timeval *b)
71 __typeof (c->tv_sec) sec = a->tv_sec - b->tv_sec;
72 __typeof (c->tv_usec) usec = a->tv_usec - b->tv_usec;
74 if (usec < 0)
76 sec -= 1;
77 usec = 1000000 + usec;
79 c->tv_usec += usec;
80 if (c->tv_usec > 1000000)
82 c->tv_sec += sec + 1;
83 c->tv_usec -= 1000000;
85 else
87 c->tv_sec += sec;
91 /**********************************************************************
93 * File operations
95 **********************************************************************/
96 static int
97 itc_open (struct inode * inode, struct file * file);
99 static int
100 itc_release (struct inode * inode, struct file * file);
102 static int
103 itc_ioctl (struct inode * inode, struct file * file,
104 unsigned int cmd, unsigned long arg);
106 static ssize_t
107 itc_read (struct file * file, char * buf, size_t count, loff_t * ppos);
109 static struct file_operations itc_fops =
111 .owner = THIS_MODULE,
112 .open = itc_open,
113 .release = itc_release,
114 .ioctl = itc_ioctl,
115 .llseek = no_llseek,
116 .read = itc_read,
120 static int
121 itc_release (struct inode * inode, struct file * filp)
123 return 0;
126 static int
127 itc_open (struct inode * inode, struct file * filp)
129 int ret = 0;
130 const struct file_operations *old_fops = filp->f_op;
131 unsigned int minor = iminor (inode);
133 if (minor != 0)
134 return -ENODEV;
136 /* old_fops = filp->f_op; */
137 filp->f_op = fops_get (&itc_fops);
138 fops_put (old_fops);
139 return ret;
142 static ssize_t
143 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
145 int i;
146 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
147 ssize_t retval = 0;
148 unsigned long flags;
149 struct itc *itc = &global_itc[0];
151 /* printk ("itemsize=%d cpus=%d count=%d\n", itemsize, NR_CPUS, count); */
152 if (count < itemsize * NB_CPUS)
154 printk (KERN_ERR
155 "attempt to read something funny %d expected %d(%d,%d)\n",
156 count, itemsize * NB_CPUS, itemsize, NB_CPUS);
157 return -EINVAL;
160 spin_lock_irqsave (&lock, flags);
161 for (i = 0; i < NB_CPUS; ++i, ++itc)
163 if (itc->sleeping)
165 struct timeval tv;
167 do_gettimeofday (&tv);
168 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
169 itc->sleep_started.tv_sec = tv.tv_sec;
170 itc->sleep_started.tv_usec = tv.tv_usec;
173 if (copy_to_user (buf, &itc->cumm_sleep_time, itemsize))
175 printk (KERN_ERR "failed to write %zu bytes to %p\n", itemsize, buf);
176 retval = -EFAULT;
177 break;
179 retval += itemsize;
180 buf += itemsize;
183 spin_unlock_irqrestore (&lock, flags);
184 return retval;
187 /**********************************************************************
189 * ioctl handler
191 **********************************************************************/
192 static int
193 itc_ioctl (struct inode * inode, struct file * filp,
194 unsigned int cmd, unsigned long arg)
196 return 0;
199 /**********************************************************************
201 * idle
203 **********************************************************************/
204 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
205 #ifndef CONFIG_APM
206 #define QUIRK
207 #endif
208 #else
209 void default_idle (void);
210 #endif
212 static void
213 itc_idle (void)
215 struct itc *itc;
216 struct timeval tv;
217 unsigned long flags;
219 spin_lock_irqsave (&lock, flags);
220 itc = &global_itc[smp_processor_id ()];
221 do_gettimeofday (&itc->sleep_started);
222 spin_unlock_irqrestore (&lock, flags);
224 #ifdef QUIRK
225 if (orig_pm_idle)
227 orig_pm_idle ();
229 else
231 idle_func ();
233 #else
234 if (orig_pm_idle)
236 orig_pm_idle ();
238 else
240 if (idle_func)
242 idle_func ();
244 else
246 default_idle ();
249 #endif
251 spin_lock_irqsave (&lock, flags);
252 do_gettimeofday (&tv);
253 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
254 itc->sleeping = 0;
255 spin_unlock_irqrestore (&lock, flags);
258 /**********************************************************************
260 * Module constructor
262 **********************************************************************/
263 static __init int
264 init (void)
266 int err;
268 #ifdef QUIRK
269 if (!pm_idle && !idle_func)
271 printk
272 (KERN_ERR
273 "itc: no idle function\n"
274 "itc: boot kernel with idle=halt option\n"
275 "itc: or specify idle_func (modprobe itc idle_func=<address>)\n");
276 return -ENODEV;
278 #endif
280 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
281 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
283 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
284 itc_major, err);
285 return -ENODEV;
288 if (!itc_major)
290 itc_major = err;
293 printk
294 (KERN_DEBUG
295 "itc: driver loaded pm_idle=%p default_idle=%p, idle_func=%p\n",
296 pm_idle,
297 #ifdef QUIRK
298 NULL,
299 #else
300 default_idle,
301 #endif
302 idle_func
305 orig_pm_idle = pm_idle;
306 pm_idle = itc_idle;
307 return 0;
310 /**********************************************************************
312 * Module destructor
314 **********************************************************************/
315 static __exit void
316 fini (void)
318 printk (KERN_DEBUG "itc: unloading\n");
320 unregister_chrdev (itc_major, DEVNAME);
321 printk (KERN_DEBUG "itc: unloaded\n");
323 pm_idle = orig_pm_idle;
326 module_init (init);
327 module_exit (fini);