kernel-brax3-ubuntu-touch/drivers/resmon/resmon_sys.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

952 lines
No EOL
26 KiB
C
Executable file

/* Copyright Prize
* Author: Eileen <lifenfen@szprize.com>
* process monitor in kernel.
*/
#include <linux/compat.h>
#include <linux/eventfd.h>
#include <linux/vhost.h>
#include <linux/virtio_net.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
/*lijimeng*/
#include <linux/sched/cputime.h>
#include <linux/tick.h>
#include <linux/cpufreq.h>
#include <linux/swap.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/kernel_stat.h>
#include <linux/time.h>
#include <linux/task_io_accounting_ops.h>
#include <linux/fb.h>
#include <asm/div64.h>
#include <linux/cpumask.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/irqnr.h>
/*lijimeng*/
#include <linux/sched/cputime.h>
#include <linux/tick.h>
#include <linux/cpufreq.h>
#include <linux/swap.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/pid_namespace.h>
#include <linux/ptrace.h>
#include <linux/task_io_accounting_ops.h>
#include <linux/oom.h>
#include <linux/security.h>
#include "resmon.h"
#define P2M(x) ((((unsigned long)x) << (PAGE_SHIFT - 10)) >> (10))
#define RESMON_FILTER "app"
#define SYSTEM_FILTER "system_server"
struct resmon_struct *resmon_obj;
static struct proc_dir_entry *resmon_procfs;
#ifdef arch_idle_time
static u64 get_pri_idle_time(struct kernel_cpustat *kcs, int cpu)
{
u64 idle;
idle = kcs->cpustat[CPUTIME_IDLE];
if (cpu_online(cpu) && !nr_iowait_cpu(cpu))
idle += arch_idle_time(cpu);
return idle;
}
static u64 get_iowait_time(struct kernel_cpustat *kcs, int cpu)
{
u64 iowait;
iowait = kcs->cpustat[CPUTIME_IOWAIT];
if (cpu_online(cpu) && nr_iowait_cpu(cpu))
iowait += arch_idle_time(cpu);
return iowait;
}
#else
static u64 get_pri_idle_time(struct kernel_cpustat *kcs, int cpu)
{
u64 idle, idle_usecs = -1ULL;
//if (cpu_online(cpu))
idle_usecs = get_cpu_idle_time_us(cpu, NULL);
if (idle_usecs == -1ULL)
/* !NO_HZ or cpu offline so we can rely on cpustat.idle */
idle = kcs->cpustat[CPUTIME_IDLE];
else
idle = idle_usecs * NSEC_PER_USEC;
return idle;
}
static u64 get_iowait_time(struct kernel_cpustat *kcs, int cpu)
{
u64 iowait, iowait_usecs = -1ULL;
if (cpu_online(cpu))
iowait_usecs = get_cpu_iowait_time_us(cpu, NULL);
if (iowait_usecs == -1ULL)
/* !NO_HZ or cpu offline so we can rely on cpustat.iowait */
iowait = kcs->cpustat[CPUTIME_IOWAIT];
else
iowait = iowait_usecs * NSEC_PER_USEC;
return iowait;
}
#endif
struct sighand_struct *__pri_lock_task_sighand(struct task_struct *tsk,
unsigned long *flags)
{
struct sighand_struct *sighand;
rcu_read_lock();
for (;;) {
sighand = rcu_dereference(tsk->sighand);
if (unlikely(sighand == NULL))
break;
/*
* This sighand can be already freed and even reused, but
* we rely on SLAB_TYPESAFE_BY_RCU and sighand_ctor() which
* initializes ->siglock: this slab can't go away, it has
* the same object type, ->siglock can't be reinitialized.
*
* We need to ensure that tsk->sighand is still the same
* after we take the lock, we can race with de_thread() or
* __exit_signal(). In the latter case the next iteration
* must see ->sighand == NULL.
*/
spin_lock_irqsave(&sighand->siglock, *flags);
if (likely(sighand == rcu_access_pointer(tsk->sighand)))
break;
spin_unlock_irqrestore(&sighand->siglock, *flags);
}
rcu_read_unlock();
return sighand;
}
struct sighand_struct *pri_lock_task_sighand(struct task_struct *tsk,
unsigned long *flags)
{
struct sighand_struct *ret;
ret = __pri_lock_task_sighand(tsk, flags);
(void)__cond_lock(&tsk->sighand->siglock, ret);
return ret;
}
static int show_resmon_stat(struct seq_file *m, void *v)
{
#ifdef CONFIG_RESMON_DEBUG
int i = 0;
#endif
struct task_struct *task;
struct task_struct *t;
//int result = 0;
unsigned long long read_bytes, write_bytes = 0;
u64 utime, stime = 0;
unsigned long mm_size = 0;
int oom_adj = OOM_ADJUST_MIN;
struct task_io_accounting acct;
unsigned long flags;
//char value[64] = {0};
//char *secctx = value;
//char *secctx = NULL;
//bool from_system = false;
rcu_read_lock();
for_each_process(task) {
if (task->flags & PF_KTHREAD)
continue;
if (pri_lock_task_sighand(task, &flags)) {
oom_adj = task->signal->oom_score_adj;
if (oom_adj < 0) {
unlock_task_sighand(task, &flags);
continue;
}
// if (in_egroup_p(KGIDT_INIT(6666)))
// {
// from_system = true;
// }
// if (!from_system)
// {
// //kfree(secctx);
// unlock_task_sighand(task, &flags);
// continue;
// }
//from_system = false;
//kfree(secctx);
thread_group_cputime_adjusted(task, &utime, &stime);
if (task->mm)
mm_size = P2M(get_mm_rss(task->mm));
acct = task->ioac;
task_io_accounting_add(&acct, &task->signal->ioac);
t = task;
while_each_thread(task, t)
task_io_accounting_add(&acct, &t->ioac);
#ifdef CONFIG_TASK_IO_ACCOUNTING
read_bytes = acct.read_bytes;
write_bytes = acct.write_bytes;
#endif
//if (task->signal->oom_score_adj == OOM_SCORE_ADJ_MAX)
//oom_adj = OOM_ADJUST_MAX;
//else /*modify for oom_score_adj->oom_adj round*/
//oom_adj = ((task->signal->oom_score_adj * -OOM_DISABLE * 10)/OOM_SCORE_ADJ_MAX+5)/10;
seq_printf(m, "%d ", task->pid);
seq_printf(m, "%llu " , (long long unsigned int)nsec_to_clock_t(utime));
//seq_put_decimal_ull(m, " ", (long long unsigned int)nsec_to_clock_t(utime));
seq_printf(m, "%llu ", (long long unsigned int)nsec_to_clock_t(stime));
//seq_put_decimal_ull(m, " ", (long long unsigned int)nsec_to_clock_t(stime));
//seq_put_decimal_ull(m, " ", (long long unsigned int)mm_size);
seq_printf(m, "%llu ", (long long unsigned int)mm_size);
//seq_put_decimal_ull(m, " ", read_bytes);
seq_printf(m, "%llu ", read_bytes);
//seq_put_decimal_ull(m, " ", write_bytes);
seq_printf(m, "%llu ", write_bytes);
seq_printf(m, " %d\n", oom_adj);
#ifdef CONFIG_RESMON_DEBUG
pos += sprintf(test + pos, "%d ", task->pid);
pos += sprintf(test + pos, "%llu ", (long long unsigned int)nsec_to_clock_t(utime) );
pos += sprintf(test + pos, "%llu ", (long long unsigned int)nsec_to_clock_t(stime) );
pos += sprintf(test + pos, "%llu ", (long long unsigned int)mm_size);
pos += sprintf(test + pos, "%llu ", read_bytes);
pos += sprintf(test + pos, "%llu ", write_bytes);
pos += sprintf(test + pos, "%d\n", oom_adj );
#endif
#ifdef CONFIG_RESMON_KILL_DEBUG
test_set_pid(count ++, task->pid);
#endif
unlock_task_sighand(task, &flags);
}
}
rcu_read_unlock();
#ifdef CONFIG_RESMON_DEBUG
printk("+ %s \n", __func__);
for (i = 0; i < pos; i++)
printk("%c", test[i]);
printk("- %s \n", __func__);
pos = 0;
#endif
#ifdef CONFIG_RESMON_KILL_DEBUG
count = 0;
#endif
return 0;
}
static void cpustat_info(unsigned long long *info, int len)
{
int i;
if (len >= NR_STATS)
{
for_each_possible_cpu(i) {
struct kernel_cpustat kcpustat;
u64 *cpustat = kcpustat.cpustat;
kcpustat_cpu_fetch(&kcpustat, i);
info[CPUTIME_USER] += cpustat[CPUTIME_USER];
info[CPUTIME_NICE] += cpustat[CPUTIME_NICE];
info[CPUTIME_SYSTEM] += cpustat[CPUTIME_SYSTEM];
info[CPUTIME_IDLE] += get_pri_idle_time(&kcpustat, i);
info[CPUTIME_IOWAIT] += get_iowait_time(&kcpustat, i);
info[CPUTIME_IRQ] += cpustat[CPUTIME_IRQ];
info[CPUTIME_SOFTIRQ] += cpustat[CPUTIME_SOFTIRQ];
info[CPUTIME_STEAL] += cpustat[CPUTIME_STEAL];
info[CPUTIME_GUEST] += cpustat[CPUTIME_GUEST];
info[CPUTIME_GUEST_NICE] += cpustat[CPUTIME_GUEST_NICE];
}
}
}
MODULE_IMPORT_NS(MINIDUMP);
static long meminfo_available(void)
{
struct sysinfo i;
long cached;
long available;
si_meminfo(&i);
si_swapinfo(&i);
cached = global_node_page_state(NR_FILE_PAGES) -
total_swapcache_pages() - i.bufferram;
if (cached < 0)
cached = 0;
available = i.freeram + cached;
return P2M(available);
}
static int send_event(enum resmon_trigger trigger, int value)
{
char name_buf[32];
char state_buf[32];
char *envp[3];
int env_offset = 0;
int ret = 0;
snprintf(name_buf, sizeof(name_buf),"TYPE=%d", trigger);
envp[env_offset++] = name_buf;
snprintf(state_buf, sizeof(state_buf),"LOAD=%d", value);
envp[env_offset++] = state_buf;
envp[env_offset] = NULL;
//resmon_error("resmon send_event - sending event - %s in %s\n", *envp, kobject_get_path(&resmon_obj->mdev.this_device->kobj, GFP_KERNEL));
ret = kobject_uevent_env(&resmon_obj->mdev.this_device->kobj, KOBJ_CHANGE, envp);
if (ret < 0)
resmon_error("uevent sending failed with ret = %d\n", ret);
return ret;
}
#ifdef CONFIG_RESMON_KILL_DEBUG
int test_pid[1024] = {0};
static int test_count = 0;
void test_set_pid(int id, int pid)
{
test_pid[id] = pid;
}
#endif
static void resmon_process(void)
{
unsigned long long cpustat[NR_STATS] = {0};
unsigned long long cpuloads, cpuidle, iowait = 0;
long long cpu_delta, idle_delta, io_delta= 0;
long long cur_loads, cur_iowait, cur_mem = 0; /* prizem modify compile error */
mutex_lock(&resmon_obj->lock);
cpustat_info(cpustat, NR_STATS);
cpuloads = cpustat[CPUTIME_USER] + cpustat[CPUTIME_NICE]
+ cpustat[CPUTIME_SYSTEM] + cpustat[CPUTIME_SOFTIRQ]
+ cpustat[CPUTIME_IRQ] + cpustat[CPUTIME_IDLE]
+ cpustat[CPUTIME_IOWAIT];
cpuidle = cpustat[CPUTIME_IDLE];
iowait = cpustat[CPUTIME_IOWAIT];
if (resmon_obj->cpuloads != 0)
{
cpu_delta = cpuloads - resmon_obj->cpuloads;
idle_delta = cpuidle - resmon_obj->cpuidle;
io_delta = iowait - resmon_obj->iowait;
cur_mem = meminfo_available();
resmon_debug("%s cpu_delta:%lld idle_delta:%lld io_delta:%lld\n", __func__, cpu_delta, idle_delta, io_delta);
#if BITS_PER_LONG == 32
cur_loads = (cpu_delta - idle_delta) * 100;
do_div(cur_loads, cpu_delta);
cur_iowait = io_delta * 100;
do_div(cur_iowait, cpu_delta);
#else
cur_loads = 100 * (cpu_delta - idle_delta) / (cpu_delta);
cur_iowait = 100 * (io_delta) / (cpu_delta);
#endif
resmon_error("resmon %s cur_loads:%d cur_iowait:%d cur_mem:%d\n", __func__, cur_loads, cur_iowait, cur_mem);
if (cur_loads > resmon_obj->cpuload_thresh)
send_event(CPU_TRIGGER, cur_loads);
if (cur_iowait > resmon_obj->io_thresh)
send_event(IOW_TRIGGER, cur_iowait);
if (cur_mem < resmon_obj->mem_thresh)
send_event(MEM_TRIGGER, cur_mem);
}
resmon_obj->cpuloads = cpuloads;
resmon_obj->cpuidle = cpuidle;
resmon_obj->iowait = iowait;
mutex_unlock(&resmon_obj->lock);
#ifdef CONFIG_RESMON_KILL_DEBUG
while(test_pid[test_count] && (test_count < 5))
{
resmon_error("kil pid = %d\n", test_pid[test_count]);
kill_pid(find_vpid(test_pid[test_count]), SIGKILL, 1);
test_count ++;
}
test_count = 0;
#endif
}
static int _resmon_task_main(void *data)
{
resmon_warn("enter _resmon_task_main\n");
while (1) {
resmon_process();
#if RESMON_PERIODICAL_BY_HR_TIMER
hrtimer_cancel(&resmon_obj->hr_timer);
ktime = ktime_set(resmon_obj->trigger_time / 1000, (resmon_obj->trigger_time % 1000) * 1000000);
hrtimer_start(&resmon_obj->hr_timer, ktime, HRTIMER_MODE_REL);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
#else
mod_timer(&resmon_obj->tmr_list,
(jiffies + msecs_to_jiffies(resmon_obj->trigger_time)));
set_current_state(TASK_INTERRUPTIBLE);
schedule();
#endif
if (kthread_should_stop())
break;
}
resmon_warn("leave _resmon_task_main\n");
return 0;
}
static void _resmon_timer_callback(struct timer_list *t)
{
/* resmon_warn("_resmon_timer_callback\n"); */
if (resmon_obj->tsk_struct_ptr)
wake_up_process(resmon_obj->tsk_struct_ptr);
//return HRTIMER_NORESTART;
}
static int resmon_task_start(void)
{
resmon_warn("resmon resmon_task_start");
if (!atomic_read(&resmon_obj->state))
{
atomic_set(&resmon_obj->state, 1);
//struct sched_param param = {.sched_priority = RESMON_TASK_PRIORITY };
if (resmon_obj->tsk_struct_ptr == NULL) {
//resmon_obj->cpuloads = 1;
resmon_obj->tsk_struct_ptr = kthread_create(_resmon_task_main, NULL, "resmon_main");
if (IS_ERR(resmon_obj->tsk_struct_ptr))
return PTR_ERR(resmon_obj->tsk_struct_ptr);
//sched_setscheduler_nocheck(resmon_obj->tsk_struct_ptr, SCHED_FIFO, &param);
get_task_struct(resmon_obj->tsk_struct_ptr);
wake_up_process(resmon_obj->tsk_struct_ptr);
resmon_warn("resmon task start success, ptr: %p, pid: %d\n",
resmon_obj->tsk_struct_ptr, resmon_obj->tsk_struct_ptr->pid);
} else {
resmon_warn("resmon task already exist, ptr: %p, pid: %d\n",
resmon_obj->tsk_struct_ptr, resmon_obj->tsk_struct_ptr->pid);
}
}
return 0;
}
static int resmon_task_stop(void)
{
int r = 0;
resmon_warn("resmon_task_stop\n");
if (atomic_read(&resmon_obj->state))
{
atomic_set(&resmon_obj->state, 0);
#if RESMON_PERIODICAL_BY_HR_TIMER
/*deinit timer */
r = hrtimer_cancel(&resmon_obj->hr_timer);
if (r)
resmon_error("resmon hr timer delete error!\n");
#else
/*deinit timer */
del_timer_sync(&resmon_obj->tmr_list);
#endif
if (resmon_obj->tsk_struct_ptr) {
kthread_stop(resmon_obj->tsk_struct_ptr);
put_task_struct(resmon_obj->tsk_struct_ptr);
resmon_obj->tsk_struct_ptr = NULL;
resmon_obj->cpuloads = 0;
resmon_obj->cpuidle = 0;
resmon_obj->iowait = 0;
}
}
return r;
}
static ssize_t show_system_stat(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int i, pos = 0;
unsigned long long cpustat[NR_STATS] = {0};
cpustat_info(cpustat, NR_STATS);
for(i = CPUTIME_USER; i < NR_STATS; i++)
pos += snprintf(buf + pos, 64, " %llu", nsec_to_clock_t(cpustat[i]));
for_each_possible_cpu(i)
{
pos += snprintf(buf + pos, 64, " %u", cpufreq_quick_get_max(i));
}
pos += snprintf(buf + pos, 64, " %lu\n", meminfo_available());
return pos;
}
static DEVICE_ATTR(system_stat, S_IRUGO, show_system_stat, NULL);
static ssize_t show_system_io_mem_stat(struct device *dev,
struct device_attribute *attr,
char *buf)
{
#ifdef CONFIG_RESMON_DEBUG
int i = 0;
#endif
int pos = 0;
struct task_struct *task;
struct task_struct *t;
//int result = 0;
unsigned long long read_bytes, write_bytes = 0;
u64 utime, stime = 0;
unsigned long mm_size = 0;
int oom_adj = OOM_ADJUST_MIN;
struct task_io_accounting acct;
unsigned long flags;
//char value[64] = {0};
//char *secctx = value;
//char *secctx = NULL;
//bool from_system = false;
rcu_read_lock();
for_each_process(task) {
if (task->flags & PF_KTHREAD)
continue;
if (pri_lock_task_sighand(task, &flags)) {
// result = security_getprocattr(task, NULL, "current", &secctx);
// if (result <= 0)
// {
// kfree(secctx);
// unlock_task_sighand(task, &flags);
// continue;
// }
oom_adj = task->signal->oom_score_adj;
if (oom_adj < 0) {
unlock_task_sighand(task, &flags);
continue;
}
// if (in_egroup_p(KGIDT_INIT(6666)))
// {
// from_system = true;
// }
// if (!from_system)
// {
// //kfree(secctx);
// unlock_task_sighand(task, &flags);
// continue;
// }
// from_system = false;
//kfree(secctx);
thread_group_cputime_adjusted(task, &utime, &stime);
if (task->mm)
mm_size = P2M(get_mm_rss(task->mm));
acct = task->ioac;
task_io_accounting_add(&acct, &task->signal->ioac);
t = task;
while_each_thread(task, t)
task_io_accounting_add(&acct, &t->ioac);
#ifdef CONFIG_TASK_IO_ACCOUNTING
read_bytes = acct.read_bytes;
write_bytes = acct.write_bytes;
#endif
//if (task->signal->oom_score_adj == OOM_SCORE_ADJ_MAX)
//oom_adj = OOM_ADJUST_MAX;
//else /*modify for oom_score_adj->oom_adj round*/
//oom_adj = ((task->signal->oom_score_adj * -OOM_DISABLE * 10)/OOM_SCORE_ADJ_MAX+5)/10;
oom_adj = task->signal->oom_score_adj;
//resmon_error("resmon pid:%d, utime:%llu, stime:%llu,mm:%llu, read:%llu,write:%llu,oom_adj:%d",task->pid,nsec_to_clock_t(utime),nsec_to_clock_t(stime),mm_size,read_bytes,write_bytes,oom_adj);
pos += sprintf(buf + pos, "%d ", task->pid);
pos += sprintf(buf + pos, "%llu ", (long long unsigned int)nsec_to_clock_t(utime) );
pos += sprintf(buf + pos, "%llu ", (long long unsigned int)nsec_to_clock_t(stime) );
pos += sprintf(buf + pos, "%llu ", (long long unsigned int)mm_size);
pos += sprintf(buf + pos, "%llu ", read_bytes);
pos += sprintf(buf + pos, "%llu ", write_bytes);
pos += sprintf(buf + pos, "%d\n", oom_adj );
#ifdef CONFIG_RESMON_KILL_DEBUG
test_set_pid(count ++, task->pid);
#endif
unlock_task_sighand(task, &flags);
}
}
rcu_read_unlock();
#ifdef CONFIG_RESMON_KILL_DEBUG
count = 0;
#endif
return pos;
}
static DEVICE_ATTR(system_io_mem_stat, S_IRUGO, show_system_io_mem_stat, NULL);
static ssize_t
resmon_sysfs_cpuload_thresh_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, 8, "%d\n", resmon_obj->cpuload_thresh);
}
static ssize_t
resmon_sysfs_cpuload_thresh_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
long int val = 0;
/* We only accept numbers. */
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
resmon_obj->cpuload_thresh = val;
return count;
}
static DEVICE_ATTR(cpuload_thresh, S_IRUGO | S_IWUSR, resmon_sysfs_cpuload_thresh_show, resmon_sysfs_cpuload_thresh_store);
static ssize_t
resmon_sysfs_io_thresh_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, 8, "%d\n", resmon_obj->io_thresh);
}
static ssize_t
resmon_sysfs_io_thresh_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
long int val = 0;
/* We only accept numbers. */
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
resmon_obj->io_thresh = val;
return count;
}
static DEVICE_ATTR(io_thresh, S_IRUGO | S_IWUSR, resmon_sysfs_io_thresh_show, resmon_sysfs_io_thresh_store);
static ssize_t
resmon_sysfs_mem_thresh_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, 8, "%d\n", resmon_obj->mem_thresh);
}
static ssize_t
resmon_sysfs_mem_thresh_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
long int val = 0;
/* We only accept numbers. */
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
resmon_obj->mem_thresh = val;
return count;
}
static DEVICE_ATTR(mem_thresh, S_IRUGO | S_IWUSR, resmon_sysfs_mem_thresh_show, resmon_sysfs_mem_thresh_store);
static ssize_t
resmon_sysfs_trigger_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, 8, "%d\n", resmon_obj->trigger_time);
}
static ssize_t
resmon_sysfs_trigger_time_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
long int val = 0;
/* We only accept numbers. */
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
resmon_obj->trigger_time = val;
return count;
}
static DEVICE_ATTR(trigger_time, S_IRUGO | S_IWUSR, resmon_sysfs_trigger_time_show, resmon_sysfs_trigger_time_store);
static ssize_t
resmon_sysfs_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, 8, "%d\n", resmon_obj->enable);
}
static ssize_t
resmon_sysfs_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
long int val = 0;
/* We only accept numbers. */
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
resmon_obj->enable = !!val;
//resmon_error("resmon resmon_sysfs_enable_store enable=%d, in_suspend=%d",resmon_obj->enable,resmon_obj->in_suspend);
if(!atomic_read(&resmon_obj->in_suspend)) {
if (resmon_obj->enable == 1)
resmon_task_start();
else
resmon_task_stop();
}
return count;
}
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, resmon_sysfs_enable_show, resmon_sysfs_enable_store);
static struct attribute*
resmon_sysfs_parameters_attrs[] = {
&dev_attr_cpuload_thresh.attr,
&dev_attr_io_thresh.attr,
&dev_attr_mem_thresh.attr,
&dev_attr_trigger_time.attr,
&dev_attr_enable.attr,
NULL,
};
static const struct attribute_group
resmon_sysfs_parameters_grp = {
.name = "parameters",
.attrs = resmon_sysfs_parameters_attrs,
};
static int resmon_fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = data;
unsigned int blank;
int retval = 0;
/* If we aren't interested in this event, skip it immediately ... */
if (event != FB_EVENT_BLANK /* FB_EARLY_EVENT_BLANK */)
return 0;
blank = *(int *)evdata->data;
switch (blank) {
case FB_BLANK_UNBLANK:
atomic_set(&resmon_obj->in_suspend, 0);
/* thread state is controlled by user cmd rather than kernel fb notification */
if (resmon_obj->enable == 1)
resmon_task_start();
break;
case FB_BLANK_POWERDOWN:
atomic_set(&resmon_obj->in_suspend, 1);
//if (resmon_obj->enable == 1)
resmon_task_stop();
break;
default:
resmon_debug("[%s] : other notifier, ignore\n", __func__);
break;
}
return retval;
}
static struct resmon_struct *resmon_alloc_object(void)
{
struct resmon_struct *obj = kzalloc(sizeof(*obj), GFP_KERNEL);
if (!obj) {
resmon_error("Alloc resmon object error!\n");
return NULL;
}
obj->cpuload_thresh = RESMON_CPU_P;
obj->io_thresh = RESMON_IO_P;
obj->mem_thresh = 0;
obj->trigger_time = RESMON_TIMER_INTERVAL_MS;
obj->enable = RESMON_DEFAULT_ENABLE;
obj->tsk_struct_ptr = NULL;
obj->cpuloads = 0;
obj->cpuidle = 0;
obj->iowait = 0;
atomic_set(&obj->in_suspend, 0);
atomic_set(&obj->state, 0);
mutex_init(&obj->lock);
#if RESMON_PERIODICAL_BY_HR_TIMER
/*init Hrtimer */
hrtimer_init(&obj->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
obj->hr_timer.function = (void *)&_resmon_timer_callback;
#else
/*init timer */
//init_timer(&obj->tmr_list);
//obj->tmr_list.function = (void *)&_resmon_timer_callback;
//obj->tmr_list.data = (unsigned long)obj;
timer_setup(&obj->tmr_list, _resmon_timer_callback, 0);
obj->tmr_list.expires = jiffies + msecs_to_jiffies(obj->trigger_time);
#endif
return obj;
}
static int stat_open(struct inode *inode, struct file *file)
{
size_t size = PAGE_SIZE * 3;
return single_open_size(file, show_resmon_stat, NULL, size);
}
static const struct proc_ops proc_stat_operations = {
.proc_read = seq_read,
.proc_open = stat_open,
.proc_release = single_release,
.proc_lseek = seq_lseek,
};
static int __init proc_resmon_init(void)
{
resmon_procfs = proc_create("resmon_stat", 0, NULL, &proc_stat_operations);
return 0;
}
static int resmon_init(void)
{
int ret = 0;
resmon_obj = resmon_alloc_object();
if (!resmon_obj) {
ret = -ENOMEM;
resmon_error("unable to allocate resmon obj!\n");
return ret;
}
resmon_obj->mdev.minor = MISC_DYNAMIC_MINOR;
resmon_obj->mdev.name = RESMON_DEV_NAME;
ret = misc_register(&resmon_obj->mdev);
if (ret) {
resmon_error("misc_register error:%d\n", ret);
goto exit_alloc_data_failed;
}
/* register screen on/off callback */
resmon_obj->notifier.notifier_call = resmon_fb_notifier_callback;
ret = fb_register_client(&resmon_obj->notifier);
if (ret)
goto exit_misc_register_failed;
ret = device_create_file(resmon_obj->mdev.this_device, &dev_attr_system_stat);
if (ret)
goto err_create_file_failed;
ret = device_create_file(resmon_obj->mdev.this_device, &dev_attr_system_io_mem_stat);
if (ret)
goto err_create_system_failed;
ret = sysfs_create_group(&resmon_obj->mdev.this_device->kobj, &resmon_sysfs_parameters_grp);
if (ret)
goto err_create_attr_failed;
if (resmon_obj->enable == 1)
{
ret = resmon_task_start();
if (ret)
goto err_create_attr_failed;
}
proc_resmon_init();
return 0;
err_create_attr_failed:
sysfs_remove_group(&resmon_obj->mdev.this_device->kobj, &resmon_sysfs_parameters_grp);
err_create_system_failed:
device_remove_file(resmon_obj->mdev.this_device, &dev_attr_system_io_mem_stat);
err_create_file_failed:
device_remove_file(resmon_obj->mdev.this_device, &dev_attr_system_stat);
exit_misc_register_failed:
misc_deregister(&resmon_obj->mdev);
exit_alloc_data_failed:
kfree(resmon_obj);
return ret;
}
module_init(resmon_init);
static void resmon_exit(void)
{
proc_remove(resmon_procfs);
resmon_task_stop();
device_remove_file(resmon_obj->mdev.this_device, &dev_attr_system_io_mem_stat);
device_remove_file(resmon_obj->mdev.this_device, &dev_attr_system_stat);
sysfs_remove_group(&resmon_obj->mdev.this_device->kobj, &resmon_sysfs_parameters_grp);
misc_deregister(&resmon_obj->mdev);
kfree(resmon_obj);
}
module_exit(resmon_exit);
MODULE_LICENSE("GPL");