xref: /openbmc/linux/arch/s390/appldata/appldata_base.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
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  *
8  * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH.
9  *
10  * Author: Gerald Schaefer <geraldsc@de.ibm.com>
11  */
12 
13 #include <linux/config.h>
14 #include <linux/module.h>
15 #include <linux/init.h>
16 #include <linux/slab.h>
17 #include <linux/errno.h>
18 #include <asm/uaccess.h>
19 #include <asm/io.h>
20 #include <asm/smp.h>
21 #include <linux/interrupt.h>
22 #include <linux/proc_fs.h>
23 #include <linux/page-flags.h>
24 #include <linux/swap.h>
25 #include <linux/pagemap.h>
26 #include <linux/sysctl.h>
27 #include <asm/timer.h>
28 //#include <linux/kernel_stat.h>
29 #include <linux/notifier.h>
30 #include <linux/cpu.h>
31 
32 #include "appldata.h"
33 
34 
35 #define MY_PRINT_NAME	"appldata"		/* for debug messages, etc. */
36 #define APPLDATA_CPU_INTERVAL	10000		/* default (CPU) time for
37 						   sampling interval in
38 						   milliseconds */
39 
40 #define TOD_MICRO	0x01000			/* nr. of TOD clock units
41 						   for 1 microsecond */
42 #ifndef CONFIG_ARCH_S390X
43 
44 #define APPLDATA_START_INTERVAL_REC 0x00   	/* Function codes for */
45 #define APPLDATA_STOP_REC	    0x01	/* DIAG 0xDC	  */
46 #define APPLDATA_GEN_EVENT_RECORD   0x02
47 #define APPLDATA_START_CONFIG_REC   0x03
48 
49 #else
50 
51 #define APPLDATA_START_INTERVAL_REC 0x80
52 #define APPLDATA_STOP_REC   	    0x81
53 #define APPLDATA_GEN_EVENT_RECORD   0x82
54 #define APPLDATA_START_CONFIG_REC   0x83
55 
56 #endif /* CONFIG_ARCH_S390X */
57 
58 
59 /*
60  * Parameter list for DIAGNOSE X'DC'
61  */
62 #ifndef CONFIG_ARCH_S390X
63 struct appldata_parameter_list {
64 	u16 diag;		/* The DIAGNOSE code X'00DC'          */
65 	u8  function;		/* The function code for the DIAGNOSE */
66 	u8  parlist_length;	/* Length of the parameter list       */
67 	u32 product_id_addr;	/* Address of the 16-byte product ID  */
68 	u16 reserved;
69 	u16 buffer_length;	/* Length of the application data buffer  */
70 	u32 buffer_addr;	/* Address of the application data buffer */
71 };
72 #else
73 struct appldata_parameter_list {
74 	u16 diag;
75 	u8  function;
76 	u8  parlist_length;
77 	u32 unused01;
78 	u16 reserved;
79 	u16 buffer_length;
80 	u32 unused02;
81 	u64 product_id_addr;
82 	u64 buffer_addr;
83 };
84 #endif /* CONFIG_ARCH_S390X */
85 
86 /*
87  * /proc entries (sysctl)
88  */
89 static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
90 static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
91 				  void __user *buffer, size_t *lenp, loff_t *ppos);
92 static int appldata_interval_handler(ctl_table *ctl, int write,
93 					 struct file *filp,
94 					 void __user *buffer,
95 					 size_t *lenp, loff_t *ppos);
96 
97 static struct ctl_table_header *appldata_sysctl_header;
98 static struct ctl_table appldata_table[] = {
99 	{
100 		.ctl_name	= CTL_APPLDATA_TIMER,
101 		.procname	= "timer",
102 		.mode		= S_IRUGO | S_IWUSR,
103 		.proc_handler	= &appldata_timer_handler,
104 	},
105 	{
106 		.ctl_name	= CTL_APPLDATA_INTERVAL,
107 		.procname	= "interval",
108 		.mode		= S_IRUGO | S_IWUSR,
109 		.proc_handler	= &appldata_interval_handler,
110 	},
111 	{ .ctl_name = 0 }
112 };
113 
114 static struct ctl_table appldata_dir_table[] = {
115 	{
116 		.ctl_name	= CTL_APPLDATA,
117 		.procname	= appldata_proc_name,
118 		.maxlen		= 0,
119 		.mode		= S_IRUGO | S_IXUGO,
120 		.child		= appldata_table,
121 	},
122 	{ .ctl_name = 0 }
123 };
124 
125 /*
126  * Timer
127  */
128 DEFINE_PER_CPU(struct vtimer_list, appldata_timer);
129 static atomic_t appldata_expire_count = ATOMIC_INIT(0);
130 
131 static DEFINE_SPINLOCK(appldata_timer_lock);
132 static int appldata_interval = APPLDATA_CPU_INTERVAL;
133 static int appldata_timer_active;
134 
135 /*
136  * Tasklet
137  */
138 static struct tasklet_struct appldata_tasklet_struct;
139 
140 /*
141  * Ops list
142  */
143 static DEFINE_SPINLOCK(appldata_ops_lock);
144 static LIST_HEAD(appldata_ops_list);
145 
146 
147 /************************* timer, tasklet, DIAG ******************************/
148 /*
149  * appldata_timer_function()
150  *
151  * schedule tasklet and reschedule timer
152  */
153 static void appldata_timer_function(unsigned long data, struct pt_regs *regs)
154 {
155 	P_DEBUG("   -= Timer =-\n");
156 	P_DEBUG("CPU: %i, expire_count: %i\n", smp_processor_id(),
157 		atomic_read(&appldata_expire_count));
158 	if (atomic_dec_and_test(&appldata_expire_count)) {
159 		atomic_set(&appldata_expire_count, num_online_cpus());
160 		tasklet_schedule((struct tasklet_struct *) data);
161 	}
162 }
163 
164 /*
165  * appldata_tasklet_function()
166  *
167  * call data gathering function for each (active) module
168  */
169 static void appldata_tasklet_function(unsigned long data)
170 {
171 	struct list_head *lh;
172 	struct appldata_ops *ops;
173 	int i;
174 
175 	P_DEBUG("  -= Tasklet =-\n");
176 	i = 0;
177 	spin_lock(&appldata_ops_lock);
178 	list_for_each(lh, &appldata_ops_list) {
179 		ops = list_entry(lh, struct appldata_ops, list);
180 		P_DEBUG("list_for_each loop: %i) active = %u, name = %s\n",
181 			++i, ops->active, ops->name);
182 		if (ops->active == 1) {
183 			ops->callback(ops->data);
184 		}
185 	}
186 	spin_unlock(&appldata_ops_lock);
187 }
188 
189 /*
190  * appldata_diag()
191  *
192  * prepare parameter list, issue DIAG 0xDC
193  */
194 static int appldata_diag(char record_nr, u16 function, unsigned long buffer,
195 			u16 length)
196 {
197 	unsigned long ry;
198 	struct appldata_product_id {
199 		char prod_nr[7];			/* product nr. */
200 		char prod_fn[2];			/* product function */
201 		char record_nr;				/* record nr. */
202 		char version_nr[2];			/* version */
203 		char release_nr[2];			/* release */
204 		char mod_lvl[2];			/* modification lvl. */
205 	} appldata_product_id = {
206 	/* all strings are EBCDIC, record_nr is byte */
207 		.prod_nr    = {0xD3, 0xC9, 0xD5, 0xE4,
208 				0xE7, 0xD2, 0xD9},	/* "LINUXKR" */
209 		.prod_fn    = {0xD5, 0xD3},		/* "NL" */
210 		.record_nr  = record_nr,
211 		.version_nr = {0xF2, 0xF6},		/* "26" */
212 		.release_nr = {0xF0, 0xF1},		/* "01" */
213 		.mod_lvl    = {0xF0, 0xF0},		/* "00" */
214 	};
215 	struct appldata_parameter_list appldata_parameter_list = {
216 				.diag = 0xDC,
217 				.function = function,
218 				.parlist_length =
219 					sizeof(appldata_parameter_list),
220 				.buffer_length = length,
221 				.product_id_addr =
222 					(unsigned long) &appldata_product_id,
223 				.buffer_addr = virt_to_phys((void *) buffer)
224 	};
225 
226 	if (!MACHINE_IS_VM)
227 		return -ENOSYS;
228 	ry = -1;
229 	asm volatile(
230 			"diag %1,%0,0xDC\n\t"
231 			: "=d" (ry) : "d" (&(appldata_parameter_list)) : "cc");
232 	return (int) ry;
233 }
234 /********************** timer, tasklet, DIAG <END> ***************************/
235 
236 
237 /****************************** /proc stuff **********************************/
238 
239 /*
240  * appldata_mod_vtimer_wrap()
241  *
242  * wrapper function for mod_virt_timer(), because smp_call_function_on()
243  * accepts only one parameter.
244  */
245 static void __appldata_mod_vtimer_wrap(void *p) {
246 	struct {
247 		struct vtimer_list *timer;
248 		u64    expires;
249 	} *args = p;
250 	mod_virt_timer(args->timer, args->expires);
251 }
252 
253 #define APPLDATA_ADD_TIMER	0
254 #define APPLDATA_DEL_TIMER	1
255 #define APPLDATA_MOD_TIMER	2
256 
257 /*
258  * __appldata_vtimer_setup()
259  *
260  * Add, delete or modify virtual timers on all online cpus.
261  * The caller needs to get the appldata_timer_lock spinlock.
262  */
263 static void
264 __appldata_vtimer_setup(int cmd)
265 {
266 	u64 per_cpu_interval;
267 	int i;
268 
269 	switch (cmd) {
270 	case APPLDATA_ADD_TIMER:
271 		if (appldata_timer_active)
272 			break;
273 		per_cpu_interval = (u64) (appldata_interval*1000 /
274 					  num_online_cpus()) * TOD_MICRO;
275 		for_each_online_cpu(i) {
276 			per_cpu(appldata_timer, i).expires = per_cpu_interval;
277 			smp_call_function_on(add_virt_timer_periodic,
278 					     &per_cpu(appldata_timer, i),
279 					     0, 1, i);
280 		}
281 		appldata_timer_active = 1;
282 		P_INFO("Monitoring timer started.\n");
283 		break;
284 	case APPLDATA_DEL_TIMER:
285 		for_each_online_cpu(i)
286 			del_virt_timer(&per_cpu(appldata_timer, i));
287 		if (!appldata_timer_active)
288 			break;
289 		appldata_timer_active = 0;
290 		atomic_set(&appldata_expire_count, num_online_cpus());
291 		P_INFO("Monitoring timer stopped.\n");
292 		break;
293 	case APPLDATA_MOD_TIMER:
294 		per_cpu_interval = (u64) (appldata_interval*1000 /
295 					  num_online_cpus()) * TOD_MICRO;
296 		if (!appldata_timer_active)
297 			break;
298 		for_each_online_cpu(i) {
299 			struct {
300 				struct vtimer_list *timer;
301 				u64    expires;
302 			} args;
303 			args.timer = &per_cpu(appldata_timer, i);
304 			args.expires = per_cpu_interval;
305 			smp_call_function_on(__appldata_mod_vtimer_wrap,
306 					     &args, 0, 1, i);
307 		}
308 	}
309 }
310 
311 /*
312  * appldata_timer_handler()
313  *
314  * Start/Stop timer, show status of timer (0 = not active, 1 = active)
315  */
316 static int
317 appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
318 			   void __user *buffer, size_t *lenp, loff_t *ppos)
319 {
320 	int len;
321 	char buf[2];
322 
323 	if (!*lenp || *ppos) {
324 		*lenp = 0;
325 		return 0;
326 	}
327 	if (!write) {
328 		len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n");
329 		if (len > *lenp)
330 			len = *lenp;
331 		if (copy_to_user(buffer, buf, len))
332 			return -EFAULT;
333 		goto out;
334 	}
335 	len = *lenp;
336 	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
337 		return -EFAULT;
338 	spin_lock(&appldata_timer_lock);
339 	if (buf[0] == '1')
340 		__appldata_vtimer_setup(APPLDATA_ADD_TIMER);
341 	else if (buf[0] == '0')
342 		__appldata_vtimer_setup(APPLDATA_DEL_TIMER);
343 	spin_unlock(&appldata_timer_lock);
344 out:
345 	*lenp = len;
346 	*ppos += len;
347 	return 0;
348 }
349 
350 /*
351  * appldata_interval_handler()
352  *
353  * Set (CPU) timer interval for collection of data (in milliseconds), show
354  * current timer interval.
355  */
356 static int
357 appldata_interval_handler(ctl_table *ctl, int write, struct file *filp,
358 			   void __user *buffer, size_t *lenp, loff_t *ppos)
359 {
360 	int len, interval;
361 	char buf[16];
362 
363 	if (!*lenp || *ppos) {
364 		*lenp = 0;
365 		return 0;
366 	}
367 	if (!write) {
368 		len = sprintf(buf, "%i\n", appldata_interval);
369 		if (len > *lenp)
370 			len = *lenp;
371 		if (copy_to_user(buffer, buf, len))
372 			return -EFAULT;
373 		goto out;
374 	}
375 	len = *lenp;
376 	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) {
377 		return -EFAULT;
378 	}
379 	sscanf(buf, "%i", &interval);
380 	if (interval <= 0) {
381 		P_ERROR("Timer CPU interval has to be > 0!\n");
382 		return -EINVAL;
383 	}
384 
385 	spin_lock(&appldata_timer_lock);
386 	appldata_interval = interval;
387 	__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
388 	spin_unlock(&appldata_timer_lock);
389 
390 	P_INFO("Monitoring CPU interval set to %u milliseconds.\n",
391 		 interval);
392 out:
393 	*lenp = len;
394 	*ppos += len;
395 	return 0;
396 }
397 
398 /*
399  * appldata_generic_handler()
400  *
401  * Generic start/stop monitoring and DIAG, show status of
402  * monitoring (0 = not in process, 1 = in process)
403  */
404 static int
405 appldata_generic_handler(ctl_table *ctl, int write, struct file *filp,
406 			   void __user *buffer, size_t *lenp, loff_t *ppos)
407 {
408 	struct appldata_ops *ops = NULL, *tmp_ops;
409 	int rc, len, found;
410 	char buf[2];
411 	struct list_head *lh;
412 
413 	found = 0;
414 	spin_lock_bh(&appldata_ops_lock);
415 	list_for_each(lh, &appldata_ops_list) {
416 		tmp_ops = list_entry(lh, struct appldata_ops, list);
417 		if (&tmp_ops->ctl_table[2] == ctl) {
418 			found = 1;
419 		}
420 	}
421 	if (!found) {
422 		spin_unlock_bh(&appldata_ops_lock);
423 		return -ENODEV;
424 	}
425 	ops = ctl->data;
426 	if (!try_module_get(ops->owner)) {	// protect this function
427 		spin_unlock_bh(&appldata_ops_lock);
428 		return -ENODEV;
429 	}
430 	spin_unlock_bh(&appldata_ops_lock);
431 
432 	if (!*lenp || *ppos) {
433 		*lenp = 0;
434 		module_put(ops->owner);
435 		return 0;
436 	}
437 	if (!write) {
438 		len = sprintf(buf, ops->active ? "1\n" : "0\n");
439 		if (len > *lenp)
440 			len = *lenp;
441 		if (copy_to_user(buffer, buf, len)) {
442 			module_put(ops->owner);
443 			return -EFAULT;
444 		}
445 		goto out;
446 	}
447 	len = *lenp;
448 	if (copy_from_user(buf, buffer,
449 			   len > sizeof(buf) ? sizeof(buf) : len)) {
450 		module_put(ops->owner);
451 		return -EFAULT;
452 	}
453 
454 	spin_lock_bh(&appldata_ops_lock);
455 	if ((buf[0] == '1') && (ops->active == 0)) {
456 		if (!try_module_get(ops->owner)) {	// protect tasklet
457 			spin_unlock_bh(&appldata_ops_lock);
458 			module_put(ops->owner);
459 			return -ENODEV;
460 		}
461 		ops->active = 1;
462 		ops->callback(ops->data);	// init record
463 		rc = appldata_diag(ops->record_nr,
464 					APPLDATA_START_INTERVAL_REC,
465 					(unsigned long) ops->data, ops->size);
466 		if (rc != 0) {
467 			P_ERROR("START DIAG 0xDC for %s failed, "
468 				"return code: %d\n", ops->name, rc);
469 			module_put(ops->owner);
470 			ops->active = 0;
471 		} else {
472 			P_INFO("Monitoring %s data enabled, "
473 				"DIAG 0xDC started.\n", ops->name);
474 		}
475 	} else if ((buf[0] == '0') && (ops->active == 1)) {
476 		ops->active = 0;
477 		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
478 				(unsigned long) ops->data, ops->size);
479 		if (rc != 0) {
480 			P_ERROR("STOP DIAG 0xDC for %s failed, "
481 				"return code: %d\n", ops->name, rc);
482 		} else {
483 			P_INFO("Monitoring %s data disabled, "
484 				"DIAG 0xDC stopped.\n", ops->name);
485 		}
486 		module_put(ops->owner);
487 	}
488 	spin_unlock_bh(&appldata_ops_lock);
489 out:
490 	*lenp = len;
491 	*ppos += len;
492 	module_put(ops->owner);
493 	return 0;
494 }
495 
496 /*************************** /proc stuff <END> *******************************/
497 
498 
499 /************************* module-ops management *****************************/
500 /*
501  * appldata_register_ops()
502  *
503  * update ops list, register /proc/sys entries
504  */
505 int appldata_register_ops(struct appldata_ops *ops)
506 {
507 	struct list_head *lh;
508 	struct appldata_ops *tmp_ops;
509 	int i;
510 
511 	i = 0;
512 
513 	if ((ops->size > APPLDATA_MAX_REC_SIZE) ||
514 		(ops->size < 0)){
515 		P_ERROR("Invalid size of %s record = %i, maximum = %i!\n",
516 			ops->name, ops->size, APPLDATA_MAX_REC_SIZE);
517 		return -ENOMEM;
518 	}
519 	if ((ops->ctl_nr == CTL_APPLDATA) ||
520 	    (ops->ctl_nr == CTL_APPLDATA_TIMER) ||
521 	    (ops->ctl_nr == CTL_APPLDATA_INTERVAL)) {
522 		P_ERROR("ctl_nr %i already in use!\n", ops->ctl_nr);
523 		return -EBUSY;
524 	}
525 	ops->ctl_table = kmalloc(4*sizeof(struct ctl_table), GFP_KERNEL);
526 	if (ops->ctl_table == NULL) {
527 		P_ERROR("Not enough memory for %s ctl_table!\n", ops->name);
528 		return -ENOMEM;
529 	}
530 	memset(ops->ctl_table, 0, 4*sizeof(struct ctl_table));
531 
532 	spin_lock_bh(&appldata_ops_lock);
533 	list_for_each(lh, &appldata_ops_list) {
534 		tmp_ops = list_entry(lh, struct appldata_ops, list);
535 		P_DEBUG("register_ops loop: %i) name = %s, ctl = %i\n",
536 			++i, tmp_ops->name, tmp_ops->ctl_nr);
537 		P_DEBUG("Comparing %s (ctl %i) with %s (ctl %i)\n",
538 			tmp_ops->name, tmp_ops->ctl_nr, ops->name,
539 			ops->ctl_nr);
540 		if (strncmp(tmp_ops->name, ops->name,
541 				APPLDATA_PROC_NAME_LENGTH) == 0) {
542 			P_ERROR("Name \"%s\" already registered!\n", ops->name);
543 			kfree(ops->ctl_table);
544 			spin_unlock_bh(&appldata_ops_lock);
545 			return -EBUSY;
546 		}
547 		if (tmp_ops->ctl_nr == ops->ctl_nr) {
548 			P_ERROR("ctl_nr %i already registered!\n", ops->ctl_nr);
549 			kfree(ops->ctl_table);
550 			spin_unlock_bh(&appldata_ops_lock);
551 			return -EBUSY;
552 		}
553 	}
554 	list_add(&ops->list, &appldata_ops_list);
555 	spin_unlock_bh(&appldata_ops_lock);
556 
557 	ops->ctl_table[0].ctl_name = CTL_APPLDATA;
558 	ops->ctl_table[0].procname = appldata_proc_name;
559 	ops->ctl_table[0].maxlen   = 0;
560 	ops->ctl_table[0].mode     = S_IRUGO | S_IXUGO;
561 	ops->ctl_table[0].child    = &ops->ctl_table[2];
562 
563 	ops->ctl_table[1].ctl_name = 0;
564 
565 	ops->ctl_table[2].ctl_name = ops->ctl_nr;
566 	ops->ctl_table[2].procname = ops->name;
567 	ops->ctl_table[2].mode     = S_IRUGO | S_IWUSR;
568 	ops->ctl_table[2].proc_handler = appldata_generic_handler;
569 	ops->ctl_table[2].data = ops;
570 
571 	ops->ctl_table[3].ctl_name = 0;
572 
573 	ops->sysctl_header = register_sysctl_table(ops->ctl_table,1);
574 
575 	P_INFO("%s-ops registered!\n", ops->name);
576 	return 0;
577 }
578 
579 /*
580  * appldata_unregister_ops()
581  *
582  * update ops list, unregister /proc entries, stop DIAG if necessary
583  */
584 void appldata_unregister_ops(struct appldata_ops *ops)
585 {
586 	spin_lock_bh(&appldata_ops_lock);
587 	unregister_sysctl_table(ops->sysctl_header);
588 	list_del(&ops->list);
589 	kfree(ops->ctl_table);
590 	ops->ctl_table = NULL;
591 	spin_unlock_bh(&appldata_ops_lock);
592 	P_INFO("%s-ops unregistered!\n", ops->name);
593 }
594 /********************** module-ops management <END> **************************/
595 
596 
597 /******************************* init / exit *********************************/
598 
599 static void
600 appldata_online_cpu(int cpu)
601 {
602 	init_virt_timer(&per_cpu(appldata_timer, cpu));
603 	per_cpu(appldata_timer, cpu).function = appldata_timer_function;
604 	per_cpu(appldata_timer, cpu).data = (unsigned long)
605 		&appldata_tasklet_struct;
606 	atomic_inc(&appldata_expire_count);
607 	spin_lock(&appldata_timer_lock);
608 	__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
609 	spin_unlock(&appldata_timer_lock);
610 }
611 
612 static void
613 appldata_offline_cpu(int cpu)
614 {
615 	del_virt_timer(&per_cpu(appldata_timer, cpu));
616 	if (atomic_dec_and_test(&appldata_expire_count)) {
617 		atomic_set(&appldata_expire_count, num_online_cpus());
618 		tasklet_schedule(&appldata_tasklet_struct);
619 	}
620 	spin_lock(&appldata_timer_lock);
621 	__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
622 	spin_unlock(&appldata_timer_lock);
623 }
624 
625 static int
626 appldata_cpu_notify(struct notifier_block *self,
627 		    unsigned long action, void *hcpu)
628 {
629 	switch (action) {
630 	case CPU_ONLINE:
631 		appldata_online_cpu((long) hcpu);
632 		break;
633 #ifdef CONFIG_HOTPLUG_CPU
634 	case CPU_DEAD:
635 		appldata_offline_cpu((long) hcpu);
636 		break;
637 #endif
638 	default:
639 		break;
640 	}
641 	return NOTIFY_OK;
642 }
643 
644 static struct notifier_block __devinitdata appldata_nb = {
645 	.notifier_call = appldata_cpu_notify,
646 };
647 
648 /*
649  * appldata_init()
650  *
651  * init timer and tasklet, register /proc entries
652  */
653 static int __init appldata_init(void)
654 {
655 	int i;
656 
657 	P_DEBUG("sizeof(parameter_list) = %lu\n",
658 		sizeof(struct appldata_parameter_list));
659 
660 	for_each_online_cpu(i)
661 		appldata_online_cpu(i);
662 
663 	/* Register cpu hotplug notifier */
664 	register_cpu_notifier(&appldata_nb);
665 
666 	appldata_sysctl_header = register_sysctl_table(appldata_dir_table, 1);
667 #ifdef MODULE
668 	appldata_dir_table[0].de->owner = THIS_MODULE;
669 	appldata_table[0].de->owner = THIS_MODULE;
670 	appldata_table[1].de->owner = THIS_MODULE;
671 #endif
672 
673 	tasklet_init(&appldata_tasklet_struct, appldata_tasklet_function, 0);
674 	P_DEBUG("Base interface initialized.\n");
675 	return 0;
676 }
677 
678 /*
679  * appldata_exit()
680  *
681  * stop timer and tasklet, unregister /proc entries
682  */
683 static void __exit appldata_exit(void)
684 {
685 	struct list_head *lh;
686 	struct appldata_ops *ops;
687 	int rc, i;
688 
689 	P_DEBUG("Unloading module ...\n");
690 	/*
691 	 * ops list should be empty, but just in case something went wrong...
692 	 */
693 	spin_lock_bh(&appldata_ops_lock);
694 	list_for_each(lh, &appldata_ops_list) {
695 		ops = list_entry(lh, struct appldata_ops, list);
696 		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
697 				(unsigned long) ops->data, ops->size);
698 		if (rc != 0) {
699 			P_ERROR("STOP DIAG 0xDC for %s failed, "
700 				"return code: %d\n", ops->name, rc);
701 		}
702 	}
703 	spin_unlock_bh(&appldata_ops_lock);
704 
705 	for_each_online_cpu(i)
706 		appldata_offline_cpu(i);
707 
708 	appldata_timer_active = 0;
709 
710 	unregister_sysctl_table(appldata_sysctl_header);
711 
712 	tasklet_kill(&appldata_tasklet_struct);
713 	P_DEBUG("... module unloaded!\n");
714 }
715 /**************************** init / exit <END> ******************************/
716 
717 
718 module_init(appldata_init);
719 module_exit(appldata_exit);
720 MODULE_LICENSE("GPL");
721 MODULE_AUTHOR("Gerald Schaefer");
722 MODULE_DESCRIPTION("Linux-VM Monitor Stream, base infrastructure");
723 
724 EXPORT_SYMBOL_GPL(appldata_register_ops);
725 EXPORT_SYMBOL_GPL(appldata_unregister_ops);
726 
727 #ifdef MODULE
728 /*
729  * Kernel symbols needed by appldata_mem and appldata_os modules.
730  * However, if this file is compiled as a module (for testing only), these
731  * symbols are not exported. In this case, we define them locally and export
732  * those.
733  */
734 void si_swapinfo(struct sysinfo *val)
735 {
736 	val->freeswap = -1ul;
737 	val->totalswap = -1ul;
738 }
739 
740 unsigned long avenrun[3] = {-1 - FIXED_1/200, -1 - FIXED_1/200,
741 				-1 - FIXED_1/200};
742 int nr_threads = -1;
743 
744 void get_full_page_state(struct page_state *ps)
745 {
746 	memset(ps, -1, sizeof(struct page_state));
747 }
748 
749 unsigned long nr_running(void)
750 {
751 	return -1;
752 }
753 
754 unsigned long nr_iowait(void)
755 {
756 	return -1;
757 }
758 
759 /*unsigned long nr_context_switches(void)
760 {
761 	return -1;
762 }*/
763 #endif /* MODULE */
764 EXPORT_SYMBOL_GPL(si_swapinfo);
765 EXPORT_SYMBOL_GPL(nr_threads);
766 EXPORT_SYMBOL_GPL(avenrun);
767 EXPORT_SYMBOL_GPL(get_full_page_state);
768 EXPORT_SYMBOL_GPL(nr_running);
769 EXPORT_SYMBOL_GPL(nr_iowait);
770 //EXPORT_SYMBOL_GPL(nr_context_switches);
771