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