db/mtag_info.schema: add a UNIQUE constraint and add an index
[smatch.git] / check_preempt.c
blobbebfb614e6f9346e86bf626a31aec43151d1c00b
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 int get_preempt_cnt(void)
44 struct smatch_state *state;
46 state = get_state(my_id, "preempt", NULL);
47 if (!state)
48 return 0;
49 return PTR_INT(state->data);
52 void clear_preempt_cnt(void)
54 struct smatch_state *state;
56 if (local_debug)
57 sm_msg("%s: here", __func__);
59 state = get_state(my_id, "preempt", NULL);
60 if (!state || PTR_INT(state->data) == 0)
61 return;
62 set_state(my_id, "preempt", NULL, alloc_state_num(0));
65 static unsigned long fn_decrements_preempt;
66 bool function_decrements_preempt(void)
68 return fn_decrements_preempt;
71 static int get_start_preempt_cnt(void)
73 struct stree *orig, *start;
74 int ret;
76 start = get_start_states();
77 orig = __swap_cur_stree(start);
79 ret = get_preempt_cnt();
81 __swap_cur_stree(orig);
82 return ret;
85 static void match_call_info(struct expression *expr)
87 int start_cnt, cnt;
88 int param;
89 const char *disables = "";
91 if (is_fn_ptr(expr->fn))
92 return;
93 cnt = get_preempt_cnt();
94 if (cnt <= 0)
95 return;
96 start_cnt = get_start_preempt_cnt();
97 if (start_cnt < cnt)
98 disables = "<- disables preempt";
100 param = get_gfp_param(expr);
101 if (param >= 0)
102 return;
103 sql_insert_caller_info(expr, PREEMPT_ADD, -1, "", disables);
106 static struct smatch_state *merge_func(struct smatch_state *s1, struct smatch_state *s2)
108 if (__in_function_def) {
109 if (PTR_INT(s1->data) > PTR_INT(s2->data))
110 return s1;
111 return s2;
113 if (PTR_INT(s1->data) < PTR_INT(s2->data))
114 return s1;
115 return s2;
118 static void select_call_info(const char *name, struct symbol *sym, char *key, char *value)
120 set_state(my_id, "preempt", NULL, alloc_state_num(1));
123 void __preempt_add(void)
125 set_state(my_id, "preempt", NULL, alloc_state_num(get_preempt_cnt() + 1));
128 void __preempt_sub(void)
130 fn_decrements_preempt = 1;
131 set_state(my_id, "preempt", NULL, alloc_state_num(get_preempt_cnt() - 1));
134 static void match_preempt_count_zero(const char *fn, struct expression *call_expr,
135 struct expression *assign_expr, void *_param)
137 set_state(my_id, "preempt", NULL, alloc_state_num(0));
140 static void match_preempt_count_non_zero(const char *fn, struct expression *call_expr,
141 struct expression *assign_expr, void *_param)
143 int cnt;
145 cnt = get_preempt_cnt();
146 if (cnt == 0)
147 cnt = 1;
148 set_state(my_id, "preempt", NULL, alloc_state_num(cnt));
151 void check_preempt(int id)
153 my_id = id;
155 if (option_project != PROJ_KERNEL)
156 return;
158 add_function_data(&fn_decrements_preempt);
159 set_dynamic_states(my_id);
160 add_merge_hook(my_id, &merge_func);
162 return_implies_state("preempt_count", 0, 0, &match_preempt_count_zero, NULL);
163 return_implies_state("preempt_count", 1, INT_MAX, &match_preempt_count_non_zero, NULL);
165 select_caller_info_hook(&select_call_info, PREEMPT_ADD);
166 add_hook(&match_call_info, FUNCTION_CALL_HOOK);