Module for 3.x kernels
[apc.git] / mod3 / itc-mod.c
blob7a19fee92cd59aea3b86b8c0916704e13d4702e9
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/system.h>
20 #include <asm/uaccess.h>
21 #include <asm/idle.h>
23 #if defined CONFIG_6xx || defined CONFIG_PPC64
24 #define ACCOUNT_IRQ
25 #endif
27 MODULE_DESCRIPTION ("Idle time collector");
28 MODULE_LICENSE ("GPL");
30 #define DEVNAME "itc"
32 #if LINUX_VERSION_CODE < KERNEL_VERSION (3, 0, 0)
33 static spinlock_t lock = SPIN_LOCK_UNLOCKED;
34 #else
35 DEFINE_SPINLOCK (lock);
36 #endif
38 static unsigned int itc_major;
39 static atomic_t in_use;
41 struct itc
43 struct timeval cumm_sleep_time;
44 struct timeval sleep_started;
45 int sleeping;
48 static struct itc global_itc[NR_CPUS];
50 /**********************************************************************
52 * Utility functions
54 **********************************************************************/
55 static void
56 cpeamb (struct timeval *c, struct timeval *a, struct timeval *b)
58 __typeof (c->tv_sec) sec = a->tv_sec - b->tv_sec;
59 __typeof (c->tv_usec) usec = a->tv_usec - b->tv_usec;
61 if (usec < 0)
63 sec -= 1;
64 usec = 1000000 + usec;
66 c->tv_usec += usec;
67 if (c->tv_usec > 1000000)
69 c->tv_sec += sec + 1;
70 c->tv_usec -= 1000000;
72 else
74 c->tv_sec += sec;
78 static void
79 itc_monotonic (struct timeval *tv)
81 do_gettimeofday (tv);
84 #ifdef ACCOUNT_IRQ
85 static cputime64_t
86 itc_irq_time (void)
88 struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat;
89 return cpustat->irq;
91 #endif
93 /**********************************************************************
95 * File operations
97 **********************************************************************/
98 static int
99 itc_open (struct inode * inode, struct file * file);
101 static int
102 itc_release (struct inode * inode, struct file * file);
104 static ssize_t
105 itc_read (struct file * file, char * buf, size_t count, loff_t * ppos);
107 static struct file_operations itc_fops =
109 .owner = THIS_MODULE,
110 .open = itc_open,
111 .release = itc_release,
112 .llseek = no_llseek,
113 .read = itc_read,
116 static struct miscdevice itc_misc_dev =
118 .minor = MISC_DYNAMIC_MINOR,
119 .name = "itc",
120 .fops = &itc_fops
124 static int idle_notification (struct notifier_block *nblk, unsigned long cmd,
125 void *y)
127 struct itc *itc;
128 struct timeval tv;
130 itc = &global_itc[smp_processor_id ()];
131 if (cmd == IDLE_START)
133 itc_monotonic (&itc->sleep_started);
134 itc->sleeping = 1;
136 else
138 itc_monotonic (&tv);
139 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
140 itc->sleeping = 0;
142 /* printk ("idle_notification %ld %p\n", cmd, y); */
143 return 0;
146 static struct notifier_block nblk =
148 .notifier_call = idle_notification
151 static int
152 itc_release (struct inode * inode, struct file * filp)
154 idle_notifier_unregister (&nblk);
155 atomic_set (&in_use, 0);
156 return 0;
159 static int
160 itc_open (struct inode * inode, struct file * filp)
162 int ret = 0;
163 unsigned int minor = iminor (inode);
165 if (itc_major)
167 if (minor != 0)
169 return -ENODEV;
173 if (atomic_cmpxchg (&in_use, 0, 1))
175 return -EALREADY;
178 filp->f_op = &itc_fops;
179 idle_notifier_register (&nblk);
181 return ret;
184 static ssize_t
185 itc_read (struct file *file, char * buf, size_t count, loff_t * ppos)
187 int i;
188 size_t itemsize = sizeof (global_itc[0].cumm_sleep_time);
189 ssize_t retval = 0;
190 unsigned long flags;
191 struct itc *itc = &global_itc[0];
192 struct timeval tmp[NR_CPUS], *tmpp;
194 tmpp = tmp;
195 if (count < itemsize * num_present_cpus ())
197 printk (KERN_ERR
198 "attempt to read something funny %zu expected %zu(%zu,%u)\n",
199 count, itemsize * num_present_cpus (),
200 itemsize, num_present_cpus ());
201 return -EINVAL;
204 spin_lock_irqsave (&lock, flags);
205 for (i = 0; i < NR_CPUS; ++i, ++itc)
207 if (cpu_present (i))
209 if (itc->sleeping)
211 struct timeval tv;
213 itc_monotonic (&tv);
214 cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started);
215 itc->sleep_started.tv_sec = tv.tv_sec;
216 itc->sleep_started.tv_usec = tv.tv_usec;
219 *tmpp++ = itc->cumm_sleep_time;
220 retval += itemsize;
223 spin_unlock_irqrestore (&lock, flags);
225 if (copy_to_user (buf, tmp, retval))
227 printk (KERN_ERR "failed to write %zu bytes to %p\n",
228 retval, buf);
229 retval = -EFAULT;
231 return retval;
234 /**********************************************************************
236 * Module constructor
238 **********************************************************************/
239 static __init int
240 init (void)
242 int err;
244 if (itc_major)
246 err = register_chrdev (itc_major, DEVNAME, &itc_fops);
247 if (err < 0 || ((itc_major && err) || (!itc_major && !err)))
249 printk (KERN_ERR "itc: register_chrdev failed itc_major=%d err=%d\n",
250 itc_major, err);
251 return -ENODEV;
254 if (!itc_major)
256 itc_major = err;
259 else
261 err = misc_register (&itc_misc_dev);
262 if (err < 0)
264 printk (KERN_ERR "itc: misc_register failed err=%d\n", err);
265 return err;
269 return err;
272 /**********************************************************************
274 * Module destructor
276 **********************************************************************/
277 static __exit void
278 fini (void)
280 if (itc_major)
282 unregister_chrdev (itc_major, DEVNAME);
284 else
286 misc_deregister (&itc_misc_dev);
288 printk (KERN_DEBUG "itc: unloaded\n");
291 module_init (init);
292 module_exit (fini);