13dcf60bcSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0
275bb4625SJens Axboe /*
375bb4625SJens Axboe * CPU <-> hardware queue mapping helpers
475bb4625SJens Axboe *
575bb4625SJens Axboe * Copyright (C) 2013-2014 Jens Axboe
675bb4625SJens Axboe */
7320ae51fSJens Axboe #include <linux/kernel.h>
8320ae51fSJens Axboe #include <linux/threads.h>
9320ae51fSJens Axboe #include <linux/module.h>
10320ae51fSJens Axboe #include <linux/mm.h>
11320ae51fSJens Axboe #include <linux/smp.h>
12320ae51fSJens Axboe #include <linux/cpu.h>
136a6dcae8SMing Lei #include <linux/group_cpus.h>
141452e9b4SDaniel Wagner #include <linux/device/bus.h>
15*3f27c1deSDaniel Wagner #include <linux/sched/isolation.h>
16320ae51fSJens Axboe
17320ae51fSJens Axboe #include "blk.h"
18320ae51fSJens Axboe #include "blk-mq.h"
19320ae51fSJens Axboe
blk_mq_num_queues(const struct cpumask * mask,unsigned int max_queues)20*3f27c1deSDaniel Wagner static unsigned int blk_mq_num_queues(const struct cpumask *mask,
21*3f27c1deSDaniel Wagner unsigned int max_queues)
22*3f27c1deSDaniel Wagner {
23*3f27c1deSDaniel Wagner unsigned int num;
24*3f27c1deSDaniel Wagner
25*3f27c1deSDaniel Wagner num = cpumask_weight(mask);
26*3f27c1deSDaniel Wagner return min_not_zero(num, max_queues);
27*3f27c1deSDaniel Wagner }
28*3f27c1deSDaniel Wagner
29*3f27c1deSDaniel Wagner /**
30*3f27c1deSDaniel Wagner * blk_mq_num_possible_queues - Calc nr of queues for multiqueue devices
31*3f27c1deSDaniel Wagner * @max_queues: The maximum number of queues the hardware/driver
32*3f27c1deSDaniel Wagner * supports. If max_queues is 0, the argument is
33*3f27c1deSDaniel Wagner * ignored.
34*3f27c1deSDaniel Wagner *
35*3f27c1deSDaniel Wagner * Calculates the number of queues to be used for a multiqueue
36*3f27c1deSDaniel Wagner * device based on the number of possible CPUs.
37*3f27c1deSDaniel Wagner */
blk_mq_num_possible_queues(unsigned int max_queues)38*3f27c1deSDaniel Wagner unsigned int blk_mq_num_possible_queues(unsigned int max_queues)
39*3f27c1deSDaniel Wagner {
40*3f27c1deSDaniel Wagner return blk_mq_num_queues(cpu_possible_mask, max_queues);
41*3f27c1deSDaniel Wagner }
42*3f27c1deSDaniel Wagner EXPORT_SYMBOL_GPL(blk_mq_num_possible_queues);
43*3f27c1deSDaniel Wagner
44*3f27c1deSDaniel Wagner /**
45*3f27c1deSDaniel Wagner * blk_mq_num_online_queues - Calc nr of queues for multiqueue devices
46*3f27c1deSDaniel Wagner * @max_queues: The maximum number of queues the hardware/driver
47*3f27c1deSDaniel Wagner * supports. If max_queues is 0, the argument is
48*3f27c1deSDaniel Wagner * ignored.
49*3f27c1deSDaniel Wagner *
50*3f27c1deSDaniel Wagner * Calculates the number of queues to be used for a multiqueue
51*3f27c1deSDaniel Wagner * device based on the number of online CPUs.
52*3f27c1deSDaniel Wagner */
blk_mq_num_online_queues(unsigned int max_queues)53*3f27c1deSDaniel Wagner unsigned int blk_mq_num_online_queues(unsigned int max_queues)
54*3f27c1deSDaniel Wagner {
55*3f27c1deSDaniel Wagner return blk_mq_num_queues(cpu_online_mask, max_queues);
56*3f27c1deSDaniel Wagner }
57*3f27c1deSDaniel Wagner EXPORT_SYMBOL_GPL(blk_mq_num_online_queues);
58*3f27c1deSDaniel Wagner
blk_mq_map_queues(struct blk_mq_queue_map * qmap)59a4e1d0b7SBart Van Assche void blk_mq_map_queues(struct blk_mq_queue_map *qmap)
60320ae51fSJens Axboe {
616a6dcae8SMing Lei const struct cpumask *masks;
62b6139a6aSDaniel Wagner unsigned int queue, cpu, nr_masks;
63556f36e9SMing Lei
64b6139a6aSDaniel Wagner masks = group_cpus_evenly(qmap->nr_queues, &nr_masks);
656a6dcae8SMing Lei if (!masks) {
66556f36e9SMing Lei for_each_possible_cpu(cpu)
676a6dcae8SMing Lei qmap->mq_map[cpu] = qmap->queue_offset;
686a6dcae8SMing Lei return;
69556f36e9SMing Lei }
70320ae51fSJens Axboe
716a6dcae8SMing Lei for (queue = 0; queue < qmap->nr_queues; queue++) {
72b6139a6aSDaniel Wagner for_each_cpu(cpu, &masks[queue % nr_masks])
736a6dcae8SMing Lei qmap->mq_map[cpu] = qmap->queue_offset + queue;
74fe631457SMax Gurtovoy }
756a6dcae8SMing Lei kfree(masks);
76320ae51fSJens Axboe }
779e5a7e22SChristoph Hellwig EXPORT_SYMBOL_GPL(blk_mq_map_queues);
78320ae51fSJens Axboe
79cd669f88SBart Van Assche /**
80cd669f88SBart Van Assche * blk_mq_hw_queue_to_node - Look up the memory node for a hardware queue index
81cd669f88SBart Van Assche * @qmap: CPU to hardware queue map.
82cd669f88SBart Van Assche * @index: hardware queue index.
83cd669f88SBart Van Assche *
84f14bbe77SJens Axboe * We have no quick way of doing reverse lookups. This is only used at
85f14bbe77SJens Axboe * queue init time, so runtime isn't important.
86f14bbe77SJens Axboe */
blk_mq_hw_queue_to_node(struct blk_mq_queue_map * qmap,unsigned int index)87ed76e329SJens Axboe int blk_mq_hw_queue_to_node(struct blk_mq_queue_map *qmap, unsigned int index)
88f14bbe77SJens Axboe {
89f14bbe77SJens Axboe int i;
90f14bbe77SJens Axboe
91f14bbe77SJens Axboe for_each_possible_cpu(i) {
92ed76e329SJens Axboe if (index == qmap->mq_map[i])
93576e85c5SXianting Tian return cpu_to_node(i);
94f14bbe77SJens Axboe }
95f14bbe77SJens Axboe
96f14bbe77SJens Axboe return NUMA_NO_NODE;
97f14bbe77SJens Axboe }
981452e9b4SDaniel Wagner
991452e9b4SDaniel Wagner /**
1001452e9b4SDaniel Wagner * blk_mq_map_hw_queues - Create CPU to hardware queue mapping
1011452e9b4SDaniel Wagner * @qmap: CPU to hardware queue map
1021452e9b4SDaniel Wagner * @dev: The device to map queues
1031452e9b4SDaniel Wagner * @offset: Queue offset to use for the device
1041452e9b4SDaniel Wagner *
1051452e9b4SDaniel Wagner * Create a CPU to hardware queue mapping in @qmap. The struct bus_type
1061452e9b4SDaniel Wagner * irq_get_affinity callback will be used to retrieve the affinity.
1071452e9b4SDaniel Wagner */
blk_mq_map_hw_queues(struct blk_mq_queue_map * qmap,struct device * dev,unsigned int offset)1081452e9b4SDaniel Wagner void blk_mq_map_hw_queues(struct blk_mq_queue_map *qmap,
1091452e9b4SDaniel Wagner struct device *dev, unsigned int offset)
1101452e9b4SDaniel Wagner
1111452e9b4SDaniel Wagner {
1121452e9b4SDaniel Wagner const struct cpumask *mask;
1131452e9b4SDaniel Wagner unsigned int queue, cpu;
1141452e9b4SDaniel Wagner
1151452e9b4SDaniel Wagner if (!dev->bus->irq_get_affinity)
1161452e9b4SDaniel Wagner goto fallback;
1171452e9b4SDaniel Wagner
1181452e9b4SDaniel Wagner for (queue = 0; queue < qmap->nr_queues; queue++) {
1191452e9b4SDaniel Wagner mask = dev->bus->irq_get_affinity(dev, queue + offset);
1201452e9b4SDaniel Wagner if (!mask)
1211452e9b4SDaniel Wagner goto fallback;
1221452e9b4SDaniel Wagner
1231452e9b4SDaniel Wagner for_each_cpu(cpu, mask)
1241452e9b4SDaniel Wagner qmap->mq_map[cpu] = qmap->queue_offset + queue;
1251452e9b4SDaniel Wagner }
1261452e9b4SDaniel Wagner
1271452e9b4SDaniel Wagner return;
1281452e9b4SDaniel Wagner
1291452e9b4SDaniel Wagner fallback:
130a9ae6fe1SDaniel Wagner blk_mq_map_queues(qmap);
1311452e9b4SDaniel Wagner }
1321452e9b4SDaniel Wagner EXPORT_SYMBOL_GPL(blk_mq_map_hw_queues);
133