diff options
Diffstat (limited to 'drivers/base/memory.c')
| -rw-r--r-- | drivers/base/memory.c | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 989429cfed88..c4c8f2e1dd15 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c | |||
| @@ -341,6 +341,64 @@ static inline int memory_probe_init(void) | |||
| 341 | } | 341 | } |
| 342 | #endif | 342 | #endif |
| 343 | 343 | ||
| 344 | #ifdef CONFIG_MEMORY_FAILURE | ||
| 345 | /* | ||
| 346 | * Support for offlining pages of memory | ||
| 347 | */ | ||
| 348 | |||
| 349 | /* Soft offline a page */ | ||
| 350 | static ssize_t | ||
| 351 | store_soft_offline_page(struct class *class, const char *buf, size_t count) | ||
| 352 | { | ||
| 353 | int ret; | ||
| 354 | u64 pfn; | ||
| 355 | if (!capable(CAP_SYS_ADMIN)) | ||
| 356 | return -EPERM; | ||
| 357 | if (strict_strtoull(buf, 0, &pfn) < 0) | ||
| 358 | return -EINVAL; | ||
| 359 | pfn >>= PAGE_SHIFT; | ||
| 360 | if (!pfn_valid(pfn)) | ||
| 361 | return -ENXIO; | ||
| 362 | ret = soft_offline_page(pfn_to_page(pfn), 0); | ||
| 363 | return ret == 0 ? count : ret; | ||
| 364 | } | ||
| 365 | |||
| 366 | /* Forcibly offline a page, including killing processes. */ | ||
| 367 | static ssize_t | ||
| 368 | store_hard_offline_page(struct class *class, const char *buf, size_t count) | ||
| 369 | { | ||
| 370 | int ret; | ||
| 371 | u64 pfn; | ||
| 372 | if (!capable(CAP_SYS_ADMIN)) | ||
| 373 | return -EPERM; | ||
| 374 | if (strict_strtoull(buf, 0, &pfn) < 0) | ||
| 375 | return -EINVAL; | ||
| 376 | pfn >>= PAGE_SHIFT; | ||
| 377 | ret = __memory_failure(pfn, 0, 0); | ||
| 378 | return ret ? ret : count; | ||
| 379 | } | ||
| 380 | |||
| 381 | static CLASS_ATTR(soft_offline_page, 0644, NULL, store_soft_offline_page); | ||
| 382 | static CLASS_ATTR(hard_offline_page, 0644, NULL, store_hard_offline_page); | ||
| 383 | |||
| 384 | static __init int memory_fail_init(void) | ||
| 385 | { | ||
| 386 | int err; | ||
| 387 | |||
| 388 | err = sysfs_create_file(&memory_sysdev_class.kset.kobj, | ||
| 389 | &class_attr_soft_offline_page.attr); | ||
| 390 | if (!err) | ||
| 391 | err = sysfs_create_file(&memory_sysdev_class.kset.kobj, | ||
| 392 | &class_attr_hard_offline_page.attr); | ||
| 393 | return err; | ||
| 394 | } | ||
| 395 | #else | ||
| 396 | static inline int memory_fail_init(void) | ||
| 397 | { | ||
| 398 | return 0; | ||
| 399 | } | ||
| 400 | #endif | ||
| 401 | |||
| 344 | /* | 402 | /* |
| 345 | * Note that phys_device is optional. It is here to allow for | 403 | * Note that phys_device is optional. It is here to allow for |
| 346 | * differentiation between which *physical* devices each | 404 | * differentiation between which *physical* devices each |
| @@ -473,6 +531,9 @@ int __init memory_dev_init(void) | |||
| 473 | err = memory_probe_init(); | 531 | err = memory_probe_init(); |
| 474 | if (!ret) | 532 | if (!ret) |
| 475 | ret = err; | 533 | ret = err; |
| 534 | err = memory_fail_init(); | ||
| 535 | if (!ret) | ||
| 536 | ret = err; | ||
| 476 | err = block_size_init(); | 537 | err = block_size_init(); |
| 477 | if (!ret) | 538 | if (!ret) |
| 478 | ret = err; | 539 | ret = err; |
