Skip to content

Commit b741ae7

Browse files
Vladimir Sementsov-Ogievskiyebblake
Vladimir Sementsov-Ogievskiy
authored andcommitted
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: Vladimir Sementsov-Ogievskiy <[email protected]> Message-Id: <[email protected]> Reviewed-by: Eric Blake <[email protected]> Reviewed-by: Stefan Hajnoczi <[email protected]> Signed-off-by: Eric Blake <[email protected]>
1 parent d003f7a commit b741ae7

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed

block/accounting.c

+91
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,94 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
9494
cookie->type = type;
9595
}
9696

97+
/* block_latency_histogram_compare_func:
98+
* Compare @key with interval [@it[0], @it[1]).
99+
* Return: -1 if @key < @it[0]
100+
* 0 if @key in [@it[0], @it[1])
101+
* +1 if @key >= @it[1]
102+
*/
103+
static int block_latency_histogram_compare_func(const void *key, const void *it)
104+
{
105+
uint64_t k = *(uint64_t *)key;
106+
uint64_t a = ((uint64_t *)it)[0];
107+
uint64_t b = ((uint64_t *)it)[1];
108+
109+
return k < a ? -1 : (k < b ? 0 : 1);
110+
}
111+
112+
static void block_latency_histogram_account(BlockLatencyHistogram *hist,
113+
int64_t latency_ns)
114+
{
115+
uint64_t *pos;
116+
117+
if (hist->bins == NULL) {
118+
/* histogram disabled */
119+
return;
120+
}
121+
122+
123+
if (latency_ns < hist->boundaries[0]) {
124+
hist->bins[0]++;
125+
return;
126+
}
127+
128+
if (latency_ns >= hist->boundaries[hist->nbins - 2]) {
129+
hist->bins[hist->nbins - 1]++;
130+
return;
131+
}
132+
133+
pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2,
134+
sizeof(hist->boundaries[0]),
135+
block_latency_histogram_compare_func);
136+
assert(pos != NULL);
137+
138+
hist->bins[pos - hist->boundaries + 1]++;
139+
}
140+
141+
int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
142+
uint64List *boundaries)
143+
{
144+
BlockLatencyHistogram *hist = &stats->latency_histogram[type];
145+
uint64List *entry;
146+
uint64_t *ptr;
147+
uint64_t prev = 0;
148+
int new_nbins = 1;
149+
150+
for (entry = boundaries; entry; entry = entry->next) {
151+
if (entry->value <= prev) {
152+
return -EINVAL;
153+
}
154+
new_nbins++;
155+
prev = entry->value;
156+
}
157+
158+
hist->nbins = new_nbins;
159+
g_free(hist->boundaries);
160+
hist->boundaries = g_new(uint64_t, hist->nbins - 1);
161+
for (entry = boundaries, ptr = hist->boundaries; entry;
162+
entry = entry->next, ptr++)
163+
{
164+
*ptr = entry->value;
165+
}
166+
167+
g_free(hist->bins);
168+
hist->bins = g_new0(uint64_t, hist->nbins);
169+
170+
return 0;
171+
}
172+
173+
void block_latency_histograms_clear(BlockAcctStats *stats)
174+
{
175+
int i;
176+
177+
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
178+
BlockLatencyHistogram *hist = &stats->latency_histogram[i];
179+
g_free(hist->bins);
180+
g_free(hist->boundaries);
181+
memset(hist, 0, sizeof(*hist));
182+
}
183+
}
184+
97185
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
98186
bool failed)
99187
{
@@ -116,6 +204,9 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
116204
stats->nr_ops[cookie->type]++;
117205
}
118206

207+
block_latency_histogram_account(&stats->latency_histogram[cookie->type],
208+
latency_ns);
209+
119210
if (!failed || stats->account_failed) {
120211
stats->total_time_ns[cookie->type] += latency_ns;
121212
stats->last_access_time_ns = time_ns;

include/block/accounting.h

+35
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include "qemu/timed-average.h"
2929
#include "qemu/thread.h"
30+
#include "qapi/qapi-builtin-types.h"
3031

3132
typedef struct BlockAcctTimedStats BlockAcctTimedStats;
3233
typedef struct BlockAcctStats BlockAcctStats;
@@ -45,6 +46,36 @@ struct BlockAcctTimedStats {
4546
QSLIST_ENTRY(BlockAcctTimedStats) entries;
4647
};
4748

49+
typedef struct BlockLatencyHistogram {
50+
/* The following histogram is represented like this:
51+
*
52+
* 5| *
53+
* 4| *
54+
* 3| * *
55+
* 2| * * *
56+
* 1| * * * *
57+
* +------------------
58+
* 10 50 100
59+
*
60+
* BlockLatencyHistogram histogram = {
61+
* .nbins = 4,
62+
* .boundaries = {10, 50, 100},
63+
* .bins = {3, 1, 5, 2},
64+
* };
65+
*
66+
* @boundaries array define histogram intervals as follows:
67+
* [0, boundaries[0]), [boundaries[0], boundaries[1]), ...
68+
* [boundaries[nbins-2], +inf)
69+
*
70+
* So, for example above, histogram intervals are:
71+
* [0, 10), [10, 50), [50, 100), [100, +inf)
72+
*/
73+
int nbins;
74+
uint64_t *boundaries; /* @nbins-1 numbers here
75+
(all boundaries, except 0 and +inf) */
76+
uint64_t *bins;
77+
} BlockLatencyHistogram;
78+
4879
struct BlockAcctStats {
4980
QemuMutex lock;
5081
uint64_t nr_bytes[BLOCK_MAX_IOTYPE];
@@ -57,6 +88,7 @@ struct BlockAcctStats {
5788
QSLIST_HEAD(, BlockAcctTimedStats) intervals;
5889
bool account_invalid;
5990
bool account_failed;
91+
BlockLatencyHistogram latency_histogram[BLOCK_MAX_IOTYPE];
6092
};
6193

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

86121
#endif

0 commit comments

Comments
 (0)