.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * pseries Memory Hotplug infrastructure. |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2008 Badari Pulavarty, IBM Corporation |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or |
---|
7 | | - * modify it under the terms of the GNU General Public License |
---|
8 | | - * as published by the Free Software Foundation; either version |
---|
9 | | - * 2 of the License, or (at your option) any later version. |
---|
10 | 6 | */ |
---|
11 | 7 | |
---|
12 | 8 | #define pr_fmt(fmt) "pseries-hotplug-mem: " fmt |
---|
.. | .. |
---|
26 | 22 | #include <asm/drmem.h> |
---|
27 | 23 | #include "pseries.h" |
---|
28 | 24 | |
---|
29 | | -static bool rtas_hp_event; |
---|
30 | | - |
---|
31 | 25 | unsigned long pseries_memory_block_size(void) |
---|
32 | 26 | { |
---|
33 | 27 | struct device_node *np; |
---|
.. | .. |
---|
36 | 30 | |
---|
37 | 31 | np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); |
---|
38 | 32 | if (np) { |
---|
39 | | - const __be64 *size; |
---|
| 33 | + int len; |
---|
| 34 | + int size_cells; |
---|
| 35 | + const __be32 *prop; |
---|
40 | 36 | |
---|
41 | | - size = of_get_property(np, "ibm,lmb-size", NULL); |
---|
42 | | - if (size) |
---|
43 | | - memblock_size = be64_to_cpup(size); |
---|
| 37 | + size_cells = of_n_size_cells(np); |
---|
| 38 | + |
---|
| 39 | + prop = of_get_property(np, "ibm,lmb-size", &len); |
---|
| 40 | + if (prop && len >= size_cells * sizeof(__be32)) |
---|
| 41 | + memblock_size = of_read_number(prop, size_cells); |
---|
44 | 42 | of_node_put(np); |
---|
| 43 | + |
---|
45 | 44 | } else if (machine_is(pseries)) { |
---|
46 | 45 | /* This fallback really only applies to pseries */ |
---|
47 | 46 | unsigned int memzero_size = 0; |
---|
.. | .. |
---|
283 | 282 | return dlpar_change_lmb_state(lmb, false); |
---|
284 | 283 | } |
---|
285 | 284 | |
---|
286 | | -static int pseries_remove_memblock(unsigned long base, unsigned int memblock_size) |
---|
| 285 | +static int pseries_remove_memblock(unsigned long base, unsigned long memblock_size) |
---|
287 | 286 | { |
---|
288 | 287 | unsigned long block_sz, start_pfn; |
---|
289 | 288 | int sections_per_block; |
---|
.. | .. |
---|
314 | 313 | |
---|
315 | 314 | static int pseries_remove_mem_node(struct device_node *np) |
---|
316 | 315 | { |
---|
317 | | - const char *type; |
---|
318 | | - const __be32 *regs; |
---|
| 316 | + const __be32 *prop; |
---|
319 | 317 | unsigned long base; |
---|
320 | | - unsigned int lmb_size; |
---|
| 318 | + unsigned long lmb_size; |
---|
321 | 319 | int ret = -EINVAL; |
---|
| 320 | + int addr_cells, size_cells; |
---|
322 | 321 | |
---|
323 | 322 | /* |
---|
324 | 323 | * Check to see if we are actually removing memory |
---|
325 | 324 | */ |
---|
326 | | - type = of_get_property(np, "device_type", NULL); |
---|
327 | | - if (type == NULL || strcmp(type, "memory") != 0) |
---|
| 325 | + if (!of_node_is_type(np, "memory")) |
---|
328 | 326 | return 0; |
---|
329 | 327 | |
---|
330 | 328 | /* |
---|
331 | 329 | * Find the base address and size of the memblock |
---|
332 | 330 | */ |
---|
333 | | - regs = of_get_property(np, "reg", NULL); |
---|
334 | | - if (!regs) |
---|
| 331 | + prop = of_get_property(np, "reg", NULL); |
---|
| 332 | + if (!prop) |
---|
335 | 333 | return ret; |
---|
336 | 334 | |
---|
337 | | - base = be64_to_cpu(*(unsigned long *)regs); |
---|
338 | | - lmb_size = be32_to_cpu(regs[3]); |
---|
| 335 | + addr_cells = of_n_addr_cells(np); |
---|
| 336 | + size_cells = of_n_size_cells(np); |
---|
| 337 | + |
---|
| 338 | + /* |
---|
| 339 | + * "reg" property represents (addr,size) tuple. |
---|
| 340 | + */ |
---|
| 341 | + base = of_read_number(prop, addr_cells); |
---|
| 342 | + prop += addr_cells; |
---|
| 343 | + lmb_size = of_read_number(prop, size_cells); |
---|
339 | 344 | |
---|
340 | 345 | pseries_remove_memblock(base, lmb_size); |
---|
341 | 346 | return 0; |
---|
.. | .. |
---|
343 | 348 | |
---|
344 | 349 | static bool lmb_is_removable(struct drmem_lmb *lmb) |
---|
345 | 350 | { |
---|
346 | | - int i, scns_per_block; |
---|
347 | | - int rc = 1; |
---|
348 | | - unsigned long pfn, block_sz; |
---|
349 | | - u64 phys_addr; |
---|
350 | | - |
---|
351 | 351 | if (!(lmb->flags & DRCONF_MEM_ASSIGNED)) |
---|
352 | 352 | return false; |
---|
353 | | - |
---|
354 | | - block_sz = memory_block_size_bytes(); |
---|
355 | | - scns_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE; |
---|
356 | | - phys_addr = lmb->base_addr; |
---|
357 | 353 | |
---|
358 | 354 | #ifdef CONFIG_FA_DUMP |
---|
359 | 355 | /* |
---|
360 | 356 | * Don't hot-remove memory that falls in fadump boot memory area |
---|
361 | 357 | * and memory that is reserved for capturing old kernel memory. |
---|
362 | 358 | */ |
---|
363 | | - if (is_fadump_memory_area(phys_addr, block_sz)) |
---|
| 359 | + if (is_fadump_memory_area(lmb->base_addr, memory_block_size_bytes())) |
---|
364 | 360 | return false; |
---|
365 | 361 | #endif |
---|
366 | | - |
---|
367 | | - for (i = 0; i < scns_per_block; i++) { |
---|
368 | | - pfn = PFN_DOWN(phys_addr); |
---|
369 | | - if (!pfn_present(pfn)) { |
---|
370 | | - phys_addr += MIN_MEMORY_BLOCK_SIZE; |
---|
371 | | - continue; |
---|
372 | | - } |
---|
373 | | - |
---|
374 | | - rc &= is_mem_section_removable(pfn, PAGES_PER_SECTION); |
---|
375 | | - phys_addr += MIN_MEMORY_BLOCK_SIZE; |
---|
376 | | - } |
---|
377 | | - |
---|
378 | | - return rc ? true : false; |
---|
| 362 | + /* device_offline() will determine if we can actually remove this lmb */ |
---|
| 363 | + return true; |
---|
379 | 364 | } |
---|
380 | 365 | |
---|
381 | 366 | static int dlpar_add_lmb(struct drmem_lmb *); |
---|
382 | 367 | |
---|
383 | 368 | static int dlpar_remove_lmb(struct drmem_lmb *lmb) |
---|
384 | 369 | { |
---|
| 370 | + struct memory_block *mem_block; |
---|
385 | 371 | unsigned long block_sz; |
---|
386 | | - int nid, rc; |
---|
| 372 | + int rc; |
---|
387 | 373 | |
---|
388 | 374 | if (!lmb_is_removable(lmb)) |
---|
389 | 375 | return -EINVAL; |
---|
390 | 376 | |
---|
| 377 | + mem_block = lmb_to_memblock(lmb); |
---|
| 378 | + if (mem_block == NULL) |
---|
| 379 | + return -EINVAL; |
---|
| 380 | + |
---|
391 | 381 | rc = dlpar_offline_lmb(lmb); |
---|
392 | | - if (rc) |
---|
| 382 | + if (rc) { |
---|
| 383 | + put_device(&mem_block->dev); |
---|
393 | 384 | return rc; |
---|
| 385 | + } |
---|
394 | 386 | |
---|
395 | 387 | block_sz = pseries_memory_block_size(); |
---|
396 | | - nid = memory_add_physaddr_to_nid(lmb->base_addr); |
---|
397 | 388 | |
---|
398 | | - __remove_memory(nid, lmb->base_addr, block_sz); |
---|
| 389 | + __remove_memory(mem_block->nid, lmb->base_addr, block_sz); |
---|
| 390 | + put_device(&mem_block->dev); |
---|
399 | 391 | |
---|
400 | 392 | /* Update memory regions for memory remove */ |
---|
401 | 393 | memblock_remove(lmb->base_addr, block_sz); |
---|
.. | .. |
---|
513 | 505 | return rc; |
---|
514 | 506 | } |
---|
515 | 507 | |
---|
516 | | -static int dlpar_memory_readd_by_index(u32 drc_index) |
---|
517 | | -{ |
---|
518 | | - struct drmem_lmb *lmb; |
---|
519 | | - int lmb_found; |
---|
520 | | - int rc; |
---|
521 | | - |
---|
522 | | - pr_info("Attempting to update LMB, drc index %x\n", drc_index); |
---|
523 | | - |
---|
524 | | - lmb_found = 0; |
---|
525 | | - for_each_drmem_lmb(lmb) { |
---|
526 | | - if (lmb->drc_index == drc_index) { |
---|
527 | | - lmb_found = 1; |
---|
528 | | - rc = dlpar_remove_lmb(lmb); |
---|
529 | | - if (!rc) { |
---|
530 | | - rc = dlpar_add_lmb(lmb); |
---|
531 | | - if (rc) |
---|
532 | | - dlpar_release_drc(lmb->drc_index); |
---|
533 | | - } |
---|
534 | | - break; |
---|
535 | | - } |
---|
536 | | - } |
---|
537 | | - |
---|
538 | | - if (!lmb_found) |
---|
539 | | - rc = -EINVAL; |
---|
540 | | - |
---|
541 | | - if (rc) |
---|
542 | | - pr_info("Failed to update memory at %llx\n", |
---|
543 | | - lmb->base_addr); |
---|
544 | | - else |
---|
545 | | - pr_info("Memory at %llx was updated\n", lmb->base_addr); |
---|
546 | | - |
---|
547 | | - return rc; |
---|
548 | | -} |
---|
549 | | - |
---|
550 | 508 | static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index) |
---|
551 | 509 | { |
---|
552 | 510 | struct drmem_lmb *lmb, *start_lmb, *end_lmb; |
---|
.. | .. |
---|
619 | 577 | |
---|
620 | 578 | #else |
---|
621 | 579 | static inline int pseries_remove_memblock(unsigned long base, |
---|
622 | | - unsigned int memblock_size) |
---|
| 580 | + unsigned long memblock_size) |
---|
623 | 581 | { |
---|
624 | 582 | return -EOPNOTSUPP; |
---|
625 | 583 | } |
---|
.. | .. |
---|
640 | 598 | return -EOPNOTSUPP; |
---|
641 | 599 | } |
---|
642 | 600 | static int dlpar_memory_remove_by_index(u32 drc_index) |
---|
643 | | -{ |
---|
644 | | - return -EOPNOTSUPP; |
---|
645 | | -} |
---|
646 | | -static int dlpar_memory_readd_by_index(u32 drc_index) |
---|
647 | 601 | { |
---|
648 | 602 | return -EOPNOTSUPP; |
---|
649 | 603 | } |
---|
.. | .. |
---|
670 | 624 | |
---|
671 | 625 | block_sz = memory_block_size_bytes(); |
---|
672 | 626 | |
---|
673 | | - /* Find the node id for this address */ |
---|
674 | | - nid = memory_add_physaddr_to_nid(lmb->base_addr); |
---|
| 627 | + /* Find the node id for this LMB. Fake one if necessary. */ |
---|
| 628 | + nid = of_drconf_to_nid_single(lmb); |
---|
| 629 | + if (nid < 0 || !node_possible(nid)) |
---|
| 630 | + nid = first_online_node; |
---|
675 | 631 | |
---|
676 | 632 | /* Add the memory */ |
---|
677 | | - rc = __add_memory(nid, lmb->base_addr, block_sz); |
---|
| 633 | + rc = __add_memory(nid, lmb->base_addr, block_sz, MHP_NONE); |
---|
678 | 634 | if (rc) { |
---|
679 | 635 | invalidate_lmb_associativity_index(lmb); |
---|
680 | 636 | return rc; |
---|
.. | .. |
---|
889 | 845 | |
---|
890 | 846 | switch (hp_elog->action) { |
---|
891 | 847 | case PSERIES_HP_ELOG_ACTION_ADD: |
---|
892 | | - if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) { |
---|
| 848 | + switch (hp_elog->id_type) { |
---|
| 849 | + case PSERIES_HP_ELOG_ID_DRC_COUNT: |
---|
893 | 850 | count = hp_elog->_drc_u.drc_count; |
---|
894 | 851 | rc = dlpar_memory_add_by_count(count); |
---|
895 | | - } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) { |
---|
| 852 | + break; |
---|
| 853 | + case PSERIES_HP_ELOG_ID_DRC_INDEX: |
---|
896 | 854 | drc_index = hp_elog->_drc_u.drc_index; |
---|
897 | 855 | rc = dlpar_memory_add_by_index(drc_index); |
---|
898 | | - } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_IC) { |
---|
| 856 | + break; |
---|
| 857 | + case PSERIES_HP_ELOG_ID_DRC_IC: |
---|
899 | 858 | count = hp_elog->_drc_u.ic.count; |
---|
900 | 859 | drc_index = hp_elog->_drc_u.ic.index; |
---|
901 | 860 | rc = dlpar_memory_add_by_ic(count, drc_index); |
---|
902 | | - } else { |
---|
| 861 | + break; |
---|
| 862 | + default: |
---|
903 | 863 | rc = -EINVAL; |
---|
| 864 | + break; |
---|
904 | 865 | } |
---|
905 | 866 | |
---|
906 | 867 | break; |
---|
907 | 868 | case PSERIES_HP_ELOG_ACTION_REMOVE: |
---|
908 | | - if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) { |
---|
| 869 | + switch (hp_elog->id_type) { |
---|
| 870 | + case PSERIES_HP_ELOG_ID_DRC_COUNT: |
---|
909 | 871 | count = hp_elog->_drc_u.drc_count; |
---|
910 | 872 | rc = dlpar_memory_remove_by_count(count); |
---|
911 | | - } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) { |
---|
| 873 | + break; |
---|
| 874 | + case PSERIES_HP_ELOG_ID_DRC_INDEX: |
---|
912 | 875 | drc_index = hp_elog->_drc_u.drc_index; |
---|
913 | 876 | rc = dlpar_memory_remove_by_index(drc_index); |
---|
914 | | - } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_IC) { |
---|
| 877 | + break; |
---|
| 878 | + case PSERIES_HP_ELOG_ID_DRC_IC: |
---|
915 | 879 | count = hp_elog->_drc_u.ic.count; |
---|
916 | 880 | drc_index = hp_elog->_drc_u.ic.index; |
---|
917 | 881 | rc = dlpar_memory_remove_by_ic(count, drc_index); |
---|
918 | | - } else { |
---|
| 882 | + break; |
---|
| 883 | + default: |
---|
919 | 884 | rc = -EINVAL; |
---|
| 885 | + break; |
---|
920 | 886 | } |
---|
921 | 887 | |
---|
922 | | - break; |
---|
923 | | - case PSERIES_HP_ELOG_ACTION_READD: |
---|
924 | | - drc_index = hp_elog->_drc_u.drc_index; |
---|
925 | | - rc = dlpar_memory_readd_by_index(drc_index); |
---|
926 | 888 | break; |
---|
927 | 889 | default: |
---|
928 | 890 | pr_err("Invalid action (%d) specified\n", hp_elog->action); |
---|
.. | .. |
---|
930 | 892 | break; |
---|
931 | 893 | } |
---|
932 | 894 | |
---|
933 | | - if (!rc) { |
---|
934 | | - rtas_hp_event = true; |
---|
| 895 | + if (!rc) |
---|
935 | 896 | rc = drmem_update_dt(); |
---|
936 | | - rtas_hp_event = false; |
---|
937 | | - } |
---|
938 | 897 | |
---|
939 | 898 | unlock_device_hotplug(); |
---|
940 | 899 | return rc; |
---|
.. | .. |
---|
942 | 901 | |
---|
943 | 902 | static int pseries_add_mem_node(struct device_node *np) |
---|
944 | 903 | { |
---|
945 | | - const char *type; |
---|
946 | | - const __be32 *regs; |
---|
| 904 | + const __be32 *prop; |
---|
947 | 905 | unsigned long base; |
---|
948 | | - unsigned int lmb_size; |
---|
| 906 | + unsigned long lmb_size; |
---|
949 | 907 | int ret = -EINVAL; |
---|
| 908 | + int addr_cells, size_cells; |
---|
950 | 909 | |
---|
951 | 910 | /* |
---|
952 | 911 | * Check to see if we are actually adding memory |
---|
953 | 912 | */ |
---|
954 | | - type = of_get_property(np, "device_type", NULL); |
---|
955 | | - if (type == NULL || strcmp(type, "memory") != 0) |
---|
| 913 | + if (!of_node_is_type(np, "memory")) |
---|
956 | 914 | return 0; |
---|
957 | 915 | |
---|
958 | 916 | /* |
---|
959 | 917 | * Find the base and size of the memblock |
---|
960 | 918 | */ |
---|
961 | | - regs = of_get_property(np, "reg", NULL); |
---|
962 | | - if (!regs) |
---|
| 919 | + prop = of_get_property(np, "reg", NULL); |
---|
| 920 | + if (!prop) |
---|
963 | 921 | return ret; |
---|
964 | 922 | |
---|
965 | | - base = be64_to_cpu(*(unsigned long *)regs); |
---|
966 | | - lmb_size = be32_to_cpu(regs[3]); |
---|
| 923 | + addr_cells = of_n_addr_cells(np); |
---|
| 924 | + size_cells = of_n_size_cells(np); |
---|
| 925 | + /* |
---|
| 926 | + * "reg" property represents (addr,size) tuple. |
---|
| 927 | + */ |
---|
| 928 | + base = of_read_number(prop, addr_cells); |
---|
| 929 | + prop += addr_cells; |
---|
| 930 | + lmb_size = of_read_number(prop, size_cells); |
---|
967 | 931 | |
---|
968 | 932 | /* |
---|
969 | 933 | * Update memory region to represent the memory add |
---|
970 | 934 | */ |
---|
971 | 935 | ret = memblock_add(base, lmb_size); |
---|
972 | 936 | return (ret < 0) ? -EINVAL : 0; |
---|
973 | | -} |
---|
974 | | - |
---|
975 | | -static int pseries_update_drconf_memory(struct of_reconfig_data *pr) |
---|
976 | | -{ |
---|
977 | | - struct of_drconf_cell_v1 *new_drmem, *old_drmem; |
---|
978 | | - unsigned long memblock_size; |
---|
979 | | - u32 entries; |
---|
980 | | - __be32 *p; |
---|
981 | | - int i, rc = -EINVAL; |
---|
982 | | - |
---|
983 | | - if (rtas_hp_event) |
---|
984 | | - return 0; |
---|
985 | | - |
---|
986 | | - memblock_size = pseries_memory_block_size(); |
---|
987 | | - if (!memblock_size) |
---|
988 | | - return -EINVAL; |
---|
989 | | - |
---|
990 | | - if (!pr->old_prop) |
---|
991 | | - return 0; |
---|
992 | | - |
---|
993 | | - p = (__be32 *) pr->old_prop->value; |
---|
994 | | - if (!p) |
---|
995 | | - return -EINVAL; |
---|
996 | | - |
---|
997 | | - /* The first int of the property is the number of lmb's described |
---|
998 | | - * by the property. This is followed by an array of of_drconf_cell |
---|
999 | | - * entries. Get the number of entries and skip to the array of |
---|
1000 | | - * of_drconf_cell's. |
---|
1001 | | - */ |
---|
1002 | | - entries = be32_to_cpu(*p++); |
---|
1003 | | - old_drmem = (struct of_drconf_cell_v1 *)p; |
---|
1004 | | - |
---|
1005 | | - p = (__be32 *)pr->prop->value; |
---|
1006 | | - p++; |
---|
1007 | | - new_drmem = (struct of_drconf_cell_v1 *)p; |
---|
1008 | | - |
---|
1009 | | - for (i = 0; i < entries; i++) { |
---|
1010 | | - if ((be32_to_cpu(old_drmem[i].flags) & DRCONF_MEM_ASSIGNED) && |
---|
1011 | | - (!(be32_to_cpu(new_drmem[i].flags) & DRCONF_MEM_ASSIGNED))) { |
---|
1012 | | - rc = pseries_remove_memblock( |
---|
1013 | | - be64_to_cpu(old_drmem[i].base_addr), |
---|
1014 | | - memblock_size); |
---|
1015 | | - break; |
---|
1016 | | - } else if ((!(be32_to_cpu(old_drmem[i].flags) & |
---|
1017 | | - DRCONF_MEM_ASSIGNED)) && |
---|
1018 | | - (be32_to_cpu(new_drmem[i].flags) & |
---|
1019 | | - DRCONF_MEM_ASSIGNED)) { |
---|
1020 | | - rc = memblock_add(be64_to_cpu(old_drmem[i].base_addr), |
---|
1021 | | - memblock_size); |
---|
1022 | | - rc = (rc < 0) ? -EINVAL : 0; |
---|
1023 | | - break; |
---|
1024 | | - } |
---|
1025 | | - } |
---|
1026 | | - return rc; |
---|
1027 | 937 | } |
---|
1028 | 938 | |
---|
1029 | 939 | static int pseries_memory_notifier(struct notifier_block *nb, |
---|
.. | .. |
---|
1038 | 948 | break; |
---|
1039 | 949 | case OF_RECONFIG_DETACH_NODE: |
---|
1040 | 950 | err = pseries_remove_mem_node(rd->dn); |
---|
1041 | | - break; |
---|
1042 | | - case OF_RECONFIG_UPDATE_PROPERTY: |
---|
1043 | | - if (!strcmp(rd->prop->name, "ibm,dynamic-memory")) |
---|
1044 | | - err = pseries_update_drconf_memory(rd); |
---|
1045 | 951 | break; |
---|
1046 | 952 | } |
---|
1047 | 953 | return notifier_from_errno(err); |
---|