Revision 49381094

b/Makefile.objs
19 19
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
20 20

  
21 21
block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
22
block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o
22
block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
23 23
block-nested-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
24 24
block-nested-y += qed-check.o
25 25
block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
b/block/qcow2-cache.c
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
    int                     size;
39
    Qcow2CachedTable*       entries;
40
    struct Qcow2Cache*      depends;
41
    bool                    writethrough;
42
};
43

  
44
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
45
    bool writethrough)
46
{
47
    BDRVQcowState *s = bs->opaque;
48
    Qcow2Cache *c;
49
    int i;
50

  
51
    c = qemu_mallocz(sizeof(*c));
52
    c->size = num_tables;
53
    c->entries = qemu_mallocz(sizeof(*c->entries) * num_tables);
54
    c->writethrough = writethrough;
55

  
56
    for (i = 0; i < c->size; i++) {
57
        c->entries[i].table = qemu_blockalign(bs, s->cluster_size);
58
    }
59

  
60
    return c;
61
}
62

  
63
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c)
64
{
65
    int i;
66

  
67
    for (i = 0; i < c->size; i++) {
68
        assert(c->entries[i].ref == 0);
69
        qemu_vfree(c->entries[i].table);
70
    }
71

  
72
    qemu_free(c->entries);
73
    qemu_free(c);
74

  
75
    return 0;
76
}
77

  
78
static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c)
79
{
80
    int ret;
81

  
82
    ret = qcow2_cache_flush(bs, c->depends);
83
    if (ret < 0) {
84
        return ret;
85
    }
86

  
87
    c->depends = NULL;
88
    return 0;
89
}
90

  
91
static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
92
{
93
    BDRVQcowState *s = bs->opaque;
94
    int ret;
95

  
96
    if (!c->entries[i].dirty || !c->entries[i].offset) {
97
        return 0;
98
    }
99

  
100
    if (c->depends) {
101
        ret = qcow2_cache_flush_dependency(bs, c);
102
        if (ret < 0) {
103
            return ret;
104
        }
105
    }
106

  
107
    ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table,
108
        s->cluster_size);
109
    if (ret < 0) {
110
        return ret;
111
    }
112

  
113
    c->entries[i].dirty = false;
114

  
115
    return 0;
116
}
117

  
118
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
119
{
120
    int result = 0;
121
    int ret;
122
    int i;
123

  
124
    for (i = 0; i < c->size; i++) {
125
        ret = qcow2_cache_entry_flush(bs, c, i);
126
        if (ret < 0 && result != -ENOSPC) {
127
            result = ret;
128
        }
129
    }
130

  
131
    if (result == 0) {
132
        ret = bdrv_flush(bs->file);
133
        if (ret < 0) {
134
            result = ret;
135
        }
136
    }
137

  
138
    return result;
139
}
140

  
141
int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
142
    Qcow2Cache *dependency)
143
{
144
    int ret;
145

  
146
    if (dependency->depends) {
147
        ret = qcow2_cache_flush_dependency(bs, dependency);
148
        if (ret < 0) {
149
            return ret;
150
        }
151
    }
152

  
153
    if (c->depends && (c->depends != dependency)) {
154
        ret = qcow2_cache_flush_dependency(bs, c);
155
        if (ret < 0) {
156
            return ret;
157
        }
158
    }
159

  
160
    c->depends = dependency;
161
    return 0;
162
}
163

  
164
static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c)
165
{
166
    int i;
167
    int min_count = INT_MAX;
168
    int min_index = -1;
169

  
170

  
171
    for (i = 0; i < c->size; i++) {
172
        if (c->entries[i].ref) {
173
            continue;
174
        }
175

  
176
        if (c->entries[i].cache_hits < min_count) {
177
            min_index = i;
178
            min_count = c->entries[i].cache_hits;
179
        }
180

  
181
        /* Give newer hits priority */
182
        /* TODO Check how to optimize the replacement strategy */
183
        c->entries[i].cache_hits /= 2;
184
    }
185

  
186
    if (min_index == -1) {
187
        /* This can't happen in current synchronous code, but leave the check
188
         * here as a reminder for whoever starts using AIO with the cache */
189
        abort();
190
    }
191
    return min_index;
192
}
193

  
194
static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
195
    uint64_t offset, void **table, bool read_from_disk)
196
{
197
    BDRVQcowState *s = bs->opaque;
198
    int i;
199
    int ret;
200

  
201
    /* Check if the table is already cached */
202
    for (i = 0; i < c->size; i++) {
203
        if (c->entries[i].offset == offset) {
204
            goto found;
205
        }
206
    }
207

  
208
    /* If not, write a table back and replace it */
209
    i = qcow2_cache_find_entry_to_replace(c);
210
    if (i < 0) {
211
        return i;
212
    }
213

  
214
    ret = qcow2_cache_entry_flush(bs, c, i);
215
    if (ret < 0) {
216
        return ret;
217
    }
218

  
219
    c->entries[i].offset = 0;
220
    if (read_from_disk) {
221
        ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size);
222
        if (ret < 0) {
223
            return ret;
224
        }
225
    }
226

  
227
    /* Give the table some hits for the start so that it won't be replaced
228
     * immediately. The number 32 is completely arbitrary. */
229
    c->entries[i].cache_hits = 32;
230
    c->entries[i].offset = offset;
231

  
232
    /* And return the right table */
233
found:
234
    c->entries[i].cache_hits++;
235
    c->entries[i].ref++;
236
    *table = c->entries[i].table;
237
    return 0;
238
}
239

  
240
int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
241
    void **table)
242
{
243
    return qcow2_cache_do_get(bs, c, offset, table, true);
244
}
245

  
246
int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
247
    void **table)
248
{
249
    return qcow2_cache_do_get(bs, c, offset, table, false);
250
}
251

  
252
int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table)
253
{
254
    int i;
255

  
256
    for (i = 0; i < c->size; i++) {
257
        if (c->entries[i].table == *table) {
258
            goto found;
259
        }
260
    }
261
    return -ENOENT;
262

  
263
found:
264
    c->entries[i].ref--;
265
    *table = NULL;
266

  
267
    assert(c->entries[i].ref >= 0);
268

  
269
    if (c->writethrough) {
270
        return qcow2_cache_entry_flush(bs, c, i);
271
    } else {
272
        return 0;
273
    }
274
}
275

  
276
void qcow2_cache_entry_mark_dirty(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
    abort();
286

  
287
found:
288
    c->entries[i].dirty = true;
289
}
290

  
b/block/qcow2.h
78 78
    uint64_t vm_clock_nsec;
79 79
} QCowSnapshot;
80 80

  
81
struct Qcow2Cache;
82
typedef struct Qcow2Cache Qcow2Cache;
83

  
81 84
typedef struct BDRVQcowState {
82 85
    int cluster_bits;
83 86
    int cluster_size;
......
215 218
void qcow2_free_snapshots(BlockDriverState *bs);
216 219
int qcow2_read_snapshots(BlockDriverState *bs);
217 220

  
221
/* qcow2-cache.c functions */
222
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
223
    bool writethrough);
224
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c);
225

  
226
void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table);
227
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c);
228
int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
229
    Qcow2Cache *dependency);
230

  
231
int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
232
    void **table);
233
int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
234
    void **table);
235
int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table);
236

  
218 237
#endif

Also available in: Unified diff