xref: /openbmc/linux/block/blk-stat.c (revision 7d8d00140715a115193bf502fa3b0cfe620f0dd9)
1cf43e6beSJens Axboe /*
2cf43e6beSJens Axboe  * Block stat tracking code
3cf43e6beSJens Axboe  *
4cf43e6beSJens Axboe  * Copyright (C) 2016 Jens Axboe
5cf43e6beSJens Axboe  */
6cf43e6beSJens Axboe #include <linux/kernel.h>
7cf43e6beSJens Axboe #include <linux/blk-mq.h>
8cf43e6beSJens Axboe 
9cf43e6beSJens Axboe #include "blk-stat.h"
10cf43e6beSJens Axboe #include "blk-mq.h"
11cf43e6beSJens Axboe 
12cf43e6beSJens Axboe static void blk_stat_flush_batch(struct blk_rq_stat *stat)
13cf43e6beSJens Axboe {
14cf43e6beSJens Axboe 	const s32 nr_batch = READ_ONCE(stat->nr_batch);
15209200efSShaohua Li 	const s32 nr_samples = READ_ONCE(stat->nr_samples);
16cf43e6beSJens Axboe 
17cf43e6beSJens Axboe 	if (!nr_batch)
18cf43e6beSJens Axboe 		return;
19cf43e6beSJens Axboe 	if (!nr_samples)
20cf43e6beSJens Axboe 		stat->mean = div64_s64(stat->batch, nr_batch);
21cf43e6beSJens Axboe 	else {
22cf43e6beSJens Axboe 		stat->mean = div64_s64((stat->mean * nr_samples) +
23cf43e6beSJens Axboe 					stat->batch,
24cf43e6beSJens Axboe 					nr_batch + nr_samples);
25cf43e6beSJens Axboe 	}
26cf43e6beSJens Axboe 
27cf43e6beSJens Axboe 	stat->nr_samples += nr_batch;
28cf43e6beSJens Axboe 	stat->nr_batch = stat->batch = 0;
29cf43e6beSJens Axboe }
30cf43e6beSJens Axboe 
31cf43e6beSJens Axboe static void blk_stat_sum(struct blk_rq_stat *dst, struct blk_rq_stat *src)
32cf43e6beSJens Axboe {
33*7d8d0014SOmar Sandoval 	blk_stat_flush_batch(src);
34*7d8d0014SOmar Sandoval 
35cf43e6beSJens Axboe 	if (!src->nr_samples)
36cf43e6beSJens Axboe 		return;
37cf43e6beSJens Axboe 
38cf43e6beSJens Axboe 	dst->min = min(dst->min, src->min);
39cf43e6beSJens Axboe 	dst->max = max(dst->max, src->max);
40cf43e6beSJens Axboe 
41cf43e6beSJens Axboe 	if (!dst->nr_samples)
42cf43e6beSJens Axboe 		dst->mean = src->mean;
43cf43e6beSJens Axboe 	else {
44cf43e6beSJens Axboe 		dst->mean = div64_s64((src->mean * src->nr_samples) +
45cf43e6beSJens Axboe 					(dst->mean * dst->nr_samples),
46cf43e6beSJens Axboe 					dst->nr_samples + src->nr_samples);
47cf43e6beSJens Axboe 	}
48cf43e6beSJens Axboe 	dst->nr_samples += src->nr_samples;
49cf43e6beSJens Axboe }
50cf43e6beSJens Axboe 
51cf43e6beSJens Axboe static void blk_mq_stat_get(struct request_queue *q, struct blk_rq_stat *dst)
52cf43e6beSJens Axboe {
53cf43e6beSJens Axboe 	struct blk_mq_hw_ctx *hctx;
54cf43e6beSJens Axboe 	struct blk_mq_ctx *ctx;
55cf43e6beSJens Axboe 	uint64_t latest = 0;
56cf43e6beSJens Axboe 	int i, j, nr;
57cf43e6beSJens Axboe 
58cf43e6beSJens Axboe 	blk_stat_init(&dst[BLK_STAT_READ]);
59cf43e6beSJens Axboe 	blk_stat_init(&dst[BLK_STAT_WRITE]);
60cf43e6beSJens Axboe 
61cf43e6beSJens Axboe 	nr = 0;
62cf43e6beSJens Axboe 	do {
63cf43e6beSJens Axboe 		uint64_t newest = 0;
64cf43e6beSJens Axboe 
65cf43e6beSJens Axboe 		queue_for_each_hw_ctx(q, hctx, i) {
66cf43e6beSJens Axboe 			hctx_for_each_ctx(hctx, ctx, j) {
677cd54aa8SJens Axboe 				blk_stat_flush_batch(&ctx->stat[BLK_STAT_READ]);
687cd54aa8SJens Axboe 				blk_stat_flush_batch(&ctx->stat[BLK_STAT_WRITE]);
697cd54aa8SJens Axboe 
70cf43e6beSJens Axboe 				if (!ctx->stat[BLK_STAT_READ].nr_samples &&
71cf43e6beSJens Axboe 				    !ctx->stat[BLK_STAT_WRITE].nr_samples)
72cf43e6beSJens Axboe 					continue;
73cf43e6beSJens Axboe 				if (ctx->stat[BLK_STAT_READ].time > newest)
74cf43e6beSJens Axboe 					newest = ctx->stat[BLK_STAT_READ].time;
75cf43e6beSJens Axboe 				if (ctx->stat[BLK_STAT_WRITE].time > newest)
76cf43e6beSJens Axboe 					newest = ctx->stat[BLK_STAT_WRITE].time;
77cf43e6beSJens Axboe 			}
78cf43e6beSJens Axboe 		}
79cf43e6beSJens Axboe 
80cf43e6beSJens Axboe 		/*
81cf43e6beSJens Axboe 		 * No samples
82cf43e6beSJens Axboe 		 */
83cf43e6beSJens Axboe 		if (!newest)
84cf43e6beSJens Axboe 			break;
85cf43e6beSJens Axboe 
86cf43e6beSJens Axboe 		if (newest > latest)
87cf43e6beSJens Axboe 			latest = newest;
88cf43e6beSJens Axboe 
89cf43e6beSJens Axboe 		queue_for_each_hw_ctx(q, hctx, i) {
90cf43e6beSJens Axboe 			hctx_for_each_ctx(hctx, ctx, j) {
91cf43e6beSJens Axboe 				if (ctx->stat[BLK_STAT_READ].time == newest) {
92cf43e6beSJens Axboe 					blk_stat_sum(&dst[BLK_STAT_READ],
93cf43e6beSJens Axboe 						     &ctx->stat[BLK_STAT_READ]);
94cf43e6beSJens Axboe 					nr++;
95cf43e6beSJens Axboe 				}
96cf43e6beSJens Axboe 				if (ctx->stat[BLK_STAT_WRITE].time == newest) {
97cf43e6beSJens Axboe 					blk_stat_sum(&dst[BLK_STAT_WRITE],
98cf43e6beSJens Axboe 						     &ctx->stat[BLK_STAT_WRITE]);
99cf43e6beSJens Axboe 					nr++;
100cf43e6beSJens Axboe 				}
101cf43e6beSJens Axboe 			}
102cf43e6beSJens Axboe 		}
103cf43e6beSJens Axboe 		/*
104cf43e6beSJens Axboe 		 * If we race on finding an entry, just loop back again.
105cf43e6beSJens Axboe 		 * Should be very rare.
106cf43e6beSJens Axboe 		 */
107cf43e6beSJens Axboe 	} while (!nr);
108cf43e6beSJens Axboe 
109cf43e6beSJens Axboe 	dst[BLK_STAT_READ].time = dst[BLK_STAT_WRITE].time = latest;
110cf43e6beSJens Axboe }
111cf43e6beSJens Axboe 
112cf43e6beSJens Axboe void blk_queue_stat_get(struct request_queue *q, struct blk_rq_stat *dst)
113cf43e6beSJens Axboe {
114cf43e6beSJens Axboe 	if (q->mq_ops)
115cf43e6beSJens Axboe 		blk_mq_stat_get(q, dst);
116cf43e6beSJens Axboe 	else {
1177cd54aa8SJens Axboe 		blk_stat_flush_batch(&q->rq_stats[BLK_STAT_READ]);
1187cd54aa8SJens Axboe 		blk_stat_flush_batch(&q->rq_stats[BLK_STAT_WRITE]);
119cf43e6beSJens Axboe 		memcpy(&dst[BLK_STAT_READ], &q->rq_stats[BLK_STAT_READ],
120cf43e6beSJens Axboe 				sizeof(struct blk_rq_stat));
121cf43e6beSJens Axboe 		memcpy(&dst[BLK_STAT_WRITE], &q->rq_stats[BLK_STAT_WRITE],
122cf43e6beSJens Axboe 				sizeof(struct blk_rq_stat));
123cf43e6beSJens Axboe 	}
124cf43e6beSJens Axboe }
125cf43e6beSJens Axboe 
126cf43e6beSJens Axboe void blk_hctx_stat_get(struct blk_mq_hw_ctx *hctx, struct blk_rq_stat *dst)
127cf43e6beSJens Axboe {
128cf43e6beSJens Axboe 	struct blk_mq_ctx *ctx;
129cf43e6beSJens Axboe 	unsigned int i, nr;
130cf43e6beSJens Axboe 
131cf43e6beSJens Axboe 	nr = 0;
132cf43e6beSJens Axboe 	do {
133cf43e6beSJens Axboe 		uint64_t newest = 0;
134cf43e6beSJens Axboe 
135cf43e6beSJens Axboe 		hctx_for_each_ctx(hctx, ctx, i) {
1367cd54aa8SJens Axboe 			blk_stat_flush_batch(&ctx->stat[BLK_STAT_READ]);
1377cd54aa8SJens Axboe 			blk_stat_flush_batch(&ctx->stat[BLK_STAT_WRITE]);
1387cd54aa8SJens Axboe 
139cf43e6beSJens Axboe 			if (!ctx->stat[BLK_STAT_READ].nr_samples &&
140cf43e6beSJens Axboe 			    !ctx->stat[BLK_STAT_WRITE].nr_samples)
141cf43e6beSJens Axboe 				continue;
142cf43e6beSJens Axboe 
143cf43e6beSJens Axboe 			if (ctx->stat[BLK_STAT_READ].time > newest)
144cf43e6beSJens Axboe 				newest = ctx->stat[BLK_STAT_READ].time;
145cf43e6beSJens Axboe 			if (ctx->stat[BLK_STAT_WRITE].time > newest)
146cf43e6beSJens Axboe 				newest = ctx->stat[BLK_STAT_WRITE].time;
147cf43e6beSJens Axboe 		}
148cf43e6beSJens Axboe 
149cf43e6beSJens Axboe 		if (!newest)
150cf43e6beSJens Axboe 			break;
151cf43e6beSJens Axboe 
152cf43e6beSJens Axboe 		hctx_for_each_ctx(hctx, ctx, i) {
153cf43e6beSJens Axboe 			if (ctx->stat[BLK_STAT_READ].time == newest) {
154cf43e6beSJens Axboe 				blk_stat_sum(&dst[BLK_STAT_READ],
155cf43e6beSJens Axboe 						&ctx->stat[BLK_STAT_READ]);
156cf43e6beSJens Axboe 				nr++;
157cf43e6beSJens Axboe 			}
158cf43e6beSJens Axboe 			if (ctx->stat[BLK_STAT_WRITE].time == newest) {
159cf43e6beSJens Axboe 				blk_stat_sum(&dst[BLK_STAT_WRITE],
160cf43e6beSJens Axboe 						&ctx->stat[BLK_STAT_WRITE]);
161cf43e6beSJens Axboe 				nr++;
162cf43e6beSJens Axboe 			}
163cf43e6beSJens Axboe 		}
164cf43e6beSJens Axboe 		/*
165cf43e6beSJens Axboe 		 * If we race on finding an entry, just loop back again.
166cf43e6beSJens Axboe 		 * Should be very rare, as the window is only updated
167cf43e6beSJens Axboe 		 * occasionally
168cf43e6beSJens Axboe 		 */
169cf43e6beSJens Axboe 	} while (!nr);
170cf43e6beSJens Axboe }
171cf43e6beSJens Axboe 
172cf43e6beSJens Axboe static void __blk_stat_init(struct blk_rq_stat *stat, s64 time_now)
173cf43e6beSJens Axboe {
174cf43e6beSJens Axboe 	stat->min = -1ULL;
175cf43e6beSJens Axboe 	stat->max = stat->nr_samples = stat->mean = 0;
176cf43e6beSJens Axboe 	stat->batch = stat->nr_batch = 0;
177cf43e6beSJens Axboe 	stat->time = time_now & BLK_STAT_NSEC_MASK;
178cf43e6beSJens Axboe }
179cf43e6beSJens Axboe 
180cf43e6beSJens Axboe void blk_stat_init(struct blk_rq_stat *stat)
181cf43e6beSJens Axboe {
182cf43e6beSJens Axboe 	__blk_stat_init(stat, ktime_to_ns(ktime_get()));
183cf43e6beSJens Axboe }
184cf43e6beSJens Axboe 
185cf43e6beSJens Axboe static bool __blk_stat_is_current(struct blk_rq_stat *stat, s64 now)
186cf43e6beSJens Axboe {
187cf43e6beSJens Axboe 	return (now & BLK_STAT_NSEC_MASK) == (stat->time & BLK_STAT_NSEC_MASK);
188cf43e6beSJens Axboe }
189cf43e6beSJens Axboe 
190cf43e6beSJens Axboe bool blk_stat_is_current(struct blk_rq_stat *stat)
191cf43e6beSJens Axboe {
192cf43e6beSJens Axboe 	return __blk_stat_is_current(stat, ktime_to_ns(ktime_get()));
193cf43e6beSJens Axboe }
194cf43e6beSJens Axboe 
195cf43e6beSJens Axboe void blk_stat_add(struct blk_rq_stat *stat, struct request *rq)
196cf43e6beSJens Axboe {
197cf43e6beSJens Axboe 	s64 now, value;
198cf43e6beSJens Axboe 
199cf43e6beSJens Axboe 	now = __blk_stat_time(ktime_to_ns(ktime_get()));
200cf43e6beSJens Axboe 	if (now < blk_stat_time(&rq->issue_stat))
201cf43e6beSJens Axboe 		return;
202cf43e6beSJens Axboe 
203cf43e6beSJens Axboe 	if (!__blk_stat_is_current(stat, now))
204cf43e6beSJens Axboe 		__blk_stat_init(stat, now);
205cf43e6beSJens Axboe 
206cf43e6beSJens Axboe 	value = now - blk_stat_time(&rq->issue_stat);
207cf43e6beSJens Axboe 	if (value > stat->max)
208cf43e6beSJens Axboe 		stat->max = value;
209cf43e6beSJens Axboe 	if (value < stat->min)
210cf43e6beSJens Axboe 		stat->min = value;
211cf43e6beSJens Axboe 
212cf43e6beSJens Axboe 	if (stat->batch + value < stat->batch ||
213cf43e6beSJens Axboe 	    stat->nr_batch + 1 == BLK_RQ_STAT_BATCH)
214cf43e6beSJens Axboe 		blk_stat_flush_batch(stat);
215cf43e6beSJens Axboe 
216cf43e6beSJens Axboe 	stat->batch += value;
217cf43e6beSJens Axboe 	stat->nr_batch++;
218cf43e6beSJens Axboe }
219cf43e6beSJens Axboe 
220cf43e6beSJens Axboe void blk_stat_clear(struct request_queue *q)
221cf43e6beSJens Axboe {
222cf43e6beSJens Axboe 	if (q->mq_ops) {
223cf43e6beSJens Axboe 		struct blk_mq_hw_ctx *hctx;
224cf43e6beSJens Axboe 		struct blk_mq_ctx *ctx;
225cf43e6beSJens Axboe 		int i, j;
226cf43e6beSJens Axboe 
227cf43e6beSJens Axboe 		queue_for_each_hw_ctx(q, hctx, i) {
228cf43e6beSJens Axboe 			hctx_for_each_ctx(hctx, ctx, j) {
229cf43e6beSJens Axboe 				blk_stat_init(&ctx->stat[BLK_STAT_READ]);
230cf43e6beSJens Axboe 				blk_stat_init(&ctx->stat[BLK_STAT_WRITE]);
231cf43e6beSJens Axboe 			}
232cf43e6beSJens Axboe 		}
233cf43e6beSJens Axboe 	} else {
234cf43e6beSJens Axboe 		blk_stat_init(&q->rq_stats[BLK_STAT_READ]);
235cf43e6beSJens Axboe 		blk_stat_init(&q->rq_stats[BLK_STAT_WRITE]);
236cf43e6beSJens Axboe 	}
237cf43e6beSJens Axboe }
238cf43e6beSJens Axboe 
239cf43e6beSJens Axboe void blk_stat_set_issue_time(struct blk_issue_stat *stat)
240cf43e6beSJens Axboe {
241cf43e6beSJens Axboe 	stat->time = (stat->time & BLK_STAT_MASK) |
242cf43e6beSJens Axboe 			(ktime_to_ns(ktime_get()) & BLK_STAT_TIME_MASK);
243cf43e6beSJens Axboe }
244cf43e6beSJens Axboe 
245cf43e6beSJens Axboe /*
246cf43e6beSJens Axboe  * Enable stat tracking, return whether it was enabled
247cf43e6beSJens Axboe  */
248cf43e6beSJens Axboe bool blk_stat_enable(struct request_queue *q)
249cf43e6beSJens Axboe {
250cf43e6beSJens Axboe 	if (!test_bit(QUEUE_FLAG_STATS, &q->queue_flags)) {
251cf43e6beSJens Axboe 		set_bit(QUEUE_FLAG_STATS, &q->queue_flags);
252cf43e6beSJens Axboe 		return false;
253cf43e6beSJens Axboe 	}
254cf43e6beSJens Axboe 
255cf43e6beSJens Axboe 	return true;
256cf43e6beSJens Axboe }
257