1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * SMP Support
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
61da177e4SLinus Torvalds * Copyright (C) 1999, 2001, 2003 David Mosberger-Tang <davidm@hpl.hp.com>
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * Lots of stuff stolen from arch/alpha/kernel/smp.c
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * 01/05/16 Rohit Seth <rohit.seth@intel.com> IA64-SMP functions. Reorganized
111da177e4SLinus Torvalds * the existing code (on the lines of x86 port).
121da177e4SLinus Torvalds * 00/09/11 David Mosberger <davidm@hpl.hp.com> Do loops_per_jiffy
131da177e4SLinus Torvalds * calibration on each CPU.
141da177e4SLinus Torvalds * 00/08/23 Asit Mallick <asit.k.mallick@intel.com> fixed logical processor id
151da177e4SLinus Torvalds * 00/03/31 Rohit Seth <rohit.seth@intel.com> Fixes for Bootstrap Processor
161da177e4SLinus Torvalds * & cpu_online_map now gets done here (instead of setup.c)
171da177e4SLinus Torvalds * 99/10/05 davidm Update to bring it in sync with new command-line processing
181da177e4SLinus Torvalds * scheme.
191da177e4SLinus Torvalds * 10/13/00 Goutham Rao <goutham.rao@intel.com> Updated smp_call_function and
201da177e4SLinus Torvalds * smp_call_function_single to resend IPI on timeouts
211da177e4SLinus Torvalds */
221da177e4SLinus Torvalds #include <linux/module.h>
231da177e4SLinus Torvalds #include <linux/kernel.h>
241da177e4SLinus Torvalds #include <linux/sched.h>
251da177e4SLinus Torvalds #include <linux/init.h>
261da177e4SLinus Torvalds #include <linux/interrupt.h>
271da177e4SLinus Torvalds #include <linux/smp.h>
281da177e4SLinus Torvalds #include <linux/kernel_stat.h>
291da177e4SLinus Torvalds #include <linux/mm.h>
301da177e4SLinus Torvalds #include <linux/cache.h>
311da177e4SLinus Torvalds #include <linux/delay.h>
321da177e4SLinus Torvalds #include <linux/efi.h>
331da177e4SLinus Torvalds #include <linux/bitops.h>
34a7956113SZou Nan hai #include <linux/kexec.h>
351da177e4SLinus Torvalds
3660063497SArun Sharma #include <linux/atomic.h>
371da177e4SLinus Torvalds #include <asm/current.h>
381da177e4SLinus Torvalds #include <asm/delay.h>
391da177e4SLinus Torvalds #include <asm/io.h>
401da177e4SLinus Torvalds #include <asm/irq.h>
411da177e4SLinus Torvalds #include <asm/page.h>
421da177e4SLinus Torvalds #include <asm/processor.h>
431da177e4SLinus Torvalds #include <asm/ptrace.h>
441da177e4SLinus Torvalds #include <asm/sal.h>
451da177e4SLinus Torvalds #include <asm/tlbflush.h>
461da177e4SLinus Torvalds #include <asm/unistd.h>
471da177e4SLinus Torvalds #include <asm/mca.h>
48b3545192SPeter Zijlstra #include <asm/xtp.h>
491da177e4SLinus Torvalds
501da177e4SLinus Torvalds /*
513be44b9cSJack Steiner * Note: alignment of 4 entries/cacheline was empirically determined
523be44b9cSJack Steiner * to be a good tradeoff between hot cachelines & spreading the array
533be44b9cSJack Steiner * across too many cacheline.
543be44b9cSJack Steiner */
553be44b9cSJack Steiner static struct local_tlb_flush_counts {
563be44b9cSJack Steiner unsigned int count;
573be44b9cSJack Steiner } __attribute__((__aligned__(32))) local_tlb_flush_counts[NR_CPUS];
583be44b9cSJack Steiner
59b9bf3121STejun Heo static DEFINE_PER_CPU_SHARED_ALIGNED(unsigned short [NR_CPUS],
60b9bf3121STejun Heo shadow_flush_counts);
613be44b9cSJack Steiner
621da177e4SLinus Torvalds #define IPI_CALL_FUNC 0
631da177e4SLinus Torvalds #define IPI_CPU_STOP 1
64f27b433eSJens Axboe #define IPI_CALL_FUNC_SINGLE 2
65a7956113SZou Nan hai #define IPI_KDUMP_CPU_STOP 3
661da177e4SLinus Torvalds
671da177e4SLinus Torvalds /* This needs to be cacheline aligned because it is written to by *other* CPUs. */
68e088a4adSMatthew Wilcox static DEFINE_PER_CPU_SHARED_ALIGNED(unsigned long, ipi_operation);
691da177e4SLinus Torvalds
701da177e4SLinus Torvalds extern void cpu_halt (void);
711da177e4SLinus Torvalds
721da177e4SLinus Torvalds static void
stop_this_cpu(void)731da177e4SLinus Torvalds stop_this_cpu(void)
741da177e4SLinus Torvalds {
751da177e4SLinus Torvalds /*
761da177e4SLinus Torvalds * Remove this CPU:
771da177e4SLinus Torvalds */
787d7f9848SSrivatsa S. Bhat set_cpu_online(smp_processor_id(), false);
791da177e4SLinus Torvalds max_xtp();
801da177e4SLinus Torvalds local_irq_disable();
811da177e4SLinus Torvalds cpu_halt();
821da177e4SLinus Torvalds }
831da177e4SLinus Torvalds
841da177e4SLinus Torvalds void
cpu_die(void)851da177e4SLinus Torvalds cpu_die(void)
861da177e4SLinus Torvalds {
871da177e4SLinus Torvalds max_xtp();
881da177e4SLinus Torvalds local_irq_disable();
891da177e4SLinus Torvalds cpu_halt();
901da177e4SLinus Torvalds /* Should never be here */
911da177e4SLinus Torvalds BUG();
921da177e4SLinus Torvalds for (;;);
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds
951da177e4SLinus Torvalds irqreturn_t
handle_IPI(int irq,void * dev_id)96024e4f2cSKeith Owens handle_IPI (int irq, void *dev_id)
971da177e4SLinus Torvalds {
981da177e4SLinus Torvalds int this_cpu = get_cpu();
991da177e4SLinus Torvalds unsigned long *pending_ipis = &__ia64_per_cpu_var(ipi_operation);
1001da177e4SLinus Torvalds unsigned long ops;
1011da177e4SLinus Torvalds
1021da177e4SLinus Torvalds mb(); /* Order interrupt and bit testing. */
1031da177e4SLinus Torvalds while ((ops = xchg(pending_ipis, 0)) != 0) {
1041da177e4SLinus Torvalds mb(); /* Order bit clearing and data access. */
1051da177e4SLinus Torvalds do {
1061da177e4SLinus Torvalds unsigned long which;
1071da177e4SLinus Torvalds
1081da177e4SLinus Torvalds which = ffz(~ops);
1091da177e4SLinus Torvalds ops &= ~(1 << which);
1101da177e4SLinus Torvalds
1111da177e4SLinus Torvalds switch (which) {
1121da177e4SLinus Torvalds case IPI_CPU_STOP:
1131da177e4SLinus Torvalds stop_this_cpu();
1141da177e4SLinus Torvalds break;
115f27b433eSJens Axboe case IPI_CALL_FUNC:
116f27b433eSJens Axboe generic_smp_call_function_interrupt();
117f27b433eSJens Axboe break;
118f27b433eSJens Axboe case IPI_CALL_FUNC_SINGLE:
119f27b433eSJens Axboe generic_smp_call_function_single_interrupt();
120f27b433eSJens Axboe break;
12145a98fc6SHorms #ifdef CONFIG_KEXEC
122a7956113SZou Nan hai case IPI_KDUMP_CPU_STOP:
123a7956113SZou Nan hai unw_init_running(kdump_cpu_freeze, NULL);
124a7956113SZou Nan hai break;
125a7956113SZou Nan hai #endif
1261da177e4SLinus Torvalds default:
127c0cd661bSHidetoshi Seto printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n",
128c0cd661bSHidetoshi Seto this_cpu, which);
1291da177e4SLinus Torvalds break;
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds } while (ops);
1321da177e4SLinus Torvalds mb(); /* Order data access and bit testing. */
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds put_cpu();
1351da177e4SLinus Torvalds return IRQ_HANDLED;
1361da177e4SLinus Torvalds }
1371da177e4SLinus Torvalds
138f27b433eSJens Axboe
139f27b433eSJens Axboe
1401da177e4SLinus Torvalds /*
14172fdbdceSSimon Arlott * Called with preemption disabled.
1421da177e4SLinus Torvalds */
1431da177e4SLinus Torvalds static inline void
send_IPI_single(int dest_cpu,int op)1441da177e4SLinus Torvalds send_IPI_single (int dest_cpu, int op)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds set_bit(op, &per_cpu(ipi_operation, dest_cpu));
14705933aacSChristoph Hellwig ia64_send_ipi(dest_cpu, IA64_IPI_VECTOR, IA64_IPI_DM_INT, 0);
1481da177e4SLinus Torvalds }
1491da177e4SLinus Torvalds
1501da177e4SLinus Torvalds /*
15172fdbdceSSimon Arlott * Called with preemption disabled.
1521da177e4SLinus Torvalds */
1531da177e4SLinus Torvalds static inline void
send_IPI_allbutself(int op)1541da177e4SLinus Torvalds send_IPI_allbutself (int op)
1551da177e4SLinus Torvalds {
1561da177e4SLinus Torvalds unsigned int i;
1571da177e4SLinus Torvalds
158dc565b52Shawkes@sgi.com for_each_online_cpu(i) {
159dc565b52Shawkes@sgi.com if (i != smp_processor_id())
1601da177e4SLinus Torvalds send_IPI_single(i, op);
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds
1641da177e4SLinus Torvalds /*
16572fdbdceSSimon Arlott * Called with preemption disabled.
1661da177e4SLinus Torvalds */
1671da177e4SLinus Torvalds static inline void
send_IPI_mask(const struct cpumask * mask,int op)16840fe697aSRusty Russell send_IPI_mask(const struct cpumask *mask, int op)
16931a6b11fSXiantao Zhang {
17031a6b11fSXiantao Zhang unsigned int cpu;
17131a6b11fSXiantao Zhang
17240fe697aSRusty Russell for_each_cpu(cpu, mask) {
17331a6b11fSXiantao Zhang send_IPI_single(cpu, op);
17431a6b11fSXiantao Zhang }
17531a6b11fSXiantao Zhang }
17631a6b11fSXiantao Zhang
17731a6b11fSXiantao Zhang /*
17831a6b11fSXiantao Zhang * Called with preemption disabled.
17931a6b11fSXiantao Zhang */
18031a6b11fSXiantao Zhang static inline void
send_IPI_all(int op)1811da177e4SLinus Torvalds send_IPI_all (int op)
1821da177e4SLinus Torvalds {
1831da177e4SLinus Torvalds int i;
1841da177e4SLinus Torvalds
185dc565b52Shawkes@sgi.com for_each_online_cpu(i) {
1861da177e4SLinus Torvalds send_IPI_single(i, op);
1871da177e4SLinus Torvalds }
188dc565b52Shawkes@sgi.com }
1891da177e4SLinus Torvalds
1901da177e4SLinus Torvalds /*
19172fdbdceSSimon Arlott * Called with preemption disabled.
1921da177e4SLinus Torvalds */
1931da177e4SLinus Torvalds static inline void
send_IPI_self(int op)1941da177e4SLinus Torvalds send_IPI_self (int op)
1951da177e4SLinus Torvalds {
1961da177e4SLinus Torvalds send_IPI_single(smp_processor_id(), op);
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds
19945a98fc6SHorms #ifdef CONFIG_KEXEC
200a7956113SZou Nan hai void
kdump_smp_send_stop(void)201ccbebdacSAl Viro kdump_smp_send_stop(void)
202a7956113SZou Nan hai {
203a7956113SZou Nan hai send_IPI_allbutself(IPI_KDUMP_CPU_STOP);
204a7956113SZou Nan hai }
205a7956113SZou Nan hai
206a7956113SZou Nan hai void
kdump_smp_send_init(void)207ccbebdacSAl Viro kdump_smp_send_init(void)
208a7956113SZou Nan hai {
209a7956113SZou Nan hai unsigned int cpu, self_cpu;
210a7956113SZou Nan hai self_cpu = smp_processor_id();
211a7956113SZou Nan hai for_each_online_cpu(cpu) {
212a7956113SZou Nan hai if (cpu != self_cpu) {
213a7956113SZou Nan hai if(kdump_status[cpu] == 0)
21405933aacSChristoph Hellwig ia64_send_ipi(cpu, 0, IA64_IPI_DM_INIT, 0);
215a7956113SZou Nan hai }
216a7956113SZou Nan hai }
217a7956113SZou Nan hai }
218a7956113SZou Nan hai #endif
2191da177e4SLinus Torvalds /*
22072fdbdceSSimon Arlott * Called with preemption disabled.
2211da177e4SLinus Torvalds */
2221da177e4SLinus Torvalds void
arch_smp_send_reschedule(int cpu)223*4c8c3c7fSValentin Schneider arch_smp_send_reschedule (int cpu)
2241da177e4SLinus Torvalds {
22505933aacSChristoph Hellwig ia64_send_ipi(cpu, IA64_IPI_RESCHEDULE, IA64_IPI_DM_INT, 0);
2261da177e4SLinus Torvalds }
227*4c8c3c7fSValentin Schneider EXPORT_SYMBOL_GPL(arch_smp_send_reschedule);
2281da177e4SLinus Torvalds
2293be44b9cSJack Steiner /*
23072fdbdceSSimon Arlott * Called with preemption disabled.
2313be44b9cSJack Steiner */
2323be44b9cSJack Steiner static void
smp_send_local_flush_tlb(int cpu)2333be44b9cSJack Steiner smp_send_local_flush_tlb (int cpu)
2343be44b9cSJack Steiner {
23505933aacSChristoph Hellwig ia64_send_ipi(cpu, IA64_IPI_LOCAL_TLB_FLUSH, IA64_IPI_DM_INT, 0);
2363be44b9cSJack Steiner }
2373be44b9cSJack Steiner
2383be44b9cSJack Steiner void
smp_local_flush_tlb(void)2393be44b9cSJack Steiner smp_local_flush_tlb(void)
2403be44b9cSJack Steiner {
2413be44b9cSJack Steiner /*
2423be44b9cSJack Steiner * Use atomic ops. Otherwise, the load/increment/store sequence from
2433be44b9cSJack Steiner * a "++" operation can have the line stolen between the load & store.
2443be44b9cSJack Steiner * The overhead of the atomic op in negligible in this case & offers
2453be44b9cSJack Steiner * significant benefit for the brief periods where lots of cpus
2463be44b9cSJack Steiner * are simultaneously flushing TLBs.
2473be44b9cSJack Steiner */
2483be44b9cSJack Steiner ia64_fetchadd(1, &local_tlb_flush_counts[smp_processor_id()].count, acq);
2493be44b9cSJack Steiner local_flush_tlb_all();
2503be44b9cSJack Steiner }
2513be44b9cSJack Steiner
2523be44b9cSJack Steiner #define FLUSH_DELAY 5 /* Usec backoff to eliminate excessive cacheline bouncing */
2533be44b9cSJack Steiner
2543be44b9cSJack Steiner void
smp_flush_tlb_cpumask(cpumask_t xcpumask)2553be44b9cSJack Steiner smp_flush_tlb_cpumask(cpumask_t xcpumask)
2563be44b9cSJack Steiner {
25797653f92SRobin Holt unsigned short *counts = __ia64_per_cpu_var(shadow_flush_counts);
2583be44b9cSJack Steiner cpumask_t cpumask = xcpumask;
2593be44b9cSJack Steiner int mycpu, cpu, flush_mycpu = 0;
2603be44b9cSJack Steiner
2613be44b9cSJack Steiner preempt_disable();
2623be44b9cSJack Steiner mycpu = smp_processor_id();
2633be44b9cSJack Steiner
2645d2068daSRusty Russell for_each_cpu(cpu, &cpumask)
26597653f92SRobin Holt counts[cpu] = local_tlb_flush_counts[cpu].count & 0xffff;
2663be44b9cSJack Steiner
2673be44b9cSJack Steiner mb();
2685d2068daSRusty Russell for_each_cpu(cpu, &cpumask) {
2693be44b9cSJack Steiner if (cpu == mycpu)
2703be44b9cSJack Steiner flush_mycpu = 1;
2713be44b9cSJack Steiner else
2723be44b9cSJack Steiner smp_send_local_flush_tlb(cpu);
2733be44b9cSJack Steiner }
2743be44b9cSJack Steiner
2753be44b9cSJack Steiner if (flush_mycpu)
2763be44b9cSJack Steiner smp_local_flush_tlb();
2773be44b9cSJack Steiner
2785d2068daSRusty Russell for_each_cpu(cpu, &cpumask)
27997653f92SRobin Holt while(counts[cpu] == (local_tlb_flush_counts[cpu].count & 0xffff))
2803be44b9cSJack Steiner udelay(FLUSH_DELAY);
2813be44b9cSJack Steiner
2823be44b9cSJack Steiner preempt_enable();
2833be44b9cSJack Steiner }
2843be44b9cSJack Steiner
2851da177e4SLinus Torvalds void
smp_flush_tlb_all(void)2861da177e4SLinus Torvalds smp_flush_tlb_all (void)
2871da177e4SLinus Torvalds {
28815c8b6c1SJens Axboe on_each_cpu((void (*)(void *))local_flush_tlb_all, NULL, 1);
2891da177e4SLinus Torvalds }
2901da177e4SLinus Torvalds
2911da177e4SLinus Torvalds void
smp_flush_tlb_mm(struct mm_struct * mm)2921da177e4SLinus Torvalds smp_flush_tlb_mm (struct mm_struct *mm)
2931da177e4SLinus Torvalds {
29475c1c91cSDimitri Sivanich cpumask_var_t cpus;
295a68db763SPeter Chubb preempt_disable();
2961da177e4SLinus Torvalds /* this happens for the common case of a single-threaded fork(): */
2971da177e4SLinus Torvalds if (likely(mm == current->active_mm && atomic_read(&mm->mm_users) == 1))
2981da177e4SLinus Torvalds {
2991da177e4SLinus Torvalds local_finish_flush_tlb_mm(mm);
300a68db763SPeter Chubb preempt_enable();
3011da177e4SLinus Torvalds return;
3021da177e4SLinus Torvalds }
30375c1c91cSDimitri Sivanich if (!alloc_cpumask_var(&cpus, GFP_ATOMIC)) {
30475c1c91cSDimitri Sivanich smp_call_function((void (*)(void *))local_finish_flush_tlb_mm,
30575c1c91cSDimitri Sivanich mm, 1);
30675c1c91cSDimitri Sivanich } else {
30775c1c91cSDimitri Sivanich cpumask_copy(cpus, mm_cpumask(mm));
30875c1c91cSDimitri Sivanich smp_call_function_many(cpus,
309edb91dc0SDimitri Sivanich (void (*)(void *))local_finish_flush_tlb_mm, mm, 1);
31075c1c91cSDimitri Sivanich free_cpumask_var(cpus);
31175c1c91cSDimitri Sivanich }
312edb91dc0SDimitri Sivanich local_irq_disable();
313edb91dc0SDimitri Sivanich local_finish_flush_tlb_mm(mm);
314edb91dc0SDimitri Sivanich local_irq_enable();
315a68db763SPeter Chubb preempt_enable();
3161da177e4SLinus Torvalds }
3171da177e4SLinus Torvalds
arch_send_call_function_single_ipi(int cpu)318f27b433eSJens Axboe void arch_send_call_function_single_ipi(int cpu)
3191da177e4SLinus Torvalds {
320f27b433eSJens Axboe send_IPI_single(cpu, IPI_CALL_FUNC_SINGLE);
3211da177e4SLinus Torvalds }
3221da177e4SLinus Torvalds
arch_send_call_function_ipi_mask(const struct cpumask * mask)32340fe697aSRusty Russell void arch_send_call_function_ipi_mask(const struct cpumask *mask)
32431a6b11fSXiantao Zhang {
32531a6b11fSXiantao Zhang send_IPI_mask(mask, IPI_CALL_FUNC);
32631a6b11fSXiantao Zhang }
3271da177e4SLinus Torvalds
3281da177e4SLinus Torvalds /*
3291da177e4SLinus Torvalds * this function calls the 'stop' function on all other CPUs in the system.
3301da177e4SLinus Torvalds */
3311da177e4SLinus Torvalds void
smp_send_stop(void)3321da177e4SLinus Torvalds smp_send_stop (void)
3331da177e4SLinus Torvalds {
3341da177e4SLinus Torvalds send_IPI_allbutself(IPI_CPU_STOP);
3351da177e4SLinus Torvalds }
336