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