Redesign KProbe module (separating core and arch parts).
[kernel/swap-modules.git] / kprobe / dbi_insn_slots.c
1 /*
2  *  Kernel Probes (KProbes)
3  *  kernel/kprobes.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) IBM Corporation, 2002, 2004
20  */
21
22 /*
23  *  Dynamic Binary Instrumentation Module based on KProbes
24  *  modules/kprobe/dbi_insn_slots.c
25  *
26  * This program is free software; you can redistribute it and/or modify
27  * it under the terms of the GNU General Public License as published by
28  * the Free Software Foundation; either version 2 of the License, or
29  * (at your option) any later version.
30  *
31  * This program is distributed in the hope that it will be useful,
32  * but WITHOUT ANY WARRANTY; without even the implied warranty of
33  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
34  * GNU General Public License for more details.
35  *
36  * You should have received a copy of the GNU General Public License
37  * along with this program; if not, write to the Free Software
38  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
39  *
40  * Copyright (C) Samsung Electronics, 2006-2010
41  *
42  * 2008-2009    Alexey Gerenkov <a.gerenkov@samsung.com> User-Space
43  *              Probes initial implementation; Support x86/ARM/MIPS for both user and kernel spaces.
44  * 2010         Ekaterina Gorelkina <e.gorelkina@samsung.com>: redesign module for separating core and arch parts 
45  *
46
47  */
48
49 #include "dbi_insn_slots.h"
50 #include "dbi_uprobes.h"
51 #include "dbi_kdebug.h"
52
53 #include <linux/hash.h>
54 #include <linux/mman.h>
55 #include <linux/hugetlb.h>
56
57
58 extern struct hlist_head uprobe_insn_slot_table[KPROBE_TABLE_SIZE];
59
60 struct hlist_head kprobe_insn_pages;
61 int kprobe_garbage_slots;
62 struct hlist_head uprobe_insn_pages;
63 int uprobe_garbage_slots;
64
65
66 struct kprobe_insn_page
67 {
68         struct hlist_node hlist;
69         kprobe_opcode_t *insns; /* Page of instruction slots */
70         char *slot_used;        
71         int nused;
72         int ngarbage;
73         int tgid;
74 };
75
76 enum kprobe_slot_state
77 {
78         SLOT_CLEAN = 0,
79         SLOT_DIRTY = 1,
80         SLOT_USED = 2,
81 };
82
83
84 unsigned long alloc_user_pages(struct task_struct *task, unsigned long len, unsigned long prot, unsigned long flags, int atomic)
85 {
86         long ret = 0;
87         struct task_struct *otask = current;
88         struct mm_struct *mm;
89
90         mm = atomic ? task->active_mm : get_task_mm (task);
91         if (mm){
92                 if(!atomic)
93                         down_write (&mm->mmap_sem);
94                 // FIXME: its seems to be bad decision to replace 'current' pointer temporarily 
95                 current_thread_info()->task = task;
96                 ret = (unsigned long)do_mmap_pgoff(0, 0, len, prot, flags, 0);
97                 current_thread_info()->task = otask;
98                 if(!atomic){
99                         up_write (&mm->mmap_sem);
100                         mmput(mm);
101                 }
102         }
103         else
104                 printk ("proc %d has no mm", task->pid);
105         return (unsigned long)ret;
106 }
107
108         int
109 check_safety (void)
110 {
111         synchronize_sched ();
112         return 0;
113 }
114
115
116 /**
117  * get_us_insn_slot() - Find a slot on an executable page for an instruction.
118  * We allocate an executable page if there's no room on existing ones.
119  */
120 kprobe_opcode_t *get_insn_slot (struct task_struct *task, int atomic)
121 {
122         struct kprobe_insn_page *kip;
123         struct hlist_node *pos;
124         struct hlist_head *page_list = task ? &uprobe_insn_pages : &kprobe_insn_pages;
125         unsigned slots_per_page = INSNS_PER_PAGE, slot_size = MAX_INSN_SIZE;
126
127         if(task) {
128                 slots_per_page = INSNS_PER_PAGE/UPROBES_TRAMP_LEN;
129                 slot_size = UPROBES_TRAMP_LEN;
130         }
131         else {
132                 slots_per_page = INSNS_PER_PAGE/KPROBES_TRAMP_LEN;
133                 slot_size = KPROBES_TRAMP_LEN;          
134         }
135
136 retry:
137         hlist_for_each_entry (kip, pos, page_list, hlist)
138         {
139                 if (kip->nused < slots_per_page)
140                 {
141                         int i;
142                         for (i = 0; i < slots_per_page; i++)
143                         {
144                                 if (kip->slot_used[i] == SLOT_CLEAN)
145                                 {
146                                         if(!task || (kip->tgid == task->tgid)){
147                                                 kip->slot_used[i] = SLOT_USED;
148                                                 kip->nused++;
149                                                 return kip->insns + (i * slot_size);
150                                         }
151                                 }
152                         }
153                         /* Surprise!  No unused slots.  Fix kip->nused. */
154                         kip->nused = slots_per_page;
155                 }
156         }
157
158         /* If there are any garbage slots, collect it and try again. */
159         if(task) {
160                 if (uprobe_garbage_slots && collect_garbage_slots(page_list, task) == 0)
161                         goto retry;
162         }
163         else {
164                 if (kprobe_garbage_slots && collect_garbage_slots(page_list, task) == 0)
165                         goto retry;             
166         }
167
168         /* All out of space.  Need to allocate a new page. Use slot 0. */
169         kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_KERNEL);
170         if (!kip)
171                 return NULL;
172
173         kip->slot_used = kmalloc(sizeof(char)*slots_per_page, GFP_KERNEL);
174         if (!kip->slot_used){
175                 kfree(kip);
176                 return NULL;
177         }
178
179         if(task) {
180                 kip->insns = (kprobe_opcode_t *)alloc_user_pages(task, PAGE_SIZE, 
181                                 PROT_EXEC|PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, atomic);
182         }
183         else {
184                 kip->insns = kmalloc(PAGE_SIZE, GFP_KERNEL);
185         }
186         if (!kip->insns)
187         {
188                 kfree (kip->slot_used);
189                 kfree (kip);
190                 return NULL;
191         }       
192         INIT_HLIST_NODE (&kip->hlist);
193         hlist_add_head (&kip->hlist, page_list);
194         memset(kip->slot_used, SLOT_CLEAN, slots_per_page);
195         kip->slot_used[0] = SLOT_USED;
196         kip->nused = 1;
197         kip->ngarbage = 0;
198         kip->tgid = task ? task->tgid : 0;
199         return kip->insns;
200 }
201
202 /* Return 1 if all garbages are collected, otherwise 0. */
203 static 
204 int collect_one_slot (struct hlist_head *page_list, struct task_struct *task, 
205                 struct kprobe_insn_page *kip, int idx)
206 {
207         kip->slot_used[idx] = SLOT_CLEAN;
208         kip->nused--;
209         DBPRINTF("collect_one_slot: nused=%d", kip->nused);
210         if (kip->nused == 0)
211         {
212                 /*
213                  * Page is no longer in use.  Free it unless
214                  * it's the last one.  We keep the last one
215                  * so as not to have to set it up again the
216                  * next time somebody inserts a probe.
217                  */
218                 hlist_del (&kip->hlist);
219                 if (!task && hlist_empty (page_list))
220                 {
221                         INIT_HLIST_NODE (&kip->hlist);
222                         hlist_add_head (&kip->hlist, page_list);
223                 }
224                 else
225                 {
226                         if(task){
227                                 //E. G.: This code provides kernel dump because of rescheduling while atomic. 
228                                 //As workaround, this code was commented. In this case we will have memory leaks 
229                                 //for instrumented process, but instrumentation process should functionate correctly. 
230                                 //Planned that good solution for this problem will be done during redesigning KProbe 
231                                 //for improving supportability and performance.
232 #if 0
233                                 //printk("collect_one_slot %p/%d\n", task, task->pid);
234                                 mm = get_task_mm (task);
235                                 if (mm){                        
236                                         down_write (&mm->mmap_sem);
237                                         do_munmap(mm, (unsigned long)(kip->insns), PAGE_SIZE);
238                                         up_write (&mm->mmap_sem);
239                                         mmput(mm);
240                                 }
241 #endif
242                                 kip->insns = NULL; //workaround
243                                 kip->tgid = 0;
244                         }
245                         else {
246                                 kfree(kip->insns);
247                         }
248                         kfree (kip->slot_used);
249                         kfree (kip);
250                 }
251                 return 1;
252         }
253         return 0;
254 }
255
256 int collect_garbage_slots (struct hlist_head *page_list, struct task_struct *task)
257 {
258         struct kprobe_insn_page *kip;
259         struct hlist_node *pos, *next;
260         unsigned slots_per_page = INSNS_PER_PAGE;
261
262         /* Ensure no-one is preepmted on the garbages */
263         if (!task && check_safety() != 0)
264                 return -EAGAIN;
265
266         if(task)
267                 slots_per_page = INSNS_PER_PAGE/UPROBES_TRAMP_LEN;
268         else
269                 slots_per_page = INSNS_PER_PAGE/KPROBES_TRAMP_LEN;
270
271         hlist_for_each_entry_safe (kip, pos, next, page_list, hlist)
272         {
273                 int i;
274                 if ((task && (kip->tgid != task->tgid)) || (kip->ngarbage == 0))
275                         continue;
276                 kip->ngarbage = 0;      /* we will collect all garbages */
277                 for (i = 0; i < slots_per_page; i++)
278                 {
279                         if (kip->slot_used[i] == SLOT_DIRTY && collect_one_slot (page_list, task, kip, i))
280                                 break;
281                 }
282         }
283         if(task)        uprobe_garbage_slots = 0;
284         else            kprobe_garbage_slots = 0;
285         return 0;
286 }
287
288 void purge_garbage_uslots(struct task_struct *task, int atomic)
289 {
290         if(collect_garbage_slots(&uprobe_insn_pages, task))
291                 panic("failed to collect garbage slotsfo for task %s/%d/%d", task->comm, task->tgid, task->pid);
292 }
293
294 void free_insn_slot (struct hlist_head *page_list, struct task_struct *task, kprobe_opcode_t *slot, int dirty)
295 {
296         struct kprobe_insn_page *kip;
297         struct hlist_node *pos;
298         unsigned slots_per_page = INSNS_PER_PAGE, slot_size = MAX_INSN_SIZE;
299
300         if(task) {      
301                 slots_per_page = INSNS_PER_PAGE/UPROBES_TRAMP_LEN;
302                 slot_size = UPROBES_TRAMP_LEN;
303         }
304         else {
305                 slots_per_page = INSNS_PER_PAGE/KPROBES_TRAMP_LEN;
306                 slot_size = KPROBES_TRAMP_LEN;
307         }
308
309         DBPRINTF("free_insn_slot: dirty %d, %p/%d", dirty, task, task?task->pid:0);
310         hlist_for_each_entry (kip, pos, page_list, hlist)
311         {
312                 DBPRINTF("free_insn_slot: kip->insns=%p slot=%p", kip->insns, slot);
313                 if ((kip->insns <= slot) && (slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)))
314                 {
315                         int i = (slot - kip->insns) / slot_size;
316                         if (dirty)
317                         {
318                                 kip->slot_used[i] = SLOT_DIRTY;
319                                 kip->ngarbage++;
320                         }
321                         else
322                         {
323                                 collect_one_slot (page_list, task, kip, i);
324                         }
325                         break;
326                 }
327         }
328
329         if (dirty){
330                 if(task){
331                         if(++uprobe_garbage_slots > slots_per_page)
332                                 collect_garbage_slots (page_list, task);
333                 }
334                 else if(++kprobe_garbage_slots > slots_per_page)
335                         collect_garbage_slots (page_list, task);
336         }
337 }
338
339 struct kprobe *get_kprobe_by_insn_slot (void *addr, int tgid, struct task_struct *ctask)
340 {
341         struct hlist_head *head;
342         struct hlist_node *node;
343         struct kprobe *p, *retVal = NULL;
344         int uprobe_found;
345
346         //TODO: test - two processes invokes instrumented function
347         head = &uprobe_insn_slot_table[hash_ptr (addr, KPROBE_HASH_BITS)];
348         hlist_for_each_entry_rcu (p, node, head, is_hlist)
349         {
350                 //if looking for kernel probe and this is kernel probe with the same addr OR
351                 //if looking for the user space probe and this is user space probe probe with the same addr and pid
352                 DBPRINTF ("get_kprobe: check probe at %p/%p, task %d/%d", addr, p->ainsn.insn, tgid, p->tgid);
353                 if (p->ainsn.insn == addr)
354                 {
355                         uprobe_found = 0;
356                         if (tgid == p->tgid)
357                                 uprobe_found = 1;
358                         if (!tgid || uprobe_found)
359                         {
360                                 retVal = p;
361                                 if (tgid)
362                                         DBPRINTF ("get_kprobe: found user space probe at %p for task %d", p->addr, p->tgid);
363                                 else
364                                         DBPRINTF ("get_kprobe: found kernel probe at %p", p->addr);
365                                 break;
366                         }
367                 }
368         }
369
370         DBPRINTF ("get_kprobe: probe %p", retVal);
371         return retVal;
372 }
373
374