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