|
@@ -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]));
|