xref: /linux/arch/s390/appldata/appldata_base.c (revision e648e004e401955a9546320fa9b845838be7b353)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.
4  * Exports appldata_register_ops() and appldata_unregister_ops() for the
5  * data gathering modules.
6  *
7  * Copyright IBM Corp. 2003, 2009
8  *
9  * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
10  */
11 
12 #define pr_fmt(fmt) "appldata: " fmt
13 
14 #include <linux/export.h>
15 #include <linux/module.h>
16 #include <linux/sched/stat.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/uaccess.h>
30 #include <linux/io.h>
31 #include <asm/appldata.h>
32 #include <asm/vtimer.h>
33 #include <asm/smp.h>
34 
35 #include "appldata.h"
36 
37 
38 #define APPLDATA_CPU_INTERVAL	10000		/* default (CPU) time for
39 						   sampling interval in
40 						   milliseconds */
41 
42 #define TOD_MICRO	0x01000			/* nr. of TOD clock units
43 						   for 1 microsecond */
44 
45 /*
46  * /proc entries (sysctl)
47  */
48 static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
49 static int appldata_timer_handler(const struct ctl_table *ctl, int write,
50 				  void *buffer, size_t *lenp, loff_t *ppos);
51 static int appldata_interval_handler(const struct ctl_table *ctl, int write,
52 				     void *buffer, size_t *lenp, loff_t *ppos);
53 
54 static const struct ctl_table appldata_table[] = {
55 	{
56 		.procname	= "timer",
57 		.mode		= S_IRUGO | S_IWUSR,
58 		.proc_handler	= appldata_timer_handler,
59 	},
60 	{
61 		.procname	= "interval",
62 		.mode		= S_IRUGO | S_IWUSR,
63 		.proc_handler	= appldata_interval_handler,
64 	},
65 };
66 
67 /*
68  * Timer
69  */
70 static struct vtimer_list appldata_timer;
71 
72 static DEFINE_SPINLOCK(appldata_timer_lock);
73 static int appldata_interval = APPLDATA_CPU_INTERVAL;
74 static int appldata_timer_active;
75 
76 /*
77  * Work queue
78  */
79 static struct workqueue_struct *appldata_wq;
80 static void appldata_work_fn(struct work_struct *work);
81 static DECLARE_WORK(appldata_work, appldata_work_fn);
82 
83 
84 /*
85  * Ops list
86  */
87 static DEFINE_MUTEX(appldata_ops_mutex);
88 static LIST_HEAD(appldata_ops_list);
89 
90 
91 /*************************** timer, work, DIAG *******************************/
92 /*
93  * appldata_timer_function()
94  *
95  * schedule work and reschedule timer
96  */
97 static void appldata_timer_function(unsigned long data)
98 {
99 	queue_work(appldata_wq, (struct work_struct *) data);
100 }
101 
102 /*
103  * appldata_work_fn()
104  *
105  * call data gathering function for each (active) module
106  */
107 static void appldata_work_fn(struct work_struct *work)
108 {
109 	struct list_head *lh;
110 	struct appldata_ops *ops;
111 
112 	mutex_lock(&appldata_ops_mutex);
113 	list_for_each(lh, &appldata_ops_list) {
114 		ops = list_entry(lh, struct appldata_ops, list);
115 		if (ops->active == 1) {
116 			ops->callback(ops->data);
117 		}
118 	}
119 	mutex_unlock(&appldata_ops_mutex);
120 }
121 
122 static struct appldata_product_id appldata_id = {
123 	.prod_nr    = {0xD3, 0xC9, 0xD5, 0xE4,
124 		       0xE7, 0xD2, 0xD9},	/* "LINUXKR" */
125 	.prod_fn    = 0xD5D3,			/* "NL" */
126 	.version_nr = 0xF2F6,			/* "26" */
127 	.release_nr = 0xF0F1,			/* "01" */
128 };
129 
130 /*
131  * appldata_diag()
132  *
133  * prepare parameter list, issue DIAG 0xDC
134  */
135 int appldata_diag(char record_nr, u16 function, unsigned long buffer,
136 			u16 length, char *mod_lvl)
137 {
138 	struct appldata_parameter_list *parm_list;
139 	struct appldata_product_id *id;
140 	int rc;
141 
142 	parm_list = kmalloc_obj(*parm_list);
143 	id = kmemdup(&appldata_id, sizeof(appldata_id), GFP_KERNEL);
144 	rc = -ENOMEM;
145 	if (parm_list && id) {
146 		id->record_nr = record_nr;
147 		id->mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1];
148 		rc = appldata_asm(parm_list, id, function,
149 				  (void *) buffer, length);
150 	}
151 	kfree(id);
152 	kfree(parm_list);
153 	return rc;
154 }
155 /************************ timer, work, DIAG <END> ****************************/
156 
157 
158 /****************************** /proc stuff **********************************/
159 
160 #define APPLDATA_ADD_TIMER	0
161 #define APPLDATA_DEL_TIMER	1
162 #define APPLDATA_MOD_TIMER	2
163 
164 /*
165  * __appldata_vtimer_setup()
166  *
167  * Add, delete or modify virtual timers on all online cpus.
168  * The caller needs to get the appldata_timer_lock spinlock.
169  */
170 static void __appldata_vtimer_setup(int cmd)
171 {
172 	u64 timer_interval = (u64) appldata_interval * 1000 * TOD_MICRO;
173 
174 	switch (cmd) {
175 	case APPLDATA_ADD_TIMER:
176 		if (appldata_timer_active)
177 			break;
178 		appldata_timer.expires = timer_interval;
179 		add_virt_timer_periodic(&appldata_timer);
180 		appldata_timer_active = 1;
181 		break;
182 	case APPLDATA_DEL_TIMER:
183 		del_virt_timer(&appldata_timer);
184 		if (!appldata_timer_active)
185 			break;
186 		appldata_timer_active = 0;
187 		break;
188 	case APPLDATA_MOD_TIMER:
189 		if (!appldata_timer_active)
190 			break;
191 		mod_virt_timer_periodic(&appldata_timer, timer_interval);
192 	}
193 }
194 
195 /*
196  * appldata_timer_handler()
197  *
198  * Start/Stop timer, show status of timer (0 = not active, 1 = active)
199  */
200 static int
201 appldata_timer_handler(const struct ctl_table *ctl, int write,
202 			   void *buffer, size_t *lenp, loff_t *ppos)
203 {
204 	int timer_active = appldata_timer_active;
205 	int rc;
206 	struct ctl_table ctl_entry = {
207 		.procname	= ctl->procname,
208 		.data		= &timer_active,
209 		.maxlen		= sizeof(int),
210 		.extra1		= SYSCTL_ZERO,
211 		.extra2		= SYSCTL_ONE,
212 	};
213 
214 	rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
215 	if (rc < 0 || !write)
216 		return rc;
217 
218 	spin_lock(&appldata_timer_lock);
219 	if (timer_active)
220 		__appldata_vtimer_setup(APPLDATA_ADD_TIMER);
221 	else
222 		__appldata_vtimer_setup(APPLDATA_DEL_TIMER);
223 	spin_unlock(&appldata_timer_lock);
224 	return 0;
225 }
226 
227 /*
228  * appldata_interval_handler()
229  *
230  * Set (CPU) timer interval for collection of data (in milliseconds), show
231  * current timer interval.
232  */
233 static int
234 appldata_interval_handler(const struct ctl_table *ctl, int write,
235 			   void *buffer, size_t *lenp, loff_t *ppos)
236 {
237 	int interval = appldata_interval;
238 	int rc;
239 	struct ctl_table ctl_entry = {
240 		.procname	= ctl->procname,
241 		.data		= &interval,
242 		.maxlen		= sizeof(int),
243 		.extra1		= SYSCTL_ONE,
244 	};
245 
246 	rc = proc_dointvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
247 	if (rc < 0 || !write)
248 		return rc;
249 
250 	spin_lock(&appldata_timer_lock);
251 	appldata_interval = interval;
252 	__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
253 	spin_unlock(&appldata_timer_lock);
254 	return 0;
255 }
256 
257 /*
258  * appldata_generic_handler()
259  *
260  * Generic start/stop monitoring and DIAG, show status of
261  * monitoring (0 = not in process, 1 = in process)
262  */
263 static int
264 appldata_generic_handler(const struct ctl_table *ctl, int write,
265 			   void *buffer, size_t *lenp, loff_t *ppos)
266 {
267 	struct appldata_ops *ops = NULL, *tmp_ops;
268 	struct list_head *lh;
269 	int rc, found;
270 	int active;
271 	struct ctl_table ctl_entry = {
272 		.data		= &active,
273 		.maxlen		= sizeof(int),
274 		.extra1		= SYSCTL_ZERO,
275 		.extra2		= SYSCTL_ONE,
276 	};
277 
278 	found = 0;
279 	mutex_lock(&appldata_ops_mutex);
280 	list_for_each(lh, &appldata_ops_list) {
281 		tmp_ops = list_entry(lh, struct appldata_ops, list);
282 		if (&tmp_ops->ctl_table[0] == ctl) {
283 			found = 1;
284 		}
285 	}
286 	if (!found) {
287 		mutex_unlock(&appldata_ops_mutex);
288 		return -ENODEV;
289 	}
290 	ops = ctl->data;
291 	if (!try_module_get(ops->owner)) {	// protect this function
292 		mutex_unlock(&appldata_ops_mutex);
293 		return -ENODEV;
294 	}
295 	mutex_unlock(&appldata_ops_mutex);
296 
297 	active = ops->active;
298 	rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
299 	if (rc < 0 || !write) {
300 		module_put(ops->owner);
301 		return rc;
302 	}
303 
304 	mutex_lock(&appldata_ops_mutex);
305 	if (active && (ops->active == 0)) {
306 		// protect work queue callback
307 		if (!try_module_get(ops->owner)) {
308 			mutex_unlock(&appldata_ops_mutex);
309 			module_put(ops->owner);
310 			return -ENODEV;
311 		}
312 		ops->callback(ops->data);	// init record
313 		rc = appldata_diag(ops->record_nr,
314 					APPLDATA_START_INTERVAL_REC,
315 					(unsigned long) ops->data, ops->size,
316 					ops->mod_lvl);
317 		if (rc != 0) {
318 			pr_err("Starting the data collection for %s "
319 			       "failed with rc=%d\n", ops->name, rc);
320 			module_put(ops->owner);
321 		} else
322 			ops->active = 1;
323 	} else if (!active && (ops->active == 1)) {
324 		ops->active = 0;
325 		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
326 				(unsigned long) ops->data, ops->size,
327 				ops->mod_lvl);
328 		if (rc != 0)
329 			pr_err("Stopping the data collection for %s "
330 			       "failed with rc=%d\n", ops->name, rc);
331 		module_put(ops->owner);
332 	}
333 	mutex_unlock(&appldata_ops_mutex);
334 	module_put(ops->owner);
335 	return 0;
336 }
337 
338 /*************************** /proc stuff <END> *******************************/
339 
340 
341 /************************* module-ops management *****************************/
342 /*
343  * appldata_register_ops()
344  *
345  * update ops list, register /proc/sys entries
346  */
347 int appldata_register_ops(struct appldata_ops *ops)
348 {
349 	if (ops->size > APPLDATA_MAX_REC_SIZE)
350 		return -EINVAL;
351 
352 	ops->ctl_table = kzalloc_objs(struct ctl_table, 1);
353 	if (!ops->ctl_table)
354 		return -ENOMEM;
355 
356 	mutex_lock(&appldata_ops_mutex);
357 	list_add(&ops->list, &appldata_ops_list);
358 	mutex_unlock(&appldata_ops_mutex);
359 
360 	ops->ctl_table[0].procname = ops->name;
361 	ops->ctl_table[0].mode = S_IRUGO | S_IWUSR;
362 	ops->ctl_table[0].proc_handler = appldata_generic_handler;
363 	ops->ctl_table[0].data = ops;
364 
365 	ops->sysctl_header = register_sysctl_sz(appldata_proc_name, ops->ctl_table, 1);
366 	if (!ops->sysctl_header)
367 		goto out;
368 	return 0;
369 out:
370 	mutex_lock(&appldata_ops_mutex);
371 	list_del(&ops->list);
372 	mutex_unlock(&appldata_ops_mutex);
373 	kfree(ops->ctl_table);
374 	return -ENOMEM;
375 }
376 
377 /*
378  * appldata_unregister_ops()
379  *
380  * update ops list, unregister /proc entries, stop DIAG if necessary
381  */
382 void appldata_unregister_ops(struct appldata_ops *ops)
383 {
384 	mutex_lock(&appldata_ops_mutex);
385 	list_del(&ops->list);
386 	mutex_unlock(&appldata_ops_mutex);
387 	unregister_sysctl_table(ops->sysctl_header);
388 	kfree(ops->ctl_table);
389 }
390 /********************** module-ops management <END> **************************/
391 
392 
393 /******************************* init / exit *********************************/
394 
395 /*
396  * appldata_init()
397  *
398  * init timer, register /proc entries
399  */
400 static int __init appldata_init(void)
401 {
402 	init_virt_timer(&appldata_timer);
403 	appldata_timer.function = appldata_timer_function;
404 	appldata_timer.data = (unsigned long) &appldata_work;
405 	appldata_wq = alloc_ordered_workqueue("appldata", 0);
406 	if (!appldata_wq)
407 		return -ENOMEM;
408 	register_sysctl(appldata_proc_name, appldata_table);
409 	return 0;
410 }
411 
412 __initcall(appldata_init);
413 
414 /**************************** init / exit <END> ******************************/
415 
416 EXPORT_SYMBOL_GPL(appldata_register_ops);
417 EXPORT_SYMBOL_GPL(appldata_unregister_ops);
418 EXPORT_SYMBOL_GPL(appldata_diag);
419 
420 #ifdef CONFIG_SWAP
421 EXPORT_SYMBOL_GPL(si_swapinfo);
422 #endif
423 EXPORT_SYMBOL_GPL(nr_threads);
424 EXPORT_SYMBOL_GPL(nr_running);
425 EXPORT_SYMBOL_GPL(nr_iowait);
426