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