* [Qemu-devel] [PATCH v2 0/3] COW: Speed up writes
@ 2013-11-06 12:56 Charlie Shepherd
2013-11-06 12:56 ` [Qemu-devel] [PATCH v2 1/3] " Charlie Shepherd
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Charlie Shepherd @ 2013-11-06 12:56 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, pbonzini, gabriel, Charlie Shepherd, stefanha
v2:
- Fix bit position calculations in cow_update_bitmap
- Add necessary check in cow_set_bits
Following on from Paolo's commits 26ae980 and 276cbc7, this patchset
implements some changes he recommended earlier which I didn't previously have
time to do while on GSoC.
Charlie Shepherd (3):
COW: Speed up writes
COW: Extend checking allocated bits to beyond one sector
COW: Skip setting already set bits
block/cow.c | 123 ++++++++++++++++++++++++++++++++++++------------------------
1 file changed, 75 insertions(+), 48 deletions(-)
--
1.8.4.rc3
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Qemu-devel] [PATCH v2 1/3] COW: Speed up writes
2013-11-06 12:56 [Qemu-devel] [PATCH v2 0/3] COW: Speed up writes Charlie Shepherd
@ 2013-11-06 12:56 ` Charlie Shepherd
2013-11-06 12:56 ` [Qemu-devel] [PATCH v2 2/3] COW: Extend checking allocated bits to beyond one sector Charlie Shepherd
2013-11-06 12:56 ` [Qemu-devel] [PATCH v2 3/3] COW: Skip setting already set bits Charlie Shepherd
2 siblings, 0 replies; 6+ messages in thread
From: Charlie Shepherd @ 2013-11-06 12:56 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, pbonzini, gabriel, Charlie Shepherd, stefanha
Process a whole sector's worth of COW bits by reading a sector, setting the bits, then writing it
out again. Make sure we only flush once, before writing metadata.
Signed-off-by: Charlie Shepherd <charlie@ctshepherd.com>
---
block/cow.c | 79 ++++++++++++++++++++++++++++++++-----------------------------
1 file changed, 41 insertions(+), 38 deletions(-)
diff --git a/block/cow.c b/block/cow.c
index 909c3e7..5dfffb0 100644
--- a/block/cow.c
+++ b/block/cow.c
@@ -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 |= (1 << (bitnum % 8));
-
- ret = bdrv_pwrite(bs->file, offset, &bitmap, sizeof(bitmap));
- if (ret < 0) {
- return ret;
+ bitmap[bitnum/8] |= (1 << (bitnum % 8));
+ bitnum++;
}
- return 0;
}
#define BITS_PER_BITMAP_SECTOR (512 * 8)
@@ -204,18 +182,43 @@ 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;
- for (i = 0; i < nb_sectors; i++) {
- error = cow_set_bit(bs, sector_num + i, &first);
- if (error) {
- break;
+ while (nb_sectors) {
+ int ret;
+ uint8_t bitmap[BDRV_SECTOR_SIZE];
+
+ bitnum &= BITS_PER_BITMAP_SECTOR - 1;
+ int sector_bits = MIN(nb_sectors, BITS_PER_BITMAP_SECTOR - bitnum);
+
+ ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
+ if (ret < 0) {
+ return ret;
}
+
+ if (first) {
+ ret = bdrv_flush(bs->file);
+ if (ret < 0) {
+ return ret;
+ }
+ first = false;
+ }
+
+ cow_set_bits(bitmap, bitnum, sector_bits);
+
+ ret = bdrv_pwrite(bs->file, offset, &bitmap, sizeof(bitmap));
+ if (ret < 0) {
+ return ret;
+ }
+
+ bitnum += sector_bits;
+ nb_sectors -= sector_bits;
+ offset += BDRV_SECTOR_SIZE;
}
- return error;
+ return 0;
}
static int coroutine_fn cow_read(BlockDriverState *bs, int64_t sector_num,
--
1.8.4.rc3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Qemu-devel] [PATCH v2 2/3] COW: Extend checking allocated bits to beyond one sector
2013-11-06 12:56 [Qemu-devel] [PATCH v2 0/3] COW: Speed up writes Charlie Shepherd
2013-11-06 12:56 ` [Qemu-devel] [PATCH v2 1/3] " Charlie Shepherd
@ 2013-11-06 12:56 ` Charlie Shepherd
2013-11-06 12:56 ` [Qemu-devel] [PATCH v2 3/3] COW: Skip setting already set bits Charlie Shepherd
2 siblings, 0 replies; 6+ messages in thread
From: Charlie Shepherd @ 2013-11-06 12:56 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, pbonzini, gabriel, Charlie Shepherd, stefanha
cow_co_is_allocated() only checks one sector's worth of allocated bits before returning. This is
allowed but (slightly) inefficient, so extend it to check all of the file's metadata sectors.
Signed-off-by: Charlie Shepherd <charlie@ctshepherd.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
---
block/cow.c | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/block/cow.c b/block/cow.c
index 5dfffb0..41097d8 100644
--- a/block/cow.c
+++ b/block/cow.c
@@ -152,18 +152,34 @@ static int coroutine_fn cow_co_is_allocated(BlockDriverState *bs,
{
int64_t bitnum = sector_num + sizeof(struct cow_header_v2) * 8;
uint64_t offset = (bitnum / 8) & -BDRV_SECTOR_SIZE;
- uint8_t bitmap[BDRV_SECTOR_SIZE];
- int ret;
- int changed;
+ bool first = true;
+ int changed, same = 0;
- ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
- if (ret < 0) {
- return ret;
- }
+ do {
+ int ret;
+ uint8_t bitmap[BDRV_SECTOR_SIZE];
+
+ bitnum &= BITS_PER_BITMAP_SECTOR - 1;
+ int sector_bits = MIN(nb_sectors, BITS_PER_BITMAP_SECTOR - bitnum);
+
+ ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (first) {
+ changed = cow_test_bit(bitnum, bitmap);
+ first = false;
+ }
+
+ same += cow_find_streak(bitmap, changed, bitnum, nb_sectors);
+
+ bitnum += sector_bits;
+ nb_sectors -= sector_bits;
+ offset += BDRV_SECTOR_SIZE;
+ } while (nb_sectors);
- bitnum &= BITS_PER_BITMAP_SECTOR - 1;
- changed = cow_test_bit(bitnum, bitmap);
- *num_same = cow_find_streak(bitmap, changed, bitnum, nb_sectors);
+ *num_same = same;
return changed;
}
--
1.8.4.rc3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [Qemu-devel] [PATCH v2 3/3] COW: Skip setting already set bits
2013-11-06 12:56 [Qemu-devel] [PATCH v2 0/3] COW: Speed up writes Charlie Shepherd
2013-11-06 12:56 ` [Qemu-devel] [PATCH v2 1/3] " Charlie Shepherd
2013-11-06 12:56 ` [Qemu-devel] [PATCH v2 2/3] COW: Extend checking allocated bits to beyond one sector Charlie Shepherd
@ 2013-11-06 12:56 ` Charlie Shepherd
2013-11-06 13:03 ` Paolo Bonzini
2 siblings, 1 reply; 6+ messages in thread
From: Charlie Shepherd @ 2013-11-06 12:56 UTC (permalink / raw)
To: qemu-devel; +Cc: kwolf, pbonzini, gabriel, Charlie Shepherd, stefanha
Rather than unnecessarily setting bits that are already set, re-use cow_find_streak to find how
many bits are already set for this sector, and only set unset bits. Do this before the flush to
avoid it if no bits need to be set at all.
Signed-off-by: Charlie Shepherd <charlie@ctshepherd.com>
---
block/cow.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/block/cow.c b/block/cow.c
index 41097d8..93207eb 100644
--- a/block/cow.c
+++ b/block/cow.c
@@ -203,7 +203,7 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
bool first = true;
while (nb_sectors) {
- int ret;
+ int ret, set;
uint8_t bitmap[BDRV_SECTOR_SIZE];
bitnum &= BITS_PER_BITMAP_SECTOR - 1;
@@ -214,6 +214,15 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
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 (set == sector_bits) {
+ continue;
+ }
+
if (first) {
ret = bdrv_flush(bs->file);
if (ret < 0) {
@@ -228,7 +237,6 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
if (ret < 0) {
return ret;
}
-
bitnum += sector_bits;
nb_sectors -= sector_bits;
offset += BDRV_SECTOR_SIZE;
--
1.8.4.rc3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH v2 3/3] COW: Skip setting already set bits
2013-11-06 12:56 ` [Qemu-devel] [PATCH v2 3/3] COW: Skip setting already set bits Charlie Shepherd
@ 2013-11-06 13:03 ` Paolo Bonzini
2013-11-06 13:07 ` Charlie Shepherd
0 siblings, 1 reply; 6+ messages in thread
From: Paolo Bonzini @ 2013-11-06 13:03 UTC (permalink / raw)
To: Charlie Shepherd; +Cc: kwolf, stefanha, gabriel, qemu-devel
Il 06/11/2013 13:56, Charlie Shepherd ha scritto:
> Rather than unnecessarily setting bits that are already set, re-use cow_find_streak to find how
> many bits are already set for this sector, and only set unset bits. Do this before the flush to
> avoid it if no bits need to be set at all.
>
> Signed-off-by: Charlie Shepherd <charlie@ctshepherd.com>
> ---
> block/cow.c | 12 ++++++++++--
> 1 file changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/block/cow.c b/block/cow.c
> index 41097d8..93207eb 100644
> --- a/block/cow.c
> +++ b/block/cow.c
> @@ -203,7 +203,7 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
> bool first = true;
>
> while (nb_sectors) {
You still need to make this a "for" and put "offset += BDRV_SECTOR_SIZE"
in the third clause.
> - int ret;
> + int ret, set;
> uint8_t bitmap[BDRV_SECTOR_SIZE];
>
> bitnum &= BITS_PER_BITMAP_SECTOR - 1;
> @@ -214,6 +214,15 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
> 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 (set == sector_bits) {
> + continue;
> + }
This now has to be "if (set == 0)". With the change to the "for" above,
that's correct.
> if (first) {
> ret = bdrv_flush(bs->file);
> if (ret < 0) {
> @@ -228,7 +237,6 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
> if (ret < 0) {
> return ret;
> }
> -
> bitnum += sector_bits;
> nb_sectors -= sector_bits;
> offset += BDRV_SECTOR_SIZE;
>
I also noticed that patch 1 introduces a small regression, in that it
will always flush data even if the metadata doesn't change. This patch
fixes it, because the "bdrv_flush" is preceded by the check to skip over
any already set bits.
So I suggest that, together with the above fixes, you squash patches 1
and 3 together.
Paolo
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH v2 3/3] COW: Skip setting already set bits
2013-11-06 13:03 ` Paolo Bonzini
@ 2013-11-06 13:07 ` Charlie Shepherd
0 siblings, 0 replies; 6+ messages in thread
From: Charlie Shepherd @ 2013-11-06 13:07 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: kwolf, stefanha, gabriel, qemu-devel
On 06/11/2013 13:03, Paolo Bonzini wrote:
> Il 06/11/2013 13:56, Charlie Shepherd ha scritto:
>> Rather than unnecessarily setting bits that are already set, re-use cow_find_streak to find how
>> many bits are already set for this sector, and only set unset bits. Do this before the flush to
>> avoid it if no bits need to be set at all.
>>
>> Signed-off-by: Charlie Shepherd <charlie@ctshepherd.com>
>> ---
>> block/cow.c | 12 ++++++++++--
>> 1 file changed, 10 insertions(+), 2 deletions(-)
>>
>> diff --git a/block/cow.c b/block/cow.c
>> index 41097d8..93207eb 100644
>> --- a/block/cow.c
>> +++ b/block/cow.c
>> @@ -203,7 +203,7 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
>> bool first = true;
>>
>> while (nb_sectors) {
> You still need to make this a "for" and put "offset += BDRV_SECTOR_SIZE"
> in the third clause.
Ah true, I thought I could get away without doing that because it looked
a bit ugly but I'll update it to use a for.
>> - int ret;
>> + int ret, set;
>> uint8_t bitmap[BDRV_SECTOR_SIZE];
>>
>> bitnum &= BITS_PER_BITMAP_SECTOR - 1;
>> @@ -214,6 +214,15 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
>> 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 (set == sector_bits) {
>> + continue;
>> + }
> This now has to be "if (set == 0)". With the change to the "for" above,
> that's correct.
>
>> if (first) {
>> ret = bdrv_flush(bs->file);
>> if (ret < 0) {
>> @@ -228,7 +237,6 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
>> if (ret < 0) {
>> return ret;
>> }
>> -
>> bitnum += sector_bits;
>> nb_sectors -= sector_bits;
>> offset += BDRV_SECTOR_SIZE;
>>
> I also noticed that patch 1 introduces a small regression, in that it
> will always flush data even if the metadata doesn't change. This patch
> fixes it, because the "bdrv_flush" is preceded by the check to skip over
> any already set bits.
>
> So I suggest that, together with the above fixes, you squash patches 1
> and 3 together.
Ah that's true, I'll do that.
Charlie
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2013-11-06 13:07 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-11-06 12:56 [Qemu-devel] [PATCH v2 0/3] COW: Speed up writes Charlie Shepherd
2013-11-06 12:56 ` [Qemu-devel] [PATCH v2 1/3] " Charlie Shepherd
2013-11-06 12:56 ` [Qemu-devel] [PATCH v2 2/3] COW: Extend checking allocated bits to beyond one sector Charlie Shepherd
2013-11-06 12:56 ` [Qemu-devel] [PATCH v2 3/3] COW: Skip setting already set bits Charlie Shepherd
2013-11-06 13:03 ` Paolo Bonzini
2013-11-06 13:07 ` Charlie Shepherd
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).