xref: /openbmc/linux/block/blk-stat.c (revision 209200efa3db3d09380ee7c796efa73d900d5f3a)
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);
15*209200efSShaohua 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 {
33cf43e6beSJens Axboe 	if (!src->nr_samples)
34cf43e6beSJens Axboe 		return;
35cf43e6beSJens Axboe 
36cf43e6beSJens Axboe 	blk_stat_flush_batch(src);
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) {
67cf43e6beSJens Axboe 				if (!ctx->stat[BLK_STAT_READ].nr_samples &&
68cf43e6beSJens Axboe 				    !ctx->stat[BLK_STAT_WRITE].nr_samples)
69cf43e6beSJens Axboe 					continue;
70cf43e6beSJens Axboe 				if (ctx->stat[BLK_STAT_READ].time > newest)
71cf43e6beSJens Axboe 					newest = ctx->stat[BLK_STAT_READ].time;
72cf43e6beSJens Axboe 				if (ctx->stat[BLK_STAT_WRITE].time > newest)
73cf43e6beSJens Axboe 					newest = ctx->stat[BLK_STAT_WRITE].time;
74cf43e6beSJens Axboe 			}
75cf43e6beSJens Axboe 		}
76cf43e6beSJens Axboe 
77cf43e6beSJens Axboe 		/*
78cf43e6beSJens Axboe 		 * No samples
79cf43e6beSJens Axboe 		 */
80cf43e6beSJens Axboe 		if (!newest)
81cf43e6beSJens Axboe 			break;
82cf43e6beSJens Axboe 
83cf43e6beSJens Axboe 		if (newest > latest)
84cf43e6beSJens Axboe 			latest = newest;
85cf43e6beSJens Axboe 
86cf43e6beSJens Axboe 		queue_for_each_hw_ctx(q, hctx, i) {
87cf43e6beSJens Axboe 			hctx_for_each_ctx(hctx, ctx, j) {
88cf43e6beSJens Axboe 				if (ctx->stat[BLK_STAT_READ].time == newest) {
89cf43e6beSJens Axboe 					blk_stat_sum(&dst[BLK_STAT_READ],
90cf43e6beSJens Axboe 						     &ctx->stat[BLK_STAT_READ]);
91cf43e6beSJens Axboe 					nr++;
92cf43e6beSJens Axboe 				}
93cf43e6beSJens Axboe 				if (ctx->stat[BLK_STAT_WRITE].time == newest) {
94cf43e6beSJens Axboe 					blk_stat_sum(&dst[BLK_STAT_WRITE],
95cf43e6beSJens Axboe 						     &ctx->stat[BLK_STAT_WRITE]);
96cf43e6beSJens Axboe 					nr++;
97cf43e6beSJens Axboe 				}
98cf43e6beSJens Axboe 			}
99cf43e6beSJens Axboe 		}
100cf43e6beSJens Axboe 		/*
101cf43e6beSJens Axboe 		 * If we race on finding an entry, just loop back again.
102cf43e6beSJens Axboe 		 * Should be very rare.
103cf43e6beSJens Axboe 		 */
104cf43e6beSJens Axboe 	} while (!nr);
105cf43e6beSJens Axboe 
106cf43e6beSJens Axboe 	dst[BLK_STAT_READ].time = dst[BLK_STAT_WRITE].time = latest;
107cf43e6beSJens Axboe }
108cf43e6beSJens Axboe 
109cf43e6beSJens Axboe void blk_queue_stat_get(struct request_queue *q, struct blk_rq_stat *dst)
110cf43e6beSJens Axboe {
111cf43e6beSJens Axboe 	if (q->mq_ops)
112cf43e6beSJens Axboe 		blk_mq_stat_get(q, dst);
113cf43e6beSJens Axboe 	else {
114cf43e6beSJens Axboe 		memcpy(&dst[BLK_STAT_READ], &q->rq_stats[BLK_STAT_READ],
115cf43e6beSJens Axboe 				sizeof(struct blk_rq_stat));
116cf43e6beSJens Axboe 		memcpy(&dst[BLK_STAT_WRITE], &q->rq_stats[BLK_STAT_WRITE],
117cf43e6beSJens Axboe 				sizeof(struct blk_rq_stat));
118cf43e6beSJens Axboe 	}
119cf43e6beSJens Axboe }
120cf43e6beSJens Axboe 
121cf43e6beSJens Axboe void blk_hctx_stat_get(struct blk_mq_hw_ctx *hctx, struct blk_rq_stat *dst)
122cf43e6beSJens Axboe {
123cf43e6beSJens Axboe 	struct blk_mq_ctx *ctx;
124cf43e6beSJens Axboe 	unsigned int i, nr;
125cf43e6beSJens Axboe 
126cf43e6beSJens Axboe 	nr = 0;
127cf43e6beSJens Axboe 	do {
128cf43e6beSJens Axboe 		uint64_t newest = 0;
129cf43e6beSJens Axboe 
130cf43e6beSJens Axboe 		hctx_for_each_ctx(hctx, ctx, i) {
131cf43e6beSJens Axboe 			if (!ctx->stat[BLK_STAT_READ].nr_samples &&
132cf43e6beSJens Axboe 			    !ctx->stat[BLK_STAT_WRITE].nr_samples)
133cf43e6beSJens Axboe 				continue;
134cf43e6beSJens Axboe 
135cf43e6beSJens Axboe 			if (ctx->stat[BLK_STAT_READ].time > newest)
136cf43e6beSJens Axboe 				newest = ctx->stat[BLK_STAT_READ].time;
137cf43e6beSJens Axboe 			if (ctx->stat[BLK_STAT_WRITE].time > newest)
138cf43e6beSJens Axboe 				newest = ctx->stat[BLK_STAT_WRITE].time;
139cf43e6beSJens Axboe 		}
140cf43e6beSJens Axboe 
141cf43e6beSJens Axboe 		if (!newest)
142cf43e6beSJens Axboe 			break;
143cf43e6beSJens Axboe 
144cf43e6beSJens Axboe 		hctx_for_each_ctx(hctx, ctx, i) {
145cf43e6beSJens Axboe 			if (ctx->stat[BLK_STAT_READ].time == newest) {
146cf43e6beSJens Axboe 				blk_stat_sum(&dst[BLK_STAT_READ],
147cf43e6beSJens Axboe 						&ctx->stat[BLK_STAT_READ]);
148cf43e6beSJens Axboe 				nr++;
149cf43e6beSJens Axboe 			}
150cf43e6beSJens Axboe 			if (ctx->stat[BLK_STAT_WRITE].time == newest) {
151cf43e6beSJens Axboe 				blk_stat_sum(&dst[BLK_STAT_WRITE],
152cf43e6beSJens Axboe 						&ctx->stat[BLK_STAT_WRITE]);
153cf43e6beSJens Axboe 				nr++;
154cf43e6beSJens Axboe 			}
155cf43e6beSJens Axboe 		}
156cf43e6beSJens Axboe 		/*
157cf43e6beSJens Axboe 		 * If we race on finding an entry, just loop back again.
158cf43e6beSJens Axboe 		 * Should be very rare, as the window is only updated
159cf43e6beSJens Axboe 		 * occasionally
160cf43e6beSJens Axboe 		 */
161cf43e6beSJens Axboe 	} while (!nr);
162cf43e6beSJens Axboe }
163cf43e6beSJens Axboe 
164cf43e6beSJens Axboe static void __blk_stat_init(struct blk_rq_stat *stat, s64 time_now)
165cf43e6beSJens Axboe {
166cf43e6beSJens Axboe 	stat->min = -1ULL;
167cf43e6beSJens Axboe 	stat->max = stat->nr_samples = stat->mean = 0;
168cf43e6beSJens Axboe 	stat->batch = stat->nr_batch = 0;
169cf43e6beSJens Axboe 	stat->time = time_now & BLK_STAT_NSEC_MASK;
170cf43e6beSJens Axboe }
171cf43e6beSJens Axboe 
172cf43e6beSJens Axboe void blk_stat_init(struct blk_rq_stat *stat)
173cf43e6beSJens Axboe {
174cf43e6beSJens Axboe 	__blk_stat_init(stat, ktime_to_ns(ktime_get()));
175cf43e6beSJens Axboe }
176cf43e6beSJens Axboe 
177cf43e6beSJens Axboe static bool __blk_stat_is_current(struct blk_rq_stat *stat, s64 now)
178cf43e6beSJens Axboe {
179cf43e6beSJens Axboe 	return (now & BLK_STAT_NSEC_MASK) == (stat->time & BLK_STAT_NSEC_MASK);
180cf43e6beSJens Axboe }
181cf43e6beSJens Axboe 
182cf43e6beSJens Axboe bool blk_stat_is_current(struct blk_rq_stat *stat)
183cf43e6beSJens Axboe {
184cf43e6beSJens Axboe 	return __blk_stat_is_current(stat, ktime_to_ns(ktime_get()));
185cf43e6beSJens Axboe }
186cf43e6beSJens Axboe 
187cf43e6beSJens Axboe void blk_stat_add(struct blk_rq_stat *stat, struct request *rq)
188cf43e6beSJens Axboe {
189cf43e6beSJens Axboe 	s64 now, value;
190cf43e6beSJens Axboe 
191cf43e6beSJens Axboe 	now = __blk_stat_time(ktime_to_ns(ktime_get()));
192cf43e6beSJens Axboe 	if (now < blk_stat_time(&rq->issue_stat))
193cf43e6beSJens Axboe 		return;
194cf43e6beSJens Axboe 
195cf43e6beSJens Axboe 	if (!__blk_stat_is_current(stat, now))
196cf43e6beSJens Axboe 		__blk_stat_init(stat, now);
197cf43e6beSJens Axboe 
198cf43e6beSJens Axboe 	value = now - blk_stat_time(&rq->issue_stat);
199cf43e6beSJens Axboe 	if (value > stat->max)
200cf43e6beSJens Axboe 		stat->max = value;
201cf43e6beSJens Axboe 	if (value < stat->min)
202cf43e6beSJens Axboe 		stat->min = value;
203cf43e6beSJens Axboe 
204cf43e6beSJens Axboe 	if (stat->batch + value < stat->batch ||
205cf43e6beSJens Axboe 	    stat->nr_batch + 1 == BLK_RQ_STAT_BATCH)
206cf43e6beSJens Axboe 		blk_stat_flush_batch(stat);
207cf43e6beSJens Axboe 
208cf43e6beSJens Axboe 	stat->batch += value;
209cf43e6beSJens Axboe 	stat->nr_batch++;
210cf43e6beSJens Axboe }
211cf43e6beSJens Axboe 
212cf43e6beSJens Axboe void blk_stat_clear(struct request_queue *q)
213cf43e6beSJens Axboe {
214cf43e6beSJens Axboe 	if (q->mq_ops) {
215cf43e6beSJens Axboe 		struct blk_mq_hw_ctx *hctx;
216cf43e6beSJens Axboe 		struct blk_mq_ctx *ctx;
217cf43e6beSJens Axboe 		int i, j;
218cf43e6beSJens Axboe 
219cf43e6beSJens Axboe 		queue_for_each_hw_ctx(q, hctx, i) {
220cf43e6beSJens Axboe 			hctx_for_each_ctx(hctx, ctx, j) {
221cf43e6beSJens Axboe 				blk_stat_init(&ctx->stat[BLK_STAT_READ]);
222cf43e6beSJens Axboe 				blk_stat_init(&ctx->stat[BLK_STAT_WRITE]);
223cf43e6beSJens Axboe 			}
224cf43e6beSJens Axboe 		}
225cf43e6beSJens Axboe 	} else {
226cf43e6beSJens Axboe 		blk_stat_init(&q->rq_stats[BLK_STAT_READ]);
227cf43e6beSJens Axboe 		blk_stat_init(&q->rq_stats[BLK_STAT_WRITE]);
228cf43e6beSJens Axboe 	}
229cf43e6beSJens Axboe }
230cf43e6beSJens Axboe 
231cf43e6beSJens Axboe void blk_stat_set_issue_time(struct blk_issue_stat *stat)
232cf43e6beSJens Axboe {
233cf43e6beSJens Axboe 	stat->time = (stat->time & BLK_STAT_MASK) |
234cf43e6beSJens Axboe 			(ktime_to_ns(ktime_get()) & BLK_STAT_TIME_MASK);
235cf43e6beSJens Axboe }
236cf43e6beSJens Axboe 
237cf43e6beSJens Axboe /*
238cf43e6beSJens Axboe  * Enable stat tracking, return whether it was enabled
239cf43e6beSJens Axboe  */
240cf43e6beSJens Axboe bool blk_stat_enable(struct request_queue *q)
241cf43e6beSJens Axboe {
242cf43e6beSJens Axboe 	if (!test_bit(QUEUE_FLAG_STATS, &q->queue_flags)) {
243cf43e6beSJens Axboe 		set_bit(QUEUE_FLAG_STATS, &q->queue_flags);
244cf43e6beSJens Axboe 		return false;
245cf43e6beSJens Axboe 	}
246cf43e6beSJens Axboe 
247cf43e6beSJens Axboe 	return true;
248cf43e6beSJens Axboe }
249