1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright(c) 2017 Intel Corporation. All rights reserved.
5 #include <linux/libnvdimm.h>
6 #include <linux/badblocks.h>
7 #include <linux/export.h>
8 #include <linux/module.h>
9 #include <linux/blkdev.h>
10 #include <linux/device.h>
11 #include <linux/ctype.h>
12 #include <linux/ndctl.h>
13 #include <linux/mutex.h>
14 #include <linux/slab.h>
19 void badrange_init(struct badrange
*badrange
)
21 INIT_LIST_HEAD(&badrange
->list
);
22 spin_lock_init(&badrange
->lock
);
24 EXPORT_SYMBOL_GPL(badrange_init
);
26 static void append_badrange_entry(struct badrange
*badrange
,
27 struct badrange_entry
*bre
, u64 addr
, u64 length
)
29 lockdep_assert_held(&badrange
->lock
);
32 list_add_tail(&bre
->list
, &badrange
->list
);
35 static int alloc_and_append_badrange_entry(struct badrange
*badrange
,
36 u64 addr
, u64 length
, gfp_t flags
)
38 struct badrange_entry
*bre
;
40 bre
= kzalloc(sizeof(*bre
), flags
);
44 append_badrange_entry(badrange
, bre
, addr
, length
);
48 static int add_badrange(struct badrange
*badrange
, u64 addr
, u64 length
)
50 struct badrange_entry
*bre
, *bre_new
;
52 spin_unlock(&badrange
->lock
);
53 bre_new
= kzalloc(sizeof(*bre_new
), GFP_KERNEL
);
54 spin_lock(&badrange
->lock
);
56 if (list_empty(&badrange
->list
)) {
59 append_badrange_entry(badrange
, bre_new
, addr
, length
);
64 * There is a chance this is a duplicate, check for those first.
65 * This will be the common case as ARS_STATUS returns all known
66 * errors in the SPA space, and we can't query it per region
68 list_for_each_entry(bre
, &badrange
->list
, list
)
69 if (bre
->start
== addr
) {
70 /* If length has changed, update this list entry */
71 if (bre
->length
!= length
)
78 * If not a duplicate or a simple length update, add the entry as is,
79 * as any overlapping ranges will get resolved when the list is consumed
80 * and converted to badblocks
84 append_badrange_entry(badrange
, bre_new
, addr
, length
);
89 int badrange_add(struct badrange
*badrange
, u64 addr
, u64 length
)
93 spin_lock(&badrange
->lock
);
94 rc
= add_badrange(badrange
, addr
, length
);
95 spin_unlock(&badrange
->lock
);
99 EXPORT_SYMBOL_GPL(badrange_add
);
101 void badrange_forget(struct badrange
*badrange
, phys_addr_t start
,
104 struct list_head
*badrange_list
= &badrange
->list
;
105 u64 clr_end
= start
+ len
- 1;
106 struct badrange_entry
*bre
, *next
;
108 spin_lock(&badrange
->lock
);
111 * [start, clr_end] is the badrange interval being cleared.
112 * [bre->start, bre_end] is the badrange_list entry we're comparing
113 * the above interval against. The badrange list entry may need
114 * to be modified (update either start or length), deleted, or
115 * split into two based on the overlap characteristics
118 list_for_each_entry_safe(bre
, next
, badrange_list
, list
) {
119 u64 bre_end
= bre
->start
+ bre
->length
- 1;
121 /* Skip intervals with no intersection */
124 if (bre
->start
> clr_end
)
126 /* Delete completely overlapped badrange entries */
127 if ((bre
->start
>= start
) && (bre_end
<= clr_end
)) {
128 list_del(&bre
->list
);
132 /* Adjust start point of partially cleared entries */
133 if ((start
<= bre
->start
) && (clr_end
> bre
->start
)) {
134 bre
->length
-= clr_end
- bre
->start
+ 1;
135 bre
->start
= clr_end
+ 1;
138 /* Adjust bre->length for partial clearing at the tail end */
139 if ((bre
->start
< start
) && (bre_end
<= clr_end
)) {
140 /* bre->start remains the same */
141 bre
->length
= start
- bre
->start
;
145 * If clearing in the middle of an entry, we split it into
146 * two by modifying the current entry to represent one half of
147 * the split, and adding a new entry for the second half.
149 if ((bre
->start
< start
) && (bre_end
> clr_end
)) {
150 u64 new_start
= clr_end
+ 1;
151 u64 new_len
= bre_end
- new_start
+ 1;
153 /* Add new entry covering the right half */
154 alloc_and_append_badrange_entry(badrange
, new_start
,
155 new_len
, GFP_NOWAIT
);
156 /* Adjust this entry to cover the left half */
157 bre
->length
= start
- bre
->start
;
161 spin_unlock(&badrange
->lock
);
163 EXPORT_SYMBOL_GPL(badrange_forget
);
165 static void set_badblock(struct badblocks
*bb
, sector_t s
, int num
)
167 dev_dbg(bb
->dev
, "Found a bad range (0x%llx, 0x%llx)\n",
168 (u64
) s
* 512, (u64
) num
* 512);
169 /* this isn't an error as the hardware will still throw an exception */
170 if (badblocks_set(bb
, s
, num
, 1))
171 dev_info_once(bb
->dev
, "%s: failed for sector %llx\n",
176 * __add_badblock_range() - Convert a physical address range to bad sectors
177 * @bb: badblocks instance to populate
178 * @ns_offset: namespace offset where the error range begins (in bytes)
179 * @len: number of bytes of badrange to be added
181 * This assumes that the range provided with (ns_offset, len) is within
182 * the bounds of physical addresses for this namespace, i.e. lies in the
183 * interval [ns_start, ns_start + ns_size)
185 static void __add_badblock_range(struct badblocks
*bb
, u64 ns_offset
, u64 len
)
187 const unsigned int sector_size
= 512;
188 sector_t start_sector
, end_sector
;
192 start_sector
= div_u64(ns_offset
, sector_size
);
193 end_sector
= div_u64_rem(ns_offset
+ len
, sector_size
, &rem
);
196 num_sectors
= end_sector
- start_sector
;
198 if (unlikely(num_sectors
> (u64
)INT_MAX
)) {
199 u64 remaining
= num_sectors
;
200 sector_t s
= start_sector
;
203 int done
= min_t(u64
, remaining
, INT_MAX
);
205 set_badblock(bb
, s
, done
);
210 set_badblock(bb
, start_sector
, num_sectors
);
213 static void badblocks_populate(struct badrange
*badrange
,
214 struct badblocks
*bb
, const struct resource
*res
)
216 struct badrange_entry
*bre
;
218 if (list_empty(&badrange
->list
))
221 list_for_each_entry(bre
, &badrange
->list
, list
) {
222 u64 bre_end
= bre
->start
+ bre
->length
- 1;
224 /* Discard intervals with no intersection */
225 if (bre_end
< res
->start
)
227 if (bre
->start
> res
->end
)
229 /* Deal with any overlap after start of the namespace */
230 if (bre
->start
>= res
->start
) {
231 u64 start
= bre
->start
;
234 if (bre_end
<= res
->end
)
237 len
= res
->start
+ resource_size(res
)
239 __add_badblock_range(bb
, start
- res
->start
, len
);
243 * Deal with overlap for badrange starting before
246 if (bre
->start
< res
->start
) {
249 if (bre_end
< res
->end
)
250 len
= bre
->start
+ bre
->length
- res
->start
;
252 len
= resource_size(res
);
253 __add_badblock_range(bb
, 0, len
);
259 * nvdimm_badblocks_populate() - Convert a list of badranges to badblocks
260 * @region: parent region of the range to interrogate
261 * @bb: badblocks instance to populate
262 * @res: resource range to consider
264 * The badrange list generated during bus initialization may contain
265 * multiple, possibly overlapping physical address ranges. Compare each
266 * of these ranges to the resource range currently being initialized,
267 * and add badblocks entries for all matching sub-ranges
269 void nvdimm_badblocks_populate(struct nd_region
*nd_region
,
270 struct badblocks
*bb
, const struct resource
*res
)
272 struct nvdimm_bus
*nvdimm_bus
;
274 if (!is_memory(&nd_region
->dev
)) {
275 dev_WARN_ONCE(&nd_region
->dev
, 1,
276 "%s only valid for pmem regions\n", __func__
);
279 nvdimm_bus
= walk_to_nvdimm_bus(&nd_region
->dev
);
281 nvdimm_bus_lock(&nvdimm_bus
->dev
);
282 badblocks_populate(&nvdimm_bus
->badrange
, bb
, res
);
283 nvdimm_bus_unlock(&nvdimm_bus
->dev
);
285 EXPORT_SYMBOL_GPL(nvdimm_badblocks_populate
);