Add cache handling functions

Add MRU page cache mechanism.
The page are accessed by their address.

Signed-off-by: Benoit Hudzia <benoit.hudzia@sap.com>
Signed-off-by: Petter Svard <petters@cs.umu.se>
Signed-off-by: Aidan Shribman <aidan.shribman@sap.com>
Signed-off-by: Orit Wasserman <owasserm@redhat.com>

Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Orit Wasserman 2012-08-06 21:42:50 +03:00 committed by Juan Quintela
parent 34c26412b7
commit 9fb26641ab
5 changed files with 320 additions and 0 deletions

View File

@ -77,6 +77,7 @@ common-obj-y += qemu-char.o #aio.o
common-obj-y += block-migration.o iohandler.o
common-obj-y += pflib.o
common-obj-y += bitmap.o bitops.o
common-obj-y += page_cache.o
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
common-obj-$(CONFIG_WIN32) += version.o

View File

@ -382,3 +382,12 @@ int qemu_parse_fd(const char *param)
}
return fd;
}
/* round down to the nearest power of 2*/
int64_t pow2floor(int64_t value)
{
if (!is_power_of_2(value)) {
value = 0x8000000000000000ULL >> clz64(value);
}
return value;
}

79
include/qemu/page_cache.h Normal file
View File

@ -0,0 +1,79 @@
/*
* Page cache for QEMU
* The cache is base on a hash of the page address
*
* Copyright 2012 Red Hat, Inc. and/or its affiliates
*
* Authors:
* Orit Wasserman <owasserm@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef PAGE_CACHE_H
#define PAGE_CACHE_H
/* Page cache for storing guest pages */
typedef struct PageCache PageCache;
/**
* cache_init: Initialize the page cache
*
*
* Returns new allocated cache or NULL on error
*
* @cache pointer to the PageCache struct
* @num_pages: cache maximal number of cached pages
* @page_size: cache page size
*/
PageCache *cache_init(int64_t num_pages, unsigned int page_size);
/**
* cache_fini: free all cache resources
* @cache pointer to the PageCache struct
*/
void cache_fini(PageCache *cache);
/**
* cache_is_cached: Checks to see if the page is cached
*
* Returns %true if page is cached
*
* @cache pointer to the PageCache struct
* @addr: page addr
*/
bool cache_is_cached(const PageCache *cache, uint64_t addr);
/**
* get_cached_data: Get the data cached for an addr
*
* Returns pointer to the data cached or NULL if not cached
*
* @cache pointer to the PageCache struct
* @addr: page addr
*/
uint8_t *get_cached_data(const PageCache *cache, uint64_t addr);
/**
* cache_insert: insert the page into the cache. the previous value will be overwritten
*
* @cache pointer to the PageCache struct
* @addr: page address
* @pdata: pointer to the page
*/
void cache_insert(PageCache *cache, uint64_t addr, uint8_t *pdata);
/**
* cache_resize: resize the page cache. In case of size reduction the extra
* pages will be freed
*
* Returns -1 on error new cache size on success
*
* @cache pointer to the PageCache struct
* @num_pages: new page cache size (in pages)
*/
int64_t cache_resize(PageCache *cache, int64_t num_pages);
#endif

218
page_cache.c Normal file
View File

@ -0,0 +1,218 @@
/*
* Page cache for QEMU
* The cache is base on a hash of the page address
*
* Copyright 2012 Red Hat, Inc. and/or its affiliates
*
* Authors:
* Orit Wasserman <owasserm@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdbool.h>
#include <glib.h>
#include <strings.h>
#include "qemu-common.h"
#include "qemu/page_cache.h"
#ifdef DEBUG_CACHE
#define DPRINTF(fmt, ...) \
do { fprintf(stdout, "cache: " fmt, ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) \
do { } while (0)
#endif
typedef struct CacheItem CacheItem;
struct CacheItem {
uint64_t it_addr;
uint64_t it_age;
uint8_t *it_data;
};
struct PageCache {
CacheItem *page_cache;
unsigned int page_size;
int64_t max_num_items;
uint64_t max_item_age;
int64_t num_items;
};
PageCache *cache_init(int64_t num_pages, unsigned int page_size)
{
int64_t i;
PageCache *cache;
if (num_pages <= 0) {
DPRINTF("invalid number of pages\n");
return NULL;
}
cache = g_malloc(sizeof(*cache));
/* round down to the nearest power of 2 */
if (!is_power_of_2(num_pages)) {
num_pages = pow2floor(num_pages);
DPRINTF("rounding down to %" PRId64 "\n", num_pages);
}
cache->page_size = page_size;
cache->num_items = 0;
cache->max_item_age = 0;
cache->max_num_items = num_pages;
DPRINTF("Setting cache buckets to %" PRId64 "\n", cache->max_num_items);
cache->page_cache = g_malloc((cache->max_num_items) *
sizeof(*cache->page_cache));
for (i = 0; i < cache->max_num_items; i++) {
cache->page_cache[i].it_data = NULL;
cache->page_cache[i].it_age = 0;
cache->page_cache[i].it_addr = -1;
}
return cache;
}
void cache_fini(PageCache *cache)
{
int64_t i;
g_assert(cache);
g_assert(cache->page_cache);
for (i = 0; i < cache->max_num_items; i++) {
g_free(cache->page_cache[i].it_data);
}
g_free(cache->page_cache);
cache->page_cache = NULL;
}
static size_t cache_get_cache_pos(const PageCache *cache,
uint64_t address)
{
size_t pos;
g_assert(cache->max_num_items);
pos = (address / cache->page_size) & (cache->max_num_items - 1);
return pos;
}
bool cache_is_cached(const PageCache *cache, uint64_t addr)
{
size_t pos;
g_assert(cache);
g_assert(cache->page_cache);
pos = cache_get_cache_pos(cache, addr);
return (cache->page_cache[pos].it_addr == addr);
}
static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr)
{
size_t pos;
g_assert(cache);
g_assert(cache->page_cache);
pos = cache_get_cache_pos(cache, addr);
return &cache->page_cache[pos];
}
uint8_t *get_cached_data(const PageCache *cache, uint64_t addr)
{
return cache_get_by_addr(cache, addr)->it_data;
}
void cache_insert(PageCache *cache, uint64_t addr, uint8_t *pdata)
{
CacheItem *it = NULL;
g_assert(cache);
g_assert(cache->page_cache);
/* actual update of entry */
it = cache_get_by_addr(cache, addr);
if (!it->it_data) {
cache->num_items++;
}
it->it_data = pdata;
it->it_age = ++cache->max_item_age;
it->it_addr = addr;
}
int64_t cache_resize(PageCache *cache, int64_t new_num_pages)
{
PageCache *new_cache;
int64_t i;
CacheItem *old_it, *new_it;
g_assert(cache);
/* cache was not inited */
if (cache->page_cache == NULL) {
return -1;
}
/* same size */
if (pow2floor(new_num_pages) == cache->max_num_items) {
return cache->max_num_items;
}
new_cache = cache_init(new_num_pages, cache->page_size);
if (!(new_cache)) {
DPRINTF("Error creating new cache\n");
return -1;
}
/* move all data from old cache */
for (i = 0; i < cache->max_num_items; i++) {
old_it = &cache->page_cache[i];
if (old_it->it_addr != -1) {
/* check for collision, if there is, keep MRU page */
new_it = cache_get_by_addr(new_cache, old_it->it_addr);
if (new_it->it_data) {
/* keep the MRU page */
if (new_it->it_age >= old_it->it_age) {
g_free(old_it->it_data);
} else {
g_free(new_it->it_data);
new_it->it_data = old_it->it_data;
new_it->it_age = old_it->it_age;
new_it->it_addr = old_it->it_addr;
}
} else {
cache_insert(new_cache, old_it->it_addr, old_it->it_data);
}
}
}
cache->page_cache = new_cache->page_cache;
cache->max_num_items = new_cache->max_num_items;
cache->num_items = new_cache->num_items;
g_free(new_cache);
return cache->max_num_items;
}

View File

@ -1,3 +1,4 @@
/* Common header file that is included by all of qemu. */
#ifndef QEMU_COMMON_H
#define QEMU_COMMON_H
@ -428,6 +429,18 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
/* Round number up to multiple */
#define QEMU_ALIGN_UP(n, m) QEMU_ALIGN_DOWN((n) + (m) - 1, (m))
static inline bool is_power_of_2(uint64_t value)
{
if (!value) {
return 0;
}
return !(value & (value - 1));
}
/* round down to the nearest power of 2*/
int64_t pow2floor(int64_t value);
#include "module.h"
#endif