310 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/kernel.h>3#include <linux/module.h>4#include <linux/backing-dev.h>5#include <linux/bio.h>6#include <linux/blkdev.h>7#include <linux/mm.h>8#include <linux/init.h>9#include <linux/slab.h>10#include <linux/workqueue.h>11#include <linux/smp.h>12 13#include "blk.h"14#include "blk-mq.h"15 16static void blk_mq_sysfs_release(struct kobject *kobj)17{18 struct blk_mq_ctxs *ctxs = container_of(kobj, struct blk_mq_ctxs, kobj);19 20 free_percpu(ctxs->queue_ctx);21 kfree(ctxs);22}23 24static void blk_mq_ctx_sysfs_release(struct kobject *kobj)25{26 struct blk_mq_ctx *ctx = container_of(kobj, struct blk_mq_ctx, kobj);27 28 /* ctx->ctxs won't be released until all ctx are freed */29 kobject_put(&ctx->ctxs->kobj);30}31 32static void blk_mq_hw_sysfs_release(struct kobject *kobj)33{34 struct blk_mq_hw_ctx *hctx = container_of(kobj, struct blk_mq_hw_ctx,35 kobj);36 37 blk_free_flush_queue(hctx->fq);38 sbitmap_free(&hctx->ctx_map);39 free_cpumask_var(hctx->cpumask);40 kfree(hctx->ctxs);41 kfree(hctx);42}43 44struct blk_mq_hw_ctx_sysfs_entry {45 struct attribute attr;46 ssize_t (*show)(struct blk_mq_hw_ctx *, char *);47};48 49static ssize_t blk_mq_hw_sysfs_show(struct kobject *kobj,50 struct attribute *attr, char *page)51{52 struct blk_mq_hw_ctx_sysfs_entry *entry;53 struct blk_mq_hw_ctx *hctx;54 struct request_queue *q;55 ssize_t res;56 57 entry = container_of(attr, struct blk_mq_hw_ctx_sysfs_entry, attr);58 hctx = container_of(kobj, struct blk_mq_hw_ctx, kobj);59 q = hctx->queue;60 61 if (!entry->show)62 return -EIO;63 64 mutex_lock(&q->sysfs_lock);65 res = entry->show(hctx, page);66 mutex_unlock(&q->sysfs_lock);67 return res;68}69 70static ssize_t blk_mq_hw_sysfs_nr_tags_show(struct blk_mq_hw_ctx *hctx,71 char *page)72{73 return sprintf(page, "%u\n", hctx->tags->nr_tags);74}75 76static ssize_t blk_mq_hw_sysfs_nr_reserved_tags_show(struct blk_mq_hw_ctx *hctx,77 char *page)78{79 return sprintf(page, "%u\n", hctx->tags->nr_reserved_tags);80}81 82static ssize_t blk_mq_hw_sysfs_cpus_show(struct blk_mq_hw_ctx *hctx, char *page)83{84 const size_t size = PAGE_SIZE - 1;85 unsigned int i, first = 1;86 int ret = 0, pos = 0;87 88 for_each_cpu(i, hctx->cpumask) {89 if (first)90 ret = snprintf(pos + page, size - pos, "%u", i);91 else92 ret = snprintf(pos + page, size - pos, ", %u", i);93 94 if (ret >= size - pos)95 break;96 97 first = 0;98 pos += ret;99 }100 101 ret = snprintf(pos + page, size + 1 - pos, "\n");102 return pos + ret;103}104 105static struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_nr_tags = {106 .attr = {.name = "nr_tags", .mode = 0444 },107 .show = blk_mq_hw_sysfs_nr_tags_show,108};109static struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_nr_reserved_tags = {110 .attr = {.name = "nr_reserved_tags", .mode = 0444 },111 .show = blk_mq_hw_sysfs_nr_reserved_tags_show,112};113static struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_cpus = {114 .attr = {.name = "cpu_list", .mode = 0444 },115 .show = blk_mq_hw_sysfs_cpus_show,116};117 118static struct attribute *default_hw_ctx_attrs[] = {119 &blk_mq_hw_sysfs_nr_tags.attr,120 &blk_mq_hw_sysfs_nr_reserved_tags.attr,121 &blk_mq_hw_sysfs_cpus.attr,122 NULL,123};124ATTRIBUTE_GROUPS(default_hw_ctx);125 126static const struct sysfs_ops blk_mq_hw_sysfs_ops = {127 .show = blk_mq_hw_sysfs_show,128};129 130static const struct kobj_type blk_mq_ktype = {131 .release = blk_mq_sysfs_release,132};133 134static const struct kobj_type blk_mq_ctx_ktype = {135 .release = blk_mq_ctx_sysfs_release,136};137 138static const struct kobj_type blk_mq_hw_ktype = {139 .sysfs_ops = &blk_mq_hw_sysfs_ops,140 .default_groups = default_hw_ctx_groups,141 .release = blk_mq_hw_sysfs_release,142};143 144static void blk_mq_unregister_hctx(struct blk_mq_hw_ctx *hctx)145{146 struct blk_mq_ctx *ctx;147 int i;148 149 if (!hctx->nr_ctx)150 return;151 152 hctx_for_each_ctx(hctx, ctx, i)153 kobject_del(&ctx->kobj);154 155 kobject_del(&hctx->kobj);156}157 158static int blk_mq_register_hctx(struct blk_mq_hw_ctx *hctx)159{160 struct request_queue *q = hctx->queue;161 struct blk_mq_ctx *ctx;162 int i, j, ret;163 164 if (!hctx->nr_ctx)165 return 0;166 167 ret = kobject_add(&hctx->kobj, q->mq_kobj, "%u", hctx->queue_num);168 if (ret)169 return ret;170 171 hctx_for_each_ctx(hctx, ctx, i) {172 ret = kobject_add(&ctx->kobj, &hctx->kobj, "cpu%u", ctx->cpu);173 if (ret)174 goto out;175 }176 177 return 0;178out:179 hctx_for_each_ctx(hctx, ctx, j) {180 if (j < i)181 kobject_del(&ctx->kobj);182 }183 kobject_del(&hctx->kobj);184 return ret;185}186 187void blk_mq_hctx_kobj_init(struct blk_mq_hw_ctx *hctx)188{189 kobject_init(&hctx->kobj, &blk_mq_hw_ktype);190}191 192void blk_mq_sysfs_deinit(struct request_queue *q)193{194 struct blk_mq_ctx *ctx;195 int cpu;196 197 for_each_possible_cpu(cpu) {198 ctx = per_cpu_ptr(q->queue_ctx, cpu);199 kobject_put(&ctx->kobj);200 }201 kobject_put(q->mq_kobj);202}203 204void blk_mq_sysfs_init(struct request_queue *q)205{206 struct blk_mq_ctx *ctx;207 int cpu;208 209 kobject_init(q->mq_kobj, &blk_mq_ktype);210 211 for_each_possible_cpu(cpu) {212 ctx = per_cpu_ptr(q->queue_ctx, cpu);213 214 kobject_get(q->mq_kobj);215 kobject_init(&ctx->kobj, &blk_mq_ctx_ktype);216 }217}218 219int blk_mq_sysfs_register(struct gendisk *disk)220{221 struct request_queue *q = disk->queue;222 struct blk_mq_hw_ctx *hctx;223 unsigned long i, j;224 int ret;225 226 lockdep_assert_held(&q->sysfs_dir_lock);227 228 ret = kobject_add(q->mq_kobj, &disk_to_dev(disk)->kobj, "mq");229 if (ret < 0)230 goto out;231 232 kobject_uevent(q->mq_kobj, KOBJ_ADD);233 234 queue_for_each_hw_ctx(q, hctx, i) {235 ret = blk_mq_register_hctx(hctx);236 if (ret)237 goto unreg;238 }239 240 q->mq_sysfs_init_done = true;241 242out:243 return ret;244 245unreg:246 queue_for_each_hw_ctx(q, hctx, j) {247 if (j < i)248 blk_mq_unregister_hctx(hctx);249 }250 251 kobject_uevent(q->mq_kobj, KOBJ_REMOVE);252 kobject_del(q->mq_kobj);253 return ret;254}255 256void blk_mq_sysfs_unregister(struct gendisk *disk)257{258 struct request_queue *q = disk->queue;259 struct blk_mq_hw_ctx *hctx;260 unsigned long i;261 262 lockdep_assert_held(&q->sysfs_dir_lock);263 264 queue_for_each_hw_ctx(q, hctx, i)265 blk_mq_unregister_hctx(hctx);266 267 kobject_uevent(q->mq_kobj, KOBJ_REMOVE);268 kobject_del(q->mq_kobj);269 270 q->mq_sysfs_init_done = false;271}272 273void blk_mq_sysfs_unregister_hctxs(struct request_queue *q)274{275 struct blk_mq_hw_ctx *hctx;276 unsigned long i;277 278 mutex_lock(&q->sysfs_dir_lock);279 if (!q->mq_sysfs_init_done)280 goto unlock;281 282 queue_for_each_hw_ctx(q, hctx, i)283 blk_mq_unregister_hctx(hctx);284 285unlock:286 mutex_unlock(&q->sysfs_dir_lock);287}288 289int blk_mq_sysfs_register_hctxs(struct request_queue *q)290{291 struct blk_mq_hw_ctx *hctx;292 unsigned long i;293 int ret = 0;294 295 mutex_lock(&q->sysfs_dir_lock);296 if (!q->mq_sysfs_init_done)297 goto unlock;298 299 queue_for_each_hw_ctx(q, hctx, i) {300 ret = blk_mq_register_hctx(hctx);301 if (ret)302 break;303 }304 305unlock:306 mutex_unlock(&q->sysfs_dir_lock);307 308 return ret;309}310