فهرست منبع

powerpc/ps3: Fix lost SMP IPIs

Fixes the PS3 bootup hang introduced in 3.0-rc1 by:

  commit 317f394160e9beb97d19a84c39b7e5eb3d7815a
  sched: Move the second half of ttwu() to the remote cpu

Move the PS3's LV1 EOI call lv1_end_of_interrupt_ext() from ps3_chip_eoi()
to ps3_get_irq() for IPI messages.

If lv1_send_event_locally() is called between a previous call to
lv1_send_event_locally() and the coresponding call to
lv1_end_of_interrupt_ext() the second event will not be delivered to the
target cpu.

The PS3's SMP IPIs are implemented using lv1_send_event_locally(), so if two
IPI messages of the same type are sent to the same target in a relatively
short period of time the second IPI event can become lost when
lv1_end_of_interrupt_ext() is called from ps3_chip_eoi().

CC: stable@kernel.org
Signed-off-by: Geoff Levand <geoff@infradead.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Geoff Levand 13 سال پیش
والد
کامیت
72f3bea075
3فایلهای تغییر یافته به همراه25 افزوده شده و 1 حذف شده
  1. 22 1
      arch/powerpc/platforms/ps3/interrupt.c
  2. 1 0
      arch/powerpc/platforms/ps3/platform.h
  3. 2 0
      arch/powerpc/platforms/ps3/smp.c

+ 22 - 1
arch/powerpc/platforms/ps3/interrupt.c

@@ -88,6 +88,7 @@ struct ps3_private {
 	struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN)));
 	struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN)));
 	u64 ppe_id;
 	u64 ppe_id;
 	u64 thread_id;
 	u64 thread_id;
+	unsigned long ipi_mask;
 };
 };
 
 
 static DEFINE_PER_CPU(struct ps3_private, ps3_private);
 static DEFINE_PER_CPU(struct ps3_private, ps3_private);
@@ -144,7 +145,11 @@ static void ps3_chip_unmask(struct irq_data *d)
 static void ps3_chip_eoi(struct irq_data *d)
 static void ps3_chip_eoi(struct irq_data *d)
 {
 {
 	const struct ps3_private *pd = irq_data_get_irq_chip_data(d);
 	const struct ps3_private *pd = irq_data_get_irq_chip_data(d);
-	lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, d->irq);
+
+	/* non-IPIs are EOIed here. */
+
+	if (!test_bit(63 - d->irq, &pd->ipi_mask))
+		lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, d->irq);
 }
 }
 
 
 /**
 /**
@@ -691,6 +696,16 @@ void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
 		cpu, virq, pd->bmp.ipi_debug_brk_mask);
 		cpu, virq, pd->bmp.ipi_debug_brk_mask);
 }
 }
 
 
+void __init ps3_register_ipi_irq(unsigned int cpu, unsigned int virq)
+{
+	struct ps3_private *pd = &per_cpu(ps3_private, cpu);
+
+	set_bit(63 - virq, &pd->ipi_mask);
+
+	DBG("%s:%d: cpu %u, virq %u, ipi_mask %lxh\n", __func__, __LINE__,
+		cpu, virq, pd->ipi_mask);
+}
+
 static unsigned int ps3_get_irq(void)
 static unsigned int ps3_get_irq(void)
 {
 {
 	struct ps3_private *pd = &__get_cpu_var(ps3_private);
 	struct ps3_private *pd = &__get_cpu_var(ps3_private);
@@ -720,6 +735,12 @@ static unsigned int ps3_get_irq(void)
 		BUG();
 		BUG();
 	}
 	}
 #endif
 #endif
+
+	/* IPIs are EOIed here. */
+
+	if (test_bit(63 - plug, &pd->ipi_mask))
+		lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, plug);
+
 	return plug;
 	return plug;
 }
 }
 
 

+ 1 - 0
arch/powerpc/platforms/ps3/platform.h

@@ -43,6 +43,7 @@ void ps3_mm_shutdown(void);
 void ps3_init_IRQ(void);
 void ps3_init_IRQ(void);
 void ps3_shutdown_IRQ(int cpu);
 void ps3_shutdown_IRQ(int cpu);
 void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq);
 void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq);
+void __init ps3_register_ipi_irq(unsigned int cpu, unsigned int virq);
 
 
 /* smp */
 /* smp */
 
 

+ 2 - 0
arch/powerpc/platforms/ps3/smp.c

@@ -94,6 +94,8 @@ static void __init ps3_smp_setup_cpu(int cpu)
 
 
 		if (result)
 		if (result)
 			virqs[i] = NO_IRQ;
 			virqs[i] = NO_IRQ;
+		else
+			ps3_register_ipi_irq(cpu, virqs[i]);
 	}
 	}
 
 
 	ps3_register_ipi_debug_brk(cpu, virqs[PPC_MSG_DEBUGGER_BREAK]);
 	ps3_register_ipi_debug_brk(cpu, virqs[PPC_MSG_DEBUGGER_BREAK]);