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.
37 #include "smatch_slist.h"
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
)
122 sql_insert_return_states(return_id
, return_ranges
,
123 (state
== &add
) ? PREEMPT_ADD
: PREEMPT_SUB
,
127 static void preempt_count_add(const char *fn
, struct expression
*expr
, void *_index
)
129 struct smatch_state
*state
;
133 /* If the state is already set then ignore it */
134 state
= get_state(my_id
, "preempt", NULL
);
136 set_state(my_id
, "preempt", NULL
, &ignore
);
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
;
147 /* If the state is already set then ignore it */
148 state
= get_state(my_id
, "preempt", NULL
);
150 set_state(my_id
, "preempt", NULL
, &ignore
);
152 set_state(my_id
, "preempt", NULL
, &sub
);
155 static bool is_preempt_primitive(struct expression
*expr
)
159 while (expr
->type
== EXPR_ASSIGNMENT
)
160 expr
= strip_expr(expr
->right
);
161 if (expr
->type
!= EXPR_CALL
)
164 if (expr
->fn
->type
!= EXPR_SYMBOL
)
167 for (i
= 0; i
< ARRAY_SIZE(func_table
); i
++) {
168 if (sym_name_is(func_table
[i
].name
, expr
->fn
))
175 static void select_return_add(struct expression
*expr
, int param
, char *key
, char *value
)
177 if (is_preempt_primitive(expr
))
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
))
186 preempt_count_sub(NULL
, NULL
, NULL
);
189 void check_preempt_info(int id
)
195 if (option_project
!= PROJ_KERNEL
)
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
);
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
);