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