Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Yasuaki Ishimatsu and committed by
Linus Torvalds
46c66c4b bbc76be6

+193 -14
+183 -13
drivers/firmware/memmap.c
··· 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 ------------------------------------------------------------------ ··· 53 52 static ssize_t end_show(struct firmware_map_entry *entry, char *buf); 54 53 static ssize_t type_show(struct firmware_map_entry *entry, char *buf); 55 54 55 + static struct firmware_map_entry * __meminit 56 + firmware_map_find_entry(u64 start, u64 end, const char *type); 57 + 56 58 /* 57 59 * Static data ----------------------------------------------------------------- 58 60 */ ··· 83 79 .show = memmap_attr_show, 84 80 }; 85 81 86 - static struct kobj_type memmap_ktype = { 82 + /* Firmware memory map entries. */ 83 + static LIST_HEAD(map_entries); 84 + static DEFINE_SPINLOCK(map_entries_lock); 85 + 86 + /* 87 + * For memory hotplug, there is no way to free memory map entries allocated 88 + * by boot mem after the system is up. So when we hot-remove memory whose 89 + * map entry is allocated by bootmem, we need to remember the storage and 90 + * reuse it when the memory is hot-added again. 91 + */ 92 + static LIST_HEAD(map_entries_bootmem); 93 + static DEFINE_SPINLOCK(map_entries_bootmem_lock); 94 + 95 + 96 + static inline struct firmware_map_entry * 97 + to_memmap_entry(struct kobject *kobj) 98 + { 99 + return container_of(kobj, struct firmware_map_entry, kobj); 100 + } 101 + 102 + static void __meminit release_firmware_map_entry(struct kobject *kobj) 103 + { 104 + struct firmware_map_entry *entry = to_memmap_entry(kobj); 105 + 106 + if (PageReserved(virt_to_page(entry))) { 107 + /* 108 + * Remember the storage allocated by bootmem, and reuse it when 109 + * the memory is hot-added again. The entry will be added to 110 + * map_entries_bootmem here, and deleted from &map_entries in 111 + * firmware_map_remove_entry(). 112 + */ 113 + if (firmware_map_find_entry(entry->start, entry->end, 114 + entry->type)) { 115 + spin_lock(&map_entries_bootmem_lock); 116 + list_add(&entry->list, &map_entries_bootmem); 117 + spin_unlock(&map_entries_bootmem_lock); 118 + } 119 + 120 + return; 121 + } 122 + 123 + kfree(entry); 124 + } 125 + 126 + static struct kobj_type __refdata memmap_ktype = { 127 + .release = release_firmware_map_entry, 87 128 .sysfs_ops = &memmap_attr_ops, 88 129 .default_attrs = def_attrs, 89 130 }; ··· 136 87 /* 137 88 * Registration functions ------------------------------------------------------ 138 89 */ 139 - 140 - /* 141 - * Firmware memory map entries. No locking is needed because the 142 - * firmware_map_add() and firmware_map_add_early() functions are called 143 - * in firmware initialisation code in one single thread of execution. 144 - */ 145 - static LIST_HEAD(map_entries); 146 90 147 91 /** 148 92 * firmware_map_add_entry() - Does the real work to add a firmware memmap entry. ··· 160 118 INIT_LIST_HEAD(&entry->list); 161 119 kobject_init(&entry->kobj, &memmap_ktype); 162 120 121 + spin_lock(&map_entries_lock); 163 122 list_add_tail(&entry->list, &map_entries); 123 + spin_unlock(&map_entries_lock); 164 124 165 125 return 0; 126 + } 127 + 128 + /** 129 + * firmware_map_remove_entry() - Does the real work to remove a firmware 130 + * memmap entry. 131 + * @entry: removed entry. 132 + * 133 + * The caller must hold map_entries_lock, and release it properly. 134 + **/ 135 + static inline void firmware_map_remove_entry(struct firmware_map_entry *entry) 136 + { 137 + list_del(&entry->list); 166 138 } 167 139 168 140 /* ··· 200 144 return 0; 201 145 } 202 146 147 + /* 148 + * Remove memmap entry on sysfs 149 + */ 150 + static inline void remove_sysfs_fw_map_entry(struct firmware_map_entry *entry) 151 + { 152 + kobject_put(&entry->kobj); 153 + } 154 + 155 + /* 156 + * firmware_map_find_entry_in_list() - Search memmap entry in a given list. 157 + * @start: Start of the memory range. 158 + * @end: End of the memory range (exclusive). 159 + * @type: Type of the memory range. 160 + * @list: In which to find the entry. 161 + * 162 + * This function is to find the memmap entey of a given memory range in a 163 + * given list. The caller must hold map_entries_lock, and must not release 164 + * the lock until the processing of the returned entry has completed. 165 + * 166 + * Return: Pointer to the entry to be found on success, or NULL on failure. 167 + */ 168 + static struct firmware_map_entry * __meminit 169 + firmware_map_find_entry_in_list(u64 start, u64 end, const char *type, 170 + struct list_head *list) 171 + { 172 + struct firmware_map_entry *entry; 173 + 174 + list_for_each_entry(entry, list, list) 175 + if ((entry->start == start) && (entry->end == end) && 176 + (!strcmp(entry->type, type))) { 177 + return entry; 178 + } 179 + 180 + return NULL; 181 + } 182 + 183 + /* 184 + * firmware_map_find_entry() - Search memmap entry in map_entries. 185 + * @start: Start of the memory range. 186 + * @end: End of the memory range (exclusive). 187 + * @type: Type of the memory range. 188 + * 189 + * This function is to find the memmap entey of a given memory range. 190 + * The caller must hold map_entries_lock, and must not release the lock 191 + * until the processing of the returned entry has completed. 192 + * 193 + * Return: Pointer to the entry to be found on success, or NULL on failure. 194 + */ 195 + static struct firmware_map_entry * __meminit 196 + firmware_map_find_entry(u64 start, u64 end, const char *type) 197 + { 198 + return firmware_map_find_entry_in_list(start, end, type, &map_entries); 199 + } 200 + 201 + /* 202 + * firmware_map_find_entry_bootmem() - Search memmap entry in map_entries_bootmem. 203 + * @start: Start of the memory range. 204 + * @end: End of the memory range (exclusive). 205 + * @type: Type of the memory range. 206 + * 207 + * This function is similar to firmware_map_find_entry except that it find the 208 + * given entry in map_entries_bootmem. 209 + * 210 + * Return: Pointer to the entry to be found on success, or NULL on failure. 211 + */ 212 + static struct firmware_map_entry * __meminit 213 + firmware_map_find_entry_bootmem(u64 start, u64 end, const char *type) 214 + { 215 + return firmware_map_find_entry_in_list(start, end, type, 216 + &map_entries_bootmem); 217 + } 218 + 203 219 /** 204 220 * firmware_map_add_hotplug() - Adds a firmware mapping entry when we do 205 221 * memory hotplug. ··· 289 161 { 290 162 struct firmware_map_entry *entry; 291 163 292 - entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC); 293 - if (!entry) 294 - return -ENOMEM; 164 + entry = firmware_map_find_entry_bootmem(start, end, type); 165 + if (!entry) { 166 + entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC); 167 + if (!entry) 168 + return -ENOMEM; 169 + } else { 170 + /* Reuse storage allocated by bootmem. */ 171 + spin_lock(&map_entries_bootmem_lock); 172 + list_del(&entry->list); 173 + spin_unlock(&map_entries_bootmem_lock); 174 + 175 + memset(entry, 0, sizeof(*entry)); 176 + } 295 177 296 178 firmware_map_add_entry(start, end, type, entry); 297 179 /* create the memmap entry */ ··· 334 196 return firmware_map_add_entry(start, end, type, entry); 335 197 } 336 198 199 + /** 200 + * firmware_map_remove() - remove a firmware mapping entry 201 + * @start: Start of the memory range. 202 + * @end: End of the memory range. 203 + * @type: Type of the memory range. 204 + * 205 + * removes a firmware mapping entry. 206 + * 207 + * Returns 0 on success, or -EINVAL if no entry. 208 + **/ 209 + int __meminit firmware_map_remove(u64 start, u64 end, const char *type) 210 + { 211 + struct firmware_map_entry *entry; 212 + 213 + spin_lock(&map_entries_lock); 214 + entry = firmware_map_find_entry(start, end - 1, type); 215 + if (!entry) { 216 + spin_unlock(&map_entries_lock); 217 + return -EINVAL; 218 + } 219 + 220 + firmware_map_remove_entry(entry); 221 + spin_unlock(&map_entries_lock); 222 + 223 + /* remove the memmap entry */ 224 + remove_sysfs_fw_map_entry(entry); 225 + 226 + return 0; 227 + } 228 + 337 229 /* 338 230 * Sysfs functions ------------------------------------------------------------- 339 231 */ ··· 385 217 return snprintf(buf, PAGE_SIZE, "%s\n", entry->type); 386 218 } 387 219 388 - #define to_memmap_attr(_attr) container_of(_attr, struct memmap_attribute, attr) 389 - #define to_memmap_entry(obj) container_of(obj, struct firmware_map_entry, kobj) 220 + static inline struct memmap_attribute *to_memmap_attr(struct attribute *attr) 221 + { 222 + return container_of(attr, struct memmap_attribute, attr); 223 + } 390 224 391 225 static ssize_t memmap_attr_show(struct kobject *kobj, 392 226 struct attribute *attr, char *buf)
+6
include/linux/firmware-map.h
··· 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 ··· 35 34 } 36 35 37 36 static inline int firmware_map_add_hotplug(u64 start, u64 end, const char *type) 37 + { 38 + return 0; 39 + } 40 + 41 + static inline int firmware_map_remove(u64 start, u64 end, const char *type) 38 42 { 39 43 return 0; 40 44 }
+4 -1
mm/memory_hotplug.c
··· 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 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