📄 appldata_base.c
字号:
/* * 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 IBM Corporation, IBM Deutschland Entwicklung GmbH. * * Author: Gerald Schaefer <geraldsc@de.ibm.com> */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/errno.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/smp.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/page-flags.h>#include <linux/swap.h>#include <linux/pagemap.h>#include <linux/sysctl.h>#include <asm/timer.h>//#include <linux/kernel_stat.h>#include <linux/notifier.h>#include <linux/cpu.h>#include <linux/workqueue.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 */#ifndef CONFIG_ARCH_S390X#define APPLDATA_START_INTERVAL_REC 0x00 /* Function codes for */#define APPLDATA_STOP_REC 0x01 /* DIAG 0xDC */#define APPLDATA_GEN_EVENT_RECORD 0x02#define APPLDATA_START_CONFIG_REC 0x03#else#define APPLDATA_START_INTERVAL_REC 0x80#define APPLDATA_STOP_REC 0x81#define APPLDATA_GEN_EVENT_RECORD 0x82#define APPLDATA_START_CONFIG_REC 0x83#endif /* CONFIG_ARCH_S390X *//* * Parameter list for DIAGNOSE X'DC' */#ifndef CONFIG_ARCH_S390Xstruct appldata_parameter_list { u16 diag; /* The DIAGNOSE code X'00DC' */ u8 function; /* The function code for the DIAGNOSE */ u8 parlist_length; /* Length of the parameter list */ u32 product_id_addr; /* Address of the 16-byte product ID */ u16 reserved; u16 buffer_length; /* Length of the application data buffer */ u32 buffer_addr; /* Address of the application data buffer */};#elsestruct appldata_parameter_list { u16 diag; u8 function; u8 parlist_length; u32 unused01; u16 reserved; u16 buffer_length; u32 unused02; u64 product_id_addr; u64 buffer_addr;};#endif /* CONFIG_ARCH_S390X *//* * /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[] = { { .ctl_name = CTL_APPLDATA_TIMER, .procname = "timer", .mode = S_IRUGO | S_IWUSR, .proc_handler = &appldata_timer_handler, }, { .ctl_name = CTL_APPLDATA_INTERVAL, .procname = "interval", .mode = S_IRUGO | S_IWUSR, .proc_handler = &appldata_interval_handler, }, { .ctl_name = 0 }};static struct ctl_table appldata_dir_table[] = { { .ctl_name = CTL_APPLDATA, .procname = appldata_proc_name, .maxlen = 0, .mode = S_IRUGO | S_IXUGO, .child = appldata_table, }, { .ctl_name = 0 }};/* * Timer */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(void *data);static DECLARE_WORK(appldata_work, appldata_work_fn, NULL);/* * 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, struct pt_regs *regs){ 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(void *data){ 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 */static int appldata_diag(char record_nr, u16 function, unsigned long buffer, u16 length){ unsigned long ry; struct appldata_product_id { char prod_nr[7]; /* product nr. */ char prod_fn[2]; /* product function */ char record_nr; /* record nr. */ char version_nr[2]; /* version */ char release_nr[2]; /* release */ char mod_lvl[2]; /* modification lvl. */ } appldata_product_id = { /* all strings are EBCDIC, record_nr is byte */ .prod_nr = {0xD3, 0xC9, 0xD5, 0xE4, 0xE7, 0xD2, 0xD9}, /* "LINUXKR" */ .prod_fn = {0xD5, 0xD3}, /* "NL" */ .record_nr = record_nr, .version_nr = {0xF2, 0xF6}, /* "26" */ .release_nr = {0xF0, 0xF1}, /* "01" */ .mod_lvl = {0xF0, 0xF0}, /* "00" */ }; struct appldata_parameter_list appldata_parameter_list = { .diag = 0xDC, .function = function, .parlist_length = sizeof(appldata_parameter_list), .buffer_length = length, .product_id_addr = (unsigned long) &appldata_product_id, .buffer_addr = virt_to_phys((void *) buffer) }; if (!MACHINE_IS_VM) return -ENOSYS; ry = -1; asm volatile( "diag %1,%0,0xDC\n\t" : "=d" (ry) : "d" (&appldata_parameter_list), "m" (appldata_parameter_list), "m" (appldata_product_id) : "cc"); return (int) ry;}/************************ timer, work, DIAG <END> ****************************//****************************** /proc stuff **********************************//* * appldata_mod_vtimer_wrap() * * wrapper function for mod_virt_timer(), because smp_call_function_on() * 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_on(add_virt_timer_periodic, &per_cpu(appldata_timer, i), 0, 1, i); } 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_on(__appldata_mod_vtimer_wrap, &args, 0, 1, i); } }}/* * 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; } 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -