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