]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - arch/powerpc/platforms/powernv/opal-memory-errors.c
Merge tag 'staging-3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[karo-tx-linux.git] / arch / powerpc / platforms / powernv / opal-memory-errors.c
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);