diff options
Diffstat (limited to 'arch/arc/kernel/smp.c')
| -rw-r--r-- | arch/arc/kernel/smp.c | 149 | 
1 files changed, 86 insertions, 63 deletions
diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index bca3052c956..c802bb50060 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c @@ -95,7 +95,7 @@ void __init smp_cpus_done(unsigned int max_cpus)   *        If it turns out to be elaborate, it's better to code it in assembly   *   */ -void __attribute__((weak)) arc_platform_smp_wait_to_boot(int cpu) +void __weak arc_platform_smp_wait_to_boot(int cpu)  {  	/*  	 * As a hack for debugging - since debugger will single-step over the @@ -128,6 +128,7 @@ void start_kernel_secondary(void)  	atomic_inc(&mm->mm_users);  	atomic_inc(&mm->mm_count);  	current->active_mm = mm; +	cpumask_set_cpu(cpu, mm_cpumask(mm));  	notify_cpu_starting(cpu);  	set_cpu_online(cpu, true); @@ -137,7 +138,7 @@ void start_kernel_secondary(void)  	if (machine_desc->init_smp)  		machine_desc->init_smp(smp_processor_id()); -	arc_local_timer_setup(cpu); +	arc_local_timer_setup();  	local_irq_enable();  	preempt_disable(); @@ -196,52 +197,65 @@ int __init setup_profiling_timer(unsigned int multiplier)  /*              Inter Processor Interrupt Handling                           */  /*****************************************************************************/ -/* - * structures for inter-processor calls - * A Collection of single bit ipi messages - * - */ - -/* - * TODO_rajesh investigate tlb message types. - * IPI Timer not needed because each ARC has an individual Interrupting Timer - */  enum ipi_msg_type { -	IPI_NOP = 0, +	IPI_EMPTY = 0,  	IPI_RESCHEDULE = 1,  	IPI_CALL_FUNC, -	IPI_CALL_FUNC_SINGLE, -	IPI_CPU_STOP +	IPI_CPU_STOP,  }; -struct ipi_data { -	unsigned long bits; -}; +/* + * In arches with IRQ for each msg type (above), receiver can use IRQ-id  to + * figure out what msg was sent. For those which don't (ARC has dedicated IPI + * IRQ), the msg-type needs to be conveyed via per-cpu data + */ -static DEFINE_PER_CPU(struct ipi_data, ipi_data); +static DEFINE_PER_CPU(unsigned long, ipi_data); -static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg) +static void ipi_send_msg_one(int cpu, enum ipi_msg_type msg)  { +	unsigned long __percpu *ipi_data_ptr = per_cpu_ptr(&ipi_data, cpu); +	unsigned long old, new;  	unsigned long flags; -	unsigned int cpu; + +	pr_debug("%d Sending msg [%d] to %d\n", smp_processor_id(), msg, cpu);  	local_irq_save(flags); -	for_each_cpu(cpu, callmap) { -		struct ipi_data *ipi = &per_cpu(ipi_data, cpu); -		set_bit(msg, &ipi->bits); -	} +	/* +	 * Atomically write new msg bit (in case others are writing too), +	 * and read back old value +	 */ +	do { +		new = old = *ipi_data_ptr; +		new |= 1U << msg; +	} while (cmpxchg(ipi_data_ptr, old, new) != old); -	/* Call the platform specific cross-CPU call function  */ -	if (plat_smp_ops.ipi_send) -		plat_smp_ops.ipi_send((void *)callmap); +	/* +	 * Call the platform specific IPI kick function, but avoid if possible: +	 * Only do so if there's no pending msg from other concurrent sender(s). +	 * Otherwise, recevier will see this msg as well when it takes the +	 * IPI corresponding to that msg. This is true, even if it is already in +	 * IPI handler, because !@old means it has not yet dequeued the msg(s) +	 * so @new msg can be a free-loader +	 */ +	if (plat_smp_ops.ipi_send && !old) +		plat_smp_ops.ipi_send(cpu);  	local_irq_restore(flags);  } +static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg) +{ +	unsigned int cpu; + +	for_each_cpu(cpu, callmap) +		ipi_send_msg_one(cpu, msg); +} +  void smp_send_reschedule(int cpu)  { -	ipi_send_msg(cpumask_of(cpu), IPI_RESCHEDULE); +	ipi_send_msg_one(cpu, IPI_RESCHEDULE);  }  void smp_send_stop(void) @@ -254,7 +268,7 @@ void smp_send_stop(void)  void arch_send_call_function_single_ipi(int cpu)  { -	ipi_send_msg(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE); +	ipi_send_msg_one(cpu, IPI_CALL_FUNC);  }  void arch_send_call_function_ipi_mask(const struct cpumask *mask) @@ -265,37 +279,29 @@ void arch_send_call_function_ipi_mask(const struct cpumask *mask)  /*   * ipi_cpu_stop - handle IPI from smp_send_stop()   */ -static void ipi_cpu_stop(unsigned int cpu) +static void ipi_cpu_stop(void)  {  	machine_halt();  } -static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu) +static inline void __do_IPI(unsigned long msg)  { -	unsigned long msg = 0; - -	do { -		msg = find_next_bit(ops, BITS_PER_LONG, msg+1); +	switch (msg) { +	case IPI_RESCHEDULE: +		scheduler_ipi(); +		break; -		switch (msg) { -		case IPI_RESCHEDULE: -			scheduler_ipi(); -			break; - -		case IPI_CALL_FUNC: -			generic_smp_call_function_interrupt(); -			break; - -		case IPI_CALL_FUNC_SINGLE: -			generic_smp_call_function_single_interrupt(); -			break; +	case IPI_CALL_FUNC: +		generic_smp_call_function_interrupt(); +		break; -		case IPI_CPU_STOP: -			ipi_cpu_stop(cpu); -			break; -		} -	} while (msg < BITS_PER_LONG); +	case IPI_CPU_STOP: +		ipi_cpu_stop(); +		break; +	default: +		pr_warn("IPI with unexpected msg %ld\n", msg); +	}  }  /* @@ -304,19 +310,25 @@ static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu)   */  irqreturn_t do_IPI(int irq, void *dev_id)  { -	int cpu = smp_processor_id(); -	struct ipi_data *ipi = &per_cpu(ipi_data, cpu); -	unsigned long ops; +	unsigned long pending; + +	pr_debug("IPI [%ld] received on cpu %d\n", +		 *this_cpu_ptr(&ipi_data), smp_processor_id());  	if (plat_smp_ops.ipi_clear) -		plat_smp_ops.ipi_clear(cpu, irq); +		plat_smp_ops.ipi_clear(irq);  	/* -	 * XXX: is this loop really needed -	 * And do we need to move ipi_clean inside +	 * "dequeue" the msg corresponding to this IPI (and possibly other +	 * piggybacked msg from elided IPIs: see ipi_send_msg_one() above)  	 */ -	while ((ops = xchg(&ipi->bits, 0)) != 0) -		__do_IPI(&ops, ipi, cpu); +	pending = xchg(this_cpu_ptr(&ipi_data), 0); + +	do { +		unsigned long msg = __ffs(pending); +		__do_IPI(msg); +		pending &= ~(1U << msg); +	} while (pending);  	return IRQ_HANDLED;  } @@ -325,8 +337,19 @@ irqreturn_t do_IPI(int irq, void *dev_id)   * API called by platform code to hookup arch-common ISR to their IPI IRQ   */  static DEFINE_PER_CPU(int, ipi_dev); + +static struct irqaction arc_ipi_irq = { +        .name    = "IPI Interrupt", +        .flags   = IRQF_PERCPU, +        .handler = do_IPI, +}; +  int smp_ipi_irq_setup(int cpu, int irq)  { -	int *dev_id = &per_cpu(ipi_dev, smp_processor_id()); -	return request_percpu_irq(irq, do_IPI, "IPI Interrupt", dev_id); +	if (!cpu) +		return setup_irq(irq, &arc_ipi_irq); +	else +		arch_unmask_irq(irq); + +	return 0;  }  | 
