15b497af4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 24a826c83SDan Williams /* 34a826c83SDan Williams * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 44a826c83SDan Williams */ 54a826c83SDan Williams #include <linux/device.h> 64a826c83SDan Williams #include <linux/ndctl.h> 7b3fde74eSDan Williams #include <linux/uuid.h> 8f524bf27SDan Williams #include <linux/slab.h> 94a826c83SDan Williams #include <linux/io.h> 104a826c83SDan Williams #include <linux/nd.h> 114a826c83SDan Williams #include "nd-core.h" 124a826c83SDan Williams #include "label.h" 134a826c83SDan Williams #include "nd.h" 144a826c83SDan Williams 15b3fde74eSDan Williams static guid_t nvdimm_btt_guid; 1614e49454SVishal Verma static guid_t nvdimm_btt2_guid; 17b3fde74eSDan Williams static guid_t nvdimm_pfn_guid; 18b3fde74eSDan Williams static guid_t nvdimm_dax_guid; 19b3fde74eSDan Williams 20c01dafadSQian Cai static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0"; 21c01dafadSQian Cai 224a826c83SDan Williams static u32 best_seq(u32 a, u32 b) 234a826c83SDan Williams { 244a826c83SDan Williams a &= NSINDEX_SEQ_MASK; 254a826c83SDan Williams b &= NSINDEX_SEQ_MASK; 264a826c83SDan Williams 274a826c83SDan Williams if (a == 0 || a == b) 284a826c83SDan Williams return b; 294a826c83SDan Williams else if (b == 0) 304a826c83SDan Williams return a; 314a826c83SDan Williams else if (nd_inc_seq(a) == b) 324a826c83SDan Williams return b; 334a826c83SDan Williams else 344a826c83SDan Williams return a; 354a826c83SDan Williams } 364a826c83SDan Williams 37564e871aSDan Williams unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd) 38564e871aSDan Williams { 39564e871aSDan Williams return ndd->nslabel_size; 40564e871aSDan Williams } 41564e871aSDan Williams 429e694d9cSToshi Kani static size_t __sizeof_namespace_index(u32 nslot) 439e694d9cSToshi Kani { 449e694d9cSToshi Kani return ALIGN(sizeof(struct nd_namespace_index) + DIV_ROUND_UP(nslot, 8), 459e694d9cSToshi Kani NSINDEX_ALIGN); 469e694d9cSToshi Kani } 479e694d9cSToshi Kani 489e694d9cSToshi Kani static int __nvdimm_num_label_slots(struct nvdimm_drvdata *ndd, 499e694d9cSToshi Kani size_t index_size) 509e694d9cSToshi Kani { 519e694d9cSToshi Kani return (ndd->nsarea.config_size - index_size * 2) / 529e694d9cSToshi Kani sizeof_namespace_label(ndd); 539e694d9cSToshi Kani } 549e694d9cSToshi Kani 5502881768SDan Williams int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd) 5602881768SDan Williams { 579e694d9cSToshi Kani u32 tmp_nslot, n; 589e694d9cSToshi Kani 599e694d9cSToshi Kani tmp_nslot = ndd->nsarea.config_size / sizeof_namespace_label(ndd); 609e694d9cSToshi Kani n = __sizeof_namespace_index(tmp_nslot) / NSINDEX_ALIGN; 619e694d9cSToshi Kani 629e694d9cSToshi Kani return __nvdimm_num_label_slots(ndd, NSINDEX_ALIGN * n); 6302881768SDan Williams } 6402881768SDan Williams 654a826c83SDan Williams size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd) 664a826c83SDan Williams { 6702881768SDan Williams u32 nslot, space, size; 684a826c83SDan Williams 694a826c83SDan Williams /* 709e694d9cSToshi Kani * Per UEFI 2.7, the minimum size of the Label Storage Area is large 719e694d9cSToshi Kani * enough to hold 2 index blocks and 2 labels. The minimum index 721cfeb66eSAlexander Duyck * block size is 256 bytes. The label size is 128 for namespaces 731cfeb66eSAlexander Duyck * prior to version 1.2 and at minimum 256 for version 1.2 and later. 744a826c83SDan Williams */ 7502881768SDan Williams nslot = nvdimm_num_label_slots(ndd); 7602881768SDan Williams space = ndd->nsarea.config_size - nslot * sizeof_namespace_label(ndd); 779e694d9cSToshi Kani size = __sizeof_namespace_index(nslot) * 2; 789e694d9cSToshi Kani if (size <= space && nslot >= 2) 7902881768SDan Williams return size / 2; 804a826c83SDan Williams 8102881768SDan Williams dev_err(ndd->dev, "label area (%d) too small to host (%d byte) labels\n", 8202881768SDan Williams ndd->nsarea.config_size, sizeof_namespace_label(ndd)); 8302881768SDan Williams return 0; 84f524bf27SDan Williams } 85f524bf27SDan Williams 86564e871aSDan Williams static int __nd_label_validate(struct nvdimm_drvdata *ndd) 874a826c83SDan Williams { 884a826c83SDan Williams /* 894a826c83SDan Williams * On media label format consists of two index blocks followed 904a826c83SDan Williams * by an array of labels. None of these structures are ever 914a826c83SDan Williams * updated in place. A sequence number tracks the current 924a826c83SDan Williams * active index and the next one to write, while labels are 934a826c83SDan Williams * written to free slots. 944a826c83SDan Williams * 954a826c83SDan Williams * +------------+ 964a826c83SDan Williams * | | 974a826c83SDan Williams * | nsindex0 | 984a826c83SDan Williams * | | 994a826c83SDan Williams * +------------+ 1004a826c83SDan Williams * | | 1014a826c83SDan Williams * | nsindex1 | 1024a826c83SDan Williams * | | 1034a826c83SDan Williams * +------------+ 1044a826c83SDan Williams * | label0 | 1054a826c83SDan Williams * +------------+ 1064a826c83SDan Williams * | label1 | 1074a826c83SDan Williams * +------------+ 1084a826c83SDan Williams * | | 1094a826c83SDan Williams * ....nslot... 1104a826c83SDan Williams * | | 1114a826c83SDan Williams * +------------+ 1124a826c83SDan Williams * | labelN | 1134a826c83SDan Williams * +------------+ 1144a826c83SDan Williams */ 1154a826c83SDan Williams struct nd_namespace_index *nsindex[] = { 1164a826c83SDan Williams to_namespace_index(ndd, 0), 1174a826c83SDan Williams to_namespace_index(ndd, 1), 1184a826c83SDan Williams }; 1194a826c83SDan Williams const int num_index = ARRAY_SIZE(nsindex); 1204a826c83SDan Williams struct device *dev = ndd->dev; 1214a826c83SDan Williams bool valid[2] = { 0 }; 1224a826c83SDan Williams int i, num_valid = 0; 1234a826c83SDan Williams u32 seq; 1244a826c83SDan Williams 1254a826c83SDan Williams for (i = 0; i < num_index; i++) { 1264a826c83SDan Williams u32 nslot; 1274a826c83SDan Williams u8 sig[NSINDEX_SIG_LEN]; 1284a826c83SDan Williams u64 sum_save, sum, size; 129564e871aSDan Williams unsigned int version, labelsize; 1304a826c83SDan Williams 1314a826c83SDan Williams memcpy(sig, nsindex[i]->sig, NSINDEX_SIG_LEN); 1324a826c83SDan Williams if (memcmp(sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN) != 0) { 133426824d6SDan Williams dev_dbg(dev, "nsindex%d signature invalid\n", i); 1344a826c83SDan Williams continue; 1354a826c83SDan Williams } 136564e871aSDan Williams 137564e871aSDan Williams /* label sizes larger than 128 arrived with v1.2 */ 138564e871aSDan Williams version = __le16_to_cpu(nsindex[i]->major) * 100 139564e871aSDan Williams + __le16_to_cpu(nsindex[i]->minor); 140564e871aSDan Williams if (version >= 102) 141564e871aSDan Williams labelsize = 1 << (7 + nsindex[i]->labelsize); 142564e871aSDan Williams else 143564e871aSDan Williams labelsize = 128; 144564e871aSDan Williams 145564e871aSDan Williams if (labelsize != sizeof_namespace_label(ndd)) { 146426824d6SDan Williams dev_dbg(dev, "nsindex%d labelsize %d invalid\n", 147426824d6SDan Williams i, nsindex[i]->labelsize); 148564e871aSDan Williams continue; 149564e871aSDan Williams } 150564e871aSDan Williams 1514a826c83SDan Williams sum_save = __le64_to_cpu(nsindex[i]->checksum); 1524a826c83SDan Williams nsindex[i]->checksum = __cpu_to_le64(0); 1534a826c83SDan Williams sum = nd_fletcher64(nsindex[i], sizeof_namespace_index(ndd), 1); 1544a826c83SDan Williams nsindex[i]->checksum = __cpu_to_le64(sum_save); 1554a826c83SDan Williams if (sum != sum_save) { 156426824d6SDan Williams dev_dbg(dev, "nsindex%d checksum invalid\n", i); 1574a826c83SDan Williams continue; 1584a826c83SDan Williams } 1594a826c83SDan Williams 1604a826c83SDan Williams seq = __le32_to_cpu(nsindex[i]->seq); 1614a826c83SDan Williams if ((seq & NSINDEX_SEQ_MASK) == 0) { 162426824d6SDan Williams dev_dbg(dev, "nsindex%d sequence: %#x invalid\n", i, seq); 1634a826c83SDan Williams continue; 1644a826c83SDan Williams } 1654a826c83SDan Williams 1664a826c83SDan Williams /* sanity check the index against expected values */ 1674a826c83SDan Williams if (__le64_to_cpu(nsindex[i]->myoff) 1684a826c83SDan Williams != i * sizeof_namespace_index(ndd)) { 169426824d6SDan Williams dev_dbg(dev, "nsindex%d myoff: %#llx invalid\n", 170426824d6SDan Williams i, (unsigned long long) 1714a826c83SDan Williams __le64_to_cpu(nsindex[i]->myoff)); 1724a826c83SDan Williams continue; 1734a826c83SDan Williams } 1744a826c83SDan Williams if (__le64_to_cpu(nsindex[i]->otheroff) 1754a826c83SDan Williams != (!i) * sizeof_namespace_index(ndd)) { 176426824d6SDan Williams dev_dbg(dev, "nsindex%d otheroff: %#llx invalid\n", 177426824d6SDan Williams i, (unsigned long long) 1784a826c83SDan Williams __le64_to_cpu(nsindex[i]->otheroff)); 1794a826c83SDan Williams continue; 1804a826c83SDan Williams } 181d86d4d63SAlexander Duyck if (__le64_to_cpu(nsindex[i]->labeloff) 182d86d4d63SAlexander Duyck != 2 * sizeof_namespace_index(ndd)) { 183d86d4d63SAlexander Duyck dev_dbg(dev, "nsindex%d labeloff: %#llx invalid\n", 184d86d4d63SAlexander Duyck i, (unsigned long long) 185d86d4d63SAlexander Duyck __le64_to_cpu(nsindex[i]->labeloff)); 186d86d4d63SAlexander Duyck continue; 187d86d4d63SAlexander Duyck } 1884a826c83SDan Williams 1894a826c83SDan Williams size = __le64_to_cpu(nsindex[i]->mysize); 1904a826c83SDan Williams if (size > sizeof_namespace_index(ndd) 1914a826c83SDan Williams || size < sizeof(struct nd_namespace_index)) { 192426824d6SDan Williams dev_dbg(dev, "nsindex%d mysize: %#llx invalid\n", i, size); 1934a826c83SDan Williams continue; 1944a826c83SDan Williams } 1954a826c83SDan Williams 1964a826c83SDan Williams nslot = __le32_to_cpu(nsindex[i]->nslot); 197564e871aSDan Williams if (nslot * sizeof_namespace_label(ndd) 1984a826c83SDan Williams + 2 * sizeof_namespace_index(ndd) 1994a826c83SDan Williams > ndd->nsarea.config_size) { 200426824d6SDan Williams dev_dbg(dev, "nsindex%d nslot: %u invalid, config_size: %#x\n", 201426824d6SDan Williams i, nslot, ndd->nsarea.config_size); 2024a826c83SDan Williams continue; 2034a826c83SDan Williams } 2044a826c83SDan Williams valid[i] = true; 2054a826c83SDan Williams num_valid++; 2064a826c83SDan Williams } 2074a826c83SDan Williams 2084a826c83SDan Williams switch (num_valid) { 2094a826c83SDan Williams case 0: 2104a826c83SDan Williams break; 2114a826c83SDan Williams case 1: 2124a826c83SDan Williams for (i = 0; i < num_index; i++) 2134a826c83SDan Williams if (valid[i]) 2144a826c83SDan Williams return i; 2154a826c83SDan Williams /* can't have num_valid > 0 but valid[] = { false, false } */ 2164a826c83SDan Williams WARN_ON(1); 2174a826c83SDan Williams break; 2184a826c83SDan Williams default: 2194a826c83SDan Williams /* pick the best index... */ 2204a826c83SDan Williams seq = best_seq(__le32_to_cpu(nsindex[0]->seq), 2214a826c83SDan Williams __le32_to_cpu(nsindex[1]->seq)); 2224a826c83SDan Williams if (seq == (__le32_to_cpu(nsindex[1]->seq) & NSINDEX_SEQ_MASK)) 2234a826c83SDan Williams return 1; 2244a826c83SDan Williams else 2254a826c83SDan Williams return 0; 2264a826c83SDan Williams break; 2274a826c83SDan Williams } 2284a826c83SDan Williams 2294a826c83SDan Williams return -1; 2304a826c83SDan Williams } 2314a826c83SDan Williams 2327d47aad4SAlexander Duyck static int nd_label_validate(struct nvdimm_drvdata *ndd) 233564e871aSDan Williams { 234564e871aSDan Williams /* 235564e871aSDan Williams * In order to probe for and validate namespace index blocks we 236564e871aSDan Williams * need to know the size of the labels, and we can't trust the 237564e871aSDan Williams * size of the labels until we validate the index blocks. 238564e871aSDan Williams * Resolve this dependency loop by probing for known label 2398990cdf1SDan Williams * sizes, but default to v1.2 256-byte namespace labels if 2408990cdf1SDan Williams * discovery fails. 241564e871aSDan Williams */ 2428990cdf1SDan Williams int label_size[] = { 128, 256 }; 243564e871aSDan Williams int i, rc; 244564e871aSDan Williams 245564e871aSDan Williams for (i = 0; i < ARRAY_SIZE(label_size); i++) { 246564e871aSDan Williams ndd->nslabel_size = label_size[i]; 247564e871aSDan Williams rc = __nd_label_validate(ndd); 248564e871aSDan Williams if (rc >= 0) 249564e871aSDan Williams return rc; 250564e871aSDan Williams } 251564e871aSDan Williams 252564e871aSDan Williams return -1; 253564e871aSDan Williams } 254564e871aSDan Williams 2557d47aad4SAlexander Duyck static void nd_label_copy(struct nvdimm_drvdata *ndd, 2567d47aad4SAlexander Duyck struct nd_namespace_index *dst, 2574a826c83SDan Williams struct nd_namespace_index *src) 2584a826c83SDan Williams { 25919418b02SAlexander Duyck /* just exit if either destination or source is NULL */ 26019418b02SAlexander Duyck if (!dst || !src) 2614a826c83SDan Williams return; 2624a826c83SDan Williams 2634a826c83SDan Williams memcpy(dst, src, sizeof_namespace_index(ndd)); 2644a826c83SDan Williams } 2654a826c83SDan Williams 2664a826c83SDan Williams static struct nd_namespace_label *nd_label_base(struct nvdimm_drvdata *ndd) 2674a826c83SDan Williams { 2684a826c83SDan Williams void *base = to_namespace_index(ndd, 0); 2694a826c83SDan Williams 2704a826c83SDan Williams return base + 2 * sizeof_namespace_index(ndd); 2714a826c83SDan Williams } 2724a826c83SDan Williams 273f524bf27SDan Williams static int to_slot(struct nvdimm_drvdata *ndd, 274f524bf27SDan Williams struct nd_namespace_label *nd_label) 275f524bf27SDan Williams { 276564e871aSDan Williams unsigned long label, base; 277564e871aSDan Williams 278564e871aSDan Williams label = (unsigned long) nd_label; 279564e871aSDan Williams base = (unsigned long) nd_label_base(ndd); 280564e871aSDan Williams 281564e871aSDan Williams return (label - base) / sizeof_namespace_label(ndd); 282564e871aSDan Williams } 283564e871aSDan Williams 284564e871aSDan Williams static struct nd_namespace_label *to_label(struct nvdimm_drvdata *ndd, int slot) 285564e871aSDan Williams { 286564e871aSDan Williams unsigned long label, base; 287564e871aSDan Williams 288564e871aSDan Williams base = (unsigned long) nd_label_base(ndd); 289564e871aSDan Williams label = base + sizeof_namespace_label(ndd) * slot; 290564e871aSDan Williams 291564e871aSDan Williams return (struct nd_namespace_label *) label; 292f524bf27SDan Williams } 293f524bf27SDan Williams 2944a826c83SDan Williams #define for_each_clear_bit_le(bit, addr, size) \ 2954a826c83SDan Williams for ((bit) = find_next_zero_bit_le((addr), (size), 0); \ 2964a826c83SDan Williams (bit) < (size); \ 2974a826c83SDan Williams (bit) = find_next_zero_bit_le((addr), (size), (bit) + 1)) 2984a826c83SDan Williams 2994a826c83SDan Williams /** 300f524bf27SDan Williams * preamble_index - common variable initialization for nd_label_* routines 3014a826c83SDan Williams * @ndd: dimm container for the relevant label set 302f524bf27SDan Williams * @idx: namespace_index index 3034a826c83SDan Williams * @nsindex_out: on return set to the currently active namespace index 3044a826c83SDan Williams * @free: on return set to the free label bitmap in the index 3054a826c83SDan Williams * @nslot: on return set to the number of slots in the label space 3064a826c83SDan Williams */ 307f524bf27SDan Williams static bool preamble_index(struct nvdimm_drvdata *ndd, int idx, 3084a826c83SDan Williams struct nd_namespace_index **nsindex_out, 3094a826c83SDan Williams unsigned long **free, u32 *nslot) 3104a826c83SDan Williams { 3114a826c83SDan Williams struct nd_namespace_index *nsindex; 3124a826c83SDan Williams 313f524bf27SDan Williams nsindex = to_namespace_index(ndd, idx); 3144a826c83SDan Williams if (nsindex == NULL) 3154a826c83SDan Williams return false; 3164a826c83SDan Williams 3174a826c83SDan Williams *free = (unsigned long *) nsindex->free; 3184a826c83SDan Williams *nslot = __le32_to_cpu(nsindex->nslot); 3194a826c83SDan Williams *nsindex_out = nsindex; 3204a826c83SDan Williams 3214a826c83SDan Williams return true; 3224a826c83SDan Williams } 3234a826c83SDan Williams 324bf9bccc1SDan Williams char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags) 3254a826c83SDan Williams { 3264a826c83SDan Williams if (!label_id || !uuid) 3274a826c83SDan Williams return NULL; 3284a826c83SDan Williams snprintf(label_id->id, ND_LABEL_ID_SIZE, "%s-%pUb", 3294a826c83SDan Williams flags & NSLABEL_FLAG_LOCAL ? "blk" : "pmem", uuid); 3304a826c83SDan Williams return label_id->id; 3314a826c83SDan Williams } 3324a826c83SDan Williams 333f524bf27SDan Williams static bool preamble_current(struct nvdimm_drvdata *ndd, 334f524bf27SDan Williams struct nd_namespace_index **nsindex, 335f524bf27SDan Williams unsigned long **free, u32 *nslot) 336f524bf27SDan Williams { 337f524bf27SDan Williams return preamble_index(ndd, ndd->ns_current, nsindex, 338f524bf27SDan Williams free, nslot); 339f524bf27SDan Williams } 340f524bf27SDan Williams 341f524bf27SDan Williams static bool preamble_next(struct nvdimm_drvdata *ndd, 342f524bf27SDan Williams struct nd_namespace_index **nsindex, 343f524bf27SDan Williams unsigned long **free, u32 *nslot) 344f524bf27SDan Williams { 345f524bf27SDan Williams return preamble_index(ndd, ndd->ns_next, nsindex, 346f524bf27SDan Williams free, nslot); 347f524bf27SDan Williams } 348f524bf27SDan Williams 349355d8388SDan Williams static bool slot_valid(struct nvdimm_drvdata *ndd, 350355d8388SDan Williams struct nd_namespace_label *nd_label, u32 slot) 3514a826c83SDan Williams { 3524a826c83SDan Williams /* check that we are written where we expect to be written */ 353*b4366a82SDan Williams if (slot != nsl_get_slot(ndd, nd_label)) 3544a826c83SDan Williams return false; 3554a826c83SDan Williams 356355d8388SDan Williams /* check checksum */ 357355d8388SDan Williams if (namespace_label_has(ndd, checksum)) { 358355d8388SDan Williams u64 sum, sum_save; 359355d8388SDan Williams 360*b4366a82SDan Williams sum_save = nsl_get_checksum(ndd, nd_label); 361355d8388SDan Williams nd_label->checksum = __cpu_to_le64(0); 362355d8388SDan Williams sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1); 363355d8388SDan Williams nd_label->checksum = __cpu_to_le64(sum_save); 364355d8388SDan Williams if (sum != sum_save) { 365426824d6SDan Williams dev_dbg(ndd->dev, "fail checksum. slot: %d expect: %#llx\n", 366426824d6SDan Williams slot, sum); 367355d8388SDan Williams return false; 368355d8388SDan Williams } 369355d8388SDan Williams } 370355d8388SDan Williams 3714a826c83SDan Williams return true; 3724a826c83SDan Williams } 3734a826c83SDan Williams 3744a826c83SDan Williams int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) 3754a826c83SDan Williams { 3764a826c83SDan Williams struct nd_namespace_index *nsindex; 3774a826c83SDan Williams unsigned long *free; 3784a826c83SDan Williams u32 nslot, slot; 3794a826c83SDan Williams 3804a826c83SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 3814a826c83SDan Williams return 0; /* no label, nothing to reserve */ 3824a826c83SDan Williams 3834a826c83SDan Williams for_each_clear_bit_le(slot, free, nslot) { 384d5d30d5aSDan Williams struct nvdimm *nvdimm = to_nvdimm(ndd->dev); 3854a826c83SDan Williams struct nd_namespace_label *nd_label; 3864a826c83SDan Williams struct nd_region *nd_region = NULL; 3874a826c83SDan Williams u8 label_uuid[NSLABEL_UUID_LEN]; 3884a826c83SDan Williams struct nd_label_id label_id; 3894a826c83SDan Williams struct resource *res; 3904a826c83SDan Williams u32 flags; 3914a826c83SDan Williams 392564e871aSDan Williams nd_label = to_label(ndd, slot); 3934a826c83SDan Williams 394355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) 3954a826c83SDan Williams continue; 3964a826c83SDan Williams 3974a826c83SDan Williams memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); 398*b4366a82SDan Williams flags = nsl_get_flags(ndd, nd_label); 399d5d30d5aSDan Williams if (test_bit(NDD_NOBLK, &nvdimm->flags)) 400d5d30d5aSDan Williams flags &= ~NSLABEL_FLAG_LOCAL; 4014a826c83SDan Williams nd_label_gen_id(&label_id, label_uuid, flags); 4024a826c83SDan Williams res = nvdimm_allocate_dpa(ndd, &label_id, 403*b4366a82SDan Williams nsl_get_dpa(ndd, nd_label), 404*b4366a82SDan Williams nsl_get_rawsize(ndd, nd_label)); 4054a826c83SDan Williams nd_dbg_dpa(nd_region, ndd, res, "reserve\n"); 4064a826c83SDan Williams if (!res) 4074a826c83SDan Williams return -EBUSY; 4084a826c83SDan Williams } 4094a826c83SDan Williams 4104a826c83SDan Williams return 0; 4114a826c83SDan Williams } 412bf9bccc1SDan Williams 4132d657d17SAlexander Duyck int nd_label_data_init(struct nvdimm_drvdata *ndd) 4142d657d17SAlexander Duyck { 4157d47aad4SAlexander Duyck size_t config_size, read_size, max_xfer, offset; 4167d47aad4SAlexander Duyck struct nd_namespace_index *nsindex; 4177d47aad4SAlexander Duyck unsigned int i; 4182d657d17SAlexander Duyck int rc = 0; 41997052c1cSDan Williams u32 nslot; 4202d657d17SAlexander Duyck 4212d657d17SAlexander Duyck if (ndd->data) 4222d657d17SAlexander Duyck return 0; 4232d657d17SAlexander Duyck 4242d657d17SAlexander Duyck if (ndd->nsarea.status || ndd->nsarea.max_xfer == 0) { 4252d657d17SAlexander Duyck dev_dbg(ndd->dev, "failed to init config data area: (%u:%u)\n", 4262d657d17SAlexander Duyck ndd->nsarea.max_xfer, ndd->nsarea.config_size); 4272d657d17SAlexander Duyck return -ENXIO; 4282d657d17SAlexander Duyck } 4292d657d17SAlexander Duyck 4302d657d17SAlexander Duyck /* 4312d657d17SAlexander Duyck * We need to determine the maximum index area as this is the section 4322d657d17SAlexander Duyck * we must read and validate before we can start processing labels. 4332d657d17SAlexander Duyck * 4342d657d17SAlexander Duyck * If the area is too small to contain the two indexes and 2 labels 4352d657d17SAlexander Duyck * then we abort. 4362d657d17SAlexander Duyck * 4372d657d17SAlexander Duyck * Start at a label size of 128 as this should result in the largest 4382d657d17SAlexander Duyck * possible namespace index size. 4392d657d17SAlexander Duyck */ 4402d657d17SAlexander Duyck ndd->nslabel_size = 128; 4412d657d17SAlexander Duyck read_size = sizeof_namespace_index(ndd) * 2; 4422d657d17SAlexander Duyck if (!read_size) 4432d657d17SAlexander Duyck return -ENXIO; 4442d657d17SAlexander Duyck 4452d657d17SAlexander Duyck /* Allocate config data */ 4462d657d17SAlexander Duyck config_size = ndd->nsarea.config_size; 4472d657d17SAlexander Duyck ndd->data = kvzalloc(config_size, GFP_KERNEL); 4482d657d17SAlexander Duyck if (!ndd->data) 4492d657d17SAlexander Duyck return -ENOMEM; 4502d657d17SAlexander Duyck 4517d47aad4SAlexander Duyck /* 4527d47aad4SAlexander Duyck * We want to guarantee as few reads as possible while conserving 4537d47aad4SAlexander Duyck * memory. To do that we figure out how much unused space will be left 4547d47aad4SAlexander Duyck * in the last read, divide that by the total number of reads it is 4557d47aad4SAlexander Duyck * going to take given our maximum transfer size, and then reduce our 4567d47aad4SAlexander Duyck * maximum transfer size based on that result. 4577d47aad4SAlexander Duyck */ 4587d47aad4SAlexander Duyck max_xfer = min_t(size_t, ndd->nsarea.max_xfer, config_size); 4597d47aad4SAlexander Duyck if (read_size < max_xfer) { 4607d47aad4SAlexander Duyck /* trim waste */ 4617d47aad4SAlexander Duyck max_xfer -= ((max_xfer - 1) - (config_size - 1) % max_xfer) / 4627d47aad4SAlexander Duyck DIV_ROUND_UP(config_size, max_xfer); 4637d47aad4SAlexander Duyck /* make certain we read indexes in exactly 1 read */ 4647d47aad4SAlexander Duyck if (max_xfer < read_size) 4657d47aad4SAlexander Duyck max_xfer = read_size; 4667d47aad4SAlexander Duyck } 4677d47aad4SAlexander Duyck 4687d47aad4SAlexander Duyck /* Make our initial read size a multiple of max_xfer size */ 4697d47aad4SAlexander Duyck read_size = min(DIV_ROUND_UP(read_size, max_xfer) * max_xfer, 4707d47aad4SAlexander Duyck config_size); 4717d47aad4SAlexander Duyck 4727d47aad4SAlexander Duyck /* Read the index data */ 4737d47aad4SAlexander Duyck rc = nvdimm_get_config_data(ndd, ndd->data, 0, read_size); 4747d47aad4SAlexander Duyck if (rc) 4757d47aad4SAlexander Duyck goto out_err; 4767d47aad4SAlexander Duyck 4777d47aad4SAlexander Duyck /* Validate index data, if not valid assume all labels are invalid */ 4787d47aad4SAlexander Duyck ndd->ns_current = nd_label_validate(ndd); 4797d47aad4SAlexander Duyck if (ndd->ns_current < 0) 4807d47aad4SAlexander Duyck return 0; 4817d47aad4SAlexander Duyck 4827d47aad4SAlexander Duyck /* Record our index values */ 4837d47aad4SAlexander Duyck ndd->ns_next = nd_label_next_nsindex(ndd->ns_current); 4847d47aad4SAlexander Duyck 4857d47aad4SAlexander Duyck /* Copy "current" index on top of the "next" index */ 4867d47aad4SAlexander Duyck nsindex = to_current_namespace_index(ndd); 4877d47aad4SAlexander Duyck nd_label_copy(ndd, to_next_namespace_index(ndd), nsindex); 4887d47aad4SAlexander Duyck 4897d47aad4SAlexander Duyck /* Determine starting offset for label data */ 4907d47aad4SAlexander Duyck offset = __le64_to_cpu(nsindex->labeloff); 49197052c1cSDan Williams nslot = __le32_to_cpu(nsindex->nslot); 4927d47aad4SAlexander Duyck 4937d47aad4SAlexander Duyck /* Loop through the free list pulling in any active labels */ 49497052c1cSDan Williams for (i = 0; i < nslot; i++, offset += ndd->nslabel_size) { 4957d47aad4SAlexander Duyck size_t label_read_size; 4967d47aad4SAlexander Duyck 4977d47aad4SAlexander Duyck /* zero out the unused labels */ 4987d47aad4SAlexander Duyck if (test_bit_le(i, nsindex->free)) { 4997d47aad4SAlexander Duyck memset(ndd->data + offset, 0, ndd->nslabel_size); 5007d47aad4SAlexander Duyck continue; 5017d47aad4SAlexander Duyck } 5027d47aad4SAlexander Duyck 5037d47aad4SAlexander Duyck /* if we already read past here then just continue */ 5047d47aad4SAlexander Duyck if (offset + ndd->nslabel_size <= read_size) 5057d47aad4SAlexander Duyck continue; 5067d47aad4SAlexander Duyck 5077d47aad4SAlexander Duyck /* if we haven't read in a while reset our read_size offset */ 5087d47aad4SAlexander Duyck if (read_size < offset) 5097d47aad4SAlexander Duyck read_size = offset; 5107d47aad4SAlexander Duyck 5117d47aad4SAlexander Duyck /* determine how much more will be read after this next call. */ 5127d47aad4SAlexander Duyck label_read_size = offset + ndd->nslabel_size - read_size; 5137d47aad4SAlexander Duyck label_read_size = DIV_ROUND_UP(label_read_size, max_xfer) * 5147d47aad4SAlexander Duyck max_xfer; 5157d47aad4SAlexander Duyck 5167d47aad4SAlexander Duyck /* truncate last read if needed */ 5177d47aad4SAlexander Duyck if (read_size + label_read_size > config_size) 5187d47aad4SAlexander Duyck label_read_size = config_size - read_size; 5197d47aad4SAlexander Duyck 5207d47aad4SAlexander Duyck /* Read the label data */ 5217d47aad4SAlexander Duyck rc = nvdimm_get_config_data(ndd, ndd->data + read_size, 5227d47aad4SAlexander Duyck read_size, label_read_size); 5237d47aad4SAlexander Duyck if (rc) 5247d47aad4SAlexander Duyck goto out_err; 5257d47aad4SAlexander Duyck 5267d47aad4SAlexander Duyck /* push read_size to next read offset */ 5277d47aad4SAlexander Duyck read_size += label_read_size; 5287d47aad4SAlexander Duyck } 5297d47aad4SAlexander Duyck 5307d47aad4SAlexander Duyck dev_dbg(ndd->dev, "len: %zu rc: %d\n", offset, rc); 5317d47aad4SAlexander Duyck out_err: 5327d47aad4SAlexander Duyck return rc; 5332d657d17SAlexander Duyck } 5342d657d17SAlexander Duyck 535bf9bccc1SDan Williams int nd_label_active_count(struct nvdimm_drvdata *ndd) 536bf9bccc1SDan Williams { 537bf9bccc1SDan Williams struct nd_namespace_index *nsindex; 538bf9bccc1SDan Williams unsigned long *free; 539bf9bccc1SDan Williams u32 nslot, slot; 540bf9bccc1SDan Williams int count = 0; 541bf9bccc1SDan Williams 542bf9bccc1SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 543bf9bccc1SDan Williams return 0; 544bf9bccc1SDan Williams 545bf9bccc1SDan Williams for_each_clear_bit_le(slot, free, nslot) { 546bf9bccc1SDan Williams struct nd_namespace_label *nd_label; 547bf9bccc1SDan Williams 548564e871aSDan Williams nd_label = to_label(ndd, slot); 549bf9bccc1SDan Williams 550355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) { 551*b4366a82SDan Williams u32 label_slot = nsl_get_slot(ndd, nd_label); 552*b4366a82SDan Williams u64 size = nsl_get_rawsize(ndd, nd_label); 553*b4366a82SDan Williams u64 dpa = nsl_get_dpa(ndd, nd_label); 554bf9bccc1SDan Williams 555bf9bccc1SDan Williams dev_dbg(ndd->dev, 556426824d6SDan Williams "slot%d invalid slot: %d dpa: %llx size: %llx\n", 557426824d6SDan Williams slot, label_slot, dpa, size); 558bf9bccc1SDan Williams continue; 559bf9bccc1SDan Williams } 560bf9bccc1SDan Williams count++; 561bf9bccc1SDan Williams } 562bf9bccc1SDan Williams return count; 563bf9bccc1SDan Williams } 564bf9bccc1SDan Williams 565bf9bccc1SDan Williams struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n) 566bf9bccc1SDan Williams { 567bf9bccc1SDan Williams struct nd_namespace_index *nsindex; 568bf9bccc1SDan Williams unsigned long *free; 569bf9bccc1SDan Williams u32 nslot, slot; 570bf9bccc1SDan Williams 571bf9bccc1SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 572bf9bccc1SDan Williams return NULL; 573bf9bccc1SDan Williams 574bf9bccc1SDan Williams for_each_clear_bit_le(slot, free, nslot) { 575bf9bccc1SDan Williams struct nd_namespace_label *nd_label; 576bf9bccc1SDan Williams 577564e871aSDan Williams nd_label = to_label(ndd, slot); 578355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) 579bf9bccc1SDan Williams continue; 580bf9bccc1SDan Williams 581bf9bccc1SDan Williams if (n-- == 0) 582564e871aSDan Williams return to_label(ndd, slot); 583bf9bccc1SDan Williams } 584bf9bccc1SDan Williams 585bf9bccc1SDan Williams return NULL; 586bf9bccc1SDan Williams } 587f524bf27SDan Williams 5880ba1c634SDan Williams u32 nd_label_alloc_slot(struct nvdimm_drvdata *ndd) 589f524bf27SDan Williams { 590f524bf27SDan Williams struct nd_namespace_index *nsindex; 591f524bf27SDan Williams unsigned long *free; 592f524bf27SDan Williams u32 nslot, slot; 593f524bf27SDan Williams 594f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 595f524bf27SDan Williams return UINT_MAX; 596f524bf27SDan Williams 597f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 598f524bf27SDan Williams 599f524bf27SDan Williams slot = find_next_bit_le(free, nslot, 0); 600f524bf27SDan Williams if (slot == nslot) 601f524bf27SDan Williams return UINT_MAX; 602f524bf27SDan Williams 603f524bf27SDan Williams clear_bit_le(slot, free); 604f524bf27SDan Williams 605f524bf27SDan Williams return slot; 606f524bf27SDan Williams } 607f524bf27SDan Williams 6080ba1c634SDan Williams bool nd_label_free_slot(struct nvdimm_drvdata *ndd, u32 slot) 609f524bf27SDan Williams { 610f524bf27SDan Williams struct nd_namespace_index *nsindex; 611f524bf27SDan Williams unsigned long *free; 612f524bf27SDan Williams u32 nslot; 613f524bf27SDan Williams 614f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 615f524bf27SDan Williams return false; 616f524bf27SDan Williams 617f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 618f524bf27SDan Williams 619f524bf27SDan Williams if (slot < nslot) 620f524bf27SDan Williams return !test_and_set_bit_le(slot, free); 621f524bf27SDan Williams return false; 622f524bf27SDan Williams } 623f524bf27SDan Williams 624f524bf27SDan Williams u32 nd_label_nfree(struct nvdimm_drvdata *ndd) 625f524bf27SDan Williams { 626f524bf27SDan Williams struct nd_namespace_index *nsindex; 627f524bf27SDan Williams unsigned long *free; 628f524bf27SDan Williams u32 nslot; 629f524bf27SDan Williams 630f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 631f524bf27SDan Williams 632f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 6330ba1c634SDan Williams return nvdimm_num_label_slots(ndd); 634f524bf27SDan Williams 635f524bf27SDan Williams return bitmap_weight(free, nslot); 636f524bf27SDan Williams } 637f524bf27SDan Williams 638f524bf27SDan Williams static int nd_label_write_index(struct nvdimm_drvdata *ndd, int index, u32 seq, 639f524bf27SDan Williams unsigned long flags) 640f524bf27SDan Williams { 641f524bf27SDan Williams struct nd_namespace_index *nsindex; 642f524bf27SDan Williams unsigned long offset; 643f524bf27SDan Williams u64 checksum; 644f524bf27SDan Williams u32 nslot; 645f524bf27SDan Williams int rc; 646f524bf27SDan Williams 647f524bf27SDan Williams nsindex = to_namespace_index(ndd, index); 648f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) 649f524bf27SDan Williams nslot = nvdimm_num_label_slots(ndd); 650f524bf27SDan Williams else 651f524bf27SDan Williams nslot = __le32_to_cpu(nsindex->nslot); 652f524bf27SDan Williams 653f524bf27SDan Williams memcpy(nsindex->sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN); 654564e871aSDan Williams memset(&nsindex->flags, 0, 3); 655564e871aSDan Williams nsindex->labelsize = sizeof_namespace_label(ndd) >> 8; 656f524bf27SDan Williams nsindex->seq = __cpu_to_le32(seq); 657f524bf27SDan Williams offset = (unsigned long) nsindex 658f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 659f524bf27SDan Williams nsindex->myoff = __cpu_to_le64(offset); 660f524bf27SDan Williams nsindex->mysize = __cpu_to_le64(sizeof_namespace_index(ndd)); 661f524bf27SDan Williams offset = (unsigned long) to_namespace_index(ndd, 662f524bf27SDan Williams nd_label_next_nsindex(index)) 663f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 664f524bf27SDan Williams nsindex->otheroff = __cpu_to_le64(offset); 665f524bf27SDan Williams offset = (unsigned long) nd_label_base(ndd) 666f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 667f524bf27SDan Williams nsindex->labeloff = __cpu_to_le64(offset); 668f524bf27SDan Williams nsindex->nslot = __cpu_to_le32(nslot); 669f524bf27SDan Williams nsindex->major = __cpu_to_le16(1); 6708990cdf1SDan Williams if (sizeof_namespace_label(ndd) < 256) 671f524bf27SDan Williams nsindex->minor = __cpu_to_le16(1); 6728990cdf1SDan Williams else 6738990cdf1SDan Williams nsindex->minor = __cpu_to_le16(2); 674f524bf27SDan Williams nsindex->checksum = __cpu_to_le64(0); 675f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) { 676f524bf27SDan Williams unsigned long *free = (unsigned long *) nsindex->free; 677f524bf27SDan Williams u32 nfree = ALIGN(nslot, BITS_PER_LONG); 678f524bf27SDan Williams int last_bits, i; 679f524bf27SDan Williams 680f524bf27SDan Williams memset(nsindex->free, 0xff, nfree / 8); 681f524bf27SDan Williams for (i = 0, last_bits = nfree - nslot; i < last_bits; i++) 682f524bf27SDan Williams clear_bit_le(nslot + i, free); 683f524bf27SDan Williams } 684f524bf27SDan Williams checksum = nd_fletcher64(nsindex, sizeof_namespace_index(ndd), 1); 685f524bf27SDan Williams nsindex->checksum = __cpu_to_le64(checksum); 686f524bf27SDan Williams rc = nvdimm_set_config_data(ndd, __le64_to_cpu(nsindex->myoff), 687f524bf27SDan Williams nsindex, sizeof_namespace_index(ndd)); 688f524bf27SDan Williams if (rc < 0) 689f524bf27SDan Williams return rc; 690f524bf27SDan Williams 691f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) 692f524bf27SDan Williams return 0; 693f524bf27SDan Williams 694f524bf27SDan Williams /* copy the index we just wrote to the new 'next' */ 695f524bf27SDan Williams WARN_ON(index != ndd->ns_next); 696f524bf27SDan Williams nd_label_copy(ndd, to_current_namespace_index(ndd), nsindex); 697f524bf27SDan Williams ndd->ns_current = nd_label_next_nsindex(ndd->ns_current); 698f524bf27SDan Williams ndd->ns_next = nd_label_next_nsindex(ndd->ns_next); 699f524bf27SDan Williams WARN_ON(ndd->ns_current == ndd->ns_next); 700f524bf27SDan Williams 701f524bf27SDan Williams return 0; 702f524bf27SDan Williams } 703f524bf27SDan Williams 704f524bf27SDan Williams static unsigned long nd_label_offset(struct nvdimm_drvdata *ndd, 705f524bf27SDan Williams struct nd_namespace_label *nd_label) 706f524bf27SDan Williams { 707f524bf27SDan Williams return (unsigned long) nd_label 708f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 709f524bf27SDan Williams } 710f524bf27SDan Williams 711b3fde74eSDan Williams enum nvdimm_claim_class to_nvdimm_cclass(guid_t *guid) 712b3fde74eSDan Williams { 713b3fde74eSDan Williams if (guid_equal(guid, &nvdimm_btt_guid)) 714b3fde74eSDan Williams return NVDIMM_CCLASS_BTT; 71514e49454SVishal Verma else if (guid_equal(guid, &nvdimm_btt2_guid)) 71614e49454SVishal Verma return NVDIMM_CCLASS_BTT2; 717b3fde74eSDan Williams else if (guid_equal(guid, &nvdimm_pfn_guid)) 718b3fde74eSDan Williams return NVDIMM_CCLASS_PFN; 719b3fde74eSDan Williams else if (guid_equal(guid, &nvdimm_dax_guid)) 720b3fde74eSDan Williams return NVDIMM_CCLASS_DAX; 721b3fde74eSDan Williams else if (guid_equal(guid, &guid_null)) 722b3fde74eSDan Williams return NVDIMM_CCLASS_NONE; 723b3fde74eSDan Williams 724b3fde74eSDan Williams return NVDIMM_CCLASS_UNKNOWN; 725b3fde74eSDan Williams } 726b3fde74eSDan Williams 727b3fde74eSDan Williams static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class, 728b3fde74eSDan Williams guid_t *target) 729b3fde74eSDan Williams { 730b3fde74eSDan Williams if (claim_class == NVDIMM_CCLASS_BTT) 731b3fde74eSDan Williams return &nvdimm_btt_guid; 73214e49454SVishal Verma else if (claim_class == NVDIMM_CCLASS_BTT2) 73314e49454SVishal Verma return &nvdimm_btt2_guid; 734b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_PFN) 735b3fde74eSDan Williams return &nvdimm_pfn_guid; 736b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_DAX) 737b3fde74eSDan Williams return &nvdimm_dax_guid; 738b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_UNKNOWN) { 739b3fde74eSDan Williams /* 740b3fde74eSDan Williams * If we're modifying a namespace for which we don't 741b3fde74eSDan Williams * know the claim_class, don't touch the existing guid. 742b3fde74eSDan Williams */ 743b3fde74eSDan Williams return target; 744b3fde74eSDan Williams } else 745b3fde74eSDan Williams return &guid_null; 746b3fde74eSDan Williams } 747b3fde74eSDan Williams 748c4703ce1SDan Williams static void reap_victim(struct nd_mapping *nd_mapping, 749c4703ce1SDan Williams struct nd_label_ent *victim) 750c4703ce1SDan Williams { 751c4703ce1SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 752c4703ce1SDan Williams u32 slot = to_slot(ndd, victim->label); 753c4703ce1SDan Williams 754c4703ce1SDan Williams dev_dbg(ndd->dev, "free: %d\n", slot); 755c4703ce1SDan Williams nd_label_free_slot(ndd, slot); 756c4703ce1SDan Williams victim->label = NULL; 757c4703ce1SDan Williams } 758c4703ce1SDan Williams 759f524bf27SDan Williams static int __pmem_label_update(struct nd_region *nd_region, 760f524bf27SDan Williams struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm, 761966d23a0SDan Williams int pos, unsigned long flags) 762f524bf27SDan Williams { 763b3fde74eSDan Williams struct nd_namespace_common *ndns = &nspm->nsio.common; 764faec6f8aSDan Williams struct nd_interleave_set *nd_set = nd_region->nd_set; 765f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 766f524bf27SDan Williams struct nd_namespace_label *nd_label; 767f524bf27SDan Williams struct nd_namespace_index *nsindex; 768c4703ce1SDan Williams struct nd_label_ent *label_ent; 76916660eaeSDan Williams struct nd_label_id label_id; 77016660eaeSDan Williams struct resource *res; 771f524bf27SDan Williams unsigned long *free; 772f524bf27SDan Williams u32 nslot, slot; 773f524bf27SDan Williams size_t offset; 774c12c48ceSDan Williams u64 cookie; 775f524bf27SDan Williams int rc; 776f524bf27SDan Williams 777f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 778f524bf27SDan Williams return -ENXIO; 779f524bf27SDan Williams 780c12c48ceSDan Williams cookie = nd_region_interleave_set_cookie(nd_region, nsindex); 78116660eaeSDan Williams nd_label_gen_id(&label_id, nspm->uuid, 0); 78216660eaeSDan Williams for_each_dpa_resource(ndd, res) 78316660eaeSDan Williams if (strcmp(res->name, label_id.id) == 0) 78416660eaeSDan Williams break; 78516660eaeSDan Williams 78616660eaeSDan Williams if (!res) { 78716660eaeSDan Williams WARN_ON_ONCE(1); 78816660eaeSDan Williams return -ENXIO; 78916660eaeSDan Williams } 79016660eaeSDan Williams 791f524bf27SDan Williams /* allocate and write the label to the staging (next) index */ 792f524bf27SDan Williams slot = nd_label_alloc_slot(ndd); 793f524bf27SDan Williams if (slot == UINT_MAX) 794f524bf27SDan Williams return -ENXIO; 795426824d6SDan Williams dev_dbg(ndd->dev, "allocated: %d\n", slot); 796f524bf27SDan Williams 797564e871aSDan Williams nd_label = to_label(ndd, slot); 798564e871aSDan Williams memset(nd_label, 0, sizeof_namespace_label(ndd)); 799f524bf27SDan Williams memcpy(nd_label->uuid, nspm->uuid, NSLABEL_UUID_LEN); 800f524bf27SDan Williams if (nspm->alt_name) 801f524bf27SDan Williams memcpy(nd_label->name, nspm->alt_name, NSLABEL_NAME_LEN); 802966d23a0SDan Williams nd_label->flags = __cpu_to_le32(flags); 803f524bf27SDan Williams nd_label->nlabel = __cpu_to_le16(nd_region->ndr_mappings); 804f524bf27SDan Williams nd_label->position = __cpu_to_le16(pos); 805f524bf27SDan Williams nd_label->isetcookie = __cpu_to_le64(cookie); 80616660eaeSDan Williams nd_label->rawsize = __cpu_to_le64(resource_size(res)); 8072de5148fSDan Williams nd_label->lbasize = __cpu_to_le64(nspm->lbasize); 80816660eaeSDan Williams nd_label->dpa = __cpu_to_le64(res->start); 809f524bf27SDan Williams nd_label->slot = __cpu_to_le32(slot); 810faec6f8aSDan Williams if (namespace_label_has(ndd, type_guid)) 811faec6f8aSDan Williams guid_copy(&nd_label->type_guid, &nd_set->type_guid); 812b3fde74eSDan Williams if (namespace_label_has(ndd, abstraction_guid)) 813b3fde74eSDan Williams guid_copy(&nd_label->abstraction_guid, 814b3fde74eSDan Williams to_abstraction_guid(ndns->claim_class, 815b3fde74eSDan Williams &nd_label->abstraction_guid)); 816355d8388SDan Williams if (namespace_label_has(ndd, checksum)) { 817355d8388SDan Williams u64 sum; 818355d8388SDan Williams 819355d8388SDan Williams nd_label->checksum = __cpu_to_le64(0); 820355d8388SDan Williams sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1); 821355d8388SDan Williams nd_label->checksum = __cpu_to_le64(sum); 822355d8388SDan Williams } 823426824d6SDan Williams nd_dbg_dpa(nd_region, ndd, res, "\n"); 824f524bf27SDan Williams 825f524bf27SDan Williams /* update label */ 826f524bf27SDan Williams offset = nd_label_offset(ndd, nd_label); 827f524bf27SDan Williams rc = nvdimm_set_config_data(ndd, offset, nd_label, 828564e871aSDan Williams sizeof_namespace_label(ndd)); 829f524bf27SDan Williams if (rc < 0) 830f524bf27SDan Williams return rc; 831f524bf27SDan Williams 832f524bf27SDan Williams /* Garbage collect the previous label */ 833ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 83416660eaeSDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) { 83516660eaeSDan Williams if (!label_ent->label) 83616660eaeSDan Williams continue; 837c4703ce1SDan Williams if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags) 838c4703ce1SDan Williams || memcmp(nspm->uuid, label_ent->label->uuid, 839c4703ce1SDan Williams NSLABEL_UUID_LEN) == 0) 840c4703ce1SDan Williams reap_victim(nd_mapping, label_ent); 841f524bf27SDan Williams } 842f524bf27SDan Williams 843f524bf27SDan Williams /* update index */ 844f524bf27SDan Williams rc = nd_label_write_index(ndd, ndd->ns_next, 845f524bf27SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 84616660eaeSDan Williams if (rc == 0) { 84716660eaeSDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) 84816660eaeSDan Williams if (!label_ent->label) { 849ae8219f1SDan Williams label_ent->label = nd_label; 85016660eaeSDan Williams nd_label = NULL; 85116660eaeSDan Williams break; 85216660eaeSDan Williams } 85316660eaeSDan Williams dev_WARN_ONCE(&nspm->nsio.common.dev, nd_label, 85416660eaeSDan Williams "failed to track label: %d\n", 85516660eaeSDan Williams to_slot(ndd, nd_label)); 85616660eaeSDan Williams if (nd_label) 85716660eaeSDan Williams rc = -ENXIO; 85816660eaeSDan Williams } 859ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 860ae8219f1SDan Williams 861f524bf27SDan Williams return rc; 8620ba1c634SDan Williams } 8630ba1c634SDan Williams 8640ba1c634SDan Williams static bool is_old_resource(struct resource *res, struct resource **list, int n) 865f524bf27SDan Williams { 866f524bf27SDan Williams int i; 8670ba1c634SDan Williams 8680ba1c634SDan Williams if (res->flags & DPA_RESOURCE_ADJUSTED) 8690ba1c634SDan Williams return false; 8700ba1c634SDan Williams for (i = 0; i < n; i++) 8710ba1c634SDan Williams if (res == list[i]) 8720ba1c634SDan Williams return true; 8730ba1c634SDan Williams return false; 8740ba1c634SDan Williams } 8750ba1c634SDan Williams 8760ba1c634SDan Williams static struct resource *to_resource(struct nvdimm_drvdata *ndd, 8770ba1c634SDan Williams struct nd_namespace_label *nd_label) 8780ba1c634SDan Williams { 8790ba1c634SDan Williams struct resource *res; 8800ba1c634SDan Williams 8810ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 882*b4366a82SDan Williams if (res->start != nsl_get_dpa(ndd, nd_label)) 8830ba1c634SDan Williams continue; 884*b4366a82SDan Williams if (resource_size(res) != nsl_get_rawsize(ndd, nd_label)) 8850ba1c634SDan Williams continue; 8860ba1c634SDan Williams return res; 8870ba1c634SDan Williams } 8880ba1c634SDan Williams 8890ba1c634SDan Williams return NULL; 8900ba1c634SDan Williams } 8910ba1c634SDan Williams 8920ba1c634SDan Williams /* 8930ba1c634SDan Williams * 1/ Account all the labels that can be freed after this update 8940ba1c634SDan Williams * 2/ Allocate and write the label to the staging (next) index 8950ba1c634SDan Williams * 3/ Record the resources in the namespace device 8960ba1c634SDan Williams */ 8970ba1c634SDan Williams static int __blk_label_update(struct nd_region *nd_region, 8980ba1c634SDan Williams struct nd_mapping *nd_mapping, struct nd_namespace_blk *nsblk, 8990ba1c634SDan Williams int num_labels) 9000ba1c634SDan Williams { 901ae8219f1SDan Williams int i, alloc, victims, nfree, old_num_resources, nlabel, rc = -ENXIO; 902faec6f8aSDan Williams struct nd_interleave_set *nd_set = nd_region->nd_set; 903b3fde74eSDan Williams struct nd_namespace_common *ndns = &nsblk->common; 904f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 9050ba1c634SDan Williams struct nd_namespace_label *nd_label; 906ae8219f1SDan Williams struct nd_label_ent *label_ent, *e; 9070ba1c634SDan Williams struct nd_namespace_index *nsindex; 9080ba1c634SDan Williams unsigned long *free, *victim_map = NULL; 9090ba1c634SDan Williams struct resource *res, **old_res_list; 9100ba1c634SDan Williams struct nd_label_id label_id; 9110ba1c634SDan Williams u8 uuid[NSLABEL_UUID_LEN]; 9123934d841SDan Williams int min_dpa_idx = 0; 913ae8219f1SDan Williams LIST_HEAD(list); 9140ba1c634SDan Williams u32 nslot, slot; 915f524bf27SDan Williams 9160ba1c634SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 9170ba1c634SDan Williams return -ENXIO; 918f524bf27SDan Williams 9190ba1c634SDan Williams old_res_list = nsblk->res; 9200ba1c634SDan Williams nfree = nd_label_nfree(ndd); 9210ba1c634SDan Williams old_num_resources = nsblk->num_resources; 9220ba1c634SDan Williams nd_label_gen_id(&label_id, nsblk->uuid, NSLABEL_FLAG_LOCAL); 9230ba1c634SDan Williams 9240ba1c634SDan Williams /* 9250ba1c634SDan Williams * We need to loop over the old resources a few times, which seems a 9260ba1c634SDan Williams * bit inefficient, but we need to know that we have the label 9270ba1c634SDan Williams * space before we start mutating the tracking structures. 9280ba1c634SDan Williams * Otherwise the recovery method of last resort for userspace is 9290ba1c634SDan Williams * disable and re-enable the parent region. 9300ba1c634SDan Williams */ 9310ba1c634SDan Williams alloc = 0; 9320ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 9330ba1c634SDan Williams if (strcmp(res->name, label_id.id) != 0) 9340ba1c634SDan Williams continue; 9350ba1c634SDan Williams if (!is_old_resource(res, old_res_list, old_num_resources)) 9360ba1c634SDan Williams alloc++; 9370ba1c634SDan Williams } 9380ba1c634SDan Williams 9390ba1c634SDan Williams victims = 0; 9400ba1c634SDan Williams if (old_num_resources) { 9410ba1c634SDan Williams /* convert old local-label-map to dimm-slot victim-map */ 9429065ed12SAndy Shevchenko victim_map = bitmap_zalloc(nslot, GFP_KERNEL); 9430ba1c634SDan Williams if (!victim_map) 9440ba1c634SDan Williams return -ENOMEM; 9450ba1c634SDan Williams 9460ba1c634SDan Williams /* mark unused labels for garbage collection */ 9470ba1c634SDan Williams for_each_clear_bit_le(slot, free, nslot) { 948564e871aSDan Williams nd_label = to_label(ndd, slot); 9490ba1c634SDan Williams memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 9500ba1c634SDan Williams if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 9510ba1c634SDan Williams continue; 9520ba1c634SDan Williams res = to_resource(ndd, nd_label); 9530ba1c634SDan Williams if (res && is_old_resource(res, old_res_list, 9540ba1c634SDan Williams old_num_resources)) 9550ba1c634SDan Williams continue; 9560ba1c634SDan Williams slot = to_slot(ndd, nd_label); 9570ba1c634SDan Williams set_bit(slot, victim_map); 9580ba1c634SDan Williams victims++; 9590ba1c634SDan Williams } 9600ba1c634SDan Williams } 9610ba1c634SDan Williams 9620ba1c634SDan Williams /* don't allow updates that consume the last label */ 9630ba1c634SDan Williams if (nfree - alloc < 0 || nfree - alloc + victims < 1) { 9648c2f7e86SDan Williams dev_info(&nsblk->common.dev, "insufficient label space\n"); 9659065ed12SAndy Shevchenko bitmap_free(victim_map); 9660ba1c634SDan Williams return -ENOSPC; 9670ba1c634SDan Williams } 9680ba1c634SDan Williams /* from here on we need to abort on error */ 9690ba1c634SDan Williams 9700ba1c634SDan Williams 9710ba1c634SDan Williams /* assign all resources to the namespace before writing the labels */ 9720ba1c634SDan Williams nsblk->res = NULL; 9730ba1c634SDan Williams nsblk->num_resources = 0; 9740ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 9750ba1c634SDan Williams if (strcmp(res->name, label_id.id) != 0) 9760ba1c634SDan Williams continue; 9770ba1c634SDan Williams if (!nsblk_add_resource(nd_region, ndd, nsblk, res->start)) { 9780ba1c634SDan Williams rc = -ENOMEM; 9790ba1c634SDan Williams goto abort; 9800ba1c634SDan Williams } 9810ba1c634SDan Williams } 9820ba1c634SDan Williams 9832dd2a174SDan Williams /* release slots associated with any invalidated UUIDs */ 9842dd2a174SDan Williams mutex_lock(&nd_mapping->lock); 9852dd2a174SDan Williams list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) 9862dd2a174SDan Williams if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags)) { 9872dd2a174SDan Williams reap_victim(nd_mapping, label_ent); 9882dd2a174SDan Williams list_move(&label_ent->list, &list); 9892dd2a174SDan Williams } 9902dd2a174SDan Williams mutex_unlock(&nd_mapping->lock); 9912dd2a174SDan Williams 9923934d841SDan Williams /* 9933934d841SDan Williams * Find the resource associated with the first label in the set 9943934d841SDan Williams * per the v1.2 namespace specification. 9953934d841SDan Williams */ 9963934d841SDan Williams for (i = 0; i < nsblk->num_resources; i++) { 9973934d841SDan Williams struct resource *min = nsblk->res[min_dpa_idx]; 9983934d841SDan Williams 9993934d841SDan Williams res = nsblk->res[i]; 10003934d841SDan Williams if (res->start < min->start) 10013934d841SDan Williams min_dpa_idx = i; 10023934d841SDan Williams } 10033934d841SDan Williams 10040ba1c634SDan Williams for (i = 0; i < nsblk->num_resources; i++) { 10050ba1c634SDan Williams size_t offset; 10060ba1c634SDan Williams 10070ba1c634SDan Williams res = nsblk->res[i]; 10080ba1c634SDan Williams if (is_old_resource(res, old_res_list, old_num_resources)) 10090ba1c634SDan Williams continue; /* carry-over */ 10100ba1c634SDan Williams slot = nd_label_alloc_slot(ndd); 10114c467647SZhang Qilong if (slot == UINT_MAX) { 10124c467647SZhang Qilong rc = -ENXIO; 10130ba1c634SDan Williams goto abort; 10144c467647SZhang Qilong } 1015426824d6SDan Williams dev_dbg(ndd->dev, "allocated: %d\n", slot); 10160ba1c634SDan Williams 1017564e871aSDan Williams nd_label = to_label(ndd, slot); 1018564e871aSDan Williams memset(nd_label, 0, sizeof_namespace_label(ndd)); 10190ba1c634SDan Williams memcpy(nd_label->uuid, nsblk->uuid, NSLABEL_UUID_LEN); 10200ba1c634SDan Williams if (nsblk->alt_name) 10210ba1c634SDan Williams memcpy(nd_label->name, nsblk->alt_name, 10220ba1c634SDan Williams NSLABEL_NAME_LEN); 10230ba1c634SDan Williams nd_label->flags = __cpu_to_le32(NSLABEL_FLAG_LOCAL); 10248f2bc243SDan Williams 10258f2bc243SDan Williams /* 10268f2bc243SDan Williams * Use the presence of the type_guid as a flag to 10273934d841SDan Williams * determine isetcookie usage and nlabel + position 10283934d841SDan Williams * policy for blk-aperture namespaces. 10298f2bc243SDan Williams */ 10303934d841SDan Williams if (namespace_label_has(ndd, type_guid)) { 10313934d841SDan Williams if (i == min_dpa_idx) { 10323934d841SDan Williams nd_label->nlabel = __cpu_to_le16(nsblk->num_resources); 10333934d841SDan Williams nd_label->position = __cpu_to_le16(0); 10343934d841SDan Williams } else { 10353934d841SDan Williams nd_label->nlabel = __cpu_to_le16(0xffff); 10363934d841SDan Williams nd_label->position = __cpu_to_le16(0xffff); 10373934d841SDan Williams } 10388f2bc243SDan Williams nd_label->isetcookie = __cpu_to_le64(nd_set->cookie2); 10393934d841SDan Williams } else { 10403934d841SDan Williams nd_label->nlabel = __cpu_to_le16(0); /* N/A */ 10413934d841SDan Williams nd_label->position = __cpu_to_le16(0); /* N/A */ 10420ba1c634SDan Williams nd_label->isetcookie = __cpu_to_le64(0); /* N/A */ 10433934d841SDan Williams } 10448f2bc243SDan Williams 10450ba1c634SDan Williams nd_label->dpa = __cpu_to_le64(res->start); 10460ba1c634SDan Williams nd_label->rawsize = __cpu_to_le64(resource_size(res)); 10470ba1c634SDan Williams nd_label->lbasize = __cpu_to_le64(nsblk->lbasize); 10480ba1c634SDan Williams nd_label->slot = __cpu_to_le32(slot); 1049faec6f8aSDan Williams if (namespace_label_has(ndd, type_guid)) 1050faec6f8aSDan Williams guid_copy(&nd_label->type_guid, &nd_set->type_guid); 1051b3fde74eSDan Williams if (namespace_label_has(ndd, abstraction_guid)) 1052b3fde74eSDan Williams guid_copy(&nd_label->abstraction_guid, 1053b3fde74eSDan Williams to_abstraction_guid(ndns->claim_class, 1054b3fde74eSDan Williams &nd_label->abstraction_guid)); 1055b3fde74eSDan Williams 1056355d8388SDan Williams if (namespace_label_has(ndd, checksum)) { 1057355d8388SDan Williams u64 sum; 1058355d8388SDan Williams 1059355d8388SDan Williams nd_label->checksum = __cpu_to_le64(0); 1060355d8388SDan Williams sum = nd_fletcher64(nd_label, 1061355d8388SDan Williams sizeof_namespace_label(ndd), 1); 1062355d8388SDan Williams nd_label->checksum = __cpu_to_le64(sum); 1063355d8388SDan Williams } 10640ba1c634SDan Williams 10650ba1c634SDan Williams /* update label */ 10660ba1c634SDan Williams offset = nd_label_offset(ndd, nd_label); 10670ba1c634SDan Williams rc = nvdimm_set_config_data(ndd, offset, nd_label, 1068564e871aSDan Williams sizeof_namespace_label(ndd)); 10690ba1c634SDan Williams if (rc < 0) 10700ba1c634SDan Williams goto abort; 10710ba1c634SDan Williams } 10720ba1c634SDan Williams 10730ba1c634SDan Williams /* free up now unused slots in the new index */ 10740ba1c634SDan Williams for_each_set_bit(slot, victim_map, victim_map ? nslot : 0) { 1075426824d6SDan Williams dev_dbg(ndd->dev, "free: %d\n", slot); 10760ba1c634SDan Williams nd_label_free_slot(ndd, slot); 10770ba1c634SDan Williams } 10780ba1c634SDan Williams 10790ba1c634SDan Williams /* update index */ 10800ba1c634SDan Williams rc = nd_label_write_index(ndd, ndd->ns_next, 10810ba1c634SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 10820ba1c634SDan Williams if (rc) 10830ba1c634SDan Williams goto abort; 10840ba1c634SDan Williams 10850ba1c634SDan Williams /* 10860ba1c634SDan Williams * Now that the on-dimm labels are up to date, fix up the tracking 10870ba1c634SDan Williams * entries in nd_mapping->labels 10880ba1c634SDan Williams */ 10890ba1c634SDan Williams nlabel = 0; 1090ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1091ae8219f1SDan Williams list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { 1092ae8219f1SDan Williams nd_label = label_ent->label; 1093ae8219f1SDan Williams if (!nd_label) 1094ae8219f1SDan Williams continue; 10950ba1c634SDan Williams nlabel++; 10960ba1c634SDan Williams memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 10970ba1c634SDan Williams if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 10980ba1c634SDan Williams continue; 10990ba1c634SDan Williams nlabel--; 1100ae8219f1SDan Williams list_move(&label_ent->list, &list); 1101ae8219f1SDan Williams label_ent->label = NULL; 11020ba1c634SDan Williams } 1103ae8219f1SDan Williams list_splice_tail_init(&list, &nd_mapping->labels); 1104ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 1105ae8219f1SDan Williams 11060ba1c634SDan Williams if (nlabel + nsblk->num_resources > num_labels) { 11070ba1c634SDan Williams /* 11080ba1c634SDan Williams * Bug, we can't end up with more resources than 11090ba1c634SDan Williams * available labels 11100ba1c634SDan Williams */ 11110ba1c634SDan Williams WARN_ON_ONCE(1); 11120ba1c634SDan Williams rc = -ENXIO; 11130ba1c634SDan Williams goto out; 11140ba1c634SDan Williams } 11150ba1c634SDan Williams 1116ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1117ae8219f1SDan Williams label_ent = list_first_entry_or_null(&nd_mapping->labels, 1118ae8219f1SDan Williams typeof(*label_ent), list); 1119ae8219f1SDan Williams if (!label_ent) { 1120ae8219f1SDan Williams WARN_ON(1); 1121ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 1122ae8219f1SDan Williams rc = -ENXIO; 1123ae8219f1SDan Williams goto out; 1124ae8219f1SDan Williams } 11250ba1c634SDan Williams for_each_clear_bit_le(slot, free, nslot) { 1126564e871aSDan Williams nd_label = to_label(ndd, slot); 11270ba1c634SDan Williams memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 11280ba1c634SDan Williams if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 11290ba1c634SDan Williams continue; 11300ba1c634SDan Williams res = to_resource(ndd, nd_label); 11310ba1c634SDan Williams res->flags &= ~DPA_RESOURCE_ADJUSTED; 1132ae8219f1SDan Williams dev_vdbg(&nsblk->common.dev, "assign label slot: %d\n", slot); 1133ae8219f1SDan Williams list_for_each_entry_from(label_ent, &nd_mapping->labels, list) { 1134ae8219f1SDan Williams if (label_ent->label) 1135ae8219f1SDan Williams continue; 1136ae8219f1SDan Williams label_ent->label = nd_label; 1137ae8219f1SDan Williams nd_label = NULL; 1138ae8219f1SDan Williams break; 11390ba1c634SDan Williams } 1140ae8219f1SDan Williams if (nd_label) 1141ae8219f1SDan Williams dev_WARN(&nsblk->common.dev, 1142ae8219f1SDan Williams "failed to track label slot%d\n", slot); 1143ae8219f1SDan Williams } 1144ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 11450ba1c634SDan Williams 11460ba1c634SDan Williams out: 11470ba1c634SDan Williams kfree(old_res_list); 11489065ed12SAndy Shevchenko bitmap_free(victim_map); 11490ba1c634SDan Williams return rc; 11500ba1c634SDan Williams 11510ba1c634SDan Williams abort: 11520ba1c634SDan Williams /* 11530ba1c634SDan Williams * 1/ repair the allocated label bitmap in the index 11540ba1c634SDan Williams * 2/ restore the resource list 11550ba1c634SDan Williams */ 11560ba1c634SDan Williams nd_label_copy(ndd, nsindex, to_current_namespace_index(ndd)); 11570ba1c634SDan Williams kfree(nsblk->res); 11580ba1c634SDan Williams nsblk->res = old_res_list; 11590ba1c634SDan Williams nsblk->num_resources = old_num_resources; 11600ba1c634SDan Williams old_res_list = NULL; 11610ba1c634SDan Williams goto out; 11620ba1c634SDan Williams } 11630ba1c634SDan Williams 11640ba1c634SDan Williams static int init_labels(struct nd_mapping *nd_mapping, int num_labels) 11650ba1c634SDan Williams { 1166ae8219f1SDan Williams int i, old_num_labels = 0; 1167ae8219f1SDan Williams struct nd_label_ent *label_ent; 11680ba1c634SDan Williams struct nd_namespace_index *nsindex; 11690ba1c634SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 11700ba1c634SDan Williams 1171ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1172ae8219f1SDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) 11730ba1c634SDan Williams old_num_labels++; 1174ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 11750ba1c634SDan Williams 11760ba1c634SDan Williams /* 11770ba1c634SDan Williams * We need to preserve all the old labels for the mapping so 11780ba1c634SDan Williams * they can be garbage collected after writing the new labels. 11790ba1c634SDan Williams */ 1180ae8219f1SDan Williams for (i = old_num_labels; i < num_labels; i++) { 1181ae8219f1SDan Williams label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL); 1182ae8219f1SDan Williams if (!label_ent) 11830ba1c634SDan Williams return -ENOMEM; 1184ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1185ae8219f1SDan Williams list_add_tail(&label_ent->list, &nd_mapping->labels); 1186ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 11870ba1c634SDan Williams } 11880ba1c634SDan Williams 1189f524bf27SDan Williams if (ndd->ns_current == -1 || ndd->ns_next == -1) 1190f524bf27SDan Williams /* pass */; 1191f524bf27SDan Williams else 11920ba1c634SDan Williams return max(num_labels, old_num_labels); 1193f524bf27SDan Williams 1194f524bf27SDan Williams nsindex = to_namespace_index(ndd, 0); 1195f524bf27SDan Williams memset(nsindex, 0, ndd->nsarea.config_size); 1196f524bf27SDan Williams for (i = 0; i < 2; i++) { 1197b18d4b8aSDan Williams int rc = nd_label_write_index(ndd, i, 3 - i, ND_NSINDEX_INIT); 1198f524bf27SDan Williams 1199f524bf27SDan Williams if (rc) 1200f524bf27SDan Williams return rc; 1201f524bf27SDan Williams } 1202f524bf27SDan Williams ndd->ns_next = 1; 1203f524bf27SDan Williams ndd->ns_current = 0; 1204f524bf27SDan Williams 12050ba1c634SDan Williams return max(num_labels, old_num_labels); 1206f524bf27SDan Williams } 1207f524bf27SDan Williams 1208f524bf27SDan Williams static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid) 1209f524bf27SDan Williams { 1210f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 1211ae8219f1SDan Williams struct nd_label_ent *label_ent, *e; 1212f524bf27SDan Williams struct nd_namespace_index *nsindex; 1213f524bf27SDan Williams u8 label_uuid[NSLABEL_UUID_LEN]; 1214f524bf27SDan Williams unsigned long *free; 1215ae8219f1SDan Williams LIST_HEAD(list); 1216f524bf27SDan Williams u32 nslot, slot; 1217ae8219f1SDan Williams int active = 0; 1218f524bf27SDan Williams 1219f524bf27SDan Williams if (!uuid) 1220f524bf27SDan Williams return 0; 1221f524bf27SDan Williams 1222f524bf27SDan Williams /* no index || no labels == nothing to delete */ 1223ae8219f1SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 1224f524bf27SDan Williams return 0; 1225f524bf27SDan Williams 1226ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1227ae8219f1SDan Williams list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { 1228ae8219f1SDan Williams struct nd_namespace_label *nd_label = label_ent->label; 1229ae8219f1SDan Williams 1230ae8219f1SDan Williams if (!nd_label) 1231ae8219f1SDan Williams continue; 1232ae8219f1SDan Williams active++; 1233f524bf27SDan Williams memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); 1234f524bf27SDan Williams if (memcmp(label_uuid, uuid, NSLABEL_UUID_LEN) != 0) 1235f524bf27SDan Williams continue; 1236ae8219f1SDan Williams active--; 1237f524bf27SDan Williams slot = to_slot(ndd, nd_label); 1238f524bf27SDan Williams nd_label_free_slot(ndd, slot); 1239426824d6SDan Williams dev_dbg(ndd->dev, "free: %d\n", slot); 1240ae8219f1SDan Williams list_move_tail(&label_ent->list, &list); 1241ae8219f1SDan Williams label_ent->label = NULL; 1242f524bf27SDan Williams } 1243ae8219f1SDan Williams list_splice_tail_init(&list, &nd_mapping->labels); 1244f524bf27SDan Williams 1245ae8219f1SDan Williams if (active == 0) { 1246ae8219f1SDan Williams nd_mapping_free_labels(nd_mapping); 1247426824d6SDan Williams dev_dbg(ndd->dev, "no more active labels\n"); 1248f524bf27SDan Williams } 1249ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 1250f524bf27SDan Williams 1251f524bf27SDan Williams return nd_label_write_index(ndd, ndd->ns_next, 1252f524bf27SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 1253f524bf27SDan Williams } 1254f524bf27SDan Williams 1255f524bf27SDan Williams int nd_pmem_namespace_label_update(struct nd_region *nd_region, 1256f524bf27SDan Williams struct nd_namespace_pmem *nspm, resource_size_t size) 1257f524bf27SDan Williams { 1258966d23a0SDan Williams int i, rc; 1259f524bf27SDan Williams 1260f524bf27SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 1261f524bf27SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[i]; 126216660eaeSDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 126316660eaeSDan Williams struct resource *res; 1264966d23a0SDan Williams int count = 0; 1265f524bf27SDan Williams 1266f524bf27SDan Williams if (size == 0) { 1267f524bf27SDan Williams rc = del_labels(nd_mapping, nspm->uuid); 1268f524bf27SDan Williams if (rc) 1269f524bf27SDan Williams return rc; 1270f524bf27SDan Williams continue; 1271f524bf27SDan Williams } 1272f524bf27SDan Williams 127316660eaeSDan Williams for_each_dpa_resource(ndd, res) 12742d9a0274SNicolas Iooss if (strncmp(res->name, "pmem", 4) == 0) 127516660eaeSDan Williams count++; 127616660eaeSDan Williams WARN_ON_ONCE(!count); 127716660eaeSDan Williams 127816660eaeSDan Williams rc = init_labels(nd_mapping, count); 12790ba1c634SDan Williams if (rc < 0) 1280f524bf27SDan Williams return rc; 1281f524bf27SDan Williams 1282966d23a0SDan Williams rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 1283966d23a0SDan Williams NSLABEL_FLAG_UPDATING); 1284966d23a0SDan Williams if (rc) 1285966d23a0SDan Williams return rc; 1286966d23a0SDan Williams } 1287966d23a0SDan Williams 1288966d23a0SDan Williams if (size == 0) 1289966d23a0SDan Williams return 0; 1290966d23a0SDan Williams 1291966d23a0SDan Williams /* Clear the UPDATING flag per UEFI 2.7 expectations */ 1292966d23a0SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 1293966d23a0SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[i]; 1294966d23a0SDan Williams 1295966d23a0SDan Williams rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 0); 1296f524bf27SDan Williams if (rc) 1297f524bf27SDan Williams return rc; 1298f524bf27SDan Williams } 1299f524bf27SDan Williams 1300f524bf27SDan Williams return 0; 1301f524bf27SDan Williams } 13020ba1c634SDan Williams 13030ba1c634SDan Williams int nd_blk_namespace_label_update(struct nd_region *nd_region, 13040ba1c634SDan Williams struct nd_namespace_blk *nsblk, resource_size_t size) 13050ba1c634SDan Williams { 13060ba1c634SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 13070ba1c634SDan Williams struct resource *res; 13080ba1c634SDan Williams int count = 0; 13090ba1c634SDan Williams 13100ba1c634SDan Williams if (size == 0) 13110ba1c634SDan Williams return del_labels(nd_mapping, nsblk->uuid); 13120ba1c634SDan Williams 13130ba1c634SDan Williams for_each_dpa_resource(to_ndd(nd_mapping), res) 13140ba1c634SDan Williams count++; 13150ba1c634SDan Williams 13160ba1c634SDan Williams count = init_labels(nd_mapping, count); 13170ba1c634SDan Williams if (count < 0) 13180ba1c634SDan Williams return count; 13190ba1c634SDan Williams 13200ba1c634SDan Williams return __blk_label_update(nd_region, nd_mapping, nsblk, count); 13210ba1c634SDan Williams } 1322b3fde74eSDan Williams 1323b3fde74eSDan Williams int __init nd_label_init(void) 1324b3fde74eSDan Williams { 1325b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_BTT_GUID, &nvdimm_btt_guid)); 132614e49454SVishal Verma WARN_ON(guid_parse(NVDIMM_BTT2_GUID, &nvdimm_btt2_guid)); 1327b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_guid)); 1328b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_guid)); 1329b3fde74eSDan Williams 1330b3fde74eSDan Williams return 0; 1331b3fde74eSDan Williams } 1332