diff options
Diffstat (limited to 'drivers/base/memory.c')
-rw-r--r-- | drivers/base/memory.c | 112 |
1 files changed, 102 insertions, 10 deletions
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 989429cfed88..933442f40321 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/mm.h> | 22 | #include <linux/mm.h> |
23 | #include <linux/mutex.h> | 23 | #include <linux/mutex.h> |
24 | #include <linux/stat.h> | 24 | #include <linux/stat.h> |
25 | #include <linux/slab.h> | ||
25 | 26 | ||
26 | #include <asm/atomic.h> | 27 | #include <asm/atomic.h> |
27 | #include <asm/uaccess.h> | 28 | #include <asm/uaccess.h> |
@@ -44,7 +45,7 @@ static int memory_uevent(struct kset *kset, struct kobject *obj, struct kobj_uev | |||
44 | return retval; | 45 | return retval; |
45 | } | 46 | } |
46 | 47 | ||
47 | static struct kset_uevent_ops memory_uevent_ops = { | 48 | static const struct kset_uevent_ops memory_uevent_ops = { |
48 | .name = memory_uevent_name, | 49 | .name = memory_uevent_name, |
49 | .uevent = memory_uevent, | 50 | .uevent = memory_uevent, |
50 | }; | 51 | }; |
@@ -63,6 +64,20 @@ void unregister_memory_notifier(struct notifier_block *nb) | |||
63 | } | 64 | } |
64 | EXPORT_SYMBOL(unregister_memory_notifier); | 65 | EXPORT_SYMBOL(unregister_memory_notifier); |
65 | 66 | ||
67 | static ATOMIC_NOTIFIER_HEAD(memory_isolate_chain); | ||
68 | |||
69 | int register_memory_isolate_notifier(struct notifier_block *nb) | ||
70 | { | ||
71 | return atomic_notifier_chain_register(&memory_isolate_chain, nb); | ||
72 | } | ||
73 | EXPORT_SYMBOL(register_memory_isolate_notifier); | ||
74 | |||
75 | void unregister_memory_isolate_notifier(struct notifier_block *nb) | ||
76 | { | ||
77 | atomic_notifier_chain_unregister(&memory_isolate_chain, nb); | ||
78 | } | ||
79 | EXPORT_SYMBOL(unregister_memory_isolate_notifier); | ||
80 | |||
66 | /* | 81 | /* |
67 | * register_memory - Setup a sysfs device for a memory block | 82 | * register_memory - Setup a sysfs device for a memory block |
68 | */ | 83 | */ |
@@ -157,6 +172,11 @@ int memory_notify(unsigned long val, void *v) | |||
157 | return blocking_notifier_call_chain(&memory_chain, val, v); | 172 | return blocking_notifier_call_chain(&memory_chain, val, v); |
158 | } | 173 | } |
159 | 174 | ||
175 | int memory_isolate_notify(unsigned long val, void *v) | ||
176 | { | ||
177 | return atomic_notifier_call_chain(&memory_isolate_chain, val, v); | ||
178 | } | ||
179 | |||
160 | /* | 180 | /* |
161 | * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is | 181 | * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is |
162 | * OK to have direct references to sparsemem variables in here. | 182 | * OK to have direct references to sparsemem variables in here. |
@@ -290,17 +310,18 @@ static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL); | |||
290 | * Block size attribute stuff | 310 | * Block size attribute stuff |
291 | */ | 311 | */ |
292 | static ssize_t | 312 | static ssize_t |
293 | print_block_size(struct class *class, char *buf) | 313 | print_block_size(struct sysdev_class *class, struct sysdev_class_attribute *attr, |
314 | char *buf) | ||
294 | { | 315 | { |
295 | return sprintf(buf, "%lx\n", (unsigned long)PAGES_PER_SECTION * PAGE_SIZE); | 316 | return sprintf(buf, "%lx\n", (unsigned long)PAGES_PER_SECTION * PAGE_SIZE); |
296 | } | 317 | } |
297 | 318 | ||
298 | static CLASS_ATTR(block_size_bytes, 0444, print_block_size, NULL); | 319 | static SYSDEV_CLASS_ATTR(block_size_bytes, 0444, print_block_size, NULL); |
299 | 320 | ||
300 | static int block_size_init(void) | 321 | static int block_size_init(void) |
301 | { | 322 | { |
302 | return sysfs_create_file(&memory_sysdev_class.kset.kobj, | 323 | return sysfs_create_file(&memory_sysdev_class.kset.kobj, |
303 | &class_attr_block_size_bytes.attr); | 324 | &attr_block_size_bytes.attr); |
304 | } | 325 | } |
305 | 326 | ||
306 | /* | 327 | /* |
@@ -311,7 +332,8 @@ static int block_size_init(void) | |||
311 | */ | 332 | */ |
312 | #ifdef CONFIG_ARCH_MEMORY_PROBE | 333 | #ifdef CONFIG_ARCH_MEMORY_PROBE |
313 | static ssize_t | 334 | static ssize_t |
314 | memory_probe_store(struct class *class, const char *buf, size_t count) | 335 | memory_probe_store(struct class *class, struct class_attribute *attr, |
336 | const char *buf, size_t count) | ||
315 | { | 337 | { |
316 | u64 phys_addr; | 338 | u64 phys_addr; |
317 | int nid; | 339 | int nid; |
@@ -341,17 +363,83 @@ static inline int memory_probe_init(void) | |||
341 | } | 363 | } |
342 | #endif | 364 | #endif |
343 | 365 | ||
366 | #ifdef CONFIG_MEMORY_FAILURE | ||
367 | /* | ||
368 | * Support for offlining pages of memory | ||
369 | */ | ||
370 | |||
371 | /* Soft offline a page */ | ||
372 | static ssize_t | ||
373 | store_soft_offline_page(struct class *class, | ||
374 | struct class_attribute *attr, | ||
375 | const char *buf, size_t count) | ||
376 | { | ||
377 | int ret; | ||
378 | u64 pfn; | ||
379 | if (!capable(CAP_SYS_ADMIN)) | ||
380 | return -EPERM; | ||
381 | if (strict_strtoull(buf, 0, &pfn) < 0) | ||
382 | return -EINVAL; | ||
383 | pfn >>= PAGE_SHIFT; | ||
384 | if (!pfn_valid(pfn)) | ||
385 | return -ENXIO; | ||
386 | ret = soft_offline_page(pfn_to_page(pfn), 0); | ||
387 | return ret == 0 ? count : ret; | ||
388 | } | ||
389 | |||
390 | /* Forcibly offline a page, including killing processes. */ | ||
391 | static ssize_t | ||
392 | store_hard_offline_page(struct class *class, | ||
393 | struct class_attribute *attr, | ||
394 | const char *buf, size_t count) | ||
395 | { | ||
396 | int ret; | ||
397 | u64 pfn; | ||
398 | if (!capable(CAP_SYS_ADMIN)) | ||
399 | return -EPERM; | ||
400 | if (strict_strtoull(buf, 0, &pfn) < 0) | ||
401 | return -EINVAL; | ||
402 | pfn >>= PAGE_SHIFT; | ||
403 | ret = __memory_failure(pfn, 0, 0); | ||
404 | return ret ? ret : count; | ||
405 | } | ||
406 | |||
407 | static CLASS_ATTR(soft_offline_page, 0644, NULL, store_soft_offline_page); | ||
408 | static CLASS_ATTR(hard_offline_page, 0644, NULL, store_hard_offline_page); | ||
409 | |||
410 | static __init int memory_fail_init(void) | ||
411 | { | ||
412 | int err; | ||
413 | |||
414 | err = sysfs_create_file(&memory_sysdev_class.kset.kobj, | ||
415 | &class_attr_soft_offline_page.attr); | ||
416 | if (!err) | ||
417 | err = sysfs_create_file(&memory_sysdev_class.kset.kobj, | ||
418 | &class_attr_hard_offline_page.attr); | ||
419 | return err; | ||
420 | } | ||
421 | #else | ||
422 | static inline int memory_fail_init(void) | ||
423 | { | ||
424 | return 0; | ||
425 | } | ||
426 | #endif | ||
427 | |||
344 | /* | 428 | /* |
345 | * Note that phys_device is optional. It is here to allow for | 429 | * Note that phys_device is optional. It is here to allow for |
346 | * differentiation between which *physical* devices each | 430 | * differentiation between which *physical* devices each |
347 | * section belongs to... | 431 | * section belongs to... |
348 | */ | 432 | */ |
433 | int __weak arch_get_memory_phys_device(unsigned long start_pfn) | ||
434 | { | ||
435 | return 0; | ||
436 | } | ||
349 | 437 | ||
350 | static int add_memory_block(int nid, struct mem_section *section, | 438 | static int add_memory_block(int nid, struct mem_section *section, |
351 | unsigned long state, int phys_device, | 439 | unsigned long state, enum mem_add_context context) |
352 | enum mem_add_context context) | ||
353 | { | 440 | { |
354 | struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL); | 441 | struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL); |
442 | unsigned long start_pfn; | ||
355 | int ret = 0; | 443 | int ret = 0; |
356 | 444 | ||
357 | if (!mem) | 445 | if (!mem) |
@@ -360,7 +448,8 @@ static int add_memory_block(int nid, struct mem_section *section, | |||
360 | mem->phys_index = __section_nr(section); | 448 | mem->phys_index = __section_nr(section); |
361 | mem->state = state; | 449 | mem->state = state; |
362 | mutex_init(&mem->state_mutex); | 450 | mutex_init(&mem->state_mutex); |
363 | mem->phys_device = phys_device; | 451 | start_pfn = section_nr_to_pfn(mem->phys_index); |
452 | mem->phys_device = arch_get_memory_phys_device(start_pfn); | ||
364 | 453 | ||
365 | ret = register_memory(mem, section); | 454 | ret = register_memory(mem, section); |
366 | if (!ret) | 455 | if (!ret) |
@@ -432,7 +521,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section, | |||
432 | */ | 521 | */ |
433 | int register_new_memory(int nid, struct mem_section *section) | 522 | int register_new_memory(int nid, struct mem_section *section) |
434 | { | 523 | { |
435 | return add_memory_block(nid, section, MEM_OFFLINE, 0, HOTPLUG); | 524 | return add_memory_block(nid, section, MEM_OFFLINE, HOTPLUG); |
436 | } | 525 | } |
437 | 526 | ||
438 | int unregister_memory_section(struct mem_section *section) | 527 | int unregister_memory_section(struct mem_section *section) |
@@ -465,7 +554,7 @@ int __init memory_dev_init(void) | |||
465 | if (!present_section_nr(i)) | 554 | if (!present_section_nr(i)) |
466 | continue; | 555 | continue; |
467 | err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE, | 556 | err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE, |
468 | 0, BOOT); | 557 | BOOT); |
469 | if (!ret) | 558 | if (!ret) |
470 | ret = err; | 559 | ret = err; |
471 | } | 560 | } |
@@ -473,6 +562,9 @@ int __init memory_dev_init(void) | |||
473 | err = memory_probe_init(); | 562 | err = memory_probe_init(); |
474 | if (!ret) | 563 | if (!ret) |
475 | ret = err; | 564 | ret = err; |
565 | err = memory_fail_init(); | ||
566 | if (!ret) | ||
567 | ret = err; | ||
476 | err = block_size_init(); | 568 | err = block_size_init(); |
477 | if (!ret) | 569 | if (!ret) |
478 | ret = err; | 570 | ret = err; |