@@ -90,7 +90,7 @@ int mb_cache_entry_create(struct mb_cache *cache, gfp_t mask, u32 key,
90
90
return - ENOMEM ;
91
91
92
92
INIT_LIST_HEAD (& entry -> e_list );
93
- /* One ref for hash, one ref returned */
93
+ /* Initial hash reference */
94
94
atomic_set (& entry -> e_refcnt , 1 );
95
95
entry -> e_key = key ;
96
96
entry -> e_value = value ;
@@ -106,21 +106,28 @@ int mb_cache_entry_create(struct mb_cache *cache, gfp_t mask, u32 key,
106
106
}
107
107
}
108
108
hlist_bl_add_head (& entry -> e_hash_list , head );
109
- hlist_bl_unlock (head );
110
-
109
+ /*
110
+ * Add entry to LRU list before it can be found by
111
+ * mb_cache_entry_delete() to avoid races
112
+ */
111
113
spin_lock (& cache -> c_list_lock );
112
114
list_add_tail (& entry -> e_list , & cache -> c_list );
113
- /* Grab ref for LRU list */
114
- atomic_inc (& entry -> e_refcnt );
115
115
cache -> c_entry_count ++ ;
116
116
spin_unlock (& cache -> c_list_lock );
117
+ hlist_bl_unlock (head );
117
118
118
119
return 0 ;
119
120
}
120
121
EXPORT_SYMBOL (mb_cache_entry_create );
121
122
122
- void __mb_cache_entry_free (struct mb_cache_entry * entry )
123
+ void __mb_cache_entry_free (struct mb_cache * cache , struct mb_cache_entry * entry )
123
124
{
125
+ struct hlist_bl_head * head ;
126
+
127
+ head = mb_cache_entry_head (cache , entry -> e_key );
128
+ hlist_bl_lock (head );
129
+ hlist_bl_del (& entry -> e_hash_list );
130
+ hlist_bl_unlock (head );
124
131
kmem_cache_free (mb_entry_cache , entry );
125
132
}
126
133
EXPORT_SYMBOL (__mb_cache_entry_free );
@@ -134,7 +141,7 @@ EXPORT_SYMBOL(__mb_cache_entry_free);
134
141
*/
135
142
void mb_cache_entry_wait_unused (struct mb_cache_entry * entry )
136
143
{
137
- wait_var_event (& entry -> e_refcnt , atomic_read (& entry -> e_refcnt ) <= 3 );
144
+ wait_var_event (& entry -> e_refcnt , atomic_read (& entry -> e_refcnt ) <= 2 );
138
145
}
139
146
EXPORT_SYMBOL (mb_cache_entry_wait_unused );
140
147
@@ -155,10 +162,9 @@ static struct mb_cache_entry *__entry_find(struct mb_cache *cache,
155
162
while (node ) {
156
163
entry = hlist_bl_entry (node , struct mb_cache_entry ,
157
164
e_hash_list );
158
- if (entry -> e_key == key && entry -> e_reusable ) {
159
- atomic_inc (& entry -> e_refcnt );
165
+ if (entry -> e_key == key && entry -> e_reusable &&
166
+ atomic_inc_not_zero (& entry -> e_refcnt ))
160
167
goto out ;
161
- }
162
168
node = node -> next ;
163
169
}
164
170
entry = NULL ;
@@ -218,10 +224,9 @@ struct mb_cache_entry *mb_cache_entry_get(struct mb_cache *cache, u32 key,
218
224
head = mb_cache_entry_head (cache , key );
219
225
hlist_bl_lock (head );
220
226
hlist_bl_for_each_entry (entry , node , head , e_hash_list ) {
221
- if (entry -> e_key == key && entry -> e_value == value ) {
222
- atomic_inc (& entry -> e_refcnt );
227
+ if (entry -> e_key == key && entry -> e_value == value &&
228
+ atomic_inc_not_zero (& entry -> e_refcnt ))
223
229
goto out ;
224
- }
225
230
}
226
231
entry = NULL ;
227
232
out :
@@ -244,37 +249,25 @@ EXPORT_SYMBOL(mb_cache_entry_get);
244
249
struct mb_cache_entry * mb_cache_entry_delete_or_get (struct mb_cache * cache ,
245
250
u32 key , u64 value )
246
251
{
247
- struct hlist_bl_node * node ;
248
- struct hlist_bl_head * head ;
249
252
struct mb_cache_entry * entry ;
250
253
251
- head = mb_cache_entry_head (cache , key );
252
- hlist_bl_lock (head );
253
- hlist_bl_for_each_entry (entry , node , head , e_hash_list ) {
254
- if (entry -> e_key == key && entry -> e_value == value ) {
255
- if (atomic_read (& entry -> e_refcnt ) > 2 ) {
256
- atomic_inc (& entry -> e_refcnt );
257
- hlist_bl_unlock (head );
258
- return entry ;
259
- }
260
- /* We keep hash list reference to keep entry alive */
261
- hlist_bl_del_init (& entry -> e_hash_list );
262
- hlist_bl_unlock (head );
263
- spin_lock (& cache -> c_list_lock );
264
- if (!list_empty (& entry -> e_list )) {
265
- list_del_init (& entry -> e_list );
266
- if (!WARN_ONCE (cache -> c_entry_count == 0 ,
267
- "mbcache: attempt to decrement c_entry_count past zero" ))
268
- cache -> c_entry_count -- ;
269
- atomic_dec (& entry -> e_refcnt );
270
- }
271
- spin_unlock (& cache -> c_list_lock );
272
- mb_cache_entry_put (cache , entry );
273
- return NULL ;
274
- }
275
- }
276
- hlist_bl_unlock (head );
254
+ entry = mb_cache_entry_get (cache , key , value );
255
+ if (!entry )
256
+ return NULL ;
257
+
258
+ /*
259
+ * Drop the ref we got from mb_cache_entry_get() and the initial hash
260
+ * ref if we are the last user
261
+ */
262
+ if (atomic_cmpxchg (& entry -> e_refcnt , 2 , 0 ) != 2 )
263
+ return entry ;
277
264
265
+ spin_lock (& cache -> c_list_lock );
266
+ if (!list_empty (& entry -> e_list ))
267
+ list_del_init (& entry -> e_list );
268
+ cache -> c_entry_count -- ;
269
+ spin_unlock (& cache -> c_list_lock );
270
+ __mb_cache_entry_free (cache , entry );
278
271
return NULL ;
279
272
}
280
273
EXPORT_SYMBOL (mb_cache_entry_delete_or_get );
@@ -306,42 +299,24 @@ static unsigned long mb_cache_shrink(struct mb_cache *cache,
306
299
unsigned long nr_to_scan )
307
300
{
308
301
struct mb_cache_entry * entry ;
309
- struct hlist_bl_head * head ;
310
302
unsigned long shrunk = 0 ;
311
303
312
304
spin_lock (& cache -> c_list_lock );
313
305
while (nr_to_scan -- && !list_empty (& cache -> c_list )) {
314
306
entry = list_first_entry (& cache -> c_list ,
315
307
struct mb_cache_entry , e_list );
316
- if (entry -> e_referenced || atomic_read (& entry -> e_refcnt ) > 2 ) {
308
+ /* Drop initial hash reference if there is no user */
309
+ if (entry -> e_referenced ||
310
+ atomic_cmpxchg (& entry -> e_refcnt , 1 , 0 ) != 1 ) {
317
311
entry -> e_referenced = 0 ;
318
312
list_move_tail (& entry -> e_list , & cache -> c_list );
319
313
continue ;
320
314
}
321
315
list_del_init (& entry -> e_list );
322
316
cache -> c_entry_count -- ;
323
- /*
324
- * We keep LRU list reference so that entry doesn't go away
325
- * from under us.
326
- */
327
317
spin_unlock (& cache -> c_list_lock );
328
- head = mb_cache_entry_head (cache , entry -> e_key );
329
- hlist_bl_lock (head );
330
- /* Now a reliable check if the entry didn't get used... */
331
- if (atomic_read (& entry -> e_refcnt ) > 2 ) {
332
- hlist_bl_unlock (head );
333
- spin_lock (& cache -> c_list_lock );
334
- list_add_tail (& entry -> e_list , & cache -> c_list );
335
- cache -> c_entry_count ++ ;
336
- continue ;
337
- }
338
- if (!hlist_bl_unhashed (& entry -> e_hash_list )) {
339
- hlist_bl_del_init (& entry -> e_hash_list );
340
- atomic_dec (& entry -> e_refcnt );
341
- }
342
- hlist_bl_unlock (head );
343
- if (mb_cache_entry_put (cache , entry ))
344
- shrunk ++ ;
318
+ __mb_cache_entry_free (cache , entry );
319
+ shrunk ++ ;
345
320
cond_resched ();
346
321
spin_lock (& cache -> c_list_lock );
347
322
}
@@ -433,11 +408,6 @@ void mb_cache_destroy(struct mb_cache *cache)
433
408
* point.
434
409
*/
435
410
list_for_each_entry_safe (entry , next , & cache -> c_list , e_list ) {
436
- if (!hlist_bl_unhashed (& entry -> e_hash_list )) {
437
- hlist_bl_del_init (& entry -> e_hash_list );
438
- atomic_dec (& entry -> e_refcnt );
439
- } else
440
- WARN_ON (1 );
441
411
list_del (& entry -> e_list );
442
412
WARN_ON (atomic_read (& entry -> e_refcnt ) != 1 );
443
413
mb_cache_entry_put (cache , entry );
0 commit comments