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 328 static int 329 __ftrace_replace_code(struct dyn_ftrace *rec, 330 - unsigned char *old, unsigned char *new, int enable) 331 { 332 unsigned long ip, fl; 333 334 ip = rec->ip; 335 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)) 365 return 0; 366 367 /* 368 - * If it is enabled disable it, 369 - * otherwise enable it! 370 */ 371 - if (fl & FTRACE_FL_ENABLED) { 372 - /* swap new and old */ 373 - new = old; 374 - old = ftrace_call_replace(ip, FTRACE_ADDR); 375 rec->flags &= ~FTRACE_FL_ENABLED; 376 - } else { 377 - new = ftrace_call_replace(ip, FTRACE_ADDR); 378 rec->flags |= FTRACE_FL_ENABLED; 379 - } 380 } else { 381 382 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) { 396 if (rec->flags & FTRACE_FL_ENABLED) 397 return 0; 398 rec->flags |= FTRACE_FL_ENABLED; 399 } else { 400 if (!(rec->flags & FTRACE_FL_ENABLED)) 401 return 0; 402 rec->flags &= ~FTRACE_FL_ENABLED; 403 } 404 } 405 406 return ftrace_modify_code(ip, old, new); ··· 405 static void ftrace_replace_code(int enable) 406 { 407 int i, failed; 408 - unsigned char *new = NULL, *old = NULL; 409 struct dyn_ftrace *rec; 410 struct ftrace_page *pg; 411 412 - if (enable) 413 - old = ftrace_nop_replace(); 414 - else 415 - new = ftrace_nop_replace(); 416 417 for (pg = ftrace_pages_start; pg; pg = pg->next) { 418 for (i = 0; i < pg->index; i++) { ··· 427 unfreeze_record(rec); 428 } 429 430 - failed = __ftrace_replace_code(rec, old, new, enable); 431 if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { 432 rec->flags |= FTRACE_FL_FAILED; 433 if ((system_state == SYSTEM_BOOTING) || ··· 531 532 mutex_lock(&ftrace_start_lock); 533 ftrace_start++; 534 - if (ftrace_start == 1) 535 - command |= FTRACE_ENABLE_CALLS; 536 537 if (saved_ftrace_func != ftrace_trace_function) { 538 saved_ftrace_func = ftrace_trace_function;
··· 327 328 static int 329 __ftrace_replace_code(struct dyn_ftrace *rec, 330 + unsigned char *nop, int enable) 331 { 332 unsigned long ip, fl; 333 + unsigned char *call, *old, *new; 334 335 ip = rec->ip; 336 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 349 return 0; 350 351 + } else if (ftrace_filtered && enable) { 352 /* 353 + * Filtering is on: 354 */ 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) 368 rec->flags &= ~FTRACE_FL_ENABLED; 369 + else 370 + /* Otherwise record is filtered but not enabled, enable it */ 371 rec->flags |= FTRACE_FL_ENABLED; 372 } else { 373 + /* Disable or not filtered */ 374 375 if (enable) { 376 + /* if record is enabled, do nothing */ 377 if (rec->flags & FTRACE_FL_ENABLED) 378 return 0; 379 + 380 rec->flags |= FTRACE_FL_ENABLED; 381 + 382 } else { 383 + 384 + /* if record is not enabled do nothing */ 385 if (!(rec->flags & FTRACE_FL_ENABLED)) 386 return 0; 387 + 388 rec->flags &= ~FTRACE_FL_ENABLED; 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; 400 } 401 402 return ftrace_modify_code(ip, old, new); ··· 409 static void ftrace_replace_code(int enable) 410 { 411 int i, failed; 412 + unsigned char *nop = NULL; 413 struct dyn_ftrace *rec; 414 struct ftrace_page *pg; 415 416 + nop = ftrace_nop_replace(); 417 418 for (pg = ftrace_pages_start; pg; pg = pg->next) { 419 for (i = 0; i < pg->index; i++) { ··· 434 unfreeze_record(rec); 435 } 436 437 + failed = __ftrace_replace_code(rec, nop, enable); 438 if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { 439 rec->flags |= FTRACE_FL_FAILED; 440 if ((system_state == SYSTEM_BOOTING) || ··· 538 539 mutex_lock(&ftrace_start_lock); 540 ftrace_start++; 541 + command |= FTRACE_ENABLE_CALLS; 542 543 if (saved_ftrace_func != ftrace_trace_function) { 544 saved_ftrace_func = ftrace_trace_function;