Blame view
drivers/base/memory.c
16.3 KB
3947be196 [PATCH] memory ho... |
1 |
/* |
10fbcf4c6 convert 'memory' ... |
2 |
* Memory subsystem support |
3947be196 [PATCH] memory ho... |
3 4 5 6 7 8 9 10 11 |
* * Written by Matt Tolentino <matthew.e.tolentino@intel.com> * Dave Hansen <haveblue@us.ibm.com> * * This file provides the necessary infrastructure to represent * a SPARSEMEM-memory-model system's physical memory in /sysfs. * All arch-independent code that assumes MEMORY_HOTPLUG requires * SPARSEMEM should be contained here, or in mm/memory_hotplug.c. */ |
3947be196 [PATCH] memory ho... |
12 13 |
#include <linux/module.h> #include <linux/init.h> |
3947be196 [PATCH] memory ho... |
14 |
#include <linux/topology.h> |
c59ede7b7 [PATCH] move capa... |
15 |
#include <linux/capability.h> |
3947be196 [PATCH] memory ho... |
16 17 18 19 20 |
#include <linux/device.h> #include <linux/memory.h> #include <linux/kobject.h> #include <linux/memory_hotplug.h> #include <linux/mm.h> |
da19cbcf7 driver core: memo... |
21 |
#include <linux/mutex.h> |
9f1b16a51 memory_probe: fix... |
22 |
#include <linux/stat.h> |
5a0e3ad6a include cleanup: ... |
23 |
#include <linux/slab.h> |
9f1b16a51 memory_probe: fix... |
24 |
|
60063497a atomic: use <linu... |
25 |
#include <linux/atomic.h> |
3947be196 [PATCH] memory ho... |
26 |
#include <asm/uaccess.h> |
2938ffbd4 Driver core: Add ... |
27 |
static DEFINE_MUTEX(mem_sysfs_mutex); |
3947be196 [PATCH] memory ho... |
28 |
#define MEMORY_CLASS_NAME "memory" |
0c2c99b1b memory hotplug: A... |
29 30 31 32 33 34 35 |
static int sections_per_block; static inline int base_memory_block_id(int section_nr) { return section_nr / sections_per_block; } |
3947be196 [PATCH] memory ho... |
36 |
|
10fbcf4c6 convert 'memory' ... |
37 |
static struct bus_type memory_subsys = { |
af5ca3f4e Driver core: chan... |
38 |
.name = MEMORY_CLASS_NAME, |
10fbcf4c6 convert 'memory' ... |
39 |
.dev_name = MEMORY_CLASS_NAME, |
3947be196 [PATCH] memory ho... |
40 |
}; |
e041c6834 [PATCH] Notifier ... |
41 |
static BLOCKING_NOTIFIER_HEAD(memory_chain); |
3947be196 [PATCH] memory ho... |
42 |
|
98a38ebdd [PATCH] memhotplu... |
43 |
int register_memory_notifier(struct notifier_block *nb) |
3947be196 [PATCH] memory ho... |
44 |
{ |
e041c6834 [PATCH] Notifier ... |
45 |
return blocking_notifier_chain_register(&memory_chain, nb); |
3947be196 [PATCH] memory ho... |
46 |
} |
3c82c30cd memory: Introduce... |
47 |
EXPORT_SYMBOL(register_memory_notifier); |
3947be196 [PATCH] memory ho... |
48 |
|
98a38ebdd [PATCH] memhotplu... |
49 |
void unregister_memory_notifier(struct notifier_block *nb) |
3947be196 [PATCH] memory ho... |
50 |
{ |
e041c6834 [PATCH] Notifier ... |
51 |
blocking_notifier_chain_unregister(&memory_chain, nb); |
3947be196 [PATCH] memory ho... |
52 |
} |
3c82c30cd memory: Introduce... |
53 |
EXPORT_SYMBOL(unregister_memory_notifier); |
3947be196 [PATCH] memory ho... |
54 |
|
925cc71e5 mm: Add notifier ... |
55 56 57 58 59 60 61 62 63 64 65 66 67 |
static ATOMIC_NOTIFIER_HEAD(memory_isolate_chain); int register_memory_isolate_notifier(struct notifier_block *nb) { return atomic_notifier_chain_register(&memory_isolate_chain, nb); } EXPORT_SYMBOL(register_memory_isolate_notifier); void unregister_memory_isolate_notifier(struct notifier_block *nb) { atomic_notifier_chain_unregister(&memory_isolate_chain, nb); } EXPORT_SYMBOL(unregister_memory_isolate_notifier); |
3947be196 [PATCH] memory ho... |
68 69 70 |
/* * register_memory - Setup a sysfs device for a memory block */ |
00a41db52 driver core: regi... |
71 |
static |
0c2c99b1b memory hotplug: A... |
72 |
int register_memory(struct memory_block *memory) |
3947be196 [PATCH] memory ho... |
73 74 |
{ int error; |
10fbcf4c6 convert 'memory' ... |
75 76 |
memory->dev.bus = &memory_subsys; memory->dev.id = memory->start_section_nr / sections_per_block; |
3947be196 [PATCH] memory ho... |
77 |
|
10fbcf4c6 convert 'memory' ... |
78 |
error = device_register(&memory->dev); |
3947be196 [PATCH] memory ho... |
79 80 81 82 |
return error; } static void |
0c2c99b1b memory hotplug: A... |
83 |
unregister_memory(struct memory_block *memory) |
3947be196 [PATCH] memory ho... |
84 |
{ |
10fbcf4c6 convert 'memory' ... |
85 |
BUG_ON(memory->dev.bus != &memory_subsys); |
3947be196 [PATCH] memory ho... |
86 |
|
00a41db52 driver core: regi... |
87 |
/* drop the ref. we got in remove_memory_block() */ |
10fbcf4c6 convert 'memory' ... |
88 89 |
kobject_put(&memory->dev.kobj); device_unregister(&memory->dev); |
3947be196 [PATCH] memory ho... |
90 |
} |
0c2c99b1b memory hotplug: A... |
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
unsigned long __weak memory_block_size_bytes(void) { return MIN_MEMORY_BLOCK_SIZE; } static unsigned long get_memory_block_size(void) { unsigned long block_sz; block_sz = memory_block_size_bytes(); /* Validate blk_sz is a power of 2 and not less than section size */ if ((block_sz & (block_sz - 1)) || (block_sz < MIN_MEMORY_BLOCK_SIZE)) { WARN_ON(1); block_sz = MIN_MEMORY_BLOCK_SIZE; } return block_sz; } |
3947be196 [PATCH] memory ho... |
110 111 112 113 |
/* * use this as the physical section index that this memsection * uses. */ |
10fbcf4c6 convert 'memory' ... |
114 115 |
static ssize_t show_mem_start_phys_index(struct device *dev, struct device_attribute *attr, char *buf) |
3947be196 [PATCH] memory ho... |
116 117 |
{ struct memory_block *mem = |
10fbcf4c6 convert 'memory' ... |
118 |
container_of(dev, struct memory_block, dev); |
d33601644 memory hotplug: U... |
119 120 121 122 123 124 |
unsigned long phys_index; phys_index = mem->start_section_nr / sections_per_block; return sprintf(buf, "%08lx ", phys_index); } |
10fbcf4c6 convert 'memory' ... |
125 126 |
static ssize_t show_mem_end_phys_index(struct device *dev, struct device_attribute *attr, char *buf) |
d33601644 memory hotplug: U... |
127 128 |
{ struct memory_block *mem = |
10fbcf4c6 convert 'memory' ... |
129 |
container_of(dev, struct memory_block, dev); |
d33601644 memory hotplug: U... |
130 131 132 133 134 |
unsigned long phys_index; phys_index = mem->end_section_nr / sections_per_block; return sprintf(buf, "%08lx ", phys_index); |
3947be196 [PATCH] memory ho... |
135 136 137 |
} /* |
5c755e9fd memory-hotplug: a... |
138 139 |
* Show whether the section of memory is likely to be hot-removable */ |
10fbcf4c6 convert 'memory' ... |
140 141 |
static ssize_t show_mem_removable(struct device *dev, struct device_attribute *attr, char *buf) |
5c755e9fd memory-hotplug: a... |
142 |
{ |
0c2c99b1b memory hotplug: A... |
143 144 |
unsigned long i, pfn; int ret = 1; |
5c755e9fd memory-hotplug: a... |
145 |
struct memory_block *mem = |
10fbcf4c6 convert 'memory' ... |
146 |
container_of(dev, struct memory_block, dev); |
5c755e9fd memory-hotplug: a... |
147 |
|
0c2c99b1b memory hotplug: A... |
148 |
for (i = 0; i < sections_per_block; i++) { |
d33601644 memory hotplug: U... |
149 |
pfn = section_nr_to_pfn(mem->start_section_nr + i); |
0c2c99b1b memory hotplug: A... |
150 151 |
ret &= is_mem_section_removable(pfn, PAGES_PER_SECTION); } |
5c755e9fd memory-hotplug: a... |
152 153 154 155 156 |
return sprintf(buf, "%d ", ret); } /* |
3947be196 [PATCH] memory ho... |
157 158 |
* online, offline, going offline, etc. */ |
10fbcf4c6 convert 'memory' ... |
159 160 |
static ssize_t show_mem_state(struct device *dev, struct device_attribute *attr, char *buf) |
3947be196 [PATCH] memory ho... |
161 162 |
{ struct memory_block *mem = |
10fbcf4c6 convert 'memory' ... |
163 |
container_of(dev, struct memory_block, dev); |
3947be196 [PATCH] memory ho... |
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
ssize_t len = 0; /* * We can probably put these states in a nice little array * so that they're not open-coded */ switch (mem->state) { case MEM_ONLINE: len = sprintf(buf, "online "); break; case MEM_OFFLINE: len = sprintf(buf, "offline "); break; case MEM_GOING_OFFLINE: len = sprintf(buf, "going-offline "); break; default: len = sprintf(buf, "ERROR-UNKNOWN-%ld ", mem->state); WARN_ON(1); break; } return len; } |
7b78d335a memory hotplug: r... |
193 |
int memory_notify(unsigned long val, void *v) |
3947be196 [PATCH] memory ho... |
194 |
{ |
e041c6834 [PATCH] Notifier ... |
195 |
return blocking_notifier_call_chain(&memory_chain, val, v); |
3947be196 [PATCH] memory ho... |
196 |
} |
925cc71e5 mm: Add notifier ... |
197 198 199 200 |
int memory_isolate_notify(unsigned long val, void *v) { return atomic_notifier_call_chain(&memory_isolate_chain, val, v); } |
3947be196 [PATCH] memory ho... |
201 |
/* |
2bbcb8788 mm: memory hotplu... |
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 227 228 229 230 231 232 233 234 235 236 237 238 |
* The probe routines leave the pages reserved, just as the bootmem code does. * Make sure they're still that way. */ static bool pages_correctly_reserved(unsigned long start_pfn, unsigned long nr_pages) { int i, j; struct page *page; unsigned long pfn = start_pfn; /* * memmap between sections is not contiguous except with * SPARSEMEM_VMEMMAP. We lookup the page once per section * and assume memmap is contiguous within each section */ for (i = 0; i < sections_per_block; i++, pfn += PAGES_PER_SECTION) { if (WARN_ON_ONCE(!pfn_valid(pfn))) return false; page = pfn_to_page(pfn); for (j = 0; j < PAGES_PER_SECTION; j++) { if (PageReserved(page + j)) continue; printk(KERN_WARNING "section number %ld page number %d " "not reserved, was it already online? ", pfn_to_section_nr(pfn), j); return false; } } return true; } /* |
3947be196 [PATCH] memory ho... |
239 240 241 242 |
* MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is * OK to have direct references to sparsemem variables in here. */ static int |
5409d2cd8 memory hotplug: S... |
243 |
memory_block_action(unsigned long phys_index, unsigned long action) |
3947be196 [PATCH] memory ho... |
244 |
{ |
3947be196 [PATCH] memory ho... |
245 |
unsigned long start_pfn, start_paddr; |
5409d2cd8 memory hotplug: S... |
246 |
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; |
de0ed36a3 Revert "memory ho... |
247 |
struct page *first_page; |
3947be196 [PATCH] memory ho... |
248 |
int ret; |
3947be196 [PATCH] memory ho... |
249 |
|
de0ed36a3 Revert "memory ho... |
250 |
first_page = pfn_to_page(phys_index << PFN_SECTION_SHIFT); |
3947be196 [PATCH] memory ho... |
251 252 |
switch (action) { case MEM_ONLINE: |
de0ed36a3 Revert "memory ho... |
253 |
start_pfn = page_to_pfn(first_page); |
2bbcb8788 mm: memory hotplu... |
254 255 256 |
if (!pages_correctly_reserved(start_pfn, nr_pages)) return -EBUSY; |
5409d2cd8 memory hotplug: S... |
257 |
ret = online_pages(start_pfn, nr_pages); |
3947be196 [PATCH] memory ho... |
258 259 |
break; case MEM_OFFLINE: |
de0ed36a3 Revert "memory ho... |
260 |
start_paddr = page_to_pfn(first_page) << PAGE_SHIFT; |
3947be196 [PATCH] memory ho... |
261 |
ret = remove_memory(start_paddr, |
5409d2cd8 memory hotplug: S... |
262 |
nr_pages << PAGE_SHIFT); |
3947be196 [PATCH] memory ho... |
263 264 |
break; default: |
0c2c99b1b memory hotplug: A... |
265 266 267 |
WARN(1, KERN_WARNING "%s(%ld, %ld) unknown action: " "%ld ", __func__, phys_index, action, action); |
3947be196 [PATCH] memory ho... |
268 269 |
ret = -EINVAL; } |
3947be196 [PATCH] memory ho... |
270 271 272 273 274 275 276 |
return ret; } static int memory_block_change_state(struct memory_block *mem, unsigned long to_state, unsigned long from_state_req) { |
de0ed36a3 Revert "memory ho... |
277 |
int ret = 0; |
0c2c99b1b memory hotplug: A... |
278 |
|
da19cbcf7 driver core: memo... |
279 |
mutex_lock(&mem->state_mutex); |
3947be196 [PATCH] memory ho... |
280 281 282 283 284 |
if (mem->state != from_state_req) { ret = -EINVAL; goto out; } |
0c2c99b1b memory hotplug: A... |
285 286 |
if (to_state == MEM_OFFLINE) mem->state = MEM_GOING_OFFLINE; |
5409d2cd8 memory hotplug: S... |
287 |
ret = memory_block_action(mem->start_section_nr, to_state); |
0c2c99b1b memory hotplug: A... |
288 |
|
f5138e422 kdump: add udev e... |
289 |
if (ret) { |
0c2c99b1b memory hotplug: A... |
290 |
mem->state = from_state_req; |
f5138e422 kdump: add udev e... |
291 292 |
goto out; } |
3947be196 [PATCH] memory ho... |
293 |
|
f5138e422 kdump: add udev e... |
294 295 296 297 298 299 300 301 302 303 304 |
mem->state = to_state; switch (mem->state) { case MEM_OFFLINE: kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); break; case MEM_ONLINE: kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); break; default: break; } |
3947be196 [PATCH] memory ho... |
305 |
out: |
da19cbcf7 driver core: memo... |
306 |
mutex_unlock(&mem->state_mutex); |
3947be196 [PATCH] memory ho... |
307 308 309 310 |
return ret; } static ssize_t |
10fbcf4c6 convert 'memory' ... |
311 312 |
store_mem_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
3947be196 [PATCH] memory ho... |
313 314 |
{ struct memory_block *mem; |
3947be196 [PATCH] memory ho... |
315 |
int ret = -EINVAL; |
10fbcf4c6 convert 'memory' ... |
316 |
mem = container_of(dev, struct memory_block, dev); |
3947be196 [PATCH] memory ho... |
317 318 319 320 321 |
if (!strncmp(buf, "online", min((int)count, 6))) ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); else if(!strncmp(buf, "offline", min((int)count, 7))) ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); |
0c2c99b1b memory hotplug: A... |
322 |
|
3947be196 [PATCH] memory ho... |
323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
if (ret) return ret; return count; } /* * phys_device is a bad name for this. What I really want * is a way to differentiate between memory ranges that * are part of physical devices that constitute * a complete removable unit or fru. * i.e. do these ranges belong to the same physical device, * s.t. if I offline all of these sections I can then * remove the physical device? */ |
10fbcf4c6 convert 'memory' ... |
337 338 |
static ssize_t show_phys_device(struct device *dev, struct device_attribute *attr, char *buf) |
3947be196 [PATCH] memory ho... |
339 340 |
{ struct memory_block *mem = |
10fbcf4c6 convert 'memory' ... |
341 |
container_of(dev, struct memory_block, dev); |
3947be196 [PATCH] memory ho... |
342 343 344 |
return sprintf(buf, "%d ", mem->phys_device); } |
10fbcf4c6 convert 'memory' ... |
345 346 347 348 349 |
static DEVICE_ATTR(phys_index, 0444, show_mem_start_phys_index, NULL); static DEVICE_ATTR(end_phys_index, 0444, show_mem_end_phys_index, NULL); static DEVICE_ATTR(state, 0644, show_mem_state, store_mem_state); static DEVICE_ATTR(phys_device, 0444, show_phys_device, NULL); static DEVICE_ATTR(removable, 0444, show_mem_removable, NULL); |
3947be196 [PATCH] memory ho... |
350 351 |
#define mem_create_simple_file(mem, attr_name) \ |
10fbcf4c6 convert 'memory' ... |
352 |
device_create_file(&mem->dev, &dev_attr_##attr_name) |
3947be196 [PATCH] memory ho... |
353 |
#define mem_remove_simple_file(mem, attr_name) \ |
10fbcf4c6 convert 'memory' ... |
354 |
device_remove_file(&mem->dev, &dev_attr_##attr_name) |
3947be196 [PATCH] memory ho... |
355 356 357 358 359 |
/* * Block size attribute stuff */ static ssize_t |
10fbcf4c6 convert 'memory' ... |
360 |
print_block_size(struct device *dev, struct device_attribute *attr, |
8564a6c14 sysdev: Fix type ... |
361 |
char *buf) |
3947be196 [PATCH] memory ho... |
362 |
{ |
0c2c99b1b memory hotplug: A... |
363 364 |
return sprintf(buf, "%lx ", get_memory_block_size()); |
3947be196 [PATCH] memory ho... |
365 |
} |
10fbcf4c6 convert 'memory' ... |
366 |
static DEVICE_ATTR(block_size_bytes, 0444, print_block_size, NULL); |
3947be196 [PATCH] memory ho... |
367 368 369 |
static int block_size_init(void) { |
10fbcf4c6 convert 'memory' ... |
370 371 |
return device_create_file(memory_subsys.dev_root, &dev_attr_block_size_bytes); |
3947be196 [PATCH] memory ho... |
372 373 374 375 376 377 378 379 380 381 |
} /* * Some architectures will have custom drivers to do this, and * will not need to do it from userspace. The fake hot-add code * as well as ppc64 will do all of their discovery in userspace * and will require this interface. */ #ifdef CONFIG_ARCH_MEMORY_PROBE static ssize_t |
10fbcf4c6 convert 'memory' ... |
382 |
memory_probe_store(struct device *dev, struct device_attribute *attr, |
28812fe11 driver-core: Add ... |
383 |
const char *buf, size_t count) |
3947be196 [PATCH] memory ho... |
384 385 |
{ u64 phys_addr; |
bc02af93d [PATCH] pgdat all... |
386 |
int nid; |
6add7cd61 memory hotplug: s... |
387 |
int i, ret; |
61b94feaf memory hotplug: R... |
388 |
unsigned long pages_per_block = PAGES_PER_SECTION * sections_per_block; |
3947be196 [PATCH] memory ho... |
389 390 |
phys_addr = simple_strtoull(buf, NULL, 0); |
61b94feaf memory hotplug: R... |
391 392 |
if (phys_addr & ((pages_per_block << PAGE_SHIFT) - 1)) return -EINVAL; |
6add7cd61 memory hotplug: s... |
393 394 395 396 397 |
for (i = 0; i < sections_per_block; i++) { nid = memory_add_physaddr_to_nid(phys_addr); ret = add_memory(nid, phys_addr, PAGES_PER_SECTION << PAGE_SHIFT); if (ret) |
9f0af69b2 sysfs-memory: fix... |
398 |
goto out; |
6add7cd61 memory hotplug: s... |
399 400 401 |
phys_addr += MIN_MEMORY_BLOCK_SIZE; } |
3947be196 [PATCH] memory ho... |
402 |
|
9f0af69b2 sysfs-memory: fix... |
403 404 405 |
ret = count; out: return ret; |
3947be196 [PATCH] memory ho... |
406 |
} |
10fbcf4c6 convert 'memory' ... |
407 |
static DEVICE_ATTR(probe, S_IWUSR, NULL, memory_probe_store); |
3947be196 [PATCH] memory ho... |
408 409 410 |
static int memory_probe_init(void) { |
10fbcf4c6 convert 'memory' ... |
411 |
return device_create_file(memory_subsys.dev_root, &dev_attr_probe); |
3947be196 [PATCH] memory ho... |
412 413 |
} #else |
28ec24e23 [PATCH] driver/ba... |
414 415 416 417 |
static inline int memory_probe_init(void) { return 0; } |
3947be196 [PATCH] memory ho... |
418 |
#endif |
facb6011f HWPOISON: Add sof... |
419 420 421 422 423 424 425 |
#ifdef CONFIG_MEMORY_FAILURE /* * Support for offlining pages of memory */ /* Soft offline a page */ static ssize_t |
10fbcf4c6 convert 'memory' ... |
426 427 |
store_soft_offline_page(struct device *dev, struct device_attribute *attr, |
28812fe11 driver-core: Add ... |
428 |
const char *buf, size_t count) |
facb6011f HWPOISON: Add sof... |
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
{ int ret; u64 pfn; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (strict_strtoull(buf, 0, &pfn) < 0) return -EINVAL; pfn >>= PAGE_SHIFT; if (!pfn_valid(pfn)) return -ENXIO; ret = soft_offline_page(pfn_to_page(pfn), 0); return ret == 0 ? count : ret; } /* Forcibly offline a page, including killing processes. */ static ssize_t |
10fbcf4c6 convert 'memory' ... |
445 446 |
store_hard_offline_page(struct device *dev, struct device_attribute *attr, |
28812fe11 driver-core: Add ... |
447 |
const char *buf, size_t count) |
facb6011f HWPOISON: Add sof... |
448 449 450 451 452 453 454 455 456 457 458 |
{ int ret; u64 pfn; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (strict_strtoull(buf, 0, &pfn) < 0) return -EINVAL; pfn >>= PAGE_SHIFT; ret = __memory_failure(pfn, 0, 0); return ret ? ret : count; } |
10fbcf4c6 convert 'memory' ... |
459 460 |
static DEVICE_ATTR(soft_offline_page, 0644, NULL, store_soft_offline_page); static DEVICE_ATTR(hard_offline_page, 0644, NULL, store_hard_offline_page); |
facb6011f HWPOISON: Add sof... |
461 462 463 464 |
static __init int memory_fail_init(void) { int err; |
10fbcf4c6 convert 'memory' ... |
465 466 |
err = device_create_file(memory_subsys.dev_root, &dev_attr_soft_offline_page); |
facb6011f HWPOISON: Add sof... |
467 |
if (!err) |
10fbcf4c6 convert 'memory' ... |
468 469 |
err = device_create_file(memory_subsys.dev_root, &dev_attr_hard_offline_page); |
facb6011f HWPOISON: Add sof... |
470 471 472 473 474 475 476 477 |
return err; } #else static inline int memory_fail_init(void) { return 0; } #endif |
3947be196 [PATCH] memory ho... |
478 479 480 481 482 |
/* * Note that phys_device is optional. It is here to allow for * differentiation between which *physical* devices each * section belongs to... */ |
bc32df008 memory hotplug: a... |
483 484 485 486 |
int __weak arch_get_memory_phys_device(unsigned long start_pfn) { return 0; } |
3947be196 [PATCH] memory ho... |
487 |
|
10fbcf4c6 convert 'memory' ... |
488 489 490 491 |
/* * A reference for the returned object is held and the reference for the * hinted object is released. */ |
98383031e driver core: Intr... |
492 493 |
struct memory_block *find_memory_block_hinted(struct mem_section *section, struct memory_block *hint) |
3947be196 [PATCH] memory ho... |
494 |
{ |
0c2c99b1b memory hotplug: A... |
495 |
int block_id = base_memory_block_id(__section_nr(section)); |
10fbcf4c6 convert 'memory' ... |
496 497 |
struct device *hintdev = hint ? &hint->dev : NULL; struct device *dev; |
3947be196 [PATCH] memory ho... |
498 |
|
10fbcf4c6 convert 'memory' ... |
499 500 501 502 |
dev = subsys_find_device_by_id(&memory_subsys, block_id, hintdev); if (hint) put_device(&hint->dev); if (!dev) |
3947be196 [PATCH] memory ho... |
503 |
return NULL; |
10fbcf4c6 convert 'memory' ... |
504 |
return container_of(dev, struct memory_block, dev); |
3947be196 [PATCH] memory ho... |
505 |
} |
98383031e driver core: Intr... |
506 507 508 509 510 511 |
/* * For now, we have a linear search to go find the appropriate * memory_block corresponding to a particular phys_index. If * this gets to be a real problem, we can always use a radix * tree or something here. * |
10fbcf4c6 convert 'memory' ... |
512 |
* This could be made generic for all device subsystems. |
98383031e driver core: Intr... |
513 514 515 516 517 |
*/ struct memory_block *find_memory_block(struct mem_section *section) { return find_memory_block_hinted(section, NULL); } |
0c2c99b1b memory hotplug: A... |
518 519 |
static int init_memory_block(struct memory_block **memory, struct mem_section *section, unsigned long state) |
e4619c857 Driver core: Move... |
520 |
{ |
0c2c99b1b memory hotplug: A... |
521 |
struct memory_block *mem; |
e4619c857 Driver core: Move... |
522 |
unsigned long start_pfn; |
0c2c99b1b memory hotplug: A... |
523 |
int scn_nr; |
e4619c857 Driver core: Move... |
524 |
int ret = 0; |
0c2c99b1b memory hotplug: A... |
525 |
mem = kzalloc(sizeof(*mem), GFP_KERNEL); |
e4619c857 Driver core: Move... |
526 527 |
if (!mem) return -ENOMEM; |
0c2c99b1b memory hotplug: A... |
528 |
scn_nr = __section_nr(section); |
d33601644 memory hotplug: U... |
529 530 531 |
mem->start_section_nr = base_memory_block_id(scn_nr) * sections_per_block; mem->end_section_nr = mem->start_section_nr + sections_per_block - 1; |
e4619c857 Driver core: Move... |
532 |
mem->state = state; |
076812159 Driver core: Add ... |
533 |
mem->section_count++; |
e4619c857 Driver core: Move... |
534 |
mutex_init(&mem->state_mutex); |
d33601644 memory hotplug: U... |
535 |
start_pfn = section_nr_to_pfn(mem->start_section_nr); |
e4619c857 Driver core: Move... |
536 |
mem->phys_device = arch_get_memory_phys_device(start_pfn); |
0c2c99b1b memory hotplug: A... |
537 |
ret = register_memory(mem); |
e4619c857 Driver core: Move... |
538 539 540 |
if (!ret) ret = mem_create_simple_file(mem, phys_index); if (!ret) |
d33601644 memory hotplug: U... |
541 542 |
ret = mem_create_simple_file(mem, end_phys_index); if (!ret) |
e4619c857 Driver core: Move... |
543 544 545 546 547 |
ret = mem_create_simple_file(mem, state); if (!ret) ret = mem_create_simple_file(mem, phys_device); if (!ret) ret = mem_create_simple_file(mem, removable); |
0c2c99b1b memory hotplug: A... |
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
*memory = mem; return ret; } static int add_memory_section(int nid, struct mem_section *section, unsigned long state, enum mem_add_context context) { struct memory_block *mem; int ret = 0; mutex_lock(&mem_sysfs_mutex); mem = find_memory_block(section); if (mem) { mem->section_count++; |
10fbcf4c6 convert 'memory' ... |
564 |
kobject_put(&mem->dev.kobj); |
0c2c99b1b memory hotplug: A... |
565 566 |
} else ret = init_memory_block(&mem, section, state); |
e4619c857 Driver core: Move... |
567 |
if (!ret) { |
0c2c99b1b memory hotplug: A... |
568 569 |
if (context == HOTPLUG && mem->section_count == sections_per_block) |
e4619c857 Driver core: Move... |
570 571 |
ret = register_mem_sect_under_node(mem, nid); } |
2938ffbd4 Driver core: Add ... |
572 |
mutex_unlock(&mem_sysfs_mutex); |
e4619c857 Driver core: Move... |
573 574 |
return ret; } |
3947be196 [PATCH] memory ho... |
575 576 577 578 |
int remove_memory_block(unsigned long node_id, struct mem_section *section, int phys_device) { struct memory_block *mem; |
2938ffbd4 Driver core: Add ... |
579 |
mutex_lock(&mem_sysfs_mutex); |
3947be196 [PATCH] memory ho... |
580 |
mem = find_memory_block(section); |
d33601644 memory hotplug: U... |
581 |
unregister_mem_sect_under_nodes(mem, __section_nr(section)); |
076812159 Driver core: Add ... |
582 583 584 |
mem->section_count--; if (mem->section_count == 0) { |
076812159 Driver core: Add ... |
585 |
mem_remove_simple_file(mem, phys_index); |
d33601644 memory hotplug: U... |
586 |
mem_remove_simple_file(mem, end_phys_index); |
076812159 Driver core: Add ... |
587 588 589 |
mem_remove_simple_file(mem, state); mem_remove_simple_file(mem, phys_device); mem_remove_simple_file(mem, removable); |
0c2c99b1b memory hotplug: A... |
590 591 592 |
unregister_memory(mem); kfree(mem); } else |
10fbcf4c6 convert 'memory' ... |
593 |
kobject_put(&mem->dev.kobj); |
3947be196 [PATCH] memory ho... |
594 |
|
2938ffbd4 Driver core: Add ... |
595 |
mutex_unlock(&mem_sysfs_mutex); |
3947be196 [PATCH] memory ho... |
596 597 598 599 600 601 602 |
return 0; } /* * need an interface for the VM to add new memory regions, * but without onlining it. */ |
c04fc586c mm: show node to ... |
603 |
int register_new_memory(int nid, struct mem_section *section) |
3947be196 [PATCH] memory ho... |
604 |
{ |
0c2c99b1b memory hotplug: A... |
605 |
return add_memory_section(nid, section, MEM_OFFLINE, HOTPLUG); |
3947be196 [PATCH] memory ho... |
606 607 608 609 |
} int unregister_memory_section(struct mem_section *section) { |
540557b94 sparsemem: record... |
610 |
if (!present_section(section)) |
3947be196 [PATCH] memory ho... |
611 612 613 614 615 616 617 618 619 620 621 622 |
return -EINVAL; return remove_memory_block(0, section, 0); } /* * Initialize the sysfs support for memory devices... */ int __init memory_dev_init(void) { unsigned int i; int ret; |
28ec24e23 [PATCH] driver/ba... |
623 |
int err; |
0c2c99b1b memory hotplug: A... |
624 |
unsigned long block_sz; |
3947be196 [PATCH] memory ho... |
625 |
|
10fbcf4c6 convert 'memory' ... |
626 |
ret = subsys_system_register(&memory_subsys, NULL); |
28ec24e23 [PATCH] driver/ba... |
627 628 |
if (ret) goto out; |
3947be196 [PATCH] memory ho... |
629 |
|
0c2c99b1b memory hotplug: A... |
630 631 |
block_sz = get_memory_block_size(); sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE; |
3947be196 [PATCH] memory ho... |
632 633 634 635 636 |
/* * Create entries for memory sections that were found * during boot and have been initialized */ for (i = 0; i < NR_MEM_SECTIONS; i++) { |
540557b94 sparsemem: record... |
637 |
if (!present_section_nr(i)) |
3947be196 [PATCH] memory ho... |
638 |
continue; |
0c2c99b1b memory hotplug: A... |
639 640 |
err = add_memory_section(0, __nr_to_section(i), MEM_ONLINE, BOOT); |
28ec24e23 [PATCH] driver/ba... |
641 642 |
if (!ret) ret = err; |
3947be196 [PATCH] memory ho... |
643 |
} |
28ec24e23 [PATCH] driver/ba... |
644 645 646 |
err = memory_probe_init(); if (!ret) ret = err; |
facb6011f HWPOISON: Add sof... |
647 648 649 |
err = memory_fail_init(); if (!ret) ret = err; |
28ec24e23 [PATCH] driver/ba... |
650 651 652 653 654 |
err = block_size_init(); if (!ret) ret = err; out: if (ret) |
2b3a302a0 driver core: repl... |
655 656 |
printk(KERN_ERR "%s() failed: %d ", __func__, ret); |
3947be196 [PATCH] memory ho... |
657 658 |
return ret; } |