Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.
0004  * Exports appldata_register_ops() and appldata_unregister_ops() for the
0005  * data gathering modules.
0006  *
0007  * Copyright IBM Corp. 2003, 2009
0008  *
0009  * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
0010  */
0011 
0012 #define KMSG_COMPONENT  "appldata"
0013 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
0014 
0015 #include <linux/module.h>
0016 #include <linux/sched/stat.h>
0017 #include <linux/init.h>
0018 #include <linux/slab.h>
0019 #include <linux/errno.h>
0020 #include <linux/interrupt.h>
0021 #include <linux/proc_fs.h>
0022 #include <linux/mm.h>
0023 #include <linux/swap.h>
0024 #include <linux/pagemap.h>
0025 #include <linux/sysctl.h>
0026 #include <linux/notifier.h>
0027 #include <linux/cpu.h>
0028 #include <linux/workqueue.h>
0029 #include <linux/suspend.h>
0030 #include <linux/platform_device.h>
0031 #include <asm/appldata.h>
0032 #include <asm/vtimer.h>
0033 #include <linux/uaccess.h>
0034 #include <asm/io.h>
0035 #include <asm/smp.h>
0036 
0037 #include "appldata.h"
0038 
0039 
0040 #define APPLDATA_CPU_INTERVAL   10000       /* default (CPU) time for
0041                            sampling interval in
0042                            milliseconds */
0043 
0044 #define TOD_MICRO   0x01000         /* nr. of TOD clock units
0045                            for 1 microsecond */
0046 
0047 static struct platform_device *appldata_pdev;
0048 
0049 /*
0050  * /proc entries (sysctl)
0051  */
0052 static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
0053 static int appldata_timer_handler(struct ctl_table *ctl, int write,
0054                   void *buffer, size_t *lenp, loff_t *ppos);
0055 static int appldata_interval_handler(struct ctl_table *ctl, int write,
0056                      void *buffer, size_t *lenp, loff_t *ppos);
0057 
0058 static struct ctl_table_header *appldata_sysctl_header;
0059 static struct ctl_table appldata_table[] = {
0060     {
0061         .procname   = "timer",
0062         .mode       = S_IRUGO | S_IWUSR,
0063         .proc_handler   = appldata_timer_handler,
0064     },
0065     {
0066         .procname   = "interval",
0067         .mode       = S_IRUGO | S_IWUSR,
0068         .proc_handler   = appldata_interval_handler,
0069     },
0070     { },
0071 };
0072 
0073 static struct ctl_table appldata_dir_table[] = {
0074     {
0075         .procname   = appldata_proc_name,
0076         .maxlen     = 0,
0077         .mode       = S_IRUGO | S_IXUGO,
0078         .child      = appldata_table,
0079     },
0080     { },
0081 };
0082 
0083 /*
0084  * Timer
0085  */
0086 static struct vtimer_list appldata_timer;
0087 
0088 static DEFINE_SPINLOCK(appldata_timer_lock);
0089 static int appldata_interval = APPLDATA_CPU_INTERVAL;
0090 static int appldata_timer_active;
0091 static int appldata_timer_suspended = 0;
0092 
0093 /*
0094  * Work queue
0095  */
0096 static struct workqueue_struct *appldata_wq;
0097 static void appldata_work_fn(struct work_struct *work);
0098 static DECLARE_WORK(appldata_work, appldata_work_fn);
0099 
0100 
0101 /*
0102  * Ops list
0103  */
0104 static DEFINE_MUTEX(appldata_ops_mutex);
0105 static LIST_HEAD(appldata_ops_list);
0106 
0107 
0108 /*************************** timer, work, DIAG *******************************/
0109 /*
0110  * appldata_timer_function()
0111  *
0112  * schedule work and reschedule timer
0113  */
0114 static void appldata_timer_function(unsigned long data)
0115 {
0116     queue_work(appldata_wq, (struct work_struct *) data);
0117 }
0118 
0119 /*
0120  * appldata_work_fn()
0121  *
0122  * call data gathering function for each (active) module
0123  */
0124 static void appldata_work_fn(struct work_struct *work)
0125 {
0126     struct list_head *lh;
0127     struct appldata_ops *ops;
0128 
0129     mutex_lock(&appldata_ops_mutex);
0130     list_for_each(lh, &appldata_ops_list) {
0131         ops = list_entry(lh, struct appldata_ops, list);
0132         if (ops->active == 1) {
0133             ops->callback(ops->data);
0134         }
0135     }
0136     mutex_unlock(&appldata_ops_mutex);
0137 }
0138 
0139 static struct appldata_product_id appldata_id = {
0140     .prod_nr    = {0xD3, 0xC9, 0xD5, 0xE4,
0141                0xE7, 0xD2, 0xD9},   /* "LINUXKR" */
0142     .prod_fn    = 0xD5D3,           /* "NL" */
0143     .version_nr = 0xF2F6,           /* "26" */
0144     .release_nr = 0xF0F1,           /* "01" */
0145 };
0146 
0147 /*
0148  * appldata_diag()
0149  *
0150  * prepare parameter list, issue DIAG 0xDC
0151  */
0152 int appldata_diag(char record_nr, u16 function, unsigned long buffer,
0153             u16 length, char *mod_lvl)
0154 {
0155     struct appldata_parameter_list *parm_list;
0156     struct appldata_product_id *id;
0157     int rc;
0158 
0159     parm_list = kmalloc(sizeof(*parm_list), GFP_KERNEL);
0160     id = kmemdup(&appldata_id, sizeof(appldata_id), GFP_KERNEL);
0161     rc = -ENOMEM;
0162     if (parm_list && id) {
0163         id->record_nr = record_nr;
0164         id->mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1];
0165         rc = appldata_asm(parm_list, id, function,
0166                   (void *) buffer, length);
0167     }
0168     kfree(id);
0169     kfree(parm_list);
0170     return rc;
0171 }
0172 /************************ timer, work, DIAG <END> ****************************/
0173 
0174 
0175 /****************************** /proc stuff **********************************/
0176 
0177 #define APPLDATA_ADD_TIMER  0
0178 #define APPLDATA_DEL_TIMER  1
0179 #define APPLDATA_MOD_TIMER  2
0180 
0181 /*
0182  * __appldata_vtimer_setup()
0183  *
0184  * Add, delete or modify virtual timers on all online cpus.
0185  * The caller needs to get the appldata_timer_lock spinlock.
0186  */
0187 static void __appldata_vtimer_setup(int cmd)
0188 {
0189     u64 timer_interval = (u64) appldata_interval * 1000 * TOD_MICRO;
0190 
0191     switch (cmd) {
0192     case APPLDATA_ADD_TIMER:
0193         if (appldata_timer_active)
0194             break;
0195         appldata_timer.expires = timer_interval;
0196         add_virt_timer_periodic(&appldata_timer);
0197         appldata_timer_active = 1;
0198         break;
0199     case APPLDATA_DEL_TIMER:
0200         del_virt_timer(&appldata_timer);
0201         if (!appldata_timer_active)
0202             break;
0203         appldata_timer_active = 0;
0204         break;
0205     case APPLDATA_MOD_TIMER:
0206         if (!appldata_timer_active)
0207             break;
0208         mod_virt_timer_periodic(&appldata_timer, timer_interval);
0209     }
0210 }
0211 
0212 /*
0213  * appldata_timer_handler()
0214  *
0215  * Start/Stop timer, show status of timer (0 = not active, 1 = active)
0216  */
0217 static int
0218 appldata_timer_handler(struct ctl_table *ctl, int write,
0219                void *buffer, size_t *lenp, loff_t *ppos)
0220 {
0221     int timer_active = appldata_timer_active;
0222     int rc;
0223     struct ctl_table ctl_entry = {
0224         .procname   = ctl->procname,
0225         .data       = &timer_active,
0226         .maxlen     = sizeof(int),
0227         .extra1     = SYSCTL_ZERO,
0228         .extra2     = SYSCTL_ONE,
0229     };
0230 
0231     rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
0232     if (rc < 0 || !write)
0233         return rc;
0234 
0235     spin_lock(&appldata_timer_lock);
0236     if (timer_active)
0237         __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
0238     else
0239         __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
0240     spin_unlock(&appldata_timer_lock);
0241     return 0;
0242 }
0243 
0244 /*
0245  * appldata_interval_handler()
0246  *
0247  * Set (CPU) timer interval for collection of data (in milliseconds), show
0248  * current timer interval.
0249  */
0250 static int
0251 appldata_interval_handler(struct ctl_table *ctl, int write,
0252                void *buffer, size_t *lenp, loff_t *ppos)
0253 {
0254     int interval = appldata_interval;
0255     int rc;
0256     struct ctl_table ctl_entry = {
0257         .procname   = ctl->procname,
0258         .data       = &interval,
0259         .maxlen     = sizeof(int),
0260         .extra1     = SYSCTL_ONE,
0261     };
0262 
0263     rc = proc_dointvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
0264     if (rc < 0 || !write)
0265         return rc;
0266 
0267     spin_lock(&appldata_timer_lock);
0268     appldata_interval = interval;
0269     __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
0270     spin_unlock(&appldata_timer_lock);
0271     return 0;
0272 }
0273 
0274 /*
0275  * appldata_generic_handler()
0276  *
0277  * Generic start/stop monitoring and DIAG, show status of
0278  * monitoring (0 = not in process, 1 = in process)
0279  */
0280 static int
0281 appldata_generic_handler(struct ctl_table *ctl, int write,
0282                void *buffer, size_t *lenp, loff_t *ppos)
0283 {
0284     struct appldata_ops *ops = NULL, *tmp_ops;
0285     struct list_head *lh;
0286     int rc, found;
0287     int active;
0288     struct ctl_table ctl_entry = {
0289         .data       = &active,
0290         .maxlen     = sizeof(int),
0291         .extra1     = SYSCTL_ZERO,
0292         .extra2     = SYSCTL_ONE,
0293     };
0294 
0295     found = 0;
0296     mutex_lock(&appldata_ops_mutex);
0297     list_for_each(lh, &appldata_ops_list) {
0298         tmp_ops = list_entry(lh, struct appldata_ops, list);
0299         if (&tmp_ops->ctl_table[2] == ctl) {
0300             found = 1;
0301         }
0302     }
0303     if (!found) {
0304         mutex_unlock(&appldata_ops_mutex);
0305         return -ENODEV;
0306     }
0307     ops = ctl->data;
0308     if (!try_module_get(ops->owner)) {  // protect this function
0309         mutex_unlock(&appldata_ops_mutex);
0310         return -ENODEV;
0311     }
0312     mutex_unlock(&appldata_ops_mutex);
0313 
0314     active = ops->active;
0315     rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
0316     if (rc < 0 || !write) {
0317         module_put(ops->owner);
0318         return rc;
0319     }
0320 
0321     mutex_lock(&appldata_ops_mutex);
0322     if (active && (ops->active == 0)) {
0323         // protect work queue callback
0324         if (!try_module_get(ops->owner)) {
0325             mutex_unlock(&appldata_ops_mutex);
0326             module_put(ops->owner);
0327             return -ENODEV;
0328         }
0329         ops->callback(ops->data);   // init record
0330         rc = appldata_diag(ops->record_nr,
0331                     APPLDATA_START_INTERVAL_REC,
0332                     (unsigned long) ops->data, ops->size,
0333                     ops->mod_lvl);
0334         if (rc != 0) {
0335             pr_err("Starting the data collection for %s "
0336                    "failed with rc=%d\n", ops->name, rc);
0337             module_put(ops->owner);
0338         } else
0339             ops->active = 1;
0340     } else if (!active && (ops->active == 1)) {
0341         ops->active = 0;
0342         rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
0343                 (unsigned long) ops->data, ops->size,
0344                 ops->mod_lvl);
0345         if (rc != 0)
0346             pr_err("Stopping the data collection for %s "
0347                    "failed with rc=%d\n", ops->name, rc);
0348         module_put(ops->owner);
0349     }
0350     mutex_unlock(&appldata_ops_mutex);
0351     module_put(ops->owner);
0352     return 0;
0353 }
0354 
0355 /*************************** /proc stuff <END> *******************************/
0356 
0357 
0358 /************************* module-ops management *****************************/
0359 /*
0360  * appldata_register_ops()
0361  *
0362  * update ops list, register /proc/sys entries
0363  */
0364 int appldata_register_ops(struct appldata_ops *ops)
0365 {
0366     if (ops->size > APPLDATA_MAX_REC_SIZE)
0367         return -EINVAL;
0368 
0369     ops->ctl_table = kcalloc(4, sizeof(struct ctl_table), GFP_KERNEL);
0370     if (!ops->ctl_table)
0371         return -ENOMEM;
0372 
0373     mutex_lock(&appldata_ops_mutex);
0374     list_add(&ops->list, &appldata_ops_list);
0375     mutex_unlock(&appldata_ops_mutex);
0376 
0377     ops->ctl_table[0].procname = appldata_proc_name;
0378     ops->ctl_table[0].maxlen   = 0;
0379     ops->ctl_table[0].mode     = S_IRUGO | S_IXUGO;
0380     ops->ctl_table[0].child    = &ops->ctl_table[2];
0381 
0382     ops->ctl_table[2].procname = ops->name;
0383     ops->ctl_table[2].mode     = S_IRUGO | S_IWUSR;
0384     ops->ctl_table[2].proc_handler = appldata_generic_handler;
0385     ops->ctl_table[2].data = ops;
0386 
0387     ops->sysctl_header = register_sysctl_table(ops->ctl_table);
0388     if (!ops->sysctl_header)
0389         goto out;
0390     return 0;
0391 out:
0392     mutex_lock(&appldata_ops_mutex);
0393     list_del(&ops->list);
0394     mutex_unlock(&appldata_ops_mutex);
0395     kfree(ops->ctl_table);
0396     return -ENOMEM;
0397 }
0398 
0399 /*
0400  * appldata_unregister_ops()
0401  *
0402  * update ops list, unregister /proc entries, stop DIAG if necessary
0403  */
0404 void appldata_unregister_ops(struct appldata_ops *ops)
0405 {
0406     mutex_lock(&appldata_ops_mutex);
0407     list_del(&ops->list);
0408     mutex_unlock(&appldata_ops_mutex);
0409     unregister_sysctl_table(ops->sysctl_header);
0410     kfree(ops->ctl_table);
0411 }
0412 /********************** module-ops management <END> **************************/
0413 
0414 
0415 /**************************** suspend / resume *******************************/
0416 static int appldata_freeze(struct device *dev)
0417 {
0418     struct appldata_ops *ops;
0419     int rc;
0420     struct list_head *lh;
0421 
0422     spin_lock(&appldata_timer_lock);
0423     if (appldata_timer_active) {
0424         __appldata_vtimer_setup(APPLDATA_DEL_TIMER);
0425         appldata_timer_suspended = 1;
0426     }
0427     spin_unlock(&appldata_timer_lock);
0428 
0429     mutex_lock(&appldata_ops_mutex);
0430     list_for_each(lh, &appldata_ops_list) {
0431         ops = list_entry(lh, struct appldata_ops, list);
0432         if (ops->active == 1) {
0433             rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
0434                     (unsigned long) ops->data, ops->size,
0435                     ops->mod_lvl);
0436             if (rc != 0)
0437                 pr_err("Stopping the data collection for %s "
0438                        "failed with rc=%d\n", ops->name, rc);
0439         }
0440     }
0441     mutex_unlock(&appldata_ops_mutex);
0442     return 0;
0443 }
0444 
0445 static int appldata_restore(struct device *dev)
0446 {
0447     struct appldata_ops *ops;
0448     int rc;
0449     struct list_head *lh;
0450 
0451     spin_lock(&appldata_timer_lock);
0452     if (appldata_timer_suspended) {
0453         __appldata_vtimer_setup(APPLDATA_ADD_TIMER);
0454         appldata_timer_suspended = 0;
0455     }
0456     spin_unlock(&appldata_timer_lock);
0457 
0458     mutex_lock(&appldata_ops_mutex);
0459     list_for_each(lh, &appldata_ops_list) {
0460         ops = list_entry(lh, struct appldata_ops, list);
0461         if (ops->active == 1) {
0462             ops->callback(ops->data);   // init record
0463             rc = appldata_diag(ops->record_nr,
0464                     APPLDATA_START_INTERVAL_REC,
0465                     (unsigned long) ops->data, ops->size,
0466                     ops->mod_lvl);
0467             if (rc != 0) {
0468                 pr_err("Starting the data collection for %s "
0469                        "failed with rc=%d\n", ops->name, rc);
0470             }
0471         }
0472     }
0473     mutex_unlock(&appldata_ops_mutex);
0474     return 0;
0475 }
0476 
0477 static int appldata_thaw(struct device *dev)
0478 {
0479     return appldata_restore(dev);
0480 }
0481 
0482 static const struct dev_pm_ops appldata_pm_ops = {
0483     .freeze     = appldata_freeze,
0484     .thaw       = appldata_thaw,
0485     .restore    = appldata_restore,
0486 };
0487 
0488 static struct platform_driver appldata_pdrv = {
0489     .driver = {
0490         .name   = "appldata",
0491         .pm = &appldata_pm_ops,
0492     },
0493 };
0494 /************************* suspend / resume <END> ****************************/
0495 
0496 
0497 /******************************* init / exit *********************************/
0498 
0499 /*
0500  * appldata_init()
0501  *
0502  * init timer, register /proc entries
0503  */
0504 static int __init appldata_init(void)
0505 {
0506     int rc;
0507 
0508     init_virt_timer(&appldata_timer);
0509     appldata_timer.function = appldata_timer_function;
0510     appldata_timer.data = (unsigned long) &appldata_work;
0511 
0512     rc = platform_driver_register(&appldata_pdrv);
0513     if (rc)
0514         return rc;
0515 
0516     appldata_pdev = platform_device_register_simple("appldata", -1, NULL,
0517                             0);
0518     if (IS_ERR(appldata_pdev)) {
0519         rc = PTR_ERR(appldata_pdev);
0520         goto out_driver;
0521     }
0522     appldata_wq = alloc_ordered_workqueue("appldata", 0);
0523     if (!appldata_wq) {
0524         rc = -ENOMEM;
0525         goto out_device;
0526     }
0527 
0528     appldata_sysctl_header = register_sysctl_table(appldata_dir_table);
0529     return 0;
0530 
0531 out_device:
0532     platform_device_unregister(appldata_pdev);
0533 out_driver:
0534     platform_driver_unregister(&appldata_pdrv);
0535     return rc;
0536 }
0537 
0538 __initcall(appldata_init);
0539 
0540 /**************************** init / exit <END> ******************************/
0541 
0542 EXPORT_SYMBOL_GPL(appldata_register_ops);
0543 EXPORT_SYMBOL_GPL(appldata_unregister_ops);
0544 EXPORT_SYMBOL_GPL(appldata_diag);
0545 
0546 #ifdef CONFIG_SWAP
0547 EXPORT_SYMBOL_GPL(si_swapinfo);
0548 #endif
0549 EXPORT_SYMBOL_GPL(nr_threads);
0550 EXPORT_SYMBOL_GPL(nr_running);
0551 EXPORT_SYMBOL_GPL(nr_iowait);