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