2 * Copyright (C) 2018-2019 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 #include <sys/types.h>
46 #include <nbdkit-filter.h>
54 #ifndef HAVE_CACHE_RECLAIM
57 reclaim (int fd
, struct bitmap
*bm
)
62 #else /* HAVE_CACHE_RECLAIM */
64 /* If we are currently reclaiming blocks from the cache.
66 * The state machine starts in the NOT_RECLAIMING state. When the
67 * size of the cache exceeds the high threshold, we move to
68 * RECLAIMING_LRU. Once we have exhausted all LRU blocks, we move to
69 * RECLAIMING_ANY (reclaiming any blocks).
71 * If at any time the size of the cache goes below the low threshold
72 * we move back to the NOT_RECLAIMING state.
74 * A possible future enhancement is to add an extra state between LRU
75 * and ANY which reclaims blocks from lru.c:bm[1].
77 * reclaim_blk is the last block that we looked at.
85 static enum reclaim_state reclaiming
= NOT_RECLAIMING
;
86 static int64_t reclaim_blk
;
88 static void reclaim_one (int fd
, struct bitmap
*bm
);
89 static void reclaim_lru (int fd
, struct bitmap
*bm
);
90 static void reclaim_any (int fd
, struct bitmap
*bm
);
91 static void reclaim_block (int fd
, struct bitmap
*bm
);
94 reclaim (int fd
, struct bitmap
*bm
)
97 uint64_t cache_allocated
;
99 /* If the user didn't set cache-max-size, do nothing. */
100 if (max_size
== -1) return;
102 /* Check the allocated size of the cache. */
103 if (fstat (fd
, &statbuf
) == -1) {
104 nbdkit_debug ("cache: fstat: %m");
107 cache_allocated
= statbuf
.st_blocks
* UINT64_C(512);
110 /* Keep reclaiming until the cache size drops below the low threshold. */
111 if (cache_allocated
< max_size
* lo_thresh
/ 100) {
112 nbdkit_debug ("cache: stop reclaiming");
113 reclaiming
= NOT_RECLAIMING
;
118 if (cache_allocated
< max_size
* hi_thresh
/ 100)
121 /* Start reclaiming if the cache size goes over the high threshold. */
122 nbdkit_debug ("cache: start reclaiming");
123 reclaiming
= RECLAIMING_LRU
;
126 /* Reclaim up to 2 cache blocks. */
127 reclaim_one (fd
, bm
);
128 reclaim_one (fd
, bm
);
131 /* Reclaim a single cache block. */
133 reclaim_one (int fd
, struct bitmap
*bm
)
137 if (reclaiming
== RECLAIMING_LRU
)
138 reclaim_lru (fd
, bm
);
140 reclaim_any (fd
, bm
);
144 reclaim_lru (int fd
, struct bitmap
*bm
)
146 int64_t old_reclaim_blk
;
148 /* Find the next block in the cache. */
149 reclaim_blk
= bitmap_next (bm
, reclaim_blk
+1);
150 old_reclaim_blk
= reclaim_blk
;
152 /* Search for an LRU block after this one. */
154 if (! lru_has_been_recently_accessed (reclaim_blk
)) {
155 reclaim_block (fd
, bm
);
159 reclaim_blk
= bitmap_next (bm
, reclaim_blk
+1);
160 if (reclaim_blk
== -1) /* wrap around */
161 reclaim_blk
= bitmap_next (bm
, 0);
162 } while (reclaim_blk
>= 0 && old_reclaim_blk
!= reclaim_blk
);
164 if (old_reclaim_blk
== reclaim_blk
) {
165 /* Run out of LRU blocks, so start reclaiming any block in the cache. */
166 nbdkit_debug ("cache: reclaiming any blocks");
167 reclaiming
= RECLAIMING_ANY
;
168 reclaim_any (fd
, bm
);
173 reclaim_any (int fd
, struct bitmap
*bm
)
175 /* Find the next block in the cache. */
176 reclaim_blk
= bitmap_next (bm
, reclaim_blk
+1);
177 if (reclaim_blk
== -1) /* wrap around */
178 reclaim_blk
= bitmap_next (bm
, 0);
180 reclaim_block (fd
, bm
);
184 reclaim_block (int fd
, struct bitmap
*bm
)
186 if (reclaim_blk
== -1) {
187 nbdkit_debug ("cache: run out of blocks to reclaim!");
191 nbdkit_debug ("cache: reclaiming block %" PRIu64
, reclaim_blk
);
192 #ifdef FALLOC_FL_PUNCH_HOLE
193 if (fallocate (fd
, FALLOC_FL_PUNCH_HOLE
|FALLOC_FL_KEEP_SIZE
,
194 reclaim_blk
* blksize
, blksize
) == -1) {
195 nbdkit_error ("cache: reclaiming cache blocks: "
196 "fallocate: FALLOC_FL_PUNCH_HOLE: %m");
200 #error "no implementation for punching holes"
203 bitmap_set_blk (bm
, reclaim_blk
, 0);
206 #endif /* HAVE_CACHE_RECLAIM */