Commit 03c1c09d authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/cody/tags/block-pull-request' into staging



# gpg: Signature made Mon 18 Dec 2017 21:05:53 GMT
# gpg:                using RSA key 0xBDBE7B27C0DE3057
# gpg: Good signature from "Jeffrey Cody <jcody@redhat.com>"
# gpg:                 aka "Jeffrey Cody <jeff@codyprime.org>"
# gpg:                 aka "Jeffrey Cody <codyprime@gmail.com>"
# Primary key fingerprint: 9957 4B4D 3474 90E7 9D98  D624 BDBE 7B27 C0DE 3057

* remotes/cody/tags/block-pull-request:
  block/curl: fix minor memory leaks
  block/curl: check error return of curl_global_init()
  block/sheepdog: code beautification
  block/sheepdog: remove spurious NULL check
  blockjob: kick jobs on set-speed
  backup: use copy_bitmap in incremental backup
  backup: simplify non-dirty bits progress processing
  backup: init copy_bitmap from sync_bitmap for incremental
  backup: move from done_bitmap to copy_bitmap
  hbitmap: add next_zero function

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 062fcb27 996922de
Loading
Loading
Loading
Loading
+66 −52
Original line number Diff line number Diff line
@@ -40,11 +40,12 @@ typedef struct BackupBlockJob {
    BlockdevOnError on_target_error;
    CoRwlock flush_rwlock;
    uint64_t bytes_read;
    unsigned long *done_bitmap;
    int64_t cluster_size;
    bool compress;
    NotifierWithReturn before_write;
    QLIST_HEAD(, CowRequest) inflight_reqs;

    HBitmap *copy_bitmap;
} BackupBlockJob;

/* See if in-flight requests overlap and wait for them to complete */
@@ -109,10 +110,11 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
    cow_request_begin(&cow_request, job, start, end);

    for (; start < end; start += job->cluster_size) {
        if (test_bit(start / job->cluster_size, job->done_bitmap)) {
        if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
            trace_backup_do_cow_skip(job, start);
            continue; /* already copied */
        }
        hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);

        trace_backup_do_cow_process(job, start);

@@ -132,6 +134,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
            if (error_is_read) {
                *error_is_read = true;
            }
            hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
            goto out;
        }

@@ -148,11 +151,10 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
            if (error_is_read) {
                *error_is_read = false;
            }
            hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
            goto out;
        }

        set_bit(start / job->cluster_size, job->done_bitmap);

        /* Publish progress, guest I/O counts as progress too.  Note that the
         * offset field is an opaque progress value, it is not a disk offset.
         */
@@ -260,7 +262,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
    }

    len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size);
    bitmap_zero(backup_job->done_bitmap, len);
    hbitmap_set(backup_job->copy_bitmap, 0, len);
}

void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
@@ -360,64 +362,68 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)

static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
{
    int ret;
    bool error_is_read;
    int ret = 0;
    int clusters_per_iter;
    uint32_t granularity;
    int64_t offset;
    int64_t cluster;
    int64_t end;
    int64_t last_cluster = -1;
    BdrvDirtyBitmapIter *dbi;

    granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
    clusters_per_iter = MAX((granularity / job->cluster_size), 1);
    dbi = bdrv_dirty_iter_new(job->sync_bitmap);
    HBitmapIter hbi;

    /* Find the next dirty sector(s) */
    while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) {
        cluster = offset / job->cluster_size;

        /* Fake progress updates for any clusters we skipped */
        if (cluster != last_cluster + 1) {
            job->common.offset += ((cluster - last_cluster - 1) *
                                   job->cluster_size);
        }

        for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
    hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
    while ((cluster = hbitmap_iter_next(&hbi)) != -1) {
        do {
            if (yield_and_check(job)) {
                    goto out;
                return 0;
            }
            ret = backup_do_cow(job, cluster * job->cluster_size,
                                    job->cluster_size, &error_is_read,
                                    false);
                if ((ret < 0) &&
                    backup_error_action(job, error_is_read, -ret) ==
                    BLOCK_ERROR_ACTION_REPORT) {
                    goto out;
                                job->cluster_size, &error_is_read, false);
            if (ret < 0 && backup_error_action(job, error_is_read, -ret) ==
                           BLOCK_ERROR_ACTION_REPORT)
            {
                return ret;
            }
        } while (ret < 0);
    }

        /* If the bitmap granularity is smaller than the backup granularity,
         * we need to advance the iterator pointer to the next cluster. */
        if (granularity < job->cluster_size) {
            bdrv_set_dirty_iter(dbi, cluster * job->cluster_size);
    return 0;
}

        last_cluster = cluster - 1;
/* init copy_bitmap from sync_bitmap */
static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
{
    BdrvDirtyBitmapIter *dbi;
    int64_t offset;
    int64_t end = DIV_ROUND_UP(bdrv_dirty_bitmap_size(job->sync_bitmap),
                               job->cluster_size);

    dbi = bdrv_dirty_iter_new(job->sync_bitmap);
    while ((offset = bdrv_dirty_iter_next(dbi)) != -1) {
        int64_t cluster = offset / job->cluster_size;
        int64_t next_cluster;

        offset += bdrv_dirty_bitmap_granularity(job->sync_bitmap);
        if (offset >= bdrv_dirty_bitmap_size(job->sync_bitmap)) {
            hbitmap_set(job->copy_bitmap, cluster, end - cluster);
            break;
        }

    /* Play some final catchup with the progress meter */
    end = DIV_ROUND_UP(job->common.len, job->cluster_size);
    if (last_cluster + 1 < end) {
        job->common.offset += ((end - last_cluster - 1) * job->cluster_size);
        offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset);
        if (offset == -1) {
            hbitmap_set(job->copy_bitmap, cluster, end - cluster);
            break;
        }

out:
        next_cluster = DIV_ROUND_UP(offset, job->cluster_size);
        hbitmap_set(job->copy_bitmap, cluster, next_cluster - cluster);
        if (next_cluster >= end) {
            break;
        }

        bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
    }

    job->common.offset = job->common.len -
                         hbitmap_count(job->copy_bitmap) * job->cluster_size;

    bdrv_dirty_iter_free(dbi);
    return ret;
}

static void coroutine_fn backup_run(void *opaque)
@@ -425,19 +431,27 @@ static void coroutine_fn backup_run(void *opaque)
    BackupBlockJob *job = opaque;
    BackupCompleteData *data;
    BlockDriverState *bs = blk_bs(job->common.blk);
    int64_t offset;
    int64_t offset, nb_clusters;
    int ret = 0;

    QLIST_INIT(&job->inflight_reqs);
    qemu_co_rwlock_init(&job->flush_rwlock);

    job->done_bitmap = bitmap_new(DIV_ROUND_UP(job->common.len,
                                               job->cluster_size));
    nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size);
    job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
    if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
        backup_incremental_init_copy_bitmap(job);
    } else {
        hbitmap_set(job->copy_bitmap, 0, nb_clusters);
    }


    job->before_write.notify = backup_before_write_notify;
    bdrv_add_before_write_notifier(bs, &job->before_write);

    if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
        /* All bits are set in copy_bitmap to allow any cluster to be copied.
         * This does not actually require them to be copied. */
        while (!block_job_is_cancelled(&job->common)) {
            /* Yield until the job is cancelled.  We just let our before_write
             * notify callback service CoW requests. */
@@ -512,7 +526,7 @@ static void coroutine_fn backup_run(void *opaque)
    /* wait until pending backup_do_cow() calls have completed */
    qemu_co_rwlock_wrlock(&job->flush_rwlock);
    qemu_co_rwlock_unlock(&job->flush_rwlock);
    g_free(job->done_bitmap);
    hbitmap_free(job->copy_bitmap);

    data = g_malloc(sizeof(*data));
    data->ret = ret;
+18 −6
Original line number Diff line number Diff line
@@ -89,6 +89,8 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,

struct BDRVCURLState;

static bool libcurl_initialized;

typedef struct CURLAIOCB {
    Coroutine *co;
    QEMUIOVector *qiov;
@@ -686,14 +688,23 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
    double d;
    const char *secretid;
    const char *protocol_delimiter;
    int ret;

    static int inited = 0;

    if (flags & BDRV_O_RDWR) {
        error_setg(errp, "curl block device does not support writes");
        return -EROFS;
    }

    if (!libcurl_initialized) {
        ret = curl_global_init(CURL_GLOBAL_ALL);
        if (ret) {
            error_setg(errp, "libcurl initialization failed with %d", ret);
            return -EIO;
        }
        libcurl_initialized = true;
    }

    qemu_mutex_init(&s->mutex);
    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
    qemu_opts_absorb_qdict(opts, options, &local_err);
@@ -772,11 +783,6 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
        }
    }

    if (!inited) {
        curl_global_init(CURL_GLOBAL_ALL);
        inited = 1;
    }

    DPRINTF("CURL: Opening %s\n", file);
    QSIMPLEQ_INIT(&s->free_state_waitq);
    s->aio_context = bdrv_get_aio_context(bs);
@@ -851,6 +857,9 @@ out_noclean:
    qemu_mutex_destroy(&s->mutex);
    g_free(s->cookie);
    g_free(s->url);
    g_free(s->username);
    g_free(s->proxyusername);
    g_free(s->proxypassword);
    qemu_opts_del(opts);
    return -EINVAL;
}
@@ -949,6 +958,9 @@ static void curl_close(BlockDriverState *bs)

    g_free(s->cookie);
    g_free(s->url);
    g_free(s->username);
    g_free(s->proxyusername);
    g_free(s->proxypassword);
}

static int64_t curl_getlength(BlockDriverState *bs)
+5 −0
Original line number Diff line number Diff line
@@ -715,3 +715,8 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
{
    return hbitmap_sha256(bitmap->bitmap, errp);
}

int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset)
{
    return hbitmap_next_zero(bitmap->bitmap, offset);
}
+83 −83
Original line number Diff line number Diff line
@@ -1632,7 +1632,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
    if (!tag) {
        tag = "";
    }
    if (tag && strlen(tag) >= SD_MAX_VDI_TAG_LEN) {
    if (strlen(tag) >= SD_MAX_VDI_TAG_LEN) {
        error_setg(errp, "value of parameter 'tag' is too long");
        ret = -EINVAL;
        goto err_no_fd;
+29 −1
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ static void __attribute__((__constructor__)) block_job_init(void)

static void block_job_event_cancelled(BlockJob *job);
static void block_job_event_completed(BlockJob *job, const char *msg);
static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job));

/* Transactional group of block jobs */
struct BlockJobTxn {
@@ -480,9 +481,16 @@ static void block_job_completed_txn_success(BlockJob *job)
    }
}

/* Assumes the block_job_mutex is held */
static bool block_job_timer_pending(BlockJob *job)
{
    return timer_pending(&job->sleep_timer);
}

void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
{
    Error *local_err = NULL;
    int64_t old_speed = job->speed;

    if (!job->driver->set_speed) {
        error_setg(errp, QERR_UNSUPPORTED);
@@ -495,6 +503,12 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
    }

    job->speed = speed;
    if (speed <= old_speed) {
        return;
    }

    /* kick only if a timer is pending */
    block_job_enter_cond(job, block_job_timer_pending);
}

void block_job_complete(BlockJob *job, Error **errp)
@@ -821,7 +835,11 @@ void block_job_resume_all(void)
    }
}

void block_job_enter(BlockJob *job)
/*
 * Conditionally enter a block_job pending a call to fn() while
 * under the block_job_lock critical section.
 */
static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job))
{
    if (!block_job_started(job)) {
        return;
@@ -836,6 +854,11 @@ void block_job_enter(BlockJob *job)
        return;
    }

    if (fn && !fn(job)) {
        block_job_unlock();
        return;
    }

    assert(!job->deferred_to_main_loop);
    timer_del(&job->sleep_timer);
    job->busy = true;
@@ -843,6 +866,11 @@ void block_job_enter(BlockJob *job)
    aio_co_wake(job->co);
}

void block_job_enter(BlockJob *job)
{
    block_job_enter_cond(job, NULL);
}

bool block_job_is_cancelled(BlockJob *job)
{
    return job->cancelled;
Loading