qemu/migration/page_cache.c
<<
>>
Prefs
   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 "qemu/osdep.h"
  16
  17#include "qapi/qmp/qerror.h"
  18#include "qapi/error.h"
  19#include "qemu/host-utils.h"
  20#include "page_cache.h"
  21#include "trace.h"
  22
  23/* the page in cache will not be replaced in two cycles */
  24#define CACHED_PAGE_LIFETIME 2
  25
  26typedef struct CacheItem CacheItem;
  27
  28struct CacheItem {
  29    uint64_t it_addr;
  30    uint64_t it_age;
  31    uint8_t *it_data;
  32};
  33
  34struct PageCache {
  35    CacheItem *page_cache;
  36    size_t page_size;
  37    size_t max_num_items;
  38    size_t num_items;
  39};
  40
  41PageCache *cache_init(uint64_t new_size, size_t page_size, Error **errp)
  42{
  43    int64_t i;
  44    size_t num_pages = new_size / page_size;
  45    PageCache *cache;
  46
  47    if (new_size < page_size) {
  48        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
  49                   "is smaller than one target page size");
  50        return NULL;
  51    }
  52
  53    /* round down to the nearest power of 2 */
  54    if (!is_power_of_2(num_pages)) {
  55        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
  56                   "is not a power of two number of pages");
  57        return NULL;
  58    }
  59
  60    /* We prefer not to abort if there is no memory */
  61    cache = g_try_malloc(sizeof(*cache));
  62    if (!cache) {
  63        error_setg(errp, "Failed to allocate cache");
  64        return NULL;
  65    }
  66    cache->page_size = page_size;
  67    cache->num_items = 0;
  68    cache->max_num_items = num_pages;
  69
  70    trace_migration_pagecache_init(cache->max_num_items);
  71
  72    /* We prefer not to abort if there is no memory */
  73    cache->page_cache = g_try_malloc((cache->max_num_items) *
  74                                     sizeof(*cache->page_cache));
  75    if (!cache->page_cache) {
  76        error_setg(errp, "Failed to allocate page cache");
  77        g_free(cache);
  78        return NULL;
  79    }
  80
  81    for (i = 0; i < cache->max_num_items; i++) {
  82        cache->page_cache[i].it_data = NULL;
  83        cache->page_cache[i].it_age = 0;
  84        cache->page_cache[i].it_addr = -1;
  85    }
  86
  87    return cache;
  88}
  89
  90void cache_fini(PageCache *cache)
  91{
  92    int64_t i;
  93
  94    g_assert(cache);
  95    g_assert(cache->page_cache);
  96
  97    for (i = 0; i < cache->max_num_items; i++) {
  98        g_free(cache->page_cache[i].it_data);
  99    }
 100
 101    g_free(cache->page_cache);
 102    cache->page_cache = NULL;
 103    g_free(cache);
 104}
 105
 106static size_t cache_get_cache_pos(const PageCache *cache,
 107                                  uint64_t address)
 108{
 109    g_assert(cache->max_num_items);
 110    return (address / cache->page_size) & (cache->max_num_items - 1);
 111}
 112
 113static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr)
 114{
 115    size_t pos;
 116
 117    g_assert(cache);
 118    g_assert(cache->page_cache);
 119
 120    pos = cache_get_cache_pos(cache, addr);
 121
 122    return &cache->page_cache[pos];
 123}
 124
 125uint8_t *get_cached_data(const PageCache *cache, uint64_t addr)
 126{
 127    return cache_get_by_addr(cache, addr)->it_data;
 128}
 129
 130bool cache_is_cached(const PageCache *cache, uint64_t addr,
 131                     uint64_t current_age)
 132{
 133    CacheItem *it;
 134
 135    it = cache_get_by_addr(cache, addr);
 136
 137    if (it->it_addr == addr) {
 138        /* update the it_age when the cache hit */
 139        it->it_age = current_age;
 140        return true;
 141    }
 142    return false;
 143}
 144
 145int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata,
 146                 uint64_t current_age)
 147{
 148
 149    CacheItem *it;
 150
 151    /* actual update of entry */
 152    it = cache_get_by_addr(cache, addr);
 153
 154    if (it->it_data && it->it_addr != addr &&
 155        it->it_age + CACHED_PAGE_LIFETIME > current_age) {
 156        /* the cache page is fresh, don't replace it */
 157        return -1;
 158    }
 159    /* allocate page */
 160    if (!it->it_data) {
 161        it->it_data = g_try_malloc(cache->page_size);
 162        if (!it->it_data) {
 163            trace_migration_pagecache_insert();
 164            return -1;
 165        }
 166        cache->num_items++;
 167    }
 168
 169    memcpy(it->it_data, pdata, cache->page_size);
 170
 171    it->it_age = current_age;
 172    it->it_addr = addr;
 173
 174    return 0;
 175}
 176