1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#include <linux/module.h>
27#include <linux/bitops.h>
28#include <linux/slab.h>
29#include <linux/string.h>
30#include <linux/seq_file.h>
31#include <linux/lru_cache.h>
32
33MODULE_AUTHOR("Philipp Reisner <phil@linbit.com>, "
34 "Lars Ellenberg <lars@linbit.com>");
35MODULE_DESCRIPTION("lru_cache - Track sets of hot objects");
36MODULE_LICENSE("GPL");
37
38
39
40#define PARANOIA_ENTRY() do { \
41 BUG_ON(!lc); \
42 BUG_ON(!lc->nr_elements); \
43 BUG_ON(test_and_set_bit(__LC_PARANOIA, &lc->flags)); \
44} while (0)
45
46#define RETURN(x...) do { \
47 clear_bit_unlock(__LC_PARANOIA, &lc->flags); \
48 return x ; } while (0)
49
50
51#define PARANOIA_LC_ELEMENT(lc, e) do { \
52 struct lru_cache *lc_ = (lc); \
53 struct lc_element *e_ = (e); \
54 unsigned i = e_->lc_index; \
55 BUG_ON(i >= lc_->nr_elements); \
56 BUG_ON(lc_->lc_element[i] != e_); } while (0)
57
58
59
60
61
62
63
64
65
66
67int lc_try_lock(struct lru_cache *lc)
68{
69 unsigned long val;
70 do {
71 val = cmpxchg(&lc->flags, 0, LC_LOCKED);
72 } while (unlikely (val == LC_PARANOIA));
73
74 return 0 == val;
75#if 0
76
77
78 unsigned long old, new, val;
79 do {
80 old = lc->flags & LC_PARANOIA;
81 new = old | LC_LOCKED;
82 val = cmpxchg(&lc->flags, old, new);
83 } while (unlikely (val == (old ^ LC_PARANOIA)));
84 return old == val;
85#endif
86}
87
88
89
90
91
92
93
94
95
96
97
98
99struct lru_cache *lc_create(const char *name, struct kmem_cache *cache,
100 unsigned max_pending_changes,
101 unsigned e_count, size_t e_size, size_t e_off)
102{
103 struct hlist_head *slot = NULL;
104 struct lc_element **element = NULL;
105 struct lru_cache *lc;
106 struct lc_element *e;
107 unsigned cache_obj_size = kmem_cache_size(cache);
108 unsigned i;
109
110 WARN_ON(cache_obj_size < e_size);
111 if (cache_obj_size < e_size)
112 return NULL;
113
114
115
116 if (e_count > LC_MAX_ACTIVE)
117 return NULL;
118
119 slot = kcalloc(e_count, sizeof(struct hlist_head), GFP_KERNEL);
120 if (!slot)
121 goto out_fail;
122 element = kzalloc(e_count * sizeof(struct lc_element *), GFP_KERNEL);
123 if (!element)
124 goto out_fail;
125
126 lc = kzalloc(sizeof(*lc), GFP_KERNEL);
127 if (!lc)
128 goto out_fail;
129
130 INIT_LIST_HEAD(&lc->in_use);
131 INIT_LIST_HEAD(&lc->lru);
132 INIT_LIST_HEAD(&lc->free);
133 INIT_LIST_HEAD(&lc->to_be_changed);
134
135 lc->name = name;
136 lc->element_size = e_size;
137 lc->element_off = e_off;
138 lc->nr_elements = e_count;
139 lc->max_pending_changes = max_pending_changes;
140 lc->lc_cache = cache;
141 lc->lc_element = element;
142 lc->lc_slot = slot;
143
144
145 for (i = 0; i < e_count; i++) {
146 void *p = kmem_cache_alloc(cache, GFP_KERNEL);
147 if (!p)
148 break;
149 memset(p, 0, lc->element_size);
150 e = p + e_off;
151 e->lc_index = i;
152 e->lc_number = LC_FREE;
153 e->lc_new_number = LC_FREE;
154 list_add(&e->list, &lc->free);
155 element[i] = e;
156 }
157 if (i == e_count)
158 return lc;
159
160
161 for (i--; i; i--) {
162 void *p = element[i];
163 kmem_cache_free(cache, p - e_off);
164 }
165 kfree(lc);
166out_fail:
167 kfree(element);
168 kfree(slot);
169 return NULL;
170}
171
172void lc_free_by_index(struct lru_cache *lc, unsigned i)
173{
174 void *p = lc->lc_element[i];
175 WARN_ON(!p);
176 if (p) {
177 p -= lc->element_off;
178 kmem_cache_free(lc->lc_cache, p);
179 }
180}
181
182
183
184
185
186void lc_destroy(struct lru_cache *lc)
187{
188 unsigned i;
189 if (!lc)
190 return;
191 for (i = 0; i < lc->nr_elements; i++)
192 lc_free_by_index(lc, i);
193 kfree(lc->lc_element);
194 kfree(lc->lc_slot);
195 kfree(lc);
196}
197
198
199
200
201
202
203
204
205void lc_reset(struct lru_cache *lc)
206{
207 unsigned i;
208
209 INIT_LIST_HEAD(&lc->in_use);
210 INIT_LIST_HEAD(&lc->lru);
211 INIT_LIST_HEAD(&lc->free);
212 INIT_LIST_HEAD(&lc->to_be_changed);
213 lc->used = 0;
214 lc->hits = 0;
215 lc->misses = 0;
216 lc->starving = 0;
217 lc->locked = 0;
218 lc->changed = 0;
219 lc->pending_changes = 0;
220 lc->flags = 0;
221 memset(lc->lc_slot, 0, sizeof(struct hlist_head) * lc->nr_elements);
222
223 for (i = 0; i < lc->nr_elements; i++) {
224 struct lc_element *e = lc->lc_element[i];
225 void *p = e;
226 p -= lc->element_off;
227 memset(p, 0, lc->element_size);
228
229 e->lc_index = i;
230 e->lc_number = LC_FREE;
231 e->lc_new_number = LC_FREE;
232 list_add(&e->list, &lc->free);
233 }
234}
235
236
237
238
239
240
241size_t lc_seq_printf_stats(struct seq_file *seq, struct lru_cache *lc)
242{
243
244
245
246
247
248
249
250 return seq_printf(seq, "\t%s: used:%u/%u "
251 "hits:%lu misses:%lu starving:%lu locked:%lu changed:%lu\n",
252 lc->name, lc->used, lc->nr_elements,
253 lc->hits, lc->misses, lc->starving, lc->locked, lc->changed);
254}
255
256static struct hlist_head *lc_hash_slot(struct lru_cache *lc, unsigned int enr)
257{
258 return lc->lc_slot + (enr % lc->nr_elements);
259}
260
261
262static struct lc_element *__lc_find(struct lru_cache *lc, unsigned int enr,
263 bool include_changing)
264{
265 struct hlist_node *n;
266 struct lc_element *e;
267
268 BUG_ON(!lc);
269 BUG_ON(!lc->nr_elements);
270 hlist_for_each_entry(e, n, lc_hash_slot(lc, enr), colision) {
271
272
273
274 if (e->lc_new_number != enr)
275 continue;
276 if (e->lc_new_number == e->lc_number || include_changing)
277 return e;
278 break;
279 }
280 return NULL;
281}
282
283
284
285
286
287
288
289
290
291
292
293
294struct lc_element *lc_find(struct lru_cache *lc, unsigned int enr)
295{
296 return __lc_find(lc, enr, 0);
297}
298
299
300
301
302
303
304
305
306
307
308
309bool lc_is_used(struct lru_cache *lc, unsigned int enr)
310{
311 struct lc_element *e = __lc_find(lc, enr, 1);
312 return e && e->refcnt;
313}
314
315
316
317
318
319
320
321
322
323void lc_del(struct lru_cache *lc, struct lc_element *e)
324{
325 PARANOIA_ENTRY();
326 PARANOIA_LC_ELEMENT(lc, e);
327 BUG_ON(e->refcnt);
328
329 e->lc_number = e->lc_new_number = LC_FREE;
330 hlist_del_init(&e->colision);
331 list_move(&e->list, &lc->free);
332 RETURN();
333}
334
335static struct lc_element *lc_prepare_for_change(struct lru_cache *lc, unsigned new_number)
336{
337 struct list_head *n;
338 struct lc_element *e;
339
340 if (!list_empty(&lc->free))
341 n = lc->free.next;
342 else if (!list_empty(&lc->lru))
343 n = lc->lru.prev;
344 else
345 return NULL;
346
347 e = list_entry(n, struct lc_element, list);
348 PARANOIA_LC_ELEMENT(lc, e);
349
350 e->lc_new_number = new_number;
351 if (!hlist_unhashed(&e->colision))
352 __hlist_del(&e->colision);
353 hlist_add_head(&e->colision, lc_hash_slot(lc, new_number));
354 list_move(&e->list, &lc->to_be_changed);
355
356 return e;
357}
358
359static int lc_unused_element_available(struct lru_cache *lc)
360{
361 if (!list_empty(&lc->free))
362 return 1;
363 if (!list_empty(&lc->lru))
364 return 1;
365
366 return 0;
367}
368
369static struct lc_element *__lc_get(struct lru_cache *lc, unsigned int enr, bool may_change)
370{
371 struct lc_element *e;
372
373 PARANOIA_ENTRY();
374 if (lc->flags & LC_STARVING) {
375 ++lc->starving;
376 RETURN(NULL);
377 }
378
379 e = __lc_find(lc, enr, 1);
380
381
382
383
384 if (e && e->lc_new_number == e->lc_number) {
385 ++lc->hits;
386 if (e->refcnt++ == 0)
387 lc->used++;
388 list_move(&e->list, &lc->in_use);
389 RETURN(e);
390 }
391
392 ++lc->misses;
393 if (!may_change)
394 RETURN(NULL);
395
396
397
398
399 if (e)
400 RETURN(NULL);
401
402
403
404 test_and_set_bit(__LC_DIRTY, &lc->flags);
405
406
407
408
409 if (test_bit(__LC_LOCKED, &lc->flags)) {
410 ++lc->locked;
411 RETURN(NULL);
412 }
413
414
415
416
417 if (!lc_unused_element_available(lc)) {
418 __set_bit(__LC_STARVING, &lc->flags);
419 RETURN(NULL);
420 }
421
422
423
424
425 if (lc->pending_changes >= lc->max_pending_changes)
426 RETURN(NULL);
427
428 e = lc_prepare_for_change(lc, enr);
429 BUG_ON(!e);
430
431 clear_bit(__LC_STARVING, &lc->flags);
432 BUG_ON(++e->refcnt != 1);
433 lc->used++;
434 lc->pending_changes++;
435
436 RETURN(e);
437}
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479struct lc_element *lc_get(struct lru_cache *lc, unsigned int enr)
480{
481 return __lc_get(lc, enr, 1);
482}
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500struct lc_element *lc_try_get(struct lru_cache *lc, unsigned int enr)
501{
502 return __lc_get(lc, enr, 0);
503}
504
505
506
507
508
509
510
511
512
513void lc_committed(struct lru_cache *lc)
514{
515 struct lc_element *e, *tmp;
516
517 PARANOIA_ENTRY();
518 list_for_each_entry_safe(e, tmp, &lc->to_be_changed, list) {
519
520 ++lc->changed;
521 e->lc_number = e->lc_new_number;
522 list_move(&e->list, &lc->in_use);
523 }
524 lc->pending_changes = 0;
525 RETURN();
526}
527
528
529
530
531
532
533
534
535
536
537
538unsigned int lc_put(struct lru_cache *lc, struct lc_element *e)
539{
540 PARANOIA_ENTRY();
541 PARANOIA_LC_ELEMENT(lc, e);
542 BUG_ON(e->refcnt == 0);
543 BUG_ON(e->lc_number != e->lc_new_number);
544 if (--e->refcnt == 0) {
545
546 list_move(&e->list, &lc->lru);
547 lc->used--;
548 clear_bit_unlock(__LC_STARVING, &lc->flags);
549 }
550 RETURN(e->refcnt);
551}
552
553
554
555
556
557
558struct lc_element *lc_element_by_index(struct lru_cache *lc, unsigned i)
559{
560 BUG_ON(i >= lc->nr_elements);
561 BUG_ON(lc->lc_element[i] == NULL);
562 BUG_ON(lc->lc_element[i]->lc_index != i);
563 return lc->lc_element[i];
564}
565
566
567
568
569
570
571unsigned int lc_index_of(struct lru_cache *lc, struct lc_element *e)
572{
573 PARANOIA_LC_ELEMENT(lc, e);
574 return e->lc_index;
575}
576
577
578
579
580
581
582
583
584
585void lc_set(struct lru_cache *lc, unsigned int enr, int index)
586{
587 struct lc_element *e;
588 struct list_head *lh;
589
590 if (index < 0 || index >= lc->nr_elements)
591 return;
592
593 e = lc_element_by_index(lc, index);
594 BUG_ON(e->lc_number != e->lc_new_number);
595 BUG_ON(e->refcnt != 0);
596
597 e->lc_number = e->lc_new_number = enr;
598 hlist_del_init(&e->colision);
599 if (enr == LC_FREE)
600 lh = &lc->free;
601 else {
602 hlist_add_head(&e->colision, lc_hash_slot(lc, enr));
603 lh = &lc->lru;
604 }
605 list_move(&e->list, lh);
606}
607
608
609
610
611
612
613
614
615
616void lc_seq_dump_details(struct seq_file *seq, struct lru_cache *lc, char *utext,
617 void (*detail) (struct seq_file *, struct lc_element *))
618{
619 unsigned int nr_elements = lc->nr_elements;
620 struct lc_element *e;
621 int i;
622
623 seq_printf(seq, "\tnn: lc_number refcnt %s\n ", utext);
624 for (i = 0; i < nr_elements; i++) {
625 e = lc_element_by_index(lc, i);
626 if (e->lc_number == LC_FREE) {
627 seq_printf(seq, "\t%2d: FREE\n", i);
628 } else {
629 seq_printf(seq, "\t%2d: %4u %4u ", i,
630 e->lc_number, e->refcnt);
631 detail(seq, e);
632 }
633 }
634}
635
636EXPORT_SYMBOL(lc_create);
637EXPORT_SYMBOL(lc_reset);
638EXPORT_SYMBOL(lc_destroy);
639EXPORT_SYMBOL(lc_set);
640EXPORT_SYMBOL(lc_del);
641EXPORT_SYMBOL(lc_try_get);
642EXPORT_SYMBOL(lc_find);
643EXPORT_SYMBOL(lc_get);
644EXPORT_SYMBOL(lc_put);
645EXPORT_SYMBOL(lc_committed);
646EXPORT_SYMBOL(lc_element_by_index);
647EXPORT_SYMBOL(lc_index_of);
648EXPORT_SYMBOL(lc_seq_printf_stats);
649EXPORT_SYMBOL(lc_seq_dump_details);
650EXPORT_SYMBOL(lc_try_lock);
651EXPORT_SYMBOL(lc_is_used);
652