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