rosenberg: handle bit fields better
[smatch.git] / check_preempt_info.c
blob801a91375a2e6966cfa8c58be72f13f57e571a13
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 },
84 { "preempt_count_sub", PREEMPT_SUB },
85 { "__preempt_count_sub", PREEMPT_SUB },
86 { "local_bh_enable", PREEMPT_SUB },
87 { "__local_bh_enable_ip", PREEMPT_SUB },
88 { "spin_unlock", PREEMPT_SUB },
89 { "_spin_unlock", PREEMPT_SUB },
90 { "__spin_unlock", PREEMPT_SUB },
91 { "raw_spin_unlock", PREEMPT_SUB },
92 { "_raw_spin_unlock", PREEMPT_SUB },
93 { "__raw_spin_unlock", PREEMPT_SUB },
94 { "spin_unlock_irq", PREEMPT_SUB },
95 { "_spin_unlock_irq", PREEMPT_SUB },
96 { "__spin_unlock_irq", PREEMPT_SUB },
97 { "_raw_spin_unlock_irq", PREEMPT_SUB },
98 { "__raw_spin_unlock_irq", PREEMPT_SUB },
99 { "spin_unlock_irqrestore", PREEMPT_SUB },
100 { "_spin_unlock_irqrestore", PREEMPT_SUB },
101 { "__spin_unlock_irqrestore", PREEMPT_SUB },
102 { "_raw_spin_unlock_irqrestore", PREEMPT_SUB },
103 { "__raw_spin_unlock_irqrestore", PREEMPT_SUB },
104 { "spin_unlock_bh", PREEMPT_SUB },
105 { "_spin_unlock_bh", PREEMPT_SUB },
106 { "__spin_unlock_bh", PREEMPT_SUB },
107 { "task_rq_unlock", PREEMPT_SUB },
108 { "queue_request_and_unlock", PREEMPT_SUB },
109 { "netif_tx_unlock_bh", PREEMPT_SUB },
110 { "mt76_tx_status_unlock", PREEMPT_SUB },
113 static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
115 struct smatch_state *state;
117 state = get_state(my_id, "preempt", NULL);
118 if (state != &add && state != &sub)
119 return;
121 sql_insert_return_states(return_id, return_ranges,
122 (state == &add) ? PREEMPT_ADD : PREEMPT_SUB,
123 -1, "", "");
126 static void preempt_count_add(const char *fn, struct expression *expr, void *_index)
128 struct smatch_state *state;
130 __preempt_add();
132 /* If the state is already set then ignore it */
133 state = get_state(my_id, "preempt", NULL);
134 if (state)
135 set_state(my_id, "preempt", NULL, &ignore);
136 else
137 set_state(my_id, "preempt", NULL, &add);
140 static void preempt_count_sub(const char *fn, struct expression *expr, void *_index)
142 struct smatch_state *state;
144 __preempt_sub();
146 /* If the state is already set then ignore it */
147 state = get_state(my_id, "preempt", NULL);
148 if (state)
149 set_state(my_id, "preempt", NULL, &ignore);
150 else
151 set_state(my_id, "preempt", NULL, &sub);
154 static bool is_preempt_primitive(struct expression *expr)
156 int i;
158 while (expr->type == EXPR_ASSIGNMENT)
159 expr = strip_expr(expr->right);
160 if (expr->type != EXPR_CALL)
161 return false;
163 if (expr->fn->type != EXPR_SYMBOL)
164 return false;
166 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
167 if (sym_name_is(func_table[i].name, expr->fn))
168 return true;
171 return false;
174 static void select_return_add(struct expression *expr, int param, char *key, char *value)
176 if (is_preempt_primitive(expr))
177 return;
178 preempt_count_add(NULL, NULL, NULL);
181 static void select_return_sub(struct expression *expr, int param, char *key, char *value)
183 if (is_preempt_primitive(expr))
184 return;
185 preempt_count_sub(NULL, NULL, NULL);
188 void check_preempt_info(int id)
190 int i;
192 my_id = id;
194 if (option_project != PROJ_KERNEL)
195 return;
197 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
198 if (func_table[i].type == PREEMPT_ADD)
199 add_function_hook(func_table[i].name, &preempt_count_add, NULL);
200 else
201 add_function_hook(func_table[i].name, &preempt_count_sub, NULL);
204 select_return_states_hook(PREEMPT_ADD, &select_return_add);
205 select_return_states_hook(PREEMPT_SUB, &select_return_sub);
207 add_split_return_callback(&match_return_info);