18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Block Translation Table
48c2ecf20Sopenharmony_ci * Copyright (c) 2014-2015, Intel Corporation.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/highmem.h>
78c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
88c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/mutex.h>
128c2ecf20Sopenharmony_ci#include <linux/hdreg.h>
138c2ecf20Sopenharmony_ci#include <linux/genhd.h>
148c2ecf20Sopenharmony_ci#include <linux/sizes.h>
158c2ecf20Sopenharmony_ci#include <linux/ndctl.h>
168c2ecf20Sopenharmony_ci#include <linux/fs.h>
178c2ecf20Sopenharmony_ci#include <linux/nd.h>
188c2ecf20Sopenharmony_ci#include <linux/backing-dev.h>
198c2ecf20Sopenharmony_ci#include "btt.h"
208c2ecf20Sopenharmony_ci#include "nd.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cienum log_ent_request {
238c2ecf20Sopenharmony_ci	LOG_NEW_ENT = 0,
248c2ecf20Sopenharmony_ci	LOG_OLD_ENT
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic struct device *to_dev(struct arena_info *arena)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	return &arena->nd_btt->dev;
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic u64 adjust_initial_offset(struct nd_btt *nd_btt, u64 offset)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	return offset + nd_btt->initial_offset;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic int arena_read_bytes(struct arena_info *arena, resource_size_t offset,
388c2ecf20Sopenharmony_ci		void *buf, size_t n, unsigned long flags)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct nd_btt *nd_btt = arena->nd_btt;
418c2ecf20Sopenharmony_ci	struct nd_namespace_common *ndns = nd_btt->ndns;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* arena offsets may be shifted from the base of the device */
448c2ecf20Sopenharmony_ci	offset = adjust_initial_offset(nd_btt, offset);
458c2ecf20Sopenharmony_ci	return nvdimm_read_bytes(ndns, offset, buf, n, flags);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int arena_write_bytes(struct arena_info *arena, resource_size_t offset,
498c2ecf20Sopenharmony_ci		void *buf, size_t n, unsigned long flags)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct nd_btt *nd_btt = arena->nd_btt;
528c2ecf20Sopenharmony_ci	struct nd_namespace_common *ndns = nd_btt->ndns;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* arena offsets may be shifted from the base of the device */
558c2ecf20Sopenharmony_ci	offset = adjust_initial_offset(nd_btt, offset);
568c2ecf20Sopenharmony_ci	return nvdimm_write_bytes(ndns, offset, buf, n, flags);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int btt_info_write(struct arena_info *arena, struct btt_sb *super)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	int ret;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	/*
648c2ecf20Sopenharmony_ci	 * infooff and info2off should always be at least 512B aligned.
658c2ecf20Sopenharmony_ci	 * We rely on that to make sure rw_bytes does error clearing
668c2ecf20Sopenharmony_ci	 * correctly, so make sure that is the case.
678c2ecf20Sopenharmony_ci	 */
688c2ecf20Sopenharmony_ci	dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->infooff, 512),
698c2ecf20Sopenharmony_ci		"arena->infooff: %#llx is unaligned\n", arena->infooff);
708c2ecf20Sopenharmony_ci	dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->info2off, 512),
718c2ecf20Sopenharmony_ci		"arena->info2off: %#llx is unaligned\n", arena->info2off);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	ret = arena_write_bytes(arena, arena->info2off, super,
748c2ecf20Sopenharmony_ci			sizeof(struct btt_sb), 0);
758c2ecf20Sopenharmony_ci	if (ret)
768c2ecf20Sopenharmony_ci		return ret;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return arena_write_bytes(arena, arena->infooff, super,
798c2ecf20Sopenharmony_ci			sizeof(struct btt_sb), 0);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int btt_info_read(struct arena_info *arena, struct btt_sb *super)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	return arena_read_bytes(arena, arena->infooff, super,
858c2ecf20Sopenharmony_ci			sizeof(struct btt_sb), 0);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/*
898c2ecf20Sopenharmony_ci * 'raw' version of btt_map write
908c2ecf20Sopenharmony_ci * Assumptions:
918c2ecf20Sopenharmony_ci *   mapping is in little-endian
928c2ecf20Sopenharmony_ci *   mapping contains 'E' and 'Z' flags as desired
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_cistatic int __btt_map_write(struct arena_info *arena, u32 lba, __le32 mapping,
958c2ecf20Sopenharmony_ci		unsigned long flags)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	u64 ns_off = arena->mapoff + (lba * MAP_ENT_SIZE);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (unlikely(lba >= arena->external_nlba))
1008c2ecf20Sopenharmony_ci		dev_err_ratelimited(to_dev(arena),
1018c2ecf20Sopenharmony_ci			"%s: lba %#x out of range (max: %#x)\n",
1028c2ecf20Sopenharmony_ci			__func__, lba, arena->external_nlba);
1038c2ecf20Sopenharmony_ci	return arena_write_bytes(arena, ns_off, &mapping, MAP_ENT_SIZE, flags);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int btt_map_write(struct arena_info *arena, u32 lba, u32 mapping,
1078c2ecf20Sopenharmony_ci			u32 z_flag, u32 e_flag, unsigned long rwb_flags)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	u32 ze;
1108c2ecf20Sopenharmony_ci	__le32 mapping_le;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/*
1138c2ecf20Sopenharmony_ci	 * This 'mapping' is supposed to be just the LBA mapping, without
1148c2ecf20Sopenharmony_ci	 * any flags set, so strip the flag bits.
1158c2ecf20Sopenharmony_ci	 */
1168c2ecf20Sopenharmony_ci	mapping = ent_lba(mapping);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ze = (z_flag << 1) + e_flag;
1198c2ecf20Sopenharmony_ci	switch (ze) {
1208c2ecf20Sopenharmony_ci	case 0:
1218c2ecf20Sopenharmony_ci		/*
1228c2ecf20Sopenharmony_ci		 * We want to set neither of the Z or E flags, and
1238c2ecf20Sopenharmony_ci		 * in the actual layout, this means setting the bit
1248c2ecf20Sopenharmony_ci		 * positions of both to '1' to indicate a 'normal'
1258c2ecf20Sopenharmony_ci		 * map entry
1268c2ecf20Sopenharmony_ci		 */
1278c2ecf20Sopenharmony_ci		mapping |= MAP_ENT_NORMAL;
1288c2ecf20Sopenharmony_ci		break;
1298c2ecf20Sopenharmony_ci	case 1:
1308c2ecf20Sopenharmony_ci		mapping |= (1 << MAP_ERR_SHIFT);
1318c2ecf20Sopenharmony_ci		break;
1328c2ecf20Sopenharmony_ci	case 2:
1338c2ecf20Sopenharmony_ci		mapping |= (1 << MAP_TRIM_SHIFT);
1348c2ecf20Sopenharmony_ci		break;
1358c2ecf20Sopenharmony_ci	default:
1368c2ecf20Sopenharmony_ci		/*
1378c2ecf20Sopenharmony_ci		 * The case where Z and E are both sent in as '1' could be
1388c2ecf20Sopenharmony_ci		 * construed as a valid 'normal' case, but we decide not to,
1398c2ecf20Sopenharmony_ci		 * to avoid confusion
1408c2ecf20Sopenharmony_ci		 */
1418c2ecf20Sopenharmony_ci		dev_err_ratelimited(to_dev(arena),
1428c2ecf20Sopenharmony_ci			"Invalid use of Z and E flags\n");
1438c2ecf20Sopenharmony_ci		return -EIO;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	mapping_le = cpu_to_le32(mapping);
1478c2ecf20Sopenharmony_ci	return __btt_map_write(arena, lba, mapping_le, rwb_flags);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int btt_map_read(struct arena_info *arena, u32 lba, u32 *mapping,
1518c2ecf20Sopenharmony_ci			int *trim, int *error, unsigned long rwb_flags)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	int ret;
1548c2ecf20Sopenharmony_ci	__le32 in;
1558c2ecf20Sopenharmony_ci	u32 raw_mapping, postmap, ze, z_flag, e_flag;
1568c2ecf20Sopenharmony_ci	u64 ns_off = arena->mapoff + (lba * MAP_ENT_SIZE);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (unlikely(lba >= arena->external_nlba))
1598c2ecf20Sopenharmony_ci		dev_err_ratelimited(to_dev(arena),
1608c2ecf20Sopenharmony_ci			"%s: lba %#x out of range (max: %#x)\n",
1618c2ecf20Sopenharmony_ci			__func__, lba, arena->external_nlba);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	ret = arena_read_bytes(arena, ns_off, &in, MAP_ENT_SIZE, rwb_flags);
1648c2ecf20Sopenharmony_ci	if (ret)
1658c2ecf20Sopenharmony_ci		return ret;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	raw_mapping = le32_to_cpu(in);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	z_flag = ent_z_flag(raw_mapping);
1708c2ecf20Sopenharmony_ci	e_flag = ent_e_flag(raw_mapping);
1718c2ecf20Sopenharmony_ci	ze = (z_flag << 1) + e_flag;
1728c2ecf20Sopenharmony_ci	postmap = ent_lba(raw_mapping);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* Reuse the {z,e}_flag variables for *trim and *error */
1758c2ecf20Sopenharmony_ci	z_flag = 0;
1768c2ecf20Sopenharmony_ci	e_flag = 0;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	switch (ze) {
1798c2ecf20Sopenharmony_ci	case 0:
1808c2ecf20Sopenharmony_ci		/* Initial state. Return postmap = premap */
1818c2ecf20Sopenharmony_ci		*mapping = lba;
1828c2ecf20Sopenharmony_ci		break;
1838c2ecf20Sopenharmony_ci	case 1:
1848c2ecf20Sopenharmony_ci		*mapping = postmap;
1858c2ecf20Sopenharmony_ci		e_flag = 1;
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci	case 2:
1888c2ecf20Sopenharmony_ci		*mapping = postmap;
1898c2ecf20Sopenharmony_ci		z_flag = 1;
1908c2ecf20Sopenharmony_ci		break;
1918c2ecf20Sopenharmony_ci	case 3:
1928c2ecf20Sopenharmony_ci		*mapping = postmap;
1938c2ecf20Sopenharmony_ci		break;
1948c2ecf20Sopenharmony_ci	default:
1958c2ecf20Sopenharmony_ci		return -EIO;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (trim)
1998c2ecf20Sopenharmony_ci		*trim = z_flag;
2008c2ecf20Sopenharmony_ci	if (error)
2018c2ecf20Sopenharmony_ci		*error = e_flag;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return ret;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic int btt_log_group_read(struct arena_info *arena, u32 lane,
2078c2ecf20Sopenharmony_ci			struct log_group *log)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	return arena_read_bytes(arena,
2108c2ecf20Sopenharmony_ci			arena->logoff + (lane * LOG_GRP_SIZE), log,
2118c2ecf20Sopenharmony_ci			LOG_GRP_SIZE, 0);
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic struct dentry *debugfs_root;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic void arena_debugfs_init(struct arena_info *a, struct dentry *parent,
2178c2ecf20Sopenharmony_ci				int idx)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	char dirname[32];
2208c2ecf20Sopenharmony_ci	struct dentry *d;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/* If for some reason, parent bttN was not created, exit */
2238c2ecf20Sopenharmony_ci	if (!parent)
2248c2ecf20Sopenharmony_ci		return;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	snprintf(dirname, 32, "arena%d", idx);
2278c2ecf20Sopenharmony_ci	d = debugfs_create_dir(dirname, parent);
2288c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(d))
2298c2ecf20Sopenharmony_ci		return;
2308c2ecf20Sopenharmony_ci	a->debugfs_dir = d;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	debugfs_create_x64("size", S_IRUGO, d, &a->size);
2338c2ecf20Sopenharmony_ci	debugfs_create_x64("external_lba_start", S_IRUGO, d,
2348c2ecf20Sopenharmony_ci				&a->external_lba_start);
2358c2ecf20Sopenharmony_ci	debugfs_create_x32("internal_nlba", S_IRUGO, d, &a->internal_nlba);
2368c2ecf20Sopenharmony_ci	debugfs_create_u32("internal_lbasize", S_IRUGO, d,
2378c2ecf20Sopenharmony_ci				&a->internal_lbasize);
2388c2ecf20Sopenharmony_ci	debugfs_create_x32("external_nlba", S_IRUGO, d, &a->external_nlba);
2398c2ecf20Sopenharmony_ci	debugfs_create_u32("external_lbasize", S_IRUGO, d,
2408c2ecf20Sopenharmony_ci				&a->external_lbasize);
2418c2ecf20Sopenharmony_ci	debugfs_create_u32("nfree", S_IRUGO, d, &a->nfree);
2428c2ecf20Sopenharmony_ci	debugfs_create_u16("version_major", S_IRUGO, d, &a->version_major);
2438c2ecf20Sopenharmony_ci	debugfs_create_u16("version_minor", S_IRUGO, d, &a->version_minor);
2448c2ecf20Sopenharmony_ci	debugfs_create_x64("nextoff", S_IRUGO, d, &a->nextoff);
2458c2ecf20Sopenharmony_ci	debugfs_create_x64("infooff", S_IRUGO, d, &a->infooff);
2468c2ecf20Sopenharmony_ci	debugfs_create_x64("dataoff", S_IRUGO, d, &a->dataoff);
2478c2ecf20Sopenharmony_ci	debugfs_create_x64("mapoff", S_IRUGO, d, &a->mapoff);
2488c2ecf20Sopenharmony_ci	debugfs_create_x64("logoff", S_IRUGO, d, &a->logoff);
2498c2ecf20Sopenharmony_ci	debugfs_create_x64("info2off", S_IRUGO, d, &a->info2off);
2508c2ecf20Sopenharmony_ci	debugfs_create_x32("flags", S_IRUGO, d, &a->flags);
2518c2ecf20Sopenharmony_ci	debugfs_create_u32("log_index_0", S_IRUGO, d, &a->log_index[0]);
2528c2ecf20Sopenharmony_ci	debugfs_create_u32("log_index_1", S_IRUGO, d, &a->log_index[1]);
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic void btt_debugfs_init(struct btt *btt)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	int i = 0;
2588c2ecf20Sopenharmony_ci	struct arena_info *arena;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	btt->debugfs_dir = debugfs_create_dir(dev_name(&btt->nd_btt->dev),
2618c2ecf20Sopenharmony_ci						debugfs_root);
2628c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(btt->debugfs_dir))
2638c2ecf20Sopenharmony_ci		return;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	list_for_each_entry(arena, &btt->arena_list, list) {
2668c2ecf20Sopenharmony_ci		arena_debugfs_init(arena, btt->debugfs_dir, i);
2678c2ecf20Sopenharmony_ci		i++;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic u32 log_seq(struct log_group *log, int log_idx)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	return le32_to_cpu(log->ent[log_idx].seq);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci/*
2778c2ecf20Sopenharmony_ci * This function accepts two log entries, and uses the
2788c2ecf20Sopenharmony_ci * sequence number to find the 'older' entry.
2798c2ecf20Sopenharmony_ci * It also updates the sequence number in this old entry to
2808c2ecf20Sopenharmony_ci * make it the 'new' one if the mark_flag is set.
2818c2ecf20Sopenharmony_ci * Finally, it returns which of the entries was the older one.
2828c2ecf20Sopenharmony_ci *
2838c2ecf20Sopenharmony_ci * TODO The logic feels a bit kludge-y. make it better..
2848c2ecf20Sopenharmony_ci */
2858c2ecf20Sopenharmony_cistatic int btt_log_get_old(struct arena_info *a, struct log_group *log)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	int idx0 = a->log_index[0];
2888c2ecf20Sopenharmony_ci	int idx1 = a->log_index[1];
2898c2ecf20Sopenharmony_ci	int old;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/*
2928c2ecf20Sopenharmony_ci	 * the first ever time this is seen, the entry goes into [0]
2938c2ecf20Sopenharmony_ci	 * the next time, the following logic works out to put this
2948c2ecf20Sopenharmony_ci	 * (next) entry into [1]
2958c2ecf20Sopenharmony_ci	 */
2968c2ecf20Sopenharmony_ci	if (log_seq(log, idx0) == 0) {
2978c2ecf20Sopenharmony_ci		log->ent[idx0].seq = cpu_to_le32(1);
2988c2ecf20Sopenharmony_ci		return 0;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (log_seq(log, idx0) == log_seq(log, idx1))
3028c2ecf20Sopenharmony_ci		return -EINVAL;
3038c2ecf20Sopenharmony_ci	if (log_seq(log, idx0) + log_seq(log, idx1) > 5)
3048c2ecf20Sopenharmony_ci		return -EINVAL;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (log_seq(log, idx0) < log_seq(log, idx1)) {
3078c2ecf20Sopenharmony_ci		if ((log_seq(log, idx1) - log_seq(log, idx0)) == 1)
3088c2ecf20Sopenharmony_ci			old = 0;
3098c2ecf20Sopenharmony_ci		else
3108c2ecf20Sopenharmony_ci			old = 1;
3118c2ecf20Sopenharmony_ci	} else {
3128c2ecf20Sopenharmony_ci		if ((log_seq(log, idx0) - log_seq(log, idx1)) == 1)
3138c2ecf20Sopenharmony_ci			old = 1;
3148c2ecf20Sopenharmony_ci		else
3158c2ecf20Sopenharmony_ci			old = 0;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return old;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci/*
3228c2ecf20Sopenharmony_ci * This function copies the desired (old/new) log entry into ent if
3238c2ecf20Sopenharmony_ci * it is not NULL. It returns the sub-slot number (0 or 1)
3248c2ecf20Sopenharmony_ci * where the desired log entry was found. Negative return values
3258c2ecf20Sopenharmony_ci * indicate errors.
3268c2ecf20Sopenharmony_ci */
3278c2ecf20Sopenharmony_cistatic int btt_log_read(struct arena_info *arena, u32 lane,
3288c2ecf20Sopenharmony_ci			struct log_entry *ent, int old_flag)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	int ret;
3318c2ecf20Sopenharmony_ci	int old_ent, ret_ent;
3328c2ecf20Sopenharmony_ci	struct log_group log;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	ret = btt_log_group_read(arena, lane, &log);
3358c2ecf20Sopenharmony_ci	if (ret)
3368c2ecf20Sopenharmony_ci		return -EIO;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	old_ent = btt_log_get_old(arena, &log);
3398c2ecf20Sopenharmony_ci	if (old_ent < 0 || old_ent > 1) {
3408c2ecf20Sopenharmony_ci		dev_err(to_dev(arena),
3418c2ecf20Sopenharmony_ci				"log corruption (%d): lane %d seq [%d, %d]\n",
3428c2ecf20Sopenharmony_ci				old_ent, lane, log.ent[arena->log_index[0]].seq,
3438c2ecf20Sopenharmony_ci				log.ent[arena->log_index[1]].seq);
3448c2ecf20Sopenharmony_ci		/* TODO set error state? */
3458c2ecf20Sopenharmony_ci		return -EIO;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	ret_ent = (old_flag ? old_ent : (1 - old_ent));
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (ent != NULL)
3518c2ecf20Sopenharmony_ci		memcpy(ent, &log.ent[arena->log_index[ret_ent]], LOG_ENT_SIZE);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	return ret_ent;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci/*
3578c2ecf20Sopenharmony_ci * This function commits a log entry to media
3588c2ecf20Sopenharmony_ci * It does _not_ prepare the freelist entry for the next write
3598c2ecf20Sopenharmony_ci * btt_flog_write is the wrapper for updating the freelist elements
3608c2ecf20Sopenharmony_ci */
3618c2ecf20Sopenharmony_cistatic int __btt_log_write(struct arena_info *arena, u32 lane,
3628c2ecf20Sopenharmony_ci			u32 sub, struct log_entry *ent, unsigned long flags)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	int ret;
3658c2ecf20Sopenharmony_ci	u32 group_slot = arena->log_index[sub];
3668c2ecf20Sopenharmony_ci	unsigned int log_half = LOG_ENT_SIZE / 2;
3678c2ecf20Sopenharmony_ci	void *src = ent;
3688c2ecf20Sopenharmony_ci	u64 ns_off;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	ns_off = arena->logoff + (lane * LOG_GRP_SIZE) +
3718c2ecf20Sopenharmony_ci		(group_slot * LOG_ENT_SIZE);
3728c2ecf20Sopenharmony_ci	/* split the 16B write into atomic, durable halves */
3738c2ecf20Sopenharmony_ci	ret = arena_write_bytes(arena, ns_off, src, log_half, flags);
3748c2ecf20Sopenharmony_ci	if (ret)
3758c2ecf20Sopenharmony_ci		return ret;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	ns_off += log_half;
3788c2ecf20Sopenharmony_ci	src += log_half;
3798c2ecf20Sopenharmony_ci	return arena_write_bytes(arena, ns_off, src, log_half, flags);
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic int btt_flog_write(struct arena_info *arena, u32 lane, u32 sub,
3838c2ecf20Sopenharmony_ci			struct log_entry *ent)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	int ret;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	ret = __btt_log_write(arena, lane, sub, ent, NVDIMM_IO_ATOMIC);
3888c2ecf20Sopenharmony_ci	if (ret)
3898c2ecf20Sopenharmony_ci		return ret;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* prepare the next free entry */
3928c2ecf20Sopenharmony_ci	arena->freelist[lane].sub = 1 - arena->freelist[lane].sub;
3938c2ecf20Sopenharmony_ci	if (++(arena->freelist[lane].seq) == 4)
3948c2ecf20Sopenharmony_ci		arena->freelist[lane].seq = 1;
3958c2ecf20Sopenharmony_ci	if (ent_e_flag(le32_to_cpu(ent->old_map)))
3968c2ecf20Sopenharmony_ci		arena->freelist[lane].has_err = 1;
3978c2ecf20Sopenharmony_ci	arena->freelist[lane].block = ent_lba(le32_to_cpu(ent->old_map));
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	return ret;
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci/*
4038c2ecf20Sopenharmony_ci * This function initializes the BTT map to the initial state, which is
4048c2ecf20Sopenharmony_ci * all-zeroes, and indicates an identity mapping
4058c2ecf20Sopenharmony_ci */
4068c2ecf20Sopenharmony_cistatic int btt_map_init(struct arena_info *arena)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	int ret = -EINVAL;
4098c2ecf20Sopenharmony_ci	void *zerobuf;
4108c2ecf20Sopenharmony_ci	size_t offset = 0;
4118c2ecf20Sopenharmony_ci	size_t chunk_size = SZ_2M;
4128c2ecf20Sopenharmony_ci	size_t mapsize = arena->logoff - arena->mapoff;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	zerobuf = kzalloc(chunk_size, GFP_KERNEL);
4158c2ecf20Sopenharmony_ci	if (!zerobuf)
4168c2ecf20Sopenharmony_ci		return -ENOMEM;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/*
4198c2ecf20Sopenharmony_ci	 * mapoff should always be at least 512B  aligned. We rely on that to
4208c2ecf20Sopenharmony_ci	 * make sure rw_bytes does error clearing correctly, so make sure that
4218c2ecf20Sopenharmony_ci	 * is the case.
4228c2ecf20Sopenharmony_ci	 */
4238c2ecf20Sopenharmony_ci	dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->mapoff, 512),
4248c2ecf20Sopenharmony_ci		"arena->mapoff: %#llx is unaligned\n", arena->mapoff);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	while (mapsize) {
4278c2ecf20Sopenharmony_ci		size_t size = min(mapsize, chunk_size);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		dev_WARN_ONCE(to_dev(arena), size < 512,
4308c2ecf20Sopenharmony_ci			"chunk size: %#zx is unaligned\n", size);
4318c2ecf20Sopenharmony_ci		ret = arena_write_bytes(arena, arena->mapoff + offset, zerobuf,
4328c2ecf20Sopenharmony_ci				size, 0);
4338c2ecf20Sopenharmony_ci		if (ret)
4348c2ecf20Sopenharmony_ci			goto free;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		offset += size;
4378c2ecf20Sopenharmony_ci		mapsize -= size;
4388c2ecf20Sopenharmony_ci		cond_resched();
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci free:
4428c2ecf20Sopenharmony_ci	kfree(zerobuf);
4438c2ecf20Sopenharmony_ci	return ret;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci/*
4478c2ecf20Sopenharmony_ci * This function initializes the BTT log with 'fake' entries pointing
4488c2ecf20Sopenharmony_ci * to the initial reserved set of blocks as being free
4498c2ecf20Sopenharmony_ci */
4508c2ecf20Sopenharmony_cistatic int btt_log_init(struct arena_info *arena)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	size_t logsize = arena->info2off - arena->logoff;
4538c2ecf20Sopenharmony_ci	size_t chunk_size = SZ_4K, offset = 0;
4548c2ecf20Sopenharmony_ci	struct log_entry ent;
4558c2ecf20Sopenharmony_ci	void *zerobuf;
4568c2ecf20Sopenharmony_ci	int ret;
4578c2ecf20Sopenharmony_ci	u32 i;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	zerobuf = kzalloc(chunk_size, GFP_KERNEL);
4608c2ecf20Sopenharmony_ci	if (!zerobuf)
4618c2ecf20Sopenharmony_ci		return -ENOMEM;
4628c2ecf20Sopenharmony_ci	/*
4638c2ecf20Sopenharmony_ci	 * logoff should always be at least 512B  aligned. We rely on that to
4648c2ecf20Sopenharmony_ci	 * make sure rw_bytes does error clearing correctly, so make sure that
4658c2ecf20Sopenharmony_ci	 * is the case.
4668c2ecf20Sopenharmony_ci	 */
4678c2ecf20Sopenharmony_ci	dev_WARN_ONCE(to_dev(arena), !IS_ALIGNED(arena->logoff, 512),
4688c2ecf20Sopenharmony_ci		"arena->logoff: %#llx is unaligned\n", arena->logoff);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	while (logsize) {
4718c2ecf20Sopenharmony_ci		size_t size = min(logsize, chunk_size);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		dev_WARN_ONCE(to_dev(arena), size < 512,
4748c2ecf20Sopenharmony_ci			"chunk size: %#zx is unaligned\n", size);
4758c2ecf20Sopenharmony_ci		ret = arena_write_bytes(arena, arena->logoff + offset, zerobuf,
4768c2ecf20Sopenharmony_ci				size, 0);
4778c2ecf20Sopenharmony_ci		if (ret)
4788c2ecf20Sopenharmony_ci			goto free;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		offset += size;
4818c2ecf20Sopenharmony_ci		logsize -= size;
4828c2ecf20Sopenharmony_ci		cond_resched();
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	for (i = 0; i < arena->nfree; i++) {
4868c2ecf20Sopenharmony_ci		ent.lba = cpu_to_le32(i);
4878c2ecf20Sopenharmony_ci		ent.old_map = cpu_to_le32(arena->external_nlba + i);
4888c2ecf20Sopenharmony_ci		ent.new_map = cpu_to_le32(arena->external_nlba + i);
4898c2ecf20Sopenharmony_ci		ent.seq = cpu_to_le32(LOG_SEQ_INIT);
4908c2ecf20Sopenharmony_ci		ret = __btt_log_write(arena, i, 0, &ent, 0);
4918c2ecf20Sopenharmony_ci		if (ret)
4928c2ecf20Sopenharmony_ci			goto free;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci free:
4968c2ecf20Sopenharmony_ci	kfree(zerobuf);
4978c2ecf20Sopenharmony_ci	return ret;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic u64 to_namespace_offset(struct arena_info *arena, u64 lba)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	return arena->dataoff + ((u64)lba * arena->internal_lbasize);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic int arena_clear_freelist_error(struct arena_info *arena, u32 lane)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	int ret = 0;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	if (arena->freelist[lane].has_err) {
5108c2ecf20Sopenharmony_ci		void *zero_page = page_address(ZERO_PAGE(0));
5118c2ecf20Sopenharmony_ci		u32 lba = arena->freelist[lane].block;
5128c2ecf20Sopenharmony_ci		u64 nsoff = to_namespace_offset(arena, lba);
5138c2ecf20Sopenharmony_ci		unsigned long len = arena->sector_size;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci		mutex_lock(&arena->err_lock);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		while (len) {
5188c2ecf20Sopenharmony_ci			unsigned long chunk = min(len, PAGE_SIZE);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci			ret = arena_write_bytes(arena, nsoff, zero_page,
5218c2ecf20Sopenharmony_ci				chunk, 0);
5228c2ecf20Sopenharmony_ci			if (ret)
5238c2ecf20Sopenharmony_ci				break;
5248c2ecf20Sopenharmony_ci			len -= chunk;
5258c2ecf20Sopenharmony_ci			nsoff += chunk;
5268c2ecf20Sopenharmony_ci			if (len == 0)
5278c2ecf20Sopenharmony_ci				arena->freelist[lane].has_err = 0;
5288c2ecf20Sopenharmony_ci		}
5298c2ecf20Sopenharmony_ci		mutex_unlock(&arena->err_lock);
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci	return ret;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic int btt_freelist_init(struct arena_info *arena)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	int new, ret;
5378c2ecf20Sopenharmony_ci	struct log_entry log_new;
5388c2ecf20Sopenharmony_ci	u32 i, map_entry, log_oldmap, log_newmap;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	arena->freelist = kcalloc(arena->nfree, sizeof(struct free_entry),
5418c2ecf20Sopenharmony_ci					GFP_KERNEL);
5428c2ecf20Sopenharmony_ci	if (!arena->freelist)
5438c2ecf20Sopenharmony_ci		return -ENOMEM;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	for (i = 0; i < arena->nfree; i++) {
5468c2ecf20Sopenharmony_ci		new = btt_log_read(arena, i, &log_new, LOG_NEW_ENT);
5478c2ecf20Sopenharmony_ci		if (new < 0)
5488c2ecf20Sopenharmony_ci			return new;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		/* old and new map entries with any flags stripped out */
5518c2ecf20Sopenharmony_ci		log_oldmap = ent_lba(le32_to_cpu(log_new.old_map));
5528c2ecf20Sopenharmony_ci		log_newmap = ent_lba(le32_to_cpu(log_new.new_map));
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		/* sub points to the next one to be overwritten */
5558c2ecf20Sopenharmony_ci		arena->freelist[i].sub = 1 - new;
5568c2ecf20Sopenharmony_ci		arena->freelist[i].seq = nd_inc_seq(le32_to_cpu(log_new.seq));
5578c2ecf20Sopenharmony_ci		arena->freelist[i].block = log_oldmap;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci		/*
5608c2ecf20Sopenharmony_ci		 * FIXME: if error clearing fails during init, we want to make
5618c2ecf20Sopenharmony_ci		 * the BTT read-only
5628c2ecf20Sopenharmony_ci		 */
5638c2ecf20Sopenharmony_ci		if (ent_e_flag(le32_to_cpu(log_new.old_map)) &&
5648c2ecf20Sopenharmony_ci		    !ent_normal(le32_to_cpu(log_new.old_map))) {
5658c2ecf20Sopenharmony_ci			arena->freelist[i].has_err = 1;
5668c2ecf20Sopenharmony_ci			ret = arena_clear_freelist_error(arena, i);
5678c2ecf20Sopenharmony_ci			if (ret)
5688c2ecf20Sopenharmony_ci				dev_err_ratelimited(to_dev(arena),
5698c2ecf20Sopenharmony_ci					"Unable to clear known errors\n");
5708c2ecf20Sopenharmony_ci		}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci		/* This implies a newly created or untouched flog entry */
5738c2ecf20Sopenharmony_ci		if (log_oldmap == log_newmap)
5748c2ecf20Sopenharmony_ci			continue;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci		/* Check if map recovery is needed */
5778c2ecf20Sopenharmony_ci		ret = btt_map_read(arena, le32_to_cpu(log_new.lba), &map_entry,
5788c2ecf20Sopenharmony_ci				NULL, NULL, 0);
5798c2ecf20Sopenharmony_ci		if (ret)
5808c2ecf20Sopenharmony_ci			return ret;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci		/*
5838c2ecf20Sopenharmony_ci		 * The map_entry from btt_read_map is stripped of any flag bits,
5848c2ecf20Sopenharmony_ci		 * so use the stripped out versions from the log as well for
5858c2ecf20Sopenharmony_ci		 * testing whether recovery is needed. For restoration, use the
5868c2ecf20Sopenharmony_ci		 * 'raw' version of the log entries as that captured what we
5878c2ecf20Sopenharmony_ci		 * were going to write originally.
5888c2ecf20Sopenharmony_ci		 */
5898c2ecf20Sopenharmony_ci		if ((log_newmap != map_entry) && (log_oldmap == map_entry)) {
5908c2ecf20Sopenharmony_ci			/*
5918c2ecf20Sopenharmony_ci			 * Last transaction wrote the flog, but wasn't able
5928c2ecf20Sopenharmony_ci			 * to complete the map write. So fix up the map.
5938c2ecf20Sopenharmony_ci			 */
5948c2ecf20Sopenharmony_ci			ret = btt_map_write(arena, le32_to_cpu(log_new.lba),
5958c2ecf20Sopenharmony_ci					le32_to_cpu(log_new.new_map), 0, 0, 0);
5968c2ecf20Sopenharmony_ci			if (ret)
5978c2ecf20Sopenharmony_ci				return ret;
5988c2ecf20Sopenharmony_ci		}
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	return 0;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic bool ent_is_padding(struct log_entry *ent)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	return (ent->lba == 0) && (ent->old_map == 0) && (ent->new_map == 0)
6078c2ecf20Sopenharmony_ci		&& (ent->seq == 0);
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci/*
6118c2ecf20Sopenharmony_ci * Detecting valid log indices: We read a log group (see the comments in btt.h
6128c2ecf20Sopenharmony_ci * for a description of a 'log_group' and its 'slots'), and iterate over its
6138c2ecf20Sopenharmony_ci * four slots. We expect that a padding slot will be all-zeroes, and use this
6148c2ecf20Sopenharmony_ci * to detect a padding slot vs. an actual entry.
6158c2ecf20Sopenharmony_ci *
6168c2ecf20Sopenharmony_ci * If a log_group is in the initial state, i.e. hasn't been used since the
6178c2ecf20Sopenharmony_ci * creation of this BTT layout, it will have three of the four slots with
6188c2ecf20Sopenharmony_ci * zeroes. We skip over these log_groups for the detection of log_index. If
6198c2ecf20Sopenharmony_ci * all log_groups are in the initial state (i.e. the BTT has never been
6208c2ecf20Sopenharmony_ci * written to), it is safe to assume the 'new format' of log entries in slots
6218c2ecf20Sopenharmony_ci * (0, 1).
6228c2ecf20Sopenharmony_ci */
6238c2ecf20Sopenharmony_cistatic int log_set_indices(struct arena_info *arena)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	bool idx_set = false, initial_state = true;
6268c2ecf20Sopenharmony_ci	int ret, log_index[2] = {-1, -1};
6278c2ecf20Sopenharmony_ci	u32 i, j, next_idx = 0;
6288c2ecf20Sopenharmony_ci	struct log_group log;
6298c2ecf20Sopenharmony_ci	u32 pad_count = 0;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	for (i = 0; i < arena->nfree; i++) {
6328c2ecf20Sopenharmony_ci		ret = btt_log_group_read(arena, i, &log);
6338c2ecf20Sopenharmony_ci		if (ret < 0)
6348c2ecf20Sopenharmony_ci			return ret;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci		for (j = 0; j < 4; j++) {
6378c2ecf20Sopenharmony_ci			if (!idx_set) {
6388c2ecf20Sopenharmony_ci				if (ent_is_padding(&log.ent[j])) {
6398c2ecf20Sopenharmony_ci					pad_count++;
6408c2ecf20Sopenharmony_ci					continue;
6418c2ecf20Sopenharmony_ci				} else {
6428c2ecf20Sopenharmony_ci					/* Skip if index has been recorded */
6438c2ecf20Sopenharmony_ci					if ((next_idx == 1) &&
6448c2ecf20Sopenharmony_ci						(j == log_index[0]))
6458c2ecf20Sopenharmony_ci						continue;
6468c2ecf20Sopenharmony_ci					/* valid entry, record index */
6478c2ecf20Sopenharmony_ci					log_index[next_idx] = j;
6488c2ecf20Sopenharmony_ci					next_idx++;
6498c2ecf20Sopenharmony_ci				}
6508c2ecf20Sopenharmony_ci				if (next_idx == 2) {
6518c2ecf20Sopenharmony_ci					/* two valid entries found */
6528c2ecf20Sopenharmony_ci					idx_set = true;
6538c2ecf20Sopenharmony_ci				} else if (next_idx > 2) {
6548c2ecf20Sopenharmony_ci					/* too many valid indices */
6558c2ecf20Sopenharmony_ci					return -ENXIO;
6568c2ecf20Sopenharmony_ci				}
6578c2ecf20Sopenharmony_ci			} else {
6588c2ecf20Sopenharmony_ci				/*
6598c2ecf20Sopenharmony_ci				 * once the indices have been set, just verify
6608c2ecf20Sopenharmony_ci				 * that all subsequent log groups are either in
6618c2ecf20Sopenharmony_ci				 * their initial state or follow the same
6628c2ecf20Sopenharmony_ci				 * indices.
6638c2ecf20Sopenharmony_ci				 */
6648c2ecf20Sopenharmony_ci				if (j == log_index[0]) {
6658c2ecf20Sopenharmony_ci					/* entry must be 'valid' */
6668c2ecf20Sopenharmony_ci					if (ent_is_padding(&log.ent[j]))
6678c2ecf20Sopenharmony_ci						return -ENXIO;
6688c2ecf20Sopenharmony_ci				} else if (j == log_index[1]) {
6698c2ecf20Sopenharmony_ci					;
6708c2ecf20Sopenharmony_ci					/*
6718c2ecf20Sopenharmony_ci					 * log_index[1] can be padding if the
6728c2ecf20Sopenharmony_ci					 * lane never got used and it is still
6738c2ecf20Sopenharmony_ci					 * in the initial state (three 'padding'
6748c2ecf20Sopenharmony_ci					 * entries)
6758c2ecf20Sopenharmony_ci					 */
6768c2ecf20Sopenharmony_ci				} else {
6778c2ecf20Sopenharmony_ci					/* entry must be invalid (padding) */
6788c2ecf20Sopenharmony_ci					if (!ent_is_padding(&log.ent[j]))
6798c2ecf20Sopenharmony_ci						return -ENXIO;
6808c2ecf20Sopenharmony_ci				}
6818c2ecf20Sopenharmony_ci			}
6828c2ecf20Sopenharmony_ci		}
6838c2ecf20Sopenharmony_ci		/*
6848c2ecf20Sopenharmony_ci		 * If any of the log_groups have more than one valid,
6858c2ecf20Sopenharmony_ci		 * non-padding entry, then the we are no longer in the
6868c2ecf20Sopenharmony_ci		 * initial_state
6878c2ecf20Sopenharmony_ci		 */
6888c2ecf20Sopenharmony_ci		if (pad_count < 3)
6898c2ecf20Sopenharmony_ci			initial_state = false;
6908c2ecf20Sopenharmony_ci		pad_count = 0;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	if (!initial_state && !idx_set)
6948c2ecf20Sopenharmony_ci		return -ENXIO;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	/*
6978c2ecf20Sopenharmony_ci	 * If all the entries in the log were in the initial state,
6988c2ecf20Sopenharmony_ci	 * assume new padding scheme
6998c2ecf20Sopenharmony_ci	 */
7008c2ecf20Sopenharmony_ci	if (initial_state)
7018c2ecf20Sopenharmony_ci		log_index[1] = 1;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	/*
7048c2ecf20Sopenharmony_ci	 * Only allow the known permutations of log/padding indices,
7058c2ecf20Sopenharmony_ci	 * i.e. (0, 1), and (0, 2)
7068c2ecf20Sopenharmony_ci	 */
7078c2ecf20Sopenharmony_ci	if ((log_index[0] == 0) && ((log_index[1] == 1) || (log_index[1] == 2)))
7088c2ecf20Sopenharmony_ci		; /* known index possibilities */
7098c2ecf20Sopenharmony_ci	else {
7108c2ecf20Sopenharmony_ci		dev_err(to_dev(arena), "Found an unknown padding scheme\n");
7118c2ecf20Sopenharmony_ci		return -ENXIO;
7128c2ecf20Sopenharmony_ci	}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	arena->log_index[0] = log_index[0];
7158c2ecf20Sopenharmony_ci	arena->log_index[1] = log_index[1];
7168c2ecf20Sopenharmony_ci	dev_dbg(to_dev(arena), "log_index_0 = %d\n", log_index[0]);
7178c2ecf20Sopenharmony_ci	dev_dbg(to_dev(arena), "log_index_1 = %d\n", log_index[1]);
7188c2ecf20Sopenharmony_ci	return 0;
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_cistatic int btt_rtt_init(struct arena_info *arena)
7228c2ecf20Sopenharmony_ci{
7238c2ecf20Sopenharmony_ci	arena->rtt = kcalloc(arena->nfree, sizeof(u32), GFP_KERNEL);
7248c2ecf20Sopenharmony_ci	if (arena->rtt == NULL)
7258c2ecf20Sopenharmony_ci		return -ENOMEM;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	return 0;
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic int btt_maplocks_init(struct arena_info *arena)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	u32 i;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	arena->map_locks = kcalloc(arena->nfree, sizeof(struct aligned_lock),
7358c2ecf20Sopenharmony_ci				GFP_KERNEL);
7368c2ecf20Sopenharmony_ci	if (!arena->map_locks)
7378c2ecf20Sopenharmony_ci		return -ENOMEM;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	for (i = 0; i < arena->nfree; i++)
7408c2ecf20Sopenharmony_ci		spin_lock_init(&arena->map_locks[i].lock);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	return 0;
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cistatic struct arena_info *alloc_arena(struct btt *btt, size_t size,
7468c2ecf20Sopenharmony_ci				size_t start, size_t arena_off)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	struct arena_info *arena;
7498c2ecf20Sopenharmony_ci	u64 logsize, mapsize, datasize;
7508c2ecf20Sopenharmony_ci	u64 available = size;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	arena = kzalloc(sizeof(struct arena_info), GFP_KERNEL);
7538c2ecf20Sopenharmony_ci	if (!arena)
7548c2ecf20Sopenharmony_ci		return NULL;
7558c2ecf20Sopenharmony_ci	arena->nd_btt = btt->nd_btt;
7568c2ecf20Sopenharmony_ci	arena->sector_size = btt->sector_size;
7578c2ecf20Sopenharmony_ci	mutex_init(&arena->err_lock);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (!size)
7608c2ecf20Sopenharmony_ci		return arena;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	arena->size = size;
7638c2ecf20Sopenharmony_ci	arena->external_lba_start = start;
7648c2ecf20Sopenharmony_ci	arena->external_lbasize = btt->lbasize;
7658c2ecf20Sopenharmony_ci	arena->internal_lbasize = roundup(arena->external_lbasize,
7668c2ecf20Sopenharmony_ci					INT_LBASIZE_ALIGNMENT);
7678c2ecf20Sopenharmony_ci	arena->nfree = BTT_DEFAULT_NFREE;
7688c2ecf20Sopenharmony_ci	arena->version_major = btt->nd_btt->version_major;
7698c2ecf20Sopenharmony_ci	arena->version_minor = btt->nd_btt->version_minor;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	if (available % BTT_PG_SIZE)
7728c2ecf20Sopenharmony_ci		available -= (available % BTT_PG_SIZE);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	/* Two pages are reserved for the super block and its copy */
7758c2ecf20Sopenharmony_ci	available -= 2 * BTT_PG_SIZE;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	/* The log takes a fixed amount of space based on nfree */
7788c2ecf20Sopenharmony_ci	logsize = roundup(arena->nfree * LOG_GRP_SIZE, BTT_PG_SIZE);
7798c2ecf20Sopenharmony_ci	available -= logsize;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	/* Calculate optimal split between map and data area */
7828c2ecf20Sopenharmony_ci	arena->internal_nlba = div_u64(available - BTT_PG_SIZE,
7838c2ecf20Sopenharmony_ci			arena->internal_lbasize + MAP_ENT_SIZE);
7848c2ecf20Sopenharmony_ci	arena->external_nlba = arena->internal_nlba - arena->nfree;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	mapsize = roundup((arena->external_nlba * MAP_ENT_SIZE), BTT_PG_SIZE);
7878c2ecf20Sopenharmony_ci	datasize = available - mapsize;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	/* 'Absolute' values, relative to start of storage space */
7908c2ecf20Sopenharmony_ci	arena->infooff = arena_off;
7918c2ecf20Sopenharmony_ci	arena->dataoff = arena->infooff + BTT_PG_SIZE;
7928c2ecf20Sopenharmony_ci	arena->mapoff = arena->dataoff + datasize;
7938c2ecf20Sopenharmony_ci	arena->logoff = arena->mapoff + mapsize;
7948c2ecf20Sopenharmony_ci	arena->info2off = arena->logoff + logsize;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	/* Default log indices are (0,1) */
7978c2ecf20Sopenharmony_ci	arena->log_index[0] = 0;
7988c2ecf20Sopenharmony_ci	arena->log_index[1] = 1;
7998c2ecf20Sopenharmony_ci	return arena;
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic void free_arenas(struct btt *btt)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	struct arena_info *arena, *next;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	list_for_each_entry_safe(arena, next, &btt->arena_list, list) {
8078c2ecf20Sopenharmony_ci		list_del(&arena->list);
8088c2ecf20Sopenharmony_ci		kfree(arena->rtt);
8098c2ecf20Sopenharmony_ci		kfree(arena->map_locks);
8108c2ecf20Sopenharmony_ci		kfree(arena->freelist);
8118c2ecf20Sopenharmony_ci		debugfs_remove_recursive(arena->debugfs_dir);
8128c2ecf20Sopenharmony_ci		kfree(arena);
8138c2ecf20Sopenharmony_ci	}
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci/*
8178c2ecf20Sopenharmony_ci * This function reads an existing valid btt superblock and
8188c2ecf20Sopenharmony_ci * populates the corresponding arena_info struct
8198c2ecf20Sopenharmony_ci */
8208c2ecf20Sopenharmony_cistatic void parse_arena_meta(struct arena_info *arena, struct btt_sb *super,
8218c2ecf20Sopenharmony_ci				u64 arena_off)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	arena->internal_nlba = le32_to_cpu(super->internal_nlba);
8248c2ecf20Sopenharmony_ci	arena->internal_lbasize = le32_to_cpu(super->internal_lbasize);
8258c2ecf20Sopenharmony_ci	arena->external_nlba = le32_to_cpu(super->external_nlba);
8268c2ecf20Sopenharmony_ci	arena->external_lbasize = le32_to_cpu(super->external_lbasize);
8278c2ecf20Sopenharmony_ci	arena->nfree = le32_to_cpu(super->nfree);
8288c2ecf20Sopenharmony_ci	arena->version_major = le16_to_cpu(super->version_major);
8298c2ecf20Sopenharmony_ci	arena->version_minor = le16_to_cpu(super->version_minor);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	arena->nextoff = (super->nextoff == 0) ? 0 : (arena_off +
8328c2ecf20Sopenharmony_ci			le64_to_cpu(super->nextoff));
8338c2ecf20Sopenharmony_ci	arena->infooff = arena_off;
8348c2ecf20Sopenharmony_ci	arena->dataoff = arena_off + le64_to_cpu(super->dataoff);
8358c2ecf20Sopenharmony_ci	arena->mapoff = arena_off + le64_to_cpu(super->mapoff);
8368c2ecf20Sopenharmony_ci	arena->logoff = arena_off + le64_to_cpu(super->logoff);
8378c2ecf20Sopenharmony_ci	arena->info2off = arena_off + le64_to_cpu(super->info2off);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	arena->size = (le64_to_cpu(super->nextoff) > 0)
8408c2ecf20Sopenharmony_ci		? (le64_to_cpu(super->nextoff))
8418c2ecf20Sopenharmony_ci		: (arena->info2off - arena->infooff + BTT_PG_SIZE);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	arena->flags = le32_to_cpu(super->flags);
8448c2ecf20Sopenharmony_ci}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_cistatic int discover_arenas(struct btt *btt)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	int ret = 0;
8498c2ecf20Sopenharmony_ci	struct arena_info *arena;
8508c2ecf20Sopenharmony_ci	struct btt_sb *super;
8518c2ecf20Sopenharmony_ci	size_t remaining = btt->rawsize;
8528c2ecf20Sopenharmony_ci	u64 cur_nlba = 0;
8538c2ecf20Sopenharmony_ci	size_t cur_off = 0;
8548c2ecf20Sopenharmony_ci	int num_arenas = 0;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	super = kzalloc(sizeof(*super), GFP_KERNEL);
8578c2ecf20Sopenharmony_ci	if (!super)
8588c2ecf20Sopenharmony_ci		return -ENOMEM;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	while (remaining) {
8618c2ecf20Sopenharmony_ci		/* Alloc memory for arena */
8628c2ecf20Sopenharmony_ci		arena = alloc_arena(btt, 0, 0, 0);
8638c2ecf20Sopenharmony_ci		if (!arena) {
8648c2ecf20Sopenharmony_ci			ret = -ENOMEM;
8658c2ecf20Sopenharmony_ci			goto out_super;
8668c2ecf20Sopenharmony_ci		}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci		arena->infooff = cur_off;
8698c2ecf20Sopenharmony_ci		ret = btt_info_read(arena, super);
8708c2ecf20Sopenharmony_ci		if (ret)
8718c2ecf20Sopenharmony_ci			goto out;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci		if (!nd_btt_arena_is_valid(btt->nd_btt, super)) {
8748c2ecf20Sopenharmony_ci			if (remaining == btt->rawsize) {
8758c2ecf20Sopenharmony_ci				btt->init_state = INIT_NOTFOUND;
8768c2ecf20Sopenharmony_ci				dev_info(to_dev(arena), "No existing arenas\n");
8778c2ecf20Sopenharmony_ci				goto out;
8788c2ecf20Sopenharmony_ci			} else {
8798c2ecf20Sopenharmony_ci				dev_err(to_dev(arena),
8808c2ecf20Sopenharmony_ci						"Found corrupted metadata!\n");
8818c2ecf20Sopenharmony_ci				ret = -ENODEV;
8828c2ecf20Sopenharmony_ci				goto out;
8838c2ecf20Sopenharmony_ci			}
8848c2ecf20Sopenharmony_ci		}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		arena->external_lba_start = cur_nlba;
8878c2ecf20Sopenharmony_ci		parse_arena_meta(arena, super, cur_off);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci		ret = log_set_indices(arena);
8908c2ecf20Sopenharmony_ci		if (ret) {
8918c2ecf20Sopenharmony_ci			dev_err(to_dev(arena),
8928c2ecf20Sopenharmony_ci				"Unable to deduce log/padding indices\n");
8938c2ecf20Sopenharmony_ci			goto out;
8948c2ecf20Sopenharmony_ci		}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci		ret = btt_freelist_init(arena);
8978c2ecf20Sopenharmony_ci		if (ret)
8988c2ecf20Sopenharmony_ci			goto out;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci		ret = btt_rtt_init(arena);
9018c2ecf20Sopenharmony_ci		if (ret)
9028c2ecf20Sopenharmony_ci			goto out;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		ret = btt_maplocks_init(arena);
9058c2ecf20Sopenharmony_ci		if (ret)
9068c2ecf20Sopenharmony_ci			goto out;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci		list_add_tail(&arena->list, &btt->arena_list);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci		remaining -= arena->size;
9118c2ecf20Sopenharmony_ci		cur_off += arena->size;
9128c2ecf20Sopenharmony_ci		cur_nlba += arena->external_nlba;
9138c2ecf20Sopenharmony_ci		num_arenas++;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		if (arena->nextoff == 0)
9168c2ecf20Sopenharmony_ci			break;
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci	btt->num_arenas = num_arenas;
9198c2ecf20Sopenharmony_ci	btt->nlba = cur_nlba;
9208c2ecf20Sopenharmony_ci	btt->init_state = INIT_READY;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	kfree(super);
9238c2ecf20Sopenharmony_ci	return ret;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci out:
9268c2ecf20Sopenharmony_ci	kfree(arena);
9278c2ecf20Sopenharmony_ci	free_arenas(btt);
9288c2ecf20Sopenharmony_ci out_super:
9298c2ecf20Sopenharmony_ci	kfree(super);
9308c2ecf20Sopenharmony_ci	return ret;
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_cistatic int create_arenas(struct btt *btt)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	size_t remaining = btt->rawsize;
9368c2ecf20Sopenharmony_ci	size_t cur_off = 0;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	while (remaining) {
9398c2ecf20Sopenharmony_ci		struct arena_info *arena;
9408c2ecf20Sopenharmony_ci		size_t arena_size = min_t(u64, ARENA_MAX_SIZE, remaining);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci		remaining -= arena_size;
9438c2ecf20Sopenharmony_ci		if (arena_size < ARENA_MIN_SIZE)
9448c2ecf20Sopenharmony_ci			break;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci		arena = alloc_arena(btt, arena_size, btt->nlba, cur_off);
9478c2ecf20Sopenharmony_ci		if (!arena) {
9488c2ecf20Sopenharmony_ci			free_arenas(btt);
9498c2ecf20Sopenharmony_ci			return -ENOMEM;
9508c2ecf20Sopenharmony_ci		}
9518c2ecf20Sopenharmony_ci		btt->nlba += arena->external_nlba;
9528c2ecf20Sopenharmony_ci		if (remaining >= ARENA_MIN_SIZE)
9538c2ecf20Sopenharmony_ci			arena->nextoff = arena->size;
9548c2ecf20Sopenharmony_ci		else
9558c2ecf20Sopenharmony_ci			arena->nextoff = 0;
9568c2ecf20Sopenharmony_ci		cur_off += arena_size;
9578c2ecf20Sopenharmony_ci		list_add_tail(&arena->list, &btt->arena_list);
9588c2ecf20Sopenharmony_ci	}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	return 0;
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci/*
9648c2ecf20Sopenharmony_ci * This function completes arena initialization by writing
9658c2ecf20Sopenharmony_ci * all the metadata.
9668c2ecf20Sopenharmony_ci * It is only called for an uninitialized arena when a write
9678c2ecf20Sopenharmony_ci * to that arena occurs for the first time.
9688c2ecf20Sopenharmony_ci */
9698c2ecf20Sopenharmony_cistatic int btt_arena_write_layout(struct arena_info *arena)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	int ret;
9728c2ecf20Sopenharmony_ci	u64 sum;
9738c2ecf20Sopenharmony_ci	struct btt_sb *super;
9748c2ecf20Sopenharmony_ci	struct nd_btt *nd_btt = arena->nd_btt;
9758c2ecf20Sopenharmony_ci	const u8 *parent_uuid = nd_dev_to_uuid(&nd_btt->ndns->dev);
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	ret = btt_map_init(arena);
9788c2ecf20Sopenharmony_ci	if (ret)
9798c2ecf20Sopenharmony_ci		return ret;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	ret = btt_log_init(arena);
9828c2ecf20Sopenharmony_ci	if (ret)
9838c2ecf20Sopenharmony_ci		return ret;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	super = kzalloc(sizeof(struct btt_sb), GFP_NOIO);
9868c2ecf20Sopenharmony_ci	if (!super)
9878c2ecf20Sopenharmony_ci		return -ENOMEM;
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	strncpy(super->signature, BTT_SIG, BTT_SIG_LEN);
9908c2ecf20Sopenharmony_ci	memcpy(super->uuid, nd_btt->uuid, 16);
9918c2ecf20Sopenharmony_ci	memcpy(super->parent_uuid, parent_uuid, 16);
9928c2ecf20Sopenharmony_ci	super->flags = cpu_to_le32(arena->flags);
9938c2ecf20Sopenharmony_ci	super->version_major = cpu_to_le16(arena->version_major);
9948c2ecf20Sopenharmony_ci	super->version_minor = cpu_to_le16(arena->version_minor);
9958c2ecf20Sopenharmony_ci	super->external_lbasize = cpu_to_le32(arena->external_lbasize);
9968c2ecf20Sopenharmony_ci	super->external_nlba = cpu_to_le32(arena->external_nlba);
9978c2ecf20Sopenharmony_ci	super->internal_lbasize = cpu_to_le32(arena->internal_lbasize);
9988c2ecf20Sopenharmony_ci	super->internal_nlba = cpu_to_le32(arena->internal_nlba);
9998c2ecf20Sopenharmony_ci	super->nfree = cpu_to_le32(arena->nfree);
10008c2ecf20Sopenharmony_ci	super->infosize = cpu_to_le32(sizeof(struct btt_sb));
10018c2ecf20Sopenharmony_ci	super->nextoff = cpu_to_le64(arena->nextoff);
10028c2ecf20Sopenharmony_ci	/*
10038c2ecf20Sopenharmony_ci	 * Subtract arena->infooff (arena start) so numbers are relative
10048c2ecf20Sopenharmony_ci	 * to 'this' arena
10058c2ecf20Sopenharmony_ci	 */
10068c2ecf20Sopenharmony_ci	super->dataoff = cpu_to_le64(arena->dataoff - arena->infooff);
10078c2ecf20Sopenharmony_ci	super->mapoff = cpu_to_le64(arena->mapoff - arena->infooff);
10088c2ecf20Sopenharmony_ci	super->logoff = cpu_to_le64(arena->logoff - arena->infooff);
10098c2ecf20Sopenharmony_ci	super->info2off = cpu_to_le64(arena->info2off - arena->infooff);
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	super->flags = 0;
10128c2ecf20Sopenharmony_ci	sum = nd_sb_checksum((struct nd_gen_sb *) super);
10138c2ecf20Sopenharmony_ci	super->checksum = cpu_to_le64(sum);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	ret = btt_info_write(arena, super);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	kfree(super);
10188c2ecf20Sopenharmony_ci	return ret;
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci/*
10228c2ecf20Sopenharmony_ci * This function completes the initialization for the BTT namespace
10238c2ecf20Sopenharmony_ci * such that it is ready to accept IOs
10248c2ecf20Sopenharmony_ci */
10258c2ecf20Sopenharmony_cistatic int btt_meta_init(struct btt *btt)
10268c2ecf20Sopenharmony_ci{
10278c2ecf20Sopenharmony_ci	int ret = 0;
10288c2ecf20Sopenharmony_ci	struct arena_info *arena;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	mutex_lock(&btt->init_lock);
10318c2ecf20Sopenharmony_ci	list_for_each_entry(arena, &btt->arena_list, list) {
10328c2ecf20Sopenharmony_ci		ret = btt_arena_write_layout(arena);
10338c2ecf20Sopenharmony_ci		if (ret)
10348c2ecf20Sopenharmony_ci			goto unlock;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci		ret = btt_freelist_init(arena);
10378c2ecf20Sopenharmony_ci		if (ret)
10388c2ecf20Sopenharmony_ci			goto unlock;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci		ret = btt_rtt_init(arena);
10418c2ecf20Sopenharmony_ci		if (ret)
10428c2ecf20Sopenharmony_ci			goto unlock;
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci		ret = btt_maplocks_init(arena);
10458c2ecf20Sopenharmony_ci		if (ret)
10468c2ecf20Sopenharmony_ci			goto unlock;
10478c2ecf20Sopenharmony_ci	}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	btt->init_state = INIT_READY;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci unlock:
10528c2ecf20Sopenharmony_ci	mutex_unlock(&btt->init_lock);
10538c2ecf20Sopenharmony_ci	return ret;
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_cistatic u32 btt_meta_size(struct btt *btt)
10578c2ecf20Sopenharmony_ci{
10588c2ecf20Sopenharmony_ci	return btt->lbasize - btt->sector_size;
10598c2ecf20Sopenharmony_ci}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci/*
10628c2ecf20Sopenharmony_ci * This function calculates the arena in which the given LBA lies
10638c2ecf20Sopenharmony_ci * by doing a linear walk. This is acceptable since we expect only
10648c2ecf20Sopenharmony_ci * a few arenas. If we have backing devices that get much larger,
10658c2ecf20Sopenharmony_ci * we can construct a balanced binary tree of arenas at init time
10668c2ecf20Sopenharmony_ci * so that this range search becomes faster.
10678c2ecf20Sopenharmony_ci */
10688c2ecf20Sopenharmony_cistatic int lba_to_arena(struct btt *btt, sector_t sector, __u32 *premap,
10698c2ecf20Sopenharmony_ci				struct arena_info **arena)
10708c2ecf20Sopenharmony_ci{
10718c2ecf20Sopenharmony_ci	struct arena_info *arena_list;
10728c2ecf20Sopenharmony_ci	__u64 lba = div_u64(sector << SECTOR_SHIFT, btt->sector_size);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	list_for_each_entry(arena_list, &btt->arena_list, list) {
10758c2ecf20Sopenharmony_ci		if (lba < arena_list->external_nlba) {
10768c2ecf20Sopenharmony_ci			*arena = arena_list;
10778c2ecf20Sopenharmony_ci			*premap = lba;
10788c2ecf20Sopenharmony_ci			return 0;
10798c2ecf20Sopenharmony_ci		}
10808c2ecf20Sopenharmony_ci		lba -= arena_list->external_nlba;
10818c2ecf20Sopenharmony_ci	}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	return -EIO;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci/*
10878c2ecf20Sopenharmony_ci * The following (lock_map, unlock_map) are mostly just to improve
10888c2ecf20Sopenharmony_ci * readability, since they index into an array of locks
10898c2ecf20Sopenharmony_ci */
10908c2ecf20Sopenharmony_cistatic void lock_map(struct arena_info *arena, u32 premap)
10918c2ecf20Sopenharmony_ci		__acquires(&arena->map_locks[idx].lock)
10928c2ecf20Sopenharmony_ci{
10938c2ecf20Sopenharmony_ci	u32 idx = (premap * MAP_ENT_SIZE / L1_CACHE_BYTES) % arena->nfree;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	spin_lock(&arena->map_locks[idx].lock);
10968c2ecf20Sopenharmony_ci}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_cistatic void unlock_map(struct arena_info *arena, u32 premap)
10998c2ecf20Sopenharmony_ci		__releases(&arena->map_locks[idx].lock)
11008c2ecf20Sopenharmony_ci{
11018c2ecf20Sopenharmony_ci	u32 idx = (premap * MAP_ENT_SIZE / L1_CACHE_BYTES) % arena->nfree;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	spin_unlock(&arena->map_locks[idx].lock);
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_cistatic int btt_data_read(struct arena_info *arena, struct page *page,
11078c2ecf20Sopenharmony_ci			unsigned int off, u32 lba, u32 len)
11088c2ecf20Sopenharmony_ci{
11098c2ecf20Sopenharmony_ci	int ret;
11108c2ecf20Sopenharmony_ci	u64 nsoff = to_namespace_offset(arena, lba);
11118c2ecf20Sopenharmony_ci	void *mem = kmap_atomic(page);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	ret = arena_read_bytes(arena, nsoff, mem + off, len, NVDIMM_IO_ATOMIC);
11148c2ecf20Sopenharmony_ci	kunmap_atomic(mem);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	return ret;
11178c2ecf20Sopenharmony_ci}
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_cistatic int btt_data_write(struct arena_info *arena, u32 lba,
11208c2ecf20Sopenharmony_ci			struct page *page, unsigned int off, u32 len)
11218c2ecf20Sopenharmony_ci{
11228c2ecf20Sopenharmony_ci	int ret;
11238c2ecf20Sopenharmony_ci	u64 nsoff = to_namespace_offset(arena, lba);
11248c2ecf20Sopenharmony_ci	void *mem = kmap_atomic(page);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	ret = arena_write_bytes(arena, nsoff, mem + off, len, NVDIMM_IO_ATOMIC);
11278c2ecf20Sopenharmony_ci	kunmap_atomic(mem);
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	return ret;
11308c2ecf20Sopenharmony_ci}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_cistatic void zero_fill_data(struct page *page, unsigned int off, u32 len)
11338c2ecf20Sopenharmony_ci{
11348c2ecf20Sopenharmony_ci	void *mem = kmap_atomic(page);
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	memset(mem + off, 0, len);
11378c2ecf20Sopenharmony_ci	kunmap_atomic(mem);
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INTEGRITY
11418c2ecf20Sopenharmony_cistatic int btt_rw_integrity(struct btt *btt, struct bio_integrity_payload *bip,
11428c2ecf20Sopenharmony_ci			struct arena_info *arena, u32 postmap, int rw)
11438c2ecf20Sopenharmony_ci{
11448c2ecf20Sopenharmony_ci	unsigned int len = btt_meta_size(btt);
11458c2ecf20Sopenharmony_ci	u64 meta_nsoff;
11468c2ecf20Sopenharmony_ci	int ret = 0;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	if (bip == NULL)
11498c2ecf20Sopenharmony_ci		return 0;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	meta_nsoff = to_namespace_offset(arena, postmap) + btt->sector_size;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	while (len) {
11548c2ecf20Sopenharmony_ci		unsigned int cur_len;
11558c2ecf20Sopenharmony_ci		struct bio_vec bv;
11568c2ecf20Sopenharmony_ci		void *mem;
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci		bv = bvec_iter_bvec(bip->bip_vec, bip->bip_iter);
11598c2ecf20Sopenharmony_ci		/*
11608c2ecf20Sopenharmony_ci		 * The 'bv' obtained from bvec_iter_bvec has its .bv_len and
11618c2ecf20Sopenharmony_ci		 * .bv_offset already adjusted for iter->bi_bvec_done, and we
11628c2ecf20Sopenharmony_ci		 * can use those directly
11638c2ecf20Sopenharmony_ci		 */
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci		cur_len = min(len, bv.bv_len);
11668c2ecf20Sopenharmony_ci		mem = kmap_atomic(bv.bv_page);
11678c2ecf20Sopenharmony_ci		if (rw)
11688c2ecf20Sopenharmony_ci			ret = arena_write_bytes(arena, meta_nsoff,
11698c2ecf20Sopenharmony_ci					mem + bv.bv_offset, cur_len,
11708c2ecf20Sopenharmony_ci					NVDIMM_IO_ATOMIC);
11718c2ecf20Sopenharmony_ci		else
11728c2ecf20Sopenharmony_ci			ret = arena_read_bytes(arena, meta_nsoff,
11738c2ecf20Sopenharmony_ci					mem + bv.bv_offset, cur_len,
11748c2ecf20Sopenharmony_ci					NVDIMM_IO_ATOMIC);
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci		kunmap_atomic(mem);
11778c2ecf20Sopenharmony_ci		if (ret)
11788c2ecf20Sopenharmony_ci			return ret;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci		len -= cur_len;
11818c2ecf20Sopenharmony_ci		meta_nsoff += cur_len;
11828c2ecf20Sopenharmony_ci		if (!bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len))
11838c2ecf20Sopenharmony_ci			return -EIO;
11848c2ecf20Sopenharmony_ci	}
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	return ret;
11878c2ecf20Sopenharmony_ci}
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci#else /* CONFIG_BLK_DEV_INTEGRITY */
11908c2ecf20Sopenharmony_cistatic int btt_rw_integrity(struct btt *btt, struct bio_integrity_payload *bip,
11918c2ecf20Sopenharmony_ci			struct arena_info *arena, u32 postmap, int rw)
11928c2ecf20Sopenharmony_ci{
11938c2ecf20Sopenharmony_ci	return 0;
11948c2ecf20Sopenharmony_ci}
11958c2ecf20Sopenharmony_ci#endif
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_cistatic int btt_read_pg(struct btt *btt, struct bio_integrity_payload *bip,
11988c2ecf20Sopenharmony_ci			struct page *page, unsigned int off, sector_t sector,
11998c2ecf20Sopenharmony_ci			unsigned int len)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	int ret = 0;
12028c2ecf20Sopenharmony_ci	int t_flag, e_flag;
12038c2ecf20Sopenharmony_ci	struct arena_info *arena = NULL;
12048c2ecf20Sopenharmony_ci	u32 lane = 0, premap, postmap;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	while (len) {
12078c2ecf20Sopenharmony_ci		u32 cur_len;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci		lane = nd_region_acquire_lane(btt->nd_region);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci		ret = lba_to_arena(btt, sector, &premap, &arena);
12128c2ecf20Sopenharmony_ci		if (ret)
12138c2ecf20Sopenharmony_ci			goto out_lane;
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci		cur_len = min(btt->sector_size, len);
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci		ret = btt_map_read(arena, premap, &postmap, &t_flag, &e_flag,
12188c2ecf20Sopenharmony_ci				NVDIMM_IO_ATOMIC);
12198c2ecf20Sopenharmony_ci		if (ret)
12208c2ecf20Sopenharmony_ci			goto out_lane;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci		/*
12238c2ecf20Sopenharmony_ci		 * We loop to make sure that the post map LBA didn't change
12248c2ecf20Sopenharmony_ci		 * from under us between writing the RTT and doing the actual
12258c2ecf20Sopenharmony_ci		 * read.
12268c2ecf20Sopenharmony_ci		 */
12278c2ecf20Sopenharmony_ci		while (1) {
12288c2ecf20Sopenharmony_ci			u32 new_map;
12298c2ecf20Sopenharmony_ci			int new_t, new_e;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci			if (t_flag) {
12328c2ecf20Sopenharmony_ci				zero_fill_data(page, off, cur_len);
12338c2ecf20Sopenharmony_ci				goto out_lane;
12348c2ecf20Sopenharmony_ci			}
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci			if (e_flag) {
12378c2ecf20Sopenharmony_ci				ret = -EIO;
12388c2ecf20Sopenharmony_ci				goto out_lane;
12398c2ecf20Sopenharmony_ci			}
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci			arena->rtt[lane] = RTT_VALID | postmap;
12428c2ecf20Sopenharmony_ci			/*
12438c2ecf20Sopenharmony_ci			 * Barrier to make sure this write is not reordered
12448c2ecf20Sopenharmony_ci			 * to do the verification map_read before the RTT store
12458c2ecf20Sopenharmony_ci			 */
12468c2ecf20Sopenharmony_ci			barrier();
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci			ret = btt_map_read(arena, premap, &new_map, &new_t,
12498c2ecf20Sopenharmony_ci						&new_e, NVDIMM_IO_ATOMIC);
12508c2ecf20Sopenharmony_ci			if (ret)
12518c2ecf20Sopenharmony_ci				goto out_rtt;
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci			if ((postmap == new_map) && (t_flag == new_t) &&
12548c2ecf20Sopenharmony_ci					(e_flag == new_e))
12558c2ecf20Sopenharmony_ci				break;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci			postmap = new_map;
12588c2ecf20Sopenharmony_ci			t_flag = new_t;
12598c2ecf20Sopenharmony_ci			e_flag = new_e;
12608c2ecf20Sopenharmony_ci		}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci		ret = btt_data_read(arena, page, off, postmap, cur_len);
12638c2ecf20Sopenharmony_ci		if (ret) {
12648c2ecf20Sopenharmony_ci			/* Media error - set the e_flag */
12658c2ecf20Sopenharmony_ci			if (btt_map_write(arena, premap, postmap, 0, 1, NVDIMM_IO_ATOMIC))
12668c2ecf20Sopenharmony_ci				dev_warn_ratelimited(to_dev(arena),
12678c2ecf20Sopenharmony_ci					"Error persistently tracking bad blocks at %#x\n",
12688c2ecf20Sopenharmony_ci					premap);
12698c2ecf20Sopenharmony_ci			goto out_rtt;
12708c2ecf20Sopenharmony_ci		}
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci		if (bip) {
12738c2ecf20Sopenharmony_ci			ret = btt_rw_integrity(btt, bip, arena, postmap, READ);
12748c2ecf20Sopenharmony_ci			if (ret)
12758c2ecf20Sopenharmony_ci				goto out_rtt;
12768c2ecf20Sopenharmony_ci		}
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci		arena->rtt[lane] = RTT_INVALID;
12798c2ecf20Sopenharmony_ci		nd_region_release_lane(btt->nd_region, lane);
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci		len -= cur_len;
12828c2ecf20Sopenharmony_ci		off += cur_len;
12838c2ecf20Sopenharmony_ci		sector += btt->sector_size >> SECTOR_SHIFT;
12848c2ecf20Sopenharmony_ci	}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	return 0;
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci out_rtt:
12898c2ecf20Sopenharmony_ci	arena->rtt[lane] = RTT_INVALID;
12908c2ecf20Sopenharmony_ci out_lane:
12918c2ecf20Sopenharmony_ci	nd_region_release_lane(btt->nd_region, lane);
12928c2ecf20Sopenharmony_ci	return ret;
12938c2ecf20Sopenharmony_ci}
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci/*
12968c2ecf20Sopenharmony_ci * Normally, arena_{read,write}_bytes will take care of the initial offset
12978c2ecf20Sopenharmony_ci * adjustment, but in the case of btt_is_badblock, where we query is_bad_pmem,
12988c2ecf20Sopenharmony_ci * we need the final, raw namespace offset here
12998c2ecf20Sopenharmony_ci */
13008c2ecf20Sopenharmony_cistatic bool btt_is_badblock(struct btt *btt, struct arena_info *arena,
13018c2ecf20Sopenharmony_ci		u32 postmap)
13028c2ecf20Sopenharmony_ci{
13038c2ecf20Sopenharmony_ci	u64 nsoff = adjust_initial_offset(arena->nd_btt,
13048c2ecf20Sopenharmony_ci			to_namespace_offset(arena, postmap));
13058c2ecf20Sopenharmony_ci	sector_t phys_sector = nsoff >> 9;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	return is_bad_pmem(btt->phys_bb, phys_sector, arena->internal_lbasize);
13088c2ecf20Sopenharmony_ci}
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_cistatic int btt_write_pg(struct btt *btt, struct bio_integrity_payload *bip,
13118c2ecf20Sopenharmony_ci			sector_t sector, struct page *page, unsigned int off,
13128c2ecf20Sopenharmony_ci			unsigned int len)
13138c2ecf20Sopenharmony_ci{
13148c2ecf20Sopenharmony_ci	int ret = 0;
13158c2ecf20Sopenharmony_ci	struct arena_info *arena = NULL;
13168c2ecf20Sopenharmony_ci	u32 premap = 0, old_postmap, new_postmap, lane = 0, i;
13178c2ecf20Sopenharmony_ci	struct log_entry log;
13188c2ecf20Sopenharmony_ci	int sub;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	while (len) {
13218c2ecf20Sopenharmony_ci		u32 cur_len;
13228c2ecf20Sopenharmony_ci		int e_flag;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci retry:
13258c2ecf20Sopenharmony_ci		lane = nd_region_acquire_lane(btt->nd_region);
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci		ret = lba_to_arena(btt, sector, &premap, &arena);
13288c2ecf20Sopenharmony_ci		if (ret)
13298c2ecf20Sopenharmony_ci			goto out_lane;
13308c2ecf20Sopenharmony_ci		cur_len = min(btt->sector_size, len);
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci		if ((arena->flags & IB_FLAG_ERROR_MASK) != 0) {
13338c2ecf20Sopenharmony_ci			ret = -EIO;
13348c2ecf20Sopenharmony_ci			goto out_lane;
13358c2ecf20Sopenharmony_ci		}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci		if (btt_is_badblock(btt, arena, arena->freelist[lane].block))
13388c2ecf20Sopenharmony_ci			arena->freelist[lane].has_err = 1;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci		if (mutex_is_locked(&arena->err_lock)
13418c2ecf20Sopenharmony_ci				|| arena->freelist[lane].has_err) {
13428c2ecf20Sopenharmony_ci			nd_region_release_lane(btt->nd_region, lane);
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci			ret = arena_clear_freelist_error(arena, lane);
13458c2ecf20Sopenharmony_ci			if (ret)
13468c2ecf20Sopenharmony_ci				return ret;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci			/* OK to acquire a different lane/free block */
13498c2ecf20Sopenharmony_ci			goto retry;
13508c2ecf20Sopenharmony_ci		}
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci		new_postmap = arena->freelist[lane].block;
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci		/* Wait if the new block is being read from */
13558c2ecf20Sopenharmony_ci		for (i = 0; i < arena->nfree; i++)
13568c2ecf20Sopenharmony_ci			while (arena->rtt[i] == (RTT_VALID | new_postmap))
13578c2ecf20Sopenharmony_ci				cpu_relax();
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci		if (new_postmap >= arena->internal_nlba) {
13618c2ecf20Sopenharmony_ci			ret = -EIO;
13628c2ecf20Sopenharmony_ci			goto out_lane;
13638c2ecf20Sopenharmony_ci		}
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci		ret = btt_data_write(arena, new_postmap, page, off, cur_len);
13668c2ecf20Sopenharmony_ci		if (ret)
13678c2ecf20Sopenharmony_ci			goto out_lane;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci		if (bip) {
13708c2ecf20Sopenharmony_ci			ret = btt_rw_integrity(btt, bip, arena, new_postmap,
13718c2ecf20Sopenharmony_ci						WRITE);
13728c2ecf20Sopenharmony_ci			if (ret)
13738c2ecf20Sopenharmony_ci				goto out_lane;
13748c2ecf20Sopenharmony_ci		}
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci		lock_map(arena, premap);
13778c2ecf20Sopenharmony_ci		ret = btt_map_read(arena, premap, &old_postmap, NULL, &e_flag,
13788c2ecf20Sopenharmony_ci				NVDIMM_IO_ATOMIC);
13798c2ecf20Sopenharmony_ci		if (ret)
13808c2ecf20Sopenharmony_ci			goto out_map;
13818c2ecf20Sopenharmony_ci		if (old_postmap >= arena->internal_nlba) {
13828c2ecf20Sopenharmony_ci			ret = -EIO;
13838c2ecf20Sopenharmony_ci			goto out_map;
13848c2ecf20Sopenharmony_ci		}
13858c2ecf20Sopenharmony_ci		if (e_flag)
13868c2ecf20Sopenharmony_ci			set_e_flag(old_postmap);
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci		log.lba = cpu_to_le32(premap);
13898c2ecf20Sopenharmony_ci		log.old_map = cpu_to_le32(old_postmap);
13908c2ecf20Sopenharmony_ci		log.new_map = cpu_to_le32(new_postmap);
13918c2ecf20Sopenharmony_ci		log.seq = cpu_to_le32(arena->freelist[lane].seq);
13928c2ecf20Sopenharmony_ci		sub = arena->freelist[lane].sub;
13938c2ecf20Sopenharmony_ci		ret = btt_flog_write(arena, lane, sub, &log);
13948c2ecf20Sopenharmony_ci		if (ret)
13958c2ecf20Sopenharmony_ci			goto out_map;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci		ret = btt_map_write(arena, premap, new_postmap, 0, 0,
13988c2ecf20Sopenharmony_ci			NVDIMM_IO_ATOMIC);
13998c2ecf20Sopenharmony_ci		if (ret)
14008c2ecf20Sopenharmony_ci			goto out_map;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci		unlock_map(arena, premap);
14038c2ecf20Sopenharmony_ci		nd_region_release_lane(btt->nd_region, lane);
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci		if (e_flag) {
14068c2ecf20Sopenharmony_ci			ret = arena_clear_freelist_error(arena, lane);
14078c2ecf20Sopenharmony_ci			if (ret)
14088c2ecf20Sopenharmony_ci				return ret;
14098c2ecf20Sopenharmony_ci		}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci		len -= cur_len;
14128c2ecf20Sopenharmony_ci		off += cur_len;
14138c2ecf20Sopenharmony_ci		sector += btt->sector_size >> SECTOR_SHIFT;
14148c2ecf20Sopenharmony_ci	}
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	return 0;
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci out_map:
14198c2ecf20Sopenharmony_ci	unlock_map(arena, premap);
14208c2ecf20Sopenharmony_ci out_lane:
14218c2ecf20Sopenharmony_ci	nd_region_release_lane(btt->nd_region, lane);
14228c2ecf20Sopenharmony_ci	return ret;
14238c2ecf20Sopenharmony_ci}
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_cistatic int btt_do_bvec(struct btt *btt, struct bio_integrity_payload *bip,
14268c2ecf20Sopenharmony_ci			struct page *page, unsigned int len, unsigned int off,
14278c2ecf20Sopenharmony_ci			unsigned int op, sector_t sector)
14288c2ecf20Sopenharmony_ci{
14298c2ecf20Sopenharmony_ci	int ret;
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	if (!op_is_write(op)) {
14328c2ecf20Sopenharmony_ci		ret = btt_read_pg(btt, bip, page, off, sector, len);
14338c2ecf20Sopenharmony_ci		flush_dcache_page(page);
14348c2ecf20Sopenharmony_ci	} else {
14358c2ecf20Sopenharmony_ci		flush_dcache_page(page);
14368c2ecf20Sopenharmony_ci		ret = btt_write_pg(btt, bip, sector, page, off, len);
14378c2ecf20Sopenharmony_ci	}
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	return ret;
14408c2ecf20Sopenharmony_ci}
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_cistatic blk_qc_t btt_submit_bio(struct bio *bio)
14438c2ecf20Sopenharmony_ci{
14448c2ecf20Sopenharmony_ci	struct bio_integrity_payload *bip = bio_integrity(bio);
14458c2ecf20Sopenharmony_ci	struct btt *btt = bio->bi_disk->private_data;
14468c2ecf20Sopenharmony_ci	struct bvec_iter iter;
14478c2ecf20Sopenharmony_ci	unsigned long start;
14488c2ecf20Sopenharmony_ci	struct bio_vec bvec;
14498c2ecf20Sopenharmony_ci	int err = 0;
14508c2ecf20Sopenharmony_ci	bool do_acct;
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	if (!bio_integrity_prep(bio))
14538c2ecf20Sopenharmony_ci		return BLK_QC_T_NONE;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	do_acct = blk_queue_io_stat(bio->bi_disk->queue);
14568c2ecf20Sopenharmony_ci	if (do_acct)
14578c2ecf20Sopenharmony_ci		start = bio_start_io_acct(bio);
14588c2ecf20Sopenharmony_ci	bio_for_each_segment(bvec, bio, iter) {
14598c2ecf20Sopenharmony_ci		unsigned int len = bvec.bv_len;
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci		if (len > PAGE_SIZE || len < btt->sector_size ||
14628c2ecf20Sopenharmony_ci				len % btt->sector_size) {
14638c2ecf20Sopenharmony_ci			dev_err_ratelimited(&btt->nd_btt->dev,
14648c2ecf20Sopenharmony_ci				"unaligned bio segment (len: %d)\n", len);
14658c2ecf20Sopenharmony_ci			bio->bi_status = BLK_STS_IOERR;
14668c2ecf20Sopenharmony_ci			break;
14678c2ecf20Sopenharmony_ci		}
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci		err = btt_do_bvec(btt, bip, bvec.bv_page, len, bvec.bv_offset,
14708c2ecf20Sopenharmony_ci				  bio_op(bio), iter.bi_sector);
14718c2ecf20Sopenharmony_ci		if (err) {
14728c2ecf20Sopenharmony_ci			dev_err(&btt->nd_btt->dev,
14738c2ecf20Sopenharmony_ci					"io error in %s sector %lld, len %d,\n",
14748c2ecf20Sopenharmony_ci					(op_is_write(bio_op(bio))) ? "WRITE" :
14758c2ecf20Sopenharmony_ci					"READ",
14768c2ecf20Sopenharmony_ci					(unsigned long long) iter.bi_sector, len);
14778c2ecf20Sopenharmony_ci			bio->bi_status = errno_to_blk_status(err);
14788c2ecf20Sopenharmony_ci			break;
14798c2ecf20Sopenharmony_ci		}
14808c2ecf20Sopenharmony_ci	}
14818c2ecf20Sopenharmony_ci	if (do_acct)
14828c2ecf20Sopenharmony_ci		bio_end_io_acct(bio, start);
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	bio_endio(bio);
14858c2ecf20Sopenharmony_ci	return BLK_QC_T_NONE;
14868c2ecf20Sopenharmony_ci}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_cistatic int btt_rw_page(struct block_device *bdev, sector_t sector,
14898c2ecf20Sopenharmony_ci		struct page *page, unsigned int op)
14908c2ecf20Sopenharmony_ci{
14918c2ecf20Sopenharmony_ci	struct btt *btt = bdev->bd_disk->private_data;
14928c2ecf20Sopenharmony_ci	int rc;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	rc = btt_do_bvec(btt, NULL, page, thp_size(page), 0, op, sector);
14958c2ecf20Sopenharmony_ci	if (rc == 0)
14968c2ecf20Sopenharmony_ci		page_endio(page, op_is_write(op), 0);
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	return rc;
14998c2ecf20Sopenharmony_ci}
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_cistatic int btt_getgeo(struct block_device *bd, struct hd_geometry *geo)
15038c2ecf20Sopenharmony_ci{
15048c2ecf20Sopenharmony_ci	/* some standard values */
15058c2ecf20Sopenharmony_ci	geo->heads = 1 << 6;
15068c2ecf20Sopenharmony_ci	geo->sectors = 1 << 5;
15078c2ecf20Sopenharmony_ci	geo->cylinders = get_capacity(bd->bd_disk) >> 11;
15088c2ecf20Sopenharmony_ci	return 0;
15098c2ecf20Sopenharmony_ci}
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_cistatic const struct block_device_operations btt_fops = {
15128c2ecf20Sopenharmony_ci	.owner =		THIS_MODULE,
15138c2ecf20Sopenharmony_ci	.submit_bio =		btt_submit_bio,
15148c2ecf20Sopenharmony_ci	.rw_page =		btt_rw_page,
15158c2ecf20Sopenharmony_ci	.getgeo =		btt_getgeo,
15168c2ecf20Sopenharmony_ci};
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_cistatic int btt_blk_init(struct btt *btt)
15198c2ecf20Sopenharmony_ci{
15208c2ecf20Sopenharmony_ci	struct nd_btt *nd_btt = btt->nd_btt;
15218c2ecf20Sopenharmony_ci	struct nd_namespace_common *ndns = nd_btt->ndns;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	/* create a new disk and request queue for btt */
15248c2ecf20Sopenharmony_ci	btt->btt_queue = blk_alloc_queue(NUMA_NO_NODE);
15258c2ecf20Sopenharmony_ci	if (!btt->btt_queue)
15268c2ecf20Sopenharmony_ci		return -ENOMEM;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	btt->btt_disk = alloc_disk(0);
15298c2ecf20Sopenharmony_ci	if (!btt->btt_disk) {
15308c2ecf20Sopenharmony_ci		blk_cleanup_queue(btt->btt_queue);
15318c2ecf20Sopenharmony_ci		return -ENOMEM;
15328c2ecf20Sopenharmony_ci	}
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	nvdimm_namespace_disk_name(ndns, btt->btt_disk->disk_name);
15358c2ecf20Sopenharmony_ci	btt->btt_disk->first_minor = 0;
15368c2ecf20Sopenharmony_ci	btt->btt_disk->fops = &btt_fops;
15378c2ecf20Sopenharmony_ci	btt->btt_disk->private_data = btt;
15388c2ecf20Sopenharmony_ci	btt->btt_disk->queue = btt->btt_queue;
15398c2ecf20Sopenharmony_ci	btt->btt_disk->flags = GENHD_FL_EXT_DEVT;
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	blk_queue_logical_block_size(btt->btt_queue, btt->sector_size);
15428c2ecf20Sopenharmony_ci	blk_queue_max_hw_sectors(btt->btt_queue, UINT_MAX);
15438c2ecf20Sopenharmony_ci	blk_queue_flag_set(QUEUE_FLAG_NONROT, btt->btt_queue);
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	if (btt_meta_size(btt)) {
15468c2ecf20Sopenharmony_ci		int rc = nd_integrity_init(btt->btt_disk, btt_meta_size(btt));
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci		if (rc) {
15498c2ecf20Sopenharmony_ci			del_gendisk(btt->btt_disk);
15508c2ecf20Sopenharmony_ci			put_disk(btt->btt_disk);
15518c2ecf20Sopenharmony_ci			blk_cleanup_queue(btt->btt_queue);
15528c2ecf20Sopenharmony_ci			return rc;
15538c2ecf20Sopenharmony_ci		}
15548c2ecf20Sopenharmony_ci	}
15558c2ecf20Sopenharmony_ci	set_capacity(btt->btt_disk, btt->nlba * btt->sector_size >> 9);
15568c2ecf20Sopenharmony_ci	device_add_disk(&btt->nd_btt->dev, btt->btt_disk, NULL);
15578c2ecf20Sopenharmony_ci	btt->nd_btt->size = btt->nlba * (u64)btt->sector_size;
15588c2ecf20Sopenharmony_ci	nvdimm_check_and_set_ro(btt->btt_disk);
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	return 0;
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_cistatic void btt_blk_cleanup(struct btt *btt)
15648c2ecf20Sopenharmony_ci{
15658c2ecf20Sopenharmony_ci	del_gendisk(btt->btt_disk);
15668c2ecf20Sopenharmony_ci	put_disk(btt->btt_disk);
15678c2ecf20Sopenharmony_ci	blk_cleanup_queue(btt->btt_queue);
15688c2ecf20Sopenharmony_ci}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci/**
15718c2ecf20Sopenharmony_ci * btt_init - initialize a block translation table for the given device
15728c2ecf20Sopenharmony_ci * @nd_btt:	device with BTT geometry and backing device info
15738c2ecf20Sopenharmony_ci * @rawsize:	raw size in bytes of the backing device
15748c2ecf20Sopenharmony_ci * @lbasize:	lba size of the backing device
15758c2ecf20Sopenharmony_ci * @uuid:	A uuid for the backing device - this is stored on media
15768c2ecf20Sopenharmony_ci * @maxlane:	maximum number of parallel requests the device can handle
15778c2ecf20Sopenharmony_ci *
15788c2ecf20Sopenharmony_ci * Initialize a Block Translation Table on a backing device to provide
15798c2ecf20Sopenharmony_ci * single sector power fail atomicity.
15808c2ecf20Sopenharmony_ci *
15818c2ecf20Sopenharmony_ci * Context:
15828c2ecf20Sopenharmony_ci * Might sleep.
15838c2ecf20Sopenharmony_ci *
15848c2ecf20Sopenharmony_ci * Returns:
15858c2ecf20Sopenharmony_ci * Pointer to a new struct btt on success, NULL on failure.
15868c2ecf20Sopenharmony_ci */
15878c2ecf20Sopenharmony_cistatic struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
15888c2ecf20Sopenharmony_ci		u32 lbasize, u8 *uuid, struct nd_region *nd_region)
15898c2ecf20Sopenharmony_ci{
15908c2ecf20Sopenharmony_ci	int ret;
15918c2ecf20Sopenharmony_ci	struct btt *btt;
15928c2ecf20Sopenharmony_ci	struct nd_namespace_io *nsio;
15938c2ecf20Sopenharmony_ci	struct device *dev = &nd_btt->dev;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	btt = devm_kzalloc(dev, sizeof(struct btt), GFP_KERNEL);
15968c2ecf20Sopenharmony_ci	if (!btt)
15978c2ecf20Sopenharmony_ci		return NULL;
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	btt->nd_btt = nd_btt;
16008c2ecf20Sopenharmony_ci	btt->rawsize = rawsize;
16018c2ecf20Sopenharmony_ci	btt->lbasize = lbasize;
16028c2ecf20Sopenharmony_ci	btt->sector_size = ((lbasize >= 4096) ? 4096 : 512);
16038c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&btt->arena_list);
16048c2ecf20Sopenharmony_ci	mutex_init(&btt->init_lock);
16058c2ecf20Sopenharmony_ci	btt->nd_region = nd_region;
16068c2ecf20Sopenharmony_ci	nsio = to_nd_namespace_io(&nd_btt->ndns->dev);
16078c2ecf20Sopenharmony_ci	btt->phys_bb = &nsio->bb;
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	ret = discover_arenas(btt);
16108c2ecf20Sopenharmony_ci	if (ret) {
16118c2ecf20Sopenharmony_ci		dev_err(dev, "init: error in arena_discover: %d\n", ret);
16128c2ecf20Sopenharmony_ci		return NULL;
16138c2ecf20Sopenharmony_ci	}
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	if (btt->init_state != INIT_READY && nd_region->ro) {
16168c2ecf20Sopenharmony_ci		dev_warn(dev, "%s is read-only, unable to init btt metadata\n",
16178c2ecf20Sopenharmony_ci				dev_name(&nd_region->dev));
16188c2ecf20Sopenharmony_ci		return NULL;
16198c2ecf20Sopenharmony_ci	} else if (btt->init_state != INIT_READY) {
16208c2ecf20Sopenharmony_ci		btt->num_arenas = (rawsize / ARENA_MAX_SIZE) +
16218c2ecf20Sopenharmony_ci			((rawsize % ARENA_MAX_SIZE) ? 1 : 0);
16228c2ecf20Sopenharmony_ci		dev_dbg(dev, "init: %d arenas for %llu rawsize\n",
16238c2ecf20Sopenharmony_ci				btt->num_arenas, rawsize);
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci		ret = create_arenas(btt);
16268c2ecf20Sopenharmony_ci		if (ret) {
16278c2ecf20Sopenharmony_ci			dev_info(dev, "init: create_arenas: %d\n", ret);
16288c2ecf20Sopenharmony_ci			return NULL;
16298c2ecf20Sopenharmony_ci		}
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci		ret = btt_meta_init(btt);
16328c2ecf20Sopenharmony_ci		if (ret) {
16338c2ecf20Sopenharmony_ci			dev_err(dev, "init: error in meta_init: %d\n", ret);
16348c2ecf20Sopenharmony_ci			return NULL;
16358c2ecf20Sopenharmony_ci		}
16368c2ecf20Sopenharmony_ci	}
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	ret = btt_blk_init(btt);
16398c2ecf20Sopenharmony_ci	if (ret) {
16408c2ecf20Sopenharmony_ci		dev_err(dev, "init: error in blk_init: %d\n", ret);
16418c2ecf20Sopenharmony_ci		return NULL;
16428c2ecf20Sopenharmony_ci	}
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	btt_debugfs_init(btt);
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	return btt;
16478c2ecf20Sopenharmony_ci}
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci/**
16508c2ecf20Sopenharmony_ci * btt_fini - de-initialize a BTT
16518c2ecf20Sopenharmony_ci * @btt:	the BTT handle that was generated by btt_init
16528c2ecf20Sopenharmony_ci *
16538c2ecf20Sopenharmony_ci * De-initialize a Block Translation Table on device removal
16548c2ecf20Sopenharmony_ci *
16558c2ecf20Sopenharmony_ci * Context:
16568c2ecf20Sopenharmony_ci * Might sleep.
16578c2ecf20Sopenharmony_ci */
16588c2ecf20Sopenharmony_cistatic void btt_fini(struct btt *btt)
16598c2ecf20Sopenharmony_ci{
16608c2ecf20Sopenharmony_ci	if (btt) {
16618c2ecf20Sopenharmony_ci		btt_blk_cleanup(btt);
16628c2ecf20Sopenharmony_ci		free_arenas(btt);
16638c2ecf20Sopenharmony_ci		debugfs_remove_recursive(btt->debugfs_dir);
16648c2ecf20Sopenharmony_ci	}
16658c2ecf20Sopenharmony_ci}
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ciint nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns)
16688c2ecf20Sopenharmony_ci{
16698c2ecf20Sopenharmony_ci	struct nd_btt *nd_btt = to_nd_btt(ndns->claim);
16708c2ecf20Sopenharmony_ci	struct nd_region *nd_region;
16718c2ecf20Sopenharmony_ci	struct btt_sb *btt_sb;
16728c2ecf20Sopenharmony_ci	struct btt *btt;
16738c2ecf20Sopenharmony_ci	size_t size, rawsize;
16748c2ecf20Sopenharmony_ci	int rc;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci	if (!nd_btt->uuid || !nd_btt->ndns || !nd_btt->lbasize) {
16778c2ecf20Sopenharmony_ci		dev_dbg(&nd_btt->dev, "incomplete btt configuration\n");
16788c2ecf20Sopenharmony_ci		return -ENODEV;
16798c2ecf20Sopenharmony_ci	}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	btt_sb = devm_kzalloc(&nd_btt->dev, sizeof(*btt_sb), GFP_KERNEL);
16828c2ecf20Sopenharmony_ci	if (!btt_sb)
16838c2ecf20Sopenharmony_ci		return -ENOMEM;
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	size = nvdimm_namespace_capacity(ndns);
16868c2ecf20Sopenharmony_ci	rc = devm_namespace_enable(&nd_btt->dev, ndns, size);
16878c2ecf20Sopenharmony_ci	if (rc)
16888c2ecf20Sopenharmony_ci		return rc;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	/*
16918c2ecf20Sopenharmony_ci	 * If this returns < 0, that is ok as it just means there wasn't
16928c2ecf20Sopenharmony_ci	 * an existing BTT, and we're creating a new one. We still need to
16938c2ecf20Sopenharmony_ci	 * call this as we need the version dependent fields in nd_btt to be
16948c2ecf20Sopenharmony_ci	 * set correctly based on the holder class
16958c2ecf20Sopenharmony_ci	 */
16968c2ecf20Sopenharmony_ci	nd_btt_version(nd_btt, ndns, btt_sb);
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	rawsize = size - nd_btt->initial_offset;
16998c2ecf20Sopenharmony_ci	if (rawsize < ARENA_MIN_SIZE) {
17008c2ecf20Sopenharmony_ci		dev_dbg(&nd_btt->dev, "%s must be at least %ld bytes\n",
17018c2ecf20Sopenharmony_ci				dev_name(&ndns->dev),
17028c2ecf20Sopenharmony_ci				ARENA_MIN_SIZE + nd_btt->initial_offset);
17038c2ecf20Sopenharmony_ci		return -ENXIO;
17048c2ecf20Sopenharmony_ci	}
17058c2ecf20Sopenharmony_ci	nd_region = to_nd_region(nd_btt->dev.parent);
17068c2ecf20Sopenharmony_ci	btt = btt_init(nd_btt, rawsize, nd_btt->lbasize, nd_btt->uuid,
17078c2ecf20Sopenharmony_ci			nd_region);
17088c2ecf20Sopenharmony_ci	if (!btt)
17098c2ecf20Sopenharmony_ci		return -ENOMEM;
17108c2ecf20Sopenharmony_ci	nd_btt->btt = btt;
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	return 0;
17138c2ecf20Sopenharmony_ci}
17148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvdimm_namespace_attach_btt);
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ciint nvdimm_namespace_detach_btt(struct nd_btt *nd_btt)
17178c2ecf20Sopenharmony_ci{
17188c2ecf20Sopenharmony_ci	struct btt *btt = nd_btt->btt;
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	btt_fini(btt);
17218c2ecf20Sopenharmony_ci	nd_btt->btt = NULL;
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	return 0;
17248c2ecf20Sopenharmony_ci}
17258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nvdimm_namespace_detach_btt);
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_cistatic int __init nd_btt_init(void)
17288c2ecf20Sopenharmony_ci{
17298c2ecf20Sopenharmony_ci	int rc = 0;
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	debugfs_root = debugfs_create_dir("btt", NULL);
17328c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(debugfs_root))
17338c2ecf20Sopenharmony_ci		rc = -ENXIO;
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	return rc;
17368c2ecf20Sopenharmony_ci}
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_cistatic void __exit nd_btt_exit(void)
17398c2ecf20Sopenharmony_ci{
17408c2ecf20Sopenharmony_ci	debugfs_remove_recursive(debugfs_root);
17418c2ecf20Sopenharmony_ci}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ciMODULE_ALIAS_ND_DEVICE(ND_DEVICE_BTT);
17448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vishal Verma <vishal.l.verma@linux.intel.com>");
17458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
17468c2ecf20Sopenharmony_cimodule_init(nd_btt_init);
17478c2ecf20Sopenharmony_cimodule_exit(nd_btt_exit);
1748