1 /* 2 * Copyright(c) 2017 Intel Corporation. All rights reserved. 3 * 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. 7 * 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. 12 */ 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> 23 #include <linux/io.h> 24 #include "nd-core.h" 25 #include "nd.h" 26 27 void badrange_init(struct badrange *badrange) 28 { 29 INIT_LIST_HEAD(&badrange->list); 30 spin_lock_init(&badrange->lock); 31 } 32 EXPORT_SYMBOL_GPL(badrange_init); 33 34 static void append_badrange_entry(struct badrange *badrange, 35 struct badrange_entry *bre, u64 addr, u64 length) 36 { 37 lockdep_assert_held(&badrange->lock); 38 bre->start = addr; 39 bre->length = length; 40 list_add_tail(&bre->list, &badrange->list); 41 } 42 43 static int alloc_and_append_badrange_entry(struct badrange *badrange, 44 u64 addr, u64 length, gfp_t flags) 45 { 46 struct badrange_entry *bre; 47 48 bre = kzalloc(sizeof(*bre), flags); 49 if (!bre) 50 return -ENOMEM; 51 52 append_badrange_entry(badrange, bre, addr, length); 53 return 0; 54 } 55 56 static int add_badrange(struct badrange *badrange, u64 addr, u64 length) 57 { 58 struct badrange_entry *bre, *bre_new; 59 60 spin_unlock(&badrange->lock); 61 bre_new = kzalloc(sizeof(*bre_new), GFP_KERNEL); 62 spin_lock(&badrange->lock); 63 64 if (list_empty(&badrange->list)) { 65 if (!bre_new) 66 return -ENOMEM; 67 append_badrange_entry(badrange, bre_new, addr, length); 68 return 0; 69 } 70 71 /* 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 75 */ 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) 80 bre->length = length; 81 kfree(bre_new); 82 return 0; 83 } 84 85 /* 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 89 */ 90 if (!bre_new) 91 return -ENOMEM; 92 append_badrange_entry(badrange, bre_new, addr, length); 93 94 return 0; 95 } 96 97 int badrange_add(struct badrange *badrange, u64 addr, u64 length) 98 { 99 int rc; 100 101 spin_lock(&badrange->lock); 102 rc = add_badrange(badrange, addr, length); 103 spin_unlock(&badrange->lock); 104 105 return rc; 106 } 107 EXPORT_SYMBOL_GPL(badrange_add); 108 109 void badrange_forget(struct badrange *badrange, phys_addr_t start, 110 unsigned int len) 111 { 112 struct list_head *badrange_list = &badrange->list; 113 u64 clr_end = start + len - 1; 114 struct badrange_entry *bre, *next; 115 116 spin_lock(&badrange->lock); 117 118 /* 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 124 */ 125 126 list_for_each_entry_safe(bre, next, badrange_list, list) { 127 u64 bre_end = bre->start + bre->length - 1; 128 129 /* Skip intervals with no intersection */ 130 if (bre_end < start) 131 continue; 132 if (bre->start > clr_end) 133 continue; 134 /* Delete completely overlapped badrange entries */ 135 if ((bre->start >= start) && (bre_end <= clr_end)) { 136 list_del(&bre->list); 137 kfree(bre); 138 continue; 139 } 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; 144 continue; 145 } 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; 150 continue; 151 } 152 /* 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. 156 */ 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; 160 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; 166 continue; 167 } 168 } 169 spin_unlock(&badrange->lock); 170 } 171 EXPORT_SYMBOL_GPL(badrange_forget); 172 173 static void set_badblock(struct badblocks *bb, sector_t s, int num) 174 { 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", 180 __func__, (u64) s); 181 } 182 183 /** 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 188 * 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) 192 */ 193 static void __add_badblock_range(struct badblocks *bb, u64 ns_offset, u64 len) 194 { 195 const unsigned int sector_size = 512; 196 sector_t start_sector, end_sector; 197 u64 num_sectors; 198 u32 rem; 199 200 start_sector = div_u64(ns_offset, sector_size); 201 end_sector = div_u64_rem(ns_offset + len, sector_size, &rem); 202 if (rem) 203 end_sector++; 204 num_sectors = end_sector - start_sector; 205 206 if (unlikely(num_sectors > (u64)INT_MAX)) { 207 u64 remaining = num_sectors; 208 sector_t s = start_sector; 209 210 while (remaining) { 211 int done = min_t(u64, remaining, INT_MAX); 212 213 set_badblock(bb, s, done); 214 remaining -= done; 215 s += done; 216 } 217 } else 218 set_badblock(bb, start_sector, num_sectors); 219 } 220 221 static void badblocks_populate(struct badrange *badrange, 222 struct badblocks *bb, const struct resource *res) 223 { 224 struct badrange_entry *bre; 225 226 if (list_empty(&badrange->list)) 227 return; 228 229 list_for_each_entry(bre, &badrange->list, list) { 230 u64 bre_end = bre->start + bre->length - 1; 231 232 /* Discard intervals with no intersection */ 233 if (bre_end < res->start) 234 continue; 235 if (bre->start > res->end) 236 continue; 237 /* Deal with any overlap after start of the namespace */ 238 if (bre->start >= res->start) { 239 u64 start = bre->start; 240 u64 len; 241 242 if (bre_end <= res->end) 243 len = bre->length; 244 else 245 len = res->start + resource_size(res) 246 - bre->start; 247 __add_badblock_range(bb, start - res->start, len); 248 continue; 249 } 250 /* 251 * Deal with overlap for badrange starting before 252 * the namespace. 253 */ 254 if (bre->start < res->start) { 255 u64 len; 256 257 if (bre_end < res->end) 258 len = bre->start + bre->length - res->start; 259 else 260 len = resource_size(res); 261 __add_badblock_range(bb, 0, len); 262 } 263 } 264 } 265 266 /** 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 271 * 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 276 */ 277 void nvdimm_badblocks_populate(struct nd_region *nd_region, 278 struct badblocks *bb, const struct resource *res) 279 { 280 struct nvdimm_bus *nvdimm_bus; 281 282 if (!is_memory(&nd_region->dev)) { 283 dev_WARN_ONCE(&nd_region->dev, 1, 284 "%s only valid for pmem regions\n", __func__); 285 return; 286 } 287 nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); 288 289 nvdimm_bus_lock(&nvdimm_bus->dev); 290 badblocks_populate(&nvdimm_bus->badrange, bb, res); 291 nvdimm_bus_unlock(&nvdimm_bus->dev); 292 } 293 EXPORT_SYMBOL_GPL(nvdimm_badblocks_populate); 294