244 lines · 5.4 KB · C
RawDownload
/* * Copyright 2010 Tilera Corporation. All Rights Reserved. * *   This program is free software; you can redistribute it and/or *   modify it under the terms of the GNU General Public License *   as published by the Free Software Foundation, version 2. * *   This program is distributed in the hope that it will be useful, but *   WITHOUT ANY WARRANTY; without even the implied warranty of *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or *   NON INFRINGEMENT.  See the GNU General Public License for *   more details. * * TILE SMP support routines. */ #include <linux/smp.h>#include <linux/interrupt.h>#include <linux/io.h>#include <linux/irq.h>#include <linux/module.h>#include <asm/cacheflush.h> HV_Topology smp_topology __write_once;EXPORT_SYMBOL(smp_topology); #if CHIP_HAS_IPI()static unsigned long __iomem *ipi_mappings[NR_CPUS];#endif  /* * Top-level send_IPI*() functions to send messages to other cpus. */ /* Set by smp_send_stop() to avoid recursive panics. */static int stopping_cpus; static void __send_IPI_many(HV_Recipient *recip, int nrecip, int tag){	int sent = 0;	while (sent < nrecip) {		int rc = hv_send_message(recip, nrecip,					 (HV_VirtAddr)&tag, sizeof(tag));		if (rc < 0) {			if (!stopping_cpus)  /* avoid recursive panic */				panic("hv_send_message returned %d", rc);			break;		}		WARN_ONCE(rc == 0, "hv_send_message() returned zero\n");		sent += rc;	}} void send_IPI_single(int cpu, int tag){	HV_Recipient recip = {		.y = cpu / smp_width,		.x = cpu % smp_width,		.state = HV_TO_BE_SENT	};	__send_IPI_many(&recip, 1, tag);} void send_IPI_many(const struct cpumask *mask, int tag){	HV_Recipient recip[NR_CPUS];	int cpu;	int nrecip = 0;	int my_cpu = smp_processor_id();	for_each_cpu(cpu, mask) {		HV_Recipient *r;		BUG_ON(cpu == my_cpu);		r = &recip[nrecip++];		r->y = cpu / smp_width;		r->x = cpu % smp_width;		r->state = HV_TO_BE_SENT;	}	__send_IPI_many(recip, nrecip, tag);} void send_IPI_allbutself(int tag){	struct cpumask mask;	cpumask_copy(&mask, cpu_online_mask);	cpumask_clear_cpu(smp_processor_id(), &mask);	send_IPI_many(&mask, tag);} /* * Functions related to starting/stopping cpus. */ /* Handler to start the current cpu. */static void smp_start_cpu_interrupt(void){	get_irq_regs()->pc = start_cpu_function_addr;} /* Handler to stop the current cpu. */static void smp_stop_cpu_interrupt(void){	set_cpu_online(smp_processor_id(), 0);	arch_local_irq_disable_all();	for (;;)		asm("nap; nop");} /* This function calls the 'stop' function on all other CPUs in the system. */void smp_send_stop(void){	stopping_cpus = 1;	send_IPI_allbutself(MSG_TAG_STOP_CPU);} /* On panic, just wait; we may get an smp_send_stop() later on. */void panic_smp_self_stop(void){	while (1)		asm("nap; nop");} /* * Dispatch code called from hv_message_intr() for HV_MSG_TILE hv messages. */void evaluate_message(int tag){	switch (tag) {	case MSG_TAG_START_CPU: /* Start up a cpu */		smp_start_cpu_interrupt();		break; 	case MSG_TAG_STOP_CPU: /* Sent to shut down slave CPU's */		smp_stop_cpu_interrupt();		break; 	case MSG_TAG_CALL_FUNCTION_MANY: /* Call function on cpumask */		generic_smp_call_function_interrupt();		break; 	case MSG_TAG_CALL_FUNCTION_SINGLE: /* Call function on one other CPU */		generic_smp_call_function_single_interrupt();		break; 	default:		panic("Unknown IPI message tag %d", tag);		break;	}}  /* * flush_icache_range() code uses smp_call_function(). */ struct ipi_flush {	unsigned long start;	unsigned long end;}; static void ipi_flush_icache_range(void *info){	struct ipi_flush *flush = (struct ipi_flush *) info;	__flush_icache_range(flush->start, flush->end);} void flush_icache_range(unsigned long start, unsigned long end){	struct ipi_flush flush = { start, end };	preempt_disable();	on_each_cpu(ipi_flush_icache_range, &flush, 1);	preempt_enable();}  /* Called when smp_send_reschedule() triggers IRQ_RESCHEDULE. */static irqreturn_t handle_reschedule_ipi(int irq, void *token){	__get_cpu_var(irq_stat).irq_resched_count++;	scheduler_ipi(); 	return IRQ_HANDLED;} static struct irqaction resched_action = {	.handler = handle_reschedule_ipi,	.name = "resched",	.dev_id = handle_reschedule_ipi /* unique token */,}; void __init ipi_init(void){#if CHIP_HAS_IPI()	int cpu;	/* Map IPI trigger MMIO addresses. */	for_each_possible_cpu(cpu) {		HV_Coord tile;		HV_PTE pte;		unsigned long offset; 		tile.x = cpu_x(cpu);		tile.y = cpu_y(cpu);		if (hv_get_ipi_pte(tile, KERNEL_PL, &pte) != 0)			panic("Failed to initialize IPI for cpu %d\n", cpu); 		offset = PFN_PHYS(pte_pfn(pte));		ipi_mappings[cpu] = ioremap_prot(offset, PAGE_SIZE, pte);	}#endif 	/* Bind handle_reschedule_ipi() to IRQ_RESCHEDULE. */	tile_irq_activate(IRQ_RESCHEDULE, TILE_IRQ_PERCPU);	BUG_ON(setup_irq(IRQ_RESCHEDULE, &resched_action));} #if CHIP_HAS_IPI() void smp_send_reschedule(int cpu){	WARN_ON(cpu_is_offline(cpu)); 	/*	 * We just want to do an MMIO store.  The traditional writeq()	 * functions aren't really correct here, since they're always	 * directed at the PCI shim.  For now, just do a raw store,	 * casting away the __iomem attribute.	 */	((unsigned long __force *)ipi_mappings[cpu])[IRQ_RESCHEDULE] = 0;} #else void smp_send_reschedule(int cpu){	HV_Coord coord; 	WARN_ON(cpu_is_offline(cpu)); 	coord.y = cpu_y(cpu);	coord.x = cpu_x(cpu);	hv_trigger_ipi(coord, IRQ_RESCHEDULE);} #endif /* CHIP_HAS_IPI() */

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t