root / page_cache.c @ 8cfd0495
History | View | Annotate | Download (5 kB)
1 |
/*
|
---|---|
2 |
* Page cache for QEMU
|
3 |
* The cache is base on a hash of the page address
|
4 |
*
|
5 |
* Copyright 2012 Red Hat, Inc. and/or its affiliates
|
6 |
*
|
7 |
* Authors:
|
8 |
* Orit Wasserman <owasserm@redhat.com>
|
9 |
*
|
10 |
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
11 |
* See the COPYING file in the top-level directory.
|
12 |
*
|
13 |
*/
|
14 |
|
15 |
#include <stdint.h> |
16 |
#include <stdio.h> |
17 |
#include <stdlib.h> |
18 |
#include <strings.h> |
19 |
#include <string.h> |
20 |
#include <sys/time.h> |
21 |
#include <sys/types.h> |
22 |
#include <stdbool.h> |
23 |
#include <glib.h> |
24 |
|
25 |
#include "qemu-common.h" |
26 |
#include "migration/page_cache.h" |
27 |
|
28 |
#ifdef DEBUG_CACHE
|
29 |
#define DPRINTF(fmt, ...) \
|
30 |
do { fprintf(stdout, "cache: " fmt, ## __VA_ARGS__); } while (0) |
31 |
#else
|
32 |
#define DPRINTF(fmt, ...) \
|
33 |
do { } while (0) |
34 |
#endif
|
35 |
|
36 |
typedef struct CacheItem CacheItem; |
37 |
|
38 |
struct CacheItem {
|
39 |
uint64_t it_addr; |
40 |
uint64_t it_age; |
41 |
uint8_t *it_data; |
42 |
}; |
43 |
|
44 |
struct PageCache {
|
45 |
CacheItem *page_cache; |
46 |
unsigned int page_size; |
47 |
int64_t max_num_items; |
48 |
uint64_t max_item_age; |
49 |
int64_t num_items; |
50 |
}; |
51 |
|
52 |
PageCache *cache_init(int64_t num_pages, unsigned int page_size) |
53 |
{ |
54 |
int64_t i; |
55 |
|
56 |
PageCache *cache; |
57 |
|
58 |
if (num_pages <= 0) { |
59 |
DPRINTF("invalid number of pages\n");
|
60 |
return NULL; |
61 |
} |
62 |
|
63 |
cache = g_malloc(sizeof(*cache));
|
64 |
|
65 |
/* round down to the nearest power of 2 */
|
66 |
if (!is_power_of_2(num_pages)) {
|
67 |
num_pages = pow2floor(num_pages); |
68 |
DPRINTF("rounding down to %" PRId64 "\n", num_pages); |
69 |
} |
70 |
cache->page_size = page_size; |
71 |
cache->num_items = 0;
|
72 |
cache->max_item_age = 0;
|
73 |
cache->max_num_items = num_pages; |
74 |
|
75 |
DPRINTF("Setting cache buckets to %" PRId64 "\n", cache->max_num_items); |
76 |
|
77 |
cache->page_cache = g_malloc((cache->max_num_items) * |
78 |
sizeof(*cache->page_cache));
|
79 |
|
80 |
for (i = 0; i < cache->max_num_items; i++) { |
81 |
cache->page_cache[i].it_data = NULL;
|
82 |
cache->page_cache[i].it_age = 0;
|
83 |
cache->page_cache[i].it_addr = -1;
|
84 |
} |
85 |
|
86 |
return cache;
|
87 |
} |
88 |
|
89 |
void cache_fini(PageCache *cache)
|
90 |
{ |
91 |
int64_t i; |
92 |
|
93 |
g_assert(cache); |
94 |
g_assert(cache->page_cache); |
95 |
|
96 |
for (i = 0; i < cache->max_num_items; i++) { |
97 |
g_free(cache->page_cache[i].it_data); |
98 |
} |
99 |
|
100 |
g_free(cache->page_cache); |
101 |
cache->page_cache = NULL;
|
102 |
} |
103 |
|
104 |
static size_t cache_get_cache_pos(const PageCache *cache, |
105 |
uint64_t address) |
106 |
{ |
107 |
size_t pos; |
108 |
|
109 |
g_assert(cache->max_num_items); |
110 |
pos = (address / cache->page_size) & (cache->max_num_items - 1);
|
111 |
return pos;
|
112 |
} |
113 |
|
114 |
bool cache_is_cached(const PageCache *cache, uint64_t addr) |
115 |
{ |
116 |
size_t pos; |
117 |
|
118 |
g_assert(cache); |
119 |
g_assert(cache->page_cache); |
120 |
|
121 |
pos = cache_get_cache_pos(cache, addr); |
122 |
|
123 |
return (cache->page_cache[pos].it_addr == addr);
|
124 |
} |
125 |
|
126 |
static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr) |
127 |
{ |
128 |
size_t pos; |
129 |
|
130 |
g_assert(cache); |
131 |
g_assert(cache->page_cache); |
132 |
|
133 |
pos = cache_get_cache_pos(cache, addr); |
134 |
|
135 |
return &cache->page_cache[pos];
|
136 |
} |
137 |
|
138 |
uint8_t *get_cached_data(const PageCache *cache, uint64_t addr)
|
139 |
{ |
140 |
return cache_get_by_addr(cache, addr)->it_data;
|
141 |
} |
142 |
|
143 |
void cache_insert(PageCache *cache, uint64_t addr, uint8_t *pdata)
|
144 |
{ |
145 |
|
146 |
CacheItem *it = NULL;
|
147 |
|
148 |
g_assert(cache); |
149 |
g_assert(cache->page_cache); |
150 |
|
151 |
/* actual update of entry */
|
152 |
it = cache_get_by_addr(cache, addr); |
153 |
|
154 |
/* free old cached data if any */
|
155 |
g_free(it->it_data); |
156 |
|
157 |
if (!it->it_data) {
|
158 |
cache->num_items++; |
159 |
} |
160 |
|
161 |
it->it_data = g_memdup(pdata, cache->page_size); |
162 |
it->it_age = ++cache->max_item_age; |
163 |
it->it_addr = addr; |
164 |
} |
165 |
|
166 |
int64_t cache_resize(PageCache *cache, int64_t new_num_pages) |
167 |
{ |
168 |
PageCache *new_cache; |
169 |
int64_t i; |
170 |
|
171 |
CacheItem *old_it, *new_it; |
172 |
|
173 |
g_assert(cache); |
174 |
|
175 |
/* cache was not inited */
|
176 |
if (cache->page_cache == NULL) { |
177 |
return -1; |
178 |
} |
179 |
|
180 |
/* same size */
|
181 |
if (pow2floor(new_num_pages) == cache->max_num_items) {
|
182 |
return cache->max_num_items;
|
183 |
} |
184 |
|
185 |
new_cache = cache_init(new_num_pages, cache->page_size); |
186 |
if (!(new_cache)) {
|
187 |
DPRINTF("Error creating new cache\n");
|
188 |
return -1; |
189 |
} |
190 |
|
191 |
/* move all data from old cache */
|
192 |
for (i = 0; i < cache->max_num_items; i++) { |
193 |
old_it = &cache->page_cache[i]; |
194 |
if (old_it->it_addr != -1) { |
195 |
/* check for collision, if there is, keep MRU page */
|
196 |
new_it = cache_get_by_addr(new_cache, old_it->it_addr); |
197 |
if (new_it->it_data && new_it->it_age >= old_it->it_age) {
|
198 |
/* keep the MRU page */
|
199 |
g_free(old_it->it_data); |
200 |
} else {
|
201 |
if (!new_it->it_data) {
|
202 |
new_cache->num_items++; |
203 |
} |
204 |
g_free(new_it->it_data); |
205 |
new_it->it_data = old_it->it_data; |
206 |
new_it->it_age = old_it->it_age; |
207 |
new_it->it_addr = old_it->it_addr; |
208 |
} |
209 |
} |
210 |
} |
211 |
|
212 |
g_free(cache->page_cache); |
213 |
cache->page_cache = new_cache->page_cache; |
214 |
cache->max_num_items = new_cache->max_num_items; |
215 |
cache->num_items = new_cache->num_items; |
216 |
|
217 |
g_free(new_cache); |
218 |
|
219 |
return cache->max_num_items;
|
220 |
} |