14a826c83SDan Williams /* 24a826c83SDan Williams * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 34a826c83SDan Williams * 44a826c83SDan Williams * This program is free software; you can redistribute it and/or modify 54a826c83SDan Williams * it under the terms of version 2 of the GNU General Public License as 64a826c83SDan Williams * published by the Free Software Foundation. 74a826c83SDan Williams * 84a826c83SDan Williams * This program is distributed in the hope that it will be useful, but 94a826c83SDan Williams * WITHOUT ANY WARRANTY; without even the implied warranty of 104a826c83SDan Williams * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 114a826c83SDan Williams * General Public License for more details. 124a826c83SDan Williams */ 134a826c83SDan Williams #include <linux/device.h> 144a826c83SDan Williams #include <linux/ndctl.h> 15b3fde74eSDan Williams #include <linux/uuid.h> 16f524bf27SDan Williams #include <linux/slab.h> 174a826c83SDan Williams #include <linux/io.h> 184a826c83SDan Williams #include <linux/nd.h> 194a826c83SDan Williams #include "nd-core.h" 204a826c83SDan Williams #include "label.h" 214a826c83SDan Williams #include "nd.h" 224a826c83SDan Williams 23b3fde74eSDan Williams static guid_t nvdimm_btt_guid; 2414e49454SVishal Verma static guid_t nvdimm_btt2_guid; 25b3fde74eSDan Williams static guid_t nvdimm_pfn_guid; 26b3fde74eSDan Williams static guid_t nvdimm_dax_guid; 27b3fde74eSDan Williams 284a826c83SDan Williams static u32 best_seq(u32 a, u32 b) 294a826c83SDan Williams { 304a826c83SDan Williams a &= NSINDEX_SEQ_MASK; 314a826c83SDan Williams b &= NSINDEX_SEQ_MASK; 324a826c83SDan Williams 334a826c83SDan Williams if (a == 0 || a == b) 344a826c83SDan Williams return b; 354a826c83SDan Williams else if (b == 0) 364a826c83SDan Williams return a; 374a826c83SDan Williams else if (nd_inc_seq(a) == b) 384a826c83SDan Williams return b; 394a826c83SDan Williams else 404a826c83SDan Williams return a; 414a826c83SDan Williams } 424a826c83SDan Williams 43564e871aSDan Williams unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd) 44564e871aSDan Williams { 45564e871aSDan Williams return ndd->nslabel_size; 46564e871aSDan Williams } 47564e871aSDan Williams 4802881768SDan Williams int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd) 4902881768SDan Williams { 5002881768SDan Williams return ndd->nsarea.config_size / (sizeof_namespace_label(ndd) + 1); 5102881768SDan Williams } 5202881768SDan Williams 534a826c83SDan Williams size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd) 544a826c83SDan Williams { 5502881768SDan Williams u32 nslot, space, size; 564a826c83SDan Williams 574a826c83SDan Williams /* 584a826c83SDan Williams * The minimum index space is 512 bytes, with that amount of 594a826c83SDan Williams * index we can describe ~1400 labels which is less than a byte 604a826c83SDan Williams * of overhead per label. Round up to a byte of overhead per 614a826c83SDan Williams * label and determine the size of the index region. Yes, this 624a826c83SDan Williams * starts to waste space at larger config_sizes, but it's 634a826c83SDan Williams * unlikely we'll ever see anything but 128K. 644a826c83SDan Williams */ 6502881768SDan Williams nslot = nvdimm_num_label_slots(ndd); 6602881768SDan Williams space = ndd->nsarea.config_size - nslot * sizeof_namespace_label(ndd); 6702881768SDan Williams size = ALIGN(sizeof(struct nd_namespace_index) + DIV_ROUND_UP(nslot, 8), 6802881768SDan Williams NSINDEX_ALIGN) * 2; 6902881768SDan Williams if (size <= space) 7002881768SDan Williams return size / 2; 714a826c83SDan Williams 7202881768SDan Williams dev_err(ndd->dev, "label area (%d) too small to host (%d byte) labels\n", 7302881768SDan Williams ndd->nsarea.config_size, sizeof_namespace_label(ndd)); 7402881768SDan Williams return 0; 75f524bf27SDan Williams } 76f524bf27SDan Williams 77564e871aSDan Williams static int __nd_label_validate(struct nvdimm_drvdata *ndd) 784a826c83SDan Williams { 794a826c83SDan Williams /* 804a826c83SDan Williams * On media label format consists of two index blocks followed 814a826c83SDan Williams * by an array of labels. None of these structures are ever 824a826c83SDan Williams * updated in place. A sequence number tracks the current 834a826c83SDan Williams * active index and the next one to write, while labels are 844a826c83SDan Williams * written to free slots. 854a826c83SDan Williams * 864a826c83SDan Williams * +------------+ 874a826c83SDan Williams * | | 884a826c83SDan Williams * | nsindex0 | 894a826c83SDan Williams * | | 904a826c83SDan Williams * +------------+ 914a826c83SDan Williams * | | 924a826c83SDan Williams * | nsindex1 | 934a826c83SDan Williams * | | 944a826c83SDan Williams * +------------+ 954a826c83SDan Williams * | label0 | 964a826c83SDan Williams * +------------+ 974a826c83SDan Williams * | label1 | 984a826c83SDan Williams * +------------+ 994a826c83SDan Williams * | | 1004a826c83SDan Williams * ....nslot... 1014a826c83SDan Williams * | | 1024a826c83SDan Williams * +------------+ 1034a826c83SDan Williams * | labelN | 1044a826c83SDan Williams * +------------+ 1054a826c83SDan Williams */ 1064a826c83SDan Williams struct nd_namespace_index *nsindex[] = { 1074a826c83SDan Williams to_namespace_index(ndd, 0), 1084a826c83SDan Williams to_namespace_index(ndd, 1), 1094a826c83SDan Williams }; 1104a826c83SDan Williams const int num_index = ARRAY_SIZE(nsindex); 1114a826c83SDan Williams struct device *dev = ndd->dev; 1124a826c83SDan Williams bool valid[2] = { 0 }; 1134a826c83SDan Williams int i, num_valid = 0; 1144a826c83SDan Williams u32 seq; 1154a826c83SDan Williams 1164a826c83SDan Williams for (i = 0; i < num_index; i++) { 1174a826c83SDan Williams u32 nslot; 1184a826c83SDan Williams u8 sig[NSINDEX_SIG_LEN]; 1194a826c83SDan Williams u64 sum_save, sum, size; 120564e871aSDan Williams unsigned int version, labelsize; 1214a826c83SDan Williams 1224a826c83SDan Williams memcpy(sig, nsindex[i]->sig, NSINDEX_SIG_LEN); 1234a826c83SDan Williams if (memcmp(sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN) != 0) { 1244a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d signature invalid\n", 1254a826c83SDan Williams __func__, i); 1264a826c83SDan Williams continue; 1274a826c83SDan Williams } 128564e871aSDan Williams 129564e871aSDan Williams /* label sizes larger than 128 arrived with v1.2 */ 130564e871aSDan Williams version = __le16_to_cpu(nsindex[i]->major) * 100 131564e871aSDan Williams + __le16_to_cpu(nsindex[i]->minor); 132564e871aSDan Williams if (version >= 102) 133564e871aSDan Williams labelsize = 1 << (7 + nsindex[i]->labelsize); 134564e871aSDan Williams else 135564e871aSDan Williams labelsize = 128; 136564e871aSDan Williams 137564e871aSDan Williams if (labelsize != sizeof_namespace_label(ndd)) { 138564e871aSDan Williams dev_dbg(dev, "%s: nsindex%d labelsize %d invalid\n", 139564e871aSDan Williams __func__, i, nsindex[i]->labelsize); 140564e871aSDan Williams continue; 141564e871aSDan Williams } 142564e871aSDan Williams 1434a826c83SDan Williams sum_save = __le64_to_cpu(nsindex[i]->checksum); 1444a826c83SDan Williams nsindex[i]->checksum = __cpu_to_le64(0); 1454a826c83SDan Williams sum = nd_fletcher64(nsindex[i], sizeof_namespace_index(ndd), 1); 1464a826c83SDan Williams nsindex[i]->checksum = __cpu_to_le64(sum_save); 1474a826c83SDan Williams if (sum != sum_save) { 1484a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d checksum invalid\n", 1494a826c83SDan Williams __func__, i); 1504a826c83SDan Williams continue; 1514a826c83SDan Williams } 1524a826c83SDan Williams 1534a826c83SDan Williams seq = __le32_to_cpu(nsindex[i]->seq); 1544a826c83SDan Williams if ((seq & NSINDEX_SEQ_MASK) == 0) { 1554a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d sequence: %#x invalid\n", 1564a826c83SDan Williams __func__, i, seq); 1574a826c83SDan Williams continue; 1584a826c83SDan Williams } 1594a826c83SDan Williams 1604a826c83SDan Williams /* sanity check the index against expected values */ 1614a826c83SDan Williams if (__le64_to_cpu(nsindex[i]->myoff) 1624a826c83SDan Williams != i * sizeof_namespace_index(ndd)) { 1634a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d myoff: %#llx invalid\n", 1644a826c83SDan Williams __func__, i, (unsigned long long) 1654a826c83SDan Williams __le64_to_cpu(nsindex[i]->myoff)); 1664a826c83SDan Williams continue; 1674a826c83SDan Williams } 1684a826c83SDan Williams if (__le64_to_cpu(nsindex[i]->otheroff) 1694a826c83SDan Williams != (!i) * sizeof_namespace_index(ndd)) { 1704a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d otheroff: %#llx invalid\n", 1714a826c83SDan Williams __func__, i, (unsigned long long) 1724a826c83SDan Williams __le64_to_cpu(nsindex[i]->otheroff)); 1734a826c83SDan Williams continue; 1744a826c83SDan Williams } 1754a826c83SDan Williams 1764a826c83SDan Williams size = __le64_to_cpu(nsindex[i]->mysize); 1774a826c83SDan Williams if (size > sizeof_namespace_index(ndd) 1784a826c83SDan Williams || size < sizeof(struct nd_namespace_index)) { 1794a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d mysize: %#llx invalid\n", 1804a826c83SDan Williams __func__, i, size); 1814a826c83SDan Williams continue; 1824a826c83SDan Williams } 1834a826c83SDan Williams 1844a826c83SDan Williams nslot = __le32_to_cpu(nsindex[i]->nslot); 185564e871aSDan Williams if (nslot * sizeof_namespace_label(ndd) 1864a826c83SDan Williams + 2 * sizeof_namespace_index(ndd) 1874a826c83SDan Williams > ndd->nsarea.config_size) { 1884a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d nslot: %u invalid, config_size: %#x\n", 1894a826c83SDan Williams __func__, i, nslot, 1904a826c83SDan Williams ndd->nsarea.config_size); 1914a826c83SDan Williams continue; 1924a826c83SDan Williams } 1934a826c83SDan Williams valid[i] = true; 1944a826c83SDan Williams num_valid++; 1954a826c83SDan Williams } 1964a826c83SDan Williams 1974a826c83SDan Williams switch (num_valid) { 1984a826c83SDan Williams case 0: 1994a826c83SDan Williams break; 2004a826c83SDan Williams case 1: 2014a826c83SDan Williams for (i = 0; i < num_index; i++) 2024a826c83SDan Williams if (valid[i]) 2034a826c83SDan Williams return i; 2044a826c83SDan Williams /* can't have num_valid > 0 but valid[] = { false, false } */ 2054a826c83SDan Williams WARN_ON(1); 2064a826c83SDan Williams break; 2074a826c83SDan Williams default: 2084a826c83SDan Williams /* pick the best index... */ 2094a826c83SDan Williams seq = best_seq(__le32_to_cpu(nsindex[0]->seq), 2104a826c83SDan Williams __le32_to_cpu(nsindex[1]->seq)); 2114a826c83SDan Williams if (seq == (__le32_to_cpu(nsindex[1]->seq) & NSINDEX_SEQ_MASK)) 2124a826c83SDan Williams return 1; 2134a826c83SDan Williams else 2144a826c83SDan Williams return 0; 2154a826c83SDan Williams break; 2164a826c83SDan Williams } 2174a826c83SDan Williams 2184a826c83SDan Williams return -1; 2194a826c83SDan Williams } 2204a826c83SDan Williams 221564e871aSDan Williams int nd_label_validate(struct nvdimm_drvdata *ndd) 222564e871aSDan Williams { 223564e871aSDan Williams /* 224564e871aSDan Williams * In order to probe for and validate namespace index blocks we 225564e871aSDan Williams * need to know the size of the labels, and we can't trust the 226564e871aSDan Williams * size of the labels until we validate the index blocks. 227564e871aSDan Williams * Resolve this dependency loop by probing for known label 2288990cdf1SDan Williams * sizes, but default to v1.2 256-byte namespace labels if 2298990cdf1SDan Williams * discovery fails. 230564e871aSDan Williams */ 2318990cdf1SDan Williams int label_size[] = { 128, 256 }; 232564e871aSDan Williams int i, rc; 233564e871aSDan Williams 234564e871aSDan Williams for (i = 0; i < ARRAY_SIZE(label_size); i++) { 235564e871aSDan Williams ndd->nslabel_size = label_size[i]; 236564e871aSDan Williams rc = __nd_label_validate(ndd); 237564e871aSDan Williams if (rc >= 0) 238564e871aSDan Williams return rc; 239564e871aSDan Williams } 240564e871aSDan Williams 241564e871aSDan Williams return -1; 242564e871aSDan Williams } 243564e871aSDan Williams 2444a826c83SDan Williams void nd_label_copy(struct nvdimm_drvdata *ndd, struct nd_namespace_index *dst, 2454a826c83SDan Williams struct nd_namespace_index *src) 2464a826c83SDan Williams { 2474a826c83SDan Williams if (dst && src) 2484a826c83SDan Williams /* pass */; 2494a826c83SDan Williams else 2504a826c83SDan Williams return; 2514a826c83SDan Williams 2524a826c83SDan Williams memcpy(dst, src, sizeof_namespace_index(ndd)); 2534a826c83SDan Williams } 2544a826c83SDan Williams 2554a826c83SDan Williams static struct nd_namespace_label *nd_label_base(struct nvdimm_drvdata *ndd) 2564a826c83SDan Williams { 2574a826c83SDan Williams void *base = to_namespace_index(ndd, 0); 2584a826c83SDan Williams 2594a826c83SDan Williams return base + 2 * sizeof_namespace_index(ndd); 2604a826c83SDan Williams } 2614a826c83SDan Williams 262f524bf27SDan Williams static int to_slot(struct nvdimm_drvdata *ndd, 263f524bf27SDan Williams struct nd_namespace_label *nd_label) 264f524bf27SDan Williams { 265564e871aSDan Williams unsigned long label, base; 266564e871aSDan Williams 267564e871aSDan Williams label = (unsigned long) nd_label; 268564e871aSDan Williams base = (unsigned long) nd_label_base(ndd); 269564e871aSDan Williams 270564e871aSDan Williams return (label - base) / sizeof_namespace_label(ndd); 271564e871aSDan Williams } 272564e871aSDan Williams 273564e871aSDan Williams static struct nd_namespace_label *to_label(struct nvdimm_drvdata *ndd, int slot) 274564e871aSDan Williams { 275564e871aSDan Williams unsigned long label, base; 276564e871aSDan Williams 277564e871aSDan Williams base = (unsigned long) nd_label_base(ndd); 278564e871aSDan Williams label = base + sizeof_namespace_label(ndd) * slot; 279564e871aSDan Williams 280564e871aSDan Williams return (struct nd_namespace_label *) label; 281f524bf27SDan Williams } 282f524bf27SDan Williams 2834a826c83SDan Williams #define for_each_clear_bit_le(bit, addr, size) \ 2844a826c83SDan Williams for ((bit) = find_next_zero_bit_le((addr), (size), 0); \ 2854a826c83SDan Williams (bit) < (size); \ 2864a826c83SDan Williams (bit) = find_next_zero_bit_le((addr), (size), (bit) + 1)) 2874a826c83SDan Williams 2884a826c83SDan Williams /** 289f524bf27SDan Williams * preamble_index - common variable initialization for nd_label_* routines 2904a826c83SDan Williams * @ndd: dimm container for the relevant label set 291f524bf27SDan Williams * @idx: namespace_index index 2924a826c83SDan Williams * @nsindex_out: on return set to the currently active namespace index 2934a826c83SDan Williams * @free: on return set to the free label bitmap in the index 2944a826c83SDan Williams * @nslot: on return set to the number of slots in the label space 2954a826c83SDan Williams */ 296f524bf27SDan Williams static bool preamble_index(struct nvdimm_drvdata *ndd, int idx, 2974a826c83SDan Williams struct nd_namespace_index **nsindex_out, 2984a826c83SDan Williams unsigned long **free, u32 *nslot) 2994a826c83SDan Williams { 3004a826c83SDan Williams struct nd_namespace_index *nsindex; 3014a826c83SDan Williams 302f524bf27SDan Williams nsindex = to_namespace_index(ndd, idx); 3034a826c83SDan Williams if (nsindex == NULL) 3044a826c83SDan Williams return false; 3054a826c83SDan Williams 3064a826c83SDan Williams *free = (unsigned long *) nsindex->free; 3074a826c83SDan Williams *nslot = __le32_to_cpu(nsindex->nslot); 3084a826c83SDan Williams *nsindex_out = nsindex; 3094a826c83SDan Williams 3104a826c83SDan Williams return true; 3114a826c83SDan Williams } 3124a826c83SDan Williams 313bf9bccc1SDan Williams char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags) 3144a826c83SDan Williams { 3154a826c83SDan Williams if (!label_id || !uuid) 3164a826c83SDan Williams return NULL; 3174a826c83SDan Williams snprintf(label_id->id, ND_LABEL_ID_SIZE, "%s-%pUb", 3184a826c83SDan Williams flags & NSLABEL_FLAG_LOCAL ? "blk" : "pmem", uuid); 3194a826c83SDan Williams return label_id->id; 3204a826c83SDan Williams } 3214a826c83SDan Williams 322f524bf27SDan Williams static bool preamble_current(struct nvdimm_drvdata *ndd, 323f524bf27SDan Williams struct nd_namespace_index **nsindex, 324f524bf27SDan Williams unsigned long **free, u32 *nslot) 325f524bf27SDan Williams { 326f524bf27SDan Williams return preamble_index(ndd, ndd->ns_current, nsindex, 327f524bf27SDan Williams free, nslot); 328f524bf27SDan Williams } 329f524bf27SDan Williams 330f524bf27SDan Williams static bool preamble_next(struct nvdimm_drvdata *ndd, 331f524bf27SDan Williams struct nd_namespace_index **nsindex, 332f524bf27SDan Williams unsigned long **free, u32 *nslot) 333f524bf27SDan Williams { 334f524bf27SDan Williams return preamble_index(ndd, ndd->ns_next, nsindex, 335f524bf27SDan Williams free, nslot); 336f524bf27SDan Williams } 337f524bf27SDan Williams 338355d8388SDan Williams static bool slot_valid(struct nvdimm_drvdata *ndd, 339355d8388SDan Williams struct nd_namespace_label *nd_label, u32 slot) 3404a826c83SDan Williams { 3414a826c83SDan Williams /* check that we are written where we expect to be written */ 3424a826c83SDan Williams if (slot != __le32_to_cpu(nd_label->slot)) 3434a826c83SDan Williams return false; 3444a826c83SDan Williams 3454a826c83SDan Williams /* check that DPA allocations are page aligned */ 3464a826c83SDan Williams if ((__le64_to_cpu(nd_label->dpa) 3474a826c83SDan Williams | __le64_to_cpu(nd_label->rawsize)) % SZ_4K) 3484a826c83SDan Williams return false; 3494a826c83SDan Williams 350355d8388SDan Williams /* check checksum */ 351355d8388SDan Williams if (namespace_label_has(ndd, checksum)) { 352355d8388SDan Williams u64 sum, sum_save; 353355d8388SDan Williams 354355d8388SDan Williams sum_save = __le64_to_cpu(nd_label->checksum); 355355d8388SDan Williams nd_label->checksum = __cpu_to_le64(0); 356355d8388SDan Williams sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1); 357355d8388SDan Williams nd_label->checksum = __cpu_to_le64(sum_save); 358355d8388SDan Williams if (sum != sum_save) { 359355d8388SDan Williams dev_dbg(ndd->dev, "%s fail checksum. slot: %d expect: %#llx\n", 360355d8388SDan Williams __func__, slot, sum); 361355d8388SDan Williams return false; 362355d8388SDan Williams } 363355d8388SDan Williams } 364355d8388SDan Williams 3654a826c83SDan Williams return true; 3664a826c83SDan Williams } 3674a826c83SDan Williams 3684a826c83SDan Williams int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) 3694a826c83SDan Williams { 3704a826c83SDan Williams struct nd_namespace_index *nsindex; 3714a826c83SDan Williams unsigned long *free; 3724a826c83SDan Williams u32 nslot, slot; 3734a826c83SDan Williams 3744a826c83SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 3754a826c83SDan Williams return 0; /* no label, nothing to reserve */ 3764a826c83SDan Williams 3774a826c83SDan Williams for_each_clear_bit_le(slot, free, nslot) { 3784a826c83SDan Williams struct nd_namespace_label *nd_label; 3794a826c83SDan Williams struct nd_region *nd_region = NULL; 3804a826c83SDan Williams u8 label_uuid[NSLABEL_UUID_LEN]; 3814a826c83SDan Williams struct nd_label_id label_id; 3824a826c83SDan Williams struct resource *res; 3834a826c83SDan Williams u32 flags; 3844a826c83SDan Williams 385564e871aSDan Williams nd_label = to_label(ndd, slot); 3864a826c83SDan Williams 387355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) 3884a826c83SDan Williams continue; 3894a826c83SDan Williams 3904a826c83SDan Williams memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); 3914a826c83SDan Williams flags = __le32_to_cpu(nd_label->flags); 3924a826c83SDan Williams nd_label_gen_id(&label_id, label_uuid, flags); 3934a826c83SDan Williams res = nvdimm_allocate_dpa(ndd, &label_id, 3944a826c83SDan Williams __le64_to_cpu(nd_label->dpa), 3954a826c83SDan Williams __le64_to_cpu(nd_label->rawsize)); 3964a826c83SDan Williams nd_dbg_dpa(nd_region, ndd, res, "reserve\n"); 3974a826c83SDan Williams if (!res) 3984a826c83SDan Williams return -EBUSY; 3994a826c83SDan Williams } 4004a826c83SDan Williams 4014a826c83SDan Williams return 0; 4024a826c83SDan Williams } 403bf9bccc1SDan Williams 404bf9bccc1SDan Williams int nd_label_active_count(struct nvdimm_drvdata *ndd) 405bf9bccc1SDan Williams { 406bf9bccc1SDan Williams struct nd_namespace_index *nsindex; 407bf9bccc1SDan Williams unsigned long *free; 408bf9bccc1SDan Williams u32 nslot, slot; 409bf9bccc1SDan Williams int count = 0; 410bf9bccc1SDan Williams 411bf9bccc1SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 412bf9bccc1SDan Williams return 0; 413bf9bccc1SDan Williams 414bf9bccc1SDan Williams for_each_clear_bit_le(slot, free, nslot) { 415bf9bccc1SDan Williams struct nd_namespace_label *nd_label; 416bf9bccc1SDan Williams 417564e871aSDan Williams nd_label = to_label(ndd, slot); 418bf9bccc1SDan Williams 419355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) { 420bf9bccc1SDan Williams u32 label_slot = __le32_to_cpu(nd_label->slot); 421bf9bccc1SDan Williams u64 size = __le64_to_cpu(nd_label->rawsize); 422bf9bccc1SDan Williams u64 dpa = __le64_to_cpu(nd_label->dpa); 423bf9bccc1SDan Williams 424bf9bccc1SDan Williams dev_dbg(ndd->dev, 425bf9bccc1SDan Williams "%s: slot%d invalid slot: %d dpa: %llx size: %llx\n", 426bf9bccc1SDan Williams __func__, slot, label_slot, dpa, size); 427bf9bccc1SDan Williams continue; 428bf9bccc1SDan Williams } 429bf9bccc1SDan Williams count++; 430bf9bccc1SDan Williams } 431bf9bccc1SDan Williams return count; 432bf9bccc1SDan Williams } 433bf9bccc1SDan Williams 434bf9bccc1SDan Williams struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n) 435bf9bccc1SDan Williams { 436bf9bccc1SDan Williams struct nd_namespace_index *nsindex; 437bf9bccc1SDan Williams unsigned long *free; 438bf9bccc1SDan Williams u32 nslot, slot; 439bf9bccc1SDan Williams 440bf9bccc1SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 441bf9bccc1SDan Williams return NULL; 442bf9bccc1SDan Williams 443bf9bccc1SDan Williams for_each_clear_bit_le(slot, free, nslot) { 444bf9bccc1SDan Williams struct nd_namespace_label *nd_label; 445bf9bccc1SDan Williams 446564e871aSDan Williams nd_label = to_label(ndd, slot); 447355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) 448bf9bccc1SDan Williams continue; 449bf9bccc1SDan Williams 450bf9bccc1SDan Williams if (n-- == 0) 451564e871aSDan Williams return to_label(ndd, slot); 452bf9bccc1SDan Williams } 453bf9bccc1SDan Williams 454bf9bccc1SDan Williams return NULL; 455bf9bccc1SDan Williams } 456f524bf27SDan Williams 4570ba1c634SDan Williams u32 nd_label_alloc_slot(struct nvdimm_drvdata *ndd) 458f524bf27SDan Williams { 459f524bf27SDan Williams struct nd_namespace_index *nsindex; 460f524bf27SDan Williams unsigned long *free; 461f524bf27SDan Williams u32 nslot, slot; 462f524bf27SDan Williams 463f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 464f524bf27SDan Williams return UINT_MAX; 465f524bf27SDan Williams 466f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 467f524bf27SDan Williams 468f524bf27SDan Williams slot = find_next_bit_le(free, nslot, 0); 469f524bf27SDan Williams if (slot == nslot) 470f524bf27SDan Williams return UINT_MAX; 471f524bf27SDan Williams 472f524bf27SDan Williams clear_bit_le(slot, free); 473f524bf27SDan Williams 474f524bf27SDan Williams return slot; 475f524bf27SDan Williams } 476f524bf27SDan Williams 4770ba1c634SDan Williams bool nd_label_free_slot(struct nvdimm_drvdata *ndd, u32 slot) 478f524bf27SDan Williams { 479f524bf27SDan Williams struct nd_namespace_index *nsindex; 480f524bf27SDan Williams unsigned long *free; 481f524bf27SDan Williams u32 nslot; 482f524bf27SDan Williams 483f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 484f524bf27SDan Williams return false; 485f524bf27SDan Williams 486f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 487f524bf27SDan Williams 488f524bf27SDan Williams if (slot < nslot) 489f524bf27SDan Williams return !test_and_set_bit_le(slot, free); 490f524bf27SDan Williams return false; 491f524bf27SDan Williams } 492f524bf27SDan Williams 493f524bf27SDan Williams u32 nd_label_nfree(struct nvdimm_drvdata *ndd) 494f524bf27SDan Williams { 495f524bf27SDan Williams struct nd_namespace_index *nsindex; 496f524bf27SDan Williams unsigned long *free; 497f524bf27SDan Williams u32 nslot; 498f524bf27SDan Williams 499f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 500f524bf27SDan Williams 501f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 5020ba1c634SDan Williams return nvdimm_num_label_slots(ndd); 503f524bf27SDan Williams 504f524bf27SDan Williams return bitmap_weight(free, nslot); 505f524bf27SDan Williams } 506f524bf27SDan Williams 507f524bf27SDan Williams static int nd_label_write_index(struct nvdimm_drvdata *ndd, int index, u32 seq, 508f524bf27SDan Williams unsigned long flags) 509f524bf27SDan Williams { 510f524bf27SDan Williams struct nd_namespace_index *nsindex; 511f524bf27SDan Williams unsigned long offset; 512f524bf27SDan Williams u64 checksum; 513f524bf27SDan Williams u32 nslot; 514f524bf27SDan Williams int rc; 515f524bf27SDan Williams 516f524bf27SDan Williams nsindex = to_namespace_index(ndd, index); 517f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) 518f524bf27SDan Williams nslot = nvdimm_num_label_slots(ndd); 519f524bf27SDan Williams else 520f524bf27SDan Williams nslot = __le32_to_cpu(nsindex->nslot); 521f524bf27SDan Williams 522f524bf27SDan Williams memcpy(nsindex->sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN); 523564e871aSDan Williams memset(&nsindex->flags, 0, 3); 524564e871aSDan Williams nsindex->labelsize = sizeof_namespace_label(ndd) >> 8; 525f524bf27SDan Williams nsindex->seq = __cpu_to_le32(seq); 526f524bf27SDan Williams offset = (unsigned long) nsindex 527f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 528f524bf27SDan Williams nsindex->myoff = __cpu_to_le64(offset); 529f524bf27SDan Williams nsindex->mysize = __cpu_to_le64(sizeof_namespace_index(ndd)); 530f524bf27SDan Williams offset = (unsigned long) to_namespace_index(ndd, 531f524bf27SDan Williams nd_label_next_nsindex(index)) 532f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 533f524bf27SDan Williams nsindex->otheroff = __cpu_to_le64(offset); 534f524bf27SDan Williams offset = (unsigned long) nd_label_base(ndd) 535f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 536f524bf27SDan Williams nsindex->labeloff = __cpu_to_le64(offset); 537f524bf27SDan Williams nsindex->nslot = __cpu_to_le32(nslot); 538f524bf27SDan Williams nsindex->major = __cpu_to_le16(1); 5398990cdf1SDan Williams if (sizeof_namespace_label(ndd) < 256) 540f524bf27SDan Williams nsindex->minor = __cpu_to_le16(1); 5418990cdf1SDan Williams else 5428990cdf1SDan Williams nsindex->minor = __cpu_to_le16(2); 543f524bf27SDan Williams nsindex->checksum = __cpu_to_le64(0); 544f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) { 545f524bf27SDan Williams unsigned long *free = (unsigned long *) nsindex->free; 546f524bf27SDan Williams u32 nfree = ALIGN(nslot, BITS_PER_LONG); 547f524bf27SDan Williams int last_bits, i; 548f524bf27SDan Williams 549f524bf27SDan Williams memset(nsindex->free, 0xff, nfree / 8); 550f524bf27SDan Williams for (i = 0, last_bits = nfree - nslot; i < last_bits; i++) 551f524bf27SDan Williams clear_bit_le(nslot + i, free); 552f524bf27SDan Williams } 553f524bf27SDan Williams checksum = nd_fletcher64(nsindex, sizeof_namespace_index(ndd), 1); 554f524bf27SDan Williams nsindex->checksum = __cpu_to_le64(checksum); 555f524bf27SDan Williams rc = nvdimm_set_config_data(ndd, __le64_to_cpu(nsindex->myoff), 556f524bf27SDan Williams nsindex, sizeof_namespace_index(ndd)); 557f524bf27SDan Williams if (rc < 0) 558f524bf27SDan Williams return rc; 559f524bf27SDan Williams 560f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) 561f524bf27SDan Williams return 0; 562f524bf27SDan Williams 563f524bf27SDan Williams /* copy the index we just wrote to the new 'next' */ 564f524bf27SDan Williams WARN_ON(index != ndd->ns_next); 565f524bf27SDan Williams nd_label_copy(ndd, to_current_namespace_index(ndd), nsindex); 566f524bf27SDan Williams ndd->ns_current = nd_label_next_nsindex(ndd->ns_current); 567f524bf27SDan Williams ndd->ns_next = nd_label_next_nsindex(ndd->ns_next); 568f524bf27SDan Williams WARN_ON(ndd->ns_current == ndd->ns_next); 569f524bf27SDan Williams 570f524bf27SDan Williams return 0; 571f524bf27SDan Williams } 572f524bf27SDan Williams 573f524bf27SDan Williams static unsigned long nd_label_offset(struct nvdimm_drvdata *ndd, 574f524bf27SDan Williams struct nd_namespace_label *nd_label) 575f524bf27SDan Williams { 576f524bf27SDan Williams return (unsigned long) nd_label 577f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 578f524bf27SDan Williams } 579f524bf27SDan Williams 580b3fde74eSDan Williams enum nvdimm_claim_class to_nvdimm_cclass(guid_t *guid) 581b3fde74eSDan Williams { 582b3fde74eSDan Williams if (guid_equal(guid, &nvdimm_btt_guid)) 583b3fde74eSDan Williams return NVDIMM_CCLASS_BTT; 58414e49454SVishal Verma else if (guid_equal(guid, &nvdimm_btt2_guid)) 58514e49454SVishal Verma return NVDIMM_CCLASS_BTT2; 586b3fde74eSDan Williams else if (guid_equal(guid, &nvdimm_pfn_guid)) 587b3fde74eSDan Williams return NVDIMM_CCLASS_PFN; 588b3fde74eSDan Williams else if (guid_equal(guid, &nvdimm_dax_guid)) 589b3fde74eSDan Williams return NVDIMM_CCLASS_DAX; 590b3fde74eSDan Williams else if (guid_equal(guid, &guid_null)) 591b3fde74eSDan Williams return NVDIMM_CCLASS_NONE; 592b3fde74eSDan Williams 593b3fde74eSDan Williams return NVDIMM_CCLASS_UNKNOWN; 594b3fde74eSDan Williams } 595b3fde74eSDan Williams 596b3fde74eSDan Williams static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class, 597b3fde74eSDan Williams guid_t *target) 598b3fde74eSDan Williams { 599b3fde74eSDan Williams if (claim_class == NVDIMM_CCLASS_BTT) 600b3fde74eSDan Williams return &nvdimm_btt_guid; 60114e49454SVishal Verma else if (claim_class == NVDIMM_CCLASS_BTT2) 60214e49454SVishal Verma return &nvdimm_btt2_guid; 603b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_PFN) 604b3fde74eSDan Williams return &nvdimm_pfn_guid; 605b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_DAX) 606b3fde74eSDan Williams return &nvdimm_dax_guid; 607b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_UNKNOWN) { 608b3fde74eSDan Williams /* 609b3fde74eSDan Williams * If we're modifying a namespace for which we don't 610b3fde74eSDan Williams * know the claim_class, don't touch the existing guid. 611b3fde74eSDan Williams */ 612b3fde74eSDan Williams return target; 613b3fde74eSDan Williams } else 614b3fde74eSDan Williams return &guid_null; 615b3fde74eSDan Williams } 616b3fde74eSDan Williams 617f524bf27SDan Williams static int __pmem_label_update(struct nd_region *nd_region, 618f524bf27SDan Williams struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm, 619f524bf27SDan Williams int pos) 620f524bf27SDan Williams { 621b3fde74eSDan Williams struct nd_namespace_common *ndns = &nspm->nsio.common; 622faec6f8aSDan Williams struct nd_interleave_set *nd_set = nd_region->nd_set; 623f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 62416660eaeSDan Williams struct nd_label_ent *label_ent, *victim = NULL; 625f524bf27SDan Williams struct nd_namespace_label *nd_label; 626f524bf27SDan Williams struct nd_namespace_index *nsindex; 62716660eaeSDan Williams struct nd_label_id label_id; 62816660eaeSDan Williams struct resource *res; 629f524bf27SDan Williams unsigned long *free; 630f524bf27SDan Williams u32 nslot, slot; 631f524bf27SDan Williams size_t offset; 632c12c48ceSDan Williams u64 cookie; 633f524bf27SDan Williams int rc; 634f524bf27SDan Williams 635f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 636f524bf27SDan Williams return -ENXIO; 637f524bf27SDan Williams 638c12c48ceSDan Williams cookie = nd_region_interleave_set_cookie(nd_region, nsindex); 63916660eaeSDan Williams nd_label_gen_id(&label_id, nspm->uuid, 0); 64016660eaeSDan Williams for_each_dpa_resource(ndd, res) 64116660eaeSDan Williams if (strcmp(res->name, label_id.id) == 0) 64216660eaeSDan Williams break; 64316660eaeSDan Williams 64416660eaeSDan Williams if (!res) { 64516660eaeSDan Williams WARN_ON_ONCE(1); 64616660eaeSDan Williams return -ENXIO; 64716660eaeSDan Williams } 64816660eaeSDan Williams 649f524bf27SDan Williams /* allocate and write the label to the staging (next) index */ 650f524bf27SDan Williams slot = nd_label_alloc_slot(ndd); 651f524bf27SDan Williams if (slot == UINT_MAX) 652f524bf27SDan Williams return -ENXIO; 653f524bf27SDan Williams dev_dbg(ndd->dev, "%s: allocated: %d\n", __func__, slot); 654f524bf27SDan Williams 655564e871aSDan Williams nd_label = to_label(ndd, slot); 656564e871aSDan Williams memset(nd_label, 0, sizeof_namespace_label(ndd)); 657f524bf27SDan Williams memcpy(nd_label->uuid, nspm->uuid, NSLABEL_UUID_LEN); 658f524bf27SDan Williams if (nspm->alt_name) 659f524bf27SDan Williams memcpy(nd_label->name, nspm->alt_name, NSLABEL_NAME_LEN); 660f524bf27SDan Williams nd_label->flags = __cpu_to_le32(NSLABEL_FLAG_UPDATING); 661f524bf27SDan Williams nd_label->nlabel = __cpu_to_le16(nd_region->ndr_mappings); 662f524bf27SDan Williams nd_label->position = __cpu_to_le16(pos); 663f524bf27SDan Williams nd_label->isetcookie = __cpu_to_le64(cookie); 66416660eaeSDan Williams nd_label->rawsize = __cpu_to_le64(resource_size(res)); 6652de5148fSDan Williams nd_label->lbasize = __cpu_to_le64(nspm->lbasize); 66616660eaeSDan Williams nd_label->dpa = __cpu_to_le64(res->start); 667f524bf27SDan Williams nd_label->slot = __cpu_to_le32(slot); 668faec6f8aSDan Williams if (namespace_label_has(ndd, type_guid)) 669faec6f8aSDan Williams guid_copy(&nd_label->type_guid, &nd_set->type_guid); 670b3fde74eSDan Williams if (namespace_label_has(ndd, abstraction_guid)) 671b3fde74eSDan Williams guid_copy(&nd_label->abstraction_guid, 672b3fde74eSDan Williams to_abstraction_guid(ndns->claim_class, 673b3fde74eSDan Williams &nd_label->abstraction_guid)); 674355d8388SDan Williams if (namespace_label_has(ndd, checksum)) { 675355d8388SDan Williams u64 sum; 676355d8388SDan Williams 677355d8388SDan Williams nd_label->checksum = __cpu_to_le64(0); 678355d8388SDan Williams sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1); 679355d8388SDan Williams nd_label->checksum = __cpu_to_le64(sum); 680355d8388SDan Williams } 68116660eaeSDan Williams nd_dbg_dpa(nd_region, ndd, res, "%s\n", __func__); 682f524bf27SDan Williams 683f524bf27SDan Williams /* update label */ 684f524bf27SDan Williams offset = nd_label_offset(ndd, nd_label); 685f524bf27SDan Williams rc = nvdimm_set_config_data(ndd, offset, nd_label, 686564e871aSDan Williams sizeof_namespace_label(ndd)); 687f524bf27SDan Williams if (rc < 0) 688f524bf27SDan Williams return rc; 689f524bf27SDan Williams 690f524bf27SDan Williams /* Garbage collect the previous label */ 691ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 69216660eaeSDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) { 69316660eaeSDan Williams if (!label_ent->label) 69416660eaeSDan Williams continue; 69516660eaeSDan Williams if (memcmp(nspm->uuid, label_ent->label->uuid, 69616660eaeSDan Williams NSLABEL_UUID_LEN) != 0) 69716660eaeSDan Williams continue; 69816660eaeSDan Williams victim = label_ent; 69916660eaeSDan Williams list_move_tail(&victim->list, &nd_mapping->labels); 70016660eaeSDan Williams break; 70116660eaeSDan Williams } 70216660eaeSDan Williams if (victim) { 703f524bf27SDan Williams dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot); 70416660eaeSDan Williams slot = to_slot(ndd, victim->label); 70516660eaeSDan Williams nd_label_free_slot(ndd, slot); 70616660eaeSDan Williams victim->label = NULL; 707f524bf27SDan Williams } 708f524bf27SDan Williams 709f524bf27SDan Williams /* update index */ 710f524bf27SDan Williams rc = nd_label_write_index(ndd, ndd->ns_next, 711f524bf27SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 71216660eaeSDan Williams if (rc == 0) { 71316660eaeSDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) 71416660eaeSDan Williams if (!label_ent->label) { 715ae8219f1SDan Williams label_ent->label = nd_label; 71616660eaeSDan Williams nd_label = NULL; 71716660eaeSDan Williams break; 71816660eaeSDan Williams } 71916660eaeSDan Williams dev_WARN_ONCE(&nspm->nsio.common.dev, nd_label, 72016660eaeSDan Williams "failed to track label: %d\n", 72116660eaeSDan Williams to_slot(ndd, nd_label)); 72216660eaeSDan Williams if (nd_label) 72316660eaeSDan Williams rc = -ENXIO; 72416660eaeSDan Williams } 725ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 726ae8219f1SDan Williams 727f524bf27SDan Williams return rc; 7280ba1c634SDan Williams } 7290ba1c634SDan Williams 7300ba1c634SDan Williams static bool is_old_resource(struct resource *res, struct resource **list, int n) 731f524bf27SDan Williams { 732f524bf27SDan Williams int i; 7330ba1c634SDan Williams 7340ba1c634SDan Williams if (res->flags & DPA_RESOURCE_ADJUSTED) 7350ba1c634SDan Williams return false; 7360ba1c634SDan Williams for (i = 0; i < n; i++) 7370ba1c634SDan Williams if (res == list[i]) 7380ba1c634SDan Williams return true; 7390ba1c634SDan Williams return false; 7400ba1c634SDan Williams } 7410ba1c634SDan Williams 7420ba1c634SDan Williams static struct resource *to_resource(struct nvdimm_drvdata *ndd, 7430ba1c634SDan Williams struct nd_namespace_label *nd_label) 7440ba1c634SDan Williams { 7450ba1c634SDan Williams struct resource *res; 7460ba1c634SDan Williams 7470ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 7480ba1c634SDan Williams if (res->start != __le64_to_cpu(nd_label->dpa)) 7490ba1c634SDan Williams continue; 7500ba1c634SDan Williams if (resource_size(res) != __le64_to_cpu(nd_label->rawsize)) 7510ba1c634SDan Williams continue; 7520ba1c634SDan Williams return res; 7530ba1c634SDan Williams } 7540ba1c634SDan Williams 7550ba1c634SDan Williams return NULL; 7560ba1c634SDan Williams } 7570ba1c634SDan Williams 7580ba1c634SDan Williams /* 7590ba1c634SDan Williams * 1/ Account all the labels that can be freed after this update 7600ba1c634SDan Williams * 2/ Allocate and write the label to the staging (next) index 7610ba1c634SDan Williams * 3/ Record the resources in the namespace device 7620ba1c634SDan Williams */ 7630ba1c634SDan Williams static int __blk_label_update(struct nd_region *nd_region, 7640ba1c634SDan Williams struct nd_mapping *nd_mapping, struct nd_namespace_blk *nsblk, 7650ba1c634SDan Williams int num_labels) 7660ba1c634SDan Williams { 767ae8219f1SDan Williams int i, alloc, victims, nfree, old_num_resources, nlabel, rc = -ENXIO; 768faec6f8aSDan Williams struct nd_interleave_set *nd_set = nd_region->nd_set; 769b3fde74eSDan Williams struct nd_namespace_common *ndns = &nsblk->common; 770f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 7710ba1c634SDan Williams struct nd_namespace_label *nd_label; 772ae8219f1SDan Williams struct nd_label_ent *label_ent, *e; 7730ba1c634SDan Williams struct nd_namespace_index *nsindex; 7740ba1c634SDan Williams unsigned long *free, *victim_map = NULL; 7750ba1c634SDan Williams struct resource *res, **old_res_list; 7760ba1c634SDan Williams struct nd_label_id label_id; 7770ba1c634SDan Williams u8 uuid[NSLABEL_UUID_LEN]; 7783934d841SDan Williams int min_dpa_idx = 0; 779ae8219f1SDan Williams LIST_HEAD(list); 7800ba1c634SDan Williams u32 nslot, slot; 781f524bf27SDan Williams 7820ba1c634SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 7830ba1c634SDan Williams return -ENXIO; 784f524bf27SDan Williams 7850ba1c634SDan Williams old_res_list = nsblk->res; 7860ba1c634SDan Williams nfree = nd_label_nfree(ndd); 7870ba1c634SDan Williams old_num_resources = nsblk->num_resources; 7880ba1c634SDan Williams nd_label_gen_id(&label_id, nsblk->uuid, NSLABEL_FLAG_LOCAL); 7890ba1c634SDan Williams 7900ba1c634SDan Williams /* 7910ba1c634SDan Williams * We need to loop over the old resources a few times, which seems a 7920ba1c634SDan Williams * bit inefficient, but we need to know that we have the label 7930ba1c634SDan Williams * space before we start mutating the tracking structures. 7940ba1c634SDan Williams * Otherwise the recovery method of last resort for userspace is 7950ba1c634SDan Williams * disable and re-enable the parent region. 7960ba1c634SDan Williams */ 7970ba1c634SDan Williams alloc = 0; 7980ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 7990ba1c634SDan Williams if (strcmp(res->name, label_id.id) != 0) 8000ba1c634SDan Williams continue; 8010ba1c634SDan Williams if (!is_old_resource(res, old_res_list, old_num_resources)) 8020ba1c634SDan Williams alloc++; 8030ba1c634SDan Williams } 8040ba1c634SDan Williams 8050ba1c634SDan Williams victims = 0; 8060ba1c634SDan Williams if (old_num_resources) { 8070ba1c634SDan Williams /* convert old local-label-map to dimm-slot victim-map */ 8080ba1c634SDan Williams victim_map = kcalloc(BITS_TO_LONGS(nslot), sizeof(long), 8090ba1c634SDan Williams GFP_KERNEL); 8100ba1c634SDan Williams if (!victim_map) 8110ba1c634SDan Williams return -ENOMEM; 8120ba1c634SDan Williams 8130ba1c634SDan Williams /* mark unused labels for garbage collection */ 8140ba1c634SDan Williams for_each_clear_bit_le(slot, free, nslot) { 815564e871aSDan Williams nd_label = to_label(ndd, slot); 8160ba1c634SDan Williams memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 8170ba1c634SDan Williams if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 8180ba1c634SDan Williams continue; 8190ba1c634SDan Williams res = to_resource(ndd, nd_label); 8200ba1c634SDan Williams if (res && is_old_resource(res, old_res_list, 8210ba1c634SDan Williams old_num_resources)) 8220ba1c634SDan Williams continue; 8230ba1c634SDan Williams slot = to_slot(ndd, nd_label); 8240ba1c634SDan Williams set_bit(slot, victim_map); 8250ba1c634SDan Williams victims++; 8260ba1c634SDan Williams } 8270ba1c634SDan Williams } 8280ba1c634SDan Williams 8290ba1c634SDan Williams /* don't allow updates that consume the last label */ 8300ba1c634SDan Williams if (nfree - alloc < 0 || nfree - alloc + victims < 1) { 8318c2f7e86SDan Williams dev_info(&nsblk->common.dev, "insufficient label space\n"); 8320ba1c634SDan Williams kfree(victim_map); 8330ba1c634SDan Williams return -ENOSPC; 8340ba1c634SDan Williams } 8350ba1c634SDan Williams /* from here on we need to abort on error */ 8360ba1c634SDan Williams 8370ba1c634SDan Williams 8380ba1c634SDan Williams /* assign all resources to the namespace before writing the labels */ 8390ba1c634SDan Williams nsblk->res = NULL; 8400ba1c634SDan Williams nsblk->num_resources = 0; 8410ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 8420ba1c634SDan Williams if (strcmp(res->name, label_id.id) != 0) 8430ba1c634SDan Williams continue; 8440ba1c634SDan Williams if (!nsblk_add_resource(nd_region, ndd, nsblk, res->start)) { 8450ba1c634SDan Williams rc = -ENOMEM; 8460ba1c634SDan Williams goto abort; 8470ba1c634SDan Williams } 8480ba1c634SDan Williams } 8490ba1c634SDan Williams 8503934d841SDan Williams /* 8513934d841SDan Williams * Find the resource associated with the first label in the set 8523934d841SDan Williams * per the v1.2 namespace specification. 8533934d841SDan Williams */ 8543934d841SDan Williams for (i = 0; i < nsblk->num_resources; i++) { 8553934d841SDan Williams struct resource *min = nsblk->res[min_dpa_idx]; 8563934d841SDan Williams 8573934d841SDan Williams res = nsblk->res[i]; 8583934d841SDan Williams if (res->start < min->start) 8593934d841SDan Williams min_dpa_idx = i; 8603934d841SDan Williams } 8613934d841SDan Williams 8620ba1c634SDan Williams for (i = 0; i < nsblk->num_resources; i++) { 8630ba1c634SDan Williams size_t offset; 8640ba1c634SDan Williams 8650ba1c634SDan Williams res = nsblk->res[i]; 8660ba1c634SDan Williams if (is_old_resource(res, old_res_list, old_num_resources)) 8670ba1c634SDan Williams continue; /* carry-over */ 8680ba1c634SDan Williams slot = nd_label_alloc_slot(ndd); 8690ba1c634SDan Williams if (slot == UINT_MAX) 8700ba1c634SDan Williams goto abort; 8710ba1c634SDan Williams dev_dbg(ndd->dev, "%s: allocated: %d\n", __func__, slot); 8720ba1c634SDan Williams 873564e871aSDan Williams nd_label = to_label(ndd, slot); 874564e871aSDan Williams memset(nd_label, 0, sizeof_namespace_label(ndd)); 8750ba1c634SDan Williams memcpy(nd_label->uuid, nsblk->uuid, NSLABEL_UUID_LEN); 8760ba1c634SDan Williams if (nsblk->alt_name) 8770ba1c634SDan Williams memcpy(nd_label->name, nsblk->alt_name, 8780ba1c634SDan Williams NSLABEL_NAME_LEN); 8790ba1c634SDan Williams nd_label->flags = __cpu_to_le32(NSLABEL_FLAG_LOCAL); 8808f2bc243SDan Williams 8818f2bc243SDan Williams /* 8828f2bc243SDan Williams * Use the presence of the type_guid as a flag to 8833934d841SDan Williams * determine isetcookie usage and nlabel + position 8843934d841SDan Williams * policy for blk-aperture namespaces. 8858f2bc243SDan Williams */ 8863934d841SDan Williams if (namespace_label_has(ndd, type_guid)) { 8873934d841SDan Williams if (i == min_dpa_idx) { 8883934d841SDan Williams nd_label->nlabel = __cpu_to_le16(nsblk->num_resources); 8893934d841SDan Williams nd_label->position = __cpu_to_le16(0); 8903934d841SDan Williams } else { 8913934d841SDan Williams nd_label->nlabel = __cpu_to_le16(0xffff); 8923934d841SDan Williams nd_label->position = __cpu_to_le16(0xffff); 8933934d841SDan Williams } 8948f2bc243SDan Williams nd_label->isetcookie = __cpu_to_le64(nd_set->cookie2); 8953934d841SDan Williams } else { 8963934d841SDan Williams nd_label->nlabel = __cpu_to_le16(0); /* N/A */ 8973934d841SDan Williams nd_label->position = __cpu_to_le16(0); /* N/A */ 8980ba1c634SDan Williams nd_label->isetcookie = __cpu_to_le64(0); /* N/A */ 8993934d841SDan Williams } 9008f2bc243SDan Williams 9010ba1c634SDan Williams nd_label->dpa = __cpu_to_le64(res->start); 9020ba1c634SDan Williams nd_label->rawsize = __cpu_to_le64(resource_size(res)); 9030ba1c634SDan Williams nd_label->lbasize = __cpu_to_le64(nsblk->lbasize); 9040ba1c634SDan Williams nd_label->slot = __cpu_to_le32(slot); 905faec6f8aSDan Williams if (namespace_label_has(ndd, type_guid)) 906faec6f8aSDan Williams guid_copy(&nd_label->type_guid, &nd_set->type_guid); 907b3fde74eSDan Williams if (namespace_label_has(ndd, abstraction_guid)) 908b3fde74eSDan Williams guid_copy(&nd_label->abstraction_guid, 909b3fde74eSDan Williams to_abstraction_guid(ndns->claim_class, 910b3fde74eSDan Williams &nd_label->abstraction_guid)); 911b3fde74eSDan Williams 912355d8388SDan Williams if (namespace_label_has(ndd, checksum)) { 913355d8388SDan Williams u64 sum; 914355d8388SDan Williams 915355d8388SDan Williams nd_label->checksum = __cpu_to_le64(0); 916355d8388SDan Williams sum = nd_fletcher64(nd_label, 917355d8388SDan Williams sizeof_namespace_label(ndd), 1); 918355d8388SDan Williams nd_label->checksum = __cpu_to_le64(sum); 919355d8388SDan Williams } 9200ba1c634SDan Williams 9210ba1c634SDan Williams /* update label */ 9220ba1c634SDan Williams offset = nd_label_offset(ndd, nd_label); 9230ba1c634SDan Williams rc = nvdimm_set_config_data(ndd, offset, nd_label, 924564e871aSDan Williams sizeof_namespace_label(ndd)); 9250ba1c634SDan Williams if (rc < 0) 9260ba1c634SDan Williams goto abort; 9270ba1c634SDan Williams } 9280ba1c634SDan Williams 9290ba1c634SDan Williams /* free up now unused slots in the new index */ 9300ba1c634SDan Williams for_each_set_bit(slot, victim_map, victim_map ? nslot : 0) { 9310ba1c634SDan Williams dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot); 9320ba1c634SDan Williams nd_label_free_slot(ndd, slot); 9330ba1c634SDan Williams } 9340ba1c634SDan Williams 9350ba1c634SDan Williams /* update index */ 9360ba1c634SDan Williams rc = nd_label_write_index(ndd, ndd->ns_next, 9370ba1c634SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 9380ba1c634SDan Williams if (rc) 9390ba1c634SDan Williams goto abort; 9400ba1c634SDan Williams 9410ba1c634SDan Williams /* 9420ba1c634SDan Williams * Now that the on-dimm labels are up to date, fix up the tracking 9430ba1c634SDan Williams * entries in nd_mapping->labels 9440ba1c634SDan Williams */ 9450ba1c634SDan Williams nlabel = 0; 946ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 947ae8219f1SDan Williams list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { 948ae8219f1SDan Williams nd_label = label_ent->label; 949ae8219f1SDan Williams if (!nd_label) 950ae8219f1SDan Williams continue; 9510ba1c634SDan Williams nlabel++; 9520ba1c634SDan Williams memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 9530ba1c634SDan Williams if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 9540ba1c634SDan Williams continue; 9550ba1c634SDan Williams nlabel--; 956ae8219f1SDan Williams list_move(&label_ent->list, &list); 957ae8219f1SDan Williams label_ent->label = NULL; 9580ba1c634SDan Williams } 959ae8219f1SDan Williams list_splice_tail_init(&list, &nd_mapping->labels); 960ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 961ae8219f1SDan Williams 9620ba1c634SDan Williams if (nlabel + nsblk->num_resources > num_labels) { 9630ba1c634SDan Williams /* 9640ba1c634SDan Williams * Bug, we can't end up with more resources than 9650ba1c634SDan Williams * available labels 9660ba1c634SDan Williams */ 9670ba1c634SDan Williams WARN_ON_ONCE(1); 9680ba1c634SDan Williams rc = -ENXIO; 9690ba1c634SDan Williams goto out; 9700ba1c634SDan Williams } 9710ba1c634SDan Williams 972ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 973ae8219f1SDan Williams label_ent = list_first_entry_or_null(&nd_mapping->labels, 974ae8219f1SDan Williams typeof(*label_ent), list); 975ae8219f1SDan Williams if (!label_ent) { 976ae8219f1SDan Williams WARN_ON(1); 977ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 978ae8219f1SDan Williams rc = -ENXIO; 979ae8219f1SDan Williams goto out; 980ae8219f1SDan Williams } 9810ba1c634SDan Williams for_each_clear_bit_le(slot, free, nslot) { 982564e871aSDan Williams nd_label = to_label(ndd, slot); 9830ba1c634SDan Williams memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 9840ba1c634SDan Williams if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 9850ba1c634SDan Williams continue; 9860ba1c634SDan Williams res = to_resource(ndd, nd_label); 9870ba1c634SDan Williams res->flags &= ~DPA_RESOURCE_ADJUSTED; 988ae8219f1SDan Williams dev_vdbg(&nsblk->common.dev, "assign label slot: %d\n", slot); 989ae8219f1SDan Williams list_for_each_entry_from(label_ent, &nd_mapping->labels, list) { 990ae8219f1SDan Williams if (label_ent->label) 991ae8219f1SDan Williams continue; 992ae8219f1SDan Williams label_ent->label = nd_label; 993ae8219f1SDan Williams nd_label = NULL; 994ae8219f1SDan Williams break; 9950ba1c634SDan Williams } 996ae8219f1SDan Williams if (nd_label) 997ae8219f1SDan Williams dev_WARN(&nsblk->common.dev, 998ae8219f1SDan Williams "failed to track label slot%d\n", slot); 999ae8219f1SDan Williams } 1000ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 10010ba1c634SDan Williams 10020ba1c634SDan Williams out: 10030ba1c634SDan Williams kfree(old_res_list); 10040ba1c634SDan Williams kfree(victim_map); 10050ba1c634SDan Williams return rc; 10060ba1c634SDan Williams 10070ba1c634SDan Williams abort: 10080ba1c634SDan Williams /* 10090ba1c634SDan Williams * 1/ repair the allocated label bitmap in the index 10100ba1c634SDan Williams * 2/ restore the resource list 10110ba1c634SDan Williams */ 10120ba1c634SDan Williams nd_label_copy(ndd, nsindex, to_current_namespace_index(ndd)); 10130ba1c634SDan Williams kfree(nsblk->res); 10140ba1c634SDan Williams nsblk->res = old_res_list; 10150ba1c634SDan Williams nsblk->num_resources = old_num_resources; 10160ba1c634SDan Williams old_res_list = NULL; 10170ba1c634SDan Williams goto out; 10180ba1c634SDan Williams } 10190ba1c634SDan Williams 10200ba1c634SDan Williams static int init_labels(struct nd_mapping *nd_mapping, int num_labels) 10210ba1c634SDan Williams { 1022ae8219f1SDan Williams int i, old_num_labels = 0; 1023ae8219f1SDan Williams struct nd_label_ent *label_ent; 10240ba1c634SDan Williams struct nd_namespace_index *nsindex; 10250ba1c634SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 10260ba1c634SDan Williams 1027ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1028ae8219f1SDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) 10290ba1c634SDan Williams old_num_labels++; 1030ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 10310ba1c634SDan Williams 10320ba1c634SDan Williams /* 10330ba1c634SDan Williams * We need to preserve all the old labels for the mapping so 10340ba1c634SDan Williams * they can be garbage collected after writing the new labels. 10350ba1c634SDan Williams */ 1036ae8219f1SDan Williams for (i = old_num_labels; i < num_labels; i++) { 1037ae8219f1SDan Williams label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL); 1038ae8219f1SDan Williams if (!label_ent) 10390ba1c634SDan Williams return -ENOMEM; 1040ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1041ae8219f1SDan Williams list_add_tail(&label_ent->list, &nd_mapping->labels); 1042ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 10430ba1c634SDan Williams } 10440ba1c634SDan Williams 1045f524bf27SDan Williams if (ndd->ns_current == -1 || ndd->ns_next == -1) 1046f524bf27SDan Williams /* pass */; 1047f524bf27SDan Williams else 10480ba1c634SDan Williams return max(num_labels, old_num_labels); 1049f524bf27SDan Williams 1050f524bf27SDan Williams nsindex = to_namespace_index(ndd, 0); 1051f524bf27SDan Williams memset(nsindex, 0, ndd->nsarea.config_size); 1052f524bf27SDan Williams for (i = 0; i < 2; i++) { 1053*b18d4b8aSDan Williams int rc = nd_label_write_index(ndd, i, 3 - i, ND_NSINDEX_INIT); 1054f524bf27SDan Williams 1055f524bf27SDan Williams if (rc) 1056f524bf27SDan Williams return rc; 1057f524bf27SDan Williams } 1058f524bf27SDan Williams ndd->ns_next = 1; 1059f524bf27SDan Williams ndd->ns_current = 0; 1060f524bf27SDan Williams 10610ba1c634SDan Williams return max(num_labels, old_num_labels); 1062f524bf27SDan Williams } 1063f524bf27SDan Williams 1064f524bf27SDan Williams static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid) 1065f524bf27SDan Williams { 1066f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 1067ae8219f1SDan Williams struct nd_label_ent *label_ent, *e; 1068f524bf27SDan Williams struct nd_namespace_index *nsindex; 1069f524bf27SDan Williams u8 label_uuid[NSLABEL_UUID_LEN]; 1070f524bf27SDan Williams unsigned long *free; 1071ae8219f1SDan Williams LIST_HEAD(list); 1072f524bf27SDan Williams u32 nslot, slot; 1073ae8219f1SDan Williams int active = 0; 1074f524bf27SDan Williams 1075f524bf27SDan Williams if (!uuid) 1076f524bf27SDan Williams return 0; 1077f524bf27SDan Williams 1078f524bf27SDan Williams /* no index || no labels == nothing to delete */ 1079ae8219f1SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 1080f524bf27SDan Williams return 0; 1081f524bf27SDan Williams 1082ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1083ae8219f1SDan Williams list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { 1084ae8219f1SDan Williams struct nd_namespace_label *nd_label = label_ent->label; 1085ae8219f1SDan Williams 1086ae8219f1SDan Williams if (!nd_label) 1087ae8219f1SDan Williams continue; 1088ae8219f1SDan Williams active++; 1089f524bf27SDan Williams memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); 1090f524bf27SDan Williams if (memcmp(label_uuid, uuid, NSLABEL_UUID_LEN) != 0) 1091f524bf27SDan Williams continue; 1092ae8219f1SDan Williams active--; 1093f524bf27SDan Williams slot = to_slot(ndd, nd_label); 1094f524bf27SDan Williams nd_label_free_slot(ndd, slot); 1095f524bf27SDan Williams dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot); 1096ae8219f1SDan Williams list_move_tail(&label_ent->list, &list); 1097ae8219f1SDan Williams label_ent->label = NULL; 1098f524bf27SDan Williams } 1099ae8219f1SDan Williams list_splice_tail_init(&list, &nd_mapping->labels); 1100f524bf27SDan Williams 1101ae8219f1SDan Williams if (active == 0) { 1102ae8219f1SDan Williams nd_mapping_free_labels(nd_mapping); 1103ae8219f1SDan Williams dev_dbg(ndd->dev, "%s: no more active labels\n", __func__); 1104f524bf27SDan Williams } 1105ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 1106f524bf27SDan Williams 1107f524bf27SDan Williams return nd_label_write_index(ndd, ndd->ns_next, 1108f524bf27SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 1109f524bf27SDan Williams } 1110f524bf27SDan Williams 1111f524bf27SDan Williams int nd_pmem_namespace_label_update(struct nd_region *nd_region, 1112f524bf27SDan Williams struct nd_namespace_pmem *nspm, resource_size_t size) 1113f524bf27SDan Williams { 1114f524bf27SDan Williams int i; 1115f524bf27SDan Williams 1116f524bf27SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 1117f524bf27SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[i]; 111816660eaeSDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 111916660eaeSDan Williams struct resource *res; 112016660eaeSDan Williams int rc, count = 0; 1121f524bf27SDan Williams 1122f524bf27SDan Williams if (size == 0) { 1123f524bf27SDan Williams rc = del_labels(nd_mapping, nspm->uuid); 1124f524bf27SDan Williams if (rc) 1125f524bf27SDan Williams return rc; 1126f524bf27SDan Williams continue; 1127f524bf27SDan Williams } 1128f524bf27SDan Williams 112916660eaeSDan Williams for_each_dpa_resource(ndd, res) 11302d9a0274SNicolas Iooss if (strncmp(res->name, "pmem", 4) == 0) 113116660eaeSDan Williams count++; 113216660eaeSDan Williams WARN_ON_ONCE(!count); 113316660eaeSDan Williams 113416660eaeSDan Williams rc = init_labels(nd_mapping, count); 11350ba1c634SDan Williams if (rc < 0) 1136f524bf27SDan Williams return rc; 1137f524bf27SDan Williams 1138f524bf27SDan Williams rc = __pmem_label_update(nd_region, nd_mapping, nspm, i); 1139f524bf27SDan Williams if (rc) 1140f524bf27SDan Williams return rc; 1141f524bf27SDan Williams } 1142f524bf27SDan Williams 1143f524bf27SDan Williams return 0; 1144f524bf27SDan Williams } 11450ba1c634SDan Williams 11460ba1c634SDan Williams int nd_blk_namespace_label_update(struct nd_region *nd_region, 11470ba1c634SDan Williams struct nd_namespace_blk *nsblk, resource_size_t size) 11480ba1c634SDan Williams { 11490ba1c634SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 11500ba1c634SDan Williams struct resource *res; 11510ba1c634SDan Williams int count = 0; 11520ba1c634SDan Williams 11530ba1c634SDan Williams if (size == 0) 11540ba1c634SDan Williams return del_labels(nd_mapping, nsblk->uuid); 11550ba1c634SDan Williams 11560ba1c634SDan Williams for_each_dpa_resource(to_ndd(nd_mapping), res) 11570ba1c634SDan Williams count++; 11580ba1c634SDan Williams 11590ba1c634SDan Williams count = init_labels(nd_mapping, count); 11600ba1c634SDan Williams if (count < 0) 11610ba1c634SDan Williams return count; 11620ba1c634SDan Williams 11630ba1c634SDan Williams return __blk_label_update(nd_region, nd_mapping, nsblk, count); 11640ba1c634SDan Williams } 1165b3fde74eSDan Williams 1166b3fde74eSDan Williams int __init nd_label_init(void) 1167b3fde74eSDan Williams { 1168b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_BTT_GUID, &nvdimm_btt_guid)); 116914e49454SVishal Verma WARN_ON(guid_parse(NVDIMM_BTT2_GUID, &nvdimm_btt2_guid)); 1170b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_guid)); 1171b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_guid)); 1172b3fde74eSDan Williams 1173b3fde74eSDan Williams return 0; 1174b3fde74eSDan Williams } 1175