diff options
Diffstat (limited to 'arch/x86/kernel/apic/vector.c')
| -rw-r--r-- | arch/x86/kernel/apic/vector.c | 25 | 
1 files changed, 22 insertions, 3 deletions
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c index 3cc471beb50b..bb6f7a2148d7 100644 --- a/arch/x86/kernel/apic/vector.c +++ b/arch/x86/kernel/apic/vector.c @@ -134,21 +134,40 @@ static void apic_update_vector(struct irq_data *irqd, unsigned int newvec,  {  	struct apic_chip_data *apicd = apic_chip_data(irqd);  	struct irq_desc *desc = irq_data_to_desc(irqd); +	bool managed = irqd_affinity_is_managed(irqd);  	lockdep_assert_held(&vector_lock);  	trace_vector_update(irqd->irq, newvec, newcpu, apicd->vector,  			    apicd->cpu); -	/* Setup the vector move, if required  */ -	if (apicd->vector && cpu_online(apicd->cpu)) { +	/* +	 * If there is no vector associated or if the associated vector is +	 * the shutdown vector, which is associated to make PCI/MSI +	 * shutdown mode work, then there is nothing to release. Clear out +	 * prev_vector for this and the offlined target case. +	 */ +	apicd->prev_vector = 0; +	if (!apicd->vector || apicd->vector == MANAGED_IRQ_SHUTDOWN_VECTOR) +		goto setnew; +	/* +	 * If the target CPU of the previous vector is online, then mark +	 * the vector as move in progress and store it for cleanup when the +	 * first interrupt on the new vector arrives. If the target CPU is +	 * offline then the regular release mechanism via the cleanup +	 * vector is not possible and the vector can be immediately freed +	 * in the underlying matrix allocator. +	 */ +	if (cpu_online(apicd->cpu)) {  		apicd->move_in_progress = true;  		apicd->prev_vector = apicd->vector;  		apicd->prev_cpu = apicd->cpu;  	} else { -		apicd->prev_vector = 0; +		irq_matrix_free(vector_matrix, apicd->cpu, apicd->vector, +				managed);  	} +setnew:  	apicd->vector = newvec;  	apicd->cpu = newcpu;  	BUG_ON(!IS_ERR_OR_NULL(per_cpu(vector_irq, newcpu)[newvec]));  |