PS2SDK
PS2 Homebrew Libraries
Loading...
Searching...
No Matches
bd_cache.c
1#include <bd_cache.h>
2#include <string.h>
3#include <sysmem.h>
4
5//#define DEBUG //comment out this line when not debugging
6#include "module_debug.h"
7
8#define SECTORS_PER_BLOCK 8 // 8 * 512b = 4KiB
9#define BLOCK_COUNT 32 // 32 * 4KiB = 128KiB
10#define BLOCK_WEIGHT_FACTOR 256 // Fixed point math (24.8)
11
13{
14 struct block_device *bd;
15 int weight[BLOCK_COUNT];
16 u64 sector[BLOCK_COUNT];
17 u8 cache[BLOCK_COUNT][SECTORS_PER_BLOCK*512];
18#ifdef DEBUG
19 u32 sectors_read;
20 u32 sectors_cache;
21 u32 sectors_dev;
22#endif
23};
24
25/* cache overlaps with requested area ? */
26static int _overlaps(u64 csector, u64 sector, u16 count)
27{
28 if ((sector < (csector + SECTORS_PER_BLOCK)) && ((sector + count) > csector))
29 return 1;
30 else
31 return 0;
32}
33
34/* cache contains requested area ? */
35static int _contains(u64 csector, u64 sector, u16 count)
36{
37 if ((sector >= csector) && ((sector + count) <= (csector + SECTORS_PER_BLOCK)))
38 return 1;
39 else
40 return 0;
41}
42
43static void _invalidate(struct bd_cache *c, u64 sector, u16 count)
44{
45 int blkidx;
46
47 for (blkidx = 0; blkidx < BLOCK_COUNT; blkidx++) {
48 if (_overlaps(c->sector[blkidx], sector, count)) {
49 // Invalidate cache entry
50 c->sector[blkidx] = 0xffffffffffffffff;
51 }
52 }
53}
54
55static int _read(struct block_device *bd, u64 sector, void *buffer, u16 count)
56{
57 struct bd_cache *c = bd->priv;
58
59 DEBUG_U64_2XU32(sector);
60 M_DEBUG("%s(0x%08x%08x, %d)\n", __FUNCTION__, sector_u32[1], sector_u32[0], count);
61
62 if (count >= SECTORS_PER_BLOCK) {
63 // Do a direct read
64 return c->bd->read(c->bd, sector, buffer, count);
65 }
66
67#ifdef DEBUG
68 c->sectors_read += count;
69#endif
70
71 // Do a cached read
72 int blkidx;
73 for (blkidx = 0; blkidx < BLOCK_COUNT; blkidx++) {
74 if (_contains(c->sector[blkidx], sector, count)) {
75#ifdef DEBUG
76 c->sectors_cache += count;
77 //M_DEBUG("- CACHE HIT[%d] [block %d] [devread %ds, hit-ratio %d%%]\n", sector, blkidx, c->sectors_dev, (c->sectors_cache * 100) / c->sectors_read);
78#endif
79 // Minimum weight
80 if (c->weight[blkidx] < 0)
81 c->weight[blkidx] = 0;
82
83 c->weight[blkidx] += count * BLOCK_WEIGHT_FACTOR;
84
85 // Read from cache
86 u64 offset = (sector - c->sector[blkidx]) * 512;
87 memcpy(buffer, &c->cache[blkidx][offset], count * 512);
88 return count;
89 }
90 }
91
92 // Find block with the lowest weight
93 int blkidx_best_weight = 0x7fffffff;
94 int blkidx_best = 0;
95 M_DEBUG("- list: ");
96 for (blkidx = 0; blkidx < BLOCK_COUNT; blkidx++) {
97#ifdef DEBUG
98 printf("%*d ", 3, c->weight[blkidx] / BLOCK_WEIGHT_FACTOR);
99#endif
100
101 // Dynamic aging
102 c->weight[blkidx] -= (SECTORS_PER_BLOCK * BLOCK_WEIGHT_FACTOR / BLOCK_COUNT) + (c->weight[blkidx] / 32);
103
104 if (c->weight[blkidx] < blkidx_best_weight) {
105 // Better block found
106 blkidx_best_weight = c->weight[blkidx];
107 blkidx_best = blkidx;
108 }
109 }
110#ifdef DEBUG
111 printf(" devread: %*d, evict %*d [%*d], add [%*d]\n", 4, c->sectors_dev, 2, blkidx_best, 8, c->sector[blkidx_best], 8, sector);
112 c->sectors_dev += SECTORS_PER_BLOCK;
113 //M_DEBUG("- CACHE READ[%d] -> [block %d] [devread %ds, hit-ratio %d%%]\n", sector, blkidx_best, c->sectors_dev, (c->sectors_cache * 100) / c->sectors_read);
114#endif
115
116 // Fill the block
117 c->bd->read(c->bd, sector, c->cache[blkidx_best], SECTORS_PER_BLOCK);
118 c->sector[blkidx_best] = sector;
119
120 // Read from cache
121 u64 offset = (sector - c->sector[blkidx_best]) * 512;
122 c->weight[blkidx_best] = count * BLOCK_WEIGHT_FACTOR;
123 memcpy(buffer, &c->cache[blkidx_best][offset], count * 512);
124 return count;
125}
126
127static int _write(struct block_device *bd, u64 sector, const void *buffer, u16 count)
128{
129 struct bd_cache *c = bd->priv;
130
131 DEBUG_U64_2XU32(sector);
132 M_DEBUG("%s(0x%08x%08x, %d)\n", __FUNCTION__, sector_u32[1], sector_u32[0], count);
133
134 _invalidate(c, sector, count);
135
136 return c->bd->write(c->bd, sector, buffer, count);
137}
138
139static void _flush(struct block_device *bd)
140{
141 struct bd_cache *c = bd->priv;
142
143 M_DEBUG("%s\n", __FUNCTION__);
144
145 return c->bd->flush(c->bd);
146}
147
148static int _stop(struct block_device *bd)
149{
150 struct bd_cache *c = bd->priv;
151
152 M_DEBUG("%s\n", __FUNCTION__);
153
154 return c->bd->stop(c->bd);
155}
156
157struct block_device *bd_cache_create(struct block_device *bd)
158{
159 int blkidx;
160
161 // Create new block device
162 struct block_device *cbd = AllocSysMemory(ALLOC_FIRST, sizeof(struct block_device), NULL);
163 // Create new private data
164 struct bd_cache *c = AllocSysMemory(ALLOC_FIRST, sizeof(struct bd_cache), NULL);
165
166 M_DEBUG("%s\n", __FUNCTION__);
167
168 c->bd = bd;
169 for (blkidx = 0; blkidx < BLOCK_COUNT; blkidx++) {
170 c->weight[blkidx] = 0;
171 c->sector[blkidx] = 0xffffffffffffffff;
172 }
173#ifdef DEBUG
174 c->sectors_read = 0;
175 c->sectors_cache = 0;
176 c->sectors_dev = 0;
177#endif
178
179 // copy all parameters becouse we are the same blocks device
180 // only difference is we are cached.
181 cbd->priv = c;
182 cbd->name = bd->name;
183 cbd->devNr = bd->devNr;
184 cbd->parNr = bd->parNr;
185 cbd->parId = bd->parId;
186 cbd->sectorSize = bd->sectorSize;
187 cbd->sectorOffset = bd->sectorOffset;
188 cbd->sectorCount = bd->sectorCount;
189
190 cbd->read = _read;
191 cbd->write = _write;
192 cbd->flush = _flush;
193 cbd->stop = _stop;
194
195 return cbd;
196}
197
198void bd_cache_destroy(struct block_device *cbd)
199{
200 M_DEBUG("%s\n", __FUNCTION__);
201
202 FreeSysMemory(cbd->priv);
203 FreeSysMemory(cbd);
204}
u32 count
start sector of fragmented bd/file