14
14
#include <linux/limits.h>
15
15
#include <linux/list.h>
16
16
#include <linux/mutex.h>
17
+ #include <linux/rculist.h>
17
18
#include <linux/rbtree.h>
18
19
#include <linux/rwsem.h>
19
20
#include <linux/serdev.h>
@@ -145,133 +146,96 @@ static bool ssam_event_matches_notifier(const struct ssam_event_notifier *n,
145
146
*/
146
147
static int ssam_nfblk_call_chain (struct ssam_nf_head * nh , struct ssam_event * event )
147
148
{
148
- struct ssam_notifier_block * nb , * next_nb ;
149
149
struct ssam_event_notifier * nf ;
150
150
int ret = 0 , idx ;
151
151
152
152
idx = srcu_read_lock (& nh -> srcu );
153
153
154
- nb = rcu_dereference_raw (nh -> head );
155
- while (nb ) {
156
- nf = container_of (nb , struct ssam_event_notifier , base );
157
- next_nb = rcu_dereference_raw (nb -> next );
158
-
154
+ list_for_each_entry_rcu (nf , & nh -> head , base .node ) {
159
155
if (ssam_event_matches_notifier (nf , event )) {
160
- ret = (ret & SSAM_NOTIF_STATE_MASK ) | nb -> fn (nf , event );
156
+ ret = (ret & SSAM_NOTIF_STATE_MASK ) | nf -> base . fn (nf , event );
161
157
if (ret & SSAM_NOTIF_STOP )
162
158
break ;
163
159
}
164
-
165
- nb = next_nb ;
166
160
}
167
161
168
162
srcu_read_unlock (& nh -> srcu , idx );
169
163
return ret ;
170
164
}
171
165
172
166
/**
173
- * __ssam_nfblk_insert () - Insert a new notifier block into the given notifier
167
+ * ssam_nfblk_insert () - Insert a new notifier block into the given notifier
174
168
* list.
175
169
* @nh: The notifier head into which the block should be inserted.
176
170
* @nb: The notifier block to add.
177
171
*
178
172
* Note: This function must be synchronized by the caller with respect to other
179
- * insert and/or remove calls.
173
+ * insert, find, and/or remove calls.
180
174
*
181
175
* Return: Returns zero on success, %-EEXIST if the notifier block has already
182
176
* been registered.
183
177
*/
184
- static int __ssam_nfblk_insert (struct ssam_nf_head * nh , struct ssam_notifier_block * nb )
178
+ static int ssam_nfblk_insert (struct ssam_nf_head * nh , struct ssam_notifier_block * nb )
185
179
{
186
- struct ssam_notifier_block * * link = & nh -> head ;
180
+ struct ssam_notifier_block * p ;
181
+ struct list_head * h ;
187
182
188
- while (* link ) {
189
- if (unlikely (* link == nb )) {
183
+ /* Runs under lock, no need for RCU variant. */
184
+ list_for_each (h , & nh -> head ) {
185
+ p = list_entry (h , struct ssam_notifier_block , node );
186
+
187
+ if (unlikely (p == nb )) {
190
188
WARN (1 , "double register detected" );
191
189
return - EEXIST ;
192
190
}
193
191
194
- if (nb -> priority > ( * link ) -> priority )
192
+ if (nb -> priority > p -> priority )
195
193
break ;
196
-
197
- link = & ((* link )-> next );
198
194
}
199
195
200
- nb -> next = * link ;
201
- rcu_assign_pointer (* link , nb );
202
-
196
+ list_add_tail_rcu (& nb -> node , h );
203
197
return 0 ;
204
198
}
205
199
206
200
/**
207
- * __ssam_nfblk_find_link() - Find a notifier block link on the given list.
208
- * @nh: The notifier head on which the search should be conducted.
201
+ * ssam_nfblk_find() - Check if a notifier block is registered on the given
202
+ * notifier head.
203
+ * list.
204
+ * @nh: The notifier head on which to search.
209
205
* @nb: The notifier block to search for.
210
206
*
211
- * Note: This function must be synchronized by the caller with respect to
212
- * insert and/or remove calls.
207
+ * Note: This function must be synchronized by the caller with respect to other
208
+ * insert, find, and/or remove calls.
213
209
*
214
- * Return: Returns a pointer to the link (i.e. pointer pointing) to the given
215
- * notifier block, from the previous node in the list, or %NULL if the given
216
- * notifier block is not contained in the notifier list.
210
+ * Return: Returns true if the given notifier block is registered on the given
211
+ * notifier head, false otherwise.
217
212
*/
218
- static struct ssam_notifier_block * * __ssam_nfblk_find_link (
219
- struct ssam_nf_head * nh , struct ssam_notifier_block * nb )
213
+ static bool ssam_nfblk_find (struct ssam_nf_head * nh , struct ssam_notifier_block * nb )
220
214
{
221
- struct ssam_notifier_block * * link = & nh -> head ;
222
-
223
- while (* link ) {
224
- if (* link == nb )
225
- return link ;
215
+ struct ssam_notifier_block * p ;
226
216
227
- link = & ((* link )-> next );
217
+ /* Runs under lock, no need for RCU variant. */
218
+ list_for_each_entry (p , & nh -> head , node ) {
219
+ if (p == nb )
220
+ return true;
228
221
}
229
222
230
- return NULL ;
223
+ return false ;
231
224
}
232
225
233
226
/**
234
- * __ssam_nfblk_erase() - Erase a notifier block link in the given notifier
235
- * list.
236
- * @link: The link to be erased.
227
+ * ssam_nfblk_remove() - Remove a notifier block from its notifier list.
228
+ * @nb: The notifier block to be removed.
237
229
*
238
230
* Note: This function must be synchronized by the caller with respect to
239
- * other insert and/or remove/erase/find calls. The caller _must_ ensure SRCU
231
+ * other insert, find and/or remove calls. The caller _must_ ensure SRCU
240
232
* synchronization by calling synchronize_srcu() with ``nh->srcu`` after
241
233
* leaving the critical section, to ensure that the removed notifier block is
242
234
* not in use any more.
243
235
*/
244
- static void __ssam_nfblk_erase (struct ssam_notifier_block * * link )
236
+ static void ssam_nfblk_remove (struct ssam_notifier_block * nb )
245
237
{
246
- rcu_assign_pointer (* link , (* link )-> next );
247
- }
248
-
249
-
250
- /**
251
- * __ssam_nfblk_remove() - Remove a notifier block from the given notifier list.
252
- * @nh: The notifier head from which the block should be removed.
253
- * @nb: The notifier block to remove.
254
- *
255
- * Note: This function must be synchronized by the caller with respect to
256
- * other insert and/or remove calls. On success, the caller *must* ensure SRCU
257
- * synchronization by calling synchronize_srcu() with ``nh->srcu`` after
258
- * leaving the critical section, to ensure that the removed notifier block is
259
- * not in use any more.
260
- *
261
- * Return: Returns zero on success, %-ENOENT if the specified notifier block
262
- * could not be found on the notifier list.
263
- */
264
- static int __ssam_nfblk_remove (struct ssam_nf_head * nh ,
265
- struct ssam_notifier_block * nb )
266
- {
267
- struct ssam_notifier_block * * link ;
268
-
269
- link = __ssam_nfblk_find_link (nh , nb );
270
- if (!link )
271
- return - ENOENT ;
272
-
273
- __ssam_nfblk_erase (link );
274
- return 0 ;
238
+ list_del_rcu (& nb -> node );
275
239
}
276
240
277
241
/**
@@ -286,7 +250,7 @@ static int ssam_nf_head_init(struct ssam_nf_head *nh)
286
250
if (status )
287
251
return status ;
288
252
289
- nh -> head = NULL ;
253
+ INIT_LIST_HEAD ( & nh -> head ) ;
290
254
return 0 ;
291
255
}
292
256
@@ -2153,7 +2117,7 @@ int ssam_notifier_register(struct ssam_controller *ctrl,
2153
2117
n -> event .reg .target_category , n -> event .id .target_category ,
2154
2118
n -> event .id .instance , entry -> refcount );
2155
2119
2156
- status = __ssam_nfblk_insert (nf_head , & n -> base );
2120
+ status = ssam_nfblk_insert (nf_head , & n -> base );
2157
2121
if (status ) {
2158
2122
entry = ssam_nf_refcount_dec (nf , n -> event .reg , n -> event .id );
2159
2123
if (entry -> refcount == 0 )
@@ -2167,7 +2131,7 @@ int ssam_notifier_register(struct ssam_controller *ctrl,
2167
2131
status = ssam_ssh_event_enable (ctrl , n -> event .reg , n -> event .id ,
2168
2132
n -> event .flags );
2169
2133
if (status ) {
2170
- __ssam_nfblk_remove ( nf_head , & n -> base );
2134
+ ssam_nfblk_remove ( & n -> base );
2171
2135
kfree (ssam_nf_refcount_dec (nf , n -> event .reg , n -> event .id ));
2172
2136
mutex_unlock (& nf -> lock );
2173
2137
synchronize_srcu (& nf_head -> srcu );
@@ -2206,7 +2170,6 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl,
2206
2170
struct ssam_event_notifier * n )
2207
2171
{
2208
2172
u16 rqid = ssh_tc_to_rqid (n -> event .id .target_category );
2209
- struct ssam_notifier_block * * link ;
2210
2173
struct ssam_nf_refcount_entry * entry ;
2211
2174
struct ssam_nf_head * nf_head ;
2212
2175
struct ssam_nf * nf ;
@@ -2220,16 +2183,21 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl,
2220
2183
2221
2184
mutex_lock (& nf -> lock );
2222
2185
2223
- link = __ssam_nfblk_find_link (nf_head , & n -> base );
2224
- if (!link ) {
2186
+ if (!ssam_nfblk_find (nf_head , & n -> base )) {
2225
2187
mutex_unlock (& nf -> lock );
2226
2188
return - ENOENT ;
2227
2189
}
2228
2190
2229
2191
entry = ssam_nf_refcount_dec (nf , n -> event .reg , n -> event .id );
2230
2192
if (WARN_ON (!entry )) {
2231
- mutex_unlock (& nf -> lock );
2232
- return - ENOENT ;
2193
+ /*
2194
+ * If this does not return an entry, there's a logic error
2195
+ * somewhere: The notifier block is registered, but the event
2196
+ * refcount entry is not there. Remove the notifier block
2197
+ * anyways.
2198
+ */
2199
+ status = - ENOENT ;
2200
+ goto remove ;
2233
2201
}
2234
2202
2235
2203
ssam_dbg (ctrl , "disabling event (reg: 0x%02x, tc: 0x%02x, iid: 0x%02x, rc: %d)\n" ,
@@ -2249,7 +2217,8 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl,
2249
2217
kfree (entry );
2250
2218
}
2251
2219
2252
- __ssam_nfblk_erase (link );
2220
+ remove :
2221
+ ssam_nfblk_remove (& n -> base );
2253
2222
mutex_unlock (& nf -> lock );
2254
2223
synchronize_srcu (& nf_head -> srcu );
2255
2224
0 commit comments