diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-25 13:50:43 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-25 13:50:43 -0500 |
| commit | 7ae0e06b909e31bf9a396a2326ef09b768d9b4c4 (patch) | |
| tree | 6dc477b7d657642d54aaacb214927318b7715833 | |
| parent | 9b83d851a2bdd021e2135999e5bce3eb8fef94e6 (diff) | |
| parent | e57d339a6264355df8c98948f05a46ff2bc5d504 (diff) | |
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
| -rw-r--r-- | arch/arc/Kconfig | 11 | ||||
| -rw-r--r-- | arch/arc/include/asm/smp.h | 8 | ||||
| -rw-r--r-- | arch/arc/kernel/head.S | 26 | ||||
| -rw-r--r-- | arch/arc/kernel/setup.c | 48 | ||||
| -rw-r--r-- | arch/arc/kernel/smp.c | 124 | ||||
| -rw-r--r-- | arch/arc/plat-arcfpga/smp.c | 12 |
6 files changed, 121 insertions, 108 deletions
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 5438cabbc45d..9be30c8cb0c2 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig | |||
| @@ -409,17 +409,6 @@ config ARC_DBG_TLB_MISS_COUNT | |||
| 409 | Counts number of I and D TLB Misses and exports them via Debugfs | 409 | Counts number of I and D TLB Misses and exports them via Debugfs |
| 410 | The counters can be cleared via Debugfs as well | 410 | The counters can be cleared via Debugfs as well |
| 411 | 411 | ||
| 412 | config CMDLINE_UBOOT | ||
| 413 | bool "Support U-boot kernel command line passing" | ||
| 414 | default n | ||
| 415 | help | ||
| 416 | If you are using U-boot (www.denx.de) and wish to pass the kernel | ||
| 417 | command line from the U-boot environment to the Linux kernel then | ||
| 418 | switch this option on. | ||
| 419 | ARC U-boot will setup the cmdline in RAM/flash and set r2 to point | ||
| 420 | to it. kernel startup code will append this to DeviceTree | ||
| 421 | /bootargs provided cmdline args. | ||
| 422 | |||
| 423 | config ARC_BUILTIN_DTB_NAME | 412 | config ARC_BUILTIN_DTB_NAME |
| 424 | string "Built in DTB" | 413 | string "Built in DTB" |
| 425 | help | 414 | help |
diff --git a/arch/arc/include/asm/smp.h b/arch/arc/include/asm/smp.h index eefc29f08cdb..5d06eee43ea9 100644 --- a/arch/arc/include/asm/smp.h +++ b/arch/arc/include/asm/smp.h | |||
| @@ -46,14 +46,14 @@ extern int smp_ipi_irq_setup(int cpu, int irq); | |||
| 46 | * | 46 | * |
| 47 | * @info: SoC SMP specific info for /proc/cpuinfo etc | 47 | * @info: SoC SMP specific info for /proc/cpuinfo etc |
| 48 | * @cpu_kick: For Master to kickstart a cpu (optionally at a PC) | 48 | * @cpu_kick: For Master to kickstart a cpu (optionally at a PC) |
| 49 | * @ipi_send: To send IPI to a @cpumask | 49 | * @ipi_send: To send IPI to a @cpu |
| 50 | * @ips_clear: To clear IPI received by @cpu at @irq | 50 | * @ips_clear: To clear IPI received at @irq |
| 51 | */ | 51 | */ |
| 52 | struct plat_smp_ops { | 52 | struct plat_smp_ops { |
| 53 | const char *info; | 53 | const char *info; |
| 54 | void (*cpu_kick)(int cpu, unsigned long pc); | 54 | void (*cpu_kick)(int cpu, unsigned long pc); |
| 55 | void (*ipi_send)(void *callmap); | 55 | void (*ipi_send)(int cpu); |
| 56 | void (*ipi_clear)(int cpu, int irq); | 56 | void (*ipi_clear)(int irq); |
| 57 | }; | 57 | }; |
| 58 | 58 | ||
| 59 | /* TBD: stop exporting it for direct population by platform */ | 59 | /* TBD: stop exporting it for direct population by platform */ |
diff --git a/arch/arc/kernel/head.S b/arch/arc/kernel/head.S index 2c878e964a64..991997269d02 100644 --- a/arch/arc/kernel/head.S +++ b/arch/arc/kernel/head.S | |||
| @@ -49,25 +49,13 @@ stext: | |||
| 49 | st.ab 0, [r5,4] | 49 | st.ab 0, [r5,4] |
| 50 | brlt r5, r6, 1b | 50 | brlt r5, r6, 1b |
| 51 | 51 | ||
| 52 | #ifdef CONFIG_CMDLINE_UBOOT | 52 | ; Uboot - kernel ABI |
| 53 | ; support for bootloader provided cmdline | 53 | ; r0 = [0] No uboot interaction, [1] cmdline in r2, [2] DTB in r2 |
| 54 | ; If cmdline passed by u-boot, then | 54 | ; r1 = magic number (board identity, unused as of now |
| 55 | ; r0 = 1 (because ATAGS parsing, now retired, used to use 0) | 55 | ; r2 = pointer to uboot provided cmdline or external DTB in mem |
| 56 | ; r1 = magic number (board identity) | 56 | ; These are handled later in setup_arch() |
| 57 | ; r2 = addr of cmdline string (somewhere in memory/flash) | 57 | st r0, [@uboot_tag] |
| 58 | 58 | st r2, [@uboot_arg] | |
| 59 | brne r0, 1, .Lother_bootup_chores ; u-boot didn't pass cmdline | ||
| 60 | breq r2, 0, .Lother_bootup_chores ; or cmdline is NULL | ||
| 61 | |||
| 62 | mov r5, @command_line | ||
| 63 | 1: | ||
| 64 | ldb.ab r6, [r2, 1] | ||
| 65 | breq r6, 0, .Lother_bootup_chores | ||
| 66 | b.d 1b | ||
| 67 | stb.ab r6, [r5, 1] | ||
| 68 | #endif | ||
| 69 | |||
| 70 | .Lother_bootup_chores: | ||
| 71 | 59 | ||
| 72 | ; Identify if running on ISS vs Silicon | 60 | ; Identify if running on ISS vs Silicon |
| 73 | ; IDENTITY Reg [ 3 2 1 0 ] | 61 | ; IDENTITY Reg [ 3 2 1 0 ] |
diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 643eae4436e0..119dddb752b2 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c | |||
| @@ -29,7 +29,10 @@ | |||
| 29 | 29 | ||
| 30 | int running_on_hw = 1; /* vs. on ISS */ | 30 | int running_on_hw = 1; /* vs. on ISS */ |
| 31 | 31 | ||
| 32 | char __initdata command_line[COMMAND_LINE_SIZE]; | 32 | /* Part of U-boot ABI: see head.S */ |
| 33 | int __initdata uboot_tag; | ||
| 34 | char __initdata *uboot_arg; | ||
| 35 | |||
| 33 | const struct machine_desc *machine_desc; | 36 | const struct machine_desc *machine_desc; |
| 34 | 37 | ||
| 35 | struct task_struct *_current_task[NR_CPUS]; /* For stack switching */ | 38 | struct task_struct *_current_task[NR_CPUS]; /* For stack switching */ |
| @@ -311,19 +314,40 @@ void setup_processor(void) | |||
| 311 | arc_chk_fpu(); | 314 | arc_chk_fpu(); |
| 312 | } | 315 | } |
| 313 | 316 | ||
| 317 | static inline int is_kernel(unsigned long addr) | ||
| 318 | { | ||
| 319 | if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end) | ||
| 320 | return 1; | ||
| 321 | return 0; | ||
| 322 | } | ||
| 323 | |||
| 314 | void __init setup_arch(char **cmdline_p) | 324 | void __init setup_arch(char **cmdline_p) |
| 315 | { | 325 | { |
| 316 | /* This also populates @boot_command_line from /bootargs */ | 326 | /* make sure that uboot passed pointer to cmdline/dtb is valid */ |
| 317 | machine_desc = setup_machine_fdt(__dtb_start); | 327 | if (uboot_tag && is_kernel((unsigned long)uboot_arg)) |
| 318 | if (!machine_desc) | 328 | panic("Invalid uboot arg\n"); |
| 319 | panic("Embedded DT invalid\n"); | 329 | |
| 320 | 330 | /* See if u-boot passed an external Device Tree blob */ | |
| 321 | /* Append any u-boot provided cmdline */ | 331 | machine_desc = setup_machine_fdt(uboot_arg); /* uboot_tag == 2 */ |
| 322 | #ifdef CONFIG_CMDLINE_UBOOT | 332 | if (!machine_desc) { |
| 323 | /* Add a whitespace seperator between the 2 cmdlines */ | 333 | /* No, so try the embedded one */ |
| 324 | strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); | 334 | machine_desc = setup_machine_fdt(__dtb_start); |
| 325 | strlcat(boot_command_line, command_line, COMMAND_LINE_SIZE); | 335 | if (!machine_desc) |
| 326 | #endif | 336 | panic("Embedded DT invalid\n"); |
| 337 | |||
| 338 | /* | ||
| 339 | * If we are here, it is established that @uboot_arg didn't | ||
| 340 | * point to DT blob. Instead if u-boot says it is cmdline, | ||
| 341 | * Appent to embedded DT cmdline. | ||
| 342 | * setup_machine_fdt() would have populated @boot_command_line | ||
| 343 | */ | ||
| 344 | if (uboot_tag == 1) { | ||
| 345 | /* Ensure a whitespace between the 2 cmdlines */ | ||
| 346 | strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); | ||
| 347 | strlcat(boot_command_line, uboot_arg, | ||
| 348 | COMMAND_LINE_SIZE); | ||
| 349 | } | ||
| 350 | } | ||
| 327 | 351 | ||
| 328 | /* Save unparsed command line copy for /proc/cmdline */ | 352 | /* Save unparsed command line copy for /proc/cmdline */ |
| 329 | *cmdline_p = boot_command_line; | 353 | *cmdline_p = boot_command_line; |
diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index c2f9ebbc38f6..40859e5619f9 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c | |||
| @@ -197,51 +197,65 @@ int __init setup_profiling_timer(unsigned int multiplier) | |||
| 197 | /* Inter Processor Interrupt Handling */ | 197 | /* Inter Processor Interrupt Handling */ |
| 198 | /*****************************************************************************/ | 198 | /*****************************************************************************/ |
| 199 | 199 | ||
| 200 | /* | ||
| 201 | * structures for inter-processor calls | ||
| 202 | * A Collection of single bit ipi messages | ||
| 203 | * | ||
| 204 | */ | ||
| 205 | |||
| 206 | /* | ||
| 207 | * TODO_rajesh investigate tlb message types. | ||
| 208 | * IPI Timer not needed because each ARC has an individual Interrupting Timer | ||
| 209 | */ | ||
| 210 | enum ipi_msg_type { | 200 | enum ipi_msg_type { |
| 211 | IPI_NOP = 0, | 201 | IPI_EMPTY = 0, |
| 212 | IPI_RESCHEDULE = 1, | 202 | IPI_RESCHEDULE = 1, |
| 213 | IPI_CALL_FUNC, | 203 | IPI_CALL_FUNC, |
| 214 | IPI_CPU_STOP | 204 | IPI_CPU_STOP, |
| 215 | }; | 205 | }; |
| 216 | 206 | ||
| 217 | struct ipi_data { | 207 | /* |
| 218 | unsigned long bits; | 208 | * In arches with IRQ for each msg type (above), receiver can use IRQ-id to |
| 219 | }; | 209 | * figure out what msg was sent. For those which don't (ARC has dedicated IPI |
| 210 | * IRQ), the msg-type needs to be conveyed via per-cpu data | ||
| 211 | */ | ||
| 220 | 212 | ||
| 221 | static DEFINE_PER_CPU(struct ipi_data, ipi_data); | 213 | static DEFINE_PER_CPU(unsigned long, ipi_data); |
| 222 | 214 | ||
| 223 | static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg) | 215 | static void ipi_send_msg_one(int cpu, enum ipi_msg_type msg) |
| 224 | { | 216 | { |
| 217 | unsigned long __percpu *ipi_data_ptr = per_cpu_ptr(&ipi_data, cpu); | ||
| 218 | unsigned long old, new; | ||
| 225 | unsigned long flags; | 219 | unsigned long flags; |
| 226 | unsigned int cpu; | 220 | |
| 221 | pr_debug("%d Sending msg [%d] to %d\n", smp_processor_id(), msg, cpu); | ||
| 227 | 222 | ||
| 228 | local_irq_save(flags); | 223 | local_irq_save(flags); |
| 229 | 224 | ||
| 230 | for_each_cpu(cpu, callmap) { | 225 | /* |
| 231 | struct ipi_data *ipi = &per_cpu(ipi_data, cpu); | 226 | * Atomically write new msg bit (in case others are writing too), |
| 232 | set_bit(msg, &ipi->bits); | 227 | * and read back old value |
| 233 | } | 228 | */ |
| 229 | do { | ||
| 230 | new = old = *ipi_data_ptr; | ||
| 231 | new |= 1U << msg; | ||
| 232 | } while (cmpxchg(ipi_data_ptr, old, new) != old); | ||
| 234 | 233 | ||
| 235 | /* Call the platform specific cross-CPU call function */ | 234 | /* |
| 236 | if (plat_smp_ops.ipi_send) | 235 | * Call the platform specific IPI kick function, but avoid if possible: |
| 237 | plat_smp_ops.ipi_send((void *)callmap); | 236 | * Only do so if there's no pending msg from other concurrent sender(s). |
| 237 | * Otherwise, recevier will see this msg as well when it takes the | ||
| 238 | * IPI corresponding to that msg. This is true, even if it is already in | ||
| 239 | * IPI handler, because !@old means it has not yet dequeued the msg(s) | ||
| 240 | * so @new msg can be a free-loader | ||
| 241 | */ | ||
| 242 | if (plat_smp_ops.ipi_send && !old) | ||
| 243 | plat_smp_ops.ipi_send(cpu); | ||
| 238 | 244 | ||
| 239 | local_irq_restore(flags); | 245 | local_irq_restore(flags); |
| 240 | } | 246 | } |
| 241 | 247 | ||
| 248 | static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg) | ||
| 249 | { | ||
| 250 | unsigned int cpu; | ||
| 251 | |||
| 252 | for_each_cpu(cpu, callmap) | ||
| 253 | ipi_send_msg_one(cpu, msg); | ||
| 254 | } | ||
| 255 | |||
| 242 | void smp_send_reschedule(int cpu) | 256 | void smp_send_reschedule(int cpu) |
| 243 | { | 257 | { |
| 244 | ipi_send_msg(cpumask_of(cpu), IPI_RESCHEDULE); | 258 | ipi_send_msg_one(cpu, IPI_RESCHEDULE); |
| 245 | } | 259 | } |
| 246 | 260 | ||
| 247 | void smp_send_stop(void) | 261 | void smp_send_stop(void) |
| @@ -254,7 +268,7 @@ void smp_send_stop(void) | |||
| 254 | 268 | ||
| 255 | void arch_send_call_function_single_ipi(int cpu) | 269 | void arch_send_call_function_single_ipi(int cpu) |
| 256 | { | 270 | { |
| 257 | ipi_send_msg(cpumask_of(cpu), IPI_CALL_FUNC); | 271 | ipi_send_msg_one(cpu, IPI_CALL_FUNC); |
| 258 | } | 272 | } |
| 259 | 273 | ||
| 260 | void arch_send_call_function_ipi_mask(const struct cpumask *mask) | 274 | 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) | |||
| 265 | /* | 279 | /* |
| 266 | * ipi_cpu_stop - handle IPI from smp_send_stop() | 280 | * ipi_cpu_stop - handle IPI from smp_send_stop() |
| 267 | */ | 281 | */ |
| 268 | static void ipi_cpu_stop(unsigned int cpu) | 282 | static void ipi_cpu_stop(void) |
| 269 | { | 283 | { |
| 270 | machine_halt(); | 284 | machine_halt(); |
| 271 | } | 285 | } |
| 272 | 286 | ||
| 273 | static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu) | 287 | static inline void __do_IPI(unsigned long msg) |
| 274 | { | 288 | { |
| 275 | unsigned long msg = 0; | 289 | switch (msg) { |
| 290 | case IPI_RESCHEDULE: | ||
| 291 | scheduler_ipi(); | ||
| 292 | break; | ||
| 276 | 293 | ||
| 277 | do { | 294 | case IPI_CALL_FUNC: |
| 278 | msg = find_next_bit(ops, BITS_PER_LONG, msg+1); | 295 | generic_smp_call_function_interrupt(); |
| 296 | break; | ||
| 279 | 297 | ||
| 280 | switch (msg) { | 298 | case IPI_CPU_STOP: |
| 281 | case IPI_RESCHEDULE: | 299 | ipi_cpu_stop(); |
| 282 | scheduler_ipi(); | 300 | break; |
| 283 | break; | ||
| 284 | |||
| 285 | case IPI_CALL_FUNC: | ||
| 286 | generic_smp_call_function_interrupt(); | ||
| 287 | break; | ||
| 288 | |||
| 289 | case IPI_CPU_STOP: | ||
| 290 | ipi_cpu_stop(cpu); | ||
| 291 | break; | ||
| 292 | } | ||
| 293 | } while (msg < BITS_PER_LONG); | ||
| 294 | 301 | ||
| 302 | default: | ||
| 303 | pr_warn("IPI with unexpected msg %ld\n", msg); | ||
| 304 | } | ||
| 295 | } | 305 | } |
| 296 | 306 | ||
| 297 | /* | 307 | /* |
| @@ -300,19 +310,25 @@ static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu) | |||
| 300 | */ | 310 | */ |
| 301 | irqreturn_t do_IPI(int irq, void *dev_id) | 311 | irqreturn_t do_IPI(int irq, void *dev_id) |
| 302 | { | 312 | { |
| 303 | int cpu = smp_processor_id(); | 313 | unsigned long pending; |
| 304 | struct ipi_data *ipi = &per_cpu(ipi_data, cpu); | 314 | |
| 305 | unsigned long ops; | 315 | pr_debug("IPI [%ld] received on cpu %d\n", |
| 316 | *this_cpu_ptr(&ipi_data), smp_processor_id()); | ||
| 306 | 317 | ||
| 307 | if (plat_smp_ops.ipi_clear) | 318 | if (plat_smp_ops.ipi_clear) |
| 308 | plat_smp_ops.ipi_clear(cpu, irq); | 319 | plat_smp_ops.ipi_clear(irq); |
| 309 | 320 | ||
| 310 | /* | 321 | /* |
| 311 | * XXX: is this loop really needed | 322 | * "dequeue" the msg corresponding to this IPI (and possibly other |
| 312 | * And do we need to move ipi_clean inside | 323 | * piggybacked msg from elided IPIs: see ipi_send_msg_one() above) |
| 313 | */ | 324 | */ |
| 314 | while ((ops = xchg(&ipi->bits, 0)) != 0) | 325 | pending = xchg(this_cpu_ptr(&ipi_data), 0); |
| 315 | __do_IPI(&ops, ipi, cpu); | 326 | |
| 327 | do { | ||
| 328 | unsigned long msg = __ffs(pending); | ||
| 329 | __do_IPI(msg); | ||
| 330 | pending &= ~(1U << msg); | ||
| 331 | } while (pending); | ||
| 316 | 332 | ||
| 317 | return IRQ_HANDLED; | 333 | return IRQ_HANDLED; |
| 318 | } | 334 | } |
diff --git a/arch/arc/plat-arcfpga/smp.c b/arch/arc/plat-arcfpga/smp.c index 91b55349a5f8..8a12741f5f7a 100644 --- a/arch/arc/plat-arcfpga/smp.c +++ b/arch/arc/plat-arcfpga/smp.c | |||
| @@ -88,18 +88,14 @@ void iss_model_init_smp(unsigned int cpu) | |||
| 88 | smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu); | 88 | smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu); |
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | static void iss_model_ipi_send(void *arg) | 91 | static void iss_model_ipi_send(int cpu) |
| 92 | { | 92 | { |
| 93 | struct cpumask *callmap = arg; | 93 | idu_irq_assert(cpu); |
| 94 | unsigned int cpu; | ||
| 95 | |||
| 96 | for_each_cpu(cpu, callmap) | ||
| 97 | idu_irq_assert(cpu); | ||
| 98 | } | 94 | } |
| 99 | 95 | ||
| 100 | static void iss_model_ipi_clear(int cpu, int irq) | 96 | static void iss_model_ipi_clear(int irq) |
| 101 | { | 97 | { |
| 102 | idu_irq_clear(IDU_INTERRUPT_0 + cpu); | 98 | idu_irq_clear(IDU_INTERRUPT_0 + smp_processor_id()); |
| 103 | } | 99 | } |
| 104 | 100 | ||
| 105 | void iss_model_init_early_smp(void) | 101 | void iss_model_init_early_smp(void) |
