blob: 8c4144332ae816b9e3ef9af397ceaf4a414c9ab2 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * cache.c
3 *
4 * Include routines to caching the reading and writing the sectors.
5 * we use the "Least Recently Used" algorithm as our cache algorithm
6 * that discards the least recently used items. By building a static
7 * double list in an array to track the least recently used item.
8 * implimentation file.
9 *
10 * Copyright (C) knightray@gmail.com
11 *
12 * SPDX-License-Identifier: GPL-2.0+
13 */
14
15#include "pubstruct.h"
16#include "debug.h"
17#include "cache.h"
18
19#ifndef DBG_CACHE
20#undef DBG
21#define DBG nulldbg
22#endif
23
24/* Private interface declaration. */
25
26static BOOL
27_find_in_list(
28 IN tcache_t * pcache,
29 IN uint32 sec,
30 OUT int16 * pret
31);
32
33static int16
34_hash_function(
35 IN tcache_t * pcache,
36 IN uint32 sec
37);
38
39static void
40_del_from_list(
41 IN tcache_t * pcache,
42 IN int16 sec_index
43);
44
45static void
46_insert_into_list(
47 IN tcache_t * pcache,
48 IN int16 sec_index
49);
50
51static int16
52_do_cache_miss(
53 IN tcache_t * pcache,
54 IN int16 secindex,
55 IN uint32 sec,
56 IN BOOL is_read /* Marvell fixed */
57);
58
59static void
60_do_cache_hint(
61 IN tcache_t * pcache,
62 IN int16 secindex
63);
64
65/*----------------------------------------------------------------------------------------------------*/
66
67tcache_t *
68cache_init(
69 IN tdev_handle_t hdev,
70 IN uint32 seccnt,
71 IN uint32 sector_size)
72{
73 tcache_t * pcache;
74 int16 seci;
75
76 pcache = (tcache_t *)Malloc(sizeof(tcache_t));
77 pcache->seccnt = seccnt;
78 pcache->hdev = hdev;
79 pcache->sector_size = sector_size;
80 if (seccnt > 0) {
81 pcache->secbufs = (sec_buf_t *)Malloc(sizeof(sec_buf_t) * seccnt);
82 if (pcache->secbufs == NULL) {
83 Free(pcache);
84 return NULL;
85 }
86
87 Memset(pcache->secbufs, 0, sizeof(sec_buf_t) * seccnt);
88 for (seci = 0; seci < pcache->seccnt; seci++) {
89 pcache->secbufs[seci].secbuf = (ubyte *)Malloc(pcache->sector_size);
90 pcache->secbufs[seci].sec = 0;
91 }
92 pcache->head = SECBUF_EOF;
93 pcache->tail = SECBUF_EOF;
94
95 pcache->use_cache = 1;
96 }
97 else {
98 pcache->use_cache = 0;
99 }
100
101 return pcache;
102}
103
104int32
105cache_readsector(
106 IN tcache_t * pcache,
107 IN int32 addr,
108 OUT ubyte * ptr)
109{
110 int16 secindex;
111 int32 ret;
112
113 ret = CACHE_OK;
114
115 if (!pcache->use_cache) {
116 return HAI_readsector(pcache->hdev, addr, ptr);
117 }
118
119 if (!_find_in_list(pcache, addr, &secindex)) {
120 secindex = _do_cache_miss(pcache, secindex, addr, TRUE);
121 if (secindex < 0)
122 ret = secindex;
123
124 }
125 else {
126 _do_cache_hint(pcache, secindex);
127 }
128
129 if (ret >= 0) {
130 Memcpy(ptr, pcache->secbufs[secindex].secbuf, pcache->sector_size);
131 }
132
133 return ret;
134}
135
136int32
137cache_writesector(
138 IN tcache_t * pcache,
139 IN int32 addr,
140 IN ubyte * ptr)
141{
142 int16 secindex;
143 int32 ret;
144
145 ret = CACHE_OK;
146
147 if (!pcache->use_cache) {
148 /* Marvell fixed: was HAI_readsector */
149 return HAI_writesector(pcache->hdev, addr, ptr);
150 }
151
152 if (!_find_in_list(pcache, addr, &secindex)) {
153 secindex = _do_cache_miss(pcache, secindex, addr, FALSE);
154 if (secindex < 0)
155 ret = secindex;
156
157 }
158 else {
159 _do_cache_hint(pcache, secindex);
160 }
161
162 if (ret >= 0) {
163 Memcpy(pcache->secbufs[secindex].secbuf, ptr, pcache->sector_size);
164 pcache->secbufs[secindex].is_dirty = 1;
165 }
166
167 return ret;
168}
169
170int32
171cache_flush(
172 IN tcache_t * pcache)
173{
174 int32 ret;
175 int16 seci;
176
177 ret = CACHE_OK;
178 if (pcache->use_cache) {
179 for (seci = 0; seci < pcache->seccnt; seci++) {
180 if (pcache->secbufs[seci].is_dirty) {
181 DBG("%s:write sector %d\n", __FUNCTION__, seci);
182 if (HAI_writesector(pcache->hdev, pcache->secbufs[seci].sec,
183 pcache->secbufs[seci].secbuf) != HAI_OK) {
184 ret = ERR_CACHE_HARDWARE_FAIL;
185 break;
186 }
187 }
188 }
189 }
190 return ret;
191}
192
193
194int32
195cache_destroy(
196 IN tcache_t * pcache)
197{
198 int32 ret;
199 int16 seci;
200
201 ret = CACHE_OK;
202 if (pcache->use_cache) {
203 ret = cache_flush(pcache);
204
205 for (seci = 0; seci < pcache->seccnt; seci++) {
206 Free(pcache->secbufs[seci].secbuf);
207 }
208
209 Free(pcache->secbufs);
210 }
211
212 Free(pcache);
213
214 return ret;
215}
216
217/*----------------------------------------------------------------------------------------------------*/
218
219int16
220_do_cache_miss(
221 IN tcache_t * pcache,
222 IN int16 secindex,
223 IN uint32 sec,
224 IN BOOL is_read) /* Marvell fixed: prevent sector read on write miss */
225{
226 int16 ret_sec_index;
227
228 ret_sec_index = secindex;
229 if (secindex == pcache->seccnt) {
230 /* The cache list is full, we have to get the last element to swap to disk. */
231 DBG("%s:cache is full, del %d from cache.\n", __FUNCTION__, pcache->tail);
232 if (pcache->secbufs[pcache->tail].is_dirty) {
233 if (HAI_writesector(pcache->hdev, pcache->secbufs[pcache->tail].sec,
234 pcache->secbufs[pcache->tail].secbuf) != HAI_OK) {
235 ret_sec_index = ERR_CACHE_HARDWARE_FAIL;
236 }
237 }
238 secindex = pcache->tail;
239 _del_from_list(pcache, pcache->tail);
240 }
241
242 if (ret_sec_index >= 0 && (!is_read || (HAI_readsector(pcache->hdev, sec,
243 pcache->secbufs[secindex].secbuf) == HAI_OK))) {
244 pcache->secbufs[secindex].sec = sec;
245 pcache->secbufs[secindex].is_dirty = 0;
246 _insert_into_list(pcache, secindex);
247 ret_sec_index = secindex;
248 }
249 else {
250 ret_sec_index = ERR_CACHE_HARDWARE_FAIL;
251 }
252
253 return ret_sec_index;
254}
255
256void
257_do_cache_hint(
258 IN tcache_t * pcache,
259 IN int16 secindex)
260{
261 _del_from_list(pcache, secindex);
262 _insert_into_list(pcache, secindex);
263}
264
265void
266_insert_into_list(
267 IN tcache_t * pcache,
268 IN int16 sec_index)
269{
270 if (pcache->head == SECBUF_EOF) {
271 /* list is empty. */
272 pcache->secbufs[sec_index].pre = SECBUF_EOF;
273 pcache->secbufs[sec_index].next = SECBUF_EOF;
274 pcache->head = sec_index;
275 pcache->tail = sec_index;
276 }
277 else {
278 /* insert into the head of the list. */
279 pcache->secbufs[sec_index].pre = SECBUF_EOF;
280 pcache->secbufs[sec_index].next = pcache->head;
281 pcache->secbufs[pcache->head].pre = sec_index;
282 pcache->head = sec_index;
283 }
284}
285
286void
287_del_from_list(
288 IN tcache_t * pcache,
289 IN int16 sec_index)
290{
291 //ASSERT(pcache->secbufs[sec_index].sec != 0);
292
293 DBG("%s:sec_index = %d\n", __FUNCTION__, sec_index);
294 if (pcache->secbufs[sec_index].pre == SECBUF_EOF) {
295 /* the element is the first one. */
296 pcache->head = pcache->secbufs[sec_index].next;
297 }
298 else {
299 pcache->secbufs[pcache->secbufs[sec_index].pre].next = pcache->secbufs[sec_index].next;
300 }
301
302 if (pcache->secbufs[sec_index].next != SECBUF_EOF) {
303 /* modify the next element's pre field only when next element is not EOF. */
304 pcache->secbufs[pcache->secbufs[sec_index].next].pre = pcache->secbufs[sec_index].pre;
305 }
306 else {
307 pcache->tail = pcache->secbufs[sec_index].pre;
308 }
309}
310
311int16
312_hash_function(
313 IN tcache_t * pcache,
314 IN uint32 sec)
315{
316 return sec % pcache->seccnt;
317}
318
319BOOL
320_find_in_list(
321 IN tcache_t * pcache,
322 IN uint32 sec,
323 OUT int16 * pret)
324{
325 int16 start_index;
326 BOOL is_found;
327 int16 cur_index;
328
329 start_index = _hash_function(pcache, sec);
330 cur_index = start_index;
331 while (1) {
332 DBG("%s:cur_index = %d\n", __FUNCTION__, cur_index);
333 if (pcache->secbufs[cur_index].sec == sec) {
334 is_found = TRUE;
335 *pret = cur_index;
336 break;
337 }
338 else if (pcache->secbufs[cur_index].sec == 0) {
339 is_found = FALSE;
340 *pret = cur_index;
341 break;
342 }
343
344 cur_index++;
345 cur_index = cur_index % pcache->seccnt;
346 if (cur_index == start_index) {
347 is_found = FALSE;
348 *pret = pcache->seccnt;
349 break;
350 }
351 }
352
353 return is_found;
354}
355