ftrace: fix dyn ftrace filter selection

Impact: clean up and fix for dyn ftrace filter selection

The previous logic of the dynamic ftrace selection of enabling
or disabling functions was complex and incorrect. This patch simplifies
the code and corrects the usage. This simplification also makes the
code more robust.

Here is the correct logic:

Given a function that can be traced by dynamic ftrace:

If the function is not to be traced, disable it if it was enabled.
(this is if the function is in the set_ftrace_notrace file)

(filter is on if there exists any functions in set_ftrace_filter file)

If the filter is on, and we are enabling functions:
If the function is in set_ftrace_filter, enable it if it is not
already enabled.
If the function is not in set_ftrace_filter, disable it if it is not
already disabled.

Otherwise, if the filter is off and we are enabling function tracing:
Enable the function if it is not already enabled.

Otherwise, if we are disabling function tracing:
Disable the function if it is not already disabled.

This code now sets or clears the ENABLED flag in the record, and at the
end it will enable the function if the flag is set, or disable the function
if the flag is cleared.

The parameters for the function that does the above logic is also
simplified. Instead of passing in confusing "new" and "old" where
they might be swapped if the "enabled" flag is not set. The old logic
even had one of the above always NULL and had to be filled in. The new
logic simply passes in one parameter called "nop". A "call" is calculated
in the code, and at the end of the logic, when we know we need to either
disable or enable the function, we can then use the "nop" and "call"
properly.

This code is more robust than the previous version.

Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

authored by Steven Rostedt and committed by Steven Rostedt 32464779 82043278

+52 -60
+52 -60
kernel/trace/ftrace.c
··· 327 327 328 328 static int 329 329 __ftrace_replace_code(struct dyn_ftrace *rec, 330 - unsigned char *old, unsigned char *new, int enable) 330 + unsigned char *nop, int enable) 331 331 { 332 332 unsigned long ip, fl; 333 + unsigned char *call, *old, *new; 333 334 334 335 ip = rec->ip; 335 336 336 - if (ftrace_filtered && enable) { 337 - /* 338 - * If filtering is on: 339 - * 340 - * If this record is set to be filtered and 341 - * is enabled then do nothing. 342 - * 343 - * If this record is set to be filtered and 344 - * it is not enabled, enable it. 345 - * 346 - * If this record is not set to be filtered 347 - * and it is not enabled do nothing. 348 - * 349 - * If this record is set not to trace then 350 - * do nothing. 351 - * 352 - * If this record is set not to trace and 353 - * it is enabled then disable it. 354 - * 355 - * If this record is not set to be filtered and 356 - * it is enabled, disable it. 357 - */ 358 - 359 - fl = rec->flags & (FTRACE_FL_FILTER | FTRACE_FL_NOTRACE | 360 - FTRACE_FL_ENABLED); 361 - 362 - if ((fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) || 363 - (fl == (FTRACE_FL_FILTER | FTRACE_FL_NOTRACE)) || 364 - !fl || (fl == FTRACE_FL_NOTRACE)) 337 + /* 338 + * If this record is not to be traced and 339 + * it is not enabled then do nothing. 340 + * 341 + * If this record is not to be traced and 342 + * it is enabled then disabled it. 343 + * 344 + */ 345 + if (rec->flags & FTRACE_FL_NOTRACE) { 346 + if (rec->flags & FTRACE_FL_ENABLED) 347 + rec->flags &= ~FTRACE_FL_ENABLED; 348 + else 365 349 return 0; 366 350 351 + } else if (ftrace_filtered && enable) { 367 352 /* 368 - * If it is enabled disable it, 369 - * otherwise enable it! 353 + * Filtering is on: 370 354 */ 371 - if (fl & FTRACE_FL_ENABLED) { 372 - /* swap new and old */ 373 - new = old; 374 - old = ftrace_call_replace(ip, FTRACE_ADDR); 355 + 356 + fl = rec->flags & (FTRACE_FL_FILTER | FTRACE_FL_ENABLED); 357 + 358 + /* Record is filtered and enabled, do nothing */ 359 + if (fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) 360 + return 0; 361 + 362 + /* Record is not filtered and is not enabled do nothing */ 363 + if (!fl) 364 + return 0; 365 + 366 + /* Record is not filtered but enabled, disable it */ 367 + if (fl == FTRACE_FL_ENABLED) 375 368 rec->flags &= ~FTRACE_FL_ENABLED; 376 - } else { 377 - new = ftrace_call_replace(ip, FTRACE_ADDR); 369 + else 370 + /* Otherwise record is filtered but not enabled, enable it */ 378 371 rec->flags |= FTRACE_FL_ENABLED; 379 - } 380 372 } else { 373 + /* Disable or not filtered */ 381 374 382 375 if (enable) { 383 - /* 384 - * If this record is set not to trace and is 385 - * not enabled, do nothing. 386 - */ 387 - fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED); 388 - if (fl == FTRACE_FL_NOTRACE) 389 - return 0; 390 - 391 - new = ftrace_call_replace(ip, FTRACE_ADDR); 392 - } else 393 - old = ftrace_call_replace(ip, FTRACE_ADDR); 394 - 395 - if (enable) { 376 + /* if record is enabled, do nothing */ 396 377 if (rec->flags & FTRACE_FL_ENABLED) 397 378 return 0; 379 + 398 380 rec->flags |= FTRACE_FL_ENABLED; 381 + 399 382 } else { 383 + 384 + /* if record is not enabled do nothing */ 400 385 if (!(rec->flags & FTRACE_FL_ENABLED)) 401 386 return 0; 387 + 402 388 rec->flags &= ~FTRACE_FL_ENABLED; 403 389 } 390 + } 391 + 392 + call = ftrace_call_replace(ip, FTRACE_ADDR); 393 + 394 + if (rec->flags & FTRACE_FL_ENABLED) { 395 + old = nop; 396 + new = call; 397 + } else { 398 + old = call; 399 + new = nop; 404 400 } 405 401 406 402 return ftrace_modify_code(ip, old, new); ··· 405 409 static void ftrace_replace_code(int enable) 406 410 { 407 411 int i, failed; 408 - unsigned char *new = NULL, *old = NULL; 412 + unsigned char *nop = NULL; 409 413 struct dyn_ftrace *rec; 410 414 struct ftrace_page *pg; 411 415 412 - if (enable) 413 - old = ftrace_nop_replace(); 414 - else 415 - new = ftrace_nop_replace(); 416 + nop = ftrace_nop_replace(); 416 417 417 418 for (pg = ftrace_pages_start; pg; pg = pg->next) { 418 419 for (i = 0; i < pg->index; i++) { ··· 427 434 unfreeze_record(rec); 428 435 } 429 436 430 - failed = __ftrace_replace_code(rec, old, new, enable); 437 + failed = __ftrace_replace_code(rec, nop, enable); 431 438 if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { 432 439 rec->flags |= FTRACE_FL_FAILED; 433 440 if ((system_state == SYSTEM_BOOTING) || ··· 531 538 532 539 mutex_lock(&ftrace_start_lock); 533 540 ftrace_start++; 534 - if (ftrace_start == 1) 535 - command |= FTRACE_ENABLE_CALLS; 541 + command |= FTRACE_ENABLE_CALLS; 536 542 537 543 if (saved_ftrace_func != ftrace_trace_function) { 538 544 saved_ftrace_func = ftrace_trace_function;