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