xref: /linux/drivers/nvdimm/label.c (revision bf9bccc14c05dae8caba29df6187c731710f5380)
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>
154a826c83SDan Williams #include <linux/io.h>
164a826c83SDan Williams #include <linux/nd.h>
174a826c83SDan Williams #include "nd-core.h"
184a826c83SDan Williams #include "label.h"
194a826c83SDan Williams #include "nd.h"
204a826c83SDan Williams 
214a826c83SDan Williams static u32 best_seq(u32 a, u32 b)
224a826c83SDan Williams {
234a826c83SDan Williams 	a &= NSINDEX_SEQ_MASK;
244a826c83SDan Williams 	b &= NSINDEX_SEQ_MASK;
254a826c83SDan Williams 
264a826c83SDan Williams 	if (a == 0 || a == b)
274a826c83SDan Williams 		return b;
284a826c83SDan Williams 	else if (b == 0)
294a826c83SDan Williams 		return a;
304a826c83SDan Williams 	else if (nd_inc_seq(a) == b)
314a826c83SDan Williams 		return b;
324a826c83SDan Williams 	else
334a826c83SDan Williams 		return a;
344a826c83SDan Williams }
354a826c83SDan Williams 
364a826c83SDan Williams size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd)
374a826c83SDan Williams {
384a826c83SDan Williams 	u32 index_span;
394a826c83SDan Williams 
404a826c83SDan Williams 	if (ndd->nsindex_size)
414a826c83SDan Williams 		return ndd->nsindex_size;
424a826c83SDan Williams 
434a826c83SDan Williams 	/*
444a826c83SDan Williams 	 * The minimum index space is 512 bytes, with that amount of
454a826c83SDan Williams 	 * index we can describe ~1400 labels which is less than a byte
464a826c83SDan Williams 	 * of overhead per label.  Round up to a byte of overhead per
474a826c83SDan Williams 	 * label and determine the size of the index region.  Yes, this
484a826c83SDan Williams 	 * starts to waste space at larger config_sizes, but it's
494a826c83SDan Williams 	 * unlikely we'll ever see anything but 128K.
504a826c83SDan Williams 	 */
514a826c83SDan Williams 	index_span = ndd->nsarea.config_size / 129;
524a826c83SDan Williams 	index_span /= NSINDEX_ALIGN * 2;
534a826c83SDan Williams 	ndd->nsindex_size = index_span * NSINDEX_ALIGN;
544a826c83SDan Williams 
554a826c83SDan Williams 	return ndd->nsindex_size;
564a826c83SDan Williams }
574a826c83SDan Williams 
584a826c83SDan Williams int nd_label_validate(struct nvdimm_drvdata *ndd)
594a826c83SDan Williams {
604a826c83SDan Williams 	/*
614a826c83SDan Williams 	 * On media label format consists of two index blocks followed
624a826c83SDan Williams 	 * by an array of labels.  None of these structures are ever
634a826c83SDan Williams 	 * updated in place.  A sequence number tracks the current
644a826c83SDan Williams 	 * active index and the next one to write, while labels are
654a826c83SDan Williams 	 * written to free slots.
664a826c83SDan Williams 	 *
674a826c83SDan Williams 	 *     +------------+
684a826c83SDan Williams 	 *     |            |
694a826c83SDan Williams 	 *     |  nsindex0  |
704a826c83SDan Williams 	 *     |            |
714a826c83SDan Williams 	 *     +------------+
724a826c83SDan Williams 	 *     |            |
734a826c83SDan Williams 	 *     |  nsindex1  |
744a826c83SDan Williams 	 *     |            |
754a826c83SDan Williams 	 *     +------------+
764a826c83SDan Williams 	 *     |   label0   |
774a826c83SDan Williams 	 *     +------------+
784a826c83SDan Williams 	 *     |   label1   |
794a826c83SDan Williams 	 *     +------------+
804a826c83SDan Williams 	 *     |            |
814a826c83SDan Williams 	 *      ....nslot...
824a826c83SDan Williams 	 *     |            |
834a826c83SDan Williams 	 *     +------------+
844a826c83SDan Williams 	 *     |   labelN   |
854a826c83SDan Williams 	 *     +------------+
864a826c83SDan Williams 	 */
874a826c83SDan Williams 	struct nd_namespace_index *nsindex[] = {
884a826c83SDan Williams 		to_namespace_index(ndd, 0),
894a826c83SDan Williams 		to_namespace_index(ndd, 1),
904a826c83SDan Williams 	};
914a826c83SDan Williams 	const int num_index = ARRAY_SIZE(nsindex);
924a826c83SDan Williams 	struct device *dev = ndd->dev;
934a826c83SDan Williams 	bool valid[2] = { 0 };
944a826c83SDan Williams 	int i, num_valid = 0;
954a826c83SDan Williams 	u32 seq;
964a826c83SDan Williams 
974a826c83SDan Williams 	for (i = 0; i < num_index; i++) {
984a826c83SDan Williams 		u32 nslot;
994a826c83SDan Williams 		u8 sig[NSINDEX_SIG_LEN];
1004a826c83SDan Williams 		u64 sum_save, sum, size;
1014a826c83SDan Williams 
1024a826c83SDan Williams 		memcpy(sig, nsindex[i]->sig, NSINDEX_SIG_LEN);
1034a826c83SDan Williams 		if (memcmp(sig, NSINDEX_SIGNATURE, NSINDEX_SIG_LEN) != 0) {
1044a826c83SDan Williams 			dev_dbg(dev, "%s: nsindex%d signature invalid\n",
1054a826c83SDan Williams 					__func__, i);
1064a826c83SDan Williams 			continue;
1074a826c83SDan Williams 		}
1084a826c83SDan Williams 		sum_save = __le64_to_cpu(nsindex[i]->checksum);
1094a826c83SDan Williams 		nsindex[i]->checksum = __cpu_to_le64(0);
1104a826c83SDan Williams 		sum = nd_fletcher64(nsindex[i], sizeof_namespace_index(ndd), 1);
1114a826c83SDan Williams 		nsindex[i]->checksum = __cpu_to_le64(sum_save);
1124a826c83SDan Williams 		if (sum != sum_save) {
1134a826c83SDan Williams 			dev_dbg(dev, "%s: nsindex%d checksum invalid\n",
1144a826c83SDan Williams 					__func__, i);
1154a826c83SDan Williams 			continue;
1164a826c83SDan Williams 		}
1174a826c83SDan Williams 
1184a826c83SDan Williams 		seq = __le32_to_cpu(nsindex[i]->seq);
1194a826c83SDan Williams 		if ((seq & NSINDEX_SEQ_MASK) == 0) {
1204a826c83SDan Williams 			dev_dbg(dev, "%s: nsindex%d sequence: %#x invalid\n",
1214a826c83SDan Williams 					__func__, i, seq);
1224a826c83SDan Williams 			continue;
1234a826c83SDan Williams 		}
1244a826c83SDan Williams 
1254a826c83SDan Williams 		/* sanity check the index against expected values */
1264a826c83SDan Williams 		if (__le64_to_cpu(nsindex[i]->myoff)
1274a826c83SDan Williams 				!= i * sizeof_namespace_index(ndd)) {
1284a826c83SDan Williams 			dev_dbg(dev, "%s: nsindex%d myoff: %#llx invalid\n",
1294a826c83SDan Williams 					__func__, i, (unsigned long long)
1304a826c83SDan Williams 					__le64_to_cpu(nsindex[i]->myoff));
1314a826c83SDan Williams 			continue;
1324a826c83SDan Williams 		}
1334a826c83SDan Williams 		if (__le64_to_cpu(nsindex[i]->otheroff)
1344a826c83SDan Williams 				!= (!i) * sizeof_namespace_index(ndd)) {
1354a826c83SDan Williams 			dev_dbg(dev, "%s: nsindex%d otheroff: %#llx invalid\n",
1364a826c83SDan Williams 					__func__, i, (unsigned long long)
1374a826c83SDan Williams 					__le64_to_cpu(nsindex[i]->otheroff));
1384a826c83SDan Williams 			continue;
1394a826c83SDan Williams 		}
1404a826c83SDan Williams 
1414a826c83SDan Williams 		size = __le64_to_cpu(nsindex[i]->mysize);
1424a826c83SDan Williams 		if (size > sizeof_namespace_index(ndd)
1434a826c83SDan Williams 				|| size < sizeof(struct nd_namespace_index)) {
1444a826c83SDan Williams 			dev_dbg(dev, "%s: nsindex%d mysize: %#llx invalid\n",
1454a826c83SDan Williams 					__func__, i, size);
1464a826c83SDan Williams 			continue;
1474a826c83SDan Williams 		}
1484a826c83SDan Williams 
1494a826c83SDan Williams 		nslot = __le32_to_cpu(nsindex[i]->nslot);
1504a826c83SDan Williams 		if (nslot * sizeof(struct nd_namespace_label)
1514a826c83SDan Williams 				+ 2 * sizeof_namespace_index(ndd)
1524a826c83SDan Williams 				> ndd->nsarea.config_size) {
1534a826c83SDan Williams 			dev_dbg(dev, "%s: nsindex%d nslot: %u invalid, config_size: %#x\n",
1544a826c83SDan Williams 					__func__, i, nslot,
1554a826c83SDan Williams 					ndd->nsarea.config_size);
1564a826c83SDan Williams 			continue;
1574a826c83SDan Williams 		}
1584a826c83SDan Williams 		valid[i] = true;
1594a826c83SDan Williams 		num_valid++;
1604a826c83SDan Williams 	}
1614a826c83SDan Williams 
1624a826c83SDan Williams 	switch (num_valid) {
1634a826c83SDan Williams 	case 0:
1644a826c83SDan Williams 		break;
1654a826c83SDan Williams 	case 1:
1664a826c83SDan Williams 		for (i = 0; i < num_index; i++)
1674a826c83SDan Williams 			if (valid[i])
1684a826c83SDan Williams 				return i;
1694a826c83SDan Williams 		/* can't have num_valid > 0 but valid[] = { false, false } */
1704a826c83SDan Williams 		WARN_ON(1);
1714a826c83SDan Williams 		break;
1724a826c83SDan Williams 	default:
1734a826c83SDan Williams 		/* pick the best index... */
1744a826c83SDan Williams 		seq = best_seq(__le32_to_cpu(nsindex[0]->seq),
1754a826c83SDan Williams 				__le32_to_cpu(nsindex[1]->seq));
1764a826c83SDan Williams 		if (seq == (__le32_to_cpu(nsindex[1]->seq) & NSINDEX_SEQ_MASK))
1774a826c83SDan Williams 			return 1;
1784a826c83SDan Williams 		else
1794a826c83SDan Williams 			return 0;
1804a826c83SDan Williams 		break;
1814a826c83SDan Williams 	}
1824a826c83SDan Williams 
1834a826c83SDan Williams 	return -1;
1844a826c83SDan Williams }
1854a826c83SDan Williams 
1864a826c83SDan Williams void nd_label_copy(struct nvdimm_drvdata *ndd, struct nd_namespace_index *dst,
1874a826c83SDan Williams 		struct nd_namespace_index *src)
1884a826c83SDan Williams {
1894a826c83SDan Williams 	if (dst && src)
1904a826c83SDan Williams 		/* pass */;
1914a826c83SDan Williams 	else
1924a826c83SDan Williams 		return;
1934a826c83SDan Williams 
1944a826c83SDan Williams 	memcpy(dst, src, sizeof_namespace_index(ndd));
1954a826c83SDan Williams }
1964a826c83SDan Williams 
1974a826c83SDan Williams static struct nd_namespace_label *nd_label_base(struct nvdimm_drvdata *ndd)
1984a826c83SDan Williams {
1994a826c83SDan Williams 	void *base = to_namespace_index(ndd, 0);
2004a826c83SDan Williams 
2014a826c83SDan Williams 	return base + 2 * sizeof_namespace_index(ndd);
2024a826c83SDan Williams }
2034a826c83SDan Williams 
2044a826c83SDan Williams #define for_each_clear_bit_le(bit, addr, size) \
2054a826c83SDan Williams 	for ((bit) = find_next_zero_bit_le((addr), (size), 0);  \
2064a826c83SDan Williams 	     (bit) < (size);                                    \
2074a826c83SDan Williams 	     (bit) = find_next_zero_bit_le((addr), (size), (bit) + 1))
2084a826c83SDan Williams 
2094a826c83SDan Williams /**
2104a826c83SDan Williams  * preamble_current - common variable initialization for nd_label_* routines
2114a826c83SDan Williams  * @ndd: dimm container for the relevant label set
2124a826c83SDan Williams  * @nsindex_out: on return set to the currently active namespace index
2134a826c83SDan Williams  * @free: on return set to the free label bitmap in the index
2144a826c83SDan Williams  * @nslot: on return set to the number of slots in the label space
2154a826c83SDan Williams  */
2164a826c83SDan Williams static bool preamble_current(struct nvdimm_drvdata *ndd,
2174a826c83SDan Williams 		struct nd_namespace_index **nsindex_out,
2184a826c83SDan Williams 		unsigned long **free, u32 *nslot)
2194a826c83SDan Williams {
2204a826c83SDan Williams 	struct nd_namespace_index *nsindex;
2214a826c83SDan Williams 
2224a826c83SDan Williams 	nsindex = to_current_namespace_index(ndd);
2234a826c83SDan Williams 	if (nsindex == NULL)
2244a826c83SDan Williams 		return false;
2254a826c83SDan Williams 
2264a826c83SDan Williams 	*free = (unsigned long *) nsindex->free;
2274a826c83SDan Williams 	*nslot = __le32_to_cpu(nsindex->nslot);
2284a826c83SDan Williams 	*nsindex_out = nsindex;
2294a826c83SDan Williams 
2304a826c83SDan Williams 	return true;
2314a826c83SDan Williams }
2324a826c83SDan Williams 
233*bf9bccc1SDan Williams char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags)
2344a826c83SDan Williams {
2354a826c83SDan Williams 	if (!label_id || !uuid)
2364a826c83SDan Williams 		return NULL;
2374a826c83SDan Williams 	snprintf(label_id->id, ND_LABEL_ID_SIZE, "%s-%pUb",
2384a826c83SDan Williams 			flags & NSLABEL_FLAG_LOCAL ? "blk" : "pmem", uuid);
2394a826c83SDan Williams 	return label_id->id;
2404a826c83SDan Williams }
2414a826c83SDan Williams 
2424a826c83SDan Williams static bool slot_valid(struct nd_namespace_label *nd_label, u32 slot)
2434a826c83SDan Williams {
2444a826c83SDan Williams 	/* check that we are written where we expect to be written */
2454a826c83SDan Williams 	if (slot != __le32_to_cpu(nd_label->slot))
2464a826c83SDan Williams 		return false;
2474a826c83SDan Williams 
2484a826c83SDan Williams 	/* check that DPA allocations are page aligned */
2494a826c83SDan Williams 	if ((__le64_to_cpu(nd_label->dpa)
2504a826c83SDan Williams 				| __le64_to_cpu(nd_label->rawsize)) % SZ_4K)
2514a826c83SDan Williams 		return false;
2524a826c83SDan Williams 
2534a826c83SDan Williams 	return true;
2544a826c83SDan Williams }
2554a826c83SDan Williams 
2564a826c83SDan Williams int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
2574a826c83SDan Williams {
2584a826c83SDan Williams 	struct nd_namespace_index *nsindex;
2594a826c83SDan Williams 	unsigned long *free;
2604a826c83SDan Williams 	u32 nslot, slot;
2614a826c83SDan Williams 
2624a826c83SDan Williams 	if (!preamble_current(ndd, &nsindex, &free, &nslot))
2634a826c83SDan Williams 		return 0; /* no label, nothing to reserve */
2644a826c83SDan Williams 
2654a826c83SDan Williams 	for_each_clear_bit_le(slot, free, nslot) {
2664a826c83SDan Williams 		struct nd_namespace_label *nd_label;
2674a826c83SDan Williams 		struct nd_region *nd_region = NULL;
2684a826c83SDan Williams 		u8 label_uuid[NSLABEL_UUID_LEN];
2694a826c83SDan Williams 		struct nd_label_id label_id;
2704a826c83SDan Williams 		struct resource *res;
2714a826c83SDan Williams 		u32 flags;
2724a826c83SDan Williams 
2734a826c83SDan Williams 		nd_label = nd_label_base(ndd) + slot;
2744a826c83SDan Williams 
2754a826c83SDan Williams 		if (!slot_valid(nd_label, slot))
2764a826c83SDan Williams 			continue;
2774a826c83SDan Williams 
2784a826c83SDan Williams 		memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN);
2794a826c83SDan Williams 		flags = __le32_to_cpu(nd_label->flags);
2804a826c83SDan Williams 		nd_label_gen_id(&label_id, label_uuid, flags);
2814a826c83SDan Williams 		res = nvdimm_allocate_dpa(ndd, &label_id,
2824a826c83SDan Williams 				__le64_to_cpu(nd_label->dpa),
2834a826c83SDan Williams 				__le64_to_cpu(nd_label->rawsize));
2844a826c83SDan Williams 		nd_dbg_dpa(nd_region, ndd, res, "reserve\n");
2854a826c83SDan Williams 		if (!res)
2864a826c83SDan Williams 			return -EBUSY;
2874a826c83SDan Williams 	}
2884a826c83SDan Williams 
2894a826c83SDan Williams 	return 0;
2904a826c83SDan Williams }
291*bf9bccc1SDan Williams 
292*bf9bccc1SDan Williams int nd_label_active_count(struct nvdimm_drvdata *ndd)
293*bf9bccc1SDan Williams {
294*bf9bccc1SDan Williams 	struct nd_namespace_index *nsindex;
295*bf9bccc1SDan Williams 	unsigned long *free;
296*bf9bccc1SDan Williams 	u32 nslot, slot;
297*bf9bccc1SDan Williams 	int count = 0;
298*bf9bccc1SDan Williams 
299*bf9bccc1SDan Williams 	if (!preamble_current(ndd, &nsindex, &free, &nslot))
300*bf9bccc1SDan Williams 		return 0;
301*bf9bccc1SDan Williams 
302*bf9bccc1SDan Williams 	for_each_clear_bit_le(slot, free, nslot) {
303*bf9bccc1SDan Williams 		struct nd_namespace_label *nd_label;
304*bf9bccc1SDan Williams 
305*bf9bccc1SDan Williams 		nd_label = nd_label_base(ndd) + slot;
306*bf9bccc1SDan Williams 
307*bf9bccc1SDan Williams 		if (!slot_valid(nd_label, slot)) {
308*bf9bccc1SDan Williams 			u32 label_slot = __le32_to_cpu(nd_label->slot);
309*bf9bccc1SDan Williams 			u64 size = __le64_to_cpu(nd_label->rawsize);
310*bf9bccc1SDan Williams 			u64 dpa = __le64_to_cpu(nd_label->dpa);
311*bf9bccc1SDan Williams 
312*bf9bccc1SDan Williams 			dev_dbg(ndd->dev,
313*bf9bccc1SDan Williams 				"%s: slot%d invalid slot: %d dpa: %llx size: %llx\n",
314*bf9bccc1SDan Williams 					__func__, slot, label_slot, dpa, size);
315*bf9bccc1SDan Williams 			continue;
316*bf9bccc1SDan Williams 		}
317*bf9bccc1SDan Williams 		count++;
318*bf9bccc1SDan Williams 	}
319*bf9bccc1SDan Williams 	return count;
320*bf9bccc1SDan Williams }
321*bf9bccc1SDan Williams 
322*bf9bccc1SDan Williams struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n)
323*bf9bccc1SDan Williams {
324*bf9bccc1SDan Williams 	struct nd_namespace_index *nsindex;
325*bf9bccc1SDan Williams 	unsigned long *free;
326*bf9bccc1SDan Williams 	u32 nslot, slot;
327*bf9bccc1SDan Williams 
328*bf9bccc1SDan Williams 	if (!preamble_current(ndd, &nsindex, &free, &nslot))
329*bf9bccc1SDan Williams 		return NULL;
330*bf9bccc1SDan Williams 
331*bf9bccc1SDan Williams 	for_each_clear_bit_le(slot, free, nslot) {
332*bf9bccc1SDan Williams 		struct nd_namespace_label *nd_label;
333*bf9bccc1SDan Williams 
334*bf9bccc1SDan Williams 		nd_label = nd_label_base(ndd) + slot;
335*bf9bccc1SDan Williams 		if (!slot_valid(nd_label, slot))
336*bf9bccc1SDan Williams 			continue;
337*bf9bccc1SDan Williams 
338*bf9bccc1SDan Williams 		if (n-- == 0)
339*bf9bccc1SDan Williams 			return nd_label_base(ndd) + slot;
340*bf9bccc1SDan Williams 	}
341*bf9bccc1SDan Williams 
342*bf9bccc1SDan Williams 	return NULL;
343*bf9bccc1SDan Williams }
344