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
},
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
)
127 sql_insert_return_states(return_id
, return_ranges
,
128 (state
== &add
) ? PREEMPT_ADD
: PREEMPT_SUB
,
132 static void preempt_count_add(const char *fn
, struct expression
*expr
, void *_index
)
134 struct smatch_state
*state
;
138 /* If the state is already set then ignore it */
139 state
= get_state(my_id
, "preempt", NULL
);
141 set_state(my_id
, "preempt", NULL
, &ignore
);
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
;
152 /* If the state is already set then ignore it */
153 state
= get_state(my_id
, "preempt", NULL
);
155 set_state(my_id
, "preempt", NULL
, &ignore
);
157 set_state(my_id
, "preempt", NULL
, &sub
);
160 static bool is_preempt_primitive(struct expression
*expr
)
164 while (expr
->type
== EXPR_ASSIGNMENT
)
165 expr
= strip_expr(expr
->right
);
166 if (expr
->type
!= EXPR_CALL
)
169 if (expr
->fn
->type
!= EXPR_SYMBOL
)
172 for (i
= 0; i
< ARRAY_SIZE(func_table
); i
++) {
173 if (sym_name_is(func_table
[i
].name
, expr
->fn
))
180 static void select_return_add(struct expression
*expr
, int param
, char *key
, char *value
)
182 if (is_preempt_primitive(expr
))
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
))
191 preempt_count_sub(NULL
, NULL
, NULL
);
194 void check_preempt_info(int id
)
200 if (option_project
!= PROJ_KERNEL
)
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
);
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
);