Skip to content

Commit

Permalink
COW: Speed up writes
Browse files Browse the repository at this point in the history
Process a whole sector's worth of COW bits by reading a sector, setting
the bits after skipping any already set bits, then writing it out again.
Make sure we only flush once before writing metadata, and only if we
need to write metadata.

Signed-off-by: Charlie Shepherd <charlie@ctshepherd.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
  • Loading branch information
ctshepherd authored and kevmw committed Nov 29, 2013
1 parent 21b5683 commit 14b98fd
Showing 1 changed file with 50 additions and 38 deletions.
88 changes: 50 additions & 38 deletions block/cow.c
Expand Up @@ -103,40 +103,18 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags,
return ret;
}

/*
* XXX(hch): right now these functions are extremely inefficient.
* We should just read the whole bitmap we'll need in one go instead.
*/
static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum, bool *first)
static inline void cow_set_bits(uint8_t *bitmap, int start, int64_t nb_sectors)
{
uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8;
uint8_t bitmap;
int ret;

ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
if (ret < 0) {
return ret;
}

if (bitmap & (1 << (bitnum % 8))) {
return 0;
}

if (*first) {
ret = bdrv_flush(bs->file);
if (ret < 0) {
return ret;
int64_t bitnum = start, last = start + nb_sectors;
while (bitnum < last) {
if ((bitnum & 7) == 0 && bitnum + 8 <= last) {
bitmap[bitnum / 8] = 0xFF;
bitnum += 8;
continue;
}
*first = false;
bitmap[bitnum/8] |= (1 << (bitnum % 8));
bitnum++;
}

bitmap |= (1 << (bitnum % 8));

ret = bdrv_pwrite(bs->file, offset, &bitmap, sizeof(bitmap));
if (ret < 0) {
return ret;
}
return 0;
}

#define BITS_PER_BITMAP_SECTOR (512 * 8)
Expand Down Expand Up @@ -204,18 +182,52 @@ static int64_t coroutine_fn cow_co_get_block_status(BlockDriverState *bs,
static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
int nb_sectors)
{
int error = 0;
int i;
int64_t bitnum = sector_num + sizeof(struct cow_header_v2) * 8;
uint64_t offset = (bitnum / 8) & -BDRV_SECTOR_SIZE;
bool first = true;
int sector_bits;

for ( ; nb_sectors;
bitnum += sector_bits,
nb_sectors -= sector_bits,
offset += BDRV_SECTOR_SIZE) {
int ret, set;
uint8_t bitmap[BDRV_SECTOR_SIZE];

bitnum &= BITS_PER_BITMAP_SECTOR - 1;
sector_bits = MIN(nb_sectors, BITS_PER_BITMAP_SECTOR - bitnum);

ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
if (ret < 0) {
return ret;
}

/* Skip over any already set bits */
set = cow_find_streak(bitmap, 1, bitnum, sector_bits);
bitnum += set;
sector_bits -= set;
nb_sectors -= set;
if (!sector_bits) {
continue;
}

if (first) {
ret = bdrv_flush(bs->file);
if (ret < 0) {
return ret;
}
first = false;
}

cow_set_bits(bitmap, bitnum, sector_bits);

for (i = 0; i < nb_sectors; i++) {
error = cow_set_bit(bs, sector_num + i, &first);
if (error) {
break;
ret = bdrv_pwrite(bs->file, offset, &bitmap, sizeof(bitmap));
if (ret < 0) {
return ret;
}
}

return error;
return 0;
}

static int coroutine_fn cow_read(BlockDriverState *bs, int64_t sector_num,
Expand Down

0 comments on commit 14b98fd

Please sign in to comment.