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 20*5af96835SDan Williams static uuid_t nvdimm_btt_uuid; 21*5af96835SDan Williams static uuid_t nvdimm_btt2_uuid; 22*5af96835SDan Williams static uuid_t nvdimm_pfn_uuid; 23*5af96835SDan Williams static uuid_t nvdimm_dax_uuid; 24*5af96835SDan Williams 25*5af96835SDan Williams static uuid_t cxl_region_uuid; 26*5af96835SDan Williams static uuid_t cxl_namespace_uuid; 27*5af96835SDan Williams 28c01dafadSQian Cai static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0"; 29c01dafadSQian Cai 304a826c83SDan Williams static u32 best_seq(u32 a, u32 b) 314a826c83SDan Williams { 324a826c83SDan Williams a &= NSINDEX_SEQ_MASK; 334a826c83SDan Williams b &= NSINDEX_SEQ_MASK; 344a826c83SDan Williams 354a826c83SDan Williams if (a == 0 || a == b) 364a826c83SDan Williams return b; 374a826c83SDan Williams else if (b == 0) 384a826c83SDan Williams return a; 394a826c83SDan Williams else if (nd_inc_seq(a) == b) 404a826c83SDan Williams return b; 414a826c83SDan Williams else 424a826c83SDan Williams return a; 434a826c83SDan Williams } 444a826c83SDan Williams 45564e871aSDan Williams unsigned sizeof_namespace_label(struct nvdimm_drvdata *ndd) 46564e871aSDan Williams { 47564e871aSDan Williams return ndd->nslabel_size; 48564e871aSDan Williams } 49564e871aSDan Williams 509e694d9cSToshi Kani static size_t __sizeof_namespace_index(u32 nslot) 519e694d9cSToshi Kani { 529e694d9cSToshi Kani return ALIGN(sizeof(struct nd_namespace_index) + DIV_ROUND_UP(nslot, 8), 539e694d9cSToshi Kani NSINDEX_ALIGN); 549e694d9cSToshi Kani } 559e694d9cSToshi Kani 569e694d9cSToshi Kani static int __nvdimm_num_label_slots(struct nvdimm_drvdata *ndd, 579e694d9cSToshi Kani size_t index_size) 589e694d9cSToshi Kani { 599e694d9cSToshi Kani return (ndd->nsarea.config_size - index_size * 2) / 609e694d9cSToshi Kani sizeof_namespace_label(ndd); 619e694d9cSToshi Kani } 629e694d9cSToshi Kani 6302881768SDan Williams int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd) 6402881768SDan Williams { 659e694d9cSToshi Kani u32 tmp_nslot, n; 669e694d9cSToshi Kani 679e694d9cSToshi Kani tmp_nslot = ndd->nsarea.config_size / sizeof_namespace_label(ndd); 689e694d9cSToshi Kani n = __sizeof_namespace_index(tmp_nslot) / NSINDEX_ALIGN; 699e694d9cSToshi Kani 709e694d9cSToshi Kani return __nvdimm_num_label_slots(ndd, NSINDEX_ALIGN * n); 7102881768SDan Williams } 7202881768SDan Williams 734a826c83SDan Williams size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd) 744a826c83SDan Williams { 7502881768SDan Williams u32 nslot, space, size; 764a826c83SDan Williams 774a826c83SDan Williams /* 789e694d9cSToshi Kani * Per UEFI 2.7, the minimum size of the Label Storage Area is large 799e694d9cSToshi Kani * enough to hold 2 index blocks and 2 labels. The minimum index 801cfeb66eSAlexander Duyck * block size is 256 bytes. The label size is 128 for namespaces 811cfeb66eSAlexander Duyck * prior to version 1.2 and at minimum 256 for version 1.2 and later. 824a826c83SDan Williams */ 8302881768SDan Williams nslot = nvdimm_num_label_slots(ndd); 8402881768SDan Williams space = ndd->nsarea.config_size - nslot * sizeof_namespace_label(ndd); 859e694d9cSToshi Kani size = __sizeof_namespace_index(nslot) * 2; 869e694d9cSToshi Kani if (size <= space && nslot >= 2) 8702881768SDan Williams return size / 2; 884a826c83SDan Williams 8902881768SDan Williams dev_err(ndd->dev, "label area (%d) too small to host (%d byte) labels\n", 9002881768SDan Williams ndd->nsarea.config_size, sizeof_namespace_label(ndd)); 9102881768SDan Williams return 0; 92f524bf27SDan Williams } 93f524bf27SDan Williams 94564e871aSDan Williams static int __nd_label_validate(struct nvdimm_drvdata *ndd) 954a826c83SDan Williams { 964a826c83SDan Williams /* 974a826c83SDan Williams * On media label format consists of two index blocks followed 984a826c83SDan Williams * by an array of labels. None of these structures are ever 994a826c83SDan Williams * updated in place. A sequence number tracks the current 1004a826c83SDan Williams * active index and the next one to write, while labels are 1014a826c83SDan Williams * written to free slots. 1024a826c83SDan Williams * 1034a826c83SDan Williams * +------------+ 1044a826c83SDan Williams * | | 1054a826c83SDan Williams * | nsindex0 | 1064a826c83SDan Williams * | | 1074a826c83SDan Williams * +------------+ 1084a826c83SDan Williams * | | 1094a826c83SDan Williams * | nsindex1 | 1104a826c83SDan Williams * | | 1114a826c83SDan Williams * +------------+ 1124a826c83SDan Williams * | label0 | 1134a826c83SDan Williams * +------------+ 1144a826c83SDan Williams * | label1 | 1154a826c83SDan Williams * +------------+ 1164a826c83SDan Williams * | | 1174a826c83SDan Williams * ....nslot... 1184a826c83SDan Williams * | | 1194a826c83SDan Williams * +------------+ 1204a826c83SDan Williams * | labelN | 1214a826c83SDan Williams * +------------+ 1224a826c83SDan Williams */ 1234a826c83SDan Williams struct nd_namespace_index *nsindex[] = { 1244a826c83SDan Williams to_namespace_index(ndd, 0), 1254a826c83SDan Williams to_namespace_index(ndd, 1), 1264a826c83SDan Williams }; 1274a826c83SDan Williams const int num_index = ARRAY_SIZE(nsindex); 1284a826c83SDan Williams struct device *dev = ndd->dev; 1294a826c83SDan Williams bool valid[2] = { 0 }; 1304a826c83SDan Williams int i, num_valid = 0; 1314a826c83SDan Williams u32 seq; 1324a826c83SDan Williams 1334a826c83SDan Williams for (i = 0; i < num_index; i++) { 1344a826c83SDan Williams u32 nslot; 1354a826c83SDan Williams u8 sig[NSINDEX_SIG_LEN]; 1364a826c83SDan Williams u64 sum_save, sum, size; 137564e871aSDan Williams unsigned int version, labelsize; 1384a826c83SDan Williams 1394a826c83SDan Williams memcpy(sig, nsindex[i]->sig, NSINDEX_SIG_LEN); 1404a826c83SDan Williams if (memcmp(sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN) != 0) { 141426824d6SDan Williams dev_dbg(dev, "nsindex%d signature invalid\n", i); 1424a826c83SDan Williams continue; 1434a826c83SDan Williams } 144564e871aSDan Williams 145564e871aSDan Williams /* label sizes larger than 128 arrived with v1.2 */ 146564e871aSDan Williams version = __le16_to_cpu(nsindex[i]->major) * 100 147564e871aSDan Williams + __le16_to_cpu(nsindex[i]->minor); 148564e871aSDan Williams if (version >= 102) 149564e871aSDan Williams labelsize = 1 << (7 + nsindex[i]->labelsize); 150564e871aSDan Williams else 151564e871aSDan Williams labelsize = 128; 152564e871aSDan Williams 153564e871aSDan Williams if (labelsize != sizeof_namespace_label(ndd)) { 154426824d6SDan Williams dev_dbg(dev, "nsindex%d labelsize %d invalid\n", 155426824d6SDan Williams i, nsindex[i]->labelsize); 156564e871aSDan Williams continue; 157564e871aSDan Williams } 158564e871aSDan Williams 1594a826c83SDan Williams sum_save = __le64_to_cpu(nsindex[i]->checksum); 1604a826c83SDan Williams nsindex[i]->checksum = __cpu_to_le64(0); 1614a826c83SDan Williams sum = nd_fletcher64(nsindex[i], sizeof_namespace_index(ndd), 1); 1624a826c83SDan Williams nsindex[i]->checksum = __cpu_to_le64(sum_save); 1634a826c83SDan Williams if (sum != sum_save) { 164426824d6SDan Williams dev_dbg(dev, "nsindex%d checksum invalid\n", i); 1654a826c83SDan Williams continue; 1664a826c83SDan Williams } 1674a826c83SDan Williams 1684a826c83SDan Williams seq = __le32_to_cpu(nsindex[i]->seq); 1694a826c83SDan Williams if ((seq & NSINDEX_SEQ_MASK) == 0) { 170426824d6SDan Williams dev_dbg(dev, "nsindex%d sequence: %#x invalid\n", i, seq); 1714a826c83SDan Williams continue; 1724a826c83SDan Williams } 1734a826c83SDan Williams 1744a826c83SDan Williams /* sanity check the index against expected values */ 1754a826c83SDan Williams if (__le64_to_cpu(nsindex[i]->myoff) 1764a826c83SDan Williams != i * sizeof_namespace_index(ndd)) { 177426824d6SDan Williams dev_dbg(dev, "nsindex%d myoff: %#llx invalid\n", 178426824d6SDan Williams i, (unsigned long long) 1794a826c83SDan Williams __le64_to_cpu(nsindex[i]->myoff)); 1804a826c83SDan Williams continue; 1814a826c83SDan Williams } 1824a826c83SDan Williams if (__le64_to_cpu(nsindex[i]->otheroff) 1834a826c83SDan Williams != (!i) * sizeof_namespace_index(ndd)) { 184426824d6SDan Williams dev_dbg(dev, "nsindex%d otheroff: %#llx invalid\n", 185426824d6SDan Williams i, (unsigned long long) 1864a826c83SDan Williams __le64_to_cpu(nsindex[i]->otheroff)); 1874a826c83SDan Williams continue; 1884a826c83SDan Williams } 189d86d4d63SAlexander Duyck if (__le64_to_cpu(nsindex[i]->labeloff) 190d86d4d63SAlexander Duyck != 2 * sizeof_namespace_index(ndd)) { 191d86d4d63SAlexander Duyck dev_dbg(dev, "nsindex%d labeloff: %#llx invalid\n", 192d86d4d63SAlexander Duyck i, (unsigned long long) 193d86d4d63SAlexander Duyck __le64_to_cpu(nsindex[i]->labeloff)); 194d86d4d63SAlexander Duyck continue; 195d86d4d63SAlexander Duyck } 1964a826c83SDan Williams 1974a826c83SDan Williams size = __le64_to_cpu(nsindex[i]->mysize); 1984a826c83SDan Williams if (size > sizeof_namespace_index(ndd) 1994a826c83SDan Williams || size < sizeof(struct nd_namespace_index)) { 200426824d6SDan Williams dev_dbg(dev, "nsindex%d mysize: %#llx invalid\n", i, size); 2014a826c83SDan Williams continue; 2024a826c83SDan Williams } 2034a826c83SDan Williams 2044a826c83SDan Williams nslot = __le32_to_cpu(nsindex[i]->nslot); 205564e871aSDan Williams if (nslot * sizeof_namespace_label(ndd) 2064a826c83SDan Williams + 2 * sizeof_namespace_index(ndd) 2074a826c83SDan Williams > ndd->nsarea.config_size) { 208426824d6SDan Williams dev_dbg(dev, "nsindex%d nslot: %u invalid, config_size: %#x\n", 209426824d6SDan Williams i, nslot, ndd->nsarea.config_size); 2104a826c83SDan Williams continue; 2114a826c83SDan Williams } 2124a826c83SDan Williams valid[i] = true; 2134a826c83SDan Williams num_valid++; 2144a826c83SDan Williams } 2154a826c83SDan Williams 2164a826c83SDan Williams switch (num_valid) { 2174a826c83SDan Williams case 0: 2184a826c83SDan Williams break; 2194a826c83SDan Williams case 1: 2204a826c83SDan Williams for (i = 0; i < num_index; i++) 2214a826c83SDan Williams if (valid[i]) 2224a826c83SDan Williams return i; 2234a826c83SDan Williams /* can't have num_valid > 0 but valid[] = { false, false } */ 2244a826c83SDan Williams WARN_ON(1); 2254a826c83SDan Williams break; 2264a826c83SDan Williams default: 2274a826c83SDan Williams /* pick the best index... */ 2284a826c83SDan Williams seq = best_seq(__le32_to_cpu(nsindex[0]->seq), 2294a826c83SDan Williams __le32_to_cpu(nsindex[1]->seq)); 2304a826c83SDan Williams if (seq == (__le32_to_cpu(nsindex[1]->seq) & NSINDEX_SEQ_MASK)) 2314a826c83SDan Williams return 1; 2324a826c83SDan Williams else 2334a826c83SDan Williams return 0; 2344a826c83SDan Williams break; 2354a826c83SDan Williams } 2364a826c83SDan Williams 2374a826c83SDan Williams return -1; 2384a826c83SDan Williams } 2394a826c83SDan Williams 2407d47aad4SAlexander Duyck static int nd_label_validate(struct nvdimm_drvdata *ndd) 241564e871aSDan Williams { 242564e871aSDan Williams /* 243564e871aSDan Williams * In order to probe for and validate namespace index blocks we 244564e871aSDan Williams * need to know the size of the labels, and we can't trust the 245564e871aSDan Williams * size of the labels until we validate the index blocks. 246564e871aSDan Williams * Resolve this dependency loop by probing for known label 2478990cdf1SDan Williams * sizes, but default to v1.2 256-byte namespace labels if 2488990cdf1SDan Williams * discovery fails. 249564e871aSDan Williams */ 2508990cdf1SDan Williams int label_size[] = { 128, 256 }; 251564e871aSDan Williams int i, rc; 252564e871aSDan Williams 253564e871aSDan Williams for (i = 0; i < ARRAY_SIZE(label_size); i++) { 254564e871aSDan Williams ndd->nslabel_size = label_size[i]; 255564e871aSDan Williams rc = __nd_label_validate(ndd); 256564e871aSDan Williams if (rc >= 0) 257564e871aSDan Williams return rc; 258564e871aSDan Williams } 259564e871aSDan Williams 260564e871aSDan Williams return -1; 261564e871aSDan Williams } 262564e871aSDan Williams 2637d47aad4SAlexander Duyck static void nd_label_copy(struct nvdimm_drvdata *ndd, 2647d47aad4SAlexander Duyck struct nd_namespace_index *dst, 2654a826c83SDan Williams struct nd_namespace_index *src) 2664a826c83SDan Williams { 26719418b02SAlexander Duyck /* just exit if either destination or source is NULL */ 26819418b02SAlexander Duyck if (!dst || !src) 2694a826c83SDan Williams return; 2704a826c83SDan Williams 2714a826c83SDan Williams memcpy(dst, src, sizeof_namespace_index(ndd)); 2724a826c83SDan Williams } 2734a826c83SDan Williams 2744a826c83SDan Williams static struct nd_namespace_label *nd_label_base(struct nvdimm_drvdata *ndd) 2754a826c83SDan Williams { 2764a826c83SDan Williams void *base = to_namespace_index(ndd, 0); 2774a826c83SDan Williams 2784a826c83SDan Williams return base + 2 * sizeof_namespace_index(ndd); 2794a826c83SDan Williams } 2804a826c83SDan Williams 281f524bf27SDan Williams static int to_slot(struct nvdimm_drvdata *ndd, 282f524bf27SDan Williams struct nd_namespace_label *nd_label) 283f524bf27SDan Williams { 284564e871aSDan Williams unsigned long label, base; 285564e871aSDan Williams 286564e871aSDan Williams label = (unsigned long) nd_label; 287564e871aSDan Williams base = (unsigned long) nd_label_base(ndd); 288564e871aSDan Williams 289564e871aSDan Williams return (label - base) / sizeof_namespace_label(ndd); 290564e871aSDan Williams } 291564e871aSDan Williams 292564e871aSDan Williams static struct nd_namespace_label *to_label(struct nvdimm_drvdata *ndd, int slot) 293564e871aSDan Williams { 294564e871aSDan Williams unsigned long label, base; 295564e871aSDan Williams 296564e871aSDan Williams base = (unsigned long) nd_label_base(ndd); 297564e871aSDan Williams label = base + sizeof_namespace_label(ndd) * slot; 298564e871aSDan Williams 299564e871aSDan Williams return (struct nd_namespace_label *) label; 300f524bf27SDan Williams } 301f524bf27SDan Williams 3024a826c83SDan Williams #define for_each_clear_bit_le(bit, addr, size) \ 3034a826c83SDan Williams for ((bit) = find_next_zero_bit_le((addr), (size), 0); \ 3044a826c83SDan Williams (bit) < (size); \ 3054a826c83SDan Williams (bit) = find_next_zero_bit_le((addr), (size), (bit) + 1)) 3064a826c83SDan Williams 3074a826c83SDan Williams /** 308f524bf27SDan Williams * preamble_index - common variable initialization for nd_label_* routines 3094a826c83SDan Williams * @ndd: dimm container for the relevant label set 310f524bf27SDan Williams * @idx: namespace_index index 3114a826c83SDan Williams * @nsindex_out: on return set to the currently active namespace index 3124a826c83SDan Williams * @free: on return set to the free label bitmap in the index 3134a826c83SDan Williams * @nslot: on return set to the number of slots in the label space 3144a826c83SDan Williams */ 315f524bf27SDan Williams static bool preamble_index(struct nvdimm_drvdata *ndd, int idx, 3164a826c83SDan Williams struct nd_namespace_index **nsindex_out, 3174a826c83SDan Williams unsigned long **free, u32 *nslot) 3184a826c83SDan Williams { 3194a826c83SDan Williams struct nd_namespace_index *nsindex; 3204a826c83SDan Williams 321f524bf27SDan Williams nsindex = to_namespace_index(ndd, idx); 3224a826c83SDan Williams if (nsindex == NULL) 3234a826c83SDan Williams return false; 3244a826c83SDan Williams 3254a826c83SDan Williams *free = (unsigned long *) nsindex->free; 3264a826c83SDan Williams *nslot = __le32_to_cpu(nsindex->nslot); 3274a826c83SDan Williams *nsindex_out = nsindex; 3284a826c83SDan Williams 3294a826c83SDan Williams return true; 3304a826c83SDan Williams } 3314a826c83SDan Williams 332d1c6e08eSDan Williams char *nd_label_gen_id(struct nd_label_id *label_id, const uuid_t *uuid, 333d1c6e08eSDan Williams u32 flags) 3344a826c83SDan Williams { 3354a826c83SDan Williams if (!label_id || !uuid) 3364a826c83SDan Williams return NULL; 3374a826c83SDan Williams snprintf(label_id->id, ND_LABEL_ID_SIZE, "%s-%pUb", 3384a826c83SDan Williams flags & NSLABEL_FLAG_LOCAL ? "blk" : "pmem", uuid); 3394a826c83SDan Williams return label_id->id; 3404a826c83SDan Williams } 3414a826c83SDan Williams 342f524bf27SDan Williams static bool preamble_current(struct nvdimm_drvdata *ndd, 343f524bf27SDan Williams struct nd_namespace_index **nsindex, 344f524bf27SDan Williams unsigned long **free, u32 *nslot) 345f524bf27SDan Williams { 346f524bf27SDan Williams return preamble_index(ndd, ndd->ns_current, nsindex, 347f524bf27SDan Williams free, nslot); 348f524bf27SDan Williams } 349f524bf27SDan Williams 350f524bf27SDan Williams static bool preamble_next(struct nvdimm_drvdata *ndd, 351f524bf27SDan Williams struct nd_namespace_index **nsindex, 352f524bf27SDan Williams unsigned long **free, u32 *nslot) 353f524bf27SDan Williams { 354f524bf27SDan Williams return preamble_index(ndd, ndd->ns_next, nsindex, 355f524bf27SDan Williams free, nslot); 356f524bf27SDan Williams } 357f524bf27SDan Williams 3587cd35b29SDan Williams static bool nsl_validate_checksum(struct nvdimm_drvdata *ndd, 3597cd35b29SDan Williams struct nd_namespace_label *nd_label) 3604a826c83SDan Williams { 361355d8388SDan Williams u64 sum, sum_save; 362355d8388SDan Williams 363*5af96835SDan Williams if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum)) 3647cd35b29SDan Williams return true; 3657cd35b29SDan Williams 366b4366a82SDan Williams sum_save = nsl_get_checksum(ndd, nd_label); 3678176f147SDan Williams nsl_set_checksum(ndd, nd_label, 0); 368355d8388SDan Williams sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1); 3698176f147SDan Williams nsl_set_checksum(ndd, nd_label, sum_save); 3707cd35b29SDan Williams return sum == sum_save; 371355d8388SDan Williams } 372355d8388SDan Williams 3737cd35b29SDan Williams static void nsl_calculate_checksum(struct nvdimm_drvdata *ndd, 3747cd35b29SDan Williams struct nd_namespace_label *nd_label) 3757cd35b29SDan Williams { 3767cd35b29SDan Williams u64 sum; 3777cd35b29SDan Williams 378*5af96835SDan Williams if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum)) 3797cd35b29SDan Williams return; 3807cd35b29SDan Williams nsl_set_checksum(ndd, nd_label, 0); 3817cd35b29SDan Williams sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1); 3827cd35b29SDan Williams nsl_set_checksum(ndd, nd_label, sum); 3837cd35b29SDan Williams } 3847cd35b29SDan Williams 3857cd35b29SDan Williams static bool slot_valid(struct nvdimm_drvdata *ndd, 3867cd35b29SDan Williams struct nd_namespace_label *nd_label, u32 slot) 3877cd35b29SDan Williams { 3887cd35b29SDan Williams bool valid; 3897cd35b29SDan Williams 3907cd35b29SDan Williams /* check that we are written where we expect to be written */ 3917cd35b29SDan Williams if (slot != nsl_get_slot(ndd, nd_label)) 3927cd35b29SDan Williams return false; 3937cd35b29SDan Williams valid = nsl_validate_checksum(ndd, nd_label); 3947cd35b29SDan Williams if (!valid) 3957cd35b29SDan Williams dev_dbg(ndd->dev, "fail checksum. slot: %d\n", slot); 3967cd35b29SDan Williams return valid; 3974a826c83SDan Williams } 3984a826c83SDan Williams 3994a826c83SDan Williams int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) 4004a826c83SDan Williams { 4014a826c83SDan Williams struct nd_namespace_index *nsindex; 4024a826c83SDan Williams unsigned long *free; 4034a826c83SDan Williams u32 nslot, slot; 4044a826c83SDan Williams 4054a826c83SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 4064a826c83SDan Williams return 0; /* no label, nothing to reserve */ 4074a826c83SDan Williams 4084a826c83SDan Williams for_each_clear_bit_le(slot, free, nslot) { 409d5d30d5aSDan Williams struct nvdimm *nvdimm = to_nvdimm(ndd->dev); 4104a826c83SDan Williams struct nd_namespace_label *nd_label; 4114a826c83SDan Williams struct nd_region *nd_region = NULL; 4124a826c83SDan Williams struct nd_label_id label_id; 4134a826c83SDan Williams struct resource *res; 414d1c6e08eSDan Williams uuid_t label_uuid; 4154a826c83SDan Williams u32 flags; 4164a826c83SDan Williams 417564e871aSDan Williams nd_label = to_label(ndd, slot); 4184a826c83SDan Williams 419355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) 4204a826c83SDan Williams continue; 4214a826c83SDan Williams 422d1c6e08eSDan Williams nsl_get_uuid(ndd, nd_label, &label_uuid); 423b4366a82SDan Williams flags = nsl_get_flags(ndd, nd_label); 424d5d30d5aSDan Williams if (test_bit(NDD_NOBLK, &nvdimm->flags)) 425d5d30d5aSDan Williams flags &= ~NSLABEL_FLAG_LOCAL; 426d1c6e08eSDan Williams nd_label_gen_id(&label_id, &label_uuid, flags); 4274a826c83SDan Williams res = nvdimm_allocate_dpa(ndd, &label_id, 428b4366a82SDan Williams nsl_get_dpa(ndd, nd_label), 429b4366a82SDan Williams nsl_get_rawsize(ndd, nd_label)); 4304a826c83SDan Williams nd_dbg_dpa(nd_region, ndd, res, "reserve\n"); 4314a826c83SDan Williams if (!res) 4324a826c83SDan Williams return -EBUSY; 4334a826c83SDan Williams } 4344a826c83SDan Williams 4354a826c83SDan Williams return 0; 4364a826c83SDan Williams } 437bf9bccc1SDan Williams 4382d657d17SAlexander Duyck int nd_label_data_init(struct nvdimm_drvdata *ndd) 4392d657d17SAlexander Duyck { 4407d47aad4SAlexander Duyck size_t config_size, read_size, max_xfer, offset; 4417d47aad4SAlexander Duyck struct nd_namespace_index *nsindex; 4427d47aad4SAlexander Duyck unsigned int i; 4432d657d17SAlexander Duyck int rc = 0; 44497052c1cSDan Williams u32 nslot; 4452d657d17SAlexander Duyck 4462d657d17SAlexander Duyck if (ndd->data) 4472d657d17SAlexander Duyck return 0; 4482d657d17SAlexander Duyck 4492d657d17SAlexander Duyck if (ndd->nsarea.status || ndd->nsarea.max_xfer == 0) { 4502d657d17SAlexander Duyck dev_dbg(ndd->dev, "failed to init config data area: (%u:%u)\n", 4512d657d17SAlexander Duyck ndd->nsarea.max_xfer, ndd->nsarea.config_size); 4522d657d17SAlexander Duyck return -ENXIO; 4532d657d17SAlexander Duyck } 4542d657d17SAlexander Duyck 4552d657d17SAlexander Duyck /* 4562d657d17SAlexander Duyck * We need to determine the maximum index area as this is the section 4572d657d17SAlexander Duyck * we must read and validate before we can start processing labels. 4582d657d17SAlexander Duyck * 4592d657d17SAlexander Duyck * If the area is too small to contain the two indexes and 2 labels 4602d657d17SAlexander Duyck * then we abort. 4612d657d17SAlexander Duyck * 4622d657d17SAlexander Duyck * Start at a label size of 128 as this should result in the largest 4632d657d17SAlexander Duyck * possible namespace index size. 4642d657d17SAlexander Duyck */ 4652d657d17SAlexander Duyck ndd->nslabel_size = 128; 4662d657d17SAlexander Duyck read_size = sizeof_namespace_index(ndd) * 2; 4672d657d17SAlexander Duyck if (!read_size) 4682d657d17SAlexander Duyck return -ENXIO; 4692d657d17SAlexander Duyck 4702d657d17SAlexander Duyck /* Allocate config data */ 4712d657d17SAlexander Duyck config_size = ndd->nsarea.config_size; 4722d657d17SAlexander Duyck ndd->data = kvzalloc(config_size, GFP_KERNEL); 4732d657d17SAlexander Duyck if (!ndd->data) 4742d657d17SAlexander Duyck return -ENOMEM; 4752d657d17SAlexander Duyck 4767d47aad4SAlexander Duyck /* 4777d47aad4SAlexander Duyck * We want to guarantee as few reads as possible while conserving 4787d47aad4SAlexander Duyck * memory. To do that we figure out how much unused space will be left 4797d47aad4SAlexander Duyck * in the last read, divide that by the total number of reads it is 4807d47aad4SAlexander Duyck * going to take given our maximum transfer size, and then reduce our 4817d47aad4SAlexander Duyck * maximum transfer size based on that result. 4827d47aad4SAlexander Duyck */ 4837d47aad4SAlexander Duyck max_xfer = min_t(size_t, ndd->nsarea.max_xfer, config_size); 4847d47aad4SAlexander Duyck if (read_size < max_xfer) { 4857d47aad4SAlexander Duyck /* trim waste */ 4867d47aad4SAlexander Duyck max_xfer -= ((max_xfer - 1) - (config_size - 1) % max_xfer) / 4877d47aad4SAlexander Duyck DIV_ROUND_UP(config_size, max_xfer); 4887d47aad4SAlexander Duyck /* make certain we read indexes in exactly 1 read */ 4897d47aad4SAlexander Duyck if (max_xfer < read_size) 4907d47aad4SAlexander Duyck max_xfer = read_size; 4917d47aad4SAlexander Duyck } 4927d47aad4SAlexander Duyck 4937d47aad4SAlexander Duyck /* Make our initial read size a multiple of max_xfer size */ 4947d47aad4SAlexander Duyck read_size = min(DIV_ROUND_UP(read_size, max_xfer) * max_xfer, 4957d47aad4SAlexander Duyck config_size); 4967d47aad4SAlexander Duyck 4977d47aad4SAlexander Duyck /* Read the index data */ 4987d47aad4SAlexander Duyck rc = nvdimm_get_config_data(ndd, ndd->data, 0, read_size); 4997d47aad4SAlexander Duyck if (rc) 5007d47aad4SAlexander Duyck goto out_err; 5017d47aad4SAlexander Duyck 5027d47aad4SAlexander Duyck /* Validate index data, if not valid assume all labels are invalid */ 5037d47aad4SAlexander Duyck ndd->ns_current = nd_label_validate(ndd); 5047d47aad4SAlexander Duyck if (ndd->ns_current < 0) 5057d47aad4SAlexander Duyck return 0; 5067d47aad4SAlexander Duyck 5077d47aad4SAlexander Duyck /* Record our index values */ 5087d47aad4SAlexander Duyck ndd->ns_next = nd_label_next_nsindex(ndd->ns_current); 5097d47aad4SAlexander Duyck 5107d47aad4SAlexander Duyck /* Copy "current" index on top of the "next" index */ 5117d47aad4SAlexander Duyck nsindex = to_current_namespace_index(ndd); 5127d47aad4SAlexander Duyck nd_label_copy(ndd, to_next_namespace_index(ndd), nsindex); 5137d47aad4SAlexander Duyck 5147d47aad4SAlexander Duyck /* Determine starting offset for label data */ 5157d47aad4SAlexander Duyck offset = __le64_to_cpu(nsindex->labeloff); 51697052c1cSDan Williams nslot = __le32_to_cpu(nsindex->nslot); 5177d47aad4SAlexander Duyck 5187d47aad4SAlexander Duyck /* Loop through the free list pulling in any active labels */ 51997052c1cSDan Williams for (i = 0; i < nslot; i++, offset += ndd->nslabel_size) { 5207d47aad4SAlexander Duyck size_t label_read_size; 5217d47aad4SAlexander Duyck 5227d47aad4SAlexander Duyck /* zero out the unused labels */ 5237d47aad4SAlexander Duyck if (test_bit_le(i, nsindex->free)) { 5247d47aad4SAlexander Duyck memset(ndd->data + offset, 0, ndd->nslabel_size); 5257d47aad4SAlexander Duyck continue; 5267d47aad4SAlexander Duyck } 5277d47aad4SAlexander Duyck 5287d47aad4SAlexander Duyck /* if we already read past here then just continue */ 5297d47aad4SAlexander Duyck if (offset + ndd->nslabel_size <= read_size) 5307d47aad4SAlexander Duyck continue; 5317d47aad4SAlexander Duyck 5327d47aad4SAlexander Duyck /* if we haven't read in a while reset our read_size offset */ 5337d47aad4SAlexander Duyck if (read_size < offset) 5347d47aad4SAlexander Duyck read_size = offset; 5357d47aad4SAlexander Duyck 5367d47aad4SAlexander Duyck /* determine how much more will be read after this next call. */ 5377d47aad4SAlexander Duyck label_read_size = offset + ndd->nslabel_size - read_size; 5387d47aad4SAlexander Duyck label_read_size = DIV_ROUND_UP(label_read_size, max_xfer) * 5397d47aad4SAlexander Duyck max_xfer; 5407d47aad4SAlexander Duyck 5417d47aad4SAlexander Duyck /* truncate last read if needed */ 5427d47aad4SAlexander Duyck if (read_size + label_read_size > config_size) 5437d47aad4SAlexander Duyck label_read_size = config_size - read_size; 5447d47aad4SAlexander Duyck 5457d47aad4SAlexander Duyck /* Read the label data */ 5467d47aad4SAlexander Duyck rc = nvdimm_get_config_data(ndd, ndd->data + read_size, 5477d47aad4SAlexander Duyck read_size, label_read_size); 5487d47aad4SAlexander Duyck if (rc) 5497d47aad4SAlexander Duyck goto out_err; 5507d47aad4SAlexander Duyck 5517d47aad4SAlexander Duyck /* push read_size to next read offset */ 5527d47aad4SAlexander Duyck read_size += label_read_size; 5537d47aad4SAlexander Duyck } 5547d47aad4SAlexander Duyck 5557d47aad4SAlexander Duyck dev_dbg(ndd->dev, "len: %zu rc: %d\n", offset, rc); 5567d47aad4SAlexander Duyck out_err: 5577d47aad4SAlexander Duyck return rc; 5582d657d17SAlexander Duyck } 5592d657d17SAlexander Duyck 560bf9bccc1SDan Williams int nd_label_active_count(struct nvdimm_drvdata *ndd) 561bf9bccc1SDan Williams { 562bf9bccc1SDan Williams struct nd_namespace_index *nsindex; 563bf9bccc1SDan Williams unsigned long *free; 564bf9bccc1SDan Williams u32 nslot, slot; 565bf9bccc1SDan Williams int count = 0; 566bf9bccc1SDan Williams 567bf9bccc1SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 568bf9bccc1SDan Williams return 0; 569bf9bccc1SDan Williams 570bf9bccc1SDan Williams for_each_clear_bit_le(slot, free, nslot) { 571bf9bccc1SDan Williams struct nd_namespace_label *nd_label; 572bf9bccc1SDan Williams 573564e871aSDan Williams nd_label = to_label(ndd, slot); 574bf9bccc1SDan Williams 575355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) { 576b4366a82SDan Williams u32 label_slot = nsl_get_slot(ndd, nd_label); 577b4366a82SDan Williams u64 size = nsl_get_rawsize(ndd, nd_label); 578b4366a82SDan Williams u64 dpa = nsl_get_dpa(ndd, nd_label); 579bf9bccc1SDan Williams 580bf9bccc1SDan Williams dev_dbg(ndd->dev, 581426824d6SDan Williams "slot%d invalid slot: %d dpa: %llx size: %llx\n", 582426824d6SDan Williams slot, label_slot, dpa, size); 583bf9bccc1SDan Williams continue; 584bf9bccc1SDan Williams } 585bf9bccc1SDan Williams count++; 586bf9bccc1SDan Williams } 587bf9bccc1SDan Williams return count; 588bf9bccc1SDan Williams } 589bf9bccc1SDan Williams 590bf9bccc1SDan Williams struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n) 591bf9bccc1SDan Williams { 592bf9bccc1SDan Williams struct nd_namespace_index *nsindex; 593bf9bccc1SDan Williams unsigned long *free; 594bf9bccc1SDan Williams u32 nslot, slot; 595bf9bccc1SDan Williams 596bf9bccc1SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 597bf9bccc1SDan Williams return NULL; 598bf9bccc1SDan Williams 599bf9bccc1SDan Williams for_each_clear_bit_le(slot, free, nslot) { 600bf9bccc1SDan Williams struct nd_namespace_label *nd_label; 601bf9bccc1SDan Williams 602564e871aSDan Williams nd_label = to_label(ndd, slot); 603355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) 604bf9bccc1SDan Williams continue; 605bf9bccc1SDan Williams 606bf9bccc1SDan Williams if (n-- == 0) 607564e871aSDan Williams return to_label(ndd, slot); 608bf9bccc1SDan Williams } 609bf9bccc1SDan Williams 610bf9bccc1SDan Williams return NULL; 611bf9bccc1SDan Williams } 612f524bf27SDan Williams 6130ba1c634SDan Williams u32 nd_label_alloc_slot(struct nvdimm_drvdata *ndd) 614f524bf27SDan Williams { 615f524bf27SDan Williams struct nd_namespace_index *nsindex; 616f524bf27SDan Williams unsigned long *free; 617f524bf27SDan Williams u32 nslot, slot; 618f524bf27SDan Williams 619f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 620f524bf27SDan Williams return UINT_MAX; 621f524bf27SDan Williams 622f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 623f524bf27SDan Williams 624f524bf27SDan Williams slot = find_next_bit_le(free, nslot, 0); 625f524bf27SDan Williams if (slot == nslot) 626f524bf27SDan Williams return UINT_MAX; 627f524bf27SDan Williams 628f524bf27SDan Williams clear_bit_le(slot, free); 629f524bf27SDan Williams 630f524bf27SDan Williams return slot; 631f524bf27SDan Williams } 632f524bf27SDan Williams 6330ba1c634SDan Williams bool nd_label_free_slot(struct nvdimm_drvdata *ndd, u32 slot) 634f524bf27SDan Williams { 635f524bf27SDan Williams struct nd_namespace_index *nsindex; 636f524bf27SDan Williams unsigned long *free; 637f524bf27SDan Williams u32 nslot; 638f524bf27SDan Williams 639f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 640f524bf27SDan Williams return false; 641f524bf27SDan Williams 642f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 643f524bf27SDan Williams 644f524bf27SDan Williams if (slot < nslot) 645f524bf27SDan Williams return !test_and_set_bit_le(slot, free); 646f524bf27SDan Williams return false; 647f524bf27SDan Williams } 648f524bf27SDan Williams 649f524bf27SDan Williams u32 nd_label_nfree(struct nvdimm_drvdata *ndd) 650f524bf27SDan Williams { 651f524bf27SDan Williams struct nd_namespace_index *nsindex; 652f524bf27SDan Williams unsigned long *free; 653f524bf27SDan Williams u32 nslot; 654f524bf27SDan Williams 655f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 656f524bf27SDan Williams 657f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 6580ba1c634SDan Williams return nvdimm_num_label_slots(ndd); 659f524bf27SDan Williams 660f524bf27SDan Williams return bitmap_weight(free, nslot); 661f524bf27SDan Williams } 662f524bf27SDan Williams 663f524bf27SDan Williams static int nd_label_write_index(struct nvdimm_drvdata *ndd, int index, u32 seq, 664f524bf27SDan Williams unsigned long flags) 665f524bf27SDan Williams { 666f524bf27SDan Williams struct nd_namespace_index *nsindex; 667f524bf27SDan Williams unsigned long offset; 668f524bf27SDan Williams u64 checksum; 669f524bf27SDan Williams u32 nslot; 670f524bf27SDan Williams int rc; 671f524bf27SDan Williams 672f524bf27SDan Williams nsindex = to_namespace_index(ndd, index); 673f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) 674f524bf27SDan Williams nslot = nvdimm_num_label_slots(ndd); 675f524bf27SDan Williams else 676f524bf27SDan Williams nslot = __le32_to_cpu(nsindex->nslot); 677f524bf27SDan Williams 678f524bf27SDan Williams memcpy(nsindex->sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN); 679564e871aSDan Williams memset(&nsindex->flags, 0, 3); 680564e871aSDan Williams nsindex->labelsize = sizeof_namespace_label(ndd) >> 8; 681f524bf27SDan Williams nsindex->seq = __cpu_to_le32(seq); 682f524bf27SDan Williams offset = (unsigned long) nsindex 683f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 684f524bf27SDan Williams nsindex->myoff = __cpu_to_le64(offset); 685f524bf27SDan Williams nsindex->mysize = __cpu_to_le64(sizeof_namespace_index(ndd)); 686f524bf27SDan Williams offset = (unsigned long) to_namespace_index(ndd, 687f524bf27SDan Williams nd_label_next_nsindex(index)) 688f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 689f524bf27SDan Williams nsindex->otheroff = __cpu_to_le64(offset); 690f524bf27SDan Williams offset = (unsigned long) nd_label_base(ndd) 691f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 692f524bf27SDan Williams nsindex->labeloff = __cpu_to_le64(offset); 693f524bf27SDan Williams nsindex->nslot = __cpu_to_le32(nslot); 694f524bf27SDan Williams nsindex->major = __cpu_to_le16(1); 6958990cdf1SDan Williams if (sizeof_namespace_label(ndd) < 256) 696f524bf27SDan Williams nsindex->minor = __cpu_to_le16(1); 6978990cdf1SDan Williams else 6988990cdf1SDan Williams nsindex->minor = __cpu_to_le16(2); 699f524bf27SDan Williams nsindex->checksum = __cpu_to_le64(0); 700f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) { 701f524bf27SDan Williams unsigned long *free = (unsigned long *) nsindex->free; 702f524bf27SDan Williams u32 nfree = ALIGN(nslot, BITS_PER_LONG); 703f524bf27SDan Williams int last_bits, i; 704f524bf27SDan Williams 705f524bf27SDan Williams memset(nsindex->free, 0xff, nfree / 8); 706f524bf27SDan Williams for (i = 0, last_bits = nfree - nslot; i < last_bits; i++) 707f524bf27SDan Williams clear_bit_le(nslot + i, free); 708f524bf27SDan Williams } 709f524bf27SDan Williams checksum = nd_fletcher64(nsindex, sizeof_namespace_index(ndd), 1); 710f524bf27SDan Williams nsindex->checksum = __cpu_to_le64(checksum); 711f524bf27SDan Williams rc = nvdimm_set_config_data(ndd, __le64_to_cpu(nsindex->myoff), 712f524bf27SDan Williams nsindex, sizeof_namespace_index(ndd)); 713f524bf27SDan Williams if (rc < 0) 714f524bf27SDan Williams return rc; 715f524bf27SDan Williams 716f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) 717f524bf27SDan Williams return 0; 718f524bf27SDan Williams 719f524bf27SDan Williams /* copy the index we just wrote to the new 'next' */ 720f524bf27SDan Williams WARN_ON(index != ndd->ns_next); 721f524bf27SDan Williams nd_label_copy(ndd, to_current_namespace_index(ndd), nsindex); 722f524bf27SDan Williams ndd->ns_current = nd_label_next_nsindex(ndd->ns_current); 723f524bf27SDan Williams ndd->ns_next = nd_label_next_nsindex(ndd->ns_next); 724f524bf27SDan Williams WARN_ON(ndd->ns_current == ndd->ns_next); 725f524bf27SDan Williams 726f524bf27SDan Williams return 0; 727f524bf27SDan Williams } 728f524bf27SDan Williams 729f524bf27SDan Williams static unsigned long nd_label_offset(struct nvdimm_drvdata *ndd, 730f524bf27SDan Williams struct nd_namespace_label *nd_label) 731f524bf27SDan Williams { 732f524bf27SDan Williams return (unsigned long) nd_label 733f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 734f524bf27SDan Williams } 735f524bf27SDan Williams 736*5af96835SDan Williams static enum nvdimm_claim_class guid_to_nvdimm_cclass(guid_t *guid) 737b3fde74eSDan Williams { 738b3fde74eSDan Williams if (guid_equal(guid, &nvdimm_btt_guid)) 739b3fde74eSDan Williams return NVDIMM_CCLASS_BTT; 74014e49454SVishal Verma else if (guid_equal(guid, &nvdimm_btt2_guid)) 74114e49454SVishal Verma return NVDIMM_CCLASS_BTT2; 742b3fde74eSDan Williams else if (guid_equal(guid, &nvdimm_pfn_guid)) 743b3fde74eSDan Williams return NVDIMM_CCLASS_PFN; 744b3fde74eSDan Williams else if (guid_equal(guid, &nvdimm_dax_guid)) 745b3fde74eSDan Williams return NVDIMM_CCLASS_DAX; 746b3fde74eSDan Williams else if (guid_equal(guid, &guid_null)) 747b3fde74eSDan Williams return NVDIMM_CCLASS_NONE; 748b3fde74eSDan Williams 749b3fde74eSDan Williams return NVDIMM_CCLASS_UNKNOWN; 750b3fde74eSDan Williams } 751b3fde74eSDan Williams 752*5af96835SDan Williams /* CXL labels store UUIDs instead of GUIDs for the same data */ 753*5af96835SDan Williams static enum nvdimm_claim_class uuid_to_nvdimm_cclass(uuid_t *uuid) 754*5af96835SDan Williams { 755*5af96835SDan Williams if (uuid_equal(uuid, &nvdimm_btt_uuid)) 756*5af96835SDan Williams return NVDIMM_CCLASS_BTT; 757*5af96835SDan Williams else if (uuid_equal(uuid, &nvdimm_btt2_uuid)) 758*5af96835SDan Williams return NVDIMM_CCLASS_BTT2; 759*5af96835SDan Williams else if (uuid_equal(uuid, &nvdimm_pfn_uuid)) 760*5af96835SDan Williams return NVDIMM_CCLASS_PFN; 761*5af96835SDan Williams else if (uuid_equal(uuid, &nvdimm_dax_uuid)) 762*5af96835SDan Williams return NVDIMM_CCLASS_DAX; 763*5af96835SDan Williams else if (uuid_equal(uuid, &uuid_null)) 764*5af96835SDan Williams return NVDIMM_CCLASS_NONE; 765*5af96835SDan Williams 766*5af96835SDan Williams return NVDIMM_CCLASS_UNKNOWN; 767*5af96835SDan Williams } 768*5af96835SDan Williams 769b3fde74eSDan Williams static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class, 770b3fde74eSDan Williams guid_t *target) 771b3fde74eSDan Williams { 772b3fde74eSDan Williams if (claim_class == NVDIMM_CCLASS_BTT) 773b3fde74eSDan Williams return &nvdimm_btt_guid; 77414e49454SVishal Verma else if (claim_class == NVDIMM_CCLASS_BTT2) 77514e49454SVishal Verma return &nvdimm_btt2_guid; 776b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_PFN) 777b3fde74eSDan Williams return &nvdimm_pfn_guid; 778b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_DAX) 779b3fde74eSDan Williams return &nvdimm_dax_guid; 780b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_UNKNOWN) { 781b3fde74eSDan Williams /* 782b3fde74eSDan Williams * If we're modifying a namespace for which we don't 783b3fde74eSDan Williams * know the claim_class, don't touch the existing guid. 784b3fde74eSDan Williams */ 785b3fde74eSDan Williams return target; 786b3fde74eSDan Williams } else 787b3fde74eSDan Williams return &guid_null; 788b3fde74eSDan Williams } 789b3fde74eSDan Williams 790*5af96835SDan Williams /* CXL labels store UUIDs instead of GUIDs for the same data */ 791*5af96835SDan Williams static const uuid_t *to_abstraction_uuid(enum nvdimm_claim_class claim_class, 792*5af96835SDan Williams uuid_t *target) 793*5af96835SDan Williams { 794*5af96835SDan Williams if (claim_class == NVDIMM_CCLASS_BTT) 795*5af96835SDan Williams return &nvdimm_btt_uuid; 796*5af96835SDan Williams else if (claim_class == NVDIMM_CCLASS_BTT2) 797*5af96835SDan Williams return &nvdimm_btt2_uuid; 798*5af96835SDan Williams else if (claim_class == NVDIMM_CCLASS_PFN) 799*5af96835SDan Williams return &nvdimm_pfn_uuid; 800*5af96835SDan Williams else if (claim_class == NVDIMM_CCLASS_DAX) 801*5af96835SDan Williams return &nvdimm_dax_uuid; 802*5af96835SDan Williams else if (claim_class == NVDIMM_CCLASS_UNKNOWN) { 803*5af96835SDan Williams /* 804*5af96835SDan Williams * If we're modifying a namespace for which we don't 805*5af96835SDan Williams * know the claim_class, don't touch the existing uuid. 806*5af96835SDan Williams */ 807*5af96835SDan Williams return target; 808*5af96835SDan Williams } else 809*5af96835SDan Williams return &uuid_null; 810*5af96835SDan Williams } 811*5af96835SDan Williams 812c4703ce1SDan Williams static void reap_victim(struct nd_mapping *nd_mapping, 813c4703ce1SDan Williams struct nd_label_ent *victim) 814c4703ce1SDan Williams { 815c4703ce1SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 816c4703ce1SDan Williams u32 slot = to_slot(ndd, victim->label); 817c4703ce1SDan Williams 818c4703ce1SDan Williams dev_dbg(ndd->dev, "free: %d\n", slot); 819c4703ce1SDan Williams nd_label_free_slot(ndd, slot); 820c4703ce1SDan Williams victim->label = NULL; 821c4703ce1SDan Williams } 822c4703ce1SDan Williams 8238b03aa0eSDan Williams static void nsl_set_type_guid(struct nvdimm_drvdata *ndd, 8248b03aa0eSDan Williams struct nd_namespace_label *nd_label, guid_t *guid) 8258b03aa0eSDan Williams { 826*5af96835SDan Williams if (efi_namespace_label_has(ndd, type_guid)) 827*5af96835SDan Williams guid_copy(&nd_label->efi.type_guid, guid); 8288b03aa0eSDan Williams } 8298b03aa0eSDan Williams 8308b03aa0eSDan Williams bool nsl_validate_type_guid(struct nvdimm_drvdata *ndd, 8318b03aa0eSDan Williams struct nd_namespace_label *nd_label, guid_t *guid) 8328b03aa0eSDan Williams { 833*5af96835SDan Williams if (ndd->cxl || !efi_namespace_label_has(ndd, type_guid)) 8348b03aa0eSDan Williams return true; 835*5af96835SDan Williams if (!guid_equal(&nd_label->efi.type_guid, guid)) { 8368b03aa0eSDan Williams dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb\n", guid, 837*5af96835SDan Williams &nd_label->efi.type_guid); 8388b03aa0eSDan Williams return false; 8398b03aa0eSDan Williams } 8408b03aa0eSDan Williams return true; 8418b03aa0eSDan Williams } 8428b03aa0eSDan Williams 843a6e6d722SDan Williams static void nsl_set_claim_class(struct nvdimm_drvdata *ndd, 844a6e6d722SDan Williams struct nd_namespace_label *nd_label, 845a6e6d722SDan Williams enum nvdimm_claim_class claim_class) 846a6e6d722SDan Williams { 847*5af96835SDan Williams if (ndd->cxl) { 848*5af96835SDan Williams uuid_t uuid; 849*5af96835SDan Williams 850*5af96835SDan Williams import_uuid(&uuid, nd_label->cxl.abstraction_uuid); 851*5af96835SDan Williams export_uuid(nd_label->cxl.abstraction_uuid, 852*5af96835SDan Williams to_abstraction_uuid(claim_class, &uuid)); 853a6e6d722SDan Williams return; 854*5af96835SDan Williams } 855*5af96835SDan Williams 856*5af96835SDan Williams if (!efi_namespace_label_has(ndd, abstraction_guid)) 857*5af96835SDan Williams return; 858*5af96835SDan Williams guid_copy(&nd_label->efi.abstraction_guid, 859a6e6d722SDan Williams to_abstraction_guid(claim_class, 860*5af96835SDan Williams &nd_label->efi.abstraction_guid)); 861a6e6d722SDan Williams } 862a6e6d722SDan Williams 863a6e6d722SDan Williams enum nvdimm_claim_class nsl_get_claim_class(struct nvdimm_drvdata *ndd, 864a6e6d722SDan Williams struct nd_namespace_label *nd_label) 865a6e6d722SDan Williams { 866*5af96835SDan Williams if (ndd->cxl) { 867*5af96835SDan Williams uuid_t uuid; 868*5af96835SDan Williams 869*5af96835SDan Williams import_uuid(&uuid, nd_label->cxl.abstraction_uuid); 870*5af96835SDan Williams return uuid_to_nvdimm_cclass(&uuid); 871*5af96835SDan Williams } 872*5af96835SDan Williams if (!efi_namespace_label_has(ndd, abstraction_guid)) 873a6e6d722SDan Williams return NVDIMM_CCLASS_NONE; 874*5af96835SDan Williams return guid_to_nvdimm_cclass(&nd_label->efi.abstraction_guid); 875a6e6d722SDan Williams } 876a6e6d722SDan Williams 877f524bf27SDan Williams static int __pmem_label_update(struct nd_region *nd_region, 878f524bf27SDan Williams struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm, 879966d23a0SDan Williams int pos, unsigned long flags) 880f524bf27SDan Williams { 881b3fde74eSDan Williams struct nd_namespace_common *ndns = &nspm->nsio.common; 882faec6f8aSDan Williams struct nd_interleave_set *nd_set = nd_region->nd_set; 883f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 884f524bf27SDan Williams struct nd_namespace_label *nd_label; 885f524bf27SDan Williams struct nd_namespace_index *nsindex; 886c4703ce1SDan Williams struct nd_label_ent *label_ent; 88716660eaeSDan Williams struct nd_label_id label_id; 88816660eaeSDan Williams struct resource *res; 889f524bf27SDan Williams unsigned long *free; 890f524bf27SDan Williams u32 nslot, slot; 891f524bf27SDan Williams size_t offset; 892c12c48ceSDan Williams u64 cookie; 893f524bf27SDan Williams int rc; 894f524bf27SDan Williams 895f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 896f524bf27SDan Williams return -ENXIO; 897f524bf27SDan Williams 898c12c48ceSDan Williams cookie = nd_region_interleave_set_cookie(nd_region, nsindex); 89916660eaeSDan Williams nd_label_gen_id(&label_id, nspm->uuid, 0); 90016660eaeSDan Williams for_each_dpa_resource(ndd, res) 90116660eaeSDan Williams if (strcmp(res->name, label_id.id) == 0) 90216660eaeSDan Williams break; 90316660eaeSDan Williams 90416660eaeSDan Williams if (!res) { 90516660eaeSDan Williams WARN_ON_ONCE(1); 90616660eaeSDan Williams return -ENXIO; 90716660eaeSDan Williams } 90816660eaeSDan Williams 909f524bf27SDan Williams /* allocate and write the label to the staging (next) index */ 910f524bf27SDan Williams slot = nd_label_alloc_slot(ndd); 911f524bf27SDan Williams if (slot == UINT_MAX) 912f524bf27SDan Williams return -ENXIO; 913426824d6SDan Williams dev_dbg(ndd->dev, "allocated: %d\n", slot); 914f524bf27SDan Williams 915564e871aSDan Williams nd_label = to_label(ndd, slot); 916564e871aSDan Williams memset(nd_label, 0, sizeof_namespace_label(ndd)); 917d1c6e08eSDan Williams nsl_set_uuid(ndd, nd_label, nspm->uuid); 9188176f147SDan Williams nsl_set_name(ndd, nd_label, nspm->alt_name); 9198176f147SDan Williams nsl_set_flags(ndd, nd_label, flags); 9208176f147SDan Williams nsl_set_nlabel(ndd, nd_label, nd_region->ndr_mappings); 92142e192aaSDan Williams nsl_set_nrange(ndd, nd_label, 1); 9228176f147SDan Williams nsl_set_position(ndd, nd_label, pos); 9238176f147SDan Williams nsl_set_isetcookie(ndd, nd_label, cookie); 9248176f147SDan Williams nsl_set_rawsize(ndd, nd_label, resource_size(res)); 9258176f147SDan Williams nsl_set_lbasize(ndd, nd_label, nspm->lbasize); 9268176f147SDan Williams nsl_set_dpa(ndd, nd_label, res->start); 9278176f147SDan Williams nsl_set_slot(ndd, nd_label, slot); 9288b03aa0eSDan Williams nsl_set_type_guid(ndd, nd_label, &nd_set->type_guid); 929a6e6d722SDan Williams nsl_set_claim_class(ndd, nd_label, ndns->claim_class); 9307cd35b29SDan Williams nsl_calculate_checksum(ndd, nd_label); 931426824d6SDan Williams nd_dbg_dpa(nd_region, ndd, res, "\n"); 932f524bf27SDan Williams 933f524bf27SDan Williams /* update label */ 934f524bf27SDan Williams offset = nd_label_offset(ndd, nd_label); 935f524bf27SDan Williams rc = nvdimm_set_config_data(ndd, offset, nd_label, 936564e871aSDan Williams sizeof_namespace_label(ndd)); 937f524bf27SDan Williams if (rc < 0) 938f524bf27SDan Williams return rc; 939f524bf27SDan Williams 940f524bf27SDan Williams /* Garbage collect the previous label */ 941ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 94216660eaeSDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) { 94316660eaeSDan Williams if (!label_ent->label) 94416660eaeSDan Williams continue; 945d1c6e08eSDan Williams if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags) || 946d1c6e08eSDan Williams nsl_uuid_equal(ndd, label_ent->label, nspm->uuid)) 947c4703ce1SDan Williams reap_victim(nd_mapping, label_ent); 948f524bf27SDan Williams } 949f524bf27SDan Williams 950f524bf27SDan Williams /* update index */ 951f524bf27SDan Williams rc = nd_label_write_index(ndd, ndd->ns_next, 952f524bf27SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 95316660eaeSDan Williams if (rc == 0) { 95416660eaeSDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) 95516660eaeSDan Williams if (!label_ent->label) { 956ae8219f1SDan Williams label_ent->label = nd_label; 95716660eaeSDan Williams nd_label = NULL; 95816660eaeSDan Williams break; 95916660eaeSDan Williams } 96016660eaeSDan Williams dev_WARN_ONCE(&nspm->nsio.common.dev, nd_label, 96116660eaeSDan Williams "failed to track label: %d\n", 96216660eaeSDan Williams to_slot(ndd, nd_label)); 96316660eaeSDan Williams if (nd_label) 96416660eaeSDan Williams rc = -ENXIO; 96516660eaeSDan Williams } 966ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 967ae8219f1SDan Williams 968f524bf27SDan Williams return rc; 9690ba1c634SDan Williams } 9700ba1c634SDan Williams 9710ba1c634SDan Williams static bool is_old_resource(struct resource *res, struct resource **list, int n) 972f524bf27SDan Williams { 973f524bf27SDan Williams int i; 9740ba1c634SDan Williams 9750ba1c634SDan Williams if (res->flags & DPA_RESOURCE_ADJUSTED) 9760ba1c634SDan Williams return false; 9770ba1c634SDan Williams for (i = 0; i < n; i++) 9780ba1c634SDan Williams if (res == list[i]) 9790ba1c634SDan Williams return true; 9800ba1c634SDan Williams return false; 9810ba1c634SDan Williams } 9820ba1c634SDan Williams 9830ba1c634SDan Williams static struct resource *to_resource(struct nvdimm_drvdata *ndd, 9840ba1c634SDan Williams struct nd_namespace_label *nd_label) 9850ba1c634SDan Williams { 9860ba1c634SDan Williams struct resource *res; 9870ba1c634SDan Williams 9880ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 989b4366a82SDan Williams if (res->start != nsl_get_dpa(ndd, nd_label)) 9900ba1c634SDan Williams continue; 991b4366a82SDan Williams if (resource_size(res) != nsl_get_rawsize(ndd, nd_label)) 9920ba1c634SDan Williams continue; 9930ba1c634SDan Williams return res; 9940ba1c634SDan Williams } 9950ba1c634SDan Williams 9960ba1c634SDan Williams return NULL; 9970ba1c634SDan Williams } 9980ba1c634SDan Williams 999de8fa48bSDan Williams /* 1000de8fa48bSDan Williams * Use the presence of the type_guid as a flag to determine isetcookie 1001de8fa48bSDan Williams * usage and nlabel + position policy for blk-aperture namespaces. 1002de8fa48bSDan Williams */ 1003f56541a7SDan Williams static void nsl_set_blk_isetcookie(struct nvdimm_drvdata *ndd, 1004f56541a7SDan Williams struct nd_namespace_label *nd_label, 1005f56541a7SDan Williams u64 isetcookie) 1006f56541a7SDan Williams { 1007*5af96835SDan Williams if (efi_namespace_label_has(ndd, type_guid)) { 1008f56541a7SDan Williams nsl_set_isetcookie(ndd, nd_label, isetcookie); 1009f56541a7SDan Williams return; 1010f56541a7SDan Williams } 1011f56541a7SDan Williams nsl_set_isetcookie(ndd, nd_label, 0); /* N/A */ 1012f56541a7SDan Williams } 1013f56541a7SDan Williams 1014f56541a7SDan Williams bool nsl_validate_blk_isetcookie(struct nvdimm_drvdata *ndd, 1015f56541a7SDan Williams struct nd_namespace_label *nd_label, 1016f56541a7SDan Williams u64 isetcookie) 1017f56541a7SDan Williams { 1018*5af96835SDan Williams if (!efi_namespace_label_has(ndd, type_guid)) 1019f56541a7SDan Williams return true; 1020f56541a7SDan Williams 1021f56541a7SDan Williams if (nsl_get_isetcookie(ndd, nd_label) != isetcookie) { 1022f56541a7SDan Williams dev_dbg(ndd->dev, "expect cookie %#llx got %#llx\n", isetcookie, 1023f56541a7SDan Williams nsl_get_isetcookie(ndd, nd_label)); 1024f56541a7SDan Williams return false; 1025f56541a7SDan Williams } 1026f56541a7SDan Williams 1027f56541a7SDan Williams return true; 1028f56541a7SDan Williams } 1029f56541a7SDan Williams 1030de8fa48bSDan Williams static void nsl_set_blk_nlabel(struct nvdimm_drvdata *ndd, 1031de8fa48bSDan Williams struct nd_namespace_label *nd_label, int nlabel, 1032de8fa48bSDan Williams bool first) 1033de8fa48bSDan Williams { 1034*5af96835SDan Williams if (!efi_namespace_label_has(ndd, type_guid)) { 1035de8fa48bSDan Williams nsl_set_nlabel(ndd, nd_label, 0); /* N/A */ 1036de8fa48bSDan Williams return; 1037de8fa48bSDan Williams } 1038de8fa48bSDan Williams nsl_set_nlabel(ndd, nd_label, first ? nlabel : 0xffff); 1039de8fa48bSDan Williams } 1040de8fa48bSDan Williams 1041de8fa48bSDan Williams static void nsl_set_blk_position(struct nvdimm_drvdata *ndd, 1042de8fa48bSDan Williams struct nd_namespace_label *nd_label, 1043de8fa48bSDan Williams bool first) 1044de8fa48bSDan Williams { 1045*5af96835SDan Williams if (!efi_namespace_label_has(ndd, type_guid)) { 1046de8fa48bSDan Williams nsl_set_position(ndd, nd_label, 0); 1047de8fa48bSDan Williams return; 1048de8fa48bSDan Williams } 1049de8fa48bSDan Williams nsl_set_position(ndd, nd_label, first ? 0 : 0xffff); 1050de8fa48bSDan Williams } 1051de8fa48bSDan Williams 10520ba1c634SDan Williams /* 10530ba1c634SDan Williams * 1/ Account all the labels that can be freed after this update 10540ba1c634SDan Williams * 2/ Allocate and write the label to the staging (next) index 10550ba1c634SDan Williams * 3/ Record the resources in the namespace device 10560ba1c634SDan Williams */ 10570ba1c634SDan Williams static int __blk_label_update(struct nd_region *nd_region, 10580ba1c634SDan Williams struct nd_mapping *nd_mapping, struct nd_namespace_blk *nsblk, 10590ba1c634SDan Williams int num_labels) 10600ba1c634SDan Williams { 1061ae8219f1SDan Williams int i, alloc, victims, nfree, old_num_resources, nlabel, rc = -ENXIO; 1062faec6f8aSDan Williams struct nd_interleave_set *nd_set = nd_region->nd_set; 1063b3fde74eSDan Williams struct nd_namespace_common *ndns = &nsblk->common; 1064f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 10650ba1c634SDan Williams struct nd_namespace_label *nd_label; 1066ae8219f1SDan Williams struct nd_label_ent *label_ent, *e; 10670ba1c634SDan Williams struct nd_namespace_index *nsindex; 10680ba1c634SDan Williams unsigned long *free, *victim_map = NULL; 10690ba1c634SDan Williams struct resource *res, **old_res_list; 10700ba1c634SDan Williams struct nd_label_id label_id; 10713934d841SDan Williams int min_dpa_idx = 0; 1072ae8219f1SDan Williams LIST_HEAD(list); 10730ba1c634SDan Williams u32 nslot, slot; 1074f524bf27SDan Williams 10750ba1c634SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 10760ba1c634SDan Williams return -ENXIO; 1077f524bf27SDan Williams 10780ba1c634SDan Williams old_res_list = nsblk->res; 10790ba1c634SDan Williams nfree = nd_label_nfree(ndd); 10800ba1c634SDan Williams old_num_resources = nsblk->num_resources; 10810ba1c634SDan Williams nd_label_gen_id(&label_id, nsblk->uuid, NSLABEL_FLAG_LOCAL); 10820ba1c634SDan Williams 10830ba1c634SDan Williams /* 10840ba1c634SDan Williams * We need to loop over the old resources a few times, which seems a 10850ba1c634SDan Williams * bit inefficient, but we need to know that we have the label 10860ba1c634SDan Williams * space before we start mutating the tracking structures. 10870ba1c634SDan Williams * Otherwise the recovery method of last resort for userspace is 10880ba1c634SDan Williams * disable and re-enable the parent region. 10890ba1c634SDan Williams */ 10900ba1c634SDan Williams alloc = 0; 10910ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 10920ba1c634SDan Williams if (strcmp(res->name, label_id.id) != 0) 10930ba1c634SDan Williams continue; 10940ba1c634SDan Williams if (!is_old_resource(res, old_res_list, old_num_resources)) 10950ba1c634SDan Williams alloc++; 10960ba1c634SDan Williams } 10970ba1c634SDan Williams 10980ba1c634SDan Williams victims = 0; 10990ba1c634SDan Williams if (old_num_resources) { 11000ba1c634SDan Williams /* convert old local-label-map to dimm-slot victim-map */ 11019065ed12SAndy Shevchenko victim_map = bitmap_zalloc(nslot, GFP_KERNEL); 11020ba1c634SDan Williams if (!victim_map) 11030ba1c634SDan Williams return -ENOMEM; 11040ba1c634SDan Williams 11050ba1c634SDan Williams /* mark unused labels for garbage collection */ 11060ba1c634SDan Williams for_each_clear_bit_le(slot, free, nslot) { 1107564e871aSDan Williams nd_label = to_label(ndd, slot); 1108d1c6e08eSDan Williams if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid)) 11090ba1c634SDan Williams continue; 11100ba1c634SDan Williams res = to_resource(ndd, nd_label); 11110ba1c634SDan Williams if (res && is_old_resource(res, old_res_list, 11120ba1c634SDan Williams old_num_resources)) 11130ba1c634SDan Williams continue; 11140ba1c634SDan Williams slot = to_slot(ndd, nd_label); 11150ba1c634SDan Williams set_bit(slot, victim_map); 11160ba1c634SDan Williams victims++; 11170ba1c634SDan Williams } 11180ba1c634SDan Williams } 11190ba1c634SDan Williams 11200ba1c634SDan Williams /* don't allow updates that consume the last label */ 11210ba1c634SDan Williams if (nfree - alloc < 0 || nfree - alloc + victims < 1) { 11228c2f7e86SDan Williams dev_info(&nsblk->common.dev, "insufficient label space\n"); 11239065ed12SAndy Shevchenko bitmap_free(victim_map); 11240ba1c634SDan Williams return -ENOSPC; 11250ba1c634SDan Williams } 11260ba1c634SDan Williams /* from here on we need to abort on error */ 11270ba1c634SDan Williams 11280ba1c634SDan Williams 11290ba1c634SDan Williams /* assign all resources to the namespace before writing the labels */ 11300ba1c634SDan Williams nsblk->res = NULL; 11310ba1c634SDan Williams nsblk->num_resources = 0; 11320ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 11330ba1c634SDan Williams if (strcmp(res->name, label_id.id) != 0) 11340ba1c634SDan Williams continue; 11350ba1c634SDan Williams if (!nsblk_add_resource(nd_region, ndd, nsblk, res->start)) { 11360ba1c634SDan Williams rc = -ENOMEM; 11370ba1c634SDan Williams goto abort; 11380ba1c634SDan Williams } 11390ba1c634SDan Williams } 11400ba1c634SDan Williams 11412dd2a174SDan Williams /* release slots associated with any invalidated UUIDs */ 11422dd2a174SDan Williams mutex_lock(&nd_mapping->lock); 11432dd2a174SDan Williams list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) 11442dd2a174SDan Williams if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags)) { 11452dd2a174SDan Williams reap_victim(nd_mapping, label_ent); 11462dd2a174SDan Williams list_move(&label_ent->list, &list); 11472dd2a174SDan Williams } 11482dd2a174SDan Williams mutex_unlock(&nd_mapping->lock); 11492dd2a174SDan Williams 11503934d841SDan Williams /* 11513934d841SDan Williams * Find the resource associated with the first label in the set 11523934d841SDan Williams * per the v1.2 namespace specification. 11533934d841SDan Williams */ 11543934d841SDan Williams for (i = 0; i < nsblk->num_resources; i++) { 11553934d841SDan Williams struct resource *min = nsblk->res[min_dpa_idx]; 11563934d841SDan Williams 11573934d841SDan Williams res = nsblk->res[i]; 11583934d841SDan Williams if (res->start < min->start) 11593934d841SDan Williams min_dpa_idx = i; 11603934d841SDan Williams } 11613934d841SDan Williams 11620ba1c634SDan Williams for (i = 0; i < nsblk->num_resources; i++) { 11630ba1c634SDan Williams size_t offset; 11640ba1c634SDan Williams 11650ba1c634SDan Williams res = nsblk->res[i]; 11660ba1c634SDan Williams if (is_old_resource(res, old_res_list, old_num_resources)) 11670ba1c634SDan Williams continue; /* carry-over */ 11680ba1c634SDan Williams slot = nd_label_alloc_slot(ndd); 11694c467647SZhang Qilong if (slot == UINT_MAX) { 11704c467647SZhang Qilong rc = -ENXIO; 11710ba1c634SDan Williams goto abort; 11724c467647SZhang Qilong } 1173426824d6SDan Williams dev_dbg(ndd->dev, "allocated: %d\n", slot); 11740ba1c634SDan Williams 1175564e871aSDan Williams nd_label = to_label(ndd, slot); 1176564e871aSDan Williams memset(nd_label, 0, sizeof_namespace_label(ndd)); 1177d1c6e08eSDan Williams nsl_set_uuid(ndd, nd_label, nsblk->uuid); 11788176f147SDan Williams nsl_set_name(ndd, nd_label, nsblk->alt_name); 11798176f147SDan Williams nsl_set_flags(ndd, nd_label, NSLABEL_FLAG_LOCAL); 11808f2bc243SDan Williams 1181de8fa48bSDan Williams nsl_set_blk_nlabel(ndd, nd_label, nsblk->num_resources, 1182de8fa48bSDan Williams i == min_dpa_idx); 1183de8fa48bSDan Williams nsl_set_blk_position(ndd, nd_label, i == min_dpa_idx); 1184f56541a7SDan Williams nsl_set_blk_isetcookie(ndd, nd_label, nd_set->cookie2); 11858f2bc243SDan Williams 11868176f147SDan Williams nsl_set_dpa(ndd, nd_label, res->start); 11878176f147SDan Williams nsl_set_rawsize(ndd, nd_label, resource_size(res)); 11888176f147SDan Williams nsl_set_lbasize(ndd, nd_label, nsblk->lbasize); 11898176f147SDan Williams nsl_set_slot(ndd, nd_label, slot); 11908b03aa0eSDan Williams nsl_set_type_guid(ndd, nd_label, &nd_set->type_guid); 1191a6e6d722SDan Williams nsl_set_claim_class(ndd, nd_label, ndns->claim_class); 11927cd35b29SDan Williams nsl_calculate_checksum(ndd, nd_label); 11930ba1c634SDan Williams 11940ba1c634SDan Williams /* update label */ 11950ba1c634SDan Williams offset = nd_label_offset(ndd, nd_label); 11960ba1c634SDan Williams rc = nvdimm_set_config_data(ndd, offset, nd_label, 1197564e871aSDan Williams sizeof_namespace_label(ndd)); 11980ba1c634SDan Williams if (rc < 0) 11990ba1c634SDan Williams goto abort; 12000ba1c634SDan Williams } 12010ba1c634SDan Williams 12020ba1c634SDan Williams /* free up now unused slots in the new index */ 12030ba1c634SDan Williams for_each_set_bit(slot, victim_map, victim_map ? nslot : 0) { 1204426824d6SDan Williams dev_dbg(ndd->dev, "free: %d\n", slot); 12050ba1c634SDan Williams nd_label_free_slot(ndd, slot); 12060ba1c634SDan Williams } 12070ba1c634SDan Williams 12080ba1c634SDan Williams /* update index */ 12090ba1c634SDan Williams rc = nd_label_write_index(ndd, ndd->ns_next, 12100ba1c634SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 12110ba1c634SDan Williams if (rc) 12120ba1c634SDan Williams goto abort; 12130ba1c634SDan Williams 12140ba1c634SDan Williams /* 12150ba1c634SDan Williams * Now that the on-dimm labels are up to date, fix up the tracking 12160ba1c634SDan Williams * entries in nd_mapping->labels 12170ba1c634SDan Williams */ 12180ba1c634SDan Williams nlabel = 0; 1219ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1220ae8219f1SDan Williams list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { 1221ae8219f1SDan Williams nd_label = label_ent->label; 1222ae8219f1SDan Williams if (!nd_label) 1223ae8219f1SDan Williams continue; 12240ba1c634SDan Williams nlabel++; 1225d1c6e08eSDan Williams if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid)) 12260ba1c634SDan Williams continue; 12270ba1c634SDan Williams nlabel--; 1228ae8219f1SDan Williams list_move(&label_ent->list, &list); 1229ae8219f1SDan Williams label_ent->label = NULL; 12300ba1c634SDan Williams } 1231ae8219f1SDan Williams list_splice_tail_init(&list, &nd_mapping->labels); 1232ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 1233ae8219f1SDan Williams 12340ba1c634SDan Williams if (nlabel + nsblk->num_resources > num_labels) { 12350ba1c634SDan Williams /* 12360ba1c634SDan Williams * Bug, we can't end up with more resources than 12370ba1c634SDan Williams * available labels 12380ba1c634SDan Williams */ 12390ba1c634SDan Williams WARN_ON_ONCE(1); 12400ba1c634SDan Williams rc = -ENXIO; 12410ba1c634SDan Williams goto out; 12420ba1c634SDan Williams } 12430ba1c634SDan Williams 1244ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1245ae8219f1SDan Williams label_ent = list_first_entry_or_null(&nd_mapping->labels, 1246ae8219f1SDan Williams typeof(*label_ent), list); 1247ae8219f1SDan Williams if (!label_ent) { 1248ae8219f1SDan Williams WARN_ON(1); 1249ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 1250ae8219f1SDan Williams rc = -ENXIO; 1251ae8219f1SDan Williams goto out; 1252ae8219f1SDan Williams } 12530ba1c634SDan Williams for_each_clear_bit_le(slot, free, nslot) { 1254564e871aSDan Williams nd_label = to_label(ndd, slot); 1255d1c6e08eSDan Williams if (!nsl_uuid_equal(ndd, nd_label, nsblk->uuid)) 12560ba1c634SDan Williams continue; 12570ba1c634SDan Williams res = to_resource(ndd, nd_label); 12580ba1c634SDan Williams res->flags &= ~DPA_RESOURCE_ADJUSTED; 1259ae8219f1SDan Williams dev_vdbg(&nsblk->common.dev, "assign label slot: %d\n", slot); 1260ae8219f1SDan Williams list_for_each_entry_from(label_ent, &nd_mapping->labels, list) { 1261ae8219f1SDan Williams if (label_ent->label) 1262ae8219f1SDan Williams continue; 1263ae8219f1SDan Williams label_ent->label = nd_label; 1264ae8219f1SDan Williams nd_label = NULL; 1265ae8219f1SDan Williams break; 12660ba1c634SDan Williams } 1267ae8219f1SDan Williams if (nd_label) 1268ae8219f1SDan Williams dev_WARN(&nsblk->common.dev, 1269ae8219f1SDan Williams "failed to track label slot%d\n", slot); 1270ae8219f1SDan Williams } 1271ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 12720ba1c634SDan Williams 12730ba1c634SDan Williams out: 12740ba1c634SDan Williams kfree(old_res_list); 12759065ed12SAndy Shevchenko bitmap_free(victim_map); 12760ba1c634SDan Williams return rc; 12770ba1c634SDan Williams 12780ba1c634SDan Williams abort: 12790ba1c634SDan Williams /* 12800ba1c634SDan Williams * 1/ repair the allocated label bitmap in the index 12810ba1c634SDan Williams * 2/ restore the resource list 12820ba1c634SDan Williams */ 12830ba1c634SDan Williams nd_label_copy(ndd, nsindex, to_current_namespace_index(ndd)); 12840ba1c634SDan Williams kfree(nsblk->res); 12850ba1c634SDan Williams nsblk->res = old_res_list; 12860ba1c634SDan Williams nsblk->num_resources = old_num_resources; 12870ba1c634SDan Williams old_res_list = NULL; 12880ba1c634SDan Williams goto out; 12890ba1c634SDan Williams } 12900ba1c634SDan Williams 12910ba1c634SDan Williams static int init_labels(struct nd_mapping *nd_mapping, int num_labels) 12920ba1c634SDan Williams { 1293ae8219f1SDan Williams int i, old_num_labels = 0; 1294ae8219f1SDan Williams struct nd_label_ent *label_ent; 12950ba1c634SDan Williams struct nd_namespace_index *nsindex; 12960ba1c634SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 12970ba1c634SDan Williams 1298ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1299ae8219f1SDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) 13000ba1c634SDan Williams old_num_labels++; 1301ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 13020ba1c634SDan Williams 13030ba1c634SDan Williams /* 13040ba1c634SDan Williams * We need to preserve all the old labels for the mapping so 13050ba1c634SDan Williams * they can be garbage collected after writing the new labels. 13060ba1c634SDan Williams */ 1307ae8219f1SDan Williams for (i = old_num_labels; i < num_labels; i++) { 1308ae8219f1SDan Williams label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL); 1309ae8219f1SDan Williams if (!label_ent) 13100ba1c634SDan Williams return -ENOMEM; 1311ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1312ae8219f1SDan Williams list_add_tail(&label_ent->list, &nd_mapping->labels); 1313ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 13140ba1c634SDan Williams } 13150ba1c634SDan Williams 1316f524bf27SDan Williams if (ndd->ns_current == -1 || ndd->ns_next == -1) 1317f524bf27SDan Williams /* pass */; 1318f524bf27SDan Williams else 13190ba1c634SDan Williams return max(num_labels, old_num_labels); 1320f524bf27SDan Williams 1321f524bf27SDan Williams nsindex = to_namespace_index(ndd, 0); 1322f524bf27SDan Williams memset(nsindex, 0, ndd->nsarea.config_size); 1323f524bf27SDan Williams for (i = 0; i < 2; i++) { 1324b18d4b8aSDan Williams int rc = nd_label_write_index(ndd, i, 3 - i, ND_NSINDEX_INIT); 1325f524bf27SDan Williams 1326f524bf27SDan Williams if (rc) 1327f524bf27SDan Williams return rc; 1328f524bf27SDan Williams } 1329f524bf27SDan Williams ndd->ns_next = 1; 1330f524bf27SDan Williams ndd->ns_current = 0; 1331f524bf27SDan Williams 13320ba1c634SDan Williams return max(num_labels, old_num_labels); 1333f524bf27SDan Williams } 1334f524bf27SDan Williams 1335d1c6e08eSDan Williams static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid) 1336f524bf27SDan Williams { 1337f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 1338ae8219f1SDan Williams struct nd_label_ent *label_ent, *e; 1339f524bf27SDan Williams struct nd_namespace_index *nsindex; 1340f524bf27SDan Williams unsigned long *free; 1341ae8219f1SDan Williams LIST_HEAD(list); 1342f524bf27SDan Williams u32 nslot, slot; 1343ae8219f1SDan Williams int active = 0; 1344f524bf27SDan Williams 1345f524bf27SDan Williams if (!uuid) 1346f524bf27SDan Williams return 0; 1347f524bf27SDan Williams 1348f524bf27SDan Williams /* no index || no labels == nothing to delete */ 1349ae8219f1SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 1350f524bf27SDan Williams return 0; 1351f524bf27SDan Williams 1352ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1353ae8219f1SDan Williams list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { 1354ae8219f1SDan Williams struct nd_namespace_label *nd_label = label_ent->label; 1355ae8219f1SDan Williams 1356ae8219f1SDan Williams if (!nd_label) 1357ae8219f1SDan Williams continue; 1358ae8219f1SDan Williams active++; 1359d1c6e08eSDan Williams if (!nsl_uuid_equal(ndd, nd_label, uuid)) 1360f524bf27SDan Williams continue; 1361ae8219f1SDan Williams active--; 1362f524bf27SDan Williams slot = to_slot(ndd, nd_label); 1363f524bf27SDan Williams nd_label_free_slot(ndd, slot); 1364426824d6SDan Williams dev_dbg(ndd->dev, "free: %d\n", slot); 1365ae8219f1SDan Williams list_move_tail(&label_ent->list, &list); 1366ae8219f1SDan Williams label_ent->label = NULL; 1367f524bf27SDan Williams } 1368ae8219f1SDan Williams list_splice_tail_init(&list, &nd_mapping->labels); 1369f524bf27SDan Williams 1370ae8219f1SDan Williams if (active == 0) { 1371ae8219f1SDan Williams nd_mapping_free_labels(nd_mapping); 1372426824d6SDan Williams dev_dbg(ndd->dev, "no more active labels\n"); 1373f524bf27SDan Williams } 1374ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 1375f524bf27SDan Williams 1376f524bf27SDan Williams return nd_label_write_index(ndd, ndd->ns_next, 1377f524bf27SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 1378f524bf27SDan Williams } 1379f524bf27SDan Williams 1380f524bf27SDan Williams int nd_pmem_namespace_label_update(struct nd_region *nd_region, 1381f524bf27SDan Williams struct nd_namespace_pmem *nspm, resource_size_t size) 1382f524bf27SDan Williams { 1383966d23a0SDan Williams int i, rc; 1384f524bf27SDan Williams 1385f524bf27SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 1386f524bf27SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[i]; 138716660eaeSDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 138816660eaeSDan Williams struct resource *res; 1389966d23a0SDan Williams int count = 0; 1390f524bf27SDan Williams 1391f524bf27SDan Williams if (size == 0) { 1392f524bf27SDan Williams rc = del_labels(nd_mapping, nspm->uuid); 1393f524bf27SDan Williams if (rc) 1394f524bf27SDan Williams return rc; 1395f524bf27SDan Williams continue; 1396f524bf27SDan Williams } 1397f524bf27SDan Williams 139816660eaeSDan Williams for_each_dpa_resource(ndd, res) 13992d9a0274SNicolas Iooss if (strncmp(res->name, "pmem", 4) == 0) 140016660eaeSDan Williams count++; 140116660eaeSDan Williams WARN_ON_ONCE(!count); 140216660eaeSDan Williams 140316660eaeSDan Williams rc = init_labels(nd_mapping, count); 14040ba1c634SDan Williams if (rc < 0) 1405f524bf27SDan Williams return rc; 1406f524bf27SDan Williams 1407966d23a0SDan Williams rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 1408966d23a0SDan Williams NSLABEL_FLAG_UPDATING); 1409966d23a0SDan Williams if (rc) 1410966d23a0SDan Williams return rc; 1411966d23a0SDan Williams } 1412966d23a0SDan Williams 1413966d23a0SDan Williams if (size == 0) 1414966d23a0SDan Williams return 0; 1415966d23a0SDan Williams 1416966d23a0SDan Williams /* Clear the UPDATING flag per UEFI 2.7 expectations */ 1417966d23a0SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 1418966d23a0SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[i]; 1419966d23a0SDan Williams 1420966d23a0SDan Williams rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 0); 1421f524bf27SDan Williams if (rc) 1422f524bf27SDan Williams return rc; 1423f524bf27SDan Williams } 1424f524bf27SDan Williams 1425f524bf27SDan Williams return 0; 1426f524bf27SDan Williams } 14270ba1c634SDan Williams 14280ba1c634SDan Williams int nd_blk_namespace_label_update(struct nd_region *nd_region, 14290ba1c634SDan Williams struct nd_namespace_blk *nsblk, resource_size_t size) 14300ba1c634SDan Williams { 14310ba1c634SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 14320ba1c634SDan Williams struct resource *res; 14330ba1c634SDan Williams int count = 0; 14340ba1c634SDan Williams 14350ba1c634SDan Williams if (size == 0) 14360ba1c634SDan Williams return del_labels(nd_mapping, nsblk->uuid); 14370ba1c634SDan Williams 14380ba1c634SDan Williams for_each_dpa_resource(to_ndd(nd_mapping), res) 14390ba1c634SDan Williams count++; 14400ba1c634SDan Williams 14410ba1c634SDan Williams count = init_labels(nd_mapping, count); 14420ba1c634SDan Williams if (count < 0) 14430ba1c634SDan Williams return count; 14440ba1c634SDan Williams 14450ba1c634SDan Williams return __blk_label_update(nd_region, nd_mapping, nsblk, count); 14460ba1c634SDan Williams } 1447b3fde74eSDan Williams 1448b3fde74eSDan Williams int __init nd_label_init(void) 1449b3fde74eSDan Williams { 1450b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_BTT_GUID, &nvdimm_btt_guid)); 145114e49454SVishal Verma WARN_ON(guid_parse(NVDIMM_BTT2_GUID, &nvdimm_btt2_guid)); 1452b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_guid)); 1453b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_guid)); 1454b3fde74eSDan Williams 1455*5af96835SDan Williams WARN_ON(uuid_parse(NVDIMM_BTT_GUID, &nvdimm_btt_uuid)); 1456*5af96835SDan Williams WARN_ON(uuid_parse(NVDIMM_BTT2_GUID, &nvdimm_btt2_uuid)); 1457*5af96835SDan Williams WARN_ON(uuid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_uuid)); 1458*5af96835SDan Williams WARN_ON(uuid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_uuid)); 1459*5af96835SDan Williams 1460*5af96835SDan Williams WARN_ON(uuid_parse(CXL_REGION_UUID, &cxl_region_uuid)); 1461*5af96835SDan Williams WARN_ON(uuid_parse(CXL_NAMESPACE_UUID, &cxl_namespace_uuid)); 1462*5af96835SDan Williams 1463b3fde74eSDan Williams return 0; 1464b3fde74eSDan Williams } 1465