aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorYasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>2013-02-22 19:32:56 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-23 20:50:12 -0500
commit46c66c4b7ba0f9bb3e2ae3a3cfd40cd3472c8f80 (patch)
treeeb33a350aab3adae19a91a6e63918df1a3904585 /drivers/firmware
parentbbc76be67c2c0c12548937a07ea3643c32a95b8c (diff)
memory-hotplug: remove /sys/firmware/memmap/X sysfs
When (hot)adding memory into system, /sys/firmware/memmap/X/{end, start, type} sysfs files are created. But there is no code to remove these files. This patch implements the function to remove them. We cannot free firmware_map_entry which is allocated by bootmem because there is no way to do so when the system is up. But we can at least remember the address of that memory and reuse the storage when the memory is added next time. This patch also introduces a new list map_entries_bootmem to link the map entries allocated by bootmem when they are removed, and a lock to protect it. And these entries will be reused when the memory is hot-added again. The idea is suggestted by Andrew Morton. NOTE: It is unsafe to return an entry pointer and release the map_entries_lock. So we should not hold the map_entries_lock separately in firmware_map_find_entry() and firmware_map_remove_entry(). Hold the map_entries_lock across find and remove /sys/firmware/memmap/X operation. And also, users of these two functions need to be careful to hold the lock when using these two functions. [tangchen@cn.fujitsu.com: Hold spinlock across find|remove /sys operation] [tangchen@cn.fujitsu.com: fix the wrong comments of map_entries] [tangchen@cn.fujitsu.com: reuse the storage of /sys/firmware/memmap/X/ allocated by bootmem] [tangchen@cn.fujitsu.com: fix section mismatch problem] [tangchen@cn.fujitsu.com: fix the doc format in drivers/firmware/memmap.c] Signed-off-by: Wen Congyang <wency@cn.fujitsu.com> Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com> Signed-off-by: Tang Chen <tangchen@cn.fujitsu.com> Reviewed-by: Kamezawa Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Cc: Jiang Liu <jiang.liu@huawei.com> Cc: Jianguo Wu <wujianguo@huawei.com> Cc: Lai Jiangshan <laijs@cn.fujitsu.com> Cc: Tang Chen <tangchen@cn.fujitsu.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Julian Calaby <julian.calaby@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/memmap.c196
1 files changed, 183 insertions, 13 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)