diff options
-rw-r--r-- | drivers/firmware/memmap.c | 196 | ||||
-rw-r--r-- | include/linux/firmware-map.h | 6 | ||||
-rw-r--r-- | mm/memory_hotplug.c | 5 |
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); | |||
52 | static ssize_t end_show(struct firmware_map_entry *entry, char *buf); | 53 | static ssize_t end_show(struct firmware_map_entry *entry, char *buf); |
53 | static ssize_t type_show(struct firmware_map_entry *entry, char *buf); | 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 | * 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 | ||
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 | .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 | */ | ||
96 | static 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 | **/ | ||
177 | static 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 | */ | ||
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 | * 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 | **/ | ||
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 | * 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) | 388 | static 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 | ||
223 | static ssize_t memmap_attr_show(struct kobject *kobj, | 393 | static 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 | ||
26 | int firmware_map_add_early(u64 start, u64 end, const char *type); | 26 | int firmware_map_add_early(u64 start, u64 end, const char *type); |
27 | int firmware_map_add_hotplug(u64 start, u64 end, const char *type); | 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 | #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 | ||
42 | static 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 | ||
1463 | int remove_memory(u64 start, u64 size) | 1463 | int __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; |