linux-block.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Coly Li <colyli@suse.de>
To: Xiao Ni <xni@redhat.com>
Cc: nvdimm@lists.linux.dev, linux-raid <linux-raid@vger.kernel.org>,
	linux-block@vger.kernel.org,
	Dan Williams <dan.j.williams@intel.com>,
	Geliang Tang <geliang.tang@suse.com>,
	Hannes Reinecke <hare@suse.de>, Jens Axboe <axboe@kernel.dk>,
	NeilBrown <neilb@suse.de>,
	Vishal L Verma <vishal.l.verma@intel.com>
Subject: Re: [PATCH v5 2/6] badblocks: add helper routines for badblock ranges handling
Date: Mon, 6 Jun 2022 15:47:00 +0800	[thread overview]
Message-ID: <B19DCF56-0FC1-4B87-A399-3A5FC8C4A416@suse.de> (raw)
In-Reply-To: <CALTww28HF2SPrrQAaLd+H_De5F8aOemBHfU03zMVAYatb7k19Q@mail.gmail.com>



> 2022年1月2日 15:04,Xiao Ni <xni@redhat.com> 写道:
> 
> On Sat, Dec 11, 2021 at 12:05 AM Coly Li <colyli@suse.de> wrote:
>> 
>> 

[snipped]

>> block/badblocks.c | 376 ++++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 376 insertions(+)
>> 
>> diff --git a/block/badblocks.c b/block/badblocks.c
>> index d39056630d9c..30958cc4469f 100644
>> --- a/block/badblocks.c
>> +++ b/block/badblocks.c
>> @@ -16,6 +16,382 @@
>> #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;
>> +
>> +       while (hi - lo > 1) {
>> +               int mid = (lo + hi)/2;
>> +               sector_t a = BB_OFFSET(p[mid]);
>> +
>> +               if (a <= s)
>> +                       lo = mid;
>> +               else
>> +                       hi = mid;
>> +       }
> 
> Hi Coly

Hi Xiao,

> 
> Does it need to stop the loop when "a == s". For example, there are
> 100 bad blocks.
> The new bad starts equals offset(50). In the first loop, it can find
> the result. It doesn't
> need to go on running in the loop. If it still runs the loop, only the
> hi can be handled.
> It has no meaning.

Yes, you are right. It can be improved with your suggestion, to avoid unnecessary extra loop.


> 
>> +
>> +       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])) &&
> 
> In fact, it can never trigger s == BB_OFFSET here. By the design, if s
> == offset(pos), prev_badblocks
> can find it. Then front_merge/front_overwrite can handle it.

Yes, s == BB_OFFSET(p[behind]) should not happen in current situation. It is overthought, can be removed.

> 
>> +           ((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])) {
>> +               WARN_ON((BB_LEN(p[behind]) + merged) >= BB_MAX_LEN);
>> +
>> +               merged = min_t(sector_t, sectors, BB_OFFSET(p[behind]) - s);
> 
> sectors must be >= BB_OFFSET(p[behind] - s) here? It's behind_merge, so the end
> of bad should be >= BB_OFFSET(p[behind])


Yes, it’s overthought there, it can be simplified as,
	merged = BB_OFFSET(p[behind]) - s;


> 
> If we need to check merged value. The WARN_ON should be checked after merged

Maybe you are right, but it is comfortable for me to check the length before doing the merge calculation. Anyway, almost all the WARN_ON() locations are overthought, most of them can be removed if not triggered during real workload for a while.

> 
>> +               p[behind] =  BB_MAKE(s, BB_LEN(p[behind]) + merged, bad->ack);
>> +       } else {
>> +               merged = min_t(sector_t, sectors, BB_LEN(p[behind]));
>> +       }
> 
> If we don't need to consider s == offset(pos) situation, it only needs
> to consider s < offset(pos) here

Yes, the overthought part can be removed. I will re-write this part.


>> +
>> +       WARN_ON(merged == 0);
>> +
>> +       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;
>> +}
> 
> could you explain why BB_OFFSET(p[prev]) should == bad->start. If
> bad(prev-1) and

This is a special case, which the state-machine style loop in _badblocks_set() cannot handle.
Here is an example why can_combine_front() is necessary, the bad range is represent as (start offset, end offset), second number is not range len,
- existed bad ranges: [0, 16], [20, 500], [700, 800]
- inserting bad range: [10, 511]
- bad blocks table is full, no free slot can be allocated.

After the first loop in _badblocks_set(), the bad ranges and inserting bad range are,
- existed bad ranges: [0, 19], [20, 500], [700, 800]
- inserting range: [20, 511]

With can_combine_front() check, the first 2 existed bad ranges can be merged into 1, then the bad ranges can be,
- existed bad ranges: [0, 500], [700]   (a free slot is available after front_combine())
- inserting bad range: [20, 511]

Then next loop in _badblocks_set(), there is 1 free slot in bad blocks table, so the result is,
- existed bad ranges: [0, 511], [700, 800]
All inserting bad block range is handled and recored in bad blocks table.


If we don’t do the front_combine() with checking can_combine_front(), after the first loop in _badblocks_set(),
- existed bad ranges: [0, 19], [20, 511], [700, 800]
- inserting range: [20, 511]

Then after the last loop in _badblocks_set(), the result is,
- existed bad ranges: [0, 19], [20, 511], [700, 800]
The first 2 bad ranges have no chance to be combined into larger one.

> bad(prev) are adjacent, can they be combine directly without
> considering bad->start

So without combine_front(), in such situation these two bad ranges won’t be merged with current state-machine style code in _badblocks_set().

Thanks for your comments, I will update the patch to drop the overthought part.

Coly Li





  reply	other threads:[~2022-06-06  7:47 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-10 16:04 [PATCH v5 0/7] badblocks improvement for multiple bad block ranges Coly Li
2021-12-10 16:04 ` [PATCH v5 1/6] badblocks: add more helper structure and routines in badblocks.h Coly Li
2021-12-10 16:04 ` [PATCH v5 2/6] badblocks: add helper routines for badblock ranges handling Coly Li
2022-01-02  7:04   ` Xiao Ni
2022-06-06  7:47     ` Coly Li [this message]
2021-12-10 16:04 ` [PATCH v5 3/6] badblocks: improve badblocks_set() for multiple " Coly Li
2022-01-02 13:48   ` Xiao Ni
2022-06-06  7:47     ` Coly Li
2021-12-10 16:04 ` [PATCH v5 4/6] badblocks: improve badblocks_clear() " Coly Li
2021-12-10 16:04 ` [PATCH v5 5/6] badblocks: improve badblocks_check() " Coly Li
2021-12-10 16:04 ` [PATCH v5 6/6] badblocks: switch to the improved badblock handling code Coly Li
2021-12-10 16:04 ` [PATCH v5] test: user space code to test badblocks APIs Coly Li

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=B19DCF56-0FC1-4B87-A399-3A5FC8C4A416@suse.de \
    --to=colyli@suse.de \
    --cc=axboe@kernel.dk \
    --cc=dan.j.williams@intel.com \
    --cc=geliang.tang@suse.com \
    --cc=hare@suse.de \
    --cc=linux-block@vger.kernel.org \
    --cc=linux-raid@vger.kernel.org \
    --cc=neilb@suse.de \
    --cc=nvdimm@lists.linux.dev \
    --cc=vishal.l.verma@intel.com \
    --cc=xni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).