128 lines · 3.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2016 Thomas Gleixner. * Copyright (C) 2016-2017 Christoph Hellwig. */#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/cpu.h>#include <linux/group_cpus.h> static void default_calc_sets(struct irq_affinity *affd, unsigned int affvecs){	affd->nr_sets = 1;	affd->set_size[0] = affvecs;} /** * irq_create_affinity_masks - Create affinity masks for multiqueue spreading * @nvecs:	The total number of vectors * @affd:	Description of the affinity requirements * * Returns the irq_affinity_desc pointer or NULL if allocation failed. */struct irq_affinity_desc *irq_create_affinity_masks(unsigned int nvecs, struct irq_affinity *affd){	unsigned int affvecs, curvec, usedvecs, i;	struct irq_affinity_desc *masks = NULL; 	/*	 * Determine the number of vectors which need interrupt affinities	 * assigned. If the pre/post request exhausts the available vectors	 * then nothing to do here except for invoking the calc_sets()	 * callback so the device driver can adjust to the situation.	 */	if (nvecs > affd->pre_vectors + affd->post_vectors)		affvecs = nvecs - affd->pre_vectors - affd->post_vectors;	else		affvecs = 0; 	/*	 * Simple invocations do not provide a calc_sets() callback. Install	 * the generic one.	 */	if (!affd->calc_sets)		affd->calc_sets = default_calc_sets; 	/* Recalculate the sets */	affd->calc_sets(affd, affvecs); 	if (WARN_ON_ONCE(affd->nr_sets > IRQ_AFFINITY_MAX_SETS))		return NULL; 	/* Nothing to assign? */	if (!affvecs)		return NULL; 	masks = kcalloc(nvecs, sizeof(*masks), GFP_KERNEL);	if (!masks)		return NULL; 	/* Fill out vectors at the beginning that don't need affinity */	for (curvec = 0; curvec < affd->pre_vectors; curvec++)		cpumask_copy(&masks[curvec].mask, irq_default_affinity); 	/*	 * Spread on present CPUs starting from affd->pre_vectors. If we	 * 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); 		if (!result) {			kfree(masks);			return NULL;		} 		for (j = 0; j < this_vecs; j++)			cpumask_copy(&masks[curvec + j].mask, &result[j]);		kfree(result); 		curvec += this_vecs;		usedvecs += this_vecs;	} 	/* Fill out vectors at the end that don't need affinity */	if (usedvecs >= affvecs)		curvec = affd->pre_vectors + affvecs;	else		curvec = affd->pre_vectors + usedvecs;	for (; curvec < nvecs; curvec++)		cpumask_copy(&masks[curvec].mask, irq_default_affinity); 	/* Mark the managed interrupts */	for (i = affd->pre_vectors; i < nvecs - affd->post_vectors; i++)		masks[i].is_managed = 1; 	return masks;} /** * irq_calc_affinity_vectors - Calculate the optimal number of vectors * @minvec:	The minimum number of vectors available * @maxvec:	The maximum number of vectors available * @affd:	Description of the affinity requirements */unsigned int irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec,				       const struct irq_affinity *affd){	unsigned int resv = affd->pre_vectors + affd->post_vectors;	unsigned int set_vecs; 	if (resv > minvec)		return 0; 	if (affd->calc_sets) {		set_vecs = maxvec - resv;	} else {		cpus_read_lock();		set_vecs = cpumask_weight(cpu_possible_mask);		cpus_read_unlock();	} 	return resv + min(set_vecs, maxvec - resv);}