block: make accounting thread-safe

I'm not trying too hard yet.  Later, with multiqueue support,
this may cause mutex contention or cacheline bouncing.

Cc: Alberto Garcia <berto@igalia.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20170605123908.18777-20-pbonzini@redhat.com>
Reviewed-by: Alberto Garcia <berto@igalia.com>
Signed-off-by: Fam Zheng <famz@redhat.com>
This commit is contained in:
Paolo Bonzini 2017-06-05 14:39:08 +02:00 committed by Fam Zheng
parent 9caa6f3dbe
commit 5b50bf77ce
2 changed files with 22 additions and 2 deletions

View file

@ -34,6 +34,7 @@ static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000;
void block_acct_init(BlockAcctStats *stats) void block_acct_init(BlockAcctStats *stats)
{ {
qemu_mutex_init(&stats->lock);
if (qtest_enabled()) { if (qtest_enabled()) {
clock_type = QEMU_CLOCK_VIRTUAL; clock_type = QEMU_CLOCK_VIRTUAL;
} }
@ -52,6 +53,7 @@ void block_acct_cleanup(BlockAcctStats *stats)
QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) { QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) {
g_free(s); g_free(s);
} }
qemu_mutex_destroy(&stats->lock);
} }
void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length) void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
@ -61,12 +63,15 @@ void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length)
s = g_new0(BlockAcctTimedStats, 1); s = g_new0(BlockAcctTimedStats, 1);
s->interval_length = interval_length; s->interval_length = interval_length;
s->stats = stats;
qemu_mutex_lock(&stats->lock);
QSLIST_INSERT_HEAD(&stats->intervals, s, entries); QSLIST_INSERT_HEAD(&stats->intervals, s, entries);
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
timed_average_init(&s->latency[i], clock_type, timed_average_init(&s->latency[i], clock_type,
(uint64_t) interval_length * NANOSECONDS_PER_SECOND); (uint64_t) interval_length * NANOSECONDS_PER_SECOND);
} }
qemu_mutex_unlock(&stats->lock);
} }
BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats, BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats,
@ -102,6 +107,8 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
assert(cookie->type < BLOCK_MAX_IOTYPE); assert(cookie->type < BLOCK_MAX_IOTYPE);
qemu_mutex_lock(&stats->lock);
if (failed) { if (failed) {
stats->failed_ops[cookie->type]++; stats->failed_ops[cookie->type]++;
} else { } else {
@ -117,6 +124,8 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
timed_average_account(&s->latency[cookie->type], latency_ns); timed_average_account(&s->latency[cookie->type], latency_ns);
} }
} }
qemu_mutex_unlock(&stats->lock);
} }
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie) void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
@ -137,18 +146,23 @@ void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type)
* not. The reason is that invalid requests are accounted during their * not. The reason is that invalid requests are accounted during their
* submission, therefore there's no actual I/O involved. * submission, therefore there's no actual I/O involved.
*/ */
qemu_mutex_lock(&stats->lock);
stats->invalid_ops[type]++; stats->invalid_ops[type]++;
if (stats->account_invalid) { if (stats->account_invalid) {
stats->last_access_time_ns = qemu_clock_get_ns(clock_type); stats->last_access_time_ns = qemu_clock_get_ns(clock_type);
} }
qemu_mutex_unlock(&stats->lock);
} }
void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
int num_requests) int num_requests)
{ {
assert(type < BLOCK_MAX_IOTYPE); assert(type < BLOCK_MAX_IOTYPE);
qemu_mutex_lock(&stats->lock);
stats->merged[type] += num_requests; stats->merged[type] += num_requests;
qemu_mutex_unlock(&stats->lock);
} }
int64_t block_acct_idle_time_ns(BlockAcctStats *stats) int64_t block_acct_idle_time_ns(BlockAcctStats *stats)
@ -163,7 +177,9 @@ double block_acct_queue_depth(BlockAcctTimedStats *stats,
assert(type < BLOCK_MAX_IOTYPE); assert(type < BLOCK_MAX_IOTYPE);
qemu_mutex_lock(&stats->stats->lock);
sum = timed_average_sum(&stats->latency[type], &elapsed); sum = timed_average_sum(&stats->latency[type], &elapsed);
qemu_mutex_unlock(&stats->stats->lock);
return (double) sum / elapsed; return (double) sum / elapsed;
} }

View file

@ -26,8 +26,10 @@
#define BLOCK_ACCOUNTING_H #define BLOCK_ACCOUNTING_H
#include "qemu/timed-average.h" #include "qemu/timed-average.h"
#include "qemu/thread.h"
typedef struct BlockAcctTimedStats BlockAcctTimedStats; typedef struct BlockAcctTimedStats BlockAcctTimedStats;
typedef struct BlockAcctStats BlockAcctStats;
enum BlockAcctType { enum BlockAcctType {
BLOCK_ACCT_READ, BLOCK_ACCT_READ,
@ -37,12 +39,14 @@ enum BlockAcctType {
}; };
struct BlockAcctTimedStats { struct BlockAcctTimedStats {
BlockAcctStats *stats;
TimedAverage latency[BLOCK_MAX_IOTYPE]; TimedAverage latency[BLOCK_MAX_IOTYPE];
unsigned interval_length; /* in seconds */ unsigned interval_length; /* in seconds */
QSLIST_ENTRY(BlockAcctTimedStats) entries; QSLIST_ENTRY(BlockAcctTimedStats) entries;
}; };
typedef struct BlockAcctStats { struct BlockAcctStats {
QemuMutex lock;
uint64_t nr_bytes[BLOCK_MAX_IOTYPE]; uint64_t nr_bytes[BLOCK_MAX_IOTYPE];
uint64_t nr_ops[BLOCK_MAX_IOTYPE]; uint64_t nr_ops[BLOCK_MAX_IOTYPE];
uint64_t invalid_ops[BLOCK_MAX_IOTYPE]; uint64_t invalid_ops[BLOCK_MAX_IOTYPE];
@ -53,7 +57,7 @@ typedef struct BlockAcctStats {
QSLIST_HEAD(, BlockAcctTimedStats) intervals; QSLIST_HEAD(, BlockAcctTimedStats) intervals;
bool account_invalid; bool account_invalid;
bool account_failed; bool account_failed;
} BlockAcctStats; };
typedef struct BlockAcctCookie { typedef struct BlockAcctCookie {
int64_t bytes; int64_t bytes;