162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.
462306a36Sopenharmony_ci * Exports appldata_register_ops() and appldata_unregister_ops() for the
562306a36Sopenharmony_ci * data gathering modules.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright IBM Corp. 2003, 2009
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define KMSG_COMPONENT	"appldata"
1362306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/sched/stat.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/errno.h>
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/proc_fs.h>
2262306a36Sopenharmony_ci#include <linux/mm.h>
2362306a36Sopenharmony_ci#include <linux/swap.h>
2462306a36Sopenharmony_ci#include <linux/pagemap.h>
2562306a36Sopenharmony_ci#include <linux/sysctl.h>
2662306a36Sopenharmony_ci#include <linux/notifier.h>
2762306a36Sopenharmony_ci#include <linux/cpu.h>
2862306a36Sopenharmony_ci#include <linux/workqueue.h>
2962306a36Sopenharmony_ci#include <linux/uaccess.h>
3062306a36Sopenharmony_ci#include <linux/io.h>
3162306a36Sopenharmony_ci#include <asm/appldata.h>
3262306a36Sopenharmony_ci#include <asm/vtimer.h>
3362306a36Sopenharmony_ci#include <asm/smp.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "appldata.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define APPLDATA_CPU_INTERVAL	10000		/* default (CPU) time for
3962306a36Sopenharmony_ci						   sampling interval in
4062306a36Sopenharmony_ci						   milliseconds */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define TOD_MICRO	0x01000			/* nr. of TOD clock units
4362306a36Sopenharmony_ci						   for 1 microsecond */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * /proc entries (sysctl)
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
4962306a36Sopenharmony_cistatic int appldata_timer_handler(struct ctl_table *ctl, int write,
5062306a36Sopenharmony_ci				  void *buffer, size_t *lenp, loff_t *ppos);
5162306a36Sopenharmony_cistatic int appldata_interval_handler(struct ctl_table *ctl, int write,
5262306a36Sopenharmony_ci				     void *buffer, size_t *lenp, loff_t *ppos);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic struct ctl_table_header *appldata_sysctl_header;
5562306a36Sopenharmony_cistatic struct ctl_table appldata_table[] = {
5662306a36Sopenharmony_ci	{
5762306a36Sopenharmony_ci		.procname	= "timer",
5862306a36Sopenharmony_ci		.mode		= S_IRUGO | S_IWUSR,
5962306a36Sopenharmony_ci		.proc_handler	= appldata_timer_handler,
6062306a36Sopenharmony_ci	},
6162306a36Sopenharmony_ci	{
6262306a36Sopenharmony_ci		.procname	= "interval",
6362306a36Sopenharmony_ci		.mode		= S_IRUGO | S_IWUSR,
6462306a36Sopenharmony_ci		.proc_handler	= appldata_interval_handler,
6562306a36Sopenharmony_ci	},
6662306a36Sopenharmony_ci	{ },
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * Timer
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_cistatic struct vtimer_list appldata_timer;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(appldata_timer_lock);
7562306a36Sopenharmony_cistatic int appldata_interval = APPLDATA_CPU_INTERVAL;
7662306a36Sopenharmony_cistatic int appldata_timer_active;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/*
7962306a36Sopenharmony_ci * Work queue
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_cistatic struct workqueue_struct *appldata_wq;
8262306a36Sopenharmony_cistatic void appldata_work_fn(struct work_struct *work);
8362306a36Sopenharmony_cistatic DECLARE_WORK(appldata_work, appldata_work_fn);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/*
8762306a36Sopenharmony_ci * Ops list
8862306a36Sopenharmony_ci */
8962306a36Sopenharmony_cistatic DEFINE_MUTEX(appldata_ops_mutex);
9062306a36Sopenharmony_cistatic LIST_HEAD(appldata_ops_list);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/*************************** timer, work, DIAG *******************************/
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * appldata_timer_function()
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * schedule work and reschedule timer
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_cistatic void appldata_timer_function(unsigned long data)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	queue_work(appldata_wq, (struct work_struct *) data);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*
10562306a36Sopenharmony_ci * appldata_work_fn()
10662306a36Sopenharmony_ci *
10762306a36Sopenharmony_ci * call data gathering function for each (active) module
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_cistatic void appldata_work_fn(struct work_struct *work)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct list_head *lh;
11262306a36Sopenharmony_ci	struct appldata_ops *ops;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
11562306a36Sopenharmony_ci	list_for_each(lh, &appldata_ops_list) {
11662306a36Sopenharmony_ci		ops = list_entry(lh, struct appldata_ops, list);
11762306a36Sopenharmony_ci		if (ops->active == 1) {
11862306a36Sopenharmony_ci			ops->callback(ops->data);
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic struct appldata_product_id appldata_id = {
12562306a36Sopenharmony_ci	.prod_nr    = {0xD3, 0xC9, 0xD5, 0xE4,
12662306a36Sopenharmony_ci		       0xE7, 0xD2, 0xD9},	/* "LINUXKR" */
12762306a36Sopenharmony_ci	.prod_fn    = 0xD5D3,			/* "NL" */
12862306a36Sopenharmony_ci	.version_nr = 0xF2F6,			/* "26" */
12962306a36Sopenharmony_ci	.release_nr = 0xF0F1,			/* "01" */
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/*
13362306a36Sopenharmony_ci * appldata_diag()
13462306a36Sopenharmony_ci *
13562306a36Sopenharmony_ci * prepare parameter list, issue DIAG 0xDC
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_ciint appldata_diag(char record_nr, u16 function, unsigned long buffer,
13862306a36Sopenharmony_ci			u16 length, char *mod_lvl)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct appldata_parameter_list *parm_list;
14162306a36Sopenharmony_ci	struct appldata_product_id *id;
14262306a36Sopenharmony_ci	int rc;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	parm_list = kmalloc(sizeof(*parm_list), GFP_KERNEL);
14562306a36Sopenharmony_ci	id = kmemdup(&appldata_id, sizeof(appldata_id), GFP_KERNEL);
14662306a36Sopenharmony_ci	rc = -ENOMEM;
14762306a36Sopenharmony_ci	if (parm_list && id) {
14862306a36Sopenharmony_ci		id->record_nr = record_nr;
14962306a36Sopenharmony_ci		id->mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1];
15062306a36Sopenharmony_ci		rc = appldata_asm(parm_list, id, function,
15162306a36Sopenharmony_ci				  (void *) buffer, length);
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci	kfree(id);
15462306a36Sopenharmony_ci	kfree(parm_list);
15562306a36Sopenharmony_ci	return rc;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci/************************ timer, work, DIAG <END> ****************************/
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/****************************** /proc stuff **********************************/
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci#define APPLDATA_ADD_TIMER	0
16362306a36Sopenharmony_ci#define APPLDATA_DEL_TIMER	1
16462306a36Sopenharmony_ci#define APPLDATA_MOD_TIMER	2
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/*
16762306a36Sopenharmony_ci * __appldata_vtimer_setup()
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci * Add, delete or modify virtual timers on all online cpus.
17062306a36Sopenharmony_ci * The caller needs to get the appldata_timer_lock spinlock.
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_cistatic void __appldata_vtimer_setup(int cmd)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	u64 timer_interval = (u64) appldata_interval * 1000 * TOD_MICRO;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	switch (cmd) {
17762306a36Sopenharmony_ci	case APPLDATA_ADD_TIMER:
17862306a36Sopenharmony_ci		if (appldata_timer_active)
17962306a36Sopenharmony_ci			break;
18062306a36Sopenharmony_ci		appldata_timer.expires = timer_interval;
18162306a36Sopenharmony_ci		add_virt_timer_periodic(&appldata_timer);
18262306a36Sopenharmony_ci		appldata_timer_active = 1;
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case APPLDATA_DEL_TIMER:
18562306a36Sopenharmony_ci		del_virt_timer(&appldata_timer);
18662306a36Sopenharmony_ci		if (!appldata_timer_active)
18762306a36Sopenharmony_ci			break;
18862306a36Sopenharmony_ci		appldata_timer_active = 0;
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	case APPLDATA_MOD_TIMER:
19162306a36Sopenharmony_ci		if (!appldata_timer_active)
19262306a36Sopenharmony_ci			break;
19362306a36Sopenharmony_ci		mod_virt_timer_periodic(&appldata_timer, timer_interval);
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/*
19862306a36Sopenharmony_ci * appldata_timer_handler()
19962306a36Sopenharmony_ci *
20062306a36Sopenharmony_ci * Start/Stop timer, show status of timer (0 = not active, 1 = active)
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_cistatic int
20362306a36Sopenharmony_ciappldata_timer_handler(struct ctl_table *ctl, int write,
20462306a36Sopenharmony_ci			   void *buffer, size_t *lenp, loff_t *ppos)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	int timer_active = appldata_timer_active;
20762306a36Sopenharmony_ci	int rc;
20862306a36Sopenharmony_ci	struct ctl_table ctl_entry = {
20962306a36Sopenharmony_ci		.procname	= ctl->procname,
21062306a36Sopenharmony_ci		.data		= &timer_active,
21162306a36Sopenharmony_ci		.maxlen		= sizeof(int),
21262306a36Sopenharmony_ci		.extra1		= SYSCTL_ZERO,
21362306a36Sopenharmony_ci		.extra2		= SYSCTL_ONE,
21462306a36Sopenharmony_ci	};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
21762306a36Sopenharmony_ci	if (rc < 0 || !write)
21862306a36Sopenharmony_ci		return rc;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	spin_lock(&appldata_timer_lock);
22162306a36Sopenharmony_ci	if (timer_active)
22262306a36Sopenharmony_ci		__appldata_vtimer_setup(APPLDATA_ADD_TIMER);
22362306a36Sopenharmony_ci	else
22462306a36Sopenharmony_ci		__appldata_vtimer_setup(APPLDATA_DEL_TIMER);
22562306a36Sopenharmony_ci	spin_unlock(&appldata_timer_lock);
22662306a36Sopenharmony_ci	return 0;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci/*
23062306a36Sopenharmony_ci * appldata_interval_handler()
23162306a36Sopenharmony_ci *
23262306a36Sopenharmony_ci * Set (CPU) timer interval for collection of data (in milliseconds), show
23362306a36Sopenharmony_ci * current timer interval.
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_cistatic int
23662306a36Sopenharmony_ciappldata_interval_handler(struct ctl_table *ctl, int write,
23762306a36Sopenharmony_ci			   void *buffer, size_t *lenp, loff_t *ppos)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	int interval = appldata_interval;
24062306a36Sopenharmony_ci	int rc;
24162306a36Sopenharmony_ci	struct ctl_table ctl_entry = {
24262306a36Sopenharmony_ci		.procname	= ctl->procname,
24362306a36Sopenharmony_ci		.data		= &interval,
24462306a36Sopenharmony_ci		.maxlen		= sizeof(int),
24562306a36Sopenharmony_ci		.extra1		= SYSCTL_ONE,
24662306a36Sopenharmony_ci	};
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	rc = proc_dointvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
24962306a36Sopenharmony_ci	if (rc < 0 || !write)
25062306a36Sopenharmony_ci		return rc;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	spin_lock(&appldata_timer_lock);
25362306a36Sopenharmony_ci	appldata_interval = interval;
25462306a36Sopenharmony_ci	__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
25562306a36Sopenharmony_ci	spin_unlock(&appldata_timer_lock);
25662306a36Sopenharmony_ci	return 0;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/*
26062306a36Sopenharmony_ci * appldata_generic_handler()
26162306a36Sopenharmony_ci *
26262306a36Sopenharmony_ci * Generic start/stop monitoring and DIAG, show status of
26362306a36Sopenharmony_ci * monitoring (0 = not in process, 1 = in process)
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_cistatic int
26662306a36Sopenharmony_ciappldata_generic_handler(struct ctl_table *ctl, int write,
26762306a36Sopenharmony_ci			   void *buffer, size_t *lenp, loff_t *ppos)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct appldata_ops *ops = NULL, *tmp_ops;
27062306a36Sopenharmony_ci	struct list_head *lh;
27162306a36Sopenharmony_ci	int rc, found;
27262306a36Sopenharmony_ci	int active;
27362306a36Sopenharmony_ci	struct ctl_table ctl_entry = {
27462306a36Sopenharmony_ci		.data		= &active,
27562306a36Sopenharmony_ci		.maxlen		= sizeof(int),
27662306a36Sopenharmony_ci		.extra1		= SYSCTL_ZERO,
27762306a36Sopenharmony_ci		.extra2		= SYSCTL_ONE,
27862306a36Sopenharmony_ci	};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	found = 0;
28162306a36Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
28262306a36Sopenharmony_ci	list_for_each(lh, &appldata_ops_list) {
28362306a36Sopenharmony_ci		tmp_ops = list_entry(lh, struct appldata_ops, list);
28462306a36Sopenharmony_ci		if (&tmp_ops->ctl_table[0] == ctl) {
28562306a36Sopenharmony_ci			found = 1;
28662306a36Sopenharmony_ci		}
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci	if (!found) {
28962306a36Sopenharmony_ci		mutex_unlock(&appldata_ops_mutex);
29062306a36Sopenharmony_ci		return -ENODEV;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci	ops = ctl->data;
29362306a36Sopenharmony_ci	if (!try_module_get(ops->owner)) {	// protect this function
29462306a36Sopenharmony_ci		mutex_unlock(&appldata_ops_mutex);
29562306a36Sopenharmony_ci		return -ENODEV;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	active = ops->active;
30062306a36Sopenharmony_ci	rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
30162306a36Sopenharmony_ci	if (rc < 0 || !write) {
30262306a36Sopenharmony_ci		module_put(ops->owner);
30362306a36Sopenharmony_ci		return rc;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
30762306a36Sopenharmony_ci	if (active && (ops->active == 0)) {
30862306a36Sopenharmony_ci		// protect work queue callback
30962306a36Sopenharmony_ci		if (!try_module_get(ops->owner)) {
31062306a36Sopenharmony_ci			mutex_unlock(&appldata_ops_mutex);
31162306a36Sopenharmony_ci			module_put(ops->owner);
31262306a36Sopenharmony_ci			return -ENODEV;
31362306a36Sopenharmony_ci		}
31462306a36Sopenharmony_ci		ops->callback(ops->data);	// init record
31562306a36Sopenharmony_ci		rc = appldata_diag(ops->record_nr,
31662306a36Sopenharmony_ci					APPLDATA_START_INTERVAL_REC,
31762306a36Sopenharmony_ci					(unsigned long) ops->data, ops->size,
31862306a36Sopenharmony_ci					ops->mod_lvl);
31962306a36Sopenharmony_ci		if (rc != 0) {
32062306a36Sopenharmony_ci			pr_err("Starting the data collection for %s "
32162306a36Sopenharmony_ci			       "failed with rc=%d\n", ops->name, rc);
32262306a36Sopenharmony_ci			module_put(ops->owner);
32362306a36Sopenharmony_ci		} else
32462306a36Sopenharmony_ci			ops->active = 1;
32562306a36Sopenharmony_ci	} else if (!active && (ops->active == 1)) {
32662306a36Sopenharmony_ci		ops->active = 0;
32762306a36Sopenharmony_ci		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
32862306a36Sopenharmony_ci				(unsigned long) ops->data, ops->size,
32962306a36Sopenharmony_ci				ops->mod_lvl);
33062306a36Sopenharmony_ci		if (rc != 0)
33162306a36Sopenharmony_ci			pr_err("Stopping the data collection for %s "
33262306a36Sopenharmony_ci			       "failed with rc=%d\n", ops->name, rc);
33362306a36Sopenharmony_ci		module_put(ops->owner);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
33662306a36Sopenharmony_ci	module_put(ops->owner);
33762306a36Sopenharmony_ci	return 0;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/*************************** /proc stuff <END> *******************************/
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci/************************* module-ops management *****************************/
34462306a36Sopenharmony_ci/*
34562306a36Sopenharmony_ci * appldata_register_ops()
34662306a36Sopenharmony_ci *
34762306a36Sopenharmony_ci * update ops list, register /proc/sys entries
34862306a36Sopenharmony_ci */
34962306a36Sopenharmony_ciint appldata_register_ops(struct appldata_ops *ops)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	if (ops->size > APPLDATA_MAX_REC_SIZE)
35262306a36Sopenharmony_ci		return -EINVAL;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* The last entry must be an empty one */
35562306a36Sopenharmony_ci	ops->ctl_table = kcalloc(2, sizeof(struct ctl_table), GFP_KERNEL);
35662306a36Sopenharmony_ci	if (!ops->ctl_table)
35762306a36Sopenharmony_ci		return -ENOMEM;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
36062306a36Sopenharmony_ci	list_add(&ops->list, &appldata_ops_list);
36162306a36Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	ops->ctl_table[0].procname = ops->name;
36462306a36Sopenharmony_ci	ops->ctl_table[0].mode = S_IRUGO | S_IWUSR;
36562306a36Sopenharmony_ci	ops->ctl_table[0].proc_handler = appldata_generic_handler;
36662306a36Sopenharmony_ci	ops->ctl_table[0].data = ops;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	ops->sysctl_header = register_sysctl_sz(appldata_proc_name, ops->ctl_table, 1);
36962306a36Sopenharmony_ci	if (!ops->sysctl_header)
37062306a36Sopenharmony_ci		goto out;
37162306a36Sopenharmony_ci	return 0;
37262306a36Sopenharmony_ciout:
37362306a36Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
37462306a36Sopenharmony_ci	list_del(&ops->list);
37562306a36Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
37662306a36Sopenharmony_ci	kfree(ops->ctl_table);
37762306a36Sopenharmony_ci	return -ENOMEM;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci/*
38162306a36Sopenharmony_ci * appldata_unregister_ops()
38262306a36Sopenharmony_ci *
38362306a36Sopenharmony_ci * update ops list, unregister /proc entries, stop DIAG if necessary
38462306a36Sopenharmony_ci */
38562306a36Sopenharmony_civoid appldata_unregister_ops(struct appldata_ops *ops)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
38862306a36Sopenharmony_ci	list_del(&ops->list);
38962306a36Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
39062306a36Sopenharmony_ci	unregister_sysctl_table(ops->sysctl_header);
39162306a36Sopenharmony_ci	kfree(ops->ctl_table);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci/********************** module-ops management <END> **************************/
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/******************************* init / exit *********************************/
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci/*
39962306a36Sopenharmony_ci * appldata_init()
40062306a36Sopenharmony_ci *
40162306a36Sopenharmony_ci * init timer, register /proc entries
40262306a36Sopenharmony_ci */
40362306a36Sopenharmony_cistatic int __init appldata_init(void)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	init_virt_timer(&appldata_timer);
40662306a36Sopenharmony_ci	appldata_timer.function = appldata_timer_function;
40762306a36Sopenharmony_ci	appldata_timer.data = (unsigned long) &appldata_work;
40862306a36Sopenharmony_ci	appldata_wq = alloc_ordered_workqueue("appldata", 0);
40962306a36Sopenharmony_ci	if (!appldata_wq)
41062306a36Sopenharmony_ci		return -ENOMEM;
41162306a36Sopenharmony_ci	appldata_sysctl_header = register_sysctl(appldata_proc_name, appldata_table);
41262306a36Sopenharmony_ci	return 0;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci__initcall(appldata_init);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci/**************************** init / exit <END> ******************************/
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(appldata_register_ops);
42062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(appldata_unregister_ops);
42162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(appldata_diag);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci#ifdef CONFIG_SWAP
42462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si_swapinfo);
42562306a36Sopenharmony_ci#endif
42662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nr_threads);
42762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nr_running);
42862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nr_iowait);
429