xref: /openbmc/linux/arch/ia64/kernel/smp.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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