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