]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/blob - drivers/misc/tegra-profiler/mmap.c
Merge tag 'v3.10.24' into HEAD
[sojka/nv-tegra/linux-3.10.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 #include <linux/err.h>
25 #include <linux/sched.h>
26
27 #include <linux/tegra_profiler.h>
28
29 #include "mmap.h"
30 #include "hrt.h"
31 #include "debug.h"
32
33 static struct quadd_mmap_ctx mmap_ctx;
34
35 static int binary_search_and_add(unsigned int *array,
36                         unsigned int length, unsigned int key)
37 {
38         unsigned int i_min, i_max, mid;
39
40         if (length == 0) {
41                 array[0] = key;
42                 return 1;
43         } else if (length == 1 && array[0] == key) {
44                 return 0;
45         }
46
47         i_min = 0;
48         i_max = length;
49
50         if (array[0] > key) {
51                 memmove((char *)((unsigned int *)array + 1), array,
52                         length * sizeof(unsigned int));
53                 array[0] = key;
54                 return 1;
55         } else if (array[length - 1] < key) {
56                 array[length] = key;
57                 return 1;
58         }
59
60         while (i_min < i_max) {
61                 mid = i_min + (i_max - i_min) / 2;
62
63                 if (key <= array[mid])
64                         i_max = mid;
65                 else
66                         i_min = mid + 1;
67         }
68
69         if (array[i_max] == key) {
70                 return 0;
71         } else {
72                 memmove((char *)((unsigned int *)array + i_max + 1),
73                         (char *)((unsigned int *)array + i_max),
74                         (length - i_max) * sizeof(unsigned int));
75                 array[i_max] = key;
76                 return 1;
77         }
78 }
79
80 static int check_hash(u32 key)
81 {
82         int res;
83         unsigned long flags;
84
85         spin_lock_irqsave(&mmap_ctx.lock, flags);
86
87         if (mmap_ctx.nr_hashes >= QUADD_MMAP_SIZE_ARRAY) {
88                 spin_unlock_irqrestore(&mmap_ctx.lock, flags);
89                 return 1;
90         }
91
92         res = binary_search_and_add(mmap_ctx.hash_array,
93                                     mmap_ctx.nr_hashes, key);
94         if (res > 0) {
95                 mmap_ctx.nr_hashes++;
96                 spin_unlock_irqrestore(&mmap_ctx.lock, flags);
97                 return 0;
98         }
99
100         spin_unlock_irqrestore(&mmap_ctx.lock, flags);
101         return 1;
102 }
103
104 static int find_file(const char *file_name, unsigned long addr,
105                      unsigned long len)
106 {
107         u32 crc;
108         size_t length;
109
110         if (!file_name)
111                 return 0;
112
113         length = strlen(file_name);
114
115         crc = crc32_le(~0, file_name, length);
116         crc = crc32_le(crc, (unsigned char *)&addr,
117                        sizeof(addr));
118         crc = crc32_le(crc, (unsigned char *)&len,
119                        sizeof(len));
120
121         return check_hash(crc);
122 }
123
124 static void
125 put_mmap_sample(struct quadd_mmap_data *s, char *extra_data,
126                 size_t extra_length)
127 {
128         struct quadd_record_data r;
129
130         r.magic = QUADD_RECORD_MAGIC;
131         r.record_type = QUADD_RECORD_TYPE_MMAP;
132         r.cpu_mode = QUADD_CPU_MODE_USER;
133
134         memcpy(&r.mmap, s, sizeof(*s));
135         r.mmap.filename_length = extra_length;
136
137         pr_debug("MMAP: pid: %d, file_name: '%s', addr: %#x, length: %u",
138                  s->pid, extra_data, s->addr, extra_length);
139
140         quadd_put_sample(&r, extra_data, extra_length);
141 }
142
143 void quadd_get_mmap_object(struct quadd_cpu_context *cpu_ctx,
144                            struct pt_regs *regs, pid_t pid)
145 {
146         unsigned long ip;
147         size_t length, length_aligned;
148         struct mm_struct *mm = current->mm;
149         struct vm_area_struct *vma;
150         struct file *vm_file;
151         struct path *path;
152         char *file_name = NULL;
153         struct quadd_mmap_data sample;
154         struct quadd_mmap_cpu_ctx *mm_cpu_ctx = this_cpu_ptr(mmap_ctx.cpu_ctx);
155         char *tmp_buf = mm_cpu_ctx->tmp_buf;
156
157         if (!mm)
158                 return;
159
160         ip = instruction_pointer(regs);
161
162         if (user_mode(regs)) {
163                 for (vma = find_vma(mm, ip); vma; vma = vma->vm_next) {
164                         if (ip < vma->vm_start || ip >= vma->vm_end)
165                                 continue;
166
167                         vm_file = vma->vm_file;
168                         if (!vm_file)
169                                 break;
170
171                         path = &vm_file->f_path;
172
173                         file_name = d_path(path, tmp_buf, PATH_MAX);
174                         if (IS_ERR(file_name)) {
175                                 file_name = NULL;
176                         } else {
177                                 sample.addr = vma->vm_start;
178                                 sample.len = vma->vm_end - vma->vm_start;
179                                 sample.pgoff =
180                                         (u64)vma->vm_pgoff << PAGE_SHIFT;
181                         }
182                         break;
183                 }
184         } else {
185 #ifdef CONFIG_MODULES
186                 struct module *mod;
187
188                 preempt_disable();
189                 mod = __module_address(ip);
190                 preempt_enable();
191
192                 if (mod) {
193                         file_name = mod->name;
194                         if (file_name) {
195                                 sample.addr = (u32) mod->module_core;
196                                 sample.len = mod->core_size;
197                                 sample.pgoff = 0;
198                         }
199                 }
200 #endif
201         }
202
203         if (file_name) {
204                 if (!find_file(file_name, sample.addr, sample.len)) {
205                         length = strlen(file_name) + 1;
206                         if (length > PATH_MAX)
207                                 return;
208
209                         sample.pid = pid;
210
211                         strcpy(cpu_ctx->mmap_filename, file_name);
212                         length_aligned = ALIGN(length, 8);
213
214                         put_mmap_sample(&sample, cpu_ctx->mmap_filename,
215                                         length_aligned);
216                 }
217         }
218 }
219
220 int quadd_get_current_mmap(struct quadd_cpu_context *cpu_ctx, pid_t pid)
221 {
222         struct vm_area_struct *vma;
223         struct file *vm_file;
224         struct path *path;
225         char *file_name;
226         struct task_struct *task;
227         struct mm_struct *mm;
228         struct quadd_mmap_data sample;
229         size_t length, length_aligned;
230         struct quadd_mmap_cpu_ctx *mm_cpu_ctx = this_cpu_ptr(mmap_ctx.cpu_ctx);
231         char *tmp_buf = mm_cpu_ctx->tmp_buf;
232
233         rcu_read_lock();
234         task = pid_task(find_vpid(pid), PIDTYPE_PID);
235         rcu_read_unlock();
236         if (!task) {
237                 pr_err("Process not found: %u\n", pid);
238                 return -ESRCH;
239         }
240         mm = task->mm;
241
242         pr_info("Get mapped memory objects\n");
243
244         for (vma = mm->mmap; vma; vma = vma->vm_next) {
245                 vm_file = vma->vm_file;
246                 if (!vm_file)
247                         continue;
248
249                 path = &vm_file->f_path;
250
251                 file_name = d_path(path, tmp_buf, PATH_MAX);
252                 if (IS_ERR(file_name))
253                         continue;
254
255                 if (!(vma->vm_flags & VM_EXEC))
256                         continue;
257
258                 length = strlen(file_name) + 1;
259                 if (length > PATH_MAX)
260                         continue;
261
262                 sample.pid = pid;
263                 sample.addr = vma->vm_start;
264                 sample.len = vma->vm_end - vma->vm_start;
265                 sample.pgoff = (u64)vma->vm_pgoff << PAGE_SHIFT;
266
267                 if (!find_file(file_name, sample.addr, sample.len)) {
268                         strcpy(cpu_ctx->mmap_filename, file_name);
269                         length_aligned = ALIGN(length, 8);
270
271                         put_mmap_sample(&sample, file_name, length_aligned);
272                 }
273         }
274         return 0;
275 }
276
277 struct quadd_mmap_ctx *quadd_mmap_init(struct quadd_ctx *quadd_ctx)
278 {
279         int cpu_id;
280         u32 *hash;
281         char *tmp;
282         struct quadd_mmap_cpu_ctx *cpu_ctx;
283
284         mmap_ctx.quadd_ctx = quadd_ctx;
285
286         hash = kzalloc(QUADD_MMAP_SIZE_ARRAY * sizeof(unsigned int),
287                        GFP_KERNEL);
288         if (!hash) {
289                 pr_err("Failed to allocate mmap buffer\n");
290                 return ERR_PTR(-ENOMEM);
291         }
292         mmap_ctx.hash_array = hash;
293
294         mmap_ctx.nr_hashes = 0;
295         spin_lock_init(&mmap_ctx.lock);
296
297         mmap_ctx.cpu_ctx = alloc_percpu(struct quadd_mmap_cpu_ctx);
298         if (!mmap_ctx.cpu_ctx)
299                 return ERR_PTR(-ENOMEM);
300
301         for (cpu_id = 0; cpu_id < nr_cpu_ids; cpu_id++) {
302                 cpu_ctx = per_cpu_ptr(mmap_ctx.cpu_ctx, cpu_id);
303
304                 tmp = kzalloc(PATH_MAX + sizeof(unsigned long long),
305                               GFP_KERNEL);
306                 if (!tmp) {
307                         pr_err("Failed to allocate mmap buffer\n");
308                         return ERR_PTR(-ENOMEM);
309                 }
310                 cpu_ctx->tmp_buf = tmp;
311         }
312
313         return &mmap_ctx;
314 }
315
316 void quadd_mmap_reset(void)
317 {
318         unsigned long flags;
319
320         spin_lock_irqsave(&mmap_ctx.lock, flags);
321         mmap_ctx.nr_hashes = 0;
322         spin_unlock_irqrestore(&mmap_ctx.lock, flags);
323 }
324
325 void quadd_mmap_deinit(void)
326 {
327         int cpu_id;
328         unsigned long flags;
329         struct quadd_mmap_cpu_ctx *cpu_ctx;
330
331         spin_lock_irqsave(&mmap_ctx.lock, flags);
332         kfree(mmap_ctx.hash_array);
333         mmap_ctx.hash_array = NULL;
334
335         for (cpu_id = 0; cpu_id < nr_cpu_ids; cpu_id++) {
336                 cpu_ctx = per_cpu_ptr(mmap_ctx.cpu_ctx, cpu_id);
337                 kfree(cpu_ctx->tmp_buf);
338         }
339         free_percpu(mmap_ctx.cpu_ctx);
340
341         spin_unlock_irqrestore(&mmap_ctx.lock, flags);
342 }