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 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 */
  47static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
  48static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
  49                                  void __user *buffer, size_t *lenp, loff_t *ppos);
  50static 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
  55static struct ctl_table_header *appldata_sysctl_header;
  56static 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
  70static 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 */
  83static DEFINE_PER_CPU(struct vtimer_list, appldata_timer);
  84static atomic_t appldata_expire_count = ATOMIC_INIT(0);
  85
  86static DEFINE_SPINLOCK(appldata_timer_lock);
  87static int appldata_interval = APPLDATA_CPU_INTERVAL;
  88static int appldata_timer_active;
  89
  90/*
  91 * Work queue
  92 */
  93static struct workqueue_struct *appldata_wq;
  94static void appldata_work_fn(struct work_struct *work);
  95static DECLARE_WORK(appldata_work, appldata_work_fn);
  96
  97
  98/*
  99 * Ops list
 100 */
 101static DEFINE_MUTEX(appldata_ops_mutex);
 102static LIST_HEAD(appldata_ops_list);
 103
 104
 105/*************************** timer, work, DIAG *******************************/
 106/*
 107 * appldata_timer_function()
 108 *
 109 * schedule work and reschedule timer
 110 */
 111static 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 */
 124static 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 */
 148int 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 */
 174static 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 */
 192static 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 */
 243static int
 244appldata_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();
 273out:
 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 */
 285static int
 286appldata_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();
 319out:
 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 */
 331static int
 332appldata_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);
 411out:
 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 */
 427int 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;
 454out:
 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 */
 467void 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
 480static 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
 492static 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
 504static 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
 523static 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 */
 532static 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
 556EXPORT_SYMBOL_GPL(appldata_register_ops);
 557EXPORT_SYMBOL_GPL(appldata_unregister_ops);
 558EXPORT_SYMBOL_GPL(appldata_diag);
 559
 560#ifdef CONFIG_SWAP
 561EXPORT_SYMBOL_GPL(si_swapinfo);
 562#endif
 563EXPORT_SYMBOL_GPL(nr_threads);
 564EXPORT_SYMBOL_GPL(nr_running);
 565EXPORT_SYMBOL_GPL(nr_iowait);
 566
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.