diff options
Diffstat (limited to 'kernel/kprobes.c')
-rw-r--r-- | kernel/kprobes.c | 75 |
1 files changed, 21 insertions, 54 deletions
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 6e33498d665c..9e4912dc5559 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
@@ -121,12 +121,6 @@ struct kprobe_insn_page { | |||
121 | (offsetof(struct kprobe_insn_page, slot_used) + \ | 121 | (offsetof(struct kprobe_insn_page, slot_used) + \ |
122 | (sizeof(char) * (slots))) | 122 | (sizeof(char) * (slots))) |
123 | 123 | ||
124 | struct kprobe_insn_cache { | ||
125 | struct list_head pages; /* list of kprobe_insn_page */ | ||
126 | size_t insn_size; /* size of instruction slot */ | ||
127 | int nr_garbage; | ||
128 | }; | ||
129 | |||
130 | static int slots_per_page(struct kprobe_insn_cache *c) | 124 | static int slots_per_page(struct kprobe_insn_cache *c) |
131 | { | 125 | { |
132 | return PAGE_SIZE/(c->insn_size * sizeof(kprobe_opcode_t)); | 126 | return PAGE_SIZE/(c->insn_size * sizeof(kprobe_opcode_t)); |
@@ -138,8 +132,8 @@ enum kprobe_slot_state { | |||
138 | SLOT_USED = 2, | 132 | SLOT_USED = 2, |
139 | }; | 133 | }; |
140 | 134 | ||
141 | static DEFINE_MUTEX(kprobe_insn_mutex); /* Protects kprobe_insn_slots */ | 135 | struct kprobe_insn_cache kprobe_insn_slots = { |
142 | static struct kprobe_insn_cache kprobe_insn_slots = { | 136 | .mutex = __MUTEX_INITIALIZER(kprobe_insn_slots.mutex), |
143 | .pages = LIST_HEAD_INIT(kprobe_insn_slots.pages), | 137 | .pages = LIST_HEAD_INIT(kprobe_insn_slots.pages), |
144 | .insn_size = MAX_INSN_SIZE, | 138 | .insn_size = MAX_INSN_SIZE, |
145 | .nr_garbage = 0, | 139 | .nr_garbage = 0, |
@@ -150,10 +144,12 @@ static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c); | |||
150 | * __get_insn_slot() - Find a slot on an executable page for an instruction. | 144 | * __get_insn_slot() - Find a slot on an executable page for an instruction. |
151 | * We allocate an executable page if there's no room on existing ones. | 145 | * We allocate an executable page if there's no room on existing ones. |
152 | */ | 146 | */ |
153 | static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c) | 147 | kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c) |
154 | { | 148 | { |
155 | struct kprobe_insn_page *kip; | 149 | struct kprobe_insn_page *kip; |
150 | kprobe_opcode_t *slot = NULL; | ||
156 | 151 | ||
152 | mutex_lock(&c->mutex); | ||
157 | retry: | 153 | retry: |
158 | list_for_each_entry(kip, &c->pages, list) { | 154 | list_for_each_entry(kip, &c->pages, list) { |
159 | if (kip->nused < slots_per_page(c)) { | 155 | if (kip->nused < slots_per_page(c)) { |
@@ -162,7 +158,8 @@ static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c) | |||
162 | if (kip->slot_used[i] == SLOT_CLEAN) { | 158 | if (kip->slot_used[i] == SLOT_CLEAN) { |
163 | kip->slot_used[i] = SLOT_USED; | 159 | kip->slot_used[i] = SLOT_USED; |
164 | kip->nused++; | 160 | kip->nused++; |
165 | return kip->insns + (i * c->insn_size); | 161 | slot = kip->insns + (i * c->insn_size); |
162 | goto out; | ||
166 | } | 163 | } |
167 | } | 164 | } |
168 | /* kip->nused is broken. Fix it. */ | 165 | /* kip->nused is broken. Fix it. */ |
@@ -178,7 +175,7 @@ static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c) | |||
178 | /* All out of space. Need to allocate a new page. */ | 175 | /* All out of space. Need to allocate a new page. */ |
179 | kip = kmalloc(KPROBE_INSN_PAGE_SIZE(slots_per_page(c)), GFP_KERNEL); | 176 | kip = kmalloc(KPROBE_INSN_PAGE_SIZE(slots_per_page(c)), GFP_KERNEL); |
180 | if (!kip) | 177 | if (!kip) |
181 | return NULL; | 178 | goto out; |
182 | 179 | ||
183 | /* | 180 | /* |
184 | * Use module_alloc so this page is within +/- 2GB of where the | 181 | * Use module_alloc so this page is within +/- 2GB of where the |
@@ -188,7 +185,7 @@ static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c) | |||
188 | kip->insns = module_alloc(PAGE_SIZE); | 185 | kip->insns = module_alloc(PAGE_SIZE); |
189 | if (!kip->insns) { | 186 | if (!kip->insns) { |
190 | kfree(kip); | 187 | kfree(kip); |
191 | return NULL; | 188 | goto out; |
192 | } | 189 | } |
193 | INIT_LIST_HEAD(&kip->list); | 190 | INIT_LIST_HEAD(&kip->list); |
194 | memset(kip->slot_used, SLOT_CLEAN, slots_per_page(c)); | 191 | memset(kip->slot_used, SLOT_CLEAN, slots_per_page(c)); |
@@ -196,19 +193,10 @@ static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c) | |||
196 | kip->nused = 1; | 193 | kip->nused = 1; |
197 | kip->ngarbage = 0; | 194 | kip->ngarbage = 0; |
198 | list_add(&kip->list, &c->pages); | 195 | list_add(&kip->list, &c->pages); |
199 | return kip->insns; | 196 | slot = kip->insns; |
200 | } | 197 | out: |
201 | 198 | mutex_unlock(&c->mutex); | |
202 | 199 | return slot; | |
203 | kprobe_opcode_t __kprobes *get_insn_slot(void) | ||
204 | { | ||
205 | kprobe_opcode_t *ret = NULL; | ||
206 | |||
207 | mutex_lock(&kprobe_insn_mutex); | ||
208 | ret = __get_insn_slot(&kprobe_insn_slots); | ||
209 | mutex_unlock(&kprobe_insn_mutex); | ||
210 | |||
211 | return ret; | ||
212 | } | 200 | } |
213 | 201 | ||
214 | /* Return 1 if all garbages are collected, otherwise 0. */ | 202 | /* Return 1 if all garbages are collected, otherwise 0. */ |
@@ -255,11 +243,12 @@ static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c) | |||
255 | return 0; | 243 | return 0; |
256 | } | 244 | } |
257 | 245 | ||
258 | static void __kprobes __free_insn_slot(struct kprobe_insn_cache *c, | 246 | void __kprobes __free_insn_slot(struct kprobe_insn_cache *c, |
259 | kprobe_opcode_t *slot, int dirty) | 247 | kprobe_opcode_t *slot, int dirty) |
260 | { | 248 | { |
261 | struct kprobe_insn_page *kip; | 249 | struct kprobe_insn_page *kip; |
262 | 250 | ||
251 | mutex_lock(&c->mutex); | ||
263 | list_for_each_entry(kip, &c->pages, list) { | 252 | list_for_each_entry(kip, &c->pages, list) { |
264 | long idx = ((long)slot - (long)kip->insns) / | 253 | long idx = ((long)slot - (long)kip->insns) / |
265 | (c->insn_size * sizeof(kprobe_opcode_t)); | 254 | (c->insn_size * sizeof(kprobe_opcode_t)); |
@@ -272,45 +261,23 @@ static void __kprobes __free_insn_slot(struct kprobe_insn_cache *c, | |||
272 | collect_garbage_slots(c); | 261 | collect_garbage_slots(c); |
273 | } else | 262 | } else |
274 | collect_one_slot(kip, idx); | 263 | collect_one_slot(kip, idx); |
275 | return; | 264 | goto out; |
276 | } | 265 | } |
277 | } | 266 | } |
278 | /* Could not free this slot. */ | 267 | /* Could not free this slot. */ |
279 | WARN_ON(1); | 268 | WARN_ON(1); |
269 | out: | ||
270 | mutex_unlock(&c->mutex); | ||
280 | } | 271 | } |
281 | 272 | ||
282 | void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty) | ||
283 | { | ||
284 | mutex_lock(&kprobe_insn_mutex); | ||
285 | __free_insn_slot(&kprobe_insn_slots, slot, dirty); | ||
286 | mutex_unlock(&kprobe_insn_mutex); | ||
287 | } | ||
288 | #ifdef CONFIG_OPTPROBES | 273 | #ifdef CONFIG_OPTPROBES |
289 | /* For optimized_kprobe buffer */ | 274 | /* For optimized_kprobe buffer */ |
290 | static DEFINE_MUTEX(kprobe_optinsn_mutex); /* Protects kprobe_optinsn_slots */ | 275 | struct kprobe_insn_cache kprobe_optinsn_slots = { |
291 | static struct kprobe_insn_cache kprobe_optinsn_slots = { | 276 | .mutex = __MUTEX_INITIALIZER(kprobe_optinsn_slots.mutex), |
292 | .pages = LIST_HEAD_INIT(kprobe_optinsn_slots.pages), | 277 | .pages = LIST_HEAD_INIT(kprobe_optinsn_slots.pages), |
293 | /* .insn_size is initialized later */ | 278 | /* .insn_size is initialized later */ |
294 | .nr_garbage = 0, | 279 | .nr_garbage = 0, |
295 | }; | 280 | }; |
296 | /* Get a slot for optimized_kprobe buffer */ | ||
297 | kprobe_opcode_t __kprobes *get_optinsn_slot(void) | ||
298 | { | ||
299 | kprobe_opcode_t *ret = NULL; | ||
300 | |||
301 | mutex_lock(&kprobe_optinsn_mutex); | ||
302 | ret = __get_insn_slot(&kprobe_optinsn_slots); | ||
303 | mutex_unlock(&kprobe_optinsn_mutex); | ||
304 | |||
305 | return ret; | ||
306 | } | ||
307 | |||
308 | void __kprobes free_optinsn_slot(kprobe_opcode_t * slot, int dirty) | ||
309 | { | ||
310 | mutex_lock(&kprobe_optinsn_mutex); | ||
311 | __free_insn_slot(&kprobe_optinsn_slots, slot, dirty); | ||
312 | mutex_unlock(&kprobe_optinsn_mutex); | ||
313 | } | ||
314 | #endif | 281 | #endif |
315 | #endif | 282 | #endif |
316 | 283 | ||