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 <linux/interrupt.h> 18#include <linux/proc_fs.h> 19#include <linux/mm.h> 20#include <linux/swap.h> 21#include <linux/pagemap.h> 22#include <linux/sysctl.h> 23#include <linux/notifier.h> 24#include <linux/cpu.h> 25#include <linux/workqueue.h> 26#include <asm/appldata.h> 27#include <asm/timer.h> 28#include <asm/uaccess.h> 29#include <asm/io.h> 30#include <asm/smp.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 * /proc entries (sysctl) 44 */ 45static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata"; 46static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp, 47 void __user *buffer, size_t *lenp, loff_t *ppos); 48static int appldata_interval_handler(ctl_table *ctl, int write, 49 struct file *filp, 50 void __user *buffer, 51 size_t *lenp, loff_t *ppos); 52 53static struct ctl_table_header *appldata_sysctl_header; 54static struct ctl_table appldata_table[] = { 55 { 56 .ctl_name = CTL_APPLDATA_TIMER, 57 .procname = "timer", 58 .mode = S_IRUGO | S_IWUSR, 59 .proc_handler = &appldata_timer_handler, 60 }, 61 { 62 .ctl_name = CTL_APPLDATA_INTERVAL, 63 .procname = "interval", 64 .mode = S_IRUGO | S_IWUSR, 65 .proc_handler = &appldata_interval_handler, 66 }, 67 { .ctl_name = 0 } 68}; 69 70static struct ctl_table appldata_dir_table[] = { 71 { 72 .ctl_name = CTL_APPLDATA, 73 .procname = appldata_proc_name, 74 .maxlen = 0, 75 .mode = S_IRUGO | S_IXUGO, 76 .child = appldata_table, 77 }, 78 { .ctl_name = 0 } 79}; 80 81/* 82 * Timer 83 */ 84static DEFINE_PER_CPU(struct vtimer_list, appldata_timer); 85static atomic_t appldata_expire_count = ATOMIC_INIT(0); 86 87static DEFINE_SPINLOCK(appldata_timer_lock); 88static int appldata_interval = APPLDATA_CPU_INTERVAL; 89static int appldata_timer_active; 90 91/* 92 * Work queue 93 */ 94static struct workqueue_struct *appldata_wq; 95static void appldata_work_fn(struct work_struct *work); 96static DECLARE_WORK(appldata_work, appldata_work_fn); 97 98 99/* 100 * Ops list 101 */ 102static DEFINE_SPINLOCK(appldata_ops_lock); 103static LIST_HEAD(appldata_ops_list); 104 105 106/*************************** timer, work, DIAG *******************************/ 107/* 108 * appldata_timer_function() 109 * 110 * schedule work and reschedule timer 111 */ 112static void appldata_timer_function(unsigned long data) 113{ 114 P_DEBUG(" -= Timer =-\n"); 115 P_DEBUG("CPU: %i, expire_count: %i\n", smp_processor_id(), 116 atomic_read(&appldata_expire_count)); 117 if (atomic_dec_and_test(&appldata_expire_count)) { 118 atomic_set(&appldata_expire_count, num_online_cpus()); 119 queue_work(appldata_wq, (struct work_struct *) data); 120 } 121} 122 123/* 124 * appldata_work_fn() 125 * 126 * call data gathering function for each (active) module 127 */ 128static void appldata_work_fn(struct work_struct *work) 129{ 130 struct list_head *lh; 131 struct appldata_ops *ops; 132 int i; 133 134 P_DEBUG(" -= Work Queue =-\n"); 135 i = 0; 136 spin_lock(&appldata_ops_lock); 137 list_for_each(lh, &appldata_ops_list) { 138 ops = list_entry(lh, struct appldata_ops, list); 139 P_DEBUG("list_for_each loop: %i) active = %u, name = %s\n", 140 ++i, ops->active, ops->name); 141 if (ops->active == 1) { 142 ops->callback(ops->data); 143 } 144 } 145 spin_unlock(&appldata_ops_lock); 146} 147 148/* 149 * appldata_diag() 150 * 151 * prepare parameter list, issue DIAG 0xDC 152 */ 153int appldata_diag(char record_nr, u16 function, unsigned long buffer, 154 u16 length, char *mod_lvl) 155{ 156 struct appldata_product_id id = { 157 .prod_nr = {0xD3, 0xC9, 0xD5, 0xE4, 158 0xE7, 0xD2, 0xD9}, /* "LINUXKR" */ 159 .prod_fn = 0xD5D3, /* "NL" */ 160 .version_nr = 0xF2F6, /* "26" */ 161 .release_nr = 0xF0F1, /* "01" */ 162 }; 163 164 id.record_nr = record_nr; 165 id.mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1]; 166 return appldata_asm(&id, function, (void *) buffer, length); 167} 168/************************ timer, work, DIAG <END> ****************************/ 169 170 171/****************************** /proc stuff **********************************/ 172 173/* 174 * appldata_mod_vtimer_wrap() 175 * 176 * wrapper function for mod_virt_timer(), because smp_call_function_on() 177 * 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; 184 mod_virt_timer(args->timer, args->expires); 185} 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; 211 smp_call_function_on(add_virt_timer_periodic, 212 &per_cpu(appldata_timer, i), 213 0, 1, i); 214 } 215 appldata_timer_active = 1; 216 P_INFO("Monitoring timer started.\n"); 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()); 225 P_INFO("Monitoring timer stopped.\n"); 226 break; 227 case APPLDATA_MOD_TIMER: 228 per_cpu_interval = (u64) (appldata_interval*1000 / 229 num_online_cpus()) * TOD_MICRO; 230 if (!appldata_timer_active) 231 break; 232 for_each_online_cpu(i) { 233 struct { 234 struct vtimer_list *timer; 235 u64 expires; 236 } args; 237 args.timer = &per_cpu(appldata_timer, i); 238 args.expires = per_cpu_interval; 239 smp_call_function_on(__appldata_mod_vtimer_wrap, 240 &args, 0, 1, i); 241 } 242 } 243} 244 245/* 246 * appldata_timer_handler() 247 * 248 * Start/Stop timer, show status of timer (0 = not active, 1 = active) 249 */ 250static int 251appldata_timer_handler(ctl_table *ctl, int write, struct file *filp, 252 void __user *buffer, size_t *lenp, loff_t *ppos) 253{ 254 int len; 255 char buf[2]; 256 257 if (!*lenp || *ppos) { 258 *lenp = 0; 259 return 0; 260 } 261 if (!write) { 262 len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n"); 263 if (len > *lenp) 264 len = *lenp; 265 if (copy_to_user(buffer, buf, len)) 266 return -EFAULT; 267 goto out; 268 } 269 len = *lenp; 270 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) 271 return -EFAULT; 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); 278out: 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 291appldata_interval_handler(ctl_table *ctl, int write, struct file *filp, 292 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 } 313 interval = 0; 314 sscanf(buf, "%i", &interval); 315 if (interval <= 0) { 316 P_ERROR("Timer CPU interval has to be > 0!\n"); 317 return -EINVAL; 318 } 319 320 spin_lock(&appldata_timer_lock); 321 appldata_interval = interval; 322 __appldata_vtimer_setup(APPLDATA_MOD_TIMER); 323 spin_unlock(&appldata_timer_lock); 324 325 P_INFO("Monitoring CPU interval set to %u milliseconds.\n", 326 interval); 327out: 328 *lenp = len; 329 *ppos += len; 330 return 0; 331} 332 333/* 334 * appldata_generic_handler() 335 * 336 * Generic start/stop monitoring and DIAG, show status of 337 * monitoring (0 = not in process, 1 = in process) 338 */ 339static int 340appldata_generic_handler(ctl_table *ctl, int write, struct file *filp, 341 void __user *buffer, size_t *lenp, loff_t *ppos) 342{ 343 struct appldata_ops *ops = NULL, *tmp_ops; 344 int rc, len, found; 345 char buf[2]; 346 struct list_head *lh; 347 348 found = 0; 349 spin_lock(&appldata_ops_lock); 350 list_for_each(lh, &appldata_ops_list) { 351 tmp_ops = list_entry(lh, struct appldata_ops, list); 352 if (&tmp_ops->ctl_table[2] == ctl) { 353 found = 1; 354 } 355 } 356 if (!found) { 357 spin_unlock(&appldata_ops_lock); 358 return -ENODEV; 359 } 360 ops = ctl->data; 361 if (!try_module_get(ops->owner)) { // protect this function 362 spin_unlock(&appldata_ops_lock); 363 return -ENODEV; 364 } 365 spin_unlock(&appldata_ops_lock); 366 367 if (!*lenp || *ppos) { 368 *lenp = 0; 369 module_put(ops->owner); 370 return 0; 371 } 372 if (!write) { 373 len = sprintf(buf, ops->active ? "1\n" : "0\n"); 374 if (len > *lenp) 375 len = *lenp; 376 if (copy_to_user(buffer, buf, len)) { 377 module_put(ops->owner); 378 return -EFAULT; 379 } 380 goto out; 381 } 382 len = *lenp; 383 if (copy_from_user(buf, buffer, 384 len > sizeof(buf) ? sizeof(buf) : len)) { 385 module_put(ops->owner); 386 return -EFAULT; 387 } 388 389 spin_lock(&appldata_ops_lock); 390 if ((buf[0] == '1') && (ops->active == 0)) { 391 // protect work queue callback 392 if (!try_module_get(ops->owner)) { 393 spin_unlock(&appldata_ops_lock); 394 module_put(ops->owner); 395 return -ENODEV; 396 } 397 ops->callback(ops->data); // init record 398 rc = appldata_diag(ops->record_nr, 399 APPLDATA_START_INTERVAL_REC, 400 (unsigned long) ops->data, ops->size, 401 ops->mod_lvl); 402 if (rc != 0) { 403 P_ERROR("START DIAG 0xDC for %s failed, " 404 "return code: %d\n", ops->name, rc); 405 module_put(ops->owner); 406 } else { 407 P_INFO("Monitoring %s data enabled, " 408 "DIAG 0xDC started.\n", ops->name); 409 ops->active = 1; 410 } 411 } else if ((buf[0] == '0') && (ops->active == 1)) { 412 ops->active = 0; 413 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, 414 (unsigned long) ops->data, ops->size, 415 ops->mod_lvl); 416 if (rc != 0) { 417 P_ERROR("STOP DIAG 0xDC for %s failed, " 418 "return code: %d\n", ops->name, rc); 419 } else { 420 P_INFO("Monitoring %s data disabled, " 421 "DIAG 0xDC stopped.\n", ops->name); 422 } 423 module_put(ops->owner); 424 } 425 spin_unlock(&appldata_ops_lock); 426out: 427 *lenp = len; 428 *ppos += len; 429 module_put(ops->owner); 430 return 0; 431} 432 433/*************************** /proc stuff <END> *******************************/ 434 435 436/************************* module-ops management *****************************/ 437/* 438 * appldata_register_ops() 439 * 440 * update ops list, register /proc/sys entries 441 */ 442int appldata_register_ops(struct appldata_ops *ops) 443{ 444 struct list_head *lh; 445 struct appldata_ops *tmp_ops; 446 int i; 447 448 i = 0; 449 450 if ((ops->size > APPLDATA_MAX_REC_SIZE) || 451 (ops->size < 0)){ 452 P_ERROR("Invalid size of %s record = %i, maximum = %i!\n", 453 ops->name, ops->size, APPLDATA_MAX_REC_SIZE); 454 return -ENOMEM; 455 } 456 if ((ops->ctl_nr == CTL_APPLDATA) || 457 (ops->ctl_nr == CTL_APPLDATA_TIMER) || 458 (ops->ctl_nr == CTL_APPLDATA_INTERVAL)) { 459 P_ERROR("ctl_nr %i already in use!\n", ops->ctl_nr); 460 return -EBUSY; 461 } 462 ops->ctl_table = kzalloc(4*sizeof(struct ctl_table), GFP_KERNEL); 463 if (ops->ctl_table == NULL) { 464 P_ERROR("Not enough memory for %s ctl_table!\n", ops->name); 465 return -ENOMEM; 466 } 467 468 spin_lock(&appldata_ops_lock); 469 list_for_each(lh, &appldata_ops_list) { 470 tmp_ops = list_entry(lh, struct appldata_ops, list); 471 P_DEBUG("register_ops loop: %i) name = %s, ctl = %i\n", 472 ++i, tmp_ops->name, tmp_ops->ctl_nr); 473 P_DEBUG("Comparing %s (ctl %i) with %s (ctl %i)\n", 474 tmp_ops->name, tmp_ops->ctl_nr, ops->name, 475 ops->ctl_nr); 476 if (strncmp(tmp_ops->name, ops->name, 477 APPLDATA_PROC_NAME_LENGTH) == 0) { 478 P_ERROR("Name \"%s\" already registered!\n", ops->name); 479 kfree(ops->ctl_table); 480 spin_unlock(&appldata_ops_lock); 481 return -EBUSY; 482 } 483 if (tmp_ops->ctl_nr == ops->ctl_nr) { 484 P_ERROR("ctl_nr %i already registered!\n", ops->ctl_nr); 485 kfree(ops->ctl_table); 486 spin_unlock(&appldata_ops_lock); 487 return -EBUSY; 488 } 489 } 490 list_add(&ops->list, &appldata_ops_list); 491 spin_unlock(&appldata_ops_lock); 492 493 ops->ctl_table[0].ctl_name = CTL_APPLDATA; 494 ops->ctl_table[0].procname = appldata_proc_name; 495 ops->ctl_table[0].maxlen = 0; 496 ops->ctl_table[0].mode = S_IRUGO | S_IXUGO; 497 ops->ctl_table[0].child = &ops->ctl_table[2]; 498 499 ops->ctl_table[1].ctl_name = 0; 500 501 ops->ctl_table[2].ctl_name = ops->ctl_nr; 502 ops->ctl_table[2].procname = ops->name; 503 ops->ctl_table[2].mode = S_IRUGO | S_IWUSR; 504 ops->ctl_table[2].proc_handler = appldata_generic_handler; 505 ops->ctl_table[2].data = ops; 506 507 ops->ctl_table[3].ctl_name = 0; 508 509 ops->sysctl_header = register_sysctl_table(ops->ctl_table); 510 511 P_INFO("%s-ops registered!\n", ops->name); 512 return 0; 513} 514 515/* 516 * appldata_unregister_ops() 517 * 518 * update ops list, unregister /proc entries, stop DIAG if necessary 519 */ 520void appldata_unregister_ops(struct appldata_ops *ops) 521{ 522 void *table; 523 spin_lock(&appldata_ops_lock); 524 list_del(&ops->list); 525 /* at that point any incoming access will fail */ 526 table = ops->ctl_table; 527 ops->ctl_table = NULL; 528 spin_unlock(&appldata_ops_lock); 529 unregister_sysctl_table(ops->sysctl_header); 530 kfree(table); 531 P_INFO("%s-ops unregistered!\n", ops->name); 532} 533/********************** module-ops management <END> **************************/ 534 535 536/******************************* init / exit *********************************/ 537 538static void __cpuinit appldata_online_cpu(int cpu) 539{ 540 init_virt_timer(&per_cpu(appldata_timer, cpu)); 541 per_cpu(appldata_timer, cpu).function = appldata_timer_function; 542 per_cpu(appldata_timer, cpu).data = (unsigned long) 543 &appldata_work; 544 atomic_inc(&appldata_expire_count); 545 spin_lock(&appldata_timer_lock); 546 __appldata_vtimer_setup(APPLDATA_MOD_TIMER); 547 spin_unlock(&appldata_timer_lock); 548} 549 550static void 551appldata_offline_cpu(int cpu) 552{ 553 del_virt_timer(&per_cpu(appldata_timer, cpu)); 554 if (atomic_dec_and_test(&appldata_expire_count)) { 555 atomic_set(&appldata_expire_count, num_online_cpus()); 556 queue_work(appldata_wq, &appldata_work); 557 } 558 spin_lock(&appldata_timer_lock); 559 __appldata_vtimer_setup(APPLDATA_MOD_TIMER); 560 spin_unlock(&appldata_timer_lock); 561} 562 563static int __cpuinit 564appldata_cpu_notify(struct notifier_block *self, 565 unsigned long action, void *hcpu) 566{ 567 switch (action) { 568 case CPU_ONLINE: 569 case CPU_ONLINE_FROZEN: 570 appldata_online_cpu((long) hcpu); 571 break; 572 case CPU_DEAD: 573 case CPU_DEAD_FROZEN: 574 appldata_offline_cpu((long) hcpu); 575 break; 576 default: 577 break; 578 } 579 return NOTIFY_OK; 580} 581 582static struct notifier_block __cpuinitdata appldata_nb = { 583 .notifier_call = appldata_cpu_notify, 584}; 585 586/* 587 * appldata_init() 588 * 589 * init timer, register /proc entries 590 */ 591static int __init appldata_init(void) 592{ 593 int i; 594 595 P_DEBUG("sizeof(parameter_list) = %lu\n", 596 sizeof(struct appldata_parameter_list)); 597 598 appldata_wq = create_singlethread_workqueue("appldata"); 599 if (!appldata_wq) { 600 P_ERROR("Could not create work queue\n"); 601 return -ENOMEM; 602 } 603 604 for_each_online_cpu(i) 605 appldata_online_cpu(i); 606 607 /* Register cpu hotplug notifier */ 608 register_hotcpu_notifier(&appldata_nb); 609 610 appldata_sysctl_header = register_sysctl_table(appldata_dir_table); 611#ifdef MODULE 612 appldata_dir_table[0].de->owner = THIS_MODULE; 613 appldata_table[0].de->owner = THIS_MODULE; 614 appldata_table[1].de->owner = THIS_MODULE; 615#endif 616 617 P_DEBUG("Base interface initialized.\n"); 618 return 0; 619} 620 621/* 622 * appldata_exit() 623 * 624 * stop timer, unregister /proc entries 625 */ 626static void __exit appldata_exit(void) 627{ 628 struct list_head *lh; 629 struct appldata_ops *ops; 630 int rc, i; 631 632 P_DEBUG("Unloading module ...\n"); 633 /* 634 * ops list should be empty, but just in case something went wrong... 635 */ 636 spin_lock(&appldata_ops_lock); 637 list_for_each(lh, &appldata_ops_list) { 638 ops = list_entry(lh, struct appldata_ops, list); 639 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, 640 (unsigned long) ops->data, ops->size, 641 ops->mod_lvl); 642 if (rc != 0) { 643 P_ERROR("STOP DIAG 0xDC for %s failed, " 644 "return code: %d\n", ops->name, rc); 645 } 646 } 647 spin_unlock(&appldata_ops_lock); 648 649 for_each_online_cpu(i) 650 appldata_offline_cpu(i); 651 652 appldata_timer_active = 0; 653 654 unregister_sysctl_table(appldata_sysctl_header); 655 656 destroy_workqueue(appldata_wq); 657 P_DEBUG("... module unloaded!\n"); 658} 659/**************************** init / exit <END> ******************************/ 660 661 662module_init(appldata_init); 663module_exit(appldata_exit); 664MODULE_LICENSE("GPL"); 665MODULE_AUTHOR("Gerald Schaefer"); 666MODULE_DESCRIPTION("Linux-VM Monitor Stream, base infrastructure"); 667 668EXPORT_SYMBOL_GPL(appldata_register_ops); 669EXPORT_SYMBOL_GPL(appldata_unregister_ops); 670EXPORT_SYMBOL_GPL(appldata_diag); 671 672EXPORT_SYMBOL_GPL(si_swapinfo); 673EXPORT_SYMBOL_GPL(nr_threads); 674EXPORT_SYMBOL_GPL(nr_running); 675EXPORT_SYMBOL_GPL(nr_iowait); 676