18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright(c) 2017 Intel Corporation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/libnvdimm.h>
68c2ecf20Sopenharmony_ci#include <linux/badblocks.h>
78c2ecf20Sopenharmony_ci#include <linux/export.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/ctype.h>
128c2ecf20Sopenharmony_ci#include <linux/ndctl.h>
138c2ecf20Sopenharmony_ci#include <linux/mutex.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include "nd-core.h"
178c2ecf20Sopenharmony_ci#include "nd.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_civoid badrange_init(struct badrange *badrange)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&badrange->list);
228c2ecf20Sopenharmony_ci	spin_lock_init(&badrange->lock);
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(badrange_init);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic void append_badrange_entry(struct badrange *badrange,
278c2ecf20Sopenharmony_ci		struct badrange_entry *bre, u64 addr, u64 length)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	lockdep_assert_held(&badrange->lock);
308c2ecf20Sopenharmony_ci	bre->start = addr;
318c2ecf20Sopenharmony_ci	bre->length = length;
328c2ecf20Sopenharmony_ci	list_add_tail(&bre->list, &badrange->list);
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int alloc_and_append_badrange_entry(struct badrange *badrange,
368c2ecf20Sopenharmony_ci		u64 addr, u64 length, gfp_t flags)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct badrange_entry *bre;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	bre = kzalloc(sizeof(*bre), flags);
418c2ecf20Sopenharmony_ci	if (!bre)
428c2ecf20Sopenharmony_ci		return -ENOMEM;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	append_badrange_entry(badrange, bre, addr, length);
458c2ecf20Sopenharmony_ci	return 0;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int add_badrange(struct badrange *badrange, u64 addr, u64 length)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct badrange_entry *bre, *bre_new;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	spin_unlock(&badrange->lock);
538c2ecf20Sopenharmony_ci	bre_new = kzalloc(sizeof(*bre_new), GFP_KERNEL);
548c2ecf20Sopenharmony_ci	spin_lock(&badrange->lock);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (list_empty(&badrange->list)) {
578c2ecf20Sopenharmony_ci		if (!bre_new)
588c2ecf20Sopenharmony_ci			return -ENOMEM;
598c2ecf20Sopenharmony_ci		append_badrange_entry(badrange, bre_new, addr, length);
608c2ecf20Sopenharmony_ci		return 0;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	/*
648c2ecf20Sopenharmony_ci	 * There is a chance this is a duplicate, check for those first.
658c2ecf20Sopenharmony_ci	 * This will be the common case as ARS_STATUS returns all known
668c2ecf20Sopenharmony_ci	 * errors in the SPA space, and we can't query it per region
678c2ecf20Sopenharmony_ci	 */
688c2ecf20Sopenharmony_ci	list_for_each_entry(bre, &badrange->list, list)
698c2ecf20Sopenharmony_ci		if (bre->start == addr) {
708c2ecf20Sopenharmony_ci			/* If length has changed, update this list entry */
718c2ecf20Sopenharmony_ci			if (bre->length != length)
728c2ecf20Sopenharmony_ci				bre->length = length;
738c2ecf20Sopenharmony_ci			kfree(bre_new);
748c2ecf20Sopenharmony_ci			return 0;
758c2ecf20Sopenharmony_ci		}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/*
788c2ecf20Sopenharmony_ci	 * If not a duplicate or a simple length update, add the entry as is,
798c2ecf20Sopenharmony_ci	 * as any overlapping ranges will get resolved when the list is consumed
808c2ecf20Sopenharmony_ci	 * and converted to badblocks
818c2ecf20Sopenharmony_ci	 */
828c2ecf20Sopenharmony_ci	if (!bre_new)
838c2ecf20Sopenharmony_ci		return -ENOMEM;
848c2ecf20Sopenharmony_ci	append_badrange_entry(badrange, bre_new, addr, length);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return 0;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciint badrange_add(struct badrange *badrange, u64 addr, u64 length)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	int rc;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	spin_lock(&badrange->lock);
948c2ecf20Sopenharmony_ci	rc = add_badrange(badrange, addr, length);
958c2ecf20Sopenharmony_ci	spin_unlock(&badrange->lock);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return rc;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(badrange_add);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_civoid badrange_forget(struct badrange *badrange, phys_addr_t start,
1028c2ecf20Sopenharmony_ci		unsigned int len)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct list_head *badrange_list = &badrange->list;
1058c2ecf20Sopenharmony_ci	u64 clr_end = start + len - 1;
1068c2ecf20Sopenharmony_ci	struct badrange_entry *bre, *next;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	spin_lock(&badrange->lock);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/*
1118c2ecf20Sopenharmony_ci	 * [start, clr_end] is the badrange interval being cleared.
1128c2ecf20Sopenharmony_ci	 * [bre->start, bre_end] is the badrange_list entry we're comparing
1138c2ecf20Sopenharmony_ci	 * the above interval against. The badrange list entry may need
1148c2ecf20Sopenharmony_ci	 * to be modified (update either start or length), deleted, or
1158c2ecf20Sopenharmony_ci	 * split into two based on the overlap characteristics
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	list_for_each_entry_safe(bre, next, badrange_list, list) {
1198c2ecf20Sopenharmony_ci		u64 bre_end = bre->start + bre->length - 1;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		/* Skip intervals with no intersection */
1228c2ecf20Sopenharmony_ci		if (bre_end < start)
1238c2ecf20Sopenharmony_ci			continue;
1248c2ecf20Sopenharmony_ci		if (bre->start >  clr_end)
1258c2ecf20Sopenharmony_ci			continue;
1268c2ecf20Sopenharmony_ci		/* Delete completely overlapped badrange entries */
1278c2ecf20Sopenharmony_ci		if ((bre->start >= start) && (bre_end <= clr_end)) {
1288c2ecf20Sopenharmony_ci			list_del(&bre->list);
1298c2ecf20Sopenharmony_ci			kfree(bre);
1308c2ecf20Sopenharmony_ci			continue;
1318c2ecf20Sopenharmony_ci		}
1328c2ecf20Sopenharmony_ci		/* Adjust start point of partially cleared entries */
1338c2ecf20Sopenharmony_ci		if ((start <= bre->start) && (clr_end > bre->start)) {
1348c2ecf20Sopenharmony_ci			bre->length -= clr_end - bre->start + 1;
1358c2ecf20Sopenharmony_ci			bre->start = clr_end + 1;
1368c2ecf20Sopenharmony_ci			continue;
1378c2ecf20Sopenharmony_ci		}
1388c2ecf20Sopenharmony_ci		/* Adjust bre->length for partial clearing at the tail end */
1398c2ecf20Sopenharmony_ci		if ((bre->start < start) && (bre_end <= clr_end)) {
1408c2ecf20Sopenharmony_ci			/* bre->start remains the same */
1418c2ecf20Sopenharmony_ci			bre->length = start - bre->start;
1428c2ecf20Sopenharmony_ci			continue;
1438c2ecf20Sopenharmony_ci		}
1448c2ecf20Sopenharmony_ci		/*
1458c2ecf20Sopenharmony_ci		 * If clearing in the middle of an entry, we split it into
1468c2ecf20Sopenharmony_ci		 * two by modifying the current entry to represent one half of
1478c2ecf20Sopenharmony_ci		 * the split, and adding a new entry for the second half.
1488c2ecf20Sopenharmony_ci		 */
1498c2ecf20Sopenharmony_ci		if ((bre->start < start) && (bre_end > clr_end)) {
1508c2ecf20Sopenharmony_ci			u64 new_start = clr_end + 1;
1518c2ecf20Sopenharmony_ci			u64 new_len = bre_end - new_start + 1;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci			/* Add new entry covering the right half */
1548c2ecf20Sopenharmony_ci			alloc_and_append_badrange_entry(badrange, new_start,
1558c2ecf20Sopenharmony_ci					new_len, GFP_NOWAIT);
1568c2ecf20Sopenharmony_ci			/* Adjust this entry to cover the left half */
1578c2ecf20Sopenharmony_ci			bre->length = start - bre->start;
1588c2ecf20Sopenharmony_ci			continue;
1598c2ecf20Sopenharmony_ci		}
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci	spin_unlock(&badrange->lock);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(badrange_forget);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic void set_badblock(struct badblocks *bb, sector_t s, int num)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	dev_dbg(bb->dev, "Found a bad range (0x%llx, 0x%llx)\n",
1688c2ecf20Sopenharmony_ci			(u64) s * 512, (u64) num * 512);
1698c2ecf20Sopenharmony_ci	/* this isn't an error as the hardware will still throw an exception */
1708c2ecf20Sopenharmony_ci	if (badblocks_set(bb, s, num, 1))
1718c2ecf20Sopenharmony_ci		dev_info_once(bb->dev, "%s: failed for sector %llx\n",
1728c2ecf20Sopenharmony_ci				__func__, (u64) s);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/**
1768c2ecf20Sopenharmony_ci * __add_badblock_range() - Convert a physical address range to bad sectors
1778c2ecf20Sopenharmony_ci * @bb:		badblocks instance to populate
1788c2ecf20Sopenharmony_ci * @ns_offset:	namespace offset where the error range begins (in bytes)
1798c2ecf20Sopenharmony_ci * @len:	number of bytes of badrange to be added
1808c2ecf20Sopenharmony_ci *
1818c2ecf20Sopenharmony_ci * This assumes that the range provided with (ns_offset, len) is within
1828c2ecf20Sopenharmony_ci * the bounds of physical addresses for this namespace, i.e. lies in the
1838c2ecf20Sopenharmony_ci * interval [ns_start, ns_start + ns_size)
1848c2ecf20Sopenharmony_ci */
1858c2ecf20Sopenharmony_cistatic void __add_badblock_range(struct badblocks *bb, u64 ns_offset, u64 len)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	const unsigned int sector_size = 512;
1888c2ecf20Sopenharmony_ci	sector_t start_sector, end_sector;
1898c2ecf20Sopenharmony_ci	u64 num_sectors;
1908c2ecf20Sopenharmony_ci	u32 rem;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	start_sector = div_u64(ns_offset, sector_size);
1938c2ecf20Sopenharmony_ci	end_sector = div_u64_rem(ns_offset + len, sector_size, &rem);
1948c2ecf20Sopenharmony_ci	if (rem)
1958c2ecf20Sopenharmony_ci		end_sector++;
1968c2ecf20Sopenharmony_ci	num_sectors = end_sector - start_sector;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (unlikely(num_sectors > (u64)INT_MAX)) {
1998c2ecf20Sopenharmony_ci		u64 remaining = num_sectors;
2008c2ecf20Sopenharmony_ci		sector_t s = start_sector;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		while (remaining) {
2038c2ecf20Sopenharmony_ci			int done = min_t(u64, remaining, INT_MAX);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci			set_badblock(bb, s, done);
2068c2ecf20Sopenharmony_ci			remaining -= done;
2078c2ecf20Sopenharmony_ci			s += done;
2088c2ecf20Sopenharmony_ci		}
2098c2ecf20Sopenharmony_ci	} else
2108c2ecf20Sopenharmony_ci		set_badblock(bb, start_sector, num_sectors);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic void badblocks_populate(struct badrange *badrange,
2148c2ecf20Sopenharmony_ci		struct badblocks *bb, const struct range *range)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct badrange_entry *bre;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (list_empty(&badrange->list))
2198c2ecf20Sopenharmony_ci		return;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	list_for_each_entry(bre, &badrange->list, list) {
2228c2ecf20Sopenharmony_ci		u64 bre_end = bre->start + bre->length - 1;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		/* Discard intervals with no intersection */
2258c2ecf20Sopenharmony_ci		if (bre_end < range->start)
2268c2ecf20Sopenharmony_ci			continue;
2278c2ecf20Sopenharmony_ci		if (bre->start > range->end)
2288c2ecf20Sopenharmony_ci			continue;
2298c2ecf20Sopenharmony_ci		/* Deal with any overlap after start of the namespace */
2308c2ecf20Sopenharmony_ci		if (bre->start >= range->start) {
2318c2ecf20Sopenharmony_ci			u64 start = bre->start;
2328c2ecf20Sopenharmony_ci			u64 len;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci			if (bre_end <= range->end)
2358c2ecf20Sopenharmony_ci				len = bre->length;
2368c2ecf20Sopenharmony_ci			else
2378c2ecf20Sopenharmony_ci				len = range->start + range_len(range)
2388c2ecf20Sopenharmony_ci					- bre->start;
2398c2ecf20Sopenharmony_ci			__add_badblock_range(bb, start - range->start, len);
2408c2ecf20Sopenharmony_ci			continue;
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci		/*
2438c2ecf20Sopenharmony_ci		 * Deal with overlap for badrange starting before
2448c2ecf20Sopenharmony_ci		 * the namespace.
2458c2ecf20Sopenharmony_ci		 */
2468c2ecf20Sopenharmony_ci		if (bre->start < range->start) {
2478c2ecf20Sopenharmony_ci			u64 len;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci			if (bre_end < range->end)
2508c2ecf20Sopenharmony_ci				len = bre->start + bre->length - range->start;
2518c2ecf20Sopenharmony_ci			else
2528c2ecf20Sopenharmony_ci				len = range_len(range);
2538c2ecf20Sopenharmony_ci			__add_badblock_range(bb, 0, len);
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci/**
2598c2ecf20Sopenharmony_ci * nvdimm_badblocks_populate() - Convert a list of badranges to badblocks
2608c2ecf20Sopenharmony_ci * @region: parent region of the range to interrogate
2618c2ecf20Sopenharmony_ci * @bb: badblocks instance to populate
2628c2ecf20Sopenharmony_ci * @res: resource range to consider
2638c2ecf20Sopenharmony_ci *
2648c2ecf20Sopenharmony_ci * The badrange list generated during bus initialization may contain
2658c2ecf20Sopenharmony_ci * multiple, possibly overlapping physical address ranges.  Compare each
2668c2ecf20Sopenharmony_ci * of these ranges to the resource range currently being initialized,
2678c2ecf20Sopenharmony_ci * and add badblocks entries for all matching sub-ranges
2688c2ecf20Sopenharmony_ci */
2698c2ecf20Sopenharmony_civoid nvdimm_badblocks_populate(struct nd_region *nd_region,
2708c2ecf20Sopenharmony_ci		struct badblocks *bb, const struct range *range)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct nvdimm_bus *nvdimm_bus;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (!is_memory(&nd_region->dev)) {
2758c2ecf20Sopenharmony_ci		dev_WARN_ONCE(&nd_region->dev, 1,
2768c2ecf20Sopenharmony_ci				"%s only valid for pmem regions\n", __func__);
2778c2ecf20Sopenharmony_ci		return;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci	nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	nvdimm_bus_lock(&nvdimm_bus->dev);
2828c2ecf20Sopenharmony_ci	badblocks_populate(&nvdimm_bus->badrange, bb, range);
2838c2ecf20Sopenharmony_ci	nvdimm_bus_unlock(&nvdimm_bus->dev);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvdimm_badblocks_populate);
286