Statistics
| Branch: | Revision:

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
}