linux/block/blk-mq-sysfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#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                else
  92                        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