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; |
