Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'libnvdimm-fixes-4.20-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm

Pull libnvdimm fixes from Dan Williams:
"A regression fix for the Address Range Scrub implementation, yes
another one, and support for platforms that misalign persistent memory
relative to the Linux memory hotplug section constraint. Longer term,
support for sub-section memory hotplug would alleviate alignment
waste, but until then this hack allows a 'struct page' memmap to be
established for these misaligned memory regions.

These have all appeared in a -next release, and thanks to Patrick for
reporting and testing the alignment padding fix.

Summary:

- Unless and until the core mm handles memory hotplug units smaller
than a section (128M), persistent memory namespaces must be padded
to section alignment.

The libnvdimm core already handled section collision with "System
RAM", but some configurations overlap independent "Persistent
Memory" ranges within a section, so additional padding injection is
added for that case.

- The recent reworks of the ARS (address range scrub) state machine
to reduce the number of state flags inadvertantly missed a
conversion of acpi_nfit_ars_rescan() call sites. Fix the regression
whereby user-requested ARS results in a "short" scrub rather than a
"long" scrub.

- Fixup the unit tests to handle / test the 128M section alignment of
mocked test resources.

* tag 'libnvdimm-fixes-4.20-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm:
acpi/nfit: Fix user-initiated ARS to be "ARS-long" rather than "ARS-short"
libnvdimm, pfn: Pad pfn namespaces relative to other regions
tools/testing/nvdimm: Align test resources to 128M

+114 -30
+1 -1
drivers/acpi/nfit/core.c
··· 1308 1308 if (nd_desc) { 1309 1309 struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); 1310 1310 1311 - rc = acpi_nfit_ars_rescan(acpi_desc, 0); 1311 + rc = acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG); 1312 1312 } 1313 1313 device_unlock(dev); 1314 1314 if (rc)
+2
drivers/nvdimm/nd-core.h
··· 111 111 struct nd_mapping *nd_mapping, resource_size_t *overlap); 112 112 resource_size_t nd_blk_available_dpa(struct nd_region *nd_region); 113 113 resource_size_t nd_region_available_dpa(struct nd_region *nd_region); 114 + int nd_region_conflict(struct nd_region *nd_region, resource_size_t start, 115 + resource_size_t size); 114 116 resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd, 115 117 struct nd_label_id *label_id); 116 118 int alias_dpa_busy(struct device *dev, void *data);
+37 -27
drivers/nvdimm/pfn_devs.c
··· 649 649 ALIGN_DOWN(phys, nd_pfn->align)); 650 650 } 651 651 652 + /* 653 + * Check if pmem collides with 'System RAM', or other regions when 654 + * section aligned. Trim it accordingly. 655 + */ 656 + static void trim_pfn_device(struct nd_pfn *nd_pfn, u32 *start_pad, u32 *end_trunc) 657 + { 658 + struct nd_namespace_common *ndns = nd_pfn->ndns; 659 + struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); 660 + struct nd_region *nd_region = to_nd_region(nd_pfn->dev.parent); 661 + const resource_size_t start = nsio->res.start; 662 + const resource_size_t end = start + resource_size(&nsio->res); 663 + resource_size_t adjust, size; 664 + 665 + *start_pad = 0; 666 + *end_trunc = 0; 667 + 668 + adjust = start - PHYS_SECTION_ALIGN_DOWN(start); 669 + size = resource_size(&nsio->res) + adjust; 670 + if (region_intersects(start - adjust, size, IORESOURCE_SYSTEM_RAM, 671 + IORES_DESC_NONE) == REGION_MIXED 672 + || nd_region_conflict(nd_region, start - adjust, size)) 673 + *start_pad = PHYS_SECTION_ALIGN_UP(start) - start; 674 + 675 + /* Now check that end of the range does not collide. */ 676 + adjust = PHYS_SECTION_ALIGN_UP(end) - end; 677 + size = resource_size(&nsio->res) + adjust; 678 + if (region_intersects(start, size, IORESOURCE_SYSTEM_RAM, 679 + IORES_DESC_NONE) == REGION_MIXED 680 + || !IS_ALIGNED(end, nd_pfn->align) 681 + || nd_region_conflict(nd_region, start, size + adjust)) 682 + *end_trunc = end - phys_pmem_align_down(nd_pfn, end); 683 + } 684 + 652 685 static int nd_pfn_init(struct nd_pfn *nd_pfn) 653 686 { 654 687 u32 dax_label_reserve = is_nd_dax(&nd_pfn->dev) ? SZ_128K : 0; 655 688 struct nd_namespace_common *ndns = nd_pfn->ndns; 656 - u32 start_pad = 0, end_trunc = 0; 689 + struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); 657 690 resource_size_t start, size; 658 - struct nd_namespace_io *nsio; 659 691 struct nd_region *nd_region; 692 + u32 start_pad, end_trunc; 660 693 struct nd_pfn_sb *pfn_sb; 661 694 unsigned long npfns; 662 695 phys_addr_t offset; ··· 721 688 722 689 memset(pfn_sb, 0, sizeof(*pfn_sb)); 723 690 724 - /* 725 - * Check if pmem collides with 'System RAM' when section aligned and 726 - * trim it accordingly 727 - */ 728 - nsio = to_nd_namespace_io(&ndns->dev); 729 - start = PHYS_SECTION_ALIGN_DOWN(nsio->res.start); 730 - size = resource_size(&nsio->res); 731 - if (region_intersects(start, size, IORESOURCE_SYSTEM_RAM, 732 - IORES_DESC_NONE) == REGION_MIXED) { 733 - start = nsio->res.start; 734 - start_pad = PHYS_SECTION_ALIGN_UP(start) - start; 735 - } 736 - 737 - start = nsio->res.start; 738 - size = PHYS_SECTION_ALIGN_UP(start + size) - start; 739 - if (region_intersects(start, size, IORESOURCE_SYSTEM_RAM, 740 - IORES_DESC_NONE) == REGION_MIXED 741 - || !IS_ALIGNED(start + resource_size(&nsio->res), 742 - nd_pfn->align)) { 743 - size = resource_size(&nsio->res); 744 - end_trunc = start + size - phys_pmem_align_down(nd_pfn, 745 - start + size); 746 - } 747 - 691 + trim_pfn_device(nd_pfn, &start_pad, &end_trunc); 748 692 if (start_pad + end_trunc) 749 693 dev_info(&nd_pfn->dev, "%s alignment collision, truncate %d bytes\n", 750 694 dev_name(&ndns->dev), start_pad + end_trunc); ··· 732 722 * implementation will limit the pfns advertised through 733 723 * ->direct_access() to those that are included in the memmap. 734 724 */ 735 - start += start_pad; 725 + start = nsio->res.start + start_pad; 736 726 size = resource_size(&nsio->res); 737 727 npfns = PFN_SECTION_ALIGN_UP((size - start_pad - end_trunc - SZ_8K) 738 728 / PAGE_SIZE);
+41
drivers/nvdimm/region_devs.c
··· 1184 1184 } 1185 1185 EXPORT_SYMBOL_GPL(nvdimm_has_cache); 1186 1186 1187 + struct conflict_context { 1188 + struct nd_region *nd_region; 1189 + resource_size_t start, size; 1190 + }; 1191 + 1192 + static int region_conflict(struct device *dev, void *data) 1193 + { 1194 + struct nd_region *nd_region; 1195 + struct conflict_context *ctx = data; 1196 + resource_size_t res_end, region_end, region_start; 1197 + 1198 + if (!is_memory(dev)) 1199 + return 0; 1200 + 1201 + nd_region = to_nd_region(dev); 1202 + if (nd_region == ctx->nd_region) 1203 + return 0; 1204 + 1205 + res_end = ctx->start + ctx->size; 1206 + region_start = nd_region->ndr_start; 1207 + region_end = region_start + nd_region->ndr_size; 1208 + if (ctx->start >= region_start && ctx->start < region_end) 1209 + return -EBUSY; 1210 + if (res_end > region_start && res_end <= region_end) 1211 + return -EBUSY; 1212 + return 0; 1213 + } 1214 + 1215 + int nd_region_conflict(struct nd_region *nd_region, resource_size_t start, 1216 + resource_size_t size) 1217 + { 1218 + struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); 1219 + struct conflict_context ctx = { 1220 + .nd_region = nd_region, 1221 + .start = start, 1222 + .size = size, 1223 + }; 1224 + 1225 + return device_for_each_child(&nvdimm_bus->dev, &ctx, region_conflict); 1226 + } 1227 + 1187 1228 void __exit nd_region_devs_exit(void) 1188 1229 { 1189 1230 ida_destroy(&region_ida);
+33 -2
tools/testing/nvdimm/test/nfit.c
··· 15 15 #include <linux/dma-mapping.h> 16 16 #include <linux/workqueue.h> 17 17 #include <linux/libnvdimm.h> 18 + #include <linux/genalloc.h> 18 19 #include <linux/vmalloc.h> 19 20 #include <linux/device.h> 20 21 #include <linux/module.h> ··· 215 214 }; 216 215 217 216 static struct workqueue_struct *nfit_wq; 217 + 218 + static struct gen_pool *nfit_pool; 218 219 219 220 static struct nfit_test *to_nfit_test(struct device *dev) 220 221 { ··· 1135 1132 list_del(&nfit_res->list); 1136 1133 spin_unlock(&nfit_test_lock); 1137 1134 1135 + if (resource_size(&nfit_res->res) >= DIMM_SIZE) 1136 + gen_pool_free(nfit_pool, nfit_res->res.start, 1137 + resource_size(&nfit_res->res)); 1138 1138 vfree(nfit_res->buf); 1139 1139 kfree(nfit_res); 1140 1140 } ··· 1150 1144 GFP_KERNEL); 1151 1145 int rc; 1152 1146 1153 - if (!buf || !nfit_res) 1147 + if (!buf || !nfit_res || !*dma) 1154 1148 goto err; 1155 1149 rc = devm_add_action(dev, release_nfit_res, nfit_res); 1156 1150 if (rc) ··· 1170 1164 1171 1165 return nfit_res->buf; 1172 1166 err: 1167 + if (*dma && size >= DIMM_SIZE) 1168 + gen_pool_free(nfit_pool, *dma, size); 1173 1169 if (buf) 1174 1170 vfree(buf); 1175 1171 kfree(nfit_res); ··· 1180 1172 1181 1173 static void *test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma) 1182 1174 { 1175 + struct genpool_data_align data = { 1176 + .align = SZ_128M, 1177 + }; 1183 1178 void *buf = vmalloc(size); 1184 1179 1185 - *dma = (unsigned long) buf; 1180 + if (size >= DIMM_SIZE) 1181 + *dma = gen_pool_alloc_algo(nfit_pool, size, 1182 + gen_pool_first_fit_align, &data); 1183 + else 1184 + *dma = (unsigned long) buf; 1186 1185 return __test_alloc(t, size, dma, buf); 1187 1186 } 1188 1187 ··· 2854 2839 goto err_register; 2855 2840 } 2856 2841 2842 + nfit_pool = gen_pool_create(ilog2(SZ_4M), NUMA_NO_NODE); 2843 + if (!nfit_pool) { 2844 + rc = -ENOMEM; 2845 + goto err_register; 2846 + } 2847 + 2848 + if (gen_pool_add(nfit_pool, SZ_4G, SZ_4G, NUMA_NO_NODE)) { 2849 + rc = -ENOMEM; 2850 + goto err_register; 2851 + } 2852 + 2857 2853 for (i = 0; i < NUM_NFITS; i++) { 2858 2854 struct nfit_test *nfit_test; 2859 2855 struct platform_device *pdev; ··· 2920 2894 return 0; 2921 2895 2922 2896 err_register: 2897 + if (nfit_pool) 2898 + gen_pool_destroy(nfit_pool); 2899 + 2923 2900 destroy_workqueue(nfit_wq); 2924 2901 for (i = 0; i < NUM_NFITS; i++) 2925 2902 if (instances[i]) ··· 2945 2916 platform_device_unregister(&instances[i]->pdev); 2946 2917 platform_driver_unregister(&nfit_test_driver); 2947 2918 nfit_test_teardown(); 2919 + 2920 + gen_pool_destroy(nfit_pool); 2948 2921 2949 2922 for (i = 0; i < NUM_NFITS; i++) 2950 2923 put_device(&instances[i]->pdev.dev);