Unified · Split
| @@ -886,6 +886,58 @@ static ssize_t revision_show(struct device *dev, · 885 unmodified lines | ||
| 886 | 886 | } |
| 887 | 887 | static DEVICE_ATTR_RO(revision); |
| 888 | 888 | |
| 889 | +static ssize_t hw_error_scrub_show(struct device *dev, | |
| 890 | + struct device_attribute *attr, char *buf) | |
| 891 | +{ | |
| 892 | + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); | |
| 893 | + struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus); | |
| 894 | + struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); | |
| 895 | + | |
| 896 | + return sprintf(buf, "%d\n", acpi_desc->scrub_mode); | |
| 897 | +} | |
| 898 | + | |
| 899 | +/* | |
| 900 | + * The 'hw_error_scrub' attribute can have the following values written to it: | |
| 901 | + * '0': Switch to the default mode where an exception will only insert | |
| 902 | + * the address of the memory error into the poison and badblocks lists. | |
| 903 | + * '1': Enable a full scrub to happen if an exception for a memory error is | |
| 904 | + * received. | |
| 905 | + */ | |
| 906 | +static ssize_t hw_error_scrub_store(struct device *dev, | |
| 907 | + struct device_attribute *attr, const char *buf, size_t size) | |
| 908 | +{ | |
| 909 | + struct nvdimm_bus_descriptor *nd_desc; | |
| 910 | + ssize_t rc; | |
| 911 | + long val; | |
| 912 | + | |
| 913 | + rc = kstrtol(buf, 0, &val); | |
| 914 | + if (rc) | |
| 915 | + return rc; | |
| 916 | + | |
| 917 | + device_lock(dev); | |
| 918 | + nd_desc = dev_get_drvdata(dev); | |
| 919 | + if (nd_desc) { | |
| 920 | + struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); | |
| 921 | + | |
| 922 | + switch (val) { | |
| 923 | + case HW_ERROR_SCRUB_ON: | |
| 924 | + acpi_desc->scrub_mode = HW_ERROR_SCRUB_ON; | |
| 925 | + break; | |
| 926 | + case HW_ERROR_SCRUB_OFF: | |
| 927 | + acpi_desc->scrub_mode = HW_ERROR_SCRUB_OFF; | |
| 928 | + break; | |
| 929 | + default: | |
| 930 | + rc = -EINVAL; | |
| 931 | + break; | |
| 932 | + } | |
| 933 | + } | |
| 934 | + device_unlock(dev); | |
| 935 | + if (rc) | |
| 936 | + return rc; | |
| 937 | + return size; | |
| 938 | +} | |
| 939 | +static DEVICE_ATTR_RW(hw_error_scrub); | |
| 940 | + | |
| 889 | 941 | /* |
| 890 | 942 | * This shows the number of full Address Range Scrubs that have been |
| 891 | 943 | * completed since driver load time. Userspace can wait on this using |
| @@ -958,6 +1010,7 @@ static umode_t nfit_visible(struct kobject *kobj, struct attribute *a, int n) · 66 unmodified lines | ||
| 958 | 1010 | static struct attribute *acpi_nfit_attributes[] = { |
| 959 | 1011 | &dev_attr_revision.attr, |
| 960 | 1012 | &dev_attr_scrub.attr, |
| 1013 | + &dev_attr_hw_error_scrub.attr, | |
| 961 | 1014 | NULL, |
| 962 | 1015 | }; |
| 963 | 1016 | |
| @@ -1256,6 +1309,44 @@ static struct nvdimm *acpi_nfit_dimm_by_handle(struct acpi_nfit_desc *acpi_desc, · 292 unmodified lines | ||
| 1256 | 1309 | return NULL; |
| 1257 | 1310 | } |
| 1258 | 1311 | |
| 1312 | +void __acpi_nvdimm_notify(struct device *dev, u32 event) | |
| 1313 | +{ | |
| 1314 | + struct nfit_mem *nfit_mem; | |
| 1315 | + struct acpi_nfit_desc *acpi_desc; | |
| 1316 | + | |
| 1317 | + dev_dbg(dev->parent, "%s: %s: event: %d\n", dev_name(dev), __func__, | |
| 1318 | + event); | |
| 1319 | + | |
| 1320 | + if (event != NFIT_NOTIFY_DIMM_HEALTH) { | |
| 1321 | + dev_dbg(dev->parent, "%s: unknown event: %d\n", dev_name(dev), | |
| 1322 | + event); | |
| 1323 | + return; | |
| 1324 | + } | |
| 1325 | + | |
| 1326 | + acpi_desc = dev_get_drvdata(dev->parent); | |
| 1327 | + if (!acpi_desc) | |
| 1328 | + return; | |
| 1329 | + | |
| 1330 | + /* | |
| 1331 | + * If we successfully retrieved acpi_desc, then we know nfit_mem data | |
| 1332 | + * is still valid. | |
| 1333 | + */ | |
| 1334 | + nfit_mem = dev_get_drvdata(dev); | |
| 1335 | + if (nfit_mem && nfit_mem->flags_attr) | |
| 1336 | + sysfs_notify_dirent(nfit_mem->flags_attr); | |
| 1337 | +} | |
| 1338 | +EXPORT_SYMBOL_GPL(__acpi_nvdimm_notify); | |
| 1339 | + | |
| 1340 | +static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data) | |
| 1341 | +{ | |
| 1342 | + struct acpi_device *adev = data; | |
| 1343 | + struct device *dev = &adev->dev; | |
| 1344 | + | |
| 1345 | + device_lock(dev->parent); | |
| 1346 | + __acpi_nvdimm_notify(dev, event); | |
| 1347 | + device_unlock(dev->parent); | |
| 1348 | +} | |
| 1349 | + | |
| 1259 | 1350 | static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, |
| 1260 | 1351 | struct nfit_mem *nfit_mem, u32 device_handle) |
| 1261 | 1352 | { |
| @@ -1280,6 +1371,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, · 18 unmodified lines | ||
| 1280 | 1371 | return force_enable_dimms ? 0 : -ENODEV; |
| 1281 | 1372 | } |
| 1282 | 1373 | |
| 1374 | + if (ACPI_FAILURE(acpi_install_notify_handler(adev_dimm->handle, | |
| 1375 | + ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify, adev_dimm))) { | |
| 1376 | + dev_err(dev, "%s: notification registration failed\n", | |
| 1377 | + dev_name(&adev_dimm->dev)); | |
| 1378 | + return -ENXIO; | |
| 1379 | + } | |
| 1380 | + | |
| 1283 | 1381 | /* |
| 1284 | 1382 | * Until standardization materializes we need to consider 4 |
| 1285 | 1383 | * different command sets. Note, that checking for function0 (bit0) |
| @@ -1318,18 +1416,41 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, · 32 unmodified lines | ||
| 1318 | 1416 | return 0; |
| 1319 | 1417 | } |
| 1320 | 1418 | |
| 1419 | +static void shutdown_dimm_notify(void *data) | |
| 1420 | +{ | |
| 1421 | + struct acpi_nfit_desc *acpi_desc = data; | |
| 1422 | + struct nfit_mem *nfit_mem; | |
| 1423 | + | |
| 1424 | + mutex_lock(&acpi_desc->init_mutex); | |
| 1425 | + /* | |
| 1426 | + * Clear out the nfit_mem->flags_attr and shut down dimm event | |
| 1427 | + * notifications. | |
| 1428 | + */ | |
| 1429 | + list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { | |
| 1430 | + struct acpi_device *adev_dimm = nfit_mem->adev; | |
| 1431 | + | |
| 1432 | + if (nfit_mem->flags_attr) { | |
| 1433 | + sysfs_put(nfit_mem->flags_attr); | |
| 1434 | + nfit_mem->flags_attr = NULL; | |
| 1435 | + } | |
| 1436 | + if (adev_dimm) | |
| 1437 | + acpi_remove_notify_handler(adev_dimm->handle, | |
| 1438 | + ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify); | |
| 1439 | + } | |
| 1440 | + mutex_unlock(&acpi_desc->init_mutex); | |
| 1441 | +} | |
| 1442 | + | |
| 1321 | 1443 | static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) |
| 1322 | 1444 | { |
| 1323 | 1445 | struct nfit_mem *nfit_mem; |
| 1324 | − int dimm_count = 0; | |
| 1446 | + int dimm_count = 0, rc; | |
| 1447 | + struct nvdimm *nvdimm; | |
| 1325 | 1448 | |
| 1326 | 1449 | list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { |
| 1327 | 1450 | struct acpi_nfit_flush_address *flush; |
| 1328 | 1451 | unsigned long flags = 0, cmd_mask; |
| 1329 | − struct nvdimm *nvdimm; | |
| 1330 | 1452 | u32 device_handle; |
| 1331 | 1453 | u16 mem_flags; |
| 1332 | − int rc; | |
| 1333 | 1454 | |
| 1334 | 1455 | device_handle = __to_nfit_memdev(nfit_mem)->device_handle; |
| 1335 | 1456 | nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, device_handle); |
| @@ -1382,7 +1503,30 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) · 46 unmodified lines | ||
| 1382 | 1503 | |
| 1383 | 1504 | } |
| 1384 | 1505 | |
| 1385 | − return nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count); | |
| 1506 | + rc = nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count); | |
| 1507 | + if (rc) | |
| 1508 | + return rc; | |
| 1509 | + | |
| 1510 | + /* | |
| 1511 | + * Now that dimms are successfully registered, and async registration | |
| 1512 | + * is flushed, attempt to enable event notification. | |
| 1513 | + */ | |
| 1514 | + list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { | |
| 1515 | + struct kernfs_node *nfit_kernfs; | |
| 1516 | + | |
| 1517 | + nvdimm = nfit_mem->nvdimm; | |
| 1518 | + nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit"); | |
| 1519 | + if (nfit_kernfs) | |
| 1520 | + nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs, | |
| 1521 | + "flags"); | |
| 1522 | + sysfs_put(nfit_kernfs); | |
| 1523 | + if (!nfit_mem->flags_attr) | |
| 1524 | + dev_warn(acpi_desc->dev, "%s: notifications disabled\n", | |
| 1525 | + nvdimm_name(nvdimm)); | |
| 1526 | + } | |
| 1527 | + | |
| 1528 | + return devm_add_action_or_reset(acpi_desc->dev, shutdown_dimm_notify, | |
| 1529 | + acpi_desc); | |
| 1386 | 1530 | } |
| 1387 | 1531 | |
| 1388 | 1532 | static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc) |
| @@ -1491,9 +1635,9 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc, · 102 unmodified lines | ||
| 1491 | 1635 | if (!info) |
| 1492 | 1636 | return -ENOMEM; |
| 1493 | 1637 | for (i = 0; i < nr; i++) { |
| 1494 | − struct nd_mapping *nd_mapping = &ndr_desc->nd_mapping[i]; | |
| 1638 | + struct nd_mapping_desc *mapping = &ndr_desc->mapping[i]; | |
| 1495 | 1639 | struct nfit_set_info_map *map = &info->mapping[i]; |
| 1496 | − struct nvdimm *nvdimm = nd_mapping->nvdimm; | |
| 1640 | + struct nvdimm *nvdimm = mapping->nvdimm; | |
| 1497 | 1641 | struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); |
| 1498 | 1642 | struct acpi_nfit_memory_map *memdev = memdev_from_spa(acpi_desc, |
| 1499 | 1643 | spa->range_index, i); |
| @@ -1917,7 +2061,7 @@ static int acpi_nfit_insert_resource(struct acpi_nfit_desc *acpi_desc, · 417 unmodified lines | ||
| 1917 | 2061 | } |
| 1918 | 2062 | |
| 1919 | 2063 | static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, |
| 1920 | − struct nd_mapping *nd_mapping, struct nd_region_desc *ndr_desc, | |
| 2064 | + struct nd_mapping_desc *mapping, struct nd_region_desc *ndr_desc, | |
| 1921 | 2065 | struct acpi_nfit_memory_map *memdev, |
| 1922 | 2066 | struct nfit_spa *nfit_spa) |
| 1923 | 2067 | { |
| @@ -1934,12 +2078,12 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, · 10 unmodified lines | ||
| 1934 | 2078 | return -ENODEV; |
| 1935 | 2079 | } |
| 1936 | 2080 | |
| 1937 | − nd_mapping->nvdimm = nvdimm; | |
| 2081 | + mapping->nvdimm = nvdimm; | |
| 1938 | 2082 | switch (nfit_spa_type(spa)) { |
| 1939 | 2083 | case NFIT_SPA_PM: |
| 1940 | 2084 | case NFIT_SPA_VOLATILE: |
| 1941 | − nd_mapping->start = memdev->address; | |
| 1942 | − nd_mapping->size = memdev->region_size; | |
| 2085 | + mapping->start = memdev->address; | |
| 2086 | + mapping->size = memdev->region_size; | |
| 1943 | 2087 | break; |
| 1944 | 2088 | case NFIT_SPA_DCR: |
| 1945 | 2089 | nfit_mem = nvdimm_provider_data(nvdimm); |
| @@ -1947,13 +2091,13 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, · 1 unmodified line | ||
| 1947 | 2091 | dev_dbg(acpi_desc->dev, "spa%d %s missing bdw\n", |
| 1948 | 2092 | spa->range_index, nvdimm_name(nvdimm)); |
| 1949 | 2093 | } else { |
| 1950 | − nd_mapping->size = nfit_mem->bdw->capacity; | |
| 1951 | − nd_mapping->start = nfit_mem->bdw->start_address; | |
| 2094 | + mapping->size = nfit_mem->bdw->capacity; | |
| 2095 | + mapping->start = nfit_mem->bdw->start_address; | |
| 1952 | 2096 | ndr_desc->num_lanes = nfit_mem->bdw->windows; |
| 1953 | 2097 | blk_valid = 1; |
| 1954 | 2098 | } |
| 1955 | 2099 | |
| 1956 | − ndr_desc->nd_mapping = nd_mapping; | |
| 2100 | + ndr_desc->mapping = mapping; | |
| 1957 | 2101 | ndr_desc->num_mappings = blk_valid; |
| 1958 | 2102 | ndbr_desc = to_blk_region_desc(ndr_desc); |
| 1959 | 2103 | ndbr_desc->enable = acpi_nfit_blk_region_enable; |
| @@ -1979,7 +2123,7 @@ static bool nfit_spa_is_virtual(struct acpi_nfit_system_address *spa) · 19 unmodified lines | ||
| 1979 | 2123 | static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, |
| 1980 | 2124 | struct nfit_spa *nfit_spa) |
| 1981 | 2125 | { |
| 1982 | − static struct nd_mapping nd_mappings[ND_MAX_MAPPINGS]; | |
| 2126 | + static struct nd_mapping_desc mappings[ND_MAX_MAPPINGS]; | |
| 1983 | 2127 | struct acpi_nfit_system_address *spa = nfit_spa->spa; |
| 1984 | 2128 | struct nd_blk_region_desc ndbr_desc; |
| 1985 | 2129 | struct nd_region_desc *ndr_desc; |
| @@ -1998,7 +2142,7 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, · 12 unmodified lines | ||
| 1998 | 2142 | } |
| 1999 | 2143 | |
| 2000 | 2144 | memset(&res, 0, sizeof(res)); |
| 2001 | − memset(&nd_mappings, 0, sizeof(nd_mappings)); | |
| 2145 | + memset(&mappings, 0, sizeof(mappings)); | |
| 2002 | 2146 | memset(&ndbr_desc, 0, sizeof(ndbr_desc)); |
| 2003 | 2147 | res.start = spa->address; |
| 2004 | 2148 | res.end = res.start + spa->length - 1; |
| @@ -2014,7 +2158,7 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, · 9 unmodified lines | ||
| 2014 | 2158 | |
| 2015 | 2159 | list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { |
| 2016 | 2160 | struct acpi_nfit_memory_map *memdev = nfit_memdev->memdev; |
| 2017 | − struct nd_mapping *nd_mapping; | |
| 2161 | + struct nd_mapping_desc *mapping; | |
| 2018 | 2162 | |
| 2019 | 2163 | if (memdev->range_index != spa->range_index) |
| 2020 | 2164 | continue; |
| @@ -2023,14 +2167,14 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, · 2 unmodified lines | ||
| 2023 | 2167 | spa->range_index, ND_MAX_MAPPINGS); |
| 2024 | 2168 | return -ENXIO; |
| 2025 | 2169 | } |
| 2026 | − nd_mapping = &nd_mappings[count++]; | |
| 2027 | − rc = acpi_nfit_init_mapping(acpi_desc, nd_mapping, ndr_desc, | |
| 2170 | + mapping = &mappings[count++]; | |
| 2171 | + rc = acpi_nfit_init_mapping(acpi_desc, mapping, ndr_desc, | |
| 2028 | 2172 | memdev, nfit_spa); |
| 2029 | 2173 | if (rc) |
| 2030 | 2174 | goto out; |
| 2031 | 2175 | } |
| 2032 | 2176 | |
| 2033 | − ndr_desc->nd_mapping = nd_mappings; | |
| 2177 | + ndr_desc->mapping = mappings; | |
| 2034 | 2178 | ndr_desc->num_mappings = count; |
| 2035 | 2179 | rc = acpi_nfit_init_interleave_set(acpi_desc, ndr_desc, spa); |
| 2036 | 2180 | if (rc) |
| @@ -2678,29 +2822,30 @@ static int acpi_nfit_remove(struct acpi_device *adev) · 641 unmodified lines | ||
| 2678 | 2822 | return 0; |
| 2679 | 2823 | } |
| 2680 | 2824 | |
| 2681 | −static void acpi_nfit_notify(struct acpi_device *adev, u32 event) | |
| 2825 | +void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event) | |
| 2682 | 2826 | { |
| 2683 | − struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(&adev->dev); | |
| 2827 | + struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev); | |
| 2684 | 2828 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; |
| 2685 | − struct device *dev = &adev->dev; | |
| 2686 | 2829 | union acpi_object *obj; |
| 2687 | 2830 | acpi_status status; |
| 2688 | 2831 | int ret; |
| 2689 | 2832 | |
| 2690 | 2833 | dev_dbg(dev, "%s: event: %d\n", __func__, event); |
| 2691 | 2834 | |
| 2692 | − device_lock(dev); | |
| 2835 | + if (event != NFIT_NOTIFY_UPDATE) | |
| 2836 | + return; | |
| 2837 | + | |
| 2693 | 2838 | if (!dev->driver) { |
| 2694 | 2839 | /* dev->driver may be null if we're being removed */ |
| 2695 | 2840 | dev_dbg(dev, "%s: no driver found for dev\n", __func__); |
| 2696 | − goto out_unlock; | |
| 2841 | + return; | |
| 2697 | 2842 | } |
| 2698 | 2843 | |
| 2699 | 2844 | if (!acpi_desc) { |
| 2700 | 2845 | acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); |
| 2701 | 2846 | if (!acpi_desc) |
| 2702 | − goto out_unlock; | |
| 2703 | − acpi_nfit_desc_init(acpi_desc, &adev->dev); | |
| 2847 | + return; | |
| 2848 | + acpi_nfit_desc_init(acpi_desc, dev); | |
| 2704 | 2849 | } else { |
| 2705 | 2850 | /* |
| 2706 | 2851 | * Finish previous registration before considering new |
| @@ -2710,10 +2855,10 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event) · 3 unmodified lines | ||
| 2710 | 2855 | } |
| 2711 | 2856 | |
| 2712 | 2857 | /* Evaluate _FIT */ |
| 2713 | − status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf); | |
| 2858 | + status = acpi_evaluate_object(handle, "_FIT", NULL, &buf); | |
| 2714 | 2859 | if (ACPI_FAILURE(status)) { |
| 2715 | 2860 | dev_err(dev, "failed to evaluate _FIT\n"); |
| 2716 | − goto out_unlock; | |
| 2861 | + return; | |
| 2717 | 2862 | } |
| 2718 | 2863 | |
| 2719 | 2864 | obj = buf.pointer; |
| @@ -2725,9 +2870,14 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event) · 5 unmodified lines | ||
| 2725 | 2870 | } else |
| 2726 | 2871 | dev_err(dev, "Invalid _FIT\n"); |
| 2727 | 2872 | kfree(buf.pointer); |
| 2873 | +} | |
| 2874 | +EXPORT_SYMBOL_GPL(__acpi_nfit_notify); | |
| 2728 | 2875 | |
| 2729 | − out_unlock: | |
| 2730 | − device_unlock(dev); | |
| 2876 | +static void acpi_nfit_notify(struct acpi_device *adev, u32 event) | |
| 2877 | +{ | |
| 2878 | + device_lock(&adev->dev); | |
| 2879 | + __acpi_nfit_notify(&adev->dev, adev->handle, event); | |
| 2880 | + device_unlock(&adev->dev); | |
| 2731 | 2881 | } |
| 2732 | 2882 | |
| 2733 | 2883 | static const struct acpi_device_id acpi_nfit_ids[] = { |
| @@ -14,6 +14,7 @@ · 13 unmodified lines | ||
| 14 | 14 | */ |
| 15 | 15 | #include <linux/notifier.h> |
| 16 | 16 | #include <linux/acpi.h> |
| 17 | +#include <linux/nd.h> | |
| 17 | 18 | #include <asm/mce.h> |
| 18 | 19 | #include "nfit.h" |
| 19 | 20 | |
| @@ -62,12 +63,25 @@ static int nfit_handle_mce(struct notifier_block *nb, unsigned long val, · 42 unmodified lines | ||
| 62 | 63 | } |
| 63 | 64 | mutex_unlock(&acpi_desc->init_mutex); |
| 64 | 65 | |
| 65 | − /* | |
| 66 | − * We can ignore an -EBUSY here because if an ARS is already | |
| 67 | − * in progress, just let that be the last authoritative one | |
| 68 | − */ | |
| 69 | − if (found_match) | |
| 66 | + if (!found_match) | |
| 67 | + continue; | |
| 68 | + | |
| 69 | + /* If this fails due to an -ENOMEM, there is little we can do */ | |
| 70 | + nvdimm_bus_add_poison(acpi_desc->nvdimm_bus, | |
| 71 | + ALIGN(mce->addr, L1_CACHE_BYTES), | |
| 72 | + L1_CACHE_BYTES); | |
| 73 | + nvdimm_region_notify(nfit_spa->nd_region, | |
| 74 | + NVDIMM_REVALIDATE_POISON); | |
| 75 | + | |
| 76 | + if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) { | |
| 77 | + /* | |
| 78 | + * We can ignore an -EBUSY here because if an ARS is | |
| 79 | + * already in progress, just let that be the last | |
| 80 | + * authoritative one | |
| 81 | + */ | |
| 70 | 82 | acpi_nfit_ars_rescan(acpi_desc); |
| 83 | + } | |
| 84 | + break; | |
| 71 | 85 | } |
| 72 | 86 | |
| 73 | 87 | mutex_unlock(&acpi_desc_lock); |
| @@ -78,6 +78,14 @@ enum { · 77 unmodified lines | ||
| 78 | 78 | NFIT_ARS_TIMEOUT = 90, |
| 79 | 79 | }; |
| 80 | 80 | |
| 81 | +enum nfit_root_notifiers { | |
| 82 | + NFIT_NOTIFY_UPDATE = 0x80, | |
| 83 | +}; | |
| 84 | + | |
| 85 | +enum nfit_dimm_notifiers { | |
| 86 | + NFIT_NOTIFY_DIMM_HEALTH = 0x81, | |
| 87 | +}; | |
| 88 | + | |
| 81 | 89 | struct nfit_spa { |
| 82 | 90 | struct list_head list; |
| 83 | 91 | struct nd_region *nd_region; |
| @@ -124,6 +132,7 @@ struct nfit_mem { · 40 unmodified lines | ||
| 124 | 132 | struct acpi_nfit_system_address *spa_bdw; |
| 125 | 133 | struct acpi_nfit_interleave *idt_dcr; |
| 126 | 134 | struct acpi_nfit_interleave *idt_bdw; |
| 135 | + struct kernfs_node *flags_attr; | |
| 127 | 136 | struct nfit_flush *nfit_flush; |
| 128 | 137 | struct list_head list; |
| 129 | 138 | struct acpi_device *adev; |
| @@ -152,6 +161,7 @@ struct acpi_nfit_desc { · 22 unmodified lines | ||
| 152 | 161 | struct list_head list; |
| 153 | 162 | struct kernfs_node *scrub_count_state; |
| 154 | 163 | unsigned int scrub_count; |
| 164 | + unsigned int scrub_mode; | |
| 155 | 165 | unsigned int cancel:1; |
| 156 | 166 | unsigned long dimm_cmd_force_en; |
| 157 | 167 | unsigned long bus_cmd_force_en; |
| @@ -159,6 +169,11 @@ struct acpi_nfit_desc { · 1 unmodified line | ||
| 159 | 169 | void *iobuf, u64 len, int rw); |
| 160 | 170 | }; |
| 161 | 171 | |
| 172 | +enum scrub_mode { | |
| 173 | + HW_ERROR_SCRUB_OFF, | |
| 174 | + HW_ERROR_SCRUB_ON, | |
| 175 | +}; | |
| 176 | + | |
| 162 | 177 | enum nd_blk_mmio_selector { |
| 163 | 178 | BDW, |
| 164 | 179 | DCR, |
| @@ -223,5 +238,7 @@ static inline struct acpi_nfit_desc *to_acpi_desc( · 58 unmodified lines | ||
| 223 | 238 | |
| 224 | 239 | const u8 *to_nfit_uuid(enum nfit_uuids id); |
| 225 | 240 | int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *nfit, acpi_size sz); |
| 241 | +void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event); | |
| 242 | +void __acpi_nvdimm_notify(struct device *dev, u32 event); | |
| 226 | 243 | void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev); |
| 227 | 244 | #endif /* __NFIT_H__ */ |
| @@ -217,6 +217,8 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, · 216 unmodified lines | ||
| 217 | 217 | return rc; |
| 218 | 218 | if (cmd_rc < 0) |
| 219 | 219 | return cmd_rc; |
| 220 | + | |
| 221 | + nvdimm_clear_from_poison_list(nvdimm_bus, phys, len); | |
| 220 | 222 | return clear_err.cleared; |
| 221 | 223 | } |
| 222 | 224 | EXPORT_SYMBOL_GPL(nvdimm_clear_poison); |
| @@ -547,11 +547,12 @@ void nvdimm_badblocks_populate(struct nd_region *nd_region, · 546 unmodified lines | ||
| 547 | 547 | } |
| 548 | 548 | EXPORT_SYMBOL_GPL(nvdimm_badblocks_populate); |
| 549 | 549 | |
| 550 | −static int add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) | |
| 550 | +static int add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length, | |
| 551 | + gfp_t flags) | |
| 551 | 552 | { |
| 552 | 553 | struct nd_poison *pl; |
| 553 | 554 | |
| 554 | − pl = kzalloc(sizeof(*pl), GFP_KERNEL); | |
| 555 | + pl = kzalloc(sizeof(*pl), flags); | |
| 555 | 556 | if (!pl) |
| 556 | 557 | return -ENOMEM; |
| 557 | 558 | |
| @@ -567,7 +568,7 @@ static int bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) · 9 unmodified lines | ||
| 567 | 568 | struct nd_poison *pl; |
| 568 | 569 | |
| 569 | 570 | if (list_empty(&nvdimm_bus->poison_list)) |
| 570 | − return add_poison(nvdimm_bus, addr, length); | |
| 571 | + return add_poison(nvdimm_bus, addr, length, GFP_KERNEL); | |
| 571 | 572 | |
| 572 | 573 | /* |
| 573 | 574 | * There is a chance this is a duplicate, check for those first. |
| @@ -587,7 +588,7 @@ static int bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) · 13 unmodified lines | ||
| 587 | 588 | * as any overlapping ranges will get resolved when the list is consumed |
| 588 | 589 | * and converted to badblocks |
| 589 | 590 | */ |
| 590 | − return add_poison(nvdimm_bus, addr, length); | |
| 591 | + return add_poison(nvdimm_bus, addr, length, GFP_KERNEL); | |
| 591 | 592 | } |
| 592 | 593 | |
| 593 | 594 | int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) |
| @@ -602,6 +603,70 @@ int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) · 8 unmodified lines | ||
| 602 | 603 | } |
| 603 | 604 | EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison); |
| 604 | 605 | |
| 606 | +void nvdimm_clear_from_poison_list(struct nvdimm_bus *nvdimm_bus, | |
| 607 | + phys_addr_t start, unsigned int len) | |
| 608 | +{ | |
| 609 | + struct list_head *poison_list = &nvdimm_bus->poison_list; | |
| 610 | + u64 clr_end = start + len - 1; | |
| 611 | + struct nd_poison *pl, *next; | |
| 612 | + | |
| 613 | + nvdimm_bus_lock(&nvdimm_bus->dev); | |
| 614 | + WARN_ON_ONCE(list_empty(poison_list)); | |
| 615 | + | |
| 616 | + /* | |
| 617 | + * [start, clr_end] is the poison interval being cleared. | |
| 618 | + * [pl->start, pl_end] is the poison_list entry we're comparing | |
| 619 | + * the above interval against. The poison list entry may need | |
| 620 | + * to be modified (update either start or length), deleted, or | |
| 621 | + * split into two based on the overlap characteristics | |
| 622 | + */ | |
| 623 | + | |
| 624 | + list_for_each_entry_safe(pl, next, poison_list, list) { | |
| 625 | + u64 pl_end = pl->start + pl->length - 1; | |
| 626 | + | |
| 627 | + /* Skip intervals with no intersection */ | |
| 628 | + if (pl_end < start) | |
| 629 | + continue; | |
| 630 | + if (pl->start > clr_end) | |
| 631 | + continue; | |
| 632 | + /* Delete completely overlapped poison entries */ | |
| 633 | + if ((pl->start >= start) && (pl_end <= clr_end)) { | |
| 634 | + list_del(&pl->list); | |
| 635 | + kfree(pl); | |
| 636 | + continue; | |
| 637 | + } | |
| 638 | + /* Adjust start point of partially cleared entries */ | |
| 639 | + if ((start <= pl->start) && (clr_end > pl->start)) { | |
| 640 | + pl->length -= clr_end - pl->start + 1; | |
| 641 | + pl->start = clr_end + 1; | |
| 642 | + continue; | |
| 643 | + } | |
| 644 | + /* Adjust pl->length for partial clearing at the tail end */ | |
| 645 | + if ((pl->start < start) && (pl_end <= clr_end)) { | |
| 646 | + /* pl->start remains the same */ | |
| 647 | + pl->length = start - pl->start; | |
| 648 | + continue; | |
| 649 | + } | |
| 650 | + /* | |
| 651 | + * If clearing in the middle of an entry, we split it into | |
| 652 | + * two by modifying the current entry to represent one half of | |
| 653 | + * the split, and adding a new entry for the second half. | |
| 654 | + */ | |
| 655 | + if ((pl->start < start) && (pl_end > clr_end)) { | |
| 656 | + u64 new_start = clr_end + 1; | |
| 657 | + u64 new_len = pl_end - new_start + 1; | |
| 658 | + | |
| 659 | + /* Add new entry covering the right half */ | |
| 660 | + add_poison(nvdimm_bus, new_start, new_len, GFP_NOIO); | |
| 661 | + /* Adjust this entry to cover the left half */ | |
| 662 | + pl->length = start - pl->start; | |
| 663 | + continue; | |
| 664 | + } | |
| 665 | + } | |
| 666 | + nvdimm_bus_unlock(&nvdimm_bus->dev); | |
| 667 | +} | |
| 668 | +EXPORT_SYMBOL_GPL(nvdimm_clear_from_poison_list); | |
| 669 | + | |
| 605 | 670 | #ifdef CONFIG_BLK_DEV_INTEGRITY |
| 606 | 671 | int nd_integrity_init(struct gendisk *disk, unsigned long meta_size) |
| 607 | 672 | { |
| @@ -26,6 +26,14 @@ static int nvdimm_probe(struct device *dev) · 25 unmodified lines | ||
| 26 | 26 | struct nvdimm_drvdata *ndd; |
| 27 | 27 | int rc; |
| 28 | 28 | |
| 29 | + rc = nvdimm_check_config_data(dev); | |
| 30 | + if (rc) { | |
| 31 | + /* not required for non-aliased nvdimm, ex. NVDIMM-N */ | |
| 32 | + if (rc == -ENOTTY) | |
| 33 | + rc = 0; | |
| 34 | + return rc; | |
| 35 | + } | |
| 36 | + | |
| 29 | 37 | ndd = kzalloc(sizeof(*ndd), GFP_KERNEL); |
| 30 | 38 | if (!ndd) |
| 31 | 39 | return -ENOMEM; |
| @@ -72,6 +80,9 @@ static int nvdimm_remove(struct device *dev) · 40 unmodified lines | ||
| 72 | 80 | { |
| 73 | 81 | struct nvdimm_drvdata *ndd = dev_get_drvdata(dev); |
| 74 | 82 | |
| 83 | + if (!ndd) | |
| 84 | + return 0; | |
| 85 | + | |
| 75 | 86 | nvdimm_bus_lock(dev); |
| 76 | 87 | dev_set_drvdata(dev, NULL); |
| 77 | 88 | nvdimm_bus_unlock(dev); |
| @@ -28,28 +28,30 @@ static DEFINE_IDA(dimm_ida); · 27 unmodified lines | ||
| 28 | 28 | * Retrieve bus and dimm handle and return if this bus supports |
| 29 | 29 | * get_config_data commands |
| 30 | 30 | */ |
| 31 | −static int __validate_dimm(struct nvdimm_drvdata *ndd) | |
| 31 | +int nvdimm_check_config_data(struct device *dev) | |
| 32 | 32 | { |
| 33 | − struct nvdimm *nvdimm; | |
| 34 | − | |
| 35 | − if (!ndd) | |
| 36 | − return -EINVAL; | |
| 37 | − | |
| 38 | − nvdimm = to_nvdimm(ndd->dev); | |
| 33 | + struct nvdimm *nvdimm = to_nvdimm(dev); | |
| 39 | 34 | |
| 40 | − if (!nvdimm->cmd_mask) | |
| 41 | − return -ENXIO; | |
| 42 | − if (!test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) | |
| 43 | − return -ENXIO; | |
| 35 | + if (!nvdimm->cmd_mask || | |
| 36 | + !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) { | |
| 37 | + if (nvdimm->flags & NDD_ALIASING) | |
| 38 | + return -ENXIO; | |
| 39 | + else | |
| 40 | + return -ENOTTY; | |
| 41 | + } | |
| 44 | 42 | |
| 45 | 43 | return 0; |
| 46 | 44 | } |
| 47 | 45 | |
| 48 | 46 | static int validate_dimm(struct nvdimm_drvdata *ndd) |
| 49 | 47 | { |
| 50 | − int rc = __validate_dimm(ndd); | |
| 48 | + int rc; | |
| 51 | 49 | |
| 52 | − if (rc && ndd) | |
| 50 | + if (!ndd) | |
| 51 | + return -EINVAL; | |
| 52 | + | |
| 53 | + rc = nvdimm_check_config_data(ndd->dev); | |
| 54 | + if (rc) | |
| 53 | 55 | dev_dbg(ndd->dev, "%pf: %s error: %d\n", |
| 54 | 56 | __builtin_return_address(0), __func__, rc); |
| 55 | 57 | return rc; |
| @@ -263,6 +265,12 @@ const char *nvdimm_name(struct nvdimm *nvdimm) · 207 unmodified lines | ||
| 263 | 265 | } |
| 264 | 266 | EXPORT_SYMBOL_GPL(nvdimm_name); |
| 265 | 267 | |
| 268 | +struct kobject *nvdimm_kobj(struct nvdimm *nvdimm) | |
| 269 | +{ | |
| 270 | + return &nvdimm->dev.kobj; | |
| 271 | +} | |
| 272 | +EXPORT_SYMBOL_GPL(nvdimm_kobj); | |
| 273 | + | |
| 266 | 274 | unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm) |
| 267 | 275 | { |
| 268 | 276 | return nvdimm->cmd_mask; |
| @@ -378,40 +386,166 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, · 109 unmodified lines | ||
| 378 | 386 | } |
| 379 | 387 | EXPORT_SYMBOL_GPL(nvdimm_create); |
| 380 | 388 | |
| 389 | +int alias_dpa_busy(struct device *dev, void *data) | |
| 390 | +{ | |
| 391 | + resource_size_t map_end, blk_start, new, busy; | |
| 392 | + struct blk_alloc_info *info = data; | |
| 393 | + struct nd_mapping *nd_mapping; | |
| 394 | + struct nd_region *nd_region; | |
| 395 | + struct nvdimm_drvdata *ndd; | |
| 396 | + struct resource *res; | |
| 397 | + int i; | |
| 398 | + | |
| 399 | + if (!is_nd_pmem(dev)) | |
| 400 | + return 0; | |
| 401 | + | |
| 402 | + nd_region = to_nd_region(dev); | |
| 403 | + for (i = 0; i < nd_region->ndr_mappings; i++) { | |
| 404 | + nd_mapping = &nd_region->mapping[i]; | |
| 405 | + if (nd_mapping->nvdimm == info->nd_mapping->nvdimm) | |
| 406 | + break; | |
| 407 | + } | |
| 408 | + | |
| 409 | + if (i >= nd_region->ndr_mappings) | |
| 410 | + return 0; | |
| 411 | + | |
| 412 | + ndd = to_ndd(nd_mapping); | |
| 413 | + map_end = nd_mapping->start + nd_mapping->size - 1; | |
| 414 | + blk_start = nd_mapping->start; | |
| 415 | + | |
| 416 | + /* | |
| 417 | + * In the allocation case ->res is set to free space that we are | |
| 418 | + * looking to validate against PMEM aliasing collision rules | |
| 419 | + * (i.e. BLK is allocated after all aliased PMEM). | |
| 420 | + */ | |
| 421 | + if (info->res) { | |
| 422 | + if (info->res->start >= nd_mapping->start | |
| 423 | + && info->res->start < map_end) | |
| 424 | + /* pass */; | |
| 425 | + else | |
| 426 | + return 0; | |
| 427 | + } | |
| 428 | + | |
| 429 | + retry: | |
| 430 | + /* | |
| 431 | + * Find the free dpa from the end of the last pmem allocation to | |
| 432 | + * the end of the interleave-set mapping that is not already | |
| 433 | + * covered by a blk allocation. | |
| 434 | + */ | |
| 435 | + busy = 0; | |
| 436 | + for_each_dpa_resource(ndd, res) { | |
| 437 | + if ((res->start >= blk_start && res->start < map_end) | |
| 438 | + || (res->end >= blk_start | |
| 439 | + && res->end <= map_end)) { | |
| 440 | + if (strncmp(res->name, "pmem", 4) == 0) { | |
| 441 | + new = max(blk_start, min(map_end + 1, | |
| 442 | + res->end + 1)); | |
| 443 | + if (new != blk_start) { | |
| 444 | + blk_start = new; | |
| 445 | + goto retry; | |
| 446 | + } | |
| 447 | + } else | |
| 448 | + busy += min(map_end, res->end) | |
| 449 | + - max(nd_mapping->start, res->start) + 1; | |
| 450 | + } else if (nd_mapping->start > res->start | |
| 451 | + && map_end < res->end) { | |
| 452 | + /* total eclipse of the PMEM region mapping */ | |
| 453 | + busy += nd_mapping->size; | |
| 454 | + break; | |
| 455 | + } | |
| 456 | + } | |
| 457 | + | |
| 458 | + /* update the free space range with the probed blk_start */ | |
| 459 | + if (info->res && blk_start > info->res->start) { | |
| 460 | + info->res->start = max(info->res->start, blk_start); | |
| 461 | + if (info->res->start > info->res->end) | |
| 462 | + info->res->end = info->res->start - 1; | |
| 463 | + return 1; | |
| 464 | + } | |
| 465 | + | |
| 466 | + info->available -= blk_start - nd_mapping->start + busy; | |
| 467 | + | |
| 468 | + return 0; | |
| 469 | +} | |
| 470 | + | |
| 471 | +static int blk_dpa_busy(struct device *dev, void *data) | |
| 472 | +{ | |
| 473 | + struct blk_alloc_info *info = data; | |
| 474 | + struct nd_mapping *nd_mapping; | |
| 475 | + struct nd_region *nd_region; | |
| 476 | + resource_size_t map_end; | |
| 477 | + int i; | |
| 478 | + | |
| 479 | + if (!is_nd_pmem(dev)) | |
| 480 | + return 0; | |
| 481 | + | |
| 482 | + nd_region = to_nd_region(dev); | |
| 483 | + for (i = 0; i < nd_region->ndr_mappings; i++) { | |
| 484 | + nd_mapping = &nd_region->mapping[i]; | |
| 485 | + if (nd_mapping->nvdimm == info->nd_mapping->nvdimm) | |
| 486 | + break; | |
| 487 | + } | |
| 488 | + | |
| 489 | + if (i >= nd_region->ndr_mappings) | |
| 490 | + return 0; | |
| 491 | + | |
| 492 | + map_end = nd_mapping->start + nd_mapping->size - 1; | |
| 493 | + if (info->res->start >= nd_mapping->start | |
| 494 | + && info->res->start < map_end) { | |
| 495 | + if (info->res->end <= map_end) { | |
| 496 | + info->busy = 0; | |
| 497 | + return 1; | |
| 498 | + } else { | |
| 499 | + info->busy -= info->res->end - map_end; | |
| 500 | + return 0; | |
| 501 | + } | |
| 502 | + } else if (info->res->end >= nd_mapping->start | |
| 503 | + && info->res->end <= map_end) { | |
| 504 | + info->busy -= nd_mapping->start - info->res->start; | |
| 505 | + return 0; | |
| 506 | + } else { | |
| 507 | + info->busy -= nd_mapping->size; | |
| 508 | + return 0; | |
| 509 | + } | |
| 510 | +} | |
| 511 | + | |
| 381 | 512 | /** |
| 382 | 513 | * nd_blk_available_dpa - account the unused dpa of BLK region |
| 383 | 514 | * @nd_mapping: container of dpa-resource-root + labels |
| 384 | 515 | * |
| 385 | − * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges. | |
| 516 | + * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges, but | |
| 517 | + * we arrange for them to never start at an lower dpa than the last | |
| 518 | + * PMEM allocation in an aliased region. | |
| 386 | 519 | */ |
| 387 | −resource_size_t nd_blk_available_dpa(struct nd_mapping *nd_mapping) | |
| 520 | +resource_size_t nd_blk_available_dpa(struct nd_region *nd_region) | |
| 388 | 521 | { |
| 522 | + struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); | |
| 523 | + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; | |
| 389 | 524 | struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); |
| 390 | − resource_size_t map_end, busy = 0, available; | |
| 525 | + struct blk_alloc_info info = { | |
| 526 | + .nd_mapping = nd_mapping, | |
| 527 | + .available = nd_mapping->size, | |
| 528 | + .res = NULL, | |
| 529 | + }; | |
| 391 | 530 | struct resource *res; |
| 392 | 531 | |
| 393 | 532 | if (!ndd) |
| 394 | 533 | return 0; |
| 395 | 534 | |
| 396 | − map_end = nd_mapping->start + nd_mapping->size - 1; | |
| 397 | − for_each_dpa_resource(ndd, res) | |
| 398 | − if (res->start >= nd_mapping->start && res->start < map_end) { | |
| 399 | − resource_size_t end = min(map_end, res->end); | |
| 535 | + device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy); | |
| 400 | 536 | |
| 401 | − busy += end - res->start + 1; | |
| 402 | − } else if (res->end >= nd_mapping->start | |
| 403 | − && res->end <= map_end) { | |
| 404 | − busy += res->end - nd_mapping->start; | |
| 405 | − } else if (nd_mapping->start > res->start | |
| 406 | − && nd_mapping->start < res->end) { | |
| 407 | − /* total eclipse of the BLK region mapping */ | |
| 408 | − busy += nd_mapping->size; | |
| 409 | − } | |
| 537 | + /* now account for busy blk allocations in unaliased dpa */ | |
| 538 | + for_each_dpa_resource(ndd, res) { | |
| 539 | + if (strncmp(res->name, "blk", 3) != 0) | |
| 540 | + continue; | |
| 410 | 541 | |
| 411 | − available = map_end - nd_mapping->start + 1; | |
| 412 | − if (busy < available) | |
| 413 | − return available - busy; | |
| 414 | − return 0; | |
| 542 | + info.res = res; | |
| 543 | + info.busy = resource_size(res); | |
| 544 | + device_for_each_child(&nvdimm_bus->dev, &info, blk_dpa_busy); | |
| 545 | + info.available -= info.busy; | |
| 546 | + } | |
| 547 | + | |
| 548 | + return info.available; | |
| 415 | 549 | } |
| 416 | 550 | |
| 417 | 551 | /** |
| @@ -443,21 +577,16 @@ resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, · 25 unmodified lines | ||
| 443 | 577 | map_start = nd_mapping->start; |
| 444 | 578 | map_end = map_start + nd_mapping->size - 1; |
| 445 | 579 | blk_start = max(map_start, map_end + 1 - *overlap); |
| 446 | − for_each_dpa_resource(ndd, res) | |
| 580 | + for_each_dpa_resource(ndd, res) { | |
| 447 | 581 | if (res->start >= map_start && res->start < map_end) { |
| 448 | 582 | if (strncmp(res->name, "blk", 3) == 0) |
| 449 | − blk_start = min(blk_start, res->start); | |
| 450 | − else if (res->start != map_start) { | |
| 583 | + blk_start = min(blk_start, | |
| 584 | + max(map_start, res->start)); | |
| 585 | + else if (res->end > map_end) { | |
| 451 | 586 | reason = "misaligned to iset"; |
| 452 | 587 | goto err; |
| 453 | − } else { | |
| 454 | − if (busy) { | |
| 455 | − reason = "duplicate overlapping PMEM reservations?"; | |
| 456 | − goto err; | |
| 457 | − } | |
| 588 | + } else | |
| 458 | 589 | busy += resource_size(res); |
| 459 | − continue; | |
| 460 | − } | |
| 461 | 590 | } else if (res->end >= map_start && res->end <= map_end) { |
| 462 | 591 | if (strncmp(res->name, "blk", 3) == 0) { |
| 463 | 592 | /* |
| @@ -466,15 +595,14 @@ resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, · 2 unmodified lines | ||
| 466 | 595 | * be used for BLK. |
| 467 | 596 | */ |
| 468 | 597 | blk_start = map_start; |
| 469 | − } else { | |
| 470 | − reason = "misaligned to iset"; | |
| 471 | − goto err; | |
| 472 | − } | |
| 598 | + } else | |
| 599 | + busy += resource_size(res); | |
| 473 | 600 | } else if (map_start > res->start && map_start < res->end) { |
| 474 | 601 | /* total eclipse of the mapping */ |
| 475 | 602 | busy += nd_mapping->size; |
| 476 | 603 | blk_start = map_start; |
| 477 | 604 | } |
| 605 | + } | |
| 478 | 606 | |
| 479 | 607 | *overlap = map_end + 1 - blk_start; |
| 480 | 608 | available = blk_start - map_start; |
| @@ -483,10 +611,6 @@ resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, · 2 unmodified lines | ||
| 483 | 611 | return 0; |
| 484 | 612 | |
| 485 | 613 | err: |
| 486 | − /* | |
| 487 | − * Something is wrong, PMEM must align with the start of the | |
| 488 | − * interleave set, and there can only be one allocation per set. | |
| 489 | − */ | |
| 490 | 614 | nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason); |
| 491 | 615 | return 0; |
| 492 | 616 | } |
| @@ -494,11 +494,13 @@ static int __pmem_label_update(struct nd_region *nd_region, · 493 unmodified lines | ||
| 494 | 494 | struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm, |
| 495 | 495 | int pos) |
| 496 | 496 | { |
| 497 | − u64 cookie = nd_region_interleave_set_cookie(nd_region), rawsize; | |
| 497 | + u64 cookie = nd_region_interleave_set_cookie(nd_region); | |
| 498 | 498 | struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); |
| 499 | − struct nd_namespace_label *victim_label; | |
| 499 | + struct nd_label_ent *label_ent, *victim = NULL; | |
| 500 | 500 | struct nd_namespace_label *nd_label; |
| 501 | 501 | struct nd_namespace_index *nsindex; |
| 502 | + struct nd_label_id label_id; | |
| 503 | + struct resource *res; | |
| 502 | 504 | unsigned long *free; |
| 503 | 505 | u32 nslot, slot; |
| 504 | 506 | size_t offset; |
| @@ -507,6 +509,16 @@ static int __pmem_label_update(struct nd_region *nd_region, · 2 unmodified lines | ||
| 507 | 509 | if (!preamble_next(ndd, &nsindex, &free, &nslot)) |
| 508 | 510 | return -ENXIO; |
| 509 | 511 | |
| 512 | + nd_label_gen_id(&label_id, nspm->uuid, 0); | |
| 513 | + for_each_dpa_resource(ndd, res) | |
| 514 | + if (strcmp(res->name, label_id.id) == 0) | |
| 515 | + break; | |
| 516 | + | |
| 517 | + if (!res) { | |
| 518 | + WARN_ON_ONCE(1); | |
| 519 | + return -ENXIO; | |
| 520 | + } | |
| 521 | + | |
| 510 | 522 | /* allocate and write the label to the staging (next) index */ |
| 511 | 523 | slot = nd_label_alloc_slot(ndd); |
| 512 | 524 | if (slot == UINT_MAX) |
| @@ -522,11 +534,10 @@ static int __pmem_label_update(struct nd_region *nd_region, · 9 unmodified lines | ||
| 522 | 534 | nd_label->nlabel = __cpu_to_le16(nd_region->ndr_mappings); |
| 523 | 535 | nd_label->position = __cpu_to_le16(pos); |
| 524 | 536 | nd_label->isetcookie = __cpu_to_le64(cookie); |
| 525 | − rawsize = div_u64(resource_size(&nspm->nsio.res), | |
| 526 | − nd_region->ndr_mappings); | |
| 527 | − nd_label->rawsize = __cpu_to_le64(rawsize); | |
| 528 | − nd_label->dpa = __cpu_to_le64(nd_mapping->start); | |
| 537 | + nd_label->rawsize = __cpu_to_le64(resource_size(res)); | |
| 538 | + nd_label->dpa = __cpu_to_le64(res->start); | |
| 529 | 539 | nd_label->slot = __cpu_to_le32(slot); |
| 540 | + nd_dbg_dpa(nd_region, ndd, res, "%s\n", __func__); | |
| 530 | 541 | |
| 531 | 542 | /* update label */ |
| 532 | 543 | offset = nd_label_offset(ndd, nd_label); |
| @@ -536,38 +547,43 @@ static int __pmem_label_update(struct nd_region *nd_region, · 3 unmodified lines | ||
| 536 | 547 | return rc; |
| 537 | 548 | |
| 538 | 549 | /* Garbage collect the previous label */ |
| 539 | − victim_label = nd_mapping->labels[0]; | |
| 540 | − if (victim_label) { | |
| 541 | − slot = to_slot(ndd, victim_label); | |
| 542 | − nd_label_free_slot(ndd, slot); | |
| 550 | + mutex_lock(&nd_mapping->lock); | |
| 551 | + list_for_each_entry(label_ent, &nd_mapping->labels, list) { | |
| 552 | + if (!label_ent->label) | |
| 553 | + continue; | |
| 554 | + if (memcmp(nspm->uuid, label_ent->label->uuid, | |
| 555 | + NSLABEL_UUID_LEN) != 0) | |
| 556 | + continue; | |
| 557 | + victim = label_ent; | |
| 558 | + list_move_tail(&victim->list, &nd_mapping->labels); | |
| 559 | + break; | |
| 560 | + } | |
| 561 | + if (victim) { | |
| 543 | 562 | dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot); |
| 563 | + slot = to_slot(ndd, victim->label); | |
| 564 | + nd_label_free_slot(ndd, slot); | |
| 565 | + victim->label = NULL; | |
| 544 | 566 | } |
| 545 | 567 | |
| 546 | 568 | /* update index */ |
| 547 | 569 | rc = nd_label_write_index(ndd, ndd->ns_next, |
| 548 | 570 | nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); |
| 549 | − if (rc < 0) | |
| 550 | − return rc; | |
| 551 | − | |
| 552 | − nd_mapping->labels[0] = nd_label; | |
| 553 | − | |
| 554 | − return 0; | |
| 555 | −} | |
| 556 | − | |
| 557 | −static void del_label(struct nd_mapping *nd_mapping, int l) | |
| 558 | −{ | |
| 559 | − struct nd_namespace_label *next_label, *nd_label; | |
| 560 | − struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); | |
| 561 | − unsigned int slot; | |
| 562 | − int j; | |
| 563 | − | |
| 564 | − nd_label = nd_mapping->labels[l]; | |
| 565 | − slot = to_slot(ndd, nd_label); | |
| 566 | − dev_vdbg(ndd->dev, "%s: clear: %d\n", __func__, slot); | |
| 571 | + if (rc == 0) { | |
| 572 | + list_for_each_entry(label_ent, &nd_mapping->labels, list) | |
| 573 | + if (!label_ent->label) { | |
| 574 | + label_ent->label = nd_label; | |
| 575 | + nd_label = NULL; | |
| 576 | + break; | |
| 577 | + } | |
| 578 | + dev_WARN_ONCE(&nspm->nsio.common.dev, nd_label, | |
| 579 | + "failed to track label: %d\n", | |
| 580 | + to_slot(ndd, nd_label)); | |
| 581 | + if (nd_label) | |
| 582 | + rc = -ENXIO; | |
| 583 | + } | |
| 584 | + mutex_unlock(&nd_mapping->lock); | |
| 567 | 585 | |
| 568 | − for (j = l; (next_label = nd_mapping->labels[j + 1]); j++) | |
| 569 | − nd_mapping->labels[j] = next_label; | |
| 570 | − nd_mapping->labels[j] = NULL; | |
| 586 | + return rc; | |
| 571 | 587 | } |
| 572 | 588 | |
| 573 | 589 | static bool is_old_resource(struct resource *res, struct resource **list, int n) |
| @@ -607,14 +623,16 @@ static int __blk_label_update(struct nd_region *nd_region, · 33 unmodified lines | ||
| 607 | 623 | struct nd_mapping *nd_mapping, struct nd_namespace_blk *nsblk, |
| 608 | 624 | int num_labels) |
| 609 | 625 | { |
| 610 | − int i, l, alloc, victims, nfree, old_num_resources, nlabel, rc = -ENXIO; | |
| 626 | + int i, alloc, victims, nfree, old_num_resources, nlabel, rc = -ENXIO; | |
| 611 | 627 | struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); |
| 612 | 628 | struct nd_namespace_label *nd_label; |
| 629 | + struct nd_label_ent *label_ent, *e; | |
| 613 | 630 | struct nd_namespace_index *nsindex; |
| 614 | 631 | unsigned long *free, *victim_map = NULL; |
| 615 | 632 | struct resource *res, **old_res_list; |
| 616 | 633 | struct nd_label_id label_id; |
| 617 | 634 | u8 uuid[NSLABEL_UUID_LEN]; |
| 635 | + LIST_HEAD(list); | |
| 618 | 636 | u32 nslot, slot; |
| 619 | 637 | |
| 620 | 638 | if (!preamble_next(ndd, &nsindex, &free, &nslot)) |
| @@ -736,15 +754,22 @@ static int __blk_label_update(struct nd_region *nd_region, · 115 unmodified lines | ||
| 736 | 754 | * entries in nd_mapping->labels |
| 737 | 755 | */ |
| 738 | 756 | nlabel = 0; |
| 739 | − for_each_label(l, nd_label, nd_mapping->labels) { | |
| 757 | + mutex_lock(&nd_mapping->lock); | |
| 758 | + list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { | |
| 759 | + nd_label = label_ent->label; | |
| 760 | + if (!nd_label) | |
| 761 | + continue; | |
| 740 | 762 | nlabel++; |
| 741 | 763 | memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); |
| 742 | 764 | if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) |
| 743 | 765 | continue; |
| 744 | 766 | nlabel--; |
| 745 | − del_label(nd_mapping, l); | |
| 746 | − l--; /* retry with the new label at this index */ | |
| 767 | + list_move(&label_ent->list, &list); | |
| 768 | + label_ent->label = NULL; | |
| 747 | 769 | } |
| 770 | + list_splice_tail_init(&list, &nd_mapping->labels); | |
| 771 | + mutex_unlock(&nd_mapping->lock); | |
| 772 | + | |
| 748 | 773 | if (nlabel + nsblk->num_resources > num_labels) { |
| 749 | 774 | /* |
| 750 | 775 | * Bug, we can't end up with more resources than |
| @@ -755,6 +780,15 @@ static int __blk_label_update(struct nd_region *nd_region, · 4 unmodified lines | ||
| 755 | 780 | goto out; |
| 756 | 781 | } |
| 757 | 782 | |
| 783 | + mutex_lock(&nd_mapping->lock); | |
| 784 | + label_ent = list_first_entry_or_null(&nd_mapping->labels, | |
| 785 | + typeof(*label_ent), list); | |
| 786 | + if (!label_ent) { | |
| 787 | + WARN_ON(1); | |
| 788 | + mutex_unlock(&nd_mapping->lock); | |
| 789 | + rc = -ENXIO; | |
| 790 | + goto out; | |
| 791 | + } | |
| 758 | 792 | for_each_clear_bit_le(slot, free, nslot) { |
| 759 | 793 | nd_label = nd_label_base(ndd) + slot; |
| 760 | 794 | memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); |
| @@ -762,11 +796,19 @@ static int __blk_label_update(struct nd_region *nd_region, · 1 unmodified line | ||
| 762 | 796 | continue; |
| 763 | 797 | res = to_resource(ndd, nd_label); |
| 764 | 798 | res->flags &= ~DPA_RESOURCE_ADJUSTED; |
| 765 | − dev_vdbg(&nsblk->common.dev, "assign label[%d] slot: %d\n", | |
| 766 | − l, slot); | |
| 767 | − nd_mapping->labels[l++] = nd_label; | |
| 799 | + dev_vdbg(&nsblk->common.dev, "assign label slot: %d\n", slot); | |
| 800 | + list_for_each_entry_from(label_ent, &nd_mapping->labels, list) { | |
| 801 | + if (label_ent->label) | |
| 802 | + continue; | |
| 803 | + label_ent->label = nd_label; | |
| 804 | + nd_label = NULL; | |
| 805 | + break; | |
| 806 | + } | |
| 807 | + if (nd_label) | |
| 808 | + dev_WARN(&nsblk->common.dev, | |
| 809 | + "failed to track label slot%d\n", slot); | |
| 768 | 810 | } |
| 769 | − nd_mapping->labels[l] = NULL; | |
| 811 | + mutex_unlock(&nd_mapping->lock); | |
| 770 | 812 | |
| 771 | 813 | out: |
| 772 | 814 | kfree(old_res_list); |
| @@ -788,32 +830,28 @@ static int __blk_label_update(struct nd_region *nd_region, · 15 unmodified lines | ||
| 788 | 830 | |
| 789 | 831 | static int init_labels(struct nd_mapping *nd_mapping, int num_labels) |
| 790 | 832 | { |
| 791 | − int i, l, old_num_labels = 0; | |
| 833 | + int i, old_num_labels = 0; | |
| 834 | + struct nd_label_ent *label_ent; | |
| 792 | 835 | struct nd_namespace_index *nsindex; |
| 793 | − struct nd_namespace_label *nd_label; | |
| 794 | 836 | struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); |
| 795 | − size_t size = (num_labels + 1) * sizeof(struct nd_namespace_label *); | |
| 796 | 837 | |
| 797 | − for_each_label(l, nd_label, nd_mapping->labels) | |
| 838 | + mutex_lock(&nd_mapping->lock); | |
| 839 | + list_for_each_entry(label_ent, &nd_mapping->labels, list) | |
| 798 | 840 | old_num_labels++; |
| 841 | + mutex_unlock(&nd_mapping->lock); | |
| 799 | 842 | |
| 800 | 843 | /* |
| 801 | 844 | * We need to preserve all the old labels for the mapping so |
| 802 | 845 | * they can be garbage collected after writing the new labels. |
| 803 | 846 | */ |
| 804 | − if (num_labels > old_num_labels) { | |
| 805 | − struct nd_namespace_label **labels; | |
| 806 | − | |
| 807 | − labels = krealloc(nd_mapping->labels, size, GFP_KERNEL); | |
| 808 | − if (!labels) | |
| 847 | + for (i = old_num_labels; i < num_labels; i++) { | |
| 848 | + label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL); | |
| 849 | + if (!label_ent) | |
| 809 | 850 | return -ENOMEM; |
| 810 | − nd_mapping->labels = labels; | |
| 851 | + mutex_lock(&nd_mapping->lock); | |
| 852 | + list_add_tail(&label_ent->list, &nd_mapping->labels); | |
| 853 | + mutex_unlock(&nd_mapping->lock); | |
| 811 | 854 | } |
| 812 | − if (!nd_mapping->labels) | |
| 813 | − return -ENOMEM; | |
| 814 | − | |
| 815 | − for (i = old_num_labels; i <= num_labels; i++) | |
| 816 | − nd_mapping->labels[i] = NULL; | |
| 817 | 855 | |
| 818 | 856 | if (ndd->ns_current == -1 || ndd->ns_next == -1) |
| 819 | 857 | /* pass */; |
| @@ -837,42 +875,45 @@ static int init_labels(struct nd_mapping *nd_mapping, int num_labels) · 17 unmodified lines | ||
| 837 | 875 | static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid) |
| 838 | 876 | { |
| 839 | 877 | struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); |
| 840 | − struct nd_namespace_label *nd_label; | |
| 878 | + struct nd_label_ent *label_ent, *e; | |
| 841 | 879 | struct nd_namespace_index *nsindex; |
| 842 | 880 | u8 label_uuid[NSLABEL_UUID_LEN]; |
| 843 | − int l, num_freed = 0; | |
| 844 | 881 | unsigned long *free; |
| 882 | + LIST_HEAD(list); | |
| 845 | 883 | u32 nslot, slot; |
| 884 | + int active = 0; | |
| 846 | 885 | |
| 847 | 886 | if (!uuid) |
| 848 | 887 | return 0; |
| 849 | 888 | |
| 850 | 889 | /* no index || no labels == nothing to delete */ |
| 851 | − if (!preamble_next(ndd, &nsindex, &free, &nslot) | |
| 852 | − || !nd_mapping->labels) | |
| 890 | + if (!preamble_next(ndd, &nsindex, &free, &nslot)) | |
| 853 | 891 | return 0; |
| 854 | 892 | |
| 855 | − for_each_label(l, nd_label, nd_mapping->labels) { | |
| 893 | + mutex_lock(&nd_mapping->lock); | |
| 894 | + list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { | |
| 895 | + struct nd_namespace_label *nd_label = label_ent->label; | |
| 896 | + | |
| 897 | + if (!nd_label) | |
| 898 | + continue; | |
| 899 | + active++; | |
| 856 | 900 | memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); |
| 857 | 901 | if (memcmp(label_uuid, uuid, NSLABEL_UUID_LEN) != 0) |
| 858 | 902 | continue; |
| 903 | + active--; | |
| 859 | 904 | slot = to_slot(ndd, nd_label); |
| 860 | 905 | nd_label_free_slot(ndd, slot); |
| 861 | 906 | dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot); |
| 862 | − del_label(nd_mapping, l); | |
| 863 | − num_freed++; | |
| 864 | − l--; /* retry with new label at this index */ | |
| 907 | + list_move_tail(&label_ent->list, &list); | |
| 908 | + label_ent->label = NULL; | |
| 865 | 909 | } |
| 910 | + list_splice_tail_init(&list, &nd_mapping->labels); | |
| 866 | 911 | |
| 867 | − if (num_freed > l) { | |
| 868 | − /* | |
| 869 | − * num_freed will only ever be > l when we delete the last | |
| 870 | − * label | |
| 871 | − */ | |
| 872 | − kfree(nd_mapping->labels); | |
| 873 | − nd_mapping->labels = NULL; | |
| 874 | − dev_dbg(ndd->dev, "%s: no more labels\n", __func__); | |
| 912 | + if (active == 0) { | |
| 913 | + nd_mapping_free_labels(nd_mapping); | |
| 914 | + dev_dbg(ndd->dev, "%s: no more active labels\n", __func__); | |
| 875 | 915 | } |
| 916 | + mutex_unlock(&nd_mapping->lock); | |
| 876 | 917 | |
| 877 | 918 | return nd_label_write_index(ndd, ndd->ns_next, |
| 878 | 919 | nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); |
| @@ -885,7 +926,9 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region, · 6 unmodified lines | ||
| 885 | 926 | |
| 886 | 927 | for (i = 0; i < nd_region->ndr_mappings; i++) { |
| 887 | 928 | struct nd_mapping *nd_mapping = &nd_region->mapping[i]; |
| 888 | − int rc; | |
| 929 | + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); | |
| 930 | + struct resource *res; | |
| 931 | + int rc, count = 0; | |
| 889 | 932 | |
| 890 | 933 | if (size == 0) { |
| 891 | 934 | rc = del_labels(nd_mapping, nspm->uuid); |
| @@ -894,7 +937,12 @@ int nd_pmem_namespace_label_update(struct nd_region *nd_region, · 2 unmodified lines | ||
| 894 | 937 | continue; |
| 895 | 938 | } |
| 896 | 939 | |
| 897 | − rc = init_labels(nd_mapping, 1); | |
| 940 | + for_each_dpa_resource(ndd, res) | |
| 941 | + if (strncmp(res->name, "pmem", 3) == 0) | |
| 942 | + count++; | |
| 943 | + WARN_ON_ONCE(!count); | |
| 944 | + | |
| 945 | + rc = init_labels(nd_mapping, count); | |
| 898 | 946 | if (rc < 0) |
| 899 | 947 | return rc; |
| 900 | 948 | |
| @@ -12,8 +12,10 @@ · 11 unmodified lines | ||
| 12 | 12 | */ |
| 13 | 13 | #include <linux/module.h> |
| 14 | 14 | #include <linux/device.h> |
| 15 | +#include <linux/sort.h> | |
| 15 | 16 | #include <linux/slab.h> |
| 16 | 17 | #include <linux/pmem.h> |
| 18 | +#include <linux/list.h> | |
| 17 | 19 | #include <linux/nd.h> |
| 18 | 20 | #include "nd-core.h" |
| 19 | 21 | #include "nd.h" |
| @@ -28,7 +30,10 @@ static void namespace_io_release(struct device *dev) · 8 unmodified lines | ||
| 28 | 30 | static void namespace_pmem_release(struct device *dev) |
| 29 | 31 | { |
| 30 | 32 | struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); |
| 33 | + struct nd_region *nd_region = to_nd_region(dev->parent); | |
| 31 | 34 | |
| 35 | + if (nspm->id >= 0) | |
| 36 | + ida_simple_remove(&nd_region->ns_ida, nspm->id); | |
| 32 | 37 | kfree(nspm->alt_name); |
| 33 | 38 | kfree(nspm->uuid); |
| 34 | 39 | kfree(nspm); |
| @@ -62,17 +67,17 @@ static struct device_type namespace_blk_device_type = { · 27 unmodified lines | ||
| 62 | 67 | .release = namespace_blk_release, |
| 63 | 68 | }; |
| 64 | 69 | |
| 65 | −static bool is_namespace_pmem(struct device *dev) | |
| 70 | +static bool is_namespace_pmem(const struct device *dev) | |
| 66 | 71 | { |
| 67 | 72 | return dev ? dev->type == &namespace_pmem_device_type : false; |
| 68 | 73 | } |
| 69 | 74 | |
| 70 | −static bool is_namespace_blk(struct device *dev) | |
| 75 | +static bool is_namespace_blk(const struct device *dev) | |
| 71 | 76 | { |
| 72 | 77 | return dev ? dev->type == &namespace_blk_device_type : false; |
| 73 | 78 | } |
| 74 | 79 | |
| 75 | −static bool is_namespace_io(struct device *dev) | |
| 80 | +static bool is_namespace_io(const struct device *dev) | |
| 76 | 81 | { |
| 77 | 82 | return dev ? dev->type == &namespace_io_device_type : false; |
| 78 | 83 | } |
| @@ -168,7 +173,21 @@ const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns, · 89 unmodified lines | ||
| 168 | 173 | suffix = "s"; |
| 169 | 174 | |
| 170 | 175 | if (is_namespace_pmem(&ndns->dev) || is_namespace_io(&ndns->dev)) { |
| 171 | − sprintf(name, "pmem%d%s", nd_region->id, suffix ? suffix : ""); | |
| 176 | + int nsidx = 0; | |
| 177 | + | |
| 178 | + if (is_namespace_pmem(&ndns->dev)) { | |
| 179 | + struct nd_namespace_pmem *nspm; | |
| 180 | + | |
| 181 | + nspm = to_nd_namespace_pmem(&ndns->dev); | |
| 182 | + nsidx = nspm->id; | |
| 183 | + } | |
| 184 | + | |
| 185 | + if (nsidx) | |
| 186 | + sprintf(name, "pmem%d.%d%s", nd_region->id, nsidx, | |
| 187 | + suffix ? suffix : ""); | |
| 188 | + else | |
| 189 | + sprintf(name, "pmem%d%s", nd_region->id, | |
| 190 | + suffix ? suffix : ""); | |
| 172 | 191 | } else if (is_namespace_blk(&ndns->dev)) { |
| 173 | 192 | struct nd_namespace_blk *nsblk; |
| 174 | 193 | |
| @@ -294,7 +313,7 @@ static bool __nd_namespace_blk_validate(struct nd_namespace_blk *nsblk) · 119 unmodified lines | ||
| 294 | 313 | if (strcmp(res->name, label_id.id) != 0) |
| 295 | 314 | continue; |
| 296 | 315 | /* |
| 297 | − * Resources with unacknoweldged adjustments indicate a | |
| 316 | + * Resources with unacknowledged adjustments indicate a | |
| 298 | 317 | * failure to update labels |
| 299 | 318 | */ |
| 300 | 319 | if (res->flags & DPA_RESOURCE_ADJUSTED) |
| @@ -510,19 +529,68 @@ static resource_size_t init_dpa_allocation(struct nd_label_id *label_id, · 209 unmodified lines | ||
| 510 | 529 | return rc ? n : 0; |
| 511 | 530 | } |
| 512 | 531 | |
| 513 | −static bool space_valid(bool is_pmem, bool is_reserve, | |
| 514 | − struct nd_label_id *label_id, struct resource *res) | |
| 532 | + | |
| 533 | +/** | |
| 534 | + * space_valid() - validate free dpa space against constraints | |
| 535 | + * @nd_region: hosting region of the free space | |
| 536 | + * @ndd: dimm device data for debug | |
| 537 | + * @label_id: namespace id to allocate space | |
| 538 | + * @prev: potential allocation that precedes free space | |
| 539 | + * @next: allocation that follows the given free space range | |
| 540 | + * @exist: first allocation with same id in the mapping | |
| 541 | + * @n: range that must satisfied for pmem allocations | |
| 542 | + * @valid: free space range to validate | |
| 543 | + * | |
| 544 | + * BLK-space is valid as long as it does not precede a PMEM | |
| 545 | + * allocation in a given region. PMEM-space must be contiguous | |
| 546 | + * and adjacent to an existing existing allocation (if one | |
| 547 | + * exists). If reserving PMEM any space is valid. | |
| 548 | + */ | |
| 549 | +static void space_valid(struct nd_region *nd_region, struct nvdimm_drvdata *ndd, | |
| 550 | + struct nd_label_id *label_id, struct resource *prev, | |
| 551 | + struct resource *next, struct resource *exist, | |
| 552 | + resource_size_t n, struct resource *valid) | |
| 515 | 553 | { |
| 516 | − /* | |
| 517 | − * For BLK-space any space is valid, for PMEM-space, it must be | |
| 518 | − * contiguous with an existing allocation unless we are | |
| 519 | − * reserving pmem. | |
| 520 | − */ | |
| 521 | − if (is_reserve || !is_pmem) | |
| 522 | − return true; | |
| 523 | − if (!res || strcmp(res->name, label_id->id) == 0) | |
| 524 | − return true; | |
| 525 | − return false; | |
| 554 | + bool is_reserve = strcmp(label_id->id, "pmem-reserve") == 0; | |
| 555 | + bool is_pmem = strncmp(label_id->id, "pmem", 4) == 0; | |
| 556 | + | |
| 557 | + if (valid->start >= valid->end) | |
| 558 | + goto invalid; | |
| 559 | + | |
| 560 | + if (is_reserve) | |
| 561 | + return; | |
| 562 | + | |
| 563 | + if (!is_pmem) { | |
| 564 | + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; | |
| 565 | + struct nvdimm_bus *nvdimm_bus; | |
| 566 | + struct blk_alloc_info info = { | |
| 567 | + .nd_mapping = nd_mapping, | |
| 568 | + .available = nd_mapping->size, | |
| 569 | + .res = valid, | |
| 570 | + }; | |
| 571 | + | |
| 572 | + WARN_ON(!is_nd_blk(&nd_region->dev)); | |
| 573 | + nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); | |
| 574 | + device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy); | |
| 575 | + return; | |
| 576 | + } | |
| 577 | + | |
| 578 | + /* allocation needs to be contiguous, so this is all or nothing */ | |
| 579 | + if (resource_size(valid) < n) | |
| 580 | + goto invalid; | |
| 581 | + | |
| 582 | + /* we've got all the space we need and no existing allocation */ | |
| 583 | + if (!exist) | |
| 584 | + return; | |
| 585 | + | |
| 586 | + /* allocation needs to be contiguous with the existing namespace */ | |
| 587 | + if (valid->start == exist->end + 1 | |
| 588 | + || valid->end == exist->start - 1) | |
| 589 | + return; | |
| 590 | + | |
| 591 | + invalid: | |
| 592 | + /* truncate @valid size to 0 */ | |
| 593 | + valid->end = valid->start - 1; | |
| 526 | 594 | } |
| 527 | 595 | |
| 528 | 596 | enum alloc_loc { |
| @@ -534,18 +602,24 @@ static resource_size_t scan_allocate(struct nd_region *nd_region, · 5 unmodified lines | ||
| 534 | 602 | resource_size_t n) |
| 535 | 603 | { |
| 536 | 604 | resource_size_t mapping_end = nd_mapping->start + nd_mapping->size - 1; |
| 537 | − bool is_reserve = strcmp(label_id->id, "pmem-reserve") == 0; | |
| 538 | 605 | bool is_pmem = strncmp(label_id->id, "pmem", 4) == 0; |
| 539 | 606 | struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); |
| 607 | + struct resource *res, *exist = NULL, valid; | |
| 540 | 608 | const resource_size_t to_allocate = n; |
| 541 | − struct resource *res; | |
| 542 | 609 | int first; |
| 543 | 610 | |
| 611 | + for_each_dpa_resource(ndd, res) | |
| 612 | + if (strcmp(label_id->id, res->name) == 0) | |
| 613 | + exist = res; | |
| 614 | + | |
| 615 | + valid.start = nd_mapping->start; | |
| 616 | + valid.end = mapping_end; | |
| 617 | + valid.name = "free space"; | |
| 544 | 618 | retry: |
| 545 | 619 | first = 0; |
| 546 | 620 | for_each_dpa_resource(ndd, res) { |
| 547 | − resource_size_t allocate, available = 0, free_start, free_end; | |
| 548 | 621 | struct resource *next = res->sibling, *new_res = NULL; |
| 622 | + resource_size_t allocate, available = 0; | |
| 549 | 623 | enum alloc_loc loc = ALLOC_ERR; |
| 550 | 624 | const char *action; |
| 551 | 625 | int rc = 0; |
| @@ -558,32 +632,35 @@ static resource_size_t scan_allocate(struct nd_region *nd_region, · 6 unmodified lines | ||
| 558 | 632 | |
| 559 | 633 | /* space at the beginning of the mapping */ |
| 560 | 634 | if (!first++ && res->start > nd_mapping->start) { |
| 561 | − free_start = nd_mapping->start; | |
| 562 | − available = res->start - free_start; | |
| 563 | − if (space_valid(is_pmem, is_reserve, label_id, NULL)) | |
| 635 | + valid.start = nd_mapping->start; | |
| 636 | + valid.end = res->start - 1; | |
| 637 | + space_valid(nd_region, ndd, label_id, NULL, next, exist, | |
| 638 | + to_allocate, &valid); | |
| 639 | + available = resource_size(&valid); | |
| 640 | + if (available) | |
| 564 | 641 | loc = ALLOC_BEFORE; |
| 565 | 642 | } |
| 566 | 643 | |
| 567 | 644 | /* space between allocations */ |
| 568 | 645 | if (!loc && next) { |
| 569 | − free_start = res->start + resource_size(res); | |
| 570 | − free_end = min(mapping_end, next->start - 1); | |
| 571 | − if (space_valid(is_pmem, is_reserve, label_id, res) | |
| 572 | − && free_start < free_end) { | |
| 573 | − available = free_end + 1 - free_start; | |
| 646 | + valid.start = res->start + resource_size(res); | |
| 647 | + valid.end = min(mapping_end, next->start - 1); | |
| 648 | + space_valid(nd_region, ndd, label_id, res, next, exist, | |
| 649 | + to_allocate, &valid); | |
| 650 | + available = resource_size(&valid); | |
| 651 | + if (available) | |
| 574 | 652 | loc = ALLOC_MID; |
| 575 | − } | |
| 576 | 653 | } |
| 577 | 654 | |
| 578 | 655 | /* space at the end of the mapping */ |
| 579 | 656 | if (!loc && !next) { |
| 580 | − free_start = res->start + resource_size(res); | |
| 581 | − free_end = mapping_end; | |
| 582 | − if (space_valid(is_pmem, is_reserve, label_id, res) | |
| 583 | − && free_start < free_end) { | |
| 584 | − available = free_end + 1 - free_start; | |
| 657 | + valid.start = res->start + resource_size(res); | |
| 658 | + valid.end = mapping_end; | |
| 659 | + space_valid(nd_region, ndd, label_id, res, next, exist, | |
| 660 | + to_allocate, &valid); | |
| 661 | + available = resource_size(&valid); | |
| 662 | + if (available) | |
| 585 | 663 | loc = ALLOC_AFTER; |
| 586 | − } | |
| 587 | 664 | } |
| 588 | 665 | |
| 589 | 666 | if (!loc || !available) |
| @@ -593,8 +670,6 @@ static resource_size_t scan_allocate(struct nd_region *nd_region, · 3 unmodified lines | ||
| 593 | 670 | case ALLOC_BEFORE: |
| 594 | 671 | if (strcmp(res->name, label_id->id) == 0) { |
| 595 | 672 | /* adjust current resource up */ |
| 596 | − if (is_pmem && !is_reserve) | |
| 597 | − return n; | |
| 598 | 673 | rc = adjust_resource(res, res->start - allocate, |
| 599 | 674 | resource_size(res) + allocate); |
| 600 | 675 | action = "cur grow up"; |
| @@ -604,8 +679,6 @@ static resource_size_t scan_allocate(struct nd_region *nd_region, · 3 unmodified lines | ||
| 604 | 679 | case ALLOC_MID: |
| 605 | 680 | if (strcmp(next->name, label_id->id) == 0) { |
| 606 | 681 | /* adjust next resource up */ |
| 607 | − if (is_pmem && !is_reserve) | |
| 608 | − return n; | |
| 609 | 682 | rc = adjust_resource(next, next->start |
| 610 | 683 | - allocate, resource_size(next) |
| 611 | 684 | + allocate); |
| @@ -629,12 +702,10 @@ static resource_size_t scan_allocate(struct nd_region *nd_region, · 17 unmodified lines | ||
| 629 | 702 | if (strcmp(action, "allocate") == 0) { |
| 630 | 703 | /* BLK allocate bottom up */ |
| 631 | 704 | if (!is_pmem) |
| 632 | − free_start += available - allocate; | |
| 633 | − else if (!is_reserve && free_start != nd_mapping->start) | |
| 634 | − return n; | |
| 705 | + valid.start += available - allocate; | |
| 635 | 706 | |
| 636 | 707 | new_res = nvdimm_allocate_dpa(ndd, label_id, |
| 637 | − free_start, allocate); | |
| 708 | + valid.start, allocate); | |
| 638 | 709 | if (!new_res) |
| 639 | 710 | rc = -EBUSY; |
| 640 | 711 | } else if (strcmp(action, "grow down") == 0) { |
| @@ -832,13 +903,45 @@ static int grow_dpa_allocation(struct nd_region *nd_region, · 191 unmodified lines | ||
| 832 | 903 | return 0; |
| 833 | 904 | } |
| 834 | 905 | |
| 835 | −static void nd_namespace_pmem_set_size(struct nd_region *nd_region, | |
| 906 | +static void nd_namespace_pmem_set_resource(struct nd_region *nd_region, | |
| 836 | 907 | struct nd_namespace_pmem *nspm, resource_size_t size) |
| 837 | 908 | { |
| 838 | 909 | struct resource *res = &nspm->nsio.res; |
| 910 | + resource_size_t offset = 0; | |
| 839 | 911 | |
| 840 | − res->start = nd_region->ndr_start; | |
| 841 | − res->end = nd_region->ndr_start + size - 1; | |
| 912 | + if (size && !nspm->uuid) { | |
| 913 | + WARN_ON_ONCE(1); | |
| 914 | + size = 0; | |
| 915 | + } | |
| 916 | + | |
| 917 | + if (size && nspm->uuid) { | |
| 918 | + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; | |
| 919 | + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); | |
| 920 | + struct nd_label_id label_id; | |
| 921 | + struct resource *res; | |
| 922 | + | |
| 923 | + if (!ndd) { | |
| 924 | + size = 0; | |
| 925 | + goto out; | |
| 926 | + } | |
| 927 | + | |
| 928 | + nd_label_gen_id(&label_id, nspm->uuid, 0); | |
| 929 | + | |
| 930 | + /* calculate a spa offset from the dpa allocation offset */ | |
| 931 | + for_each_dpa_resource(ndd, res) | |
| 932 | + if (strcmp(res->name, label_id.id) == 0) { | |
| 933 | + offset = (res->start - nd_mapping->start) | |
| 934 | + * nd_region->ndr_mappings; | |
| 935 | + goto out; | |
| 936 | + } | |
| 937 | + | |
| 938 | + WARN_ON_ONCE(1); | |
| 939 | + size = 0; | |
| 940 | + } | |
| 941 | + | |
| 942 | + out: | |
| 943 | + res->start = nd_region->ndr_start + offset; | |
| 944 | + res->end = res->start + size - 1; | |
| 842 | 945 | } |
| 843 | 946 | |
| 844 | 947 | static bool uuid_not_set(const u8 *uuid, struct device *dev, const char *where) |
| @@ -929,7 +1032,7 @@ static ssize_t __size_store(struct device *dev, unsigned long long val) · 84 unmodified lines | ||
| 929 | 1032 | if (is_namespace_pmem(dev)) { |
| 930 | 1033 | struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); |
| 931 | 1034 | |
| 932 | − nd_namespace_pmem_set_size(nd_region, nspm, | |
| 1035 | + nd_namespace_pmem_set_resource(nd_region, nspm, | |
| 933 | 1036 | val * nd_region->ndr_mappings); |
| 934 | 1037 | } else if (is_namespace_blk(dev)) { |
| 935 | 1038 | struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); |
| @@ -1031,22 +1134,27 @@ static ssize_t size_show(struct device *dev, · 95 unmodified lines | ||
| 1031 | 1134 | } |
| 1032 | 1135 | static DEVICE_ATTR(size, S_IRUGO, size_show, size_store); |
| 1033 | 1136 | |
| 1034 | −static ssize_t uuid_show(struct device *dev, | |
| 1035 | − struct device_attribute *attr, char *buf) | |
| 1137 | +static u8 *namespace_to_uuid(struct device *dev) | |
| 1036 | 1138 | { |
| 1037 | − u8 *uuid; | |
| 1038 | − | |
| 1039 | 1139 | if (is_namespace_pmem(dev)) { |
| 1040 | 1140 | struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); |
| 1041 | 1141 | |
| 1042 | − uuid = nspm->uuid; | |
| 1142 | + return nspm->uuid; | |
| 1043 | 1143 | } else if (is_namespace_blk(dev)) { |
| 1044 | 1144 | struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); |
| 1045 | 1145 | |
| 1046 | − uuid = nsblk->uuid; | |
| 1146 | + return nsblk->uuid; | |
| 1047 | 1147 | } else |
| 1048 | − return -ENXIO; | |
| 1148 | + return ERR_PTR(-ENXIO); | |
| 1149 | +} | |
| 1049 | 1150 | |
| 1151 | +static ssize_t uuid_show(struct device *dev, | |
| 1152 | + struct device_attribute *attr, char *buf) | |
| 1153 | +{ | |
| 1154 | + u8 *uuid = namespace_to_uuid(dev); | |
| 1155 | + | |
| 1156 | + if (IS_ERR(uuid)) | |
| 1157 | + return PTR_ERR(uuid); | |
| 1050 | 1158 | if (uuid) |
| 1051 | 1159 | return sprintf(buf, "%pUb\n", uuid); |
| 1052 | 1160 | return sprintf(buf, "\n"); |
| @@ -1089,7 +1197,7 @@ static int namespace_update_uuid(struct nd_region *nd_region, · 36 unmodified lines | ||
| 1089 | 1197 | * |
| 1090 | 1198 | * FIXME: can we delete uuid with zero dpa allocated? |
| 1091 | 1199 | */ |
| 1092 | − if (nd_mapping->labels) | |
| 1200 | + if (list_empty(&nd_mapping->labels)) | |
| 1093 | 1201 | return -EBUSY; |
| 1094 | 1202 | } |
| 1095 | 1203 | |
| @@ -1491,14 +1599,19 @@ static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid, · 395 unmodified lines | ||
| 1491 | 1599 | |
| 1492 | 1600 | for (i = 0; i < nd_region->ndr_mappings; i++) { |
| 1493 | 1601 | struct nd_mapping *nd_mapping = &nd_region->mapping[i]; |
| 1494 | − struct nd_namespace_label *nd_label; | |
| 1602 | + struct nd_label_ent *label_ent; | |
| 1495 | 1603 | bool found_uuid = false; |
| 1496 | − int l; | |
| 1497 | 1604 | |
| 1498 | − for_each_label(l, nd_label, nd_mapping->labels) { | |
| 1499 | − u64 isetcookie = __le64_to_cpu(nd_label->isetcookie); | |
| 1500 | − u16 position = __le16_to_cpu(nd_label->position); | |
| 1501 | − u16 nlabel = __le16_to_cpu(nd_label->nlabel); | |
| 1605 | + list_for_each_entry(label_ent, &nd_mapping->labels, list) { | |
| 1606 | + struct nd_namespace_label *nd_label = label_ent->label; | |
| 1607 | + u16 position, nlabel; | |
| 1608 | + u64 isetcookie; | |
| 1609 | + | |
| 1610 | + if (!nd_label) | |
| 1611 | + continue; | |
| 1612 | + isetcookie = __le64_to_cpu(nd_label->isetcookie); | |
| 1613 | + position = __le16_to_cpu(nd_label->position); | |
| 1614 | + nlabel = __le16_to_cpu(nd_label->nlabel); | |
| 1502 | 1615 | |
| 1503 | 1616 | if (isetcookie != cookie) |
| 1504 | 1617 | continue; |
| @@ -1528,7 +1641,6 @@ static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid, · 23 unmodified lines | ||
| 1528 | 1641 | |
| 1529 | 1642 | static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) |
| 1530 | 1643 | { |
| 1531 | − struct nd_namespace_label *select = NULL; | |
| 1532 | 1644 | int i; |
| 1533 | 1645 | |
| 1534 | 1646 | if (!pmem_id) |
| @@ -1536,90 +1648,106 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id) · 1 unmodified line | ||
| 1536 | 1648 | |
| 1537 | 1649 | for (i = 0; i < nd_region->ndr_mappings; i++) { |
| 1538 | 1650 | struct nd_mapping *nd_mapping = &nd_region->mapping[i]; |
| 1539 | − struct nd_namespace_label *nd_label; | |
| 1651 | + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); | |
| 1652 | + struct nd_namespace_label *nd_label = NULL; | |
| 1540 | 1653 | u64 hw_start, hw_end, pmem_start, pmem_end; |
| 1541 | − int l; | |
| 1654 | + struct nd_label_ent *label_ent; | |
| 1542 | 1655 | |
| 1543 | − for_each_label(l, nd_label, nd_mapping->labels) | |
| 1656 | + WARN_ON(!mutex_is_locked(&nd_mapping->lock)); | |
| 1657 | + list_for_each_entry(label_ent, &nd_mapping->labels, list) { | |
| 1658 | + nd_label = label_ent->label; | |
| 1659 | + if (!nd_label) | |
| 1660 | + continue; | |
| 1544 | 1661 | if (memcmp(nd_label->uuid, pmem_id, NSLABEL_UUID_LEN) == 0) |
| 1545 | 1662 | break; |
| 1663 | + nd_label = NULL; | |
| 1664 | + } | |
| 1546 | 1665 | |
| 1547 | 1666 | if (!nd_label) { |
| 1548 | 1667 | WARN_ON(1); |
| 1549 | 1668 | return -EINVAL; |
| 1550 | 1669 | } |
| 1551 | 1670 | |
| 1552 | − select = nd_label; | |
| 1553 | 1671 | /* |
| 1554 | 1672 | * Check that this label is compliant with the dpa |
| 1555 | 1673 | * range published in NFIT |
| 1556 | 1674 | */ |
| 1557 | 1675 | hw_start = nd_mapping->start; |
| 1558 | 1676 | hw_end = hw_start + nd_mapping->size; |
| 1559 | − pmem_start = __le64_to_cpu(select->dpa); | |
| 1560 | − pmem_end = pmem_start + __le64_to_cpu(select->rawsize); | |
| 1561 | − if (pmem_start == hw_start && pmem_end <= hw_end) | |
| 1677 | + pmem_start = __le64_to_cpu(nd_label->dpa); | |
| 1678 | + pmem_end = pmem_start + __le64_to_cpu(nd_label->rawsize); | |
| 1679 | + if (pmem_start >= hw_start && pmem_start < hw_end | |
| 1680 | + && pmem_end <= hw_end && pmem_end > hw_start) | |
| 1562 | 1681 | /* pass */; |
| 1563 | − else | |
| 1682 | + else { | |
| 1683 | + dev_dbg(&nd_region->dev, "%s invalid label for %pUb\n", | |
| 1684 | + dev_name(ndd->dev), nd_label->uuid); | |
| 1564 | 1685 | return -EINVAL; |
| 1686 | + } | |
| 1565 | 1687 | |
| 1566 | − nd_mapping->labels[0] = select; | |
| 1567 | − nd_mapping->labels[1] = NULL; | |
| 1688 | + /* move recently validated label to the front of the list */ | |
| 1689 | + list_move(&label_ent->list, &nd_mapping->labels); | |
| 1568 | 1690 | } |
| 1569 | 1691 | return 0; |
| 1570 | 1692 | } |
| 1571 | 1693 | |
| 1572 | 1694 | /** |
| 1573 | − * find_pmem_label_set - validate interleave set labelling, retrieve label0 | |
| 1695 | + * create_namespace_pmem - validate interleave set labelling, retrieve label0 | |
| 1574 | 1696 | * @nd_region: region with mappings to validate |
| 1697 | + * @nspm: target namespace to create | |
| 1698 | + * @nd_label: target pmem namespace label to evaluate | |
| 1575 | 1699 | */ |
| 1576 | −static int find_pmem_label_set(struct nd_region *nd_region, | |
| 1577 | − struct nd_namespace_pmem *nspm) | |
| 1700 | +struct device *create_namespace_pmem(struct nd_region *nd_region, | |
| 1701 | + struct nd_namespace_label *nd_label) | |
| 1578 | 1702 | { |
| 1579 | 1703 | u64 cookie = nd_region_interleave_set_cookie(nd_region); |
| 1580 | − struct nd_namespace_label *nd_label; | |
| 1581 | − u8 select_id[NSLABEL_UUID_LEN]; | |
| 1704 | + struct nd_label_ent *label_ent; | |
| 1705 | + struct nd_namespace_pmem *nspm; | |
| 1706 | + struct nd_mapping *nd_mapping; | |
| 1582 | 1707 | resource_size_t size = 0; |
| 1583 | − u8 *pmem_id = NULL; | |
| 1584 | − int rc = -ENODEV, l; | |
| 1708 | + struct resource *res; | |
| 1709 | + struct device *dev; | |
| 1710 | + int rc = 0; | |
| 1585 | 1711 | u16 i; |
| 1586 | 1712 | |
| 1587 | − if (cookie == 0) | |
| 1588 | − return -ENXIO; | |
| 1713 | + if (cookie == 0) { | |
| 1714 | + dev_dbg(&nd_region->dev, "invalid interleave-set-cookie\n"); | |
| 1715 | + return ERR_PTR(-ENXIO); | |
| 1716 | + } | |
| 1589 | 1717 | |
| 1590 | − /* | |
| 1591 | − * Find a complete set of labels by uuid. By definition we can start | |
| 1592 | − * with any mapping as the reference label | |
| 1593 | − */ | |
| 1594 | − for_each_label(l, nd_label, nd_region->mapping[0].labels) { | |
| 1595 | − u64 isetcookie = __le64_to_cpu(nd_label->isetcookie); | |
| 1718 | + if (__le64_to_cpu(nd_label->isetcookie) != cookie) { | |
| 1719 | + dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n", | |
| 1720 | + nd_label->uuid); | |
| 1721 | + return ERR_PTR(-EAGAIN); | |
| 1722 | + } | |
| 1596 | 1723 | |
| 1597 | − if (isetcookie != cookie) | |
| 1598 | − continue; | |
| 1724 | + nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); | |
| 1725 | + if (!nspm) | |
| 1726 | + return ERR_PTR(-ENOMEM); | |
| 1599 | 1727 | |
| 1600 | − for (i = 0; nd_region->ndr_mappings; i++) | |
| 1601 | − if (!has_uuid_at_pos(nd_region, nd_label->uuid, | |
| 1602 | − cookie, i)) | |
| 1603 | − break; | |
| 1604 | − if (i < nd_region->ndr_mappings) { | |
| 1605 | − /* | |
| 1606 | − * Give up if we don't find an instance of a | |
| 1607 | − * uuid at each position (from 0 to | |
| 1608 | − * nd_region->ndr_mappings - 1), or if we find a | |
| 1609 | − * dimm with two instances of the same uuid. | |
| 1610 | − */ | |
| 1611 | − rc = -EINVAL; | |
| 1612 | − goto err; | |
| 1613 | − } else if (pmem_id) { | |
| 1614 | − /* | |
| 1615 | − * If there is more than one valid uuid set, we | |
| 1616 | − * need userspace to clean this up. | |
| 1617 | − */ | |
| 1618 | − rc = -EBUSY; | |
| 1619 | − goto err; | |
| 1620 | − } | |
| 1621 | − memcpy(select_id, nd_label->uuid, NSLABEL_UUID_LEN); | |
| 1622 | − pmem_id = select_id; | |
| 1728 | + nspm->id = -1; | |
| 1729 | + dev = &nspm->nsio.common.dev; | |
| 1730 | + dev->type = &namespace_pmem_device_type; | |
| 1731 | + dev->parent = &nd_region->dev; | |
| 1732 | + res = &nspm->nsio.res; | |
| 1733 | + res->name = dev_name(&nd_region->dev); | |
| 1734 | + res->flags = IORESOURCE_MEM; | |
| 1735 | + | |
| 1736 | + for (i = 0; i < nd_region->ndr_mappings; i++) | |
| 1737 | + if (!has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i)) | |
| 1738 | + break; | |
| 1739 | + if (i < nd_region->ndr_mappings) { | |
| 1740 | + struct nvdimm_drvdata *ndd = to_ndd(&nd_region->mapping[i]); | |
| 1741 | + | |
| 1742 | + /* | |
| 1743 | + * Give up if we don't find an instance of a uuid at each | |
| 1744 | + * position (from 0 to nd_region->ndr_mappings - 1), or if we | |
| 1745 | + * find a dimm with two instances of the same uuid. | |
| 1746 | + */ | |
| 1747 | + dev_err(&nd_region->dev, "%s missing label for %pUb\n", | |
| 1748 | + dev_name(ndd->dev), nd_label->uuid); | |
| 1749 | + rc = -EINVAL; | |
| 1750 | + goto err; | |
| 1623 | 1751 | } |
| 1624 | 1752 | |
| 1625 | 1753 | /* |
| @@ -1630,14 +1758,23 @@ static int find_pmem_label_set(struct nd_region *nd_region, · 4 unmodified lines | ||
| 1630 | 1758 | * the dimm being enabled (i.e. nd_label_reserve_dpa() |
| 1631 | 1759 | * succeeded). |
| 1632 | 1760 | */ |
| 1633 | − rc = select_pmem_id(nd_region, pmem_id); | |
| 1761 | + rc = select_pmem_id(nd_region, nd_label->uuid); | |
| 1634 | 1762 | if (rc) |
| 1635 | 1763 | goto err; |
| 1636 | 1764 | |
| 1637 | 1765 | /* Calculate total size and populate namespace properties from label0 */ |
| 1638 | 1766 | for (i = 0; i < nd_region->ndr_mappings; i++) { |
| 1639 | − struct nd_mapping *nd_mapping = &nd_region->mapping[i]; | |
| 1640 | − struct nd_namespace_label *label0 = nd_mapping->labels[0]; | |
| 1767 | + struct nd_namespace_label *label0; | |
| 1768 | + | |
| 1769 | + nd_mapping = &nd_region->mapping[i]; | |
| 1770 | + label_ent = list_first_entry_or_null(&nd_mapping->labels, | |
| 1771 | + typeof(*label_ent), list); | |
| 1772 | + label0 = label_ent ? label_ent->label : 0; | |
| 1773 | + | |
| 1774 | + if (!label0) { | |
| 1775 | + WARN_ON(1); | |
| 1776 | + continue; | |
| 1777 | + } | |
| 1641 | 1778 | |
| 1642 | 1779 | size += __le64_to_cpu(label0->rawsize); |
| 1643 | 1780 | if (__le16_to_cpu(label0->position) != 0) |
| @@ -1654,10 +1791,11 @@ static int find_pmem_label_set(struct nd_region *nd_region, · 10 unmodified lines | ||
| 1654 | 1791 | goto err; |
| 1655 | 1792 | } |
| 1656 | 1793 | |
| 1657 | − nd_namespace_pmem_set_size(nd_region, nspm, size); | |
| 1794 | + nd_namespace_pmem_set_resource(nd_region, nspm, size); | |
| 1658 | 1795 | |
| 1659 | − return 0; | |
| 1796 | + return dev; | |
| 1660 | 1797 | err: |
| 1798 | + namespace_pmem_release(dev); | |
| 1661 | 1799 | switch (rc) { |
| 1662 | 1800 | case -EINVAL: |
| 1663 | 1801 | dev_dbg(&nd_region->dev, "%s: invalid label(s)\n", __func__); |
| @@ -1670,55 +1808,7 @@ static int find_pmem_label_set(struct nd_region *nd_region, · 6 unmodified lines | ||
| 1670 | 1808 | __func__, rc); |
| 1671 | 1809 | break; |
| 1672 | 1810 | } |
| 1673 | − return rc; | |
| 1674 | −} | |
| 1675 | − | |
| 1676 | −static struct device **create_namespace_pmem(struct nd_region *nd_region) | |
| 1677 | −{ | |
| 1678 | − struct nd_namespace_pmem *nspm; | |
| 1679 | − struct device *dev, **devs; | |
| 1680 | − struct resource *res; | |
| 1681 | − int rc; | |
| 1682 | − | |
| 1683 | − nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); | |
| 1684 | − if (!nspm) | |
| 1685 | − return NULL; | |
| 1686 | − | |
| 1687 | − dev = &nspm->nsio.common.dev; | |
| 1688 | − dev->type = &namespace_pmem_device_type; | |
| 1689 | − dev->parent = &nd_region->dev; | |
| 1690 | − res = &nspm->nsio.res; | |
| 1691 | − res->name = dev_name(&nd_region->dev); | |
| 1692 | − res->flags = IORESOURCE_MEM; | |
| 1693 | − rc = find_pmem_label_set(nd_region, nspm); | |
| 1694 | − if (rc == -ENODEV) { | |
| 1695 | − int i; | |
| 1696 | − | |
| 1697 | − /* Pass, try to permit namespace creation... */ | |
| 1698 | − for (i = 0; i < nd_region->ndr_mappings; i++) { | |
| 1699 | − struct nd_mapping *nd_mapping = &nd_region->mapping[i]; | |
| 1700 | − | |
| 1701 | − kfree(nd_mapping->labels); | |
| 1702 | − nd_mapping->labels = NULL; | |
| 1703 | − } | |
| 1704 | − | |
| 1705 | − /* Publish a zero-sized namespace for userspace to configure. */ | |
| 1706 | − nd_namespace_pmem_set_size(nd_region, nspm, 0); | |
| 1707 | − | |
| 1708 | − rc = 0; | |
| 1709 | − } else if (rc) | |
| 1710 | − goto err; | |
| 1711 | − | |
| 1712 | − devs = kcalloc(2, sizeof(struct device *), GFP_KERNEL); | |
| 1713 | − if (!devs) | |
| 1714 | − goto err; | |
| 1715 | − | |
| 1716 | − devs[0] = dev; | |
| 1717 | − return devs; | |
| 1718 | − | |
| 1719 | − err: | |
| 1720 | − namespace_pmem_release(&nspm->nsio.common.dev); | |
| 1721 | − return NULL; | |
| 1811 | + return ERR_PTR(rc); | |
| 1722 | 1812 | } |
| 1723 | 1813 | |
| 1724 | 1814 | struct resource *nsblk_add_resource(struct nd_region *nd_region, |
| @@ -1770,16 +1860,58 @@ static struct device *nd_namespace_blk_create(struct nd_region *nd_region) · 45 unmodified lines | ||
| 1770 | 1860 | return &nsblk->common.dev; |
| 1771 | 1861 | } |
| 1772 | 1862 | |
| 1773 | −void nd_region_create_blk_seed(struct nd_region *nd_region) | |
| 1863 | +static struct device *nd_namespace_pmem_create(struct nd_region *nd_region) | |
| 1864 | +{ | |
| 1865 | + struct nd_namespace_pmem *nspm; | |
| 1866 | + struct resource *res; | |
| 1867 | + struct device *dev; | |
| 1868 | + | |
| 1869 | + if (!is_nd_pmem(&nd_region->dev)) | |
| 1870 | + return NULL; | |
| 1871 | + | |
| 1872 | + nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); | |
| 1873 | + if (!nspm) | |
| 1874 | + return NULL; | |
| 1875 | + | |
| 1876 | + dev = &nspm->nsio.common.dev; | |
| 1877 | + dev->type = &namespace_pmem_device_type; | |
| 1878 | + dev->parent = &nd_region->dev; | |
| 1879 | + res = &nspm->nsio.res; | |
| 1880 | + res->name = dev_name(&nd_region->dev); | |
| 1881 | + res->flags = IORESOURCE_MEM; | |
| 1882 | + | |
| 1883 | + nspm->id = ida_simple_get(&nd_region->ns_ida, 0, 0, GFP_KERNEL); | |
| 1884 | + if (nspm->id < 0) { | |
| 1885 | + kfree(nspm); | |
| 1886 | + return NULL; | |
| 1887 | + } | |
| 1888 | + dev_set_name(dev, "namespace%d.%d", nd_region->id, nspm->id); | |
| 1889 | + dev->parent = &nd_region->dev; | |
| 1890 | + dev->groups = nd_namespace_attribute_groups; | |
| 1891 | + nd_namespace_pmem_set_resource(nd_region, nspm, 0); | |
| 1892 | + | |
| 1893 | + return dev; | |
| 1894 | +} | |
| 1895 | + | |
| 1896 | +void nd_region_create_ns_seed(struct nd_region *nd_region) | |
| 1774 | 1897 | { |
| 1775 | 1898 | WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev)); |
| 1776 | − nd_region->ns_seed = nd_namespace_blk_create(nd_region); | |
| 1899 | + | |
| 1900 | + if (nd_region_to_nstype(nd_region) == ND_DEVICE_NAMESPACE_IO) | |
| 1901 | + return; | |
| 1902 | + | |
| 1903 | + if (is_nd_blk(&nd_region->dev)) | |
| 1904 | + nd_region->ns_seed = nd_namespace_blk_create(nd_region); | |
| 1905 | + else | |
| 1906 | + nd_region->ns_seed = nd_namespace_pmem_create(nd_region); | |
| 1907 | + | |
| 1777 | 1908 | /* |
| 1778 | 1909 | * Seed creation failures are not fatal, provisioning is simply |
| 1779 | 1910 | * disabled until memory becomes available |
| 1780 | 1911 | */ |
| 1781 | 1912 | if (!nd_region->ns_seed) |
| 1782 | − dev_err(&nd_region->dev, "failed to create blk namespace\n"); | |
| 1913 | + dev_err(&nd_region->dev, "failed to create %s namespace\n", | |
| 1914 | + is_nd_blk(&nd_region->dev) ? "blk" : "pmem"); | |
| 1783 | 1915 | else |
| 1784 | 1916 | nd_device_register(nd_region->ns_seed); |
| 1785 | 1917 | } |
| @@ -1820,43 +1952,137 @@ void nd_region_create_btt_seed(struct nd_region *nd_region) · 34 unmodified lines | ||
| 1820 | 1952 | dev_err(&nd_region->dev, "failed to create btt namespace\n"); |
| 1821 | 1953 | } |
| 1822 | 1954 | |
| 1823 | −static struct device **create_namespace_blk(struct nd_region *nd_region) | |
| 1955 | +static int add_namespace_resource(struct nd_region *nd_region, | |
| 1956 | + struct nd_namespace_label *nd_label, struct device **devs, | |
| 1957 | + int count) | |
| 1824 | 1958 | { |
| 1825 | 1959 | struct nd_mapping *nd_mapping = &nd_region->mapping[0]; |
| 1826 | − struct nd_namespace_label *nd_label; | |
| 1827 | − struct device *dev, **devs = NULL; | |
| 1960 | + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); | |
| 1961 | + int i; | |
| 1962 | + | |
| 1963 | + for (i = 0; i < count; i++) { | |
| 1964 | + u8 *uuid = namespace_to_uuid(devs[i]); | |
| 1965 | + struct resource *res; | |
| 1966 | + | |
| 1967 | + if (IS_ERR_OR_NULL(uuid)) { | |
| 1968 | + WARN_ON(1); | |
| 1969 | + continue; | |
| 1970 | + } | |
| 1971 | + | |
| 1972 | + if (memcmp(uuid, nd_label->uuid, NSLABEL_UUID_LEN) != 0) | |
| 1973 | + continue; | |
| 1974 | + if (is_namespace_blk(devs[i])) { | |
| 1975 | + res = nsblk_add_resource(nd_region, ndd, | |
| 1976 | + to_nd_namespace_blk(devs[i]), | |
| 1977 | + __le64_to_cpu(nd_label->dpa)); | |
| 1978 | + if (!res) | |
| 1979 | + return -ENXIO; | |
| 1980 | + nd_dbg_dpa(nd_region, ndd, res, "%d assign\n", count); | |
| 1981 | + } else { | |
| 1982 | + dev_err(&nd_region->dev, | |
| 1983 | + "error: conflicting extents for uuid: %pUb\n", | |
| 1984 | + nd_label->uuid); | |
| 1985 | + return -ENXIO; | |
| 1986 | + } | |
| 1987 | + break; | |
| 1988 | + } | |
| 1989 | + | |
| 1990 | + return i; | |
| 1991 | +} | |
| 1992 | + | |
| 1993 | +struct device *create_namespace_blk(struct nd_region *nd_region, | |
| 1994 | + struct nd_namespace_label *nd_label, int count) | |
| 1995 | +{ | |
| 1996 | + | |
| 1997 | + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; | |
| 1998 | + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); | |
| 1828 | 1999 | struct nd_namespace_blk *nsblk; |
| 1829 | − struct nvdimm_drvdata *ndd; | |
| 1830 | − int i, l, count = 0; | |
| 2000 | + char *name[NSLABEL_NAME_LEN]; | |
| 2001 | + struct device *dev = NULL; | |
| 1831 | 2002 | struct resource *res; |
| 1832 | 2003 | |
| 1833 | − if (nd_region->ndr_mappings == 0) | |
| 1834 | − return NULL; | |
| 2004 | + nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL); | |
| 2005 | + if (!nsblk) | |
| 2006 | + return ERR_PTR(-ENOMEM); | |
| 2007 | + dev = &nsblk->common.dev; | |
| 2008 | + dev->type = &namespace_blk_device_type; | |
| 2009 | + dev->parent = &nd_region->dev; | |
| 2010 | + nsblk->id = -1; | |
| 2011 | + nsblk->lbasize = __le64_to_cpu(nd_label->lbasize); | |
| 2012 | + nsblk->uuid = kmemdup(nd_label->uuid, NSLABEL_UUID_LEN, | |
| 2013 | + GFP_KERNEL); | |
| 2014 | + if (!nsblk->uuid) | |
| 2015 | + goto blk_err; | |
| 2016 | + memcpy(name, nd_label->name, NSLABEL_NAME_LEN); | |
| 2017 | + if (name[0]) | |
| 2018 | + nsblk->alt_name = kmemdup(name, NSLABEL_NAME_LEN, | |
| 2019 | + GFP_KERNEL); | |
| 2020 | + res = nsblk_add_resource(nd_region, ndd, nsblk, | |
| 2021 | + __le64_to_cpu(nd_label->dpa)); | |
| 2022 | + if (!res) | |
| 2023 | + goto blk_err; | |
| 2024 | + nd_dbg_dpa(nd_region, ndd, res, "%d: assign\n", count); | |
| 2025 | + return dev; | |
| 2026 | + blk_err: | |
| 2027 | + namespace_blk_release(dev); | |
| 2028 | + return ERR_PTR(-ENXIO); | |
| 2029 | +} | |
| 2030 | + | |
| 2031 | +static int cmp_dpa(const void *a, const void *b) | |
| 2032 | +{ | |
| 2033 | + const struct device *dev_a = *(const struct device **) a; | |
| 2034 | + const struct device *dev_b = *(const struct device **) b; | |
| 2035 | + struct nd_namespace_blk *nsblk_a, *nsblk_b; | |
| 2036 | + struct nd_namespace_pmem *nspm_a, *nspm_b; | |
| 2037 | + | |
| 2038 | + if (is_namespace_io(dev_a)) | |
| 2039 | + return 0; | |
| 2040 | + | |
| 2041 | + if (is_namespace_blk(dev_a)) { | |
| 2042 | + nsblk_a = to_nd_namespace_blk(dev_a); | |
| 2043 | + nsblk_b = to_nd_namespace_blk(dev_b); | |
| 2044 | + | |
| 2045 | + return memcmp(&nsblk_a->res[0]->start, &nsblk_b->res[0]->start, | |
| 2046 | + sizeof(resource_size_t)); | |
| 2047 | + } | |
| 2048 | + | |
| 2049 | + nspm_a = to_nd_namespace_pmem(dev_a); | |
| 2050 | + nspm_b = to_nd_namespace_pmem(dev_b); | |
| 2051 | + | |
| 2052 | + return memcmp(&nspm_a->nsio.res.start, &nspm_b->nsio.res.start, | |
| 2053 | + sizeof(resource_size_t)); | |
| 2054 | +} | |
| 1835 | 2055 | |
| 1836 | − ndd = to_ndd(nd_mapping); | |
| 1837 | − for_each_label(l, nd_label, nd_mapping->labels) { | |
| 1838 | − u32 flags = __le32_to_cpu(nd_label->flags); | |
| 1839 | − char *name[NSLABEL_NAME_LEN]; | |
| 2056 | +static struct device **scan_labels(struct nd_region *nd_region) | |
| 2057 | +{ | |
| 2058 | + int i, count = 0; | |
| 2059 | + struct device *dev, **devs = NULL; | |
| 2060 | + struct nd_label_ent *label_ent, *e; | |
| 2061 | + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; | |
| 2062 | + resource_size_t map_end = nd_mapping->start + nd_mapping->size - 1; | |
| 2063 | + | |
| 2064 | + /* "safe" because create_namespace_pmem() might list_move() label_ent */ | |
| 2065 | + list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { | |
| 2066 | + struct nd_namespace_label *nd_label = label_ent->label; | |
| 1840 | 2067 | struct device **__devs; |
| 2068 | + u32 flags; | |
| 1841 | 2069 | |
| 1842 | − if (flags & NSLABEL_FLAG_LOCAL) | |
| 1843 | − /* pass */; | |
| 2070 | + if (!nd_label) | |
| 2071 | + continue; | |
| 2072 | + flags = __le32_to_cpu(nd_label->flags); | |
| 2073 | + if (is_nd_blk(&nd_region->dev) | |
| 2074 | + == !!(flags & NSLABEL_FLAG_LOCAL)) | |
| 2075 | + /* pass, region matches label type */; | |
| 1844 | 2076 | else |
| 1845 | 2077 | continue; |
| 1846 | 2078 | |
| 1847 | − for (i = 0; i < count; i++) { | |
| 1848 | − nsblk = to_nd_namespace_blk(devs[i]); | |
| 1849 | − if (memcmp(nsblk->uuid, nd_label->uuid, | |
| 1850 | − NSLABEL_UUID_LEN) == 0) { | |
| 1851 | − res = nsblk_add_resource(nd_region, ndd, nsblk, | |
| 1852 | − __le64_to_cpu(nd_label->dpa)); | |
| 1853 | − if (!res) | |
| 1854 | − goto err; | |
| 1855 | − nd_dbg_dpa(nd_region, ndd, res, "%s assign\n", | |
| 1856 | − dev_name(&nsblk->common.dev)); | |
| 1857 | − break; | |
| 1858 | − } | |
| 1859 | − } | |
| 2079 | + /* skip labels that describe extents outside of the region */ | |
| 2080 | + if (nd_label->dpa < nd_mapping->start || nd_label->dpa > map_end) | |
| 2081 | + continue; | |
| 2082 | + | |
| 2083 | + i = add_namespace_resource(nd_region, nd_label, devs, count); | |
| 2084 | + if (i < 0) | |
| 2085 | + goto err; | |
| 1860 | 2086 | if (i < count) |
| 1861 | 2087 | continue; |
| 1862 | 2088 | __devs = kcalloc(count + 2, sizeof(dev), GFP_KERNEL); |
| @@ -1866,67 +2092,126 @@ static struct device **create_namespace_blk(struct nd_region *nd_region) · 3 unmodified lines | ||
| 1866 | 2092 | kfree(devs); |
| 1867 | 2093 | devs = __devs; |
| 1868 | 2094 | |
| 1869 | − nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL); | |
| 1870 | − if (!nsblk) | |
| 1871 | − goto err; | |
| 1872 | − dev = &nsblk->common.dev; | |
| 1873 | − dev->type = &namespace_blk_device_type; | |
| 1874 | − dev->parent = &nd_region->dev; | |
| 1875 | − dev_set_name(dev, "namespace%d.%d", nd_region->id, count); | |
| 1876 | − devs[count++] = dev; | |
| 1877 | − nsblk->id = -1; | |
| 1878 | − nsblk->lbasize = __le64_to_cpu(nd_label->lbasize); | |
| 1879 | − nsblk->uuid = kmemdup(nd_label->uuid, NSLABEL_UUID_LEN, | |
| 1880 | − GFP_KERNEL); | |
| 1881 | − if (!nsblk->uuid) | |
| 1882 | − goto err; | |
| 1883 | − memcpy(name, nd_label->name, NSLABEL_NAME_LEN); | |
| 1884 | − if (name[0]) | |
| 1885 | − nsblk->alt_name = kmemdup(name, NSLABEL_NAME_LEN, | |
| 1886 | − GFP_KERNEL); | |
| 1887 | − res = nsblk_add_resource(nd_region, ndd, nsblk, | |
| 1888 | − __le64_to_cpu(nd_label->dpa)); | |
| 1889 | − if (!res) | |
| 1890 | − goto err; | |
| 1891 | − nd_dbg_dpa(nd_region, ndd, res, "%s assign\n", | |
| 1892 | − dev_name(&nsblk->common.dev)); | |
| 2095 | + if (is_nd_blk(&nd_region->dev)) { | |
| 2096 | + dev = create_namespace_blk(nd_region, nd_label, count); | |
| 2097 | + if (IS_ERR(dev)) | |
| 2098 | + goto err; | |
| 2099 | + devs[count++] = dev; | |
| 2100 | + } else { | |
| 2101 | + dev = create_namespace_pmem(nd_region, nd_label); | |
| 2102 | + if (IS_ERR(dev)) { | |
| 2103 | + switch (PTR_ERR(dev)) { | |
| 2104 | + case -EAGAIN: | |
| 2105 | + /* skip invalid labels */ | |
| 2106 | + continue; | |
| 2107 | + case -ENODEV: | |
| 2108 | + /* fallthrough to seed creation */ | |
| 2109 | + break; | |
| 2110 | + default: | |
| 2111 | + goto err; | |
| 2112 | + } | |
| 2113 | + } else | |
| 2114 | + devs[count++] = dev; | |
| 2115 | + } | |
| 1893 | 2116 | } |
| 1894 | 2117 | |
| 1895 | − dev_dbg(&nd_region->dev, "%s: discovered %d blk namespace%s\n", | |
| 1896 | − __func__, count, count == 1 ? "" : "s"); | |
| 2118 | + dev_dbg(&nd_region->dev, "%s: discovered %d %s namespace%s\n", | |
| 2119 | + __func__, count, is_nd_blk(&nd_region->dev) | |
| 2120 | + ? "blk" : "pmem", count == 1 ? "" : "s"); | |
| 1897 | 2121 | |
| 1898 | 2122 | if (count == 0) { |
| 1899 | 2123 | /* Publish a zero-sized namespace for userspace to configure. */ |
| 1900 | − for (i = 0; i < nd_region->ndr_mappings; i++) { | |
| 1901 | − struct nd_mapping *nd_mapping = &nd_region->mapping[i]; | |
| 1902 | − | |
| 1903 | − kfree(nd_mapping->labels); | |
| 1904 | − nd_mapping->labels = NULL; | |
| 1905 | − } | |
| 2124 | + nd_mapping_free_labels(nd_mapping); | |
| 1906 | 2125 | |
| 1907 | 2126 | devs = kcalloc(2, sizeof(dev), GFP_KERNEL); |
| 1908 | 2127 | if (!devs) |
| 1909 | 2128 | goto err; |
| 1910 | − nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL); | |
| 1911 | − if (!nsblk) | |
| 1912 | − goto err; | |
| 1913 | − dev = &nsblk->common.dev; | |
| 1914 | − dev->type = &namespace_blk_device_type; | |
| 2129 | + if (is_nd_blk(&nd_region->dev)) { | |
| 2130 | + struct nd_namespace_blk *nsblk; | |
| 2131 | + | |
| 2132 | + nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL); | |
| 2133 | + if (!nsblk) | |
| 2134 | + goto err; | |
| 2135 | + dev = &nsblk->common.dev; | |
| 2136 | + dev->type = &namespace_blk_device_type; | |
| 2137 | + } else { | |
| 2138 | + struct nd_namespace_pmem *nspm; | |
| 2139 | + | |
| 2140 | + nspm = kzalloc(sizeof(*nspm), GFP_KERNEL); | |
| 2141 | + if (!nspm) | |
| 2142 | + goto err; | |
| 2143 | + dev = &nspm->nsio.common.dev; | |
| 2144 | + dev->type = &namespace_pmem_device_type; | |
| 2145 | + nd_namespace_pmem_set_resource(nd_region, nspm, 0); | |
| 2146 | + } | |
| 1915 | 2147 | dev->parent = &nd_region->dev; |
| 1916 | 2148 | devs[count++] = dev; |
| 2149 | + } else if (is_nd_pmem(&nd_region->dev)) { | |
| 2150 | + /* clean unselected labels */ | |
| 2151 | + for (i = 0; i < nd_region->ndr_mappings; i++) { | |
| 2152 | + struct list_head *l, *e; | |
| 2153 | + LIST_HEAD(list); | |
| 2154 | + int j; | |
| 2155 | + | |
| 2156 | + nd_mapping = &nd_region->mapping[i]; | |
| 2157 | + if (list_empty(&nd_mapping->labels)) { | |
| 2158 | + WARN_ON(1); | |
| 2159 | + continue; | |
| 2160 | + } | |
| 2161 | + | |
| 2162 | + j = count; | |
| 2163 | + list_for_each_safe(l, e, &nd_mapping->labels) { | |
| 2164 | + if (!j--) | |
| 2165 | + break; | |
| 2166 | + list_move_tail(l, &list); | |
| 2167 | + } | |
| 2168 | + nd_mapping_free_labels(nd_mapping); | |
| 2169 | + list_splice_init(&list, &nd_mapping->labels); | |
| 2170 | + } | |
| 1917 | 2171 | } |
| 1918 | 2172 | |
| 2173 | + if (count > 1) | |
| 2174 | + sort(devs, count, sizeof(struct device *), cmp_dpa, NULL); | |
| 2175 | + | |
| 1919 | 2176 | return devs; |
| 1920 | 2177 | |
| 1921 | −err: | |
| 1922 | − for (i = 0; i < count; i++) { | |
| 1923 | − nsblk = to_nd_namespace_blk(devs[i]); | |
| 1924 | − namespace_blk_release(&nsblk->common.dev); | |
| 1925 | − } | |
| 2178 | + err: | |
| 2179 | + for (i = 0; devs[i]; i++) | |
| 2180 | + if (is_nd_blk(&nd_region->dev)) | |
| 2181 | + namespace_blk_release(devs[i]); | |
| 2182 | + else | |
| 2183 | + namespace_pmem_release(devs[i]); | |
| 1926 | 2184 | kfree(devs); |
| 1927 | 2185 | return NULL; |
| 1928 | 2186 | } |
| 1929 | 2187 | |
| 2188 | +static struct device **create_namespaces(struct nd_region *nd_region) | |
| 2189 | +{ | |
| 2190 | + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; | |
| 2191 | + struct device **devs; | |
| 2192 | + int i; | |
| 2193 | + | |
| 2194 | + if (nd_region->ndr_mappings == 0) | |
| 2195 | + return NULL; | |
| 2196 | + | |
| 2197 | + /* lock down all mappings while we scan labels */ | |
| 2198 | + for (i = 0; i < nd_region->ndr_mappings; i++) { | |
| 2199 | + nd_mapping = &nd_region->mapping[i]; | |
| 2200 | + mutex_lock_nested(&nd_mapping->lock, i); | |
| 2201 | + } | |
| 2202 | + | |
| 2203 | + devs = scan_labels(nd_region); | |
| 2204 | + | |
| 2205 | + for (i = 0; i < nd_region->ndr_mappings; i++) { | |
| 2206 | + int reverse = nd_region->ndr_mappings - 1 - i; | |
| 2207 | + | |
| 2208 | + nd_mapping = &nd_region->mapping[reverse]; | |
| 2209 | + mutex_unlock(&nd_mapping->lock); | |
| 2210 | + } | |
| 2211 | + | |
| 2212 | + return devs; | |
| 2213 | +} | |
| 2214 | + | |
| 1930 | 2215 | static int init_active_labels(struct nd_region *nd_region) |
| 1931 | 2216 | { |
| 1932 | 2217 | int i; |
| @@ -1935,6 +2220,7 @@ static int init_active_labels(struct nd_region *nd_region) · 2 unmodified lines | ||
| 1935 | 2220 | struct nd_mapping *nd_mapping = &nd_region->mapping[i]; |
| 1936 | 2221 | struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); |
| 1937 | 2222 | struct nvdimm *nvdimm = nd_mapping->nvdimm; |
| 2223 | + struct nd_label_ent *label_ent; | |
| 1938 | 2224 | int count, j; |
| 1939 | 2225 | |
| 1940 | 2226 | /* |
| @@ -1956,16 +2242,27 @@ static int init_active_labels(struct nd_region *nd_region) · 15 unmodified lines | ||
| 1956 | 2242 | dev_dbg(ndd->dev, "%s: %d\n", __func__, count); |
| 1957 | 2243 | if (!count) |
| 1958 | 2244 | continue; |
| 1959 | − nd_mapping->labels = kcalloc(count + 1, sizeof(void *), | |
| 1960 | − GFP_KERNEL); | |
| 1961 | − if (!nd_mapping->labels) | |
| 1962 | − return -ENOMEM; | |
| 1963 | 2245 | for (j = 0; j < count; j++) { |
| 1964 | 2246 | struct nd_namespace_label *label; |
| 1965 | 2247 | |
| 2248 | + label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL); | |
| 2249 | + if (!label_ent) | |
| 2250 | + break; | |
| 1966 | 2251 | label = nd_label_active(ndd, j); |
| 1967 | − nd_mapping->labels[j] = label; | |
| 2252 | + label_ent->label = label; | |
| 2253 | + | |
| 2254 | + mutex_lock(&nd_mapping->lock); | |
| 2255 | + list_add_tail(&label_ent->list, &nd_mapping->labels); | |
| 2256 | + mutex_unlock(&nd_mapping->lock); | |
| 1968 | 2257 | } |
| 2258 | + | |
| 2259 | + if (j >= count) | |
| 2260 | + continue; | |
| 2261 | + | |
| 2262 | + mutex_lock(&nd_mapping->lock); | |
| 2263 | + nd_mapping_free_labels(nd_mapping); | |
| 2264 | + mutex_unlock(&nd_mapping->lock); | |
| 2265 | + return -ENOMEM; | |
| 1969 | 2266 | } |
| 1970 | 2267 | |
| 1971 | 2268 | return 0; |
| @@ -1990,10 +2287,8 @@ int nd_region_register_namespaces(struct nd_region *nd_region, int *err) · 18 unmodified lines | ||
| 1990 | 2287 | devs = create_namespace_io(nd_region); |
| 1991 | 2288 | break; |
| 1992 | 2289 | case ND_DEVICE_NAMESPACE_PMEM: |
| 1993 | − devs = create_namespace_pmem(nd_region); | |
| 1994 | − break; | |
| 1995 | 2290 | case ND_DEVICE_NAMESPACE_BLK: |
| 1996 | − devs = create_namespace_blk(nd_region); | |
| 2291 | + devs = create_namespaces(nd_region); | |
| 1997 | 2292 | break; |
| 1998 | 2293 | default: |
| 1999 | 2294 | break; |
| @@ -2014,6 +2309,13 @@ int nd_region_register_namespaces(struct nd_region *nd_region, int *err) · 14 unmodified lines | ||
| 2014 | 2309 | id = ida_simple_get(&nd_region->ns_ida, 0, 0, |
| 2015 | 2310 | GFP_KERNEL); |
| 2016 | 2311 | nsblk->id = id; |
| 2312 | + } else if (type == ND_DEVICE_NAMESPACE_PMEM) { | |
| 2313 | + struct nd_namespace_pmem *nspm; | |
| 2314 | + | |
| 2315 | + nspm = to_nd_namespace_pmem(dev); | |
| 2316 | + id = ida_simple_get(&nd_region->ns_ida, 0, 0, | |
| 2317 | + GFP_KERNEL); | |
| 2318 | + nspm->id = id; | |
| 2017 | 2319 | } else |
| 2018 | 2320 | id = i; |
| 2019 | 2321 | |
| @@ -44,6 +44,23 @@ struct nvdimm { · 43 unmodified lines | ||
| 44 | 44 | struct resource *flush_wpq; |
| 45 | 45 | }; |
| 46 | 46 | |
| 47 | +/** | |
| 48 | + * struct blk_alloc_info - tracking info for BLK dpa scanning | |
| 49 | + * @nd_mapping: blk region mapping boundaries | |
| 50 | + * @available: decremented in alias_dpa_busy as aliased PMEM is scanned | |
| 51 | + * @busy: decremented in blk_dpa_busy to account for ranges already | |
| 52 | + * handled by alias_dpa_busy | |
| 53 | + * @res: alias_dpa_busy interprets this a free space range that needs to | |
| 54 | + * be truncated to the valid BLK allocation starting DPA, blk_dpa_busy | |
| 55 | + * treats it as a busy range that needs the aliased PMEM ranges | |
| 56 | + * truncated. | |
| 57 | + */ | |
| 58 | +struct blk_alloc_info { | |
| 59 | + struct nd_mapping *nd_mapping; | |
| 60 | + resource_size_t available, busy; | |
| 61 | + struct resource *res; | |
| 62 | +}; | |
| 63 | + | |
| 47 | 64 | bool is_nvdimm(struct device *dev); |
| 48 | 65 | bool is_nd_pmem(struct device *dev); |
| 49 | 66 | bool is_nd_blk(struct device *dev); |
| @@ -54,7 +71,7 @@ void nvdimm_devs_exit(void); · 4 unmodified lines | ||
| 54 | 71 | void nd_region_devs_exit(void); |
| 55 | 72 | void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev); |
| 56 | 73 | struct nd_region; |
| 57 | −void nd_region_create_blk_seed(struct nd_region *nd_region); | |
| 74 | +void nd_region_create_ns_seed(struct nd_region *nd_region); | |
| 58 | 75 | void nd_region_create_btt_seed(struct nd_region *nd_region); |
| 59 | 76 | void nd_region_create_pfn_seed(struct nd_region *nd_region); |
| 60 | 77 | void nd_region_create_dax_seed(struct nd_region *nd_region); |
| @@ -73,13 +90,14 @@ bool nd_is_uuid_unique(struct device *dev, u8 *uuid); · 12 unmodified lines | ||
| 73 | 90 | struct nd_region; |
| 74 | 91 | struct nvdimm_drvdata; |
| 75 | 92 | struct nd_mapping; |
| 93 | +void nd_mapping_free_labels(struct nd_mapping *nd_mapping); | |
| 76 | 94 | resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, |
| 77 | 95 | struct nd_mapping *nd_mapping, resource_size_t *overlap); |
| 78 | −resource_size_t nd_blk_available_dpa(struct nd_mapping *nd_mapping); | |
| 96 | +resource_size_t nd_blk_available_dpa(struct nd_region *nd_region); | |
| 79 | 97 | resource_size_t nd_region_available_dpa(struct nd_region *nd_region); |
| 80 | 98 | resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd, |
| 81 | 99 | struct nd_label_id *label_id); |
| 82 | −struct nd_mapping; | |
| 100 | +int alias_dpa_busy(struct device *dev, void *data); | |
| 83 | 101 | struct resource *nsblk_add_resource(struct nd_region *nd_region, |
| 84 | 102 | struct nvdimm_drvdata *ndd, struct nd_namespace_blk *nsblk, |
| 85 | 103 | resource_size_t start); |
| @@ -101,9 +101,6 @@ static inline struct nd_namespace_index *to_next_namespace_index( · 100 unmodified lines | ||
| 101 | 101 | (unsigned long long) (res ? resource_size(res) : 0), \ |
| 102 | 102 | (unsigned long long) (res ? res->start : 0), ##arg) |
| 103 | 103 | |
| 104 | −#define for_each_label(l, label, labels) \ | |
| 105 | − for (l = 0; (label = labels ? labels[l] : NULL); l++) | |
| 106 | − | |
| 107 | 104 | #define for_each_dpa_resource(ndd, res) \ |
| 108 | 105 | for (res = (ndd)->dpa.child; res; res = res->sibling) |
| 109 | 106 | |
| @@ -116,6 +113,31 @@ struct nd_percpu_lane { · 6 unmodified lines | ||
| 116 | 113 | spinlock_t lock; |
| 117 | 114 | }; |
| 118 | 115 | |
| 116 | +struct nd_label_ent { | |
| 117 | + struct list_head list; | |
| 118 | + struct nd_namespace_label *label; | |
| 119 | +}; | |
| 120 | + | |
| 121 | +enum nd_mapping_lock_class { | |
| 122 | + ND_MAPPING_CLASS0, | |
| 123 | + ND_MAPPING_UUID_SCAN, | |
| 124 | +}; | |
| 125 | + | |
| 126 | +struct nd_mapping { | |
| 127 | + struct nvdimm *nvdimm; | |
| 128 | + u64 start; | |
| 129 | + u64 size; | |
| 130 | + struct list_head labels; | |
| 131 | + struct mutex lock; | |
| 132 | + /* | |
| 133 | + * @ndd is for private use at region enable / disable time for | |
| 134 | + * get_ndd() + put_ndd(), all other nd_mapping to ndd | |
| 135 | + * conversions use to_ndd() which respects enabled state of the | |
| 136 | + * nvdimm. | |
| 137 | + */ | |
| 138 | + struct nvdimm_drvdata *ndd; | |
| 139 | +}; | |
| 140 | + | |
| 119 | 141 | struct nd_region { |
| 120 | 142 | struct device dev; |
| 121 | 143 | struct ida ns_ida; |
| @@ -209,6 +231,7 @@ void nvdimm_exit(void); · 87 unmodified lines | ||
| 209 | 231 | void nd_region_exit(void); |
| 210 | 232 | struct nvdimm; |
| 211 | 233 | struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping); |
| 234 | +int nvdimm_check_config_data(struct device *dev); | |
| 212 | 235 | int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd); |
| 213 | 236 | int nvdimm_init_config_data(struct nvdimm_drvdata *ndd); |
| 214 | 237 | int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset, |
| @@ -66,13 +66,32 @@ static void pmem_clear_poison(struct pmem_device *pmem, phys_addr_t offset, · 65 unmodified lines | ||
| 66 | 66 | invalidate_pmem(pmem->virt_addr + offset, len); |
| 67 | 67 | } |
| 68 | 68 | |
| 69 | +static void write_pmem(void *pmem_addr, struct page *page, | |
| 70 | + unsigned int off, unsigned int len) | |
| 71 | +{ | |
| 72 | + void *mem = kmap_atomic(page); | |
| 73 | + | |
| 74 | + memcpy_to_pmem(pmem_addr, mem + off, len); | |
| 75 | + kunmap_atomic(mem); | |
| 76 | +} | |
| 77 | + | |
| 78 | +static int read_pmem(struct page *page, unsigned int off, | |
| 79 | + void *pmem_addr, unsigned int len) | |
| 80 | +{ | |
| 81 | + int rc; | |
| 82 | + void *mem = kmap_atomic(page); | |
| 83 | + | |
| 84 | + rc = memcpy_from_pmem(mem + off, pmem_addr, len); | |
| 85 | + kunmap_atomic(mem); | |
| 86 | + return rc; | |
| 87 | +} | |
| 88 | + | |
| 69 | 89 | static int pmem_do_bvec(struct pmem_device *pmem, struct page *page, |
| 70 | 90 | unsigned int len, unsigned int off, bool is_write, |
| 71 | 91 | sector_t sector) |
| 72 | 92 | { |
| 73 | 93 | int rc = 0; |
| 74 | 94 | bool bad_pmem = false; |
| 75 | − void *mem = kmap_atomic(page); | |
| 76 | 95 | phys_addr_t pmem_off = sector * 512 + pmem->data_offset; |
| 77 | 96 | void *pmem_addr = pmem->virt_addr + pmem_off; |
| 78 | 97 | |
| @@ -83,7 +102,7 @@ static int pmem_do_bvec(struct pmem_device *pmem, struct page *page, · 4 unmodified lines | ||
| 83 | 102 | if (unlikely(bad_pmem)) |
| 84 | 103 | rc = -EIO; |
| 85 | 104 | else { |
| 86 | − rc = memcpy_from_pmem(mem + off, pmem_addr, len); | |
| 105 | + rc = read_pmem(page, off, pmem_addr, len); | |
| 87 | 106 | flush_dcache_page(page); |
| 88 | 107 | } |
| 89 | 108 | } else { |
| @@ -102,14 +121,13 @@ static int pmem_do_bvec(struct pmem_device *pmem, struct page *page, · 12 unmodified lines | ||
| 102 | 121 | * after clear poison. |
| 103 | 122 | */ |
| 104 | 123 | flush_dcache_page(page); |
| 105 | − memcpy_to_pmem(pmem_addr, mem + off, len); | |
| 124 | + write_pmem(pmem_addr, page, off, len); | |
| 106 | 125 | if (unlikely(bad_pmem)) { |
| 107 | 126 | pmem_clear_poison(pmem, pmem_off, len); |
| 108 | − memcpy_to_pmem(pmem_addr, mem + off, len); | |
| 127 | + write_pmem(pmem_addr, page, off, len); | |
| 109 | 128 | } |
| 110 | 129 | } |
| 111 | 130 | |
| 112 | − kunmap_atomic(mem); | |
| 113 | 131 | return rc; |
| 114 | 132 | } |
| 115 | 133 | |
| @@ -313,9 +313,8 @@ resource_size_t nd_region_available_dpa(struct nd_region *nd_region) · 312 unmodified lines | ||
| 313 | 313 | blk_max_overlap = overlap; |
| 314 | 314 | goto retry; |
| 315 | 315 | } |
| 316 | − } else if (is_nd_blk(&nd_region->dev)) { | |
| 317 | − available += nd_blk_available_dpa(nd_mapping); | |
| 318 | − } | |
| 316 | + } else if (is_nd_blk(&nd_region->dev)) | |
| 317 | + available += nd_blk_available_dpa(nd_region); | |
| 319 | 318 | } |
| 320 | 319 | |
| 321 | 320 | return available; |
| @@ -506,6 +505,17 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region) · 184 unmodified lines | ||
| 506 | 505 | return 0; |
| 507 | 506 | } |
| 508 | 507 | |
| 508 | +void nd_mapping_free_labels(struct nd_mapping *nd_mapping) | |
| 509 | +{ | |
| 510 | + struct nd_label_ent *label_ent, *e; | |
| 511 | + | |
| 512 | + WARN_ON(!mutex_is_locked(&nd_mapping->lock)); | |
| 513 | + list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { | |
| 514 | + list_del(&label_ent->list); | |
| 515 | + kfree(label_ent); | |
| 516 | + } | |
| 517 | +} | |
| 518 | + | |
| 509 | 519 | /* |
| 510 | 520 | * Upon successful probe/remove, take/release a reference on the |
| 511 | 521 | * associated interleave set (if present), and plant new btt + namespace |
| @@ -526,8 +536,10 @@ static void nd_region_notify_driver_action(struct nvdimm_bus *nvdimm_bus, · 14 unmodified lines | ||
| 526 | 536 | struct nvdimm_drvdata *ndd = nd_mapping->ndd; |
| 527 | 537 | struct nvdimm *nvdimm = nd_mapping->nvdimm; |
| 528 | 538 | |
| 529 | − kfree(nd_mapping->labels); | |
| 530 | − nd_mapping->labels = NULL; | |
| 539 | + mutex_lock(&nd_mapping->lock); | |
| 540 | + nd_mapping_free_labels(nd_mapping); | |
| 541 | + mutex_unlock(&nd_mapping->lock); | |
| 542 | + | |
| 531 | 543 | put_ndd(ndd); |
| 532 | 544 | nd_mapping->ndd = NULL; |
| 533 | 545 | if (ndd) |
| @@ -537,11 +549,12 @@ static void nd_region_notify_driver_action(struct nvdimm_bus *nvdimm_bus, · 3 unmodified lines | ||
| 537 | 549 | if (is_nd_pmem(dev)) |
| 538 | 550 | return; |
| 539 | 551 | } |
| 540 | − if (dev->parent && is_nd_blk(dev->parent) && probe) { | |
| 552 | + if (dev->parent && (is_nd_blk(dev->parent) || is_nd_pmem(dev->parent)) | |
| 553 | + && probe) { | |
| 541 | 554 | nd_region = to_nd_region(dev->parent); |
| 542 | 555 | nvdimm_bus_lock(dev); |
| 543 | 556 | if (nd_region->ns_seed == dev) |
| 544 | − nd_region_create_blk_seed(nd_region); | |
| 557 | + nd_region_create_ns_seed(nd_region); | |
| 545 | 558 | nvdimm_bus_unlock(dev); |
| 546 | 559 | } |
| 547 | 560 | if (is_nd_btt(dev) && probe) { |
| @@ -551,23 +564,30 @@ static void nd_region_notify_driver_action(struct nvdimm_bus *nvdimm_bus, · 3 unmodified lines | ||
| 551 | 564 | nvdimm_bus_lock(dev); |
| 552 | 565 | if (nd_region->btt_seed == dev) |
| 553 | 566 | nd_region_create_btt_seed(nd_region); |
| 554 | − if (nd_region->ns_seed == &nd_btt->ndns->dev && | |
| 555 | − is_nd_blk(dev->parent)) | |
| 556 | − nd_region_create_blk_seed(nd_region); | |
| 567 | + if (nd_region->ns_seed == &nd_btt->ndns->dev) | |
| 568 | + nd_region_create_ns_seed(nd_region); | |
| 557 | 569 | nvdimm_bus_unlock(dev); |
| 558 | 570 | } |
| 559 | 571 | if (is_nd_pfn(dev) && probe) { |
| 572 | + struct nd_pfn *nd_pfn = to_nd_pfn(dev); | |
| 573 | + | |
| 560 | 574 | nd_region = to_nd_region(dev->parent); |
| 561 | 575 | nvdimm_bus_lock(dev); |
| 562 | 576 | if (nd_region->pfn_seed == dev) |
| 563 | 577 | nd_region_create_pfn_seed(nd_region); |
| 578 | + if (nd_region->ns_seed == &nd_pfn->ndns->dev) | |
| 579 | + nd_region_create_ns_seed(nd_region); | |
| 564 | 580 | nvdimm_bus_unlock(dev); |
| 565 | 581 | } |
| 566 | 582 | if (is_nd_dax(dev) && probe) { |
| 583 | + struct nd_dax *nd_dax = to_nd_dax(dev); | |
| 584 | + | |
| 567 | 585 | nd_region = to_nd_region(dev->parent); |
| 568 | 586 | nvdimm_bus_lock(dev); |
| 569 | 587 | if (nd_region->dax_seed == dev) |
| 570 | 588 | nd_region_create_dax_seed(nd_region); |
| 589 | + if (nd_region->ns_seed == &nd_dax->nd_pfn.ndns->dev) | |
| 590 | + nd_region_create_ns_seed(nd_region); | |
| 571 | 591 | nvdimm_bus_unlock(dev); |
| 572 | 592 | } |
| 573 | 593 | } |
| @@ -774,10 +794,10 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, · 200 unmodified lines | ||
| 774 | 794 | int ro = 0; |
| 775 | 795 | |
| 776 | 796 | for (i = 0; i < ndr_desc->num_mappings; i++) { |
| 777 | − struct nd_mapping *nd_mapping = &ndr_desc->nd_mapping[i]; | |
| 778 | − struct nvdimm *nvdimm = nd_mapping->nvdimm; | |
| 797 | + struct nd_mapping_desc *mapping = &ndr_desc->mapping[i]; | |
| 798 | + struct nvdimm *nvdimm = mapping->nvdimm; | |
| 779 | 799 | |
| 780 | − if ((nd_mapping->start | nd_mapping->size) % SZ_4K) { | |
| 800 | + if ((mapping->start | mapping->size) % SZ_4K) { | |
| 781 | 801 | dev_err(&nvdimm_bus->dev, "%s: %s mapping%d is not 4K aligned\n", |
| 782 | 802 | caller, dev_name(&nvdimm->dev), i); |
| 783 | 803 | |
| @@ -828,11 +848,15 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, · 44 unmodified lines | ||
| 828 | 848 | ndl->count = 0; |
| 829 | 849 | } |
| 830 | 850 | |
| 831 | − memcpy(nd_region->mapping, ndr_desc->nd_mapping, | |
| 832 | − sizeof(struct nd_mapping) * ndr_desc->num_mappings); | |
| 833 | 851 | for (i = 0; i < ndr_desc->num_mappings; i++) { |
| 834 | − struct nd_mapping *nd_mapping = &ndr_desc->nd_mapping[i]; | |
| 835 | − struct nvdimm *nvdimm = nd_mapping->nvdimm; | |
| 852 | + struct nd_mapping_desc *mapping = &ndr_desc->mapping[i]; | |
| 853 | + struct nvdimm *nvdimm = mapping->nvdimm; | |
| 854 | + | |
| 855 | + nd_region->mapping[i].nvdimm = nvdimm; | |
| 856 | + nd_region->mapping[i].start = mapping->start; | |
| 857 | + nd_region->mapping[i].size = mapping->size; | |
| 858 | + INIT_LIST_HEAD(&nd_region->mapping[i].labels); | |
| 859 | + mutex_init(&nd_region->mapping[i].lock); | |
| 836 | 860 | |
| 837 | 861 | get_device(&nvdimm->dev); |
| 838 | 862 | } |
| @@ -50,23 +50,6 @@ typedef int (*ndctl_fn)(struct nvdimm_bus_descriptor *nd_desc, · 49 unmodified lines | ||
| 50 | 50 | struct nvdimm *nvdimm, unsigned int cmd, void *buf, |
| 51 | 51 | unsigned int buf_len, int *cmd_rc); |
| 52 | 52 | |
| 53 | −struct nd_namespace_label; | |
| 54 | −struct nvdimm_drvdata; | |
| 55 | − | |
| 56 | −struct nd_mapping { | |
| 57 | − struct nvdimm *nvdimm; | |
| 58 | − struct nd_namespace_label **labels; | |
| 59 | − u64 start; | |
| 60 | − u64 size; | |
| 61 | − /* | |
| 62 | − * @ndd is for private use at region enable / disable time for | |
| 63 | − * get_ndd() + put_ndd(), all other nd_mapping to ndd | |
| 64 | − * conversions use to_ndd() which respects enabled state of the | |
| 65 | − * nvdimm. | |
| 66 | − */ | |
| 67 | − struct nvdimm_drvdata *ndd; | |
| 68 | −}; | |
| 69 | − | |
| 70 | 53 | struct nvdimm_bus_descriptor { |
| 71 | 54 | const struct attribute_group **attr_groups; |
| 72 | 55 | unsigned long cmd_mask; |
| @@ -89,9 +72,15 @@ struct nd_interleave_set { · 16 unmodified lines | ||
| 89 | 72 | u64 cookie; |
| 90 | 73 | }; |
| 91 | 74 | |
| 75 | +struct nd_mapping_desc { | |
| 76 | + struct nvdimm *nvdimm; | |
| 77 | + u64 start; | |
| 78 | + u64 size; | |
| 79 | +}; | |
| 80 | + | |
| 92 | 81 | struct nd_region_desc { |
| 93 | 82 | struct resource *res; |
| 94 | − struct nd_mapping *nd_mapping; | |
| 83 | + struct nd_mapping_desc *mapping; | |
| 95 | 84 | u16 num_mappings; |
| 96 | 85 | const struct attribute_group **attr_groups; |
| 97 | 86 | struct nd_interleave_set *nd_set; |
| @@ -129,6 +118,8 @@ static inline struct nd_blk_region_desc *to_blk_region_desc( · 31 unmodified lines | ||
| 129 | 118 | } |
| 130 | 119 | |
| 131 | 120 | int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length); |
| 121 | +void nvdimm_clear_from_poison_list(struct nvdimm_bus *nvdimm_bus, | |
| 122 | + phys_addr_t start, unsigned int len); | |
| 132 | 123 | struct nvdimm_bus *nvdimm_bus_register(struct device *parent, |
| 133 | 124 | struct nvdimm_bus_descriptor *nfit_desc); |
| 134 | 125 | void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus); |
| @@ -139,6 +130,7 @@ struct nd_blk_region *to_nd_blk_region(struct device *dev); · 4 unmodified lines | ||
| 139 | 130 | struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus); |
| 140 | 131 | struct device *to_nvdimm_bus_dev(struct nvdimm_bus *nvdimm_bus); |
| 141 | 132 | const char *nvdimm_name(struct nvdimm *nvdimm); |
| 133 | +struct kobject *nvdimm_kobj(struct nvdimm *nvdimm); | |
| 142 | 134 | unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm); |
| 143 | 135 | void *nvdimm_provider_data(struct nvdimm *nvdimm); |
| 144 | 136 | struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, |
| @@ -77,11 +77,13 @@ struct nd_namespace_io { · 76 unmodified lines | ||
| 77 | 77 | * @nsio: device and system physical address range to drive |
| 78 | 78 | * @alt_name: namespace name supplied in the dimm label |
| 79 | 79 | * @uuid: namespace name supplied in the dimm label |
| 80 | + * @id: ida allocated id | |
| 80 | 81 | */ |
| 81 | 82 | struct nd_namespace_pmem { |
| 82 | 83 | struct nd_namespace_io nsio; |
| 83 | 84 | char *alt_name; |
| 84 | 85 | u8 *uuid; |
| 86 | + int id; | |
| 85 | 87 | }; |
| 86 | 88 | |
| 87 | 89 | /** |
| @@ -105,19 +107,19 @@ struct nd_namespace_blk { · 17 unmodified lines | ||
| 105 | 107 | struct resource **res; |
| 106 | 108 | }; |
| 107 | 109 | |
| 108 | −static inline struct nd_namespace_io *to_nd_namespace_io(struct device *dev) | |
| 110 | +static inline struct nd_namespace_io *to_nd_namespace_io(const struct device *dev) | |
| 109 | 111 | { |
| 110 | 112 | return container_of(dev, struct nd_namespace_io, common.dev); |
| 111 | 113 | } |
| 112 | 114 | |
| 113 | −static inline struct nd_namespace_pmem *to_nd_namespace_pmem(struct device *dev) | |
| 115 | +static inline struct nd_namespace_pmem *to_nd_namespace_pmem(const struct device *dev) | |
| 114 | 116 | { |
| 115 | 117 | struct nd_namespace_io *nsio = to_nd_namespace_io(dev); |
| 116 | 118 | |
| 117 | 119 | return container_of(nsio, struct nd_namespace_pmem, nsio); |
| 118 | 120 | } |
| 119 | 121 | |
| 120 | −static inline struct nd_namespace_blk *to_nd_namespace_blk(struct device *dev) | |
| 122 | +static inline struct nd_namespace_blk *to_nd_namespace_blk(const struct device *dev) | |
| 121 | 123 | { |
| 122 | 124 | return container_of(dev, struct nd_namespace_blk, common.dev); |
| 123 | 125 | } |
| @@ -21,14 +21,16 @@ struct nd_cmd_smart { · 20 unmodified lines | ||
| 21 | 21 | } __packed; |
| 22 | 22 | |
| 23 | 23 | #define ND_SMART_HEALTH_VALID (1 << 0) |
| 24 | −#define ND_SMART_TEMP_VALID (1 << 1) | |
| 25 | −#define ND_SMART_SPARES_VALID (1 << 2) | |
| 26 | −#define ND_SMART_ALARM_VALID (1 << 3) | |
| 27 | −#define ND_SMART_USED_VALID (1 << 4) | |
| 28 | −#define ND_SMART_SHUTDOWN_VALID (1 << 5) | |
| 29 | −#define ND_SMART_VENDOR_VALID (1 << 6) | |
| 30 | −#define ND_SMART_TEMP_TRIP (1 << 0) | |
| 31 | −#define ND_SMART_SPARE_TRIP (1 << 1) | |
| 24 | +#define ND_SMART_SPARES_VALID (1 << 1) | |
| 25 | +#define ND_SMART_USED_VALID (1 << 2) | |
| 26 | +#define ND_SMART_TEMP_VALID (1 << 3) | |
| 27 | +#define ND_SMART_CTEMP_VALID (1 << 4) | |
| 28 | +#define ND_SMART_ALARM_VALID (1 << 9) | |
| 29 | +#define ND_SMART_SHUTDOWN_VALID (1 << 10) | |
| 30 | +#define ND_SMART_VENDOR_VALID (1 << 11) | |
| 31 | +#define ND_SMART_SPARE_TRIP (1 << 0) | |
| 32 | +#define ND_SMART_TEMP_TRIP (1 << 1) | |
| 33 | +#define ND_SMART_CTEMP_TRIP (1 << 2) | |
| 32 | 34 | #define ND_SMART_NON_CRITICAL_HEALTH (1 << 0) |
| 33 | 35 | #define ND_SMART_CRITICAL_HEALTH (1 << 1) |
| 34 | 36 | #define ND_SMART_FATAL_HEALTH (1 << 2) |
| @@ -37,14 +39,15 @@ struct nd_smart_payload { · 2 unmodified lines | ||
| 37 | 39 | __u32 flags; |
| 38 | 40 | __u8 reserved0[4]; |
| 39 | 41 | __u8 health; |
| 40 | − __u16 temperature; | |
| 41 | 42 | __u8 spares; |
| 42 | − __u8 alarm_flags; | |
| 43 | 43 | __u8 life_used; |
| 44 | + __u8 alarm_flags; | |
| 45 | + __u16 temperature; | |
| 46 | + __u16 ctrl_temperature; | |
| 47 | + __u8 reserved1[15]; | |
| 44 | 48 | __u8 shutdown_state; |
| 45 | − __u8 reserved1; | |
| 46 | 49 | __u32 vendor_size; |
| 47 | − __u8 vendor_data[108]; | |
| 50 | + __u8 vendor_data[92]; | |
| 48 | 51 | } __packed; |
| 49 | 52 | |
| 50 | 53 | struct nd_cmd_smart_threshold { |
| @@ -53,7 +56,8 @@ struct nd_cmd_smart_threshold { · 2 unmodified lines | ||
| 53 | 56 | } __packed; |
| 54 | 57 | |
| 55 | 58 | struct nd_smart_threshold_payload { |
| 56 | − __u16 alarm_control; | |
| 59 | + __u8 alarm_control; | |
| 60 | + __u8 reserved0; | |
| 57 | 61 | __u16 temperature; |
| 58 | 62 | __u8 spares; |
| 59 | 63 | __u8 reserved[3]; |
| @@ -13,6 +13,7 @@ ldflags-y += --wrap=__release_region · 12 unmodified lines | ||
| 13 | 13 | ldflags-y += --wrap=devm_memremap_pages |
| 14 | 14 | ldflags-y += --wrap=insert_resource |
| 15 | 15 | ldflags-y += --wrap=remove_resource |
| 16 | +ldflags-y += --wrap=acpi_evaluate_object | |
| 16 | 17 | |
| 17 | 18 | DRIVERS := ../../../drivers |
| 18 | 19 | NVDIMM_SRC := $(DRIVERS)/nvdimm |
| @@ -17,6 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | #include <linux/module.h> |
| 18 | 18 | #include <linux/types.h> |
| 19 | 19 | #include <linux/pfn_t.h> |
| 20 | +#include <linux/acpi.h> | |
| 20 | 21 | #include <linux/io.h> |
| 21 | 22 | #include <linux/mm.h> |
| 22 | 23 | #include "nfit_test.h" |
| @@ -73,7 +74,7 @@ void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size, · 50 unmodified lines | ||
| 73 | 74 | |
| 74 | 75 | if (nfit_res) |
| 75 | 76 | return (void __iomem *) nfit_res->buf + offset |
| 76 | − - nfit_res->res->start; | |
| 77 | + - nfit_res->res.start; | |
| 77 | 78 | return fallback_fn(offset, size); |
| 78 | 79 | } |
| 79 | 80 | |
| @@ -84,7 +85,7 @@ void __iomem *__wrap_devm_ioremap_nocache(struct device *dev, · 4 unmodified lines | ||
| 84 | 85 | |
| 85 | 86 | if (nfit_res) |
| 86 | 87 | return (void __iomem *) nfit_res->buf + offset |
| 87 | − - nfit_res->res->start; | |
| 88 | + - nfit_res->res.start; | |
| 88 | 89 | return devm_ioremap_nocache(dev, offset, size); |
| 89 | 90 | } |
| 90 | 91 | EXPORT_SYMBOL(__wrap_devm_ioremap_nocache); |
| @@ -95,7 +96,7 @@ void *__wrap_devm_memremap(struct device *dev, resource_size_t offset, · 4 unmodified lines | ||
| 95 | 96 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
| 96 | 97 | |
| 97 | 98 | if (nfit_res) |
| 98 | − return nfit_res->buf + offset - nfit_res->res->start; | |
| 99 | + return nfit_res->buf + offset - nfit_res->res.start; | |
| 99 | 100 | return devm_memremap(dev, offset, size, flags); |
| 100 | 101 | } |
| 101 | 102 | EXPORT_SYMBOL(__wrap_devm_memremap); |
| @@ -107,7 +108,7 @@ void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res, · 5 unmodified lines | ||
| 107 | 108 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
| 108 | 109 | |
| 109 | 110 | if (nfit_res) |
| 110 | − return nfit_res->buf + offset - nfit_res->res->start; | |
| 111 | + return nfit_res->buf + offset - nfit_res->res.start; | |
| 111 | 112 | return devm_memremap_pages(dev, res, ref, altmap); |
| 112 | 113 | } |
| 113 | 114 | EXPORT_SYMBOL(__wrap_devm_memremap_pages); |
| @@ -128,7 +129,7 @@ void *__wrap_memremap(resource_size_t offset, size_t size, · 14 unmodified lines | ||
| 128 | 129 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
| 129 | 130 | |
| 130 | 131 | if (nfit_res) |
| 131 | − return nfit_res->buf + offset - nfit_res->res->start; | |
| 132 | + return nfit_res->buf + offset - nfit_res->res.start; | |
| 132 | 133 | return memremap(offset, size, flags); |
| 133 | 134 | } |
| 134 | 135 | EXPORT_SYMBOL(__wrap_memremap); |
| @@ -174,6 +175,63 @@ void __wrap_memunmap(void *addr) · 39 unmodified lines | ||
| 174 | 175 | } |
| 175 | 176 | EXPORT_SYMBOL(__wrap_memunmap); |
| 176 | 177 | |
| 178 | +static bool nfit_test_release_region(struct device *dev, | |
| 179 | + struct resource *parent, resource_size_t start, | |
| 180 | + resource_size_t n); | |
| 181 | + | |
| 182 | +static void nfit_devres_release(struct device *dev, void *data) | |
| 183 | +{ | |
| 184 | + struct resource *res = *((struct resource **) data); | |
| 185 | + | |
| 186 | + WARN_ON(!nfit_test_release_region(NULL, &iomem_resource, res->start, | |
| 187 | + resource_size(res))); | |
| 188 | +} | |
| 189 | + | |
| 190 | +static int match(struct device *dev, void *__res, void *match_data) | |
| 191 | +{ | |
| 192 | + struct resource *res = *((struct resource **) __res); | |
| 193 | + resource_size_t start = *((resource_size_t *) match_data); | |
| 194 | + | |
| 195 | + return res->start == start; | |
| 196 | +} | |
| 197 | + | |
| 198 | +static bool nfit_test_release_region(struct device *dev, | |
| 199 | + struct resource *parent, resource_size_t start, | |
| 200 | + resource_size_t n) | |
| 201 | +{ | |
| 202 | + if (parent == &iomem_resource) { | |
| 203 | + struct nfit_test_resource *nfit_res = get_nfit_res(start); | |
| 204 | + | |
| 205 | + if (nfit_res) { | |
| 206 | + struct nfit_test_request *req; | |
| 207 | + struct resource *res = NULL; | |
| 208 | + | |
| 209 | + if (dev) { | |
| 210 | + devres_release(dev, nfit_devres_release, match, | |
| 211 | + &start); | |
| 212 | + return true; | |
| 213 | + } | |
| 214 | + | |
| 215 | + spin_lock(&nfit_res->lock); | |
| 216 | + list_for_each_entry(req, &nfit_res->requests, list) | |
| 217 | + if (req->res.start == start) { | |
| 218 | + res = &req->res; | |
| 219 | + list_del(&req->list); | |
| 220 | + break; | |
| 221 | + } | |
| 222 | + spin_unlock(&nfit_res->lock); | |
| 223 | + | |
| 224 | + WARN(!res || resource_size(res) != n, | |
| 225 | + "%s: start: %llx n: %llx mismatch: %pr\n", | |
| 226 | + __func__, start, n, res); | |
| 227 | + if (res) | |
| 228 | + kfree(req); | |
| 229 | + return true; | |
| 230 | + } | |
| 231 | + } | |
| 232 | + return false; | |
| 233 | +} | |
| 234 | + | |
| 177 | 235 | static struct resource *nfit_test_request_region(struct device *dev, |
| 178 | 236 | struct resource *parent, resource_size_t start, |
| 179 | 237 | resource_size_t n, const char *name, int flags) |
| @@ -183,21 +241,57 @@ static struct resource *nfit_test_request_region(struct device *dev, · 3 unmodified lines | ||
| 183 | 241 | if (parent == &iomem_resource) { |
| 184 | 242 | nfit_res = get_nfit_res(start); |
| 185 | 243 | if (nfit_res) { |
| 186 | − struct resource *res = nfit_res->res + 1; | |
| 244 | + struct nfit_test_request *req; | |
| 245 | + struct resource *res = NULL; | |
| 187 | 246 | |
| 188 | − if (start + n > nfit_res->res->start | |
| 189 | − + resource_size(nfit_res->res)) { | |
| 247 | + if (start + n > nfit_res->res.start | |
| 248 | + + resource_size(&nfit_res->res)) { | |
| 190 | 249 | pr_debug("%s: start: %llx n: %llx overflow: %pr\n", |
| 191 | 250 | __func__, start, n, |
| 192 | − nfit_res->res); | |
| 251 | + &nfit_res->res); | |
| 193 | 252 | return NULL; |
| 194 | 253 | } |
| 195 | 254 | |
| 255 | + spin_lock(&nfit_res->lock); | |
| 256 | + list_for_each_entry(req, &nfit_res->requests, list) | |
| 257 | + if (start == req->res.start) { | |
| 258 | + res = &req->res; | |
| 259 | + break; | |
| 260 | + } | |
| 261 | + spin_unlock(&nfit_res->lock); | |
| 262 | + | |
| 263 | + if (res) { | |
| 264 | + WARN(1, "%pr already busy\n", res); | |
| 265 | + return NULL; | |
| 266 | + } | |
| 267 | + | |
| 268 | + req = kzalloc(sizeof(*req), GFP_KERNEL); | |
| 269 | + if (!req) | |
| 270 | + return NULL; | |
| 271 | + INIT_LIST_HEAD(&req->list); | |
| 272 | + res = &req->res; | |
| 273 | + | |
| 196 | 274 | res->start = start; |
| 197 | 275 | res->end = start + n - 1; |
| 198 | 276 | res->name = name; |
| 199 | 277 | res->flags = resource_type(parent); |
| 200 | 278 | res->flags |= IORESOURCE_BUSY | flags; |
| 279 | + spin_lock(&nfit_res->lock); | |
| 280 | + list_add(&req->list, &nfit_res->requests); | |
| 281 | + spin_unlock(&nfit_res->lock); | |
| 282 | + | |
| 283 | + if (dev) { | |
| 284 | + struct resource **d; | |
| 285 | + | |
| 286 | + d = devres_alloc(nfit_devres_release, | |
| 287 | + sizeof(struct resource *), | |
| 288 | + GFP_KERNEL); | |
| 289 | + if (!d) | |
| 290 | + return NULL; | |
| 291 | + *d = res; | |
| 292 | + devres_add(dev, d); | |
| 293 | + } | |
| 294 | + | |
| 201 | 295 | pr_debug("%s: %pr\n", __func__, res); |
| 202 | 296 | return res; |
| 203 | 297 | } |
| @@ -241,29 +335,10 @@ struct resource *__wrap___devm_request_region(struct device *dev, · 37 unmodified lines | ||
| 241 | 335 | } |
| 242 | 336 | EXPORT_SYMBOL(__wrap___devm_request_region); |
| 243 | 337 | |
| 244 | −static bool nfit_test_release_region(struct resource *parent, | |
| 245 | − resource_size_t start, resource_size_t n) | |
| 246 | −{ | |
| 247 | − if (parent == &iomem_resource) { | |
| 248 | − struct nfit_test_resource *nfit_res = get_nfit_res(start); | |
| 249 | − if (nfit_res) { | |
| 250 | − struct resource *res = nfit_res->res + 1; | |
| 251 | − | |
| 252 | − if (start != res->start || resource_size(res) != n) | |
| 253 | − pr_info("%s: start: %llx n: %llx mismatch: %pr\n", | |
| 254 | − __func__, start, n, res); | |
| 255 | − else | |
| 256 | − memset(res, 0, sizeof(*res)); | |
| 257 | − return true; | |
| 258 | − } | |
| 259 | − } | |
| 260 | − return false; | |
| 261 | −} | |
| 262 | − | |
| 263 | 338 | void __wrap___release_region(struct resource *parent, resource_size_t start, |
| 264 | 339 | resource_size_t n) |
| 265 | 340 | { |
| 266 | − if (!nfit_test_release_region(parent, start, n)) | |
| 341 | + if (!nfit_test_release_region(NULL, parent, start, n)) | |
| 267 | 342 | __release_region(parent, start, n); |
| 268 | 343 | } |
| 269 | 344 | EXPORT_SYMBOL(__wrap___release_region); |
| @@ -271,9 +346,25 @@ EXPORT_SYMBOL(__wrap___release_region); · 1 unmodified line | ||
| 271 | 346 | void __wrap___devm_release_region(struct device *dev, struct resource *parent, |
| 272 | 347 | resource_size_t start, resource_size_t n) |
| 273 | 348 | { |
| 274 | − if (!nfit_test_release_region(parent, start, n)) | |
| 349 | + if (!nfit_test_release_region(dev, parent, start, n)) | |
| 275 | 350 | __devm_release_region(dev, parent, start, n); |
| 276 | 351 | } |
| 277 | 352 | EXPORT_SYMBOL(__wrap___devm_release_region); |
| 278 | 353 | |
| 354 | +acpi_status __wrap_acpi_evaluate_object(acpi_handle handle, acpi_string path, | |
| 355 | + struct acpi_object_list *p, struct acpi_buffer *buf) | |
| 356 | +{ | |
| 357 | + struct nfit_test_resource *nfit_res = get_nfit_res((long) handle); | |
| 358 | + union acpi_object **obj; | |
| 359 | + | |
| 360 | + if (!nfit_res || strcmp(path, "_FIT") || !buf) | |
| 361 | + return acpi_evaluate_object(handle, path, p, buf); | |
| 362 | + | |
| 363 | + obj = nfit_res->buf; | |
| 364 | + buf->length = sizeof(union acpi_object); | |
| 365 | + buf->pointer = *obj; | |
| 366 | + return AE_OK; | |
| 367 | +} | |
| 368 | +EXPORT_SYMBOL(__wrap_acpi_evaluate_object); | |
| 369 | + | |
| 279 | 370 | MODULE_LICENSE("GPL v2"); |
| @@ -132,6 +132,8 @@ static u32 handle[NUM_DCR] = { · 131 unmodified lines | ||
| 132 | 132 | [4] = NFIT_DIMM_HANDLE(0, 1, 0, 0, 0), |
| 133 | 133 | }; |
| 134 | 134 | |
| 135 | +static unsigned long dimm_fail_cmd_flags[NUM_DCR]; | |
| 136 | + | |
| 135 | 137 | struct nfit_test { |
| 136 | 138 | struct acpi_nfit_desc acpi_desc; |
| 137 | 139 | struct platform_device pdev; |
| @@ -154,11 +156,14 @@ struct nfit_test { · 16 unmodified lines | ||
| 154 | 156 | int (*alloc)(struct nfit_test *t); |
| 155 | 157 | void (*setup)(struct nfit_test *t); |
| 156 | 158 | int setup_hotplug; |
| 159 | + union acpi_object **_fit; | |
| 160 | + dma_addr_t _fit_dma; | |
| 157 | 161 | struct ars_state { |
| 158 | 162 | struct nd_cmd_ars_status *ars_status; |
| 159 | 163 | unsigned long deadline; |
| 160 | 164 | spinlock_t lock; |
| 161 | 165 | } ars_state; |
| 166 | + struct device *dimm_dev[NUM_DCR]; | |
| 162 | 167 | }; |
| 163 | 168 | |
| 164 | 169 | static struct nfit_test *to_nfit_test(struct device *dev) |
| @@ -411,6 +416,9 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, · 246 unmodified lines | ||
| 411 | 416 | if (i >= ARRAY_SIZE(handle)) |
| 412 | 417 | return -ENXIO; |
| 413 | 418 | |
| 419 | + if ((1 << func) & dimm_fail_cmd_flags[i]) | |
| 420 | + return -EIO; | |
| 421 | + | |
| 414 | 422 | switch (func) { |
| 415 | 423 | case ND_CMD_GET_CONFIG_SIZE: |
| 416 | 424 | rc = nfit_test_cmd_get_config_size(buf, buf_len); |
| @@ -428,6 +436,9 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, · 11 unmodified lines | ||
| 428 | 436 | break; |
| 429 | 437 | case ND_CMD_SMART_THRESHOLD: |
| 430 | 438 | rc = nfit_test_cmd_smart_threshold(buf, buf_len); |
| 439 | + device_lock(&t->pdev.dev); | |
| 440 | + __acpi_nvdimm_notify(t->dimm_dev[i], 0x81); | |
| 441 | + device_unlock(&t->pdev.dev); | |
| 431 | 442 | break; |
| 432 | 443 | default: |
| 433 | 444 | return -ENOTTY; |
| @@ -467,14 +478,12 @@ static struct nfit_test *instances[NUM_NFITS]; · 33 unmodified lines | ||
| 467 | 478 | static void release_nfit_res(void *data) |
| 468 | 479 | { |
| 469 | 480 | struct nfit_test_resource *nfit_res = data; |
| 470 | − struct resource *res = nfit_res->res; | |
| 471 | 481 | |
| 472 | 482 | spin_lock(&nfit_test_lock); |
| 473 | 483 | list_del(&nfit_res->list); |
| 474 | 484 | spin_unlock(&nfit_test_lock); |
| 475 | 485 | |
| 476 | 486 | vfree(nfit_res->buf); |
| 477 | − kfree(res); | |
| 478 | 487 | kfree(nfit_res); |
| 479 | 488 | } |
| 480 | 489 | |
| @@ -482,12 +491,11 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma, · 1 unmodified line | ||
| 482 | 491 | void *buf) |
| 483 | 492 | { |
| 484 | 493 | struct device *dev = &t->pdev.dev; |
| 485 | − struct resource *res = kzalloc(sizeof(*res) * 2, GFP_KERNEL); | |
| 486 | 494 | struct nfit_test_resource *nfit_res = kzalloc(sizeof(*nfit_res), |
| 487 | 495 | GFP_KERNEL); |
| 488 | 496 | int rc; |
| 489 | 497 | |
| 490 | − if (!res || !buf || !nfit_res) | |
| 498 | + if (!buf || !nfit_res) | |
| 491 | 499 | goto err; |
| 492 | 500 | rc = devm_add_action(dev, release_nfit_res, nfit_res); |
| 493 | 501 | if (rc) |
| @@ -496,10 +504,11 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma, · 2 unmodified lines | ||
| 496 | 504 | memset(buf, 0, size); |
| 497 | 505 | nfit_res->dev = dev; |
| 498 | 506 | nfit_res->buf = buf; |
| 499 | − nfit_res->res = res; | |
| 500 | − res->start = *dma; | |
| 501 | − res->end = *dma + size - 1; | |
| 502 | − res->name = "NFIT"; | |
| 507 | + nfit_res->res.start = *dma; | |
| 508 | + nfit_res->res.end = *dma + size - 1; | |
| 509 | + nfit_res->res.name = "NFIT"; | |
| 510 | + spin_lock_init(&nfit_res->lock); | |
| 511 | + INIT_LIST_HEAD(&nfit_res->requests); | |
| 503 | 512 | spin_lock(&nfit_test_lock); |
| 504 | 513 | list_add(&nfit_res->list, &t->resources); |
| 505 | 514 | spin_unlock(&nfit_test_lock); |
| @@ -508,7 +517,6 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma, · 2 unmodified lines | ||
| 508 | 517 | err: |
| 509 | 518 | if (buf) |
| 510 | 519 | vfree(buf); |
| 511 | − kfree(res); | |
| 512 | 520 | kfree(nfit_res); |
| 513 | 521 | return NULL; |
| 514 | 522 | } |
| @@ -533,13 +541,13 @@ static struct nfit_test_resource *nfit_test_lookup(resource_size_t addr) · 18 unmodified lines | ||
| 533 | 541 | continue; |
| 534 | 542 | spin_lock(&nfit_test_lock); |
| 535 | 543 | list_for_each_entry(n, &t->resources, list) { |
| 536 | − if (addr >= n->res->start && (addr < n->res->start | |
| 537 | − + resource_size(n->res))) { | |
| 544 | + if (addr >= n->res.start && (addr < n->res.start | |
| 545 | + + resource_size(&n->res))) { | |
| 538 | 546 | nfit_res = n; |
| 539 | 547 | break; |
| 540 | 548 | } else if (addr >= (unsigned long) n->buf |
| 541 | 549 | && (addr < (unsigned long) n->buf |
| 542 | − + resource_size(n->res))) { | |
| 550 | + + resource_size(&n->res))) { | |
| 543 | 551 | nfit_res = n; |
| 544 | 552 | break; |
| 545 | 553 | } |
| @@ -564,6 +572,86 @@ static int ars_state_init(struct device *dev, struct ars_state *ars_state) · 18 unmodified lines | ||
| 564 | 572 | return 0; |
| 565 | 573 | } |
| 566 | 574 | |
| 575 | +static void put_dimms(void *data) | |
| 576 | +{ | |
| 577 | + struct device **dimm_dev = data; | |
| 578 | + int i; | |
| 579 | + | |
| 580 | + for (i = 0; i < NUM_DCR; i++) | |
| 581 | + if (dimm_dev[i]) | |
| 582 | + device_unregister(dimm_dev[i]); | |
| 583 | +} | |
| 584 | + | |
| 585 | +static struct class *nfit_test_dimm; | |
| 586 | + | |
| 587 | +static int dimm_name_to_id(struct device *dev) | |
| 588 | +{ | |
| 589 | + int dimm; | |
| 590 | + | |
| 591 | + if (sscanf(dev_name(dev), "test_dimm%d", &dimm) != 1 | |
| 592 | + || dimm >= NUM_DCR || dimm < 0) | |
| 593 | + return -ENXIO; | |
| 594 | + return dimm; | |
| 595 | +} | |
| 596 | + | |
| 597 | + | |
| 598 | +static ssize_t handle_show(struct device *dev, struct device_attribute *attr, | |
| 599 | + char *buf) | |
| 600 | +{ | |
| 601 | + int dimm = dimm_name_to_id(dev); | |
| 602 | + | |
| 603 | + if (dimm < 0) | |
| 604 | + return dimm; | |
| 605 | + | |
| 606 | + return sprintf(buf, "%#x", handle[dimm]); | |
| 607 | +} | |
| 608 | +DEVICE_ATTR_RO(handle); | |
| 609 | + | |
| 610 | +static ssize_t fail_cmd_show(struct device *dev, struct device_attribute *attr, | |
| 611 | + char *buf) | |
| 612 | +{ | |
| 613 | + int dimm = dimm_name_to_id(dev); | |
| 614 | + | |
| 615 | + if (dimm < 0) | |
| 616 | + return dimm; | |
| 617 | + | |
| 618 | + return sprintf(buf, "%#lx\n", dimm_fail_cmd_flags[dimm]); | |
| 619 | +} | |
| 620 | + | |
| 621 | +static ssize_t fail_cmd_store(struct device *dev, struct device_attribute *attr, | |
| 622 | + const char *buf, size_t size) | |
| 623 | +{ | |
| 624 | + int dimm = dimm_name_to_id(dev); | |
| 625 | + unsigned long val; | |
| 626 | + ssize_t rc; | |
| 627 | + | |
| 628 | + if (dimm < 0) | |
| 629 | + return dimm; | |
| 630 | + | |
| 631 | + rc = kstrtol(buf, 0, &val); | |
| 632 | + if (rc) | |
| 633 | + return rc; | |
| 634 | + | |
| 635 | + dimm_fail_cmd_flags[dimm] = val; | |
| 636 | + return size; | |
| 637 | +} | |
| 638 | +static DEVICE_ATTR_RW(fail_cmd); | |
| 639 | + | |
| 640 | +static struct attribute *nfit_test_dimm_attributes[] = { | |
| 641 | + &dev_attr_fail_cmd.attr, | |
| 642 | + &dev_attr_handle.attr, | |
| 643 | + NULL, | |
| 644 | +}; | |
| 645 | + | |
| 646 | +static struct attribute_group nfit_test_dimm_attribute_group = { | |
| 647 | + .attrs = nfit_test_dimm_attributes, | |
| 648 | +}; | |
| 649 | + | |
| 650 | +static const struct attribute_group *nfit_test_dimm_attribute_groups[] = { | |
| 651 | + &nfit_test_dimm_attribute_group, | |
| 652 | + NULL, | |
| 653 | +}; | |
| 654 | + | |
| 567 | 655 | static int nfit_test0_alloc(struct nfit_test *t) |
| 568 | 656 | { |
| 569 | 657 | size_t nfit_size = sizeof(struct acpi_nfit_system_address) * NUM_SPA |
| @@ -616,6 +704,21 @@ static int nfit_test0_alloc(struct nfit_test *t) · 46 unmodified lines | ||
| 616 | 704 | return -ENOMEM; |
| 617 | 705 | } |
| 618 | 706 | |
| 707 | + t->_fit = test_alloc(t, sizeof(union acpi_object **), &t->_fit_dma); | |
| 708 | + if (!t->_fit) | |
| 709 | + return -ENOMEM; | |
| 710 | + | |
| 711 | + if (devm_add_action_or_reset(&t->pdev.dev, put_dimms, t->dimm_dev)) | |
| 712 | + return -ENOMEM; | |
| 713 | + for (i = 0; i < NUM_DCR; i++) { | |
| 714 | + t->dimm_dev[i] = device_create_with_groups(nfit_test_dimm, | |
| 715 | + &t->pdev.dev, 0, NULL, | |
| 716 | + nfit_test_dimm_attribute_groups, | |
| 717 | + "test_dimm%d", i); | |
| 718 | + if (!t->dimm_dev[i]) | |
| 719 | + return -ENOMEM; | |
| 720 | + } | |
| 721 | + | |
| 619 | 722 | return ars_state_init(&t->pdev.dev, &t->ars_state); |
| 620 | 723 | } |
| 621 | 724 | |
| @@ -1409,6 +1512,8 @@ static int nfit_test_probe(struct platform_device *pdev) · 787 unmodified lines | ||
| 1409 | 1512 | struct acpi_nfit_desc *acpi_desc; |
| 1410 | 1513 | struct device *dev = &pdev->dev; |
| 1411 | 1514 | struct nfit_test *nfit_test; |
| 1515 | + struct nfit_mem *nfit_mem; | |
| 1516 | + union acpi_object *obj; | |
| 1412 | 1517 | int rc; |
| 1413 | 1518 | |
| 1414 | 1519 | nfit_test = to_nfit_test(&pdev->dev); |
| @@ -1476,14 +1581,30 @@ static int nfit_test_probe(struct platform_device *pdev) · 61 unmodified lines | ||
| 1476 | 1581 | if (nfit_test->setup != nfit_test0_setup) |
| 1477 | 1582 | return 0; |
| 1478 | 1583 | |
| 1479 | − flush_work(&acpi_desc->work); | |
| 1480 | 1584 | nfit_test->setup_hotplug = 1; |
| 1481 | 1585 | nfit_test->setup(nfit_test); |
| 1482 | 1586 | |
| 1483 | − rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_buf, | |
| 1484 | − nfit_test->nfit_size); | |
| 1485 | − if (rc) | |
| 1486 | − return rc; | |
| 1587 | + obj = kzalloc(sizeof(*obj), GFP_KERNEL); | |
| 1588 | + if (!obj) | |
| 1589 | + return -ENOMEM; | |
| 1590 | + obj->type = ACPI_TYPE_BUFFER; | |
| 1591 | + obj->buffer.length = nfit_test->nfit_size; | |
| 1592 | + obj->buffer.pointer = nfit_test->nfit_buf; | |
| 1593 | + *(nfit_test->_fit) = obj; | |
| 1594 | + __acpi_nfit_notify(&pdev->dev, nfit_test, 0x80); | |
| 1595 | + | |
| 1596 | + /* associate dimm devices with nfit_mem data for notification testing */ | |
| 1597 | + mutex_lock(&acpi_desc->init_mutex); | |
| 1598 | + list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { | |
| 1599 | + u32 nfit_handle = __to_nfit_memdev(nfit_mem)->device_handle; | |
| 1600 | + int i; | |
| 1601 | + | |
| 1602 | + for (i = 0; i < NUM_DCR; i++) | |
| 1603 | + if (nfit_handle == handle[i]) | |
| 1604 | + dev_set_drvdata(nfit_test->dimm_dev[i], | |
| 1605 | + nfit_mem); | |
| 1606 | + } | |
| 1607 | + mutex_unlock(&acpi_desc->init_mutex); | |
| 1487 | 1608 | |
| 1488 | 1609 | return 0; |
| 1489 | 1610 | } |
| @@ -1518,6 +1639,10 @@ static __init int nfit_test_init(void) · 28 unmodified lines | ||
| 1518 | 1639 | { |
| 1519 | 1640 | int rc, i; |
| 1520 | 1641 | |
| 1642 | + nfit_test_dimm = class_create(THIS_MODULE, "nfit_test_dimm"); | |
| 1643 | + if (IS_ERR(nfit_test_dimm)) | |
| 1644 | + return PTR_ERR(nfit_test_dimm); | |
| 1645 | + | |
| 1521 | 1646 | nfit_test_setup(nfit_test_lookup); |
| 1522 | 1647 | |
| 1523 | 1648 | for (i = 0; i < NUM_NFITS; i++) { |
| @@ -1584,6 +1709,7 @@ static __exit void nfit_test_exit(void) · 60 unmodified lines | ||
| 1584 | 1709 | for (i = 0; i < NUM_NFITS; i++) |
| 1585 | 1710 | platform_device_unregister(&instances[i]->pdev); |
| 1586 | 1711 | nfit_test_teardown(); |
| 1712 | + class_destroy(nfit_test_dimm); | |
| 1587 | 1713 | } |
| 1588 | 1714 | |
| 1589 | 1715 | module_init(nfit_test_init); |
| @@ -13,11 +13,21 @@ · 12 unmodified lines | ||
| 13 | 13 | #ifndef __NFIT_TEST_H__ |
| 14 | 14 | #define __NFIT_TEST_H__ |
| 15 | 15 | #include <linux/list.h> |
| 16 | +#include <linux/ioport.h> | |
| 17 | +#include <linux/spinlock_types.h> | |
| 18 | + | |
| 19 | +struct nfit_test_request { | |
| 20 | + struct list_head list; | |
| 21 | + struct resource res; | |
| 22 | +}; | |
| 16 | 23 | |
| 17 | 24 | struct nfit_test_resource { |
| 25 | + struct list_head requests; | |
| 18 | 26 | struct list_head list; |
| 19 | − struct resource *res; | |
| 27 | + struct resource res; | |
| 20 | 28 | struct device *dev; |
| 29 | + spinlock_t lock; | |
| 30 | + int req_count; | |
| 21 | 31 | void *buf; |
| 22 | 32 | }; |
| 23 | 33 | |