ARM: tegra: Add Tegra Profiler
[linux-2.6.git] / drivers / misc / tegra-profiler / ma.c
1 /*
2  * drivers/misc/tegra-profiler/ma.c
3  *
4  * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  */
16
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
19 #include <linux/module.h>
20 #include <linux/mm.h>
21 #include <linux/timer.h>
22
23 #include <linux/tegra_profiler.h>
24
25 #include "ma.h"
26 #include "quadd.h"
27 #include "hrt.h"
28 #include "comm.h"
29 #include "debug.h"
30
31 static void make_sample(struct quadd_hrt_ctx *hrt_ctx,
32                         pid_t pid, unsigned long vm_size,
33                         unsigned long rss_size)
34 {
35         struct quadd_record_data record;
36         struct quadd_ma_data *ma = &record.ma;
37         struct quadd_comm_data_interface *comm = hrt_ctx->quadd_ctx->comm;
38
39         record.magic = QUADD_RECORD_MAGIC;
40         record.record_type = QUADD_RECORD_TYPE_MA;
41         record.cpu_mode = QUADD_CPU_MODE_NONE;
42
43         ma->pid = pid;
44         ma->time = quadd_get_time();
45
46         ma->vm_size = vm_size << PAGE_SHIFT;
47         ma->rss_size = rss_size << PAGE_SHIFT;
48 /*
49         pr_debug("vm: %llu bytes (%llu mb), rss: %llu bytes (%llu mb)\n",
50                 ma->vm_size, ma->vm_size / 0x100000,
51                 ma->rss_size, ma->rss_size / 0x100000);
52 */
53         comm->put_sample(&record, NULL, 0);
54 }
55
56 static void check_ma(struct quadd_hrt_ctx *hrt_ctx)
57 {
58         pid_t pid;
59         struct pid *pid_s;
60         struct task_struct *task = NULL;
61         struct mm_struct *mm;
62         struct quadd_ctx *quadd_ctx = hrt_ctx->quadd_ctx;
63         unsigned long vm_size, rss_size, total_vm;
64
65         pid = quadd_ctx->param.pids[0];
66
67         rcu_read_lock();
68         pid_s = find_vpid(pid);
69         if (pid_s)
70                 task = pid_task(pid_s, PIDTYPE_PID);
71         rcu_read_unlock();
72         if (!task)
73                 return;
74
75         mm = task->mm;
76         if (!mm)
77                 return;
78
79         total_vm = mm->total_vm;
80         vm_size = total_vm - mm->reserved_vm;
81         rss_size = get_mm_rss(mm);
82
83         if (vm_size != hrt_ctx->vm_size_prev ||
84             rss_size != hrt_ctx->rss_size_prev) {
85                 make_sample(hrt_ctx, pid, vm_size, rss_size);
86                 hrt_ctx->vm_size_prev = vm_size;
87                 hrt_ctx->rss_size_prev = rss_size;
88         }
89 }
90
91 static void timer_interrupt(unsigned long data)
92 {
93         struct quadd_hrt_ctx *hrt_ctx = (struct quadd_hrt_ctx *)data;
94         struct timer_list *timer = &hrt_ctx->ma_timer;
95
96         if (hrt_ctx->active == 0)
97                 return;
98
99         check_ma(hrt_ctx);
100
101         timer->expires = jiffies + msecs_to_jiffies(hrt_ctx->ma_period);
102         add_timer(timer);
103 }
104
105 void quadd_ma_start(struct quadd_hrt_ctx *hrt_ctx)
106 {
107         struct timer_list *timer = &hrt_ctx->ma_timer;
108
109         if (hrt_ctx->ma_period == 0) {
110                 pr_info("QuadD MA is disabled\n");
111                 return;
112         }
113         pr_info("QuadD MA is started, interval: %u msec\n",
114                 hrt_ctx->ma_period);
115
116         hrt_ctx->vm_size_prev = 0;
117         hrt_ctx->rss_size_prev = 0;
118
119         init_timer(timer);
120         timer->function = timer_interrupt;
121         timer->expires = jiffies + msecs_to_jiffies(hrt_ctx->ma_period);
122         timer->data = (unsigned long)hrt_ctx;
123         add_timer(timer);
124 }
125
126 void quadd_ma_stop(struct quadd_hrt_ctx *hrt_ctx)
127 {
128         if (hrt_ctx->ma_period > 0) {
129                 pr_info("QuadD MA is stopped\n");
130                 del_timer_sync(&hrt_ctx->ma_timer);
131         }
132 }