Commit 46c66c4b7ba0f9bb3e2ae3a3cfd40cd3472c8f80

Authored by Yasuaki Ishimatsu
Committed by Linus Torvalds
1 parent bbc76be67c

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>

Showing 3 changed files with 193 additions and 14 deletions Side-by-side Diff

drivers/firmware/memmap.c
... ... @@ -21,6 +21,7 @@
21 21 #include <linux/types.h>
22 22 #include <linux/bootmem.h>
23 23 #include <linux/slab.h>
  24 +#include <linux/mm.h>
24 25  
25 26 /*
26 27 * Data types ------------------------------------------------------------------
... ... @@ -52,6 +53,9 @@
52 53 static ssize_t end_show(struct firmware_map_entry *entry, char *buf);
53 54 static ssize_t type_show(struct firmware_map_entry *entry, char *buf);
54 55  
  56 +static struct firmware_map_entry * __meminit
  57 +firmware_map_find_entry(u64 start, u64 end, const char *type);
  58 +
55 59 /*
56 60 * Static data -----------------------------------------------------------------
57 61 */
... ... @@ -79,7 +83,52 @@
79 83 .show = memmap_attr_show,
80 84 };
81 85  
82   -static struct kobj_type memmap_ktype = {
  86 +/* Firmware memory map entries. */
  87 +static LIST_HEAD(map_entries);
  88 +static 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 + */
  96 +static LIST_HEAD(map_entries_bootmem);
  97 +static DEFINE_SPINLOCK(map_entries_bootmem_lock);
  98 +
  99 +
  100 +static inline struct firmware_map_entry *
  101 +to_memmap_entry(struct kobject *kobj)
  102 +{
  103 + return container_of(kobj, struct firmware_map_entry, kobj);
  104 +}
  105 +
  106 +static 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 +
  130 +static struct kobj_type __refdata memmap_ktype = {
  131 + .release = release_firmware_map_entry,
83 132 .sysfs_ops = &memmap_attr_ops,
84 133 .default_attrs = def_attrs,
85 134 };
... ... @@ -88,13 +137,6 @@
88 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   - */
96   -static LIST_HEAD(map_entries);
97   -
98 140 /**
99 141 * firmware_map_add_entry() - Does the real work to add a firmware memmap entry.
100 142 * @start: Start of the memory range.
101 143  
102 144  
... ... @@ -118,11 +160,25 @@
118 160 INIT_LIST_HEAD(&entry->list);
119 161 kobject_init(&entry->kobj, &memmap_ktype);
120 162  
  163 + spin_lock(&map_entries_lock);
121 164 list_add_tail(&entry->list, &map_entries);
  165 + spin_unlock(&map_entries_lock);
122 166  
123 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 + **/
  177 +static inline void firmware_map_remove_entry(struct firmware_map_entry *entry)
  178 +{
  179 + list_del(&entry->list);
  180 +}
  181 +
126 182 /*
127 183 * Add memmap entry on sysfs
128 184 */
... ... @@ -144,6 +200,78 @@
144 200 return 0;
145 201 }
146 202  
  203 +/*
  204 + * Remove memmap entry on sysfs
  205 + */
  206 +static 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 + */
  224 +static struct firmware_map_entry * __meminit
  225 +firmware_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 + */
  251 +static struct firmware_map_entry * __meminit
  252 +firmware_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 + */
  268 +static struct firmware_map_entry * __meminit
  269 +firmware_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 276 * firmware_map_add_hotplug() - Adds a firmware mapping entry when we do
149 277 * memory hotplug.
150 278  
... ... @@ -161,10 +289,20 @@
161 289 {
162 290 struct firmware_map_entry *entry;
163 291  
164   - entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC);
165   - if (!entry)
166   - return -ENOMEM;
  292 + entry = firmware_map_find_entry_bootmem(start, end, type);
  293 + if (!entry) {
  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);
167 302  
  303 + memset(entry, 0, sizeof(*entry));
  304 + }
  305 +
168 306 firmware_map_add_entry(start, end, type, entry);
169 307 /* create the memmap entry */
170 308 add_sysfs_fw_map_entry(entry);
... ... @@ -196,6 +334,36 @@
196 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 + **/
  347 +int __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 368 * Sysfs functions -------------------------------------------------------------
201 369 */
... ... @@ -217,8 +385,10 @@
217 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)
221   -#define to_memmap_entry(obj) container_of(obj, struct firmware_map_entry, kobj)
  388 +static inline struct memmap_attribute *to_memmap_attr(struct attribute *attr)
  389 +{
  390 + return container_of(attr, struct memmap_attribute, attr);
  391 +}
222 392  
223 393 static ssize_t memmap_attr_show(struct kobject *kobj,
224 394 struct attribute *attr, char *buf)
include/linux/firmware-map.h
... ... @@ -25,6 +25,7 @@
25 25  
26 26 int firmware_map_add_early(u64 start, u64 end, const char *type);
27 27 int firmware_map_add_hotplug(u64 start, u64 end, const char *type);
  28 +int firmware_map_remove(u64 start, u64 end, const char *type);
28 29  
29 30 #else /* CONFIG_FIRMWARE_MEMMAP */
30 31  
... ... @@ -34,6 +35,11 @@
34 35 }
35 36  
36 37 static inline int firmware_map_add_hotplug(u64 start, u64 end, const char *type)
  38 +{
  39 + return 0;
  40 +}
  41 +
  42 +static inline int firmware_map_remove(u64 start, u64 end, const char *type)
37 43 {
38 44 return 0;
39 45 }
... ... @@ -1460,7 +1460,7 @@
1460 1460 return ret;
1461 1461 }
1462 1462  
1463   -int remove_memory(u64 start, u64 size)
  1463 +int __ref remove_memory(u64 start, u64 size)
1464 1464 {
1465 1465 unsigned long start_pfn, end_pfn;
1466 1466 int ret = 0;
... ... @@ -1509,6 +1509,9 @@
1509 1509 unlock_memory_hotplug();
1510 1510 return ret;
1511 1511 }
  1512 +
  1513 + /* remove memmap entry */
  1514 + firmware_map_remove(start, start + size, "System RAM");
1512 1515  
1513 1516 unlock_memory_hotplug();
1514 1517