-
Notifications
You must be signed in to change notification settings - Fork 199
/
Copy pathcache.c
135 lines (114 loc) · 3.38 KB
/
cache.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
* cache.c
*
* In-memory data cache.
*
* Written & released by Keir Fraser <[email protected]>
*
* This is free and unencumbered software released into the public domain.
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
struct cache_ent {
uint32_t id;
struct list_head lru;
struct list_head hash;
uint8_t dat[0];
};
struct cache {
uint32_t item_sz;
struct list_head lru;
struct list_head hash[32];
struct cache_ent ents[0];
};
static struct cache *cache;
#define CACHE_HASH(_id) ((_id)&31)
struct cache *cache_init(void *start, void *end, unsigned int item_sz)
{
uint8_t *s, *e;
int i, nitm;
struct cache *c;
struct cache_ent *cent;
/* Cache boundaries are four-byte aligned. */
s = (uint8_t *)(((uint32_t)start + 3) & ~3);
e = (uint8_t *)((uint32_t)end & ~3);
nitm = ((e - s) - (int)sizeof(*c)) / (int)(sizeof(*cent) + item_sz);
if (nitm < 8) {
printk("No cache: too small (%d)\n", e - s);
return NULL;
}
/* Initialise the empty cache structure. */
cache = c = (struct cache *)s;
c->item_sz = item_sz;
list_init(&c->lru);
for (i = 0; i < ARRAY_SIZE(c->hash); i++)
list_init(&c->hash[i]);
/* Insert all the cache entries into the LRU list. They are not present
* in any hash chain as none of the cache entries are yet in use. */
cent = c->ents;
for (i = 0; i < nitm; i++) {
list_insert_tail(&c->lru, ¢->lru);
list_init(¢->hash);
cent = (struct cache_ent *)((uint32_t)cent + sizeof(*cent) + item_sz);
}
printk("Cache %u items\n", nitm);
return c;
}
const void *cache_lookup(struct cache *c, uint32_t id)
{
struct list_head *hash, *ent;
struct cache_ent *cent;
/* Look up the item in the appropriate hash chain. */
hash = &c->hash[CACHE_HASH(id)];
for (ent = hash->next; ent != hash; ent = ent->next) {
cent = container_of(ent, struct cache_ent, hash);
if (cent->id == id)
goto found;
}
return NULL;
found:
/* Item is cached. Move it to head of LRU and return the data. */
list_remove(¢->lru);
list_insert_head(&c->lru, ¢->lru);
return cent->dat;
}
void cache_update(struct cache *c, uint32_t id, const void *dat)
{
struct cache_ent *cent;
void *p;
/* Already in the cache? Just update the existing data. */
if ((p = (void *)cache_lookup(c, id)) != NULL)
goto found;
/* Steal the oldest cache entry from the LRU. */
cent = container_of(c->lru.prev, struct cache_ent, lru);
p = cent->dat;
/* Remove the selected cache entry from the cache. */
list_remove(¢->lru);
if (!list_is_empty(¢->hash))
list_remove(¢->hash);
/* Reinsert the cache entry in the correct hash chain, and head of LRU. */
cent->id = id;
list_insert_head(&c->lru, ¢->lru);
list_insert_head(&c->hash[CACHE_HASH(id)], ¢->hash);
found:
/* Finally, store away the actual item data. */
memcpy(p, dat, c->item_sz);
}
void cache_update_N(struct cache *c, uint32_t id,
const void *dat, unsigned int N)
{
const uint8_t *p = dat;
while (N--) {
cache_update(c, id, p);
id++;
p += c->item_sz;
}
}
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/