2 * Copyright (C) 2018 Oracle.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
19 * This code works with check_preempt_info.c. The info.c file records
20 * return_states (this code handles caller_info states). The main
21 * thing that this code does is it provides get_preempt_count() function
22 * the check_scheduling_in_atomic.c file.
24 * The preempt count is a counter and when it's non-zero then we are not
25 * allowed to call schedule().
27 * If we're called with a lock held then we say that preempt is one. (In
28 * real life, it could be higher but this is easier to program). So if
29 * any caller is holding the lock we have preempt = 1.
31 * If during the course of parsing the call, the preempt count gets out of
32 * sync on one side of a branch statement, then we assume the lower preempt
33 * count is correct. (In other words, we choose to miss some bugs rather
34 * than add false postives).
38 #include "smatch_slist.h"
45 } break_link_table
[] = {
46 {"arm_smmu_update_ctx_desc_devices", "arm_smmu_write_ctx_desc"},
49 static bool in_break_link_table(struct expression
*expr
)
57 fn
= get_fnptr_name(expr
->fn
);
61 for (i
= 0; i
< ARRAY_SIZE(break_link_table
); i
++) {
62 if (strcmp(get_function(), break_link_table
[i
].caller
) != 0)
64 if (strcmp(fn
, break_link_table
[i
].fn
) == 0)
71 int get_preempt_cnt(void)
73 struct smatch_state
*state
;
75 state
= get_state(my_id
, "preempt", NULL
);
78 return PTR_INT(state
->data
);
81 void clear_preempt_cnt(void)
83 struct smatch_state
*state
;
85 state
= get_state(my_id
, "preempt", NULL
);
86 if (!state
|| PTR_INT(state
->data
) == 0)
88 set_state(my_id
, "preempt", NULL
, alloc_state_num(0));
91 static unsigned long fn_decrements_preempt
;
92 bool function_decrements_preempt(void)
94 return fn_decrements_preempt
;
97 static int get_start_preempt_cnt(void)
99 struct stree
*orig
, *start
;
102 start
= get_start_states();
103 orig
= __swap_cur_stree(start
);
105 ret
= get_preempt_cnt();
107 __swap_cur_stree(orig
);
111 static bool passes_MSG_DONTWAIT(struct expression
*expr
)
113 struct expression
*arg
;
116 FOR_EACH_PTR(expr
->args
, arg
) {
117 macro
= get_macro_name(arg
->pos
);
118 if (macro
&& strcmp(macro
, "MSG_DONTWAIT") == 0)
120 } END_FOR_EACH_PTR(arg
);
125 static bool is_no_preempt_caller(void)
127 char *fn
= get_function();
131 if (strcmp(fn
, "sg_miter_next") == 0)
136 static void match_call_info(struct expression
*expr
)
140 const char *disables
= "";
142 if (is_fn_ptr(expr
->fn
))
144 cnt
= get_preempt_cnt();
147 if (passes_MSG_DONTWAIT(expr
))
149 if (is_no_preempt_caller())
151 start_cnt
= get_start_preempt_cnt();
153 disables
= "<- disables preempt";
155 param
= get_gfp_param(expr
);
159 if (in_break_link_table(expr
))
162 sql_insert_caller_info(expr
, PREEMPT_ADD
, -2, "", disables
);
165 static struct smatch_state
*merge_func(struct smatch_state
*s1
, struct smatch_state
*s2
)
167 if (__in_function_def
) {
168 if (PTR_INT(s1
->data
) > PTR_INT(s2
->data
))
172 if (PTR_INT(s1
->data
) < PTR_INT(s2
->data
))
177 static void select_call_info(const char *name
, struct symbol
*sym
, char *key
, char *value
)
179 set_state(my_id
, "preempt", NULL
, alloc_state_num(1));
182 void __preempt_add(void)
184 set_state(my_id
, "preempt", NULL
, alloc_state_num(get_preempt_cnt() + 1));
187 void __preempt_sub(void)
189 fn_decrements_preempt
= 1;
190 set_state(my_id
, "preempt", NULL
, alloc_state_num(get_preempt_cnt() - 1));
193 static void match_preempt_count_zero(const char *fn
, struct expression
*call_expr
,
194 struct expression
*assign_expr
, void *_param
)
196 set_state(my_id
, "preempt", NULL
, alloc_state_num(0));
199 static void match_preempt_count_non_zero(const char *fn
, struct expression
*call_expr
,
200 struct expression
*assign_expr
, void *_param
)
202 struct sm_state
*sm
, *tmp
;
203 bool possibly_atomic
= false;
205 sm
= get_sm_state(my_id
, "preempt", NULL
);
209 FOR_EACH_PTR(sm
->possible
, tmp
) {
210 if (tmp
->state
->data
) {
211 possibly_atomic
= true;
214 } END_FOR_EACH_PTR(tmp
);
216 if (!possibly_atomic
)
219 set_state(my_id
, "preempt", NULL
, alloc_state_num(1));
222 static int set_sleeps(void *_sleeps
, int argc
, char **argv
, char **azColName
)
224 int *fn_sleeps
= _sleeps
;
230 static void silence_duplicates(struct symbol
*sym
)
234 if (!sym
|| !sym
->ident
)
237 run_sql(set_sleeps
, &fn_sleeps
,
238 "select * from return_implies where %s and type = %d;",
239 get_static_filter(sym
), SLEEP
);
242 set_state(my_id
, "preempt", NULL
, alloc_state_num(0));
245 void check_preempt(int id
)
249 if (option_project
!= PROJ_KERNEL
)
252 add_function_data(&fn_decrements_preempt
);
253 set_dynamic_states(my_id
);
254 add_merge_hook(my_id
, &merge_func
);
256 return_implies_exact("preempt_count", int_zero
, int_zero
, &match_preempt_count_zero
, NULL
);
257 return_implies_exact("preempt_count", int_one
, int_max
, &match_preempt_count_non_zero
, NULL
);
259 select_caller_info_hook(&select_call_info
, PREEMPT_ADD
);
260 add_hook(&silence_duplicates
, AFTER_DEF_HOOK
);
261 add_hook(&match_call_info
, FUNCTION_CALL_HOOK
);