db/reload_partial.sh: update to handle hashed files
[smatch.git] / check_preempt_info.c
blobc08247ad3e9d67afdc1bd672591156ceb0c4852c
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 { "spin_unlock_bh", PREEMPT_SUB },
108 { "_spin_unlock_bh", PREEMPT_SUB },
109 { "__spin_unlock_bh", PREEMPT_SUB },
110 { "task_rq_unlock", PREEMPT_SUB },
111 { "queue_request_and_unlock", PREEMPT_SUB },
112 { "netif_tx_unlock_bh", PREEMPT_SUB },
113 { "mt76_tx_status_unlock", PREEMPT_SUB },
114 { "rht_unlock", PREEMPT_SUB },
115 { "bit_spin_unlock", PREEMPT_SUB },
116 { "__bit_spin_unlock", PREEMPT_SUB },
119 static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
121 struct smatch_state *state;
123 state = get_state(my_id, "preempt", NULL);
124 if (state != &add && state != &sub)
125 return;
127 sql_insert_return_states(return_id, return_ranges,
128 (state == &add) ? PREEMPT_ADD : PREEMPT_SUB,
129 -1, "", "");
132 static void preempt_count_add(const char *fn, struct expression *expr, void *_index)
134 struct smatch_state *state;
136 __preempt_add();
138 /* If the state is already set then ignore it */
139 state = get_state(my_id, "preempt", NULL);
140 if (state)
141 set_state(my_id, "preempt", NULL, &ignore);
142 else
143 set_state(my_id, "preempt", NULL, &add);
146 static void preempt_count_sub(const char *fn, struct expression *expr, void *_index)
148 struct smatch_state *state;
150 __preempt_sub();
152 /* If the state is already set then ignore it */
153 state = get_state(my_id, "preempt", NULL);
154 if (state)
155 set_state(my_id, "preempt", NULL, &ignore);
156 else
157 set_state(my_id, "preempt", NULL, &sub);
160 static bool is_preempt_primitive(struct expression *expr)
162 int i;
164 while (expr->type == EXPR_ASSIGNMENT)
165 expr = strip_expr(expr->right);
166 if (expr->type != EXPR_CALL)
167 return false;
169 if (expr->fn->type != EXPR_SYMBOL)
170 return false;
172 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
173 if (sym_name_is(func_table[i].name, expr->fn))
174 return true;
177 return false;
180 static void select_return_add(struct expression *expr, int param, char *key, char *value)
182 if (is_preempt_primitive(expr))
183 return;
184 preempt_count_add(NULL, NULL, NULL);
187 static void select_return_sub(struct expression *expr, int param, char *key, char *value)
189 if (is_preempt_primitive(expr))
190 return;
191 preempt_count_sub(NULL, NULL, NULL);
194 void check_preempt_info(int id)
196 int i;
198 my_id = id;
200 if (option_project != PROJ_KERNEL)
201 return;
203 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
204 if (func_table[i].type == PREEMPT_ADD)
205 add_function_hook(func_table[i].name, &preempt_count_add, NULL);
206 else
207 add_function_hook(func_table[i].name, &preempt_count_sub, NULL);
210 select_return_states_hook(PREEMPT_ADD, &select_return_add);
211 select_return_states_hook(PREEMPT_SUB, &select_return_sub);
213 add_split_return_callback(&match_return_info);