Commit b741ae74 authored by Vladimir Sementsov-Ogievskiy's avatar Vladimir Sementsov-Ogievskiy Committed by Eric Blake
Browse files

block/accounting: introduce latency histogram



Introduce latency histogram statics for block devices.
For each accounted operation type, the latency region [0, +inf) is
divided into subregions by several points. Then, calculate
hits for each subregion.

Signed-off-by: default avatarVladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-Id: <20180309165212.97144-2-vsementsov@virtuozzo.com>
Reviewed-by: default avatarEric Blake <eblake@redhat.com>
Reviewed-by: default avatarStefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: default avatarEric Blake <eblake@redhat.com>
parent d003f7a8
Loading
Loading
Loading
Loading
+91 −0
Original line number Diff line number Diff line
@@ -94,6 +94,94 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
    cookie->type = type;
}

/* block_latency_histogram_compare_func:
 * Compare @key with interval [@it[0], @it[1]).
 * Return: -1 if @key < @it[0]
 *          0 if @key in [@it[0], @it[1])
 *         +1 if @key >= @it[1]
 */
static int block_latency_histogram_compare_func(const void *key, const void *it)
{
    uint64_t k = *(uint64_t *)key;
    uint64_t a = ((uint64_t *)it)[0];
    uint64_t b = ((uint64_t *)it)[1];

    return k < a ? -1 : (k < b ? 0 : 1);
}

static void block_latency_histogram_account(BlockLatencyHistogram *hist,
                                            int64_t latency_ns)
{
    uint64_t *pos;

    if (hist->bins == NULL) {
        /* histogram disabled */
        return;
    }


    if (latency_ns < hist->boundaries[0]) {
        hist->bins[0]++;
        return;
    }

    if (latency_ns >= hist->boundaries[hist->nbins - 2]) {
        hist->bins[hist->nbins - 1]++;
        return;
    }

    pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2,
                  sizeof(hist->boundaries[0]),
                  block_latency_histogram_compare_func);
    assert(pos != NULL);

    hist->bins[pos - hist->boundaries + 1]++;
}

int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
                                uint64List *boundaries)
{
    BlockLatencyHistogram *hist = &stats->latency_histogram[type];
    uint64List *entry;
    uint64_t *ptr;
    uint64_t prev = 0;
    int new_nbins = 1;

    for (entry = boundaries; entry; entry = entry->next) {
        if (entry->value <= prev) {
            return -EINVAL;
        }
        new_nbins++;
        prev = entry->value;
    }

    hist->nbins = new_nbins;
    g_free(hist->boundaries);
    hist->boundaries = g_new(uint64_t, hist->nbins - 1);
    for (entry = boundaries, ptr = hist->boundaries; entry;
         entry = entry->next, ptr++)
    {
        *ptr = entry->value;
    }

    g_free(hist->bins);
    hist->bins = g_new0(uint64_t, hist->nbins);

    return 0;
}

void block_latency_histograms_clear(BlockAcctStats *stats)
{
    int i;

    for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
        BlockLatencyHistogram *hist = &stats->latency_histogram[i];
        g_free(hist->bins);
        g_free(hist->boundaries);
        memset(hist, 0, sizeof(*hist));
    }
}

static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
                                 bool failed)
{
@@ -116,6 +204,9 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
        stats->nr_ops[cookie->type]++;
    }

    block_latency_histogram_account(&stats->latency_histogram[cookie->type],
                                    latency_ns);

    if (!failed || stats->account_failed) {
        stats->total_time_ns[cookie->type] += latency_ns;
        stats->last_access_time_ns = time_ns;
+35 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@

#include "qemu/timed-average.h"
#include "qemu/thread.h"
#include "qapi/qapi-builtin-types.h"

typedef struct BlockAcctTimedStats BlockAcctTimedStats;
typedef struct BlockAcctStats BlockAcctStats;
@@ -45,6 +46,36 @@ struct BlockAcctTimedStats {
    QSLIST_ENTRY(BlockAcctTimedStats) entries;
};

typedef struct BlockLatencyHistogram {
    /* The following histogram is represented like this:
     *
     * 5|           *
     * 4|           *
     * 3| *         *
     * 2| *         *    *
     * 1| *    *    *    *
     *  +------------------
     *      10   50   100
     *
     * BlockLatencyHistogram histogram = {
     *     .nbins = 4,
     *     .boundaries = {10, 50, 100},
     *     .bins = {3, 1, 5, 2},
     * };
     *
     * @boundaries array define histogram intervals as follows:
     * [0, boundaries[0]), [boundaries[0], boundaries[1]), ...
     * [boundaries[nbins-2], +inf)
     *
     * So, for example above, histogram intervals are:
     * [0, 10), [10, 50), [50, 100), [100, +inf)
     */
    int nbins;
    uint64_t *boundaries; /* @nbins-1 numbers here
                             (all boundaries, except 0 and +inf) */
    uint64_t *bins;
} BlockLatencyHistogram;

struct BlockAcctStats {
    QemuMutex lock;
    uint64_t nr_bytes[BLOCK_MAX_IOTYPE];
@@ -57,6 +88,7 @@ struct BlockAcctStats {
    QSLIST_HEAD(, BlockAcctTimedStats) intervals;
    bool account_invalid;
    bool account_failed;
    BlockLatencyHistogram latency_histogram[BLOCK_MAX_IOTYPE];
};

typedef struct BlockAcctCookie {
@@ -82,5 +114,8 @@ void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
int64_t block_acct_idle_time_ns(BlockAcctStats *stats);
double block_acct_queue_depth(BlockAcctTimedStats *stats,
                              enum BlockAcctType type);
int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
                                uint64List *boundaries);
void block_latency_histograms_clear(BlockAcctStats *stats);

#endif