.. | .. |
---|
| 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; |
---|
.. | .. |
---|
181 | 180 | return -ENODEV; |
---|
182 | 181 | } |
---|
183 | 182 | |
---|
| 183 | + update_numa_distance(lmb_node); |
---|
| 184 | + |
---|
184 | 185 | dr_node = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); |
---|
185 | 186 | if (!dr_node) { |
---|
186 | 187 | dlpar_free_cc_nodes(lmb_node); |
---|
.. | .. |
---|
283 | 284 | return dlpar_change_lmb_state(lmb, false); |
---|
284 | 285 | } |
---|
285 | 286 | |
---|
286 | | -static int pseries_remove_memblock(unsigned long base, unsigned int memblock_size) |
---|
| 287 | +static int pseries_remove_memblock(unsigned long base, unsigned long memblock_size) |
---|
287 | 288 | { |
---|
288 | 289 | unsigned long block_sz, start_pfn; |
---|
289 | 290 | int sections_per_block; |
---|
.. | .. |
---|
314 | 315 | |
---|
315 | 316 | static int pseries_remove_mem_node(struct device_node *np) |
---|
316 | 317 | { |
---|
317 | | - const char *type; |
---|
318 | | - const __be32 *regs; |
---|
| 318 | + const __be32 *prop; |
---|
319 | 319 | unsigned long base; |
---|
320 | | - unsigned int lmb_size; |
---|
| 320 | + unsigned long lmb_size; |
---|
321 | 321 | int ret = -EINVAL; |
---|
| 322 | + int addr_cells, size_cells; |
---|
322 | 323 | |
---|
323 | 324 | /* |
---|
324 | 325 | * Check to see if we are actually removing memory |
---|
325 | 326 | */ |
---|
326 | | - type = of_get_property(np, "device_type", NULL); |
---|
327 | | - if (type == NULL || strcmp(type, "memory") != 0) |
---|
| 327 | + if (!of_node_is_type(np, "memory")) |
---|
328 | 328 | return 0; |
---|
329 | 329 | |
---|
330 | 330 | /* |
---|
331 | 331 | * Find the base address and size of the memblock |
---|
332 | 332 | */ |
---|
333 | | - regs = of_get_property(np, "reg", NULL); |
---|
334 | | - if (!regs) |
---|
| 333 | + prop = of_get_property(np, "reg", NULL); |
---|
| 334 | + if (!prop) |
---|
335 | 335 | return ret; |
---|
336 | 336 | |
---|
337 | | - base = be64_to_cpu(*(unsigned long *)regs); |
---|
338 | | - lmb_size = be32_to_cpu(regs[3]); |
---|
| 337 | + addr_cells = of_n_addr_cells(np); |
---|
| 338 | + size_cells = of_n_size_cells(np); |
---|
| 339 | + |
---|
| 340 | + /* |
---|
| 341 | + * "reg" property represents (addr,size) tuple. |
---|
| 342 | + */ |
---|
| 343 | + base = of_read_number(prop, addr_cells); |
---|
| 344 | + prop += addr_cells; |
---|
| 345 | + lmb_size = of_read_number(prop, size_cells); |
---|
339 | 346 | |
---|
340 | 347 | pseries_remove_memblock(base, lmb_size); |
---|
341 | 348 | return 0; |
---|
.. | .. |
---|
343 | 350 | |
---|
344 | 351 | static bool lmb_is_removable(struct drmem_lmb *lmb) |
---|
345 | 352 | { |
---|
346 | | - int i, scns_per_block; |
---|
347 | | - int rc = 1; |
---|
348 | | - unsigned long pfn, block_sz; |
---|
349 | | - u64 phys_addr; |
---|
350 | | - |
---|
351 | 353 | if (!(lmb->flags & DRCONF_MEM_ASSIGNED)) |
---|
352 | 354 | 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 | 355 | |
---|
358 | 356 | #ifdef CONFIG_FA_DUMP |
---|
359 | 357 | /* |
---|
360 | 358 | * Don't hot-remove memory that falls in fadump boot memory area |
---|
361 | 359 | * and memory that is reserved for capturing old kernel memory. |
---|
362 | 360 | */ |
---|
363 | | - if (is_fadump_memory_area(phys_addr, block_sz)) |
---|
| 361 | + if (is_fadump_memory_area(lmb->base_addr, memory_block_size_bytes())) |
---|
364 | 362 | return false; |
---|
365 | 363 | #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; |
---|
| 364 | + /* device_offline() will determine if we can actually remove this lmb */ |
---|
| 365 | + return true; |
---|
379 | 366 | } |
---|
380 | 367 | |
---|
381 | 368 | static int dlpar_add_lmb(struct drmem_lmb *); |
---|
382 | 369 | |
---|
383 | 370 | static int dlpar_remove_lmb(struct drmem_lmb *lmb) |
---|
384 | 371 | { |
---|
| 372 | + struct memory_block *mem_block; |
---|
385 | 373 | unsigned long block_sz; |
---|
386 | | - int nid, rc; |
---|
| 374 | + int rc; |
---|
387 | 375 | |
---|
388 | 376 | if (!lmb_is_removable(lmb)) |
---|
389 | 377 | return -EINVAL; |
---|
390 | 378 | |
---|
| 379 | + mem_block = lmb_to_memblock(lmb); |
---|
| 380 | + if (mem_block == NULL) |
---|
| 381 | + return -EINVAL; |
---|
| 382 | + |
---|
391 | 383 | rc = dlpar_offline_lmb(lmb); |
---|
392 | | - if (rc) |
---|
| 384 | + if (rc) { |
---|
| 385 | + put_device(&mem_block->dev); |
---|
393 | 386 | return rc; |
---|
| 387 | + } |
---|
394 | 388 | |
---|
395 | 389 | block_sz = pseries_memory_block_size(); |
---|
396 | | - nid = memory_add_physaddr_to_nid(lmb->base_addr); |
---|
397 | 390 | |
---|
398 | | - __remove_memory(nid, lmb->base_addr, block_sz); |
---|
| 391 | + __remove_memory(mem_block->nid, lmb->base_addr, block_sz); |
---|
| 392 | + put_device(&mem_block->dev); |
---|
399 | 393 | |
---|
400 | 394 | /* Update memory regions for memory remove */ |
---|
401 | 395 | memblock_remove(lmb->base_addr, block_sz); |
---|
.. | .. |
---|
513 | 507 | return rc; |
---|
514 | 508 | } |
---|
515 | 509 | |
---|
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 | 510 | static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index) |
---|
551 | 511 | { |
---|
552 | 512 | struct drmem_lmb *lmb, *start_lmb, *end_lmb; |
---|
.. | .. |
---|
619 | 579 | |
---|
620 | 580 | #else |
---|
621 | 581 | static inline int pseries_remove_memblock(unsigned long base, |
---|
622 | | - unsigned int memblock_size) |
---|
| 582 | + unsigned long memblock_size) |
---|
623 | 583 | { |
---|
624 | 584 | return -EOPNOTSUPP; |
---|
625 | 585 | } |
---|
.. | .. |
---|
640 | 600 | return -EOPNOTSUPP; |
---|
641 | 601 | } |
---|
642 | 602 | 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 | 603 | { |
---|
648 | 604 | return -EOPNOTSUPP; |
---|
649 | 605 | } |
---|
.. | .. |
---|
670 | 626 | |
---|
671 | 627 | block_sz = memory_block_size_bytes(); |
---|
672 | 628 | |
---|
673 | | - /* Find the node id for this address */ |
---|
674 | | - nid = memory_add_physaddr_to_nid(lmb->base_addr); |
---|
| 629 | + /* Find the node id for this LMB. Fake one if necessary. */ |
---|
| 630 | + nid = of_drconf_to_nid_single(lmb); |
---|
| 631 | + if (nid < 0 || !node_possible(nid)) |
---|
| 632 | + nid = first_online_node; |
---|
675 | 633 | |
---|
676 | 634 | /* Add the memory */ |
---|
677 | | - rc = __add_memory(nid, lmb->base_addr, block_sz); |
---|
| 635 | + rc = __add_memory(nid, lmb->base_addr, block_sz, MHP_NONE); |
---|
678 | 636 | if (rc) { |
---|
679 | 637 | invalidate_lmb_associativity_index(lmb); |
---|
680 | 638 | return rc; |
---|
.. | .. |
---|
889 | 847 | |
---|
890 | 848 | switch (hp_elog->action) { |
---|
891 | 849 | case PSERIES_HP_ELOG_ACTION_ADD: |
---|
892 | | - if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) { |
---|
| 850 | + switch (hp_elog->id_type) { |
---|
| 851 | + case PSERIES_HP_ELOG_ID_DRC_COUNT: |
---|
893 | 852 | count = hp_elog->_drc_u.drc_count; |
---|
894 | 853 | rc = dlpar_memory_add_by_count(count); |
---|
895 | | - } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) { |
---|
| 854 | + break; |
---|
| 855 | + case PSERIES_HP_ELOG_ID_DRC_INDEX: |
---|
896 | 856 | drc_index = hp_elog->_drc_u.drc_index; |
---|
897 | 857 | rc = dlpar_memory_add_by_index(drc_index); |
---|
898 | | - } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_IC) { |
---|
| 858 | + break; |
---|
| 859 | + case PSERIES_HP_ELOG_ID_DRC_IC: |
---|
899 | 860 | count = hp_elog->_drc_u.ic.count; |
---|
900 | 861 | drc_index = hp_elog->_drc_u.ic.index; |
---|
901 | 862 | rc = dlpar_memory_add_by_ic(count, drc_index); |
---|
902 | | - } else { |
---|
| 863 | + break; |
---|
| 864 | + default: |
---|
903 | 865 | rc = -EINVAL; |
---|
| 866 | + break; |
---|
904 | 867 | } |
---|
905 | 868 | |
---|
906 | 869 | break; |
---|
907 | 870 | case PSERIES_HP_ELOG_ACTION_REMOVE: |
---|
908 | | - if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) { |
---|
| 871 | + switch (hp_elog->id_type) { |
---|
| 872 | + case PSERIES_HP_ELOG_ID_DRC_COUNT: |
---|
909 | 873 | count = hp_elog->_drc_u.drc_count; |
---|
910 | 874 | rc = dlpar_memory_remove_by_count(count); |
---|
911 | | - } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) { |
---|
| 875 | + break; |
---|
| 876 | + case PSERIES_HP_ELOG_ID_DRC_INDEX: |
---|
912 | 877 | drc_index = hp_elog->_drc_u.drc_index; |
---|
913 | 878 | rc = dlpar_memory_remove_by_index(drc_index); |
---|
914 | | - } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_IC) { |
---|
| 879 | + break; |
---|
| 880 | + case PSERIES_HP_ELOG_ID_DRC_IC: |
---|
915 | 881 | count = hp_elog->_drc_u.ic.count; |
---|
916 | 882 | drc_index = hp_elog->_drc_u.ic.index; |
---|
917 | 883 | rc = dlpar_memory_remove_by_ic(count, drc_index); |
---|
918 | | - } else { |
---|
| 884 | + break; |
---|
| 885 | + default: |
---|
919 | 886 | rc = -EINVAL; |
---|
| 887 | + break; |
---|
920 | 888 | } |
---|
921 | 889 | |
---|
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 | 890 | break; |
---|
927 | 891 | default: |
---|
928 | 892 | pr_err("Invalid action (%d) specified\n", hp_elog->action); |
---|
.. | .. |
---|
930 | 894 | break; |
---|
931 | 895 | } |
---|
932 | 896 | |
---|
933 | | - if (!rc) { |
---|
934 | | - rtas_hp_event = true; |
---|
| 897 | + if (!rc) |
---|
935 | 898 | rc = drmem_update_dt(); |
---|
936 | | - rtas_hp_event = false; |
---|
937 | | - } |
---|
938 | 899 | |
---|
939 | 900 | unlock_device_hotplug(); |
---|
940 | 901 | return rc; |
---|
.. | .. |
---|
942 | 903 | |
---|
943 | 904 | static int pseries_add_mem_node(struct device_node *np) |
---|
944 | 905 | { |
---|
945 | | - const char *type; |
---|
946 | | - const __be32 *regs; |
---|
| 906 | + const __be32 *prop; |
---|
947 | 907 | unsigned long base; |
---|
948 | | - unsigned int lmb_size; |
---|
| 908 | + unsigned long lmb_size; |
---|
949 | 909 | int ret = -EINVAL; |
---|
| 910 | + int addr_cells, size_cells; |
---|
950 | 911 | |
---|
951 | 912 | /* |
---|
952 | 913 | * Check to see if we are actually adding memory |
---|
953 | 914 | */ |
---|
954 | | - type = of_get_property(np, "device_type", NULL); |
---|
955 | | - if (type == NULL || strcmp(type, "memory") != 0) |
---|
| 915 | + if (!of_node_is_type(np, "memory")) |
---|
956 | 916 | return 0; |
---|
957 | 917 | |
---|
958 | 918 | /* |
---|
959 | 919 | * Find the base and size of the memblock |
---|
960 | 920 | */ |
---|
961 | | - regs = of_get_property(np, "reg", NULL); |
---|
962 | | - if (!regs) |
---|
| 921 | + prop = of_get_property(np, "reg", NULL); |
---|
| 922 | + if (!prop) |
---|
963 | 923 | return ret; |
---|
964 | 924 | |
---|
965 | | - base = be64_to_cpu(*(unsigned long *)regs); |
---|
966 | | - lmb_size = be32_to_cpu(regs[3]); |
---|
| 925 | + addr_cells = of_n_addr_cells(np); |
---|
| 926 | + size_cells = of_n_size_cells(np); |
---|
| 927 | + /* |
---|
| 928 | + * "reg" property represents (addr,size) tuple. |
---|
| 929 | + */ |
---|
| 930 | + base = of_read_number(prop, addr_cells); |
---|
| 931 | + prop += addr_cells; |
---|
| 932 | + lmb_size = of_read_number(prop, size_cells); |
---|
967 | 933 | |
---|
968 | 934 | /* |
---|
969 | 935 | * Update memory region to represent the memory add |
---|
970 | 936 | */ |
---|
971 | 937 | ret = memblock_add(base, lmb_size); |
---|
972 | 938 | 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 | 939 | } |
---|
1028 | 940 | |
---|
1029 | 941 | static int pseries_memory_notifier(struct notifier_block *nb, |
---|
.. | .. |
---|
1038 | 950 | break; |
---|
1039 | 951 | case OF_RECONFIG_DETACH_NODE: |
---|
1040 | 952 | 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 | 953 | break; |
---|
1046 | 954 | } |
---|
1047 | 955 | return notifier_from_errno(err); |
---|