param_key: fix container of when no struct member is referenced
[smatch.git] / check_preempt_info.c
blob13232d4dc30dc883130fe7f3404b1477a8ad940e
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_dec_and_test", PREEMPT_SUB },
86 { "__preempt_count_sub", PREEMPT_SUB },
87 { "local_bh_enable", PREEMPT_SUB },
88 { "__local_bh_enable_ip", PREEMPT_SUB },
89 { "spin_unlock", PREEMPT_SUB },
90 { "_spin_unlock", PREEMPT_SUB },
91 { "__spin_unlock", PREEMPT_SUB },
92 { "raw_spin_unlock", PREEMPT_SUB },
93 { "_raw_spin_unlock", PREEMPT_SUB },
94 { "__raw_spin_unlock", PREEMPT_SUB },
95 { "spin_unlock_irq", PREEMPT_SUB },
96 { "_spin_unlock_irq", PREEMPT_SUB },
97 { "__spin_unlock_irq", PREEMPT_SUB },
98 { "_raw_spin_unlock_irq", PREEMPT_SUB },
99 { "__raw_spin_unlock_irq", PREEMPT_SUB },
100 { "spin_unlock_irqrestore", PREEMPT_SUB },
101 { "_spin_unlock_irqrestore", PREEMPT_SUB },
102 { "__spin_unlock_irqrestore", PREEMPT_SUB },
103 { "_raw_spin_unlock_irqrestore", PREEMPT_SUB },
104 { "__raw_spin_unlock_irqrestore", PREEMPT_SUB },
105 { "spin_unlock_bh", PREEMPT_SUB },
106 { "_spin_unlock_bh", PREEMPT_SUB },
107 { "__spin_unlock_bh", PREEMPT_SUB },
108 { "task_rq_unlock", PREEMPT_SUB },
109 { "queue_request_and_unlock", PREEMPT_SUB },
110 { "netif_tx_unlock_bh", PREEMPT_SUB },
111 { "mt76_tx_status_unlock", PREEMPT_SUB },
114 static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
116 struct smatch_state *state;
118 state = get_state(my_id, "preempt", NULL);
119 if (state != &add && state != &sub)
120 return;
122 sql_insert_return_states(return_id, return_ranges,
123 (state == &add) ? PREEMPT_ADD : PREEMPT_SUB,
124 -1, "", "");
127 static void preempt_count_add(const char *fn, struct expression *expr, void *_index)
129 struct smatch_state *state;
131 __preempt_add();
133 /* If the state is already set then ignore it */
134 state = get_state(my_id, "preempt", NULL);
135 if (state)
136 set_state(my_id, "preempt", NULL, &ignore);
137 else
138 set_state(my_id, "preempt", NULL, &add);
141 static void preempt_count_sub(const char *fn, struct expression *expr, void *_index)
143 struct smatch_state *state;
145 __preempt_sub();
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, &sub);
155 static bool is_preempt_primitive(struct expression *expr)
157 int i;
159 while (expr->type == EXPR_ASSIGNMENT)
160 expr = strip_expr(expr->right);
161 if (expr->type != EXPR_CALL)
162 return false;
164 if (expr->fn->type != EXPR_SYMBOL)
165 return false;
167 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
168 if (sym_name_is(func_table[i].name, expr->fn))
169 return true;
172 return false;
175 static void select_return_add(struct expression *expr, int param, char *key, char *value)
177 if (is_preempt_primitive(expr))
178 return;
179 preempt_count_add(NULL, NULL, NULL);
182 static void select_return_sub(struct expression *expr, int param, char *key, char *value)
184 if (is_preempt_primitive(expr))
185 return;
186 preempt_count_sub(NULL, NULL, NULL);
189 void check_preempt_info(int id)
191 int i;
193 my_id = id;
195 if (option_project != PROJ_KERNEL)
196 return;
198 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
199 if (func_table[i].type == PREEMPT_ADD)
200 add_function_hook(func_table[i].name, &preempt_count_add, NULL);
201 else
202 add_function_hook(func_table[i].name, &preempt_count_sub, NULL);
205 select_return_states_hook(PREEMPT_ADD, &select_return_add);
206 select_return_states_hook(PREEMPT_SUB, &select_return_sub);
208 add_split_return_callback(&match_return_info);