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 */ 45 static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata"; 46 static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp, 47 void __user *buffer, size_t *lenp, loff_t *ppos); 48 static 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 53 static struct ctl_table_header *appldata_sysctl_header; 54 static 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 70 static 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 */ 84 static DEFINE_PER_CPU(struct vtimer_list, appldata_timer); 85 static atomic_t appldata_expire_count = ATOMIC_INIT(0); 86 87 static DEFINE_SPINLOCK(appldata_timer_lock); 88 static int appldata_interval = APPLDATA_CPU_INTERVAL; 89 static int appldata_timer_active; 90 91 /* 92 * Work queue 93 */ 94 static struct workqueue_struct *appldata_wq; 95 static void appldata_work_fn(struct work_struct *work); 96 static DECLARE_WORK(appldata_work, appldata_work_fn); 97 98 99 /* 100 * Ops list 101 */ 102 static DEFINE_SPINLOCK(appldata_ops_lock); 103 static LIST_HEAD(appldata_ops_list); 104 105 106 /*************************** timer, work, DIAG *******************************/ 107 /* 108 * appldata_timer_function() 109 * 110 * schedule work and reschedule timer 111 */ 112 static 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 */ 128 static 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 */ 153 int 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_single() 177 * accepts only one parameter. 178 */ 179 static 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 */ 197 static 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_single(i, add_virt_timer_periodic, 212 &per_cpu(appldata_timer, i), 213 0, 1); 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_single(i, __appldata_mod_vtimer_wrap, 240 &args, 0, 1); 241 } 242 } 243 } 244 245 /* 246 * appldata_timer_handler() 247 * 248 * Start/Stop timer, show status of timer (0 = not active, 1 = active) 249 */ 250 static int 251 appldata_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); 278 out: 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 */ 290 static int 291 appldata_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); 327 out: 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 */ 339 static int 340 appldata_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); 426 out: 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 */ 442 int 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 */ 520 void 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 538 static 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 550 static void 551 appldata_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 563 static int __cpuinit 564 appldata_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 582 static 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 */ 591 static 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 */ 626 static 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 662 module_init(appldata_init); 663 module_exit(appldata_exit); 664 MODULE_LICENSE("GPL"); 665 MODULE_AUTHOR("Gerald Schaefer"); 666 MODULE_DESCRIPTION("Linux-VM Monitor Stream, base infrastructure"); 667 668 EXPORT_SYMBOL_GPL(appldata_register_ops); 669 EXPORT_SYMBOL_GPL(appldata_unregister_ops); 670 EXPORT_SYMBOL_GPL(appldata_diag); 671 672 EXPORT_SYMBOL_GPL(si_swapinfo); 673 EXPORT_SYMBOL_GPL(nr_threads); 674 EXPORT_SYMBOL_GPL(nr_running); 675 EXPORT_SYMBOL_GPL(nr_iowait); 676