14a826c83SDan Williams /* 24a826c83SDan Williams * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 34a826c83SDan Williams * 44a826c83SDan Williams * This program is free software; you can redistribute it and/or modify 54a826c83SDan Williams * it under the terms of version 2 of the GNU General Public License as 64a826c83SDan Williams * published by the Free Software Foundation. 74a826c83SDan Williams * 84a826c83SDan Williams * This program is distributed in the hope that it will be useful, but 94a826c83SDan Williams * WITHOUT ANY WARRANTY; without even the implied warranty of 104a826c83SDan Williams * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 114a826c83SDan Williams * General Public License for more details. 124a826c83SDan Williams */ 134a826c83SDan Williams #include <linux/device.h> 144a826c83SDan Williams #include <linux/ndctl.h> 15f524bf27SDan Williams #include <linux/slab.h> 164a826c83SDan Williams #include <linux/io.h> 174a826c83SDan Williams #include <linux/nd.h> 184a826c83SDan Williams #include "nd-core.h" 194a826c83SDan Williams #include "label.h" 204a826c83SDan Williams #include "nd.h" 214a826c83SDan Williams 224a826c83SDan Williams static u32 best_seq(u32 a, u32 b) 234a826c83SDan Williams { 244a826c83SDan Williams a &= NSINDEX_SEQ_MASK; 254a826c83SDan Williams b &= NSINDEX_SEQ_MASK; 264a826c83SDan Williams 274a826c83SDan Williams if (a == 0 || a == b) 284a826c83SDan Williams return b; 294a826c83SDan Williams else if (b == 0) 304a826c83SDan Williams return a; 314a826c83SDan Williams else if (nd_inc_seq(a) == b) 324a826c83SDan Williams return b; 334a826c83SDan Williams else 344a826c83SDan Williams return a; 354a826c83SDan Williams } 364a826c83SDan Williams 374a826c83SDan Williams size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd) 384a826c83SDan Williams { 394a826c83SDan Williams u32 index_span; 404a826c83SDan Williams 414a826c83SDan Williams if (ndd->nsindex_size) 424a826c83SDan Williams return ndd->nsindex_size; 434a826c83SDan Williams 444a826c83SDan Williams /* 454a826c83SDan Williams * The minimum index space is 512 bytes, with that amount of 464a826c83SDan Williams * index we can describe ~1400 labels which is less than a byte 474a826c83SDan Williams * of overhead per label. Round up to a byte of overhead per 484a826c83SDan Williams * label and determine the size of the index region. Yes, this 494a826c83SDan Williams * starts to waste space at larger config_sizes, but it's 504a826c83SDan Williams * unlikely we'll ever see anything but 128K. 514a826c83SDan Williams */ 524a826c83SDan Williams index_span = ndd->nsarea.config_size / 129; 534a826c83SDan Williams index_span /= NSINDEX_ALIGN * 2; 544a826c83SDan Williams ndd->nsindex_size = index_span * NSINDEX_ALIGN; 554a826c83SDan Williams 564a826c83SDan Williams return ndd->nsindex_size; 574a826c83SDan Williams } 584a826c83SDan Williams 590ba1c634SDan Williams int nvdimm_num_label_slots(struct nvdimm_drvdata *ndd) 60f524bf27SDan Williams { 61f524bf27SDan Williams return ndd->nsarea.config_size / 129; 62f524bf27SDan Williams } 63f524bf27SDan Williams 644a826c83SDan Williams int nd_label_validate(struct nvdimm_drvdata *ndd) 654a826c83SDan Williams { 664a826c83SDan Williams /* 674a826c83SDan Williams * On media label format consists of two index blocks followed 684a826c83SDan Williams * by an array of labels. None of these structures are ever 694a826c83SDan Williams * updated in place. A sequence number tracks the current 704a826c83SDan Williams * active index and the next one to write, while labels are 714a826c83SDan Williams * written to free slots. 724a826c83SDan Williams * 734a826c83SDan Williams * +------------+ 744a826c83SDan Williams * | | 754a826c83SDan Williams * | nsindex0 | 764a826c83SDan Williams * | | 774a826c83SDan Williams * +------------+ 784a826c83SDan Williams * | | 794a826c83SDan Williams * | nsindex1 | 804a826c83SDan Williams * | | 814a826c83SDan Williams * +------------+ 824a826c83SDan Williams * | label0 | 834a826c83SDan Williams * +------------+ 844a826c83SDan Williams * | label1 | 854a826c83SDan Williams * +------------+ 864a826c83SDan Williams * | | 874a826c83SDan Williams * ....nslot... 884a826c83SDan Williams * | | 894a826c83SDan Williams * +------------+ 904a826c83SDan Williams * | labelN | 914a826c83SDan Williams * +------------+ 924a826c83SDan Williams */ 934a826c83SDan Williams struct nd_namespace_index *nsindex[] = { 944a826c83SDan Williams to_namespace_index(ndd, 0), 954a826c83SDan Williams to_namespace_index(ndd, 1), 964a826c83SDan Williams }; 974a826c83SDan Williams const int num_index = ARRAY_SIZE(nsindex); 984a826c83SDan Williams struct device *dev = ndd->dev; 994a826c83SDan Williams bool valid[2] = { 0 }; 1004a826c83SDan Williams int i, num_valid = 0; 1014a826c83SDan Williams u32 seq; 1024a826c83SDan Williams 1034a826c83SDan Williams for (i = 0; i < num_index; i++) { 1044a826c83SDan Williams u32 nslot; 1054a826c83SDan Williams u8 sig[NSINDEX_SIG_LEN]; 1064a826c83SDan Williams u64 sum_save, sum, size; 1074a826c83SDan Williams 1084a826c83SDan Williams memcpy(sig, nsindex[i]->sig, NSINDEX_SIG_LEN); 1094a826c83SDan Williams if (memcmp(sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN) != 0) { 1104a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d signature invalid\n", 1114a826c83SDan Williams __func__, i); 1124a826c83SDan Williams continue; 1134a826c83SDan Williams } 1144a826c83SDan Williams sum_save = __le64_to_cpu(nsindex[i]->checksum); 1154a826c83SDan Williams nsindex[i]->checksum = __cpu_to_le64(0); 1164a826c83SDan Williams sum = nd_fletcher64(nsindex[i], sizeof_namespace_index(ndd), 1); 1174a826c83SDan Williams nsindex[i]->checksum = __cpu_to_le64(sum_save); 1184a826c83SDan Williams if (sum != sum_save) { 1194a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d checksum invalid\n", 1204a826c83SDan Williams __func__, i); 1214a826c83SDan Williams continue; 1224a826c83SDan Williams } 1234a826c83SDan Williams 1244a826c83SDan Williams seq = __le32_to_cpu(nsindex[i]->seq); 1254a826c83SDan Williams if ((seq & NSINDEX_SEQ_MASK) == 0) { 1264a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d sequence: %#x invalid\n", 1274a826c83SDan Williams __func__, i, seq); 1284a826c83SDan Williams continue; 1294a826c83SDan Williams } 1304a826c83SDan Williams 1314a826c83SDan Williams /* sanity check the index against expected values */ 1324a826c83SDan Williams if (__le64_to_cpu(nsindex[i]->myoff) 1334a826c83SDan Williams != i * sizeof_namespace_index(ndd)) { 1344a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d myoff: %#llx invalid\n", 1354a826c83SDan Williams __func__, i, (unsigned long long) 1364a826c83SDan Williams __le64_to_cpu(nsindex[i]->myoff)); 1374a826c83SDan Williams continue; 1384a826c83SDan Williams } 1394a826c83SDan Williams if (__le64_to_cpu(nsindex[i]->otheroff) 1404a826c83SDan Williams != (!i) * sizeof_namespace_index(ndd)) { 1414a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d otheroff: %#llx invalid\n", 1424a826c83SDan Williams __func__, i, (unsigned long long) 1434a826c83SDan Williams __le64_to_cpu(nsindex[i]->otheroff)); 1444a826c83SDan Williams continue; 1454a826c83SDan Williams } 1464a826c83SDan Williams 1474a826c83SDan Williams size = __le64_to_cpu(nsindex[i]->mysize); 1484a826c83SDan Williams if (size > sizeof_namespace_index(ndd) 1494a826c83SDan Williams || size < sizeof(struct nd_namespace_index)) { 1504a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d mysize: %#llx invalid\n", 1514a826c83SDan Williams __func__, i, size); 1524a826c83SDan Williams continue; 1534a826c83SDan Williams } 1544a826c83SDan Williams 1554a826c83SDan Williams nslot = __le32_to_cpu(nsindex[i]->nslot); 1564a826c83SDan Williams if (nslot * sizeof(struct nd_namespace_label) 1574a826c83SDan Williams + 2 * sizeof_namespace_index(ndd) 1584a826c83SDan Williams > ndd->nsarea.config_size) { 1594a826c83SDan Williams dev_dbg(dev, "%s: nsindex%d nslot: %u invalid, config_size: %#x\n", 1604a826c83SDan Williams __func__, i, nslot, 1614a826c83SDan Williams ndd->nsarea.config_size); 1624a826c83SDan Williams continue; 1634a826c83SDan Williams } 1644a826c83SDan Williams valid[i] = true; 1654a826c83SDan Williams num_valid++; 1664a826c83SDan Williams } 1674a826c83SDan Williams 1684a826c83SDan Williams switch (num_valid) { 1694a826c83SDan Williams case 0: 1704a826c83SDan Williams break; 1714a826c83SDan Williams case 1: 1724a826c83SDan Williams for (i = 0; i < num_index; i++) 1734a826c83SDan Williams if (valid[i]) 1744a826c83SDan Williams return i; 1754a826c83SDan Williams /* can't have num_valid > 0 but valid[] = { false, false } */ 1764a826c83SDan Williams WARN_ON(1); 1774a826c83SDan Williams break; 1784a826c83SDan Williams default: 1794a826c83SDan Williams /* pick the best index... */ 1804a826c83SDan Williams seq = best_seq(__le32_to_cpu(nsindex[0]->seq), 1814a826c83SDan Williams __le32_to_cpu(nsindex[1]->seq)); 1824a826c83SDan Williams if (seq == (__le32_to_cpu(nsindex[1]->seq) & NSINDEX_SEQ_MASK)) 1834a826c83SDan Williams return 1; 1844a826c83SDan Williams else 1854a826c83SDan Williams return 0; 1864a826c83SDan Williams break; 1874a826c83SDan Williams } 1884a826c83SDan Williams 1894a826c83SDan Williams return -1; 1904a826c83SDan Williams } 1914a826c83SDan Williams 1924a826c83SDan Williams void nd_label_copy(struct nvdimm_drvdata *ndd, struct nd_namespace_index *dst, 1934a826c83SDan Williams struct nd_namespace_index *src) 1944a826c83SDan Williams { 1954a826c83SDan Williams if (dst && src) 1964a826c83SDan Williams /* pass */; 1974a826c83SDan Williams else 1984a826c83SDan Williams return; 1994a826c83SDan Williams 2004a826c83SDan Williams memcpy(dst, src, sizeof_namespace_index(ndd)); 2014a826c83SDan Williams } 2024a826c83SDan Williams 2034a826c83SDan Williams static struct nd_namespace_label *nd_label_base(struct nvdimm_drvdata *ndd) 2044a826c83SDan Williams { 2054a826c83SDan Williams void *base = to_namespace_index(ndd, 0); 2064a826c83SDan Williams 2074a826c83SDan Williams return base + 2 * sizeof_namespace_index(ndd); 2084a826c83SDan Williams } 2094a826c83SDan Williams 210f524bf27SDan Williams static int to_slot(struct nvdimm_drvdata *ndd, 211f524bf27SDan Williams struct nd_namespace_label *nd_label) 212f524bf27SDan Williams { 213f524bf27SDan Williams return nd_label - nd_label_base(ndd); 214f524bf27SDan Williams } 215f524bf27SDan Williams 2164a826c83SDan Williams #define for_each_clear_bit_le(bit, addr, size) \ 2174a826c83SDan Williams for ((bit) = find_next_zero_bit_le((addr), (size), 0); \ 2184a826c83SDan Williams (bit) < (size); \ 2194a826c83SDan Williams (bit) = find_next_zero_bit_le((addr), (size), (bit) + 1)) 2204a826c83SDan Williams 2214a826c83SDan Williams /** 222f524bf27SDan Williams * preamble_index - common variable initialization for nd_label_* routines 2234a826c83SDan Williams * @ndd: dimm container for the relevant label set 224f524bf27SDan Williams * @idx: namespace_index index 2254a826c83SDan Williams * @nsindex_out: on return set to the currently active namespace index 2264a826c83SDan Williams * @free: on return set to the free label bitmap in the index 2274a826c83SDan Williams * @nslot: on return set to the number of slots in the label space 2284a826c83SDan Williams */ 229f524bf27SDan Williams static bool preamble_index(struct nvdimm_drvdata *ndd, int idx, 2304a826c83SDan Williams struct nd_namespace_index **nsindex_out, 2314a826c83SDan Williams unsigned long **free, u32 *nslot) 2324a826c83SDan Williams { 2334a826c83SDan Williams struct nd_namespace_index *nsindex; 2344a826c83SDan Williams 235f524bf27SDan Williams nsindex = to_namespace_index(ndd, idx); 2364a826c83SDan Williams if (nsindex == NULL) 2374a826c83SDan Williams return false; 2384a826c83SDan Williams 2394a826c83SDan Williams *free = (unsigned long *) nsindex->free; 2404a826c83SDan Williams *nslot = __le32_to_cpu(nsindex->nslot); 2414a826c83SDan Williams *nsindex_out = nsindex; 2424a826c83SDan Williams 2434a826c83SDan Williams return true; 2444a826c83SDan Williams } 2454a826c83SDan Williams 246bf9bccc1SDan Williams char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags) 2474a826c83SDan Williams { 2484a826c83SDan Williams if (!label_id || !uuid) 2494a826c83SDan Williams return NULL; 2504a826c83SDan Williams snprintf(label_id->id, ND_LABEL_ID_SIZE, "%s-%pUb", 2514a826c83SDan Williams flags & NSLABEL_FLAG_LOCAL ? "blk" : "pmem", uuid); 2524a826c83SDan Williams return label_id->id; 2534a826c83SDan Williams } 2544a826c83SDan Williams 255f524bf27SDan Williams static bool preamble_current(struct nvdimm_drvdata *ndd, 256f524bf27SDan Williams struct nd_namespace_index **nsindex, 257f524bf27SDan Williams unsigned long **free, u32 *nslot) 258f524bf27SDan Williams { 259f524bf27SDan Williams return preamble_index(ndd, ndd->ns_current, nsindex, 260f524bf27SDan Williams free, nslot); 261f524bf27SDan Williams } 262f524bf27SDan Williams 263f524bf27SDan Williams static bool preamble_next(struct nvdimm_drvdata *ndd, 264f524bf27SDan Williams struct nd_namespace_index **nsindex, 265f524bf27SDan Williams unsigned long **free, u32 *nslot) 266f524bf27SDan Williams { 267f524bf27SDan Williams return preamble_index(ndd, ndd->ns_next, nsindex, 268f524bf27SDan Williams free, nslot); 269f524bf27SDan Williams } 270f524bf27SDan Williams 2714a826c83SDan Williams static bool slot_valid(struct nd_namespace_label *nd_label, u32 slot) 2724a826c83SDan Williams { 2734a826c83SDan Williams /* check that we are written where we expect to be written */ 2744a826c83SDan Williams if (slot != __le32_to_cpu(nd_label->slot)) 2754a826c83SDan Williams return false; 2764a826c83SDan Williams 2774a826c83SDan Williams /* check that DPA allocations are page aligned */ 2784a826c83SDan Williams if ((__le64_to_cpu(nd_label->dpa) 2794a826c83SDan Williams | __le64_to_cpu(nd_label->rawsize)) % SZ_4K) 2804a826c83SDan Williams return false; 2814a826c83SDan Williams 2824a826c83SDan Williams return true; 2834a826c83SDan Williams } 2844a826c83SDan Williams 2854a826c83SDan Williams int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd) 2864a826c83SDan Williams { 2874a826c83SDan Williams struct nd_namespace_index *nsindex; 2884a826c83SDan Williams unsigned long *free; 2894a826c83SDan Williams u32 nslot, slot; 2904a826c83SDan Williams 2914a826c83SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 2924a826c83SDan Williams return 0; /* no label, nothing to reserve */ 2934a826c83SDan Williams 2944a826c83SDan Williams for_each_clear_bit_le(slot, free, nslot) { 2954a826c83SDan Williams struct nd_namespace_label *nd_label; 2964a826c83SDan Williams struct nd_region *nd_region = NULL; 2974a826c83SDan Williams u8 label_uuid[NSLABEL_UUID_LEN]; 2984a826c83SDan Williams struct nd_label_id label_id; 2994a826c83SDan Williams struct resource *res; 3004a826c83SDan Williams u32 flags; 3014a826c83SDan Williams 3024a826c83SDan Williams nd_label = nd_label_base(ndd) + slot; 3034a826c83SDan Williams 3044a826c83SDan Williams if (!slot_valid(nd_label, slot)) 3054a826c83SDan Williams continue; 3064a826c83SDan Williams 3074a826c83SDan Williams memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); 3084a826c83SDan Williams flags = __le32_to_cpu(nd_label->flags); 3094a826c83SDan Williams nd_label_gen_id(&label_id, label_uuid, flags); 3104a826c83SDan Williams res = nvdimm_allocate_dpa(ndd, &label_id, 3114a826c83SDan Williams __le64_to_cpu(nd_label->dpa), 3124a826c83SDan Williams __le64_to_cpu(nd_label->rawsize)); 3134a826c83SDan Williams nd_dbg_dpa(nd_region, ndd, res, "reserve\n"); 3144a826c83SDan Williams if (!res) 3154a826c83SDan Williams return -EBUSY; 3164a826c83SDan Williams } 3174a826c83SDan Williams 3184a826c83SDan Williams return 0; 3194a826c83SDan Williams } 320bf9bccc1SDan Williams 321bf9bccc1SDan Williams int nd_label_active_count(struct nvdimm_drvdata *ndd) 322bf9bccc1SDan Williams { 323bf9bccc1SDan Williams struct nd_namespace_index *nsindex; 324bf9bccc1SDan Williams unsigned long *free; 325bf9bccc1SDan Williams u32 nslot, slot; 326bf9bccc1SDan Williams int count = 0; 327bf9bccc1SDan Williams 328bf9bccc1SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 329bf9bccc1SDan Williams return 0; 330bf9bccc1SDan Williams 331bf9bccc1SDan Williams for_each_clear_bit_le(slot, free, nslot) { 332bf9bccc1SDan Williams struct nd_namespace_label *nd_label; 333bf9bccc1SDan Williams 334bf9bccc1SDan Williams nd_label = nd_label_base(ndd) + slot; 335bf9bccc1SDan Williams 336bf9bccc1SDan Williams if (!slot_valid(nd_label, slot)) { 337bf9bccc1SDan Williams u32 label_slot = __le32_to_cpu(nd_label->slot); 338bf9bccc1SDan Williams u64 size = __le64_to_cpu(nd_label->rawsize); 339bf9bccc1SDan Williams u64 dpa = __le64_to_cpu(nd_label->dpa); 340bf9bccc1SDan Williams 341bf9bccc1SDan Williams dev_dbg(ndd->dev, 342bf9bccc1SDan Williams "%s: slot%d invalid slot: %d dpa: %llx size: %llx\n", 343bf9bccc1SDan Williams __func__, slot, label_slot, dpa, size); 344bf9bccc1SDan Williams continue; 345bf9bccc1SDan Williams } 346bf9bccc1SDan Williams count++; 347bf9bccc1SDan Williams } 348bf9bccc1SDan Williams return count; 349bf9bccc1SDan Williams } 350bf9bccc1SDan Williams 351bf9bccc1SDan Williams struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n) 352bf9bccc1SDan Williams { 353bf9bccc1SDan Williams struct nd_namespace_index *nsindex; 354bf9bccc1SDan Williams unsigned long *free; 355bf9bccc1SDan Williams u32 nslot, slot; 356bf9bccc1SDan Williams 357bf9bccc1SDan Williams if (!preamble_current(ndd, &nsindex, &free, &nslot)) 358bf9bccc1SDan Williams return NULL; 359bf9bccc1SDan Williams 360bf9bccc1SDan Williams for_each_clear_bit_le(slot, free, nslot) { 361bf9bccc1SDan Williams struct nd_namespace_label *nd_label; 362bf9bccc1SDan Williams 363bf9bccc1SDan Williams nd_label = nd_label_base(ndd) + slot; 364bf9bccc1SDan Williams if (!slot_valid(nd_label, slot)) 365bf9bccc1SDan Williams continue; 366bf9bccc1SDan Williams 367bf9bccc1SDan Williams if (n-- == 0) 368bf9bccc1SDan Williams return nd_label_base(ndd) + slot; 369bf9bccc1SDan Williams } 370bf9bccc1SDan Williams 371bf9bccc1SDan Williams return NULL; 372bf9bccc1SDan Williams } 373f524bf27SDan Williams 3740ba1c634SDan Williams u32 nd_label_alloc_slot(struct nvdimm_drvdata *ndd) 375f524bf27SDan Williams { 376f524bf27SDan Williams struct nd_namespace_index *nsindex; 377f524bf27SDan Williams unsigned long *free; 378f524bf27SDan Williams u32 nslot, slot; 379f524bf27SDan Williams 380f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 381f524bf27SDan Williams return UINT_MAX; 382f524bf27SDan Williams 383f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 384f524bf27SDan Williams 385f524bf27SDan Williams slot = find_next_bit_le(free, nslot, 0); 386f524bf27SDan Williams if (slot == nslot) 387f524bf27SDan Williams return UINT_MAX; 388f524bf27SDan Williams 389f524bf27SDan Williams clear_bit_le(slot, free); 390f524bf27SDan Williams 391f524bf27SDan Williams return slot; 392f524bf27SDan Williams } 393f524bf27SDan Williams 3940ba1c634SDan Williams bool nd_label_free_slot(struct nvdimm_drvdata *ndd, u32 slot) 395f524bf27SDan Williams { 396f524bf27SDan Williams struct nd_namespace_index *nsindex; 397f524bf27SDan Williams unsigned long *free; 398f524bf27SDan Williams u32 nslot; 399f524bf27SDan Williams 400f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 401f524bf27SDan Williams return false; 402f524bf27SDan Williams 403f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 404f524bf27SDan Williams 405f524bf27SDan Williams if (slot < nslot) 406f524bf27SDan Williams return !test_and_set_bit_le(slot, free); 407f524bf27SDan Williams return false; 408f524bf27SDan Williams } 409f524bf27SDan Williams 410f524bf27SDan Williams u32 nd_label_nfree(struct nvdimm_drvdata *ndd) 411f524bf27SDan Williams { 412f524bf27SDan Williams struct nd_namespace_index *nsindex; 413f524bf27SDan Williams unsigned long *free; 414f524bf27SDan Williams u32 nslot; 415f524bf27SDan Williams 416f524bf27SDan Williams WARN_ON(!is_nvdimm_bus_locked(ndd->dev)); 417f524bf27SDan Williams 418f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 4190ba1c634SDan Williams return nvdimm_num_label_slots(ndd); 420f524bf27SDan Williams 421f524bf27SDan Williams return bitmap_weight(free, nslot); 422f524bf27SDan Williams } 423f524bf27SDan Williams 424f524bf27SDan Williams static int nd_label_write_index(struct nvdimm_drvdata *ndd, int index, u32 seq, 425f524bf27SDan Williams unsigned long flags) 426f524bf27SDan Williams { 427f524bf27SDan Williams struct nd_namespace_index *nsindex; 428f524bf27SDan Williams unsigned long offset; 429f524bf27SDan Williams u64 checksum; 430f524bf27SDan Williams u32 nslot; 431f524bf27SDan Williams int rc; 432f524bf27SDan Williams 433f524bf27SDan Williams nsindex = to_namespace_index(ndd, index); 434f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) 435f524bf27SDan Williams nslot = nvdimm_num_label_slots(ndd); 436f524bf27SDan Williams else 437f524bf27SDan Williams nslot = __le32_to_cpu(nsindex->nslot); 438f524bf27SDan Williams 439f524bf27SDan Williams memcpy(nsindex->sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN); 440f524bf27SDan Williams nsindex->flags = __cpu_to_le32(0); 441f524bf27SDan Williams nsindex->seq = __cpu_to_le32(seq); 442f524bf27SDan Williams offset = (unsigned long) nsindex 443f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 444f524bf27SDan Williams nsindex->myoff = __cpu_to_le64(offset); 445f524bf27SDan Williams nsindex->mysize = __cpu_to_le64(sizeof_namespace_index(ndd)); 446f524bf27SDan Williams offset = (unsigned long) to_namespace_index(ndd, 447f524bf27SDan Williams nd_label_next_nsindex(index)) 448f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 449f524bf27SDan Williams nsindex->otheroff = __cpu_to_le64(offset); 450f524bf27SDan Williams offset = (unsigned long) nd_label_base(ndd) 451f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 452f524bf27SDan Williams nsindex->labeloff = __cpu_to_le64(offset); 453f524bf27SDan Williams nsindex->nslot = __cpu_to_le32(nslot); 454f524bf27SDan Williams nsindex->major = __cpu_to_le16(1); 455f524bf27SDan Williams nsindex->minor = __cpu_to_le16(1); 456f524bf27SDan Williams nsindex->checksum = __cpu_to_le64(0); 457f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) { 458f524bf27SDan Williams unsigned long *free = (unsigned long *) nsindex->free; 459f524bf27SDan Williams u32 nfree = ALIGN(nslot, BITS_PER_LONG); 460f524bf27SDan Williams int last_bits, i; 461f524bf27SDan Williams 462f524bf27SDan Williams memset(nsindex->free, 0xff, nfree / 8); 463f524bf27SDan Williams for (i = 0, last_bits = nfree - nslot; i < last_bits; i++) 464f524bf27SDan Williams clear_bit_le(nslot + i, free); 465f524bf27SDan Williams } 466f524bf27SDan Williams checksum = nd_fletcher64(nsindex, sizeof_namespace_index(ndd), 1); 467f524bf27SDan Williams nsindex->checksum = __cpu_to_le64(checksum); 468f524bf27SDan Williams rc = nvdimm_set_config_data(ndd, __le64_to_cpu(nsindex->myoff), 469f524bf27SDan Williams nsindex, sizeof_namespace_index(ndd)); 470f524bf27SDan Williams if (rc < 0) 471f524bf27SDan Williams return rc; 472f524bf27SDan Williams 473f524bf27SDan Williams if (flags & ND_NSINDEX_INIT) 474f524bf27SDan Williams return 0; 475f524bf27SDan Williams 476f524bf27SDan Williams /* copy the index we just wrote to the new 'next' */ 477f524bf27SDan Williams WARN_ON(index != ndd->ns_next); 478f524bf27SDan Williams nd_label_copy(ndd, to_current_namespace_index(ndd), nsindex); 479f524bf27SDan Williams ndd->ns_current = nd_label_next_nsindex(ndd->ns_current); 480f524bf27SDan Williams ndd->ns_next = nd_label_next_nsindex(ndd->ns_next); 481f524bf27SDan Williams WARN_ON(ndd->ns_current == ndd->ns_next); 482f524bf27SDan Williams 483f524bf27SDan Williams return 0; 484f524bf27SDan Williams } 485f524bf27SDan Williams 486f524bf27SDan Williams static unsigned long nd_label_offset(struct nvdimm_drvdata *ndd, 487f524bf27SDan Williams struct nd_namespace_label *nd_label) 488f524bf27SDan Williams { 489f524bf27SDan Williams return (unsigned long) nd_label 490f524bf27SDan Williams - (unsigned long) to_namespace_index(ndd, 0); 491f524bf27SDan Williams } 492f524bf27SDan Williams 493f524bf27SDan Williams static int __pmem_label_update(struct nd_region *nd_region, 494f524bf27SDan Williams struct nd_mapping *nd_mapping, struct nd_namespace_pmem *nspm, 495f524bf27SDan Williams int pos) 496f524bf27SDan Williams { 49716660eaeSDan Williams u64 cookie = nd_region_interleave_set_cookie(nd_region); 498f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 49916660eaeSDan Williams struct nd_label_ent *label_ent, *victim = NULL; 500f524bf27SDan Williams struct nd_namespace_label *nd_label; 501f524bf27SDan Williams struct nd_namespace_index *nsindex; 50216660eaeSDan Williams struct nd_label_id label_id; 50316660eaeSDan Williams struct resource *res; 504f524bf27SDan Williams unsigned long *free; 505f524bf27SDan Williams u32 nslot, slot; 506f524bf27SDan Williams size_t offset; 507f524bf27SDan Williams int rc; 508f524bf27SDan Williams 509f524bf27SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 510f524bf27SDan Williams return -ENXIO; 511f524bf27SDan Williams 51216660eaeSDan Williams nd_label_gen_id(&label_id, nspm->uuid, 0); 51316660eaeSDan Williams for_each_dpa_resource(ndd, res) 51416660eaeSDan Williams if (strcmp(res->name, label_id.id) == 0) 51516660eaeSDan Williams break; 51616660eaeSDan Williams 51716660eaeSDan Williams if (!res) { 51816660eaeSDan Williams WARN_ON_ONCE(1); 51916660eaeSDan Williams return -ENXIO; 52016660eaeSDan Williams } 52116660eaeSDan Williams 522f524bf27SDan Williams /* allocate and write the label to the staging (next) index */ 523f524bf27SDan Williams slot = nd_label_alloc_slot(ndd); 524f524bf27SDan Williams if (slot == UINT_MAX) 525f524bf27SDan Williams return -ENXIO; 526f524bf27SDan Williams dev_dbg(ndd->dev, "%s: allocated: %d\n", __func__, slot); 527f524bf27SDan Williams 528f524bf27SDan Williams nd_label = nd_label_base(ndd) + slot; 529f524bf27SDan Williams memset(nd_label, 0, sizeof(struct nd_namespace_label)); 530f524bf27SDan Williams memcpy(nd_label->uuid, nspm->uuid, NSLABEL_UUID_LEN); 531f524bf27SDan Williams if (nspm->alt_name) 532f524bf27SDan Williams memcpy(nd_label->name, nspm->alt_name, NSLABEL_NAME_LEN); 533f524bf27SDan Williams nd_label->flags = __cpu_to_le32(NSLABEL_FLAG_UPDATING); 534f524bf27SDan Williams nd_label->nlabel = __cpu_to_le16(nd_region->ndr_mappings); 535f524bf27SDan Williams nd_label->position = __cpu_to_le16(pos); 536f524bf27SDan Williams nd_label->isetcookie = __cpu_to_le64(cookie); 53716660eaeSDan Williams nd_label->rawsize = __cpu_to_le64(resource_size(res)); 53816660eaeSDan Williams nd_label->dpa = __cpu_to_le64(res->start); 539f524bf27SDan Williams nd_label->slot = __cpu_to_le32(slot); 54016660eaeSDan Williams nd_dbg_dpa(nd_region, ndd, res, "%s\n", __func__); 541f524bf27SDan Williams 542f524bf27SDan Williams /* update label */ 543f524bf27SDan Williams offset = nd_label_offset(ndd, nd_label); 544f524bf27SDan Williams rc = nvdimm_set_config_data(ndd, offset, nd_label, 545f524bf27SDan Williams sizeof(struct nd_namespace_label)); 546f524bf27SDan Williams if (rc < 0) 547f524bf27SDan Williams return rc; 548f524bf27SDan Williams 549f524bf27SDan Williams /* Garbage collect the previous label */ 550ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 55116660eaeSDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) { 55216660eaeSDan Williams if (!label_ent->label) 55316660eaeSDan Williams continue; 55416660eaeSDan Williams if (memcmp(nspm->uuid, label_ent->label->uuid, 55516660eaeSDan Williams NSLABEL_UUID_LEN) != 0) 55616660eaeSDan Williams continue; 55716660eaeSDan Williams victim = label_ent; 55816660eaeSDan Williams list_move_tail(&victim->list, &nd_mapping->labels); 55916660eaeSDan Williams break; 56016660eaeSDan Williams } 56116660eaeSDan Williams if (victim) { 562f524bf27SDan Williams dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot); 56316660eaeSDan Williams slot = to_slot(ndd, victim->label); 56416660eaeSDan Williams nd_label_free_slot(ndd, slot); 56516660eaeSDan Williams victim->label = NULL; 566f524bf27SDan Williams } 567f524bf27SDan Williams 568f524bf27SDan Williams /* update index */ 569f524bf27SDan Williams rc = nd_label_write_index(ndd, ndd->ns_next, 570f524bf27SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 57116660eaeSDan Williams if (rc == 0) { 57216660eaeSDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) 57316660eaeSDan Williams if (!label_ent->label) { 574ae8219f1SDan Williams label_ent->label = nd_label; 57516660eaeSDan Williams nd_label = NULL; 57616660eaeSDan Williams break; 57716660eaeSDan Williams } 57816660eaeSDan Williams dev_WARN_ONCE(&nspm->nsio.common.dev, nd_label, 57916660eaeSDan Williams "failed to track label: %d\n", 58016660eaeSDan Williams to_slot(ndd, nd_label)); 58116660eaeSDan Williams if (nd_label) 58216660eaeSDan Williams rc = -ENXIO; 58316660eaeSDan Williams } 584ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 585ae8219f1SDan Williams 586f524bf27SDan Williams return rc; 5870ba1c634SDan Williams } 5880ba1c634SDan Williams 5890ba1c634SDan Williams static bool is_old_resource(struct resource *res, struct resource **list, int n) 590f524bf27SDan Williams { 591f524bf27SDan Williams int i; 5920ba1c634SDan Williams 5930ba1c634SDan Williams if (res->flags & DPA_RESOURCE_ADJUSTED) 5940ba1c634SDan Williams return false; 5950ba1c634SDan Williams for (i = 0; i < n; i++) 5960ba1c634SDan Williams if (res == list[i]) 5970ba1c634SDan Williams return true; 5980ba1c634SDan Williams return false; 5990ba1c634SDan Williams } 6000ba1c634SDan Williams 6010ba1c634SDan Williams static struct resource *to_resource(struct nvdimm_drvdata *ndd, 6020ba1c634SDan Williams struct nd_namespace_label *nd_label) 6030ba1c634SDan Williams { 6040ba1c634SDan Williams struct resource *res; 6050ba1c634SDan Williams 6060ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 6070ba1c634SDan Williams if (res->start != __le64_to_cpu(nd_label->dpa)) 6080ba1c634SDan Williams continue; 6090ba1c634SDan Williams if (resource_size(res) != __le64_to_cpu(nd_label->rawsize)) 6100ba1c634SDan Williams continue; 6110ba1c634SDan Williams return res; 6120ba1c634SDan Williams } 6130ba1c634SDan Williams 6140ba1c634SDan Williams return NULL; 6150ba1c634SDan Williams } 6160ba1c634SDan Williams 6170ba1c634SDan Williams /* 6180ba1c634SDan Williams * 1/ Account all the labels that can be freed after this update 6190ba1c634SDan Williams * 2/ Allocate and write the label to the staging (next) index 6200ba1c634SDan Williams * 3/ Record the resources in the namespace device 6210ba1c634SDan Williams */ 6220ba1c634SDan Williams static int __blk_label_update(struct nd_region *nd_region, 6230ba1c634SDan Williams struct nd_mapping *nd_mapping, struct nd_namespace_blk *nsblk, 6240ba1c634SDan Williams int num_labels) 6250ba1c634SDan Williams { 626ae8219f1SDan Williams int i, alloc, victims, nfree, old_num_resources, nlabel, rc = -ENXIO; 627f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 6280ba1c634SDan Williams struct nd_namespace_label *nd_label; 629ae8219f1SDan Williams struct nd_label_ent *label_ent, *e; 6300ba1c634SDan Williams struct nd_namespace_index *nsindex; 6310ba1c634SDan Williams unsigned long *free, *victim_map = NULL; 6320ba1c634SDan Williams struct resource *res, **old_res_list; 6330ba1c634SDan Williams struct nd_label_id label_id; 6340ba1c634SDan Williams u8 uuid[NSLABEL_UUID_LEN]; 635ae8219f1SDan Williams LIST_HEAD(list); 6360ba1c634SDan Williams u32 nslot, slot; 637f524bf27SDan Williams 6380ba1c634SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 6390ba1c634SDan Williams return -ENXIO; 640f524bf27SDan Williams 6410ba1c634SDan Williams old_res_list = nsblk->res; 6420ba1c634SDan Williams nfree = nd_label_nfree(ndd); 6430ba1c634SDan Williams old_num_resources = nsblk->num_resources; 6440ba1c634SDan Williams nd_label_gen_id(&label_id, nsblk->uuid, NSLABEL_FLAG_LOCAL); 6450ba1c634SDan Williams 6460ba1c634SDan Williams /* 6470ba1c634SDan Williams * We need to loop over the old resources a few times, which seems a 6480ba1c634SDan Williams * bit inefficient, but we need to know that we have the label 6490ba1c634SDan Williams * space before we start mutating the tracking structures. 6500ba1c634SDan Williams * Otherwise the recovery method of last resort for userspace is 6510ba1c634SDan Williams * disable and re-enable the parent region. 6520ba1c634SDan Williams */ 6530ba1c634SDan Williams alloc = 0; 6540ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 6550ba1c634SDan Williams if (strcmp(res->name, label_id.id) != 0) 6560ba1c634SDan Williams continue; 6570ba1c634SDan Williams if (!is_old_resource(res, old_res_list, old_num_resources)) 6580ba1c634SDan Williams alloc++; 6590ba1c634SDan Williams } 6600ba1c634SDan Williams 6610ba1c634SDan Williams victims = 0; 6620ba1c634SDan Williams if (old_num_resources) { 6630ba1c634SDan Williams /* convert old local-label-map to dimm-slot victim-map */ 6640ba1c634SDan Williams victim_map = kcalloc(BITS_TO_LONGS(nslot), sizeof(long), 6650ba1c634SDan Williams GFP_KERNEL); 6660ba1c634SDan Williams if (!victim_map) 6670ba1c634SDan Williams return -ENOMEM; 6680ba1c634SDan Williams 6690ba1c634SDan Williams /* mark unused labels for garbage collection */ 6700ba1c634SDan Williams for_each_clear_bit_le(slot, free, nslot) { 6710ba1c634SDan Williams nd_label = nd_label_base(ndd) + slot; 6720ba1c634SDan Williams memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 6730ba1c634SDan Williams if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 6740ba1c634SDan Williams continue; 6750ba1c634SDan Williams res = to_resource(ndd, nd_label); 6760ba1c634SDan Williams if (res && is_old_resource(res, old_res_list, 6770ba1c634SDan Williams old_num_resources)) 6780ba1c634SDan Williams continue; 6790ba1c634SDan Williams slot = to_slot(ndd, nd_label); 6800ba1c634SDan Williams set_bit(slot, victim_map); 6810ba1c634SDan Williams victims++; 6820ba1c634SDan Williams } 6830ba1c634SDan Williams } 6840ba1c634SDan Williams 6850ba1c634SDan Williams /* don't allow updates that consume the last label */ 6860ba1c634SDan Williams if (nfree - alloc < 0 || nfree - alloc + victims < 1) { 6878c2f7e86SDan Williams dev_info(&nsblk->common.dev, "insufficient label space\n"); 6880ba1c634SDan Williams kfree(victim_map); 6890ba1c634SDan Williams return -ENOSPC; 6900ba1c634SDan Williams } 6910ba1c634SDan Williams /* from here on we need to abort on error */ 6920ba1c634SDan Williams 6930ba1c634SDan Williams 6940ba1c634SDan Williams /* assign all resources to the namespace before writing the labels */ 6950ba1c634SDan Williams nsblk->res = NULL; 6960ba1c634SDan Williams nsblk->num_resources = 0; 6970ba1c634SDan Williams for_each_dpa_resource(ndd, res) { 6980ba1c634SDan Williams if (strcmp(res->name, label_id.id) != 0) 6990ba1c634SDan Williams continue; 7000ba1c634SDan Williams if (!nsblk_add_resource(nd_region, ndd, nsblk, res->start)) { 7010ba1c634SDan Williams rc = -ENOMEM; 7020ba1c634SDan Williams goto abort; 7030ba1c634SDan Williams } 7040ba1c634SDan Williams } 7050ba1c634SDan Williams 7060ba1c634SDan Williams for (i = 0; i < nsblk->num_resources; i++) { 7070ba1c634SDan Williams size_t offset; 7080ba1c634SDan Williams 7090ba1c634SDan Williams res = nsblk->res[i]; 7100ba1c634SDan Williams if (is_old_resource(res, old_res_list, old_num_resources)) 7110ba1c634SDan Williams continue; /* carry-over */ 7120ba1c634SDan Williams slot = nd_label_alloc_slot(ndd); 7130ba1c634SDan Williams if (slot == UINT_MAX) 7140ba1c634SDan Williams goto abort; 7150ba1c634SDan Williams dev_dbg(ndd->dev, "%s: allocated: %d\n", __func__, slot); 7160ba1c634SDan Williams 7170ba1c634SDan Williams nd_label = nd_label_base(ndd) + slot; 7180ba1c634SDan Williams memset(nd_label, 0, sizeof(struct nd_namespace_label)); 7190ba1c634SDan Williams memcpy(nd_label->uuid, nsblk->uuid, NSLABEL_UUID_LEN); 7200ba1c634SDan Williams if (nsblk->alt_name) 7210ba1c634SDan Williams memcpy(nd_label->name, nsblk->alt_name, 7220ba1c634SDan Williams NSLABEL_NAME_LEN); 7230ba1c634SDan Williams nd_label->flags = __cpu_to_le32(NSLABEL_FLAG_LOCAL); 7240ba1c634SDan Williams nd_label->nlabel = __cpu_to_le16(0); /* N/A */ 7250ba1c634SDan Williams nd_label->position = __cpu_to_le16(0); /* N/A */ 7260ba1c634SDan Williams nd_label->isetcookie = __cpu_to_le64(0); /* N/A */ 7270ba1c634SDan Williams nd_label->dpa = __cpu_to_le64(res->start); 7280ba1c634SDan Williams nd_label->rawsize = __cpu_to_le64(resource_size(res)); 7290ba1c634SDan Williams nd_label->lbasize = __cpu_to_le64(nsblk->lbasize); 7300ba1c634SDan Williams nd_label->slot = __cpu_to_le32(slot); 7310ba1c634SDan Williams 7320ba1c634SDan Williams /* update label */ 7330ba1c634SDan Williams offset = nd_label_offset(ndd, nd_label); 7340ba1c634SDan Williams rc = nvdimm_set_config_data(ndd, offset, nd_label, 7350ba1c634SDan Williams sizeof(struct nd_namespace_label)); 7360ba1c634SDan Williams if (rc < 0) 7370ba1c634SDan Williams goto abort; 7380ba1c634SDan Williams } 7390ba1c634SDan Williams 7400ba1c634SDan Williams /* free up now unused slots in the new index */ 7410ba1c634SDan Williams for_each_set_bit(slot, victim_map, victim_map ? nslot : 0) { 7420ba1c634SDan Williams dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot); 7430ba1c634SDan Williams nd_label_free_slot(ndd, slot); 7440ba1c634SDan Williams } 7450ba1c634SDan Williams 7460ba1c634SDan Williams /* update index */ 7470ba1c634SDan Williams rc = nd_label_write_index(ndd, ndd->ns_next, 7480ba1c634SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 7490ba1c634SDan Williams if (rc) 7500ba1c634SDan Williams goto abort; 7510ba1c634SDan Williams 7520ba1c634SDan Williams /* 7530ba1c634SDan Williams * Now that the on-dimm labels are up to date, fix up the tracking 7540ba1c634SDan Williams * entries in nd_mapping->labels 7550ba1c634SDan Williams */ 7560ba1c634SDan Williams nlabel = 0; 757ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 758ae8219f1SDan Williams list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { 759ae8219f1SDan Williams nd_label = label_ent->label; 760ae8219f1SDan Williams if (!nd_label) 761ae8219f1SDan Williams continue; 7620ba1c634SDan Williams nlabel++; 7630ba1c634SDan Williams memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 7640ba1c634SDan Williams if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 7650ba1c634SDan Williams continue; 7660ba1c634SDan Williams nlabel--; 767ae8219f1SDan Williams list_move(&label_ent->list, &list); 768ae8219f1SDan Williams label_ent->label = NULL; 7690ba1c634SDan Williams } 770ae8219f1SDan Williams list_splice_tail_init(&list, &nd_mapping->labels); 771ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 772ae8219f1SDan Williams 7730ba1c634SDan Williams if (nlabel + nsblk->num_resources > num_labels) { 7740ba1c634SDan Williams /* 7750ba1c634SDan Williams * Bug, we can't end up with more resources than 7760ba1c634SDan Williams * available labels 7770ba1c634SDan Williams */ 7780ba1c634SDan Williams WARN_ON_ONCE(1); 7790ba1c634SDan Williams rc = -ENXIO; 7800ba1c634SDan Williams goto out; 7810ba1c634SDan Williams } 7820ba1c634SDan Williams 783ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 784ae8219f1SDan Williams label_ent = list_first_entry_or_null(&nd_mapping->labels, 785ae8219f1SDan Williams typeof(*label_ent), list); 786ae8219f1SDan Williams if (!label_ent) { 787ae8219f1SDan Williams WARN_ON(1); 788ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 789ae8219f1SDan Williams rc = -ENXIO; 790ae8219f1SDan Williams goto out; 791ae8219f1SDan Williams } 7920ba1c634SDan Williams for_each_clear_bit_le(slot, free, nslot) { 7930ba1c634SDan Williams nd_label = nd_label_base(ndd) + slot; 7940ba1c634SDan Williams memcpy(uuid, nd_label->uuid, NSLABEL_UUID_LEN); 7950ba1c634SDan Williams if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) != 0) 7960ba1c634SDan Williams continue; 7970ba1c634SDan Williams res = to_resource(ndd, nd_label); 7980ba1c634SDan Williams res->flags &= ~DPA_RESOURCE_ADJUSTED; 799ae8219f1SDan Williams dev_vdbg(&nsblk->common.dev, "assign label slot: %d\n", slot); 800ae8219f1SDan Williams list_for_each_entry_from(label_ent, &nd_mapping->labels, list) { 801ae8219f1SDan Williams if (label_ent->label) 802ae8219f1SDan Williams continue; 803ae8219f1SDan Williams label_ent->label = nd_label; 804ae8219f1SDan Williams nd_label = NULL; 805ae8219f1SDan Williams break; 8060ba1c634SDan Williams } 807ae8219f1SDan Williams if (nd_label) 808ae8219f1SDan Williams dev_WARN(&nsblk->common.dev, 809ae8219f1SDan Williams "failed to track label slot%d\n", slot); 810ae8219f1SDan Williams } 811ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 8120ba1c634SDan Williams 8130ba1c634SDan Williams out: 8140ba1c634SDan Williams kfree(old_res_list); 8150ba1c634SDan Williams kfree(victim_map); 8160ba1c634SDan Williams return rc; 8170ba1c634SDan Williams 8180ba1c634SDan Williams abort: 8190ba1c634SDan Williams /* 8200ba1c634SDan Williams * 1/ repair the allocated label bitmap in the index 8210ba1c634SDan Williams * 2/ restore the resource list 8220ba1c634SDan Williams */ 8230ba1c634SDan Williams nd_label_copy(ndd, nsindex, to_current_namespace_index(ndd)); 8240ba1c634SDan Williams kfree(nsblk->res); 8250ba1c634SDan Williams nsblk->res = old_res_list; 8260ba1c634SDan Williams nsblk->num_resources = old_num_resources; 8270ba1c634SDan Williams old_res_list = NULL; 8280ba1c634SDan Williams goto out; 8290ba1c634SDan Williams } 8300ba1c634SDan Williams 8310ba1c634SDan Williams static int init_labels(struct nd_mapping *nd_mapping, int num_labels) 8320ba1c634SDan Williams { 833ae8219f1SDan Williams int i, old_num_labels = 0; 834ae8219f1SDan Williams struct nd_label_ent *label_ent; 8350ba1c634SDan Williams struct nd_namespace_index *nsindex; 8360ba1c634SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 8370ba1c634SDan Williams 838ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 839ae8219f1SDan Williams list_for_each_entry(label_ent, &nd_mapping->labels, list) 8400ba1c634SDan Williams old_num_labels++; 841ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 8420ba1c634SDan Williams 8430ba1c634SDan Williams /* 8440ba1c634SDan Williams * We need to preserve all the old labels for the mapping so 8450ba1c634SDan Williams * they can be garbage collected after writing the new labels. 8460ba1c634SDan Williams */ 847ae8219f1SDan Williams for (i = old_num_labels; i < num_labels; i++) { 848ae8219f1SDan Williams label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL); 849ae8219f1SDan Williams if (!label_ent) 8500ba1c634SDan Williams return -ENOMEM; 851ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 852ae8219f1SDan Williams list_add_tail(&label_ent->list, &nd_mapping->labels); 853ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 8540ba1c634SDan Williams } 8550ba1c634SDan Williams 856f524bf27SDan Williams if (ndd->ns_current == -1 || ndd->ns_next == -1) 857f524bf27SDan Williams /* pass */; 858f524bf27SDan Williams else 8590ba1c634SDan Williams return max(num_labels, old_num_labels); 860f524bf27SDan Williams 861f524bf27SDan Williams nsindex = to_namespace_index(ndd, 0); 862f524bf27SDan Williams memset(nsindex, 0, ndd->nsarea.config_size); 863f524bf27SDan Williams for (i = 0; i < 2; i++) { 864f524bf27SDan Williams int rc = nd_label_write_index(ndd, i, i*2, ND_NSINDEX_INIT); 865f524bf27SDan Williams 866f524bf27SDan Williams if (rc) 867f524bf27SDan Williams return rc; 868f524bf27SDan Williams } 869f524bf27SDan Williams ndd->ns_next = 1; 870f524bf27SDan Williams ndd->ns_current = 0; 871f524bf27SDan Williams 8720ba1c634SDan Williams return max(num_labels, old_num_labels); 873f524bf27SDan Williams } 874f524bf27SDan Williams 875f524bf27SDan Williams static int del_labels(struct nd_mapping *nd_mapping, u8 *uuid) 876f524bf27SDan Williams { 877f524bf27SDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 878ae8219f1SDan Williams struct nd_label_ent *label_ent, *e; 879f524bf27SDan Williams struct nd_namespace_index *nsindex; 880f524bf27SDan Williams u8 label_uuid[NSLABEL_UUID_LEN]; 881f524bf27SDan Williams unsigned long *free; 882ae8219f1SDan Williams LIST_HEAD(list); 883f524bf27SDan Williams u32 nslot, slot; 884ae8219f1SDan Williams int active = 0; 885f524bf27SDan Williams 886f524bf27SDan Williams if (!uuid) 887f524bf27SDan Williams return 0; 888f524bf27SDan Williams 889f524bf27SDan Williams /* no index || no labels == nothing to delete */ 890ae8219f1SDan Williams if (!preamble_next(ndd, &nsindex, &free, &nslot)) 891f524bf27SDan Williams return 0; 892f524bf27SDan Williams 893ae8219f1SDan Williams mutex_lock(&nd_mapping->lock); 894ae8219f1SDan Williams list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { 895ae8219f1SDan Williams struct nd_namespace_label *nd_label = label_ent->label; 896ae8219f1SDan Williams 897ae8219f1SDan Williams if (!nd_label) 898ae8219f1SDan Williams continue; 899ae8219f1SDan Williams active++; 900f524bf27SDan Williams memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN); 901f524bf27SDan Williams if (memcmp(label_uuid, uuid, NSLABEL_UUID_LEN) != 0) 902f524bf27SDan Williams continue; 903ae8219f1SDan Williams active--; 904f524bf27SDan Williams slot = to_slot(ndd, nd_label); 905f524bf27SDan Williams nd_label_free_slot(ndd, slot); 906f524bf27SDan Williams dev_dbg(ndd->dev, "%s: free: %d\n", __func__, slot); 907ae8219f1SDan Williams list_move_tail(&label_ent->list, &list); 908ae8219f1SDan Williams label_ent->label = NULL; 909f524bf27SDan Williams } 910ae8219f1SDan Williams list_splice_tail_init(&list, &nd_mapping->labels); 911f524bf27SDan Williams 912ae8219f1SDan Williams if (active == 0) { 913ae8219f1SDan Williams nd_mapping_free_labels(nd_mapping); 914ae8219f1SDan Williams dev_dbg(ndd->dev, "%s: no more active labels\n", __func__); 915f524bf27SDan Williams } 916ae8219f1SDan Williams mutex_unlock(&nd_mapping->lock); 917f524bf27SDan Williams 918f524bf27SDan Williams return nd_label_write_index(ndd, ndd->ns_next, 919f524bf27SDan Williams nd_inc_seq(__le32_to_cpu(nsindex->seq)), 0); 920f524bf27SDan Williams } 921f524bf27SDan Williams 922f524bf27SDan Williams int nd_pmem_namespace_label_update(struct nd_region *nd_region, 923f524bf27SDan Williams struct nd_namespace_pmem *nspm, resource_size_t size) 924f524bf27SDan Williams { 925f524bf27SDan Williams int i; 926f524bf27SDan Williams 927f524bf27SDan Williams for (i = 0; i < nd_region->ndr_mappings; i++) { 928f524bf27SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[i]; 92916660eaeSDan Williams struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 93016660eaeSDan Williams struct resource *res; 93116660eaeSDan Williams int rc, count = 0; 932f524bf27SDan Williams 933f524bf27SDan Williams if (size == 0) { 934f524bf27SDan Williams rc = del_labels(nd_mapping, nspm->uuid); 935f524bf27SDan Williams if (rc) 936f524bf27SDan Williams return rc; 937f524bf27SDan Williams continue; 938f524bf27SDan Williams } 939f524bf27SDan Williams 94016660eaeSDan Williams for_each_dpa_resource(ndd, res) 941*2d9a0274SNicolas Iooss if (strncmp(res->name, "pmem", 4) == 0) 94216660eaeSDan Williams count++; 94316660eaeSDan Williams WARN_ON_ONCE(!count); 94416660eaeSDan Williams 94516660eaeSDan Williams rc = init_labels(nd_mapping, count); 9460ba1c634SDan Williams if (rc < 0) 947f524bf27SDan Williams return rc; 948f524bf27SDan Williams 949f524bf27SDan Williams rc = __pmem_label_update(nd_region, nd_mapping, nspm, i); 950f524bf27SDan Williams if (rc) 951f524bf27SDan Williams return rc; 952f524bf27SDan Williams } 953f524bf27SDan Williams 954f524bf27SDan Williams return 0; 955f524bf27SDan Williams } 9560ba1c634SDan Williams 9570ba1c634SDan Williams int nd_blk_namespace_label_update(struct nd_region *nd_region, 9580ba1c634SDan Williams struct nd_namespace_blk *nsblk, resource_size_t size) 9590ba1c634SDan Williams { 9600ba1c634SDan Williams struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 9610ba1c634SDan Williams struct resource *res; 9620ba1c634SDan Williams int count = 0; 9630ba1c634SDan Williams 9640ba1c634SDan Williams if (size == 0) 9650ba1c634SDan Williams return del_labels(nd_mapping, nsblk->uuid); 9660ba1c634SDan Williams 9670ba1c634SDan Williams for_each_dpa_resource(to_ndd(nd_mapping), res) 9680ba1c634SDan Williams count++; 9690ba1c634SDan Williams 9700ba1c634SDan Williams count = init_labels(nd_mapping, count); 9710ba1c634SDan Williams if (count < 0) 9720ba1c634SDan Williams return count; 9730ba1c634SDan Williams 9740ba1c634SDan Williams return __blk_label_update(nd_region, nd_mapping, nsblk, count); 9750ba1c634SDan Williams } 976