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 205af96835SDan Williams static uuid_t nvdimm_btt_uuid; 215af96835SDan Williams static uuid_t nvdimm_btt2_uuid; 225af96835SDan Williams static uuid_t nvdimm_pfn_uuid; 235af96835SDan Williams static uuid_t nvdimm_dax_uuid; 245af96835SDan Williams 255af96835SDan Williams static uuid_t cxl_region_uuid; 265af96835SDan Williams static uuid_t cxl_namespace_uuid; 275af96835SDan 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; 337*3b6c6c03SDan Williams snprintf(label_id->id, ND_LABEL_ID_SIZE, "pmem-%pUb", uuid); 3384a826c83SDan Williams return label_id->id; 3394a826c83SDan Williams } 3404a826c83SDan Williams 341f524bf27SDan Williams static bool preamble_current(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_current, nsindex, 346f524bf27SDan Williams free, nslot); 347f524bf27SDan Williams } 348f524bf27SDan Williams 349f524bf27SDan Williams static bool preamble_next(struct nvdimm_drvdata *ndd, 350f524bf27SDan Williams struct nd_namespace_index **nsindex, 351f524bf27SDan Williams unsigned long **free, u32 *nslot) 352f524bf27SDan Williams { 353f524bf27SDan Williams return preamble_index(ndd, ndd->ns_next, nsindex, 354f524bf27SDan Williams free, nslot); 355f524bf27SDan Williams } 356f524bf27SDan Williams 3577cd35b29SDan Williams static bool nsl_validate_checksum(struct nvdimm_drvdata *ndd, 3587cd35b29SDan Williams struct nd_namespace_label *nd_label) 3594a826c83SDan Williams { 360355d8388SDan Williams u64 sum, sum_save; 361355d8388SDan Williams 3625af96835SDan Williams if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum)) 3637cd35b29SDan Williams return true; 3647cd35b29SDan Williams 365b4366a82SDan Williams sum_save = nsl_get_checksum(ndd, nd_label); 3668176f147SDan Williams nsl_set_checksum(ndd, nd_label, 0); 367355d8388SDan Williams sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1); 3688176f147SDan Williams nsl_set_checksum(ndd, nd_label, sum_save); 3697cd35b29SDan Williams return sum == sum_save; 370355d8388SDan Williams } 371355d8388SDan Williams 3727cd35b29SDan Williams static void nsl_calculate_checksum(struct nvdimm_drvdata *ndd, 3737cd35b29SDan Williams struct nd_namespace_label *nd_label) 3747cd35b29SDan Williams { 3757cd35b29SDan Williams u64 sum; 3767cd35b29SDan Williams 3775af96835SDan Williams if (!ndd->cxl && !efi_namespace_label_has(ndd, checksum)) 3787cd35b29SDan Williams return; 3797cd35b29SDan Williams nsl_set_checksum(ndd, nd_label, 0); 3807cd35b29SDan Williams sum = nd_fletcher64(nd_label, sizeof_namespace_label(ndd), 1); 3817cd35b29SDan Williams nsl_set_checksum(ndd, nd_label, sum); 3827cd35b29SDan Williams } 3837cd35b29SDan Williams 3847cd35b29SDan Williams static bool slot_valid(struct nvdimm_drvdata *ndd, 3857cd35b29SDan Williams struct nd_namespace_label *nd_label, u32 slot) 3867cd35b29SDan Williams { 3877cd35b29SDan Williams bool valid; 3887cd35b29SDan Williams 3897cd35b29SDan Williams /* check that we are written where we expect to be written */ 3907cd35b29SDan Williams if (slot != nsl_get_slot(ndd, nd_label)) 3917cd35b29SDan Williams return false; 3927cd35b29SDan Williams valid = nsl_validate_checksum(ndd, nd_label); 3937cd35b29SDan Williams if (!valid) 3947cd35b29SDan Williams dev_dbg(ndd->dev, "fail checksum. slot: %d\n", slot); 3957cd35b29SDan Williams return valid; 3964a826c83SDan Williams } 3974a826c83SDan Williams 3984a826c83SDan Williams int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) 3994a826c83SDan Williams { 4004a826c83SDan Williams struct nd_namespace_index *nsindex; 4014a826c83SDan Williams unsigned long *free; 4024a826c83SDan Williams u32 nslot, slot; 4034a826c83SDan Williams 4044a826c83SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 4054a826c83SDan Williams return 0; /* no label, nothing to reserve */ 4064a826c83SDan Williams 4074a826c83SDan Williams for_each_clear_bit_le(slot, free, nslot) { 4084a826c83SDan Williams struct nd_namespace_label *nd_label; 4094a826c83SDan Williams struct nd_region *nd_region = NULL; 4104a826c83SDan Williams struct nd_label_id label_id; 4114a826c83SDan Williams struct resource *res; 412d1c6e08eSDan Williams uuid_t label_uuid; 4134a826c83SDan Williams u32 flags; 4144a826c83SDan Williams 415564e871aSDan Williams nd_label = to_label(ndd, slot); 4164a826c83SDan Williams 417355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) 4184a826c83SDan Williams continue; 4194a826c83SDan Williams 420d1c6e08eSDan Williams nsl_get_uuid(ndd, nd_label, &label_uuid); 421b4366a82SDan Williams flags = nsl_get_flags(ndd, nd_label); 422d1c6e08eSDan Williams nd_label_gen_id(&label_id, &label_uuid, flags); 4234a826c83SDan Williams res = nvdimm_allocate_dpa(ndd, &label_id, 424b4366a82SDan Williams nsl_get_dpa(ndd, nd_label), 425b4366a82SDan Williams nsl_get_rawsize(ndd, nd_label)); 4264a826c83SDan Williams nd_dbg_dpa(nd_region, ndd, res, "reserve\n"); 4274a826c83SDan Williams if (!res) 4284a826c83SDan Williams return -EBUSY; 4294a826c83SDan Williams } 4304a826c83SDan Williams 4314a826c83SDan Williams return 0; 4324a826c83SDan Williams } 433bf9bccc1SDan Williams 4342d657d17SAlexander Duyck int nd_label_data_init(struct nvdimm_drvdata *ndd) 4352d657d17SAlexander Duyck { 4367d47aad4SAlexander Duyck size_t config_size, read_size, max_xfer, offset; 4377d47aad4SAlexander Duyck struct nd_namespace_index *nsindex; 4387d47aad4SAlexander Duyck unsigned int i; 4392d657d17SAlexander Duyck int rc = 0; 44097052c1cSDan Williams u32 nslot; 4412d657d17SAlexander Duyck 4422d657d17SAlexander Duyck if (ndd->data) 4432d657d17SAlexander Duyck return 0; 4442d657d17SAlexander Duyck 4452d657d17SAlexander Duyck if (ndd->nsarea.status || ndd->nsarea.max_xfer == 0) { 4462d657d17SAlexander Duyck dev_dbg(ndd->dev, "failed to init config data area: (%u:%u)\n", 4472d657d17SAlexander Duyck ndd->nsarea.max_xfer, ndd->nsarea.config_size); 4482d657d17SAlexander Duyck return -ENXIO; 4492d657d17SAlexander Duyck } 4502d657d17SAlexander Duyck 4512d657d17SAlexander Duyck /* 4522d657d17SAlexander Duyck * We need to determine the maximum index area as this is the section 4532d657d17SAlexander Duyck * we must read and validate before we can start processing labels. 4542d657d17SAlexander Duyck * 4552d657d17SAlexander Duyck * If the area is too small to contain the two indexes and 2 labels 4562d657d17SAlexander Duyck * then we abort. 4572d657d17SAlexander Duyck * 4582d657d17SAlexander Duyck * Start at a label size of 128 as this should result in the largest 4592d657d17SAlexander Duyck * possible namespace index size. 4602d657d17SAlexander Duyck */ 4612d657d17SAlexander Duyck ndd->nslabel_size = 128; 4622d657d17SAlexander Duyck read_size = sizeof_namespace_index(ndd) * 2; 4632d657d17SAlexander Duyck if (!read_size) 4642d657d17SAlexander Duyck return -ENXIO; 4652d657d17SAlexander Duyck 4662d657d17SAlexander Duyck /* Allocate config data */ 4672d657d17SAlexander Duyck config_size = ndd->nsarea.config_size; 4682d657d17SAlexander Duyck ndd->data = kvzalloc(config_size, GFP_KERNEL); 4692d657d17SAlexander Duyck if (!ndd->data) 4702d657d17SAlexander Duyck return -ENOMEM; 4712d657d17SAlexander Duyck 4727d47aad4SAlexander Duyck /* 4737d47aad4SAlexander Duyck * We want to guarantee as few reads as possible while conserving 4747d47aad4SAlexander Duyck * memory. To do that we figure out how much unused space will be left 4757d47aad4SAlexander Duyck * in the last read, divide that by the total number of reads it is 4767d47aad4SAlexander Duyck * going to take given our maximum transfer size, and then reduce our 4777d47aad4SAlexander Duyck * maximum transfer size based on that result. 4787d47aad4SAlexander Duyck */ 4797d47aad4SAlexander Duyck max_xfer = min_t(size_t, ndd->nsarea.max_xfer, config_size); 4807d47aad4SAlexander Duyck if (read_size < max_xfer) { 4817d47aad4SAlexander Duyck /* trim waste */ 4827d47aad4SAlexander Duyck max_xfer -= ((max_xfer - 1) - (config_size - 1) % max_xfer) / 4837d47aad4SAlexander Duyck DIV_ROUND_UP(config_size, max_xfer); 4847d47aad4SAlexander Duyck /* make certain we read indexes in exactly 1 read */ 4857d47aad4SAlexander Duyck if (max_xfer < read_size) 4867d47aad4SAlexander Duyck max_xfer = read_size; 4877d47aad4SAlexander Duyck } 4887d47aad4SAlexander Duyck 4897d47aad4SAlexander Duyck /* Make our initial read size a multiple of max_xfer size */ 4907d47aad4SAlexander Duyck read_size = min(DIV_ROUND_UP(read_size, max_xfer) * max_xfer, 4917d47aad4SAlexander Duyck config_size); 4927d47aad4SAlexander Duyck 4937d47aad4SAlexander Duyck /* Read the index data */ 4947d47aad4SAlexander Duyck rc = nvdimm_get_config_data(ndd, ndd->data, 0, read_size); 4957d47aad4SAlexander Duyck if (rc) 4967d47aad4SAlexander Duyck goto out_err; 4977d47aad4SAlexander Duyck 4987d47aad4SAlexander Duyck /* Validate index data, if not valid assume all labels are invalid */ 4997d47aad4SAlexander Duyck ndd->ns_current = nd_label_validate(ndd); 5007d47aad4SAlexander Duyck if (ndd->ns_current < 0) 5017d47aad4SAlexander Duyck return 0; 5027d47aad4SAlexander Duyck 5037d47aad4SAlexander Duyck /* Record our index values */ 5047d47aad4SAlexander Duyck ndd->ns_next = nd_label_next_nsindex(ndd->ns_current); 5057d47aad4SAlexander Duyck 5067d47aad4SAlexander Duyck /* Copy "current" index on top of the "next" index */ 5077d47aad4SAlexander Duyck nsindex = to_current_namespace_index(ndd); 5087d47aad4SAlexander Duyck nd_label_copy(ndd, to_next_namespace_index(ndd), nsindex); 5097d47aad4SAlexander Duyck 5107d47aad4SAlexander Duyck /* Determine starting offset for label data */ 5117d47aad4SAlexander Duyck offset = __le64_to_cpu(nsindex->labeloff); 51297052c1cSDan Williams nslot = __le32_to_cpu(nsindex->nslot); 5137d47aad4SAlexander Duyck 5147d47aad4SAlexander Duyck /* Loop through the free list pulling in any active labels */ 51597052c1cSDan Williams for (i = 0; i < nslot; i++, offset += ndd->nslabel_size) { 5167d47aad4SAlexander Duyck size_t label_read_size; 5177d47aad4SAlexander Duyck 5187d47aad4SAlexander Duyck /* zero out the unused labels */ 5197d47aad4SAlexander Duyck if (test_bit_le(i, nsindex->free)) { 5207d47aad4SAlexander Duyck memset(ndd->data + offset, 0, ndd->nslabel_size); 5217d47aad4SAlexander Duyck continue; 5227d47aad4SAlexander Duyck } 5237d47aad4SAlexander Duyck 5247d47aad4SAlexander Duyck /* if we already read past here then just continue */ 5257d47aad4SAlexander Duyck if (offset + ndd->nslabel_size <= read_size) 5267d47aad4SAlexander Duyck continue; 5277d47aad4SAlexander Duyck 5287d47aad4SAlexander Duyck /* if we haven't read in a while reset our read_size offset */ 5297d47aad4SAlexander Duyck if (read_size < offset) 5307d47aad4SAlexander Duyck read_size = offset; 5317d47aad4SAlexander Duyck 5327d47aad4SAlexander Duyck /* determine how much more will be read after this next call. */ 5337d47aad4SAlexander Duyck label_read_size = offset + ndd->nslabel_size - read_size; 5347d47aad4SAlexander Duyck label_read_size = DIV_ROUND_UP(label_read_size, max_xfer) * 5357d47aad4SAlexander Duyck max_xfer; 5367d47aad4SAlexander Duyck 5377d47aad4SAlexander Duyck /* truncate last read if needed */ 5387d47aad4SAlexander Duyck if (read_size + label_read_size > config_size) 5397d47aad4SAlexander Duyck label_read_size = config_size - read_size; 5407d47aad4SAlexander Duyck 5417d47aad4SAlexander Duyck /* Read the label data */ 5427d47aad4SAlexander Duyck rc = nvdimm_get_config_data(ndd, ndd->data + read_size, 5437d47aad4SAlexander Duyck read_size, label_read_size); 5447d47aad4SAlexander Duyck if (rc) 5457d47aad4SAlexander Duyck goto out_err; 5467d47aad4SAlexander Duyck 5477d47aad4SAlexander Duyck /* push read_size to next read offset */ 5487d47aad4SAlexander Duyck read_size += label_read_size; 5497d47aad4SAlexander Duyck } 5507d47aad4SAlexander Duyck 5517d47aad4SAlexander Duyck dev_dbg(ndd->dev, "len: %zu rc: %d\n", offset, rc); 5527d47aad4SAlexander Duyck out_err: 5537d47aad4SAlexander Duyck return rc; 5542d657d17SAlexander Duyck } 5552d657d17SAlexander Duyck 556bf9bccc1SDan Williams int nd_label_active_count(struct nvdimm_drvdata *ndd) 557bf9bccc1SDan Williams { 558bf9bccc1SDan Williams struct nd_namespace_index *nsindex; 559bf9bccc1SDan Williams unsigned long *free; 560bf9bccc1SDan Williams u32 nslot, slot; 561bf9bccc1SDan Williams int count = 0; 562bf9bccc1SDan Williams 563bf9bccc1SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 564bf9bccc1SDan Williams return 0; 565bf9bccc1SDan Williams 566bf9bccc1SDan Williams for_each_clear_bit_le(slot, free, nslot) { 567bf9bccc1SDan Williams struct nd_namespace_label *nd_label; 568bf9bccc1SDan Williams 569564e871aSDan Williams nd_label = to_label(ndd, slot); 570bf9bccc1SDan Williams 571355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) { 572b4366a82SDan Williams u32 label_slot = nsl_get_slot(ndd, nd_label); 573b4366a82SDan Williams u64 size = nsl_get_rawsize(ndd, nd_label); 574b4366a82SDan Williams u64 dpa = nsl_get_dpa(ndd, nd_label); 575bf9bccc1SDan Williams 576bf9bccc1SDan Williams dev_dbg(ndd->dev, 577426824d6SDan Williams "slot%d invalid slot: %d dpa: %llx size: %llx\n", 578426824d6SDan Williams slot, label_slot, dpa, size); 579bf9bccc1SDan Williams continue; 580bf9bccc1SDan Williams } 581bf9bccc1SDan Williams count++; 582bf9bccc1SDan Williams } 583bf9bccc1SDan Williams return count; 584bf9bccc1SDan Williams } 585bf9bccc1SDan Williams 586bf9bccc1SDan Williams struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n) 587bf9bccc1SDan Williams { 588bf9bccc1SDan Williams struct nd_namespace_index *nsindex; 589bf9bccc1SDan Williams unsigned long *free; 590bf9bccc1SDan Williams u32 nslot, slot; 591bf9bccc1SDan Williams 592bf9bccc1SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 593bf9bccc1SDan Williams return NULL; 594bf9bccc1SDan Williams 595bf9bccc1SDan Williams for_each_clear_bit_le(slot, free, nslot) { 596bf9bccc1SDan Williams struct nd_namespace_label *nd_label; 597bf9bccc1SDan Williams 598564e871aSDan Williams nd_label = to_label(ndd, slot); 599355d8388SDan Williams if (!slot_valid(ndd, nd_label, slot)) 600bf9bccc1SDan Williams continue; 601bf9bccc1SDan Williams 602bf9bccc1SDan Williams if (n-- == 0) 603564e871aSDan Williams return to_label(ndd, slot); 604bf9bccc1SDan Williams } 605bf9bccc1SDan Williams 606bf9bccc1SDan Williams return NULL; 607bf9bccc1SDan Williams } 608f524bf27SDan Williams 6090ba1c634SDan Williams u32 nd_label_alloc_slot(struct nvdimm_drvdata *ndd) 610f524bf27SDan Williams { 611f524bf27SDan Williams struct nd_namespace_index *nsindex; 612f524bf27SDan Williams unsigned long *free; 613f524bf27SDan Williams u32 nslot, slot; 614f524bf27SDan Williams 615f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 616f524bf27SDan Williams return UINT_MAX; 617f524bf27SDan Williams 618f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 619f524bf27SDan Williams 620f524bf27SDan Williams slot = find_next_bit_le(free, nslot, 0); 621f524bf27SDan Williams if (slot == nslot) 622f524bf27SDan Williams return UINT_MAX; 623f524bf27SDan Williams 624f524bf27SDan Williams clear_bit_le(slot, free); 625f524bf27SDan Williams 626f524bf27SDan Williams return slot; 627f524bf27SDan Williams } 628f524bf27SDan Williams 6290ba1c634SDan Williams bool nd_label_free_slot(struct nvdimm_drvdata *ndd, u32 slot) 630f524bf27SDan Williams { 631f524bf27SDan Williams struct nd_namespace_index *nsindex; 632f524bf27SDan Williams unsigned long *free; 633f524bf27SDan Williams u32 nslot; 634f524bf27SDan Williams 635f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 636f524bf27SDan Williams return false; 637f524bf27SDan Williams 638f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 639f524bf27SDan Williams 640f524bf27SDan Williams if (slot < nslot) 641f524bf27SDan Williams return !test_and_set_bit_le(slot, free); 642f524bf27SDan Williams return false; 643f524bf27SDan Williams } 644f524bf27SDan Williams 645f524bf27SDan Williams u32 nd_label_nfree(struct nvdimm_drvdata *ndd) 646f524bf27SDan Williams { 647f524bf27SDan Williams struct nd_namespace_index *nsindex; 648f524bf27SDan Williams unsigned long *free; 649f524bf27SDan Williams u32 nslot; 650f524bf27SDan Williams 651f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 652f524bf27SDan Williams 653f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 6540ba1c634SDan Williams return nvdimm_num_label_slots(ndd); 655f524bf27SDan Williams 656f524bf27SDan Williams return bitmap_weight(free, nslot); 657f524bf27SDan Williams } 658f524bf27SDan Williams 659f524bf27SDan Williams static int nd_label_write_index(struct nvdimm_drvdata *ndd, int index, u32 seq, 660f524bf27SDan Williams unsigned long flags) 661f524bf27SDan Williams { 662f524bf27SDan Williams struct nd_namespace_index *nsindex; 663f524bf27SDan Williams unsigned long offset; 664f524bf27SDan Williams u64 checksum; 665f524bf27SDan Williams u32 nslot; 666f524bf27SDan Williams int rc; 667f524bf27SDan Williams 668f524bf27SDan Williams nsindex = to_namespace_index(ndd, index); 669f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) 670f524bf27SDan Williams nslot = nvdimm_num_label_slots(ndd); 671f524bf27SDan Williams else 672f524bf27SDan Williams nslot = __le32_to_cpu(nsindex->nslot); 673f524bf27SDan Williams 674f524bf27SDan Williams memcpy(nsindex->sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN); 675564e871aSDan Williams memset(&nsindex->flags, 0, 3); 676564e871aSDan Williams nsindex->labelsize = sizeof_namespace_label(ndd) >> 8; 677f524bf27SDan Williams nsindex->seq = __cpu_to_le32(seq); 678f524bf27SDan Williams offset = (unsigned long) nsindex 679f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 680f524bf27SDan Williams nsindex->myoff = __cpu_to_le64(offset); 681f524bf27SDan Williams nsindex->mysize = __cpu_to_le64(sizeof_namespace_index(ndd)); 682f524bf27SDan Williams offset = (unsigned long) to_namespace_index(ndd, 683f524bf27SDan Williams nd_label_next_nsindex(index)) 684f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 685f524bf27SDan Williams nsindex->otheroff = __cpu_to_le64(offset); 686f524bf27SDan Williams offset = (unsigned long) nd_label_base(ndd) 687f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 688f524bf27SDan Williams nsindex->labeloff = __cpu_to_le64(offset); 689f524bf27SDan Williams nsindex->nslot = __cpu_to_le32(nslot); 690f524bf27SDan Williams nsindex->major = __cpu_to_le16(1); 6918990cdf1SDan Williams if (sizeof_namespace_label(ndd) < 256) 692f524bf27SDan Williams nsindex->minor = __cpu_to_le16(1); 6938990cdf1SDan Williams else 6948990cdf1SDan Williams nsindex->minor = __cpu_to_le16(2); 695f524bf27SDan Williams nsindex->checksum = __cpu_to_le64(0); 696f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) { 697f524bf27SDan Williams unsigned long *free = (unsigned long *) nsindex->free; 698f524bf27SDan Williams u32 nfree = ALIGN(nslot, BITS_PER_LONG); 699f524bf27SDan Williams int last_bits, i; 700f524bf27SDan Williams 701f524bf27SDan Williams memset(nsindex->free, 0xff, nfree / 8); 702f524bf27SDan Williams for (i = 0, last_bits = nfree - nslot; i < last_bits; i++) 703f524bf27SDan Williams clear_bit_le(nslot + i, free); 704f524bf27SDan Williams } 705f524bf27SDan Williams checksum = nd_fletcher64(nsindex, sizeof_namespace_index(ndd), 1); 706f524bf27SDan Williams nsindex->checksum = __cpu_to_le64(checksum); 707f524bf27SDan Williams rc = nvdimm_set_config_data(ndd, __le64_to_cpu(nsindex->myoff), 708f524bf27SDan Williams nsindex, sizeof_namespace_index(ndd)); 709f524bf27SDan Williams if (rc < 0) 710f524bf27SDan Williams return rc; 711f524bf27SDan Williams 712f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) 713f524bf27SDan Williams return 0; 714f524bf27SDan Williams 715f524bf27SDan Williams /* copy the index we just wrote to the new 'next' */ 716f524bf27SDan Williams WARN_ON(index != ndd->ns_next); 717f524bf27SDan Williams nd_label_copy(ndd, to_current_namespace_index(ndd), nsindex); 718f524bf27SDan Williams ndd->ns_current = nd_label_next_nsindex(ndd->ns_current); 719f524bf27SDan Williams ndd->ns_next = nd_label_next_nsindex(ndd->ns_next); 720f524bf27SDan Williams WARN_ON(ndd->ns_current == ndd->ns_next); 721f524bf27SDan Williams 722f524bf27SDan Williams return 0; 723f524bf27SDan Williams } 724f524bf27SDan Williams 725f524bf27SDan Williams static unsigned long nd_label_offset(struct nvdimm_drvdata *ndd, 726f524bf27SDan Williams struct nd_namespace_label *nd_label) 727f524bf27SDan Williams { 728f524bf27SDan Williams return (unsigned long) nd_label 729f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 730f524bf27SDan Williams } 731f524bf27SDan Williams 7325af96835SDan Williams static enum nvdimm_claim_class guid_to_nvdimm_cclass(guid_t *guid) 733b3fde74eSDan Williams { 734b3fde74eSDan Williams if (guid_equal(guid, &nvdimm_btt_guid)) 735b3fde74eSDan Williams return NVDIMM_CCLASS_BTT; 73614e49454SVishal Verma else if (guid_equal(guid, &nvdimm_btt2_guid)) 73714e49454SVishal Verma return NVDIMM_CCLASS_BTT2; 738b3fde74eSDan Williams else if (guid_equal(guid, &nvdimm_pfn_guid)) 739b3fde74eSDan Williams return NVDIMM_CCLASS_PFN; 740b3fde74eSDan Williams else if (guid_equal(guid, &nvdimm_dax_guid)) 741b3fde74eSDan Williams return NVDIMM_CCLASS_DAX; 742b3fde74eSDan Williams else if (guid_equal(guid, &guid_null)) 743b3fde74eSDan Williams return NVDIMM_CCLASS_NONE; 744b3fde74eSDan Williams 745b3fde74eSDan Williams return NVDIMM_CCLASS_UNKNOWN; 746b3fde74eSDan Williams } 747b3fde74eSDan Williams 7485af96835SDan Williams /* CXL labels store UUIDs instead of GUIDs for the same data */ 7495af96835SDan Williams static enum nvdimm_claim_class uuid_to_nvdimm_cclass(uuid_t *uuid) 7505af96835SDan Williams { 7515af96835SDan Williams if (uuid_equal(uuid, &nvdimm_btt_uuid)) 7525af96835SDan Williams return NVDIMM_CCLASS_BTT; 7535af96835SDan Williams else if (uuid_equal(uuid, &nvdimm_btt2_uuid)) 7545af96835SDan Williams return NVDIMM_CCLASS_BTT2; 7555af96835SDan Williams else if (uuid_equal(uuid, &nvdimm_pfn_uuid)) 7565af96835SDan Williams return NVDIMM_CCLASS_PFN; 7575af96835SDan Williams else if (uuid_equal(uuid, &nvdimm_dax_uuid)) 7585af96835SDan Williams return NVDIMM_CCLASS_DAX; 7595af96835SDan Williams else if (uuid_equal(uuid, &uuid_null)) 7605af96835SDan Williams return NVDIMM_CCLASS_NONE; 7615af96835SDan Williams 7625af96835SDan Williams return NVDIMM_CCLASS_UNKNOWN; 7635af96835SDan Williams } 7645af96835SDan Williams 765b3fde74eSDan Williams static const guid_t *to_abstraction_guid(enum nvdimm_claim_class claim_class, 766b3fde74eSDan Williams guid_t *target) 767b3fde74eSDan Williams { 768b3fde74eSDan Williams if (claim_class == NVDIMM_CCLASS_BTT) 769b3fde74eSDan Williams return &nvdimm_btt_guid; 77014e49454SVishal Verma else if (claim_class == NVDIMM_CCLASS_BTT2) 77114e49454SVishal Verma return &nvdimm_btt2_guid; 772b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_PFN) 773b3fde74eSDan Williams return &nvdimm_pfn_guid; 774b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_DAX) 775b3fde74eSDan Williams return &nvdimm_dax_guid; 776b3fde74eSDan Williams else if (claim_class == NVDIMM_CCLASS_UNKNOWN) { 777b3fde74eSDan Williams /* 778b3fde74eSDan Williams * If we're modifying a namespace for which we don't 779b3fde74eSDan Williams * know the claim_class, don't touch the existing guid. 780b3fde74eSDan Williams */ 781b3fde74eSDan Williams return target; 782b3fde74eSDan Williams } else 783b3fde74eSDan Williams return &guid_null; 784b3fde74eSDan Williams } 785b3fde74eSDan Williams 7865af96835SDan Williams /* CXL labels store UUIDs instead of GUIDs for the same data */ 7875af96835SDan Williams static const uuid_t *to_abstraction_uuid(enum nvdimm_claim_class claim_class, 7885af96835SDan Williams uuid_t *target) 7895af96835SDan Williams { 7905af96835SDan Williams if (claim_class == NVDIMM_CCLASS_BTT) 7915af96835SDan Williams return &nvdimm_btt_uuid; 7925af96835SDan Williams else if (claim_class == NVDIMM_CCLASS_BTT2) 7935af96835SDan Williams return &nvdimm_btt2_uuid; 7945af96835SDan Williams else if (claim_class == NVDIMM_CCLASS_PFN) 7955af96835SDan Williams return &nvdimm_pfn_uuid; 7965af96835SDan Williams else if (claim_class == NVDIMM_CCLASS_DAX) 7975af96835SDan Williams return &nvdimm_dax_uuid; 7985af96835SDan Williams else if (claim_class == NVDIMM_CCLASS_UNKNOWN) { 7995af96835SDan Williams /* 8005af96835SDan Williams * If we're modifying a namespace for which we don't 8015af96835SDan Williams * know the claim_class, don't touch the existing uuid. 8025af96835SDan Williams */ 8035af96835SDan Williams return target; 8045af96835SDan Williams } else 8055af96835SDan Williams return &uuid_null; 8065af96835SDan Williams } 8075af96835SDan Williams 808c4703ce1SDan Williams static void reap_victim(struct nd_mapping *nd_mapping, 809c4703ce1SDan Williams struct nd_label_ent *victim) 810c4703ce1SDan Williams { 811c4703ce1SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 812c4703ce1SDan Williams u32 slot = to_slot(ndd, victim->label); 813c4703ce1SDan Williams 814c4703ce1SDan Williams dev_dbg(ndd->dev, "free: %d\n", slot); 815c4703ce1SDan Williams nd_label_free_slot(ndd, slot); 816c4703ce1SDan Williams victim->label = NULL; 817c4703ce1SDan Williams } 818c4703ce1SDan Williams 8198b03aa0eSDan Williams static void nsl_set_type_guid(struct nvdimm_drvdata *ndd, 8208b03aa0eSDan Williams struct nd_namespace_label *nd_label, guid_t *guid) 8218b03aa0eSDan Williams { 8225af96835SDan Williams if (efi_namespace_label_has(ndd, type_guid)) 8235af96835SDan Williams guid_copy(&nd_label->efi.type_guid, guid); 8248b03aa0eSDan Williams } 8258b03aa0eSDan Williams 8268b03aa0eSDan Williams bool nsl_validate_type_guid(struct nvdimm_drvdata *ndd, 8278b03aa0eSDan Williams struct nd_namespace_label *nd_label, guid_t *guid) 8288b03aa0eSDan Williams { 8295af96835SDan Williams if (ndd->cxl || !efi_namespace_label_has(ndd, type_guid)) 8308b03aa0eSDan Williams return true; 8315af96835SDan Williams if (!guid_equal(&nd_label->efi.type_guid, guid)) { 8328b03aa0eSDan Williams dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb\n", guid, 8335af96835SDan Williams &nd_label->efi.type_guid); 8348b03aa0eSDan Williams return false; 8358b03aa0eSDan Williams } 8368b03aa0eSDan Williams return true; 8378b03aa0eSDan Williams } 8388b03aa0eSDan Williams 839a6e6d722SDan Williams static void nsl_set_claim_class(struct nvdimm_drvdata *ndd, 840a6e6d722SDan Williams struct nd_namespace_label *nd_label, 841a6e6d722SDan Williams enum nvdimm_claim_class claim_class) 842a6e6d722SDan Williams { 8435af96835SDan Williams if (ndd->cxl) { 8445af96835SDan Williams uuid_t uuid; 8455af96835SDan Williams 8465af96835SDan Williams import_uuid(&uuid, nd_label->cxl.abstraction_uuid); 8475af96835SDan Williams export_uuid(nd_label->cxl.abstraction_uuid, 8485af96835SDan Williams to_abstraction_uuid(claim_class, &uuid)); 849a6e6d722SDan Williams return; 8505af96835SDan Williams } 8515af96835SDan Williams 8525af96835SDan Williams if (!efi_namespace_label_has(ndd, abstraction_guid)) 8535af96835SDan Williams return; 8545af96835SDan Williams guid_copy(&nd_label->efi.abstraction_guid, 855a6e6d722SDan Williams to_abstraction_guid(claim_class, 8565af96835SDan Williams &nd_label->efi.abstraction_guid)); 857a6e6d722SDan Williams } 858a6e6d722SDan Williams 859a6e6d722SDan Williams enum nvdimm_claim_class nsl_get_claim_class(struct nvdimm_drvdata *ndd, 860a6e6d722SDan Williams struct nd_namespace_label *nd_label) 861a6e6d722SDan Williams { 8625af96835SDan Williams if (ndd->cxl) { 8635af96835SDan Williams uuid_t uuid; 8645af96835SDan Williams 8655af96835SDan Williams import_uuid(&uuid, nd_label->cxl.abstraction_uuid); 8665af96835SDan Williams return uuid_to_nvdimm_cclass(&uuid); 8675af96835SDan Williams } 8685af96835SDan Williams if (!efi_namespace_label_has(ndd, abstraction_guid)) 869a6e6d722SDan Williams return NVDIMM_CCLASS_NONE; 8705af96835SDan Williams return guid_to_nvdimm_cclass(&nd_label->efi.abstraction_guid); 871a6e6d722SDan Williams } 872a6e6d722SDan Williams 873f524bf27SDan Williams static int __pmem_label_update(struct nd_region *nd_region, 874f524bf27SDan Williams struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm, 875966d23a0SDan Williams int pos, unsigned long flags) 876f524bf27SDan Williams { 877b3fde74eSDan Williams struct nd_namespace_common *ndns = &nspm->nsio.common; 878faec6f8aSDan Williams struct nd_interleave_set *nd_set = nd_region->nd_set; 879f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 880f524bf27SDan Williams struct nd_namespace_label *nd_label; 881f524bf27SDan Williams struct nd_namespace_index *nsindex; 882c4703ce1SDan Williams struct nd_label_ent *label_ent; 88316660eaeSDan Williams struct nd_label_id label_id; 88416660eaeSDan Williams struct resource *res; 885f524bf27SDan Williams unsigned long *free; 886f524bf27SDan Williams u32 nslot, slot; 887f524bf27SDan Williams size_t offset; 888c12c48ceSDan Williams u64 cookie; 889f524bf27SDan Williams int rc; 890f524bf27SDan Williams 891f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 892f524bf27SDan Williams return -ENXIO; 893f524bf27SDan Williams 894c12c48ceSDan Williams cookie = nd_region_interleave_set_cookie(nd_region, nsindex); 89516660eaeSDan Williams nd_label_gen_id(&label_id, nspm->uuid, 0); 89616660eaeSDan Williams for_each_dpa_resource(ndd, res) 89716660eaeSDan Williams if (strcmp(res->name, label_id.id) == 0) 89816660eaeSDan Williams break; 89916660eaeSDan Williams 90016660eaeSDan Williams if (!res) { 90116660eaeSDan Williams WARN_ON_ONCE(1); 90216660eaeSDan Williams return -ENXIO; 90316660eaeSDan Williams } 90416660eaeSDan Williams 905f524bf27SDan Williams /* allocate and write the label to the staging (next) index */ 906f524bf27SDan Williams slot = nd_label_alloc_slot(ndd); 907f524bf27SDan Williams if (slot == UINT_MAX) 908f524bf27SDan Williams return -ENXIO; 909426824d6SDan Williams dev_dbg(ndd->dev, "allocated: %d\n", slot); 910f524bf27SDan Williams 911564e871aSDan Williams nd_label = to_label(ndd, slot); 912564e871aSDan Williams memset(nd_label, 0, sizeof_namespace_label(ndd)); 913d1c6e08eSDan Williams nsl_set_uuid(ndd, nd_label, nspm->uuid); 9148176f147SDan Williams nsl_set_name(ndd, nd_label, nspm->alt_name); 9158176f147SDan Williams nsl_set_flags(ndd, nd_label, flags); 9168176f147SDan Williams nsl_set_nlabel(ndd, nd_label, nd_region->ndr_mappings); 91742e192aaSDan Williams nsl_set_nrange(ndd, nd_label, 1); 9188176f147SDan Williams nsl_set_position(ndd, nd_label, pos); 9198176f147SDan Williams nsl_set_isetcookie(ndd, nd_label, cookie); 9208176f147SDan Williams nsl_set_rawsize(ndd, nd_label, resource_size(res)); 9218176f147SDan Williams nsl_set_lbasize(ndd, nd_label, nspm->lbasize); 9228176f147SDan Williams nsl_set_dpa(ndd, nd_label, res->start); 9238176f147SDan Williams nsl_set_slot(ndd, nd_label, slot); 9248b03aa0eSDan Williams nsl_set_type_guid(ndd, nd_label, &nd_set->type_guid); 925a6e6d722SDan Williams nsl_set_claim_class(ndd, nd_label, ndns->claim_class); 9267cd35b29SDan Williams nsl_calculate_checksum(ndd, nd_label); 927426824d6SDan Williams nd_dbg_dpa(nd_region, ndd, res, "\n"); 928f524bf27SDan Williams 929f524bf27SDan Williams /* update label */ 930f524bf27SDan Williams offset = nd_label_offset(ndd, nd_label); 931f524bf27SDan Williams rc = nvdimm_set_config_data(ndd, offset, nd_label, 932564e871aSDan Williams sizeof_namespace_label(ndd)); 933f524bf27SDan Williams if (rc < 0) 934f524bf27SDan Williams return rc; 935f524bf27SDan Williams 936f524bf27SDan Williams /* Garbage collect the previous label */ 937ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 93816660eaeSDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) { 93916660eaeSDan Williams if (!label_ent->label) 94016660eaeSDan Williams continue; 941d1c6e08eSDan Williams if (test_and_clear_bit(ND_LABEL_REAP, &label_ent->flags) || 942d1c6e08eSDan Williams nsl_uuid_equal(ndd, label_ent->label, nspm->uuid)) 943c4703ce1SDan Williams reap_victim(nd_mapping, label_ent); 944f524bf27SDan Williams } 945f524bf27SDan Williams 946f524bf27SDan Williams /* update index */ 947f524bf27SDan Williams rc = nd_label_write_index(ndd, ndd->ns_next, 948f524bf27SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 94916660eaeSDan Williams if (rc == 0) { 95016660eaeSDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) 95116660eaeSDan Williams if (!label_ent->label) { 952ae8219f1SDan Williams label_ent->label = nd_label; 95316660eaeSDan Williams nd_label = NULL; 95416660eaeSDan Williams break; 95516660eaeSDan Williams } 95616660eaeSDan Williams dev_WARN_ONCE(&nspm->nsio.common.dev, nd_label, 95716660eaeSDan Williams "failed to track label: %d\n", 95816660eaeSDan Williams to_slot(ndd, nd_label)); 95916660eaeSDan Williams if (nd_label) 96016660eaeSDan Williams rc = -ENXIO; 96116660eaeSDan Williams } 962ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 963ae8219f1SDan Williams 964f524bf27SDan Williams return rc; 9650ba1c634SDan Williams } 9660ba1c634SDan Williams 9670ba1c634SDan Williams static int init_labels(struct nd_mapping *nd_mapping, int num_labels) 9680ba1c634SDan Williams { 969ae8219f1SDan Williams int i, old_num_labels = 0; 970ae8219f1SDan Williams struct nd_label_ent *label_ent; 9710ba1c634SDan Williams struct nd_namespace_index *nsindex; 9720ba1c634SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 9730ba1c634SDan Williams 974ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 975ae8219f1SDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) 9760ba1c634SDan Williams old_num_labels++; 977ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 9780ba1c634SDan Williams 9790ba1c634SDan Williams /* 9800ba1c634SDan Williams * We need to preserve all the old labels for the mapping so 9810ba1c634SDan Williams * they can be garbage collected after writing the new labels. 9820ba1c634SDan Williams */ 983ae8219f1SDan Williams for (i = old_num_labels; i < num_labels; i++) { 984ae8219f1SDan Williams label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL); 985ae8219f1SDan Williams if (!label_ent) 9860ba1c634SDan Williams return -ENOMEM; 987ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 988ae8219f1SDan Williams list_add_tail(&label_ent->list, &nd_mapping->labels); 989ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 9900ba1c634SDan Williams } 9910ba1c634SDan Williams 992f524bf27SDan Williams if (ndd->ns_current == -1 || ndd->ns_next == -1) 993f524bf27SDan Williams /* pass */; 994f524bf27SDan Williams else 9950ba1c634SDan Williams return max(num_labels, old_num_labels); 996f524bf27SDan Williams 997f524bf27SDan Williams nsindex = to_namespace_index(ndd, 0); 998f524bf27SDan Williams memset(nsindex, 0, ndd->nsarea.config_size); 999f524bf27SDan Williams for (i = 0; i < 2; i++) { 1000b18d4b8aSDan Williams int rc = nd_label_write_index(ndd, i, 3 - i, ND_NSINDEX_INIT); 1001f524bf27SDan Williams 1002f524bf27SDan Williams if (rc) 1003f524bf27SDan Williams return rc; 1004f524bf27SDan Williams } 1005f524bf27SDan Williams ndd->ns_next = 1; 1006f524bf27SDan Williams ndd->ns_current = 0; 1007f524bf27SDan Williams 10080ba1c634SDan Williams return max(num_labels, old_num_labels); 1009f524bf27SDan Williams } 1010f524bf27SDan Williams 1011d1c6e08eSDan Williams static int del_labels(struct nd_mapping *nd_mapping, uuid_t *uuid) 1012f524bf27SDan Williams { 1013f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 1014ae8219f1SDan Williams struct nd_label_ent *label_ent, *e; 1015f524bf27SDan Williams struct nd_namespace_index *nsindex; 1016f524bf27SDan Williams unsigned long *free; 1017ae8219f1SDan Williams LIST_HEAD(list); 1018f524bf27SDan Williams u32 nslot, slot; 1019ae8219f1SDan Williams int active = 0; 1020f524bf27SDan Williams 1021f524bf27SDan Williams if (!uuid) 1022f524bf27SDan Williams return 0; 1023f524bf27SDan Williams 1024f524bf27SDan Williams /* no index || no labels == nothing to delete */ 1025ae8219f1SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 1026f524bf27SDan Williams return 0; 1027f524bf27SDan Williams 1028ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 1029ae8219f1SDan Williams list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { 1030ae8219f1SDan Williams struct nd_namespace_label *nd_label = label_ent->label; 1031ae8219f1SDan Williams 1032ae8219f1SDan Williams if (!nd_label) 1033ae8219f1SDan Williams continue; 1034ae8219f1SDan Williams active++; 1035d1c6e08eSDan Williams if (!nsl_uuid_equal(ndd, nd_label, uuid)) 1036f524bf27SDan Williams continue; 1037ae8219f1SDan Williams active--; 1038f524bf27SDan Williams slot = to_slot(ndd, nd_label); 1039f524bf27SDan Williams nd_label_free_slot(ndd, slot); 1040426824d6SDan Williams dev_dbg(ndd->dev, "free: %d\n", slot); 1041ae8219f1SDan Williams list_move_tail(&label_ent->list, &list); 1042ae8219f1SDan Williams label_ent->label = NULL; 1043f524bf27SDan Williams } 1044ae8219f1SDan Williams list_splice_tail_init(&list, &nd_mapping->labels); 1045f524bf27SDan Williams 1046ae8219f1SDan Williams if (active == 0) { 1047ae8219f1SDan Williams nd_mapping_free_labels(nd_mapping); 1048426824d6SDan Williams dev_dbg(ndd->dev, "no more active labels\n"); 1049f524bf27SDan Williams } 1050ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 1051f524bf27SDan Williams 1052f524bf27SDan Williams return nd_label_write_index(ndd, ndd->ns_next, 1053f524bf27SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 1054f524bf27SDan Williams } 1055f524bf27SDan Williams 1056f524bf27SDan Williams int nd_pmem_namespace_label_update(struct nd_region *nd_region, 1057f524bf27SDan Williams struct nd_namespace_pmem *nspm, resource_size_t size) 1058f524bf27SDan Williams { 1059966d23a0SDan Williams int i, rc; 1060f524bf27SDan Williams 1061f524bf27SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 1062f524bf27SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[i]; 106316660eaeSDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 106416660eaeSDan Williams struct resource *res; 1065966d23a0SDan Williams int count = 0; 1066f524bf27SDan Williams 1067f524bf27SDan Williams if (size == 0) { 1068f524bf27SDan Williams rc = del_labels(nd_mapping, nspm->uuid); 1069f524bf27SDan Williams if (rc) 1070f524bf27SDan Williams return rc; 1071f524bf27SDan Williams continue; 1072f524bf27SDan Williams } 1073f524bf27SDan Williams 107416660eaeSDan Williams for_each_dpa_resource(ndd, res) 10752d9a0274SNicolas Iooss if (strncmp(res->name, "pmem", 4) == 0) 107616660eaeSDan Williams count++; 107716660eaeSDan Williams WARN_ON_ONCE(!count); 107816660eaeSDan Williams 107916660eaeSDan Williams rc = init_labels(nd_mapping, count); 10800ba1c634SDan Williams if (rc < 0) 1081f524bf27SDan Williams return rc; 1082f524bf27SDan Williams 1083966d23a0SDan Williams rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 1084966d23a0SDan Williams NSLABEL_FLAG_UPDATING); 1085966d23a0SDan Williams if (rc) 1086966d23a0SDan Williams return rc; 1087966d23a0SDan Williams } 1088966d23a0SDan Williams 1089966d23a0SDan Williams if (size == 0) 1090966d23a0SDan Williams return 0; 1091966d23a0SDan Williams 1092966d23a0SDan Williams /* Clear the UPDATING flag per UEFI 2.7 expectations */ 1093966d23a0SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 1094966d23a0SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[i]; 1095966d23a0SDan Williams 1096966d23a0SDan Williams rc = __pmem_label_update(nd_region, nd_mapping, nspm, i, 0); 1097f524bf27SDan Williams if (rc) 1098f524bf27SDan Williams return rc; 1099f524bf27SDan Williams } 1100f524bf27SDan Williams 1101f524bf27SDan Williams return 0; 1102f524bf27SDan Williams } 11030ba1c634SDan Williams 1104b3fde74eSDan Williams int __init nd_label_init(void) 1105b3fde74eSDan Williams { 1106b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_BTT_GUID, &nvdimm_btt_guid)); 110714e49454SVishal Verma WARN_ON(guid_parse(NVDIMM_BTT2_GUID, &nvdimm_btt2_guid)); 1108b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_guid)); 1109b3fde74eSDan Williams WARN_ON(guid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_guid)); 1110b3fde74eSDan Williams 11115af96835SDan Williams WARN_ON(uuid_parse(NVDIMM_BTT_GUID, &nvdimm_btt_uuid)); 11125af96835SDan Williams WARN_ON(uuid_parse(NVDIMM_BTT2_GUID, &nvdimm_btt2_uuid)); 11135af96835SDan Williams WARN_ON(uuid_parse(NVDIMM_PFN_GUID, &nvdimm_pfn_uuid)); 11145af96835SDan Williams WARN_ON(uuid_parse(NVDIMM_DAX_GUID, &nvdimm_dax_uuid)); 11155af96835SDan Williams 11165af96835SDan Williams WARN_ON(uuid_parse(CXL_REGION_UUID, &cxl_region_uuid)); 11175af96835SDan Williams WARN_ON(uuid_parse(CXL_NAMESPACE_UUID, &cxl_namespace_uuid)); 11185af96835SDan Williams 1119b3fde74eSDan Williams return 0; 1120b3fde74eSDan Williams } 1121