blob: 24bff4f1cc52ba906a9a6432af6f4e1ebf16c818 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * arch/s390/appldata/appldata_base.c
3 *
4 * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.
5 * Exports appldata_register_ops() and appldata_unregister_ops() for the
6 * data gathering modules.
7 *
Gerald Schaefer524dbcd2009-06-16 10:30:36 +02008 * Copyright IBM Corp. 2003, 2009
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 *
Gerald Schaefer5b5dd212006-06-29 15:08:35 +020010 * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 */
12
Gerald Schaefere7534b02008-12-25 13:39:41 +010013#define KMSG_COMPONENT "appldata"
14#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
15
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/module.h>
17#include <linux/init.h>
18#include <linux/slab.h>
19#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <linux/interrupt.h>
21#include <linux/proc_fs.h>
Heiko Carstens2dcea572006-09-29 01:58:41 -070022#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/swap.h>
24#include <linux/pagemap.h>
25#include <linux/sysctl.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <linux/notifier.h>
27#include <linux/cpu.h>
Gerald Schaeferf26d5832005-06-04 15:43:33 -070028#include <linux/workqueue.h>
Gerald Schaefer524dbcd2009-06-16 10:30:36 +020029#include <linux/suspend.h>
30#include <linux/platform_device.h>
Gerald Schaefer1f38d612006-09-20 15:59:26 +020031#include <asm/appldata.h>
32#include <asm/timer.h>
33#include <asm/uaccess.h>
34#include <asm/io.h>
35#include <asm/smp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
37#include "appldata.h"
38
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#define APPLDATA_CPU_INTERVAL 10000 /* default (CPU) time for
41 sampling interval in
42 milliseconds */
43
44#define TOD_MICRO 0x01000 /* nr. of TOD clock units
45 for 1 microsecond */
Gerald Schaefer524dbcd2009-06-16 10:30:36 +020046
47static struct platform_device *appldata_pdev;
48
Linus Torvalds1da177e2005-04-16 15:20:36 -070049/*
50 * /proc entries (sysctl)
51 */
52static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
Alexey Dobriyan8d65af72009-09-23 15:57:19 -070053static int appldata_timer_handler(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 void __user *buffer, size_t *lenp, loff_t *ppos);
55static int appldata_interval_handler(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 void __user *buffer,
57 size_t *lenp, loff_t *ppos);
58
59static struct ctl_table_header *appldata_sysctl_header;
60static struct ctl_table appldata_table[] = {
61 {
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 .procname = "timer",
63 .mode = S_IRUGO | S_IWUSR,
Eric W. Biederman6d456112009-11-16 03:11:48 -080064 .proc_handler = appldata_timer_handler,
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 },
66 {
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 .procname = "interval",
68 .mode = S_IRUGO | S_IWUSR,
Eric W. Biederman6d456112009-11-16 03:11:48 -080069 .proc_handler = appldata_interval_handler,
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 },
Heiko Carstens37e3a6a2007-11-20 11:13:34 +010071 { },
Linus Torvalds1da177e2005-04-16 15:20:36 -070072};
73
74static struct ctl_table appldata_dir_table[] = {
75 {
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 .procname = appldata_proc_name,
77 .maxlen = 0,
78 .mode = S_IRUGO | S_IXUGO,
79 .child = appldata_table,
80 },
Heiko Carstens37e3a6a2007-11-20 11:13:34 +010081 { },
Linus Torvalds1da177e2005-04-16 15:20:36 -070082};
83
84/*
85 * Timer
86 */
Heiko Carstens2b67fc42007-02-05 21:16:47 +010087static DEFINE_PER_CPU(struct vtimer_list, appldata_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088static atomic_t appldata_expire_count = ATOMIC_INIT(0);
89
90static DEFINE_SPINLOCK(appldata_timer_lock);
91static int appldata_interval = APPLDATA_CPU_INTERVAL;
92static int appldata_timer_active;
Gerald Schaefer524dbcd2009-06-16 10:30:36 +020093static int appldata_timer_suspended = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094
95/*
Gerald Schaeferf26d5832005-06-04 15:43:33 -070096 * Work queue
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 */
Gerald Schaeferf26d5832005-06-04 15:43:33 -070098static struct workqueue_struct *appldata_wq;
David Howells6d5aefb2006-12-05 19:36:26 +000099static void appldata_work_fn(struct work_struct *work);
100static DECLARE_WORK(appldata_work, appldata_work_fn);
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
103/*
104 * Ops list
105 */
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200106static DEFINE_MUTEX(appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107static LIST_HEAD(appldata_ops_list);
108
109
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700110/*************************** timer, work, DIAG *******************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111/*
112 * appldata_timer_function()
113 *
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700114 * schedule work and reschedule timer
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 */
Heiko Carstens9d0a57c2006-10-11 15:31:26 +0200116static void appldata_timer_function(unsigned long data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 if (atomic_dec_and_test(&appldata_expire_count)) {
119 atomic_set(&appldata_expire_count, num_online_cpus());
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700120 queue_work(appldata_wq, (struct work_struct *) data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 }
122}
123
124/*
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700125 * appldata_work_fn()
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 *
127 * call data gathering function for each (active) module
128 */
David Howells6d5aefb2006-12-05 19:36:26 +0000129static void appldata_work_fn(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130{
131 struct list_head *lh;
132 struct appldata_ops *ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133
Gerald Schaefer17605372008-05-30 10:03:28 +0200134 get_online_cpus();
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200135 mutex_lock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 list_for_each(lh, &appldata_ops_list) {
137 ops = list_entry(lh, struct appldata_ops, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 if (ops->active == 1) {
139 ops->callback(ops->data);
140 }
141 }
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200142 mutex_unlock(&appldata_ops_mutex);
Gerald Schaefer17605372008-05-30 10:03:28 +0200143 put_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144}
145
146/*
147 * appldata_diag()
148 *
149 * prepare parameter list, issue DIAG 0xDC
150 */
Gerald Schaefer5b5dd212006-06-29 15:08:35 +0200151int appldata_diag(char record_nr, u16 function, unsigned long buffer,
152 u16 length, char *mod_lvl)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153{
Gerald Schaefer1f38d612006-09-20 15:59:26 +0200154 struct appldata_product_id id = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 .prod_nr = {0xD3, 0xC9, 0xD5, 0xE4,
Gerald Schaefer1f38d612006-09-20 15:59:26 +0200156 0xE7, 0xD2, 0xD9}, /* "LINUXKR" */
157 .prod_fn = 0xD5D3, /* "NL" */
Gerald Schaefer1f38d612006-09-20 15:59:26 +0200158 .version_nr = 0xF2F6, /* "26" */
159 .release_nr = 0xF0F1, /* "01" */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 };
161
Gerald Schaefer925afbd2006-09-28 16:55:23 +0200162 id.record_nr = record_nr;
163 id.mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1];
Gerald Schaefer1f38d612006-09-20 15:59:26 +0200164 return appldata_asm(&id, function, (void *) buffer, length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165}
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700166/************************ timer, work, DIAG <END> ****************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167
168
169/****************************** /proc stuff **********************************/
170
171/*
172 * appldata_mod_vtimer_wrap()
173 *
Heiko Carstens3bb447f2007-07-27 12:29:08 +0200174 * wrapper function for mod_virt_timer(), because smp_call_function_single()
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 * accepts only one parameter.
176 */
177static void __appldata_mod_vtimer_wrap(void *p) {
178 struct {
179 struct vtimer_list *timer;
180 u64 expires;
181 } *args = p;
Gerald Schaefer43ae8a12009-04-14 15:36:21 +0200182 mod_virt_timer_periodic(args->timer, args->expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183}
184
185#define APPLDATA_ADD_TIMER 0
186#define APPLDATA_DEL_TIMER 1
187#define APPLDATA_MOD_TIMER 2
188
189/*
190 * __appldata_vtimer_setup()
191 *
192 * Add, delete or modify virtual timers on all online cpus.
193 * The caller needs to get the appldata_timer_lock spinlock.
194 */
195static void
196__appldata_vtimer_setup(int cmd)
197{
198 u64 per_cpu_interval;
199 int i;
200
201 switch (cmd) {
202 case APPLDATA_ADD_TIMER:
203 if (appldata_timer_active)
204 break;
205 per_cpu_interval = (u64) (appldata_interval*1000 /
206 num_online_cpus()) * TOD_MICRO;
207 for_each_online_cpu(i) {
208 per_cpu(appldata_timer, i).expires = per_cpu_interval;
Heiko Carstens3bb447f2007-07-27 12:29:08 +0200209 smp_call_function_single(i, add_virt_timer_periodic,
210 &per_cpu(appldata_timer, i),
Jens Axboe8691e5a2008-06-06 11:18:06 +0200211 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 }
213 appldata_timer_active = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 break;
215 case APPLDATA_DEL_TIMER:
216 for_each_online_cpu(i)
217 del_virt_timer(&per_cpu(appldata_timer, i));
218 if (!appldata_timer_active)
219 break;
220 appldata_timer_active = 0;
221 atomic_set(&appldata_expire_count, num_online_cpus());
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 break;
223 case APPLDATA_MOD_TIMER:
224 per_cpu_interval = (u64) (appldata_interval*1000 /
225 num_online_cpus()) * TOD_MICRO;
226 if (!appldata_timer_active)
227 break;
228 for_each_online_cpu(i) {
229 struct {
230 struct vtimer_list *timer;
231 u64 expires;
232 } args;
233 args.timer = &per_cpu(appldata_timer, i);
234 args.expires = per_cpu_interval;
Heiko Carstens3bb447f2007-07-27 12:29:08 +0200235 smp_call_function_single(i, __appldata_mod_vtimer_wrap,
Jens Axboe8691e5a2008-06-06 11:18:06 +0200236 &args, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 }
238 }
239}
240
241/*
242 * appldata_timer_handler()
243 *
244 * Start/Stop timer, show status of timer (0 = not active, 1 = active)
245 */
246static int
Alexey Dobriyan8d65af72009-09-23 15:57:19 -0700247appldata_timer_handler(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 void __user *buffer, size_t *lenp, loff_t *ppos)
249{
250 int len;
251 char buf[2];
252
253 if (!*lenp || *ppos) {
254 *lenp = 0;
255 return 0;
256 }
257 if (!write) {
258 len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n");
259 if (len > *lenp)
260 len = *lenp;
261 if (copy_to_user(buffer, buf, len))
262 return -EFAULT;
263 goto out;
264 }
265 len = *lenp;
266 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
267 return -EFAULT;
Gerald Schaefer17605372008-05-30 10:03:28 +0200268 get_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 spin_lock(&appldata_timer_lock);
270 if (buf[0] == '1')
271 __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
272 else if (buf[0] == '0')
273 __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
274 spin_unlock(&appldata_timer_lock);
Gerald Schaefer17605372008-05-30 10:03:28 +0200275 put_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276out:
277 *lenp = len;
278 *ppos += len;
279 return 0;
280}
281
282/*
283 * appldata_interval_handler()
284 *
285 * Set (CPU) timer interval for collection of data (in milliseconds), show
286 * current timer interval.
287 */
288static int
Alexey Dobriyan8d65af72009-09-23 15:57:19 -0700289appldata_interval_handler(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 void __user *buffer, size_t *lenp, loff_t *ppos)
291{
292 int len, interval;
293 char buf[16];
294
295 if (!*lenp || *ppos) {
296 *lenp = 0;
297 return 0;
298 }
299 if (!write) {
300 len = sprintf(buf, "%i\n", appldata_interval);
301 if (len > *lenp)
302 len = *lenp;
303 if (copy_to_user(buffer, buf, len))
304 return -EFAULT;
305 goto out;
306 }
307 len = *lenp;
308 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) {
309 return -EFAULT;
310 }
Gerald Schaefer95425f12006-10-27 12:39:13 +0200311 interval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 sscanf(buf, "%i", &interval);
Gerald Schaeferd3ae9422008-07-14 09:59:34 +0200313 if (interval <= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
Gerald Schaefer17605372008-05-30 10:03:28 +0200316 get_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 spin_lock(&appldata_timer_lock);
318 appldata_interval = interval;
319 __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
320 spin_unlock(&appldata_timer_lock);
Gerald Schaefer17605372008-05-30 10:03:28 +0200321 put_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322out:
323 *lenp = len;
324 *ppos += len;
325 return 0;
326}
327
328/*
329 * appldata_generic_handler()
330 *
331 * Generic start/stop monitoring and DIAG, show status of
332 * monitoring (0 = not in process, 1 = in process)
333 */
334static int
Alexey Dobriyan8d65af72009-09-23 15:57:19 -0700335appldata_generic_handler(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 void __user *buffer, size_t *lenp, loff_t *ppos)
337{
338 struct appldata_ops *ops = NULL, *tmp_ops;
339 int rc, len, found;
340 char buf[2];
341 struct list_head *lh;
342
343 found = 0;
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200344 mutex_lock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 list_for_each(lh, &appldata_ops_list) {
346 tmp_ops = list_entry(lh, struct appldata_ops, list);
347 if (&tmp_ops->ctl_table[2] == ctl) {
348 found = 1;
349 }
350 }
351 if (!found) {
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200352 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 return -ENODEV;
354 }
355 ops = ctl->data;
356 if (!try_module_get(ops->owner)) { // protect this function
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200357 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 return -ENODEV;
359 }
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200360 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
362 if (!*lenp || *ppos) {
363 *lenp = 0;
364 module_put(ops->owner);
365 return 0;
366 }
367 if (!write) {
368 len = sprintf(buf, ops->active ? "1\n" : "0\n");
369 if (len > *lenp)
370 len = *lenp;
371 if (copy_to_user(buffer, buf, len)) {
372 module_put(ops->owner);
373 return -EFAULT;
374 }
375 goto out;
376 }
377 len = *lenp;
378 if (copy_from_user(buf, buffer,
379 len > sizeof(buf) ? sizeof(buf) : len)) {
380 module_put(ops->owner);
381 return -EFAULT;
382 }
383
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200384 mutex_lock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 if ((buf[0] == '1') && (ops->active == 0)) {
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700386 // protect work queue callback
387 if (!try_module_get(ops->owner)) {
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200388 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 module_put(ops->owner);
390 return -ENODEV;
391 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 ops->callback(ops->data); // init record
393 rc = appldata_diag(ops->record_nr,
394 APPLDATA_START_INTERVAL_REC,
Gerald Schaefer5b5dd212006-06-29 15:08:35 +0200395 (unsigned long) ops->data, ops->size,
396 ops->mod_lvl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 if (rc != 0) {
Gerald Schaefere7534b02008-12-25 13:39:41 +0100398 pr_err("Starting the data collection for %s "
399 "failed with rc=%d\n", ops->name, rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 module_put(ops->owner);
Gerald Schaeferd3ae9422008-07-14 09:59:34 +0200401 } else
Gerald Schaefer5b5dd212006-06-29 15:08:35 +0200402 ops->active = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 } else if ((buf[0] == '0') && (ops->active == 1)) {
404 ops->active = 0;
405 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
Gerald Schaefer5b5dd212006-06-29 15:08:35 +0200406 (unsigned long) ops->data, ops->size,
407 ops->mod_lvl);
Gerald Schaeferd3ae9422008-07-14 09:59:34 +0200408 if (rc != 0)
Gerald Schaefere7534b02008-12-25 13:39:41 +0100409 pr_err("Stopping the data collection for %s "
410 "failed with rc=%d\n", ops->name, rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 module_put(ops->owner);
412 }
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200413 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414out:
415 *lenp = len;
416 *ppos += len;
417 module_put(ops->owner);
418 return 0;
419}
420
421/*************************** /proc stuff <END> *******************************/
422
423
424/************************* module-ops management *****************************/
425/*
426 * appldata_register_ops()
427 *
428 * update ops list, register /proc/sys entries
429 */
430int appldata_register_ops(struct appldata_ops *ops)
431{
Roel Kluin13f8b7c2008-10-28 11:10:18 +0100432 if (ops->size > APPLDATA_MAX_REC_SIZE)
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100433 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100435 ops->ctl_table = kzalloc(4 * sizeof(struct ctl_table), GFP_KERNEL);
436 if (!ops->ctl_table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200439 mutex_lock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 list_add(&ops->list, &appldata_ops_list);
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200441 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 ops->ctl_table[0].procname = appldata_proc_name;
444 ops->ctl_table[0].maxlen = 0;
445 ops->ctl_table[0].mode = S_IRUGO | S_IXUGO;
446 ops->ctl_table[0].child = &ops->ctl_table[2];
447
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 ops->ctl_table[2].procname = ops->name;
449 ops->ctl_table[2].mode = S_IRUGO | S_IWUSR;
450 ops->ctl_table[2].proc_handler = appldata_generic_handler;
451 ops->ctl_table[2].data = ops;
452
Eric W. Biederman0b4d4142007-02-14 00:34:09 -0800453 ops->sysctl_header = register_sysctl_table(ops->ctl_table);
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100454 if (!ops->sysctl_header)
455 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 return 0;
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100457out:
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200458 mutex_lock(&appldata_ops_mutex);
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100459 list_del(&ops->list);
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200460 mutex_unlock(&appldata_ops_mutex);
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100461 kfree(ops->ctl_table);
462 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463}
464
465/*
466 * appldata_unregister_ops()
467 *
468 * update ops list, unregister /proc entries, stop DIAG if necessary
469 */
470void appldata_unregister_ops(struct appldata_ops *ops)
471{
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200472 mutex_lock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 list_del(&ops->list);
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200474 mutex_unlock(&appldata_ops_mutex);
Al Viro330d57f2005-11-04 10:18:40 +0000475 unregister_sysctl_table(ops->sysctl_header);
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100476 kfree(ops->ctl_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477}
478/********************** module-ops management <END> **************************/
479
480
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200481/**************************** suspend / resume *******************************/
482static int appldata_freeze(struct device *dev)
483{
484 struct appldata_ops *ops;
485 int rc;
486 struct list_head *lh;
487
488 get_online_cpus();
489 spin_lock(&appldata_timer_lock);
490 if (appldata_timer_active) {
491 __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
492 appldata_timer_suspended = 1;
493 }
494 spin_unlock(&appldata_timer_lock);
495 put_online_cpus();
496
497 mutex_lock(&appldata_ops_mutex);
498 list_for_each(lh, &appldata_ops_list) {
499 ops = list_entry(lh, struct appldata_ops, list);
500 if (ops->active == 1) {
501 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
502 (unsigned long) ops->data, ops->size,
503 ops->mod_lvl);
504 if (rc != 0)
505 pr_err("Stopping the data collection for %s "
506 "failed with rc=%d\n", ops->name, rc);
507 }
508 }
509 mutex_unlock(&appldata_ops_mutex);
510 return 0;
511}
512
513static int appldata_restore(struct device *dev)
514{
515 struct appldata_ops *ops;
516 int rc;
517 struct list_head *lh;
518
519 get_online_cpus();
520 spin_lock(&appldata_timer_lock);
521 if (appldata_timer_suspended) {
522 __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
523 appldata_timer_suspended = 0;
524 }
525 spin_unlock(&appldata_timer_lock);
526 put_online_cpus();
527
528 mutex_lock(&appldata_ops_mutex);
529 list_for_each(lh, &appldata_ops_list) {
530 ops = list_entry(lh, struct appldata_ops, list);
531 if (ops->active == 1) {
532 ops->callback(ops->data); // init record
533 rc = appldata_diag(ops->record_nr,
534 APPLDATA_START_INTERVAL_REC,
535 (unsigned long) ops->data, ops->size,
536 ops->mod_lvl);
537 if (rc != 0) {
538 pr_err("Starting the data collection for %s "
539 "failed with rc=%d\n", ops->name, rc);
540 }
541 }
542 }
543 mutex_unlock(&appldata_ops_mutex);
544 return 0;
545}
546
547static int appldata_thaw(struct device *dev)
548{
549 return appldata_restore(dev);
550}
551
Alexey Dobriyan47145212009-12-14 18:00:08 -0800552static const struct dev_pm_ops appldata_pm_ops = {
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200553 .freeze = appldata_freeze,
554 .thaw = appldata_thaw,
555 .restore = appldata_restore,
556};
557
558static struct platform_driver appldata_pdrv = {
559 .driver = {
560 .name = "appldata",
561 .owner = THIS_MODULE,
562 .pm = &appldata_pm_ops,
563 },
564};
565/************************* suspend / resume <END> ****************************/
566
567
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568/******************************* init / exit *********************************/
569
Heiko Carstens84b36a82007-06-19 13:10:03 +0200570static void __cpuinit appldata_online_cpu(int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571{
572 init_virt_timer(&per_cpu(appldata_timer, cpu));
573 per_cpu(appldata_timer, cpu).function = appldata_timer_function;
574 per_cpu(appldata_timer, cpu).data = (unsigned long)
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700575 &appldata_work;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 atomic_inc(&appldata_expire_count);
577 spin_lock(&appldata_timer_lock);
578 __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
579 spin_unlock(&appldata_timer_lock);
580}
581
Satyam Sharma076fc802007-10-12 16:11:32 +0200582static void __cpuinit appldata_offline_cpu(int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583{
584 del_virt_timer(&per_cpu(appldata_timer, cpu));
585 if (atomic_dec_and_test(&appldata_expire_count)) {
586 atomic_set(&appldata_expire_count, num_online_cpus());
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700587 queue_work(appldata_wq, &appldata_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 }
589 spin_lock(&appldata_timer_lock);
590 __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
591 spin_unlock(&appldata_timer_lock);
592}
593
Satyam Sharma11b8bf02007-10-12 16:11:31 +0200594static int __cpuinit appldata_cpu_notify(struct notifier_block *self,
595 unsigned long action,
596 void *hcpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597{
598 switch (action) {
599 case CPU_ONLINE:
Rafael J. Wysocki8bb78442007-05-09 02:35:10 -0700600 case CPU_ONLINE_FROZEN:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 appldata_online_cpu((long) hcpu);
602 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 case CPU_DEAD:
Rafael J. Wysocki8bb78442007-05-09 02:35:10 -0700604 case CPU_DEAD_FROZEN:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 appldata_offline_cpu((long) hcpu);
606 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 default:
608 break;
609 }
610 return NOTIFY_OK;
611}
612
Heiko Carstens84b36a82007-06-19 13:10:03 +0200613static struct notifier_block __cpuinitdata appldata_nb = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 .notifier_call = appldata_cpu_notify,
615};
616
617/*
618 * appldata_init()
619 *
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700620 * init timer, register /proc entries
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 */
622static int __init appldata_init(void)
623{
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200624 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200626 rc = platform_driver_register(&appldata_pdrv);
627 if (rc)
628 return rc;
629
630 appldata_pdev = platform_device_register_simple("appldata", -1, NULL,
631 0);
632 if (IS_ERR(appldata_pdev)) {
633 rc = PTR_ERR(appldata_pdev);
634 goto out_driver;
635 }
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700636 appldata_wq = create_singlethread_workqueue("appldata");
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200637 if (!appldata_wq) {
638 rc = -ENOMEM;
639 goto out_device;
640 }
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700641
Gerald Schaefer17605372008-05-30 10:03:28 +0200642 get_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 for_each_online_cpu(i)
644 appldata_online_cpu(i);
Gerald Schaefer17605372008-05-30 10:03:28 +0200645 put_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646
647 /* Register cpu hotplug notifier */
Chandra Seetharamanbe6b5a32006-07-30 03:03:37 -0700648 register_hotcpu_notifier(&appldata_nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
Eric W. Biederman0b4d4142007-02-14 00:34:09 -0800650 appldata_sysctl_header = register_sysctl_table(appldata_dir_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 return 0;
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200652
653out_device:
654 platform_device_unregister(appldata_pdev);
655out_driver:
656 platform_driver_unregister(&appldata_pdrv);
657 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658}
659
Satyam Sharma076fc802007-10-12 16:11:32 +0200660__initcall(appldata_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662/**************************** init / exit <END> ******************************/
663
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664EXPORT_SYMBOL_GPL(appldata_register_ops);
665EXPORT_SYMBOL_GPL(appldata_unregister_ops);
Gerald Schaefer5b5dd212006-06-29 15:08:35 +0200666EXPORT_SYMBOL_GPL(appldata_diag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667
Gerald Schaefer0c3252d2008-07-14 09:57:27 +0200668#ifdef CONFIG_SWAP
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669EXPORT_SYMBOL_GPL(si_swapinfo);
Gerald Schaefer0c3252d2008-07-14 09:57:27 +0200670#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671EXPORT_SYMBOL_GPL(nr_threads);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672EXPORT_SYMBOL_GPL(nr_running);
673EXPORT_SYMBOL_GPL(nr_iowait);