Skip to content

Commit b7ffffb

Browse files
committed
ftrace: Add infrastructure for delayed enabling of module functions
Qiu Peiyang pointed out that there's a race when enabling function tracing and loading a module. In order to make the modifications of converting nops in the prologue of functions into callbacks, the text needs to be converted from read-only to read-write. When enabling function tracing, the text permission is updated, the functions are modified, and then they are put back. When loading a module, the updates to convert function calls to mcount is done before the module text is set to read-only. But after it is done, the module text is visible by the function tracer. Thus we have the following race: CPU 0 CPU 1 ----- ----- start function tracing set text to read-write load_module add functions to ftrace set module text read-only update all functions to callbacks modify module functions too < Can't it's read-only > When this happens, ftrace detects the issue and disables itself till the next reboot. To fix this, a new DISABLED flag is added for ftrace records, which all module functions get when they are added. Then later, after the module code is all set, the records will have the DISABLED flag cleared, and they will be enabled if any callback wants all functions to be traced. Note, this doesn't add the delay to later. It simply changes the ftrace_module_init() to do both the setting of DISABLED records, and then immediately calls the enable code. This helps with testing this new code as it has the same behavior as previously. Another change will come after this to have the ftrace_module_enable() called after the text is set to read-only. Cc: Qiu Peiyang <[email protected]> Signed-off-by: Steven Rostedt <[email protected]>
1 parent c5d641f commit b7ffffb

File tree

2 files changed

+110
-57
lines changed

2 files changed

+110
-57
lines changed

include/linux/ftrace.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ bool is_ftrace_trampoline(unsigned long addr);
357357
* REGS - the record wants the function to save regs
358358
* REGS_EN - the function is set up to save regs.
359359
* IPMODIFY - the record allows for the IP address to be changed.
360+
* DISABLED - the record is not ready to be touched yet
360361
*
361362
* When a new ftrace_ops is registered and wants a function to save
362363
* pt_regs, the rec->flag REGS is set. When the function has been
@@ -371,10 +372,11 @@ enum {
371372
FTRACE_FL_TRAMP = (1UL << 28),
372373
FTRACE_FL_TRAMP_EN = (1UL << 27),
373374
FTRACE_FL_IPMODIFY = (1UL << 26),
375+
FTRACE_FL_DISABLED = (1UL << 25),
374376
};
375377

376-
#define FTRACE_REF_MAX_SHIFT 26
377-
#define FTRACE_FL_BITS 6
378+
#define FTRACE_REF_MAX_SHIFT 25
379+
#define FTRACE_FL_BITS 7
378380
#define FTRACE_FL_MASKED_BITS ((1UL << FTRACE_FL_BITS) - 1)
379381
#define FTRACE_FL_MASK (FTRACE_FL_MASKED_BITS << FTRACE_REF_MAX_SHIFT)
380382
#define FTRACE_REF_MAX ((1UL << FTRACE_REF_MAX_SHIFT) - 1)

kernel/trace/ftrace.c

Lines changed: 106 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,9 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
16581658
int in_hash = 0;
16591659
int match = 0;
16601660

1661+
if (rec->flags & FTRACE_FL_DISABLED)
1662+
continue;
1663+
16611664
if (all) {
16621665
/*
16631666
* Only the filter_hash affects all records.
@@ -2023,6 +2026,9 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update)
20232026

20242027
ftrace_bug_type = FTRACE_BUG_UNKNOWN;
20252028

2029+
if (rec->flags & FTRACE_FL_DISABLED)
2030+
return FTRACE_UPDATE_IGNORE;
2031+
20262032
/*
20272033
* If we are updating calls:
20282034
*
@@ -2833,9 +2839,9 @@ ops_references_rec(struct ftrace_ops *ops, struct dyn_ftrace *rec)
28332839
if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
28342840
return 0;
28352841

2836-
/* If ops traces all mods, we already accounted for it */
2842+
/* If ops traces all then it includes this function */
28372843
if (ops_traces_mod(ops))
2838-
return 0;
2844+
return 1;
28392845

28402846
/* The function must be in the filter */
28412847
if (!ftrace_hash_empty(ops->func_hash->filter_hash) &&
@@ -2849,64 +2855,41 @@ ops_references_rec(struct ftrace_ops *ops, struct dyn_ftrace *rec)
28492855
return 1;
28502856
}
28512857

2852-
static int referenced_filters(struct dyn_ftrace *rec)
2853-
{
2854-
struct ftrace_ops *ops;
2855-
int cnt = 0;
2856-
2857-
for (ops = ftrace_ops_list; ops != &ftrace_list_end; ops = ops->next) {
2858-
if (ops_references_rec(ops, rec))
2859-
cnt++;
2860-
}
2861-
2862-
return cnt;
2863-
}
2864-
28652858
static int ftrace_update_code(struct module *mod, struct ftrace_page *new_pgs)
28662859
{
28672860
struct ftrace_page *pg;
28682861
struct dyn_ftrace *p;
28692862
cycle_t start, stop;
28702863
unsigned long update_cnt = 0;
2871-
unsigned long ref = 0;
2872-
bool test = false;
2864+
unsigned long rec_flags = 0;
28732865
int i;
28742866

2867+
start = ftrace_now(raw_smp_processor_id());
2868+
28752869
/*
2876-
* When adding a module, we need to check if tracers are
2877-
* currently enabled and if they are set to trace all functions.
2878-
* If they are, we need to enable the module functions as well
2879-
* as update the reference counts for those function records.
2870+
* When a module is loaded, this function is called to convert
2871+
* the calls to mcount in its text to nops, and also to create
2872+
* an entry in the ftrace data. Now, if ftrace is activated
2873+
* after this call, but before the module sets its text to
2874+
* read-only, the modification of enabling ftrace can fail if
2875+
* the read-only is done while ftrace is converting the calls.
2876+
* To prevent this, the module's records are set as disabled
2877+
* and will be enabled after the call to set the module's text
2878+
* to read-only.
28802879
*/
2881-
if (mod) {
2882-
struct ftrace_ops *ops;
2883-
2884-
for (ops = ftrace_ops_list;
2885-
ops != &ftrace_list_end; ops = ops->next) {
2886-
if (ops->flags & FTRACE_OPS_FL_ENABLED) {
2887-
if (ops_traces_mod(ops))
2888-
ref++;
2889-
else
2890-
test = true;
2891-
}
2892-
}
2893-
}
2894-
2895-
start = ftrace_now(raw_smp_processor_id());
2880+
if (mod)
2881+
rec_flags |= FTRACE_FL_DISABLED;
28962882

28972883
for (pg = new_pgs; pg; pg = pg->next) {
28982884

28992885
for (i = 0; i < pg->index; i++) {
2900-
int cnt = ref;
29012886

29022887
/* If something went wrong, bail without enabling anything */
29032888
if (unlikely(ftrace_disabled))
29042889
return -1;
29052890

29062891
p = &pg->records[i];
2907-
if (test)
2908-
cnt += referenced_filters(p);
2909-
p->flags = cnt;
2892+
p->flags = rec_flags;
29102893

29112894
/*
29122895
* Do the initial record conversion from mcount jump
@@ -2916,21 +2899,6 @@ static int ftrace_update_code(struct module *mod, struct ftrace_page *new_pgs)
29162899
break;
29172900

29182901
update_cnt++;
2919-
2920-
/*
2921-
* If the tracing is enabled, go ahead and enable the record.
2922-
*
2923-
* The reason not to enable the record immediatelly is the
2924-
* inherent check of ftrace_make_nop/ftrace_make_call for
2925-
* correct previous instructions. Making first the NOP
2926-
* conversion puts the module to the correct state, thus
2927-
* passing the ftrace_make_call check.
2928-
*/
2929-
if (ftrace_start_up && cnt) {
2930-
int failed = __ftrace_replace_code(p, 1);
2931-
if (failed)
2932-
ftrace_bug(failed, p);
2933-
}
29342902
}
29352903
}
29362904

@@ -4938,6 +4906,19 @@ static int ftrace_process_locs(struct module *mod,
49384906

49394907
#define next_to_ftrace_page(p) container_of(p, struct ftrace_page, next)
49404908

4909+
static int referenced_filters(struct dyn_ftrace *rec)
4910+
{
4911+
struct ftrace_ops *ops;
4912+
int cnt = 0;
4913+
4914+
for (ops = ftrace_ops_list; ops != &ftrace_list_end; ops = ops->next) {
4915+
if (ops_references_rec(ops, rec))
4916+
cnt++;
4917+
}
4918+
4919+
return cnt;
4920+
}
4921+
49414922
void ftrace_release_mod(struct module *mod)
49424923
{
49434924
struct dyn_ftrace *rec;
@@ -4980,13 +4961,83 @@ void ftrace_release_mod(struct module *mod)
49804961
mutex_unlock(&ftrace_lock);
49814962
}
49824963

4964+
static void ftrace_module_enable(struct module *mod)
4965+
{
4966+
struct dyn_ftrace *rec;
4967+
struct ftrace_page *pg;
4968+
4969+
mutex_lock(&ftrace_lock);
4970+
4971+
if (ftrace_disabled)
4972+
goto out_unlock;
4973+
4974+
/*
4975+
* If the tracing is enabled, go ahead and enable the record.
4976+
*
4977+
* The reason not to enable the record immediatelly is the
4978+
* inherent check of ftrace_make_nop/ftrace_make_call for
4979+
* correct previous instructions. Making first the NOP
4980+
* conversion puts the module to the correct state, thus
4981+
* passing the ftrace_make_call check.
4982+
*
4983+
* We also delay this to after the module code already set the
4984+
* text to read-only, as we now need to set it back to read-write
4985+
* so that we can modify the text.
4986+
*/
4987+
if (ftrace_start_up)
4988+
ftrace_arch_code_modify_prepare();
4989+
4990+
do_for_each_ftrace_rec(pg, rec) {
4991+
int cnt;
4992+
/*
4993+
* do_for_each_ftrace_rec() is a double loop.
4994+
* module text shares the pg. If a record is
4995+
* not part of this module, then skip this pg,
4996+
* which the "break" will do.
4997+
*/
4998+
if (!within_module_core(rec->ip, mod))
4999+
break;
5000+
5001+
cnt = 0;
5002+
5003+
/*
5004+
* When adding a module, we need to check if tracers are
5005+
* currently enabled and if they are, and can trace this record,
5006+
* we need to enable the module functions as well as update the
5007+
* reference counts for those function records.
5008+
*/
5009+
if (ftrace_start_up)
5010+
cnt += referenced_filters(rec);
5011+
5012+
/* This clears FTRACE_FL_DISABLED */
5013+
rec->flags = cnt;
5014+
5015+
if (ftrace_start_up && cnt) {
5016+
int failed = __ftrace_replace_code(rec, 1);
5017+
if (failed) {
5018+
ftrace_bug(failed, rec);
5019+
goto out_loop;
5020+
}
5021+
}
5022+
5023+
} while_for_each_ftrace_rec();
5024+
5025+
out_loop:
5026+
if (ftrace_start_up)
5027+
ftrace_arch_code_modify_post_process();
5028+
5029+
out_unlock:
5030+
mutex_unlock(&ftrace_lock);
5031+
}
5032+
49835033
void ftrace_module_init(struct module *mod)
49845034
{
49855035
if (ftrace_disabled || !mod->num_ftrace_callsites)
49865036
return;
49875037

49885038
ftrace_process_locs(mod, mod->ftrace_callsites,
49895039
mod->ftrace_callsites + mod->num_ftrace_callsites);
5040+
ftrace_module_enable(mod);
49905041
}
49915042

49925043
static int ftrace_module_notify_exit(struct notifier_block *self,

0 commit comments

Comments
 (0)