@@ -144,6 +144,80 @@ static void badblocks_update_acked(struct badblocks *bb)
bb->unacked_exist = false;
}
+/*
+ * try to merge new range with lo and hi.
+ * if s is within lo, merge with lo.
+ * if s + sectors > start of hi, merge with hi.
+ *
+ * Return:
+ * Number of merged sectors
+ */
+static int badblocks_merge(struct badblocks *bb, sector_t s, int sectors,
+ int acknowledged, int *lo, int *hi, bool *changed)
+{
+ u64 *p = bb->page;
+ sector_t a = BB_OFFSET(p[*lo]);
+ sector_t e = a + BB_LEN(p[*lo]);
+ int merged_sectors = 0, ack = acknowledged;
+
+ if (a > s) {
+ *hi = *lo;
+ } else if (e >= s) {
+ /* we can merge with a previous range */
+ if (s > a || s + sectors < e)
+ ack = ack && BB_ACK(p[*lo]);
+
+ if (e < s + sectors)
+ e = s + sectors;
+ if (e - a <= BB_MAX_LEN) {
+ s = e;
+ } else {
+ /*
+ * does not all fit in one range,
+ * make p[lo] maximal
+ */
+ s = a + BB_MAX_LEN;
+ }
+ if (s - a != BB_LEN(p[*lo]) || ack != BB_ACK(p[*lo])) {
+ p[*lo] = BB_MAKE(a, s - a, ack);
+ *changed = true;
+ }
+ merged_sectors += sectors - e + s;
+ sectors = e - s;
+ }
+ if (sectors && *hi < bb->count) {
+ /*
+ * 'hi' points to the first range that starts after 's'.
+ * Maybe we can merge with the start of that range
+ */
+ a = BB_OFFSET(p[*hi]);
+ e = a + BB_LEN(p[*hi]);
+ ack = acknowledged;
+
+ if (a <= s + sectors) {
+ /* merging is possible */
+ if (e <= s + sectors) {
+ /* full overlap */
+ e = s + sectors;
+ } else
+ ack = ack && BB_ACK(p[*hi]);
+
+ a = s;
+ if (e - a <= BB_MAX_LEN)
+ s = e;
+ else
+ s = a + BB_MAX_LEN;
+ p[*hi] = BB_MAKE(a, s-a, ack);
+
+ merged_sectors += sectors - e + s;
+ *changed = true;
+ *lo = *hi;
+ *hi += 1;
+ }
+ }
+ return merged_sectors;
+}
+
/**
* badblocks_set() - Add a range of bad blocks to the table.
* @bb: the badblocks structure that holds all badblock information
@@ -191,6 +265,7 @@ int badblocks_set(struct badblocks *bb, sector_t s, int sectors,
sector_t a;
sector_t e;
int ack;
+ int merged_sectors;
/* Find the last range that starts at-or-before 's' */
while (hi - lo > 1) {
@@ -203,70 +278,10 @@ int badblocks_set(struct badblocks *bb, sector_t s, int sectors,
hi = mid;
}
- /* we found a range that might merge with the start
- * of our new range
- */
- a = BB_OFFSET(p[lo]);
- e = a + BB_LEN(p[lo]);
- ack = BB_ACK(p[lo]);
-
- if (a > s) {
- hi = lo;
- } else if (e >= s) {
- /* Yes, we can merge with a previous range */
- if (s == a && s + sectors >= e)
- /* new range covers old */
- ack = acknowledged;
- else
- ack = ack && acknowledged;
-
- if (e < s + sectors)
- e = s + sectors;
- if (e - a <= BB_MAX_LEN) {
- s = e;
- } else {
- /* does not all fit in one range,
- * make p[lo] maximal
- */
- s = a + BB_MAX_LEN;
- }
- if (s - a != BB_LEN(p[lo]) || ack != BB_ACK(p[lo])) {
- p[lo] = BB_MAKE(a, s - a, ack);
- changed = true;
- }
- sectors = e - s;
- }
- if (sectors && hi < bb->count) {
- /* 'hi' points to the first range that starts after 's'.
- * Maybe we can merge with the start of that range
- */
- a = BB_OFFSET(p[hi]);
- e = a + BB_LEN(p[hi]);
- ack = BB_ACK(p[hi]);
-
- if (a <= s + sectors) {
- /* merging is possible */
- if (e <= s + sectors) {
- /* full overlap */
- e = s + sectors;
- ack = acknowledged;
- } else
- ack = ack && acknowledged;
-
- a = s;
- if (e - a <= BB_MAX_LEN) {
- p[hi] = BB_MAKE(a, e-a, ack);
- s = e;
- } else {
- p[hi] = BB_MAKE(a, BB_MAX_LEN, ack);
- s = a + BB_MAX_LEN;
- }
- sectors = e - s;
- lo = hi;
- hi++;
- changed = true;
- }
- }
+ merged_sectors = badblocks_merge(bb, s, sectors, acknowledged,
+ &lo, &hi, &changed);
+ s += merged_sectors;
+ sectors -= merged_sectors;
if (sectors == 0 && hi < bb->count) {
/* we might be able to combine lo and hi */
/* Note: 's' is at the end of 'lo' */