diff options
-rw-r--r-- | include/linux/kprobes.h | 32 | ||||
-rw-r--r-- | kernel/kprobes.c | 75 |
2 files changed, 49 insertions, 58 deletions
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index ca1d27a0d6a6..077f65321b5e 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h | |||
@@ -264,10 +264,34 @@ extern void arch_arm_kprobe(struct kprobe *p); | |||
264 | extern void arch_disarm_kprobe(struct kprobe *p); | 264 | extern void arch_disarm_kprobe(struct kprobe *p); |
265 | extern int arch_init_kprobes(void); | 265 | extern int arch_init_kprobes(void); |
266 | extern void show_registers(struct pt_regs *regs); | 266 | extern void show_registers(struct pt_regs *regs); |
267 | extern kprobe_opcode_t *get_insn_slot(void); | ||
268 | extern void free_insn_slot(kprobe_opcode_t *slot, int dirty); | ||
269 | extern void kprobes_inc_nmissed_count(struct kprobe *p); | 267 | extern void kprobes_inc_nmissed_count(struct kprobe *p); |
270 | 268 | ||
269 | struct kprobe_insn_cache { | ||
270 | struct mutex mutex; | ||
271 | struct list_head pages; /* list of kprobe_insn_page */ | ||
272 | size_t insn_size; /* size of instruction slot */ | ||
273 | int nr_garbage; | ||
274 | }; | ||
275 | |||
276 | extern kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c); | ||
277 | extern void __free_insn_slot(struct kprobe_insn_cache *c, | ||
278 | kprobe_opcode_t *slot, int dirty); | ||
279 | |||
280 | #define DEFINE_INSN_CACHE_OPS(__name) \ | ||
281 | extern struct kprobe_insn_cache kprobe_##__name##_slots; \ | ||
282 | \ | ||
283 | static inline kprobe_opcode_t *get_##__name##_slot(void) \ | ||
284 | { \ | ||
285 | return __get_insn_slot(&kprobe_##__name##_slots); \ | ||
286 | } \ | ||
287 | \ | ||
288 | static inline void free_##__name##_slot(kprobe_opcode_t *slot, int dirty)\ | ||
289 | { \ | ||
290 | __free_insn_slot(&kprobe_##__name##_slots, slot, dirty); \ | ||
291 | } \ | ||
292 | |||
293 | DEFINE_INSN_CACHE_OPS(insn); | ||
294 | |||
271 | #ifdef CONFIG_OPTPROBES | 295 | #ifdef CONFIG_OPTPROBES |
272 | /* | 296 | /* |
273 | * Internal structure for direct jump optimized probe | 297 | * Internal structure for direct jump optimized probe |
@@ -287,13 +311,13 @@ extern void arch_optimize_kprobes(struct list_head *oplist); | |||
287 | extern void arch_unoptimize_kprobes(struct list_head *oplist, | 311 | extern void arch_unoptimize_kprobes(struct list_head *oplist, |
288 | struct list_head *done_list); | 312 | struct list_head *done_list); |
289 | extern void arch_unoptimize_kprobe(struct optimized_kprobe *op); | 313 | extern void arch_unoptimize_kprobe(struct optimized_kprobe *op); |
290 | extern kprobe_opcode_t *get_optinsn_slot(void); | ||
291 | extern void free_optinsn_slot(kprobe_opcode_t *slot, int dirty); | ||
292 | extern int arch_within_optimized_kprobe(struct optimized_kprobe *op, | 314 | extern int arch_within_optimized_kprobe(struct optimized_kprobe *op, |
293 | unsigned long addr); | 315 | unsigned long addr); |
294 | 316 | ||
295 | extern void opt_pre_handler(struct kprobe *p, struct pt_regs *regs); | 317 | extern void opt_pre_handler(struct kprobe *p, struct pt_regs *regs); |
296 | 318 | ||
319 | DEFINE_INSN_CACHE_OPS(optinsn); | ||
320 | |||
297 | #ifdef CONFIG_SYSCTL | 321 | #ifdef CONFIG_SYSCTL |
298 | extern int sysctl_kprobes_optimization; | 322 | extern int sysctl_kprobes_optimization; |
299 | extern int proc_kprobes_optimization_handler(struct ctl_table *table, | 323 | extern int proc_kprobes_optimization_handler(struct ctl_table *table, |
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 | ||