1 | /* |
---|
2 | * core/cache.c: A simple LRU-based cache implementation. |
---|
3 | * |
---|
4 | */ |
---|
5 | |
---|
6 | #include <stdio.h> |
---|
7 | #include <string.h> |
---|
8 | #include <dprintf.h> |
---|
9 | #include "core.h" |
---|
10 | #include "cache.h" |
---|
11 | |
---|
12 | |
---|
13 | /* |
---|
14 | * Initialize the cache data structres. the _block_size_shift_ specify |
---|
15 | * the block size, which is 512 byte for FAT fs of the current |
---|
16 | * implementation since the block(cluster) size in FAT is a bit big. |
---|
17 | */ |
---|
18 | void cache_init(struct device *dev, int block_size_shift) |
---|
19 | { |
---|
20 | struct cache *prev, *cur; |
---|
21 | char *data = dev->cache_data; |
---|
22 | struct cache *head, *cache; |
---|
23 | int i; |
---|
24 | |
---|
25 | dev->cache_block_size = 1 << block_size_shift; |
---|
26 | |
---|
27 | if (dev->cache_size < dev->cache_block_size + 2*sizeof(struct cache)) { |
---|
28 | dev->cache_head = NULL; |
---|
29 | return; /* Cache unusably small */ |
---|
30 | } |
---|
31 | |
---|
32 | /* We need one struct cache for the headnode plus one for each block */ |
---|
33 | dev->cache_entries = |
---|
34 | (dev->cache_size - sizeof(struct cache))/ |
---|
35 | (dev->cache_block_size + sizeof(struct cache)); |
---|
36 | |
---|
37 | dev->cache_head = head = (struct cache *) |
---|
38 | (data + (dev->cache_entries << block_size_shift)); |
---|
39 | cache = head + 1; /* First cache descriptor */ |
---|
40 | |
---|
41 | head->prev = &cache[dev->cache_entries-1]; |
---|
42 | head->prev->next = head; |
---|
43 | head->block = -1; |
---|
44 | head->data = NULL; |
---|
45 | |
---|
46 | prev = head; |
---|
47 | |
---|
48 | for (i = 0; i < dev->cache_entries; i++) { |
---|
49 | cur = &cache[i]; |
---|
50 | cur->data = data; |
---|
51 | cur->block = -1; |
---|
52 | cur->prev = prev; |
---|
53 | prev->next = cur; |
---|
54 | data += dev->cache_block_size; |
---|
55 | prev = cur++; |
---|
56 | } |
---|
57 | |
---|
58 | dev->cache_init = 1; /* Set cache as initialized */ |
---|
59 | } |
---|
60 | |
---|
61 | /* |
---|
62 | * Lock a block permanently in the cache by removing it |
---|
63 | * from the LRU chain. |
---|
64 | */ |
---|
65 | void cache_lock_block(struct cache *cs) |
---|
66 | { |
---|
67 | cs->prev->next = cs->next; |
---|
68 | cs->next->prev = cs->prev; |
---|
69 | |
---|
70 | cs->next = cs->prev = NULL; |
---|
71 | } |
---|
72 | |
---|
73 | /* |
---|
74 | * Check for a particular BLOCK in the block cache, |
---|
75 | * and if it is already there, just do nothing and return; |
---|
76 | * otherwise pick a victim block and update the LRU link. |
---|
77 | */ |
---|
78 | struct cache *_get_cache_block(struct device *dev, block_t block) |
---|
79 | { |
---|
80 | struct cache *head = dev->cache_head; |
---|
81 | struct cache *cs; |
---|
82 | int i; |
---|
83 | |
---|
84 | cs = dev->cache_head + 1; |
---|
85 | |
---|
86 | for (i = 0; i < dev->cache_entries; i++) { |
---|
87 | if (cs->block == block) |
---|
88 | goto found; |
---|
89 | cs++; |
---|
90 | } |
---|
91 | |
---|
92 | /* Not found, pick a victim */ |
---|
93 | cs = head->next; |
---|
94 | |
---|
95 | found: |
---|
96 | /* Move to the end of the LRU chain, unless the block is already locked */ |
---|
97 | if (cs->next) { |
---|
98 | cs->prev->next = cs->next; |
---|
99 | cs->next->prev = cs->prev; |
---|
100 | |
---|
101 | cs->prev = head->prev; |
---|
102 | head->prev->next = cs; |
---|
103 | cs->next = head; |
---|
104 | head->prev = cs; |
---|
105 | } |
---|
106 | |
---|
107 | return cs; |
---|
108 | } |
---|
109 | |
---|
110 | /* |
---|
111 | * Check for a particular BLOCK in the block cache, |
---|
112 | * and if it is already there, just do nothing and return; |
---|
113 | * otherwise load it from disk and update the LRU link. |
---|
114 | * Return the data pointer. |
---|
115 | */ |
---|
116 | const void *get_cache(struct device *dev, block_t block) |
---|
117 | { |
---|
118 | struct cache *cs; |
---|
119 | |
---|
120 | cs = _get_cache_block(dev, block); |
---|
121 | if (cs->block != block) { |
---|
122 | cs->block = block; |
---|
123 | getoneblk(dev->disk, cs->data, block, dev->cache_block_size); |
---|
124 | } |
---|
125 | |
---|
126 | return cs->data; |
---|
127 | } |
---|
128 | |
---|
129 | /* |
---|
130 | * Read data from the cache at an arbitrary byte offset and length. |
---|
131 | * This is useful for filesystems whose metadata is not necessarily |
---|
132 | * aligned with their blocks. |
---|
133 | * |
---|
134 | * This is still reading linearly on the disk. |
---|
135 | */ |
---|
136 | size_t cache_read(struct fs_info *fs, void *buf, uint64_t offset, size_t count) |
---|
137 | { |
---|
138 | const char *cd; |
---|
139 | char *p = buf; |
---|
140 | size_t off, cnt, total; |
---|
141 | block_t block; |
---|
142 | |
---|
143 | total = count; |
---|
144 | while (count) { |
---|
145 | block = offset >> fs->block_shift; |
---|
146 | off = offset & (fs->block_size - 1); |
---|
147 | cd = get_cache(fs->fs_dev, block); |
---|
148 | if (!cd) |
---|
149 | break; |
---|
150 | cnt = fs->block_size - off; |
---|
151 | if (cnt > count) |
---|
152 | cnt = count; |
---|
153 | memcpy(p, cd + off, cnt); |
---|
154 | count -= cnt; |
---|
155 | p += cnt; |
---|
156 | offset += cnt; |
---|
157 | } |
---|
158 | return total - count; |
---|
159 | } |
---|