xref: /linux/block/blk-mq-cpumap.c (revision cb9f145f638d7afa633632a9290d6ad06caeb8ee)
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