#include <linux/types.h>
 #include <linux/slab.h>
 
+/*
+ * Find the range starts at-or-before 's' from bad table. The search
+ * starts from index 'hint' and stops at index 'hint_end' from the bad
+ * table.
+ */
+static int prev_by_hint(struct badblocks *bb, sector_t s, int hint)
+{
+       int hint_end = hint + 2;
+       u64 *p = bb->page;
+       int ret = -1;
+
+       while ((hint < hint_end) && ((hint + 1) <= bb->count) &&
+              (BB_OFFSET(p[hint]) <= s)) {
+               if ((hint + 1) == bb->count || BB_OFFSET(p[hint + 1]) > s) {
+                       ret = hint;
+                       break;
+               }
+               hint++;
+       }
+
+       return ret;
+}
+
+/*
+ * Find the range starts at-or-before bad->start. If 'hint' is provided
+ * (hint >= 0) then search in the bad table from hint firstly. It is
+ * very probably the wanted bad range can be found from the hint index,
+ * then the unnecessary while-loop iteration can be avoided.
+ */
+static int prev_badblocks(struct badblocks *bb, struct badblocks_context *bad,
+                         int hint)
+{
+       sector_t s = bad->start;
+       int ret = -1;
+       int lo, hi;
+       u64 *p;
+
+       if (!bb->count)
+               goto out;
+
+       if (hint >= 0) {
+               ret = prev_by_hint(bb, s, hint);
+               if (ret >= 0)
+                       goto out;
+       }
+
+       lo = 0;
+       hi = bb->count;
+       p = bb->page;
+
+       /* The following bisect search might be unnecessary */
+       if (BB_OFFSET(p[lo]) > s)
+               return -1;
+       if (BB_OFFSET(p[hi - 1]) <= s)
+               return hi - 1;
+
+       /* Do bisect search in bad table */
+       while (hi - lo > 1) {
+               int mid = (lo + hi)/2;
+               sector_t a = BB_OFFSET(p[mid]);
+
+               if (a == s) {
+                       ret = mid;
+                       goto out;
+               }
+
+               if (a < s)
+                       lo = mid;
+               else
+                       hi = mid;
+       }
+
+       if (BB_OFFSET(p[lo]) <= s)
+               ret = lo;
+out:
+       return ret;
+}
+
+/*
+ * Return 'true' if the range indicated by 'bad' can be backward merged
+ * with the bad range (from the bad table) index by 'behind'.
+ */
+static bool can_merge_behind(struct badblocks *bb,
+                            struct badblocks_context *bad, int behind)
+{
+       sector_t sectors = bad->len;
+       sector_t s = bad->start;
+       u64 *p = bb->page;
+
+       if ((s < BB_OFFSET(p[behind])) &&
+           ((s + sectors) >= BB_OFFSET(p[behind])) &&
+           ((BB_END(p[behind]) - s) <= BB_MAX_LEN) &&
+           BB_ACK(p[behind]) == bad->ack)
+               return true;
+       return false;
+}
+
+/*
+ * Do backward merge for range indicated by 'bad' and the bad range
+ * (from the bad table) indexed by 'behind'. The return value is merged
+ * sectors from bad->len.
+ */
+static int behind_merge(struct badblocks *bb, struct badblocks_context *bad,
+                       int behind)
+{
+       sector_t sectors = bad->len;
+       sector_t s = bad->start;
+       u64 *p = bb->page;
+       int merged = 0;
+
+       WARN_ON(s >= BB_OFFSET(p[behind]));
+       WARN_ON((s + sectors) < BB_OFFSET(p[behind]));
+
+       if (s < BB_OFFSET(p[behind])) {
+               merged = BB_OFFSET(p[behind]) - s;
+               p[behind] =  BB_MAKE(s, BB_LEN(p[behind]) + merged, bad->ack);
+
+               WARN_ON((BB_LEN(p[behind]) + merged) >= BB_MAX_LEN);
+       }
+
+       return merged;
+}
+
+/*
+ * Return 'true' if the range indicated by 'bad' can be forward
+ * merged with the bad range (from the bad table) indexed by 'prev'.
+ */
+static bool can_merge_front(struct badblocks *bb, int prev,
+                           struct badblocks_context *bad)
+{
+       sector_t s = bad->start;
+       u64 *p = bb->page;
+
+       if (BB_ACK(p[prev]) == bad->ack &&
+           (s < BB_END(p[prev]) ||
+            (s == BB_END(p[prev]) && (BB_LEN(p[prev]) < BB_MAX_LEN))))
+               return true;
+       return false;
+}
+
+/*
+ * Do forward merge for range indicated by 'bad' and the bad range
+ * (from bad table) indexed by 'prev'. The return value is sectors
+ * merged from bad->len.
+ */
+static int front_merge(struct badblocks *bb, int prev, struct badblocks_context *bad)
+{
+       sector_t sectors = bad->len;
+       sector_t s = bad->start;
+       u64 *p = bb->page;
+       int merged = 0;
+
+       WARN_ON(s > BB_END(p[prev]));
+
+       if (s < BB_END(p[prev])) {
+               merged = min_t(sector_t, sectors, BB_END(p[prev]) - s);
+       } else {
+               merged = min_t(sector_t, sectors, BB_MAX_LEN - BB_LEN(p[prev]));
+               if ((prev + 1) < bb->count &&
+                   merged > (BB_OFFSET(p[prev + 1]) - BB_END(p[prev]))) {
+                       merged = BB_OFFSET(p[prev + 1]) - BB_END(p[prev]);
+               }
+
+               p[prev] = BB_MAKE(BB_OFFSET(p[prev]),
+                                 BB_LEN(p[prev]) + merged, bad->ack);
+       }
+
+       return merged;
+}
+
+/*
+ * 'Combine' is a special case which can_merge_front() is not able to
+ * handle: If a bad range (indexed by 'prev' from bad table) exactly
+ * starts as bad->start, and the bad range ahead of 'prev' (indexed by
+ * 'prev - 1' from bad table) exactly ends at where 'prev' starts, and
+ * the sum of their lengths does not exceed BB_MAX_LEN limitation, then
+ * these two bad range (from bad table) can be combined.
+ *
+ * Return 'true' if bad ranges indexed by 'prev' and 'prev - 1' from bad
+ * table can be combined.
+ */
+static bool can_combine_front(struct badblocks *bb, int prev,
+                             struct badblocks_context *bad)
+{
+       u64 *p = bb->page;
+
+       if ((prev > 0) &&
+           (BB_OFFSET(p[prev]) == bad->start) &&
+           (BB_END(p[prev - 1]) == BB_OFFSET(p[prev])) &&
+           (BB_LEN(p[prev - 1]) + BB_LEN(p[prev]) <= BB_MAX_LEN) &&
+           (BB_ACK(p[prev - 1]) == BB_ACK(p[prev])))
+               return true;
+       return false;
+}
+
+/*
+ * Combine the bad ranges indexed by 'prev' and 'prev - 1' (from bad
+ * table) into one larger bad range, and the new range is indexed by
+ * 'prev - 1'.
+ * The caller of front_combine() will decrease bb->count, therefore
+ * it is unnecessary to clear p[perv] after front merge.
+ */
+static void front_combine(struct badblocks *bb, int prev)
+{
+       u64 *p = bb->page;
+
+       p[prev - 1] = BB_MAKE(BB_OFFSET(p[prev - 1]),
+                             BB_LEN(p[prev - 1]) + BB_LEN(p[prev]),
+                             BB_ACK(p[prev]));
+       if ((prev + 1) < bb->count)
+               memmove(p + prev, p + prev + 1, (bb->count - prev - 1) * 8);
+}
+
+/*
+ * Return 'true' if the range indicated by 'bad' is exactly forward
+ * overlapped with the bad range (from bad table) indexed by 'front'.
+ * Exactly forward overlap means the bad range (from bad table) indexed
+ * by 'prev' does not cover the whole range indicated by 'bad'.
+ */
+static bool overlap_front(struct badblocks *bb, int front,
+                         struct badblocks_context *bad)
+{
+       u64 *p = bb->page;
+
+       if (bad->start >= BB_OFFSET(p[front]) &&
+           bad->start < BB_END(p[front]))
+               return true;
+       return false;
+}
+
+/*
+ * Return 'true' if the range indicated by 'bad' is exactly backward
+ * overlapped with the bad range (from bad table) indexed by 'behind'.
+ */
+static bool overlap_behind(struct badblocks *bb, struct badblocks_context *bad,
+                          int behind)
+{
+       u64 *p = bb->page;
+
+       if (bad->start < BB_OFFSET(p[behind]) &&
+           (bad->start + bad->len) > BB_OFFSET(p[behind]))
+               return true;
+       return false;
+}
+
+/*
+ * Return 'true' if the range indicated by 'bad' can overwrite the bad
+ * range (from bad table) indexed by 'prev'.
+ *
+ * The range indicated by 'bad' can overwrite the bad range indexed by
+ * 'prev' when,
+ * 1) The whole range indicated by 'bad' can cover partial or whole bad
+ *    range (from bad table) indexed by 'prev'.
+ * 2) The ack value of 'bad' is larger or equal to the ack value of bad
+ *    range 'prev'.
+ *
+ * If the overwriting doesn't cover the whole bad range (from bad table)
+ * indexed by 'prev', new range might be split from existing bad range,
+ * 1) The overwrite covers head or tail part of existing bad range, 1
+ *    extra bad range will be split and added into the bad table.
+ * 2) The overwrite covers middle of existing bad range, 2 extra bad
+ *    ranges will be split (ahead and after the overwritten range) and
+ *    added into the bad table.
+ * The number of extra split ranges of the overwriting is stored in
+ * 'extra' and returned for the caller.
+ */
+static bool can_front_overwrite(struct badblocks *bb, int prev,
+                               struct badblocks_context *bad, int *extra)
+{
+       u64 *p = bb->page;
+       int len;
+
+       WARN_ON(!overlap_front(bb, prev, bad));
+
+       if (BB_ACK(p[prev]) >= bad->ack)
+               return false;
+
+       if (BB_END(p[prev]) <= (bad->start + bad->len)) {
+               len = BB_END(p[prev]) - bad->start;
+               if (BB_OFFSET(p[prev]) == bad->start)
+                       *extra = 0;
+               else
+                       *extra = 1;
+
+               bad->len = len;
+       } else {
+               if (BB_OFFSET(p[prev]) == bad->start)
+                       *extra = 1;
+               else
+               /*
+                * prev range will be split into two, beside the overwritten
+                * one, an extra slot needed from bad table.
+                */
+                       *extra = 2;
+       }
+
+       if ((bb->count + (*extra)) >= MAX_BADBLOCKS)
+               return false;
+
+       return true;
+}
+
+/*
+ * Do the overwrite from the range indicated by 'bad' to the bad range
+ * (from bad table) indexed by 'prev'.
+ * The previously called can_front_overwrite() will provide how many
+ * extra bad range(s) might be split and added into the bad table. All
+ * the splitting cases in the bad table will be handled here.
+ */
+static int front_overwrite(struct badblocks *bb, int prev,
+                          struct badblocks_context *bad, int extra)
+{
+       u64 *p = bb->page;
+       sector_t orig_end = BB_END(p[prev]);
+       int orig_ack = BB_ACK(p[prev]);
+
+       switch (extra) {
+       case 0:
+               p[prev] = BB_MAKE(BB_OFFSET(p[prev]), BB_LEN(p[prev]),
+                                 bad->ack);
+               break;
+       case 1:
+               if (BB_OFFSET(p[prev]) == bad->start) {
+                       p[prev] = BB_MAKE(BB_OFFSET(p[prev]),
+                                         bad->len, bad->ack);
+                       memmove(p + prev + 2, p + prev + 1,
+                               (bb->count - prev - 1) * 8);
+                       p[prev + 1] = BB_MAKE(bad->start + bad->len,
+                                             orig_end - BB_END(p[prev]),
+                                             orig_ack);
+               } else {
+                       p[prev] = BB_MAKE(BB_OFFSET(p[prev]),
+                                         bad->start - BB_OFFSET(p[prev]),
+                                         orig_ack);
+                       /*
+                        * prev +2 -> prev + 1 + 1, which is for,
+                        * 1) prev + 1: the slot index of the previous one
+                        * 2) + 1: one more slot for extra being 1.
+                        */
+                       memmove(p + prev + 2, p + prev + 1,
+                               (bb->count - prev - 1) * 8);
+                       p[prev + 1] = BB_MAKE(bad->start, bad->len, bad->ack);
+               }
+               break;
+       case 2:
+               p[prev] = BB_MAKE(BB_OFFSET(p[prev]),
+                                 bad->start - BB_OFFSET(p[prev]),
+                                 orig_ack);
+               /*
+                * prev + 3 -> prev + 1 + 2, which is for,
+                * 1) prev + 1: the slot index of the previous one
+                * 2) + 2: two more slots for extra being 2.
+                */
+               memmove(p + prev + 3, p + prev + 1,
+                       (bb->count - prev - 1) * 8);
+               p[prev + 1] = BB_MAKE(bad->start, bad->len, bad->ack);
+               p[prev + 2] = BB_MAKE(BB_END(p[prev + 1]),
+                                     orig_end - BB_END(p[prev + 1]),
+                                     orig_ack);
+               break;
+       default:
+               break;
+       }
+
+       return bad->len;
+}
+
+/*
+ * Explicitly insert a range indicated by 'bad' to the bad table, where
+ * the location is indexed by 'at'.
+ */
+static int insert_at(struct badblocks *bb, int at, struct badblocks_context *bad)
+{
+       u64 *p = bb->page;
+       int len;
+
+       WARN_ON(badblocks_full(bb));
+
+       len = min_t(sector_t, bad->len, BB_MAX_LEN);
+       if (at < bb->count)
+               memmove(p + at + 1, p + at, (bb->count - at) * 8);
+       p[at] = BB_MAKE(bad->start, len, bad->ack);
+
+       return len;
+}
+
 /**
  * badblocks_check() - check a given range for bad sectors
  * @bb:                the badblocks structure that holds all badblock information