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