sm_math1: update the validation test
[smatch.git] / check_preempt.c
blob1068431faf7e724a0080a9f6c126a4202b409751
1 /*
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).
37 #include "smatch.h"
38 #include "smatch_slist.h"
40 static int my_id;
42 static struct {
43 const char *caller;
44 const char *fn;
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)
51 char *fn;
52 int i;
54 if (!get_function())
55 return false;
57 fn = get_fnptr_name(expr->fn);
58 if (!fn)
59 return false;
61 for (i = 0; i < ARRAY_SIZE(break_link_table); i++) {
62 if (strcmp(get_function(), break_link_table[i].caller) != 0)
63 continue;
64 if (strcmp(fn, break_link_table[i].fn) == 0)
65 return true;
68 return false;
71 int get_preempt_cnt(void)
73 struct smatch_state *state;
75 state = get_state(my_id, "preempt", NULL);
76 if (!state)
77 return 0;
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)
87 return;
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;
100 int ret;
102 start = get_start_states();
103 orig = __swap_cur_stree(start);
105 ret = get_preempt_cnt();
107 __swap_cur_stree(orig);
108 return ret;
111 static bool passes_MSG_DONTWAIT(struct expression *expr)
113 struct expression *arg;
114 char *macro;
116 FOR_EACH_PTR(expr->args, arg) {
117 macro = get_macro_name(arg->pos);
118 if (macro && strcmp(macro, "MSG_DONTWAIT") == 0)
119 return true;
120 } END_FOR_EACH_PTR(arg);
122 return false;
125 static bool is_no_preempt_caller(void)
127 char *fn = get_function();
129 if (!fn)
130 return false;
131 if (strcmp(fn, "sg_miter_next") == 0)
132 return true;
133 return false;
136 static void match_call_info(struct expression *expr)
138 int start_cnt, cnt;
139 int param;
140 const char *disables = "";
142 if (is_fn_ptr(expr->fn))
143 return;
144 cnt = get_preempt_cnt();
145 if (cnt <= 0)
146 return;
147 if (passes_MSG_DONTWAIT(expr))
148 return;
149 if (is_no_preempt_caller())
150 return;
151 start_cnt = get_start_preempt_cnt();
152 if (start_cnt < cnt)
153 disables = "<- disables preempt";
155 param = get_gfp_param(expr);
156 if (param >= 0)
157 return;
159 if (in_break_link_table(expr))
160 return;
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))
169 return s1;
170 return s2;
172 if (PTR_INT(s1->data) < PTR_INT(s2->data))
173 return s1;
174 return s2;
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);
206 if (!sm)
207 return;
209 FOR_EACH_PTR(sm->possible, tmp) {
210 if (tmp->state->data) {
211 possibly_atomic = true;
212 break;
214 } END_FOR_EACH_PTR(tmp);
216 if (!possibly_atomic)
217 return;
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;
226 *fn_sleeps = 1;
227 return 0;
230 static void silence_duplicates(struct symbol *sym)
232 int fn_sleeps = 0;
234 if (!sym || !sym->ident)
235 return;
237 run_sql(set_sleeps, &fn_sleeps,
238 "select * from return_implies where %s and type = %d;",
239 get_static_filter(sym), SLEEP);
241 if (fn_sleeps)
242 set_state(my_id, "preempt", NULL, alloc_state_num(0));
245 void check_preempt(int id)
247 my_id = id;
249 if (option_project != PROJ_KERNEL)
250 return;
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);