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