18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1. 48c2ecf20Sopenharmony_ci * Exports appldata_register_ops() and appldata_unregister_ops() for the 58c2ecf20Sopenharmony_ci * data gathering modules. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2003, 2009 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "appldata" 138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/sched/stat.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 218c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 228c2ecf20Sopenharmony_ci#include <linux/mm.h> 238c2ecf20Sopenharmony_ci#include <linux/swap.h> 248c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 258c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 268c2ecf20Sopenharmony_ci#include <linux/notifier.h> 278c2ecf20Sopenharmony_ci#include <linux/cpu.h> 288c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 298c2ecf20Sopenharmony_ci#include <linux/suspend.h> 308c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 318c2ecf20Sopenharmony_ci#include <asm/appldata.h> 328c2ecf20Sopenharmony_ci#include <asm/vtimer.h> 338c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 348c2ecf20Sopenharmony_ci#include <asm/io.h> 358c2ecf20Sopenharmony_ci#include <asm/smp.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "appldata.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define APPLDATA_CPU_INTERVAL 10000 /* default (CPU) time for 418c2ecf20Sopenharmony_ci sampling interval in 428c2ecf20Sopenharmony_ci milliseconds */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define TOD_MICRO 0x01000 /* nr. of TOD clock units 458c2ecf20Sopenharmony_ci for 1 microsecond */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct platform_device *appldata_pdev; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* 508c2ecf20Sopenharmony_ci * /proc entries (sysctl) 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistatic const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata"; 538c2ecf20Sopenharmony_cistatic int appldata_timer_handler(struct ctl_table *ctl, int write, 548c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos); 558c2ecf20Sopenharmony_cistatic int appldata_interval_handler(struct ctl_table *ctl, int write, 568c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic struct ctl_table_header *appldata_sysctl_header; 598c2ecf20Sopenharmony_cistatic struct ctl_table appldata_table[] = { 608c2ecf20Sopenharmony_ci { 618c2ecf20Sopenharmony_ci .procname = "timer", 628c2ecf20Sopenharmony_ci .mode = S_IRUGO | S_IWUSR, 638c2ecf20Sopenharmony_ci .proc_handler = appldata_timer_handler, 648c2ecf20Sopenharmony_ci }, 658c2ecf20Sopenharmony_ci { 668c2ecf20Sopenharmony_ci .procname = "interval", 678c2ecf20Sopenharmony_ci .mode = S_IRUGO | S_IWUSR, 688c2ecf20Sopenharmony_ci .proc_handler = appldata_interval_handler, 698c2ecf20Sopenharmony_ci }, 708c2ecf20Sopenharmony_ci { }, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic struct ctl_table appldata_dir_table[] = { 748c2ecf20Sopenharmony_ci { 758c2ecf20Sopenharmony_ci .procname = appldata_proc_name, 768c2ecf20Sopenharmony_ci .maxlen = 0, 778c2ecf20Sopenharmony_ci .mode = S_IRUGO | S_IXUGO, 788c2ecf20Sopenharmony_ci .child = appldata_table, 798c2ecf20Sopenharmony_ci }, 808c2ecf20Sopenharmony_ci { }, 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * Timer 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic struct vtimer_list appldata_timer; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(appldata_timer_lock); 898c2ecf20Sopenharmony_cistatic int appldata_interval = APPLDATA_CPU_INTERVAL; 908c2ecf20Sopenharmony_cistatic int appldata_timer_active; 918c2ecf20Sopenharmony_cistatic int appldata_timer_suspended = 0; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* 948c2ecf20Sopenharmony_ci * Work queue 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_cistatic struct workqueue_struct *appldata_wq; 978c2ecf20Sopenharmony_cistatic void appldata_work_fn(struct work_struct *work); 988c2ecf20Sopenharmony_cistatic DECLARE_WORK(appldata_work, appldata_work_fn); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* 1028c2ecf20Sopenharmony_ci * Ops list 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(appldata_ops_mutex); 1058c2ecf20Sopenharmony_cistatic LIST_HEAD(appldata_ops_list); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/*************************** timer, work, DIAG *******************************/ 1098c2ecf20Sopenharmony_ci/* 1108c2ecf20Sopenharmony_ci * appldata_timer_function() 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci * schedule work and reschedule timer 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic void appldata_timer_function(unsigned long data) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci queue_work(appldata_wq, (struct work_struct *) data); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * appldata_work_fn() 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * call data gathering function for each (active) module 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_cistatic void appldata_work_fn(struct work_struct *work) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct list_head *lh; 1278c2ecf20Sopenharmony_ci struct appldata_ops *ops; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci mutex_lock(&appldata_ops_mutex); 1308c2ecf20Sopenharmony_ci list_for_each(lh, &appldata_ops_list) { 1318c2ecf20Sopenharmony_ci ops = list_entry(lh, struct appldata_ops, list); 1328c2ecf20Sopenharmony_ci if (ops->active == 1) { 1338c2ecf20Sopenharmony_ci ops->callback(ops->data); 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci mutex_unlock(&appldata_ops_mutex); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic struct appldata_product_id appldata_id = { 1408c2ecf20Sopenharmony_ci .prod_nr = {0xD3, 0xC9, 0xD5, 0xE4, 1418c2ecf20Sopenharmony_ci 0xE7, 0xD2, 0xD9}, /* "LINUXKR" */ 1428c2ecf20Sopenharmony_ci .prod_fn = 0xD5D3, /* "NL" */ 1438c2ecf20Sopenharmony_ci .version_nr = 0xF2F6, /* "26" */ 1448c2ecf20Sopenharmony_ci .release_nr = 0xF0F1, /* "01" */ 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* 1488c2ecf20Sopenharmony_ci * appldata_diag() 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * prepare parameter list, issue DIAG 0xDC 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ciint appldata_diag(char record_nr, u16 function, unsigned long buffer, 1538c2ecf20Sopenharmony_ci u16 length, char *mod_lvl) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct appldata_parameter_list *parm_list; 1568c2ecf20Sopenharmony_ci struct appldata_product_id *id; 1578c2ecf20Sopenharmony_ci int rc; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci parm_list = kmalloc(sizeof(*parm_list), GFP_KERNEL); 1608c2ecf20Sopenharmony_ci id = kmemdup(&appldata_id, sizeof(appldata_id), GFP_KERNEL); 1618c2ecf20Sopenharmony_ci rc = -ENOMEM; 1628c2ecf20Sopenharmony_ci if (parm_list && id) { 1638c2ecf20Sopenharmony_ci id->record_nr = record_nr; 1648c2ecf20Sopenharmony_ci id->mod_lvl = (mod_lvl[0]) << 8 | mod_lvl[1]; 1658c2ecf20Sopenharmony_ci rc = appldata_asm(parm_list, id, function, 1668c2ecf20Sopenharmony_ci (void *) buffer, length); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci kfree(id); 1698c2ecf20Sopenharmony_ci kfree(parm_list); 1708c2ecf20Sopenharmony_ci return rc; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci/************************ timer, work, DIAG <END> ****************************/ 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/****************************** /proc stuff **********************************/ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci#define APPLDATA_ADD_TIMER 0 1788c2ecf20Sopenharmony_ci#define APPLDATA_DEL_TIMER 1 1798c2ecf20Sopenharmony_ci#define APPLDATA_MOD_TIMER 2 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * __appldata_vtimer_setup() 1838c2ecf20Sopenharmony_ci * 1848c2ecf20Sopenharmony_ci * Add, delete or modify virtual timers on all online cpus. 1858c2ecf20Sopenharmony_ci * The caller needs to get the appldata_timer_lock spinlock. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_cistatic void __appldata_vtimer_setup(int cmd) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci u64 timer_interval = (u64) appldata_interval * 1000 * TOD_MICRO; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci switch (cmd) { 1928c2ecf20Sopenharmony_ci case APPLDATA_ADD_TIMER: 1938c2ecf20Sopenharmony_ci if (appldata_timer_active) 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci appldata_timer.expires = timer_interval; 1968c2ecf20Sopenharmony_ci add_virt_timer_periodic(&appldata_timer); 1978c2ecf20Sopenharmony_ci appldata_timer_active = 1; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case APPLDATA_DEL_TIMER: 2008c2ecf20Sopenharmony_ci del_virt_timer(&appldata_timer); 2018c2ecf20Sopenharmony_ci if (!appldata_timer_active) 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci appldata_timer_active = 0; 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci case APPLDATA_MOD_TIMER: 2068c2ecf20Sopenharmony_ci if (!appldata_timer_active) 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci mod_virt_timer_periodic(&appldata_timer, timer_interval); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* 2138c2ecf20Sopenharmony_ci * appldata_timer_handler() 2148c2ecf20Sopenharmony_ci * 2158c2ecf20Sopenharmony_ci * Start/Stop timer, show status of timer (0 = not active, 1 = active) 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_cistatic int 2188c2ecf20Sopenharmony_ciappldata_timer_handler(struct ctl_table *ctl, int write, 2198c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci int timer_active = appldata_timer_active; 2228c2ecf20Sopenharmony_ci int rc; 2238c2ecf20Sopenharmony_ci struct ctl_table ctl_entry = { 2248c2ecf20Sopenharmony_ci .procname = ctl->procname, 2258c2ecf20Sopenharmony_ci .data = &timer_active, 2268c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 2278c2ecf20Sopenharmony_ci .extra1 = SYSCTL_ZERO, 2288c2ecf20Sopenharmony_ci .extra2 = SYSCTL_ONE, 2298c2ecf20Sopenharmony_ci }; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos); 2328c2ecf20Sopenharmony_ci if (rc < 0 || !write) 2338c2ecf20Sopenharmony_ci return rc; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci spin_lock(&appldata_timer_lock); 2368c2ecf20Sopenharmony_ci if (timer_active) 2378c2ecf20Sopenharmony_ci __appldata_vtimer_setup(APPLDATA_ADD_TIMER); 2388c2ecf20Sopenharmony_ci else 2398c2ecf20Sopenharmony_ci __appldata_vtimer_setup(APPLDATA_DEL_TIMER); 2408c2ecf20Sopenharmony_ci spin_unlock(&appldata_timer_lock); 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* 2458c2ecf20Sopenharmony_ci * appldata_interval_handler() 2468c2ecf20Sopenharmony_ci * 2478c2ecf20Sopenharmony_ci * Set (CPU) timer interval for collection of data (in milliseconds), show 2488c2ecf20Sopenharmony_ci * current timer interval. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic int 2518c2ecf20Sopenharmony_ciappldata_interval_handler(struct ctl_table *ctl, int write, 2528c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci int interval = appldata_interval; 2558c2ecf20Sopenharmony_ci int rc; 2568c2ecf20Sopenharmony_ci struct ctl_table ctl_entry = { 2578c2ecf20Sopenharmony_ci .procname = ctl->procname, 2588c2ecf20Sopenharmony_ci .data = &interval, 2598c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 2608c2ecf20Sopenharmony_ci .extra1 = SYSCTL_ONE, 2618c2ecf20Sopenharmony_ci }; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci rc = proc_dointvec_minmax(&ctl_entry, write, buffer, lenp, ppos); 2648c2ecf20Sopenharmony_ci if (rc < 0 || !write) 2658c2ecf20Sopenharmony_ci return rc; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci spin_lock(&appldata_timer_lock); 2688c2ecf20Sopenharmony_ci appldata_interval = interval; 2698c2ecf20Sopenharmony_ci __appldata_vtimer_setup(APPLDATA_MOD_TIMER); 2708c2ecf20Sopenharmony_ci spin_unlock(&appldata_timer_lock); 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/* 2758c2ecf20Sopenharmony_ci * appldata_generic_handler() 2768c2ecf20Sopenharmony_ci * 2778c2ecf20Sopenharmony_ci * Generic start/stop monitoring and DIAG, show status of 2788c2ecf20Sopenharmony_ci * monitoring (0 = not in process, 1 = in process) 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_cistatic int 2818c2ecf20Sopenharmony_ciappldata_generic_handler(struct ctl_table *ctl, int write, 2828c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct appldata_ops *ops = NULL, *tmp_ops; 2858c2ecf20Sopenharmony_ci struct list_head *lh; 2868c2ecf20Sopenharmony_ci int rc, found; 2878c2ecf20Sopenharmony_ci int active; 2888c2ecf20Sopenharmony_ci struct ctl_table ctl_entry = { 2898c2ecf20Sopenharmony_ci .data = &active, 2908c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 2918c2ecf20Sopenharmony_ci .extra1 = SYSCTL_ZERO, 2928c2ecf20Sopenharmony_ci .extra2 = SYSCTL_ONE, 2938c2ecf20Sopenharmony_ci }; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci found = 0; 2968c2ecf20Sopenharmony_ci mutex_lock(&appldata_ops_mutex); 2978c2ecf20Sopenharmony_ci list_for_each(lh, &appldata_ops_list) { 2988c2ecf20Sopenharmony_ci tmp_ops = list_entry(lh, struct appldata_ops, list); 2998c2ecf20Sopenharmony_ci if (&tmp_ops->ctl_table[2] == ctl) { 3008c2ecf20Sopenharmony_ci found = 1; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci if (!found) { 3048c2ecf20Sopenharmony_ci mutex_unlock(&appldata_ops_mutex); 3058c2ecf20Sopenharmony_ci return -ENODEV; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci ops = ctl->data; 3088c2ecf20Sopenharmony_ci if (!try_module_get(ops->owner)) { // protect this function 3098c2ecf20Sopenharmony_ci mutex_unlock(&appldata_ops_mutex); 3108c2ecf20Sopenharmony_ci return -ENODEV; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci mutex_unlock(&appldata_ops_mutex); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci active = ops->active; 3158c2ecf20Sopenharmony_ci rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos); 3168c2ecf20Sopenharmony_ci if (rc < 0 || !write) { 3178c2ecf20Sopenharmony_ci module_put(ops->owner); 3188c2ecf20Sopenharmony_ci return rc; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci mutex_lock(&appldata_ops_mutex); 3228c2ecf20Sopenharmony_ci if (active && (ops->active == 0)) { 3238c2ecf20Sopenharmony_ci // protect work queue callback 3248c2ecf20Sopenharmony_ci if (!try_module_get(ops->owner)) { 3258c2ecf20Sopenharmony_ci mutex_unlock(&appldata_ops_mutex); 3268c2ecf20Sopenharmony_ci module_put(ops->owner); 3278c2ecf20Sopenharmony_ci return -ENODEV; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci ops->callback(ops->data); // init record 3308c2ecf20Sopenharmony_ci rc = appldata_diag(ops->record_nr, 3318c2ecf20Sopenharmony_ci APPLDATA_START_INTERVAL_REC, 3328c2ecf20Sopenharmony_ci (unsigned long) ops->data, ops->size, 3338c2ecf20Sopenharmony_ci ops->mod_lvl); 3348c2ecf20Sopenharmony_ci if (rc != 0) { 3358c2ecf20Sopenharmony_ci pr_err("Starting the data collection for %s " 3368c2ecf20Sopenharmony_ci "failed with rc=%d\n", ops->name, rc); 3378c2ecf20Sopenharmony_ci module_put(ops->owner); 3388c2ecf20Sopenharmony_ci } else 3398c2ecf20Sopenharmony_ci ops->active = 1; 3408c2ecf20Sopenharmony_ci } else if (!active && (ops->active == 1)) { 3418c2ecf20Sopenharmony_ci ops->active = 0; 3428c2ecf20Sopenharmony_ci rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, 3438c2ecf20Sopenharmony_ci (unsigned long) ops->data, ops->size, 3448c2ecf20Sopenharmony_ci ops->mod_lvl); 3458c2ecf20Sopenharmony_ci if (rc != 0) 3468c2ecf20Sopenharmony_ci pr_err("Stopping the data collection for %s " 3478c2ecf20Sopenharmony_ci "failed with rc=%d\n", ops->name, rc); 3488c2ecf20Sopenharmony_ci module_put(ops->owner); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci mutex_unlock(&appldata_ops_mutex); 3518c2ecf20Sopenharmony_ci module_put(ops->owner); 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/*************************** /proc stuff <END> *******************************/ 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/************************* module-ops management *****************************/ 3598c2ecf20Sopenharmony_ci/* 3608c2ecf20Sopenharmony_ci * appldata_register_ops() 3618c2ecf20Sopenharmony_ci * 3628c2ecf20Sopenharmony_ci * update ops list, register /proc/sys entries 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_ciint appldata_register_ops(struct appldata_ops *ops) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci if (ops->size > APPLDATA_MAX_REC_SIZE) 3678c2ecf20Sopenharmony_ci return -EINVAL; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ops->ctl_table = kcalloc(4, sizeof(struct ctl_table), GFP_KERNEL); 3708c2ecf20Sopenharmony_ci if (!ops->ctl_table) 3718c2ecf20Sopenharmony_ci return -ENOMEM; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci mutex_lock(&appldata_ops_mutex); 3748c2ecf20Sopenharmony_ci list_add(&ops->list, &appldata_ops_list); 3758c2ecf20Sopenharmony_ci mutex_unlock(&appldata_ops_mutex); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci ops->ctl_table[0].procname = appldata_proc_name; 3788c2ecf20Sopenharmony_ci ops->ctl_table[0].maxlen = 0; 3798c2ecf20Sopenharmony_ci ops->ctl_table[0].mode = S_IRUGO | S_IXUGO; 3808c2ecf20Sopenharmony_ci ops->ctl_table[0].child = &ops->ctl_table[2]; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ops->ctl_table[2].procname = ops->name; 3838c2ecf20Sopenharmony_ci ops->ctl_table[2].mode = S_IRUGO | S_IWUSR; 3848c2ecf20Sopenharmony_ci ops->ctl_table[2].proc_handler = appldata_generic_handler; 3858c2ecf20Sopenharmony_ci ops->ctl_table[2].data = ops; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci ops->sysctl_header = register_sysctl_table(ops->ctl_table); 3888c2ecf20Sopenharmony_ci if (!ops->sysctl_header) 3898c2ecf20Sopenharmony_ci goto out; 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ciout: 3928c2ecf20Sopenharmony_ci mutex_lock(&appldata_ops_mutex); 3938c2ecf20Sopenharmony_ci list_del(&ops->list); 3948c2ecf20Sopenharmony_ci mutex_unlock(&appldata_ops_mutex); 3958c2ecf20Sopenharmony_ci kfree(ops->ctl_table); 3968c2ecf20Sopenharmony_ci return -ENOMEM; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/* 4008c2ecf20Sopenharmony_ci * appldata_unregister_ops() 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci * update ops list, unregister /proc entries, stop DIAG if necessary 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_civoid appldata_unregister_ops(struct appldata_ops *ops) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci mutex_lock(&appldata_ops_mutex); 4078c2ecf20Sopenharmony_ci list_del(&ops->list); 4088c2ecf20Sopenharmony_ci mutex_unlock(&appldata_ops_mutex); 4098c2ecf20Sopenharmony_ci unregister_sysctl_table(ops->sysctl_header); 4108c2ecf20Sopenharmony_ci kfree(ops->ctl_table); 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci/********************** module-ops management <END> **************************/ 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci/**************************** suspend / resume *******************************/ 4168c2ecf20Sopenharmony_cistatic int appldata_freeze(struct device *dev) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct appldata_ops *ops; 4198c2ecf20Sopenharmony_ci int rc; 4208c2ecf20Sopenharmony_ci struct list_head *lh; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci spin_lock(&appldata_timer_lock); 4238c2ecf20Sopenharmony_ci if (appldata_timer_active) { 4248c2ecf20Sopenharmony_ci __appldata_vtimer_setup(APPLDATA_DEL_TIMER); 4258c2ecf20Sopenharmony_ci appldata_timer_suspended = 1; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci spin_unlock(&appldata_timer_lock); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci mutex_lock(&appldata_ops_mutex); 4308c2ecf20Sopenharmony_ci list_for_each(lh, &appldata_ops_list) { 4318c2ecf20Sopenharmony_ci ops = list_entry(lh, struct appldata_ops, list); 4328c2ecf20Sopenharmony_ci if (ops->active == 1) { 4338c2ecf20Sopenharmony_ci rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, 4348c2ecf20Sopenharmony_ci (unsigned long) ops->data, ops->size, 4358c2ecf20Sopenharmony_ci ops->mod_lvl); 4368c2ecf20Sopenharmony_ci if (rc != 0) 4378c2ecf20Sopenharmony_ci pr_err("Stopping the data collection for %s " 4388c2ecf20Sopenharmony_ci "failed with rc=%d\n", ops->name, rc); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci mutex_unlock(&appldata_ops_mutex); 4428c2ecf20Sopenharmony_ci return 0; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int appldata_restore(struct device *dev) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct appldata_ops *ops; 4488c2ecf20Sopenharmony_ci int rc; 4498c2ecf20Sopenharmony_ci struct list_head *lh; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci spin_lock(&appldata_timer_lock); 4528c2ecf20Sopenharmony_ci if (appldata_timer_suspended) { 4538c2ecf20Sopenharmony_ci __appldata_vtimer_setup(APPLDATA_ADD_TIMER); 4548c2ecf20Sopenharmony_ci appldata_timer_suspended = 0; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci spin_unlock(&appldata_timer_lock); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci mutex_lock(&appldata_ops_mutex); 4598c2ecf20Sopenharmony_ci list_for_each(lh, &appldata_ops_list) { 4608c2ecf20Sopenharmony_ci ops = list_entry(lh, struct appldata_ops, list); 4618c2ecf20Sopenharmony_ci if (ops->active == 1) { 4628c2ecf20Sopenharmony_ci ops->callback(ops->data); // init record 4638c2ecf20Sopenharmony_ci rc = appldata_diag(ops->record_nr, 4648c2ecf20Sopenharmony_ci APPLDATA_START_INTERVAL_REC, 4658c2ecf20Sopenharmony_ci (unsigned long) ops->data, ops->size, 4668c2ecf20Sopenharmony_ci ops->mod_lvl); 4678c2ecf20Sopenharmony_ci if (rc != 0) { 4688c2ecf20Sopenharmony_ci pr_err("Starting the data collection for %s " 4698c2ecf20Sopenharmony_ci "failed with rc=%d\n", ops->name, rc); 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci mutex_unlock(&appldata_ops_mutex); 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int appldata_thaw(struct device *dev) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci return appldata_restore(dev); 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic const struct dev_pm_ops appldata_pm_ops = { 4838c2ecf20Sopenharmony_ci .freeze = appldata_freeze, 4848c2ecf20Sopenharmony_ci .thaw = appldata_thaw, 4858c2ecf20Sopenharmony_ci .restore = appldata_restore, 4868c2ecf20Sopenharmony_ci}; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic struct platform_driver appldata_pdrv = { 4898c2ecf20Sopenharmony_ci .driver = { 4908c2ecf20Sopenharmony_ci .name = "appldata", 4918c2ecf20Sopenharmony_ci .pm = &appldata_pm_ops, 4928c2ecf20Sopenharmony_ci }, 4938c2ecf20Sopenharmony_ci}; 4948c2ecf20Sopenharmony_ci/************************* suspend / resume <END> ****************************/ 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci/******************************* init / exit *********************************/ 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci/* 5008c2ecf20Sopenharmony_ci * appldata_init() 5018c2ecf20Sopenharmony_ci * 5028c2ecf20Sopenharmony_ci * init timer, register /proc entries 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_cistatic int __init appldata_init(void) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci int rc; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci init_virt_timer(&appldata_timer); 5098c2ecf20Sopenharmony_ci appldata_timer.function = appldata_timer_function; 5108c2ecf20Sopenharmony_ci appldata_timer.data = (unsigned long) &appldata_work; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci rc = platform_driver_register(&appldata_pdrv); 5138c2ecf20Sopenharmony_ci if (rc) 5148c2ecf20Sopenharmony_ci return rc; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci appldata_pdev = platform_device_register_simple("appldata", -1, NULL, 5178c2ecf20Sopenharmony_ci 0); 5188c2ecf20Sopenharmony_ci if (IS_ERR(appldata_pdev)) { 5198c2ecf20Sopenharmony_ci rc = PTR_ERR(appldata_pdev); 5208c2ecf20Sopenharmony_ci goto out_driver; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci appldata_wq = alloc_ordered_workqueue("appldata", 0); 5238c2ecf20Sopenharmony_ci if (!appldata_wq) { 5248c2ecf20Sopenharmony_ci rc = -ENOMEM; 5258c2ecf20Sopenharmony_ci goto out_device; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci appldata_sysctl_header = register_sysctl_table(appldata_dir_table); 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ciout_device: 5328c2ecf20Sopenharmony_ci platform_device_unregister(appldata_pdev); 5338c2ecf20Sopenharmony_ciout_driver: 5348c2ecf20Sopenharmony_ci platform_driver_unregister(&appldata_pdrv); 5358c2ecf20Sopenharmony_ci return rc; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci__initcall(appldata_init); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/**************************** init / exit <END> ******************************/ 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(appldata_register_ops); 5438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(appldata_unregister_ops); 5448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(appldata_diag); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci#ifdef CONFIG_SWAP 5478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si_swapinfo); 5488c2ecf20Sopenharmony_ci#endif 5498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nr_threads); 5508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nr_running); 5518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nr_iowait); 552