2 * Copyright(c) 2017 Intel Corporation. All rights reserved.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 #include <linux/libnvdimm.h>
14 #include <linux/badblocks.h>
15 #include <linux/export.h>
16 #include <linux/module.h>
17 #include <linux/blkdev.h>
18 #include <linux/device.h>
19 #include <linux/ctype.h>
20 #include <linux/ndctl.h>
21 #include <linux/mutex.h>
22 #include <linux/slab.h>
27 void badrange_init(struct badrange
*badrange
)
29 INIT_LIST_HEAD(&badrange
->list
);
30 spin_lock_init(&badrange
->lock
);
32 EXPORT_SYMBOL_GPL(badrange_init
);
34 static void append_badrange_entry(struct badrange
*badrange
,
35 struct badrange_entry
*bre
, u64 addr
, u64 length
)
37 lockdep_assert_held(&badrange
->lock
);
40 list_add_tail(&bre
->list
, &badrange
->list
);
43 static int alloc_and_append_badrange_entry(struct badrange
*badrange
,
44 u64 addr
, u64 length
, gfp_t flags
)
46 struct badrange_entry
*bre
;
48 bre
= kzalloc(sizeof(*bre
), flags
);
52 append_badrange_entry(badrange
, bre
, addr
, length
);
56 static int add_badrange(struct badrange
*badrange
, u64 addr
, u64 length
)
58 struct badrange_entry
*bre
, *bre_new
;
60 spin_unlock(&badrange
->lock
);
61 bre_new
= kzalloc(sizeof(*bre_new
), GFP_KERNEL
);
62 spin_lock(&badrange
->lock
);
64 if (list_empty(&badrange
->list
)) {
67 append_badrange_entry(badrange
, bre_new
, addr
, length
);
72 * There is a chance this is a duplicate, check for those first.
73 * This will be the common case as ARS_STATUS returns all known
74 * errors in the SPA space, and we can't query it per region
76 list_for_each_entry(bre
, &badrange
->list
, list
)
77 if (bre
->start
== addr
) {
78 /* If length has changed, update this list entry */
79 if (bre
->length
!= length
)
86 * If not a duplicate or a simple length update, add the entry as is,
87 * as any overlapping ranges will get resolved when the list is consumed
88 * and converted to badblocks
92 append_badrange_entry(badrange
, bre_new
, addr
, length
);
97 int badrange_add(struct badrange
*badrange
, u64 addr
, u64 length
)
101 spin_lock(&badrange
->lock
);
102 rc
= add_badrange(badrange
, addr
, length
);
103 spin_unlock(&badrange
->lock
);
107 EXPORT_SYMBOL_GPL(badrange_add
);
109 void badrange_forget(struct badrange
*badrange
, phys_addr_t start
,
112 struct list_head
*badrange_list
= &badrange
->list
;
113 u64 clr_end
= start
+ len
- 1;
114 struct badrange_entry
*bre
, *next
;
116 spin_lock(&badrange
->lock
);
119 * [start, clr_end] is the badrange interval being cleared.
120 * [bre->start, bre_end] is the badrange_list entry we're comparing
121 * the above interval against. The badrange list entry may need
122 * to be modified (update either start or length), deleted, or
123 * split into two based on the overlap characteristics
126 list_for_each_entry_safe(bre
, next
, badrange_list
, list
) {
127 u64 bre_end
= bre
->start
+ bre
->length
- 1;
129 /* Skip intervals with no intersection */
132 if (bre
->start
> clr_end
)
134 /* Delete completely overlapped badrange entries */
135 if ((bre
->start
>= start
) && (bre_end
<= clr_end
)) {
136 list_del(&bre
->list
);
140 /* Adjust start point of partially cleared entries */
141 if ((start
<= bre
->start
) && (clr_end
> bre
->start
)) {
142 bre
->length
-= clr_end
- bre
->start
+ 1;
143 bre
->start
= clr_end
+ 1;
146 /* Adjust bre->length for partial clearing at the tail end */
147 if ((bre
->start
< start
) && (bre_end
<= clr_end
)) {
148 /* bre->start remains the same */
149 bre
->length
= start
- bre
->start
;
153 * If clearing in the middle of an entry, we split it into
154 * two by modifying the current entry to represent one half of
155 * the split, and adding a new entry for the second half.
157 if ((bre
->start
< start
) && (bre_end
> clr_end
)) {
158 u64 new_start
= clr_end
+ 1;
159 u64 new_len
= bre_end
- new_start
+ 1;
161 /* Add new entry covering the right half */
162 alloc_and_append_badrange_entry(badrange
, new_start
,
163 new_len
, GFP_NOWAIT
);
164 /* Adjust this entry to cover the left half */
165 bre
->length
= start
- bre
->start
;
169 spin_unlock(&badrange
->lock
);
171 EXPORT_SYMBOL_GPL(badrange_forget
);
173 static void set_badblock(struct badblocks
*bb
, sector_t s
, int num
)
175 dev_dbg(bb
->dev
, "Found a bad range (0x%llx, 0x%llx)\n",
176 (u64
) s
* 512, (u64
) num
* 512);
177 /* this isn't an error as the hardware will still throw an exception */
178 if (badblocks_set(bb
, s
, num
, 1))
179 dev_info_once(bb
->dev
, "%s: failed for sector %llx\n",
184 * __add_badblock_range() - Convert a physical address range to bad sectors
185 * @bb: badblocks instance to populate
186 * @ns_offset: namespace offset where the error range begins (in bytes)
187 * @len: number of bytes of badrange to be added
189 * This assumes that the range provided with (ns_offset, len) is within
190 * the bounds of physical addresses for this namespace, i.e. lies in the
191 * interval [ns_start, ns_start + ns_size)
193 static void __add_badblock_range(struct badblocks
*bb
, u64 ns_offset
, u64 len
)
195 const unsigned int sector_size
= 512;
196 sector_t start_sector
, end_sector
;
200 start_sector
= div_u64(ns_offset
, sector_size
);
201 end_sector
= div_u64_rem(ns_offset
+ len
, sector_size
, &rem
);
204 num_sectors
= end_sector
- start_sector
;
206 if (unlikely(num_sectors
> (u64
)INT_MAX
)) {
207 u64 remaining
= num_sectors
;
208 sector_t s
= start_sector
;
211 int done
= min_t(u64
, remaining
, INT_MAX
);
213 set_badblock(bb
, s
, done
);
218 set_badblock(bb
, start_sector
, num_sectors
);
221 static void badblocks_populate(struct badrange
*badrange
,
222 struct badblocks
*bb
, const struct resource
*res
)
224 struct badrange_entry
*bre
;
226 if (list_empty(&badrange
->list
))
229 list_for_each_entry(bre
, &badrange
->list
, list
) {
230 u64 bre_end
= bre
->start
+ bre
->length
- 1;
232 /* Discard intervals with no intersection */
233 if (bre_end
< res
->start
)
235 if (bre
->start
> res
->end
)
237 /* Deal with any overlap after start of the namespace */
238 if (bre
->start
>= res
->start
) {
239 u64 start
= bre
->start
;
242 if (bre_end
<= res
->end
)
245 len
= res
->start
+ resource_size(res
)
247 __add_badblock_range(bb
, start
- res
->start
, len
);
251 * Deal with overlap for badrange starting before
254 if (bre
->start
< res
->start
) {
257 if (bre_end
< res
->end
)
258 len
= bre
->start
+ bre
->length
- res
->start
;
260 len
= resource_size(res
);
261 __add_badblock_range(bb
, 0, len
);
267 * nvdimm_badblocks_populate() - Convert a list of badranges to badblocks
268 * @region: parent region of the range to interrogate
269 * @bb: badblocks instance to populate
270 * @res: resource range to consider
272 * The badrange list generated during bus initialization may contain
273 * multiple, possibly overlapping physical address ranges. Compare each
274 * of these ranges to the resource range currently being initialized,
275 * and add badblocks entries for all matching sub-ranges
277 void nvdimm_badblocks_populate(struct nd_region
*nd_region
,
278 struct badblocks
*bb
, const struct resource
*res
)
280 struct nvdimm_bus
*nvdimm_bus
;
282 if (!is_memory(&nd_region
->dev
)) {
283 dev_WARN_ONCE(&nd_region
->dev
, 1,
284 "%s only valid for pmem regions\n", __func__
);
287 nvdimm_bus
= walk_to_nvdimm_bus(&nd_region
->dev
);
289 nvdimm_bus_lock(&nvdimm_bus
->dev
);
290 badblocks_populate(&nvdimm_bus
->badrange
, bb
, res
);
291 nvdimm_bus_unlock(&nvdimm_bus
->dev
);
293 EXPORT_SYMBOL_GPL(nvdimm_badblocks_populate
);