xref: /openbmc/linux/arch/s390/appldata/appldata_base.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
10caa8cdfSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.
41da177e4SLinus Torvalds  * Exports appldata_register_ops() and appldata_unregister_ops() for the
51da177e4SLinus Torvalds  * data gathering modules.
61da177e4SLinus Torvalds  *
7524dbcdaSGerald Schaefer  * Copyright IBM Corp. 2003, 2009
81da177e4SLinus Torvalds  *
95b5dd21aSGerald Schaefer  * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
12e7534b0eSGerald Schaefer #define KMSG_COMPONENT	"appldata"
13e7534b0eSGerald Schaefer #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
14e7534b0eSGerald Schaefer 
151da177e4SLinus Torvalds #include <linux/module.h>
163605df49SIngo Molnar #include <linux/sched/stat.h>
171da177e4SLinus Torvalds #include <linux/init.h>
181da177e4SLinus Torvalds #include <linux/slab.h>
191da177e4SLinus Torvalds #include <linux/errno.h>
201da177e4SLinus Torvalds #include <linux/interrupt.h>
211da177e4SLinus Torvalds #include <linux/proc_fs.h>
222dcea57aSHeiko Carstens #include <linux/mm.h>
231da177e4SLinus Torvalds #include <linux/swap.h>
241da177e4SLinus Torvalds #include <linux/pagemap.h>
251da177e4SLinus Torvalds #include <linux/sysctl.h>
261da177e4SLinus Torvalds #include <linux/notifier.h>
271da177e4SLinus Torvalds #include <linux/cpu.h>
28f26d583eSGerald Schaefer #include <linux/workqueue.h>
29b378a982SHeiko Carstens #include <linux/uaccess.h>
30b378a982SHeiko Carstens #include <linux/io.h>
311f38d613SGerald Schaefer #include <asm/appldata.h>
3227f6b416SMartin Schwidefsky #include <asm/vtimer.h>
331f38d613SGerald Schaefer #include <asm/smp.h>
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds #include "appldata.h"
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds #define APPLDATA_CPU_INTERVAL	10000		/* default (CPU) time for
391da177e4SLinus Torvalds 						   sampling interval in
401da177e4SLinus Torvalds 						   milliseconds */
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds #define TOD_MICRO	0x01000			/* nr. of TOD clock units
431da177e4SLinus Torvalds 						   for 1 microsecond */
44524dbcdaSGerald Schaefer 
451da177e4SLinus Torvalds /*
461da177e4SLinus Torvalds  * /proc entries (sysctl)
471da177e4SLinus Torvalds  */
481da177e4SLinus Torvalds static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
49302bfe20SJoe Perches static int appldata_timer_handler(struct ctl_table *ctl, int write,
5032927393SChristoph Hellwig 				  void *buffer, size_t *lenp, loff_t *ppos);
51302bfe20SJoe Perches static int appldata_interval_handler(struct ctl_table *ctl, int write,
5232927393SChristoph Hellwig 				     void *buffer, size_t *lenp, loff_t *ppos);
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds static struct ctl_table_header *appldata_sysctl_header;
551da177e4SLinus Torvalds static struct ctl_table appldata_table[] = {
561da177e4SLinus Torvalds 	{
571da177e4SLinus Torvalds 		.procname	= "timer",
581da177e4SLinus Torvalds 		.mode		= S_IRUGO | S_IWUSR,
596d456111SEric W. Biederman 		.proc_handler	= appldata_timer_handler,
601da177e4SLinus Torvalds 	},
611da177e4SLinus Torvalds 	{
621da177e4SLinus Torvalds 		.procname	= "interval",
631da177e4SLinus Torvalds 		.mode		= S_IRUGO | S_IWUSR,
646d456111SEric W. Biederman 		.proc_handler	= appldata_interval_handler,
651da177e4SLinus Torvalds 	},
6637e3a6acSHeiko Carstens 	{ },
671da177e4SLinus Torvalds };
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds /*
701da177e4SLinus Torvalds  * Timer
711da177e4SLinus Torvalds  */
7227f6b416SMartin Schwidefsky static struct vtimer_list appldata_timer;
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds static DEFINE_SPINLOCK(appldata_timer_lock);
751da177e4SLinus Torvalds static int appldata_interval = APPLDATA_CPU_INTERVAL;
761da177e4SLinus Torvalds static int appldata_timer_active;
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds /*
79f26d583eSGerald Schaefer  * Work queue
801da177e4SLinus Torvalds  */
81f26d583eSGerald Schaefer static struct workqueue_struct *appldata_wq;
826d5aefb8SDavid Howells static void appldata_work_fn(struct work_struct *work);
836d5aefb8SDavid Howells static DECLARE_WORK(appldata_work, appldata_work_fn);
84f26d583eSGerald Schaefer 
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds /*
871da177e4SLinus Torvalds  * Ops list
881da177e4SLinus Torvalds  */
89b1ad171eSGerald Schaefer static DEFINE_MUTEX(appldata_ops_mutex);
901da177e4SLinus Torvalds static LIST_HEAD(appldata_ops_list);
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds 
93f26d583eSGerald Schaefer /*************************** timer, work, DIAG *******************************/
941da177e4SLinus Torvalds /*
951da177e4SLinus Torvalds  * appldata_timer_function()
961da177e4SLinus Torvalds  *
97f26d583eSGerald Schaefer  * schedule work and reschedule timer
981da177e4SLinus Torvalds  */
appldata_timer_function(unsigned long data)999d0a57cbSHeiko Carstens static void appldata_timer_function(unsigned long data)
1001da177e4SLinus Torvalds {
101f26d583eSGerald Schaefer 	queue_work(appldata_wq, (struct work_struct *) data);
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds /*
105f26d583eSGerald Schaefer  * appldata_work_fn()
1061da177e4SLinus Torvalds  *
1071da177e4SLinus Torvalds  * call data gathering function for each (active) module
1081da177e4SLinus Torvalds  */
appldata_work_fn(struct work_struct * work)1096d5aefb8SDavid Howells static void appldata_work_fn(struct work_struct *work)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds 	struct list_head *lh;
1121da177e4SLinus Torvalds 	struct appldata_ops *ops;
1131da177e4SLinus Torvalds 
114b1ad171eSGerald Schaefer 	mutex_lock(&appldata_ops_mutex);
1151da177e4SLinus Torvalds 	list_for_each(lh, &appldata_ops_list) {
1161da177e4SLinus Torvalds 		ops = list_entry(lh, struct appldata_ops, list);
1171da177e4SLinus Torvalds 		if (ops->active == 1) {
1181da177e4SLinus Torvalds 			ops->callback(ops->data);
1191da177e4SLinus Torvalds 		}
1201da177e4SLinus Torvalds 	}
121b1ad171eSGerald Schaefer 	mutex_unlock(&appldata_ops_mutex);
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds 
124d36a9281SMartin Schwidefsky static struct appldata_product_id appldata_id = {
125d36a9281SMartin Schwidefsky 	.prod_nr    = {0xD3, 0xC9, 0xD5, 0xE4,
126d36a9281SMartin Schwidefsky 		       0xE7, 0xD2, 0xD9},	/* "LINUXKR" */
127d36a9281SMartin Schwidefsky 	.prod_fn    = 0xD5D3,			/* "NL" */
128d36a9281SMartin Schwidefsky 	.version_nr = 0xF2F6,			/* "26" */
129d36a9281SMartin Schwidefsky 	.release_nr = 0xF0F1,			/* "01" */
130d36a9281SMartin Schwidefsky };
131d36a9281SMartin Schwidefsky 
1321da177e4SLinus Torvalds /*
1331da177e4SLinus Torvalds  * appldata_diag()
1341da177e4SLinus Torvalds  *
1351da177e4SLinus Torvalds  * prepare parameter list, issue DIAG 0xDC
1361da177e4SLinus Torvalds  */
appldata_diag(char record_nr,u16 function,unsigned long buffer,u16 length,char * mod_lvl)1375b5dd21aSGerald Schaefer int appldata_diag(char record_nr, u16 function, unsigned long buffer,
1385b5dd21aSGerald Schaefer 			u16 length, char *mod_lvl)
1391da177e4SLinus Torvalds {
140d36a9281SMartin Schwidefsky 	struct appldata_parameter_list *parm_list;
141d36a9281SMartin Schwidefsky 	struct appldata_product_id *id;
142d36a9281SMartin Schwidefsky 	int rc;
1431da177e4SLinus Torvalds 
144d36a9281SMartin Schwidefsky 	parm_list = kmalloc(sizeof(*parm_list), GFP_KERNEL);
145d36a9281SMartin Schwidefsky 	id = kmemdup(&appldata_id, sizeof(appldata_id), GFP_KERNEL);
146d36a9281SMartin Schwidefsky 	rc = -ENOMEM;
147d36a9281SMartin Schwidefsky 	if (parm_list && id) {
148d36a9281SMartin Schwidefsky 		id->record_nr = record_nr;
149d36a9281SMartin Schwidefsky 		id->mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1];
150d36a9281SMartin Schwidefsky 		rc = appldata_asm(parm_list, id, function,
151f689789aSMartin Schwidefsky 				  (void *) buffer, length);
1521da177e4SLinus Torvalds 	}
153d36a9281SMartin Schwidefsky 	kfree(id);
154d36a9281SMartin Schwidefsky 	kfree(parm_list);
155d36a9281SMartin Schwidefsky 	return rc;
156d36a9281SMartin Schwidefsky }
157f26d583eSGerald Schaefer /************************ timer, work, DIAG <END> ****************************/
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds /****************************** /proc stuff **********************************/
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds #define APPLDATA_ADD_TIMER	0
1631da177e4SLinus Torvalds #define APPLDATA_DEL_TIMER	1
1641da177e4SLinus Torvalds #define APPLDATA_MOD_TIMER	2
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds /*
1671da177e4SLinus Torvalds  * __appldata_vtimer_setup()
1681da177e4SLinus Torvalds  *
1691da177e4SLinus Torvalds  * Add, delete or modify virtual timers on all online cpus.
1701da177e4SLinus Torvalds  * The caller needs to get the appldata_timer_lock spinlock.
1711da177e4SLinus Torvalds  */
__appldata_vtimer_setup(int cmd)17227f6b416SMartin Schwidefsky static void __appldata_vtimer_setup(int cmd)
1731da177e4SLinus Torvalds {
17427f6b416SMartin Schwidefsky 	u64 timer_interval = (u64) appldata_interval * 1000 * TOD_MICRO;
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	switch (cmd) {
1771da177e4SLinus Torvalds 	case APPLDATA_ADD_TIMER:
1781da177e4SLinus Torvalds 		if (appldata_timer_active)
1791da177e4SLinus Torvalds 			break;
18027f6b416SMartin Schwidefsky 		appldata_timer.expires = timer_interval;
18127f6b416SMartin Schwidefsky 		add_virt_timer_periodic(&appldata_timer);
1821da177e4SLinus Torvalds 		appldata_timer_active = 1;
1831da177e4SLinus Torvalds 		break;
1841da177e4SLinus Torvalds 	case APPLDATA_DEL_TIMER:
18527f6b416SMartin Schwidefsky 		del_virt_timer(&appldata_timer);
1861da177e4SLinus Torvalds 		if (!appldata_timer_active)
1871da177e4SLinus Torvalds 			break;
1881da177e4SLinus Torvalds 		appldata_timer_active = 0;
1891da177e4SLinus Torvalds 		break;
1901da177e4SLinus Torvalds 	case APPLDATA_MOD_TIMER:
1911da177e4SLinus Torvalds 		if (!appldata_timer_active)
1921da177e4SLinus Torvalds 			break;
19327f6b416SMartin Schwidefsky 		mod_virt_timer_periodic(&appldata_timer, timer_interval);
1941da177e4SLinus Torvalds 	}
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds /*
1981da177e4SLinus Torvalds  * appldata_timer_handler()
1991da177e4SLinus Torvalds  *
2001da177e4SLinus Torvalds  * Start/Stop timer, show status of timer (0 = not active, 1 = active)
2011da177e4SLinus Torvalds  */
2021da177e4SLinus Torvalds static int
appldata_timer_handler(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)203302bfe20SJoe Perches appldata_timer_handler(struct ctl_table *ctl, int write,
20432927393SChristoph Hellwig 			   void *buffer, size_t *lenp, loff_t *ppos)
2051da177e4SLinus Torvalds {
2066c802150SVasily Gorbik 	int timer_active = appldata_timer_active;
2076c802150SVasily Gorbik 	int rc;
2086c802150SVasily Gorbik 	struct ctl_table ctl_entry = {
2096c802150SVasily Gorbik 		.procname	= ctl->procname,
2106c802150SVasily Gorbik 		.data		= &timer_active,
2116c802150SVasily Gorbik 		.maxlen		= sizeof(int),
212eec4844fSMatteo Croce 		.extra1		= SYSCTL_ZERO,
213eec4844fSMatteo Croce 		.extra2		= SYSCTL_ONE,
2146c802150SVasily Gorbik 	};
2151da177e4SLinus Torvalds 
2166c802150SVasily Gorbik 	rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
2176c802150SVasily Gorbik 	if (rc < 0 || !write)
2186c802150SVasily Gorbik 		return rc;
2196c802150SVasily Gorbik 
2201da177e4SLinus Torvalds 	spin_lock(&appldata_timer_lock);
2216c802150SVasily Gorbik 	if (timer_active)
2221da177e4SLinus Torvalds 		__appldata_vtimer_setup(APPLDATA_ADD_TIMER);
2236c802150SVasily Gorbik 	else
2241da177e4SLinus Torvalds 		__appldata_vtimer_setup(APPLDATA_DEL_TIMER);
2251da177e4SLinus Torvalds 	spin_unlock(&appldata_timer_lock);
2261da177e4SLinus Torvalds 	return 0;
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds /*
2301da177e4SLinus Torvalds  * appldata_interval_handler()
2311da177e4SLinus Torvalds  *
2321da177e4SLinus Torvalds  * Set (CPU) timer interval for collection of data (in milliseconds), show
2331da177e4SLinus Torvalds  * current timer interval.
2341da177e4SLinus Torvalds  */
2351da177e4SLinus Torvalds static int
appldata_interval_handler(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)236302bfe20SJoe Perches appldata_interval_handler(struct ctl_table *ctl, int write,
23732927393SChristoph Hellwig 			   void *buffer, size_t *lenp, loff_t *ppos)
2381da177e4SLinus Torvalds {
2396c802150SVasily Gorbik 	int interval = appldata_interval;
2406c802150SVasily Gorbik 	int rc;
2416c802150SVasily Gorbik 	struct ctl_table ctl_entry = {
2426c802150SVasily Gorbik 		.procname	= ctl->procname,
2436c802150SVasily Gorbik 		.data		= &interval,
2446c802150SVasily Gorbik 		.maxlen		= sizeof(int),
245eec4844fSMatteo Croce 		.extra1		= SYSCTL_ONE,
2466c802150SVasily Gorbik 	};
2471da177e4SLinus Torvalds 
2486c802150SVasily Gorbik 	rc = proc_dointvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
2496c802150SVasily Gorbik 	if (rc < 0 || !write)
2506c802150SVasily Gorbik 		return rc;
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	spin_lock(&appldata_timer_lock);
2531da177e4SLinus Torvalds 	appldata_interval = interval;
2541da177e4SLinus Torvalds 	__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
2551da177e4SLinus Torvalds 	spin_unlock(&appldata_timer_lock);
2561da177e4SLinus Torvalds 	return 0;
2571da177e4SLinus Torvalds }
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds /*
2601da177e4SLinus Torvalds  * appldata_generic_handler()
2611da177e4SLinus Torvalds  *
2621da177e4SLinus Torvalds  * Generic start/stop monitoring and DIAG, show status of
2631da177e4SLinus Torvalds  * monitoring (0 = not in process, 1 = in process)
2641da177e4SLinus Torvalds  */
2651da177e4SLinus Torvalds static int
appldata_generic_handler(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)266302bfe20SJoe Perches appldata_generic_handler(struct ctl_table *ctl, int write,
26732927393SChristoph Hellwig 			   void *buffer, size_t *lenp, loff_t *ppos)
2681da177e4SLinus Torvalds {
2691da177e4SLinus Torvalds 	struct appldata_ops *ops = NULL, *tmp_ops;
2701da177e4SLinus Torvalds 	struct list_head *lh;
2716c802150SVasily Gorbik 	int rc, found;
2726c802150SVasily Gorbik 	int active;
2736c802150SVasily Gorbik 	struct ctl_table ctl_entry = {
2746c802150SVasily Gorbik 		.data		= &active,
2756c802150SVasily Gorbik 		.maxlen		= sizeof(int),
276eec4844fSMatteo Croce 		.extra1		= SYSCTL_ZERO,
277eec4844fSMatteo Croce 		.extra2		= SYSCTL_ONE,
2786c802150SVasily Gorbik 	};
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	found = 0;
281b1ad171eSGerald Schaefer 	mutex_lock(&appldata_ops_mutex);
2821da177e4SLinus Torvalds 	list_for_each(lh, &appldata_ops_list) {
2831da177e4SLinus Torvalds 		tmp_ops = list_entry(lh, struct appldata_ops, list);
2847db12246SLuis Chamberlain 		if (&tmp_ops->ctl_table[0] == ctl) {
2851da177e4SLinus Torvalds 			found = 1;
2861da177e4SLinus Torvalds 		}
2871da177e4SLinus Torvalds 	}
2881da177e4SLinus Torvalds 	if (!found) {
289b1ad171eSGerald Schaefer 		mutex_unlock(&appldata_ops_mutex);
2901da177e4SLinus Torvalds 		return -ENODEV;
2911da177e4SLinus Torvalds 	}
2921da177e4SLinus Torvalds 	ops = ctl->data;
2931da177e4SLinus Torvalds 	if (!try_module_get(ops->owner)) {	// protect this function
294b1ad171eSGerald Schaefer 		mutex_unlock(&appldata_ops_mutex);
2951da177e4SLinus Torvalds 		return -ENODEV;
2961da177e4SLinus Torvalds 	}
297b1ad171eSGerald Schaefer 	mutex_unlock(&appldata_ops_mutex);
2981da177e4SLinus Torvalds 
2996c802150SVasily Gorbik 	active = ops->active;
3006c802150SVasily Gorbik 	rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
3016c802150SVasily Gorbik 	if (rc < 0 || !write) {
3021da177e4SLinus Torvalds 		module_put(ops->owner);
3036c802150SVasily Gorbik 		return rc;
3041da177e4SLinus Torvalds 	}
3051da177e4SLinus Torvalds 
306b1ad171eSGerald Schaefer 	mutex_lock(&appldata_ops_mutex);
3076c802150SVasily Gorbik 	if (active && (ops->active == 0)) {
308f26d583eSGerald Schaefer 		// protect work queue callback
309f26d583eSGerald Schaefer 		if (!try_module_get(ops->owner)) {
310b1ad171eSGerald Schaefer 			mutex_unlock(&appldata_ops_mutex);
3111da177e4SLinus Torvalds 			module_put(ops->owner);
3121da177e4SLinus Torvalds 			return -ENODEV;
3131da177e4SLinus Torvalds 		}
3141da177e4SLinus Torvalds 		ops->callback(ops->data);	// init record
3151da177e4SLinus Torvalds 		rc = appldata_diag(ops->record_nr,
3161da177e4SLinus Torvalds 					APPLDATA_START_INTERVAL_REC,
3175b5dd21aSGerald Schaefer 					(unsigned long) ops->data, ops->size,
3185b5dd21aSGerald Schaefer 					ops->mod_lvl);
3191da177e4SLinus Torvalds 		if (rc != 0) {
320e7534b0eSGerald Schaefer 			pr_err("Starting the data collection for %s "
321e7534b0eSGerald Schaefer 			       "failed with rc=%d\n", ops->name, rc);
3221da177e4SLinus Torvalds 			module_put(ops->owner);
323d3ae942dSGerald Schaefer 		} else
3245b5dd21aSGerald Schaefer 			ops->active = 1;
3256c802150SVasily Gorbik 	} else if (!active && (ops->active == 1)) {
3261da177e4SLinus Torvalds 		ops->active = 0;
3271da177e4SLinus Torvalds 		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
3285b5dd21aSGerald Schaefer 				(unsigned long) ops->data, ops->size,
3295b5dd21aSGerald Schaefer 				ops->mod_lvl);
330d3ae942dSGerald Schaefer 		if (rc != 0)
331e7534b0eSGerald Schaefer 			pr_err("Stopping the data collection for %s "
332e7534b0eSGerald Schaefer 			       "failed with rc=%d\n", ops->name, rc);
3331da177e4SLinus Torvalds 		module_put(ops->owner);
3341da177e4SLinus Torvalds 	}
335b1ad171eSGerald Schaefer 	mutex_unlock(&appldata_ops_mutex);
3361da177e4SLinus Torvalds 	module_put(ops->owner);
3371da177e4SLinus Torvalds 	return 0;
3381da177e4SLinus Torvalds }
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds /*************************** /proc stuff <END> *******************************/
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds /************************* module-ops management *****************************/
3441da177e4SLinus Torvalds /*
3451da177e4SLinus Torvalds  * appldata_register_ops()
3461da177e4SLinus Torvalds  *
3471da177e4SLinus Torvalds  * update ops list, register /proc/sys entries
3481da177e4SLinus Torvalds  */
appldata_register_ops(struct appldata_ops * ops)3491da177e4SLinus Torvalds int appldata_register_ops(struct appldata_ops *ops)
3501da177e4SLinus Torvalds {
35113f8b7c5SRoel Kluin 	if (ops->size > APPLDATA_MAX_REC_SIZE)
35237e3a6acSHeiko Carstens 		return -EINVAL;
3531da177e4SLinus Torvalds 
3547db12246SLuis Chamberlain 	/* The last entry must be an empty one */
3557db12246SLuis Chamberlain 	ops->ctl_table = kcalloc(2, sizeof(struct ctl_table), GFP_KERNEL);
35637e3a6acSHeiko Carstens 	if (!ops->ctl_table)
3571da177e4SLinus Torvalds 		return -ENOMEM;
3581da177e4SLinus Torvalds 
359b1ad171eSGerald Schaefer 	mutex_lock(&appldata_ops_mutex);
3601da177e4SLinus Torvalds 	list_add(&ops->list, &appldata_ops_list);
361b1ad171eSGerald Schaefer 	mutex_unlock(&appldata_ops_mutex);
3621da177e4SLinus Torvalds 
3637db12246SLuis Chamberlain 	ops->ctl_table[0].procname = ops->name;
3647db12246SLuis Chamberlain 	ops->ctl_table[0].mode = S_IRUGO | S_IWUSR;
3657db12246SLuis Chamberlain 	ops->ctl_table[0].proc_handler = appldata_generic_handler;
3667db12246SLuis Chamberlain 	ops->ctl_table[0].data = ops;
3671da177e4SLinus Torvalds 
368*9edbfe92SJoel Granados 	ops->sysctl_header = register_sysctl_sz(appldata_proc_name, ops->ctl_table, 1);
36937e3a6acSHeiko Carstens 	if (!ops->sysctl_header)
37037e3a6acSHeiko Carstens 		goto out;
3711da177e4SLinus Torvalds 	return 0;
37237e3a6acSHeiko Carstens out:
373b1ad171eSGerald Schaefer 	mutex_lock(&appldata_ops_mutex);
37437e3a6acSHeiko Carstens 	list_del(&ops->list);
375b1ad171eSGerald Schaefer 	mutex_unlock(&appldata_ops_mutex);
37637e3a6acSHeiko Carstens 	kfree(ops->ctl_table);
37737e3a6acSHeiko Carstens 	return -ENOMEM;
3781da177e4SLinus Torvalds }
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds /*
3811da177e4SLinus Torvalds  * appldata_unregister_ops()
3821da177e4SLinus Torvalds  *
3831da177e4SLinus Torvalds  * update ops list, unregister /proc entries, stop DIAG if necessary
3841da177e4SLinus Torvalds  */
appldata_unregister_ops(struct appldata_ops * ops)3851da177e4SLinus Torvalds void appldata_unregister_ops(struct appldata_ops *ops)
3861da177e4SLinus Torvalds {
387b1ad171eSGerald Schaefer 	mutex_lock(&appldata_ops_mutex);
3881da177e4SLinus Torvalds 	list_del(&ops->list);
389b1ad171eSGerald Schaefer 	mutex_unlock(&appldata_ops_mutex);
390330d57fbSAl Viro 	unregister_sysctl_table(ops->sysctl_header);
39137e3a6acSHeiko Carstens 	kfree(ops->ctl_table);
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds /********************** module-ops management <END> **************************/
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds /******************************* init / exit *********************************/
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds /*
3991da177e4SLinus Torvalds  * appldata_init()
4001da177e4SLinus Torvalds  *
401f26d583eSGerald Schaefer  * init timer, register /proc entries
4021da177e4SLinus Torvalds  */
appldata_init(void)4031da177e4SLinus Torvalds static int __init appldata_init(void)
4041da177e4SLinus Torvalds {
405b7c5b1aaSGerald Schaefer 	init_virt_timer(&appldata_timer);
40627f6b416SMartin Schwidefsky 	appldata_timer.function = appldata_timer_function;
40727f6b416SMartin Schwidefsky 	appldata_timer.data = (unsigned long) &appldata_work;
408e68f1d4cSBhaktipriya Shridhar 	appldata_wq = alloc_ordered_workqueue("appldata", 0);
40978c045c0SHeiko Carstens 	if (!appldata_wq)
41078c045c0SHeiko Carstens 		return -ENOMEM;
41171cb8c00SLuis Chamberlain 	appldata_sysctl_header = register_sysctl(appldata_proc_name, appldata_table);
4121da177e4SLinus Torvalds 	return 0;
4131da177e4SLinus Torvalds }
4141da177e4SLinus Torvalds 
415076fc808SSatyam Sharma __initcall(appldata_init);
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds /**************************** init / exit <END> ******************************/
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(appldata_register_ops);
4201da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(appldata_unregister_ops);
4215b5dd21aSGerald Schaefer EXPORT_SYMBOL_GPL(appldata_diag);
4221da177e4SLinus Torvalds 
4230c3252d5SGerald Schaefer #ifdef CONFIG_SWAP
4241da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(si_swapinfo);
4250c3252d5SGerald Schaefer #endif
4261da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(nr_threads);
4271da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(nr_running);
4281da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(nr_iowait);
429