Skip to content
Snippets Groups Projects
block.c 242 KiB
Newer Older
                         c->bs->filename);
        return ret;
    }

    return 0;
}

/*
 * Return the child that @bs acts as an overlay for, and from which data may be
 * copied in COW or COR operations.  Usually this is the backing file.
 */
BdrvChild *bdrv_cow_child(BlockDriverState *bs)
{
    if (!bs || !bs->drv) {
        return NULL;
    }

    if (bs->drv->is_filter) {
        return NULL;
    }

    if (!bs->backing) {
        return NULL;
    }

    assert(bs->backing->role & BDRV_CHILD_COW);
    return bs->backing;
}

/*
 * If @bs acts as a filter for exactly one of its children, return
 * that child.
 */
BdrvChild *bdrv_filter_child(BlockDriverState *bs)
{
    BdrvChild *c;

    if (!bs || !bs->drv) {
        return NULL;
    }

    if (!bs->drv->is_filter) {
        return NULL;
    }

    /* Only one of @backing or @file may be used */
    assert(!(bs->backing && bs->file));

    c = bs->backing ?: bs->file;
    if (!c) {
        return NULL;
    }

    assert(c->role & BDRV_CHILD_FILTERED);
    return c;
}

/*
 * Return either the result of bdrv_cow_child() or bdrv_filter_child(),
 * whichever is non-NULL.
 *
 * Return NULL if both are NULL.
 */
BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs)
{
    BdrvChild *cow_child = bdrv_cow_child(bs);
    BdrvChild *filter_child = bdrv_filter_child(bs);

    /* Filter nodes cannot have COW backing files */
    assert(!(cow_child && filter_child));

    return cow_child ?: filter_child;
}

/*
 * Return the primary child of this node: For filters, that is the
 * filtered child.  For other nodes, that is usually the child storing
 * metadata.
 * (A generally more helpful description is that this is (usually) the
 * child that has the same filename as @bs.)
 *
 * Drivers do not necessarily have a primary child; for example quorum
 * does not.
 */
BdrvChild *bdrv_primary_child(BlockDriverState *bs)
{
    BdrvChild *c, *found = NULL;

    QLIST_FOREACH(c, &bs->children, next) {
        if (c->role & BDRV_CHILD_PRIMARY) {
            assert(!found);
            found = c;
        }
    }

    return found;
}

static BlockDriverState *bdrv_do_skip_filters(BlockDriverState *bs,
                                              bool stop_on_explicit_filter)
{
    BdrvChild *c;

    if (!bs) {
        return NULL;
    }

    while (!(stop_on_explicit_filter && !bs->implicit)) {
        c = bdrv_filter_child(bs);
        if (!c) {
            /*
             * A filter that is embedded in a working block graph must
             * have a child.  Assert this here so this function does
             * not return a filter node that is not expected by the
             * caller.
             */
            assert(!bs->drv || !bs->drv->is_filter);
            break;
        }
        bs = c->bs;
    }
    /*
     * Note that this treats nodes with bs->drv == NULL as not being
     * filters (bs->drv == NULL should be replaced by something else
     * anyway).
     * The advantage of this behavior is that this function will thus
     * always return a non-NULL value (given a non-NULL @bs).
     */

    return bs;
}

/*
 * Return the first BDS that has not been added implicitly or that
 * does not have a filtered child down the chain starting from @bs
 * (including @bs itself).
 */
BlockDriverState *bdrv_skip_implicit_filters(BlockDriverState *bs)
{
    return bdrv_do_skip_filters(bs, true);
}

/*
 * Return the first BDS that does not have a filtered child down the
 * chain starting from @bs (including @bs itself).
 */
BlockDriverState *bdrv_skip_filters(BlockDriverState *bs)
{
    return bdrv_do_skip_filters(bs, false);
}

/*
 * For a backing chain, return the first non-filter backing image of
 * the first non-filter image.
 */
BlockDriverState *bdrv_backing_chain_next(BlockDriverState *bs)
{
    return bdrv_skip_filters(bdrv_cow_bs(bdrv_skip_filters(bs)));
}

/**
 * Check whether [offset, offset + bytes) overlaps with the cached
 * block-status data region.
 *
 * If so, and @pnum is not NULL, set *pnum to `bsc.data_end - offset`,
 * which is what bdrv_bsc_is_data()'s interface needs.
 * Otherwise, *pnum is not touched.
 */
static bool bdrv_bsc_range_overlaps_locked(BlockDriverState *bs,
                                           int64_t offset, int64_t bytes,
                                           int64_t *pnum)
{
    BdrvBlockStatusCache *bsc = qatomic_rcu_read(&bs->block_status_cache);
    bool overlaps;

    overlaps =
        qatomic_read(&bsc->valid) &&
        ranges_overlap(offset, bytes, bsc->data_start,
                       bsc->data_end - bsc->data_start);

    if (overlaps && pnum) {
        *pnum = bsc->data_end - offset;
    }

    return overlaps;
}

/**
 * See block_int.h for this function's documentation.
 */
bool bdrv_bsc_is_data(BlockDriverState *bs, int64_t offset, int64_t *pnum)
{
    RCU_READ_LOCK_GUARD();
    return bdrv_bsc_range_overlaps_locked(bs, offset, 1, pnum);
}

/**
 * See block_int.h for this function's documentation.
 */
void bdrv_bsc_invalidate_range(BlockDriverState *bs,
                               int64_t offset, int64_t bytes)
{
    RCU_READ_LOCK_GUARD();

    if (bdrv_bsc_range_overlaps_locked(bs, offset, bytes, NULL)) {
        qatomic_set(&bs->block_status_cache->valid, false);
    }
}

/**
 * See block_int.h for this function's documentation.
 */
void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes)
{
    BdrvBlockStatusCache *new_bsc = g_new(BdrvBlockStatusCache, 1);
    BdrvBlockStatusCache *old_bsc;

    *new_bsc = (BdrvBlockStatusCache) {
        .valid = true,
        .data_start = offset,
        .data_end = offset + bytes,
    };

    QEMU_LOCK_GUARD(&bs->bsc_modify_lock);

    old_bsc = qatomic_rcu_read(&bs->block_status_cache);
    qatomic_rcu_set(&bs->block_status_cache, new_bsc);
    if (old_bsc) {
        g_free_rcu(old_bsc, rcu);
    }
}