qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

accounting.c (8699B)


      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 
     32 static QEMUClockType clock_type = QEMU_CLOCK_REALTIME;
     33 static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
     34 
     35 void block_acct_init(BlockAcctStats *stats)
     36 {
     37     qemu_mutex_init(&stats->lock);
     38     if (qtest_enabled()) {
     39         clock_type = QEMU_CLOCK_VIRTUAL;
     40     }
     41     stats->account_invalid = true;
     42     stats->account_failed = true;
     43 }
     44 
     45 static bool bool_from_onoffauto(OnOffAuto val, bool def)
     46 {
     47     switch (val) {
     48     case ON_OFF_AUTO_AUTO:
     49         return def;
     50     case ON_OFF_AUTO_ON:
     51         return true;
     52     case ON_OFF_AUTO_OFF:
     53         return false;
     54     default:
     55         abort();
     56     }
     57 }
     58 
     59 void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid,
     60                       enum OnOffAuto account_failed)
     61 {
     62     stats->account_invalid = bool_from_onoffauto(account_invalid,
     63                                                  stats->account_invalid);
     64     stats->account_failed = bool_from_onoffauto(account_failed,
     65                                                 stats->account_failed);
     66 }
     67 
     68 void block_acct_cleanup(BlockAcctStats *stats)
     69 {
     70     BlockAcctTimedStats *s, *next;
     71     QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) {
     72         g_free(s);
     73     }
     74     qemu_mutex_destroy(&stats->lock);
     75 }
     76 
     77 void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
     78 {
     79     BlockAcctTimedStats *s;
     80     unsigned i;
     81 
     82     s = g_new0(BlockAcctTimedStats, 1);
     83     s->interval_length = interval_length;
     84     s->stats = stats;
     85     qemu_mutex_lock(&stats->lock);
     86     QSLIST_INSERT_HEAD(&stats->intervals, s, entries);
     87 
     88     for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
     89         timed_average_init(&s->latency[i], clock_type,
     90                            (uint64_t) interval_length * NANOSECONDS_PER_SECOND);
     91     }
     92     qemu_mutex_unlock(&stats->lock);
     93 }
     94 
     95 BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats,
     96                                               BlockAcctTimedStats *s)
     97 {
     98     if (s == NULL) {
     99         return QSLIST_FIRST(&stats->intervals);
    100     } else {
    101         return QSLIST_NEXT(s, entries);
    102     }
    103 }
    104 
    105 void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
    106                       int64_t bytes, enum BlockAcctType type)
    107 {
    108     assert(type < BLOCK_MAX_IOTYPE);
    109 
    110     cookie->bytes = bytes;
    111     cookie->start_time_ns = qemu_clock_get_ns(clock_type);
    112     cookie->type = type;
    113 }
    114 
    115 /* block_latency_histogram_compare_func:
    116  * Compare @key with interval [@it[0], @it[1]).
    117  * Return: -1 if @key < @it[0]
    118  *          0 if @key in [@it[0], @it[1])
    119  *         +1 if @key >= @it[1]
    120  */
    121 static int block_latency_histogram_compare_func(const void *key, const void *it)
    122 {
    123     uint64_t k = *(uint64_t *)key;
    124     uint64_t a = ((uint64_t *)it)[0];
    125     uint64_t b = ((uint64_t *)it)[1];
    126 
    127     return k < a ? -1 : (k < b ? 0 : 1);
    128 }
    129 
    130 static void block_latency_histogram_account(BlockLatencyHistogram *hist,
    131                                             int64_t latency_ns)
    132 {
    133     uint64_t *pos;
    134 
    135     if (hist->bins == NULL) {
    136         /* histogram disabled */
    137         return;
    138     }
    139 
    140 
    141     if (latency_ns < hist->boundaries[0]) {
    142         hist->bins[0]++;
    143         return;
    144     }
    145 
    146     if (latency_ns >= hist->boundaries[hist->nbins - 2]) {
    147         hist->bins[hist->nbins - 1]++;
    148         return;
    149     }
    150 
    151     pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2,
    152                   sizeof(hist->boundaries[0]),
    153                   block_latency_histogram_compare_func);
    154     assert(pos != NULL);
    155 
    156     hist->bins[pos - hist->boundaries + 1]++;
    157 }
    158 
    159 int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
    160                                 uint64List *boundaries)
    161 {
    162     BlockLatencyHistogram *hist = &stats->latency_histogram[type];
    163     uint64List *entry;
    164     uint64_t *ptr;
    165     uint64_t prev = 0;
    166     int new_nbins = 1;
    167 
    168     for (entry = boundaries; entry; entry = entry->next) {
    169         if (entry->value <= prev) {
    170             return -EINVAL;
    171         }
    172         new_nbins++;
    173         prev = entry->value;
    174     }
    175 
    176     hist->nbins = new_nbins;
    177     g_free(hist->boundaries);
    178     hist->boundaries = g_new(uint64_t, hist->nbins - 1);
    179     for (entry = boundaries, ptr = hist->boundaries; entry;
    180          entry = entry->next, ptr++)
    181     {
    182         *ptr = entry->value;
    183     }
    184 
    185     g_free(hist->bins);
    186     hist->bins = g_new0(uint64_t, hist->nbins);
    187 
    188     return 0;
    189 }
    190 
    191 void block_latency_histograms_clear(BlockAcctStats *stats)
    192 {
    193     int i;
    194 
    195     for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
    196         BlockLatencyHistogram *hist = &stats->latency_histogram[i];
    197         g_free(hist->bins);
    198         g_free(hist->boundaries);
    199         memset(hist, 0, sizeof(*hist));
    200     }
    201 }
    202 
    203 static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
    204                                  bool failed)
    205 {
    206     BlockAcctTimedStats *s;
    207     int64_t time_ns = qemu_clock_get_ns(clock_type);
    208     int64_t latency_ns = time_ns - cookie->start_time_ns;
    209 
    210     if (qtest_enabled()) {
    211         latency_ns = qtest_latency_ns;
    212     }
    213 
    214     assert(cookie->type < BLOCK_MAX_IOTYPE);
    215 
    216     if (cookie->type == BLOCK_ACCT_NONE) {
    217         return;
    218     }
    219 
    220     WITH_QEMU_LOCK_GUARD(&stats->lock) {
    221         if (failed) {
    222             stats->failed_ops[cookie->type]++;
    223         } else {
    224             stats->nr_bytes[cookie->type] += cookie->bytes;
    225             stats->nr_ops[cookie->type]++;
    226         }
    227 
    228         block_latency_histogram_account(&stats->latency_histogram[cookie->type],
    229                                         latency_ns);
    230 
    231         if (!failed || stats->account_failed) {
    232             stats->total_time_ns[cookie->type] += latency_ns;
    233             stats->last_access_time_ns = time_ns;
    234 
    235             QSLIST_FOREACH(s, &stats->intervals, entries) {
    236                 timed_average_account(&s->latency[cookie->type], latency_ns);
    237             }
    238         }
    239     }
    240 
    241     cookie->type = BLOCK_ACCT_NONE;
    242 }
    243 
    244 void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
    245 {
    246     block_account_one_io(stats, cookie, false);
    247 }
    248 
    249 void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie)
    250 {
    251     block_account_one_io(stats, cookie, true);
    252 }
    253 
    254 void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type)
    255 {
    256     assert(type < BLOCK_MAX_IOTYPE);
    257 
    258     /* block_account_one_io() updates total_time_ns[], but this one does
    259      * not.  The reason is that invalid requests are accounted during their
    260      * submission, therefore there's no actual I/O involved.
    261      */
    262     qemu_mutex_lock(&stats->lock);
    263     stats->invalid_ops[type]++;
    264 
    265     if (stats->account_invalid) {
    266         stats->last_access_time_ns = qemu_clock_get_ns(clock_type);
    267     }
    268     qemu_mutex_unlock(&stats->lock);
    269 }
    270 
    271 void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
    272                       int num_requests)
    273 {
    274     assert(type < BLOCK_MAX_IOTYPE);
    275 
    276     qemu_mutex_lock(&stats->lock);
    277     stats->merged[type] += num_requests;
    278     qemu_mutex_unlock(&stats->lock);
    279 }
    280 
    281 int64_t block_acct_idle_time_ns(BlockAcctStats *stats)
    282 {
    283     return qemu_clock_get_ns(clock_type) - stats->last_access_time_ns;
    284 }
    285 
    286 double block_acct_queue_depth(BlockAcctTimedStats *stats,
    287                               enum BlockAcctType type)
    288 {
    289     uint64_t sum, elapsed;
    290 
    291     assert(type < BLOCK_MAX_IOTYPE);
    292 
    293     qemu_mutex_lock(&stats->stats->lock);
    294     sum = timed_average_sum(&stats->latency[type], &elapsed);
    295     qemu_mutex_unlock(&stats->stats->lock);
    296 
    297     return (double) sum / elapsed;
    298 }