Browse Source

Merge tag 'arc-v3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc

Pull ARC changes from Vineet Gupta:
 - IPI optimization and cleanups
 - Support for bootloader provided external Device Tree blobs

* tag 'arc-v3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc:
  ARC: [cmdline] support External Device Trees from u-boot
  ARC: [cmdline] uboot cmdline handling rework
  ARC: [SMP] optimize IPI send and receive
  ARC: [SMP] simplify IPI code
  ARC: [SMP] cpu halt interface doesn't need "self" cpu-id
  ARC: [SMP] IPI ACK interface doesn't need "self" cpu-id
  ARC: [SMP] cpumask not needed in IPI send path
Linus Torvalds 11 years ago
parent
commit
7ae0e06b90
6 changed files with 121 additions and 108 deletions
  1. 0 11
      arch/arc/Kconfig
  2. 4 4
      arch/arc/include/asm/smp.h
  3. 7 19
      arch/arc/kernel/head.S
  4. 36 12
      arch/arc/kernel/setup.c
  5. 70 54
      arch/arc/kernel/smp.c
  6. 4 8
      arch/arc/plat-arcfpga/smp.c

+ 0 - 11
arch/arc/Kconfig

@@ -409,17 +409,6 @@ config ARC_DBG_TLB_MISS_COUNT
 	  Counts number of I and D TLB Misses and exports them via Debugfs
 	  The counters can be cleared via Debugfs as well
 
-config CMDLINE_UBOOT
-	bool "Support U-boot kernel command line passing"
-	default n
-	help
-	  If you are using U-boot (www.denx.de) and wish to pass the kernel
-	  command line from the U-boot environment to the Linux kernel then
-	  switch this option on.
-	  ARC U-boot will setup the cmdline in RAM/flash and set r2 to point
-	  to it. kernel startup code will append this to DeviceTree
-	  /bootargs provided cmdline args.
-
 config ARC_BUILTIN_DTB_NAME
 	string "Built in DTB"
 	help

+ 4 - 4
arch/arc/include/asm/smp.h

@@ -46,14 +46,14 @@ extern int smp_ipi_irq_setup(int cpu, int irq);
  *
  * @info:		SoC SMP specific info for /proc/cpuinfo etc
  * @cpu_kick:		For Master to kickstart a cpu (optionally at a PC)
- * @ipi_send:		To send IPI to a @cpumask
- * @ips_clear:		To clear IPI received by @cpu at @irq
+ * @ipi_send:		To send IPI to a @cpu
+ * @ips_clear:		To clear IPI received at @irq
  */
 struct plat_smp_ops {
 	const char 	*info;
 	void		(*cpu_kick)(int cpu, unsigned long pc);
-	void		(*ipi_send)(void *callmap);
-	void		(*ipi_clear)(int cpu, int irq);
+	void		(*ipi_send)(int cpu);
+	void		(*ipi_clear)(int irq);
 };
 
 /* TBD: stop exporting it for direct population by platform */

+ 7 - 19
arch/arc/kernel/head.S

@@ -49,25 +49,13 @@ stext:
 	st.ab   0, [r5,4]
 	brlt    r5, r6, 1b
 
-#ifdef CONFIG_CMDLINE_UBOOT
-	; support for bootloader provided cmdline
-	;    If cmdline passed by u-boot, then
-	;    r0 = 1  (because ATAGS parsing, now retired, used to use 0)
-	;    r1 = magic number (board identity)
-	;    r2 = addr of cmdline string (somewhere in memory/flash)
-
-	brne	r0, 1, .Lother_bootup_chores	; u-boot didn't pass cmdline
-	breq	r2, 0, .Lother_bootup_chores	; or cmdline is NULL
-
-	mov	r5, @command_line
-1:
-	ldb.ab  r6, [r2, 1]
-	breq    r6, 0, .Lother_bootup_chores
-	b.d     1b
-	stb.ab  r6, [r5, 1]
-#endif
-
-.Lother_bootup_chores:
+	; Uboot - kernel ABI
+	;    r0 = [0] No uboot interaction, [1] cmdline in r2, [2] DTB in r2
+	;    r1 = magic number (board identity, unused as of now
+	;    r2 = pointer to uboot provided cmdline or external DTB in mem
+	; These are handled later in setup_arch()
+	st	r0, [@uboot_tag]
+	st	r2, [@uboot_arg]
 
 	; Identify if running on ISS vs Silicon
 	; 	IDENTITY Reg [ 3  2  1  0 ]

+ 36 - 12
arch/arc/kernel/setup.c

@@ -29,7 +29,10 @@
 
 int running_on_hw = 1;	/* vs. on ISS */
 
-char __initdata command_line[COMMAND_LINE_SIZE];
+/* Part of U-boot ABI: see head.S */
+int __initdata uboot_tag;
+char __initdata *uboot_arg;
+
 const struct machine_desc *machine_desc;
 
 struct task_struct *_current_task[NR_CPUS];	/* For stack switching */
@@ -311,19 +314,40 @@ void setup_processor(void)
 	arc_chk_fpu();
 }
 
+static inline int is_kernel(unsigned long addr)
+{
+	if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end)
+		return 1;
+	return 0;
+}
+
 void __init setup_arch(char **cmdline_p)
 {
-	/* This also populates @boot_command_line from /bootargs */
-	machine_desc = setup_machine_fdt(__dtb_start);
-	if (!machine_desc)
-		panic("Embedded DT invalid\n");
-
-	/* Append any u-boot provided cmdline */
-#ifdef CONFIG_CMDLINE_UBOOT
-	/* Add a whitespace seperator between the 2 cmdlines */
-	strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
-	strlcat(boot_command_line, command_line, COMMAND_LINE_SIZE);
-#endif
+	/* make sure that uboot passed pointer to cmdline/dtb is valid */
+	if (uboot_tag && is_kernel((unsigned long)uboot_arg))
+		panic("Invalid uboot arg\n");
+
+	/* See if u-boot passed an external Device Tree blob */
+	machine_desc = setup_machine_fdt(uboot_arg);	/* uboot_tag == 2 */
+	if (!machine_desc) {
+		/* No, so try the embedded one */
+		machine_desc = setup_machine_fdt(__dtb_start);
+		if (!machine_desc)
+			panic("Embedded DT invalid\n");
+
+		/*
+		 * If we are here, it is established that @uboot_arg didn't
+		 * point to DT blob. Instead if u-boot says it is cmdline,
+		 * Appent to embedded DT cmdline.
+		 * setup_machine_fdt() would have populated @boot_command_line
+		 */
+		if (uboot_tag == 1) {
+			/* Ensure a whitespace between the 2 cmdlines */
+			strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
+			strlcat(boot_command_line, uboot_arg,
+				COMMAND_LINE_SIZE);
+		}
+	}
 
 	/* Save unparsed command line copy for /proc/cmdline */
 	*cmdline_p = boot_command_line;

+ 70 - 54
arch/arc/kernel/smp.c

@@ -197,51 +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_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);
+	ipi_send_msg_one(cpu, IPI_CALL_FUNC);
 }
 
 void arch_send_call_function_ipi_mask(const struct cpumask *mask)
@@ -265,33 +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;
+	switch (msg) {
+	case IPI_RESCHEDULE:
+		scheduler_ipi();
+		break;
 
-	do {
-		msg = find_next_bit(ops, BITS_PER_LONG, msg+1);
+	case IPI_CALL_FUNC:
+		generic_smp_call_function_interrupt();
+		break;
 
-		switch (msg) {
-		case IPI_RESCHEDULE:
-			scheduler_ipi();
-			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);
+	}
 }
 
 /*
@@ -300,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;
 }

+ 4 - 8
arch/arc/plat-arcfpga/smp.c

@@ -88,18 +88,14 @@ void iss_model_init_smp(unsigned int cpu)
 	smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu);
 }
 
-static void iss_model_ipi_send(void *arg)
+static void iss_model_ipi_send(int cpu)
 {
-	struct cpumask *callmap = arg;
-	unsigned int cpu;
-
-	for_each_cpu(cpu, callmap)
-		idu_irq_assert(cpu);
+	idu_irq_assert(cpu);
 }
 
-static void iss_model_ipi_clear(int cpu, int irq)
+static void iss_model_ipi_clear(int irq)
 {
-	idu_irq_clear(IDU_INTERRUPT_0 + cpu);
+	idu_irq_clear(IDU_INTERRUPT_0 + smp_processor_id());
 }
 
 void iss_model_init_early_smp(void)