1 /* 2 * OPAL asynchronus Memory error handling support in PowreNV. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 * 18 * Copyright 2013 IBM Corporation 19 * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> 20 */ 21 22 #undef DEBUG 23 24 #include <linux/kernel.h> 25 #include <linux/init.h> 26 #include <linux/of.h> 27 #include <linux/mm.h> 28 #include <linux/slab.h> 29 30 #include <asm/opal.h> 31 #include <asm/cputable.h> 32 33 static int opal_mem_err_nb_init; 34 static LIST_HEAD(opal_memory_err_list); 35 static DEFINE_SPINLOCK(opal_mem_err_lock); 36 37 struct OpalMsgNode { 38 struct list_head list; 39 struct opal_msg msg; 40 }; 41 42 static void handle_memory_error_event(struct OpalMemoryErrorData *merr_evt) 43 { 44 uint64_t paddr_start, paddr_end; 45 46 pr_debug("%s: Retrived memory error event, type: 0x%x\n", 47 __func__, merr_evt->type); 48 switch (merr_evt->type) { 49 case OPAL_MEM_ERR_TYPE_RESILIENCE: 50 paddr_start = merr_evt->u.resilience.physical_address_start; 51 paddr_end = merr_evt->u.resilience.physical_address_end; 52 break; 53 case OPAL_MEM_ERR_TYPE_DYN_DALLOC: 54 paddr_start = merr_evt->u.dyn_dealloc.physical_address_start; 55 paddr_end = merr_evt->u.dyn_dealloc.physical_address_end; 56 break; 57 default: 58 return; 59 } 60 61 for (; paddr_start < paddr_end; paddr_start += PAGE_SIZE) { 62 memory_failure(paddr_start >> PAGE_SHIFT, 0, 0); 63 } 64 } 65 66 static void handle_memory_error(void) 67 { 68 unsigned long flags; 69 struct OpalMemoryErrorData *merr_evt; 70 struct OpalMsgNode *msg_node; 71 72 spin_lock_irqsave(&opal_mem_err_lock, flags); 73 while (!list_empty(&opal_memory_err_list)) { 74 msg_node = list_entry(opal_memory_err_list.next, 75 struct OpalMsgNode, list); 76 list_del(&msg_node->list); 77 spin_unlock_irqrestore(&opal_mem_err_lock, flags); 78 79 merr_evt = (struct OpalMemoryErrorData *) 80 &msg_node->msg.params[0]; 81 handle_memory_error_event(merr_evt); 82 kfree(msg_node); 83 spin_lock_irqsave(&opal_mem_err_lock, flags); 84 } 85 spin_unlock_irqrestore(&opal_mem_err_lock, flags); 86 } 87 88 static void mem_error_handler(struct work_struct *work) 89 { 90 handle_memory_error(); 91 } 92 93 static DECLARE_WORK(mem_error_work, mem_error_handler); 94 95 /* 96 * opal_memory_err_event - notifier handler that queues up the opal message 97 * to be preocessed later. 98 */ 99 static int opal_memory_err_event(struct notifier_block *nb, 100 unsigned long msg_type, void *msg) 101 { 102 unsigned long flags; 103 struct OpalMsgNode *msg_node; 104 105 if (msg_type != OPAL_MSG_MEM_ERR) 106 return 0; 107 108 msg_node = kzalloc(sizeof(*msg_node), GFP_ATOMIC); 109 if (!msg_node) { 110 pr_err("MEMORY_ERROR: out of memory, Opal message event not" 111 "handled\n"); 112 return -ENOMEM; 113 } 114 memcpy(&msg_node->msg, msg, sizeof(struct opal_msg)); 115 116 spin_lock_irqsave(&opal_mem_err_lock, flags); 117 list_add(&msg_node->list, &opal_memory_err_list); 118 spin_unlock_irqrestore(&opal_mem_err_lock, flags); 119 120 schedule_work(&mem_error_work); 121 return 0; 122 } 123 124 static struct notifier_block opal_mem_err_nb = { 125 .notifier_call = opal_memory_err_event, 126 .next = NULL, 127 .priority = 0, 128 }; 129 130 static int __init opal_mem_err_init(void) 131 { 132 int ret; 133 134 if (!opal_mem_err_nb_init) { 135 ret = opal_message_notifier_register( 136 OPAL_MSG_MEM_ERR, &opal_mem_err_nb); 137 if (ret) { 138 pr_err("%s: Can't register OPAL event notifier (%d)\n", 139 __func__, ret); 140 return ret; 141 } 142 opal_mem_err_nb_init = 1; 143 } 144 return 0; 145 } 146 subsys_initcall(opal_mem_err_init); 147