1
2
3
4
5
6
7
8
9
10
11
12
13#define KMSG_COMPONENT "appldata"
14#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
15
16#include <linux/module.h>
17#include <linux/init.h>
18#include <linux/slab.h>
19#include <linux/errno.h>
20#include <linux/interrupt.h>
21#include <linux/proc_fs.h>
22#include <linux/mm.h>
23#include <linux/swap.h>
24#include <linux/pagemap.h>
25#include <linux/sysctl.h>
26#include <linux/notifier.h>
27#include <linux/cpu.h>
28#include <linux/workqueue.h>
29#include <linux/suspend.h>
30#include <linux/platform_device.h>
31#include <asm/appldata.h>
32#include <asm/timer.h>
33#include <asm/uaccess.h>
34#include <asm/io.h>
35#include <asm/smp.h>
36
37#include "appldata.h"
38
39
40#define APPLDATA_CPU_INTERVAL 10000
41
42
43
44#define TOD_MICRO 0x01000
45
46
47static struct platform_device *appldata_pdev;
48
49
50
51
52static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
53static int appldata_timer_handler(ctl_table *ctl, int write,
54 void __user *buffer, size_t *lenp, loff_t *ppos);
55static int appldata_interval_handler(ctl_table *ctl, int write,
56 void __user *buffer,
57 size_t *lenp, loff_t *ppos);
58
59static struct ctl_table_header *appldata_sysctl_header;
60static struct ctl_table appldata_table[] = {
61 {
62 .procname = "timer",
63 .mode = S_IRUGO | S_IWUSR,
64 .proc_handler = appldata_timer_handler,
65 },
66 {
67 .procname = "interval",
68 .mode = S_IRUGO | S_IWUSR,
69 .proc_handler = appldata_interval_handler,
70 },
71 { },
72};
73
74static struct ctl_table appldata_dir_table[] = {
75 {
76 .procname = appldata_proc_name,
77 .maxlen = 0,
78 .mode = S_IRUGO | S_IXUGO,
79 .child = appldata_table,
80 },
81 { },
82};
83
84
85
86
87static DEFINE_PER_CPU(struct vtimer_list, appldata_timer);
88static atomic_t appldata_expire_count = ATOMIC_INIT(0);
89
90static DEFINE_SPINLOCK(appldata_timer_lock);
91static int appldata_interval = APPLDATA_CPU_INTERVAL;
92static int appldata_timer_active;
93static int appldata_timer_suspended = 0;
94
95
96
97
98static struct workqueue_struct *appldata_wq;
99static void appldata_work_fn(struct work_struct *work);
100static DECLARE_WORK(appldata_work, appldata_work_fn);
101
102
103
104
105
106static DEFINE_MUTEX(appldata_ops_mutex);
107static LIST_HEAD(appldata_ops_list);
108
109
110
111
112
113
114
115
116static void appldata_timer_function(unsigned long data)
117{
118 if (atomic_dec_and_test(&appldata_expire_count)) {
119 atomic_set(&appldata_expire_count, num_online_cpus());
120 queue_work(appldata_wq, (struct work_struct *) data);
121 }
122}
123
124
125
126
127
128
129static void appldata_work_fn(struct work_struct *work)
130{
131 struct list_head *lh;
132 struct appldata_ops *ops;
133
134 get_online_cpus();
135 mutex_lock(&appldata_ops_mutex);
136 list_for_each(lh, &appldata_ops_list) {
137 ops = list_entry(lh, struct appldata_ops, list);
138 if (ops->active == 1) {
139 ops->callback(ops->data);
140 }
141 }
142 mutex_unlock(&appldata_ops_mutex);
143 put_online_cpus();
144}
145
146
147
148
149
150
151int appldata_diag(char record_nr, u16 function, unsigned long buffer,
152 u16 length, char *mod_lvl)
153{
154 struct appldata_product_id id = {
155 .prod_nr = {0xD3, 0xC9, 0xD5, 0xE4,
156 0xE7, 0xD2, 0xD9},
157 .prod_fn = 0xD5D3,
158 .version_nr = 0xF2F6,
159 .release_nr = 0xF0F1,
160 };
161
162 id.record_nr = record_nr;
163 id.mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1];
164 return appldata_asm(&id, function, (void *) buffer, length);
165}
166
167
168
169
170
171
172
173
174
175
176
177static void __appldata_mod_vtimer_wrap(void *p) {
178 struct {
179 struct vtimer_list *timer;
180 u64 expires;
181 } *args = p;
182 mod_virt_timer_periodic(args->timer, args->expires);
183}
184
185#define APPLDATA_ADD_TIMER 0
186#define APPLDATA_DEL_TIMER 1
187#define APPLDATA_MOD_TIMER 2
188
189
190
191
192
193
194
195static void
196__appldata_vtimer_setup(int cmd)
197{
198 u64 per_cpu_interval;
199 int i;
200
201 switch (cmd) {
202 case APPLDATA_ADD_TIMER:
203 if (appldata_timer_active)
204 break;
205 per_cpu_interval = (u64) (appldata_interval*1000 /
206 num_online_cpus()) * TOD_MICRO;
207 for_each_online_cpu(i) {
208 per_cpu(appldata_timer, i).expires = per_cpu_interval;
209 smp_call_function_single(i, add_virt_timer_periodic,
210 &per_cpu(appldata_timer, i),
211 1);
212 }
213 appldata_timer_active = 1;
214 break;
215 case APPLDATA_DEL_TIMER:
216 for_each_online_cpu(i)
217 del_virt_timer(&per_cpu(appldata_timer, i));
218 if (!appldata_timer_active)
219 break;
220 appldata_timer_active = 0;
221 atomic_set(&appldata_expire_count, num_online_cpus());
222 break;
223 case APPLDATA_MOD_TIMER:
224 per_cpu_interval = (u64) (appldata_interval*1000 /
225 num_online_cpus()) * TOD_MICRO;
226 if (!appldata_timer_active)
227 break;
228 for_each_online_cpu(i) {
229 struct {
230 struct vtimer_list *timer;
231 u64 expires;
232 } args;
233 args.timer = &per_cpu(appldata_timer, i);
234 args.expires = per_cpu_interval;
235 smp_call_function_single(i, __appldata_mod_vtimer_wrap,
236 &args, 1);
237 }
238 }
239}
240
241
242
243
244
245
246static int
247appldata_timer_handler(ctl_table *ctl, int write,
248 void __user *buffer, size_t *lenp, loff_t *ppos)
249{
250 int len;
251 char buf[2];
252
253 if (!*lenp || *ppos) {
254 *lenp = 0;
255 return 0;
256 }
257 if (!write) {
258 len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n");
259 if (len > *lenp)
260 len = *lenp;
261 if (copy_to_user(buffer, buf, len))
262 return -EFAULT;
263 goto out;
264 }
265 len = *lenp;
266 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
267 return -EFAULT;
268 get_online_cpus();
269 spin_lock(&appldata_timer_lock);
270 if (buf[0] == '1')
271 __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
272 else if (buf[0] == '0')
273 __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
274 spin_unlock(&appldata_timer_lock);
275 put_online_cpus();
276out:
277 *lenp = len;
278 *ppos += len;
279 return 0;
280}
281
282
283
284
285
286
287
288static int
289appldata_interval_handler(ctl_table *ctl, int write,
290 void __user *buffer, size_t *lenp, loff_t *ppos)
291{
292 int len, interval;
293 char buf[16];
294
295 if (!*lenp || *ppos) {
296 *lenp = 0;
297 return 0;
298 }
299 if (!write) {
300 len = sprintf(buf, "%i\n", appldata_interval);
301 if (len > *lenp)
302 len = *lenp;
303 if (copy_to_user(buffer, buf, len))
304 return -EFAULT;
305 goto out;
306 }
307 len = *lenp;
308 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) {
309 return -EFAULT;
310 }
311 interval = 0;
312 sscanf(buf, "%i", &interval);
313 if (interval <= 0)
314 return -EINVAL;
315
316 get_online_cpus();
317 spin_lock(&appldata_timer_lock);
318 appldata_interval = interval;
319 __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
320 spin_unlock(&appldata_timer_lock);
321 put_online_cpus();
322out:
323 *lenp = len;
324 *ppos += len;
325 return 0;
326}
327
328
329
330
331
332
333
334static int
335appldata_generic_handler(ctl_table *ctl, int write,
336 void __user *buffer, size_t *lenp, loff_t *ppos)
337{
338 struct appldata_ops *ops = NULL, *tmp_ops;
339 int rc, len, found;
340 char buf[2];
341 struct list_head *lh;
342
343 found = 0;
344 mutex_lock(&appldata_ops_mutex);
345 list_for_each(lh, &appldata_ops_list) {
346 tmp_ops = list_entry(lh, struct appldata_ops, list);
347 if (&tmp_ops->ctl_table[2] == ctl) {
348 found = 1;
349 }
350 }
351 if (!found) {
352 mutex_unlock(&appldata_ops_mutex);
353 return -ENODEV;
354 }
355 ops = ctl->data;
356 if (!try_module_get(ops->owner)) {
357 mutex_unlock(&appldata_ops_mutex);
358 return -ENODEV;
359 }
360 mutex_unlock(&appldata_ops_mutex);
361
362 if (!*lenp || *ppos) {
363 *lenp = 0;
364 module_put(ops->owner);
365 return 0;
366 }
367 if (!write) {
368 len = sprintf(buf, ops->active ? "1\n" : "0\n");
369 if (len > *lenp)
370 len = *lenp;
371 if (copy_to_user(buffer, buf, len)) {
372 module_put(ops->owner);
373 return -EFAULT;
374 }
375 goto out;
376 }
377 len = *lenp;
378 if (copy_from_user(buf, buffer,
379 len > sizeof(buf) ? sizeof(buf) : len)) {
380 module_put(ops->owner);
381 return -EFAULT;
382 }
383
384 mutex_lock(&appldata_ops_mutex);
385 if ((buf[0] == '1') && (ops->active == 0)) {
386
387 if (!try_module_get(ops->owner)) {
388 mutex_unlock(&appldata_ops_mutex);
389 module_put(ops->owner);
390 return -ENODEV;
391 }
392 ops->callback(ops->data);
393 rc = appldata_diag(ops->record_nr,
394 APPLDATA_START_INTERVAL_REC,
395 (unsigned long) ops->data, ops->size,
396 ops->mod_lvl);
397 if (rc != 0) {
398 pr_err("Starting the data collection for %s "
399 "failed with rc=%d\n", ops->name, rc);
400 module_put(ops->owner);
401 } else
402 ops->active = 1;
403 } else if ((buf[0] == '0') && (ops->active == 1)) {
404 ops->active = 0;
405 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
406 (unsigned long) ops->data, ops->size,
407 ops->mod_lvl);
408 if (rc != 0)
409 pr_err("Stopping the data collection for %s "
410 "failed with rc=%d\n", ops->name, rc);
411 module_put(ops->owner);
412 }
413 mutex_unlock(&appldata_ops_mutex);
414out:
415 *lenp = len;
416 *ppos += len;
417 module_put(ops->owner);
418 return 0;
419}
420
421
422
423
424
425
426
427
428
429
430int appldata_register_ops(struct appldata_ops *ops)
431{
432 if (ops->size > APPLDATA_MAX_REC_SIZE)
433 return -EINVAL;
434
435 ops->ctl_table = kzalloc(4 * sizeof(struct ctl_table), GFP_KERNEL);
436 if (!ops->ctl_table)
437 return -ENOMEM;
438
439 mutex_lock(&appldata_ops_mutex);
440 list_add(&ops->list, &appldata_ops_list);
441 mutex_unlock(&appldata_ops_mutex);
442
443 ops->ctl_table[0].procname = appldata_proc_name;
444 ops->ctl_table[0].maxlen = 0;
445 ops->ctl_table[0].mode = S_IRUGO | S_IXUGO;
446 ops->ctl_table[0].child = &ops->ctl_table[2];
447
448 ops->ctl_table[2].procname = ops->name;
449 ops->ctl_table[2].mode = S_IRUGO | S_IWUSR;
450 ops->ctl_table[2].proc_handler = appldata_generic_handler;
451 ops->ctl_table[2].data = ops;
452
453 ops->sysctl_header = register_sysctl_table(ops->ctl_table);
454 if (!ops->sysctl_header)
455 goto out;
456 return 0;
457out:
458 mutex_lock(&appldata_ops_mutex);
459 list_del(&ops->list);
460 mutex_unlock(&appldata_ops_mutex);
461 kfree(ops->ctl_table);
462 return -ENOMEM;
463}
464
465
466
467
468
469
470void appldata_unregister_ops(struct appldata_ops *ops)
471{
472 mutex_lock(&appldata_ops_mutex);
473 list_del(&ops->list);
474 mutex_unlock(&appldata_ops_mutex);
475 unregister_sysctl_table(ops->sysctl_header);
476 kfree(ops->ctl_table);
477}
478
479
480
481
482static int appldata_freeze(struct device *dev)
483{
484 struct appldata_ops *ops;
485 int rc;
486 struct list_head *lh;
487
488 get_online_cpus();
489 spin_lock(&appldata_timer_lock);
490 if (appldata_timer_active) {
491 __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
492 appldata_timer_suspended = 1;
493 }
494 spin_unlock(&appldata_timer_lock);
495 put_online_cpus();
496
497 mutex_lock(&appldata_ops_mutex);
498 list_for_each(lh, &appldata_ops_list) {
499 ops = list_entry(lh, struct appldata_ops, list);
500 if (ops->active == 1) {
501 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
502 (unsigned long) ops->data, ops->size,
503 ops->mod_lvl);
504 if (rc != 0)
505 pr_err("Stopping the data collection for %s "
506 "failed with rc=%d\n", ops->name, rc);
507 }
508 }
509 mutex_unlock(&appldata_ops_mutex);
510 return 0;
511}
512
513static int appldata_restore(struct device *dev)
514{
515 struct appldata_ops *ops;
516 int rc;
517 struct list_head *lh;
518
519 get_online_cpus();
520 spin_lock(&appldata_timer_lock);
521 if (appldata_timer_suspended) {
522 __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
523 appldata_timer_suspended = 0;
524 }
525 spin_unlock(&appldata_timer_lock);
526 put_online_cpus();
527
528 mutex_lock(&appldata_ops_mutex);
529 list_for_each(lh, &appldata_ops_list) {
530 ops = list_entry(lh, struct appldata_ops, list);
531 if (ops->active == 1) {
532 ops->callback(ops->data);
533 rc = appldata_diag(ops->record_nr,
534 APPLDATA_START_INTERVAL_REC,
535 (unsigned long) ops->data, ops->size,
536 ops->mod_lvl);
537 if (rc != 0) {
538 pr_err("Starting the data collection for %s "
539 "failed with rc=%d\n", ops->name, rc);
540 }
541 }
542 }
543 mutex_unlock(&appldata_ops_mutex);
544 return 0;
545}
546
547static int appldata_thaw(struct device *dev)
548{
549 return appldata_restore(dev);
550}
551
552static const struct dev_pm_ops appldata_pm_ops = {
553 .freeze = appldata_freeze,
554 .thaw = appldata_thaw,
555 .restore = appldata_restore,
556};
557
558static struct platform_driver appldata_pdrv = {
559 .driver = {
560 .name = "appldata",
561 .owner = THIS_MODULE,
562 .pm = &appldata_pm_ops,
563 },
564};
565
566
567
568
569
570static void __cpuinit appldata_online_cpu(int cpu)
571{
572 init_virt_timer(&per_cpu(appldata_timer, cpu));
573 per_cpu(appldata_timer, cpu).function = appldata_timer_function;
574 per_cpu(appldata_timer, cpu).data = (unsigned long)
575 &appldata_work;
576 atomic_inc(&appldata_expire_count);
577 spin_lock(&appldata_timer_lock);
578 __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
579 spin_unlock(&appldata_timer_lock);
580}
581
582static void __cpuinit appldata_offline_cpu(int cpu)
583{
584 del_virt_timer(&per_cpu(appldata_timer, cpu));
585 if (atomic_dec_and_test(&appldata_expire_count)) {
586 atomic_set(&appldata_expire_count, num_online_cpus());
587 queue_work(appldata_wq, &appldata_work);
588 }
589 spin_lock(&appldata_timer_lock);
590 __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
591 spin_unlock(&appldata_timer_lock);
592}
593
594static int __cpuinit appldata_cpu_notify(struct notifier_block *self,
595 unsigned long action,
596 void *hcpu)
597{
598 switch (action) {
599 case CPU_ONLINE:
600 case CPU_ONLINE_FROZEN:
601 appldata_online_cpu((long) hcpu);
602 break;
603 case CPU_DEAD:
604 case CPU_DEAD_FROZEN:
605 appldata_offline_cpu((long) hcpu);
606 break;
607 default:
608 break;
609 }
610 return NOTIFY_OK;
611}
612
613static struct notifier_block __cpuinitdata appldata_nb = {
614 .notifier_call = appldata_cpu_notify,
615};
616
617
618
619
620
621
622static int __init appldata_init(void)
623{
624 int i, rc;
625
626 rc = platform_driver_register(&appldata_pdrv);
627 if (rc)
628 return rc;
629
630 appldata_pdev = platform_device_register_simple("appldata", -1, NULL,
631 0);
632 if (IS_ERR(appldata_pdev)) {
633 rc = PTR_ERR(appldata_pdev);
634 goto out_driver;
635 }
636 appldata_wq = create_singlethread_workqueue("appldata");
637 if (!appldata_wq) {
638 rc = -ENOMEM;
639 goto out_device;
640 }
641
642 get_online_cpus();
643 for_each_online_cpu(i)
644 appldata_online_cpu(i);
645 put_online_cpus();
646
647
648 register_hotcpu_notifier(&appldata_nb);
649
650 appldata_sysctl_header = register_sysctl_table(appldata_dir_table);
651 return 0;
652
653out_device:
654 platform_device_unregister(appldata_pdev);
655out_driver:
656 platform_driver_unregister(&appldata_pdrv);
657 return rc;
658}
659
660__initcall(appldata_init);
661
662
663
664EXPORT_SYMBOL_GPL(appldata_register_ops);
665EXPORT_SYMBOL_GPL(appldata_unregister_ops);
666EXPORT_SYMBOL_GPL(appldata_diag);
667
668#ifdef CONFIG_SWAP
669EXPORT_SYMBOL_GPL(si_swapinfo);
670#endif
671EXPORT_SYMBOL_GPL(nr_threads);
672EXPORT_SYMBOL_GPL(nr_running);
673EXPORT_SYMBOL_GPL(nr_iowait);
674