1/*
2 * arch/s390/appldata/appldata_base.c
3 *
4 * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.
5 * Exports appldata_register_ops() and appldata_unregister_ops() for the
6 * data gathering modules.
7 *
8 * Copyright IBM Corp. 2003, 2009
9 *
10 * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
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		/* default (CPU) time for
41						   sampling interval in
42						   milliseconds */
43
44#define TOD_MICRO	0x01000			/* nr. of TOD clock units
45						   for 1 microsecond */
46
47static struct platform_device *appldata_pdev;
48
49/*
50 * /proc entries (sysctl)
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 * Timer
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 * Work queue
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 * Ops list
105 */
106static DEFINE_MUTEX(appldata_ops_mutex);
107static LIST_HEAD(appldata_ops_list);
108
109
110/*************************** timer, work, DIAG *******************************/
111/*
112 * appldata_timer_function()
113 *
114 * schedule work and reschedule timer
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 * appldata_work_fn()
126 *
127 * call data gathering function for each (active) module
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 * appldata_diag()
148 *
149 * prepare parameter list, issue DIAG 0xDC
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},	/* "LINUXKR" */
157		.prod_fn    = 0xD5D3,			/* "NL" */
158		.version_nr = 0xF2F6,			/* "26" */
159		.release_nr = 0xF0F1,			/* "01" */
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/************************ timer, work, DIAG <END> ****************************/
167
168
169/****************************** /proc stuff **********************************/
170
171/*
172 * appldata_mod_vtimer_wrap()
173 *
174 * wrapper function for mod_virt_timer(), because smp_call_function_single()
175 * accepts only one parameter.
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 * __appldata_vtimer_setup()
191 *
192 * Add, delete or modify virtual timers on all online cpus.
193 * The caller needs to get the appldata_timer_lock spinlock.
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 * appldata_timer_handler()
243 *
244 * Start/Stop timer, show status of timer (0 = not active, 1 = active)
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 * appldata_interval_handler()
284 *
285 * Set (CPU) timer interval for collection of data (in milliseconds), show
286 * current timer interval.
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 * appldata_generic_handler()
330 *
331 * Generic start/stop monitoring and DIAG, show status of
332 * monitoring (0 = not in process, 1 = in process)
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)) {	// protect this function
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		// protect work queue callback
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);	// init record
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/*************************** /proc stuff <END> *******************************/
422
423
424/************************* module-ops management *****************************/
425/*
426 * appldata_register_ops()
427 *
428 * update ops list, register /proc/sys entries
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 * appldata_unregister_ops()
467 *
468 * update ops list, unregister /proc entries, stop DIAG if necessary
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/********************** module-ops management <END> **************************/
479
480
481/**************************** suspend / resume *******************************/
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);	// init record
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/************************* suspend / resume <END> ****************************/
566
567
568/******************************* init / exit *********************************/
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 * appldata_init()
619 *
620 * init timer, register /proc entries
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	/* Register cpu hotplug notifier */
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/**************************** init / exit <END> ******************************/
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