ARM: tegra: Add Tegra Profiler
[linux-2.6.git] / drivers / misc / tegra-profiler / mmap.c
1 /*
2  * drivers/misc/tegra-profiler/mmap.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/crc32.h>
22 #include <linux/fs.h>
23 #include <linux/slab.h>
24
25 #include <linux/tegra_profiler.h>
26
27 #include "mmap.h"
28 #include "hrt.h"
29 #include "debug.h"
30
31 static struct quadd_mmap_ctx mmap_ctx;
32
33 static int binary_search_and_add(unsigned int *array,
34                         unsigned int length, unsigned int key)
35 {
36         unsigned int i_min, i_max, mid;
37
38         if (length == 0) {
39                 array[0] = key;
40                 return 1;
41         } else if (length == 1 && array[0] == key) {
42                 return 0;
43         }
44
45         i_min = 0;
46         i_max = length;
47
48         if (array[0] > key) {
49                 memmove((char *)((unsigned int *)array + 1), array,
50                         length * sizeof(unsigned int));
51                 array[0] = key;
52                 return 1;
53         } else if (array[length - 1] < key) {
54                 array[length] = key;
55                 return 1;
56         }
57
58         while (i_min < i_max) {
59                 mid = i_min + (i_max - i_min) / 2;
60
61                 if (key <= array[mid])
62                         i_max = mid;
63                 else
64                         i_min = mid + 1;
65         }
66
67         if (array[i_max] == key) {
68                 return 0;
69         } else {
70                 memmove((char *)((unsigned int *)array + i_max + 1),
71                         (char *)((unsigned int *)array + i_max),
72                         (length - i_max) * sizeof(unsigned int));
73                 array[i_max] = key;
74                 return 1;
75         }
76 }
77
78 static int check_hash(u32 key)
79 {
80         int res;
81         unsigned long flags;
82
83         spin_lock_irqsave(&mmap_ctx.lock, flags);
84
85         if (mmap_ctx.nr_hashes >= QUADD_MMAP_SIZE_ARRAY) {
86                 spin_unlock_irqrestore(&mmap_ctx.lock, flags);
87                 return 1;
88         }
89
90         res = binary_search_and_add(mmap_ctx.hash_array,
91                                     mmap_ctx.nr_hashes, key);
92         if (res > 0) {
93                 mmap_ctx.nr_hashes++;
94                 spin_unlock_irqrestore(&mmap_ctx.lock, flags);
95                 return 0;
96         }
97
98         spin_unlock_irqrestore(&mmap_ctx.lock, flags);
99         return 1;
100 }
101
102 char *quadd_get_mmap(struct quadd_cpu_context *cpu_ctx,
103                      struct pt_regs *regs, struct quadd_mmap_data *sample,
104                      unsigned int *extra_length)
105 {
106         struct mm_struct *mm = current->mm;
107         struct vm_area_struct *vma;
108         struct file *vm_file;
109         struct path *path;
110         char *file_name = NULL;
111         int length, length_aligned;
112         u32 crc;
113         unsigned long ip;
114
115         if (!mm) {
116                 *extra_length = 0;
117                 return NULL;
118         }
119
120         ip = instruction_pointer(regs);
121
122         if (user_mode(regs)) {
123                 for (vma = find_vma(mm, ip); vma; vma = vma->vm_next) {
124                         if (ip < vma->vm_start || ip >= vma->vm_end)
125                                 continue;
126
127                         vm_file = vma->vm_file;
128                         if (!vm_file)
129                                 break;
130
131                         path = &vm_file->f_path;
132
133                         file_name = d_path(path, mmap_ctx.tmp_buf, PATH_MAX);
134                         if (file_name) {
135                                 sample->addr = vma->vm_start;
136                                 sample->len = vma->vm_end - vma->vm_start;
137                                 sample->pgoff =
138                                         (u64)vma->vm_pgoff << PAGE_SHIFT;
139                         }
140                         break;
141                 }
142         } else {
143                 struct module *mod;
144
145                 preempt_disable();
146                 mod = __module_address(ip);
147                 preempt_enable();
148
149                 if (mod) {
150                         file_name = mod->name;
151                         if (file_name) {
152                                 sample->addr = (u32) mod->module_core;
153                                 sample->len = mod->core_size;
154                                 sample->pgoff = 0;
155                         }
156                 }
157         }
158
159         if (file_name) {
160                 length = strlen(file_name);
161                 if (length >= PATH_MAX) {
162                         *extra_length = 0;
163                         return NULL;
164                 }
165
166                 crc = crc32_le(~0, file_name, length);
167                 crc = crc32_le(crc, (unsigned char *)&sample->addr,
168                                sizeof(sample->addr));
169                 crc = crc32_le(crc, (unsigned char *)&sample->len,
170                                sizeof(sample->len));
171
172                 if (!check_hash(crc)) {
173                         strcpy(cpu_ctx->mmap_filename, file_name);
174                         length_aligned = (length + 1 + 7) & (~7);
175                         *extra_length = length_aligned;
176
177                         return cpu_ctx->mmap_filename;
178                 }
179         }
180
181         *extra_length = 0;
182         return NULL;
183 }
184
185 struct quadd_mmap_ctx *quadd_mmap_init(struct quadd_ctx *quadd_ctx)
186 {
187         u32 *hash;
188         char *tmp;
189
190         mmap_ctx.quadd_ctx = quadd_ctx;
191
192         hash = kzalloc(QUADD_MMAP_SIZE_ARRAY * sizeof(unsigned int),
193                        GFP_KERNEL);
194         if (!hash) {
195                 pr_err("Alloc error\n");
196                 return NULL;
197         }
198         mmap_ctx.hash_array = hash;
199
200         mmap_ctx.nr_hashes = 0;
201         spin_lock_init(&mmap_ctx.lock);
202
203         tmp = kzalloc(PATH_MAX + sizeof(unsigned long long),
204                       GFP_KERNEL);
205         if (!tmp) {
206                 pr_err("Alloc error\n");
207                 return NULL;
208         }
209         mmap_ctx.tmp_buf = tmp;
210
211         return &mmap_ctx;
212 }
213
214 void quadd_mmap_reset(void)
215 {
216         unsigned long flags;
217
218         spin_lock_irqsave(&mmap_ctx.lock, flags);
219         mmap_ctx.nr_hashes = 0;
220         spin_unlock_irqrestore(&mmap_ctx.lock, flags);
221 }
222
223 void quadd_mmap_deinit(void)
224 {
225         unsigned long flags;
226
227         spin_lock_irqsave(&mmap_ctx.lock, flags);
228
229         kfree(mmap_ctx.hash_array);
230         mmap_ctx.hash_array = NULL;
231
232         kfree(mmap_ctx.tmp_buf);
233         mmap_ctx.tmp_buf = NULL;
234
235         spin_unlock_irqrestore(&mmap_ctx.lock, flags);
236 }