[FIX] arch includes for kprobe, uprobe, ks_{manager,feature}
[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-2012
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  * 2012-2013    Vyacheslav Cherkashin <v.cherkashin@samsung.com> new memory allocator for slots
46  */
47
48 #include "dbi_insn_slots.h"
49 #include <linux/module.h>
50 #include <linux/rculist.h>
51 #include <linux/slab.h>
52 #include <linux/spinlock.h>
53 #include <kprobe/dbi_kprobes_deps.h>
54
55 struct chunk {
56         unsigned long *data;
57         unsigned long first_available;
58         unsigned long count_available;
59
60         spinlock_t    lock;
61         unsigned long size;
62         unsigned long *index;
63 };
64
65 struct fixed_alloc
66 {
67         struct hlist_node hlist;
68         struct chunk chunk;
69 };
70
71 static void chunk_init(struct chunk *chunk, void *data, size_t size, size_t size_block)
72 {
73         unsigned long i;
74         unsigned long *p;
75
76         spin_lock_init(&chunk->lock);
77         chunk->data = (unsigned long *)data;
78         chunk->first_available = 0;
79         chunk->count_available = size / size_block;
80         chunk->size = chunk->count_available;
81
82         chunk->index = kmalloc(sizeof(*chunk->index)*chunk->count_available, GFP_ATOMIC);
83
84         p = chunk->index;
85         for (i = 0; i != chunk->count_available; ++p) {
86                 *p = ++i;
87         }
88 }
89
90 static void chunk_uninit(struct chunk *chunk)
91 {
92         kfree(chunk->index);
93 }
94
95 static void* chunk_allocate(struct chunk *chunk, size_t size_block)
96 {
97         unsigned long *ret;
98
99         if (!chunk->count_available) {
100                 return NULL;
101         }
102
103         spin_lock(&chunk->lock);
104         ret = chunk->data + chunk->first_available*size_block;
105         chunk->first_available = chunk->index[chunk->first_available];
106         --chunk->count_available;
107         spin_unlock(&chunk->lock);
108
109         return ret;
110 }
111
112 static void chunk_deallocate(struct chunk *chunk, void *p, size_t size_block)
113 {
114         unsigned long idx = ((unsigned long *)p - chunk->data)/size_block;
115
116         spin_lock(&chunk->lock);
117         chunk->index[idx] = chunk->first_available;
118         chunk->first_available = idx;
119         ++chunk->count_available;
120         spin_unlock(&chunk->lock);
121 }
122
123 static inline int chunk_check_ptr(struct chunk *chunk, void *p, size_t size)
124 {
125         if (( chunk->data                             <= (unsigned long *)p) &&
126             ((chunk->data + size/sizeof(chunk->data))  > (unsigned long *)p)) {
127                 return 1;
128         }
129
130         return 0;
131 }
132
133 static inline int chunk_free(struct chunk *chunk)
134 {
135         return (chunk->count_available == chunk->size);
136 }
137
138 static struct fixed_alloc *create_fixed_alloc(struct slot_manager *sm)
139 {
140         void *data;
141         struct fixed_alloc *fa;
142
143         fa = kmalloc(sizeof(*fa), GFP_ATOMIC);
144         if (fa == NULL) {
145                 return NULL;
146         }
147
148         data = sm->alloc(sm);
149         if(data == NULL) {
150                 kfree(fa);
151                 return NULL;
152         }
153
154         chunk_init(&fa->chunk, data, PAGE_SIZE/sizeof(unsigned long), sm->slot_size);
155
156         return fa;
157 }
158
159 static void free_fixed_alloc(struct slot_manager *sm, struct fixed_alloc *fa)
160 {
161         chunk_uninit(&fa->chunk);
162         sm->free(sm, fa->chunk.data);
163         kfree(fa);
164 }
165
166
167 void *alloc_insn_slot(struct slot_manager *sm)
168 {
169         void *free_slot;
170         struct fixed_alloc *fa;
171         struct hlist_node *pos;
172
173         swap_hlist_for_each_entry_rcu(fa, pos, &sm->page_list, hlist) {
174                 free_slot = chunk_allocate(&fa->chunk, sm->slot_size);
175                 if (free_slot)
176                         return free_slot;
177         }
178
179         fa = create_fixed_alloc(sm);
180         if(fa == NULL)
181                 return NULL;
182
183         INIT_HLIST_NODE(&fa->hlist);
184         hlist_add_head_rcu(&fa->hlist, &sm->page_list);
185
186         return chunk_allocate(&fa->chunk, sm->slot_size);
187 }
188 EXPORT_SYMBOL_GPL(alloc_insn_slot);
189
190 void free_insn_slot(struct slot_manager *sm, void *slot)
191 {
192         struct fixed_alloc *fa;
193         struct hlist_node *pos;
194
195         swap_hlist_for_each_entry_rcu(fa, pos, &sm->page_list, hlist) {
196                 if (!chunk_check_ptr(&fa->chunk, slot, PAGE_SIZE))
197                         continue;
198
199                 chunk_deallocate(&fa->chunk, slot, sm->slot_size);
200
201                 if (chunk_free(&fa->chunk)) {
202                         hlist_del_rcu(&fa->hlist);
203                         free_fixed_alloc(sm, fa);
204                 }
205
206                 return;
207         }
208
209         panic("free_insn_slot: slot=%p is not data base\n", slot);
210 }
211 EXPORT_SYMBOL_GPL(free_insn_slot);