On Fri, Dec 30, 2011 at 22:09, Stefan Hajnoczi <stefa...@gmail.com> wrote:

> On Thu, Dec 29, 2011 at 05:36:59PM +0800, Dong Xu Wang wrote:
>
> Some comments on everything but the I/O path, which I haven't reviewed
> yet:
>
> > diff --git a/block/add-cow.c b/block/add-cow.c
> > new file mode 100644
> > index 0000000..95af5b7
> > --- /dev/null
> > +++ b/block/add-cow.c
> > @@ -0,0 +1,429 @@
>
> Missing GPL or LGPL license header.
>
> > +#include "qemu-common.h"
> > +#include "block_int.h"
> > +#include "module.h"
> > +
> > +#define ADD_COW_MAGIC       (((uint64_t)'A' << 56) | ((uint64_t)'D' <<
> 48) | \
> > +                            ((uint64_t)'D' << 40) | ((uint64_t)'_' <<
> 32) | \
> > +                            ((uint64_t)'C' << 24) | ((uint64_t)'O' <<
> 16) | \
> > +                            ((uint64_t)'W' << 8) | 0xFF)
> > +#define ADD_COW_VERSION     1
> > +#define ADD_COW_FILE_LEN    1024
> > +
> > +typedef struct AddCowHeader {
> > +    uint64_t        magic;
> > +    uint32_t        version;
> > +    char            backing_file[ADD_COW_FILE_LEN];
> > +    char            image_file[ADD_COW_FILE_LEN];
> > +    uint64_t        size;
> > +    char            reserved[492];
> > +} QEMU_PACKED AddCowHeader;
> > +
> > +typedef struct BDRVAddCowState {
> > +    char                image_file[ADD_COW_FILE_LEN];
>
> Why is this field needed?
>
> Yes, not needed.

>
> > +    BlockDriverState    *image_hd;
> > +    uint8_t             *bitmap;
> > +    uint64_t            bitmap_size;
> > +    CoMutex             lock;
> > +} BDRVAddCowState;
> > +
> > +static int add_cow_probe(const uint8_t *buf, int buf_size, const char
> *filename)
> > +{
> > +    const AddCowHeader *header = (const void *)buf;
> > +
> > +    if (be64_to_cpu(header->magic) == ADD_COW_MAGIC &&
> > +        be32_to_cpu(header->version) == ADD_COW_VERSION) {
> > +        return 100;
> > +    } else {
> > +        return 0;
> > +    }
> > +}
> > +
> > +static int add_cow_open(BlockDriverState *bs, int flags)
> > +{
> > +    AddCowHeader        header;
> > +    int64_t             size;
> > +    char                image_filename[ADD_COW_FILE_LEN];
> > +    BlockDriver         *image_drv = NULL;
> > +    int                 ret;
> > +    BDRVAddCowState     *s = bs->opaque;
> > +    BlockDriverState    *backing_bs = NULL;
> > +
> > +    ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
> > +    if (ret != sizeof(header)) {
> > +        goto fail;
> > +    }
> > +
> > +    if (be64_to_cpu(header.magic) != ADD_COW_MAGIC) {
> > +        ret = -EINVAL;
> > +        goto fail;
> > +    }
> > +    if (be32_to_cpu(header.version) != ADD_COW_VERSION) {
> > +        char version[64];
> > +        snprintf(version, sizeof(version), "ADD-COW version %d",
> header.version);
> > +        qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
> > +            bs->device_name, "add-cow", version);
> > +        ret = -ENOTSUP;
> > +        goto fail;
> > +    }
> > +
> > +    size = be64_to_cpu(header.size);
> > +    bs->total_sectors = size / BDRV_SECTOR_SIZE;
> > +
> > +    QEMU_BUILD_BUG_ON(sizeof(bs->backing_file) !=
> sizeof(header.backing_file));
> > +    QEMU_BUILD_BUG_ON(sizeof(s->image_file) !=
> sizeof(header.image_file));
> > +    pstrcpy(bs->backing_file, sizeof(bs->backing_file),
> > +            header.backing_file);
> > +    pstrcpy(s->image_file, sizeof(s->image_file),
> > +            header.image_file);
>
> This assumes that header.backing_file and header.image_file is
> NUL-terminated.  If the file happened to have the magic number and
> version but does not include '\0' bytes in the header.backing_file then
> we may crash here when trying to read beyond the end of the header
> struct.
>
> Image format code should be robust.  Please use strncpy(3) carefully
> instead of pstrcpy().
>
> Also please update the file format specification to either make these
> fields NUL-terminated or NUL-terminated unless the length is 1024
> characters (in which case there is no NUL but the string still ends).
>
Okay.

>
> > +
> > +    s->bitmap_size = ((bs->total_sectors + 7) >> 3);
> > +    s->bitmap = qemu_blockalign(bs, s->bitmap_size);
> > +
> > +    ret = bdrv_pread(bs->file, sizeof(header), s->bitmap,
> > +            s->bitmap_size);
> > +    if (ret != s->bitmap_size) {
> > +        goto fail;
> > +    }
> > +
> > +    if (s->image_file[0] == '\0') {
> > +        ret = -ENOENT;
> > +        goto fail;
> > +    }
> > +
> > +    ret = bdrv_file_open(&backing_bs, bs->backing_file, 0);
> > +    if (ret < 0) {
> > +        return ret;
> > +    }
> > +    bdrv_delete(backing_bs);
>
> What does this do?  (It leaks s->bitmap when it fails.)
>

I wanna make sure backing_file exists while opening.

>
> > +
> > +    s->image_hd = bdrv_new("");
> > +
> > +    if (path_has_protocol(s->image_file)) {
> > +        pstrcpy(image_filename, sizeof(image_filename),
> > +                s->image_file);
> > +    } else {
> > +        path_combine(image_filename, sizeof(image_filename),
> > +                     bs->filename, s->image_file);
> > +    }
> > +
> > +    image_drv = bdrv_find_format("raw");
> > +    ret = bdrv_open(s->image_hd, image_filename, flags, image_drv);
> > +    if (ret < 0) {
> > +        bdrv_delete(s->image_hd);
> > +        s->image_hd = NULL;
> > +        goto fail;
> > +    }
>
> TODO
>
> > +
> > +    qemu_co_mutex_init(&s->lock);
> > +    return 0;
> > + fail:
> > +    g_free(s->bitmap);
> > +    return ret;
> > +}
> > +
> > +static inline void add_cow_set_bit(BlockDriverState *bs, int64_t bitnum)
> > +{
> > +    uint64_t offset = bitnum >> 3;
> > +    BDRVAddCowState *s = bs->opaque;
> > +    s->bitmap[offset] |= (1 << (bitnum % 8));
> > +}
> > +
> > +static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum)
> > +{
> > +    BDRVAddCowState *s = bs->opaque;
> > +    uint64_t offset = bitnum >> 3;
> > +    return !!(s->bitmap[offset] & (1 << (bitnum % 8)));
> > +}
>
> Please use bool instead of int.  Then you can also drop the !!.
>
Okay.

>
> > +
> > +static coroutine_fn int add_cow_is_allocated(BlockDriverState *bs,
> > +        int64_t sector_num, int nb_sectors, int *num_same)
> > +{
> > +    int changed;
> > +    BDRVAddCowState *s = bs->opaque;
> > +
> > +    /* Beyond the end of bitmap, return error or read from
> backing_file? */
> > +    if (((sector_num + nb_sectors + 7) >> 3) > s->bitmap_size) {
> > +        return 0;
> > +    }
>
> If the bitmap size is tied to bs->total_sectors then you do not need
> this check since bdrv_co_is_allocated() already handles this case (and
> sets *num_same to 0).
>
> > +
> > +    if (nb_sectors == 0) {
> > +        *num_same = nb_sectors;
> > +        return 0;
> > +    }
> > +
> > +    changed = is_bit_set(bs, sector_num);
> > +    for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) {
> > +        if (is_bit_set(bs, sector_num + *num_same) != changed) {
> > +            break;
> > +        }
> > +    }
> > +
> > +    return changed;
> > +}
> > +
> > +static int add_cow_update_bitmap(BlockDriverState *bs, int64_t
> sector_num,
> > +        int nb_sectors)
> > +{
> > +    int i, ret = 0;
> > +    bool changed = false;
> > +    BDRVAddCowState *s = bs->opaque;
> > +    uint64_t start_pos = (sector_num  >> 3) & (~511);
> > +    uint64_t end_pos = (((sector_num + nb_sectors - 1)  >> 3) + 511) &
> (~511);
> > +    uint64_t sectors_to_write = MIN(end_pos - start_pos,
> > +                s->bitmap_size - start_pos);
> > +
> > +    if (start_pos > s->bitmap_size) {
> > +        return 0;
> > +    }
> > +
> > +    for (i = 0; i < nb_sectors; i++) {
> > +        if (!changed) {
> > +            changed = true;
> > +        }
> > +        add_cow_set_bit(bs, sector_num + i);
> > +    }
> > +
> > +    if (changed) {
> > +        ret = bdrv_pwrite(bs->file, sizeof(AddCowHeader) + start_pos,
> > +            s->bitmap + start_pos, sectors_to_write);
> > +    }
> > +    return ret;
> > +}
> > +
> > +static void add_cow_close(BlockDriverState *bs)
> > +{
> > +    BDRVAddCowState *s = bs->opaque;
> > +    g_free(s->bitmap);
>
> We also need to free s->image_hd.
>
>
Yep.

> > diff --git a/docs/specs/add-cow.txt b/docs/specs/add-cow.txt
> > new file mode 100644
> > index 0000000..33f358e
> > --- /dev/null
> > +++ b/docs/specs/add-cow.txt
> > @@ -0,0 +1,72 @@
> > +== General ==
> > +
> > +Raw file format does not support backing_file and copy on write
> feature. Then
> > +you can use add-cow file to implement these features.
>
> I suggest replacing the second sentence with these:
>
> "The add-cow image format makes it possible to use backing files with raw
> image by keeping a separate .add-cow metadata file.  Once all sectors
> have been written to in the raw image it is safe to discard the .add-cow
> and backing files and instead use the raw image directly."
>
> They describe what add-cow does and how it can be used in a little more
> detail.
>
> > +
> > +When using add-cow, procedures may like this:
> > +(ubuntu.img is a disk image which has been installed OS.)
> > +    1)  Create a raw image with the same size of ubuntu.img
> > +            qemu-img create -f raw test.raw 8G
> > +    2)  Create a add-cow image which will store dirty bitmap
> > +            qemu-img create -f add-cow test.add-cow -o
> backing_file=ubuntu.img,image_file=test.raw
> > +    3)  Run qemu with add-cow image
> > +            qemu -drive if=virtio,file=test.add-cow
> > +
> > +While QEMU is running, virtual size of image_file and backing_file must
> be the
> > +same. So if image_file does not have the same virtual size as
> backing_file's in
> > +step 2), qemu-img will truncate it.
>
> This limitation is unnecessary.  QCOW2 and QED will read zeroes if the
> image is larger than the backing file.  Please implement the same
> behavior so that add-cow behaves consistently with how users know QCOW2
> and QED work.
>
>
Okay.

> > +
> > +=Specification=
> > +
> > +The file format looks like this:
> > +
> > + +---------------+--------------------------+
> > + |     Header    |           Data           |
> > + +---------------+--------------------------+
> > +
> > +All numbers in add-cow are stored in Big Endian byte order.
>
> New file formats and protocols often choose little endian because x86
> and arm are very popular nowadays.  It's an arbitrary decision but I
> just wanted to suggest it.
>
> > +== Header ==
> > +
> > +The Header is included in the first bytes:
> > +
> > +    Byte  0 -  7:       magic
> > +                        add-cow magic string ("ADD_COW\xff")
> > +
> > +          8 -  11:      version
> > +                        Version number (only valid value is 1 now)
> > +
> > +          12 - 1035:    backing_file
> > +                        backing_file file name related to add-cow file.
> While
> > +                        using backing_file, must together with
> image_file. All
> > +                        unused bytes are padded with zeros.
>
> "While using backing_file, must together with image_file"
>
> What does this mean?
>
> > +
> > +         1036 - 2059:   image_file
> > +                        image_file is a raw file, While using
> image_file, must
> > +                        together with image_file. All unused bytes are
> padded
>
> "While using image_file, must together with image_file"
>
> What does this mean?
>

I mean while using add-cow, must together with image_file and backing_file.
Both of them can not be missing.
Errors with sentences like that, I will correct them in v7.

>
> > +                        with zeros.
> > +
> > +         2060 - 2067:   size
> > +                        Virtual disk size of image_file in bytes.
>
> Why is a field required to store the virtual disk size?  The image_file
> size should always tell us the virtual disk image.
>
> > +
> > +         2068 - 2559:   The Reserved field is used to make sure Data
> field starts
> > +                        at the multiple of 512, not used currently. All
> bytes are
> > +                        filled with 0.
> > +
> > +== Data ==
> > +
> > +The Data field starts at the 2560th byte, stores a bitmap related to
> backing_file
> > +and image_file. The bitmap will track whether the sector in
> backing_file is dirty
> > +or not.
> > +
> > +
> > +Each bit in the bitmap indicates one sector's status. So the size of
> bitmap is
> > +calculated according to virtual size of backing_file. In each byte, bit
> 0 to 7
> > +will track the 1st to 7th sector in sequence, bit orders in one byte
> look like:
> > + +----+----+----+----+----+----+----+----+
> > + | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
> > + +----+----+----+----+----+----+----+----+
> > +
> > +If the bit is 0, indicates the sector has not been allocated in
> image_file, data
> > +should be loaded from backing_file while reading; if the bit is 1,
>  indicates the
> > +related sector has been dirty, should be loaded from image_file while
> reading.
>
> Perhaps also add the following so we define both read and write
> semantics:
>
> Writing to a sector causes the corresponding bit to be set to 1.
>
> Thanks for your reviewing.

Reply via email to