Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / core / acpi_memhotplug.c
1 /*
2  *  xen_acpi_memhotplug.c - interface to notify Xen on memory device hotadd
3  *
4  *  Copyright (C) 2008, Intel corporation
5  *
6  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or (at
11  *  your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful, but
14  *  WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License along
19  *  with this program; if not, write to the Free Software Foundation, Inc.,
20  *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21  *
22  */
23
24 #include <xen/interface/platform.h>
25 #include <asm/hypervisor.h>
26
27 struct xen_hotmem_entry {
28         struct list_head hotmem_list;
29         uint64_t start;
30         uint64_t end;
31         uint32_t flags;
32         uint32_t pxm;
33 };
34
35 struct xen_hotmem_list {
36         struct list_head list;
37         unsigned int entry_nr;
38 };
39
40 static struct xen_hotmem_list xen_hotmem = {
41         .list = LIST_HEAD_INIT(xen_hotmem.list)
42 };
43 static DEFINE_SPINLOCK(xen_hotmem_lock);
44
45 static int xen_hyper_addmem(struct xen_hotmem_entry *entry)
46 {
47         xen_platform_op_t op;
48
49         op.cmd = XENPF_mem_hotadd;
50         op.u.mem_add.spfn = entry->start >> PAGE_SHIFT;
51         op.u.mem_add.epfn = entry->end >> PAGE_SHIFT;
52         op.u.mem_add.flags = entry->flags;
53         op.u.mem_add.pxm = entry->pxm;
54
55         return HYPERVISOR_platform_op(&op);
56 }
57
58 static int add_hotmem_entry(int pxm, uint64_t start,
59                         uint64_t length, uint32_t flags)
60 {
61         struct xen_hotmem_entry *entry;
62
63         if (pxm < 0 || !length)
64                 return -EINVAL;
65
66         entry = kzalloc(sizeof(struct xen_hotmem_entry), GFP_ATOMIC);
67         if (!entry)
68                 return -ENOMEM;
69
70         INIT_LIST_HEAD(&entry->hotmem_list);
71         entry->start = start;
72         entry->end = start + length;
73         entry->flags = flags;
74         entry->pxm = pxm;
75
76         spin_lock(&xen_hotmem_lock);
77
78         list_add_tail(&entry->hotmem_list, &xen_hotmem.list);
79         xen_hotmem.entry_nr++;
80
81         spin_unlock(&xen_hotmem_lock);
82
83         return 0;
84 }
85
86 static int free_hotmem_entry(struct xen_hotmem_entry *entry)
87 {
88         list_del(&entry->hotmem_list);
89         kfree(entry);
90
91         return 0;
92 }
93
94 static void xen_hotadd_mem_dpc(struct work_struct *work)
95 {
96         struct list_head *elem, *tmp;
97         struct xen_hotmem_entry *entry;
98         unsigned long flags;
99         int ret;
100
101         spin_lock_irqsave(&xen_hotmem_lock, flags);
102         list_for_each_safe(elem, tmp, &xen_hotmem.list) {
103                 entry = list_entry(elem, struct xen_hotmem_entry, hotmem_list);
104                 ret = xen_hyper_addmem(entry);
105                 if (ret)
106                         pr_warn("xen addmem failed with %x\n", ret);
107                 free_hotmem_entry(entry);
108                 xen_hotmem.entry_nr--;
109         }
110         spin_unlock_irqrestore(&xen_hotmem_lock, flags);
111 }
112
113 static DECLARE_WORK(xen_hotadd_mem_work, xen_hotadd_mem_dpc);
114
115 static int xen_acpi_get_pxm(acpi_handle h)
116 {
117         unsigned long long pxm;
118         acpi_status status;
119         acpi_handle handle;
120         acpi_handle phandle = h;
121
122         do {
123                 handle = phandle;
124                 status = acpi_evaluate_integer(handle, "_PXM", NULL, &pxm);
125                 if (ACPI_SUCCESS(status))
126                         return pxm;
127                 status = acpi_get_parent(handle, &phandle);
128         } while (ACPI_SUCCESS(status));
129
130         return -1;
131 }
132
133 static int xen_hotadd_memory(struct acpi_memory_device *mem_device)
134 {
135         int pxm, result;
136         int num_enabled = 0;
137         struct acpi_memory_info *info;
138
139         if (!mem_device)
140                 return -EINVAL;
141
142         pxm = xen_acpi_get_pxm(mem_device->device->handle);
143
144         if (pxm < 0)
145                 return -EINVAL;
146
147         /*
148          * Always return success to ACPI driver, and notify hypervisor later
149          * because hypervisor will utilize the memory in memory hotadd hypercall
150          */
151         list_for_each_entry(info, &mem_device->res_list, list) {
152                 if (info->enabled) { /* just sanity check...*/
153                         num_enabled++;
154                         continue;
155                 }
156                 /*
157                  * If the memory block size is zero, please ignore it.
158                  * Don't try to do the following memory hotplug flowchart.
159                  */
160                 if (!info->length)
161                         continue;
162
163                 result = add_hotmem_entry(pxm, info->start_addr,
164                                           info->length, 0);
165                 if (result)
166                         continue;
167                 info->enabled = 1;
168                 num_enabled++;
169         }
170
171         if (!num_enabled)
172                 return -EINVAL;
173
174         schedule_work(&xen_hotadd_mem_work);
175
176         return 0;
177 }
178
179 static int xen_hotadd_mem_init(void)
180 {
181         if (!is_initial_xendomain())
182                 return -ENODEV;
183
184         return 0;
185 }
186
187 static void xen_hotadd_mem_exit(void)
188 {
189         flush_scheduled_work();
190 }