| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/percpu.h>
 | 
					
						
							|  |  |  | #include <linux/smp.h>
 | 
					
						
							|  |  |  | #include <linux/hardirq.h>
 | 
					
						
							|  |  |  | #include <linux/ptrace.h>
 | 
					
						
							|  |  |  | #include <asm/hv_driver.h>
 | 
					
						
							|  |  |  | #include <asm/irq_regs.h>
 | 
					
						
							| 
									
										
										
										
											2010-06-25 17:04:17 -04:00
										 |  |  | #include <asm/traps.h>
 | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | #include <hv/hypervisor.h>
 | 
					
						
							|  |  |  | #include <arch/interrupts.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* All messages are stored here */ | 
					
						
							|  |  |  | static DEFINE_PER_CPU(HV_MsgState, msg_state); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-25 17:04:17 -04:00
										 |  |  | void __cpuinit init_messaging(void) | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	/* Allocate storage for messages in kernel space */ | 
					
						
							|  |  |  | 	HV_MsgState *state = &__get_cpu_var(msg_state); | 
					
						
							|  |  |  | 	int rc = hv_register_message_state(state); | 
					
						
							|  |  |  | 	if (rc != HV_OK) | 
					
						
							|  |  |  | 		panic("hv_register_message_state: error %d", rc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Make sure downcall interrupts will be enabled. */ | 
					
						
							| 
									
										
										
										
											2010-11-01 15:24:29 -04:00
										 |  |  | 	arch_local_irq_unmask(INT_INTCTRL_K); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void hv_message_intr(struct pt_regs *regs, int intnum) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * We enter with interrupts disabled and leave them disabled, | 
					
						
							|  |  |  | 	 * to match expectations of called functions (e.g. | 
					
						
							|  |  |  | 	 * do_ccupdate_local() in mm/slab.c).  This is also consistent | 
					
						
							|  |  |  | 	 * with normal call entry for device interrupts. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int message[HV_MAX_MESSAGE_SIZE/sizeof(int)]; | 
					
						
							|  |  |  | 	HV_RcvMsgInfo rmi; | 
					
						
							|  |  |  | 	int nmsgs = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Track time spent here in an interrupt context */ | 
					
						
							|  |  |  | 	struct pt_regs *old_regs = set_irq_regs(regs); | 
					
						
							|  |  |  | 	irq_enter(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_DEBUG_STACKOVERFLOW
 | 
					
						
							|  |  |  | 	/* Debugging check for stack overflow: less than 1/8th stack free? */ | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		long sp = stack_pointer - (long) current_thread_info(); | 
					
						
							|  |  |  | 		if (unlikely(sp < (sizeof(struct thread_info) + STACK_WARN))) { | 
					
						
							| 
									
										
										
										
											2010-06-25 17:04:17 -04:00
										 |  |  | 			pr_emerg("hv_message_intr: " | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 			       "stack overflow: %ld\n", | 
					
						
							|  |  |  | 			       sp - sizeof(struct thread_info)); | 
					
						
							|  |  |  | 			dump_stack(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (1) { | 
					
						
							|  |  |  | 		rmi = hv_receive_message(__get_cpu_var(msg_state), | 
					
						
							|  |  |  | 					 (HV_VirtAddr) message, | 
					
						
							|  |  |  | 					 sizeof(message)); | 
					
						
							|  |  |  | 		if (rmi.msglen == 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (rmi.msglen < 0) | 
					
						
							|  |  |  | 			panic("hv_receive_message failed: %d", rmi.msglen); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		++nmsgs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (rmi.source == HV_MSG_TILE) { | 
					
						
							|  |  |  | 			int tag; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* we just send tags for now */ | 
					
						
							|  |  |  | 			BUG_ON(rmi.msglen != sizeof(int)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			tag = message[0]; | 
					
						
							|  |  |  | #ifdef CONFIG_SMP
 | 
					
						
							|  |  |  | 			evaluate_message(message[0]); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 			panic("Received IPI message %d in UP mode", tag); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 		} else if (rmi.source == HV_MSG_INTR) { | 
					
						
							|  |  |  | 			HV_IntrMsg *him = (HV_IntrMsg *)message; | 
					
						
							|  |  |  | 			struct hv_driver_cb *cb = | 
					
						
							|  |  |  | 				(struct hv_driver_cb *)him->intarg; | 
					
						
							|  |  |  | 			cb->callback(cb, him->intdata); | 
					
						
							|  |  |  | 			__get_cpu_var(irq_stat).irq_hv_msg_count++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * We shouldn't have gotten a message downcall with no | 
					
						
							|  |  |  | 	 * messages available. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (nmsgs == 0) | 
					
						
							|  |  |  | 		panic("Message downcall invoked with no messages!"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Track time spent against the current process again and | 
					
						
							|  |  |  | 	 * process any softirqs if they are waiting. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	irq_exit(); | 
					
						
							|  |  |  | 	set_irq_regs(old_regs); | 
					
						
							|  |  |  | } |