[FIX] sending unmap message
[platform/kernel/swap-modules.git] / us_manager / sspt / sspt_file.c
1 /*
2  *  Dynamic Binary Instrumentation Module based on KProbes
3  *  modules/driver/sspt/sspt_file.c
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * Copyright (C) Samsung Electronics, 2013
20  *
21  * 2013         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
22  *
23  */
24
25 #include "sspt.h"
26 #include "sspt_file.h"
27 #include "sspt_page.h"
28 #include "sspt_proc.h"
29 #include <linux/slab.h>
30 #include <linux/list.h>
31 #include <linux/hash.h>
32 #include <linux/sched.h>
33 #include <kprobe/swap_kprobes_deps.h>
34
35 static int calculation_hash_bits(int cnt)
36 {
37         int bits;
38         for (bits = 1; cnt >>= 1; ++bits)
39                 ;
40
41         return bits;
42 }
43
44 /**
45  * @brief Create sspt_file struct
46  *
47  * @param dentry Dentry of file
48  * @param page_cnt Size of hash-table
49  * @return Pointer to the created sspt_file struct
50  */
51 struct sspt_file *sspt_file_create(struct dentry *dentry, int page_cnt)
52 {
53         int i, table_size;
54         struct sspt_file *obj = kmalloc(sizeof(*obj), GFP_ATOMIC);
55
56         if (obj == NULL)
57                 return NULL;
58
59         INIT_LIST_HEAD(&obj->list);
60         obj->proc = NULL;
61         obj->dentry = dentry;
62         obj->loaded = 0;
63         obj->vm_start = 0;
64         obj->vm_end = 0;
65
66         obj->page_probes_hash_bits = calculation_hash_bits(page_cnt);
67         table_size = (1 << obj->page_probes_hash_bits);
68
69         obj->page_probes_table =
70                         kmalloc(sizeof(*obj->page_probes_table)*table_size,
71                                 GFP_ATOMIC);
72
73         if (obj->page_probes_table == NULL)
74                 goto err;
75
76         for (i = 0; i < table_size; ++i)
77                 INIT_HLIST_HEAD(&obj->page_probes_table[i]);
78
79         return obj;
80
81 err:
82         kfree(obj);
83         return NULL;
84 }
85
86 /**
87  * @brief Remove sspt_file struct
88  *
89  * @param file remove object
90  * @return Void
91  */
92 void sspt_file_free(struct sspt_file *file)
93 {
94         struct hlist_head *head;
95         struct sspt_page *page;
96         int i, table_size = (1 << file->page_probes_hash_bits);
97         struct hlist_node *n;
98         DECLARE_NODE_PTR_FOR_HLIST(p);
99
100         for (i = 0; i < table_size; ++i) {
101                 head = &file->page_probes_table[i];
102                 swap_hlist_for_each_entry_safe(page, p, n, head, hlist) {
103                         hlist_del(&page->hlist);
104                         sspt_page_free(page);
105                 }
106         }
107
108         kfree(file->page_probes_table);
109         kfree(file);
110 }
111
112 static void sspt_add_page(struct sspt_file *file, struct sspt_page *page)
113 {
114         page->file = file;
115         hlist_add_head(&page->hlist,
116                        &file->page_probes_table[hash_ptr(
117                                        (void *)page->offset,
118                                        file->page_probes_hash_bits)]);
119 }
120
121 static struct sspt_page *sspt_find_page(struct sspt_file *file,
122                                         unsigned long offset)
123 {
124         struct hlist_head *head;
125         struct sspt_page *page;
126         DECLARE_NODE_PTR_FOR_HLIST(node);
127
128         head = &file->page_probes_table[hash_ptr((void *)offset,
129                                                  file->page_probes_hash_bits)];
130         swap_hlist_for_each_entry(page, node, head, hlist) {
131                 if (page->offset == offset)
132                         return page;
133         }
134
135         return NULL;
136 }
137
138 static struct sspt_page *sspt_find_page_or_new(struct sspt_file *file,
139                                                unsigned long offset)
140 {
141         struct sspt_page *page = sspt_find_page(file, offset);
142
143         if (page == NULL) {
144                 page = sspt_page_create(offset);
145                 if (page)
146                         sspt_add_page(file, page);
147         }
148
149         return page;
150 }
151
152 /**
153  * @brief Get sspt_page from sspt_file
154  *
155  * @param file Pointer to the sspt_file struct
156  * @param page Page address
157  * @return Pointer to the sspt_page struct
158  */
159 struct sspt_page *sspt_find_page_mapped(struct sspt_file *file,
160                                         unsigned long page)
161 {
162         unsigned long offset;
163
164         if (file->vm_start > page || file->vm_end < page) {
165                 /* TODO: or panic?! */
166                 printk(KERN_INFO "ERROR: file_p[vm_start..vm_end] <> page: "
167                        "file_p[vm_start=%lx, vm_end=%lx, "
168                        "d_iname=%s] page=%lx\n",
169                        file->vm_start, file->vm_end,
170                        file->dentry->d_iname, page);
171                 return NULL;
172         }
173
174         offset = page - file->vm_start;
175
176         return sspt_find_page(file, offset);
177 }
178
179 /**
180  * @brief Add instruction pointer to sspt_file
181  *
182  * @param file Pointer to the sspt_file struct
183  * @param offset File offset
184  * @param args Function arguments
185  * @param ret_type Return type
186  * @return Void
187  */
188 void sspt_file_add_ip(struct sspt_file *file, unsigned long offset,
189                       struct probe_info *probe_i)
190 {
191         struct sspt_page *page =
192                 sspt_find_page_or_new(file, offset & PAGE_MASK);
193
194         /* FIXME: delete ip */
195         struct us_ip *ip = create_ip(offset, probe_i, page);
196
197         if (page && ip)
198                 sspt_add_ip(page, ip);
199 }
200
201 void sspt_file_on_each_ip(struct sspt_file *file,
202                           void (*func)(struct us_ip *, void *), void *data)
203 {
204         int i;
205         const int table_size = (1 << file->page_probes_hash_bits);
206         struct sspt_page *page;
207         struct hlist_head *head;
208         DECLARE_NODE_PTR_FOR_HLIST(node);
209
210         for (i = 0; i < table_size; ++i) {
211                 head = &file->page_probes_table[i];
212                 swap_hlist_for_each_entry(page, node, head, hlist)
213                         sspt_page_on_each_ip(page, func, data);
214         }
215 }
216
217 /**
218  * @brief Get sspt_page from sspt_file (look)
219  *
220  * @param file Pointer to the sspt_file struct
221  * @param offset_addr File offset
222  * @return Pointer to the sspt_page struct
223  */
224 struct sspt_page *sspt_get_page(struct sspt_file *file,
225                                 unsigned long offset_addr)
226 {
227         unsigned long offset = offset_addr & PAGE_MASK;
228         struct sspt_page *page = sspt_find_page_or_new(file, offset);
229
230         spin_lock(&page->lock);
231
232         return page;
233 }
234
235 /**
236  * @brief Put sspt_page (unlook)
237  *
238  * @param file Pointer to the sspt_page struct
239  * @return void
240  */
241 void sspt_put_page(struct sspt_page *page)
242 {
243         spin_unlock(&page->lock);
244 }
245
246 /**
247  * @brief Check install sspt_file (legacy code, it is need remove)
248  *
249  * @param file Pointer to the sspt_file struct
250  * @return
251  *       - 0 - false
252  *       - 1 - true
253  */
254 int sspt_file_check_install_pages(struct sspt_file *file)
255 {
256         int i, table_size;
257         struct sspt_page *page;
258         struct hlist_head *head;
259         struct hlist_node *tmp;
260         DECLARE_NODE_PTR_FOR_HLIST(node);
261
262         table_size = (1 << file->page_probes_hash_bits);
263         for (i = 0; i < table_size; ++i) {
264                 head = &file->page_probes_table[i];
265                 swap_hlist_for_each_entry_safe(page, node, tmp, head, hlist) {
266                         if (sspt_page_is_installed(page))
267                                 return 1;
268                 }
269         }
270
271         return 0;
272 }
273
274 /**
275  * @brief Install sspt_file
276  *
277  * @param file Pointer to the sspt_file struct
278  * @return Void
279  */
280 void sspt_file_install(struct sspt_file *file)
281 {
282         struct sspt_page *page = NULL;
283         struct hlist_head *head = NULL;
284         int i, table_size = (1 << file->page_probes_hash_bits);
285         unsigned long page_addr;
286         struct mm_struct *mm;
287         DECLARE_NODE_PTR_FOR_HLIST(node);
288
289         for (i = 0; i < table_size; ++i) {
290                 head = &file->page_probes_table[i];
291                 swap_hlist_for_each_entry_rcu(page, node, head, hlist) {
292                         page_addr = file->vm_start + page->offset;
293                         if (page_addr < file->vm_start ||
294                             page_addr >= file->vm_end)
295                                 continue;
296
297                         mm = page->file->proc->task->mm;
298                         if (page_present(mm, page_addr))
299                                 sspt_register_page(page, file);
300                 }
301         }
302 }
303
304 /**
305  * @brief Uninstall sspt_file
306  *
307  * @param file Pointer to the sspt_file struct
308  * @param task Pointer to the task_stract struct
309  * @param flag Action for probes
310  * @return Void
311  */
312 int sspt_file_uninstall(struct sspt_file *file,
313                         struct task_struct *task,
314                         enum US_FLAGS flag)
315 {
316         int i, err = 0;
317         int table_size = (1 << file->page_probes_hash_bits);
318         struct sspt_page *page;
319         struct hlist_head *head;
320         struct hlist_node *tmp;
321         DECLARE_NODE_PTR_FOR_HLIST(node);
322
323         for (i = 0; i < table_size; ++i) {
324                 head = &file->page_probes_table[i];
325                 swap_hlist_for_each_entry_safe(page, node, tmp, head, hlist) {
326                         err = sspt_unregister_page(page, flag, task);
327                         if (err != 0) {
328                                 printk(KERN_INFO "ERROR sspt_file_uninstall: "
329                                        "err=%d\n", err);
330                                 return err;
331                         }
332                 }
333         }
334
335         if (flag != US_DISARM) {
336                 file->loaded = 0;
337                 file->vm_start = 0;
338                 file->vm_end = 0;
339         }
340
341         return err;
342 }
343
344 /**
345  * @brief Set mapping for sspt_file
346  *
347  * @param file Pointer to the sspt_file struct
348  * @param vma Pointer to the vm_area_struct struct
349  * @return Void
350  */
351 void sspt_file_set_mapping(struct sspt_file *file, struct vm_area_struct *vma)
352 {
353         if (file->loaded == 0) {
354                 file->loaded = 1;
355                 file->vm_start = vma->vm_start;
356                 file->vm_end = vma->vm_end;
357         }
358 }