18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.
48c2ecf20Sopenharmony_ci * Exports appldata_register_ops() and appldata_unregister_ops() for the
58c2ecf20Sopenharmony_ci * data gathering modules.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2003, 2009
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define KMSG_COMPONENT	"appldata"
138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/sched/stat.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/errno.h>
208c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
218c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
228c2ecf20Sopenharmony_ci#include <linux/mm.h>
238c2ecf20Sopenharmony_ci#include <linux/swap.h>
248c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
258c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
268c2ecf20Sopenharmony_ci#include <linux/notifier.h>
278c2ecf20Sopenharmony_ci#include <linux/cpu.h>
288c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
298c2ecf20Sopenharmony_ci#include <linux/suspend.h>
308c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
318c2ecf20Sopenharmony_ci#include <asm/appldata.h>
328c2ecf20Sopenharmony_ci#include <asm/vtimer.h>
338c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
348c2ecf20Sopenharmony_ci#include <asm/io.h>
358c2ecf20Sopenharmony_ci#include <asm/smp.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "appldata.h"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define APPLDATA_CPU_INTERVAL	10000		/* default (CPU) time for
418c2ecf20Sopenharmony_ci						   sampling interval in
428c2ecf20Sopenharmony_ci						   milliseconds */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define TOD_MICRO	0x01000			/* nr. of TOD clock units
458c2ecf20Sopenharmony_ci						   for 1 microsecond */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic struct platform_device *appldata_pdev;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/*
508c2ecf20Sopenharmony_ci * /proc entries (sysctl)
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_cistatic const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
538c2ecf20Sopenharmony_cistatic int appldata_timer_handler(struct ctl_table *ctl, int write,
548c2ecf20Sopenharmony_ci				  void *buffer, size_t *lenp, loff_t *ppos);
558c2ecf20Sopenharmony_cistatic int appldata_interval_handler(struct ctl_table *ctl, int write,
568c2ecf20Sopenharmony_ci				     void *buffer, size_t *lenp, loff_t *ppos);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic struct ctl_table_header *appldata_sysctl_header;
598c2ecf20Sopenharmony_cistatic struct ctl_table appldata_table[] = {
608c2ecf20Sopenharmony_ci	{
618c2ecf20Sopenharmony_ci		.procname	= "timer",
628c2ecf20Sopenharmony_ci		.mode		= S_IRUGO | S_IWUSR,
638c2ecf20Sopenharmony_ci		.proc_handler	= appldata_timer_handler,
648c2ecf20Sopenharmony_ci	},
658c2ecf20Sopenharmony_ci	{
668c2ecf20Sopenharmony_ci		.procname	= "interval",
678c2ecf20Sopenharmony_ci		.mode		= S_IRUGO | S_IWUSR,
688c2ecf20Sopenharmony_ci		.proc_handler	= appldata_interval_handler,
698c2ecf20Sopenharmony_ci	},
708c2ecf20Sopenharmony_ci	{ },
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic struct ctl_table appldata_dir_table[] = {
748c2ecf20Sopenharmony_ci	{
758c2ecf20Sopenharmony_ci		.procname	= appldata_proc_name,
768c2ecf20Sopenharmony_ci		.maxlen		= 0,
778c2ecf20Sopenharmony_ci		.mode		= S_IRUGO | S_IXUGO,
788c2ecf20Sopenharmony_ci		.child		= appldata_table,
798c2ecf20Sopenharmony_ci	},
808c2ecf20Sopenharmony_ci	{ },
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/*
848c2ecf20Sopenharmony_ci * Timer
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_cistatic struct vtimer_list appldata_timer;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(appldata_timer_lock);
898c2ecf20Sopenharmony_cistatic int appldata_interval = APPLDATA_CPU_INTERVAL;
908c2ecf20Sopenharmony_cistatic int appldata_timer_active;
918c2ecf20Sopenharmony_cistatic int appldata_timer_suspended = 0;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/*
948c2ecf20Sopenharmony_ci * Work queue
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_cistatic struct workqueue_struct *appldata_wq;
978c2ecf20Sopenharmony_cistatic void appldata_work_fn(struct work_struct *work);
988c2ecf20Sopenharmony_cistatic DECLARE_WORK(appldata_work, appldata_work_fn);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/*
1028c2ecf20Sopenharmony_ci * Ops list
1038c2ecf20Sopenharmony_ci */
1048c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(appldata_ops_mutex);
1058c2ecf20Sopenharmony_cistatic LIST_HEAD(appldata_ops_list);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/*************************** timer, work, DIAG *******************************/
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * appldata_timer_function()
1118c2ecf20Sopenharmony_ci *
1128c2ecf20Sopenharmony_ci * schedule work and reschedule timer
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_cistatic void appldata_timer_function(unsigned long data)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	queue_work(appldata_wq, (struct work_struct *) data);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/*
1208c2ecf20Sopenharmony_ci * appldata_work_fn()
1218c2ecf20Sopenharmony_ci *
1228c2ecf20Sopenharmony_ci * call data gathering function for each (active) module
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_cistatic void appldata_work_fn(struct work_struct *work)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct list_head *lh;
1278c2ecf20Sopenharmony_ci	struct appldata_ops *ops;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
1308c2ecf20Sopenharmony_ci	list_for_each(lh, &appldata_ops_list) {
1318c2ecf20Sopenharmony_ci		ops = list_entry(lh, struct appldata_ops, list);
1328c2ecf20Sopenharmony_ci		if (ops->active == 1) {
1338c2ecf20Sopenharmony_ci			ops->callback(ops->data);
1348c2ecf20Sopenharmony_ci		}
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic struct appldata_product_id appldata_id = {
1408c2ecf20Sopenharmony_ci	.prod_nr    = {0xD3, 0xC9, 0xD5, 0xE4,
1418c2ecf20Sopenharmony_ci		       0xE7, 0xD2, 0xD9},	/* "LINUXKR" */
1428c2ecf20Sopenharmony_ci	.prod_fn    = 0xD5D3,			/* "NL" */
1438c2ecf20Sopenharmony_ci	.version_nr = 0xF2F6,			/* "26" */
1448c2ecf20Sopenharmony_ci	.release_nr = 0xF0F1,			/* "01" */
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/*
1488c2ecf20Sopenharmony_ci * appldata_diag()
1498c2ecf20Sopenharmony_ci *
1508c2ecf20Sopenharmony_ci * prepare parameter list, issue DIAG 0xDC
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_ciint appldata_diag(char record_nr, u16 function, unsigned long buffer,
1538c2ecf20Sopenharmony_ci			u16 length, char *mod_lvl)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct appldata_parameter_list *parm_list;
1568c2ecf20Sopenharmony_ci	struct appldata_product_id *id;
1578c2ecf20Sopenharmony_ci	int rc;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	parm_list = kmalloc(sizeof(*parm_list), GFP_KERNEL);
1608c2ecf20Sopenharmony_ci	id = kmemdup(&appldata_id, sizeof(appldata_id), GFP_KERNEL);
1618c2ecf20Sopenharmony_ci	rc = -ENOMEM;
1628c2ecf20Sopenharmony_ci	if (parm_list && id) {
1638c2ecf20Sopenharmony_ci		id->record_nr = record_nr;
1648c2ecf20Sopenharmony_ci		id->mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1];
1658c2ecf20Sopenharmony_ci		rc = appldata_asm(parm_list, id, function,
1668c2ecf20Sopenharmony_ci				  (void *) buffer, length);
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci	kfree(id);
1698c2ecf20Sopenharmony_ci	kfree(parm_list);
1708c2ecf20Sopenharmony_ci	return rc;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci/************************ timer, work, DIAG <END> ****************************/
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/****************************** /proc stuff **********************************/
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci#define APPLDATA_ADD_TIMER	0
1788c2ecf20Sopenharmony_ci#define APPLDATA_DEL_TIMER	1
1798c2ecf20Sopenharmony_ci#define APPLDATA_MOD_TIMER	2
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/*
1828c2ecf20Sopenharmony_ci * __appldata_vtimer_setup()
1838c2ecf20Sopenharmony_ci *
1848c2ecf20Sopenharmony_ci * Add, delete or modify virtual timers on all online cpus.
1858c2ecf20Sopenharmony_ci * The caller needs to get the appldata_timer_lock spinlock.
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_cistatic void __appldata_vtimer_setup(int cmd)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	u64 timer_interval = (u64) appldata_interval * 1000 * TOD_MICRO;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	switch (cmd) {
1928c2ecf20Sopenharmony_ci	case APPLDATA_ADD_TIMER:
1938c2ecf20Sopenharmony_ci		if (appldata_timer_active)
1948c2ecf20Sopenharmony_ci			break;
1958c2ecf20Sopenharmony_ci		appldata_timer.expires = timer_interval;
1968c2ecf20Sopenharmony_ci		add_virt_timer_periodic(&appldata_timer);
1978c2ecf20Sopenharmony_ci		appldata_timer_active = 1;
1988c2ecf20Sopenharmony_ci		break;
1998c2ecf20Sopenharmony_ci	case APPLDATA_DEL_TIMER:
2008c2ecf20Sopenharmony_ci		del_virt_timer(&appldata_timer);
2018c2ecf20Sopenharmony_ci		if (!appldata_timer_active)
2028c2ecf20Sopenharmony_ci			break;
2038c2ecf20Sopenharmony_ci		appldata_timer_active = 0;
2048c2ecf20Sopenharmony_ci		break;
2058c2ecf20Sopenharmony_ci	case APPLDATA_MOD_TIMER:
2068c2ecf20Sopenharmony_ci		if (!appldata_timer_active)
2078c2ecf20Sopenharmony_ci			break;
2088c2ecf20Sopenharmony_ci		mod_virt_timer_periodic(&appldata_timer, timer_interval);
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/*
2138c2ecf20Sopenharmony_ci * appldata_timer_handler()
2148c2ecf20Sopenharmony_ci *
2158c2ecf20Sopenharmony_ci * Start/Stop timer, show status of timer (0 = not active, 1 = active)
2168c2ecf20Sopenharmony_ci */
2178c2ecf20Sopenharmony_cistatic int
2188c2ecf20Sopenharmony_ciappldata_timer_handler(struct ctl_table *ctl, int write,
2198c2ecf20Sopenharmony_ci			   void *buffer, size_t *lenp, loff_t *ppos)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	int timer_active = appldata_timer_active;
2228c2ecf20Sopenharmony_ci	int rc;
2238c2ecf20Sopenharmony_ci	struct ctl_table ctl_entry = {
2248c2ecf20Sopenharmony_ci		.procname	= ctl->procname,
2258c2ecf20Sopenharmony_ci		.data		= &timer_active,
2268c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
2278c2ecf20Sopenharmony_ci		.extra1		= SYSCTL_ZERO,
2288c2ecf20Sopenharmony_ci		.extra2		= SYSCTL_ONE,
2298c2ecf20Sopenharmony_ci	};
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
2328c2ecf20Sopenharmony_ci	if (rc < 0 || !write)
2338c2ecf20Sopenharmony_ci		return rc;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	spin_lock(&appldata_timer_lock);
2368c2ecf20Sopenharmony_ci	if (timer_active)
2378c2ecf20Sopenharmony_ci		__appldata_vtimer_setup(APPLDATA_ADD_TIMER);
2388c2ecf20Sopenharmony_ci	else
2398c2ecf20Sopenharmony_ci		__appldata_vtimer_setup(APPLDATA_DEL_TIMER);
2408c2ecf20Sopenharmony_ci	spin_unlock(&appldata_timer_lock);
2418c2ecf20Sopenharmony_ci	return 0;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci/*
2458c2ecf20Sopenharmony_ci * appldata_interval_handler()
2468c2ecf20Sopenharmony_ci *
2478c2ecf20Sopenharmony_ci * Set (CPU) timer interval for collection of data (in milliseconds), show
2488c2ecf20Sopenharmony_ci * current timer interval.
2498c2ecf20Sopenharmony_ci */
2508c2ecf20Sopenharmony_cistatic int
2518c2ecf20Sopenharmony_ciappldata_interval_handler(struct ctl_table *ctl, int write,
2528c2ecf20Sopenharmony_ci			   void *buffer, size_t *lenp, loff_t *ppos)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	int interval = appldata_interval;
2558c2ecf20Sopenharmony_ci	int rc;
2568c2ecf20Sopenharmony_ci	struct ctl_table ctl_entry = {
2578c2ecf20Sopenharmony_ci		.procname	= ctl->procname,
2588c2ecf20Sopenharmony_ci		.data		= &interval,
2598c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
2608c2ecf20Sopenharmony_ci		.extra1		= SYSCTL_ONE,
2618c2ecf20Sopenharmony_ci	};
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	rc = proc_dointvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
2648c2ecf20Sopenharmony_ci	if (rc < 0 || !write)
2658c2ecf20Sopenharmony_ci		return rc;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	spin_lock(&appldata_timer_lock);
2688c2ecf20Sopenharmony_ci	appldata_interval = interval;
2698c2ecf20Sopenharmony_ci	__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
2708c2ecf20Sopenharmony_ci	spin_unlock(&appldata_timer_lock);
2718c2ecf20Sopenharmony_ci	return 0;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/*
2758c2ecf20Sopenharmony_ci * appldata_generic_handler()
2768c2ecf20Sopenharmony_ci *
2778c2ecf20Sopenharmony_ci * Generic start/stop monitoring and DIAG, show status of
2788c2ecf20Sopenharmony_ci * monitoring (0 = not in process, 1 = in process)
2798c2ecf20Sopenharmony_ci */
2808c2ecf20Sopenharmony_cistatic int
2818c2ecf20Sopenharmony_ciappldata_generic_handler(struct ctl_table *ctl, int write,
2828c2ecf20Sopenharmony_ci			   void *buffer, size_t *lenp, loff_t *ppos)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct appldata_ops *ops = NULL, *tmp_ops;
2858c2ecf20Sopenharmony_ci	struct list_head *lh;
2868c2ecf20Sopenharmony_ci	int rc, found;
2878c2ecf20Sopenharmony_ci	int active;
2888c2ecf20Sopenharmony_ci	struct ctl_table ctl_entry = {
2898c2ecf20Sopenharmony_ci		.data		= &active,
2908c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
2918c2ecf20Sopenharmony_ci		.extra1		= SYSCTL_ZERO,
2928c2ecf20Sopenharmony_ci		.extra2		= SYSCTL_ONE,
2938c2ecf20Sopenharmony_ci	};
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	found = 0;
2968c2ecf20Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
2978c2ecf20Sopenharmony_ci	list_for_each(lh, &appldata_ops_list) {
2988c2ecf20Sopenharmony_ci		tmp_ops = list_entry(lh, struct appldata_ops, list);
2998c2ecf20Sopenharmony_ci		if (&tmp_ops->ctl_table[2] == ctl) {
3008c2ecf20Sopenharmony_ci			found = 1;
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	if (!found) {
3048c2ecf20Sopenharmony_ci		mutex_unlock(&appldata_ops_mutex);
3058c2ecf20Sopenharmony_ci		return -ENODEV;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci	ops = ctl->data;
3088c2ecf20Sopenharmony_ci	if (!try_module_get(ops->owner)) {	// protect this function
3098c2ecf20Sopenharmony_ci		mutex_unlock(&appldata_ops_mutex);
3108c2ecf20Sopenharmony_ci		return -ENODEV;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	active = ops->active;
3158c2ecf20Sopenharmony_ci	rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
3168c2ecf20Sopenharmony_ci	if (rc < 0 || !write) {
3178c2ecf20Sopenharmony_ci		module_put(ops->owner);
3188c2ecf20Sopenharmony_ci		return rc;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
3228c2ecf20Sopenharmony_ci	if (active && (ops->active == 0)) {
3238c2ecf20Sopenharmony_ci		// protect work queue callback
3248c2ecf20Sopenharmony_ci		if (!try_module_get(ops->owner)) {
3258c2ecf20Sopenharmony_ci			mutex_unlock(&appldata_ops_mutex);
3268c2ecf20Sopenharmony_ci			module_put(ops->owner);
3278c2ecf20Sopenharmony_ci			return -ENODEV;
3288c2ecf20Sopenharmony_ci		}
3298c2ecf20Sopenharmony_ci		ops->callback(ops->data);	// init record
3308c2ecf20Sopenharmony_ci		rc = appldata_diag(ops->record_nr,
3318c2ecf20Sopenharmony_ci					APPLDATA_START_INTERVAL_REC,
3328c2ecf20Sopenharmony_ci					(unsigned long) ops->data, ops->size,
3338c2ecf20Sopenharmony_ci					ops->mod_lvl);
3348c2ecf20Sopenharmony_ci		if (rc != 0) {
3358c2ecf20Sopenharmony_ci			pr_err("Starting the data collection for %s "
3368c2ecf20Sopenharmony_ci			       "failed with rc=%d\n", ops->name, rc);
3378c2ecf20Sopenharmony_ci			module_put(ops->owner);
3388c2ecf20Sopenharmony_ci		} else
3398c2ecf20Sopenharmony_ci			ops->active = 1;
3408c2ecf20Sopenharmony_ci	} else if (!active && (ops->active == 1)) {
3418c2ecf20Sopenharmony_ci		ops->active = 0;
3428c2ecf20Sopenharmony_ci		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
3438c2ecf20Sopenharmony_ci				(unsigned long) ops->data, ops->size,
3448c2ecf20Sopenharmony_ci				ops->mod_lvl);
3458c2ecf20Sopenharmony_ci		if (rc != 0)
3468c2ecf20Sopenharmony_ci			pr_err("Stopping the data collection for %s "
3478c2ecf20Sopenharmony_ci			       "failed with rc=%d\n", ops->name, rc);
3488c2ecf20Sopenharmony_ci		module_put(ops->owner);
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
3518c2ecf20Sopenharmony_ci	module_put(ops->owner);
3528c2ecf20Sopenharmony_ci	return 0;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/*************************** /proc stuff <END> *******************************/
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci/************************* module-ops management *****************************/
3598c2ecf20Sopenharmony_ci/*
3608c2ecf20Sopenharmony_ci * appldata_register_ops()
3618c2ecf20Sopenharmony_ci *
3628c2ecf20Sopenharmony_ci * update ops list, register /proc/sys entries
3638c2ecf20Sopenharmony_ci */
3648c2ecf20Sopenharmony_ciint appldata_register_ops(struct appldata_ops *ops)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	if (ops->size > APPLDATA_MAX_REC_SIZE)
3678c2ecf20Sopenharmony_ci		return -EINVAL;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	ops->ctl_table = kcalloc(4, sizeof(struct ctl_table), GFP_KERNEL);
3708c2ecf20Sopenharmony_ci	if (!ops->ctl_table)
3718c2ecf20Sopenharmony_ci		return -ENOMEM;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
3748c2ecf20Sopenharmony_ci	list_add(&ops->list, &appldata_ops_list);
3758c2ecf20Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	ops->ctl_table[0].procname = appldata_proc_name;
3788c2ecf20Sopenharmony_ci	ops->ctl_table[0].maxlen   = 0;
3798c2ecf20Sopenharmony_ci	ops->ctl_table[0].mode     = S_IRUGO | S_IXUGO;
3808c2ecf20Sopenharmony_ci	ops->ctl_table[0].child    = &ops->ctl_table[2];
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	ops->ctl_table[2].procname = ops->name;
3838c2ecf20Sopenharmony_ci	ops->ctl_table[2].mode     = S_IRUGO | S_IWUSR;
3848c2ecf20Sopenharmony_ci	ops->ctl_table[2].proc_handler = appldata_generic_handler;
3858c2ecf20Sopenharmony_ci	ops->ctl_table[2].data = ops;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	ops->sysctl_header = register_sysctl_table(ops->ctl_table);
3888c2ecf20Sopenharmony_ci	if (!ops->sysctl_header)
3898c2ecf20Sopenharmony_ci		goto out;
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ciout:
3928c2ecf20Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
3938c2ecf20Sopenharmony_ci	list_del(&ops->list);
3948c2ecf20Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
3958c2ecf20Sopenharmony_ci	kfree(ops->ctl_table);
3968c2ecf20Sopenharmony_ci	return -ENOMEM;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci/*
4008c2ecf20Sopenharmony_ci * appldata_unregister_ops()
4018c2ecf20Sopenharmony_ci *
4028c2ecf20Sopenharmony_ci * update ops list, unregister /proc entries, stop DIAG if necessary
4038c2ecf20Sopenharmony_ci */
4048c2ecf20Sopenharmony_civoid appldata_unregister_ops(struct appldata_ops *ops)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
4078c2ecf20Sopenharmony_ci	list_del(&ops->list);
4088c2ecf20Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
4098c2ecf20Sopenharmony_ci	unregister_sysctl_table(ops->sysctl_header);
4108c2ecf20Sopenharmony_ci	kfree(ops->ctl_table);
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci/********************** module-ops management <END> **************************/
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci/**************************** suspend / resume *******************************/
4168c2ecf20Sopenharmony_cistatic int appldata_freeze(struct device *dev)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	struct appldata_ops *ops;
4198c2ecf20Sopenharmony_ci	int rc;
4208c2ecf20Sopenharmony_ci	struct list_head *lh;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	spin_lock(&appldata_timer_lock);
4238c2ecf20Sopenharmony_ci	if (appldata_timer_active) {
4248c2ecf20Sopenharmony_ci		__appldata_vtimer_setup(APPLDATA_DEL_TIMER);
4258c2ecf20Sopenharmony_ci		appldata_timer_suspended = 1;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci	spin_unlock(&appldata_timer_lock);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
4308c2ecf20Sopenharmony_ci	list_for_each(lh, &appldata_ops_list) {
4318c2ecf20Sopenharmony_ci		ops = list_entry(lh, struct appldata_ops, list);
4328c2ecf20Sopenharmony_ci		if (ops->active == 1) {
4338c2ecf20Sopenharmony_ci			rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
4348c2ecf20Sopenharmony_ci					(unsigned long) ops->data, ops->size,
4358c2ecf20Sopenharmony_ci					ops->mod_lvl);
4368c2ecf20Sopenharmony_ci			if (rc != 0)
4378c2ecf20Sopenharmony_ci				pr_err("Stopping the data collection for %s "
4388c2ecf20Sopenharmony_ci				       "failed with rc=%d\n", ops->name, rc);
4398c2ecf20Sopenharmony_ci		}
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
4428c2ecf20Sopenharmony_ci	return 0;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic int appldata_restore(struct device *dev)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	struct appldata_ops *ops;
4488c2ecf20Sopenharmony_ci	int rc;
4498c2ecf20Sopenharmony_ci	struct list_head *lh;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	spin_lock(&appldata_timer_lock);
4528c2ecf20Sopenharmony_ci	if (appldata_timer_suspended) {
4538c2ecf20Sopenharmony_ci		__appldata_vtimer_setup(APPLDATA_ADD_TIMER);
4548c2ecf20Sopenharmony_ci		appldata_timer_suspended = 0;
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci	spin_unlock(&appldata_timer_lock);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	mutex_lock(&appldata_ops_mutex);
4598c2ecf20Sopenharmony_ci	list_for_each(lh, &appldata_ops_list) {
4608c2ecf20Sopenharmony_ci		ops = list_entry(lh, struct appldata_ops, list);
4618c2ecf20Sopenharmony_ci		if (ops->active == 1) {
4628c2ecf20Sopenharmony_ci			ops->callback(ops->data);	// init record
4638c2ecf20Sopenharmony_ci			rc = appldata_diag(ops->record_nr,
4648c2ecf20Sopenharmony_ci					APPLDATA_START_INTERVAL_REC,
4658c2ecf20Sopenharmony_ci					(unsigned long) ops->data, ops->size,
4668c2ecf20Sopenharmony_ci					ops->mod_lvl);
4678c2ecf20Sopenharmony_ci			if (rc != 0) {
4688c2ecf20Sopenharmony_ci				pr_err("Starting the data collection for %s "
4698c2ecf20Sopenharmony_ci				       "failed with rc=%d\n", ops->name, rc);
4708c2ecf20Sopenharmony_ci			}
4718c2ecf20Sopenharmony_ci		}
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci	mutex_unlock(&appldata_ops_mutex);
4748c2ecf20Sopenharmony_ci	return 0;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic int appldata_thaw(struct device *dev)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	return appldata_restore(dev);
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic const struct dev_pm_ops appldata_pm_ops = {
4838c2ecf20Sopenharmony_ci	.freeze		= appldata_freeze,
4848c2ecf20Sopenharmony_ci	.thaw		= appldata_thaw,
4858c2ecf20Sopenharmony_ci	.restore	= appldata_restore,
4868c2ecf20Sopenharmony_ci};
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic struct platform_driver appldata_pdrv = {
4898c2ecf20Sopenharmony_ci	.driver = {
4908c2ecf20Sopenharmony_ci		.name	= "appldata",
4918c2ecf20Sopenharmony_ci		.pm	= &appldata_pm_ops,
4928c2ecf20Sopenharmony_ci	},
4938c2ecf20Sopenharmony_ci};
4948c2ecf20Sopenharmony_ci/************************* suspend / resume <END> ****************************/
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci/******************************* init / exit *********************************/
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci/*
5008c2ecf20Sopenharmony_ci * appldata_init()
5018c2ecf20Sopenharmony_ci *
5028c2ecf20Sopenharmony_ci * init timer, register /proc entries
5038c2ecf20Sopenharmony_ci */
5048c2ecf20Sopenharmony_cistatic int __init appldata_init(void)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	int rc;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	init_virt_timer(&appldata_timer);
5098c2ecf20Sopenharmony_ci	appldata_timer.function = appldata_timer_function;
5108c2ecf20Sopenharmony_ci	appldata_timer.data = (unsigned long) &appldata_work;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	rc = platform_driver_register(&appldata_pdrv);
5138c2ecf20Sopenharmony_ci	if (rc)
5148c2ecf20Sopenharmony_ci		return rc;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	appldata_pdev = platform_device_register_simple("appldata", -1, NULL,
5178c2ecf20Sopenharmony_ci							0);
5188c2ecf20Sopenharmony_ci	if (IS_ERR(appldata_pdev)) {
5198c2ecf20Sopenharmony_ci		rc = PTR_ERR(appldata_pdev);
5208c2ecf20Sopenharmony_ci		goto out_driver;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci	appldata_wq = alloc_ordered_workqueue("appldata", 0);
5238c2ecf20Sopenharmony_ci	if (!appldata_wq) {
5248c2ecf20Sopenharmony_ci		rc = -ENOMEM;
5258c2ecf20Sopenharmony_ci		goto out_device;
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	appldata_sysctl_header = register_sysctl_table(appldata_dir_table);
5298c2ecf20Sopenharmony_ci	return 0;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ciout_device:
5328c2ecf20Sopenharmony_ci	platform_device_unregister(appldata_pdev);
5338c2ecf20Sopenharmony_ciout_driver:
5348c2ecf20Sopenharmony_ci	platform_driver_unregister(&appldata_pdrv);
5358c2ecf20Sopenharmony_ci	return rc;
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci__initcall(appldata_init);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci/**************************** init / exit <END> ******************************/
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(appldata_register_ops);
5438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(appldata_unregister_ops);
5448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(appldata_diag);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci#ifdef CONFIG_SWAP
5478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si_swapinfo);
5488c2ecf20Sopenharmony_ci#endif
5498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nr_threads);
5508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nr_running);
5518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nr_iowait);
552