From: NeilBrown <neilb@suse.com>
To: Lars Ellenberg <lars.ellenberg@linbit.com>
Cc: Jens Axboe <axboe@kernel.dk>,
linux-raid@vger.kernel.org,
"Martin K. Petersen" <martin.petersen@oracle.com>,
Mike Snitzer <snitzer@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
Jiri Kosina <jkosina@suse.cz>, Ming Lei <ming.lei@canonical.com>,
linux-kernel@vger.kernel.org, Zheng Liu <gnehzuil.liu@gmail.com>,
linux-block@vger.kernel.org, Takashi Iwai <tiwai@suse.de>,
linux-bcache@vger.kernel.org, Ingo Molnar <mingo@redhat.com>,
Alasdair Kergon <agk@redhat.com>,
Keith Busch <keith.busch@intel.com>,
dm-devel@redhat.com, Shaohua Li <shli@kernel.org>,
Kent Overstreet <kent.overstreet@gmail.com>,
"Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>,
Roland Kammerer <roland.kammerer@linbit.com>
Subject: Re: [dm-devel] [RFC] block: fix blk_queue_split() resource exhaustion
Date: Fri, 08 Jul 2016 19:39:36 +1000 [thread overview]
Message-ID: <877fcwfoyv.fsf@notabene.neil.brown.name> (raw)
In-Reply-To: <20160708080219.GT13335@soda.linbit>
[-- Attachment #1: Type: text/plain, Size: 6287 bytes --]
On Fri, Jul 08 2016, Lars Ellenberg wrote:
> On Fri, Jul 08, 2016 at 08:07:52AM +1000, NeilBrown wrote:
>> Before I introduced the recursion limiting, requests were handled as an
>> in-order tree walk. The code I wrote tried to preserve that but didn't
>> for several reasons. I think we need to restore the original in-order
>> walk because it makes the most sense.
>> So after processing a particular bio, we should then process all the
>> 'child' bios - bios send to underlying devices. Then the 'sibling'
>> bios, that were split off, and then any remaining parents and ancestors.
>>
>> You patch created the right structures for doing this, my proposal took
>> it a step closer, but now after more careful analysis I don't think it
>> is quite right.
>> With my previous proposal (and you latest patch - thanks!) requests for
>> "this" level are stacked, but they should be queued.
>> If a make_request_fn only ever submits one request for this level and
>> zero or more lower levels, then the difference between a queue and a
>> stack is irrelevant. If it submited more that one, a stack would cause
>> them to be handled in the reverse order.
>
> We have a device stack.
> q_this_level->make_request_fn() cannot possibly submit anything
> on "this_level", or it would create a device loop, I think.
>
> So we start with the initial, "top most" call to generic_make_request().
> That is one single bio. All queues are empty.
>
> This bio is then passed on to its destination queue make_request_fn().
>
> Which may chose to split it (via blk_queue_split, or like dm does, or
> else). If it, like blk_queue_split() does, splits it into
> "piece-I-can-handle-now" and "remainder", both still targeted at the
> top most (current) queue, I think the "remainder" should just be pushed
> back, which will make it look as if upper layers did
> generic_make_request("piece-I-can-handle-now");
> generic_make_request("remainder");
> Which I do, by using bio_list_add_head(remainder, bio); (*head*).
This is exactly what I mean by "submitting a request at 'this' level".
Maybe that is a poor way to express it. Within a make_request_fn, you
cannot "submit" a request, you can only "queue" a request.
generic_make_request hides that by doing something different for a call
From a make_request_fn that for a call from anywhere else.
The important thing is to have 2 queues to queue to.
>
> I don't see any other way for a make_request_fn(bio(l=x)) to generate
> "sibling" bios to the same level (l=x) as its own argument.
Yes, it only comes from splitting.
>
> This same q(l=x)->make_request_fn(bio(l=x)) may now call
> generic_make_request() for zero or more "child" bios (l=x+1),
> which are queued in order: bio_list_add(recursion, bio); (*tail*).
> Then, once l=x returns, the queue generated by it is spliced
> in front of the "remainder" (*head*).
> All bios are processed in the order they have been queued,
> by peeling off of the head.
>
> After all "child" bios of level l>=x+1 have been processed,
> the next bio to be processed will be the "pushed back" remainder.
>
> All "Natural order".
>
>> To make the patch "perfect", and maybe even more elegant we could treat
>> ->remainder and ->recursion more alike.
>> i.e.:
>> - generic make request has a private "stack" of requests.
>> - before calling ->make_request_fn(), both ->remainder and ->recursion
>> are initialised
>> - after ->make_request_fn(), ->remainder are spliced in to top of
>> 'stack', then ->recursion is spliced onto that.
>> - If stack is not empty, the top request is popped and we loop to top.
>>
>> This reliably follows in-order execution, and handles siblings correctly
>> (in submitted order) if/when a request splits off multiple siblings.
>
> The only splitting that creates siblings on the current level
> is blk_queue_split(), which splits the current bio into
> "front piece" and "remainder", already processed in this order.
Yes.
I imagine that a driver *could* split a bio into three parts with a
single allocation, but I cannot actually see any point in doing it. So
I was over-complicating things.
>
> Anything else creating "siblings" is not creating siblings for the
> current layer, but for the next deeper layer, which are queue on
> "recursion" and also processed in the order they have been generated.
>
>> I think that as long a requests are submitted in the order they are
>> created at each level there is no reason to expect performance
>> regressions.
>> All we are doing is changing the ordering between requests generated at
>> different levels, and I think we are restoring a more natural order.
>
> I believe both patches combined are doing exactly this already.
> I could rename .remainder to .todo or .incoming, though.
:-) neither "remainder" or "recursion" seem like brilliant names to me,
but I don't have anything better to suggest. Naming is hard!
As long as a comment explains the name clearly I could cope with X and Y.
>
> .incoming = [ bio(l=0) ]
> .recursion = []
>
> split
>
> .incoming = [ bio(l=0,now_1), bio(l=0,remainder_1) ]
> .recursion = []
>
> process head of .incoming
>
> .incoming = [ bio(l=0,remainder_1) ]
> .recursion = [ bio(l=1,a), bio(l=1,b), bio(l=1,c), ... ]
>
> merge_head
>
> .incoming = [ bio(l=1,a), bio(l=1,b), bio(l=1,c), ...,
> bio(l=0,remainder_1) ]
> .recursion = []
>
> process head of .incoming, potentially split first
>
> .incoming = [ bio(l=1,a,now), bio(l=1,a,remainder), bio(l=1,b), bio(l=1,c), ...,
> bio(l=0,remainder_1) ]
> ...
> .incoming = [ bio(l=1,a,remainder), bio(l=1,b), bio(l=1,c), ...,
> bio(l=0,remainder_1) ]
> .recursion = [ bio(l=2,aa), bio(l=2,ab), ... ]
>
> merge_head
>
> .incoming = [ bio(l=2,aa), bio(l=2,ab), ...,
> bio(l=1,a,remainder), bio(l=1,b), bio(l=1,c), ...,
> bio(l=0,remainder_1) ]
> .recursion = []
>
> ...
>
> process away ... until back at l=0
>
> .incoming = [ bio(l=0,remainder_1) ]
> .recursion = []
>
> potentially split further
> .incoming = [ bio(l=0,now_2), bio(l=0,remainder_2) ]
> .recursion = []
>
> rinse, repeat.
I think we just might be in violent agreement.
Thanks,
NeilBrown
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 818 bytes --]
next prev parent reply other threads:[~2016-07-08 9:39 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-06-22 8:22 [RFC] block: fix blk_queue_split() resource exhaustion Lars Ellenberg
2016-06-24 11:36 ` Ming Lei
2016-06-24 14:27 ` Lars Ellenberg
2016-06-24 15:15 ` Mike Snitzer
2016-06-28 8:24 ` Lars Ellenberg
2016-06-25 9:30 ` [RFC] " Ming Lei
2016-06-28 8:45 ` Lars Ellenberg
2016-07-02 10:03 ` Ming Lei
2016-07-02 10:28 ` Ming Lei
2016-07-04 8:20 ` Lars Ellenberg
2016-07-04 10:47 ` Ming Lei
2016-07-06 12:38 ` Lars Ellenberg
2016-07-06 15:57 ` Ming Lei
2016-07-07 8:03 ` Lars Ellenberg
2016-07-07 13:14 ` Ming Lei
2016-07-07 5:35 ` [dm-devel] " NeilBrown
2016-07-07 8:16 ` Lars Ellenberg
2016-07-07 12:39 ` Lars Ellenberg
2016-07-07 12:47 ` Mike Snitzer
2016-07-07 22:07 ` [dm-devel] [RFC] " NeilBrown
2016-07-08 8:02 ` Lars Ellenberg
2016-07-08 9:39 ` NeilBrown [this message]
2016-07-08 13:00 ` Lars Ellenberg
2016-07-08 13:59 ` Lars Ellenberg
2016-07-08 11:08 ` Ming Lei
2016-07-08 12:52 ` Lars Ellenberg
2016-07-08 13:05 ` Mike Snitzer
2016-07-07 12:45 ` Mike Snitzer
2016-07-07 22:40 ` NeilBrown
2016-07-07 14:36 ` Mike Snitzer
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=877fcwfoyv.fsf@notabene.neil.brown.name \
--to=neilb@suse.com \
--cc=agk@redhat.com \
--cc=axboe@kernel.dk \
--cc=dm-devel@redhat.com \
--cc=gnehzuil.liu@gmail.com \
--cc=jkosina@suse.cz \
--cc=keith.busch@intel.com \
--cc=kent.overstreet@gmail.com \
--cc=kirill.shutemov@linux.intel.com \
--cc=lars.ellenberg@linbit.com \
--cc=linux-bcache@vger.kernel.org \
--cc=linux-block@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-raid@vger.kernel.org \
--cc=martin.petersen@oracle.com \
--cc=ming.lei@canonical.com \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=roland.kammerer@linbit.com \
--cc=shli@kernel.org \
--cc=snitzer@redhat.com \
--cc=tiwai@suse.de \
/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).