v0.90
[apc.git] / mod / itc-mod.c
blob1e887f7af17f42fcda4a19bee9cbd8d62f6d4c28
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 static void (*idle_func) (void);
38 #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)
39 MODULE_PARM (idle_func, "l");
40 #else
41 module_param (idle_func, long, 0777);
42 #endif
43 MODULE_PARM_DESC (idle_func, "address of default idle function");
45 #define DEVNAME "itc"
46 static spinlock_t lock = SPIN_LOCK_UNLOCKED;
48 static void (*orig_pm_idle) (void);
49 static unsigned int itc_major;
50 static struct timeval total_idle_tv[NR_CPUS];
52 /**********************************************************************
54 * File operations
56 **********************************************************************/
57 static int
58 itc_open (struct inode * inode, struct file * file);
60 static int
61 itc_release (struct inode * inode, struct file * file);
63 static int
64 itc_ioctl (struct inode * inode, struct file * file,
65 unsigned int cmd, unsigned long arg);
67 static ssize_t
68 itc_read (struct file * file, char * buf, size_t count, loff_t * ppos);
70 static struct file_operations itc_fops =
72 .owner = THIS_MODULE,
73 .open = itc_open,
74 .release = itc_release,
75 .ioctl = itc_ioctl,
76 .llseek = no_llseek,
77 .read = itc_read,
81 static int
82 itc_release (struct inode * inode, struct file * filp)
84 return 0;
87 static int
88 itc_open (struct inode * inode, struct file * filp)
90 int ret = 0;
91 const struct file_operations *old_fops = filp->f_op;
92 unsigned int minor = iminor (inode);
94 if (minor != 0)
95 return -ENODEV;
97 /* old_fops = filp->f_op; */
98 filp->f_op = fops_get (&itc_fops);
99 fops_put (old_fops);
100 return ret;
103 static ssize_t
104 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
106 int i;
107 size_t itemsize = sizeof (total_idle_tv[0]);
108 ssize_t retval = 0;
109 unsigned long flags;
111 /* printk ("itemsize=%d cpus=%d count=%d\n", itemsize, NR_CPUS, count); */
112 if (count < itemsize * NB_CPUS)
114 printk (KERN_ERR "attempt to read something funny %d expected %d(%d,%d)\n",
115 count, itemsize * NB_CPUS, itemsize, NB_CPUS);
116 return -EINVAL;
119 spin_lock_irqsave (&lock, flags);
120 for (i = 0; i < NB_CPUS; ++i)
122 if (copy_to_user (buf, &total_idle_tv[i], itemsize))
124 printk (KERN_ERR "failed to write %zu bytes to %p\n", itemsize, buf);
125 spin_unlock_irqrestore (&lock, flags);
126 return -EFAULT;
128 retval += itemsize;
129 buf += itemsize;
132 spin_unlock_irqrestore (&lock, flags);
133 return retval;
136 /**********************************************************************
138 * ioctl handler
140 **********************************************************************/
141 static int
142 itc_ioctl (struct inode * inode, struct file * filp,
143 unsigned int cmd, unsigned long arg)
145 return 0;
148 /**********************************************************************
150 * idle
152 **********************************************************************/
153 #if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0)
154 #ifndef CONFIG_APM
155 #define QUIRK
156 #endif
157 #else
158 void default_idle (void);
159 #endif
161 static void
162 itc_idle (void)
164 struct timeval tv1, tv2, tv3, *t;
165 suseconds_t usec;
166 unsigned long flags;
168 spin_lock_irqsave (&lock, flags);
169 t = &total_idle_tv[smp_processor_id ()];
170 tv3.tv_sec = t->tv_sec;
171 tv3.tv_usec = t->tv_usec;
172 t->tv_sec = 0;
173 t->tv_usec = 0;
174 do_gettimeofday (&tv1);
175 spin_unlock_irqrestore (&lock, flags);
177 #ifdef QUIRK
178 if (orig_pm_idle)
180 orig_pm_idle ();
182 else
184 idle_func ();
186 #else
187 if (orig_pm_idle)
189 orig_pm_idle ();
191 else
193 if (idle_func)
195 idle_func ();
197 else
199 default_idle ();
202 #endif
204 spin_lock_irqsave (&lock, flags);
205 do_gettimeofday (&tv2);
206 usec = tv2.tv_usec - tv1.tv_usec + tv3.tv_usec;
207 tv3.tv_sec += (tv2.tv_sec - tv1.tv_sec);
208 while (usec > 1000000)
210 usec -= 1000000;
211 tv3.tv_sec += 1;
213 t->tv_usec = usec;
214 t->tv_sec = tv3.tv_sec;
215 spin_unlock_irqrestore (&lock, flags);
218 /**********************************************************************
220 * Module constructor
222 **********************************************************************/
223 static __init int
224 init (void)
226 int err;
228 #ifdef QUIRK
229 if (!pm_idle && !idle_func)
231 printk (KERN_ERR
232 "itc: no idle function\n"
233 "itc: boot kernel with idle=halt option\n"
234 "itc: or specify idle_func (modprobe its idle_func=<address>\n");
235 return -ENODEV;
237 #endif
239 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
240 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
242 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
243 itc_major, err);
244 return -ENODEV;
247 if (!itc_major)
249 itc_major = err;
252 printk
253 (KERN_DEBUG
254 "itc: driver successfully loaded pm_idle=%p default_idle=%p, idle_func=%p\n",
255 pm_idle,
256 #ifdef QUIRK
257 NULL,
258 #else
259 default_idle,
260 #endif
261 idle_func
264 orig_pm_idle = pm_idle;
265 pm_idle = itc_idle;
266 return 0;
269 /**********************************************************************
271 * Module destructor
273 **********************************************************************/
274 static __exit void
275 fini (void)
277 printk (KERN_DEBUG "itc: unloading\n");
279 unregister_chrdev (itc_major, DEVNAME);
280 printk (KERN_DEBUG "itc: unloaded\n");
282 pm_idle = orig_pm_idle;
285 module_init (init);
286 module_exit (fini);