db/fixup_kernel.sh: fix clear_user() handling
[smatch.git] / check_preempt_info.c
blob4133b9e9d77db10a961ecf17b06773b28a229731
1 /*
2 * Copyright (C) 2021 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 * The preempt_count is a counter as you might guess. It's incrememented
20 * if we call a spinlock and decremented when we unlock. We can hold more
21 * than one spin lock at a time so at first I thought that we should just
22 * record "We started this function with a preempt count of zero and took
23 * two locks so that's a difference of +2 or -2 if we dropped the locks."
24 * But that didn't work and it turns out more complicated than I imagined.
26 * Locking generally is complicated. I think printk() was complicated, but
27 * I didn't really figure out what the issue is. And then there is also the
28 * issue of recursion so the lock count got way out of hand.
30 * My approach is to pull out the code that controls the return_states data
31 * into this function. Only handle simple things. Locking functions are
32 * basically always simple and under 10 lines. If they take more than one
33 * spinlock then something complicated is happening so ignore that.
36 #include "smatch.h"
37 #include "smatch_slist.h"
39 static int my_id;
41 STATE(add);
42 STATE(sub);
43 STATE(ignore);
45 struct preempt_info {
46 const char *name;
47 int type;
50 static struct preempt_info func_table[] = {
51 { "preempt_count_add", PREEMPT_ADD },
52 { "__preempt_count_add", PREEMPT_ADD },
53 { "local_bh_disable", PREEMPT_ADD },
54 { "spin_lock", PREEMPT_ADD },
55 { "spin_lock_nested", PREEMPT_ADD },
56 { "_spin_lock", PREEMPT_ADD },
57 { "_spin_lock_nested", PREEMPT_ADD },
58 { "__spin_lock", PREEMPT_ADD },
59 { "__spin_lock_nested", PREEMPT_ADD },
60 { "raw_spin_lock", PREEMPT_ADD },
61 { "_raw_spin_lock", PREEMPT_ADD },
62 { "_raw_spin_lock_nested", PREEMPT_ADD },
63 { "__raw_spin_lock", PREEMPT_ADD },
64 { "spin_lock_irq", PREEMPT_ADD },
65 { "_spin_lock_irq", PREEMPT_ADD },
66 { "__spin_lock_irq", PREEMPT_ADD },
67 { "_raw_spin_lock_irq", PREEMPT_ADD },
68 { "spin_lock_irqsave", PREEMPT_ADD },
69 { "_spin_lock_irqsave", PREEMPT_ADD },
70 { "__spin_lock_irqsave", PREEMPT_ADD },
71 { "_raw_spin_lock_irqsave", PREEMPT_ADD },
72 { "__raw_spin_lock_irqsave", PREEMPT_ADD },
73 { "spin_lock_irqsave_nested", PREEMPT_ADD },
74 { "_spin_lock_irqsave_nested", PREEMPT_ADD },
75 { "__spin_lock_irqsave_nested", PREEMPT_ADD },
76 { "_raw_spin_lock_irqsave_nested", PREEMPT_ADD },
77 { "spin_lock_bh", PREEMPT_ADD },
78 { "_spin_lock_bh", PREEMPT_ADD },
79 { "__spin_lock_bh", PREEMPT_ADD },
80 { "task_rq_lock", PREEMPT_ADD },
81 { "netif_tx_lock_bh", PREEMPT_ADD },
82 { "mt76_tx_status_lock", PREEMPT_ADD },
83 { "rht_lock", PREEMPT_ADD },
84 { "bit_spin_lock", PREEMPT_ADD },
86 { "preempt_count_sub", PREEMPT_SUB },
87 { "__preempt_count_dec_and_test", PREEMPT_SUB },
88 { "__preempt_count_sub", PREEMPT_SUB },
89 { "local_bh_enable", PREEMPT_SUB },
90 { "__local_bh_enable_ip", PREEMPT_SUB },
91 { "spin_unlock", PREEMPT_SUB },
92 { "_spin_unlock", PREEMPT_SUB },
93 { "__spin_unlock", PREEMPT_SUB },
94 { "raw_spin_unlock", PREEMPT_SUB },
95 { "_raw_spin_unlock", PREEMPT_SUB },
96 { "__raw_spin_unlock", PREEMPT_SUB },
97 { "spin_unlock_irq", PREEMPT_SUB },
98 { "_spin_unlock_irq", PREEMPT_SUB },
99 { "__spin_unlock_irq", PREEMPT_SUB },
100 { "_raw_spin_unlock_irq", PREEMPT_SUB },
101 { "__raw_spin_unlock_irq", PREEMPT_SUB },
102 { "spin_unlock_irqrestore", PREEMPT_SUB },
103 { "_spin_unlock_irqrestore", PREEMPT_SUB },
104 { "__spin_unlock_irqrestore", PREEMPT_SUB },
105 { "_raw_spin_unlock_irqrestore", PREEMPT_SUB },
106 { "__raw_spin_unlock_irqrestore", PREEMPT_SUB },
107 { "class_raw_spinlock_irqsave_destructor", PREEMPT_SUB },
108 { "class_raw_spinlock_irq_destructor", PREEMPT_SUB },
109 { "class_spinlock_irq_constructor", PREEMPT_SUB },
110 { "class_spinlock_irqsave_destructor", PREEMPT_SUB },
111 { "class_write_lock_irq_destructor", PREEMPT_SUB },
112 { "class_write_lock_irqsave_destructor",PREEMPT_SUB },
113 { "class_read_lock_irqsave_destructor", PREEMPT_SUB },
114 { "class_spinlock_destructor", PREEMPT_SUB },
115 { "class_read_lock_destructor", PREEMPT_SUB },
116 { "spin_unlock_bh", PREEMPT_SUB },
117 { "_spin_unlock_bh", PREEMPT_SUB },
118 { "__spin_unlock_bh", PREEMPT_SUB },
119 { "task_rq_unlock", PREEMPT_SUB },
120 { "queue_request_and_unlock", PREEMPT_SUB },
121 { "netif_tx_unlock_bh", PREEMPT_SUB },
122 { "mt76_tx_status_unlock", PREEMPT_SUB },
123 { "rht_unlock", PREEMPT_SUB },
124 { "bit_spin_unlock", PREEMPT_SUB },
125 { "__bit_spin_unlock", PREEMPT_SUB },
128 static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
130 struct smatch_state *state;
132 state = get_state(my_id, "preempt", NULL);
133 if (state != &add && state != &sub)
134 return;
136 sql_insert_return_states(return_id, return_ranges,
137 (state == &add) ? PREEMPT_ADD : PREEMPT_SUB,
138 -2, "", "");
141 static void preempt_count_add(const char *fn, struct expression *expr, void *_index)
143 struct smatch_state *state;
145 __preempt_add();
147 /* If the state is already set then ignore it */
148 state = get_state(my_id, "preempt", NULL);
149 if (state)
150 set_state(my_id, "preempt", NULL, &ignore);
151 else
152 set_state(my_id, "preempt", NULL, &add);
155 static void preempt_count_sub(const char *fn, struct expression *expr, void *_index)
157 struct smatch_state *state;
159 __preempt_sub();
161 /* If the state is already set then ignore it */
162 state = get_state(my_id, "preempt", NULL);
163 if (state)
164 set_state(my_id, "preempt", NULL, &ignore);
165 else
166 set_state(my_id, "preempt", NULL, &sub);
169 static bool is_preempt_primitive(struct expression *expr)
171 int i;
173 while (expr->type == EXPR_ASSIGNMENT)
174 expr = strip_expr(expr->right);
175 if (expr->type != EXPR_CALL)
176 return false;
178 if (expr->fn->type != EXPR_SYMBOL)
179 return false;
181 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
182 if (sym_name_is(func_table[i].name, expr->fn))
183 return true;
186 return false;
189 static void select_return_add(struct expression *expr, int param, char *key, char *value)
191 if (is_preempt_primitive(expr))
192 return;
193 preempt_count_add(NULL, NULL, NULL);
196 static void select_return_sub(struct expression *expr, int param, char *key, char *value)
198 if (is_preempt_primitive(expr))
199 return;
200 preempt_count_sub(NULL, NULL, NULL);
203 void check_preempt_info(int id)
205 int i;
207 my_id = id;
209 if (option_project != PROJ_KERNEL)
210 return;
212 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
213 if (func_table[i].type == PREEMPT_ADD)
214 add_function_hook(func_table[i].name, &preempt_count_add, NULL);
215 else
216 add_function_hook(func_table[i].name, &preempt_count_sub, NULL);
219 select_return_states_hook(PREEMPT_ADD, &select_return_add);
220 select_return_states_hook(PREEMPT_SUB, &select_return_sub);
222 add_split_return_callback(&match_return_info);