qemu/block/accounting.c
<<
>>
Prefs
   1/*
   2 * QEMU System Emulator block accounting
   3 *
   4 * Copyright (c) 2011 Christoph Hellwig
   5 * Copyright (c) 2015 Igalia, S.L.
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25
  26#include "qemu/osdep.h"
  27#include "block/accounting.h"
  28#include "block/block_int.h"
  29#include "qemu/timer.h"
  30#include "sysemu/qtest.h"
  31
  32static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
  33static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
  34
  35void block_acct_init(BlockAcctStats *stats, bool account_invalid,
  36                     bool account_failed)
  37{
  38    stats->account_invalid = account_invalid;
  39    stats->account_failed = account_failed;
  40
  41    if (qtest_enabled()) {
  42        clock_type = QEMU_CLOCK_VIRTUAL;
  43    }
  44}
  45
  46void block_acct_cleanup(BlockAcctStats *stats)
  47{
  48    BlockAcctTimedStats *s, *next;
  49    QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) {
  50        g_free(s);
  51    }
  52}
  53
  54void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
  55{
  56    BlockAcctTimedStats *s;
  57    unsigned i;
  58
  59    s = g_new0(BlockAcctTimedStats, 1);
  60    s->interval_length = interval_length;
  61    QSLIST_INSERT_HEAD(&stats->intervals, s, entries);
  62
  63    for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
  64        timed_average_init(&s->latency[i], clock_type,
  65                           (uint64_t) interval_length * NANOSECONDS_PER_SECOND);
  66    }
  67}
  68
  69BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats,
  70                                              BlockAcctTimedStats *s)
  71{
  72    if (s == NULL) {
  73        return QSLIST_FIRST(&stats->intervals);
  74    } else {
  75        return QSLIST_NEXT(s, entries);
  76    }
  77}
  78
  79void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
  80                      int64_t bytes, enum BlockAcctType type)
  81{
  82    assert(type < BLOCK_MAX_IOTYPE);
  83
  84    cookie->bytes = bytes;
  85    cookie->start_time_ns = qemu_clock_get_ns(clock_type);
  86    cookie->type = type;
  87}
  88
  89void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
  90{
  91    BlockAcctTimedStats *s;
  92    int64_t time_ns = qemu_clock_get_ns(clock_type);
  93    int64_t latency_ns = time_ns - cookie->start_time_ns;
  94
  95    if (qtest_enabled()) {
  96        latency_ns = qtest_latency_ns;
  97    }
  98
  99    assert(cookie->type < BLOCK_MAX_IOTYPE);
 100
 101    stats->nr_bytes[cookie->type] += cookie->bytes;
 102    stats->nr_ops[cookie->type]++;
 103    stats->total_time_ns[cookie->type] += latency_ns;
 104    stats->last_access_time_ns = time_ns;
 105
 106    QSLIST_FOREACH(s, &stats->intervals, entries) {
 107        timed_average_account(&s->latency[cookie->type], latency_ns);
 108    }
 109}
 110
 111void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
 112{
 113    assert(cookie->type < BLOCK_MAX_IOTYPE);
 114
 115    stats->failed_ops[cookie->type]++;
 116
 117    if (stats->account_failed) {
 118        BlockAcctTimedStats *s;
 119        int64_t time_ns = qemu_clock_get_ns(clock_type);
 120        int64_t latency_ns = time_ns - cookie->start_time_ns;
 121
 122        if (qtest_enabled()) {
 123            latency_ns = qtest_latency_ns;
 124        }
 125
 126        stats->total_time_ns[cookie->type] += latency_ns;
 127        stats->last_access_time_ns = time_ns;
 128
 129        QSLIST_FOREACH(s, &stats->intervals, entries) {
 130            timed_average_account(&s->latency[cookie->type], latency_ns);
 131        }
 132    }
 133}
 134
 135void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type)
 136{
 137    assert(type < BLOCK_MAX_IOTYPE);
 138
 139    /* block_acct_done() and block_acct_failed() update
 140     * total_time_ns[], but this one does not. The reason is that
 141     * invalid requests are accounted during their submission,
 142     * therefore there's no actual I/O involved. */
 143
 144    stats->invalid_ops[type]++;
 145
 146    if (stats->account_invalid) {
 147        stats->last_access_time_ns = qemu_clock_get_ns(clock_type);
 148    }
 149}
 150
 151void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
 152                      int num_requests)
 153{
 154    assert(type < BLOCK_MAX_IOTYPE);
 155    stats->merged[type] += num_requests;
 156}
 157
 158int64_t block_acct_idle_time_ns(BlockAcctStats *stats)
 159{
 160    return qemu_clock_get_ns(clock_type) - stats->last_access_time_ns;
 161}
 162
 163double block_acct_queue_depth(BlockAcctTimedStats *stats,
 164                              enum BlockAcctType type)
 165{
 166    uint64_t sum, elapsed;
 167
 168    assert(type < BLOCK_MAX_IOTYPE);
 169
 170    sum = timed_average_sum(&stats->latency[type], &elapsed);
 171
 172    return (double) sum / elapsed;
 173}
 174