linux/arch/s390/appldata/appldata_base.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.
   4 * Exports appldata_register_ops() and appldata_unregister_ops() for the
   5 * data gathering modules.
   6 *
   7 * Copyright IBM Corp. 2003, 2009
   8 *
   9 * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
  10 */
  11
  12#define KMSG_COMPONENT  "appldata"
  13#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
  14
  15#include <linux/module.h>
  16#include <linux/sched/stat.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 <linux/suspend.h>
  30#include <linux/platform_device.h>
  31#include <asm/appldata.h>
  32#include <asm/vtimer.h>
  33#include <linux/uaccess.h>
  34#include <asm/io.h>
  35#include <asm/smp.h>
  36
  37#include "appldata.h"
  38
  39
  40#define APPLDATA_CPU_INTERVAL   10000           /* default (CPU) time for
  41                                                   sampling interval in
  42                                                   milliseconds */
  43
  44#define TOD_MICRO       0x01000                 /* nr. of TOD clock units
  45                                                   for 1 microsecond */
  46
  47static struct platform_device *appldata_pdev;
  48
  49/*
  50 * /proc entries (sysctl)
  51 */
  52static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
  53static int appldata_timer_handler(struct ctl_table *ctl, int write,
  54                                  void __user *buffer, size_t *lenp, loff_t *ppos);
  55static int appldata_interval_handler(struct ctl_table *ctl, int write,
  56                                         void __user *buffer,
  57                                         size_t *lenp, loff_t *ppos);
  58
  59static struct ctl_table_header *appldata_sysctl_header;
  60static struct ctl_table appldata_table[] = {
  61        {
  62                .procname       = "timer",
  63                .mode           = S_IRUGO | S_IWUSR,
  64                .proc_handler   = appldata_timer_handler,
  65        },
  66        {
  67                .procname       = "interval",
  68                .mode           = S_IRUGO | S_IWUSR,
  69                .proc_handler   = appldata_interval_handler,
  70        },
  71        { },
  72};
  73
  74static struct ctl_table appldata_dir_table[] = {
  75        {
  76                .procname       = appldata_proc_name,
  77                .maxlen         = 0,
  78                .mode           = S_IRUGO | S_IXUGO,
  79                .child          = appldata_table,
  80        },
  81        { },
  82};
  83
  84/*
  85 * Timer
  86 */
  87static struct vtimer_list appldata_timer;
  88
  89static DEFINE_SPINLOCK(appldata_timer_lock);
  90static int appldata_interval = APPLDATA_CPU_INTERVAL;
  91static int appldata_timer_active;
  92static int appldata_timer_suspended = 0;
  93
  94/*
  95 * Work queue
  96 */
  97static struct workqueue_struct *appldata_wq;
  98static void appldata_work_fn(struct work_struct *work);
  99static DECLARE_WORK(appldata_work, appldata_work_fn);
 100
 101
 102/*
 103 * Ops list
 104 */
 105static DEFINE_MUTEX(appldata_ops_mutex);
 106static LIST_HEAD(appldata_ops_list);
 107
 108
 109/*************************** timer, work, DIAG *******************************/
 110/*
 111 * appldata_timer_function()
 112 *
 113 * schedule work and reschedule timer
 114 */
 115static void appldata_timer_function(unsigned long data)
 116{
 117        queue_work(appldata_wq, (struct work_struct *) data);
 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
 130        mutex_lock(&appldata_ops_mutex);
 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        mutex_unlock(&appldata_ops_mutex);
 138}
 139
 140/*
 141 * appldata_diag()
 142 *
 143 * prepare parameter list, issue DIAG 0xDC
 144 */
 145int appldata_diag(char record_nr, u16 function, unsigned long buffer,
 146                        u16 length, char *mod_lvl)
 147{
 148        struct appldata_product_id id = {
 149                .prod_nr    = {0xD3, 0xC9, 0xD5, 0xE4,
 150                               0xE7, 0xD2, 0xD9},       /* "LINUXKR" */
 151                .prod_fn    = 0xD5D3,                   /* "NL" */
 152                .version_nr = 0xF2F6,                   /* "26" */
 153                .release_nr = 0xF0F1,                   /* "01" */
 154        };
 155
 156        id.record_nr = record_nr;
 157        id.mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1];
 158        return appldata_asm(&id, function, (void *) buffer, length);
 159}
 160/************************ timer, work, DIAG <END> ****************************/
 161
 162
 163/****************************** /proc stuff **********************************/
 164
 165#define APPLDATA_ADD_TIMER      0
 166#define APPLDATA_DEL_TIMER      1
 167#define APPLDATA_MOD_TIMER      2
 168
 169/*
 170 * __appldata_vtimer_setup()
 171 *
 172 * Add, delete or modify virtual timers on all online cpus.
 173 * The caller needs to get the appldata_timer_lock spinlock.
 174 */
 175static void __appldata_vtimer_setup(int cmd)
 176{
 177        u64 timer_interval = (u64) appldata_interval * 1000 * TOD_MICRO;
 178
 179        switch (cmd) {
 180        case APPLDATA_ADD_TIMER:
 181                if (appldata_timer_active)
 182                        break;
 183                appldata_timer.expires = timer_interval;
 184                add_virt_timer_periodic(&appldata_timer);
 185                appldata_timer_active = 1;
 186                break;
 187        case APPLDATA_DEL_TIMER:
 188                del_virt_timer(&appldata_timer);
 189                if (!appldata_timer_active)
 190                        break;
 191                appldata_timer_active = 0;
 192                break;
 193        case APPLDATA_MOD_TIMER:
 194                if (!appldata_timer_active)
 195                        break;
 196                mod_virt_timer_periodic(&appldata_timer, timer_interval);
 197        }
 198}
 199
 200/*
 201 * appldata_timer_handler()
 202 *
 203 * Start/Stop timer, show status of timer (0 = not active, 1 = active)
 204 */
 205static int
 206appldata_timer_handler(struct ctl_table *ctl, int write,
 207                           void __user *buffer, size_t *lenp, loff_t *ppos)
 208{
 209        unsigned int len;
 210        char buf[2];
 211
 212        if (!*lenp || *ppos) {
 213                *lenp = 0;
 214                return 0;
 215        }
 216        if (!write) {
 217                strncpy(buf, appldata_timer_active ? "1\n" : "0\n",
 218                        ARRAY_SIZE(buf));
 219                len = strnlen(buf, ARRAY_SIZE(buf));
 220                if (len > *lenp)
 221                        len = *lenp;
 222                if (copy_to_user(buffer, buf, len))
 223                        return -EFAULT;
 224                goto out;
 225        }
 226        len = *lenp;
 227        if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
 228                return -EFAULT;
 229        spin_lock(&appldata_timer_lock);
 230        if (buf[0] == '1')
 231                __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
 232        else if (buf[0] == '0')
 233                __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
 234        spin_unlock(&appldata_timer_lock);
 235out:
 236        *lenp = len;
 237        *ppos += len;
 238        return 0;
 239}
 240
 241/*
 242 * appldata_interval_handler()
 243 *
 244 * Set (CPU) timer interval for collection of data (in milliseconds), show
 245 * current timer interval.
 246 */
 247static int
 248appldata_interval_handler(struct ctl_table *ctl, int write,
 249                           void __user *buffer, size_t *lenp, loff_t *ppos)
 250{
 251        unsigned int len;
 252        int interval;
 253        char buf[16];
 254
 255        if (!*lenp || *ppos) {
 256                *lenp = 0;
 257                return 0;
 258        }
 259        if (!write) {
 260                len = sprintf(buf, "%i\n", appldata_interval);
 261                if (len > *lenp)
 262                        len = *lenp;
 263                if (copy_to_user(buffer, buf, len))
 264                        return -EFAULT;
 265                goto out;
 266        }
 267        len = *lenp;
 268        if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
 269                return -EFAULT;
 270        interval = 0;
 271        sscanf(buf, "%i", &interval);
 272        if (interval <= 0)
 273                return -EINVAL;
 274
 275        spin_lock(&appldata_timer_lock);
 276        appldata_interval = interval;
 277        __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
 278        spin_unlock(&appldata_timer_lock);
 279out:
 280        *lenp = len;
 281        *ppos += len;
 282        return 0;
 283}
 284
 285/*
 286 * appldata_generic_handler()
 287 *
 288 * Generic start/stop monitoring and DIAG, show status of
 289 * monitoring (0 = not in process, 1 = in process)
 290 */
 291static int
 292appldata_generic_handler(struct ctl_table *ctl, int write,
 293                           void __user *buffer, size_t *lenp, loff_t *ppos)
 294{
 295        struct appldata_ops *ops = NULL, *tmp_ops;
 296        unsigned int len;
 297        int rc, found;
 298        char buf[2];
 299        struct list_head *lh;
 300
 301        found = 0;
 302        mutex_lock(&appldata_ops_mutex);
 303        list_for_each(lh, &appldata_ops_list) {
 304                tmp_ops = list_entry(lh, struct appldata_ops, list);
 305                if (&tmp_ops->ctl_table[2] == ctl) {
 306                        found = 1;
 307                }
 308        }
 309        if (!found) {
 310                mutex_unlock(&appldata_ops_mutex);
 311                return -ENODEV;
 312        }
 313        ops = ctl->data;
 314        if (!try_module_get(ops->owner)) {      // protect this function
 315                mutex_unlock(&appldata_ops_mutex);
 316                return -ENODEV;
 317        }
 318        mutex_unlock(&appldata_ops_mutex);
 319
 320        if (!*lenp || *ppos) {
 321                *lenp = 0;
 322                module_put(ops->owner);
 323                return 0;
 324        }
 325        if (!write) {
 326                strncpy(buf, ops->active ? "1\n" : "0\n", ARRAY_SIZE(buf));
 327                len = strnlen(buf, ARRAY_SIZE(buf));
 328                if (len > *lenp)
 329                        len = *lenp;
 330                if (copy_to_user(buffer, buf, len)) {
 331                        module_put(ops->owner);
 332                        return -EFAULT;
 333                }
 334                goto out;
 335        }
 336        len = *lenp;
 337        if (copy_from_user(buf, buffer,
 338                           len > sizeof(buf) ? sizeof(buf) : len)) {
 339                module_put(ops->owner);
 340                return -EFAULT;
 341        }
 342
 343        mutex_lock(&appldata_ops_mutex);
 344        if ((buf[0] == '1') && (ops->active == 0)) {
 345                // protect work queue callback
 346                if (!try_module_get(ops->owner)) {
 347                        mutex_unlock(&appldata_ops_mutex);
 348                        module_put(ops->owner);
 349                        return -ENODEV;
 350                }
 351                ops->callback(ops->data);       // init record
 352                rc = appldata_diag(ops->record_nr,
 353                                        APPLDATA_START_INTERVAL_REC,
 354                                        (unsigned long) ops->data, ops->size,
 355                                        ops->mod_lvl);
 356                if (rc != 0) {
 357                        pr_err("Starting the data collection for %s "
 358                               "failed with rc=%d\n", ops->name, rc);
 359                        module_put(ops->owner);
 360                } else
 361                        ops->active = 1;
 362        } else if ((buf[0] == '0') && (ops->active == 1)) {
 363                ops->active = 0;
 364                rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
 365                                (unsigned long) ops->data, ops->size,
 366                                ops->mod_lvl);
 367                if (rc != 0)
 368                        pr_err("Stopping the data collection for %s "
 369                               "failed with rc=%d\n", ops->name, rc);
 370                module_put(ops->owner);
 371        }
 372        mutex_unlock(&appldata_ops_mutex);
 373out:
 374        *lenp = len;
 375        *ppos += len;
 376        module_put(ops->owner);
 377        return 0;
 378}
 379
 380/*************************** /proc stuff <END> *******************************/
 381
 382
 383/************************* module-ops management *****************************/
 384/*
 385 * appldata_register_ops()
 386 *
 387 * update ops list, register /proc/sys entries
 388 */
 389int appldata_register_ops(struct appldata_ops *ops)
 390{
 391        if (ops->size > APPLDATA_MAX_REC_SIZE)
 392                return -EINVAL;
 393
 394        ops->ctl_table = kcalloc(4, sizeof(struct ctl_table), GFP_KERNEL);
 395        if (!ops->ctl_table)
 396                return -ENOMEM;
 397
 398        mutex_lock(&appldata_ops_mutex);
 399        list_add(&ops->list, &appldata_ops_list);
 400        mutex_unlock(&appldata_ops_mutex);
 401
 402        ops->ctl_table[0].procname = appldata_proc_name;
 403        ops->ctl_table[0].maxlen   = 0;
 404        ops->ctl_table[0].mode     = S_IRUGO | S_IXUGO;
 405        ops->ctl_table[0].child    = &ops->ctl_table[2];
 406
 407        ops->ctl_table[2].procname = ops->name;
 408        ops->ctl_table[2].mode     = S_IRUGO | S_IWUSR;
 409        ops->ctl_table[2].proc_handler = appldata_generic_handler;
 410        ops->ctl_table[2].data = ops;
 411
 412        ops->sysctl_header = register_sysctl_table(ops->ctl_table);
 413        if (!ops->sysctl_header)
 414                goto out;
 415        return 0;
 416out:
 417        mutex_lock(&appldata_ops_mutex);
 418        list_del(&ops->list);
 419        mutex_unlock(&appldata_ops_mutex);
 420        kfree(ops->ctl_table);
 421        return -ENOMEM;
 422}
 423
 424/*
 425 * appldata_unregister_ops()
 426 *
 427 * update ops list, unregister /proc entries, stop DIAG if necessary
 428 */
 429void appldata_unregister_ops(struct appldata_ops *ops)
 430{
 431        mutex_lock(&appldata_ops_mutex);
 432        list_del(&ops->list);
 433        mutex_unlock(&appldata_ops_mutex);
 434        unregister_sysctl_table(ops->sysctl_header);
 435        kfree(ops->ctl_table);
 436}
 437/********************** module-ops management <END> **************************/
 438
 439
 440/**************************** suspend / resume *******************************/
 441static int appldata_freeze(struct device *dev)
 442{
 443        struct appldata_ops *ops;
 444        int rc;
 445        struct list_head *lh;
 446
 447        spin_lock(&appldata_timer_lock);
 448        if (appldata_timer_active) {
 449                __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
 450                appldata_timer_suspended = 1;
 451        }
 452        spin_unlock(&appldata_timer_lock);
 453
 454        mutex_lock(&appldata_ops_mutex);
 455        list_for_each(lh, &appldata_ops_list) {
 456                ops = list_entry(lh, struct appldata_ops, list);
 457                if (ops->active == 1) {
 458                        rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
 459                                        (unsigned long) ops->data, ops->size,
 460                                        ops->mod_lvl);
 461                        if (rc != 0)
 462                                pr_err("Stopping the data collection for %s "
 463                                       "failed with rc=%d\n", ops->name, rc);
 464                }
 465        }
 466        mutex_unlock(&appldata_ops_mutex);
 467        return 0;
 468}
 469
 470static int appldata_restore(struct device *dev)
 471{
 472        struct appldata_ops *ops;
 473        int rc;
 474        struct list_head *lh;
 475
 476        spin_lock(&appldata_timer_lock);
 477        if (appldata_timer_suspended) {
 478                __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
 479                appldata_timer_suspended = 0;
 480        }
 481        spin_unlock(&appldata_timer_lock);
 482
 483        mutex_lock(&appldata_ops_mutex);
 484        list_for_each(lh, &appldata_ops_list) {
 485                ops = list_entry(lh, struct appldata_ops, list);
 486                if (ops->active == 1) {
 487                        ops->callback(ops->data);       // init record
 488                        rc = appldata_diag(ops->record_nr,
 489                                        APPLDATA_START_INTERVAL_REC,
 490                                        (unsigned long) ops->data, ops->size,
 491                                        ops->mod_lvl);
 492                        if (rc != 0) {
 493                                pr_err("Starting the data collection for %s "
 494                                       "failed with rc=%d\n", ops->name, rc);
 495                        }
 496                }
 497        }
 498        mutex_unlock(&appldata_ops_mutex);
 499        return 0;
 500}
 501
 502static int appldata_thaw(struct device *dev)
 503{
 504        return appldata_restore(dev);
 505}
 506
 507static const struct dev_pm_ops appldata_pm_ops = {
 508        .freeze         = appldata_freeze,
 509        .thaw           = appldata_thaw,
 510        .restore        = appldata_restore,
 511};
 512
 513static struct platform_driver appldata_pdrv = {
 514        .driver = {
 515                .name   = "appldata",
 516                .pm     = &appldata_pm_ops,
 517        },
 518};
 519/************************* suspend / resume <END> ****************************/
 520
 521
 522/******************************* init / exit *********************************/
 523
 524/*
 525 * appldata_init()
 526 *
 527 * init timer, register /proc entries
 528 */
 529static int __init appldata_init(void)
 530{
 531        int rc;
 532
 533        init_virt_timer(&appldata_timer);
 534        appldata_timer.function = appldata_timer_function;
 535        appldata_timer.data = (unsigned long) &appldata_work;
 536
 537        rc = platform_driver_register(&appldata_pdrv);
 538        if (rc)
 539                return rc;
 540
 541        appldata_pdev = platform_device_register_simple("appldata", -1, NULL,
 542                                                        0);
 543        if (IS_ERR(appldata_pdev)) {
 544                rc = PTR_ERR(appldata_pdev);
 545                goto out_driver;
 546        }
 547        appldata_wq = alloc_ordered_workqueue("appldata", 0);
 548        if (!appldata_wq) {
 549                rc = -ENOMEM;
 550                goto out_device;
 551        }
 552
 553        appldata_sysctl_header = register_sysctl_table(appldata_dir_table);
 554        return 0;
 555
 556out_device:
 557        platform_device_unregister(appldata_pdev);
 558out_driver:
 559        platform_driver_unregister(&appldata_pdrv);
 560        return rc;
 561}
 562
 563__initcall(appldata_init);
 564
 565/**************************** init / exit <END> ******************************/
 566
 567EXPORT_SYMBOL_GPL(appldata_register_ops);
 568EXPORT_SYMBOL_GPL(appldata_unregister_ops);
 569EXPORT_SYMBOL_GPL(appldata_diag);
 570
 571#ifdef CONFIG_SWAP
 572EXPORT_SYMBOL_GPL(si_swapinfo);
 573#endif
 574EXPORT_SYMBOL_GPL(nr_threads);
 575EXPORT_SYMBOL_GPL(nr_running);
 576EXPORT_SYMBOL_GPL(nr_iowait);
 577