PS2SDK
PS2 Homebrew Libraries
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 
12 struct bd_cache
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 ? */
26 static 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 ? */
35 static 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 
43 static 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 
55 static 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 
127 static 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 
139 static 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 
148 static 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 
157 struct 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->path = bd->path;
184  cbd->devNr = bd->devNr;
185  cbd->parNr = bd->parNr;
186  cbd->parId = bd->parId;
187  cbd->sectorSize = bd->sectorSize;
188  cbd->sectorOffset = bd->sectorOffset;
189  cbd->sectorCount = bd->sectorCount;
190 
191  cbd->read = _read;
192  cbd->write = _write;
193  cbd->flush = _flush;
194  cbd->stop = _stop;
195 
196  return cbd;
197 }
198 
199 void bd_cache_destroy(struct block_device *cbd)
200 {
201  M_DEBUG("%s\n", __FUNCTION__);
202 
203  FreeSysMemory(cbd->priv);
204  FreeSysMemory(cbd);
205 }
bd_cache.h
count
u32 count
start sector of fragmented bd/file
Definition: usbhdfsd-common.h:3
bd_cache
Definition: bd_cache.c:12
sysmem.h
block_device
Definition: bdm.h:21