| /* |
| * 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> |
| #include <asm/traps.h> |
| #include <hv/hypervisor.h> |
| #include <arch/interrupts.h> |
| |
| /* All messages are stored here */ |
| static DEFINE_PER_CPU(HV_MsgState, msg_state); |
| |
| void init_messaging(void) |
| { |
| /* 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. */ |
| arch_local_irq_unmask(INT_INTCTRL_K); |
| } |
| |
| 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))) { |
| pr_emerg("hv_message_intr: " |
| "stack overflow: %ld\n", |
| sp - sizeof(struct thread_info)); |
| dump_stack(); |
| } |
| } |
| #endif |
| |
| while (1) { |
| HV_MsgState *state = this_cpu_ptr(&msg_state); |
| rmi = hv_receive_message(*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); |
| } |