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