Blame view
drivers/firmware/memmap.c
12 KB
69ac9cd62 sysfs: add /sys/f... |
1 2 3 |
/* * linux/drivers/firmware/memmap.c * Copyright (C) 2008 SUSE LINUX Products GmbH |
97bef7dd0 Bernhard has moved |
4 |
* by Bernhard Walle <bernhard.walle@gmx.de> |
69ac9cd62 sysfs: add /sys/f... |
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License v2.0 as published by * the Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include <linux/string.h> #include <linux/firmware-map.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/types.h> #include <linux/bootmem.h> |
5a0e3ad6a include cleanup: ... |
23 |
#include <linux/slab.h> |
46c66c4b7 memory-hotplug: r... |
24 |
#include <linux/mm.h> |
69ac9cd62 sysfs: add /sys/f... |
25 26 27 28 29 30 31 32 33 34 35 |
/* * Data types ------------------------------------------------------------------ */ /* * Firmware map entry. Because firmware memory maps are flat and not * hierarchical, it's ok to organise them in a linked list. No parent * information is necessary as for the resource tree. */ struct firmware_map_entry { |
3b0fde0fa firmware_map: fix... |
36 37 38 39 40 41 |
/* * start and end must be u64 rather than resource_size_t, because e820 * resources can lie at addresses above 4G. */ u64 start; /* start of the memory range */ u64 end; /* end of the memory range (incl.) */ |
69ac9cd62 sysfs: add /sys/f... |
42 43 44 45 46 47 48 49 50 51 52 53 54 |
const char *type; /* type of the memory range */ struct list_head list; /* entry for the linked list */ struct kobject kobj; /* kobject for each entry */ }; /* * Forward declarations -------------------------------------------------------- */ static ssize_t memmap_attr_show(struct kobject *kobj, struct attribute *attr, char *buf); static ssize_t start_show(struct firmware_map_entry *entry, char *buf); static ssize_t end_show(struct firmware_map_entry *entry, char *buf); static ssize_t type_show(struct firmware_map_entry *entry, char *buf); |
46c66c4b7 memory-hotplug: r... |
55 56 |
static struct firmware_map_entry * __meminit firmware_map_find_entry(u64 start, u64 end, const char *type); |
69ac9cd62 sysfs: add /sys/f... |
57 58 59 60 61 62 63 64 |
/* * Static data ----------------------------------------------------------------- */ struct memmap_attribute { struct attribute attr; ssize_t (*show)(struct firmware_map_entry *entry, char *buf); }; |
da2bdf9a6 Make various thin... |
65 66 67 |
static struct memmap_attribute memmap_start_attr = __ATTR_RO(start); static struct memmap_attribute memmap_end_attr = __ATTR_RO(end); static struct memmap_attribute memmap_type_attr = __ATTR_RO(type); |
69ac9cd62 sysfs: add /sys/f... |
68 69 70 71 72 73 74 75 76 77 |
/* * These are default attributes that are added for every memmap entry. */ static struct attribute *def_attrs[] = { &memmap_start_attr.attr, &memmap_end_attr.attr, &memmap_type_attr.attr, NULL }; |
52cf25d0a Driver core: Cons... |
78 |
static const struct sysfs_ops memmap_attr_ops = { |
69ac9cd62 sysfs: add /sys/f... |
79 80 |
.show = memmap_attr_show, }; |
46c66c4b7 memory-hotplug: r... |
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
/* Firmware memory map entries. */ static LIST_HEAD(map_entries); static DEFINE_SPINLOCK(map_entries_lock); /* * For memory hotplug, there is no way to free memory map entries allocated * by boot mem after the system is up. So when we hot-remove memory whose * map entry is allocated by bootmem, we need to remember the storage and * reuse it when the memory is hot-added again. */ static LIST_HEAD(map_entries_bootmem); static DEFINE_SPINLOCK(map_entries_bootmem_lock); static inline struct firmware_map_entry * to_memmap_entry(struct kobject *kobj) { return container_of(kobj, struct firmware_map_entry, kobj); } static void __meminit release_firmware_map_entry(struct kobject *kobj) { struct firmware_map_entry *entry = to_memmap_entry(kobj); if (PageReserved(virt_to_page(entry))) { /* * Remember the storage allocated by bootmem, and reuse it when * the memory is hot-added again. The entry will be added to * map_entries_bootmem here, and deleted from &map_entries in * firmware_map_remove_entry(). */ |
7a6f93b0a firmware, memmap:... |
112 113 114 |
spin_lock(&map_entries_bootmem_lock); list_add(&entry->list, &map_entries_bootmem); spin_unlock(&map_entries_bootmem_lock); |
46c66c4b7 memory-hotplug: r... |
115 116 117 118 119 120 121 122 123 |
return; } kfree(entry); } static struct kobj_type __refdata memmap_ktype = { .release = release_firmware_map_entry, |
69ac9cd62 sysfs: add /sys/f... |
124 125 126 127 128 129 130 |
.sysfs_ops = &memmap_attr_ops, .default_attrs = def_attrs, }; /* * Registration functions ------------------------------------------------------ */ |
69ac9cd62 sysfs: add /sys/f... |
131 |
/** |
31bad9246 firmware/memmap: ... |
132 |
* firmware_map_add_entry() - Does the real work to add a firmware memmap entry. |
69ac9cd62 sysfs: add /sys/f... |
133 |
* @start: Start of the memory range. |
4ed940d4c firmware_map: mak... |
134 |
* @end: End of the memory range (exclusive). |
69ac9cd62 sysfs: add /sys/f... |
135 136 137 |
* @type: Type of the memory range. * @entry: Pre-allocated (either kmalloc() or bootmem allocator), uninitialised * entry. |
31bad9246 firmware/memmap: ... |
138 139 140 |
* * Common implementation of firmware_map_add() and firmware_map_add_early() * which expects a pre-allocated struct firmware_map_entry. |
cbdc28101 drivers/firmware/... |
141 142 143 |
* * Return: 0 always */ |
3b0fde0fa firmware_map: fix... |
144 |
static int firmware_map_add_entry(u64 start, u64 end, |
69ac9cd62 sysfs: add /sys/f... |
145 146 147 148 149 150 |
const char *type, struct firmware_map_entry *entry) { BUG_ON(start > end); entry->start = start; |
4ed940d4c firmware_map: mak... |
151 |
entry->end = end - 1; |
69ac9cd62 sysfs: add /sys/f... |
152 153 154 |
entry->type = type; INIT_LIST_HEAD(&entry->list); kobject_init(&entry->kobj, &memmap_ktype); |
46c66c4b7 memory-hotplug: r... |
155 |
spin_lock(&map_entries_lock); |
69ac9cd62 sysfs: add /sys/f... |
156 |
list_add_tail(&entry->list, &map_entries); |
46c66c4b7 memory-hotplug: r... |
157 |
spin_unlock(&map_entries_lock); |
69ac9cd62 sysfs: add /sys/f... |
158 159 160 |
return 0; } |
46c66c4b7 memory-hotplug: r... |
161 162 163 164 165 166 |
/** * firmware_map_remove_entry() - Does the real work to remove a firmware * memmap entry. * @entry: removed entry. * * The caller must hold map_entries_lock, and release it properly. |
cbdc28101 drivers/firmware/... |
167 |
*/ |
46c66c4b7 memory-hotplug: r... |
168 169 170 171 |
static inline void firmware_map_remove_entry(struct firmware_map_entry *entry) { list_del(&entry->list); } |
d96ae5309 memory-hotplug: c... |
172 173 174 175 176 177 178 |
/* * Add memmap entry on sysfs */ static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry) { static int map_entries_nr; static struct kset *mmap_kset; |
22880ebe7 drivers/firmware/... |
179 180 |
if (entry->kobj.state_in_sysfs) return -EEXIST; |
d96ae5309 memory-hotplug: c... |
181 182 183 184 185 186 187 188 189 190 191 192 |
if (!mmap_kset) { mmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj); if (!mmap_kset) return -ENOMEM; } entry->kobj.kset = mmap_kset; if (kobject_add(&entry->kobj, NULL, "%d", map_entries_nr++)) kobject_put(&entry->kobj); return 0; } |
46c66c4b7 memory-hotplug: r... |
193 194 195 196 197 198 199 |
/* * Remove memmap entry on sysfs */ static inline void remove_sysfs_fw_map_entry(struct firmware_map_entry *entry) { kobject_put(&entry->kobj); } |
cbdc28101 drivers/firmware/... |
200 |
/** |
46c66c4b7 memory-hotplug: r... |
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
* firmware_map_find_entry_in_list() - Search memmap entry in a given list. * @start: Start of the memory range. * @end: End of the memory range (exclusive). * @type: Type of the memory range. * @list: In which to find the entry. * * This function is to find the memmap entey of a given memory range in a * given list. The caller must hold map_entries_lock, and must not release * the lock until the processing of the returned entry has completed. * * Return: Pointer to the entry to be found on success, or NULL on failure. */ static struct firmware_map_entry * __meminit firmware_map_find_entry_in_list(u64 start, u64 end, const char *type, struct list_head *list) { struct firmware_map_entry *entry; list_for_each_entry(entry, list, list) if ((entry->start == start) && (entry->end == end) && (!strcmp(entry->type, type))) { return entry; } return NULL; } |
cbdc28101 drivers/firmware/... |
227 |
/** |
46c66c4b7 memory-hotplug: r... |
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
* firmware_map_find_entry() - Search memmap entry in map_entries. * @start: Start of the memory range. * @end: End of the memory range (exclusive). * @type: Type of the memory range. * * This function is to find the memmap entey of a given memory range. * The caller must hold map_entries_lock, and must not release the lock * until the processing of the returned entry has completed. * * Return: Pointer to the entry to be found on success, or NULL on failure. */ static struct firmware_map_entry * __meminit firmware_map_find_entry(u64 start, u64 end, const char *type) { return firmware_map_find_entry_in_list(start, end, type, &map_entries); } |
cbdc28101 drivers/firmware/... |
244 |
/** |
46c66c4b7 memory-hotplug: r... |
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
* firmware_map_find_entry_bootmem() - Search memmap entry in map_entries_bootmem. * @start: Start of the memory range. * @end: End of the memory range (exclusive). * @type: Type of the memory range. * * This function is similar to firmware_map_find_entry except that it find the * given entry in map_entries_bootmem. * * Return: Pointer to the entry to be found on success, or NULL on failure. */ static struct firmware_map_entry * __meminit firmware_map_find_entry_bootmem(u64 start, u64 end, const char *type) { return firmware_map_find_entry_in_list(start, end, type, &map_entries_bootmem); } |
31bad9246 firmware/memmap: ... |
261 |
/** |
d96ae5309 memory-hotplug: c... |
262 263 |
* firmware_map_add_hotplug() - Adds a firmware mapping entry when we do * memory hotplug. |
31bad9246 firmware/memmap: ... |
264 |
* @start: Start of the memory range. |
4ed940d4c firmware_map: mak... |
265 |
* @end: End of the memory range (exclusive) |
31bad9246 firmware/memmap: ... |
266 267 |
* @type: Type of the memory range. * |
d96ae5309 memory-hotplug: c... |
268 269 270 |
* Adds a firmware mapping entry. This function is for memory hotplug, it is * similar to function firmware_map_add_early(). The only difference is that * it will create the syfs entry dynamically. |
31bad9246 firmware/memmap: ... |
271 |
* |
cbdc28101 drivers/firmware/... |
272 273 |
* Return: 0 on success, or -ENOMEM if no memory could be allocated. */ |
d96ae5309 memory-hotplug: c... |
274 |
int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type) |
69ac9cd62 sysfs: add /sys/f... |
275 276 |
{ struct firmware_map_entry *entry; |
f0093ede9 drivers/firmware/... |
277 278 279 |
entry = firmware_map_find_entry(start, end - 1, type); if (entry) return 0; |
49c8b24d0 drivers/firmware/... |
280 |
entry = firmware_map_find_entry_bootmem(start, end - 1, type); |
46c66c4b7 memory-hotplug: r... |
281 282 283 284 285 286 287 288 289 290 291 292 |
if (!entry) { entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC); if (!entry) return -ENOMEM; } else { /* Reuse storage allocated by bootmem. */ spin_lock(&map_entries_bootmem_lock); list_del(&entry->list); spin_unlock(&map_entries_bootmem_lock); memset(entry, 0, sizeof(*entry)); } |
69ac9cd62 sysfs: add /sys/f... |
293 |
|
d96ae5309 memory-hotplug: c... |
294 295 296 297 298 |
firmware_map_add_entry(start, end, type, entry); /* create the memmap entry */ add_sysfs_fw_map_entry(entry); return 0; |
69ac9cd62 sysfs: add /sys/f... |
299 |
} |
31bad9246 firmware/memmap: ... |
300 301 302 |
/** * firmware_map_add_early() - Adds a firmware mapping entry. * @start: Start of the memory range. |
4ed940d4c firmware_map: mak... |
303 |
* @end: End of the memory range. |
31bad9246 firmware/memmap: ... |
304 305 306 |
* @type: Type of the memory range. * * Adds a firmware mapping entry. This function uses the bootmem allocator |
d96ae5309 memory-hotplug: c... |
307 |
* for memory allocation. |
31bad9246 firmware/memmap: ... |
308 309 310 |
* * That function must be called before late_initcall. * |
cbdc28101 drivers/firmware/... |
311 312 |
* Return: 0 on success, or -ENOMEM if no memory could be allocated. */ |
3b0fde0fa firmware_map: fix... |
313 |
int __init firmware_map_add_early(u64 start, u64 end, const char *type) |
69ac9cd62 sysfs: add /sys/f... |
314 315 |
{ struct firmware_map_entry *entry; |
4fc0bc58c drivers/firmware/... |
316 |
entry = memblock_virt_alloc(sizeof(struct firmware_map_entry), 0); |
31bad9246 firmware/memmap: ... |
317 |
if (WARN_ON(!entry)) |
69ac9cd62 sysfs: add /sys/f... |
318 319 320 321 |
return -ENOMEM; return firmware_map_add_entry(start, end, type, entry); } |
46c66c4b7 memory-hotplug: r... |
322 323 324 325 326 327 328 329 |
/** * firmware_map_remove() - remove a firmware mapping entry * @start: Start of the memory range. * @end: End of the memory range. * @type: Type of the memory range. * * removes a firmware mapping entry. * |
cbdc28101 drivers/firmware/... |
330 331 |
* Return: 0 on success, or -EINVAL if no entry. */ |
46c66c4b7 memory-hotplug: r... |
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
int __meminit firmware_map_remove(u64 start, u64 end, const char *type) { struct firmware_map_entry *entry; spin_lock(&map_entries_lock); entry = firmware_map_find_entry(start, end - 1, type); if (!entry) { spin_unlock(&map_entries_lock); return -EINVAL; } firmware_map_remove_entry(entry); spin_unlock(&map_entries_lock); /* remove the memmap entry */ remove_sysfs_fw_map_entry(entry); return 0; } |
69ac9cd62 sysfs: add /sys/f... |
351 352 353 354 355 356 |
/* * Sysfs functions ------------------------------------------------------------- */ static ssize_t start_show(struct firmware_map_entry *entry, char *buf) { |
78681ac08 firmware: fix mem... |
357 358 359 |
return snprintf(buf, PAGE_SIZE, "0x%llx ", (unsigned long long)entry->start); |
69ac9cd62 sysfs: add /sys/f... |
360 361 362 363 |
} static ssize_t end_show(struct firmware_map_entry *entry, char *buf) { |
78681ac08 firmware: fix mem... |
364 365 366 |
return snprintf(buf, PAGE_SIZE, "0x%llx ", (unsigned long long)entry->end); |
69ac9cd62 sysfs: add /sys/f... |
367 368 369 370 371 372 373 |
} static ssize_t type_show(struct firmware_map_entry *entry, char *buf) { return snprintf(buf, PAGE_SIZE, "%s ", entry->type); } |
46c66c4b7 memory-hotplug: r... |
374 375 376 377 |
static inline struct memmap_attribute *to_memmap_attr(struct attribute *attr) { return container_of(attr, struct memmap_attribute, attr); } |
69ac9cd62 sysfs: add /sys/f... |
378 379 380 381 382 383 384 385 386 387 388 389 390 |
static ssize_t memmap_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct firmware_map_entry *entry = to_memmap_entry(kobj); struct memmap_attribute *memmap_attr = to_memmap_attr(attr); return memmap_attr->show(entry, buf); } /* * Initialises stuff and adds the entries in the map_entries list to * sysfs. Important is that firmware_map_add() and firmware_map_add_early() |
31bad9246 firmware/memmap: ... |
391 392 393 394 |
* must be called before late_initcall. That's just because that function * is called as late_initcall() function, which means that if you call * firmware_map_add() or firmware_map_add_early() afterwards, the entries * are not added to sysfs. |
69ac9cd62 sysfs: add /sys/f... |
395 |
*/ |
bac716966 firmware/memmap: ... |
396 |
static int __init firmware_memmap_init(void) |
69ac9cd62 sysfs: add /sys/f... |
397 |
{ |
69ac9cd62 sysfs: add /sys/f... |
398 |
struct firmware_map_entry *entry; |
69ac9cd62 sysfs: add /sys/f... |
399 |
|
d96ae5309 memory-hotplug: c... |
400 401 |
list_for_each_entry(entry, &map_entries, list) add_sysfs_fw_map_entry(entry); |
69ac9cd62 sysfs: add /sys/f... |
402 403 404 |
return 0; } |
bac716966 firmware/memmap: ... |
405 |
late_initcall(firmware_memmap_init); |
69ac9cd62 sysfs: add /sys/f... |
406 |