Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 233db2cb14db8b1935dda52a6affd97276462b82 228 lines 5.2 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Block stat tracking code 4 * 5 * Copyright (C) 2016 Jens Axboe 6 */ 7#include <linux/kernel.h> 8#include <linux/rculist.h> 9 10#include "blk-stat.h" 11#include "blk-mq.h" 12#include "blk.h" 13 14struct blk_queue_stats { 15 struct list_head callbacks; 16 spinlock_t lock; 17 int accounting; 18}; 19 20void blk_rq_stat_init(struct blk_rq_stat *stat) 21{ 22 stat->min = -1ULL; 23 stat->max = stat->nr_samples = stat->mean = 0; 24 stat->batch = 0; 25} 26 27/* src is a per-cpu stat, mean isn't initialized */ 28void blk_rq_stat_sum(struct blk_rq_stat *dst, struct blk_rq_stat *src) 29{ 30 if (dst->nr_samples + src->nr_samples <= dst->nr_samples) 31 return; 32 33 dst->min = min(dst->min, src->min); 34 dst->max = max(dst->max, src->max); 35 36 dst->mean = div_u64(src->batch + dst->mean * dst->nr_samples, 37 dst->nr_samples + src->nr_samples); 38 39 dst->nr_samples += src->nr_samples; 40} 41 42void blk_rq_stat_add(struct blk_rq_stat *stat, u64 value) 43{ 44 stat->min = min(stat->min, value); 45 stat->max = max(stat->max, value); 46 stat->batch += value; 47 stat->nr_samples++; 48} 49 50void blk_stat_add(struct request *rq, u64 now) 51{ 52 struct request_queue *q = rq->q; 53 struct blk_stat_callback *cb; 54 struct blk_rq_stat *stat; 55 int bucket, cpu; 56 u64 value; 57 58 value = (now >= rq->io_start_time_ns) ? now - rq->io_start_time_ns : 0; 59 60 rcu_read_lock(); 61 cpu = get_cpu(); 62 list_for_each_entry_rcu(cb, &q->stats->callbacks, list) { 63 if (!blk_stat_is_active(cb)) 64 continue; 65 66 bucket = cb->bucket_fn(rq); 67 if (bucket < 0) 68 continue; 69 70 stat = &per_cpu_ptr(cb->cpu_stat, cpu)[bucket]; 71 blk_rq_stat_add(stat, value); 72 } 73 put_cpu(); 74 rcu_read_unlock(); 75} 76 77static void blk_stat_timer_fn(struct timer_list *t) 78{ 79 struct blk_stat_callback *cb = timer_container_of(cb, t, timer); 80 unsigned int bucket; 81 int cpu; 82 83 for (bucket = 0; bucket < cb->buckets; bucket++) 84 blk_rq_stat_init(&cb->stat[bucket]); 85 86 for_each_online_cpu(cpu) { 87 struct blk_rq_stat *cpu_stat; 88 89 cpu_stat = per_cpu_ptr(cb->cpu_stat, cpu); 90 for (bucket = 0; bucket < cb->buckets; bucket++) { 91 blk_rq_stat_sum(&cb->stat[bucket], &cpu_stat[bucket]); 92 blk_rq_stat_init(&cpu_stat[bucket]); 93 } 94 } 95 96 cb->timer_fn(cb); 97} 98 99struct blk_stat_callback * 100blk_stat_alloc_callback(void (*timer_fn)(struct blk_stat_callback *), 101 int (*bucket_fn)(const struct request *), 102 unsigned int buckets, void *data) 103{ 104 struct blk_stat_callback *cb; 105 106 cb = kmalloc_obj(*cb); 107 if (!cb) 108 return NULL; 109 110 cb->stat = kmalloc_objs(struct blk_rq_stat, buckets); 111 if (!cb->stat) { 112 kfree(cb); 113 return NULL; 114 } 115 cb->cpu_stat = __alloc_percpu(buckets * sizeof(struct blk_rq_stat), 116 __alignof__(struct blk_rq_stat)); 117 if (!cb->cpu_stat) { 118 kfree(cb->stat); 119 kfree(cb); 120 return NULL; 121 } 122 123 cb->timer_fn = timer_fn; 124 cb->bucket_fn = bucket_fn; 125 cb->data = data; 126 cb->buckets = buckets; 127 timer_setup(&cb->timer, blk_stat_timer_fn, 0); 128 129 return cb; 130} 131 132void blk_stat_add_callback(struct request_queue *q, 133 struct blk_stat_callback *cb) 134{ 135 unsigned int bucket; 136 unsigned long flags; 137 int cpu; 138 139 for_each_possible_cpu(cpu) { 140 struct blk_rq_stat *cpu_stat; 141 142 cpu_stat = per_cpu_ptr(cb->cpu_stat, cpu); 143 for (bucket = 0; bucket < cb->buckets; bucket++) 144 blk_rq_stat_init(&cpu_stat[bucket]); 145 } 146 147 spin_lock_irqsave(&q->stats->lock, flags); 148 list_add_tail_rcu(&cb->list, &q->stats->callbacks); 149 blk_queue_flag_set(QUEUE_FLAG_STATS, q); 150 spin_unlock_irqrestore(&q->stats->lock, flags); 151} 152 153void blk_stat_remove_callback(struct request_queue *q, 154 struct blk_stat_callback *cb) 155{ 156 unsigned long flags; 157 158 spin_lock_irqsave(&q->stats->lock, flags); 159 list_del_rcu(&cb->list); 160 if (list_empty(&q->stats->callbacks) && !q->stats->accounting) 161 blk_queue_flag_clear(QUEUE_FLAG_STATS, q); 162 spin_unlock_irqrestore(&q->stats->lock, flags); 163 164 timer_delete_sync(&cb->timer); 165} 166 167static void blk_stat_free_callback_rcu(struct rcu_head *head) 168{ 169 struct blk_stat_callback *cb; 170 171 cb = container_of(head, struct blk_stat_callback, rcu); 172 free_percpu(cb->cpu_stat); 173 kfree(cb->stat); 174 kfree(cb); 175} 176 177void blk_stat_free_callback(struct blk_stat_callback *cb) 178{ 179 if (cb) 180 call_rcu(&cb->rcu, blk_stat_free_callback_rcu); 181} 182 183void blk_stat_disable_accounting(struct request_queue *q) 184{ 185 unsigned long flags; 186 187 spin_lock_irqsave(&q->stats->lock, flags); 188 if (!--q->stats->accounting && list_empty(&q->stats->callbacks)) 189 blk_queue_flag_clear(QUEUE_FLAG_STATS, q); 190 spin_unlock_irqrestore(&q->stats->lock, flags); 191} 192EXPORT_SYMBOL_GPL(blk_stat_disable_accounting); 193 194void blk_stat_enable_accounting(struct request_queue *q) 195{ 196 unsigned long flags; 197 198 spin_lock_irqsave(&q->stats->lock, flags); 199 if (!q->stats->accounting++ && list_empty(&q->stats->callbacks)) 200 blk_queue_flag_set(QUEUE_FLAG_STATS, q); 201 spin_unlock_irqrestore(&q->stats->lock, flags); 202} 203EXPORT_SYMBOL_GPL(blk_stat_enable_accounting); 204 205struct blk_queue_stats *blk_alloc_queue_stats(void) 206{ 207 struct blk_queue_stats *stats; 208 209 stats = kmalloc_obj(*stats); 210 if (!stats) 211 return NULL; 212 213 INIT_LIST_HEAD(&stats->callbacks); 214 spin_lock_init(&stats->lock); 215 stats->accounting = 0; 216 217 return stats; 218} 219 220void blk_free_queue_stats(struct blk_queue_stats *stats) 221{ 222 if (!stats) 223 return; 224 225 WARN_ON(!list_empty(&stats->callbacks)); 226 227 kfree(stats); 228}