diff options
Diffstat (limited to 'arch/x86/kernel/ftrace.c')
| -rw-r--r-- | arch/x86/kernel/ftrace.c | 500 |
1 files changed, 332 insertions, 168 deletions
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index c9a281f272fd..32ff36596ab1 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
| @@ -24,40 +24,21 @@ | |||
| 24 | #include <trace/syscall.h> | 24 | #include <trace/syscall.h> |
| 25 | 25 | ||
| 26 | #include <asm/cacheflush.h> | 26 | #include <asm/cacheflush.h> |
| 27 | #include <asm/kprobes.h> | ||
| 27 | #include <asm/ftrace.h> | 28 | #include <asm/ftrace.h> |
| 28 | #include <asm/nops.h> | 29 | #include <asm/nops.h> |
| 29 | #include <asm/nmi.h> | ||
| 30 | |||
| 31 | 30 | ||
| 32 | #ifdef CONFIG_DYNAMIC_FTRACE | 31 | #ifdef CONFIG_DYNAMIC_FTRACE |
| 33 | 32 | ||
| 34 | /* | ||
| 35 | * modifying_code is set to notify NMIs that they need to use | ||
| 36 | * memory barriers when entering or exiting. But we don't want | ||
| 37 | * to burden NMIs with unnecessary memory barriers when code | ||
| 38 | * modification is not being done (which is most of the time). | ||
| 39 | * | ||
| 40 | * A mutex is already held when ftrace_arch_code_modify_prepare | ||
| 41 | * and post_process are called. No locks need to be taken here. | ||
| 42 | * | ||
| 43 | * Stop machine will make sure currently running NMIs are done | ||
| 44 | * and new NMIs will see the updated variable before we need | ||
| 45 | * to worry about NMIs doing memory barriers. | ||
| 46 | */ | ||
| 47 | static int modifying_code __read_mostly; | ||
| 48 | static DEFINE_PER_CPU(int, save_modifying_code); | ||
| 49 | |||
| 50 | int ftrace_arch_code_modify_prepare(void) | 33 | int ftrace_arch_code_modify_prepare(void) |
| 51 | { | 34 | { |
| 52 | set_kernel_text_rw(); | 35 | set_kernel_text_rw(); |
| 53 | set_all_modules_text_rw(); | 36 | set_all_modules_text_rw(); |
| 54 | modifying_code = 1; | ||
| 55 | return 0; | 37 | return 0; |
| 56 | } | 38 | } |
| 57 | 39 | ||
| 58 | int ftrace_arch_code_modify_post_process(void) | 40 | int ftrace_arch_code_modify_post_process(void) |
| 59 | { | 41 | { |
| 60 | modifying_code = 0; | ||
| 61 | set_all_modules_text_ro(); | 42 | set_all_modules_text_ro(); |
| 62 | set_kernel_text_ro(); | 43 | set_kernel_text_ro(); |
| 63 | return 0; | 44 | return 0; |
| @@ -90,134 +71,6 @@ static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr) | |||
| 90 | return calc.code; | 71 | return calc.code; |
| 91 | } | 72 | } |
| 92 | 73 | ||
| 93 | /* | ||
| 94 | * Modifying code must take extra care. On an SMP machine, if | ||
| 95 | * the code being modified is also being executed on another CPU | ||
| 96 | * that CPU will have undefined results and possibly take a GPF. | ||
| 97 | * We use kstop_machine to stop other CPUS from exectuing code. | ||
| 98 | * But this does not stop NMIs from happening. We still need | ||
| 99 | * to protect against that. We separate out the modification of | ||
| 100 | * the code to take care of this. | ||
| 101 | * | ||
| 102 | * Two buffers are added: An IP buffer and a "code" buffer. | ||
| 103 | * | ||
| 104 | * 1) Put the instruction pointer into the IP buffer | ||
| 105 | * and the new code into the "code" buffer. | ||
| 106 | * 2) Wait for any running NMIs to finish and set a flag that says | ||
| 107 | * we are modifying code, it is done in an atomic operation. | ||
| 108 | * 3) Write the code | ||
| 109 | * 4) clear the flag. | ||
| 110 | * 5) Wait for any running NMIs to finish. | ||
| 111 | * | ||
| 112 | * If an NMI is executed, the first thing it does is to call | ||
| 113 | * "ftrace_nmi_enter". This will check if the flag is set to write | ||
| 114 | * and if it is, it will write what is in the IP and "code" buffers. | ||
| 115 | * | ||
| 116 | * The trick is, it does not matter if everyone is writing the same | ||
| 117 | * content to the code location. Also, if a CPU is executing code | ||
| 118 | * it is OK to write to that code location if the contents being written | ||
| 119 | * are the same as what exists. | ||
| 120 | */ | ||
| 121 | |||
| 122 | #define MOD_CODE_WRITE_FLAG (1 << 31) /* set when NMI should do the write */ | ||
| 123 | static atomic_t nmi_running = ATOMIC_INIT(0); | ||
| 124 | static int mod_code_status; /* holds return value of text write */ | ||
| 125 | static void *mod_code_ip; /* holds the IP to write to */ | ||
| 126 | static const void *mod_code_newcode; /* holds the text to write to the IP */ | ||
| 127 | |||
| 128 | static unsigned nmi_wait_count; | ||
| 129 | static atomic_t nmi_update_count = ATOMIC_INIT(0); | ||
| 130 | |||
| 131 | int ftrace_arch_read_dyn_info(char *buf, int size) | ||
| 132 | { | ||
| 133 | int r; | ||
| 134 | |||
| 135 | r = snprintf(buf, size, "%u %u", | ||
| 136 | nmi_wait_count, | ||
| 137 | atomic_read(&nmi_update_count)); | ||
| 138 | return r; | ||
| 139 | } | ||
| 140 | |||
| 141 | static void clear_mod_flag(void) | ||
| 142 | { | ||
| 143 | int old = atomic_read(&nmi_running); | ||
| 144 | |||
| 145 | for (;;) { | ||
| 146 | int new = old & ~MOD_CODE_WRITE_FLAG; | ||
| 147 | |||
| 148 | if (old == new) | ||
| 149 | break; | ||
| 150 | |||
| 151 | old = atomic_cmpxchg(&nmi_running, old, new); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | static void ftrace_mod_code(void) | ||
| 156 | { | ||
| 157 | /* | ||
| 158 | * Yes, more than one CPU process can be writing to mod_code_status. | ||
| 159 | * (and the code itself) | ||
| 160 | * But if one were to fail, then they all should, and if one were | ||
| 161 | * to succeed, then they all should. | ||
| 162 | */ | ||
| 163 | mod_code_status = probe_kernel_write(mod_code_ip, mod_code_newcode, | ||
| 164 | MCOUNT_INSN_SIZE); | ||
| 165 | |||
| 166 | /* if we fail, then kill any new writers */ | ||
| 167 | if (mod_code_status) | ||
| 168 | clear_mod_flag(); | ||
| 169 | } | ||
| 170 | |||
| 171 | void ftrace_nmi_enter(void) | ||
| 172 | { | ||
| 173 | __this_cpu_write(save_modifying_code, modifying_code); | ||
| 174 | |||
| 175 | if (!__this_cpu_read(save_modifying_code)) | ||
| 176 | return; | ||
| 177 | |||
| 178 | if (atomic_inc_return(&nmi_running) & MOD_CODE_WRITE_FLAG) { | ||
| 179 | smp_rmb(); | ||
| 180 | ftrace_mod_code(); | ||
| 181 | atomic_inc(&nmi_update_count); | ||
| 182 | } | ||
| 183 | /* Must have previous changes seen before executions */ | ||
| 184 | smp_mb(); | ||
| 185 | } | ||
| 186 | |||
| 187 | void ftrace_nmi_exit(void) | ||
| 188 | { | ||
| 189 | if (!__this_cpu_read(save_modifying_code)) | ||
| 190 | return; | ||
| 191 | |||
| 192 | /* Finish all executions before clearing nmi_running */ | ||
| 193 | smp_mb(); | ||
| 194 | atomic_dec(&nmi_running); | ||
| 195 | } | ||
| 196 | |||
| 197 | static void wait_for_nmi_and_set_mod_flag(void) | ||
| 198 | { | ||
| 199 | if (!atomic_cmpxchg(&nmi_running, 0, MOD_CODE_WRITE_FLAG)) | ||
| 200 | return; | ||
| 201 | |||
| 202 | do { | ||
| 203 | cpu_relax(); | ||
| 204 | } while (atomic_cmpxchg(&nmi_running, 0, MOD_CODE_WRITE_FLAG)); | ||
| 205 | |||
| 206 | nmi_wait_count++; | ||
| 207 | } | ||
| 208 | |||
| 209 | static void wait_for_nmi(void) | ||
| 210 | { | ||
| 211 | if (!atomic_read(&nmi_running)) | ||
| 212 | return; | ||
| 213 | |||
| 214 | do { | ||
| 215 | cpu_relax(); | ||
| 216 | } while (atomic_read(&nmi_running)); | ||
| 217 | |||
| 218 | nmi_wait_count++; | ||
| 219 | } | ||
| 220 | |||
| 221 | static inline int | 74 | static inline int |
| 222 | within(unsigned long addr, unsigned long start, unsigned long end) | 75 | within(unsigned long addr, unsigned long start, unsigned long end) |
| 223 | { | 76 | { |
| @@ -238,26 +91,7 @@ do_ftrace_mod_code(unsigned long ip, const void *new_code) | |||
| 238 | if (within(ip, (unsigned long)_text, (unsigned long)_etext)) | 91 | if (within(ip, (unsigned long)_text, (unsigned long)_etext)) |
| 239 | ip = (unsigned long)__va(__pa(ip)); | 92 | ip = (unsigned long)__va(__pa(ip)); |
| 240 | 93 | ||
| 241 | mod_code_ip = (void *)ip; | 94 | return probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE); |
| 242 | mod_code_newcode = new_code; | ||
| 243 | |||
| 244 | /* The buffers need to be visible before we let NMIs write them */ | ||
| 245 | smp_mb(); | ||
| 246 | |||
| 247 | wait_for_nmi_and_set_mod_flag(); | ||
| 248 | |||
| 249 | /* Make sure all running NMIs have finished before we write the code */ | ||
| 250 | smp_mb(); | ||
| 251 | |||
| 252 | ftrace_mod_code(); | ||
| 253 | |||
| 254 | /* Make sure the write happens before clearing the bit */ | ||
| 255 | smp_mb(); | ||
| 256 | |||
| 257 | clear_mod_flag(); | ||
| 258 | wait_for_nmi(); | ||
| 259 | |||
| 260 | return mod_code_status; | ||
| 261 | } | 95 | } |
| 262 | 96 | ||
| 263 | static const unsigned char *ftrace_nop_replace(void) | 97 | static const unsigned char *ftrace_nop_replace(void) |
| @@ -334,6 +168,336 @@ int ftrace_update_ftrace_func(ftrace_func_t func) | |||
| 334 | return ret; | 168 | return ret; |
| 335 | } | 169 | } |
| 336 | 170 | ||
| 171 | int modifying_ftrace_code __read_mostly; | ||
| 172 | |||
| 173 | /* | ||
| 174 | * A breakpoint was added to the code address we are about to | ||
| 175 | * modify, and this is the handle that will just skip over it. | ||
| 176 | * We are either changing a nop into a trace call, or a trace | ||
| 177 | * call to a nop. While the change is taking place, we treat | ||
| 178 | * it just like it was a nop. | ||
| 179 | */ | ||
| 180 | int ftrace_int3_handler(struct pt_regs *regs) | ||
| 181 | { | ||
| 182 | if (WARN_ON_ONCE(!regs)) | ||
| 183 | return 0; | ||
| 184 | |||
| 185 | if (!ftrace_location(regs->ip - 1)) | ||
| 186 | return 0; | ||
| 187 | |||
| 188 | regs->ip += MCOUNT_INSN_SIZE - 1; | ||
| 189 | |||
| 190 | return 1; | ||
| 191 | } | ||
| 192 | |||
| 193 | static int ftrace_write(unsigned long ip, const char *val, int size) | ||
| 194 | { | ||
| 195 | /* | ||
| 196 | * On x86_64, kernel text mappings are mapped read-only with | ||
| 197 | * CONFIG_DEBUG_RODATA. So we use the kernel identity mapping instead | ||
| 198 | * of the kernel text mapping to modify the kernel text. | ||
| 199 | * | ||
| 200 | * For 32bit kernels, these mappings are same and we can use | ||
| 201 | * kernel identity mapping to modify code. | ||
| 202 | */ | ||
| 203 | if (within(ip, (unsigned long)_text, (unsigned long)_etext)) | ||
| 204 | ip = (unsigned long)__va(__pa(ip)); | ||
| 205 | |||
| 206 | return probe_kernel_write((void *)ip, val, size); | ||
| 207 | } | ||
| 208 | |||
| 209 | static int add_break(unsigned long ip, const char *old) | ||
| 210 | { | ||
| 211 | unsigned char replaced[MCOUNT_INSN_SIZE]; | ||
| 212 | unsigned char brk = BREAKPOINT_INSTRUCTION; | ||
| 213 | |||
| 214 | if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) | ||
| 215 | return -EFAULT; | ||
| 216 | |||
| 217 | /* Make sure it is what we expect it to be */ | ||
| 218 | if (memcmp(replaced, old, MCOUNT_INSN_SIZE) != 0) | ||
| 219 | return -EINVAL; | ||
| 220 | |||
| 221 | if (ftrace_write(ip, &brk, 1)) | ||
| 222 | return -EPERM; | ||
| 223 | |||
| 224 | return 0; | ||
| 225 | } | ||
| 226 | |||
| 227 | static int add_brk_on_call(struct dyn_ftrace *rec, unsigned long addr) | ||
| 228 | { | ||
| 229 | unsigned const char *old; | ||
| 230 | unsigned long ip = rec->ip; | ||
| 231 | |||
| 232 | old = ftrace_call_replace(ip, addr); | ||
| 233 | |||
| 234 | return add_break(rec->ip, old); | ||
| 235 | } | ||
| 236 | |||
| 237 | |||
| 238 | static int add_brk_on_nop(struct dyn_ftrace *rec) | ||
| 239 | { | ||
| 240 | unsigned const char *old; | ||
| 241 | |||
| 242 | old = ftrace_nop_replace(); | ||
| 243 | |||
| 244 | return add_break(rec->ip, old); | ||
| 245 | } | ||
| 246 | |||
| 247 | static int add_breakpoints(struct dyn_ftrace *rec, int enable) | ||
| 248 | { | ||
| 249 | unsigned long ftrace_addr; | ||
| 250 | int ret; | ||
| 251 | |||
| 252 | ret = ftrace_test_record(rec, enable); | ||
| 253 | |||
| 254 | ftrace_addr = (unsigned long)FTRACE_ADDR; | ||
| 255 | |||
| 256 | switch (ret) { | ||
| 257 | case FTRACE_UPDATE_IGNORE: | ||
| 258 | return 0; | ||
| 259 | |||
| 260 | case FTRACE_UPDATE_MAKE_CALL: | ||
| 261 | /* converting nop to call */ | ||
| 262 | return add_brk_on_nop(rec); | ||
| 263 | |||
| 264 | case FTRACE_UPDATE_MAKE_NOP: | ||
| 265 | /* converting a call to a nop */ | ||
| 266 | return add_brk_on_call(rec, ftrace_addr); | ||
| 267 | } | ||
| 268 | return 0; | ||
| 269 | } | ||
| 270 | |||
| 271 | /* | ||
| 272 | * On error, we need to remove breakpoints. This needs to | ||
| 273 | * be done caefully. If the address does not currently have a | ||
| 274 | * breakpoint, we know we are done. Otherwise, we look at the | ||
| 275 | * remaining 4 bytes of the instruction. If it matches a nop | ||
| 276 | * we replace the breakpoint with the nop. Otherwise we replace | ||
| 277 | * it with the call instruction. | ||
| 278 | */ | ||
| 279 | static int remove_breakpoint(struct dyn_ftrace *rec) | ||
| 280 | { | ||
| 281 | unsigned char ins[MCOUNT_INSN_SIZE]; | ||
| 282 | unsigned char brk = BREAKPOINT_INSTRUCTION; | ||
| 283 | const unsigned char *nop; | ||
| 284 | unsigned long ftrace_addr; | ||
| 285 | unsigned long ip = rec->ip; | ||
| 286 | |||
| 287 | /* If we fail the read, just give up */ | ||
| 288 | if (probe_kernel_read(ins, (void *)ip, MCOUNT_INSN_SIZE)) | ||
| 289 | return -EFAULT; | ||
| 290 | |||
| 291 | /* If this does not have a breakpoint, we are done */ | ||
| 292 | if (ins[0] != brk) | ||
| 293 | return -1; | ||
| 294 | |||
| 295 | nop = ftrace_nop_replace(); | ||
| 296 | |||
| 297 | /* | ||
| 298 | * If the last 4 bytes of the instruction do not match | ||
| 299 | * a nop, then we assume that this is a call to ftrace_addr. | ||
| 300 | */ | ||
| 301 | if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0) { | ||
| 302 | /* | ||
| 303 | * For extra paranoidism, we check if the breakpoint is on | ||
| 304 | * a call that would actually jump to the ftrace_addr. | ||
| 305 | * If not, don't touch the breakpoint, we make just create | ||
| 306 | * a disaster. | ||
| 307 | */ | ||
| 308 | ftrace_addr = (unsigned long)FTRACE_ADDR; | ||
| 309 | nop = ftrace_call_replace(ip, ftrace_addr); | ||
| 310 | |||
| 311 | if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0) | ||
| 312 | return -EINVAL; | ||
| 313 | } | ||
| 314 | |||
| 315 | return probe_kernel_write((void *)ip, &nop[0], 1); | ||
| 316 | } | ||
| 317 | |||
| 318 | static int add_update_code(unsigned long ip, unsigned const char *new) | ||
| 319 | { | ||
| 320 | /* skip breakpoint */ | ||
| 321 | ip++; | ||
| 322 | new++; | ||
| 323 | if (ftrace_write(ip, new, MCOUNT_INSN_SIZE - 1)) | ||
| 324 | return -EPERM; | ||
| 325 | return 0; | ||
| 326 | } | ||
| 327 | |||
| 328 | static int add_update_call(struct dyn_ftrace *rec, unsigned long addr) | ||
| 329 | { | ||
| 330 | unsigned long ip = rec->ip; | ||
| 331 | unsigned const char *new; | ||
| 332 | |||
| 333 | new = ftrace_call_replace(ip, addr); | ||
| 334 | return add_update_code(ip, new); | ||
| 335 | } | ||
| 336 | |||
| 337 | static int add_update_nop(struct dyn_ftrace *rec) | ||
| 338 | { | ||
| 339 | unsigned long ip = rec->ip; | ||
| 340 | unsigned const char *new; | ||
| 341 | |||
| 342 | new = ftrace_nop_replace(); | ||
| 343 | return add_update_code(ip, new); | ||
| 344 | } | ||
| 345 | |||
| 346 | static int add_update(struct dyn_ftrace *rec, int enable) | ||
| 347 | { | ||
| 348 | unsigned long ftrace_addr; | ||
| 349 | int ret; | ||
| 350 | |||
| 351 | ret = ftrace_test_record(rec, enable); | ||
| 352 | |||
| 353 | ftrace_addr = (unsigned long)FTRACE_ADDR; | ||
| 354 | |||
| 355 | switch (ret) { | ||
| 356 | case FTRACE_UPDATE_IGNORE: | ||
| 357 | return 0; | ||
| 358 | |||
| 359 | case FTRACE_UPDATE_MAKE_CALL: | ||
| 360 | /* converting nop to call */ | ||
| 361 | return add_update_call(rec, ftrace_addr); | ||
| 362 | |||
| 363 | case FTRACE_UPDATE_MAKE_NOP: | ||
| 364 | /* converting a call to a nop */ | ||
| 365 | return add_update_nop(rec); | ||
| 366 | } | ||
| 367 | |||
| 368 | return 0; | ||
| 369 | } | ||
| 370 | |||
| 371 | static int finish_update_call(struct dyn_ftrace *rec, unsigned long addr) | ||
| 372 | { | ||
| 373 | unsigned long ip = rec->ip; | ||
| 374 | unsigned const char *new; | ||
| 375 | |||
| 376 | new = ftrace_call_replace(ip, addr); | ||
| 377 | |||
| 378 | if (ftrace_write(ip, new, 1)) | ||
| 379 | return -EPERM; | ||
| 380 | |||
| 381 | return 0; | ||
| 382 | } | ||
| 383 | |||
| 384 | static int finish_update_nop(struct dyn_ftrace *rec) | ||
| 385 | { | ||
| 386 | unsigned long ip = rec->ip; | ||
| 387 | unsigned const char *new; | ||
| 388 | |||
| 389 | new = ftrace_nop_replace(); | ||
| 390 | |||
| 391 | if (ftrace_write(ip, new, 1)) | ||
| 392 | return -EPERM; | ||
| 393 | return 0; | ||
| 394 | } | ||
| 395 | |||
| 396 | static int finish_update(struct dyn_ftrace *rec, int enable) | ||
| 397 | { | ||
| 398 | unsigned long ftrace_addr; | ||
| 399 | int ret; | ||
| 400 | |||
| 401 | ret = ftrace_update_record(rec, enable); | ||
| 402 | |||
| 403 | ftrace_addr = (unsigned long)FTRACE_ADDR; | ||
| 404 | |||
| 405 | switch (ret) { | ||
| 406 | case FTRACE_UPDATE_IGNORE: | ||
| 407 | return 0; | ||
| 408 | |||
| 409 | case FTRACE_UPDATE_MAKE_CALL: | ||
| 410 | /* converting nop to call */ | ||
| 411 | return finish_update_call(rec, ftrace_addr); | ||
| 412 | |||
| 413 | case FTRACE_UPDATE_MAKE_NOP: | ||
| 414 | /* converting a call to a nop */ | ||
| 415 | return finish_update_nop(rec); | ||
| 416 | } | ||
| 417 | |||
| 418 | return 0; | ||
| 419 | } | ||
| 420 | |||
| 421 | static void do_sync_core(void *data) | ||
| 422 | { | ||
| 423 | sync_core(); | ||
| 424 | } | ||
| 425 | |||
| 426 | static void run_sync(void) | ||
| 427 | { | ||
| 428 | int enable_irqs = irqs_disabled(); | ||
| 429 | |||
| 430 | /* We may be called with interrupts disbled (on bootup). */ | ||
| 431 | if (enable_irqs) | ||
| 432 | local_irq_enable(); | ||
| 433 | on_each_cpu(do_sync_core, NULL, 1); | ||
| 434 | if (enable_irqs) | ||
| 435 | local_irq_disable(); | ||
| 436 | } | ||
| 437 | |||
| 438 | void ftrace_replace_code(int enable) | ||
| 439 | { | ||
| 440 | struct ftrace_rec_iter *iter; | ||
| 441 | struct dyn_ftrace *rec; | ||
| 442 | const char *report = "adding breakpoints"; | ||
| 443 | int count = 0; | ||
| 444 | int ret; | ||
| 445 | |||
| 446 | for_ftrace_rec_iter(iter) { | ||
| 447 | rec = ftrace_rec_iter_record(iter); | ||
| 448 | |||
| 449 | ret = add_breakpoints(rec, enable); | ||
| 450 | if (ret) | ||
| 451 | goto remove_breakpoints; | ||
| 452 | count++; | ||
| 453 | } | ||
| 454 | |||
| 455 | run_sync(); | ||
| 456 | |||
| 457 | report = "updating code"; | ||
| 458 | |||
| 459 | for_ftrace_rec_iter(iter) { | ||
| 460 | rec = ftrace_rec_iter_record(iter); | ||
| 461 | |||
| 462 | ret = add_update(rec, enable); | ||
| 463 | if (ret) | ||
| 464 | goto remove_breakpoints; | ||
| 465 | } | ||
| 466 | |||
| 467 | run_sync(); | ||
| 468 | |||
| 469 | report = "removing breakpoints"; | ||
| 470 | |||
| 471 | for_ftrace_rec_iter(iter) { | ||
| 472 | rec = ftrace_rec_iter_record(iter); | ||
| 473 | |||
| 474 | ret = finish_update(rec, enable); | ||
| 475 | if (ret) | ||
| 476 | goto remove_breakpoints; | ||
| 477 | } | ||
| 478 | |||
| 479 | run_sync(); | ||
| 480 | |||
| 481 | return; | ||
| 482 | |||
| 483 | remove_breakpoints: | ||
| 484 | ftrace_bug(ret, rec ? rec->ip : 0); | ||
| 485 | printk(KERN_WARNING "Failed on %s (%d):\n", report, count); | ||
| 486 | for_ftrace_rec_iter(iter) { | ||
| 487 | rec = ftrace_rec_iter_record(iter); | ||
| 488 | remove_breakpoint(rec); | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | void arch_ftrace_update_code(int command) | ||
| 493 | { | ||
| 494 | modifying_ftrace_code++; | ||
| 495 | |||
| 496 | ftrace_modify_all_code(command); | ||
| 497 | |||
| 498 | modifying_ftrace_code--; | ||
| 499 | } | ||
| 500 | |||
| 337 | int __init ftrace_dyn_arch_init(void *data) | 501 | int __init ftrace_dyn_arch_init(void *data) |
| 338 | { | 502 | { |
| 339 | /* The return code is retured via data */ | 503 | /* The return code is retured via data */ |
