root / page_cache.c @ 8cfd0495
History | View | Annotate | Download (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 | 9fb26641 | Orit Wasserman | cache = g_malloc(sizeof(*cache));
|
64 | 9fb26641 | Orit Wasserman | |
65 | 9fb26641 | Orit Wasserman | /* round down to the nearest power of 2 */
|
66 | 9fb26641 | Orit Wasserman | if (!is_power_of_2(num_pages)) {
|
67 | 9fb26641 | Orit Wasserman | num_pages = pow2floor(num_pages); |
68 | 9fb26641 | Orit Wasserman | DPRINTF("rounding down to %" PRId64 "\n", num_pages); |
69 | 9fb26641 | Orit Wasserman | } |
70 | 9fb26641 | Orit Wasserman | cache->page_size = page_size; |
71 | 9fb26641 | Orit Wasserman | cache->num_items = 0;
|
72 | 9fb26641 | Orit Wasserman | cache->max_item_age = 0;
|
73 | 9fb26641 | Orit Wasserman | cache->max_num_items = num_pages; |
74 | 9fb26641 | Orit Wasserman | |
75 | 9fb26641 | Orit Wasserman | DPRINTF("Setting cache buckets to %" PRId64 "\n", cache->max_num_items); |
76 | 9fb26641 | Orit Wasserman | |
77 | 9fb26641 | Orit Wasserman | cache->page_cache = g_malloc((cache->max_num_items) * |
78 | 9fb26641 | Orit Wasserman | sizeof(*cache->page_cache));
|
79 | 9fb26641 | Orit Wasserman | |
80 | 9fb26641 | Orit Wasserman | for (i = 0; i < cache->max_num_items; i++) { |
81 | 9fb26641 | Orit Wasserman | cache->page_cache[i].it_data = NULL;
|
82 | 9fb26641 | Orit Wasserman | cache->page_cache[i].it_age = 0;
|
83 | 9fb26641 | Orit Wasserman | cache->page_cache[i].it_addr = -1;
|
84 | 9fb26641 | Orit Wasserman | } |
85 | 9fb26641 | Orit Wasserman | |
86 | 9fb26641 | Orit Wasserman | return cache;
|
87 | 9fb26641 | Orit Wasserman | } |
88 | 9fb26641 | Orit Wasserman | |
89 | 9fb26641 | Orit Wasserman | void cache_fini(PageCache *cache)
|
90 | 9fb26641 | Orit Wasserman | { |
91 | 9fb26641 | Orit Wasserman | int64_t i; |
92 | 9fb26641 | Orit Wasserman | |
93 | 9fb26641 | Orit Wasserman | g_assert(cache); |
94 | 9fb26641 | Orit Wasserman | g_assert(cache->page_cache); |
95 | 9fb26641 | Orit Wasserman | |
96 | 9fb26641 | Orit Wasserman | for (i = 0; i < cache->max_num_items; i++) { |
97 | 9fb26641 | Orit Wasserman | g_free(cache->page_cache[i].it_data); |
98 | 9fb26641 | Orit Wasserman | } |
99 | 9fb26641 | Orit Wasserman | |
100 | 9fb26641 | Orit Wasserman | g_free(cache->page_cache); |
101 | 9fb26641 | Orit Wasserman | cache->page_cache = NULL;
|
102 | 9fb26641 | Orit Wasserman | } |
103 | 9fb26641 | Orit Wasserman | |
104 | 9fb26641 | Orit Wasserman | static size_t cache_get_cache_pos(const PageCache *cache, |
105 | 9fb26641 | Orit Wasserman | uint64_t address) |
106 | 9fb26641 | Orit Wasserman | { |
107 | 9fb26641 | Orit Wasserman | size_t pos; |
108 | 9fb26641 | Orit Wasserman | |
109 | 9fb26641 | Orit Wasserman | g_assert(cache->max_num_items); |
110 | 9fb26641 | Orit Wasserman | pos = (address / cache->page_size) & (cache->max_num_items - 1);
|
111 | 9fb26641 | Orit Wasserman | return pos;
|
112 | 9fb26641 | Orit Wasserman | } |
113 | 9fb26641 | Orit Wasserman | |
114 | 9fb26641 | Orit Wasserman | bool cache_is_cached(const PageCache *cache, uint64_t addr) |
115 | 9fb26641 | Orit Wasserman | { |
116 | 9fb26641 | Orit Wasserman | size_t pos; |
117 | 9fb26641 | Orit Wasserman | |
118 | 9fb26641 | Orit Wasserman | g_assert(cache); |
119 | 9fb26641 | Orit Wasserman | g_assert(cache->page_cache); |
120 | 9fb26641 | Orit Wasserman | |
121 | 9fb26641 | Orit Wasserman | pos = cache_get_cache_pos(cache, addr); |
122 | 9fb26641 | Orit Wasserman | |
123 | 9fb26641 | Orit Wasserman | return (cache->page_cache[pos].it_addr == addr);
|
124 | 9fb26641 | Orit Wasserman | } |
125 | 9fb26641 | Orit Wasserman | |
126 | 9fb26641 | Orit Wasserman | static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr) |
127 | 9fb26641 | Orit Wasserman | { |
128 | 9fb26641 | Orit Wasserman | size_t pos; |
129 | 9fb26641 | Orit Wasserman | |
130 | 9fb26641 | Orit Wasserman | g_assert(cache); |
131 | 9fb26641 | Orit Wasserman | g_assert(cache->page_cache); |
132 | 9fb26641 | Orit Wasserman | |
133 | 9fb26641 | Orit Wasserman | pos = cache_get_cache_pos(cache, addr); |
134 | 9fb26641 | Orit Wasserman | |
135 | 9fb26641 | Orit Wasserman | return &cache->page_cache[pos];
|
136 | 9fb26641 | Orit Wasserman | } |
137 | 9fb26641 | Orit Wasserman | |
138 | 9fb26641 | Orit Wasserman | uint8_t *get_cached_data(const PageCache *cache, uint64_t addr)
|
139 | 9fb26641 | Orit Wasserman | { |
140 | 9fb26641 | Orit Wasserman | return cache_get_by_addr(cache, addr)->it_data;
|
141 | 9fb26641 | Orit Wasserman | } |
142 | 9fb26641 | Orit Wasserman | |
143 | 9fb26641 | Orit Wasserman | void cache_insert(PageCache *cache, uint64_t addr, uint8_t *pdata)
|
144 | 9fb26641 | Orit Wasserman | { |
145 | 9fb26641 | Orit Wasserman | |
146 | 9fb26641 | Orit Wasserman | CacheItem *it = NULL;
|
147 | 9fb26641 | Orit Wasserman | |
148 | 9fb26641 | Orit Wasserman | g_assert(cache); |
149 | 9fb26641 | Orit Wasserman | g_assert(cache->page_cache); |
150 | 9fb26641 | Orit Wasserman | |
151 | 9fb26641 | Orit Wasserman | /* actual update of entry */
|
152 | 9fb26641 | Orit Wasserman | it = cache_get_by_addr(cache, addr); |
153 | 9fb26641 | Orit Wasserman | |
154 | 32a1c08b | Peter Lieven | /* free old cached data if any */
|
155 | 32a1c08b | Peter Lieven | g_free(it->it_data); |
156 | 32a1c08b | Peter Lieven | |
157 | 9fb26641 | Orit Wasserman | if (!it->it_data) {
|
158 | 9fb26641 | Orit Wasserman | cache->num_items++; |
159 | 9fb26641 | Orit Wasserman | } |
160 | 9fb26641 | Orit Wasserman | |
161 | ee0b44aa | Peter Lieven | it->it_data = g_memdup(pdata, cache->page_size); |
162 | 9fb26641 | Orit Wasserman | it->it_age = ++cache->max_item_age; |
163 | 9fb26641 | Orit Wasserman | it->it_addr = addr; |
164 | 9fb26641 | Orit Wasserman | } |
165 | 9fb26641 | Orit Wasserman | |
166 | 9fb26641 | Orit Wasserman | int64_t cache_resize(PageCache *cache, int64_t new_num_pages) |
167 | 9fb26641 | Orit Wasserman | { |
168 | 9fb26641 | Orit Wasserman | PageCache *new_cache; |
169 | 9fb26641 | Orit Wasserman | int64_t i; |
170 | 9fb26641 | Orit Wasserman | |
171 | 9fb26641 | Orit Wasserman | CacheItem *old_it, *new_it; |
172 | 9fb26641 | Orit Wasserman | |
173 | 9fb26641 | Orit Wasserman | g_assert(cache); |
174 | 9fb26641 | Orit Wasserman | |
175 | 9fb26641 | Orit Wasserman | /* cache was not inited */
|
176 | 9fb26641 | Orit Wasserman | if (cache->page_cache == NULL) { |
177 | 9fb26641 | Orit Wasserman | return -1; |
178 | 9fb26641 | Orit Wasserman | } |
179 | 9fb26641 | Orit Wasserman | |
180 | 9fb26641 | Orit Wasserman | /* same size */
|
181 | 9fb26641 | Orit Wasserman | if (pow2floor(new_num_pages) == cache->max_num_items) {
|
182 | 9fb26641 | Orit Wasserman | return cache->max_num_items;
|
183 | 9fb26641 | Orit Wasserman | } |
184 | 9fb26641 | Orit Wasserman | |
185 | 9fb26641 | Orit Wasserman | new_cache = cache_init(new_num_pages, cache->page_size); |
186 | 9fb26641 | Orit Wasserman | if (!(new_cache)) {
|
187 | 9fb26641 | Orit Wasserman | DPRINTF("Error creating new cache\n");
|
188 | 9fb26641 | Orit Wasserman | return -1; |
189 | 9fb26641 | Orit Wasserman | } |
190 | 9fb26641 | Orit Wasserman | |
191 | 9fb26641 | Orit Wasserman | /* move all data from old cache */
|
192 | 9fb26641 | Orit Wasserman | for (i = 0; i < cache->max_num_items; i++) { |
193 | 9fb26641 | Orit Wasserman | old_it = &cache->page_cache[i]; |
194 | 9fb26641 | Orit Wasserman | if (old_it->it_addr != -1) { |
195 | 9fb26641 | Orit Wasserman | /* check for collision, if there is, keep MRU page */
|
196 | 9fb26641 | Orit Wasserman | new_it = cache_get_by_addr(new_cache, old_it->it_addr); |
197 | a0ee2031 | Orit Wasserman | if (new_it->it_data && new_it->it_age >= old_it->it_age) {
|
198 | 9fb26641 | Orit Wasserman | /* keep the MRU page */
|
199 | a0ee2031 | Orit Wasserman | g_free(old_it->it_data); |
200 | 9fb26641 | Orit Wasserman | } else {
|
201 | a0ee2031 | Orit Wasserman | if (!new_it->it_data) {
|
202 | a0ee2031 | Orit Wasserman | new_cache->num_items++; |
203 | a0ee2031 | Orit Wasserman | } |
204 | a0ee2031 | Orit Wasserman | g_free(new_it->it_data); |
205 | a0ee2031 | Orit Wasserman | new_it->it_data = old_it->it_data; |
206 | a0ee2031 | Orit Wasserman | new_it->it_age = old_it->it_age; |
207 | a0ee2031 | Orit Wasserman | new_it->it_addr = old_it->it_addr; |
208 | 9fb26641 | Orit Wasserman | } |
209 | 9fb26641 | Orit Wasserman | } |
210 | 9fb26641 | Orit Wasserman | } |
211 | 9fb26641 | Orit Wasserman | |
212 | 0db65d62 | Orit Wasserman | g_free(cache->page_cache); |
213 | 9fb26641 | Orit Wasserman | cache->page_cache = new_cache->page_cache; |
214 | 9fb26641 | Orit Wasserman | cache->max_num_items = new_cache->max_num_items; |
215 | 9fb26641 | Orit Wasserman | cache->num_items = new_cache->num_items; |
216 | 9fb26641 | Orit Wasserman | |
217 | 9fb26641 | Orit Wasserman | g_free(new_cache); |
218 | 9fb26641 | Orit Wasserman | |
219 | 9fb26641 | Orit Wasserman | return cache->max_num_items;
|
220 | 9fb26641 | Orit Wasserman | } |