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

libnvdimm, nfit, nd_blk: driver for BLK-mode access persistent memory

The libnvdimm implementation handles allocating dimm address space (DPA)
between PMEM and BLK mode interfaces. After DPA has been allocated from
a BLK-region to a BLK-namespace the nd_blk driver attaches to handle I/O
as a struct bio based block device. Unlike PMEM, BLK is required to
handle platform specific details like mmio register formats and memory
controller interleave. For this reason the libnvdimm generic nd_blk
driver calls back into the bus provider to carry out the I/O.

This initial implementation handles the BLK interface defined by the
ACPI 6 NFIT [1] and the NVDIMM DSM Interface Example [2] composed from
DCR (dimm control region), BDW (block data window), IDT (interleave
descriptor) NFIT structures and the hardware register format.
[1]: http://www.uefi.org/sites/default/files/resources/ACPI_6.0.pdf
[2]: http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf

Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Boaz Harrosh <boaz@plexistor.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Jens Axboe <axboe@fb.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

authored by

Ross Zwisler and committed by
Dan Williams
047fc8a1 5212e11f

+941 -34
+435 -14
drivers/acpi/nfit.c
··· 13 13 #include <linux/list_sort.h> 14 14 #include <linux/libnvdimm.h> 15 15 #include <linux/module.h> 16 + #include <linux/mutex.h> 16 17 #include <linux/ndctl.h> 17 18 #include <linux/list.h> 18 19 #include <linux/acpi.h> 19 20 #include <linux/sort.h> 21 + #include <linux/io.h> 20 22 #include "nfit.h" 23 + 24 + /* 25 + * For readq() and writeq() on 32-bit builds, the hi-lo, lo-hi order is 26 + * irrelevant. 27 + */ 28 + #include <asm-generic/io-64-nonatomic-hi-lo.h> 21 29 22 30 static bool force_enable_dimms; 23 31 module_param(force_enable_dimms, bool, S_IRUGO|S_IWUSR); ··· 80 72 81 73 if (!adev) 82 74 return -ENOTTY; 83 - dimm_name = dev_name(&adev->dev); 75 + dimm_name = nvdimm_name(nvdimm); 84 76 cmd_name = nvdimm_cmd_name(cmd); 85 77 dsm_mask = nfit_mem->dsm_mask; 86 78 desc = nd_cmd_dimm_desc(cmd); ··· 287 279 return true; 288 280 } 289 281 282 + static bool add_idt(struct acpi_nfit_desc *acpi_desc, 283 + struct acpi_nfit_interleave *idt) 284 + { 285 + struct device *dev = acpi_desc->dev; 286 + struct nfit_idt *nfit_idt = devm_kzalloc(dev, sizeof(*nfit_idt), 287 + GFP_KERNEL); 288 + 289 + if (!nfit_idt) 290 + return false; 291 + INIT_LIST_HEAD(&nfit_idt->list); 292 + nfit_idt->idt = idt; 293 + list_add_tail(&nfit_idt->list, &acpi_desc->idts); 294 + dev_dbg(dev, "%s: idt index: %d num_lines: %d\n", __func__, 295 + idt->interleave_index, idt->line_count); 296 + return true; 297 + } 298 + 290 299 static void *add_table(struct acpi_nfit_desc *acpi_desc, void *table, 291 300 const void *end) 292 301 { ··· 332 307 if (!add_bdw(acpi_desc, table)) 333 308 return err; 334 309 break; 335 - /* TODO */ 336 310 case ACPI_NFIT_TYPE_INTERLEAVE: 337 - dev_dbg(dev, "%s: idt\n", __func__); 311 + if (!add_idt(acpi_desc, table)) 312 + return err; 338 313 break; 339 314 case ACPI_NFIT_TYPE_FLUSH_ADDRESS: 340 315 dev_dbg(dev, "%s: flush\n", __func__); ··· 387 362 struct nfit_mem *nfit_mem, struct acpi_nfit_system_address *spa) 388 363 { 389 364 u16 dcr = __to_nfit_memdev(nfit_mem)->region_index; 365 + struct nfit_memdev *nfit_memdev; 390 366 struct nfit_dcr *nfit_dcr; 391 367 struct nfit_bdw *nfit_bdw; 368 + struct nfit_idt *nfit_idt; 369 + u16 idt_idx, range_index; 392 370 393 371 list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) { 394 372 if (nfit_dcr->dcr->region_index != dcr) ··· 424 396 return 0; 425 397 426 398 nfit_mem_find_spa_bdw(acpi_desc, nfit_mem); 399 + 400 + if (!nfit_mem->spa_bdw) 401 + return 0; 402 + 403 + range_index = nfit_mem->spa_bdw->range_index; 404 + list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { 405 + if (nfit_memdev->memdev->range_index != range_index || 406 + nfit_memdev->memdev->region_index != dcr) 407 + continue; 408 + nfit_mem->memdev_bdw = nfit_memdev->memdev; 409 + idt_idx = nfit_memdev->memdev->interleave_index; 410 + list_for_each_entry(nfit_idt, &acpi_desc->idts, list) { 411 + if (nfit_idt->idt->interleave_index != idt_idx) 412 + continue; 413 + nfit_mem->idt_bdw = nfit_idt->idt; 414 + break; 415 + } 416 + break; 417 + } 418 + 427 419 return 0; 428 420 } 429 421 ··· 487 439 } 488 440 489 441 if (type == NFIT_SPA_DCR) { 442 + struct nfit_idt *nfit_idt; 443 + u16 idt_idx; 444 + 490 445 /* multiple dimms may share a SPA when interleaved */ 491 446 nfit_mem->spa_dcr = spa; 492 447 nfit_mem->memdev_dcr = nfit_memdev->memdev; 448 + idt_idx = nfit_memdev->memdev->interleave_index; 449 + list_for_each_entry(nfit_idt, &acpi_desc->idts, list) { 450 + if (nfit_idt->idt->interleave_index != idt_idx) 451 + continue; 452 + nfit_mem->idt_dcr = nfit_idt->idt; 453 + break; 454 + } 493 455 } else { 494 456 /* 495 457 * A single dimm may belong to multiple SPA-PM ··· 929 871 return 0; 930 872 } 931 873 874 + static u64 to_interleave_offset(u64 offset, struct nfit_blk_mmio *mmio) 875 + { 876 + struct acpi_nfit_interleave *idt = mmio->idt; 877 + u32 sub_line_offset, line_index, line_offset; 878 + u64 line_no, table_skip_count, table_offset; 879 + 880 + line_no = div_u64_rem(offset, mmio->line_size, &sub_line_offset); 881 + table_skip_count = div_u64_rem(line_no, mmio->num_lines, &line_index); 882 + line_offset = idt->line_offset[line_index] 883 + * mmio->line_size; 884 + table_offset = table_skip_count * mmio->table_size; 885 + 886 + return mmio->base_offset + line_offset + table_offset + sub_line_offset; 887 + } 888 + 889 + static u64 read_blk_stat(struct nfit_blk *nfit_blk, unsigned int bw) 890 + { 891 + struct nfit_blk_mmio *mmio = &nfit_blk->mmio[DCR]; 892 + u64 offset = nfit_blk->stat_offset + mmio->size * bw; 893 + 894 + if (mmio->num_lines) 895 + offset = to_interleave_offset(offset, mmio); 896 + 897 + return readq(mmio->base + offset); 898 + } 899 + 900 + static void write_blk_ctl(struct nfit_blk *nfit_blk, unsigned int bw, 901 + resource_size_t dpa, unsigned int len, unsigned int write) 902 + { 903 + u64 cmd, offset; 904 + struct nfit_blk_mmio *mmio = &nfit_blk->mmio[DCR]; 905 + 906 + enum { 907 + BCW_OFFSET_MASK = (1ULL << 48)-1, 908 + BCW_LEN_SHIFT = 48, 909 + BCW_LEN_MASK = (1ULL << 8) - 1, 910 + BCW_CMD_SHIFT = 56, 911 + }; 912 + 913 + cmd = (dpa >> L1_CACHE_SHIFT) & BCW_OFFSET_MASK; 914 + len = len >> L1_CACHE_SHIFT; 915 + cmd |= ((u64) len & BCW_LEN_MASK) << BCW_LEN_SHIFT; 916 + cmd |= ((u64) write) << BCW_CMD_SHIFT; 917 + 918 + offset = nfit_blk->cmd_offset + mmio->size * bw; 919 + if (mmio->num_lines) 920 + offset = to_interleave_offset(offset, mmio); 921 + 922 + writeq(cmd, mmio->base + offset); 923 + /* FIXME: conditionally perform read-back if mandated by firmware */ 924 + } 925 + 926 + static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk, 927 + resource_size_t dpa, void *iobuf, size_t len, int rw, 928 + unsigned int lane) 929 + { 930 + struct nfit_blk_mmio *mmio = &nfit_blk->mmio[BDW]; 931 + unsigned int copied = 0; 932 + u64 base_offset; 933 + int rc; 934 + 935 + base_offset = nfit_blk->bdw_offset + dpa % L1_CACHE_BYTES 936 + + lane * mmio->size; 937 + /* TODO: non-temporal access, flush hints, cache management etc... */ 938 + write_blk_ctl(nfit_blk, lane, dpa, len, rw); 939 + while (len) { 940 + unsigned int c; 941 + u64 offset; 942 + 943 + if (mmio->num_lines) { 944 + u32 line_offset; 945 + 946 + offset = to_interleave_offset(base_offset + copied, 947 + mmio); 948 + div_u64_rem(offset, mmio->line_size, &line_offset); 949 + c = min_t(size_t, len, mmio->line_size - line_offset); 950 + } else { 951 + offset = base_offset + nfit_blk->bdw_offset; 952 + c = len; 953 + } 954 + 955 + if (rw) 956 + memcpy(mmio->aperture + offset, iobuf + copied, c); 957 + else 958 + memcpy(iobuf + copied, mmio->aperture + offset, c); 959 + 960 + copied += c; 961 + len -= c; 962 + } 963 + rc = read_blk_stat(nfit_blk, lane) ? -EIO : 0; 964 + return rc; 965 + } 966 + 967 + static int acpi_nfit_blk_region_do_io(struct nd_blk_region *ndbr, 968 + resource_size_t dpa, void *iobuf, u64 len, int rw) 969 + { 970 + struct nfit_blk *nfit_blk = nd_blk_region_provider_data(ndbr); 971 + struct nfit_blk_mmio *mmio = &nfit_blk->mmio[BDW]; 972 + struct nd_region *nd_region = nfit_blk->nd_region; 973 + unsigned int lane, copied = 0; 974 + int rc = 0; 975 + 976 + lane = nd_region_acquire_lane(nd_region); 977 + while (len) { 978 + u64 c = min(len, mmio->size); 979 + 980 + rc = acpi_nfit_blk_single_io(nfit_blk, dpa + copied, 981 + iobuf + copied, c, rw, lane); 982 + if (rc) 983 + break; 984 + 985 + copied += c; 986 + len -= c; 987 + } 988 + nd_region_release_lane(nd_region, lane); 989 + 990 + return rc; 991 + } 992 + 993 + static void nfit_spa_mapping_release(struct kref *kref) 994 + { 995 + struct nfit_spa_mapping *spa_map = to_spa_map(kref); 996 + struct acpi_nfit_system_address *spa = spa_map->spa; 997 + struct acpi_nfit_desc *acpi_desc = spa_map->acpi_desc; 998 + 999 + WARN_ON(!mutex_is_locked(&acpi_desc->spa_map_mutex)); 1000 + dev_dbg(acpi_desc->dev, "%s: SPA%d\n", __func__, spa->range_index); 1001 + iounmap(spa_map->iomem); 1002 + release_mem_region(spa->address, spa->length); 1003 + list_del(&spa_map->list); 1004 + kfree(spa_map); 1005 + } 1006 + 1007 + static struct nfit_spa_mapping *find_spa_mapping( 1008 + struct acpi_nfit_desc *acpi_desc, 1009 + struct acpi_nfit_system_address *spa) 1010 + { 1011 + struct nfit_spa_mapping *spa_map; 1012 + 1013 + WARN_ON(!mutex_is_locked(&acpi_desc->spa_map_mutex)); 1014 + list_for_each_entry(spa_map, &acpi_desc->spa_maps, list) 1015 + if (spa_map->spa == spa) 1016 + return spa_map; 1017 + 1018 + return NULL; 1019 + } 1020 + 1021 + static void nfit_spa_unmap(struct acpi_nfit_desc *acpi_desc, 1022 + struct acpi_nfit_system_address *spa) 1023 + { 1024 + struct nfit_spa_mapping *spa_map; 1025 + 1026 + mutex_lock(&acpi_desc->spa_map_mutex); 1027 + spa_map = find_spa_mapping(acpi_desc, spa); 1028 + 1029 + if (spa_map) 1030 + kref_put(&spa_map->kref, nfit_spa_mapping_release); 1031 + mutex_unlock(&acpi_desc->spa_map_mutex); 1032 + } 1033 + 1034 + static void __iomem *__nfit_spa_map(struct acpi_nfit_desc *acpi_desc, 1035 + struct acpi_nfit_system_address *spa) 1036 + { 1037 + resource_size_t start = spa->address; 1038 + resource_size_t n = spa->length; 1039 + struct nfit_spa_mapping *spa_map; 1040 + struct resource *res; 1041 + 1042 + WARN_ON(!mutex_is_locked(&acpi_desc->spa_map_mutex)); 1043 + 1044 + spa_map = find_spa_mapping(acpi_desc, spa); 1045 + if (spa_map) { 1046 + kref_get(&spa_map->kref); 1047 + return spa_map->iomem; 1048 + } 1049 + 1050 + spa_map = kzalloc(sizeof(*spa_map), GFP_KERNEL); 1051 + if (!spa_map) 1052 + return NULL; 1053 + 1054 + INIT_LIST_HEAD(&spa_map->list); 1055 + spa_map->spa = spa; 1056 + kref_init(&spa_map->kref); 1057 + spa_map->acpi_desc = acpi_desc; 1058 + 1059 + res = request_mem_region(start, n, dev_name(acpi_desc->dev)); 1060 + if (!res) 1061 + goto err_mem; 1062 + 1063 + /* TODO: cacheability based on the spa type */ 1064 + spa_map->iomem = ioremap_nocache(start, n); 1065 + if (!spa_map->iomem) 1066 + goto err_map; 1067 + 1068 + list_add_tail(&spa_map->list, &acpi_desc->spa_maps); 1069 + return spa_map->iomem; 1070 + 1071 + err_map: 1072 + release_mem_region(start, n); 1073 + err_mem: 1074 + kfree(spa_map); 1075 + return NULL; 1076 + } 1077 + 1078 + /** 1079 + * nfit_spa_map - interleave-aware managed-mappings of acpi_nfit_system_address ranges 1080 + * @nvdimm_bus: NFIT-bus that provided the spa table entry 1081 + * @nfit_spa: spa table to map 1082 + * 1083 + * In the case where block-data-window apertures and 1084 + * dimm-control-regions are interleaved they will end up sharing a 1085 + * single request_mem_region() + ioremap() for the address range. In 1086 + * the style of devm nfit_spa_map() mappings are automatically dropped 1087 + * when all region devices referencing the same mapping are disabled / 1088 + * unbound. 1089 + */ 1090 + static void __iomem *nfit_spa_map(struct acpi_nfit_desc *acpi_desc, 1091 + struct acpi_nfit_system_address *spa) 1092 + { 1093 + void __iomem *iomem; 1094 + 1095 + mutex_lock(&acpi_desc->spa_map_mutex); 1096 + iomem = __nfit_spa_map(acpi_desc, spa); 1097 + mutex_unlock(&acpi_desc->spa_map_mutex); 1098 + 1099 + return iomem; 1100 + } 1101 + 1102 + static int nfit_blk_init_interleave(struct nfit_blk_mmio *mmio, 1103 + struct acpi_nfit_interleave *idt, u16 interleave_ways) 1104 + { 1105 + if (idt) { 1106 + mmio->num_lines = idt->line_count; 1107 + mmio->line_size = idt->line_size; 1108 + if (interleave_ways == 0) 1109 + return -ENXIO; 1110 + mmio->table_size = mmio->num_lines * interleave_ways 1111 + * mmio->line_size; 1112 + } 1113 + 1114 + return 0; 1115 + } 1116 + 1117 + static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus, 1118 + struct device *dev) 1119 + { 1120 + struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus); 1121 + struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); 1122 + struct nd_blk_region *ndbr = to_nd_blk_region(dev); 1123 + struct nfit_blk_mmio *mmio; 1124 + struct nfit_blk *nfit_blk; 1125 + struct nfit_mem *nfit_mem; 1126 + struct nvdimm *nvdimm; 1127 + int rc; 1128 + 1129 + nvdimm = nd_blk_region_to_dimm(ndbr); 1130 + nfit_mem = nvdimm_provider_data(nvdimm); 1131 + if (!nfit_mem || !nfit_mem->dcr || !nfit_mem->bdw) { 1132 + dev_dbg(dev, "%s: missing%s%s%s\n", __func__, 1133 + nfit_mem ? "" : " nfit_mem", 1134 + nfit_mem->dcr ? "" : " dcr", 1135 + nfit_mem->bdw ? "" : " bdw"); 1136 + return -ENXIO; 1137 + } 1138 + 1139 + nfit_blk = devm_kzalloc(dev, sizeof(*nfit_blk), GFP_KERNEL); 1140 + if (!nfit_blk) 1141 + return -ENOMEM; 1142 + nd_blk_region_set_provider_data(ndbr, nfit_blk); 1143 + nfit_blk->nd_region = to_nd_region(dev); 1144 + 1145 + /* map block aperture memory */ 1146 + nfit_blk->bdw_offset = nfit_mem->bdw->offset; 1147 + mmio = &nfit_blk->mmio[BDW]; 1148 + mmio->base = nfit_spa_map(acpi_desc, nfit_mem->spa_bdw); 1149 + if (!mmio->base) { 1150 + dev_dbg(dev, "%s: %s failed to map bdw\n", __func__, 1151 + nvdimm_name(nvdimm)); 1152 + return -ENOMEM; 1153 + } 1154 + mmio->size = nfit_mem->bdw->size; 1155 + mmio->base_offset = nfit_mem->memdev_bdw->region_offset; 1156 + mmio->idt = nfit_mem->idt_bdw; 1157 + mmio->spa = nfit_mem->spa_bdw; 1158 + rc = nfit_blk_init_interleave(mmio, nfit_mem->idt_bdw, 1159 + nfit_mem->memdev_bdw->interleave_ways); 1160 + if (rc) { 1161 + dev_dbg(dev, "%s: %s failed to init bdw interleave\n", 1162 + __func__, nvdimm_name(nvdimm)); 1163 + return rc; 1164 + } 1165 + 1166 + /* map block control memory */ 1167 + nfit_blk->cmd_offset = nfit_mem->dcr->command_offset; 1168 + nfit_blk->stat_offset = nfit_mem->dcr->status_offset; 1169 + mmio = &nfit_blk->mmio[DCR]; 1170 + mmio->base = nfit_spa_map(acpi_desc, nfit_mem->spa_dcr); 1171 + if (!mmio->base) { 1172 + dev_dbg(dev, "%s: %s failed to map dcr\n", __func__, 1173 + nvdimm_name(nvdimm)); 1174 + return -ENOMEM; 1175 + } 1176 + mmio->size = nfit_mem->dcr->window_size; 1177 + mmio->base_offset = nfit_mem->memdev_dcr->region_offset; 1178 + mmio->idt = nfit_mem->idt_dcr; 1179 + mmio->spa = nfit_mem->spa_dcr; 1180 + rc = nfit_blk_init_interleave(mmio, nfit_mem->idt_dcr, 1181 + nfit_mem->memdev_dcr->interleave_ways); 1182 + if (rc) { 1183 + dev_dbg(dev, "%s: %s failed to init dcr interleave\n", 1184 + __func__, nvdimm_name(nvdimm)); 1185 + return rc; 1186 + } 1187 + 1188 + if (mmio->line_size == 0) 1189 + return 0; 1190 + 1191 + if ((u32) nfit_blk->cmd_offset % mmio->line_size 1192 + + 8 > mmio->line_size) { 1193 + dev_dbg(dev, "cmd_offset crosses interleave boundary\n"); 1194 + return -ENXIO; 1195 + } else if ((u32) nfit_blk->stat_offset % mmio->line_size 1196 + + 8 > mmio->line_size) { 1197 + dev_dbg(dev, "stat_offset crosses interleave boundary\n"); 1198 + return -ENXIO; 1199 + } 1200 + 1201 + return 0; 1202 + } 1203 + 1204 + static void acpi_nfit_blk_region_disable(struct nvdimm_bus *nvdimm_bus, 1205 + struct device *dev) 1206 + { 1207 + struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus); 1208 + struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); 1209 + struct nd_blk_region *ndbr = to_nd_blk_region(dev); 1210 + struct nfit_blk *nfit_blk = nd_blk_region_provider_data(ndbr); 1211 + int i; 1212 + 1213 + if (!nfit_blk) 1214 + return; /* never enabled */ 1215 + 1216 + /* auto-free BLK spa mappings */ 1217 + for (i = 0; i < 2; i++) { 1218 + struct nfit_blk_mmio *mmio = &nfit_blk->mmio[i]; 1219 + 1220 + if (mmio->base) 1221 + nfit_spa_unmap(acpi_desc, mmio->spa); 1222 + } 1223 + nd_blk_region_set_provider_data(ndbr, NULL); 1224 + /* devm will free nfit_blk */ 1225 + } 1226 + 932 1227 static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, 933 1228 struct nd_mapping *nd_mapping, struct nd_region_desc *ndr_desc, 934 1229 struct acpi_nfit_memory_map *memdev, ··· 1289 878 { 1290 879 struct nvdimm *nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, 1291 880 memdev->device_handle); 881 + struct nd_blk_region_desc *ndbr_desc; 1292 882 struct nfit_mem *nfit_mem; 1293 883 int blk_valid = 0; 1294 884 ··· 1320 908 1321 909 ndr_desc->nd_mapping = nd_mapping; 1322 910 ndr_desc->num_mappings = blk_valid; 911 + ndbr_desc = to_blk_region_desc(ndr_desc); 912 + ndbr_desc->enable = acpi_nfit_blk_region_enable; 913 + ndbr_desc->disable = acpi_nfit_blk_region_disable; 914 + ndbr_desc->do_io = acpi_nfit_blk_region_do_io; 1323 915 if (!nvdimm_blk_region_create(acpi_desc->nvdimm_bus, ndr_desc)) 1324 916 return -ENOMEM; 1325 917 break; ··· 1337 921 { 1338 922 static struct nd_mapping nd_mappings[ND_MAX_MAPPINGS]; 1339 923 struct acpi_nfit_system_address *spa = nfit_spa->spa; 924 + struct nd_blk_region_desc ndbr_desc; 925 + struct nd_region_desc *ndr_desc; 1340 926 struct nfit_memdev *nfit_memdev; 1341 - struct nd_region_desc ndr_desc; 1342 927 struct nvdimm_bus *nvdimm_bus; 1343 928 struct resource res; 1344 929 int count = 0, rc; ··· 1352 935 1353 936 memset(&res, 0, sizeof(res)); 1354 937 memset(&nd_mappings, 0, sizeof(nd_mappings)); 1355 - memset(&ndr_desc, 0, sizeof(ndr_desc)); 938 + memset(&ndbr_desc, 0, sizeof(ndbr_desc)); 1356 939 res.start = spa->address; 1357 940 res.end = res.start + spa->length - 1; 1358 - ndr_desc.res = &res; 1359 - ndr_desc.provider_data = nfit_spa; 1360 - ndr_desc.attr_groups = acpi_nfit_region_attribute_groups; 941 + ndr_desc = &ndbr_desc.ndr_desc; 942 + ndr_desc->res = &res; 943 + ndr_desc->provider_data = nfit_spa; 944 + ndr_desc->attr_groups = acpi_nfit_region_attribute_groups; 1361 945 list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { 1362 946 struct acpi_nfit_memory_map *memdev = nfit_memdev->memdev; 1363 947 struct nd_mapping *nd_mapping; ··· 1371 953 return -ENXIO; 1372 954 } 1373 955 nd_mapping = &nd_mappings[count++]; 1374 - rc = acpi_nfit_init_mapping(acpi_desc, nd_mapping, &ndr_desc, 956 + rc = acpi_nfit_init_mapping(acpi_desc, nd_mapping, ndr_desc, 1375 957 memdev, spa); 1376 958 if (rc) 1377 959 return rc; 1378 960 } 1379 961 1380 - ndr_desc.nd_mapping = nd_mappings; 1381 - ndr_desc.num_mappings = count; 1382 - rc = acpi_nfit_init_interleave_set(acpi_desc, &ndr_desc, spa); 962 + ndr_desc->nd_mapping = nd_mappings; 963 + ndr_desc->num_mappings = count; 964 + rc = acpi_nfit_init_interleave_set(acpi_desc, ndr_desc, spa); 1383 965 if (rc) 1384 966 return rc; 1385 967 1386 968 nvdimm_bus = acpi_desc->nvdimm_bus; 1387 969 if (nfit_spa_type(spa) == NFIT_SPA_PM) { 1388 - if (!nvdimm_pmem_region_create(nvdimm_bus, &ndr_desc)) 970 + if (!nvdimm_pmem_region_create(nvdimm_bus, ndr_desc)) 1389 971 return -ENOMEM; 1390 972 } else if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE) { 1391 - if (!nvdimm_volatile_region_create(nvdimm_bus, &ndr_desc)) 973 + if (!nvdimm_volatile_region_create(nvdimm_bus, ndr_desc)) 1392 974 return -ENOMEM; 1393 975 } 1394 976 return 0; ··· 1414 996 u8 *data; 1415 997 int rc; 1416 998 999 + INIT_LIST_HEAD(&acpi_desc->spa_maps); 1417 1000 INIT_LIST_HEAD(&acpi_desc->spas); 1418 1001 INIT_LIST_HEAD(&acpi_desc->dcrs); 1419 1002 INIT_LIST_HEAD(&acpi_desc->bdws); 1003 + INIT_LIST_HEAD(&acpi_desc->idts); 1420 1004 INIT_LIST_HEAD(&acpi_desc->memdevs); 1421 1005 INIT_LIST_HEAD(&acpi_desc->dimms); 1006 + mutex_init(&acpi_desc->spa_map_mutex); 1422 1007 1423 1008 data = (u8 *) acpi_desc->nfit; 1424 1009 end = data + sz;
+49
drivers/acpi/nfit.h
··· 52 52 struct list_head list; 53 53 }; 54 54 55 + struct nfit_idt { 56 + struct acpi_nfit_interleave *idt; 57 + struct list_head list; 58 + }; 59 + 55 60 struct nfit_memdev { 56 61 struct acpi_nfit_memory_map *memdev; 57 62 struct list_head list; ··· 67 62 struct nvdimm *nvdimm; 68 63 struct acpi_nfit_memory_map *memdev_dcr; 69 64 struct acpi_nfit_memory_map *memdev_pmem; 65 + struct acpi_nfit_memory_map *memdev_bdw; 70 66 struct acpi_nfit_control_region *dcr; 71 67 struct acpi_nfit_data_region *bdw; 72 68 struct acpi_nfit_system_address *spa_dcr; 73 69 struct acpi_nfit_system_address *spa_bdw; 70 + struct acpi_nfit_interleave *idt_dcr; 71 + struct acpi_nfit_interleave *idt_bdw; 74 72 struct list_head list; 75 73 struct acpi_device *adev; 76 74 unsigned long dsm_mask; ··· 82 74 struct acpi_nfit_desc { 83 75 struct nvdimm_bus_descriptor nd_desc; 84 76 struct acpi_table_nfit *nfit; 77 + struct mutex spa_map_mutex; 78 + struct list_head spa_maps; 85 79 struct list_head memdevs; 86 80 struct list_head dimms; 87 81 struct list_head spas; 88 82 struct list_head dcrs; 89 83 struct list_head bdws; 84 + struct list_head idts; 90 85 struct nvdimm_bus *nvdimm_bus; 91 86 struct device *dev; 92 87 unsigned long dimm_dsm_force_en; 93 88 }; 89 + 90 + enum nd_blk_mmio_selector { 91 + BDW, 92 + DCR, 93 + }; 94 + 95 + struct nfit_blk { 96 + struct nfit_blk_mmio { 97 + union { 98 + void __iomem *base; 99 + void *aperture; 100 + }; 101 + u64 size; 102 + u64 base_offset; 103 + u32 line_size; 104 + u32 num_lines; 105 + u32 table_size; 106 + struct acpi_nfit_interleave *idt; 107 + struct acpi_nfit_system_address *spa; 108 + } mmio[2]; 109 + struct nd_region *nd_region; 110 + u64 bdw_offset; /* post interleave offset */ 111 + u64 stat_offset; 112 + u64 cmd_offset; 113 + }; 114 + 115 + struct nfit_spa_mapping { 116 + struct acpi_nfit_desc *acpi_desc; 117 + struct acpi_nfit_system_address *spa; 118 + struct list_head list; 119 + struct kref kref; 120 + void __iomem *iomem; 121 + }; 122 + 123 + static inline struct nfit_spa_mapping *to_spa_map(struct kref *kref) 124 + { 125 + return container_of(kref, struct nfit_spa_mapping, kref); 126 + } 94 127 95 128 static inline struct acpi_nfit_memory_map *__to_nfit_memdev( 96 129 struct nfit_mem *nfit_mem)
+13
drivers/nvdimm/Kconfig
··· 34 34 35 35 Say Y if you want to use an NVDIMM 36 36 37 + config ND_BLK 38 + tristate "BLK: Block data window (aperture) device support" 39 + default LIBNVDIMM 40 + select ND_BTT if BTT 41 + help 42 + Support NVDIMMs, or other devices, that implement a BLK-mode 43 + access capability. BLK-mode access uses memory-mapped-i/o 44 + apertures to access persistent media. 45 + 46 + Say Y if your platform firmware emits an ACPI.NFIT table 47 + (CONFIG_ACPI_NFIT), or otherwise exposes BLK-mode 48 + capabilities. 49 + 37 50 config ND_BTT 38 51 tristate 39 52
+3
drivers/nvdimm/Makefile
··· 1 1 obj-$(CONFIG_LIBNVDIMM) += libnvdimm.o 2 2 obj-$(CONFIG_BLK_DEV_PMEM) += nd_pmem.o 3 3 obj-$(CONFIG_ND_BTT) += nd_btt.o 4 + obj-$(CONFIG_ND_BLK) += nd_blk.o 4 5 5 6 nd_pmem-y := pmem.o 6 7 7 8 nd_btt-y := btt.o 9 + 10 + nd_blk-y := blk.o 8 11 9 12 libnvdimm-y := core.o 10 13 libnvdimm-y += bus.o
+245
drivers/nvdimm/blk.c
··· 1 + /* 2 + * NVDIMM Block Window Driver 3 + * Copyright (c) 2014, Intel Corporation. 4 + * 5 + * This program is free software; you can redistribute it and/or modify it 6 + * under the terms and conditions of the GNU General Public License, 7 + * version 2, as published by the Free Software Foundation. 8 + * 9 + * This program is distributed in the hope it will be useful, but WITHOUT 10 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 + * more details. 13 + */ 14 + 15 + #include <linux/blkdev.h> 16 + #include <linux/fs.h> 17 + #include <linux/genhd.h> 18 + #include <linux/module.h> 19 + #include <linux/moduleparam.h> 20 + #include <linux/nd.h> 21 + #include <linux/sizes.h> 22 + #include "nd.h" 23 + 24 + struct nd_blk_device { 25 + struct request_queue *queue; 26 + struct gendisk *disk; 27 + struct nd_namespace_blk *nsblk; 28 + struct nd_blk_region *ndbr; 29 + size_t disk_size; 30 + }; 31 + 32 + static int nd_blk_major; 33 + 34 + static resource_size_t to_dev_offset(struct nd_namespace_blk *nsblk, 35 + resource_size_t ns_offset, unsigned int len) 36 + { 37 + int i; 38 + 39 + for (i = 0; i < nsblk->num_resources; i++) { 40 + if (ns_offset < resource_size(nsblk->res[i])) { 41 + if (ns_offset + len > resource_size(nsblk->res[i])) { 42 + dev_WARN_ONCE(&nsblk->common.dev, 1, 43 + "illegal request\n"); 44 + return SIZE_MAX; 45 + } 46 + return nsblk->res[i]->start + ns_offset; 47 + } 48 + ns_offset -= resource_size(nsblk->res[i]); 49 + } 50 + 51 + dev_WARN_ONCE(&nsblk->common.dev, 1, "request out of range\n"); 52 + return SIZE_MAX; 53 + } 54 + 55 + static void nd_blk_make_request(struct request_queue *q, struct bio *bio) 56 + { 57 + struct block_device *bdev = bio->bi_bdev; 58 + struct gendisk *disk = bdev->bd_disk; 59 + struct nd_namespace_blk *nsblk; 60 + struct nd_blk_device *blk_dev; 61 + struct nd_blk_region *ndbr; 62 + struct bvec_iter iter; 63 + struct bio_vec bvec; 64 + int err = 0, rw; 65 + 66 + blk_dev = disk->private_data; 67 + nsblk = blk_dev->nsblk; 68 + ndbr = blk_dev->ndbr; 69 + rw = bio_data_dir(bio); 70 + bio_for_each_segment(bvec, bio, iter) { 71 + unsigned int len = bvec.bv_len; 72 + resource_size_t dev_offset; 73 + void *iobuf; 74 + 75 + BUG_ON(len > PAGE_SIZE); 76 + 77 + dev_offset = to_dev_offset(nsblk, 78 + iter.bi_sector << SECTOR_SHIFT, len); 79 + if (dev_offset == SIZE_MAX) { 80 + err = -EIO; 81 + goto out; 82 + } 83 + 84 + iobuf = kmap_atomic(bvec.bv_page); 85 + err = ndbr->do_io(ndbr, dev_offset, iobuf + bvec.bv_offset, 86 + len, rw); 87 + kunmap_atomic(iobuf); 88 + if (err) 89 + goto out; 90 + } 91 + 92 + out: 93 + bio_endio(bio, err); 94 + } 95 + 96 + static int nd_blk_rw_bytes(struct nd_namespace_common *ndns, 97 + resource_size_t offset, void *iobuf, size_t n, int rw) 98 + { 99 + struct nd_blk_device *blk_dev = dev_get_drvdata(ndns->claim); 100 + struct nd_namespace_blk *nsblk = blk_dev->nsblk; 101 + struct nd_blk_region *ndbr = blk_dev->ndbr; 102 + resource_size_t dev_offset; 103 + 104 + dev_offset = to_dev_offset(nsblk, offset, n); 105 + 106 + if (unlikely(offset + n > blk_dev->disk_size)) { 107 + dev_WARN_ONCE(&ndns->dev, 1, "request out of range\n"); 108 + return -EFAULT; 109 + } 110 + 111 + if (dev_offset == SIZE_MAX) 112 + return -EIO; 113 + 114 + return ndbr->do_io(ndbr, dev_offset, iobuf, n, rw); 115 + } 116 + 117 + static const struct block_device_operations nd_blk_fops = { 118 + .owner = THIS_MODULE, 119 + }; 120 + 121 + static int nd_blk_attach_disk(struct nd_namespace_common *ndns, 122 + struct nd_blk_device *blk_dev) 123 + { 124 + struct nd_namespace_blk *nsblk = to_nd_namespace_blk(&ndns->dev); 125 + struct gendisk *disk; 126 + 127 + blk_dev->queue = blk_alloc_queue(GFP_KERNEL); 128 + if (!blk_dev->queue) 129 + return -ENOMEM; 130 + 131 + blk_queue_make_request(blk_dev->queue, nd_blk_make_request); 132 + blk_queue_max_hw_sectors(blk_dev->queue, UINT_MAX); 133 + blk_queue_bounce_limit(blk_dev->queue, BLK_BOUNCE_ANY); 134 + blk_queue_logical_block_size(blk_dev->queue, nsblk->lbasize); 135 + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, blk_dev->queue); 136 + 137 + disk = blk_dev->disk = alloc_disk(0); 138 + if (!disk) { 139 + blk_cleanup_queue(blk_dev->queue); 140 + return -ENOMEM; 141 + } 142 + 143 + disk->driverfs_dev = &ndns->dev; 144 + disk->major = nd_blk_major; 145 + disk->first_minor = 0; 146 + disk->fops = &nd_blk_fops; 147 + disk->private_data = blk_dev; 148 + disk->queue = blk_dev->queue; 149 + disk->flags = GENHD_FL_EXT_DEVT; 150 + nvdimm_namespace_disk_name(ndns, disk->disk_name); 151 + set_capacity(disk, blk_dev->disk_size >> SECTOR_SHIFT); 152 + add_disk(disk); 153 + 154 + return 0; 155 + } 156 + 157 + static int nd_blk_probe(struct device *dev) 158 + { 159 + struct nd_namespace_common *ndns; 160 + struct nd_blk_device *blk_dev; 161 + int rc; 162 + 163 + ndns = nvdimm_namespace_common_probe(dev); 164 + if (IS_ERR(ndns)) 165 + return PTR_ERR(ndns); 166 + 167 + blk_dev = kzalloc(sizeof(*blk_dev), GFP_KERNEL); 168 + if (!blk_dev) 169 + return -ENOMEM; 170 + 171 + blk_dev->disk_size = nvdimm_namespace_capacity(ndns); 172 + blk_dev->ndbr = to_nd_blk_region(dev->parent); 173 + blk_dev->nsblk = to_nd_namespace_blk(&ndns->dev); 174 + dev_set_drvdata(dev, blk_dev); 175 + 176 + ndns->rw_bytes = nd_blk_rw_bytes; 177 + if (is_nd_btt(dev)) 178 + rc = nvdimm_namespace_attach_btt(ndns); 179 + else if (nd_btt_probe(ndns, blk_dev) == 0) { 180 + /* we'll come back as btt-blk */ 181 + rc = -ENXIO; 182 + } else 183 + rc = nd_blk_attach_disk(ndns, blk_dev); 184 + if (rc) 185 + kfree(blk_dev); 186 + return rc; 187 + } 188 + 189 + static void nd_blk_detach_disk(struct nd_blk_device *blk_dev) 190 + { 191 + del_gendisk(blk_dev->disk); 192 + put_disk(blk_dev->disk); 193 + blk_cleanup_queue(blk_dev->queue); 194 + } 195 + 196 + static int nd_blk_remove(struct device *dev) 197 + { 198 + struct nd_blk_device *blk_dev = dev_get_drvdata(dev); 199 + 200 + if (is_nd_btt(dev)) 201 + nvdimm_namespace_detach_btt(to_nd_btt(dev)->ndns); 202 + else 203 + nd_blk_detach_disk(blk_dev); 204 + kfree(blk_dev); 205 + 206 + return 0; 207 + } 208 + 209 + static struct nd_device_driver nd_blk_driver = { 210 + .probe = nd_blk_probe, 211 + .remove = nd_blk_remove, 212 + .drv = { 213 + .name = "nd_blk", 214 + }, 215 + .type = ND_DRIVER_NAMESPACE_BLK, 216 + }; 217 + 218 + static int __init nd_blk_init(void) 219 + { 220 + int rc; 221 + 222 + rc = register_blkdev(0, "nd_blk"); 223 + if (rc < 0) 224 + return rc; 225 + 226 + nd_blk_major = rc; 227 + rc = nd_driver_register(&nd_blk_driver); 228 + 229 + if (rc < 0) 230 + unregister_blkdev(nd_blk_major, "nd_blk"); 231 + 232 + return rc; 233 + } 234 + 235 + static void __exit nd_blk_exit(void) 236 + { 237 + driver_unregister(&nd_blk_driver.drv); 238 + unregister_blkdev(nd_blk_major, "nd_blk"); 239 + } 240 + 241 + MODULE_AUTHOR("Ross Zwisler <ross.zwisler@linux.intel.com>"); 242 + MODULE_LICENSE("GPL v2"); 243 + MODULE_ALIAS_ND_DEVICE(ND_DEVICE_NAMESPACE_BLK); 244 + module_init(nd_blk_init); 245 + module_exit(nd_blk_exit);
+9
drivers/nvdimm/dimm_devs.c
··· 209 209 } 210 210 EXPORT_SYMBOL_GPL(to_nvdimm); 211 211 212 + struct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr) 213 + { 214 + struct nd_region *nd_region = &ndbr->nd_region; 215 + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 216 + 217 + return nd_mapping->nvdimm; 218 + } 219 + EXPORT_SYMBOL_GPL(nd_blk_region_to_dimm); 220 + 212 221 struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping) 213 222 { 214 223 struct nvdimm *nvdimm = nd_mapping->nvdimm;
+64 -1
drivers/nvdimm/namespace_devs.c
··· 173 173 return size; 174 174 } 175 175 176 + static bool __nd_namespace_blk_validate(struct nd_namespace_blk *nsblk) 177 + { 178 + struct nd_region *nd_region = to_nd_region(nsblk->common.dev.parent); 179 + struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 180 + struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 181 + struct nd_label_id label_id; 182 + struct resource *res; 183 + int count, i; 184 + 185 + if (!nsblk->uuid || !nsblk->lbasize || !ndd) 186 + return false; 187 + 188 + count = 0; 189 + nd_label_gen_id(&label_id, nsblk->uuid, NSLABEL_FLAG_LOCAL); 190 + for_each_dpa_resource(ndd, res) { 191 + if (strcmp(res->name, label_id.id) != 0) 192 + continue; 193 + /* 194 + * Resources with unacknoweldged adjustments indicate a 195 + * failure to update labels 196 + */ 197 + if (res->flags & DPA_RESOURCE_ADJUSTED) 198 + return false; 199 + count++; 200 + } 201 + 202 + /* These values match after a successful label update */ 203 + if (count != nsblk->num_resources) 204 + return false; 205 + 206 + for (i = 0; i < nsblk->num_resources; i++) { 207 + struct resource *found = NULL; 208 + 209 + for_each_dpa_resource(ndd, res) 210 + if (res == nsblk->res[i]) { 211 + found = res; 212 + break; 213 + } 214 + /* stale resource */ 215 + if (!found) 216 + return false; 217 + } 218 + 219 + return true; 220 + } 221 + 222 + resource_size_t nd_namespace_blk_validate(struct nd_namespace_blk *nsblk) 223 + { 224 + resource_size_t size; 225 + 226 + nvdimm_bus_lock(&nsblk->common.dev); 227 + size = __nd_namespace_blk_validate(nsblk); 228 + nvdimm_bus_unlock(&nsblk->common.dev); 229 + 230 + return size; 231 + } 232 + EXPORT_SYMBOL(nd_namespace_blk_validate); 233 + 234 + 176 235 static int nd_namespace_label_update(struct nd_region *nd_region, 177 236 struct device *dev) 178 237 { ··· 1283 1224 return ERR_PTR(-ENODEV); 1284 1225 } 1285 1226 } else if (is_namespace_blk(&ndns->dev)) { 1286 - return ERR_PTR(-ENODEV); /* TODO */ 1227 + struct nd_namespace_blk *nsblk; 1228 + 1229 + nsblk = to_nd_namespace_blk(&ndns->dev); 1230 + if (!nd_namespace_blk_validate(nsblk)) 1231 + return ERR_PTR(-ENODEV); 1287 1232 } 1288 1233 1289 1234 return ndns;
+1 -2
drivers/nvdimm/nd-core.h
··· 43 43 }; 44 44 45 45 bool is_nvdimm(struct device *dev); 46 - bool is_nd_blk(struct device *dev); 47 46 bool is_nd_pmem(struct device *dev); 48 - struct nd_btt; 47 + bool is_nd_blk(struct device *dev); 49 48 struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev); 50 49 int __init nvdimm_bus_init(void); 51 50 void nvdimm_bus_exit(void);
+11 -2
drivers/nvdimm/nd.h
··· 102 102 struct nd_mapping mapping[0]; 103 103 }; 104 104 105 + struct nd_blk_region { 106 + int (*enable)(struct nvdimm_bus *nvdimm_bus, struct device *dev); 107 + void (*disable)(struct nvdimm_bus *nvdimm_bus, struct device *dev); 108 + int (*do_io)(struct nd_blk_region *ndbr, resource_size_t dpa, 109 + void *iobuf, u64 len, int rw); 110 + void *blk_provider_data; 111 + struct nd_region nd_region; 112 + }; 113 + 105 114 /* 106 115 * Lookup next in the repeating sequence of 01, 10, and 11. 107 116 */ ··· 180 171 181 172 #endif 182 173 struct nd_region *to_nd_region(struct device *dev); 183 - unsigned int nd_region_acquire_lane(struct nd_region *nd_region); 184 - void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane); 185 174 int nd_region_to_nstype(struct nd_region *nd_region); 186 175 int nd_region_register_namespaces(struct nd_region *nd_region, int *err); 187 176 u64 nd_region_interleave_set_cookie(struct nd_region *nd_region); ··· 199 192 int nvdimm_namespace_detach_btt(struct nd_namespace_common *ndns); 200 193 const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns, 201 194 char *name); 195 + int nd_blk_region_init(struct nd_region *nd_region); 196 + resource_size_t nd_namespace_blk_validate(struct nd_namespace_blk *nsblk); 202 197 #endif /* __ND_H__ */
+6 -2
drivers/nvdimm/region.c
··· 18 18 19 19 static int nd_region_probe(struct device *dev) 20 20 { 21 - int err; 21 + int err, rc; 22 22 static unsigned long once; 23 23 struct nd_region_namespaces *num_ns; 24 24 struct nd_region *nd_region = to_nd_region(dev); 25 - int rc = nd_region_register_namespaces(nd_region, &err); 26 25 27 26 if (nd_region->num_lanes > num_online_cpus() 28 27 && nd_region->num_lanes < num_possible_cpus() ··· 33 34 nd_region->num_lanes); 34 35 } 35 36 37 + rc = nd_blk_region_init(nd_region); 38 + if (rc) 39 + return rc; 40 + 41 + rc = nd_region_register_namespaces(nd_region, &err); 36 42 num_ns = devm_kzalloc(dev, sizeof(*num_ns), GFP_KERNEL); 37 43 if (!num_ns) 38 44 return -ENOMEM;
+80 -11
drivers/nvdimm/region_devs.c
··· 11 11 * General Public License for more details. 12 12 */ 13 13 #include <linux/scatterlist.h> 14 + #include <linux/highmem.h> 14 15 #include <linux/sched.h> 15 16 #include <linux/slab.h> 16 17 #include <linux/sort.h> ··· 35 34 } 36 35 free_percpu(nd_region->lane); 37 36 ida_simple_remove(&region_ida, nd_region->id); 38 - kfree(nd_region); 37 + if (is_nd_blk(dev)) 38 + kfree(to_nd_blk_region(dev)); 39 + else 40 + kfree(nd_region); 39 41 } 40 42 41 43 static struct device_type nd_blk_device_type = { ··· 74 70 return nd_region; 75 71 } 76 72 EXPORT_SYMBOL_GPL(to_nd_region); 73 + 74 + struct nd_blk_region *to_nd_blk_region(struct device *dev) 75 + { 76 + struct nd_region *nd_region = to_nd_region(dev); 77 + 78 + WARN_ON(!is_nd_blk(dev)); 79 + return container_of(nd_region, struct nd_blk_region, nd_region); 80 + } 81 + EXPORT_SYMBOL_GPL(to_nd_blk_region); 82 + 83 + void *nd_region_provider_data(struct nd_region *nd_region) 84 + { 85 + return nd_region->provider_data; 86 + } 87 + EXPORT_SYMBOL_GPL(nd_region_provider_data); 88 + 89 + void *nd_blk_region_provider_data(struct nd_blk_region *ndbr) 90 + { 91 + return ndbr->blk_provider_data; 92 + } 93 + EXPORT_SYMBOL_GPL(nd_blk_region_provider_data); 94 + 95 + void nd_blk_region_set_provider_data(struct nd_blk_region *ndbr, void *data) 96 + { 97 + ndbr->blk_provider_data = data; 98 + } 99 + EXPORT_SYMBOL_GPL(nd_blk_region_set_provider_data); 77 100 78 101 /** 79 102 * nd_region_to_nstype() - region to an integer namespace type ··· 396 365 /* 397 366 * Upon successful probe/remove, take/release a reference on the 398 367 * associated interleave set (if present), and plant new btt + namespace 399 - * seeds. 368 + * seeds. Also, on the removal of a BLK region, notify the provider to 369 + * disable the region. 400 370 */ 401 371 static void nd_region_notify_driver_action(struct nvdimm_bus *nvdimm_bus, 402 372 struct device *dev, bool probe) ··· 417 385 nd_mapping->labels = NULL; 418 386 put_ndd(ndd); 419 387 nd_mapping->ndd = NULL; 420 - atomic_dec(&nvdimm->busy); 388 + if (ndd) 389 + atomic_dec(&nvdimm->busy); 421 390 } 391 + 392 + if (is_nd_pmem(dev)) 393 + return; 394 + 395 + to_nd_blk_region(dev)->disable(nvdimm_bus, dev); 422 396 } 423 397 if (dev->parent && is_nd_blk(dev->parent) && probe) { 424 398 nd_region = to_nd_region(dev->parent); ··· 564 526 }; 565 527 EXPORT_SYMBOL_GPL(nd_mapping_attribute_group); 566 528 567 - void *nd_region_provider_data(struct nd_region *nd_region) 529 + int nd_blk_region_init(struct nd_region *nd_region) 568 530 { 569 - return nd_region->provider_data; 531 + struct device *dev = &nd_region->dev; 532 + struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 533 + 534 + if (!is_nd_blk(dev)) 535 + return 0; 536 + 537 + if (nd_region->ndr_mappings < 1) { 538 + dev_err(dev, "invalid BLK region\n"); 539 + return -ENXIO; 540 + } 541 + 542 + return to_nd_blk_region(dev)->enable(nvdimm_bus, dev); 570 543 } 571 - EXPORT_SYMBOL_GPL(nd_region_provider_data); 572 544 573 545 /** 574 546 * nd_region_acquire_lane - allocate and lock a lane ··· 639 591 { 640 592 struct nd_region *nd_region; 641 593 struct device *dev; 594 + void *region_buf; 642 595 unsigned int i; 643 596 644 597 for (i = 0; i < ndr_desc->num_mappings; i++) { ··· 654 605 } 655 606 } 656 607 657 - nd_region = kzalloc(sizeof(struct nd_region) 658 - + sizeof(struct nd_mapping) * ndr_desc->num_mappings, 659 - GFP_KERNEL); 660 - if (!nd_region) 608 + if (dev_type == &nd_blk_device_type) { 609 + struct nd_blk_region_desc *ndbr_desc; 610 + struct nd_blk_region *ndbr; 611 + 612 + ndbr_desc = to_blk_region_desc(ndr_desc); 613 + ndbr = kzalloc(sizeof(*ndbr) + sizeof(struct nd_mapping) 614 + * ndr_desc->num_mappings, 615 + GFP_KERNEL); 616 + if (ndbr) { 617 + nd_region = &ndbr->nd_region; 618 + ndbr->enable = ndbr_desc->enable; 619 + ndbr->disable = ndbr_desc->disable; 620 + ndbr->do_io = ndbr_desc->do_io; 621 + } 622 + region_buf = ndbr; 623 + } else { 624 + nd_region = kzalloc(sizeof(struct nd_region) 625 + + sizeof(struct nd_mapping) 626 + * ndr_desc->num_mappings, 627 + GFP_KERNEL); 628 + region_buf = nd_region; 629 + } 630 + 631 + if (!region_buf) 661 632 return NULL; 662 633 nd_region->id = ida_simple_get(&region_ida, 0, 0, GFP_KERNEL); 663 634 if (nd_region->id < 0) ··· 723 654 err_percpu: 724 655 ida_simple_remove(&region_ida, nd_region->id); 725 656 err_id: 726 - kfree(nd_region); 657 + kfree(region_buf); 727 658 return NULL; 728 659 } 729 660
+25 -2
include/linux/libnvdimm.h
··· 14 14 */ 15 15 #ifndef __LIBNVDIMM_H__ 16 16 #define __LIBNVDIMM_H__ 17 + #include <linux/kernel.h> 17 18 #include <linux/sizes.h> 18 19 #include <linux/types.h> 19 20 ··· 90 89 }; 91 90 92 91 struct nvdimm_bus; 93 - struct device; 94 92 struct module; 93 + struct device; 94 + struct nd_blk_region; 95 + struct nd_blk_region_desc { 96 + int (*enable)(struct nvdimm_bus *nvdimm_bus, struct device *dev); 97 + void (*disable)(struct nvdimm_bus *nvdimm_bus, struct device *dev); 98 + int (*do_io)(struct nd_blk_region *ndbr, resource_size_t dpa, 99 + void *iobuf, u64 len, int rw); 100 + struct nd_region_desc ndr_desc; 101 + }; 102 + 103 + static inline struct nd_blk_region_desc *to_blk_region_desc( 104 + struct nd_region_desc *ndr_desc) 105 + { 106 + return container_of(ndr_desc, struct nd_blk_region_desc, ndr_desc); 107 + 108 + } 109 + 95 110 struct nvdimm_bus *__nvdimm_bus_register(struct device *parent, 96 111 struct nvdimm_bus_descriptor *nfit_desc, struct module *module); 97 112 #define nvdimm_bus_register(parent, desc) \ ··· 116 99 struct nvdimm_bus *to_nvdimm_bus(struct device *dev); 117 100 struct nvdimm *to_nvdimm(struct device *dev); 118 101 struct nd_region *to_nd_region(struct device *dev); 102 + struct nd_blk_region *to_nd_blk_region(struct device *dev); 119 103 struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus); 120 104 const char *nvdimm_name(struct nvdimm *nvdimm); 121 105 void *nvdimm_provider_data(struct nvdimm *nvdimm); 122 - void *nd_region_provider_data(struct nd_region *nd_region); 123 106 struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, 124 107 const struct attribute_group **groups, unsigned long flags, 125 108 unsigned long *dsm_mask); ··· 137 120 struct nd_region_desc *ndr_desc); 138 121 struct nd_region *nvdimm_volatile_region_create(struct nvdimm_bus *nvdimm_bus, 139 122 struct nd_region_desc *ndr_desc); 123 + void *nd_region_provider_data(struct nd_region *nd_region); 124 + void *nd_blk_region_provider_data(struct nd_blk_region *ndbr); 125 + void nd_blk_region_set_provider_data(struct nd_blk_region *ndbr, void *data); 126 + struct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr); 127 + unsigned int nd_region_acquire_lane(struct nd_region *nd_region); 128 + void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane); 140 129 u64 nd_fletcher64(void *addr, size_t len, bool le); 141 130 #endif /* __LIBNVDIMM_H__ */