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 len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n"); 216 if (len > *lenp) 217 len = *lenp; 218 if (copy_to_user(buffer, buf, len)) 219 return -EFAULT; 220 goto out; 221 } 222 len = *lenp; 223 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) 224 return -EFAULT; 225 spin_lock(&appldata_timer_lock); 226 if (buf[0] == '1') 227 __appldata_vtimer_setup(APPLDATA_ADD_TIMER); 228 else if (buf[0] == '0') 229 __appldata_vtimer_setup(APPLDATA_DEL_TIMER); 230 spin_unlock(&appldata_timer_lock); 231 out: 232 *lenp = len; 233 *ppos += len; 234 return 0; 235 } 236 237 /* 238 * appldata_interval_handler() 239 * 240 * Set (CPU) timer interval for collection of data (in milliseconds), show 241 * current timer interval. 242 */ 243 static int 244 appldata_interval_handler(ctl_table *ctl, int write, 245 void __user *buffer, size_t *lenp, loff_t *ppos) 246 { 247 int len, interval; 248 char buf[16]; 249 250 if (!*lenp || *ppos) { 251 *lenp = 0; 252 return 0; 253 } 254 if (!write) { 255 len = sprintf(buf, "%i\n", appldata_interval); 256 if (len > *lenp) 257 len = *lenp; 258 if (copy_to_user(buffer, buf, len)) 259 return -EFAULT; 260 goto out; 261 } 262 len = *lenp; 263 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) 264 return -EFAULT; 265 interval = 0; 266 sscanf(buf, "%i", &interval); 267 if (interval <= 0) 268 return -EINVAL; 269 270 spin_lock(&appldata_timer_lock); 271 appldata_interval = interval; 272 __appldata_vtimer_setup(APPLDATA_MOD_TIMER); 273 spin_unlock(&appldata_timer_lock); 274 out: 275 *lenp = len; 276 *ppos += len; 277 return 0; 278 } 279 280 /* 281 * appldata_generic_handler() 282 * 283 * Generic start/stop monitoring and DIAG, show status of 284 * monitoring (0 = not in process, 1 = in process) 285 */ 286 static int 287 appldata_generic_handler(ctl_table *ctl, int write, 288 void __user *buffer, size_t *lenp, loff_t *ppos) 289 { 290 struct appldata_ops *ops = NULL, *tmp_ops; 291 int rc, len, found; 292 char buf[2]; 293 struct list_head *lh; 294 295 found = 0; 296 mutex_lock(&appldata_ops_mutex); 297 list_for_each(lh, &appldata_ops_list) { 298 tmp_ops = list_entry(lh, struct appldata_ops, list); 299 if (&tmp_ops->ctl_table[2] == ctl) { 300 found = 1; 301 } 302 } 303 if (!found) { 304 mutex_unlock(&appldata_ops_mutex); 305 return -ENODEV; 306 } 307 ops = ctl->data; 308 if (!try_module_get(ops->owner)) { // protect this function 309 mutex_unlock(&appldata_ops_mutex); 310 return -ENODEV; 311 } 312 mutex_unlock(&appldata_ops_mutex); 313 314 if (!*lenp || *ppos) { 315 *lenp = 0; 316 module_put(ops->owner); 317 return 0; 318 } 319 if (!write) { 320 len = sprintf(buf, ops->active ? "1\n" : "0\n"); 321 if (len > *lenp) 322 len = *lenp; 323 if (copy_to_user(buffer, buf, len)) { 324 module_put(ops->owner); 325 return -EFAULT; 326 } 327 goto out; 328 } 329 len = *lenp; 330 if (copy_from_user(buf, buffer, 331 len > sizeof(buf) ? sizeof(buf) : len)) { 332 module_put(ops->owner); 333 return -EFAULT; 334 } 335 336 mutex_lock(&appldata_ops_mutex); 337 if ((buf[0] == '1') && (ops->active == 0)) { 338 // protect work queue callback 339 if (!try_module_get(ops->owner)) { 340 mutex_unlock(&appldata_ops_mutex); 341 module_put(ops->owner); 342 return -ENODEV; 343 } 344 ops->callback(ops->data); // init record 345 rc = appldata_diag(ops->record_nr, 346 APPLDATA_START_INTERVAL_REC, 347 (unsigned long) ops->data, ops->size, 348 ops->mod_lvl); 349 if (rc != 0) { 350 pr_err("Starting the data collection for %s " 351 "failed with rc=%d\n", ops->name, rc); 352 module_put(ops->owner); 353 } else 354 ops->active = 1; 355 } else if ((buf[0] == '0') && (ops->active == 1)) { 356 ops->active = 0; 357 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, 358 (unsigned long) ops->data, ops->size, 359 ops->mod_lvl); 360 if (rc != 0) 361 pr_err("Stopping the data collection for %s " 362 "failed with rc=%d\n", ops->name, rc); 363 module_put(ops->owner); 364 } 365 mutex_unlock(&appldata_ops_mutex); 366 out: 367 *lenp = len; 368 *ppos += len; 369 module_put(ops->owner); 370 return 0; 371 } 372 373 /*************************** /proc stuff <END> *******************************/ 374 375 376 /************************* module-ops management *****************************/ 377 /* 378 * appldata_register_ops() 379 * 380 * update ops list, register /proc/sys entries 381 */ 382 int appldata_register_ops(struct appldata_ops *ops) 383 { 384 if (ops->size > APPLDATA_MAX_REC_SIZE) 385 return -EINVAL; 386 387 ops->ctl_table = kzalloc(4 * sizeof(struct ctl_table), GFP_KERNEL); 388 if (!ops->ctl_table) 389 return -ENOMEM; 390 391 mutex_lock(&appldata_ops_mutex); 392 list_add(&ops->list, &appldata_ops_list); 393 mutex_unlock(&appldata_ops_mutex); 394 395 ops->ctl_table[0].procname = appldata_proc_name; 396 ops->ctl_table[0].maxlen = 0; 397 ops->ctl_table[0].mode = S_IRUGO | S_IXUGO; 398 ops->ctl_table[0].child = &ops->ctl_table[2]; 399 400 ops->ctl_table[2].procname = ops->name; 401 ops->ctl_table[2].mode = S_IRUGO | S_IWUSR; 402 ops->ctl_table[2].proc_handler = appldata_generic_handler; 403 ops->ctl_table[2].data = ops; 404 405 ops->sysctl_header = register_sysctl_table(ops->ctl_table); 406 if (!ops->sysctl_header) 407 goto out; 408 return 0; 409 out: 410 mutex_lock(&appldata_ops_mutex); 411 list_del(&ops->list); 412 mutex_unlock(&appldata_ops_mutex); 413 kfree(ops->ctl_table); 414 return -ENOMEM; 415 } 416 417 /* 418 * appldata_unregister_ops() 419 * 420 * update ops list, unregister /proc entries, stop DIAG if necessary 421 */ 422 void appldata_unregister_ops(struct appldata_ops *ops) 423 { 424 mutex_lock(&appldata_ops_mutex); 425 list_del(&ops->list); 426 mutex_unlock(&appldata_ops_mutex); 427 unregister_sysctl_table(ops->sysctl_header); 428 kfree(ops->ctl_table); 429 } 430 /********************** module-ops management <END> **************************/ 431 432 433 /**************************** suspend / resume *******************************/ 434 static int appldata_freeze(struct device *dev) 435 { 436 struct appldata_ops *ops; 437 int rc; 438 struct list_head *lh; 439 440 spin_lock(&appldata_timer_lock); 441 if (appldata_timer_active) { 442 __appldata_vtimer_setup(APPLDATA_DEL_TIMER); 443 appldata_timer_suspended = 1; 444 } 445 spin_unlock(&appldata_timer_lock); 446 447 mutex_lock(&appldata_ops_mutex); 448 list_for_each(lh, &appldata_ops_list) { 449 ops = list_entry(lh, struct appldata_ops, list); 450 if (ops->active == 1) { 451 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, 452 (unsigned long) ops->data, ops->size, 453 ops->mod_lvl); 454 if (rc != 0) 455 pr_err("Stopping the data collection for %s " 456 "failed with rc=%d\n", ops->name, rc); 457 } 458 } 459 mutex_unlock(&appldata_ops_mutex); 460 return 0; 461 } 462 463 static int appldata_restore(struct device *dev) 464 { 465 struct appldata_ops *ops; 466 int rc; 467 struct list_head *lh; 468 469 spin_lock(&appldata_timer_lock); 470 if (appldata_timer_suspended) { 471 __appldata_vtimer_setup(APPLDATA_ADD_TIMER); 472 appldata_timer_suspended = 0; 473 } 474 spin_unlock(&appldata_timer_lock); 475 476 mutex_lock(&appldata_ops_mutex); 477 list_for_each(lh, &appldata_ops_list) { 478 ops = list_entry(lh, struct appldata_ops, list); 479 if (ops->active == 1) { 480 ops->callback(ops->data); // init record 481 rc = appldata_diag(ops->record_nr, 482 APPLDATA_START_INTERVAL_REC, 483 (unsigned long) ops->data, ops->size, 484 ops->mod_lvl); 485 if (rc != 0) { 486 pr_err("Starting the data collection for %s " 487 "failed with rc=%d\n", ops->name, rc); 488 } 489 } 490 } 491 mutex_unlock(&appldata_ops_mutex); 492 return 0; 493 } 494 495 static int appldata_thaw(struct device *dev) 496 { 497 return appldata_restore(dev); 498 } 499 500 static const struct dev_pm_ops appldata_pm_ops = { 501 .freeze = appldata_freeze, 502 .thaw = appldata_thaw, 503 .restore = appldata_restore, 504 }; 505 506 static struct platform_driver appldata_pdrv = { 507 .driver = { 508 .name = "appldata", 509 .owner = THIS_MODULE, 510 .pm = &appldata_pm_ops, 511 }, 512 }; 513 /************************* suspend / resume <END> ****************************/ 514 515 516 /******************************* init / exit *********************************/ 517 518 /* 519 * appldata_init() 520 * 521 * init timer, register /proc entries 522 */ 523 static int __init appldata_init(void) 524 { 525 int rc; 526 527 appldata_timer.function = appldata_timer_function; 528 appldata_timer.data = (unsigned long) &appldata_work; 529 530 rc = platform_driver_register(&appldata_pdrv); 531 if (rc) 532 return rc; 533 534 appldata_pdev = platform_device_register_simple("appldata", -1, NULL, 535 0); 536 if (IS_ERR(appldata_pdev)) { 537 rc = PTR_ERR(appldata_pdev); 538 goto out_driver; 539 } 540 appldata_wq = create_singlethread_workqueue("appldata"); 541 if (!appldata_wq) { 542 rc = -ENOMEM; 543 goto out_device; 544 } 545 546 appldata_sysctl_header = register_sysctl_table(appldata_dir_table); 547 return 0; 548 549 out_device: 550 platform_device_unregister(appldata_pdev); 551 out_driver: 552 platform_driver_unregister(&appldata_pdrv); 553 return rc; 554 } 555 556 __initcall(appldata_init); 557 558 /**************************** init / exit <END> ******************************/ 559 560 EXPORT_SYMBOL_GPL(appldata_register_ops); 561 EXPORT_SYMBOL_GPL(appldata_unregister_ops); 562 EXPORT_SYMBOL_GPL(appldata_diag); 563 564 #ifdef CONFIG_SWAP 565 EXPORT_SYMBOL_GPL(si_swapinfo); 566 #endif 567 EXPORT_SYMBOL_GPL(nr_threads); 568 EXPORT_SYMBOL_GPL(nr_running); 569 EXPORT_SYMBOL_GPL(nr_iowait); 570