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, 2008 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 .procname = "timer", 57 .mode = S_IRUGO | S_IWUSR, 58 .proc_handler = &appldata_timer_handler, 59 }, 60 { 61 .procname = "interval", 62 .mode = S_IRUGO | S_IWUSR, 63 .proc_handler = &appldata_interval_handler, 64 }, 65 { }, 66 }; 67 68 static struct ctl_table appldata_dir_table[] = { 69 { 70 .procname = appldata_proc_name, 71 .maxlen = 0, 72 .mode = S_IRUGO | S_IXUGO, 73 .child = appldata_table, 74 }, 75 { }, 76 }; 77 78 /* 79 * Timer 80 */ 81 static DEFINE_PER_CPU(struct vtimer_list, appldata_timer); 82 static atomic_t appldata_expire_count = ATOMIC_INIT(0); 83 84 static DEFINE_SPINLOCK(appldata_timer_lock); 85 static int appldata_interval = APPLDATA_CPU_INTERVAL; 86 static int appldata_timer_active; 87 88 /* 89 * Work queue 90 */ 91 static struct workqueue_struct *appldata_wq; 92 static void appldata_work_fn(struct work_struct *work); 93 static DECLARE_WORK(appldata_work, appldata_work_fn); 94 95 96 /* 97 * Ops list 98 */ 99 static DEFINE_SPINLOCK(appldata_ops_lock); 100 static LIST_HEAD(appldata_ops_list); 101 102 103 /*************************** timer, work, DIAG *******************************/ 104 /* 105 * appldata_timer_function() 106 * 107 * schedule work and reschedule timer 108 */ 109 static void appldata_timer_function(unsigned long data) 110 { 111 if (atomic_dec_and_test(&appldata_expire_count)) { 112 atomic_set(&appldata_expire_count, num_online_cpus()); 113 queue_work(appldata_wq, (struct work_struct *) data); 114 } 115 } 116 117 /* 118 * appldata_work_fn() 119 * 120 * call data gathering function for each (active) module 121 */ 122 static void appldata_work_fn(struct work_struct *work) 123 { 124 struct list_head *lh; 125 struct appldata_ops *ops; 126 int i; 127 128 i = 0; 129 get_online_cpus(); 130 spin_lock(&appldata_ops_lock); 131 list_for_each(lh, &appldata_ops_list) { 132 ops = list_entry(lh, struct appldata_ops, list); 133 if (ops->active == 1) { 134 ops->callback(ops->data); 135 } 136 } 137 spin_unlock(&appldata_ops_lock); 138 put_online_cpus(); 139 } 140 141 /* 142 * appldata_diag() 143 * 144 * prepare parameter list, issue DIAG 0xDC 145 */ 146 int appldata_diag(char record_nr, u16 function, unsigned long buffer, 147 u16 length, char *mod_lvl) 148 { 149 struct appldata_product_id id = { 150 .prod_nr = {0xD3, 0xC9, 0xD5, 0xE4, 151 0xE7, 0xD2, 0xD9}, /* "LINUXKR" */ 152 .prod_fn = 0xD5D3, /* "NL" */ 153 .version_nr = 0xF2F6, /* "26" */ 154 .release_nr = 0xF0F1, /* "01" */ 155 }; 156 157 id.record_nr = record_nr; 158 id.mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1]; 159 return appldata_asm(&id, function, (void *) buffer, length); 160 } 161 /************************ timer, work, DIAG <END> ****************************/ 162 163 164 /****************************** /proc stuff **********************************/ 165 166 /* 167 * appldata_mod_vtimer_wrap() 168 * 169 * wrapper function for mod_virt_timer(), because smp_call_function_single() 170 * accepts only one parameter. 171 */ 172 static void __appldata_mod_vtimer_wrap(void *p) { 173 struct { 174 struct vtimer_list *timer; 175 u64 expires; 176 } *args = p; 177 mod_virt_timer(args->timer, args->expires); 178 } 179 180 #define APPLDATA_ADD_TIMER 0 181 #define APPLDATA_DEL_TIMER 1 182 #define APPLDATA_MOD_TIMER 2 183 184 /* 185 * __appldata_vtimer_setup() 186 * 187 * Add, delete or modify virtual timers on all online cpus. 188 * The caller needs to get the appldata_timer_lock spinlock. 189 */ 190 static void 191 __appldata_vtimer_setup(int cmd) 192 { 193 u64 per_cpu_interval; 194 int i; 195 196 switch (cmd) { 197 case APPLDATA_ADD_TIMER: 198 if (appldata_timer_active) 199 break; 200 per_cpu_interval = (u64) (appldata_interval*1000 / 201 num_online_cpus()) * TOD_MICRO; 202 for_each_online_cpu(i) { 203 per_cpu(appldata_timer, i).expires = per_cpu_interval; 204 smp_call_function_single(i, add_virt_timer_periodic, 205 &per_cpu(appldata_timer, i), 206 1); 207 } 208 appldata_timer_active = 1; 209 break; 210 case APPLDATA_DEL_TIMER: 211 for_each_online_cpu(i) 212 del_virt_timer(&per_cpu(appldata_timer, i)); 213 if (!appldata_timer_active) 214 break; 215 appldata_timer_active = 0; 216 atomic_set(&appldata_expire_count, num_online_cpus()); 217 break; 218 case APPLDATA_MOD_TIMER: 219 per_cpu_interval = (u64) (appldata_interval*1000 / 220 num_online_cpus()) * TOD_MICRO; 221 if (!appldata_timer_active) 222 break; 223 for_each_online_cpu(i) { 224 struct { 225 struct vtimer_list *timer; 226 u64 expires; 227 } args; 228 args.timer = &per_cpu(appldata_timer, i); 229 args.expires = per_cpu_interval; 230 smp_call_function_single(i, __appldata_mod_vtimer_wrap, 231 &args, 1); 232 } 233 } 234 } 235 236 /* 237 * appldata_timer_handler() 238 * 239 * Start/Stop timer, show status of timer (0 = not active, 1 = active) 240 */ 241 static int 242 appldata_timer_handler(ctl_table *ctl, int write, struct file *filp, 243 void __user *buffer, size_t *lenp, loff_t *ppos) 244 { 245 int len; 246 char buf[2]; 247 248 if (!*lenp || *ppos) { 249 *lenp = 0; 250 return 0; 251 } 252 if (!write) { 253 len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n"); 254 if (len > *lenp) 255 len = *lenp; 256 if (copy_to_user(buffer, buf, len)) 257 return -EFAULT; 258 goto out; 259 } 260 len = *lenp; 261 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) 262 return -EFAULT; 263 get_online_cpus(); 264 spin_lock(&appldata_timer_lock); 265 if (buf[0] == '1') 266 __appldata_vtimer_setup(APPLDATA_ADD_TIMER); 267 else if (buf[0] == '0') 268 __appldata_vtimer_setup(APPLDATA_DEL_TIMER); 269 spin_unlock(&appldata_timer_lock); 270 put_online_cpus(); 271 out: 272 *lenp = len; 273 *ppos += len; 274 return 0; 275 } 276 277 /* 278 * appldata_interval_handler() 279 * 280 * Set (CPU) timer interval for collection of data (in milliseconds), show 281 * current timer interval. 282 */ 283 static int 284 appldata_interval_handler(ctl_table *ctl, int write, struct file *filp, 285 void __user *buffer, size_t *lenp, loff_t *ppos) 286 { 287 int len, interval; 288 char buf[16]; 289 290 if (!*lenp || *ppos) { 291 *lenp = 0; 292 return 0; 293 } 294 if (!write) { 295 len = sprintf(buf, "%i\n", appldata_interval); 296 if (len > *lenp) 297 len = *lenp; 298 if (copy_to_user(buffer, buf, len)) 299 return -EFAULT; 300 goto out; 301 } 302 len = *lenp; 303 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) { 304 return -EFAULT; 305 } 306 interval = 0; 307 sscanf(buf, "%i", &interval); 308 if (interval <= 0) 309 return -EINVAL; 310 311 get_online_cpus(); 312 spin_lock(&appldata_timer_lock); 313 appldata_interval = interval; 314 __appldata_vtimer_setup(APPLDATA_MOD_TIMER); 315 spin_unlock(&appldata_timer_lock); 316 put_online_cpus(); 317 out: 318 *lenp = len; 319 *ppos += len; 320 return 0; 321 } 322 323 /* 324 * appldata_generic_handler() 325 * 326 * Generic start/stop monitoring and DIAG, show status of 327 * monitoring (0 = not in process, 1 = in process) 328 */ 329 static int 330 appldata_generic_handler(ctl_table *ctl, int write, struct file *filp, 331 void __user *buffer, size_t *lenp, loff_t *ppos) 332 { 333 struct appldata_ops *ops = NULL, *tmp_ops; 334 int rc, len, found; 335 char buf[2]; 336 struct list_head *lh; 337 338 found = 0; 339 spin_lock(&appldata_ops_lock); 340 list_for_each(lh, &appldata_ops_list) { 341 tmp_ops = list_entry(lh, struct appldata_ops, list); 342 if (&tmp_ops->ctl_table[2] == ctl) { 343 found = 1; 344 } 345 } 346 if (!found) { 347 spin_unlock(&appldata_ops_lock); 348 return -ENODEV; 349 } 350 ops = ctl->data; 351 if (!try_module_get(ops->owner)) { // protect this function 352 spin_unlock(&appldata_ops_lock); 353 return -ENODEV; 354 } 355 spin_unlock(&appldata_ops_lock); 356 357 if (!*lenp || *ppos) { 358 *lenp = 0; 359 module_put(ops->owner); 360 return 0; 361 } 362 if (!write) { 363 len = sprintf(buf, ops->active ? "1\n" : "0\n"); 364 if (len > *lenp) 365 len = *lenp; 366 if (copy_to_user(buffer, buf, len)) { 367 module_put(ops->owner); 368 return -EFAULT; 369 } 370 goto out; 371 } 372 len = *lenp; 373 if (copy_from_user(buf, buffer, 374 len > sizeof(buf) ? sizeof(buf) : len)) { 375 module_put(ops->owner); 376 return -EFAULT; 377 } 378 379 spin_lock(&appldata_ops_lock); 380 if ((buf[0] == '1') && (ops->active == 0)) { 381 // protect work queue callback 382 if (!try_module_get(ops->owner)) { 383 spin_unlock(&appldata_ops_lock); 384 module_put(ops->owner); 385 return -ENODEV; 386 } 387 ops->callback(ops->data); // init record 388 rc = appldata_diag(ops->record_nr, 389 APPLDATA_START_INTERVAL_REC, 390 (unsigned long) ops->data, ops->size, 391 ops->mod_lvl); 392 if (rc != 0) { 393 P_ERROR("START DIAG 0xDC for %s failed, " 394 "return code: %d\n", ops->name, rc); 395 module_put(ops->owner); 396 } else 397 ops->active = 1; 398 } else if ((buf[0] == '0') && (ops->active == 1)) { 399 ops->active = 0; 400 rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, 401 (unsigned long) ops->data, ops->size, 402 ops->mod_lvl); 403 if (rc != 0) 404 P_ERROR("STOP DIAG 0xDC for %s failed, " 405 "return code: %d\n", ops->name, rc); 406 module_put(ops->owner); 407 } 408 spin_unlock(&appldata_ops_lock); 409 out: 410 *lenp = len; 411 *ppos += len; 412 module_put(ops->owner); 413 return 0; 414 } 415 416 /*************************** /proc stuff <END> *******************************/ 417 418 419 /************************* module-ops management *****************************/ 420 /* 421 * appldata_register_ops() 422 * 423 * update ops list, register /proc/sys entries 424 */ 425 int appldata_register_ops(struct appldata_ops *ops) 426 { 427 if ((ops->size > APPLDATA_MAX_REC_SIZE) || (ops->size < 0)) 428 return -EINVAL; 429 430 ops->ctl_table = kzalloc(4 * sizeof(struct ctl_table), GFP_KERNEL); 431 if (!ops->ctl_table) 432 return -ENOMEM; 433 434 spin_lock(&appldata_ops_lock); 435 list_add(&ops->list, &appldata_ops_list); 436 spin_unlock(&appldata_ops_lock); 437 438 ops->ctl_table[0].procname = appldata_proc_name; 439 ops->ctl_table[0].maxlen = 0; 440 ops->ctl_table[0].mode = S_IRUGO | S_IXUGO; 441 ops->ctl_table[0].child = &ops->ctl_table[2]; 442 443 ops->ctl_table[2].procname = ops->name; 444 ops->ctl_table[2].mode = S_IRUGO | S_IWUSR; 445 ops->ctl_table[2].proc_handler = appldata_generic_handler; 446 ops->ctl_table[2].data = ops; 447 448 ops->sysctl_header = register_sysctl_table(ops->ctl_table); 449 if (!ops->sysctl_header) 450 goto out; 451 return 0; 452 out: 453 spin_lock(&appldata_ops_lock); 454 list_del(&ops->list); 455 spin_unlock(&appldata_ops_lock); 456 kfree(ops->ctl_table); 457 return -ENOMEM; 458 } 459 460 /* 461 * appldata_unregister_ops() 462 * 463 * update ops list, unregister /proc entries, stop DIAG if necessary 464 */ 465 void appldata_unregister_ops(struct appldata_ops *ops) 466 { 467 spin_lock(&appldata_ops_lock); 468 list_del(&ops->list); 469 spin_unlock(&appldata_ops_lock); 470 unregister_sysctl_table(ops->sysctl_header); 471 kfree(ops->ctl_table); 472 } 473 /********************** module-ops management <END> **************************/ 474 475 476 /******************************* init / exit *********************************/ 477 478 static void __cpuinit appldata_online_cpu(int cpu) 479 { 480 init_virt_timer(&per_cpu(appldata_timer, cpu)); 481 per_cpu(appldata_timer, cpu).function = appldata_timer_function; 482 per_cpu(appldata_timer, cpu).data = (unsigned long) 483 &appldata_work; 484 atomic_inc(&appldata_expire_count); 485 spin_lock(&appldata_timer_lock); 486 __appldata_vtimer_setup(APPLDATA_MOD_TIMER); 487 spin_unlock(&appldata_timer_lock); 488 } 489 490 static void __cpuinit appldata_offline_cpu(int cpu) 491 { 492 del_virt_timer(&per_cpu(appldata_timer, cpu)); 493 if (atomic_dec_and_test(&appldata_expire_count)) { 494 atomic_set(&appldata_expire_count, num_online_cpus()); 495 queue_work(appldata_wq, &appldata_work); 496 } 497 spin_lock(&appldata_timer_lock); 498 __appldata_vtimer_setup(APPLDATA_MOD_TIMER); 499 spin_unlock(&appldata_timer_lock); 500 } 501 502 static int __cpuinit appldata_cpu_notify(struct notifier_block *self, 503 unsigned long action, 504 void *hcpu) 505 { 506 switch (action) { 507 case CPU_ONLINE: 508 case CPU_ONLINE_FROZEN: 509 appldata_online_cpu((long) hcpu); 510 break; 511 case CPU_DEAD: 512 case CPU_DEAD_FROZEN: 513 appldata_offline_cpu((long) hcpu); 514 break; 515 default: 516 break; 517 } 518 return NOTIFY_OK; 519 } 520 521 static struct notifier_block __cpuinitdata appldata_nb = { 522 .notifier_call = appldata_cpu_notify, 523 }; 524 525 /* 526 * appldata_init() 527 * 528 * init timer, register /proc entries 529 */ 530 static int __init appldata_init(void) 531 { 532 int i; 533 534 appldata_wq = create_singlethread_workqueue("appldata"); 535 if (!appldata_wq) 536 return -ENOMEM; 537 538 get_online_cpus(); 539 for_each_online_cpu(i) 540 appldata_online_cpu(i); 541 put_online_cpus(); 542 543 /* Register cpu hotplug notifier */ 544 register_hotcpu_notifier(&appldata_nb); 545 546 appldata_sysctl_header = register_sysctl_table(appldata_dir_table); 547 return 0; 548 } 549 550 __initcall(appldata_init); 551 552 /**************************** init / exit <END> ******************************/ 553 554 EXPORT_SYMBOL_GPL(appldata_register_ops); 555 EXPORT_SYMBOL_GPL(appldata_unregister_ops); 556 EXPORT_SYMBOL_GPL(appldata_diag); 557 558 #ifdef CONFIG_SWAP 559 EXPORT_SYMBOL_GPL(si_swapinfo); 560 #endif 561 EXPORT_SYMBOL_GPL(nr_threads); 562 EXPORT_SYMBOL_GPL(nr_running); 563 EXPORT_SYMBOL_GPL(nr_iowait); 564