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:
parent
9caa6f3dbe
commit
5b50bf77ce
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue