aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/firmware/memmap.c196
-rw-r--r--include/linux/firmware-map.h6
-rw-r--r--mm/memory_hotplug.c5
3 files changed, 193 insertions, 14 deletions
diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c
index 90723e65b081..0b5b5f619c75 100644
--- a/drivers/firmware/memmap.c
+++ b/drivers/firmware/memmap.c
@@ -21,6 +21,7 @@
21#include <linux/types.h> 21#include <linux/types.h>
22#include <linux/bootmem.h> 22#include <linux/bootmem.h>
23#include <linux/slab.h> 23#include <linux/slab.h>
24#include <linux/mm.h>
24 25
25/* 26/*
26 * Data types ------------------------------------------------------------------ 27 * Data types ------------------------------------------------------------------
@@ -52,6 +53,9 @@ static ssize_t start_show(struct firmware_map_entry *entry, char *buf);
52static ssize_t end_show(struct firmware_map_entry *entry, char *buf); 53static ssize_t end_show(struct firmware_map_entry *entry, char *buf);
53static ssize_t type_show(struct firmware_map_entry *entry, char *buf); 54static ssize_t type_show(struct firmware_map_entry *entry, char *buf);
54 55
56static struct firmware_map_entry * __meminit
57firmware_map_find_entry(u64 start, u64 end, const char *type);
58
55/* 59/*
56 * Static data ----------------------------------------------------------------- 60 * Static data -----------------------------------------------------------------
57 */ 61 */
@@ -79,7 +83,52 @@ static const struct sysfs_ops memmap_attr_ops = {
79 .show = memmap_attr_show, 83 .show = memmap_attr_show,
80}; 84};
81 85
82static struct kobj_type memmap_ktype = { 86/* Firmware memory map entries. */
87static LIST_HEAD(map_entries);
88static DEFINE_SPINLOCK(map_entries_lock);
89
90/*
91 * For memory hotplug, there is no way to free memory map entries allocated
92 * by boot mem after the system is up. So when we hot-remove memory whose
93 * map entry is allocated by bootmem, we need to remember the storage and
94 * reuse it when the memory is hot-added again.
95 */
96static LIST_HEAD(map_entries_bootmem);
97static DEFINE_SPINLOCK(map_entries_bootmem_lock);
98
99
100static inline struct firmware_map_entry *
101to_memmap_entry(struct kobject *kobj)
102{
103 return container_of(kobj, struct firmware_map_entry, kobj);
104}
105
106static void __meminit release_firmware_map_entry(struct kobject *kobj)
107{
108 struct firmware_map_entry *entry = to_memmap_entry(kobj);
109
110 if (PageReserved(virt_to_page(entry))) {
111 /*
112 * Remember the storage allocated by bootmem, and reuse it when
113 * the memory is hot-added again. The entry will be added to
114 * map_entries_bootmem here, and deleted from &map_entries in
115 * firmware_map_remove_entry().
116 */
117 if (firmware_map_find_entry(entry->start, entry->end,
118 entry->type)) {
119 spin_lock(&map_entries_bootmem_lock);
120 list_add(&entry->list, &map_entries_bootmem);
121 spin_unlock(&map_entries_bootmem_lock);
122 }
123
124 return;
125 }
126
127 kfree(entry);
128}
129
130static struct kobj_type __refdata memmap_ktype = {
131 .release = release_firmware_map_entry,
83 .sysfs_ops = &memmap_attr_ops, 132 .sysfs_ops = &memmap_attr_ops,
84 .default_attrs = def_attrs, 133 .default_attrs = def_attrs,
85}; 134};
@@ -88,13 +137,6 @@ static struct kobj_type memmap_ktype = {
88 * Registration functions ------------------------------------------------------ 137 * Registration functions ------------------------------------------------------
89 */ 138 */
90 139
91/*
92 * Firmware memory map entries. No locking is needed because the
93 * firmware_map_add() and firmware_map_add_early() functions are called
94 * in firmware initialisation code in one single thread of execution.
95 */
96static LIST_HEAD(map_entries);
97
98/** 140/**
99 * firmware_map_add_entry() - Does the real work to add a firmware memmap entry. 141 * firmware_map_add_entry() - Does the real work to add a firmware memmap entry.
100 * @start: Start of the memory range. 142 * @start: Start of the memory range.
@@ -118,11 +160,25 @@ static int firmware_map_add_entry(u64 start, u64 end,
118 INIT_LIST_HEAD(&entry->list); 160 INIT_LIST_HEAD(&entry->list);
119 kobject_init(&entry->kobj, &memmap_ktype); 161 kobject_init(&entry->kobj, &memmap_ktype);
120 162
163 spin_lock(&map_entries_lock);
121 list_add_tail(&entry->list, &map_entries); 164 list_add_tail(&entry->list, &map_entries);
165 spin_unlock(&map_entries_lock);
122 166
123 return 0; 167 return 0;
124} 168}
125 169
170/**
171 * firmware_map_remove_entry() - Does the real work to remove a firmware
172 * memmap entry.
173 * @entry: removed entry.
174 *
175 * The caller must hold map_entries_lock, and release it properly.
176 **/
177static inline void firmware_map_remove_entry(struct firmware_map_entry *entry)
178{
179 list_del(&entry->list);
180}
181
126/* 182/*
127 * Add memmap entry on sysfs 183 * Add memmap entry on sysfs
128 */ 184 */
@@ -144,6 +200,78 @@ static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry)
144 return 0; 200 return 0;
145} 201}
146 202
203/*
204 * Remove memmap entry on sysfs
205 */
206static inline void remove_sysfs_fw_map_entry(struct firmware_map_entry *entry)
207{
208 kobject_put(&entry->kobj);
209}
210
211/*
212 * firmware_map_find_entry_in_list() - Search memmap entry in a given list.
213 * @start: Start of the memory range.
214 * @end: End of the memory range (exclusive).
215 * @type: Type of the memory range.
216 * @list: In which to find the entry.
217 *
218 * This function is to find the memmap entey of a given memory range in a
219 * given list. The caller must hold map_entries_lock, and must not release
220 * the lock until the processing of the returned entry has completed.
221 *
222 * Return: Pointer to the entry to be found on success, or NULL on failure.
223 */
224static struct firmware_map_entry * __meminit
225firmware_map_find_entry_in_list(u64 start, u64 end, const char *type,
226 struct list_head *list)
227{
228 struct firmware_map_entry *entry;
229
230 list_for_each_entry(entry, list, list)
231 if ((entry->start == start) && (entry->end == end) &&
232 (!strcmp(entry->type, type))) {
233 return entry;
234 }
235
236 return NULL;
237}
238
239/*
240 * firmware_map_find_entry() - Search memmap entry in map_entries.
241 * @start: Start of the memory range.
242 * @end: End of the memory range (exclusive).
243 * @type: Type of the memory range.
244 *
245 * This function is to find the memmap entey of a given memory range.
246 * The caller must hold map_entries_lock, and must not release the lock
247 * until the processing of the returned entry has completed.
248 *
249 * Return: Pointer to the entry to be found on success, or NULL on failure.
250 */
251static struct firmware_map_entry * __meminit
252firmware_map_find_entry(u64 start, u64 end, const char *type)
253{
254 return firmware_map_find_entry_in_list(start, end, type, &map_entries);
255}
256
257/*
258 * firmware_map_find_entry_bootmem() - Search memmap entry in map_entries_bootmem.
259 * @start: Start of the memory range.
260 * @end: End of the memory range (exclusive).
261 * @type: Type of the memory range.
262 *
263 * This function is similar to firmware_map_find_entry except that it find the
264 * given entry in map_entries_bootmem.
265 *
266 * Return: Pointer to the entry to be found on success, or NULL on failure.
267 */
268static struct firmware_map_entry * __meminit
269firmware_map_find_entry_bootmem(u64 start, u64 end, const char *type)
270{
271 return firmware_map_find_entry_in_list(start, end, type,
272 &map_entries_bootmem);
273}
274
147/** 275/**
148 * firmware_map_add_hotplug() - Adds a firmware mapping entry when we do 276 * firmware_map_add_hotplug() - Adds a firmware mapping entry when we do
149 * memory hotplug. 277 * memory hotplug.
@@ -161,9 +289,19 @@ int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type)
161{ 289{
162 struct firmware_map_entry *entry; 290 struct firmware_map_entry *entry;
163 291
164 entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC); 292 entry = firmware_map_find_entry_bootmem(start, end, type);
165 if (!entry) 293 if (!entry) {
166 return -ENOMEM; 294 entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC);
295 if (!entry)
296 return -ENOMEM;
297 } else {
298 /* Reuse storage allocated by bootmem. */
299 spin_lock(&map_entries_bootmem_lock);
300 list_del(&entry->list);
301 spin_unlock(&map_entries_bootmem_lock);
302
303 memset(entry, 0, sizeof(*entry));
304 }
167 305
168 firmware_map_add_entry(start, end, type, entry); 306 firmware_map_add_entry(start, end, type, entry);
169 /* create the memmap entry */ 307 /* create the memmap entry */
@@ -196,6 +334,36 @@ int __init firmware_map_add_early(u64 start, u64 end, const char *type)
196 return firmware_map_add_entry(start, end, type, entry); 334 return firmware_map_add_entry(start, end, type, entry);
197} 335}
198 336
337/**
338 * firmware_map_remove() - remove a firmware mapping entry
339 * @start: Start of the memory range.
340 * @end: End of the memory range.
341 * @type: Type of the memory range.
342 *
343 * removes a firmware mapping entry.
344 *
345 * Returns 0 on success, or -EINVAL if no entry.
346 **/
347int __meminit firmware_map_remove(u64 start, u64 end, const char *type)
348{
349 struct firmware_map_entry *entry;
350
351 spin_lock(&map_entries_lock);
352 entry = firmware_map_find_entry(start, end - 1, type);
353 if (!entry) {
354 spin_unlock(&map_entries_lock);
355 return -EINVAL;
356 }
357
358 firmware_map_remove_entry(entry);
359 spin_unlock(&map_entries_lock);
360
361 /* remove the memmap entry */
362 remove_sysfs_fw_map_entry(entry);
363
364 return 0;
365}
366
199/* 367/*
200 * Sysfs functions ------------------------------------------------------------- 368 * Sysfs functions -------------------------------------------------------------
201 */ 369 */
@@ -217,8 +385,10 @@ static ssize_t type_show(struct firmware_map_entry *entry, char *buf)
217 return snprintf(buf, PAGE_SIZE, "%s\n", entry->type); 385 return snprintf(buf, PAGE_SIZE, "%s\n", entry->type);
218} 386}
219 387
220#define to_memmap_attr(_attr) container_of(_attr, struct memmap_attribute, attr) 388static inline struct memmap_attribute *to_memmap_attr(struct attribute *attr)
221#define to_memmap_entry(obj) container_of(obj, struct firmware_map_entry, kobj) 389{
390 return container_of(attr, struct memmap_attribute, attr);
391}
222 392
223static ssize_t memmap_attr_show(struct kobject *kobj, 393static ssize_t memmap_attr_show(struct kobject *kobj,
224 struct attribute *attr, char *buf) 394 struct attribute *attr, char *buf)
diff --git a/include/linux/firmware-map.h b/include/linux/firmware-map.h
index 43fe52fcef0f..71d4fa721db9 100644
--- a/include/linux/firmware-map.h
+++ b/include/linux/firmware-map.h
@@ -25,6 +25,7 @@
25 25
26int firmware_map_add_early(u64 start, u64 end, const char *type); 26int firmware_map_add_early(u64 start, u64 end, const char *type);
27int firmware_map_add_hotplug(u64 start, u64 end, const char *type); 27int firmware_map_add_hotplug(u64 start, u64 end, const char *type);
28int firmware_map_remove(u64 start, u64 end, const char *type);
28 29
29#else /* CONFIG_FIRMWARE_MEMMAP */ 30#else /* CONFIG_FIRMWARE_MEMMAP */
30 31
@@ -38,6 +39,11 @@ static inline int firmware_map_add_hotplug(u64 start, u64 end, const char *type)
38 return 0; 39 return 0;
39} 40}
40 41
42static inline int firmware_map_remove(u64 start, u64 end, const char *type)
43{
44 return 0;
45}
46
41#endif /* CONFIG_FIRMWARE_MEMMAP */ 47#endif /* CONFIG_FIRMWARE_MEMMAP */
42 48
43#endif /* _LINUX_FIRMWARE_MAP_H */ 49#endif /* _LINUX_FIRMWARE_MAP_H */
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index e5b91b12cec3..a776dbf3fa00 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1460,7 +1460,7 @@ static int is_memblock_offlined_cb(struct memory_block *mem, void *arg)
1460 return ret; 1460 return ret;
1461} 1461}
1462 1462
1463int remove_memory(u64 start, u64 size) 1463int __ref remove_memory(u64 start, u64 size)
1464{ 1464{
1465 unsigned long start_pfn, end_pfn; 1465 unsigned long start_pfn, end_pfn;
1466 int ret = 0; 1466 int ret = 0;
@@ -1510,6 +1510,9 @@ repeat:
1510 return ret; 1510 return ret;
1511 } 1511 }
1512 1512
1513 /* remove memmap entry */
1514 firmware_map_remove(start, start + size, "System RAM");
1515
1513 unlock_memory_hotplug(); 1516 unlock_memory_hotplug();
1514 1517
1515 return 0; 1518 return 0;