diff options
Diffstat (limited to 'drivers/base/memory.c')
-rw-r--r-- | drivers/base/memory.c | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 989429cfed88..d7d77d4a402c 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c | |||
@@ -63,6 +63,20 @@ void unregister_memory_notifier(struct notifier_block *nb) | |||
63 | } | 63 | } |
64 | EXPORT_SYMBOL(unregister_memory_notifier); | 64 | EXPORT_SYMBOL(unregister_memory_notifier); |
65 | 65 | ||
66 | static ATOMIC_NOTIFIER_HEAD(memory_isolate_chain); | ||
67 | |||
68 | int register_memory_isolate_notifier(struct notifier_block *nb) | ||
69 | { | ||
70 | return atomic_notifier_chain_register(&memory_isolate_chain, nb); | ||
71 | } | ||
72 | EXPORT_SYMBOL(register_memory_isolate_notifier); | ||
73 | |||
74 | void unregister_memory_isolate_notifier(struct notifier_block *nb) | ||
75 | { | ||
76 | atomic_notifier_chain_unregister(&memory_isolate_chain, nb); | ||
77 | } | ||
78 | EXPORT_SYMBOL(unregister_memory_isolate_notifier); | ||
79 | |||
66 | /* | 80 | /* |
67 | * register_memory - Setup a sysfs device for a memory block | 81 | * register_memory - Setup a sysfs device for a memory block |
68 | */ | 82 | */ |
@@ -157,6 +171,11 @@ int memory_notify(unsigned long val, void *v) | |||
157 | return blocking_notifier_call_chain(&memory_chain, val, v); | 171 | return blocking_notifier_call_chain(&memory_chain, val, v); |
158 | } | 172 | } |
159 | 173 | ||
174 | int memory_isolate_notify(unsigned long val, void *v) | ||
175 | { | ||
176 | return atomic_notifier_call_chain(&memory_isolate_chain, val, v); | ||
177 | } | ||
178 | |||
160 | /* | 179 | /* |
161 | * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is | 180 | * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is |
162 | * OK to have direct references to sparsemem variables in here. | 181 | * OK to have direct references to sparsemem variables in here. |
@@ -341,6 +360,64 @@ static inline int memory_probe_init(void) | |||
341 | } | 360 | } |
342 | #endif | 361 | #endif |
343 | 362 | ||
363 | #ifdef CONFIG_MEMORY_FAILURE | ||
364 | /* | ||
365 | * Support for offlining pages of memory | ||
366 | */ | ||
367 | |||
368 | /* Soft offline a page */ | ||
369 | static ssize_t | ||
370 | store_soft_offline_page(struct class *class, const char *buf, size_t count) | ||
371 | { | ||
372 | int ret; | ||
373 | u64 pfn; | ||
374 | if (!capable(CAP_SYS_ADMIN)) | ||
375 | return -EPERM; | ||
376 | if (strict_strtoull(buf, 0, &pfn) < 0) | ||
377 | return -EINVAL; | ||
378 | pfn >>= PAGE_SHIFT; | ||
379 | if (!pfn_valid(pfn)) | ||
380 | return -ENXIO; | ||
381 | ret = soft_offline_page(pfn_to_page(pfn), 0); | ||
382 | return ret == 0 ? count : ret; | ||
383 | } | ||
384 | |||
385 | /* Forcibly offline a page, including killing processes. */ | ||
386 | static ssize_t | ||
387 | store_hard_offline_page(struct class *class, const char *buf, size_t count) | ||
388 | { | ||
389 | int ret; | ||
390 | u64 pfn; | ||
391 | if (!capable(CAP_SYS_ADMIN)) | ||
392 | return -EPERM; | ||
393 | if (strict_strtoull(buf, 0, &pfn) < 0) | ||
394 | return -EINVAL; | ||
395 | pfn >>= PAGE_SHIFT; | ||
396 | ret = __memory_failure(pfn, 0, 0); | ||
397 | return ret ? ret : count; | ||
398 | } | ||
399 | |||
400 | static CLASS_ATTR(soft_offline_page, 0644, NULL, store_soft_offline_page); | ||
401 | static CLASS_ATTR(hard_offline_page, 0644, NULL, store_hard_offline_page); | ||
402 | |||
403 | static __init int memory_fail_init(void) | ||
404 | { | ||
405 | int err; | ||
406 | |||
407 | err = sysfs_create_file(&memory_sysdev_class.kset.kobj, | ||
408 | &class_attr_soft_offline_page.attr); | ||
409 | if (!err) | ||
410 | err = sysfs_create_file(&memory_sysdev_class.kset.kobj, | ||
411 | &class_attr_hard_offline_page.attr); | ||
412 | return err; | ||
413 | } | ||
414 | #else | ||
415 | static inline int memory_fail_init(void) | ||
416 | { | ||
417 | return 0; | ||
418 | } | ||
419 | #endif | ||
420 | |||
344 | /* | 421 | /* |
345 | * Note that phys_device is optional. It is here to allow for | 422 | * Note that phys_device is optional. It is here to allow for |
346 | * differentiation between which *physical* devices each | 423 | * differentiation between which *physical* devices each |
@@ -473,6 +550,9 @@ int __init memory_dev_init(void) | |||
473 | err = memory_probe_init(); | 550 | err = memory_probe_init(); |
474 | if (!ret) | 551 | if (!ret) |
475 | ret = err; | 552 | ret = err; |
553 | err = memory_fail_init(); | ||
554 | if (!ret) | ||
555 | ret = err; | ||
476 | err = block_size_init(); | 556 | err = block_size_init(); |
477 | if (!ret) | 557 | if (!ret) |
478 | ret = err; | 558 | ret = err; |