xref: /kernel/linux/linux-6.6/block/blk-mq-sysfs.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/kernel.h>
362306a36Sopenharmony_ci#include <linux/module.h>
462306a36Sopenharmony_ci#include <linux/backing-dev.h>
562306a36Sopenharmony_ci#include <linux/bio.h>
662306a36Sopenharmony_ci#include <linux/blkdev.h>
762306a36Sopenharmony_ci#include <linux/mm.h>
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/workqueue.h>
1162306a36Sopenharmony_ci#include <linux/smp.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "blk.h"
1462306a36Sopenharmony_ci#include "blk-mq.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic void blk_mq_sysfs_release(struct kobject *kobj)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	struct blk_mq_ctxs *ctxs = container_of(kobj, struct blk_mq_ctxs, kobj);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	free_percpu(ctxs->queue_ctx);
2162306a36Sopenharmony_ci	kfree(ctxs);
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic void blk_mq_ctx_sysfs_release(struct kobject *kobj)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct blk_mq_ctx *ctx = container_of(kobj, struct blk_mq_ctx, kobj);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	/* ctx->ctxs won't be released until all ctx are freed */
2962306a36Sopenharmony_ci	kobject_put(&ctx->ctxs->kobj);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic void blk_mq_hw_sysfs_release(struct kobject *kobj)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct blk_mq_hw_ctx *hctx = container_of(kobj, struct blk_mq_hw_ctx,
3562306a36Sopenharmony_ci						  kobj);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	blk_free_flush_queue(hctx->fq);
3862306a36Sopenharmony_ci	sbitmap_free(&hctx->ctx_map);
3962306a36Sopenharmony_ci	free_cpumask_var(hctx->cpumask);
4062306a36Sopenharmony_ci	kfree(hctx->ctxs);
4162306a36Sopenharmony_ci	kfree(hctx);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct blk_mq_hw_ctx_sysfs_entry {
4562306a36Sopenharmony_ci	struct attribute attr;
4662306a36Sopenharmony_ci	ssize_t (*show)(struct blk_mq_hw_ctx *, char *);
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic ssize_t blk_mq_hw_sysfs_show(struct kobject *kobj,
5062306a36Sopenharmony_ci				    struct attribute *attr, char *page)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct blk_mq_hw_ctx_sysfs_entry *entry;
5362306a36Sopenharmony_ci	struct blk_mq_hw_ctx *hctx;
5462306a36Sopenharmony_ci	struct request_queue *q;
5562306a36Sopenharmony_ci	ssize_t res;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	entry = container_of(attr, struct blk_mq_hw_ctx_sysfs_entry, attr);
5862306a36Sopenharmony_ci	hctx = container_of(kobj, struct blk_mq_hw_ctx, kobj);
5962306a36Sopenharmony_ci	q = hctx->queue;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (!entry->show)
6262306a36Sopenharmony_ci		return -EIO;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	mutex_lock(&q->sysfs_lock);
6562306a36Sopenharmony_ci	res = entry->show(hctx, page);
6662306a36Sopenharmony_ci	mutex_unlock(&q->sysfs_lock);
6762306a36Sopenharmony_ci	return res;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic ssize_t blk_mq_hw_sysfs_nr_tags_show(struct blk_mq_hw_ctx *hctx,
7162306a36Sopenharmony_ci					    char *page)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	return sprintf(page, "%u\n", hctx->tags->nr_tags);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic ssize_t blk_mq_hw_sysfs_nr_reserved_tags_show(struct blk_mq_hw_ctx *hctx,
7762306a36Sopenharmony_ci						     char *page)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	return sprintf(page, "%u\n", hctx->tags->nr_reserved_tags);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic ssize_t blk_mq_hw_sysfs_cpus_show(struct blk_mq_hw_ctx *hctx, char *page)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	const size_t size = PAGE_SIZE - 1;
8562306a36Sopenharmony_ci	unsigned int i, first = 1;
8662306a36Sopenharmony_ci	int ret = 0, pos = 0;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	for_each_cpu(i, hctx->cpumask) {
8962306a36Sopenharmony_ci		if (first)
9062306a36Sopenharmony_ci			ret = snprintf(pos + page, size - pos, "%u", i);
9162306a36Sopenharmony_ci		else
9262306a36Sopenharmony_ci			ret = snprintf(pos + page, size - pos, ", %u", i);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		if (ret >= size - pos)
9562306a36Sopenharmony_ci			break;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		first = 0;
9862306a36Sopenharmony_ci		pos += ret;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	ret = snprintf(pos + page, size + 1 - pos, "\n");
10262306a36Sopenharmony_ci	return pos + ret;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_nr_tags = {
10662306a36Sopenharmony_ci	.attr = {.name = "nr_tags", .mode = 0444 },
10762306a36Sopenharmony_ci	.show = blk_mq_hw_sysfs_nr_tags_show,
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_cistatic struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_nr_reserved_tags = {
11062306a36Sopenharmony_ci	.attr = {.name = "nr_reserved_tags", .mode = 0444 },
11162306a36Sopenharmony_ci	.show = blk_mq_hw_sysfs_nr_reserved_tags_show,
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_cistatic struct blk_mq_hw_ctx_sysfs_entry blk_mq_hw_sysfs_cpus = {
11462306a36Sopenharmony_ci	.attr = {.name = "cpu_list", .mode = 0444 },
11562306a36Sopenharmony_ci	.show = blk_mq_hw_sysfs_cpus_show,
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic struct attribute *default_hw_ctx_attrs[] = {
11962306a36Sopenharmony_ci	&blk_mq_hw_sysfs_nr_tags.attr,
12062306a36Sopenharmony_ci	&blk_mq_hw_sysfs_nr_reserved_tags.attr,
12162306a36Sopenharmony_ci	&blk_mq_hw_sysfs_cpus.attr,
12262306a36Sopenharmony_ci	NULL,
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ciATTRIBUTE_GROUPS(default_hw_ctx);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic const struct sysfs_ops blk_mq_hw_sysfs_ops = {
12762306a36Sopenharmony_ci	.show	= blk_mq_hw_sysfs_show,
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic const struct kobj_type blk_mq_ktype = {
13162306a36Sopenharmony_ci	.release	= blk_mq_sysfs_release,
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic const struct kobj_type blk_mq_ctx_ktype = {
13562306a36Sopenharmony_ci	.release	= blk_mq_ctx_sysfs_release,
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic const struct kobj_type blk_mq_hw_ktype = {
13962306a36Sopenharmony_ci	.sysfs_ops	= &blk_mq_hw_sysfs_ops,
14062306a36Sopenharmony_ci	.default_groups = default_hw_ctx_groups,
14162306a36Sopenharmony_ci	.release	= blk_mq_hw_sysfs_release,
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic void blk_mq_unregister_hctx(struct blk_mq_hw_ctx *hctx)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct blk_mq_ctx *ctx;
14762306a36Sopenharmony_ci	int i;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (!hctx->nr_ctx)
15062306a36Sopenharmony_ci		return;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	hctx_for_each_ctx(hctx, ctx, i)
15362306a36Sopenharmony_ci		kobject_del(&ctx->kobj);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	kobject_del(&hctx->kobj);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic int blk_mq_register_hctx(struct blk_mq_hw_ctx *hctx)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct request_queue *q = hctx->queue;
16162306a36Sopenharmony_ci	struct blk_mq_ctx *ctx;
16262306a36Sopenharmony_ci	int i, j, ret;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (!hctx->nr_ctx)
16562306a36Sopenharmony_ci		return 0;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	ret = kobject_add(&hctx->kobj, q->mq_kobj, "%u", hctx->queue_num);
16862306a36Sopenharmony_ci	if (ret)
16962306a36Sopenharmony_ci		return ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	hctx_for_each_ctx(hctx, ctx, i) {
17262306a36Sopenharmony_ci		ret = kobject_add(&ctx->kobj, &hctx->kobj, "cpu%u", ctx->cpu);
17362306a36Sopenharmony_ci		if (ret)
17462306a36Sopenharmony_ci			goto out;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return 0;
17862306a36Sopenharmony_ciout:
17962306a36Sopenharmony_ci	hctx_for_each_ctx(hctx, ctx, j) {
18062306a36Sopenharmony_ci		if (j < i)
18162306a36Sopenharmony_ci			kobject_del(&ctx->kobj);
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci	kobject_del(&hctx->kobj);
18462306a36Sopenharmony_ci	return ret;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_civoid blk_mq_hctx_kobj_init(struct blk_mq_hw_ctx *hctx)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	kobject_init(&hctx->kobj, &blk_mq_hw_ktype);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_civoid blk_mq_sysfs_deinit(struct request_queue *q)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct blk_mq_ctx *ctx;
19562306a36Sopenharmony_ci	int cpu;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
19862306a36Sopenharmony_ci		ctx = per_cpu_ptr(q->queue_ctx, cpu);
19962306a36Sopenharmony_ci		kobject_put(&ctx->kobj);
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci	kobject_put(q->mq_kobj);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_civoid blk_mq_sysfs_init(struct request_queue *q)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct blk_mq_ctx *ctx;
20762306a36Sopenharmony_ci	int cpu;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	kobject_init(q->mq_kobj, &blk_mq_ktype);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
21262306a36Sopenharmony_ci		ctx = per_cpu_ptr(q->queue_ctx, cpu);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		kobject_get(q->mq_kobj);
21562306a36Sopenharmony_ci		kobject_init(&ctx->kobj, &blk_mq_ctx_ktype);
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ciint blk_mq_sysfs_register(struct gendisk *disk)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct request_queue *q = disk->queue;
22262306a36Sopenharmony_ci	struct blk_mq_hw_ctx *hctx;
22362306a36Sopenharmony_ci	unsigned long i, j;
22462306a36Sopenharmony_ci	int ret;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	lockdep_assert_held(&q->sysfs_dir_lock);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	ret = kobject_add(q->mq_kobj, &disk_to_dev(disk)->kobj, "mq");
22962306a36Sopenharmony_ci	if (ret < 0)
23062306a36Sopenharmony_ci		goto out;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	kobject_uevent(q->mq_kobj, KOBJ_ADD);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	queue_for_each_hw_ctx(q, hctx, i) {
23562306a36Sopenharmony_ci		ret = blk_mq_register_hctx(hctx);
23662306a36Sopenharmony_ci		if (ret)
23762306a36Sopenharmony_ci			goto unreg;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	q->mq_sysfs_init_done = true;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ciout:
24362306a36Sopenharmony_ci	return ret;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ciunreg:
24662306a36Sopenharmony_ci	queue_for_each_hw_ctx(q, hctx, j) {
24762306a36Sopenharmony_ci		if (j < i)
24862306a36Sopenharmony_ci			blk_mq_unregister_hctx(hctx);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	kobject_uevent(q->mq_kobj, KOBJ_REMOVE);
25262306a36Sopenharmony_ci	kobject_del(q->mq_kobj);
25362306a36Sopenharmony_ci	return ret;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_civoid blk_mq_sysfs_unregister(struct gendisk *disk)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct request_queue *q = disk->queue;
25962306a36Sopenharmony_ci	struct blk_mq_hw_ctx *hctx;
26062306a36Sopenharmony_ci	unsigned long i;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	lockdep_assert_held(&q->sysfs_dir_lock);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	queue_for_each_hw_ctx(q, hctx, i)
26562306a36Sopenharmony_ci		blk_mq_unregister_hctx(hctx);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	kobject_uevent(q->mq_kobj, KOBJ_REMOVE);
26862306a36Sopenharmony_ci	kobject_del(q->mq_kobj);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	q->mq_sysfs_init_done = false;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_civoid blk_mq_sysfs_unregister_hctxs(struct request_queue *q)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct blk_mq_hw_ctx *hctx;
27662306a36Sopenharmony_ci	unsigned long i;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	mutex_lock(&q->sysfs_dir_lock);
27962306a36Sopenharmony_ci	if (!q->mq_sysfs_init_done)
28062306a36Sopenharmony_ci		goto unlock;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	queue_for_each_hw_ctx(q, hctx, i)
28362306a36Sopenharmony_ci		blk_mq_unregister_hctx(hctx);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ciunlock:
28662306a36Sopenharmony_ci	mutex_unlock(&q->sysfs_dir_lock);
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ciint blk_mq_sysfs_register_hctxs(struct request_queue *q)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct blk_mq_hw_ctx *hctx;
29262306a36Sopenharmony_ci	unsigned long i;
29362306a36Sopenharmony_ci	int ret = 0;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	mutex_lock(&q->sysfs_dir_lock);
29662306a36Sopenharmony_ci	if (!q->mq_sysfs_init_done)
29762306a36Sopenharmony_ci		goto unlock;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	queue_for_each_hw_ctx(q, hctx, i) {
30062306a36Sopenharmony_ci		ret = blk_mq_register_hctx(hctx);
30162306a36Sopenharmony_ci		if (ret)
30262306a36Sopenharmony_ci			break;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ciunlock:
30662306a36Sopenharmony_ci	mutex_unlock(&q->sysfs_dir_lock);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return ret;
30962306a36Sopenharmony_ci}
310