appldata_base.c
来自「linux 内核源代码」· C语言 代码 · 共 583 行
C
583 行
/* * arch/s390/appldata/appldata_base.c * * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1. * Exports appldata_register_ops() and appldata_unregister_ops() for the * data gathering modules. * * Copyright (C) 2003,2006 IBM Corporation, IBM Deutschland Entwicklung GmbH. * * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> */#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/mm.h>#include <linux/swap.h>#include <linux/pagemap.h>#include <linux/sysctl.h>#include <linux/notifier.h>#include <linux/cpu.h>#include <linux/workqueue.h>#include <asm/appldata.h>#include <asm/timer.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/smp.h>#include "appldata.h"#define MY_PRINT_NAME "appldata" /* for debug messages, etc. */#define APPLDATA_CPU_INTERVAL 10000 /* default (CPU) time for sampling interval in milliseconds */#define TOD_MICRO 0x01000 /* nr. of TOD clock units for 1 microsecond *//* * /proc entries (sysctl) */static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos);static int appldata_interval_handler(ctl_table *ctl, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos);static struct ctl_table_header *appldata_sysctl_header;static struct ctl_table appldata_table[] = { { .procname = "timer", .mode = S_IRUGO | S_IWUSR, .proc_handler = &appldata_timer_handler, }, { .procname = "interval", .mode = S_IRUGO | S_IWUSR, .proc_handler = &appldata_interval_handler, }, { },};static struct ctl_table appldata_dir_table[] = { { .procname = appldata_proc_name, .maxlen = 0, .mode = S_IRUGO | S_IXUGO, .child = appldata_table, }, { },};/* * Timer */static DEFINE_PER_CPU(struct vtimer_list, appldata_timer);static atomic_t appldata_expire_count = ATOMIC_INIT(0);static DEFINE_SPINLOCK(appldata_timer_lock);static int appldata_interval = APPLDATA_CPU_INTERVAL;static int appldata_timer_active;/* * Work queue */static struct workqueue_struct *appldata_wq;static void appldata_work_fn(struct work_struct *work);static DECLARE_WORK(appldata_work, appldata_work_fn);/* * Ops list */static DEFINE_SPINLOCK(appldata_ops_lock);static LIST_HEAD(appldata_ops_list);/*************************** timer, work, DIAG *******************************//* * appldata_timer_function() * * schedule work and reschedule timer */static void appldata_timer_function(unsigned long data){ P_DEBUG(" -= Timer =-\n"); P_DEBUG("CPU: %i, expire_count: %i\n", smp_processor_id(), atomic_read(&appldata_expire_count)); if (atomic_dec_and_test(&appldata_expire_count)) { atomic_set(&appldata_expire_count, num_online_cpus()); queue_work(appldata_wq, (struct work_struct *) data); }}/* * appldata_work_fn() * * call data gathering function for each (active) module */static void appldata_work_fn(struct work_struct *work){ struct list_head *lh; struct appldata_ops *ops; int i; P_DEBUG(" -= Work Queue =-\n"); i = 0; spin_lock(&appldata_ops_lock); list_for_each(lh, &appldata_ops_list) { ops = list_entry(lh, struct appldata_ops, list); P_DEBUG("list_for_each loop: %i) active = %u, name = %s\n", ++i, ops->active, ops->name); if (ops->active == 1) { ops->callback(ops->data); } } spin_unlock(&appldata_ops_lock);}/* * appldata_diag() * * prepare parameter list, issue DIAG 0xDC */int appldata_diag(char record_nr, u16 function, unsigned long buffer, u16 length, char *mod_lvl){ struct appldata_product_id id = { .prod_nr = {0xD3, 0xC9, 0xD5, 0xE4, 0xE7, 0xD2, 0xD9}, /* "LINUXKR" */ .prod_fn = 0xD5D3, /* "NL" */ .version_nr = 0xF2F6, /* "26" */ .release_nr = 0xF0F1, /* "01" */ }; id.record_nr = record_nr; id.mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1]; return appldata_asm(&id, function, (void *) buffer, length);}/************************ timer, work, DIAG <END> ****************************//****************************** /proc stuff **********************************//* * appldata_mod_vtimer_wrap() * * wrapper function for mod_virt_timer(), because smp_call_function_single() * accepts only one parameter. */static void __appldata_mod_vtimer_wrap(void *p) { struct { struct vtimer_list *timer; u64 expires; } *args = p; mod_virt_timer(args->timer, args->expires);}#define APPLDATA_ADD_TIMER 0#define APPLDATA_DEL_TIMER 1#define APPLDATA_MOD_TIMER 2/* * __appldata_vtimer_setup() * * Add, delete or modify virtual timers on all online cpus. * The caller needs to get the appldata_timer_lock spinlock. */static void__appldata_vtimer_setup(int cmd){ u64 per_cpu_interval; int i; switch (cmd) { case APPLDATA_ADD_TIMER: if (appldata_timer_active) break; per_cpu_interval = (u64) (appldata_interval*1000 / num_online_cpus()) * TOD_MICRO; for_each_online_cpu(i) { per_cpu(appldata_timer, i).expires = per_cpu_interval; smp_call_function_single(i, add_virt_timer_periodic, &per_cpu(appldata_timer, i), 0, 1); } appldata_timer_active = 1; P_INFO("Monitoring timer started.\n"); break; case APPLDATA_DEL_TIMER: for_each_online_cpu(i) del_virt_timer(&per_cpu(appldata_timer, i)); if (!appldata_timer_active) break; appldata_timer_active = 0; atomic_set(&appldata_expire_count, num_online_cpus()); P_INFO("Monitoring timer stopped.\n"); break; case APPLDATA_MOD_TIMER: per_cpu_interval = (u64) (appldata_interval*1000 / num_online_cpus()) * TOD_MICRO; if (!appldata_timer_active) break; for_each_online_cpu(i) { struct { struct vtimer_list *timer; u64 expires; } args; args.timer = &per_cpu(appldata_timer, i); args.expires = per_cpu_interval; smp_call_function_single(i, __appldata_mod_vtimer_wrap, &args, 0, 1); } }}/* * appldata_timer_handler() * * Start/Stop timer, show status of timer (0 = not active, 1 = active) */static intappldata_timer_handler(ctl_table *ctl, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos){ int len; char buf[2]; if (!*lenp || *ppos) { *lenp = 0; return 0; } if (!write) { len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n"); if (len > *lenp) len = *lenp; if (copy_to_user(buffer, buf, len)) return -EFAULT; goto out; } len = *lenp; if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) return -EFAULT; spin_lock(&appldata_timer_lock); if (buf[0] == '1') __appldata_vtimer_setup(APPLDATA_ADD_TIMER); else if (buf[0] == '0') __appldata_vtimer_setup(APPLDATA_DEL_TIMER); spin_unlock(&appldata_timer_lock);out: *lenp = len; *ppos += len; return 0;}/* * appldata_interval_handler() * * Set (CPU) timer interval for collection of data (in milliseconds), show * current timer interval. */static intappldata_interval_handler(ctl_table *ctl, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos){ int len, interval; char buf[16]; if (!*lenp || *ppos) { *lenp = 0; return 0; } if (!write) { len = sprintf(buf, "%i\n", appldata_interval); if (len > *lenp) len = *lenp; if (copy_to_user(buffer, buf, len)) return -EFAULT; goto out; } len = *lenp; if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) { return -EFAULT; } interval = 0; sscanf(buf, "%i", &interval); if (interval <= 0) { P_ERROR("Timer CPU interval has to be > 0!\n"); return -EINVAL; } spin_lock(&appldata_timer_lock); appldata_interval = interval; __appldata_vtimer_setup(APPLDATA_MOD_TIMER); spin_unlock(&appldata_timer_lock); P_INFO("Monitoring CPU interval set to %u milliseconds.\n", interval);out: *lenp = len; *ppos += len; return 0;}/* * appldata_generic_handler() * * Generic start/stop monitoring and DIAG, show status of * monitoring (0 = not in process, 1 = in process) */static intappldata_generic_handler(ctl_table *ctl, int write, struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos){ struct appldata_ops *ops = NULL, *tmp_ops; int rc, len, found; char buf[2]; struct list_head *lh; found = 0; spin_lock(&appldata_ops_lock); list_for_each(lh, &appldata_ops_list) { tmp_ops = list_entry(lh, struct appldata_ops, list); if (&tmp_ops->ctl_table[2] == ctl) { found = 1; } } if (!found) { spin_unlock(&appldata_ops_lock); return -ENODEV; } ops = ctl->data; if (!try_module_get(ops->owner)) { // protect this function spin_unlock(&appldata_ops_lock); return -ENODEV; } spin_unlock(&appldata_ops_lock); if (!*lenp || *ppos) { *lenp = 0; module_put(ops->owner); return 0; } if (!write) { len = sprintf(buf, ops->active ? "1\n" : "0\n"); if (len > *lenp) len = *lenp; if (copy_to_user(buffer, buf, len)) { module_put(ops->owner); return -EFAULT; } goto out; } len = *lenp; if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) { module_put(ops->owner); return -EFAULT; } spin_lock(&appldata_ops_lock); if ((buf[0] == '1') && (ops->active == 0)) { // protect work queue callback if (!try_module_get(ops->owner)) { spin_unlock(&appldata_ops_lock); module_put(ops->owner); return -ENODEV; } ops->callback(ops->data); // init record rc = appldata_diag(ops->record_nr, APPLDATA_START_INTERVAL_REC, (unsigned long) ops->data, ops->size, ops->mod_lvl); if (rc != 0) { P_ERROR("START DIAG 0xDC for %s failed, " "return code: %d\n", ops->name, rc); module_put(ops->owner); } else { P_INFO("Monitoring %s data enabled, " "DIAG 0xDC started.\n", ops->name); ops->active = 1; } } else if ((buf[0] == '0') && (ops->active == 1)) { ops->active = 0; rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, (unsigned long) ops->data, ops->size, ops->mod_lvl); if (rc != 0) { P_ERROR("STOP DIAG 0xDC for %s failed, " "return code: %d\n", ops->name, rc); } else { P_INFO("Monitoring %s data disabled, " "DIAG 0xDC stopped.\n", ops->name); } module_put(ops->owner); } spin_unlock(&appldata_ops_lock);out: *lenp = len; *ppos += len; module_put(ops->owner); return 0;}/*************************** /proc stuff <END> *******************************//************************* module-ops management *****************************//* * appldata_register_ops() * * update ops list, register /proc/sys entries */int appldata_register_ops(struct appldata_ops *ops){ if ((ops->size > APPLDATA_MAX_REC_SIZE) || (ops->size < 0)) return -EINVAL; ops->ctl_table = kzalloc(4 * sizeof(struct ctl_table), GFP_KERNEL); if (!ops->ctl_table) return -ENOMEM; spin_lock(&appldata_ops_lock); list_add(&ops->list, &appldata_ops_list); spin_unlock(&appldata_ops_lock); ops->ctl_table[0].procname = appldata_proc_name; ops->ctl_table[0].maxlen = 0; ops->ctl_table[0].mode = S_IRUGO | S_IXUGO; ops->ctl_table[0].child = &ops->ctl_table[2]; ops->ctl_table[2].procname = ops->name; ops->ctl_table[2].mode = S_IRUGO | S_IWUSR; ops->ctl_table[2].proc_handler = appldata_generic_handler; ops->ctl_table[2].data = ops; ops->sysctl_header = register_sysctl_table(ops->ctl_table); if (!ops->sysctl_header) goto out; P_INFO("%s-ops registered!\n", ops->name); return 0;out: spin_lock(&appldata_ops_lock); list_del(&ops->list); spin_unlock(&appldata_ops_lock); kfree(ops->ctl_table); return -ENOMEM;}/* * appldata_unregister_ops() * * update ops list, unregister /proc entries, stop DIAG if necessary */void appldata_unregister_ops(struct appldata_ops *ops){ spin_lock(&appldata_ops_lock); list_del(&ops->list); spin_unlock(&appldata_ops_lock); unregister_sysctl_table(ops->sysctl_header); kfree(ops->ctl_table); P_INFO("%s-ops unregistered!\n", ops->name);}/********************** module-ops management <END> **************************//******************************* init / exit *********************************/static void __cpuinit appldata_online_cpu(int cpu){ init_virt_timer(&per_cpu(appldata_timer, cpu)); per_cpu(appldata_timer, cpu).function = appldata_timer_function; per_cpu(appldata_timer, cpu).data = (unsigned long) &appldata_work; atomic_inc(&appldata_expire_count); spin_lock(&appldata_timer_lock); __appldata_vtimer_setup(APPLDATA_MOD_TIMER); spin_unlock(&appldata_timer_lock);}static void __cpuinit appldata_offline_cpu(int cpu){ del_virt_timer(&per_cpu(appldata_timer, cpu)); if (atomic_dec_and_test(&appldata_expire_count)) { atomic_set(&appldata_expire_count, num_online_cpus()); queue_work(appldata_wq, &appldata_work); } spin_lock(&appldata_timer_lock); __appldata_vtimer_setup(APPLDATA_MOD_TIMER); spin_unlock(&appldata_timer_lock);}static int __cpuinit appldata_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu){ switch (action) { case CPU_ONLINE: case CPU_ONLINE_FROZEN: appldata_online_cpu((long) hcpu); break; case CPU_DEAD: case CPU_DEAD_FROZEN: appldata_offline_cpu((long) hcpu); break; default: break; } return NOTIFY_OK;}static struct notifier_block __cpuinitdata appldata_nb = { .notifier_call = appldata_cpu_notify,};/* * appldata_init() * * init timer, register /proc entries */static int __init appldata_init(void){ int i; P_DEBUG("sizeof(parameter_list) = %lu\n", sizeof(struct appldata_parameter_list)); appldata_wq = create_singlethread_workqueue("appldata"); if (!appldata_wq) { P_ERROR("Could not create work queue\n"); return -ENOMEM; } for_each_online_cpu(i) appldata_online_cpu(i); /* Register cpu hotplug notifier */ register_hotcpu_notifier(&appldata_nb); appldata_sysctl_header = register_sysctl_table(appldata_dir_table); P_DEBUG("Base interface initialized.\n"); return 0;}__initcall(appldata_init);/**************************** init / exit <END> ******************************/EXPORT_SYMBOL_GPL(appldata_register_ops);EXPORT_SYMBOL_GPL(appldata_unregister_ops);EXPORT_SYMBOL_GPL(appldata_diag);EXPORT_SYMBOL_GPL(si_swapinfo);EXPORT_SYMBOL_GPL(nr_threads);EXPORT_SYMBOL_GPL(nr_running);EXPORT_SYMBOL_GPL(nr_iowait);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?