2 * Kernel Probes (KProbes)
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.
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.
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.
19 * Copyright (C) IBM Corporation, 2002, 2004
23 * Dynamic Binary Instrumentation Module based on KProbes
24 * modules/kprobe/dbi_insn_slots.c
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.
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.
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.
40 * Copyright (C) Samsung Electronics, 2006-2010
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
49 #include "dbi_insn_slots.h"
50 #include "dbi_uprobes.h"
51 #include "dbi_kdebug.h"
53 #include <linux/hash.h>
54 #include <linux/mman.h>
55 #include <linux/hugetlb.h>
58 extern struct hlist_head uprobe_insn_slot_table[KPROBE_TABLE_SIZE];
60 struct hlist_head kprobe_insn_pages;
61 int kprobe_garbage_slots;
62 struct hlist_head uprobe_insn_pages;
63 int uprobe_garbage_slots;
66 struct kprobe_insn_page
68 struct hlist_node hlist;
69 kprobe_opcode_t *insns; /* Page of instruction slots */
76 enum kprobe_slot_state
84 unsigned long alloc_user_pages(struct task_struct *task, unsigned long len, unsigned long prot, unsigned long flags, int atomic)
87 struct task_struct *otask = current;
90 mm = atomic ? task->active_mm : get_task_mm (task);
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;
99 up_write (&mm->mmap_sem);
104 printk ("proc %d has no mm", task->pid);
105 return (unsigned long)ret;
111 synchronize_sched ();
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.
120 kprobe_opcode_t *get_insn_slot (struct task_struct *task, int atomic)
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;
128 slots_per_page = INSNS_PER_PAGE/UPROBES_TRAMP_LEN;
129 slot_size = UPROBES_TRAMP_LEN;
132 slots_per_page = INSNS_PER_PAGE/KPROBES_TRAMP_LEN;
133 slot_size = KPROBES_TRAMP_LEN;
137 hlist_for_each_entry (kip, pos, page_list, hlist)
139 if (kip->nused < slots_per_page)
142 for (i = 0; i < slots_per_page; i++)
144 if (kip->slot_used[i] == SLOT_CLEAN)
146 if(!task || (kip->tgid == task->tgid)){
147 kip->slot_used[i] = SLOT_USED;
149 return kip->insns + (i * slot_size);
153 /* Surprise! No unused slots. Fix kip->nused. */
154 kip->nused = slots_per_page;
158 /* If there are any garbage slots, collect it and try again. */
160 if (uprobe_garbage_slots && collect_garbage_slots(page_list, task) == 0)
164 if (kprobe_garbage_slots && collect_garbage_slots(page_list, task) == 0)
168 /* All out of space. Need to allocate a new page. Use slot 0. */
169 kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_KERNEL);
173 kip->slot_used = kmalloc(sizeof(char)*slots_per_page, GFP_KERNEL);
174 if (!kip->slot_used){
180 kip->insns = (kprobe_opcode_t *)alloc_user_pages(task, PAGE_SIZE,
181 PROT_EXEC|PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, atomic);
184 kip->insns = kmalloc(PAGE_SIZE, GFP_KERNEL);
188 kfree (kip->slot_used);
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;
198 kip->tgid = task ? task->tgid : 0;
202 /* Return 1 if all garbages are collected, otherwise 0. */
204 int collect_one_slot (struct hlist_head *page_list, struct task_struct *task,
205 struct kprobe_insn_page *kip, int idx)
207 kip->slot_used[idx] = SLOT_CLEAN;
209 DBPRINTF("collect_one_slot: nused=%d", kip->nused);
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.
218 hlist_del (&kip->hlist);
219 if (!task && hlist_empty (page_list))
221 INIT_HLIST_NODE (&kip->hlist);
222 hlist_add_head (&kip->hlist, page_list);
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.
233 //printk("collect_one_slot %p/%d\n", task, task->pid);
234 mm = get_task_mm (task);
236 down_write (&mm->mmap_sem);
237 do_munmap(mm, (unsigned long)(kip->insns), PAGE_SIZE);
238 up_write (&mm->mmap_sem);
242 kip->insns = NULL; //workaround
248 kfree (kip->slot_used);
256 int collect_garbage_slots (struct hlist_head *page_list, struct task_struct *task)
258 struct kprobe_insn_page *kip;
259 struct hlist_node *pos, *next;
260 unsigned slots_per_page = INSNS_PER_PAGE;
262 /* Ensure no-one is preepmted on the garbages */
263 if (!task && check_safety() != 0)
267 slots_per_page = INSNS_PER_PAGE/UPROBES_TRAMP_LEN;
269 slots_per_page = INSNS_PER_PAGE/KPROBES_TRAMP_LEN;
271 hlist_for_each_entry_safe (kip, pos, next, page_list, hlist)
274 if ((task && (kip->tgid != task->tgid)) || (kip->ngarbage == 0))
276 kip->ngarbage = 0; /* we will collect all garbages */
277 for (i = 0; i < slots_per_page; i++)
279 if (kip->slot_used[i] == SLOT_DIRTY && collect_one_slot (page_list, task, kip, i))
283 if(task) uprobe_garbage_slots = 0;
284 else kprobe_garbage_slots = 0;
288 void purge_garbage_uslots(struct task_struct *task, int atomic)
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);
294 void free_insn_slot (struct hlist_head *page_list, struct task_struct *task, kprobe_opcode_t *slot, int dirty)
296 struct kprobe_insn_page *kip;
297 struct hlist_node *pos;
298 unsigned slots_per_page = INSNS_PER_PAGE, slot_size = MAX_INSN_SIZE;
301 slots_per_page = INSNS_PER_PAGE/UPROBES_TRAMP_LEN;
302 slot_size = UPROBES_TRAMP_LEN;
305 slots_per_page = INSNS_PER_PAGE/KPROBES_TRAMP_LEN;
306 slot_size = KPROBES_TRAMP_LEN;
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)
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)))
315 int i = (slot - kip->insns) / slot_size;
318 kip->slot_used[i] = SLOT_DIRTY;
323 collect_one_slot (page_list, task, kip, i);
331 if(++uprobe_garbage_slots > slots_per_page)
332 collect_garbage_slots (page_list, task);
334 else if(++kprobe_garbage_slots > slots_per_page)
335 collect_garbage_slots (page_list, task);
339 struct kprobe *get_kprobe_by_insn_slot (void *addr, int tgid, struct task_struct *ctask)
341 struct hlist_head *head;
342 struct hlist_node *node;
343 struct kprobe *p, *retVal = NULL;
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)
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)
358 if (!tgid || uprobe_found)
362 DBPRINTF ("get_kprobe: found user space probe at %p for task %d", p->addr, p->tgid);
364 DBPRINTF ("get_kprobe: found kernel probe at %p", p->addr);
370 DBPRINTF ("get_kprobe: probe %p", retVal);