Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d911aed8 authored by James Bottomley's avatar James Bottomley Committed by Kyle McMartin
Browse files

[PARISC] Fix our interrupts not to use smp_call_function



Fix our interrupts not to use smp_call_function

On K and D class smp, the generic code calls this under an irq
spinlock, which causes the WARN_ON() message in smp_call_function()
(and is also illegal because it could deadlock).

The fix is to use a new scheme based on the IPI_NOP.

Signed-off-by: default avatarJames Bottomley <jejb@parisc-linux.org>
Signed-off-by: default avatarKyle McMartin <kyle@parisc-linux.org>
parent 3f902886
Loading
Loading
Loading
Loading
+17 −9
Original line number Original line Diff line number Diff line
@@ -43,26 +43,34 @@ extern irqreturn_t ipi_interrupt(int, void *, struct pt_regs *);
*/
*/
static volatile unsigned long cpu_eiem = 0;
static volatile unsigned long cpu_eiem = 0;


static void cpu_set_eiem(void *info)
static void cpu_disable_irq(unsigned int irq)
{
	set_eiem((unsigned long) info);
}

static inline void cpu_disable_irq(unsigned int irq)
{
{
	unsigned long eirr_bit = EIEM_MASK(irq);
	unsigned long eirr_bit = EIEM_MASK(irq);


	cpu_eiem &= ~eirr_bit;
	cpu_eiem &= ~eirr_bit;
        on_each_cpu(cpu_set_eiem, (void *) cpu_eiem, 1, 1);
	/* Do nothing on the other CPUs.  If they get this interrupt,
	 * The & cpu_eiem in the do_cpu_irq_mask() ensures they won't
	 * handle it, and the set_eiem() at the bottom will ensure it
	 * then gets disabled */
}
}


static void cpu_enable_irq(unsigned int irq)
static void cpu_enable_irq(unsigned int irq)
{
{
	unsigned long eirr_bit = EIEM_MASK(irq);
	unsigned long eirr_bit = EIEM_MASK(irq);


	mtctl(eirr_bit, 23);	/* clear EIRR bit before unmasking */
	cpu_eiem |= eirr_bit;
	cpu_eiem |= eirr_bit;
        on_each_cpu(cpu_set_eiem, (void *) cpu_eiem, 1, 1);

	/* FIXME: while our interrupts aren't nested, we cannot reset
	 * the eiem mask if we're already in an interrupt.  Once we
	 * implement nested interrupts, this can go away
	 */
	if (!in_interrupt())
		set_eiem(cpu_eiem);

	/* This is just a simple NOP IPI.  But what it does is cause
	 * all the other CPUs to do a set_eiem(cpu_eiem) at the end
	 * of the interrupt handler */
	smp_send_all_nop();
}
}


static unsigned int cpu_startup_irq(unsigned int irq)
static unsigned int cpu_startup_irq(unsigned int irq)
+14 −6
Original line number Original line Diff line number Diff line
@@ -181,12 +181,19 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
		while (ops) {
		while (ops) {
			unsigned long which = ffz(~ops);
			unsigned long which = ffz(~ops);


			ops &= ~(1 << which);

			switch (which) {
			switch (which) {
			case IPI_NOP:
#if (kDEBUG>=100)
				printk(KERN_DEBUG "CPU%d IPI_NOP\n",this_cpu);
#endif /* kDEBUG */
				break;
				
			case IPI_RESCHEDULE:
			case IPI_RESCHEDULE:
#if (kDEBUG>=100)
#if (kDEBUG>=100)
				printk(KERN_DEBUG "CPU%d IPI_RESCHEDULE\n",this_cpu);
				printk(KERN_DEBUG "CPU%d IPI_RESCHEDULE\n",this_cpu);
#endif /* kDEBUG */
#endif /* kDEBUG */
				ops &= ~(1 << IPI_RESCHEDULE);
				/*
				/*
				 * Reschedule callback.  Everything to be
				 * Reschedule callback.  Everything to be
				 * done is done by the interrupt return path.
				 * done is done by the interrupt return path.
@@ -197,7 +204,6 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#if (kDEBUG>=100)
#if (kDEBUG>=100)
				printk(KERN_DEBUG "CPU%d IPI_CALL_FUNC\n",this_cpu);
				printk(KERN_DEBUG "CPU%d IPI_CALL_FUNC\n",this_cpu);
#endif /* kDEBUG */
#endif /* kDEBUG */
				ops &= ~(1 << IPI_CALL_FUNC);
				{
				{
					volatile struct smp_call_struct *data;
					volatile struct smp_call_struct *data;
					void (*func)(void *info);
					void (*func)(void *info);
@@ -231,7 +237,6 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#if (kDEBUG>=100)
#if (kDEBUG>=100)
				printk(KERN_DEBUG "CPU%d IPI_CPU_START\n",this_cpu);
				printk(KERN_DEBUG "CPU%d IPI_CPU_START\n",this_cpu);
#endif /* kDEBUG */
#endif /* kDEBUG */
				ops &= ~(1 << IPI_CPU_START);
#ifdef ENTRY_SYS_CPUS
#ifdef ENTRY_SYS_CPUS
				p->state = STATE_RUNNING;
				p->state = STATE_RUNNING;
#endif
#endif
@@ -241,7 +246,6 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#if (kDEBUG>=100)
#if (kDEBUG>=100)
				printk(KERN_DEBUG "CPU%d IPI_CPU_STOP\n",this_cpu);
				printk(KERN_DEBUG "CPU%d IPI_CPU_STOP\n",this_cpu);
#endif /* kDEBUG */
#endif /* kDEBUG */
				ops &= ~(1 << IPI_CPU_STOP);
#ifdef ENTRY_SYS_CPUS
#ifdef ENTRY_SYS_CPUS
#else
#else
				halt_processor();
				halt_processor();
@@ -252,13 +256,11 @@ ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#if (kDEBUG>=100)
#if (kDEBUG>=100)
				printk(KERN_DEBUG "CPU%d is alive!\n",this_cpu);
				printk(KERN_DEBUG "CPU%d is alive!\n",this_cpu);
#endif /* kDEBUG */
#endif /* kDEBUG */
				ops &= ~(1 << IPI_CPU_TEST);
				break;
				break;


			default:
			default:
				printk(KERN_CRIT "Unknown IPI num on CPU%d: %lu\n",
				printk(KERN_CRIT "Unknown IPI num on CPU%d: %lu\n",
					this_cpu, which);
					this_cpu, which);
				ops &= ~(1 << which);
				return IRQ_NONE;
				return IRQ_NONE;
			} /* Switch */
			} /* Switch */
		} /* while (ops) */
		} /* while (ops) */
@@ -312,6 +314,12 @@ smp_send_start(void) { send_IPI_allbutself(IPI_CPU_START); }
void 
void 
smp_send_reschedule(int cpu) { send_IPI_single(cpu, IPI_RESCHEDULE); }
smp_send_reschedule(int cpu) { send_IPI_single(cpu, IPI_RESCHEDULE); }


void
smp_send_all_nop(void)
{
	send_IPI_allbutself(IPI_NOP);
}



/**
/**
 * Run a function on all other CPUs.
 * Run a function on all other CPUs.
+1 −0
Original line number Original line Diff line number Diff line
@@ -29,6 +29,7 @@ extern cpumask_t cpu_online_map;
#define cpu_logical_map(cpu)	(cpu)
#define cpu_logical_map(cpu)	(cpu)


extern void smp_send_reschedule(int cpu);
extern void smp_send_reschedule(int cpu);
extern void smp_send_all_nop(void);


#endif /* !ASSEMBLY */
#endif /* !ASSEMBLY */