Path: blob/master/arch/s390/appldata/appldata_base.c
10817 views
/*1* arch/s390/appldata/appldata_base.c2*3* Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.4* Exports appldata_register_ops() and appldata_unregister_ops() for the5* data gathering modules.6*7* Copyright IBM Corp. 2003, 20098*9* Author: Gerald Schaefer <[email protected]>10*/1112#define KMSG_COMPONENT "appldata"13#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt1415#include <linux/module.h>16#include <linux/init.h>17#include <linux/slab.h>18#include <linux/errno.h>19#include <linux/interrupt.h>20#include <linux/proc_fs.h>21#include <linux/mm.h>22#include <linux/swap.h>23#include <linux/pagemap.h>24#include <linux/sysctl.h>25#include <linux/notifier.h>26#include <linux/cpu.h>27#include <linux/workqueue.h>28#include <linux/suspend.h>29#include <linux/platform_device.h>30#include <asm/appldata.h>31#include <asm/timer.h>32#include <asm/uaccess.h>33#include <asm/io.h>34#include <asm/smp.h>3536#include "appldata.h"373839#define APPLDATA_CPU_INTERVAL 10000 /* default (CPU) time for40sampling interval in41milliseconds */4243#define TOD_MICRO 0x01000 /* nr. of TOD clock units44for 1 microsecond */4546static struct platform_device *appldata_pdev;4748/*49* /proc entries (sysctl)50*/51static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";52static int appldata_timer_handler(ctl_table *ctl, int write,53void __user *buffer, size_t *lenp, loff_t *ppos);54static int appldata_interval_handler(ctl_table *ctl, int write,55void __user *buffer,56size_t *lenp, loff_t *ppos);5758static struct ctl_table_header *appldata_sysctl_header;59static struct ctl_table appldata_table[] = {60{61.procname = "timer",62.mode = S_IRUGO | S_IWUSR,63.proc_handler = appldata_timer_handler,64},65{66.procname = "interval",67.mode = S_IRUGO | S_IWUSR,68.proc_handler = appldata_interval_handler,69},70{ },71};7273static struct ctl_table appldata_dir_table[] = {74{75.procname = appldata_proc_name,76.maxlen = 0,77.mode = S_IRUGO | S_IXUGO,78.child = appldata_table,79},80{ },81};8283/*84* Timer85*/86static DEFINE_PER_CPU(struct vtimer_list, appldata_timer);87static atomic_t appldata_expire_count = ATOMIC_INIT(0);8889static DEFINE_SPINLOCK(appldata_timer_lock);90static int appldata_interval = APPLDATA_CPU_INTERVAL;91static int appldata_timer_active;92static int appldata_timer_suspended = 0;9394/*95* Work queue96*/97static struct workqueue_struct *appldata_wq;98static void appldata_work_fn(struct work_struct *work);99static DECLARE_WORK(appldata_work, appldata_work_fn);100101102/*103* Ops list104*/105static DEFINE_MUTEX(appldata_ops_mutex);106static LIST_HEAD(appldata_ops_list);107108109/*************************** timer, work, DIAG *******************************/110/*111* appldata_timer_function()112*113* schedule work and reschedule timer114*/115static void appldata_timer_function(unsigned long data)116{117if (atomic_dec_and_test(&appldata_expire_count)) {118atomic_set(&appldata_expire_count, num_online_cpus());119queue_work(appldata_wq, (struct work_struct *) data);120}121}122123/*124* appldata_work_fn()125*126* call data gathering function for each (active) module127*/128static void appldata_work_fn(struct work_struct *work)129{130struct list_head *lh;131struct appldata_ops *ops;132133get_online_cpus();134mutex_lock(&appldata_ops_mutex);135list_for_each(lh, &appldata_ops_list) {136ops = list_entry(lh, struct appldata_ops, list);137if (ops->active == 1) {138ops->callback(ops->data);139}140}141mutex_unlock(&appldata_ops_mutex);142put_online_cpus();143}144145/*146* appldata_diag()147*148* prepare parameter list, issue DIAG 0xDC149*/150int appldata_diag(char record_nr, u16 function, unsigned long buffer,151u16 length, char *mod_lvl)152{153struct appldata_product_id id = {154.prod_nr = {0xD3, 0xC9, 0xD5, 0xE4,1550xE7, 0xD2, 0xD9}, /* "LINUXKR" */156.prod_fn = 0xD5D3, /* "NL" */157.version_nr = 0xF2F6, /* "26" */158.release_nr = 0xF0F1, /* "01" */159};160161id.record_nr = record_nr;162id.mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1];163return appldata_asm(&id, function, (void *) buffer, length);164}165/************************ timer, work, DIAG <END> ****************************/166167168/****************************** /proc stuff **********************************/169170/*171* appldata_mod_vtimer_wrap()172*173* wrapper function for mod_virt_timer(), because smp_call_function_single()174* accepts only one parameter.175*/176static void __appldata_mod_vtimer_wrap(void *p) {177struct {178struct vtimer_list *timer;179u64 expires;180} *args = p;181mod_virt_timer_periodic(args->timer, args->expires);182}183184#define APPLDATA_ADD_TIMER 0185#define APPLDATA_DEL_TIMER 1186#define APPLDATA_MOD_TIMER 2187188/*189* __appldata_vtimer_setup()190*191* Add, delete or modify virtual timers on all online cpus.192* The caller needs to get the appldata_timer_lock spinlock.193*/194static void195__appldata_vtimer_setup(int cmd)196{197u64 per_cpu_interval;198int i;199200switch (cmd) {201case APPLDATA_ADD_TIMER:202if (appldata_timer_active)203break;204per_cpu_interval = (u64) (appldata_interval*1000 /205num_online_cpus()) * TOD_MICRO;206for_each_online_cpu(i) {207per_cpu(appldata_timer, i).expires = per_cpu_interval;208smp_call_function_single(i, add_virt_timer_periodic,209&per_cpu(appldata_timer, i),2101);211}212appldata_timer_active = 1;213break;214case APPLDATA_DEL_TIMER:215for_each_online_cpu(i)216del_virt_timer(&per_cpu(appldata_timer, i));217if (!appldata_timer_active)218break;219appldata_timer_active = 0;220atomic_set(&appldata_expire_count, num_online_cpus());221break;222case APPLDATA_MOD_TIMER:223per_cpu_interval = (u64) (appldata_interval*1000 /224num_online_cpus()) * TOD_MICRO;225if (!appldata_timer_active)226break;227for_each_online_cpu(i) {228struct {229struct vtimer_list *timer;230u64 expires;231} args;232args.timer = &per_cpu(appldata_timer, i);233args.expires = per_cpu_interval;234smp_call_function_single(i, __appldata_mod_vtimer_wrap,235&args, 1);236}237}238}239240/*241* appldata_timer_handler()242*243* Start/Stop timer, show status of timer (0 = not active, 1 = active)244*/245static int246appldata_timer_handler(ctl_table *ctl, int write,247void __user *buffer, size_t *lenp, loff_t *ppos)248{249int len;250char buf[2];251252if (!*lenp || *ppos) {253*lenp = 0;254return 0;255}256if (!write) {257len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n");258if (len > *lenp)259len = *lenp;260if (copy_to_user(buffer, buf, len))261return -EFAULT;262goto out;263}264len = *lenp;265if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))266return -EFAULT;267get_online_cpus();268spin_lock(&appldata_timer_lock);269if (buf[0] == '1')270__appldata_vtimer_setup(APPLDATA_ADD_TIMER);271else if (buf[0] == '0')272__appldata_vtimer_setup(APPLDATA_DEL_TIMER);273spin_unlock(&appldata_timer_lock);274put_online_cpus();275out:276*lenp = len;277*ppos += len;278return 0;279}280281/*282* appldata_interval_handler()283*284* Set (CPU) timer interval for collection of data (in milliseconds), show285* current timer interval.286*/287static int288appldata_interval_handler(ctl_table *ctl, int write,289void __user *buffer, size_t *lenp, loff_t *ppos)290{291int len, interval;292char buf[16];293294if (!*lenp || *ppos) {295*lenp = 0;296return 0;297}298if (!write) {299len = sprintf(buf, "%i\n", appldata_interval);300if (len > *lenp)301len = *lenp;302if (copy_to_user(buffer, buf, len))303return -EFAULT;304goto out;305}306len = *lenp;307if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) {308return -EFAULT;309}310interval = 0;311sscanf(buf, "%i", &interval);312if (interval <= 0)313return -EINVAL;314315get_online_cpus();316spin_lock(&appldata_timer_lock);317appldata_interval = interval;318__appldata_vtimer_setup(APPLDATA_MOD_TIMER);319spin_unlock(&appldata_timer_lock);320put_online_cpus();321out:322*lenp = len;323*ppos += len;324return 0;325}326327/*328* appldata_generic_handler()329*330* Generic start/stop monitoring and DIAG, show status of331* monitoring (0 = not in process, 1 = in process)332*/333static int334appldata_generic_handler(ctl_table *ctl, int write,335void __user *buffer, size_t *lenp, loff_t *ppos)336{337struct appldata_ops *ops = NULL, *tmp_ops;338int rc, len, found;339char buf[2];340struct list_head *lh;341342found = 0;343mutex_lock(&appldata_ops_mutex);344list_for_each(lh, &appldata_ops_list) {345tmp_ops = list_entry(lh, struct appldata_ops, list);346if (&tmp_ops->ctl_table[2] == ctl) {347found = 1;348}349}350if (!found) {351mutex_unlock(&appldata_ops_mutex);352return -ENODEV;353}354ops = ctl->data;355if (!try_module_get(ops->owner)) { // protect this function356mutex_unlock(&appldata_ops_mutex);357return -ENODEV;358}359mutex_unlock(&appldata_ops_mutex);360361if (!*lenp || *ppos) {362*lenp = 0;363module_put(ops->owner);364return 0;365}366if (!write) {367len = sprintf(buf, ops->active ? "1\n" : "0\n");368if (len > *lenp)369len = *lenp;370if (copy_to_user(buffer, buf, len)) {371module_put(ops->owner);372return -EFAULT;373}374goto out;375}376len = *lenp;377if (copy_from_user(buf, buffer,378len > sizeof(buf) ? sizeof(buf) : len)) {379module_put(ops->owner);380return -EFAULT;381}382383mutex_lock(&appldata_ops_mutex);384if ((buf[0] == '1') && (ops->active == 0)) {385// protect work queue callback386if (!try_module_get(ops->owner)) {387mutex_unlock(&appldata_ops_mutex);388module_put(ops->owner);389return -ENODEV;390}391ops->callback(ops->data); // init record392rc = appldata_diag(ops->record_nr,393APPLDATA_START_INTERVAL_REC,394(unsigned long) ops->data, ops->size,395ops->mod_lvl);396if (rc != 0) {397pr_err("Starting the data collection for %s "398"failed with rc=%d\n", ops->name, rc);399module_put(ops->owner);400} else401ops->active = 1;402} else if ((buf[0] == '0') && (ops->active == 1)) {403ops->active = 0;404rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,405(unsigned long) ops->data, ops->size,406ops->mod_lvl);407if (rc != 0)408pr_err("Stopping the data collection for %s "409"failed with rc=%d\n", ops->name, rc);410module_put(ops->owner);411}412mutex_unlock(&appldata_ops_mutex);413out:414*lenp = len;415*ppos += len;416module_put(ops->owner);417return 0;418}419420/*************************** /proc stuff <END> *******************************/421422423/************************* module-ops management *****************************/424/*425* appldata_register_ops()426*427* update ops list, register /proc/sys entries428*/429int appldata_register_ops(struct appldata_ops *ops)430{431if (ops->size > APPLDATA_MAX_REC_SIZE)432return -EINVAL;433434ops->ctl_table = kzalloc(4 * sizeof(struct ctl_table), GFP_KERNEL);435if (!ops->ctl_table)436return -ENOMEM;437438mutex_lock(&appldata_ops_mutex);439list_add(&ops->list, &appldata_ops_list);440mutex_unlock(&appldata_ops_mutex);441442ops->ctl_table[0].procname = appldata_proc_name;443ops->ctl_table[0].maxlen = 0;444ops->ctl_table[0].mode = S_IRUGO | S_IXUGO;445ops->ctl_table[0].child = &ops->ctl_table[2];446447ops->ctl_table[2].procname = ops->name;448ops->ctl_table[2].mode = S_IRUGO | S_IWUSR;449ops->ctl_table[2].proc_handler = appldata_generic_handler;450ops->ctl_table[2].data = ops;451452ops->sysctl_header = register_sysctl_table(ops->ctl_table);453if (!ops->sysctl_header)454goto out;455return 0;456out:457mutex_lock(&appldata_ops_mutex);458list_del(&ops->list);459mutex_unlock(&appldata_ops_mutex);460kfree(ops->ctl_table);461return -ENOMEM;462}463464/*465* appldata_unregister_ops()466*467* update ops list, unregister /proc entries, stop DIAG if necessary468*/469void appldata_unregister_ops(struct appldata_ops *ops)470{471mutex_lock(&appldata_ops_mutex);472list_del(&ops->list);473mutex_unlock(&appldata_ops_mutex);474unregister_sysctl_table(ops->sysctl_header);475kfree(ops->ctl_table);476}477/********************** module-ops management <END> **************************/478479480/**************************** suspend / resume *******************************/481static int appldata_freeze(struct device *dev)482{483struct appldata_ops *ops;484int rc;485struct list_head *lh;486487get_online_cpus();488spin_lock(&appldata_timer_lock);489if (appldata_timer_active) {490__appldata_vtimer_setup(APPLDATA_DEL_TIMER);491appldata_timer_suspended = 1;492}493spin_unlock(&appldata_timer_lock);494put_online_cpus();495496mutex_lock(&appldata_ops_mutex);497list_for_each(lh, &appldata_ops_list) {498ops = list_entry(lh, struct appldata_ops, list);499if (ops->active == 1) {500rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,501(unsigned long) ops->data, ops->size,502ops->mod_lvl);503if (rc != 0)504pr_err("Stopping the data collection for %s "505"failed with rc=%d\n", ops->name, rc);506}507}508mutex_unlock(&appldata_ops_mutex);509return 0;510}511512static int appldata_restore(struct device *dev)513{514struct appldata_ops *ops;515int rc;516struct list_head *lh;517518get_online_cpus();519spin_lock(&appldata_timer_lock);520if (appldata_timer_suspended) {521__appldata_vtimer_setup(APPLDATA_ADD_TIMER);522appldata_timer_suspended = 0;523}524spin_unlock(&appldata_timer_lock);525put_online_cpus();526527mutex_lock(&appldata_ops_mutex);528list_for_each(lh, &appldata_ops_list) {529ops = list_entry(lh, struct appldata_ops, list);530if (ops->active == 1) {531ops->callback(ops->data); // init record532rc = appldata_diag(ops->record_nr,533APPLDATA_START_INTERVAL_REC,534(unsigned long) ops->data, ops->size,535ops->mod_lvl);536if (rc != 0) {537pr_err("Starting the data collection for %s "538"failed with rc=%d\n", ops->name, rc);539}540}541}542mutex_unlock(&appldata_ops_mutex);543return 0;544}545546static int appldata_thaw(struct device *dev)547{548return appldata_restore(dev);549}550551static const struct dev_pm_ops appldata_pm_ops = {552.freeze = appldata_freeze,553.thaw = appldata_thaw,554.restore = appldata_restore,555};556557static struct platform_driver appldata_pdrv = {558.driver = {559.name = "appldata",560.owner = THIS_MODULE,561.pm = &appldata_pm_ops,562},563};564/************************* suspend / resume <END> ****************************/565566567/******************************* init / exit *********************************/568569static void __cpuinit appldata_online_cpu(int cpu)570{571init_virt_timer(&per_cpu(appldata_timer, cpu));572per_cpu(appldata_timer, cpu).function = appldata_timer_function;573per_cpu(appldata_timer, cpu).data = (unsigned long)574&appldata_work;575atomic_inc(&appldata_expire_count);576spin_lock(&appldata_timer_lock);577__appldata_vtimer_setup(APPLDATA_MOD_TIMER);578spin_unlock(&appldata_timer_lock);579}580581static void __cpuinit appldata_offline_cpu(int cpu)582{583del_virt_timer(&per_cpu(appldata_timer, cpu));584if (atomic_dec_and_test(&appldata_expire_count)) {585atomic_set(&appldata_expire_count, num_online_cpus());586queue_work(appldata_wq, &appldata_work);587}588spin_lock(&appldata_timer_lock);589__appldata_vtimer_setup(APPLDATA_MOD_TIMER);590spin_unlock(&appldata_timer_lock);591}592593static int __cpuinit appldata_cpu_notify(struct notifier_block *self,594unsigned long action,595void *hcpu)596{597switch (action) {598case CPU_ONLINE:599case CPU_ONLINE_FROZEN:600appldata_online_cpu((long) hcpu);601break;602case CPU_DEAD:603case CPU_DEAD_FROZEN:604appldata_offline_cpu((long) hcpu);605break;606default:607break;608}609return NOTIFY_OK;610}611612static struct notifier_block __cpuinitdata appldata_nb = {613.notifier_call = appldata_cpu_notify,614};615616/*617* appldata_init()618*619* init timer, register /proc entries620*/621static int __init appldata_init(void)622{623int i, rc;624625rc = platform_driver_register(&appldata_pdrv);626if (rc)627return rc;628629appldata_pdev = platform_device_register_simple("appldata", -1, NULL,6300);631if (IS_ERR(appldata_pdev)) {632rc = PTR_ERR(appldata_pdev);633goto out_driver;634}635appldata_wq = create_singlethread_workqueue("appldata");636if (!appldata_wq) {637rc = -ENOMEM;638goto out_device;639}640641get_online_cpus();642for_each_online_cpu(i)643appldata_online_cpu(i);644put_online_cpus();645646/* Register cpu hotplug notifier */647register_hotcpu_notifier(&appldata_nb);648649appldata_sysctl_header = register_sysctl_table(appldata_dir_table);650return 0;651652out_device:653platform_device_unregister(appldata_pdev);654out_driver:655platform_driver_unregister(&appldata_pdrv);656return rc;657}658659__initcall(appldata_init);660661/**************************** init / exit <END> ******************************/662663EXPORT_SYMBOL_GPL(appldata_register_ops);664EXPORT_SYMBOL_GPL(appldata_unregister_ops);665EXPORT_SYMBOL_GPL(appldata_diag);666667#ifdef CONFIG_SWAP668EXPORT_SYMBOL_GPL(si_swapinfo);669#endif670EXPORT_SYMBOL_GPL(nr_threads);671EXPORT_SYMBOL_GPL(nr_running);672EXPORT_SYMBOL_GPL(nr_iowait);673674675