summaryrefslogtreecommitdiff
path: root/cache.c
blob: 963eb76f9f9a7b598017d6a2a100f51a4a0df2b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * cache.h
 *
 * Created by: Petr Tesarik <ptesarik@suse.cz>
 *
 * Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include "makedumpfile.h"
#include "cache.h"
#include "print_info.h"

struct cache {
	struct cache_entry *head, *tail;
};

/* 8 pages covers 4-level paging plus 4 data pages */
#define CACHE_SIZE	8
static struct cache_entry entries[CACHE_SIZE];
static struct cache_entry *pool[CACHE_SIZE];
static int avail = CACHE_SIZE;

static struct cache used, pending;

static void *cachebuf;

int
cache_init(void)
{
	int i;

	cachebuf = malloc(info->page_size * CACHE_SIZE);
	if (cachebuf == NULL) {
		ERRMSG("Can't allocate memory for cache. %s\n",
		       strerror(errno));
		return FALSE;
	}

	for (i = 0; i < CACHE_SIZE; ++i)
		pool[i] = &entries[i];

	return TRUE;
}

static void
add_entry(struct cache *cache, struct cache_entry *entry)
{
	entry->next = cache->head;
	entry->prev = NULL;
	if (cache->head)
		cache->head->prev = entry;
	cache->head = entry;
	if (!cache->tail)
		cache->tail = entry;
}

static void
remove_entry(struct cache *cache, struct cache_entry *entry)
{
	if (entry->next)
		entry->next->prev = entry->prev;
	else
		cache->tail = entry->prev;

	if (entry->prev)
		entry->prev->next = entry->next;
	else
		cache->head = entry->next;
}

void *
cache_search(unsigned long long paddr, unsigned long length)
{
	struct cache_entry *entry;
	for (entry = used.head; entry; entry = entry->next) {
		size_t off = paddr - entry->paddr;
		if (off < entry->buflen &&
		    length <= entry->buflen - off) {
			if (entry != used.head) {
				remove_entry(&used, entry);
				add_entry(&used, entry);
			}
			return entry->bufptr + off;
		}
	}

	return NULL;		/* cache miss */
}

struct cache_entry *
cache_alloc(unsigned long long paddr)
{
	struct cache_entry *entry = NULL;
	int idx;

	if (avail) {
		entry = pool[--avail];
	} else if (used.tail) {
		entry = used.tail;
		remove_entry(&used, entry);
		if (entry->discard)
			entry->discard(entry);
	} else
		return NULL;

	idx = entry - entries;
	entry->paddr = paddr;
	entry->bufptr = cachebuf + idx * info->page_size;
	entry->buflen = info->page_size;
	entry->discard = NULL;
	add_entry(&pending, entry);

	return entry;
}

void
cache_add(struct cache_entry *entry)
{
	remove_entry(&pending, entry);
	add_entry(&used, entry);
}

void
cache_free(struct cache_entry *entry)
{
	remove_entry(&pending, entry);
	pool[avail++] = entry;
}