lib/group_cpus: Let group_cpu_evenly() return the number of initialized masks

group_cpu_evenly() might have allocated less groups then requested:

group_cpu_evenly()
  __group_cpus_evenly()
    alloc_nodes_groups()
      # allocated total groups may be less than numgrps when
      # active total CPU number is less then numgrps

In this case, the caller will do an out of bound access because the
caller assumes the masks returned has numgrps.

Return the number of groups created so the caller can limit the access
range accordingly.

Acked-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Daniel Wagner <wagi@kernel.org>
Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/r/20250617-isolcpus-queue-counters-v1-1-13923686b54b@kernel.org
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Daniel Wagner 2025-06-17 15:43:23 +02:00 committed by Jens Axboe
parent 857f431824
commit b6139a6abf
6 changed files with 25 additions and 25 deletions

View file

@ -19,9 +19,9 @@
void blk_mq_map_queues(struct blk_mq_queue_map *qmap)
{
const struct cpumask *masks;
unsigned int queue, cpu;
unsigned int queue, cpu, nr_masks;
masks = group_cpus_evenly(qmap->nr_queues);
masks = group_cpus_evenly(qmap->nr_queues, &nr_masks);
if (!masks) {
for_each_possible_cpu(cpu)
qmap->mq_map[cpu] = qmap->queue_offset;
@ -29,7 +29,7 @@ void blk_mq_map_queues(struct blk_mq_queue_map *qmap)
}
for (queue = 0; queue < qmap->nr_queues; queue++) {
for_each_cpu(cpu, &masks[queue])
for_each_cpu(cpu, &masks[queue % nr_masks])
qmap->mq_map[cpu] = qmap->queue_offset + queue;
}
kfree(masks);

View file

@ -329,20 +329,21 @@ create_affinity_masks(unsigned int nvecs, struct irq_affinity *affd)
for (i = 0, usedvecs = 0; i < affd->nr_sets; i++) {
unsigned int this_vecs = affd->set_size[i];
unsigned int nr_masks;
int j;
struct cpumask *result = group_cpus_evenly(this_vecs);
struct cpumask *result = group_cpus_evenly(this_vecs, &nr_masks);
if (!result) {
kfree(masks);
return NULL;
}
for (j = 0; j < this_vecs; j++)
for (j = 0; j < nr_masks; j++)
cpumask_copy(&masks[curvec + j], &result[j]);
kfree(result);
curvec += this_vecs;
usedvecs += this_vecs;
curvec += nr_masks;
usedvecs += nr_masks;
}
/* Fill out vectors at the end that don't need affinity */

View file

@ -862,7 +862,7 @@ static void virtio_fs_requests_done_work(struct work_struct *work)
static void virtio_fs_map_queues(struct virtio_device *vdev, struct virtio_fs *fs)
{
const struct cpumask *mask, *masks;
unsigned int q, cpu;
unsigned int q, cpu, nr_masks;
/* First attempt to map using existing transport layer affinities
* e.g. PCIe MSI-X
@ -882,7 +882,7 @@ static void virtio_fs_map_queues(struct virtio_device *vdev, struct virtio_fs *f
return;
fallback:
/* Attempt to map evenly in groups over the CPUs */
masks = group_cpus_evenly(fs->num_request_queues);
masks = group_cpus_evenly(fs->num_request_queues, &nr_masks);
/* If even this fails we default to all CPUs use first request queue */
if (!masks) {
for_each_possible_cpu(cpu)
@ -891,7 +891,7 @@ fallback:
}
for (q = 0; q < fs->num_request_queues; q++) {
for_each_cpu(cpu, &masks[q])
for_each_cpu(cpu, &masks[q % nr_masks])
fs->mq_map[cpu] = q + VQ_REQUEST;
}
kfree(masks);

View file

@ -9,6 +9,6 @@
#include <linux/kernel.h>
#include <linux/cpu.h>
struct cpumask *group_cpus_evenly(unsigned int numgrps);
struct cpumask *group_cpus_evenly(unsigned int numgrps, unsigned int *nummasks);
#endif

View file

@ -69,21 +69,20 @@ irq_create_affinity_masks(unsigned int nvecs, struct irq_affinity *affd)
* have multiple sets, build each sets affinity mask separately.
*/
for (i = 0, usedvecs = 0; i < affd->nr_sets; i++) {
unsigned int this_vecs = affd->set_size[i];
int j;
struct cpumask *result = group_cpus_evenly(this_vecs);
unsigned int nr_masks, this_vecs = affd->set_size[i];
struct cpumask *result = group_cpus_evenly(this_vecs, &nr_masks);
if (!result) {
kfree(masks);
return NULL;
}
for (j = 0; j < this_vecs; j++)
for (int j = 0; j < nr_masks; j++)
cpumask_copy(&masks[curvec + j].mask, &result[j]);
kfree(result);
curvec += this_vecs;
usedvecs += this_vecs;
curvec += nr_masks;
usedvecs += nr_masks;
}
/* Fill out vectors at the end that don't need affinity */

View file

@ -332,9 +332,11 @@ static int __group_cpus_evenly(unsigned int startgrp, unsigned int numgrps,
/**
* group_cpus_evenly - Group all CPUs evenly per NUMA/CPU locality
* @numgrps: number of groups
* @nummasks: number of initialized cpumasks
*
* Return: cpumask array if successful, NULL otherwise. And each element
* includes CPUs assigned to this group
* includes CPUs assigned to this group. nummasks contains the number
* of initialized masks which can be less than numgrps.
*
* Try to put close CPUs from viewpoint of CPU and NUMA locality into
* same group, and run two-stage grouping:
@ -344,7 +346,7 @@ static int __group_cpus_evenly(unsigned int startgrp, unsigned int numgrps,
* We guarantee in the resulted grouping that all CPUs are covered, and
* no same CPU is assigned to multiple groups
*/
struct cpumask *group_cpus_evenly(unsigned int numgrps)
struct cpumask *group_cpus_evenly(unsigned int numgrps, unsigned int *nummasks)
{
unsigned int curgrp = 0, nr_present = 0, nr_others = 0;
cpumask_var_t *node_to_cpumask;
@ -389,7 +391,7 @@ struct cpumask *group_cpus_evenly(unsigned int numgrps)
ret = __group_cpus_evenly(curgrp, numgrps, node_to_cpumask,
npresmsk, nmsk, masks);
if (ret < 0)
goto fail_build_affinity;
goto fail_node_to_cpumask;
nr_present = ret;
/*
@ -408,10 +410,6 @@ struct cpumask *group_cpus_evenly(unsigned int numgrps)
if (ret >= 0)
nr_others = ret;
fail_build_affinity:
if (ret >= 0)
WARN_ON(nr_present + nr_others < numgrps);
fail_node_to_cpumask:
free_node_to_cpumask(node_to_cpumask);
@ -424,10 +422,11 @@ struct cpumask *group_cpus_evenly(unsigned int numgrps)
kfree(masks);
return NULL;
}
*nummasks = min(nr_present + nr_others, numgrps);
return masks;
}
#else /* CONFIG_SMP */
struct cpumask *group_cpus_evenly(unsigned int numgrps)
struct cpumask *group_cpus_evenly(unsigned int numgrps, unsigned int *nummasks)
{
struct cpumask *masks;
@ -440,6 +439,7 @@ struct cpumask *group_cpus_evenly(unsigned int numgrps)
/* assign all CPUs(cpu 0) to the 1st group only */
cpumask_copy(&masks[0], cpu_possible_mask);
*nummasks = 1;
return masks;
}
#endif /* CONFIG_SMP */