mirror of https://github.com/xemu-project/xemu.git
block: implement dirty bitmap using HBitmap
This actually uses the dirty bitmap in the block layer, and converts mirroring to use an HBitmapIter. Reviewed-by: Laszlo Ersek <lersek@redhat.com> (except block/mirror.c parts) Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
e7c033c3fa
commit
8f0720ecbc
96
block.c
96
block.c
|
@ -1286,7 +1286,6 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
|
||||||
bs_dest->iostatus = bs_src->iostatus;
|
bs_dest->iostatus = bs_src->iostatus;
|
||||||
|
|
||||||
/* dirty bitmap */
|
/* dirty bitmap */
|
||||||
bs_dest->dirty_count = bs_src->dirty_count;
|
|
||||||
bs_dest->dirty_bitmap = bs_src->dirty_bitmap;
|
bs_dest->dirty_bitmap = bs_src->dirty_bitmap;
|
||||||
|
|
||||||
/* job */
|
/* job */
|
||||||
|
@ -2035,36 +2034,6 @@ int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
|
|
||||||
|
|
||||||
static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num,
|
|
||||||
int nb_sectors, int dirty)
|
|
||||||
{
|
|
||||||
int64_t start, end;
|
|
||||||
unsigned long val, idx, bit;
|
|
||||||
|
|
||||||
start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK;
|
|
||||||
end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK;
|
|
||||||
|
|
||||||
for (; start <= end; start++) {
|
|
||||||
idx = start / BITS_PER_LONG;
|
|
||||||
bit = start % BITS_PER_LONG;
|
|
||||||
val = bs->dirty_bitmap[idx];
|
|
||||||
if (dirty) {
|
|
||||||
if (!(val & (1UL << bit))) {
|
|
||||||
bs->dirty_count++;
|
|
||||||
val |= 1UL << bit;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (val & (1UL << bit)) {
|
|
||||||
bs->dirty_count--;
|
|
||||||
val &= ~(1UL << bit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bs->dirty_bitmap[idx] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return < 0 if error. Important errors are:
|
/* Return < 0 if error. Important errors are:
|
||||||
-EIO generic I/O error (may happen for all errors)
|
-EIO generic I/O error (may happen for all errors)
|
||||||
-ENOMEDIUM No media inserted.
|
-ENOMEDIUM No media inserted.
|
||||||
|
@ -4173,7 +4142,7 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bs->dirty_bitmap) {
|
if (bs->dirty_bitmap) {
|
||||||
set_dirty_bitmap(bs, sector_num, nb_sectors, 0);
|
bdrv_reset_dirty(bs, sector_num, nb_sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bs->drv->bdrv_co_discard) {
|
if (bs->drv->bdrv_co_discard) {
|
||||||
|
@ -4335,18 +4304,15 @@ void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable)
|
||||||
{
|
{
|
||||||
int64_t bitmap_size;
|
int64_t bitmap_size;
|
||||||
|
|
||||||
bs->dirty_count = 0;
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
if (!bs->dirty_bitmap) {
|
if (!bs->dirty_bitmap) {
|
||||||
bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) +
|
bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS);
|
||||||
BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG - 1;
|
bs->dirty_bitmap = hbitmap_alloc(bitmap_size,
|
||||||
bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG;
|
BDRV_LOG_SECTORS_PER_DIRTY_CHUNK);
|
||||||
|
|
||||||
bs->dirty_bitmap = g_new0(unsigned long, bitmap_size);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (bs->dirty_bitmap) {
|
if (bs->dirty_bitmap) {
|
||||||
g_free(bs->dirty_bitmap);
|
hbitmap_free(bs->dirty_bitmap);
|
||||||
bs->dirty_bitmap = NULL;
|
bs->dirty_bitmap = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4354,67 +4320,37 @@ void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable)
|
||||||
|
|
||||||
int bdrv_get_dirty(BlockDriverState *bs, int64_t sector)
|
int bdrv_get_dirty(BlockDriverState *bs, int64_t sector)
|
||||||
{
|
{
|
||||||
int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
|
if (bs->dirty_bitmap) {
|
||||||
|
return hbitmap_get(bs->dirty_bitmap, sector);
|
||||||
if (bs->dirty_bitmap &&
|
|
||||||
(sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) {
|
|
||||||
return !!(bs->dirty_bitmap[chunk / BITS_PER_LONG] &
|
|
||||||
(1UL << (chunk % BITS_PER_LONG)));
|
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t bdrv_get_next_dirty(BlockDriverState *bs, int64_t sector)
|
void bdrv_dirty_iter_init(BlockDriverState *bs, HBitmapIter *hbi)
|
||||||
{
|
{
|
||||||
int64_t chunk;
|
hbitmap_iter_init(hbi, bs->dirty_bitmap, 0);
|
||||||
int bit, elem;
|
|
||||||
|
|
||||||
/* Avoid an infinite loop. */
|
|
||||||
assert(bs->dirty_count > 0);
|
|
||||||
|
|
||||||
sector = (sector | (BDRV_SECTORS_PER_DIRTY_CHUNK - 1)) + 1;
|
|
||||||
chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
|
|
||||||
|
|
||||||
QEMU_BUILD_BUG_ON(sizeof(bs->dirty_bitmap[0]) * 8 != BITS_PER_LONG);
|
|
||||||
elem = chunk / BITS_PER_LONG;
|
|
||||||
bit = chunk % BITS_PER_LONG;
|
|
||||||
for (;;) {
|
|
||||||
if (sector >= bs->total_sectors) {
|
|
||||||
sector = 0;
|
|
||||||
bit = elem = 0;
|
|
||||||
}
|
|
||||||
if (bit == 0 && bs->dirty_bitmap[elem] == 0) {
|
|
||||||
sector += BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG;
|
|
||||||
elem++;
|
|
||||||
} else {
|
|
||||||
if (bs->dirty_bitmap[elem] & (1UL << bit)) {
|
|
||||||
return sector;
|
|
||||||
}
|
|
||||||
sector += BDRV_SECTORS_PER_DIRTY_CHUNK;
|
|
||||||
if (++bit == BITS_PER_LONG) {
|
|
||||||
bit = 0;
|
|
||||||
elem++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||||
int nr_sectors)
|
int nr_sectors)
|
||||||
{
|
{
|
||||||
set_dirty_bitmap(bs, cur_sector, nr_sectors, 1);
|
hbitmap_set(bs->dirty_bitmap, cur_sector, nr_sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
|
void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
|
||||||
int nr_sectors)
|
int nr_sectors)
|
||||||
{
|
{
|
||||||
set_dirty_bitmap(bs, cur_sector, nr_sectors, 0);
|
hbitmap_reset(bs->dirty_bitmap, cur_sector, nr_sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t bdrv_get_dirty_count(BlockDriverState *bs)
|
int64_t bdrv_get_dirty_count(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
return bs->dirty_count;
|
if (bs->dirty_bitmap) {
|
||||||
|
return hbitmap_count(bs->dirty_bitmap) >> BDRV_LOG_SECTORS_PER_DIRTY_CHUNK;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_set_in_use(BlockDriverState *bs, int in_use)
|
void bdrv_set_in_use(BlockDriverState *bs, int in_use)
|
||||||
|
|
|
@ -36,6 +36,7 @@ typedef struct MirrorBlockJob {
|
||||||
bool synced;
|
bool synced;
|
||||||
bool should_complete;
|
bool should_complete;
|
||||||
int64_t sector_num;
|
int64_t sector_num;
|
||||||
|
HBitmapIter hbi;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
} MirrorBlockJob;
|
} MirrorBlockJob;
|
||||||
|
|
||||||
|
@ -62,8 +63,15 @@ static int coroutine_fn mirror_iteration(MirrorBlockJob *s,
|
||||||
int64_t end;
|
int64_t end;
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
|
|
||||||
|
s->sector_num = hbitmap_iter_next(&s->hbi);
|
||||||
|
if (s->sector_num < 0) {
|
||||||
|
bdrv_dirty_iter_init(source, &s->hbi);
|
||||||
|
s->sector_num = hbitmap_iter_next(&s->hbi);
|
||||||
|
trace_mirror_restart_iter(s, bdrv_get_dirty_count(source));
|
||||||
|
assert(s->sector_num >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
end = s->common.len >> BDRV_SECTOR_BITS;
|
end = s->common.len >> BDRV_SECTOR_BITS;
|
||||||
s->sector_num = bdrv_get_next_dirty(source, s->sector_num);
|
|
||||||
nb_sectors = MIN(BDRV_SECTORS_PER_DIRTY_CHUNK, end - s->sector_num);
|
nb_sectors = MIN(BDRV_SECTORS_PER_DIRTY_CHUNK, end - s->sector_num);
|
||||||
bdrv_reset_dirty(source, s->sector_num, nb_sectors);
|
bdrv_reset_dirty(source, s->sector_num, nb_sectors);
|
||||||
|
|
||||||
|
@ -136,7 +144,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s->sector_num = -1;
|
bdrv_dirty_iter_init(bs, &s->hbi);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
uint64_t delay_ns;
|
uint64_t delay_ns;
|
||||||
int64_t cnt;
|
int64_t cnt;
|
||||||
|
|
|
@ -351,13 +351,15 @@ void bdrv_set_buffer_alignment(BlockDriverState *bs, int align);
|
||||||
void *qemu_blockalign(BlockDriverState *bs, size_t size);
|
void *qemu_blockalign(BlockDriverState *bs, size_t size);
|
||||||
bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov);
|
bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov);
|
||||||
|
|
||||||
#define BDRV_SECTORS_PER_DIRTY_CHUNK 2048
|
#define BDRV_SECTORS_PER_DIRTY_CHUNK (1 << BDRV_LOG_SECTORS_PER_DIRTY_CHUNK)
|
||||||
|
#define BDRV_LOG_SECTORS_PER_DIRTY_CHUNK 11
|
||||||
|
|
||||||
|
struct HBitmapIter;
|
||||||
void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable);
|
void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable);
|
||||||
int bdrv_get_dirty(BlockDriverState *bs, int64_t sector);
|
int bdrv_get_dirty(BlockDriverState *bs, int64_t sector);
|
||||||
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors);
|
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors);
|
||||||
void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors);
|
void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors);
|
||||||
int64_t bdrv_get_next_dirty(BlockDriverState *bs, int64_t sector);
|
void bdrv_dirty_iter_init(BlockDriverState *bs, struct HBitmapIter *hbi);
|
||||||
int64_t bdrv_get_dirty_count(BlockDriverState *bs);
|
int64_t bdrv_get_dirty_count(BlockDriverState *bs);
|
||||||
|
|
||||||
void bdrv_enable_copy_on_read(BlockDriverState *bs);
|
void bdrv_enable_copy_on_read(BlockDriverState *bs);
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "qapi-types.h"
|
#include "qapi-types.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "monitor/monitor.h"
|
#include "monitor/monitor.h"
|
||||||
|
#include "qemu/hbitmap.h"
|
||||||
|
|
||||||
#define BLOCK_FLAG_ENCRYPT 1
|
#define BLOCK_FLAG_ENCRYPT 1
|
||||||
#define BLOCK_FLAG_COMPAT6 4
|
#define BLOCK_FLAG_COMPAT6 4
|
||||||
|
@ -275,8 +276,7 @@ struct BlockDriverState {
|
||||||
bool iostatus_enabled;
|
bool iostatus_enabled;
|
||||||
BlockDeviceIoStatus iostatus;
|
BlockDeviceIoStatus iostatus;
|
||||||
char device_name[32];
|
char device_name[32];
|
||||||
unsigned long *dirty_bitmap;
|
HBitmap *dirty_bitmap;
|
||||||
int64_t dirty_count;
|
|
||||||
int in_use; /* users other than guest access, eg. block migration */
|
int in_use; /* users other than guest access, eg. block migration */
|
||||||
QTAILQ_ENTRY(BlockDriverState) list;
|
QTAILQ_ENTRY(BlockDriverState) list;
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ commit_start(void *bs, void *base, void *top, void *s, void *co, void *opaque) "
|
||||||
|
|
||||||
# block/mirror.c
|
# block/mirror.c
|
||||||
mirror_start(void *bs, void *s, void *co, void *opaque) "bs %p s %p co %p opaque %p"
|
mirror_start(void *bs, void *s, void *co, void *opaque) "bs %p s %p co %p opaque %p"
|
||||||
|
mirror_restart_iter(void *s, int64_t cnt) "s %p dirty count %"PRId64
|
||||||
mirror_before_flush(void *s) "s %p"
|
mirror_before_flush(void *s) "s %p"
|
||||||
mirror_before_drain(void *s, int64_t cnt) "s %p dirty count %"PRId64
|
mirror_before_drain(void *s, int64_t cnt) "s %p dirty count %"PRId64
|
||||||
mirror_before_sleep(void *s, int64_t cnt, int synced) "s %p dirty count %"PRId64" synced %d"
|
mirror_before_sleep(void *s, int64_t cnt, int synced) "s %p dirty count %"PRId64" synced %d"
|
||||||
|
|
Loading…
Reference in New Issue