Patchwork [v3,1/2] COW: Speed up writes

login
register
mail settings
Submitter Charlie Shepherd
Date Nov. 6, 2013, 3:59 p.m.
Message ID <1383753568-15844-2-git-send-email-charlie@ctshepherd.com>
Download mbox | patch
Permalink /patch/288964/
State New
Headers show

Comments

Charlie Shepherd - Nov. 6, 2013, 3:59 p.m.
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>
---
 block/cow.c | 87 ++++++++++++++++++++++++++++++++++---------------------------
 1 file changed, 49 insertions(+), 38 deletions(-)
Paolo Bonzini - Nov. 6, 2013, 4:17 p.m.
Il 06/11/2013 16:59, Charlie Shepherd ha scritto:
> 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>
> ---
>  block/cow.c | 87 ++++++++++++++++++++++++++++++++++---------------------------
>  1 file changed, 49 insertions(+), 38 deletions(-)
> 
> diff --git a/block/cow.c b/block/cow.c
> index 909c3e7..bf447fd 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[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)
> @@ -204,18 +182,51 @@ 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;
> +    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;
> +        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;
> +        }
> +
> +        /* 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);
> +
> +        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,
> 

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Charlie Shepherd - Nov. 6, 2013, 4:24 p.m.
On 06/11/2013 16:17, Paolo Bonzini wrote:
> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

Thanks for your help with this series!

Charlie
Kevin Wolf - Nov. 13, 2013, 12:59 p.m.
Am 06.11.2013 um 16:59 hat Charlie Shepherd geschrieben:
> 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>
> ---
>  block/cow.c | 87 ++++++++++++++++++++++++++++++++++---------------------------
>  1 file changed, 49 insertions(+), 38 deletions(-)

> @@ -204,18 +182,51 @@ 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;
> +    for ( ; nb_sectors;
> +            bitnum += sector_bits,
> +            nb_sectors -= sector_bits,
> +            offset += BDRV_SECTOR_SIZE) {

block/cow.c: In function 'cow_update_bitmap':
block/cow.c:206:23: error: 'sector_bits' undeclared (first use in this function)

Kevin
Charlie Shepherd - Nov. 15, 2013, 6:43 p.m.
On 13/11/2013 12:59, Kevin Wolf wrote:
> Am 06.11.2013 um 16:59 hat Charlie Shepherd geschrieben:
>> 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>
>> ---
>>   block/cow.c | 87 ++++++++++++++++++++++++++++++++++---------------------------
>>   1 file changed, 49 insertions(+), 38 deletions(-)
>> @@ -204,18 +182,51 @@ 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;
>> +    for ( ; nb_sectors;
>> +            bitnum += sector_bits,
>> +            nb_sectors -= sector_bits,
>> +            offset += BDRV_SECTOR_SIZE) {
> block/cow.c: In function 'cow_update_bitmap':
> block/cow.c:206:23: error: 'sector_bits' undeclared (first use in this function)

Sorry I should have caught that, resending a new version.


Charlie

Patch

diff --git a/block/cow.c b/block/cow.c
index 909c3e7..bf447fd 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[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)
@@ -204,18 +182,51 @@  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;
+    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;
+        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;
+        }
+
+        /* 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);
+
+        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,