qemu-block
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-block] [PATCH v3 14/16] block/qcow2: falloc/full preallocating


From: Max Reitz
Subject: Re: [Qemu-block] [PATCH v3 14/16] block/qcow2: falloc/full preallocating growth
Date: Wed, 31 May 2017 14:19:17 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.1.0

On 2017-05-31 12:41, Stefan Hajnoczi wrote:
> On Fri, May 26, 2017 at 06:55:16PM +0200, Max Reitz wrote:
>> @@ -2677,6 +2679,102 @@ static int qcow2_truncate(BlockDriverState *bs, 
>> int64_t offset,
>>          }
>>          break;
>>  
>> +    case PREALLOC_MODE_FALLOC:
>> +    case PREALLOC_MODE_FULL:
>> +    {
>> +        int64_t allocation_start, host_offset, guest_offset;
>> +        int64_t clusters_allocated;
>> +        int64_t old_file_size, new_file_size;
>> +        uint64_t nb_new_data_clusters, nb_new_l2_tables;
>> +
>> +        old_file_size = bdrv_getlength(bs->file->bs);
>> +        if (old_file_size < 0) {
>> +            error_setg_errno(errp, -old_file_size,
>> +                             "Failed to inquire current file length");
>> +            return ret;
>> +        }
>> +
>> +        nb_new_data_clusters = DIV_ROUND_UP(offset - old_length,
>> +                                            s->cluster_size);
>> +
>> +        /* This is an overestimation; we will not actually allocate space 
>> for
>> +         * these in the file but just make sure the new refcount structures 
>> are
>> +         * able to cover them so we will not have to allocate new refblocks
>> +         * while entering the data blocks in the potentially new L2 tables.
>> +         * (We do not actually care where the L2 tables are placed. Maybe 
>> they
>> +         *  are already allocated or they can be placed somewhere before
>> +         *  @old_file_size. It does not matter because they will be fully
>> +         *  allocated automatically, so they do not need to be covered by 
>> the
>> +         *  preallocation. All that matters is that we will not have to 
>> allocate
>> +         *  new refcount structures for them.) */
>> +        nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters,
>> +                                        s->cluster_size / sizeof(uint64_t));
>> +        /* The cluster range may not be aligned to L2 boundaries, so add 
>> one L2
>> +         * table for a potential head/tail */
>> +        nb_new_l2_tables++;
>> +
>> +        allocation_start = qcow2_refcount_area(bs, old_file_size,
>> +                                               nb_new_data_clusters +
>> +                                               nb_new_l2_tables,
>> +                                               true, 0, 0);
>> +        if (allocation_start < 0) {
>> +            error_setg_errno(errp, -allocation_start,
>> +                             "Failed to resize refcount structures");
>> +            return -allocation_start;
>> +        }
>> +
>> +        clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start,
>> +                                                     nb_new_data_clusters);
>> +        if (clusters_allocated < 0) {
>> +            error_setg_errno(errp, -clusters_allocated,
>> +                             "Failed to allocate data clusters");
>> +            return -clusters_allocated;
>> +        }
>> +
>> +        assert(clusters_allocated == nb_new_data_clusters);
>> +
>> +        /* Allocate the data area */
>> +        new_file_size = allocation_start +
>> +                        nb_new_data_clusters * s->cluster_size;
>> +        ret = bdrv_truncate(bs->file, new_file_size, prealloc, errp);
>> +        if (ret < 0) {
>> +            error_prepend(errp, "Failed to resize underlying file: ");
>> +            qcow2_free_clusters(bs, allocation_start,
>> +                                nb_new_data_clusters * s->cluster_size,
>> +                                QCOW2_DISCARD_OTHER);
>> +            return ret;
>> +        }
>> +
>> +        /* Create the necessary L2 entries */
>> +        host_offset = allocation_start;
>> +        guest_offset = old_length;
>> +        while (nb_new_data_clusters) {
>> +            int64_t guest_cluster = guest_offset >> s->cluster_bits;
>> +            int64_t nb_clusters = MIN(nb_new_data_clusters,
>> +                                      s->l2_size - guest_cluster % 
>> s->l2_size);
>> +            QCowL2Meta allocation = {
>> +                .offset       = guest_offset,
>> +                .alloc_offset = host_offset,
>> +                .nb_clusters  = nb_clusters,
>> +            };
>> +            qemu_co_queue_init(&allocation.dependent_requests);
>> +
>> +            ret = qcow2_alloc_cluster_link_l2(bs, &allocation);
>> +            if (ret < 0) {
>> +                error_setg_errno(errp, -ret, "Failed to update L2 tables");
>> +                qcow2_free_clusters(bs, host_offset,
>> +                                    nb_new_data_clusters * s->cluster_size,
>> +                                    QCOW2_DISCARD_OTHER);
>> +                return ret;
>> +            }
>> +
>> +            guest_offset += nb_clusters * s->cluster_size;
>> +            host_offset += nb_clusters * s->cluster_size;
>> +            nb_new_data_clusters -= nb_clusters;
>> +        }
>> +        break;
>> +    }
> 
> Flush L1/L2 tables?

Right. It would be good to actually have the area allocated for sure
before writing the new size. :-)

The same applies to the metadata-only preallocate(), though. So this
should probably be added in patch 11.

Thanks for spotting this, and thanks for reviewing!

Max

Attachment: signature.asc
Description: OpenPGP digital signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]