Make sure idle times are properly initialized for all cpus
[apc.git] / mod3 / itc-mod.c
blob4bfe503e0380f38ee5527d7a3cfa4ef63d1a30df
1 #include <linux/signal.h>
2 #include <linux/sched.h>
3 #include <linux/interrupt.h>
5 #include <linux/fs.h>
6 #include <linux/version.h>
7 #include <linux/vmalloc.h>
8 #include <linux/module.h>
9 #include <linux/delay.h>
10 #include <linux/init.h>
11 #include <linux/slab.h>
12 #include <linux/smp.h>
13 #include <linux/mm.h>
14 #include <linux/pm.h>
15 #include <linux/miscdevice.h>
16 #include <linux/kernel_stat.h>
17 #include <linux/cpuidle.h>
19 #include <asm/uaccess.h>
20 #include <asm/idle.h>
22 #if defined CONFIG_6xx || defined CONFIG_PPC64
23 #define ACCOUNT_IRQ
24 #endif
26 MODULE_DESCRIPTION ("Idle time collector");
27 MODULE_LICENSE ("GPL");
29 #define DEVNAME "itc"
31 #if LINUX_VERSION_CODE < KERNEL_VERSION (3, 0, 0)
32 static spinlock_t lock = SPIN_LOCK_UNLOCKED;
33 #else
34 DEFINE_SPINLOCK (lock);
35 #endif
37 static unsigned int itc_major;
38 static atomic_t in_use;
40 struct itc
42 struct timeval cumm_sleep_time;
43 struct timeval sleep_started;
44 int sleeping;
47 static struct itc global_itc[NR_CPUS];
49 /**********************************************************************
51 * Utility functions
53 **********************************************************************/
54 static void
55 cpeamb (struct timeval *c, struct timeval *a, struct timeval *b)
57 __typeof (c->tv_sec) sec = a->tv_sec - b->tv_sec;
58 __typeof (c->tv_usec) usec = a->tv_usec - b->tv_usec;
60 if (usec < 0)
62 sec -= 1;
63 usec = 1000000 + usec;
65 c->tv_usec += usec;
66 if (c->tv_usec > 1000000)
68 c->tv_sec += sec + 1;
69 c->tv_usec -= 1000000;
71 else
73 c->tv_sec += sec;
77 static void
78 itc_monotonic (struct timeval *tv)
80 do_gettimeofday (tv);
83 #ifdef ACCOUNT_IRQ
84 static cputime64_t
85 itc_irq_time (void)
87 struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat;
88 return cpustat->irq;
90 #endif
92 /**********************************************************************
94 * File operations
96 **********************************************************************/
97 static int
98 itc_open (struct inode * inode, struct file * file);
100 static int
101 itc_release (struct inode * inode, struct file * file);
103 static ssize_t
104 itc_read (struct file * file, char * buf, size_t count, loff_t * ppos);
106 static struct file_operations itc_fops =
108 .owner = THIS_MODULE,
109 .open = itc_open,
110 .release = itc_release,
111 .llseek = no_llseek,
112 .read = itc_read,
115 static struct miscdevice itc_misc_dev =
117 .minor = MISC_DYNAMIC_MINOR,
118 .name = "itc",
119 .fops = &itc_fops
123 static int idle_notification (struct notifier_block *nblk, unsigned long cmd,
124 void *y)
126 struct itc *itc;
127 struct timeval tv;
129 itc = &global_itc[smp_processor_id ()];
130 if (cmd == IDLE_START)
132 itc_monotonic (&itc->sleep_started);
133 itc->sleeping = 1;
135 else
137 itc_monotonic (&tv);
138 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
139 itc->sleeping = 0;
141 /* printk ("idle_notification %ld %p\n", cmd, y); */
142 return 0;
145 static struct notifier_block nblk =
147 .notifier_call = idle_notification
150 static int
151 itc_release (struct inode * inode, struct file * filp)
153 idle_notifier_unregister (&nblk);
154 atomic_set (&in_use, 0);
155 return 0;
158 static void
159 dummy_wakeup (void *unused)
163 static int
164 itc_open (struct inode * inode, struct file * filp)
166 int ret = 0;
167 unsigned int minor = iminor (inode);
169 if (itc_major)
171 if (minor != 0)
173 return -ENODEV;
177 if (atomic_cmpxchg (&in_use, 0, 1))
179 return -EALREADY;
182 filp->f_op = &itc_fops;
183 idle_notifier_register (&nblk);
184 on_each_cpu (dummy_wakeup, NULL, 1);
186 return ret;
189 static ssize_t
190 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
192 int i;
193 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
194 ssize_t retval = 0;
195 unsigned long flags;
196 struct itc *itc = &global_itc[0];
197 struct timeval tmp[NR_CPUS], *tmpp;
199 tmpp = tmp;
200 if (count < itemsize * num_present_cpus ())
202 printk (KERN_ERR
203 "attempt to read something funny %zu expected %zu(%zu,%u)\n",
204 count, itemsize * num_present_cpus (),
205 itemsize, num_present_cpus ());
206 return -EINVAL;
209 spin_lock_irqsave (&lock, flags);
210 for (i = 0; i < NR_CPUS; ++i, ++itc)
212 if (cpu_present (i))
214 if (itc->sleeping)
216 struct timeval tv;
218 itc_monotonic (&tv);
219 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
220 itc->sleep_started.tv_sec = tv.tv_sec;
221 itc->sleep_started.tv_usec = tv.tv_usec;
224 *tmpp++ = itc->cumm_sleep_time;
225 retval += itemsize;
228 spin_unlock_irqrestore (&lock, flags);
230 if (copy_to_user (buf, tmp, retval))
232 printk (KERN_ERR "failed to write %zu bytes to %p\n",
233 retval, buf);
234 retval = -EFAULT;
236 return retval;
239 /**********************************************************************
241 * Module constructor
243 **********************************************************************/
244 static __init int
245 init (void)
247 int err;
249 if (itc_major)
251 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
252 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
254 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
255 itc_major, err);
256 return -ENODEV;
259 if (!itc_major)
261 itc_major = err;
264 else
266 err = misc_register (&itc_misc_dev);
267 if (err < 0)
269 printk (KERN_ERR "itc: misc_register failed err=%d\n", err);
270 return err;
274 return err;
277 /**********************************************************************
279 * Module destructor
281 **********************************************************************/
282 static __exit void
283 fini (void)
285 if (itc_major)
287 unregister_chrdev (itc_major, DEVNAME);
289 else
291 misc_deregister (&itc_misc_dev);
293 printk (KERN_DEBUG "itc: unloaded\n");
296 module_init (init);
297 module_exit (fini);