~ubuntu-branches/ubuntu/wily/qemu-kvm-spice/wily

« back to all changes in this revision

Viewing changes to block/qcow2-cache.c

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-10-19 10:44:56 UTC
  • Revision ID: james.westby@ubuntu.com-20111019104456-xgvskumk3sxi97f4
Tags: upstream-0.15.0+noroms
ImportĀ upstreamĀ versionĀ 0.15.0+noroms

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * L2/refcount table cache for the QCOW2 format
 
3
 *
 
4
 * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
 
5
 *
 
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 
7
 * of this software and associated documentation files (the "Software"), to deal
 
8
 * in the Software without restriction, including without limitation the rights
 
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
10
 * copies of the Software, and to permit persons to whom the Software is
 
11
 * furnished to do so, subject to the following conditions:
 
12
 *
 
13
 * The above copyright notice and this permission notice shall be included in
 
14
 * all copies or substantial portions of the Software.
 
15
 *
 
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 
19
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
22
 * THE SOFTWARE.
 
23
 */
 
24
 
 
25
#include "block_int.h"
 
26
#include "qemu-common.h"
 
27
#include "qcow2.h"
 
28
 
 
29
typedef struct Qcow2CachedTable {
 
30
    void*   table;
 
31
    int64_t offset;
 
32
    bool    dirty;
 
33
    int     cache_hits;
 
34
    int     ref;
 
35
} Qcow2CachedTable;
 
36
 
 
37
struct Qcow2Cache {
 
38
    Qcow2CachedTable*       entries;
 
39
    struct Qcow2Cache*      depends;
 
40
    int                     size;
 
41
    bool                    depends_on_flush;
 
42
    bool                    writethrough;
 
43
};
 
44
 
 
45
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
 
46
    bool writethrough)
 
47
{
 
48
    BDRVQcowState *s = bs->opaque;
 
49
    Qcow2Cache *c;
 
50
    int i;
 
51
 
 
52
    c = qemu_mallocz(sizeof(*c));
 
53
    c->size = num_tables;
 
54
    c->entries = qemu_mallocz(sizeof(*c->entries) * num_tables);
 
55
    c->writethrough = writethrough;
 
56
 
 
57
    for (i = 0; i < c->size; i++) {
 
58
        c->entries[i].table = qemu_blockalign(bs, s->cluster_size);
 
59
    }
 
60
 
 
61
    return c;
 
62
}
 
63
 
 
64
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c)
 
65
{
 
66
    int i;
 
67
 
 
68
    for (i = 0; i < c->size; i++) {
 
69
        assert(c->entries[i].ref == 0);
 
70
        qemu_vfree(c->entries[i].table);
 
71
    }
 
72
 
 
73
    qemu_free(c->entries);
 
74
    qemu_free(c);
 
75
 
 
76
    return 0;
 
77
}
 
78
 
 
79
static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c)
 
80
{
 
81
    int ret;
 
82
 
 
83
    ret = qcow2_cache_flush(bs, c->depends);
 
84
    if (ret < 0) {
 
85
        return ret;
 
86
    }
 
87
 
 
88
    c->depends = NULL;
 
89
    c->depends_on_flush = false;
 
90
 
 
91
    return 0;
 
92
}
 
93
 
 
94
static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
 
95
{
 
96
    BDRVQcowState *s = bs->opaque;
 
97
    int ret = 0;
 
98
 
 
99
    if (!c->entries[i].dirty || !c->entries[i].offset) {
 
100
        return 0;
 
101
    }
 
102
 
 
103
    if (c->depends) {
 
104
        ret = qcow2_cache_flush_dependency(bs, c);
 
105
    } else if (c->depends_on_flush) {
 
106
        ret = bdrv_flush(bs->file);
 
107
        if (ret >= 0) {
 
108
            c->depends_on_flush = false;
 
109
        }
 
110
    }
 
111
 
 
112
    if (ret < 0) {
 
113
        return ret;
 
114
    }
 
115
 
 
116
    if (c == s->refcount_block_cache) {
 
117
        BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
 
118
    } else if (c == s->l2_table_cache) {
 
119
        BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
 
120
    }
 
121
 
 
122
    ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table,
 
123
        s->cluster_size);
 
124
    if (ret < 0) {
 
125
        return ret;
 
126
    }
 
127
 
 
128
    c->entries[i].dirty = false;
 
129
 
 
130
    return 0;
 
131
}
 
132
 
 
133
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
 
134
{
 
135
    int result = 0;
 
136
    int ret;
 
137
    int i;
 
138
 
 
139
    for (i = 0; i < c->size; i++) {
 
140
        ret = qcow2_cache_entry_flush(bs, c, i);
 
141
        if (ret < 0 && result != -ENOSPC) {
 
142
            result = ret;
 
143
        }
 
144
    }
 
145
 
 
146
    if (result == 0) {
 
147
        ret = bdrv_flush(bs->file);
 
148
        if (ret < 0) {
 
149
            result = ret;
 
150
        }
 
151
    }
 
152
 
 
153
    return result;
 
154
}
 
155
 
 
156
int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
 
157
    Qcow2Cache *dependency)
 
158
{
 
159
    int ret;
 
160
 
 
161
    if (dependency->depends) {
 
162
        ret = qcow2_cache_flush_dependency(bs, dependency);
 
163
        if (ret < 0) {
 
164
            return ret;
 
165
        }
 
166
    }
 
167
 
 
168
    if (c->depends && (c->depends != dependency)) {
 
169
        ret = qcow2_cache_flush_dependency(bs, c);
 
170
        if (ret < 0) {
 
171
            return ret;
 
172
        }
 
173
    }
 
174
 
 
175
    c->depends = dependency;
 
176
    return 0;
 
177
}
 
178
 
 
179
void qcow2_cache_depends_on_flush(Qcow2Cache *c)
 
180
{
 
181
    c->depends_on_flush = true;
 
182
}
 
183
 
 
184
static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c)
 
185
{
 
186
    int i;
 
187
    int min_count = INT_MAX;
 
188
    int min_index = -1;
 
189
 
 
190
 
 
191
    for (i = 0; i < c->size; i++) {
 
192
        if (c->entries[i].ref) {
 
193
            continue;
 
194
        }
 
195
 
 
196
        if (c->entries[i].cache_hits < min_count) {
 
197
            min_index = i;
 
198
            min_count = c->entries[i].cache_hits;
 
199
        }
 
200
 
 
201
        /* Give newer hits priority */
 
202
        /* TODO Check how to optimize the replacement strategy */
 
203
        c->entries[i].cache_hits /= 2;
 
204
    }
 
205
 
 
206
    if (min_index == -1) {
 
207
        /* This can't happen in current synchronous code, but leave the check
 
208
         * here as a reminder for whoever starts using AIO with the cache */
 
209
        abort();
 
210
    }
 
211
    return min_index;
 
212
}
 
213
 
 
214
static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
 
215
    uint64_t offset, void **table, bool read_from_disk)
 
216
{
 
217
    BDRVQcowState *s = bs->opaque;
 
218
    int i;
 
219
    int ret;
 
220
 
 
221
    /* Check if the table is already cached */
 
222
    for (i = 0; i < c->size; i++) {
 
223
        if (c->entries[i].offset == offset) {
 
224
            goto found;
 
225
        }
 
226
    }
 
227
 
 
228
    /* If not, write a table back and replace it */
 
229
    i = qcow2_cache_find_entry_to_replace(c);
 
230
    if (i < 0) {
 
231
        return i;
 
232
    }
 
233
 
 
234
    ret = qcow2_cache_entry_flush(bs, c, i);
 
235
    if (ret < 0) {
 
236
        return ret;
 
237
    }
 
238
 
 
239
    c->entries[i].offset = 0;
 
240
    if (read_from_disk) {
 
241
        if (c == s->l2_table_cache) {
 
242
            BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
 
243
        }
 
244
 
 
245
        ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size);
 
246
        if (ret < 0) {
 
247
            return ret;
 
248
        }
 
249
    }
 
250
 
 
251
    /* Give the table some hits for the start so that it won't be replaced
 
252
     * immediately. The number 32 is completely arbitrary. */
 
253
    c->entries[i].cache_hits = 32;
 
254
    c->entries[i].offset = offset;
 
255
 
 
256
    /* And return the right table */
 
257
found:
 
258
    c->entries[i].cache_hits++;
 
259
    c->entries[i].ref++;
 
260
    *table = c->entries[i].table;
 
261
    return 0;
 
262
}
 
263
 
 
264
int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
 
265
    void **table)
 
266
{
 
267
    return qcow2_cache_do_get(bs, c, offset, table, true);
 
268
}
 
269
 
 
270
int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
 
271
    void **table)
 
272
{
 
273
    return qcow2_cache_do_get(bs, c, offset, table, false);
 
274
}
 
275
 
 
276
int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table)
 
277
{
 
278
    int i;
 
279
 
 
280
    for (i = 0; i < c->size; i++) {
 
281
        if (c->entries[i].table == *table) {
 
282
            goto found;
 
283
        }
 
284
    }
 
285
    return -ENOENT;
 
286
 
 
287
found:
 
288
    c->entries[i].ref--;
 
289
    *table = NULL;
 
290
 
 
291
    assert(c->entries[i].ref >= 0);
 
292
 
 
293
    if (c->writethrough) {
 
294
        return qcow2_cache_entry_flush(bs, c, i);
 
295
    } else {
 
296
        return 0;
 
297
    }
 
298
}
 
299
 
 
300
void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table)
 
301
{
 
302
    int i;
 
303
 
 
304
    for (i = 0; i < c->size; i++) {
 
305
        if (c->entries[i].table == table) {
 
306
            goto found;
 
307
        }
 
308
    }
 
309
    abort();
 
310
 
 
311
found:
 
312
    c->entries[i].dirty = true;
 
313
}
 
314
 
 
315
bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
 
316
    bool enable)
 
317
{
 
318
    bool old = c->writethrough;
 
319
 
 
320
    if (!old && enable) {
 
321
        qcow2_cache_flush(bs, c);
 
322
    }
 
323
 
 
324
    c->writethrough = enable;
 
325
    return old;
 
326
}