]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - libxfs/cache.c
Perform true sequential bulk read prefetching in xfs_repair
[thirdparty/xfsprogs-dev.git] / libxfs / cache.c
CommitLineData
e80aa729
NS
1/*
2 * Copyright (c) 2006 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would 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.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23#include <pthread.h>
24
25#include <xfs/platform_defs.h>
f5015c24 26#include <xfs/list.h>
e80aa729
NS
27#include <xfs/cache.h>
28
29#define CACHE_DEBUG 1
00418cb4
NS
30#undef CACHE_DEBUG
31#define CACHE_DEBUG 1
32#undef CACHE_ABORT
33/* #define CACHE_ABORT 1 */
e80aa729 34
1c4110bd
NS
35static unsigned int cache_generic_bulkrelse(struct cache *, struct list_head *);
36
e80aa729
NS
37struct cache *
38cache_init(
39 unsigned int hashsize,
40 struct cache_operations *cache_operations)
41{
42 struct cache * cache;
43 unsigned int i, maxcount;
44
b6281496 45 maxcount = hashsize * HASH_CACHE_RATIO;
e80aa729
NS
46
47 if (!(cache = malloc(sizeof(struct cache))))
48 return NULL;
49 if (!(cache->c_hash = calloc(hashsize, sizeof(struct cache_hash)))) {
50 free(cache);
51 return NULL;
52 }
53
54 cache->c_count = 0;
9f38f08d
MV
55 cache->c_max = 0;
56 cache->c_hits = 0;
57 cache->c_misses = 0;
e80aa729
NS
58 cache->c_maxcount = maxcount;
59 cache->c_hashsize = hashsize;
60 cache->hash = cache_operations->hash;
61 cache->alloc = cache_operations->alloc;
33165ec3 62 cache->flush = cache_operations->flush;
e80aa729
NS
63 cache->relse = cache_operations->relse;
64 cache->compare = cache_operations->compare;
1c4110bd
NS
65 cache->bulkrelse = cache_operations->bulkrelse ?
66 cache_operations->bulkrelse : cache_generic_bulkrelse;
e80aa729
NS
67 pthread_mutex_init(&cache->c_mutex, NULL);
68
69 for (i = 0; i < hashsize; i++) {
70 list_head_init(&cache->c_hash[i].ch_list);
b6281496 71 cache->c_hash[i].ch_count = 0;
e80aa729
NS
72 pthread_mutex_init(&cache->c_hash[i].ch_mutex, NULL);
73 }
74 return cache;
75}
76
77void
78cache_walk(
79 struct cache * cache,
80 cache_walk_t visit)
81{
82 struct cache_hash * hash;
83 struct list_head * head;
84 struct list_head * pos;
85 unsigned int i;
86
87 for (i = 0; i < cache->c_hashsize; i++) {
88 hash = &cache->c_hash[i];
89 head = &hash->ch_list;
90 pthread_mutex_lock(&hash->ch_mutex);
91 for (pos = head->next; pos != head; pos = pos->next)
92 visit((struct cache_node *)pos);
93 pthread_mutex_unlock(&hash->ch_mutex);
94 }
95}
96
00418cb4
NS
97#ifdef CACHE_ABORT
98#define cache_abort() abort()
99#else
100#define cache_abort() do { } while (0)
101#endif
102
e80aa729
NS
103#ifdef CACHE_DEBUG
104static void
105cache_zero_check(
106 struct cache_node * node)
107{
108 if (node->cn_count > 0) {
109 fprintf(stderr, "%s: refcount is %u, not zero (node=%p)\n",
110 __FUNCTION__, node->cn_count, node);
00418cb4 111 cache_abort();
e80aa729
NS
112 }
113}
114#define cache_destroy_check(c) cache_walk((c), cache_zero_check)
115#else
116#define cache_destroy_check(c) do { } while (0)
117#endif
118
119void
120cache_destroy(
121 struct cache * cache)
122{
123 unsigned int i;
124
125 cache_destroy_check(cache);
126 for (i = 0; i < cache->c_hashsize; i++) {
127 list_head_destroy(&cache->c_hash[i].ch_list);
128 pthread_mutex_destroy(&cache->c_hash[i].ch_mutex);
129 }
130 pthread_mutex_destroy(&cache->c_mutex);
131 free(cache->c_hash);
132 free(cache);
133}
134
135static int
136cache_shake_node(
137 struct cache * cache,
138 cache_key_t key,
139 struct cache_node * node)
140{
141 struct list_head * head;
142 struct list_head * pos;
143 struct list_head * n;
144 struct cache_hash * hash;
145 int count = -1;
146
147 hash = cache->c_hash + cache->hash(key, cache->c_hashsize);
148 head = &hash->ch_list;
149 pthread_mutex_lock(&hash->ch_mutex);
150 for (pos = head->next, n = pos->next;
151 pos != head;
152 pos = n, n = pos->next) {
153 if ((struct cache_node *)pos != node)
154 continue;
155 pthread_mutex_lock(&node->cn_mutex);
156 count = node->cn_count;
157 pthread_mutex_unlock(&node->cn_mutex);
158 if (count != 0)
159 break;
160 pthread_mutex_destroy(&node->cn_mutex);
161 list_del_init(&node->cn_list);
b6281496 162 hash->ch_count--;
e80aa729
NS
163 cache->relse(node);
164 break;
165 }
166 pthread_mutex_unlock(&hash->ch_mutex);
167 return count;
168}
169
170/*
171 * We've hit the limit on cache size, so we need to start reclaiming
172 * nodes we've used. This reclaims from the one given hash bucket
173 * only. Returns the number of freed up nodes, its left to the
174 * caller to updates the global counter of used nodes for the cache.
175 * The hash chain lock is held for the hash list argument, must be
176 * dropped before returning.
177 * We walk backwards through the hash (remembering we keep recently
178 * used nodes toward the front) until we hit an in-use node. We'll
179 * stop there if its a low priority call but keep going if its not.
180 */
181static unsigned int
182cache_shake_hash(
183 struct cache * cache,
184 struct cache_hash * hash,
185 unsigned int priority)
186{
187 struct list_head temp;
188 struct list_head * head;
189 struct list_head * pos;
190 struct list_head * n;
191 struct cache_node * node;
192 unsigned int inuse = 0;
e80aa729
NS
193
194 list_head_init(&temp);
195 head = &hash->ch_list;
196 for (pos = head->prev, n = pos->prev;
197 pos != head;
198 pos = n, n = pos->prev) {
199 node = (struct cache_node *)pos;
200 pthread_mutex_lock(&node->cn_mutex);
b6281496
MV
201 if (!(inuse = (node->cn_count > 0))) {
202 hash->ch_count--;
61c58904 203 list_move_tail(&node->cn_list, &temp);
b6281496 204 }
e80aa729
NS
205 pthread_mutex_unlock(&node->cn_mutex);
206 if (inuse && !priority)
207 break;
208 }
209 pthread_mutex_unlock(&hash->ch_mutex);
1c4110bd
NS
210 return cache->bulkrelse(cache, &temp);
211}
212
213/*
214 * Generic implementation of bulk release, which just iterates over
215 * the list calling the single node relse routine for each node.
216 */
217static unsigned int
218cache_generic_bulkrelse(
219 struct cache * cache,
220 struct list_head * list)
221{
222 struct cache_node * node;
223 unsigned int count = 0;
224
225 while (!list_empty(list)) {
226 node = (struct cache_node *)list->next;
e80aa729
NS
227 pthread_mutex_destroy(&node->cn_mutex);
228 list_del_init(&node->cn_list);
229 cache->relse(node);
230 count++;
231 }
232 return count;
233}
234
235/*
236 * We've hit the limit on cache size, so we need to start reclaiming
237 * nodes we've used. Start by shaking this hash chain only, unless
238 * the shake priority has been increased already.
239 * The hash chain lock is held for the hash list argument, must be
240 * dropped before returning.
241 * Returns new priority at end of the call (in case we call again).
242 */
243static unsigned int
244cache_shake(
245 struct cache * cache,
246 struct cache_hash * hash,
247 unsigned int priority)
248{
249 unsigned int count;
250 unsigned int i;
251
252 if (!priority) { /* do just one */
253 count = cache_shake_hash(cache, hash, priority);
254 } else { /* use a bigger hammer */
255 pthread_mutex_unlock(&hash->ch_mutex);
256 for (count = 0, i = 0; i < cache->c_hashsize; i++) {
257 hash = &cache->c_hash[i];
258 pthread_mutex_lock(&hash->ch_mutex);
259 count += cache_shake_hash(cache, hash, priority - 1);
260 }
261 }
262 if (count) {
263 pthread_mutex_lock(&cache->c_mutex);
264 cache->c_count -= count;
265 pthread_mutex_unlock(&cache->c_mutex);
266 }
267 return ++priority;
268}
269
270/*
271 * Allocate a new hash node (updating atomic counter in the process),
272 * unless doing so will push us over the maximum cache size.
273 */
274struct cache_node *
275cache_node_allocate(
276 struct cache * cache,
2556c98b
BN
277 struct cache_hash * hashlist,
278 cache_key_t key)
e80aa729
NS
279{
280 unsigned int nodesfree;
281 struct cache_node * node;
282
283 pthread_mutex_lock(&cache->c_mutex);
9f38f08d 284 if ((nodesfree = (cache->c_count < cache->c_maxcount))) {
e80aa729 285 cache->c_count++;
9f38f08d
MV
286 if (cache->c_count > cache->c_max)
287 cache->c_max = cache->c_count;
288 }
289 cache->c_misses++;
e80aa729
NS
290 pthread_mutex_unlock(&cache->c_mutex);
291 if (!nodesfree)
292 return NULL;
2556c98b 293 if (!(node = cache->alloc(key))) { /* uh-oh */
e80aa729
NS
294 pthread_mutex_lock(&cache->c_mutex);
295 cache->c_count--;
296 pthread_mutex_unlock(&cache->c_mutex);
297 return NULL;
298 }
299 pthread_mutex_init(&node->cn_mutex, NULL);
300 list_head_init(&node->cn_list);
301 node->cn_count = 1;
302 return node;
303}
304
2556c98b
BN
305int
306cache_overflowed(
307 struct cache * cache)
308{
309 return (cache->c_maxcount == cache->c_max);
310}
311
e80aa729
NS
312/*
313 * Lookup in the cache hash table. With any luck we'll get a cache
314 * hit, in which case this will all be over quickly and painlessly.
315 * Otherwise, we allocate a new node, taking care not to expand the
316 * cache beyond the requested maximum size (shrink it if it would).
317 * Returns one if hit in cache, otherwise zero. A node is _always_
318 * returned, however.
319 */
320int
321cache_node_get(
322 struct cache * cache,
323 cache_key_t key,
324 struct cache_node ** nodep)
325{
326 struct cache_node * node = NULL;
327 struct cache_hash * hash;
328 struct list_head * head;
329 struct list_head * pos;
330 int priority = 0;
331 int allocated = 0;
332
333 hash = cache->c_hash + cache->hash(key, cache->c_hashsize);
334 head = &hash->ch_list;
335
336 restart:
337 pthread_mutex_lock(&hash->ch_mutex);
338 for (pos = head->next; pos != head; pos = pos->next) {
339 node = (struct cache_node *)pos;
340 if (cache->compare(node, key) == 0)
341 continue;
342 pthread_mutex_lock(&node->cn_mutex);
343 node->cn_count++;
344 pthread_mutex_unlock(&node->cn_mutex);
9f38f08d
MV
345 pthread_mutex_lock(&cache->c_mutex);
346 cache->c_hits++;
347 pthread_mutex_unlock(&cache->c_mutex);
e80aa729
NS
348 break;
349 }
350 if (pos == head) {
2556c98b 351 node = cache_node_allocate(cache, hash, key);
e80aa729
NS
352 if (!node) {
353 priority = cache_shake(cache, hash, priority);
354 goto restart;
355 }
356 allocated = 1;
b6281496 357 hash->ch_count++; /* new entry */
e80aa729
NS
358 }
359 /* looked at it, move to hash list head */
360 list_move(&node->cn_list, &hash->ch_list);
361 pthread_mutex_unlock(&hash->ch_mutex);
362 *nodep = node;
363 return allocated;
364}
365
366void
367cache_node_put(
368 struct cache_node * node)
369{
370 pthread_mutex_lock(&node->cn_mutex);
371#ifdef CACHE_DEBUG
372 if (node->cn_count < 1) {
373 fprintf(stderr, "%s: node put on refcount %u (node=%p)\n",
374 __FUNCTION__, node->cn_count, node);
00418cb4 375 cache_abort();
e80aa729
NS
376 }
377#endif
378 node->cn_count--;
379 pthread_mutex_unlock(&node->cn_mutex);
380}
381
382/*
383 * Purge a specific node from the cache. Reference count must be zero.
384 */
385int
386cache_node_purge(
387 struct cache * cache,
388 cache_key_t key,
389 struct cache_node * node)
390{
391 int refcount;
392
393 refcount = cache_shake_node(cache, key, node);
394 if (refcount == 0) {
395 pthread_mutex_lock(&cache->c_mutex);
396 cache->c_count--;
397 pthread_mutex_unlock(&cache->c_mutex);
398 }
399#ifdef CACHE_DEBUG
400 if (refcount >= 1) {
401 fprintf(stderr, "%s: refcount was %u, not zero (node=%p)\n",
402 __FUNCTION__, refcount, node);
00418cb4 403 cache_abort();
e80aa729
NS
404 }
405 if (refcount == -1) {
406 fprintf(stderr, "%s: purge node not found! (node=%p)\n",
407 __FUNCTION__, node);
00418cb4 408 cache_abort();
e80aa729
NS
409 }
410#endif
411 return (refcount == 0);
412}
413
414/*
415 * Purge all nodes from the cache. All reference counts must be zero.
416 */
417void
418cache_purge(
419 struct cache * cache)
420{
421 struct cache_hash * hash;
422
423 hash = &cache->c_hash[0];
424 pthread_mutex_lock(&hash->ch_mutex);
425 cache_shake(cache, hash, (unsigned int)-1);
426#ifdef CACHE_DEBUG
427 if (cache->c_count != 0) {
428 fprintf(stderr, "%s: shake on cache %p left %u nodes!?\n",
429 __FUNCTION__, cache, cache->c_count);
00418cb4 430 cache_abort();
e80aa729
NS
431 }
432#endif
33165ec3
BN
433 /* flush any remaining nodes to disk */
434 cache_flush(cache);
435}
436
437/*
2556c98b 438 * Flush all nodes in the cache to disk.
33165ec3
BN
439 */
440void
441cache_flush(
442 struct cache * cache)
443{
444 struct cache_hash * hash;
445 struct list_head * head;
446 struct list_head * pos;
447 struct cache_node * node;
448 int i;
2556c98b 449
33165ec3
BN
450 if (!cache->flush)
451 return;
2556c98b 452
33165ec3
BN
453 for (i = 0; i < cache->c_hashsize; i++) {
454 hash = &cache->c_hash[i];
2556c98b 455
33165ec3
BN
456 pthread_mutex_lock(&hash->ch_mutex);
457 head = &hash->ch_list;
458 for (pos = head->next; pos != head; pos = pos->next) {
459 node = (struct cache_node *)pos;
460 pthread_mutex_lock(&node->cn_mutex);
461 cache->flush(node);
462 pthread_mutex_unlock(&node->cn_mutex);
463 }
464 pthread_mutex_unlock(&hash->ch_mutex);
465 }
e80aa729 466}
9f38f08d 467
b6281496 468#define HASH_REPORT (3*HASH_CACHE_RATIO)
9f38f08d 469void
b6281496 470cache_report(FILE *fp, const char *name, struct cache * cache)
9f38f08d 471{
b6281496
MV
472 int i;
473 unsigned long count, index, total;
474 unsigned long hash_bucket_lengths[HASH_REPORT+2];
475
476 if ((cache->c_hits+cache->c_misses) == 0)
477 return;
478
479 /* report cache summary */
480 fprintf(fp, "%s: %p\n"
9f38f08d
MV
481 "Max supported entries = %u\n"
482 "Max utilized entries = %u\n"
483 "Active entries = %u\n"
484 "Hash table size = %u\n"
485 "Hits = %llu\n"
486 "Misses = %llu\n"
487 "Hit ratio = %5.2f\n",
488 name, cache,
489 cache->c_maxcount,
490 cache->c_max,
491 cache->c_count,
492 cache->c_hashsize,
493 cache->c_hits,
494 cache->c_misses,
495 (double) (cache->c_hits*100/(cache->c_hits+cache->c_misses))
496 );
b6281496
MV
497
498 /* report hash bucket lengths */
499 bzero(hash_bucket_lengths, sizeof(hash_bucket_lengths));
500
501 for (i = 0; i < cache->c_hashsize; i++) {
502 count = cache->c_hash[i].ch_count;
503 if (count > HASH_REPORT)
504 index = HASH_REPORT + 1;
505 else
506 index = count;
507 hash_bucket_lengths[index]++;
508 }
509
510 total = 0;
511 for (i = 0; i < HASH_REPORT+1; i++) {
512 total += i*hash_bucket_lengths[i];
513 if (hash_bucket_lengths[i] == 0)
514 continue;
2556c98b 515 fprintf(fp, "Hash buckets with %2d entries %5ld (%3ld%%)\n",
b6281496
MV
516 i, hash_bucket_lengths[i], (i*hash_bucket_lengths[i]*100)/cache->c_count);
517 }
518 if (hash_bucket_lengths[i]) /* last report bucket is the overflow bucket */
2556c98b 519 fprintf(fp, "Hash buckets with >%2d entries %5ld (%3ld%%)\n",
b6281496 520 i-1, hash_bucket_lengths[i], ((cache->c_count-total)*100)/cache->c_count);
9f38f08d 521}