2 * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * 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.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /** @addtogroup lwext4
34 * @brief Block cache allocator.
37 #include "ext4_config.h"
38 #include "ext4_bcache.h"
39 #include "ext4_debug.h"
40 #include "ext4_errno.h"
45 static int ext4_bcache_lba_compare(struct ext4_buf *a, struct ext4_buf *b)
49 else if (a->lba < b->lba)
54 static int ext4_bcache_lru_compare(struct ext4_buf *a, struct ext4_buf *b)
56 if (a->lru_id > b->lru_id)
58 else if (a->lru_id < b->lru_id)
63 RB_GENERATE_INTERNAL(ext4_buf_lba, ext4_buf, lba_node,
64 ext4_bcache_lba_compare, static inline)
65 RB_GENERATE_INTERNAL(ext4_buf_lru, ext4_buf, lru_node,
66 ext4_bcache_lru_compare, static inline)
68 int ext4_bcache_init_dynamic(struct ext4_bcache *bc, uint32_t cnt,
71 ext4_assert(bc && cnt && itemsize);
73 memset(bc, 0, sizeof(struct ext4_bcache));
76 bc->itemsize = itemsize;
78 bc->max_ref_blocks = 0;
83 int ext4_bcache_fini_dynamic(struct ext4_bcache *bc)
85 memset(bc, 0, sizeof(struct ext4_bcache));
91 * This is ext4_bcache, the module handling basic buffer-cache stuff.
93 * Buffers in a bcache are sorted by their LBA and stored in a
96 * Bcache also maintains another RB-Tree(lru_root) right now, where
97 * buffers are sorted by their LRU id.
99 * A singly-linked list is used to track those dirty buffers which are
100 * ready to be flushed. (Those buffers which are dirty but also referenced
101 * are not considered ready to be flushed.)
103 * When a buffer is not referenced, it will be stored in both lba_root
104 * and lru_root, while it will only be stored in lba_root when it is
108 static struct ext4_buf *
109 ext4_buf_alloc(struct ext4_bcache *bc, uint64_t lba)
112 struct ext4_buf *buf;
113 data = malloc(bc->itemsize);
117 buf = calloc(1, sizeof(struct ext4_buf));
128 static void ext4_buf_free(struct ext4_buf *buf)
134 static struct ext4_buf *
135 ext4_buf_lookup(struct ext4_bcache *bc, uint64_t lba)
137 struct ext4_buf tmp = {
141 return RB_FIND(ext4_buf_lba, &bc->lba_root, &tmp);
144 struct ext4_buf *ext4_buf_lowest_lru(struct ext4_bcache *bc)
146 return RB_MIN(ext4_buf_lru, &bc->lru_root);
149 void ext4_bcache_drop_buf(struct ext4_bcache *bc, struct ext4_buf *buf)
151 /*Cannot drop any referenced buffers.*/
152 ext4_assert(!buf->refctr);
154 RB_REMOVE(ext4_buf_lba, &bc->lba_root, buf);
155 RB_REMOVE(ext4_buf_lru, &bc->lru_root, buf);
157 /*Forcibly drop dirty buffer.*/
158 if (ext4_bcache_test_flag(buf, BC_DIRTY))
159 SLIST_REMOVE(&bc->dirty_list,
168 int ext4_bcache_alloc(struct ext4_bcache *bc, struct ext4_block *b,
171 /* Try to search the buffer with exaxt LBA. */
172 struct ext4_buf *buf = ext4_buf_lookup(bc, b->lb_id);
174 /* If buffer is not referenced. */
176 /* Assign new value to LRU id and increment LRU counter
178 buf->lru_id = ++bc->lru_ctr;
179 RB_REMOVE(ext4_buf_lru, &bc->lru_root, buf);
180 if (ext4_bcache_test_flag(buf, BC_DIRTY))
181 SLIST_REMOVE(&bc->dirty_list,
190 b->uptodate = ext4_bcache_test_flag(buf, BC_UPTODATE);
191 /* Right now we don't propagate the dirty flag from ext4_buf to
201 /* We need to allocate one buffer.*/
202 buf = ext4_buf_alloc(bc, b->lb_id);
206 RB_INSERT(ext4_buf_lba, &bc->lba_root, buf);
207 /* One more buffer in bcache now. :-) */
211 /* Assign new value to LRU id and increment LRU counter
213 buf->lru_id = ++bc->lru_ctr;
224 int ext4_bcache_free(struct ext4_bcache *bc, struct ext4_block *b)
226 struct ext4_buf *buf = b->buf;
228 ext4_assert(bc && b);
231 ext4_assert(b->lb_id);
233 /*Block should have a valid pointer to ext4_buf.*/
236 /*Check if someone don't try free unreferenced block cache.*/
237 ext4_assert(buf->refctr);
239 /*Just decrease reference counter*/
242 /* If buffer is modified, buf will be mark up-to-date and dirty. */
244 ext4_bcache_set_flag(buf, BC_DIRTY);
245 ext4_bcache_set_flag(buf, BC_UPTODATE);
248 /* Someone might want to drop this buffer from bcache. */
250 ext4_bcache_clear_flag(buf, BC_DIRTY);
251 ext4_bcache_clear_flag(buf, BC_UPTODATE);
254 /* We are the last one touching this buffer, do the cleanups. */
256 RB_INSERT(ext4_buf_lru, &bc->lru_root, buf);
257 /* This buffer is ready to be flushed. */
258 if (ext4_bcache_test_flag(buf, BC_DIRTY))
259 SLIST_INSERT_HEAD(&bc->dirty_list, buf, dirty_node);
261 /* The buffer is invalidated...drop it. */
262 if (!ext4_bcache_test_flag(buf, BC_UPTODATE))
263 ext4_bcache_drop_buf(bc, buf);
275 bool ext4_bcache_is_full(struct ext4_bcache *bc)
277 return (bc->cnt <= bc->ref_blocks);