blob: 5c91995b74e4b598e7eb5e974492ecb13087ff6a [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;
133 int i;
134
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 i = 0;
Gerald Schaefer17605372008-05-30 10:03:28 +0200136 get_online_cpus();
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200137 mutex_lock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 list_for_each(lh, &appldata_ops_list) {
139 ops = list_entry(lh, struct appldata_ops, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 if (ops->active == 1) {
141 ops->callback(ops->data);
142 }
143 }
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200144 mutex_unlock(&appldata_ops_mutex);
Gerald Schaefer17605372008-05-30 10:03:28 +0200145 put_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146}
147
148/*
149 * appldata_diag()
150 *
151 * prepare parameter list, issue DIAG 0xDC
152 */
Gerald Schaefer5b5dd212006-06-29 15:08:35 +0200153int appldata_diag(char record_nr, u16 function, unsigned long buffer,
154 u16 length, char *mod_lvl)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155{
Gerald Schaefer1f38d612006-09-20 15:59:26 +0200156 struct appldata_product_id id = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 .prod_nr = {0xD3, 0xC9, 0xD5, 0xE4,
Gerald Schaefer1f38d612006-09-20 15:59:26 +0200158 0xE7, 0xD2, 0xD9}, /* "LINUXKR" */
159 .prod_fn = 0xD5D3, /* "NL" */
Gerald Schaefer1f38d612006-09-20 15:59:26 +0200160 .version_nr = 0xF2F6, /* "26" */
161 .release_nr = 0xF0F1, /* "01" */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 };
163
Gerald Schaefer925afbd2006-09-28 16:55:23 +0200164 id.record_nr = record_nr;
165 id.mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1];
Gerald Schaefer1f38d612006-09-20 15:59:26 +0200166 return appldata_asm(&id, function, (void *) buffer, length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167}
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700168/************************ timer, work, DIAG <END> ****************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
170
171/****************************** /proc stuff **********************************/
172
173/*
174 * appldata_mod_vtimer_wrap()
175 *
Heiko Carstens3bb447f2007-07-27 12:29:08 +0200176 * wrapper function for mod_virt_timer(), because smp_call_function_single()
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 * accepts only one parameter.
178 */
179static void __appldata_mod_vtimer_wrap(void *p) {
180 struct {
181 struct vtimer_list *timer;
182 u64 expires;
183 } *args = p;
Gerald Schaefer43ae8a12009-04-14 15:36:21 +0200184 mod_virt_timer_periodic(args->timer, args->expires);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185}
186
187#define APPLDATA_ADD_TIMER 0
188#define APPLDATA_DEL_TIMER 1
189#define APPLDATA_MOD_TIMER 2
190
191/*
192 * __appldata_vtimer_setup()
193 *
194 * Add, delete or modify virtual timers on all online cpus.
195 * The caller needs to get the appldata_timer_lock spinlock.
196 */
197static void
198__appldata_vtimer_setup(int cmd)
199{
200 u64 per_cpu_interval;
201 int i;
202
203 switch (cmd) {
204 case APPLDATA_ADD_TIMER:
205 if (appldata_timer_active)
206 break;
207 per_cpu_interval = (u64) (appldata_interval*1000 /
208 num_online_cpus()) * TOD_MICRO;
209 for_each_online_cpu(i) {
210 per_cpu(appldata_timer, i).expires = per_cpu_interval;
Heiko Carstens3bb447f2007-07-27 12:29:08 +0200211 smp_call_function_single(i, add_virt_timer_periodic,
212 &per_cpu(appldata_timer, i),
Jens Axboe8691e5a2008-06-06 11:18:06 +0200213 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 }
215 appldata_timer_active = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 break;
217 case APPLDATA_DEL_TIMER:
218 for_each_online_cpu(i)
219 del_virt_timer(&per_cpu(appldata_timer, i));
220 if (!appldata_timer_active)
221 break;
222 appldata_timer_active = 0;
223 atomic_set(&appldata_expire_count, num_online_cpus());
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 break;
225 case APPLDATA_MOD_TIMER:
226 per_cpu_interval = (u64) (appldata_interval*1000 /
227 num_online_cpus()) * TOD_MICRO;
228 if (!appldata_timer_active)
229 break;
230 for_each_online_cpu(i) {
231 struct {
232 struct vtimer_list *timer;
233 u64 expires;
234 } args;
235 args.timer = &per_cpu(appldata_timer, i);
236 args.expires = per_cpu_interval;
Heiko Carstens3bb447f2007-07-27 12:29:08 +0200237 smp_call_function_single(i, __appldata_mod_vtimer_wrap,
Jens Axboe8691e5a2008-06-06 11:18:06 +0200238 &args, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 }
240 }
241}
242
243/*
244 * appldata_timer_handler()
245 *
246 * Start/Stop timer, show status of timer (0 = not active, 1 = active)
247 */
248static int
Alexey Dobriyan8d65af72009-09-23 15:57:19 -0700249appldata_timer_handler(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 void __user *buffer, size_t *lenp, loff_t *ppos)
251{
252 int len;
253 char buf[2];
254
255 if (!*lenp || *ppos) {
256 *lenp = 0;
257 return 0;
258 }
259 if (!write) {
260 len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n");
261 if (len > *lenp)
262 len = *lenp;
263 if (copy_to_user(buffer, buf, len))
264 return -EFAULT;
265 goto out;
266 }
267 len = *lenp;
268 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
269 return -EFAULT;
Gerald Schaefer17605372008-05-30 10:03:28 +0200270 get_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 spin_lock(&appldata_timer_lock);
272 if (buf[0] == '1')
273 __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
274 else if (buf[0] == '0')
275 __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
276 spin_unlock(&appldata_timer_lock);
Gerald Schaefer17605372008-05-30 10:03:28 +0200277 put_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278out:
279 *lenp = len;
280 *ppos += len;
281 return 0;
282}
283
284/*
285 * appldata_interval_handler()
286 *
287 * Set (CPU) timer interval for collection of data (in milliseconds), show
288 * current timer interval.
289 */
290static int
Alexey Dobriyan8d65af72009-09-23 15:57:19 -0700291appldata_interval_handler(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 void __user *buffer, size_t *lenp, loff_t *ppos)
293{
294 int len, interval;
295 char buf[16];
296
297 if (!*lenp || *ppos) {
298 *lenp = 0;
299 return 0;
300 }
301 if (!write) {
302 len = sprintf(buf, "%i\n", appldata_interval);
303 if (len > *lenp)
304 len = *lenp;
305 if (copy_to_user(buffer, buf, len))
306 return -EFAULT;
307 goto out;
308 }
309 len = *lenp;
310 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) {
311 return -EFAULT;
312 }
Gerald Schaefer95425f12006-10-27 12:39:13 +0200313 interval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 sscanf(buf, "%i", &interval);
Gerald Schaeferd3ae9422008-07-14 09:59:34 +0200315 if (interval <= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
Gerald Schaefer17605372008-05-30 10:03:28 +0200318 get_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 spin_lock(&appldata_timer_lock);
320 appldata_interval = interval;
321 __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
322 spin_unlock(&appldata_timer_lock);
Gerald Schaefer17605372008-05-30 10:03:28 +0200323 put_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324out:
325 *lenp = len;
326 *ppos += len;
327 return 0;
328}
329
330/*
331 * appldata_generic_handler()
332 *
333 * Generic start/stop monitoring and DIAG, show status of
334 * monitoring (0 = not in process, 1 = in process)
335 */
336static int
Alexey Dobriyan8d65af72009-09-23 15:57:19 -0700337appldata_generic_handler(ctl_table *ctl, int write,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 void __user *buffer, size_t *lenp, loff_t *ppos)
339{
340 struct appldata_ops *ops = NULL, *tmp_ops;
341 int rc, len, found;
342 char buf[2];
343 struct list_head *lh;
344
345 found = 0;
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200346 mutex_lock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 list_for_each(lh, &appldata_ops_list) {
348 tmp_ops = list_entry(lh, struct appldata_ops, list);
349 if (&tmp_ops->ctl_table[2] == ctl) {
350 found = 1;
351 }
352 }
353 if (!found) {
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200354 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 return -ENODEV;
356 }
357 ops = ctl->data;
358 if (!try_module_get(ops->owner)) { // protect this function
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200359 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 return -ENODEV;
361 }
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200362 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
364 if (!*lenp || *ppos) {
365 *lenp = 0;
366 module_put(ops->owner);
367 return 0;
368 }
369 if (!write) {
370 len = sprintf(buf, ops->active ? "1\n" : "0\n");
371 if (len > *lenp)
372 len = *lenp;
373 if (copy_to_user(buffer, buf, len)) {
374 module_put(ops->owner);
375 return -EFAULT;
376 }
377 goto out;
378 }
379 len = *lenp;
380 if (copy_from_user(buf, buffer,
381 len > sizeof(buf) ? sizeof(buf) : len)) {
382 module_put(ops->owner);
383 return -EFAULT;
384 }
385
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200386 mutex_lock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 if ((buf[0] == '1') && (ops->active == 0)) {
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700388 // protect work queue callback
389 if (!try_module_get(ops->owner)) {
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200390 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 module_put(ops->owner);
392 return -ENODEV;
393 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 ops->callback(ops->data); // init record
395 rc = appldata_diag(ops->record_nr,
396 APPLDATA_START_INTERVAL_REC,
Gerald Schaefer5b5dd212006-06-29 15:08:35 +0200397 (unsigned long) ops->data, ops->size,
398 ops->mod_lvl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 if (rc != 0) {
Gerald Schaefere7534b02008-12-25 13:39:41 +0100400 pr_err("Starting the data collection for %s "
401 "failed with rc=%d\n", ops->name, rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 module_put(ops->owner);
Gerald Schaeferd3ae9422008-07-14 09:59:34 +0200403 } else
Gerald Schaefer5b5dd212006-06-29 15:08:35 +0200404 ops->active = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 } else if ((buf[0] == '0') && (ops->active == 1)) {
406 ops->active = 0;
407 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
Gerald Schaefer5b5dd212006-06-29 15:08:35 +0200408 (unsigned long) ops->data, ops->size,
409 ops->mod_lvl);
Gerald Schaeferd3ae9422008-07-14 09:59:34 +0200410 if (rc != 0)
Gerald Schaefere7534b02008-12-25 13:39:41 +0100411 pr_err("Stopping the data collection for %s "
412 "failed with rc=%d\n", ops->name, rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 module_put(ops->owner);
414 }
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200415 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416out:
417 *lenp = len;
418 *ppos += len;
419 module_put(ops->owner);
420 return 0;
421}
422
423/*************************** /proc stuff <END> *******************************/
424
425
426/************************* module-ops management *****************************/
427/*
428 * appldata_register_ops()
429 *
430 * update ops list, register /proc/sys entries
431 */
432int appldata_register_ops(struct appldata_ops *ops)
433{
Roel Kluin13f8b7c2008-10-28 11:10:18 +0100434 if (ops->size > APPLDATA_MAX_REC_SIZE)
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100435 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100437 ops->ctl_table = kzalloc(4 * sizeof(struct ctl_table), GFP_KERNEL);
438 if (!ops->ctl_table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200441 mutex_lock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 list_add(&ops->list, &appldata_ops_list);
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200443 mutex_unlock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 ops->ctl_table[0].procname = appldata_proc_name;
446 ops->ctl_table[0].maxlen = 0;
447 ops->ctl_table[0].mode = S_IRUGO | S_IXUGO;
448 ops->ctl_table[0].child = &ops->ctl_table[2];
449
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 ops->ctl_table[2].procname = ops->name;
451 ops->ctl_table[2].mode = S_IRUGO | S_IWUSR;
452 ops->ctl_table[2].proc_handler = appldata_generic_handler;
453 ops->ctl_table[2].data = ops;
454
Eric W. Biederman0b4d4142007-02-14 00:34:09 -0800455 ops->sysctl_header = register_sysctl_table(ops->ctl_table);
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100456 if (!ops->sysctl_header)
457 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 return 0;
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100459out:
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200460 mutex_lock(&appldata_ops_mutex);
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100461 list_del(&ops->list);
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200462 mutex_unlock(&appldata_ops_mutex);
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100463 kfree(ops->ctl_table);
464 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465}
466
467/*
468 * appldata_unregister_ops()
469 *
470 * update ops list, unregister /proc entries, stop DIAG if necessary
471 */
472void appldata_unregister_ops(struct appldata_ops *ops)
473{
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200474 mutex_lock(&appldata_ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 list_del(&ops->list);
Gerald Schaeferb1ad1712009-04-23 13:58:07 +0200476 mutex_unlock(&appldata_ops_mutex);
Al Viro330d57f2005-11-04 10:18:40 +0000477 unregister_sysctl_table(ops->sysctl_header);
Heiko Carstens37e3a6a2007-11-20 11:13:34 +0100478 kfree(ops->ctl_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479}
480/********************** module-ops management <END> **************************/
481
482
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200483/**************************** suspend / resume *******************************/
484static int appldata_freeze(struct device *dev)
485{
486 struct appldata_ops *ops;
487 int rc;
488 struct list_head *lh;
489
490 get_online_cpus();
491 spin_lock(&appldata_timer_lock);
492 if (appldata_timer_active) {
493 __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
494 appldata_timer_suspended = 1;
495 }
496 spin_unlock(&appldata_timer_lock);
497 put_online_cpus();
498
499 mutex_lock(&appldata_ops_mutex);
500 list_for_each(lh, &appldata_ops_list) {
501 ops = list_entry(lh, struct appldata_ops, list);
502 if (ops->active == 1) {
503 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
504 (unsigned long) ops->data, ops->size,
505 ops->mod_lvl);
506 if (rc != 0)
507 pr_err("Stopping the data collection for %s "
508 "failed with rc=%d\n", ops->name, rc);
509 }
510 }
511 mutex_unlock(&appldata_ops_mutex);
512 return 0;
513}
514
515static int appldata_restore(struct device *dev)
516{
517 struct appldata_ops *ops;
518 int rc;
519 struct list_head *lh;
520
521 get_online_cpus();
522 spin_lock(&appldata_timer_lock);
523 if (appldata_timer_suspended) {
524 __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
525 appldata_timer_suspended = 0;
526 }
527 spin_unlock(&appldata_timer_lock);
528 put_online_cpus();
529
530 mutex_lock(&appldata_ops_mutex);
531 list_for_each(lh, &appldata_ops_list) {
532 ops = list_entry(lh, struct appldata_ops, list);
533 if (ops->active == 1) {
534 ops->callback(ops->data); // init record
535 rc = appldata_diag(ops->record_nr,
536 APPLDATA_START_INTERVAL_REC,
537 (unsigned long) ops->data, ops->size,
538 ops->mod_lvl);
539 if (rc != 0) {
540 pr_err("Starting the data collection for %s "
541 "failed with rc=%d\n", ops->name, rc);
542 }
543 }
544 }
545 mutex_unlock(&appldata_ops_mutex);
546 return 0;
547}
548
549static int appldata_thaw(struct device *dev)
550{
551 return appldata_restore(dev);
552}
553
Alexey Dobriyan47145212009-12-14 18:00:08 -0800554static const struct dev_pm_ops appldata_pm_ops = {
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200555 .freeze = appldata_freeze,
556 .thaw = appldata_thaw,
557 .restore = appldata_restore,
558};
559
560static struct platform_driver appldata_pdrv = {
561 .driver = {
562 .name = "appldata",
563 .owner = THIS_MODULE,
564 .pm = &appldata_pm_ops,
565 },
566};
567/************************* suspend / resume <END> ****************************/
568
569
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570/******************************* init / exit *********************************/
571
Heiko Carstens84b36a82007-06-19 13:10:03 +0200572static void __cpuinit appldata_online_cpu(int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573{
574 init_virt_timer(&per_cpu(appldata_timer, cpu));
575 per_cpu(appldata_timer, cpu).function = appldata_timer_function;
576 per_cpu(appldata_timer, cpu).data = (unsigned long)
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700577 &appldata_work;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 atomic_inc(&appldata_expire_count);
579 spin_lock(&appldata_timer_lock);
580 __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
581 spin_unlock(&appldata_timer_lock);
582}
583
Satyam Sharma076fc802007-10-12 16:11:32 +0200584static void __cpuinit appldata_offline_cpu(int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585{
586 del_virt_timer(&per_cpu(appldata_timer, cpu));
587 if (atomic_dec_and_test(&appldata_expire_count)) {
588 atomic_set(&appldata_expire_count, num_online_cpus());
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700589 queue_work(appldata_wq, &appldata_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 }
591 spin_lock(&appldata_timer_lock);
592 __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
593 spin_unlock(&appldata_timer_lock);
594}
595
Satyam Sharma11b8bf02007-10-12 16:11:31 +0200596static int __cpuinit appldata_cpu_notify(struct notifier_block *self,
597 unsigned long action,
598 void *hcpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599{
600 switch (action) {
601 case CPU_ONLINE:
Rafael J. Wysocki8bb78442007-05-09 02:35:10 -0700602 case CPU_ONLINE_FROZEN:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 appldata_online_cpu((long) hcpu);
604 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 case CPU_DEAD:
Rafael J. Wysocki8bb78442007-05-09 02:35:10 -0700606 case CPU_DEAD_FROZEN:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 appldata_offline_cpu((long) hcpu);
608 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 default:
610 break;
611 }
612 return NOTIFY_OK;
613}
614
Heiko Carstens84b36a82007-06-19 13:10:03 +0200615static struct notifier_block __cpuinitdata appldata_nb = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 .notifier_call = appldata_cpu_notify,
617};
618
619/*
620 * appldata_init()
621 *
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700622 * init timer, register /proc entries
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 */
624static int __init appldata_init(void)
625{
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200626 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200628 rc = platform_driver_register(&appldata_pdrv);
629 if (rc)
630 return rc;
631
632 appldata_pdev = platform_device_register_simple("appldata", -1, NULL,
633 0);
634 if (IS_ERR(appldata_pdev)) {
635 rc = PTR_ERR(appldata_pdev);
636 goto out_driver;
637 }
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700638 appldata_wq = create_singlethread_workqueue("appldata");
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200639 if (!appldata_wq) {
640 rc = -ENOMEM;
641 goto out_device;
642 }
Gerald Schaeferf26d5832005-06-04 15:43:33 -0700643
Gerald Schaefer17605372008-05-30 10:03:28 +0200644 get_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 for_each_online_cpu(i)
646 appldata_online_cpu(i);
Gerald Schaefer17605372008-05-30 10:03:28 +0200647 put_online_cpus();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
649 /* Register cpu hotplug notifier */
Chandra Seetharamanbe6b5a32006-07-30 03:03:37 -0700650 register_hotcpu_notifier(&appldata_nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
Eric W. Biederman0b4d4142007-02-14 00:34:09 -0800652 appldata_sysctl_header = register_sysctl_table(appldata_dir_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 return 0;
Gerald Schaefer524dbcd2009-06-16 10:30:36 +0200654
655out_device:
656 platform_device_unregister(appldata_pdev);
657out_driver:
658 platform_driver_unregister(&appldata_pdrv);
659 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660}
661
Satyam Sharma076fc802007-10-12 16:11:32 +0200662__initcall(appldata_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664/**************************** init / exit <END> ******************************/
665
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666EXPORT_SYMBOL_GPL(appldata_register_ops);
667EXPORT_SYMBOL_GPL(appldata_unregister_ops);
Gerald Schaefer5b5dd212006-06-29 15:08:35 +0200668EXPORT_SYMBOL_GPL(appldata_diag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
Gerald Schaefer0c3252d2008-07-14 09:57:27 +0200670#ifdef CONFIG_SWAP
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671EXPORT_SYMBOL_GPL(si_swapinfo);
Gerald Schaefer0c3252d2008-07-14 09:57:27 +0200672#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673EXPORT_SYMBOL_GPL(nr_threads);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674EXPORT_SYMBOL_GPL(nr_running);
675EXPORT_SYMBOL_GPL(nr_iowait);