| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 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 Schaefer | 524dbcd | 2009-06-16 10:30:36 +0200 | [diff] [blame] | 8 |  * Copyright IBM Corp. 2003, 2009 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 9 |  * | 
| Gerald Schaefer | 5b5dd21 | 2006-06-29 15:08:35 +0200 | [diff] [blame] | 10 |  * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 11 |  */ | 
 | 12 |  | 
| Gerald Schaefer | e7534b0 | 2008-12-25 13:39:41 +0100 | [diff] [blame] | 13 | #define KMSG_COMPONENT	"appldata" | 
 | 14 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | 
 | 15 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 16 | #include <linux/module.h> | 
 | 17 | #include <linux/init.h> | 
 | 18 | #include <linux/slab.h> | 
 | 19 | #include <linux/errno.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 20 | #include <linux/interrupt.h> | 
 | 21 | #include <linux/proc_fs.h> | 
| Heiko Carstens | 2dcea57 | 2006-09-29 01:58:41 -0700 | [diff] [blame] | 22 | #include <linux/mm.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 23 | #include <linux/swap.h> | 
 | 24 | #include <linux/pagemap.h> | 
 | 25 | #include <linux/sysctl.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 26 | #include <linux/notifier.h> | 
 | 27 | #include <linux/cpu.h> | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 28 | #include <linux/workqueue.h> | 
| Gerald Schaefer | 524dbcd | 2009-06-16 10:30:36 +0200 | [diff] [blame] | 29 | #include <linux/suspend.h> | 
 | 30 | #include <linux/platform_device.h> | 
| Gerald Schaefer | 1f38d61 | 2006-09-20 15:59:26 +0200 | [diff] [blame] | 31 | #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 Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 36 |  | 
 | 37 | #include "appldata.h" | 
 | 38 |  | 
 | 39 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | #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 Schaefer | 524dbcd | 2009-06-16 10:30:36 +0200 | [diff] [blame] | 46 |  | 
 | 47 | static struct platform_device *appldata_pdev; | 
 | 48 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 49 | /* | 
 | 50 |  * /proc entries (sysctl) | 
 | 51 |  */ | 
 | 52 | static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata"; | 
 | 53 | static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp, | 
 | 54 | 				  void __user *buffer, size_t *lenp, loff_t *ppos); | 
 | 55 | static int appldata_interval_handler(ctl_table *ctl, int write, | 
 | 56 | 					 struct file *filp, | 
 | 57 | 					 void __user *buffer, | 
 | 58 | 					 size_t *lenp, loff_t *ppos); | 
 | 59 |  | 
 | 60 | static struct ctl_table_header *appldata_sysctl_header; | 
 | 61 | static struct ctl_table appldata_table[] = { | 
 | 62 | 	{ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 63 | 		.procname	= "timer", | 
 | 64 | 		.mode		= S_IRUGO | S_IWUSR, | 
 | 65 | 		.proc_handler	= &appldata_timer_handler, | 
 | 66 | 	}, | 
 | 67 | 	{ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 68 | 		.procname	= "interval", | 
 | 69 | 		.mode		= S_IRUGO | S_IWUSR, | 
 | 70 | 		.proc_handler	= &appldata_interval_handler, | 
 | 71 | 	}, | 
| Heiko Carstens | 37e3a6a | 2007-11-20 11:13:34 +0100 | [diff] [blame] | 72 | 	{ }, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 73 | }; | 
 | 74 |  | 
 | 75 | static struct ctl_table appldata_dir_table[] = { | 
 | 76 | 	{ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 77 | 		.procname	= appldata_proc_name, | 
 | 78 | 		.maxlen		= 0, | 
 | 79 | 		.mode		= S_IRUGO | S_IXUGO, | 
 | 80 | 		.child		= appldata_table, | 
 | 81 | 	}, | 
| Heiko Carstens | 37e3a6a | 2007-11-20 11:13:34 +0100 | [diff] [blame] | 82 | 	{ }, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 83 | }; | 
 | 84 |  | 
 | 85 | /* | 
 | 86 |  * Timer | 
 | 87 |  */ | 
| Heiko Carstens | 2b67fc4 | 2007-02-05 21:16:47 +0100 | [diff] [blame] | 88 | static DEFINE_PER_CPU(struct vtimer_list, appldata_timer); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 89 | static atomic_t appldata_expire_count = ATOMIC_INIT(0); | 
 | 90 |  | 
 | 91 | static DEFINE_SPINLOCK(appldata_timer_lock); | 
 | 92 | static int appldata_interval = APPLDATA_CPU_INTERVAL; | 
 | 93 | static int appldata_timer_active; | 
| Gerald Schaefer | 524dbcd | 2009-06-16 10:30:36 +0200 | [diff] [blame] | 94 | static int appldata_timer_suspended = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 95 |  | 
 | 96 | /* | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 97 |  * Work queue | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 98 |  */ | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 99 | static struct workqueue_struct *appldata_wq; | 
| David Howells | 6d5aefb | 2006-12-05 19:36:26 +0000 | [diff] [blame] | 100 | static void appldata_work_fn(struct work_struct *work); | 
 | 101 | static DECLARE_WORK(appldata_work, appldata_work_fn); | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 102 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 103 |  | 
 | 104 | /* | 
 | 105 |  * Ops list | 
 | 106 |  */ | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 107 | static DEFINE_MUTEX(appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 108 | static LIST_HEAD(appldata_ops_list); | 
 | 109 |  | 
 | 110 |  | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 111 | /*************************** timer, work, DIAG *******************************/ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 112 | /* | 
 | 113 |  * appldata_timer_function() | 
 | 114 |  * | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 115 |  * schedule work and reschedule timer | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 116 |  */ | 
| Heiko Carstens | 9d0a57c | 2006-10-11 15:31:26 +0200 | [diff] [blame] | 117 | static void appldata_timer_function(unsigned long data) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 118 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 119 | 	if (atomic_dec_and_test(&appldata_expire_count)) { | 
 | 120 | 		atomic_set(&appldata_expire_count, num_online_cpus()); | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 121 | 		queue_work(appldata_wq, (struct work_struct *) data); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 122 | 	} | 
 | 123 | } | 
 | 124 |  | 
 | 125 | /* | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 126 |  * appldata_work_fn() | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 127 |  * | 
 | 128 |  * call data gathering function for each (active) module | 
 | 129 |  */ | 
| David Howells | 6d5aefb | 2006-12-05 19:36:26 +0000 | [diff] [blame] | 130 | static void appldata_work_fn(struct work_struct *work) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 131 | { | 
 | 132 | 	struct list_head *lh; | 
 | 133 | 	struct appldata_ops *ops; | 
 | 134 | 	int i; | 
 | 135 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 136 | 	i = 0; | 
| Gerald Schaefer | 1760537 | 2008-05-30 10:03:28 +0200 | [diff] [blame] | 137 | 	get_online_cpus(); | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 138 | 	mutex_lock(&appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 139 | 	list_for_each(lh, &appldata_ops_list) { | 
 | 140 | 		ops = list_entry(lh, struct appldata_ops, list); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 141 | 		if (ops->active == 1) { | 
 | 142 | 			ops->callback(ops->data); | 
 | 143 | 		} | 
 | 144 | 	} | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 145 | 	mutex_unlock(&appldata_ops_mutex); | 
| Gerald Schaefer | 1760537 | 2008-05-30 10:03:28 +0200 | [diff] [blame] | 146 | 	put_online_cpus(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 147 | } | 
 | 148 |  | 
 | 149 | /* | 
 | 150 |  * appldata_diag() | 
 | 151 |  * | 
 | 152 |  * prepare parameter list, issue DIAG 0xDC | 
 | 153 |  */ | 
| Gerald Schaefer | 5b5dd21 | 2006-06-29 15:08:35 +0200 | [diff] [blame] | 154 | int appldata_diag(char record_nr, u16 function, unsigned long buffer, | 
 | 155 | 			u16 length, char *mod_lvl) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 156 | { | 
| Gerald Schaefer | 1f38d61 | 2006-09-20 15:59:26 +0200 | [diff] [blame] | 157 | 	struct appldata_product_id id = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 158 | 		.prod_nr    = {0xD3, 0xC9, 0xD5, 0xE4, | 
| Gerald Schaefer | 1f38d61 | 2006-09-20 15:59:26 +0200 | [diff] [blame] | 159 | 			       0xE7, 0xD2, 0xD9},	/* "LINUXKR" */ | 
 | 160 | 		.prod_fn    = 0xD5D3,			/* "NL" */ | 
| Gerald Schaefer | 1f38d61 | 2006-09-20 15:59:26 +0200 | [diff] [blame] | 161 | 		.version_nr = 0xF2F6,			/* "26" */ | 
 | 162 | 		.release_nr = 0xF0F1,			/* "01" */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 163 | 	}; | 
 | 164 |  | 
| Gerald Schaefer | 925afbd | 2006-09-28 16:55:23 +0200 | [diff] [blame] | 165 | 	id.record_nr = record_nr; | 
 | 166 | 	id.mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1]; | 
| Gerald Schaefer | 1f38d61 | 2006-09-20 15:59:26 +0200 | [diff] [blame] | 167 | 	return appldata_asm(&id, function, (void *) buffer, length); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 168 | } | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 169 | /************************ timer, work, DIAG <END> ****************************/ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 170 |  | 
 | 171 |  | 
 | 172 | /****************************** /proc stuff **********************************/ | 
 | 173 |  | 
 | 174 | /* | 
 | 175 |  * appldata_mod_vtimer_wrap() | 
 | 176 |  * | 
| Heiko Carstens | 3bb447f | 2007-07-27 12:29:08 +0200 | [diff] [blame] | 177 |  * wrapper function for mod_virt_timer(), because smp_call_function_single() | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 178 |  * accepts only one parameter. | 
 | 179 |  */ | 
 | 180 | static void __appldata_mod_vtimer_wrap(void *p) { | 
 | 181 | 	struct { | 
 | 182 | 		struct vtimer_list *timer; | 
 | 183 | 		u64    expires; | 
 | 184 | 	} *args = p; | 
| Gerald Schaefer | 43ae8a1 | 2009-04-14 15:36:21 +0200 | [diff] [blame] | 185 | 	mod_virt_timer_periodic(args->timer, args->expires); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 186 | } | 
 | 187 |  | 
 | 188 | #define APPLDATA_ADD_TIMER	0 | 
 | 189 | #define APPLDATA_DEL_TIMER	1 | 
 | 190 | #define APPLDATA_MOD_TIMER	2 | 
 | 191 |  | 
 | 192 | /* | 
 | 193 |  * __appldata_vtimer_setup() | 
 | 194 |  * | 
 | 195 |  * Add, delete or modify virtual timers on all online cpus. | 
 | 196 |  * The caller needs to get the appldata_timer_lock spinlock. | 
 | 197 |  */ | 
 | 198 | static void | 
 | 199 | __appldata_vtimer_setup(int cmd) | 
 | 200 | { | 
 | 201 | 	u64 per_cpu_interval; | 
 | 202 | 	int i; | 
 | 203 |  | 
 | 204 | 	switch (cmd) { | 
 | 205 | 	case APPLDATA_ADD_TIMER: | 
 | 206 | 		if (appldata_timer_active) | 
 | 207 | 			break; | 
 | 208 | 		per_cpu_interval = (u64) (appldata_interval*1000 / | 
 | 209 | 					  num_online_cpus()) * TOD_MICRO; | 
 | 210 | 		for_each_online_cpu(i) { | 
 | 211 | 			per_cpu(appldata_timer, i).expires = per_cpu_interval; | 
| Heiko Carstens | 3bb447f | 2007-07-27 12:29:08 +0200 | [diff] [blame] | 212 | 			smp_call_function_single(i, add_virt_timer_periodic, | 
 | 213 | 						 &per_cpu(appldata_timer, i), | 
| Jens Axboe | 8691e5a | 2008-06-06 11:18:06 +0200 | [diff] [blame] | 214 | 						 1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 215 | 		} | 
 | 216 | 		appldata_timer_active = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 217 | 		break; | 
 | 218 | 	case APPLDATA_DEL_TIMER: | 
 | 219 | 		for_each_online_cpu(i) | 
 | 220 | 			del_virt_timer(&per_cpu(appldata_timer, i)); | 
 | 221 | 		if (!appldata_timer_active) | 
 | 222 | 			break; | 
 | 223 | 		appldata_timer_active = 0; | 
 | 224 | 		atomic_set(&appldata_expire_count, num_online_cpus()); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 225 | 		break; | 
 | 226 | 	case APPLDATA_MOD_TIMER: | 
 | 227 | 		per_cpu_interval = (u64) (appldata_interval*1000 / | 
 | 228 | 					  num_online_cpus()) * TOD_MICRO; | 
 | 229 | 		if (!appldata_timer_active) | 
 | 230 | 			break; | 
 | 231 | 		for_each_online_cpu(i) { | 
 | 232 | 			struct { | 
 | 233 | 				struct vtimer_list *timer; | 
 | 234 | 				u64    expires; | 
 | 235 | 			} args; | 
 | 236 | 			args.timer = &per_cpu(appldata_timer, i); | 
 | 237 | 			args.expires = per_cpu_interval; | 
| Heiko Carstens | 3bb447f | 2007-07-27 12:29:08 +0200 | [diff] [blame] | 238 | 			smp_call_function_single(i, __appldata_mod_vtimer_wrap, | 
| Jens Axboe | 8691e5a | 2008-06-06 11:18:06 +0200 | [diff] [blame] | 239 | 						 &args, 1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 240 | 		} | 
 | 241 | 	} | 
 | 242 | } | 
 | 243 |  | 
 | 244 | /* | 
 | 245 |  * appldata_timer_handler() | 
 | 246 |  * | 
 | 247 |  * Start/Stop timer, show status of timer (0 = not active, 1 = active) | 
 | 248 |  */ | 
 | 249 | static int | 
 | 250 | appldata_timer_handler(ctl_table *ctl, int write, struct file *filp, | 
 | 251 | 			   void __user *buffer, size_t *lenp, loff_t *ppos) | 
 | 252 | { | 
 | 253 | 	int len; | 
 | 254 | 	char buf[2]; | 
 | 255 |  | 
 | 256 | 	if (!*lenp || *ppos) { | 
 | 257 | 		*lenp = 0; | 
 | 258 | 		return 0; | 
 | 259 | 	} | 
 | 260 | 	if (!write) { | 
 | 261 | 		len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n"); | 
 | 262 | 		if (len > *lenp) | 
 | 263 | 			len = *lenp; | 
 | 264 | 		if (copy_to_user(buffer, buf, len)) | 
 | 265 | 			return -EFAULT; | 
 | 266 | 		goto out; | 
 | 267 | 	} | 
 | 268 | 	len = *lenp; | 
 | 269 | 	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) | 
 | 270 | 		return -EFAULT; | 
| Gerald Schaefer | 1760537 | 2008-05-30 10:03:28 +0200 | [diff] [blame] | 271 | 	get_online_cpus(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 272 | 	spin_lock(&appldata_timer_lock); | 
 | 273 | 	if (buf[0] == '1') | 
 | 274 | 		__appldata_vtimer_setup(APPLDATA_ADD_TIMER); | 
 | 275 | 	else if (buf[0] == '0') | 
 | 276 | 		__appldata_vtimer_setup(APPLDATA_DEL_TIMER); | 
 | 277 | 	spin_unlock(&appldata_timer_lock); | 
| Gerald Schaefer | 1760537 | 2008-05-30 10:03:28 +0200 | [diff] [blame] | 278 | 	put_online_cpus(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 279 | out: | 
 | 280 | 	*lenp = len; | 
 | 281 | 	*ppos += len; | 
 | 282 | 	return 0; | 
 | 283 | } | 
 | 284 |  | 
 | 285 | /* | 
 | 286 |  * appldata_interval_handler() | 
 | 287 |  * | 
 | 288 |  * Set (CPU) timer interval for collection of data (in milliseconds), show | 
 | 289 |  * current timer interval. | 
 | 290 |  */ | 
 | 291 | static int | 
 | 292 | appldata_interval_handler(ctl_table *ctl, int write, struct file *filp, | 
 | 293 | 			   void __user *buffer, size_t *lenp, loff_t *ppos) | 
 | 294 | { | 
 | 295 | 	int len, interval; | 
 | 296 | 	char buf[16]; | 
 | 297 |  | 
 | 298 | 	if (!*lenp || *ppos) { | 
 | 299 | 		*lenp = 0; | 
 | 300 | 		return 0; | 
 | 301 | 	} | 
 | 302 | 	if (!write) { | 
 | 303 | 		len = sprintf(buf, "%i\n", appldata_interval); | 
 | 304 | 		if (len > *lenp) | 
 | 305 | 			len = *lenp; | 
 | 306 | 		if (copy_to_user(buffer, buf, len)) | 
 | 307 | 			return -EFAULT; | 
 | 308 | 		goto out; | 
 | 309 | 	} | 
 | 310 | 	len = *lenp; | 
 | 311 | 	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) { | 
 | 312 | 		return -EFAULT; | 
 | 313 | 	} | 
| Gerald Schaefer | 95425f1 | 2006-10-27 12:39:13 +0200 | [diff] [blame] | 314 | 	interval = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 315 | 	sscanf(buf, "%i", &interval); | 
| Gerald Schaefer | d3ae942 | 2008-07-14 09:59:34 +0200 | [diff] [blame] | 316 | 	if (interval <= 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 317 | 		return -EINVAL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 318 |  | 
| Gerald Schaefer | 1760537 | 2008-05-30 10:03:28 +0200 | [diff] [blame] | 319 | 	get_online_cpus(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 320 | 	spin_lock(&appldata_timer_lock); | 
 | 321 | 	appldata_interval = interval; | 
 | 322 | 	__appldata_vtimer_setup(APPLDATA_MOD_TIMER); | 
 | 323 | 	spin_unlock(&appldata_timer_lock); | 
| Gerald Schaefer | 1760537 | 2008-05-30 10:03:28 +0200 | [diff] [blame] | 324 | 	put_online_cpus(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 325 | out: | 
 | 326 | 	*lenp = len; | 
 | 327 | 	*ppos += len; | 
 | 328 | 	return 0; | 
 | 329 | } | 
 | 330 |  | 
 | 331 | /* | 
 | 332 |  * appldata_generic_handler() | 
 | 333 |  * | 
 | 334 |  * Generic start/stop monitoring and DIAG, show status of | 
 | 335 |  * monitoring (0 = not in process, 1 = in process) | 
 | 336 |  */ | 
 | 337 | static int | 
 | 338 | appldata_generic_handler(ctl_table *ctl, int write, struct file *filp, | 
 | 339 | 			   void __user *buffer, size_t *lenp, loff_t *ppos) | 
 | 340 | { | 
 | 341 | 	struct appldata_ops *ops = NULL, *tmp_ops; | 
 | 342 | 	int rc, len, found; | 
 | 343 | 	char buf[2]; | 
 | 344 | 	struct list_head *lh; | 
 | 345 |  | 
 | 346 | 	found = 0; | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 347 | 	mutex_lock(&appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 348 | 	list_for_each(lh, &appldata_ops_list) { | 
 | 349 | 		tmp_ops = list_entry(lh, struct appldata_ops, list); | 
 | 350 | 		if (&tmp_ops->ctl_table[2] == ctl) { | 
 | 351 | 			found = 1; | 
 | 352 | 		} | 
 | 353 | 	} | 
 | 354 | 	if (!found) { | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 355 | 		mutex_unlock(&appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 356 | 		return -ENODEV; | 
 | 357 | 	} | 
 | 358 | 	ops = ctl->data; | 
 | 359 | 	if (!try_module_get(ops->owner)) {	// protect this function | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 360 | 		mutex_unlock(&appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 361 | 		return -ENODEV; | 
 | 362 | 	} | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 363 | 	mutex_unlock(&appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 364 |  | 
 | 365 | 	if (!*lenp || *ppos) { | 
 | 366 | 		*lenp = 0; | 
 | 367 | 		module_put(ops->owner); | 
 | 368 | 		return 0; | 
 | 369 | 	} | 
 | 370 | 	if (!write) { | 
 | 371 | 		len = sprintf(buf, ops->active ? "1\n" : "0\n"); | 
 | 372 | 		if (len > *lenp) | 
 | 373 | 			len = *lenp; | 
 | 374 | 		if (copy_to_user(buffer, buf, len)) { | 
 | 375 | 			module_put(ops->owner); | 
 | 376 | 			return -EFAULT; | 
 | 377 | 		} | 
 | 378 | 		goto out; | 
 | 379 | 	} | 
 | 380 | 	len = *lenp; | 
 | 381 | 	if (copy_from_user(buf, buffer, | 
 | 382 | 			   len > sizeof(buf) ? sizeof(buf) : len)) { | 
 | 383 | 		module_put(ops->owner); | 
 | 384 | 		return -EFAULT; | 
 | 385 | 	} | 
 | 386 |  | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 387 | 	mutex_lock(&appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 388 | 	if ((buf[0] == '1') && (ops->active == 0)) { | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 389 | 		// protect work queue callback | 
 | 390 | 		if (!try_module_get(ops->owner)) { | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 391 | 			mutex_unlock(&appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 392 | 			module_put(ops->owner); | 
 | 393 | 			return -ENODEV; | 
 | 394 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 395 | 		ops->callback(ops->data);	// init record | 
 | 396 | 		rc = appldata_diag(ops->record_nr, | 
 | 397 | 					APPLDATA_START_INTERVAL_REC, | 
| Gerald Schaefer | 5b5dd21 | 2006-06-29 15:08:35 +0200 | [diff] [blame] | 398 | 					(unsigned long) ops->data, ops->size, | 
 | 399 | 					ops->mod_lvl); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 400 | 		if (rc != 0) { | 
| Gerald Schaefer | e7534b0 | 2008-12-25 13:39:41 +0100 | [diff] [blame] | 401 | 			pr_err("Starting the data collection for %s " | 
 | 402 | 			       "failed with rc=%d\n", ops->name, rc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 403 | 			module_put(ops->owner); | 
| Gerald Schaefer | d3ae942 | 2008-07-14 09:59:34 +0200 | [diff] [blame] | 404 | 		} else | 
| Gerald Schaefer | 5b5dd21 | 2006-06-29 15:08:35 +0200 | [diff] [blame] | 405 | 			ops->active = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 406 | 	} else if ((buf[0] == '0') && (ops->active == 1)) { | 
 | 407 | 		ops->active = 0; | 
 | 408 | 		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, | 
| Gerald Schaefer | 5b5dd21 | 2006-06-29 15:08:35 +0200 | [diff] [blame] | 409 | 				(unsigned long) ops->data, ops->size, | 
 | 410 | 				ops->mod_lvl); | 
| Gerald Schaefer | d3ae942 | 2008-07-14 09:59:34 +0200 | [diff] [blame] | 411 | 		if (rc != 0) | 
| Gerald Schaefer | e7534b0 | 2008-12-25 13:39:41 +0100 | [diff] [blame] | 412 | 			pr_err("Stopping the data collection for %s " | 
 | 413 | 			       "failed with rc=%d\n", ops->name, rc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 414 | 		module_put(ops->owner); | 
 | 415 | 	} | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 416 | 	mutex_unlock(&appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 417 | out: | 
 | 418 | 	*lenp = len; | 
 | 419 | 	*ppos += len; | 
 | 420 | 	module_put(ops->owner); | 
 | 421 | 	return 0; | 
 | 422 | } | 
 | 423 |  | 
 | 424 | /*************************** /proc stuff <END> *******************************/ | 
 | 425 |  | 
 | 426 |  | 
 | 427 | /************************* module-ops management *****************************/ | 
 | 428 | /* | 
 | 429 |  * appldata_register_ops() | 
 | 430 |  * | 
 | 431 |  * update ops list, register /proc/sys entries | 
 | 432 |  */ | 
 | 433 | int appldata_register_ops(struct appldata_ops *ops) | 
 | 434 | { | 
| Roel Kluin | 13f8b7c | 2008-10-28 11:10:18 +0100 | [diff] [blame] | 435 | 	if (ops->size > APPLDATA_MAX_REC_SIZE) | 
| Heiko Carstens | 37e3a6a | 2007-11-20 11:13:34 +0100 | [diff] [blame] | 436 | 		return -EINVAL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 437 |  | 
| Heiko Carstens | 37e3a6a | 2007-11-20 11:13:34 +0100 | [diff] [blame] | 438 | 	ops->ctl_table = kzalloc(4 * sizeof(struct ctl_table), GFP_KERNEL); | 
 | 439 | 	if (!ops->ctl_table) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 440 | 		return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 441 |  | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 442 | 	mutex_lock(&appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 443 | 	list_add(&ops->list, &appldata_ops_list); | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 444 | 	mutex_unlock(&appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 445 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 446 | 	ops->ctl_table[0].procname = appldata_proc_name; | 
 | 447 | 	ops->ctl_table[0].maxlen   = 0; | 
 | 448 | 	ops->ctl_table[0].mode     = S_IRUGO | S_IXUGO; | 
 | 449 | 	ops->ctl_table[0].child    = &ops->ctl_table[2]; | 
 | 450 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 451 | 	ops->ctl_table[2].procname = ops->name; | 
 | 452 | 	ops->ctl_table[2].mode     = S_IRUGO | S_IWUSR; | 
 | 453 | 	ops->ctl_table[2].proc_handler = appldata_generic_handler; | 
 | 454 | 	ops->ctl_table[2].data = ops; | 
 | 455 |  | 
| Eric W. Biederman | 0b4d414 | 2007-02-14 00:34:09 -0800 | [diff] [blame] | 456 | 	ops->sysctl_header = register_sysctl_table(ops->ctl_table); | 
| Heiko Carstens | 37e3a6a | 2007-11-20 11:13:34 +0100 | [diff] [blame] | 457 | 	if (!ops->sysctl_header) | 
 | 458 | 		goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 459 | 	return 0; | 
| Heiko Carstens | 37e3a6a | 2007-11-20 11:13:34 +0100 | [diff] [blame] | 460 | out: | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 461 | 	mutex_lock(&appldata_ops_mutex); | 
| Heiko Carstens | 37e3a6a | 2007-11-20 11:13:34 +0100 | [diff] [blame] | 462 | 	list_del(&ops->list); | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 463 | 	mutex_unlock(&appldata_ops_mutex); | 
| Heiko Carstens | 37e3a6a | 2007-11-20 11:13:34 +0100 | [diff] [blame] | 464 | 	kfree(ops->ctl_table); | 
 | 465 | 	return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 466 | } | 
 | 467 |  | 
 | 468 | /* | 
 | 469 |  * appldata_unregister_ops() | 
 | 470 |  * | 
 | 471 |  * update ops list, unregister /proc entries, stop DIAG if necessary | 
 | 472 |  */ | 
 | 473 | void appldata_unregister_ops(struct appldata_ops *ops) | 
 | 474 | { | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 475 | 	mutex_lock(&appldata_ops_mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 476 | 	list_del(&ops->list); | 
| Gerald Schaefer | b1ad171 | 2009-04-23 13:58:07 +0200 | [diff] [blame] | 477 | 	mutex_unlock(&appldata_ops_mutex); | 
| Al Viro | 330d57f | 2005-11-04 10:18:40 +0000 | [diff] [blame] | 478 | 	unregister_sysctl_table(ops->sysctl_header); | 
| Heiko Carstens | 37e3a6a | 2007-11-20 11:13:34 +0100 | [diff] [blame] | 479 | 	kfree(ops->ctl_table); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 480 | } | 
 | 481 | /********************** module-ops management <END> **************************/ | 
 | 482 |  | 
 | 483 |  | 
| Gerald Schaefer | 524dbcd | 2009-06-16 10:30:36 +0200 | [diff] [blame] | 484 | /**************************** suspend / resume *******************************/ | 
 | 485 | static int appldata_freeze(struct device *dev) | 
 | 486 | { | 
 | 487 | 	struct appldata_ops *ops; | 
 | 488 | 	int rc; | 
 | 489 | 	struct list_head *lh; | 
 | 490 |  | 
 | 491 | 	get_online_cpus(); | 
 | 492 | 	spin_lock(&appldata_timer_lock); | 
 | 493 | 	if (appldata_timer_active) { | 
 | 494 | 		__appldata_vtimer_setup(APPLDATA_DEL_TIMER); | 
 | 495 | 		appldata_timer_suspended = 1; | 
 | 496 | 	} | 
 | 497 | 	spin_unlock(&appldata_timer_lock); | 
 | 498 | 	put_online_cpus(); | 
 | 499 |  | 
 | 500 | 	mutex_lock(&appldata_ops_mutex); | 
 | 501 | 	list_for_each(lh, &appldata_ops_list) { | 
 | 502 | 		ops = list_entry(lh, struct appldata_ops, list); | 
 | 503 | 		if (ops->active == 1) { | 
 | 504 | 			rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, | 
 | 505 | 					(unsigned long) ops->data, ops->size, | 
 | 506 | 					ops->mod_lvl); | 
 | 507 | 			if (rc != 0) | 
 | 508 | 				pr_err("Stopping the data collection for %s " | 
 | 509 | 				       "failed with rc=%d\n", ops->name, rc); | 
 | 510 | 		} | 
 | 511 | 	} | 
 | 512 | 	mutex_unlock(&appldata_ops_mutex); | 
 | 513 | 	return 0; | 
 | 514 | } | 
 | 515 |  | 
 | 516 | static int appldata_restore(struct device *dev) | 
 | 517 | { | 
 | 518 | 	struct appldata_ops *ops; | 
 | 519 | 	int rc; | 
 | 520 | 	struct list_head *lh; | 
 | 521 |  | 
 | 522 | 	get_online_cpus(); | 
 | 523 | 	spin_lock(&appldata_timer_lock); | 
 | 524 | 	if (appldata_timer_suspended) { | 
 | 525 | 		__appldata_vtimer_setup(APPLDATA_ADD_TIMER); | 
 | 526 | 		appldata_timer_suspended = 0; | 
 | 527 | 	} | 
 | 528 | 	spin_unlock(&appldata_timer_lock); | 
 | 529 | 	put_online_cpus(); | 
 | 530 |  | 
 | 531 | 	mutex_lock(&appldata_ops_mutex); | 
 | 532 | 	list_for_each(lh, &appldata_ops_list) { | 
 | 533 | 		ops = list_entry(lh, struct appldata_ops, list); | 
 | 534 | 		if (ops->active == 1) { | 
 | 535 | 			ops->callback(ops->data);	// init record | 
 | 536 | 			rc = appldata_diag(ops->record_nr, | 
 | 537 | 					APPLDATA_START_INTERVAL_REC, | 
 | 538 | 					(unsigned long) ops->data, ops->size, | 
 | 539 | 					ops->mod_lvl); | 
 | 540 | 			if (rc != 0) { | 
 | 541 | 				pr_err("Starting the data collection for %s " | 
 | 542 | 				       "failed with rc=%d\n", ops->name, rc); | 
 | 543 | 			} | 
 | 544 | 		} | 
 | 545 | 	} | 
 | 546 | 	mutex_unlock(&appldata_ops_mutex); | 
 | 547 | 	return 0; | 
 | 548 | } | 
 | 549 |  | 
 | 550 | static int appldata_thaw(struct device *dev) | 
 | 551 | { | 
 | 552 | 	return appldata_restore(dev); | 
 | 553 | } | 
 | 554 |  | 
 | 555 | static struct dev_pm_ops appldata_pm_ops = { | 
 | 556 | 	.freeze		= appldata_freeze, | 
 | 557 | 	.thaw		= appldata_thaw, | 
 | 558 | 	.restore	= appldata_restore, | 
 | 559 | }; | 
 | 560 |  | 
 | 561 | static struct platform_driver appldata_pdrv = { | 
 | 562 | 	.driver = { | 
 | 563 | 		.name	= "appldata", | 
 | 564 | 		.owner	= THIS_MODULE, | 
 | 565 | 		.pm	= &appldata_pm_ops, | 
 | 566 | 	}, | 
 | 567 | }; | 
 | 568 | /************************* suspend / resume <END> ****************************/ | 
 | 569 |  | 
 | 570 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 571 | /******************************* init / exit *********************************/ | 
 | 572 |  | 
| Heiko Carstens | 84b36a8 | 2007-06-19 13:10:03 +0200 | [diff] [blame] | 573 | static void __cpuinit appldata_online_cpu(int cpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 574 | { | 
 | 575 | 	init_virt_timer(&per_cpu(appldata_timer, cpu)); | 
 | 576 | 	per_cpu(appldata_timer, cpu).function = appldata_timer_function; | 
 | 577 | 	per_cpu(appldata_timer, cpu).data = (unsigned long) | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 578 | 		&appldata_work; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 579 | 	atomic_inc(&appldata_expire_count); | 
 | 580 | 	spin_lock(&appldata_timer_lock); | 
 | 581 | 	__appldata_vtimer_setup(APPLDATA_MOD_TIMER); | 
 | 582 | 	spin_unlock(&appldata_timer_lock); | 
 | 583 | } | 
 | 584 |  | 
| Satyam Sharma | 076fc80 | 2007-10-12 16:11:32 +0200 | [diff] [blame] | 585 | static void __cpuinit appldata_offline_cpu(int cpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 586 | { | 
 | 587 | 	del_virt_timer(&per_cpu(appldata_timer, cpu)); | 
 | 588 | 	if (atomic_dec_and_test(&appldata_expire_count)) { | 
 | 589 | 		atomic_set(&appldata_expire_count, num_online_cpus()); | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 590 | 		queue_work(appldata_wq, &appldata_work); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 591 | 	} | 
 | 592 | 	spin_lock(&appldata_timer_lock); | 
 | 593 | 	__appldata_vtimer_setup(APPLDATA_MOD_TIMER); | 
 | 594 | 	spin_unlock(&appldata_timer_lock); | 
 | 595 | } | 
 | 596 |  | 
| Satyam Sharma | 11b8bf0 | 2007-10-12 16:11:31 +0200 | [diff] [blame] | 597 | static int __cpuinit appldata_cpu_notify(struct notifier_block *self, | 
 | 598 | 					 unsigned long action, | 
 | 599 | 					 void *hcpu) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 600 | { | 
 | 601 | 	switch (action) { | 
 | 602 | 	case CPU_ONLINE: | 
| Rafael J. Wysocki | 8bb7844 | 2007-05-09 02:35:10 -0700 | [diff] [blame] | 603 | 	case CPU_ONLINE_FROZEN: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 604 | 		appldata_online_cpu((long) hcpu); | 
 | 605 | 		break; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 606 | 	case CPU_DEAD: | 
| Rafael J. Wysocki | 8bb7844 | 2007-05-09 02:35:10 -0700 | [diff] [blame] | 607 | 	case CPU_DEAD_FROZEN: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 608 | 		appldata_offline_cpu((long) hcpu); | 
 | 609 | 		break; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 610 | 	default: | 
 | 611 | 		break; | 
 | 612 | 	} | 
 | 613 | 	return NOTIFY_OK; | 
 | 614 | } | 
 | 615 |  | 
| Heiko Carstens | 84b36a8 | 2007-06-19 13:10:03 +0200 | [diff] [blame] | 616 | static struct notifier_block __cpuinitdata appldata_nb = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 617 | 	.notifier_call = appldata_cpu_notify, | 
 | 618 | }; | 
 | 619 |  | 
 | 620 | /* | 
 | 621 |  * appldata_init() | 
 | 622 |  * | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 623 |  * init timer, register /proc entries | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 624 |  */ | 
 | 625 | static int __init appldata_init(void) | 
 | 626 | { | 
| Gerald Schaefer | 524dbcd | 2009-06-16 10:30:36 +0200 | [diff] [blame] | 627 | 	int i, rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 628 |  | 
| Gerald Schaefer | 524dbcd | 2009-06-16 10:30:36 +0200 | [diff] [blame] | 629 | 	rc = platform_driver_register(&appldata_pdrv); | 
 | 630 | 	if (rc) | 
 | 631 | 		return rc; | 
 | 632 |  | 
 | 633 | 	appldata_pdev = platform_device_register_simple("appldata", -1, NULL, | 
 | 634 | 							0); | 
 | 635 | 	if (IS_ERR(appldata_pdev)) { | 
 | 636 | 		rc = PTR_ERR(appldata_pdev); | 
 | 637 | 		goto out_driver; | 
 | 638 | 	} | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 639 | 	appldata_wq = create_singlethread_workqueue("appldata"); | 
| Gerald Schaefer | 524dbcd | 2009-06-16 10:30:36 +0200 | [diff] [blame] | 640 | 	if (!appldata_wq) { | 
 | 641 | 		rc = -ENOMEM; | 
 | 642 | 		goto out_device; | 
 | 643 | 	} | 
| Gerald Schaefer | f26d583 | 2005-06-04 15:43:33 -0700 | [diff] [blame] | 644 |  | 
| Gerald Schaefer | 1760537 | 2008-05-30 10:03:28 +0200 | [diff] [blame] | 645 | 	get_online_cpus(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 646 | 	for_each_online_cpu(i) | 
 | 647 | 		appldata_online_cpu(i); | 
| Gerald Schaefer | 1760537 | 2008-05-30 10:03:28 +0200 | [diff] [blame] | 648 | 	put_online_cpus(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 649 |  | 
 | 650 | 	/* Register cpu hotplug notifier */ | 
| Chandra Seetharaman | be6b5a3 | 2006-07-30 03:03:37 -0700 | [diff] [blame] | 651 | 	register_hotcpu_notifier(&appldata_nb); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 652 |  | 
| Eric W. Biederman | 0b4d414 | 2007-02-14 00:34:09 -0800 | [diff] [blame] | 653 | 	appldata_sysctl_header = register_sysctl_table(appldata_dir_table); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 654 | 	return 0; | 
| Gerald Schaefer | 524dbcd | 2009-06-16 10:30:36 +0200 | [diff] [blame] | 655 |  | 
 | 656 | out_device: | 
 | 657 | 	platform_device_unregister(appldata_pdev); | 
 | 658 | out_driver: | 
 | 659 | 	platform_driver_unregister(&appldata_pdrv); | 
 | 660 | 	return rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 661 | } | 
 | 662 |  | 
| Satyam Sharma | 076fc80 | 2007-10-12 16:11:32 +0200 | [diff] [blame] | 663 | __initcall(appldata_init); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 664 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 665 | /**************************** init / exit <END> ******************************/ | 
 | 666 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 667 | EXPORT_SYMBOL_GPL(appldata_register_ops); | 
 | 668 | EXPORT_SYMBOL_GPL(appldata_unregister_ops); | 
| Gerald Schaefer | 5b5dd21 | 2006-06-29 15:08:35 +0200 | [diff] [blame] | 669 | EXPORT_SYMBOL_GPL(appldata_diag); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 670 |  | 
| Gerald Schaefer | 0c3252d | 2008-07-14 09:57:27 +0200 | [diff] [blame] | 671 | #ifdef CONFIG_SWAP | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 672 | EXPORT_SYMBOL_GPL(si_swapinfo); | 
| Gerald Schaefer | 0c3252d | 2008-07-14 09:57:27 +0200 | [diff] [blame] | 673 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 674 | EXPORT_SYMBOL_GPL(nr_threads); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 675 | EXPORT_SYMBOL_GPL(nr_running); | 
 | 676 | EXPORT_SYMBOL_GPL(nr_iowait); |