162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Bad block management 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * - Heavily based on MD badblocks code from Neil Brown 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2015, Intel Corporation. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/badblocks.h> 1162306a36Sopenharmony_ci#include <linux/seqlock.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/stddef.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/** 2062306a36Sopenharmony_ci * badblocks_check() - check a given range for bad sectors 2162306a36Sopenharmony_ci * @bb: the badblocks structure that holds all badblock information 2262306a36Sopenharmony_ci * @s: sector (start) at which to check for badblocks 2362306a36Sopenharmony_ci * @sectors: number of sectors to check for badblocks 2462306a36Sopenharmony_ci * @first_bad: pointer to store location of the first badblock 2562306a36Sopenharmony_ci * @bad_sectors: pointer to store number of badblocks after @first_bad 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * We can record which blocks on each device are 'bad' and so just 2862306a36Sopenharmony_ci * fail those blocks, or that stripe, rather than the whole device. 2962306a36Sopenharmony_ci * Entries in the bad-block table are 64bits wide. This comprises: 3062306a36Sopenharmony_ci * Length of bad-range, in sectors: 0-511 for lengths 1-512 3162306a36Sopenharmony_ci * Start of bad-range, sector offset, 54 bits (allows 8 exbibytes) 3262306a36Sopenharmony_ci * A 'shift' can be set so that larger blocks are tracked and 3362306a36Sopenharmony_ci * consequently larger devices can be covered. 3462306a36Sopenharmony_ci * 'Acknowledged' flag - 1 bit. - the most significant bit. 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * Locking of the bad-block table uses a seqlock so badblocks_check 3762306a36Sopenharmony_ci * might need to retry if it is very unlucky. 3862306a36Sopenharmony_ci * We will sometimes want to check for bad blocks in a bi_end_io function, 3962306a36Sopenharmony_ci * so we use the write_seqlock_irq variant. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * When looking for a bad block we specify a range and want to 4262306a36Sopenharmony_ci * know if any block in the range is bad. So we binary-search 4362306a36Sopenharmony_ci * to the last range that starts at-or-before the given endpoint, 4462306a36Sopenharmony_ci * (or "before the sector after the target range") 4562306a36Sopenharmony_ci * then see if it ends after the given start. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Return: 4862306a36Sopenharmony_ci * 0: there are no known bad blocks in the range 4962306a36Sopenharmony_ci * 1: there are known bad block which are all acknowledged 5062306a36Sopenharmony_ci * -1: there are bad blocks which have not yet been acknowledged in metadata. 5162306a36Sopenharmony_ci * plus the start/length of the first bad section we overlap. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ciint badblocks_check(struct badblocks *bb, sector_t s, int sectors, 5462306a36Sopenharmony_ci sector_t *first_bad, int *bad_sectors) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci int hi; 5762306a36Sopenharmony_ci int lo; 5862306a36Sopenharmony_ci u64 *p = bb->page; 5962306a36Sopenharmony_ci int rv; 6062306a36Sopenharmony_ci sector_t target = s + sectors; 6162306a36Sopenharmony_ci unsigned seq; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (bb->shift > 0) { 6462306a36Sopenharmony_ci /* round the start down, and the end up */ 6562306a36Sopenharmony_ci s >>= bb->shift; 6662306a36Sopenharmony_ci target += (1<<bb->shift) - 1; 6762306a36Sopenharmony_ci target >>= bb->shift; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci /* 'target' is now the first block after the bad range */ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciretry: 7262306a36Sopenharmony_ci seq = read_seqbegin(&bb->lock); 7362306a36Sopenharmony_ci lo = 0; 7462306a36Sopenharmony_ci rv = 0; 7562306a36Sopenharmony_ci hi = bb->count; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Binary search between lo and hi for 'target' 7862306a36Sopenharmony_ci * i.e. for the last range that starts before 'target' 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci /* INVARIANT: ranges before 'lo' and at-or-after 'hi' 8162306a36Sopenharmony_ci * are known not to be the last range before target. 8262306a36Sopenharmony_ci * VARIANT: hi-lo is the number of possible 8362306a36Sopenharmony_ci * ranges, and decreases until it reaches 1 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci while (hi - lo > 1) { 8662306a36Sopenharmony_ci int mid = (lo + hi) / 2; 8762306a36Sopenharmony_ci sector_t a = BB_OFFSET(p[mid]); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (a < target) 9062306a36Sopenharmony_ci /* This could still be the one, earlier ranges 9162306a36Sopenharmony_ci * could not. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci lo = mid; 9462306a36Sopenharmony_ci else 9562306a36Sopenharmony_ci /* This and later ranges are definitely out. */ 9662306a36Sopenharmony_ci hi = mid; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci /* 'lo' might be the last that started before target, but 'hi' isn't */ 9962306a36Sopenharmony_ci if (hi > lo) { 10062306a36Sopenharmony_ci /* need to check all range that end after 's' to see if 10162306a36Sopenharmony_ci * any are unacknowledged. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci while (lo >= 0 && 10462306a36Sopenharmony_ci BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) { 10562306a36Sopenharmony_ci if (BB_OFFSET(p[lo]) < target) { 10662306a36Sopenharmony_ci /* starts before the end, and finishes after 10762306a36Sopenharmony_ci * the start, so they must overlap 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci if (rv != -1 && BB_ACK(p[lo])) 11062306a36Sopenharmony_ci rv = 1; 11162306a36Sopenharmony_ci else 11262306a36Sopenharmony_ci rv = -1; 11362306a36Sopenharmony_ci *first_bad = BB_OFFSET(p[lo]); 11462306a36Sopenharmony_ci *bad_sectors = BB_LEN(p[lo]); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci lo--; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (read_seqretry(&bb->lock, seq)) 12162306a36Sopenharmony_ci goto retry; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return rv; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(badblocks_check); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void badblocks_update_acked(struct badblocks *bb) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci u64 *p = bb->page; 13062306a36Sopenharmony_ci int i; 13162306a36Sopenharmony_ci bool unacked = false; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!bb->unacked_exist) 13462306a36Sopenharmony_ci return; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci for (i = 0; i < bb->count ; i++) { 13762306a36Sopenharmony_ci if (!BB_ACK(p[i])) { 13862306a36Sopenharmony_ci unacked = true; 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!unacked) 14462306a36Sopenharmony_ci bb->unacked_exist = 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/** 14862306a36Sopenharmony_ci * badblocks_set() - Add a range of bad blocks to the table. 14962306a36Sopenharmony_ci * @bb: the badblocks structure that holds all badblock information 15062306a36Sopenharmony_ci * @s: first sector to mark as bad 15162306a36Sopenharmony_ci * @sectors: number of sectors to mark as bad 15262306a36Sopenharmony_ci * @acknowledged: weather to mark the bad sectors as acknowledged 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * This might extend the table, or might contract it if two adjacent ranges 15562306a36Sopenharmony_ci * can be merged. We binary-search to find the 'insertion' point, then 15662306a36Sopenharmony_ci * decide how best to handle it. 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci * Return: 15962306a36Sopenharmony_ci * 0: success 16062306a36Sopenharmony_ci * 1: failed to set badblocks (out of space) 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ciint badblocks_set(struct badblocks *bb, sector_t s, int sectors, 16362306a36Sopenharmony_ci int acknowledged) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci u64 *p; 16662306a36Sopenharmony_ci int lo, hi; 16762306a36Sopenharmony_ci int rv = 0; 16862306a36Sopenharmony_ci unsigned long flags; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (bb->shift < 0) 17162306a36Sopenharmony_ci /* badblocks are disabled */ 17262306a36Sopenharmony_ci return 1; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (bb->shift) { 17562306a36Sopenharmony_ci /* round the start down, and the end up */ 17662306a36Sopenharmony_ci sector_t next = s + sectors; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci s >>= bb->shift; 17962306a36Sopenharmony_ci next += (1<<bb->shift) - 1; 18062306a36Sopenharmony_ci next >>= bb->shift; 18162306a36Sopenharmony_ci sectors = next - s; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci write_seqlock_irqsave(&bb->lock, flags); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci p = bb->page; 18762306a36Sopenharmony_ci lo = 0; 18862306a36Sopenharmony_ci hi = bb->count; 18962306a36Sopenharmony_ci /* Find the last range that starts at-or-before 's' */ 19062306a36Sopenharmony_ci while (hi - lo > 1) { 19162306a36Sopenharmony_ci int mid = (lo + hi) / 2; 19262306a36Sopenharmony_ci sector_t a = BB_OFFSET(p[mid]); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (a <= s) 19562306a36Sopenharmony_ci lo = mid; 19662306a36Sopenharmony_ci else 19762306a36Sopenharmony_ci hi = mid; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci if (hi > lo && BB_OFFSET(p[lo]) > s) 20062306a36Sopenharmony_ci hi = lo; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (hi > lo) { 20362306a36Sopenharmony_ci /* we found a range that might merge with the start 20462306a36Sopenharmony_ci * of our new range 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci sector_t a = BB_OFFSET(p[lo]); 20762306a36Sopenharmony_ci sector_t e = a + BB_LEN(p[lo]); 20862306a36Sopenharmony_ci int ack = BB_ACK(p[lo]); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (e >= s) { 21162306a36Sopenharmony_ci /* Yes, we can merge with a previous range */ 21262306a36Sopenharmony_ci if (s == a && s + sectors >= e) 21362306a36Sopenharmony_ci /* new range covers old */ 21462306a36Sopenharmony_ci ack = acknowledged; 21562306a36Sopenharmony_ci else 21662306a36Sopenharmony_ci ack = ack && acknowledged; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (e < s + sectors) 21962306a36Sopenharmony_ci e = s + sectors; 22062306a36Sopenharmony_ci if (e - a <= BB_MAX_LEN) { 22162306a36Sopenharmony_ci p[lo] = BB_MAKE(a, e-a, ack); 22262306a36Sopenharmony_ci s = e; 22362306a36Sopenharmony_ci } else { 22462306a36Sopenharmony_ci /* does not all fit in one range, 22562306a36Sopenharmony_ci * make p[lo] maximal 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci if (BB_LEN(p[lo]) != BB_MAX_LEN) 22862306a36Sopenharmony_ci p[lo] = BB_MAKE(a, BB_MAX_LEN, ack); 22962306a36Sopenharmony_ci s = a + BB_MAX_LEN; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci sectors = e - s; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci if (sectors && hi < bb->count) { 23562306a36Sopenharmony_ci /* 'hi' points to the first range that starts after 's'. 23662306a36Sopenharmony_ci * Maybe we can merge with the start of that range 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci sector_t a = BB_OFFSET(p[hi]); 23962306a36Sopenharmony_ci sector_t e = a + BB_LEN(p[hi]); 24062306a36Sopenharmony_ci int ack = BB_ACK(p[hi]); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (a <= s + sectors) { 24362306a36Sopenharmony_ci /* merging is possible */ 24462306a36Sopenharmony_ci if (e <= s + sectors) { 24562306a36Sopenharmony_ci /* full overlap */ 24662306a36Sopenharmony_ci e = s + sectors; 24762306a36Sopenharmony_ci ack = acknowledged; 24862306a36Sopenharmony_ci } else 24962306a36Sopenharmony_ci ack = ack && acknowledged; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci a = s; 25262306a36Sopenharmony_ci if (e - a <= BB_MAX_LEN) { 25362306a36Sopenharmony_ci p[hi] = BB_MAKE(a, e-a, ack); 25462306a36Sopenharmony_ci s = e; 25562306a36Sopenharmony_ci } else { 25662306a36Sopenharmony_ci p[hi] = BB_MAKE(a, BB_MAX_LEN, ack); 25762306a36Sopenharmony_ci s = a + BB_MAX_LEN; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci sectors = e - s; 26062306a36Sopenharmony_ci lo = hi; 26162306a36Sopenharmony_ci hi++; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci if (sectors == 0 && hi < bb->count) { 26562306a36Sopenharmony_ci /* we might be able to combine lo and hi */ 26662306a36Sopenharmony_ci /* Note: 's' is at the end of 'lo' */ 26762306a36Sopenharmony_ci sector_t a = BB_OFFSET(p[hi]); 26862306a36Sopenharmony_ci int lolen = BB_LEN(p[lo]); 26962306a36Sopenharmony_ci int hilen = BB_LEN(p[hi]); 27062306a36Sopenharmony_ci int newlen = lolen + hilen - (s - a); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (s >= a && newlen < BB_MAX_LEN) { 27362306a36Sopenharmony_ci /* yes, we can combine them */ 27462306a36Sopenharmony_ci int ack = BB_ACK(p[lo]) && BB_ACK(p[hi]); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci p[lo] = BB_MAKE(BB_OFFSET(p[lo]), newlen, ack); 27762306a36Sopenharmony_ci memmove(p + hi, p + hi + 1, 27862306a36Sopenharmony_ci (bb->count - hi - 1) * 8); 27962306a36Sopenharmony_ci bb->count--; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci while (sectors) { 28362306a36Sopenharmony_ci /* didn't merge (it all). 28462306a36Sopenharmony_ci * Need to add a range just before 'hi' 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci if (bb->count >= MAX_BADBLOCKS) { 28762306a36Sopenharmony_ci /* No room for more */ 28862306a36Sopenharmony_ci rv = 1; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci } else { 29162306a36Sopenharmony_ci int this_sectors = sectors; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci memmove(p + hi + 1, p + hi, 29462306a36Sopenharmony_ci (bb->count - hi) * 8); 29562306a36Sopenharmony_ci bb->count++; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (this_sectors > BB_MAX_LEN) 29862306a36Sopenharmony_ci this_sectors = BB_MAX_LEN; 29962306a36Sopenharmony_ci p[hi] = BB_MAKE(s, this_sectors, acknowledged); 30062306a36Sopenharmony_ci sectors -= this_sectors; 30162306a36Sopenharmony_ci s += this_sectors; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci bb->changed = 1; 30662306a36Sopenharmony_ci if (!acknowledged) 30762306a36Sopenharmony_ci bb->unacked_exist = 1; 30862306a36Sopenharmony_ci else 30962306a36Sopenharmony_ci badblocks_update_acked(bb); 31062306a36Sopenharmony_ci write_sequnlock_irqrestore(&bb->lock, flags); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return rv; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(badblocks_set); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci/** 31762306a36Sopenharmony_ci * badblocks_clear() - Remove a range of bad blocks to the table. 31862306a36Sopenharmony_ci * @bb: the badblocks structure that holds all badblock information 31962306a36Sopenharmony_ci * @s: first sector to mark as bad 32062306a36Sopenharmony_ci * @sectors: number of sectors to mark as bad 32162306a36Sopenharmony_ci * 32262306a36Sopenharmony_ci * This may involve extending the table if we spilt a region, 32362306a36Sopenharmony_ci * but it must not fail. So if the table becomes full, we just 32462306a36Sopenharmony_ci * drop the remove request. 32562306a36Sopenharmony_ci * 32662306a36Sopenharmony_ci * Return: 32762306a36Sopenharmony_ci * 0: success 32862306a36Sopenharmony_ci * 1: failed to clear badblocks 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ciint badblocks_clear(struct badblocks *bb, sector_t s, int sectors) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci u64 *p; 33362306a36Sopenharmony_ci int lo, hi; 33462306a36Sopenharmony_ci sector_t target = s + sectors; 33562306a36Sopenharmony_ci int rv = 0; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (bb->shift > 0) { 33862306a36Sopenharmony_ci /* When clearing we round the start up and the end down. 33962306a36Sopenharmony_ci * This should not matter as the shift should align with 34062306a36Sopenharmony_ci * the block size and no rounding should ever be needed. 34162306a36Sopenharmony_ci * However it is better the think a block is bad when it 34262306a36Sopenharmony_ci * isn't than to think a block is not bad when it is. 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ci s += (1<<bb->shift) - 1; 34562306a36Sopenharmony_ci s >>= bb->shift; 34662306a36Sopenharmony_ci target >>= bb->shift; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci write_seqlock_irq(&bb->lock); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci p = bb->page; 35262306a36Sopenharmony_ci lo = 0; 35362306a36Sopenharmony_ci hi = bb->count; 35462306a36Sopenharmony_ci /* Find the last range that starts before 'target' */ 35562306a36Sopenharmony_ci while (hi - lo > 1) { 35662306a36Sopenharmony_ci int mid = (lo + hi) / 2; 35762306a36Sopenharmony_ci sector_t a = BB_OFFSET(p[mid]); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (a < target) 36062306a36Sopenharmony_ci lo = mid; 36162306a36Sopenharmony_ci else 36262306a36Sopenharmony_ci hi = mid; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci if (hi > lo) { 36562306a36Sopenharmony_ci /* p[lo] is the last range that could overlap the 36662306a36Sopenharmony_ci * current range. Earlier ranges could also overlap, 36762306a36Sopenharmony_ci * but only this one can overlap the end of the range. 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci if ((BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > target) && 37062306a36Sopenharmony_ci (BB_OFFSET(p[lo]) < target)) { 37162306a36Sopenharmony_ci /* Partial overlap, leave the tail of this range */ 37262306a36Sopenharmony_ci int ack = BB_ACK(p[lo]); 37362306a36Sopenharmony_ci sector_t a = BB_OFFSET(p[lo]); 37462306a36Sopenharmony_ci sector_t end = a + BB_LEN(p[lo]); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (a < s) { 37762306a36Sopenharmony_ci /* we need to split this range */ 37862306a36Sopenharmony_ci if (bb->count >= MAX_BADBLOCKS) { 37962306a36Sopenharmony_ci rv = -ENOSPC; 38062306a36Sopenharmony_ci goto out; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci memmove(p+lo+1, p+lo, (bb->count - lo) * 8); 38362306a36Sopenharmony_ci bb->count++; 38462306a36Sopenharmony_ci p[lo] = BB_MAKE(a, s-a, ack); 38562306a36Sopenharmony_ci lo++; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci p[lo] = BB_MAKE(target, end - target, ack); 38862306a36Sopenharmony_ci /* there is no longer an overlap */ 38962306a36Sopenharmony_ci hi = lo; 39062306a36Sopenharmony_ci lo--; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci while (lo >= 0 && 39362306a36Sopenharmony_ci (BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) && 39462306a36Sopenharmony_ci (BB_OFFSET(p[lo]) < target)) { 39562306a36Sopenharmony_ci /* This range does overlap */ 39662306a36Sopenharmony_ci if (BB_OFFSET(p[lo]) < s) { 39762306a36Sopenharmony_ci /* Keep the early parts of this range. */ 39862306a36Sopenharmony_ci int ack = BB_ACK(p[lo]); 39962306a36Sopenharmony_ci sector_t start = BB_OFFSET(p[lo]); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci p[lo] = BB_MAKE(start, s - start, ack); 40262306a36Sopenharmony_ci /* now low doesn't overlap, so.. */ 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci lo--; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci /* 'lo' is strictly before, 'hi' is strictly after, 40862306a36Sopenharmony_ci * anything between needs to be discarded 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci if (hi - lo > 1) { 41162306a36Sopenharmony_ci memmove(p+lo+1, p+hi, (bb->count - hi) * 8); 41262306a36Sopenharmony_ci bb->count -= (hi - lo - 1); 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci badblocks_update_acked(bb); 41762306a36Sopenharmony_ci bb->changed = 1; 41862306a36Sopenharmony_ciout: 41962306a36Sopenharmony_ci write_sequnlock_irq(&bb->lock); 42062306a36Sopenharmony_ci return rv; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(badblocks_clear); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci/** 42562306a36Sopenharmony_ci * ack_all_badblocks() - Acknowledge all bad blocks in a list. 42662306a36Sopenharmony_ci * @bb: the badblocks structure that holds all badblock information 42762306a36Sopenharmony_ci * 42862306a36Sopenharmony_ci * This only succeeds if ->changed is clear. It is used by 42962306a36Sopenharmony_ci * in-kernel metadata updates 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_civoid ack_all_badblocks(struct badblocks *bb) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci if (bb->page == NULL || bb->changed) 43462306a36Sopenharmony_ci /* no point even trying */ 43562306a36Sopenharmony_ci return; 43662306a36Sopenharmony_ci write_seqlock_irq(&bb->lock); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (bb->changed == 0 && bb->unacked_exist) { 43962306a36Sopenharmony_ci u64 *p = bb->page; 44062306a36Sopenharmony_ci int i; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci for (i = 0; i < bb->count ; i++) { 44362306a36Sopenharmony_ci if (!BB_ACK(p[i])) { 44462306a36Sopenharmony_ci sector_t start = BB_OFFSET(p[i]); 44562306a36Sopenharmony_ci int len = BB_LEN(p[i]); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci p[i] = BB_MAKE(start, len, 1); 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci bb->unacked_exist = 0; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci write_sequnlock_irq(&bb->lock); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ack_all_badblocks); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/** 45762306a36Sopenharmony_ci * badblocks_show() - sysfs access to bad-blocks list 45862306a36Sopenharmony_ci * @bb: the badblocks structure that holds all badblock information 45962306a36Sopenharmony_ci * @page: buffer received from sysfs 46062306a36Sopenharmony_ci * @unack: weather to show unacknowledged badblocks 46162306a36Sopenharmony_ci * 46262306a36Sopenharmony_ci * Return: 46362306a36Sopenharmony_ci * Length of returned data 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_cissize_t badblocks_show(struct badblocks *bb, char *page, int unack) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci size_t len; 46862306a36Sopenharmony_ci int i; 46962306a36Sopenharmony_ci u64 *p = bb->page; 47062306a36Sopenharmony_ci unsigned seq; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (bb->shift < 0) 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ciretry: 47662306a36Sopenharmony_ci seq = read_seqbegin(&bb->lock); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci len = 0; 47962306a36Sopenharmony_ci i = 0; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci while (len < PAGE_SIZE && i < bb->count) { 48262306a36Sopenharmony_ci sector_t s = BB_OFFSET(p[i]); 48362306a36Sopenharmony_ci unsigned int length = BB_LEN(p[i]); 48462306a36Sopenharmony_ci int ack = BB_ACK(p[i]); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci i++; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (unack && ack) 48962306a36Sopenharmony_ci continue; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci len += snprintf(page+len, PAGE_SIZE-len, "%llu %u\n", 49262306a36Sopenharmony_ci (unsigned long long)s << bb->shift, 49362306a36Sopenharmony_ci length << bb->shift); 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci if (unack && len == 0) 49662306a36Sopenharmony_ci bb->unacked_exist = 0; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (read_seqretry(&bb->lock, seq)) 49962306a36Sopenharmony_ci goto retry; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return len; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(badblocks_show); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci/** 50662306a36Sopenharmony_ci * badblocks_store() - sysfs access to bad-blocks list 50762306a36Sopenharmony_ci * @bb: the badblocks structure that holds all badblock information 50862306a36Sopenharmony_ci * @page: buffer received from sysfs 50962306a36Sopenharmony_ci * @len: length of data received from sysfs 51062306a36Sopenharmony_ci * @unack: weather to show unacknowledged badblocks 51162306a36Sopenharmony_ci * 51262306a36Sopenharmony_ci * Return: 51362306a36Sopenharmony_ci * Length of the buffer processed or -ve error. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_cissize_t badblocks_store(struct badblocks *bb, const char *page, size_t len, 51662306a36Sopenharmony_ci int unack) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci unsigned long long sector; 51962306a36Sopenharmony_ci int length; 52062306a36Sopenharmony_ci char newline; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci switch (sscanf(page, "%llu %d%c", §or, &length, &newline)) { 52362306a36Sopenharmony_ci case 3: 52462306a36Sopenharmony_ci if (newline != '\n') 52562306a36Sopenharmony_ci return -EINVAL; 52662306a36Sopenharmony_ci fallthrough; 52762306a36Sopenharmony_ci case 2: 52862306a36Sopenharmony_ci if (length <= 0) 52962306a36Sopenharmony_ci return -EINVAL; 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci default: 53262306a36Sopenharmony_ci return -EINVAL; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (badblocks_set(bb, sector, length, !unack)) 53662306a36Sopenharmony_ci return -ENOSPC; 53762306a36Sopenharmony_ci else 53862306a36Sopenharmony_ci return len; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(badblocks_store); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int __badblocks_init(struct device *dev, struct badblocks *bb, 54362306a36Sopenharmony_ci int enable) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci bb->dev = dev; 54662306a36Sopenharmony_ci bb->count = 0; 54762306a36Sopenharmony_ci if (enable) 54862306a36Sopenharmony_ci bb->shift = 0; 54962306a36Sopenharmony_ci else 55062306a36Sopenharmony_ci bb->shift = -1; 55162306a36Sopenharmony_ci if (dev) 55262306a36Sopenharmony_ci bb->page = devm_kzalloc(dev, PAGE_SIZE, GFP_KERNEL); 55362306a36Sopenharmony_ci else 55462306a36Sopenharmony_ci bb->page = kzalloc(PAGE_SIZE, GFP_KERNEL); 55562306a36Sopenharmony_ci if (!bb->page) { 55662306a36Sopenharmony_ci bb->shift = -1; 55762306a36Sopenharmony_ci return -ENOMEM; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci seqlock_init(&bb->lock); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/** 56562306a36Sopenharmony_ci * badblocks_init() - initialize the badblocks structure 56662306a36Sopenharmony_ci * @bb: the badblocks structure that holds all badblock information 56762306a36Sopenharmony_ci * @enable: weather to enable badblocks accounting 56862306a36Sopenharmony_ci * 56962306a36Sopenharmony_ci * Return: 57062306a36Sopenharmony_ci * 0: success 57162306a36Sopenharmony_ci * -ve errno: on error 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_ciint badblocks_init(struct badblocks *bb, int enable) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci return __badblocks_init(NULL, bb, enable); 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(badblocks_init); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ciint devm_init_badblocks(struct device *dev, struct badblocks *bb) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci if (!bb) 58262306a36Sopenharmony_ci return -EINVAL; 58362306a36Sopenharmony_ci return __badblocks_init(dev, bb, 1); 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_init_badblocks); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci/** 58862306a36Sopenharmony_ci * badblocks_exit() - free the badblocks structure 58962306a36Sopenharmony_ci * @bb: the badblocks structure that holds all badblock information 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_civoid badblocks_exit(struct badblocks *bb) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci if (!bb) 59462306a36Sopenharmony_ci return; 59562306a36Sopenharmony_ci if (bb->dev) 59662306a36Sopenharmony_ci devm_kfree(bb->dev, bb->page); 59762306a36Sopenharmony_ci else 59862306a36Sopenharmony_ci kfree(bb->page); 59962306a36Sopenharmony_ci bb->page = NULL; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(badblocks_exit); 602