Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1574647 > unrolled thread
| Started by | Pavel Machek <pavel@ucw.cz> |
|---|---|
| First post | 2017-02-06 14:00 +0100 |
| Last post | 2017-02-08 03:50 +0100 |
| Articles | 13 — 6 participants |
Back to article view | Back to linux.kernel
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone Pavel Machek <pavel@ucw.cz> - 2017-02-06 14:00 +0100
Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone Kent Overstreet <kent.overstreet@gmail.com> - 2017-02-07 02:50 +0100
Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone Kent Overstreet <kent.overstreet@gmail.com> - 2017-02-07 03:50 +0100
Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone Mike Snitzer <snitzer@redhat.com> - 2017-02-07 18:20 +0100
Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone Pavel Machek <pavel@ucw.cz> - 2017-02-07 21:40 +0100
Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone Mike Galbraith <efault@gmx.de> - 2017-02-08 04:20 +0100
Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone Kent Overstreet <kent.overstreet@gmail.com> - 2017-02-08 06:00 +0100
Re: [PATCH] block: Make rescuer threads per request_queue, not per bioset kbuild test robot <lkp@intel.com> - 2017-02-08 07:30 +0100
Re: [PATCH] block: Make rescuer threads per request_queue, not per bioset kbuild test robot <lkp@intel.com> - 2017-02-08 07:30 +0100
Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone Mike Galbraith <efault@gmx.de> - 2017-02-08 08:00 +0100
Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone Mike Snitzer <snitzer@redhat.com> - 2017-02-08 17:50 +0100
Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone Kent Overstreet <kent.overstreet@gmail.com> - 2017-02-09 22:40 +0100
Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone Ming Lei <ming.lei@canonical.com> - 2017-02-08 03:50 +0100
| From | Pavel Machek <pavel@ucw.cz> |
|---|---|
| Date | 2017-02-06 14:00 +0100 |
| Subject | v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone |
| Message-ID | <t7R3I-53r-11@gated-at.bofh.it> |
[Multipart message — attachments visible in raw view] — view raw
On Sat 2016-02-20 21:04:32, Pavel Machek wrote:
> Hi!
>
> > > > > > I know it is normal to spawn 8 threads for every single function,
> > > > > ...
> > > > > > but 28 threads?
> > > > > >
> > > > > > root 974 0.0 0.0 0 0 ? S< Dec08 0:00 [bioset]
> > > > > ...
> > > > >
> > > > > How many physical block devices do you have?
> > > > >
> > > > > DM is doing its part to not contribute to this:
> > > > > dbba42d8a ("dm: eliminate unused "bioset" process for each bio-based DM device")
> > > > >
> > > > > (but yeah, all these extra 'bioset' threads aren't ideal)
> > > >
> > > > Still there in 4.4-final.
> > >
> > > ...and still there in 4.5-rc4 :-(.
> >
> > You're directing this concern to the wrong person.
> >
> > I already told you DM is _not_ contributing any extra "bioset" threads
> > (ever since commit dbba42d8a).
>
> Well, sorry about that. Note that l-k is on the cc list, so hopefully
> the right person sees it too.
>
> Ok, let me check... it seems that
> 54efd50bfd873e2dbf784e0b21a8027ba4299a3e is responsible, thus Kent
> Overstreet <kent.overstreet@gmail.com> is to blame.
>
> Um, and you acked the patch, so you are partly responsible.
Still there on v4.9, 36 threads on nokia n900 cellphone.
So.. what needs to be done there?
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[toc] | [next] | [standalone]
| From | Kent Overstreet <kent.overstreet@gmail.com> |
|---|---|
| Date | 2017-02-07 02:50 +0100 |
| Subject | Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone |
| Message-ID | <t834R-4uf-5@gated-at.bofh.it> |
| In reply to | #1574647 |
On Mon, Feb 06, 2017 at 01:53:09PM +0100, Pavel Machek wrote:
> On Sat 2016-02-20 21:04:32, Pavel Machek wrote:
> > Hi!
> >
> > > > > > > I know it is normal to spawn 8 threads for every single function,
> > > > > > ...
> > > > > > > but 28 threads?
> > > > > > >
> > > > > > > root 974 0.0 0.0 0 0 ? S< Dec08 0:00 [bioset]
> > > > > > ...
> > > > > >
> > > > > > How many physical block devices do you have?
> > > > > >
> > > > > > DM is doing its part to not contribute to this:
> > > > > > dbba42d8a ("dm: eliminate unused "bioset" process for each bio-based DM device")
> > > > > >
> > > > > > (but yeah, all these extra 'bioset' threads aren't ideal)
> > > > >
> > > > > Still there in 4.4-final.
> > > >
> > > > ...and still there in 4.5-rc4 :-(.
> > >
> > > You're directing this concern to the wrong person.
> > >
> > > I already told you DM is _not_ contributing any extra "bioset" threads
> > > (ever since commit dbba42d8a).
> >
> > Well, sorry about that. Note that l-k is on the cc list, so hopefully
> > the right person sees it too.
> >
> > Ok, let me check... it seems that
> > 54efd50bfd873e2dbf784e0b21a8027ba4299a3e is responsible, thus Kent
> > Overstreet <kent.overstreet@gmail.com> is to blame.
> >
> > Um, and you acked the patch, so you are partly responsible.
>
> Still there on v4.9, 36 threads on nokia n900 cellphone.
>
> So.. what needs to be done there?
So background:
We need rescuer threads because:
Say you allocate a bio from a bioset, submit that bio, and then allocate again
from that same bioset: if you're running underneath generic_make_request(),
you'll deadlock. Real submission of the first bio you allocated is blocked until
you return from your make_request_fn(), but you're blocking that trying to
allocate - this is handled (in a hacky way!) with the punt_bios_to_rescuer code
when we go to allocate from a bioset but have to block.
We need more than a single global rescuer, because:
The rescuer thread is just resubmitting bios, so if in the course of submitting
bios, _their_ drivers allocate new bios from biosets and block - oops, we're
recursing.
However:
The rescuer threads don't inherently need to be per bioset - they really ought
to be per block device.
Additionally, triggering the "punt bios to rescuer" code only when we go to
allocate from a bioset and block is wrong: it's possible to create these sorts
of deadlocks by blocking on other things. The right thing to do would be to
trigger this "punt bios to rescuer" thing whenever we schedule, and there's
still bios on current->bio_list.
This is actually how Jens's new(er) plugging code works (which post dates my
punt bios to rescuer hack). What needs to happen is Jens's scheduler hook for
block plugging needs to be be unified with both the current->bio_list thing
(which is really a block plug, just open coded, as it predates _all_ of this)
and the rescuer thread stuff.
The tricky part is going to be making the rescuer threads per block device
_correctly_ (without introducing new deadlocks)... reasoning out these deadlocks
always makes my head hurt, the biggest reason I made the rescuer threads per
bioset was that when I wrote the code I wasn't at all confident I could get
anything else right. Still uneasy about that :)
What needs rescuer threads?
- if we're allocating from a bioset, but we're not running under
generic_make_request() (e.g. we're in filesystem code) - don't need a rescuer
there, we're not blocking previously allocated bios from being submitted.
- if we're a block driver that doesn't allocate new bios, we don't need a
rescuer thread.
But note that any block driver that calls blk_queue_split() to handle
arbitrary size bios is allocating bios. If we converted e.g. the standard
request queue code to process bios/requests incrementally, instead of
requiring them to be split, this would go away (and that's something that
should be done anyways, it would improve performance by getting rid of segment
counting).
So, we should only need one rescuer thread per block device - and if we get rid
of splitting to handle arbitrary size bios, most block devices won't need
rescuers.
The catch is that the correct rescuer to punt a bio to corresponds to the device
that _issued_ the bio, not the device the bio was submitted to, and that
information is currently lost by the time we block - that's the other reason I
made rescuers per bioset, since bios do track the bioset they were allocated
from.
But, I just got an idea for how to handle this that might be halfway sane, maybe
I'll try and come up with a patch...
[toc] | [prev] | [next] | [standalone]
| From | Kent Overstreet <kent.overstreet@gmail.com> |
|---|---|
| Date | 2017-02-07 03:50 +0100 |
| Subject | Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone |
| Message-ID | <t840V-54B-11@gated-at.bofh.it> |
| In reply to | #1575318 |
On Mon, Feb 06, 2017 at 04:47:24PM -0900, Kent Overstreet wrote:
> On Mon, Feb 06, 2017 at 01:53:09PM +0100, Pavel Machek wrote:
> > Still there on v4.9, 36 threads on nokia n900 cellphone.
> >
> > So.. what needs to be done there?
> But, I just got an idea for how to handle this that might be halfway sane, maybe
> I'll try and come up with a patch...
Ok, here's such a patch, only lightly tested:
-- >8 --
Subject: [PATCH] block: Make rescuer threads per request_queue, not per bioset
Note: this patch is very lightly tested.
Also, trigger rescuing whenever with bios on current->bio_list, instead
of only when we block in bio_alloc_bioset(). This is more correct, and
should result in fewer rescuer threads.
XXX: The current->bio_list plugging needs to be unified with the
blk_plug mechanism.
TODO: If we change normal request_queue drivers to handle arbitrary size
bios by processing requests incrementally, instead of splitting bios,
then we can get rid of rescuer threads from those devices.
---
block/bio.c | 107 ++++---------------------------------------------
block/blk-core.c | 58 ++++++++++++++++++++++++---
block/blk-sysfs.c | 2 +
include/linux/bio.h | 16 ++++----
include/linux/blkdev.h | 10 +++++
include/linux/sched.h | 2 +-
kernel/sched/core.c | 4 ++
7 files changed, 83 insertions(+), 116 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index f3b5786202..9ad54a9b12 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -336,54 +336,6 @@ void bio_chain(struct bio *bio, struct bio *parent)
}
EXPORT_SYMBOL(bio_chain);
-static void bio_alloc_rescue(struct work_struct *work)
-{
- struct bio_set *bs = container_of(work, struct bio_set, rescue_work);
- struct bio *bio;
-
- while (1) {
- spin_lock(&bs->rescue_lock);
- bio = bio_list_pop(&bs->rescue_list);
- spin_unlock(&bs->rescue_lock);
-
- if (!bio)
- break;
-
- generic_make_request(bio);
- }
-}
-
-static void punt_bios_to_rescuer(struct bio_set *bs)
-{
- struct bio_list punt, nopunt;
- struct bio *bio;
-
- /*
- * In order to guarantee forward progress we must punt only bios that
- * were allocated from this bio_set; otherwise, if there was a bio on
- * there for a stacking driver higher up in the stack, processing it
- * could require allocating bios from this bio_set, and doing that from
- * our own rescuer would be bad.
- *
- * Since bio lists are singly linked, pop them all instead of trying to
- * remove from the middle of the list:
- */
-
- bio_list_init(&punt);
- bio_list_init(&nopunt);
-
- while ((bio = bio_list_pop(current->bio_list)))
- bio_list_add(bio->bi_pool == bs ? &punt : &nopunt, bio);
-
- *current->bio_list = nopunt;
-
- spin_lock(&bs->rescue_lock);
- bio_list_merge(&bs->rescue_list, &punt);
- spin_unlock(&bs->rescue_lock);
-
- queue_work(bs->rescue_workqueue, &bs->rescue_work);
-}
-
/**
* bio_alloc_bioset - allocate a bio for I/O
* @gfp_mask: the GFP_ mask given to the slab allocator
@@ -421,54 +373,27 @@ static void punt_bios_to_rescuer(struct bio_set *bs)
*/
struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
{
- gfp_t saved_gfp = gfp_mask;
unsigned front_pad;
unsigned inline_vecs;
struct bio_vec *bvl = NULL;
struct bio *bio;
void *p;
- if (!bs) {
- if (nr_iovecs > UIO_MAXIOV)
- return NULL;
+ WARN(current->bio_list &&
+ !current->bio_list->q->rescue_workqueue,
+ "allocating bio beneath generic_make_request() without rescuer");
+ if (nr_iovecs > UIO_MAXIOV)
+ return NULL;
+
+ if (!bs) {
p = kmalloc(sizeof(struct bio) +
nr_iovecs * sizeof(struct bio_vec),
gfp_mask);
front_pad = 0;
inline_vecs = nr_iovecs;
} else {
- /*
- * generic_make_request() converts recursion to iteration; this
- * means if we're running beneath it, any bios we allocate and
- * submit will not be submitted (and thus freed) until after we
- * return.
- *
- * This exposes us to a potential deadlock if we allocate
- * multiple bios from the same bio_set() while running
- * underneath generic_make_request(). If we were to allocate
- * multiple bios (say a stacking block driver that was splitting
- * bios), we would deadlock if we exhausted the mempool's
- * reserve.
- *
- * We solve this, and guarantee forward progress, with a rescuer
- * workqueue per bio_set. If we go to allocate and there are
- * bios on current->bio_list, we first try the allocation
- * without __GFP_DIRECT_RECLAIM; if that fails, we punt those
- * bios we would be blocking to the rescuer workqueue before
- * we retry with the original gfp_flags.
- */
-
- if (current->bio_list && !bio_list_empty(current->bio_list))
- gfp_mask &= ~__GFP_DIRECT_RECLAIM;
-
p = mempool_alloc(&bs->bio_pool, gfp_mask);
- if (!p && gfp_mask != saved_gfp) {
- punt_bios_to_rescuer(bs);
- gfp_mask = saved_gfp;
- p = mempool_alloc(&bs->bio_pool, gfp_mask);
- }
-
front_pad = bs->front_pad;
inline_vecs = BIO_INLINE_VECS;
}
@@ -483,12 +408,6 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
unsigned long idx = 0;
bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, &bs->bvec_pool);
- if (!bvl && gfp_mask != saved_gfp) {
- punt_bios_to_rescuer(bs);
- gfp_mask = saved_gfp;
- bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, &bs->bvec_pool);
- }
-
if (unlikely(!bvl))
goto err_free;
@@ -1938,10 +1857,6 @@ int biovec_init_pool(mempool_t *pool, int pool_entries)
void bioset_exit(struct bio_set *bs)
{
- if (bs->rescue_workqueue)
- destroy_workqueue(bs->rescue_workqueue);
- bs->rescue_workqueue = NULL;
-
mempool_exit(&bs->bio_pool);
mempool_exit(&bs->bvec_pool);
@@ -1968,10 +1883,6 @@ static int __bioset_init(struct bio_set *bs,
bs->front_pad = front_pad;
- spin_lock_init(&bs->rescue_lock);
- bio_list_init(&bs->rescue_list);
- INIT_WORK(&bs->rescue_work, bio_alloc_rescue);
-
bs->bio_slab = bio_find_or_create_slab(front_pad + back_pad);
if (!bs->bio_slab)
return -ENOMEM;
@@ -1983,10 +1894,6 @@ static int __bioset_init(struct bio_set *bs,
biovec_init_pool(&bs->bvec_pool, pool_size))
goto bad;
- bs->rescue_workqueue = alloc_workqueue("bioset", WQ_MEM_RECLAIM, 0);
- if (!bs->rescue_workqueue)
- goto bad;
-
return 0;
bad:
bioset_exit(bs);
diff --git a/block/blk-core.c b/block/blk-core.c
index 7e3cfa9c88..f716164cb3 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -48,6 +48,8 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(block_unplug);
DEFINE_IDA(blk_queue_ida);
+static void bio_rescue_work(struct work_struct *);
+
/*
* For the allocated request tables
*/
@@ -759,11 +761,21 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
PERCPU_REF_INIT_ATOMIC, GFP_KERNEL))
goto fail_bdi;
- if (blkcg_init_queue(q))
+ spin_lock_init(&q->rescue_lock);
+ bio_list_init(&q->rescue_list);
+ INIT_WORK(&q->rescue_work, bio_rescue_work);
+
+ q->rescue_workqueue = alloc_workqueue("bioset", WQ_MEM_RECLAIM, 0);
+ if (!q->rescue_workqueue)
goto fail_ref;
+ if (blkcg_init_queue(q))
+ goto fail_rescue;
+
return q;
+fail_rescue:
+ destroy_workqueue(q->rescue_workqueue);
fail_ref:
percpu_ref_exit(&q->q_usage_counter);
fail_bdi:
@@ -1994,7 +2006,7 @@ generic_make_request_checks(struct bio *bio)
*/
blk_qc_t generic_make_request(struct bio *bio)
{
- struct bio_list bio_list_on_stack;
+ struct bio_plug_list bio_list_on_stack;
blk_qc_t ret = BLK_QC_T_NONE;
if (!generic_make_request_checks(bio))
@@ -2011,7 +2023,9 @@ blk_qc_t generic_make_request(struct bio *bio)
* should be added at the tail
*/
if (current->bio_list) {
- bio_list_add(current->bio_list, bio);
+ WARN(!current->bio_list->q->rescue_workqueue,
+ "submitting bio beneath generic_make_request() without rescuer");
+ bio_list_add(¤t->bio_list->bios, bio);
goto out;
}
@@ -2030,19 +2044,23 @@ blk_qc_t generic_make_request(struct bio *bio)
* bio_list, and call into ->make_request() again.
*/
BUG_ON(bio->bi_next);
- bio_list_init(&bio_list_on_stack);
+ bio_list_init(&bio_list_on_stack.bios);
current->bio_list = &bio_list_on_stack;
+
do {
struct request_queue *q = bdev_get_queue(bio->bi_bdev);
+ current->bio_list->q = q;
+
if (likely(blk_queue_enter(q, false) == 0)) {
ret = q->make_request_fn(q, bio);
blk_queue_exit(q);
- bio = bio_list_pop(current->bio_list);
+ bio = bio_list_pop(¤t->bio_list->bios);
} else {
- struct bio *bio_next = bio_list_pop(current->bio_list);
+ struct bio *bio_next =
+ bio_list_pop(¤t->bio_list->bios);
bio_io_error(bio);
bio = bio_next;
@@ -2055,6 +2073,34 @@ blk_qc_t generic_make_request(struct bio *bio)
}
EXPORT_SYMBOL(generic_make_request);
+static void bio_rescue_work(struct work_struct *work)
+{
+ struct request_queue *q =
+ container_of(work, struct request_queue, rescue_work);
+ struct bio *bio;
+
+ while (1) {
+ spin_lock(&q->rescue_lock);
+ bio = bio_list_pop(&q->rescue_list);
+ spin_unlock(&q->rescue_lock);
+
+ if (!bio)
+ break;
+
+ generic_make_request(bio);
+ }
+}
+
+void blk_punt_blocked_bios(struct bio_plug_list *list)
+{
+ spin_lock(&list->q->rescue_lock);
+ bio_list_merge(&list->q->rescue_list, &list->bios);
+ bio_list_init(&list->bios);
+ spin_unlock(&list->q->rescue_lock);
+
+ queue_work(list->q->rescue_workqueue, &list->q->rescue_work);
+}
+
/**
* submit_bio - submit a bio to the block device layer for I/O
* @bio: The &struct bio which describes the I/O
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index 7f27a18cc4..77529238d1 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -660,6 +660,8 @@ static void blk_release_queue(struct kobject *kobj)
blk_trace_shutdown(q);
+ if (q->rescue_workqueue)
+ destroy_workqueue(q->rescue_workqueue);
if (q->bio_split)
bioset_free(q->bio_split);
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 1ffe8e37ae..87eeec7eda 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -658,6 +658,13 @@ static inline struct bio *bio_list_get(struct bio_list *bl)
return bio;
}
+struct bio_plug_list {
+ struct bio_list bios;
+ struct request_queue *q;
+};
+
+void blk_punt_blocked_bios(struct bio_plug_list *);
+
/*
* Increment chain count for the bio. Make sure the CHAIN flag update
* is visible before the raised count.
@@ -687,15 +694,6 @@ struct bio_set {
mempool_t bio_integrity_pool;
mempool_t bvec_integrity_pool;
#endif
-
- /*
- * Deadlock avoidance for stacking block drivers: see comments in
- * bio_alloc_bioset() for details
- */
- spinlock_t rescue_lock;
- struct bio_list rescue_list;
- struct work_struct rescue_work;
- struct workqueue_struct *rescue_workqueue;
};
struct biovec_slab {
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index c47c358ba0..f64b886c65 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -476,6 +476,16 @@ struct request_queue {
struct bio_set *bio_split;
bool mq_sysfs_init_done;
+
+ /*
+ * Deadlock avoidance, to deal with the plugging in
+ * generic_make_request() that converts recursion to iteration to avoid
+ * stack overflow:
+ */
+ spinlock_t rescue_lock;
+ struct bio_list rescue_list;
+ struct work_struct rescue_work;
+ struct workqueue_struct *rescue_workqueue;
};
#define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 2865d10a28..59df7a1030 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1762,7 +1762,7 @@ struct task_struct {
void *journal_info;
/* stacked block device info */
- struct bio_list *bio_list;
+ struct bio_plug_list *bio_list;
#ifdef CONFIG_BLOCK
/* stack plugging */
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index bd39d698cb..23b6290ba1 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3439,6 +3439,10 @@ static inline void sched_submit_work(struct task_struct *tsk)
{
if (!tsk->state || tsk_is_pi_blocked(tsk))
return;
+
+ if (tsk->bio_list && !bio_list_empty(&tsk->bio_list->bios))
+ blk_punt_blocked_bios(tsk->bio_list);
+
/*
* If we are going to sleep and we have plugged IO queued,
* make sure to submit it to avoid deadlocks.
--
2.11.0
[toc] | [prev] | [next] | [standalone]
| From | Mike Snitzer <snitzer@redhat.com> |
|---|---|
| Date | 2017-02-07 18:20 +0100 |
| Subject | Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone |
| Message-ID | <t8hAR-5JV-9@gated-at.bofh.it> |
| In reply to | #1575338 |
On Mon, Feb 06 2017 at 9:49pm -0500, Kent Overstreet <kent.overstreet@gmail.com> wrote: > On Mon, Feb 06, 2017 at 04:47:24PM -0900, Kent Overstreet wrote: > > On Mon, Feb 06, 2017 at 01:53:09PM +0100, Pavel Machek wrote: > > > Still there on v4.9, 36 threads on nokia n900 cellphone. > > > > > > So.. what needs to be done there? > > > But, I just got an idea for how to handle this that might be halfway sane, maybe > > I'll try and come up with a patch... > > Ok, here's such a patch, only lightly tested: > > -- >8 -- > Subject: [PATCH] block: Make rescuer threads per request_queue, not per bioset > > Note: this patch is very lightly tested. > > Also, trigger rescuing whenever with bios on current->bio_list, instead > of only when we block in bio_alloc_bioset(). This is more correct, and > should result in fewer rescuer threads. > > XXX: The current->bio_list plugging needs to be unified with the > blk_plug mechanism. > > TODO: If we change normal request_queue drivers to handle arbitrary size > bios by processing requests incrementally, instead of splitting bios, > then we can get rid of rescuer threads from those devices. Hi Kent, I really appreciate you working on this further. Thanks. As I think you're probably already aware, a long standing issue with the per bio_set rescuer is this bug (which manifests in dm-snapshot deadlocks): https://bugzilla.kernel.org/show_bug.cgi?id=119841 Please also see this patch header, from a private branch from a while ago, that describes the problem in detail: http://git.kernel.org/cgit/linux/kernel/git/snitzer/linux.git/commit/?h=wip&id=cd2c760b5a609e2aaf3735a7b9503a953535c368 Would welcome your consideration of that BZ as you think further and/or iterate on this line of work. Mike
[toc] | [prev] | [next] | [standalone]
| From | Pavel Machek <pavel@ucw.cz> |
|---|---|
| Date | 2017-02-07 21:40 +0100 |
| Subject | Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone |
| Message-ID | <t8kIq-7B3-27@gated-at.bofh.it> |
| In reply to | #1575338 |
[Multipart message — attachments visible in raw view] — view raw
On Mon 2017-02-06 17:49:06, Kent Overstreet wrote:
> On Mon, Feb 06, 2017 at 04:47:24PM -0900, Kent Overstreet wrote:
> > On Mon, Feb 06, 2017 at 01:53:09PM +0100, Pavel Machek wrote:
> > > Still there on v4.9, 36 threads on nokia n900 cellphone.
> > >
> > > So.. what needs to be done there?
>
> > But, I just got an idea for how to handle this that might be halfway sane, maybe
> > I'll try and come up with a patch...
>
> Ok, here's such a patch, only lightly tested:
I guess it would be nice for me to test it... but what it is against?
I tried after v4.10-rc5 and linux-next, but got rejects in both cases.
Thanks,
Pavel
> -- >8 --
> Subject: [PATCH] block: Make rescuer threads per request_queue, not per bioset
>
> Note: this patch is very lightly tested.
>
> Also, trigger rescuing whenever with bios on current->bio_list, instead
> of only when we block in bio_alloc_bioset(). This is more correct, and
> should result in fewer rescuer threads.
>
> XXX: The current->bio_list plugging needs to be unified with the
> blk_plug mechanism.
>
> TODO: If we change normal request_queue drivers to handle arbitrary size
> bios by processing requests incrementally, instead of splitting bios,
> then we can get rid of rescuer threads from those devices.
> ---
> block/bio.c | 107 ++++---------------------------------------------
> block/blk-core.c | 58 ++++++++++++++++++++++++---
> block/blk-sysfs.c | 2 +
> include/linux/bio.h | 16 ++++----
> include/linux/blkdev.h | 10 +++++
> include/linux/sched.h | 2 +-
> kernel/sched/core.c | 4 ++
> 7 files changed, 83 insertions(+), 116 deletions(-)
>
> diff --git a/block/bio.c b/block/bio.c
> index f3b5786202..9ad54a9b12 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -336,54 +336,6 @@ void bio_chain(struct bio *bio, struct bio *parent)
> }
> EXPORT_SYMBOL(bio_chain);
>
> -static void bio_alloc_rescue(struct work_struct *work)
> -{
> - struct bio_set *bs = container_of(work, struct bio_set, rescue_work);
> - struct bio *bio;
> -
> - while (1) {
> - spin_lock(&bs->rescue_lock);
> - bio = bio_list_pop(&bs->rescue_list);
> - spin_unlock(&bs->rescue_lock);
> -
> - if (!bio)
> - break;
> -
> - generic_make_request(bio);
> - }
> -}
> -
> -static void punt_bios_to_rescuer(struct bio_set *bs)
> -{
> - struct bio_list punt, nopunt;
> - struct bio *bio;
> -
> - /*
> - * In order to guarantee forward progress we must punt only bios that
> - * were allocated from this bio_set; otherwise, if there was a bio on
> - * there for a stacking driver higher up in the stack, processing it
> - * could require allocating bios from this bio_set, and doing that from
> - * our own rescuer would be bad.
> - *
> - * Since bio lists are singly linked, pop them all instead of trying to
> - * remove from the middle of the list:
> - */
> -
> - bio_list_init(&punt);
> - bio_list_init(&nopunt);
> -
> - while ((bio = bio_list_pop(current->bio_list)))
> - bio_list_add(bio->bi_pool == bs ? &punt : &nopunt, bio);
> -
> - *current->bio_list = nopunt;
> -
> - spin_lock(&bs->rescue_lock);
> - bio_list_merge(&bs->rescue_list, &punt);
> - spin_unlock(&bs->rescue_lock);
> -
> - queue_work(bs->rescue_workqueue, &bs->rescue_work);
> -}
> -
> /**
> * bio_alloc_bioset - allocate a bio for I/O
> * @gfp_mask: the GFP_ mask given to the slab allocator
> @@ -421,54 +373,27 @@ static void punt_bios_to_rescuer(struct bio_set *bs)
> */
> struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
> {
> - gfp_t saved_gfp = gfp_mask;
> unsigned front_pad;
> unsigned inline_vecs;
> struct bio_vec *bvl = NULL;
> struct bio *bio;
> void *p;
>
> - if (!bs) {
> - if (nr_iovecs > UIO_MAXIOV)
> - return NULL;
> + WARN(current->bio_list &&
> + !current->bio_list->q->rescue_workqueue,
> + "allocating bio beneath generic_make_request() without rescuer");
>
> + if (nr_iovecs > UIO_MAXIOV)
> + return NULL;
> +
> + if (!bs) {
> p = kmalloc(sizeof(struct bio) +
> nr_iovecs * sizeof(struct bio_vec),
> gfp_mask);
> front_pad = 0;
> inline_vecs = nr_iovecs;
> } else {
> - /*
> - * generic_make_request() converts recursion to iteration; this
> - * means if we're running beneath it, any bios we allocate and
> - * submit will not be submitted (and thus freed) until after we
> - * return.
> - *
> - * This exposes us to a potential deadlock if we allocate
> - * multiple bios from the same bio_set() while running
> - * underneath generic_make_request(). If we were to allocate
> - * multiple bios (say a stacking block driver that was splitting
> - * bios), we would deadlock if we exhausted the mempool's
> - * reserve.
> - *
> - * We solve this, and guarantee forward progress, with a rescuer
> - * workqueue per bio_set. If we go to allocate and there are
> - * bios on current->bio_list, we first try the allocation
> - * without __GFP_DIRECT_RECLAIM; if that fails, we punt those
> - * bios we would be blocking to the rescuer workqueue before
> - * we retry with the original gfp_flags.
> - */
> -
> - if (current->bio_list && !bio_list_empty(current->bio_list))
> - gfp_mask &= ~__GFP_DIRECT_RECLAIM;
> -
> p = mempool_alloc(&bs->bio_pool, gfp_mask);
> - if (!p && gfp_mask != saved_gfp) {
> - punt_bios_to_rescuer(bs);
> - gfp_mask = saved_gfp;
> - p = mempool_alloc(&bs->bio_pool, gfp_mask);
> - }
> -
> front_pad = bs->front_pad;
> inline_vecs = BIO_INLINE_VECS;
> }
> @@ -483,12 +408,6 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
> unsigned long idx = 0;
>
> bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, &bs->bvec_pool);
> - if (!bvl && gfp_mask != saved_gfp) {
> - punt_bios_to_rescuer(bs);
> - gfp_mask = saved_gfp;
> - bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, &bs->bvec_pool);
> - }
> -
> if (unlikely(!bvl))
> goto err_free;
>
> @@ -1938,10 +1857,6 @@ int biovec_init_pool(mempool_t *pool, int pool_entries)
>
> void bioset_exit(struct bio_set *bs)
> {
> - if (bs->rescue_workqueue)
> - destroy_workqueue(bs->rescue_workqueue);
> - bs->rescue_workqueue = NULL;
> -
> mempool_exit(&bs->bio_pool);
> mempool_exit(&bs->bvec_pool);
>
> @@ -1968,10 +1883,6 @@ static int __bioset_init(struct bio_set *bs,
>
> bs->front_pad = front_pad;
>
> - spin_lock_init(&bs->rescue_lock);
> - bio_list_init(&bs->rescue_list);
> - INIT_WORK(&bs->rescue_work, bio_alloc_rescue);
> -
> bs->bio_slab = bio_find_or_create_slab(front_pad + back_pad);
> if (!bs->bio_slab)
> return -ENOMEM;
> @@ -1983,10 +1894,6 @@ static int __bioset_init(struct bio_set *bs,
> biovec_init_pool(&bs->bvec_pool, pool_size))
> goto bad;
>
> - bs->rescue_workqueue = alloc_workqueue("bioset", WQ_MEM_RECLAIM, 0);
> - if (!bs->rescue_workqueue)
> - goto bad;
> -
> return 0;
> bad:
> bioset_exit(bs);
> diff --git a/block/blk-core.c b/block/blk-core.c
> index 7e3cfa9c88..f716164cb3 100644
> --- a/block/blk-core.c
> +++ b/block/blk-core.c
> @@ -48,6 +48,8 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(block_unplug);
>
> DEFINE_IDA(blk_queue_ida);
>
> +static void bio_rescue_work(struct work_struct *);
> +
> /*
> * For the allocated request tables
> */
> @@ -759,11 +761,21 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
> PERCPU_REF_INIT_ATOMIC, GFP_KERNEL))
> goto fail_bdi;
>
> - if (blkcg_init_queue(q))
> + spin_lock_init(&q->rescue_lock);
> + bio_list_init(&q->rescue_list);
> + INIT_WORK(&q->rescue_work, bio_rescue_work);
> +
> + q->rescue_workqueue = alloc_workqueue("bioset", WQ_MEM_RECLAIM, 0);
> + if (!q->rescue_workqueue)
> goto fail_ref;
>
> + if (blkcg_init_queue(q))
> + goto fail_rescue;
> +
> return q;
>
> +fail_rescue:
> + destroy_workqueue(q->rescue_workqueue);
> fail_ref:
> percpu_ref_exit(&q->q_usage_counter);
> fail_bdi:
> @@ -1994,7 +2006,7 @@ generic_make_request_checks(struct bio *bio)
> */
> blk_qc_t generic_make_request(struct bio *bio)
> {
> - struct bio_list bio_list_on_stack;
> + struct bio_plug_list bio_list_on_stack;
> blk_qc_t ret = BLK_QC_T_NONE;
>
> if (!generic_make_request_checks(bio))
> @@ -2011,7 +2023,9 @@ blk_qc_t generic_make_request(struct bio *bio)
> * should be added at the tail
> */
> if (current->bio_list) {
> - bio_list_add(current->bio_list, bio);
> + WARN(!current->bio_list->q->rescue_workqueue,
> + "submitting bio beneath generic_make_request() without rescuer");
> + bio_list_add(¤t->bio_list->bios, bio);
> goto out;
> }
>
> @@ -2030,19 +2044,23 @@ blk_qc_t generic_make_request(struct bio *bio)
> * bio_list, and call into ->make_request() again.
> */
> BUG_ON(bio->bi_next);
> - bio_list_init(&bio_list_on_stack);
> + bio_list_init(&bio_list_on_stack.bios);
> current->bio_list = &bio_list_on_stack;
> +
> do {
> struct request_queue *q = bdev_get_queue(bio->bi_bdev);
>
> + current->bio_list->q = q;
> +
> if (likely(blk_queue_enter(q, false) == 0)) {
> ret = q->make_request_fn(q, bio);
>
> blk_queue_exit(q);
>
> - bio = bio_list_pop(current->bio_list);
> + bio = bio_list_pop(¤t->bio_list->bios);
> } else {
> - struct bio *bio_next = bio_list_pop(current->bio_list);
> + struct bio *bio_next =
> + bio_list_pop(¤t->bio_list->bios);
>
> bio_io_error(bio);
> bio = bio_next;
> @@ -2055,6 +2073,34 @@ blk_qc_t generic_make_request(struct bio *bio)
> }
> EXPORT_SYMBOL(generic_make_request);
>
> +static void bio_rescue_work(struct work_struct *work)
> +{
> + struct request_queue *q =
> + container_of(work, struct request_queue, rescue_work);
> + struct bio *bio;
> +
> + while (1) {
> + spin_lock(&q->rescue_lock);
> + bio = bio_list_pop(&q->rescue_list);
> + spin_unlock(&q->rescue_lock);
> +
> + if (!bio)
> + break;
> +
> + generic_make_request(bio);
> + }
> +}
> +
> +void blk_punt_blocked_bios(struct bio_plug_list *list)
> +{
> + spin_lock(&list->q->rescue_lock);
> + bio_list_merge(&list->q->rescue_list, &list->bios);
> + bio_list_init(&list->bios);
> + spin_unlock(&list->q->rescue_lock);
> +
> + queue_work(list->q->rescue_workqueue, &list->q->rescue_work);
> +}
> +
> /**
> * submit_bio - submit a bio to the block device layer for I/O
> * @bio: The &struct bio which describes the I/O
> diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
> index 7f27a18cc4..77529238d1 100644
> --- a/block/blk-sysfs.c
> +++ b/block/blk-sysfs.c
> @@ -660,6 +660,8 @@ static void blk_release_queue(struct kobject *kobj)
>
> blk_trace_shutdown(q);
>
> + if (q->rescue_workqueue)
> + destroy_workqueue(q->rescue_workqueue);
> if (q->bio_split)
> bioset_free(q->bio_split);
>
> diff --git a/include/linux/bio.h b/include/linux/bio.h
> index 1ffe8e37ae..87eeec7eda 100644
> --- a/include/linux/bio.h
> +++ b/include/linux/bio.h
> @@ -658,6 +658,13 @@ static inline struct bio *bio_list_get(struct bio_list *bl)
> return bio;
> }
>
> +struct bio_plug_list {
> + struct bio_list bios;
> + struct request_queue *q;
> +};
> +
> +void blk_punt_blocked_bios(struct bio_plug_list *);
> +
> /*
> * Increment chain count for the bio. Make sure the CHAIN flag update
> * is visible before the raised count.
> @@ -687,15 +694,6 @@ struct bio_set {
> mempool_t bio_integrity_pool;
> mempool_t bvec_integrity_pool;
> #endif
> -
> - /*
> - * Deadlock avoidance for stacking block drivers: see comments in
> - * bio_alloc_bioset() for details
> - */
> - spinlock_t rescue_lock;
> - struct bio_list rescue_list;
> - struct work_struct rescue_work;
> - struct workqueue_struct *rescue_workqueue;
> };
>
> struct biovec_slab {
> diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
> index c47c358ba0..f64b886c65 100644
> --- a/include/linux/blkdev.h
> +++ b/include/linux/blkdev.h
> @@ -476,6 +476,16 @@ struct request_queue {
> struct bio_set *bio_split;
>
> bool mq_sysfs_init_done;
> +
> + /*
> + * Deadlock avoidance, to deal with the plugging in
> + * generic_make_request() that converts recursion to iteration to avoid
> + * stack overflow:
> + */
> + spinlock_t rescue_lock;
> + struct bio_list rescue_list;
> + struct work_struct rescue_work;
> + struct workqueue_struct *rescue_workqueue;
> };
>
> #define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */
> diff --git a/include/linux/sched.h b/include/linux/sched.h
> index 2865d10a28..59df7a1030 100644
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -1762,7 +1762,7 @@ struct task_struct {
> void *journal_info;
>
> /* stacked block device info */
> - struct bio_list *bio_list;
> + struct bio_plug_list *bio_list;
>
> #ifdef CONFIG_BLOCK
> /* stack plugging */
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index bd39d698cb..23b6290ba1 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -3439,6 +3439,10 @@ static inline void sched_submit_work(struct task_struct *tsk)
> {
> if (!tsk->state || tsk_is_pi_blocked(tsk))
> return;
> +
> + if (tsk->bio_list && !bio_list_empty(&tsk->bio_list->bios))
> + blk_punt_blocked_bios(tsk->bio_list);
> +
> /*
> * If we are going to sleep and we have plugged IO queued,
> * make sure to submit it to avoid deadlocks.
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[toc] | [prev] | [next] | [standalone]
| From | Mike Galbraith <efault@gmx.de> |
|---|---|
| Date | 2017-02-08 04:20 +0100 |
| Subject | Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone |
| Message-ID | <t8qXw-3aG-7@gated-at.bofh.it> |
| In reply to | #1576042 |
On Tue, 2017-02-07 at 21:39 +0100, Pavel Machek wrote: > On Mon 2017-02-06 17:49:06, Kent Overstreet wrote: > > On Mon, Feb 06, 2017 at 04:47:24PM -0900, Kent Overstreet wrote: > > > On Mon, Feb 06, 2017 at 01:53:09PM +0100, Pavel Machek wrote: > > > > Still there on v4.9, 36 threads on nokia n900 cellphone. > > > > > > > > So.. what needs to be done there? > > > > > But, I just got an idea for how to handle this that might be halfway sane, maybe > > > I'll try and come up with a patch... > > > > Ok, here's such a patch, only lightly tested: > > I guess it would be nice for me to test it... but what it is against? > I tried after v4.10-rc5 and linux-next, but got rejects in both cases. It wedged into master easily enough (box still seems to work.. but I'll be rebooting in a very few seconds just in case:), but threads on my desktop box only dropped from 73 to 71. Poo. -Mike
[toc] | [prev] | [next] | [standalone]
| From | Kent Overstreet <kent.overstreet@gmail.com> |
|---|---|
| Date | 2017-02-08 06:00 +0100 |
| Subject | Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone |
| Message-ID | <t8swi-3YJ-1@gated-at.bofh.it> |
| In reply to | #1576042 |
On Tue, Feb 07, 2017 at 09:39:11PM +0100, Pavel Machek wrote:
> On Mon 2017-02-06 17:49:06, Kent Overstreet wrote:
> > On Mon, Feb 06, 2017 at 04:47:24PM -0900, Kent Overstreet wrote:
> > > On Mon, Feb 06, 2017 at 01:53:09PM +0100, Pavel Machek wrote:
> > > > Still there on v4.9, 36 threads on nokia n900 cellphone.
> > > >
> > > > So.. what needs to be done there?
> >
> > > But, I just got an idea for how to handle this that might be halfway sane, maybe
> > > I'll try and come up with a patch...
> >
> > Ok, here's such a patch, only lightly tested:
>
> I guess it would be nice for me to test it... but what it is against?
> I tried after v4.10-rc5 and linux-next, but got rejects in both cases.
Sorry, I forgot I had a few other patches in my branch that touch
mempool/biosets code.
Also, after thinking about it more and looking at the relevant code, I'm pretty
sure we don't need rescuer threads for block devices that just split bios - i.e.
most of them, so I changed my patch to do that.
Tested it by ripping out the current->bio_list checks/workarounds from the
bcache code, appears to work:
-- >8 --
Subject: [PATCH] block: Make rescuer threads per request_queue, not per bioset
Also, trigger rescuing whenever with bios on current->bio_list, instead
of only when we block in bio_alloc_bioset(). This is more correct, and
should result in fewer rescuer threads.
XXX: The current->bio_list plugging needs to be unified with the
blk_plug mechanism.
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
block/bio.c | 105 +++--------------------------------------
block/blk-core.c | 69 +++++++++++++++++++++++----
block/blk-mq.c | 3 +-
block/blk-sysfs.c | 2 +
drivers/block/brd.c | 2 +-
drivers/block/drbd/drbd_main.c | 2 +-
drivers/block/null_blk.c | 3 +-
drivers/block/pktcdvd.c | 2 +-
drivers/block/ps3vram.c | 2 +-
drivers/block/rsxx/dev.c | 2 +-
drivers/block/umem.c | 2 +-
drivers/block/zram/zram_drv.c | 2 +-
drivers/lightnvm/gennvm.c | 2 +-
drivers/md/bcache/super.c | 2 +-
drivers/md/dm.c | 2 +-
drivers/md/md.c | 2 +-
drivers/nvdimm/blk.c | 2 +-
drivers/nvdimm/btt.c | 2 +-
drivers/nvdimm/pmem.c | 3 +-
drivers/s390/block/dcssblk.c | 2 +-
drivers/s390/block/xpram.c | 2 +-
include/linux/bio.h | 16 +++----
include/linux/blkdev.h | 16 ++++++-
include/linux/sched.h | 2 +-
kernel/sched/core.c | 6 +++
25 files changed, 117 insertions(+), 138 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index 2b375020fc..9b89be1719 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -340,54 +340,6 @@ void bio_chain(struct bio *bio, struct bio *parent)
}
EXPORT_SYMBOL(bio_chain);
-static void bio_alloc_rescue(struct work_struct *work)
-{
- struct bio_set *bs = container_of(work, struct bio_set, rescue_work);
- struct bio *bio;
-
- while (1) {
- spin_lock(&bs->rescue_lock);
- bio = bio_list_pop(&bs->rescue_list);
- spin_unlock(&bs->rescue_lock);
-
- if (!bio)
- break;
-
- generic_make_request(bio);
- }
-}
-
-static void punt_bios_to_rescuer(struct bio_set *bs)
-{
- struct bio_list punt, nopunt;
- struct bio *bio;
-
- /*
- * In order to guarantee forward progress we must punt only bios that
- * were allocated from this bio_set; otherwise, if there was a bio on
- * there for a stacking driver higher up in the stack, processing it
- * could require allocating bios from this bio_set, and doing that from
- * our own rescuer would be bad.
- *
- * Since bio lists are singly linked, pop them all instead of trying to
- * remove from the middle of the list:
- */
-
- bio_list_init(&punt);
- bio_list_init(&nopunt);
-
- while ((bio = bio_list_pop(current->bio_list)))
- bio_list_add(bio->bi_pool == bs ? &punt : &nopunt, bio);
-
- *current->bio_list = nopunt;
-
- spin_lock(&bs->rescue_lock);
- bio_list_merge(&bs->rescue_list, &punt);
- spin_unlock(&bs->rescue_lock);
-
- queue_work(bs->rescue_workqueue, &bs->rescue_work);
-}
-
/**
* bio_alloc_bioset - allocate a bio for I/O
* @gfp_mask: the GFP_ mask given to the slab allocator
@@ -425,17 +377,20 @@ static void punt_bios_to_rescuer(struct bio_set *bs)
*/
struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
{
- gfp_t saved_gfp = gfp_mask;
unsigned front_pad;
unsigned inline_vecs;
struct bio_vec *bvl = NULL;
struct bio *bio;
void *p;
- if (!bs) {
- if (nr_iovecs > UIO_MAXIOV)
- return NULL;
+ WARN(current->bio_list &&
+ !current->bio_list->q->rescue_workqueue,
+ "allocating bio beneath generic_make_request() without rescuer");
+ if (nr_iovecs > UIO_MAXIOV)
+ return NULL;
+
+ if (!bs) {
p = kmalloc(sizeof(struct bio) +
nr_iovecs * sizeof(struct bio_vec),
gfp_mask);
@@ -445,37 +400,8 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
/* should not use nobvec bioset for nr_iovecs > 0 */
if (WARN_ON_ONCE(!bs->bvec_pool && nr_iovecs > 0))
return NULL;
- /*
- * generic_make_request() converts recursion to iteration; this
- * means if we're running beneath it, any bios we allocate and
- * submit will not be submitted (and thus freed) until after we
- * return.
- *
- * This exposes us to a potential deadlock if we allocate
- * multiple bios from the same bio_set() while running
- * underneath generic_make_request(). If we were to allocate
- * multiple bios (say a stacking block driver that was splitting
- * bios), we would deadlock if we exhausted the mempool's
- * reserve.
- *
- * We solve this, and guarantee forward progress, with a rescuer
- * workqueue per bio_set. If we go to allocate and there are
- * bios on current->bio_list, we first try the allocation
- * without __GFP_DIRECT_RECLAIM; if that fails, we punt those
- * bios we would be blocking to the rescuer workqueue before
- * we retry with the original gfp_flags.
- */
-
- if (current->bio_list && !bio_list_empty(current->bio_list))
- gfp_mask &= ~__GFP_DIRECT_RECLAIM;
p = mempool_alloc(bs->bio_pool, gfp_mask);
- if (!p && gfp_mask != saved_gfp) {
- punt_bios_to_rescuer(bs);
- gfp_mask = saved_gfp;
- p = mempool_alloc(bs->bio_pool, gfp_mask);
- }
-
front_pad = bs->front_pad;
inline_vecs = BIO_INLINE_VECS;
}
@@ -490,12 +416,6 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
unsigned long idx = 0;
bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, bs->bvec_pool);
- if (!bvl && gfp_mask != saved_gfp) {
- punt_bios_to_rescuer(bs);
- gfp_mask = saved_gfp;
- bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, bs->bvec_pool);
- }
-
if (unlikely(!bvl))
goto err_free;
@@ -1892,9 +1812,6 @@ mempool_t *biovec_create_pool(int pool_entries)
void bioset_free(struct bio_set *bs)
{
- if (bs->rescue_workqueue)
- destroy_workqueue(bs->rescue_workqueue);
-
if (bs->bio_pool)
mempool_destroy(bs->bio_pool);
@@ -1921,10 +1838,6 @@ static struct bio_set *__bioset_create(unsigned int pool_size,
bs->front_pad = front_pad;
- spin_lock_init(&bs->rescue_lock);
- bio_list_init(&bs->rescue_list);
- INIT_WORK(&bs->rescue_work, bio_alloc_rescue);
-
bs->bio_slab = bio_find_or_create_slab(front_pad + back_pad);
if (!bs->bio_slab) {
kfree(bs);
@@ -1941,10 +1854,6 @@ static struct bio_set *__bioset_create(unsigned int pool_size,
goto bad;
}
- bs->rescue_workqueue = alloc_workqueue("bioset", WQ_MEM_RECLAIM, 0);
- if (!bs->rescue_workqueue)
- goto bad;
-
return bs;
bad:
bioset_free(bs);
diff --git a/block/blk-core.c b/block/blk-core.c
index 61ba08c58b..2222fd40e2 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -49,6 +49,8 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(block_unplug);
DEFINE_IDA(blk_queue_ida);
+static void bio_rescue_work(struct work_struct *);
+
/*
* For the allocated request tables
*/
@@ -643,9 +645,9 @@ void blk_exit_rl(struct request_list *rl)
mempool_destroy(rl->rq_pool);
}
-struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
+struct request_queue *blk_alloc_queue(gfp_t gfp_mask, int flags)
{
- return blk_alloc_queue_node(gfp_mask, NUMA_NO_NODE);
+ return blk_alloc_queue_node(gfp_mask, NUMA_NO_NODE, flags);
}
EXPORT_SYMBOL(blk_alloc_queue);
@@ -690,7 +692,7 @@ static void blk_rq_timed_out_timer(unsigned long data)
kblockd_schedule_work(&q->timeout_work);
}
-struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
+struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id, int flags)
{
struct request_queue *q;
int err;
@@ -760,11 +762,23 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
PERCPU_REF_INIT_ATOMIC, GFP_KERNEL))
goto fail_bdi;
+ spin_lock_init(&q->rescue_lock);
+ bio_list_init(&q->rescue_list);
+ INIT_WORK(&q->rescue_work, bio_rescue_work);
+
+ if (!(flags & BLK_QUEUE_NO_RESCUER)) {
+ q->rescue_workqueue = alloc_workqueue("rescue", WQ_MEM_RECLAIM, 0);
+ if (!q->rescue_workqueue)
+ goto fail_ref;
+ }
+
if (blkcg_init_queue(q))
- goto fail_ref;
+ goto fail_rescue;
return q;
+fail_rescue:
+ destroy_workqueue(q->rescue_workqueue);
fail_ref:
percpu_ref_exit(&q->q_usage_counter);
fail_bdi:
@@ -823,7 +837,8 @@ blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)
{
struct request_queue *uninit_q, *q;
- uninit_q = blk_alloc_queue_node(GFP_KERNEL, node_id);
+ uninit_q = blk_alloc_queue_node(GFP_KERNEL, node_id,
+ BLK_QUEUE_NO_RESCUER);
if (!uninit_q)
return NULL;
@@ -1977,7 +1992,7 @@ generic_make_request_checks(struct bio *bio)
*/
blk_qc_t generic_make_request(struct bio *bio)
{
- struct bio_list bio_list_on_stack;
+ struct bio_plug_list bio_list_on_stack;
blk_qc_t ret = BLK_QC_T_NONE;
if (!generic_make_request_checks(bio))
@@ -1994,7 +2009,9 @@ blk_qc_t generic_make_request(struct bio *bio)
* should be added at the tail
*/
if (current->bio_list) {
- bio_list_add(current->bio_list, bio);
+ WARN(!current->bio_list->q->rescue_workqueue,
+ "submitting bio beneath generic_make_request() without rescuer");
+ bio_list_add(¤t->bio_list->bios, bio);
goto out;
}
@@ -2013,19 +2030,23 @@ blk_qc_t generic_make_request(struct bio *bio)
* bio_list, and call into ->make_request() again.
*/
BUG_ON(bio->bi_next);
- bio_list_init(&bio_list_on_stack);
+ bio_list_init(&bio_list_on_stack.bios);
current->bio_list = &bio_list_on_stack;
+
do {
struct request_queue *q = bdev_get_queue(bio->bi_bdev);
+ current->bio_list->q = q;
+
if (likely(blk_queue_enter(q, false) == 0)) {
ret = q->make_request_fn(q, bio);
blk_queue_exit(q);
- bio = bio_list_pop(current->bio_list);
+ bio = bio_list_pop(¤t->bio_list->bios);
} else {
- struct bio *bio_next = bio_list_pop(current->bio_list);
+ struct bio *bio_next =
+ bio_list_pop(¤t->bio_list->bios);
bio_io_error(bio);
bio = bio_next;
@@ -2038,6 +2059,34 @@ blk_qc_t generic_make_request(struct bio *bio)
}
EXPORT_SYMBOL(generic_make_request);
+static void bio_rescue_work(struct work_struct *work)
+{
+ struct request_queue *q =
+ container_of(work, struct request_queue, rescue_work);
+ struct bio *bio;
+
+ while (1) {
+ spin_lock(&q->rescue_lock);
+ bio = bio_list_pop(&q->rescue_list);
+ spin_unlock(&q->rescue_lock);
+
+ if (!bio)
+ break;
+
+ generic_make_request(bio);
+ }
+}
+
+void blk_punt_blocked_bios(struct bio_plug_list *list)
+{
+ spin_lock(&list->q->rescue_lock);
+ bio_list_merge(&list->q->rescue_list, &list->bios);
+ bio_list_init(&list->bios);
+ spin_unlock(&list->q->rescue_lock);
+
+ queue_work(list->q->rescue_workqueue, &list->q->rescue_work);
+}
+
/**
* submit_bio - submit a bio to the block device layer for I/O
* @bio: The &struct bio which describes the I/O
diff --git a/block/blk-mq.c b/block/blk-mq.c
index c3400b5444..5e7f67c108 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -2043,7 +2043,8 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set)
{
struct request_queue *uninit_q, *q;
- uninit_q = blk_alloc_queue_node(GFP_KERNEL, set->numa_node);
+ uninit_q = blk_alloc_queue_node(GFP_KERNEL, set->numa_node,
+ BLK_QUEUE_NO_RESCUER);
if (!uninit_q)
return ERR_PTR(-ENOMEM);
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index 1dbce05759..1ab82c342b 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -821,6 +821,8 @@ static void blk_release_queue(struct kobject *kobj)
blk_trace_shutdown(q);
+ if (q->rescue_workqueue)
+ destroy_workqueue(q->rescue_workqueue);
if (q->bio_split)
bioset_free(q->bio_split);
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index 3adc32a315..43ff4b23e4 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -449,7 +449,7 @@ static struct brd_device *brd_alloc(int i)
spin_lock_init(&brd->brd_lock);
INIT_RADIX_TREE(&brd->brd_pages, GFP_ATOMIC);
- brd->brd_queue = blk_alloc_queue(GFP_KERNEL);
+ brd->brd_queue = blk_alloc_queue(GFP_KERNEL, BLK_QUEUE_NO_RESCUER);
if (!brd->brd_queue)
goto out_free_dev;
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 83482721bc..e46821ebc6 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -2810,7 +2810,7 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
drbd_init_set_defaults(device);
- q = blk_alloc_queue(GFP_KERNEL);
+ q = blk_alloc_queue(GFP_KERNEL, 0);
if (!q)
goto out_no_q;
device->rq_queue = q;
diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c
index c0e14e5490..0ce25ce95f 100644
--- a/drivers/block/null_blk.c
+++ b/drivers/block/null_blk.c
@@ -734,7 +734,8 @@ static int null_add_dev(void)
goto out_cleanup_tags;
}
} else if (queue_mode == NULL_Q_BIO) {
- nullb->q = blk_alloc_queue_node(GFP_KERNEL, home_node);
+ nullb->q = blk_alloc_queue_node(GFP_KERNEL, home_node,
+ BLK_QUEUE_NO_RESCUER);
if (!nullb->q) {
rv = -ENOMEM;
goto out_cleanup_queues;
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index 1b94c1ca5c..3ab1629475 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -2737,7 +2737,7 @@ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev)
strcpy(disk->disk_name, pd->name);
disk->devnode = pktcdvd_devnode;
disk->private_data = pd;
- disk->queue = blk_alloc_queue(GFP_KERNEL);
+ disk->queue = blk_alloc_queue(GFP_KERNEL, BLK_QUEUE_NO_RESCUER);
if (!disk->queue)
goto out_mem2;
diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c
index 456b4fe215..167e17058c 100644
--- a/drivers/block/ps3vram.c
+++ b/drivers/block/ps3vram.c
@@ -746,7 +746,7 @@ static int ps3vram_probe(struct ps3_system_bus_device *dev)
ps3vram_cache_init(dev);
ps3vram_proc_init(dev);
- queue = blk_alloc_queue(GFP_KERNEL);
+ queue = blk_alloc_queue(GFP_KERNEL, BLK_QUEUE_NO_RESCUER);
if (!queue) {
dev_err(&dev->core, "blk_alloc_queue failed\n");
error = -ENOMEM;
diff --git a/drivers/block/rsxx/dev.c b/drivers/block/rsxx/dev.c
index f81d70b39d..e53cea595f 100644
--- a/drivers/block/rsxx/dev.c
+++ b/drivers/block/rsxx/dev.c
@@ -266,7 +266,7 @@ int rsxx_setup_dev(struct rsxx_cardinfo *card)
return -ENOMEM;
}
- card->queue = blk_alloc_queue(GFP_KERNEL);
+ card->queue = blk_alloc_queue(GFP_KERNEL, BLK_QUEUE_NO_RESCUER);
if (!card->queue) {
dev_err(CARD_TO_DEV(card), "Failed queue alloc\n");
unregister_blkdev(card->major, DRIVER_NAME);
diff --git a/drivers/block/umem.c b/drivers/block/umem.c
index c141cc3be2..7d496364c4 100644
--- a/drivers/block/umem.c
+++ b/drivers/block/umem.c
@@ -890,7 +890,7 @@ static int mm_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
card->bio = NULL;
card->biotail = &card->bio;
- card->queue = blk_alloc_queue(GFP_KERNEL);
+ card->queue = blk_alloc_queue(GFP_KERNEL, BLK_QUEUE_NO_RESCUER);
if (!card->queue)
goto failed_alloc;
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index e5ab7d9e8c..85ab96f15f 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -1245,7 +1245,7 @@ static int zram_add(void)
init_rwsem(&zram->init_lock);
- queue = blk_alloc_queue(GFP_KERNEL);
+ queue = blk_alloc_queue(GFP_KERNEL, BLK_QUEUE_NO_RESCUER);
if (!queue) {
pr_err("Error allocating disk queue for device %d\n",
device_id);
diff --git a/drivers/lightnvm/gennvm.c b/drivers/lightnvm/gennvm.c
index ca7880082d..d36a155b42 100644
--- a/drivers/lightnvm/gennvm.c
+++ b/drivers/lightnvm/gennvm.c
@@ -233,7 +233,7 @@ static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
goto err_reserve;
}
- tqueue = blk_alloc_queue_node(GFP_KERNEL, dev->q->node);
+ tqueue = blk_alloc_queue_node(GFP_KERNEL, dev->q->node, 0);
if (!tqueue)
goto err_dev;
blk_queue_make_request(tqueue, tt->make_rq);
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 3a19cbc8b2..9cdbeb54f6 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -800,7 +800,7 @@ static int bcache_device_init(struct bcache_device *d, unsigned block_size,
d->disk->fops = &bcache_ops;
d->disk->private_data = d;
- q = blk_alloc_queue(GFP_KERNEL);
+ q = blk_alloc_queue(GFP_KERNEL, 0);
if (!q)
return -ENOMEM;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 3086da5664..e1b22a68d9 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1490,7 +1490,7 @@ static struct mapped_device *alloc_dev(int minor)
INIT_LIST_HEAD(&md->table_devices);
spin_lock_init(&md->uevent_lock);
- md->queue = blk_alloc_queue_node(GFP_KERNEL, numa_node_id);
+ md->queue = blk_alloc_queue_node(GFP_KERNEL, numa_node_id, 0);
if (!md->queue)
goto bad;
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 01175dac0d..0038d241d7 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -5061,7 +5061,7 @@ static int md_alloc(dev_t dev, char *name)
}
error = -ENOMEM;
- mddev->queue = blk_alloc_queue(GFP_KERNEL);
+ mddev->queue = blk_alloc_queue(GFP_KERNEL, 0);
if (!mddev->queue)
goto abort;
mddev->queue->queuedata = mddev;
diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c
index 9faaa9694d..a1d2e7a6ab 100644
--- a/drivers/nvdimm/blk.c
+++ b/drivers/nvdimm/blk.c
@@ -264,7 +264,7 @@ static int nsblk_attach_disk(struct nd_namespace_blk *nsblk)
internal_nlba = div_u64(nsblk->size, nsblk_internal_lbasize(nsblk));
available_disk_size = internal_nlba * nsblk_sector_size(nsblk);
- q = blk_alloc_queue(GFP_KERNEL);
+ q = blk_alloc_queue(GFP_KERNEL, BLK_QUEUE_NO_RESCUER);
if (!q)
return -ENOMEM;
if (devm_add_action_or_reset(dev, nd_blk_release_queue, q))
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index 368795aad5..7bd6135b77 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -1232,7 +1232,7 @@ static int btt_blk_init(struct btt *btt)
struct nd_namespace_common *ndns = nd_btt->ndns;
/* create a new disk and request queue for btt */
- btt->btt_queue = blk_alloc_queue(GFP_KERNEL);
+ btt->btt_queue = blk_alloc_queue(GFP_KERNEL, BLK_QUEUE_NO_RESCUER);
if (!btt->btt_queue)
return -ENOMEM;
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 5b536be5a1..314ac480bf 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -280,7 +280,8 @@ static int pmem_attach_disk(struct device *dev,
return -EBUSY;
}
- q = blk_alloc_queue_node(GFP_KERNEL, dev_to_node(dev));
+ q = blk_alloc_queue_node(GFP_KERNEL, dev_to_node(dev),
+ BLK_QUEUE_NO_RESCUER);
if (!q)
return -ENOMEM;
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index 9d66b4fb17..101e0ae2f7 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -612,7 +612,7 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
}
dev_info->gd->major = dcssblk_major;
dev_info->gd->fops = &dcssblk_devops;
- dev_info->dcssblk_queue = blk_alloc_queue(GFP_KERNEL);
+ dev_info->dcssblk_queue = blk_alloc_queue(GFP_KERNEL, BLK_QUEUE_NO_RESCUER);
dev_info->gd->queue = dev_info->dcssblk_queue;
dev_info->gd->private_data = dev_info;
blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index b9d7e755c8..72f52de17b 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -342,7 +342,7 @@ static int __init xpram_setup_blkdev(void)
xpram_disks[i] = alloc_disk(1);
if (!xpram_disks[i])
goto out;
- xpram_queues[i] = blk_alloc_queue(GFP_KERNEL);
+ xpram_queues[i] = blk_alloc_queue(GFP_KERNEL, BLK_QUEUE_NO_RESCUER);
if (!xpram_queues[i]) {
put_disk(xpram_disks[i]);
goto out;
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 7cf8a6c70a..ac333e9528 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -656,6 +656,13 @@ static inline struct bio *bio_list_get(struct bio_list *bl)
return bio;
}
+struct bio_plug_list {
+ struct bio_list bios;
+ struct request_queue *q;
+};
+
+void blk_punt_blocked_bios(struct bio_plug_list *);
+
/*
* Increment chain count for the bio. Make sure the CHAIN flag update
* is visible before the raised count.
@@ -685,15 +692,6 @@ struct bio_set {
mempool_t *bio_integrity_pool;
mempool_t *bvec_integrity_pool;
#endif
-
- /*
- * Deadlock avoidance for stacking block drivers: see comments in
- * bio_alloc_bioset() for details
- */
- spinlock_t rescue_lock;
- struct bio_list rescue_list;
- struct work_struct rescue_work;
- struct workqueue_struct *rescue_workqueue;
};
struct biovec_slab {
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 1ca8e8fd10..01acaf9bf9 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -570,6 +570,16 @@ struct request_queue {
struct bio_set *bio_split;
bool mq_sysfs_init_done;
+
+ /*
+ * Deadlock avoidance, to deal with the plugging in
+ * generic_make_request() that converts recursion to iteration to avoid
+ * stack overflow:
+ */
+ spinlock_t rescue_lock;
+ struct bio_list rescue_list;
+ struct work_struct rescue_work;
+ struct workqueue_struct *rescue_workqueue;
};
#define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */
@@ -1192,9 +1202,11 @@ extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatte
extern void blk_dump_rq_flags(struct request *, char *);
extern long nr_blockdev_pages(void);
+#define BLK_QUEUE_NO_RESCUER 1
+
bool __must_check blk_get_queue(struct request_queue *);
-struct request_queue *blk_alloc_queue(gfp_t);
-struct request_queue *blk_alloc_queue_node(gfp_t, int);
+struct request_queue *blk_alloc_queue(gfp_t, int);
+struct request_queue *blk_alloc_queue_node(gfp_t, int, int);
extern void blk_put_queue(struct request_queue *);
extern void blk_set_queue_dying(struct request_queue *);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index ad3ec9ec61..574ddc4f13 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1797,7 +1797,7 @@ struct task_struct {
void *journal_info;
/* stacked block device info */
- struct bio_list *bio_list;
+ struct bio_plug_list *bio_list;
#ifdef CONFIG_BLOCK
/* stack plugging */
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index c56fb57f29..07309d9610 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3440,6 +3440,12 @@ static inline void sched_submit_work(struct task_struct *tsk)
{
if (!tsk->state || tsk_is_pi_blocked(tsk))
return;
+
+ if (tsk->bio_list &&
+ !bio_list_empty(&tsk->bio_list->bios) &&
+ tsk->bio_list->q->rescue_workqueue)
+ blk_punt_blocked_bios(tsk->bio_list);
+
/*
* If we are going to sleep and we have plugged IO queued,
* make sure to submit it to avoid deadlocks.
--
2.11.0
[toc] | [prev] | [next] | [standalone]
| From | kbuild test robot <lkp@intel.com> |
|---|---|
| Date | 2017-02-08 07:30 +0100 |
| Subject | Re: [PATCH] block: Make rescuer threads per request_queue, not per bioset |
| Message-ID | <t8tVn-5al-9@gated-at.bofh.it> |
| In reply to | #1576277 |
[Multipart message — attachments visible in raw view] — view raw
Hi Kent,
[auto build test WARNING on linus/master]
[also build test WARNING on v4.10-rc7]
[cannot apply to next-20170207]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Kent-Overstreet/block-Make-rescuer-threads-per-request_queue-not-per-bioset/20170208-130414
config: x86_64-randconfig-x017-201706 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All warnings (new ones prefixed by >>):
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/uapi/linux/capability.h:16,
from include/linux/capability.h:15,
from include/linux/sched.h:15,
from include/linux/kasan.h:4,
from kernel/sched/core.c:29:
kernel/sched/core.c: In function 'sched_submit_work':
kernel/sched/core.c:3445:7: error: implicit declaration of function 'bio_list_empty' [-Werror=implicit-function-declaration]
!bio_list_empty(&tsk->bio_list->bios) &&
^
include/linux/compiler.h:149:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^~~~
>> kernel/sched/core.c:3444:2: note: in expansion of macro 'if'
if (tsk->bio_list &&
^~
kernel/sched/core.c:3445:36: error: dereferencing pointer to incomplete type 'struct bio_plug_list'
!bio_list_empty(&tsk->bio_list->bios) &&
^
include/linux/compiler.h:149:30: note: in definition of macro '__trace_if'
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \
^~~~
>> kernel/sched/core.c:3444:2: note: in expansion of macro 'if'
if (tsk->bio_list &&
^~
kernel/sched/core.c:3447:3: error: implicit declaration of function 'blk_punt_blocked_bios' [-Werror=implicit-function-declaration]
blk_punt_blocked_bios(tsk->bio_list);
^~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
vim +/if +3444 kernel/sched/core.c
3428
3429 /* causes final put_task_struct in finish_task_switch(). */
3430 __set_current_state(TASK_DEAD);
3431 current->flags |= PF_NOFREEZE; /* tell freezer to ignore us */
3432 __schedule(false);
3433 BUG();
3434 /* Avoid "noreturn function does return". */
3435 for (;;)
3436 cpu_relax(); /* For when BUG is null */
3437 }
3438
3439 static inline void sched_submit_work(struct task_struct *tsk)
3440 {
3441 if (!tsk->state || tsk_is_pi_blocked(tsk))
3442 return;
3443
> 3444 if (tsk->bio_list &&
3445 !bio_list_empty(&tsk->bio_list->bios) &&
3446 tsk->bio_list->q->rescue_workqueue)
3447 blk_punt_blocked_bios(tsk->bio_list);
3448
3449 /*
3450 * If we are going to sleep and we have plugged IO queued,
3451 * make sure to submit it to avoid deadlocks.
3452 */
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[toc] | [prev] | [next] | [standalone]
| From | kbuild test robot <lkp@intel.com> |
|---|---|
| Date | 2017-02-08 07:30 +0100 |
| Subject | Re: [PATCH] block: Make rescuer threads per request_queue, not per bioset |
| Message-ID | <t8tVo-5al-11@gated-at.bofh.it> |
| In reply to | #1576277 |
[Multipart message — attachments visible in raw view] — view raw
Hi Kent,
[auto build test ERROR on linus/master]
[also build test ERROR on v4.10-rc7]
[cannot apply to next-20170207]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Kent-Overstreet/block-Make-rescuer-threads-per-request_queue-not-per-bioset/20170208-130414
config: x86_64-randconfig-x014-201706 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
kernel/sched/core.c: In function 'sched_submit_work':
>> kernel/sched/core.c:3445:7: error: implicit declaration of function 'bio_list_empty' [-Werror=implicit-function-declaration]
!bio_list_empty(&tsk->bio_list->bios) &&
^~~~~~~~~~~~~~
>> kernel/sched/core.c:3445:36: error: dereferencing pointer to incomplete type 'struct bio_plug_list'
!bio_list_empty(&tsk->bio_list->bios) &&
^~
>> kernel/sched/core.c:3447:3: error: implicit declaration of function 'blk_punt_blocked_bios' [-Werror=implicit-function-declaration]
blk_punt_blocked_bios(tsk->bio_list);
^~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
vim +/bio_list_empty +3445 kernel/sched/core.c
3439 static inline void sched_submit_work(struct task_struct *tsk)
3440 {
3441 if (!tsk->state || tsk_is_pi_blocked(tsk))
3442 return;
3443
3444 if (tsk->bio_list &&
> 3445 !bio_list_empty(&tsk->bio_list->bios) &&
3446 tsk->bio_list->q->rescue_workqueue)
> 3447 blk_punt_blocked_bios(tsk->bio_list);
3448
3449 /*
3450 * If we are going to sleep and we have plugged IO queued,
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[toc] | [prev] | [next] | [standalone]
| From | Mike Galbraith <efault@gmx.de> |
|---|---|
| Date | 2017-02-08 08:00 +0100 |
| Subject | Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone |
| Message-ID | <t8uoq-5m1-5@gated-at.bofh.it> |
| In reply to | #1576277 |
On Tue, 2017-02-07 at 19:58 -0900, Kent Overstreet wrote: > On Tue, Feb 07, 2017 at 09:39:11PM +0100, Pavel Machek wrote: > > On Mon 2017-02-06 17:49:06, Kent Overstreet wrote: > > > On Mon, Feb 06, 2017 at 04:47:24PM -0900, Kent Overstreet wrote: > > > > On Mon, Feb 06, 2017 at 01:53:09PM +0100, Pavel Machek wrote: > > > > > Still there on v4.9, 36 threads on nokia n900 cellphone. > > > > > > > > > > So.. what needs to be done there? > > > > > > > But, I just got an idea for how to handle this that might be halfway sane, maybe > > > > I'll try and come up with a patch... > > > > > > Ok, here's such a patch, only lightly tested: > > > > I guess it would be nice for me to test it... but what it is against? > > I tried after v4.10-rc5 and linux-next, but got rejects in both cases. > > Sorry, I forgot I had a few other patches in my branch that touch > mempool/biosets code. > > Also, after thinking about it more and looking at the relevant code, I'm pretty > sure we don't need rescuer threads for block devices that just split bios - i.e. > most of them, so I changed my patch to do that. > > Tested it by ripping out the current->bio_list checks/workarounds from the > bcache code, appears to work: Patch killed every last one of them, but.. homer:/root # dmesg|grep WARNING [ 11.701447] WARNING: CPU: 4 PID: 801 at block/bio.c:388 bio_alloc_bioset+0x1a7/0x240 [ 11.711027] WARNING: CPU: 4 PID: 801 at block/blk-core.c:2013 generic_make_request+0x191/0x1f0 [ 19.728989] WARNING: CPU: 0 PID: 717 at block/bio.c:388 bio_alloc_bioset+0x1a7/0x240 [ 19.737020] WARNING: CPU: 0 PID: 717 at block/blk-core.c:2013 generic_make_request+0x191/0x1f0 [ 19.746173] WARNING: CPU: 0 PID: 717 at block/bio.c:388 bio_alloc_bioset+0x1a7/0x240 [ 19.755260] WARNING: CPU: 0 PID: 717 at block/blk-core.c:2013 generic_make_request+0x191/0x1f0 [ 19.763837] WARNING: CPU: 0 PID: 717 at block/bio.c:388 bio_alloc_bioset+0x1a7/0x240 [ 19.772526] WARNING: CPU: 0 PID: 717 at block/blk-core.c:2013 generic_make_request+0x191/0x1f0
[toc] | [prev] | [next] | [standalone]
| From | Mike Snitzer <snitzer@redhat.com> |
|---|---|
| Date | 2017-02-08 17:50 +0100 |
| Subject | Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone |
| Message-ID | <t8DBo-2Hv-15@gated-at.bofh.it> |
| In reply to | #1576277 |
On Tue, Feb 07 2017 at 11:58pm -0500, Kent Overstreet <kent.overstreet@gmail.com> wrote: > On Tue, Feb 07, 2017 at 09:39:11PM +0100, Pavel Machek wrote: > > On Mon 2017-02-06 17:49:06, Kent Overstreet wrote: > > > On Mon, Feb 06, 2017 at 04:47:24PM -0900, Kent Overstreet wrote: > > > > On Mon, Feb 06, 2017 at 01:53:09PM +0100, Pavel Machek wrote: > > > > > Still there on v4.9, 36 threads on nokia n900 cellphone. > > > > > > > > > > So.. what needs to be done there? > > > > > > > But, I just got an idea for how to handle this that might be halfway sane, maybe > > > > I'll try and come up with a patch... > > > > > > Ok, here's such a patch, only lightly tested: > > > > I guess it would be nice for me to test it... but what it is against? > > I tried after v4.10-rc5 and linux-next, but got rejects in both cases. > > Sorry, I forgot I had a few other patches in my branch that touch > mempool/biosets code. > > Also, after thinking about it more and looking at the relevant code, I'm pretty > sure we don't need rescuer threads for block devices that just split bios - i.e. > most of them, so I changed my patch to do that. > > Tested it by ripping out the current->bio_list checks/workarounds from the > bcache code, appears to work: Feedback on this patch below, but first: There are deeper issues with the current->bio_list and rescue workqueues than thread counts. I cannot help but feel like you (and Jens) are repeatedly ignoring the issue that has been raised numerous times, most recently: https://www.redhat.com/archives/dm-devel/2017-February/msg00059.html FYI, this test (albeit ugly) can be used to check if the dm-snapshot deadlock is fixed: https://www.redhat.com/archives/dm-devel/2017-January/msg00064.html This situation is the unfortunate pathological worst case for what happens when changes are merged and nobody wants to own fixing the unforseen implications/regressions. Like everyone else in a position of Linux maintenance I've tried to stay away from owning the responsibility of a fix -- it isn't working. Ok, I'll stop bitching now.. I do bear responsibility for not digging in myself. We're all busy and this issue is "hard". > -- >8 -- > Subject: [PATCH] block: Make rescuer threads per request_queue, not per bioset > > Also, trigger rescuing whenever with bios on current->bio_list, instead > of only when we block in bio_alloc_bioset(). This is more correct, and > should result in fewer rescuer threads. > > XXX: The current->bio_list plugging needs to be unified with the > blk_plug mechanism. > > Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com> > --- ... > diff --git a/drivers/md/dm.c b/drivers/md/dm.c > index 3086da5664..e1b22a68d9 100644 > --- a/drivers/md/dm.c > +++ b/drivers/md/dm.c > @@ -1490,7 +1490,7 @@ static struct mapped_device *alloc_dev(int minor) > INIT_LIST_HEAD(&md->table_devices); > spin_lock_init(&md->uevent_lock); > > - md->queue = blk_alloc_queue_node(GFP_KERNEL, numa_node_id); > + md->queue = blk_alloc_queue_node(GFP_KERNEL, numa_node_id, 0); > if (!md->queue) > goto bad; > This should be BLK_QUEUE_NO_RESCUER as DM isn't making direct use of bio_queue_split() for its own internal spliting (maybe it should and that'd start to fix the issue I've been harping about?) but as is DM destroys the rescuer workqueue (since commit dbba42d8a9eb "dm: eliminate unused "bioset" process for each bio-based DM device"). Mike
[toc] | [prev] | [next] | [standalone]
| From | Kent Overstreet <kent.overstreet@gmail.com> |
|---|---|
| Date | 2017-02-09 22:40 +0100 |
| Subject | Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone |
| Message-ID | <t94BA-2X8-19@gated-at.bofh.it> |
| In reply to | #1576709 |
On Wed, Feb 08, 2017 at 11:34:07AM -0500, Mike Snitzer wrote: > On Tue, Feb 07 2017 at 11:58pm -0500, > Kent Overstreet <kent.overstreet@gmail.com> wrote: > > > On Tue, Feb 07, 2017 at 09:39:11PM +0100, Pavel Machek wrote: > > > On Mon 2017-02-06 17:49:06, Kent Overstreet wrote: > > > > On Mon, Feb 06, 2017 at 04:47:24PM -0900, Kent Overstreet wrote: > > > > > On Mon, Feb 06, 2017 at 01:53:09PM +0100, Pavel Machek wrote: > > > > > > Still there on v4.9, 36 threads on nokia n900 cellphone. > > > > > > > > > > > > So.. what needs to be done there? > > > > > > > > > But, I just got an idea for how to handle this that might be halfway sane, maybe > > > > > I'll try and come up with a patch... > > > > > > > > Ok, here's such a patch, only lightly tested: > > > > > > I guess it would be nice for me to test it... but what it is against? > > > I tried after v4.10-rc5 and linux-next, but got rejects in both cases. > > > > Sorry, I forgot I had a few other patches in my branch that touch > > mempool/biosets code. > > > > Also, after thinking about it more and looking at the relevant code, I'm pretty > > sure we don't need rescuer threads for block devices that just split bios - i.e. > > most of them, so I changed my patch to do that. > > > > Tested it by ripping out the current->bio_list checks/workarounds from the > > bcache code, appears to work: > > Feedback on this patch below, but first: > > There are deeper issues with the current->bio_list and rescue workqueues > than thread counts. > > I cannot help but feel like you (and Jens) are repeatedly ignoring the > issue that has been raised numerous times, most recently: > https://www.redhat.com/archives/dm-devel/2017-February/msg00059.html > > FYI, this test (albeit ugly) can be used to check if the dm-snapshot > deadlock is fixed: > https://www.redhat.com/archives/dm-devel/2017-January/msg00064.html > > This situation is the unfortunate pathological worst case for what > happens when changes are merged and nobody wants to own fixing the > unforseen implications/regressions. Like everyone else in a position > of Linux maintenance I've tried to stay away from owning the > responsibility of a fix -- it isn't working. Ok, I'll stop bitching > now.. I do bear responsibility for not digging in myself. We're all > busy and this issue is "hard". Mike, it's not my job to debug DM code for you or sift through your bug reports. I don't read dm-devel, and I don't know why you think I that's my job. If there's something you think the block layer should be doing differently, post patches - or at the very least, explain what you'd like to be done, with words. Don't get pissy because I'm not sifting through your bug reports. Hell, I'm not getting paid to work on kernel code at all right now, and you trying to rope me into fixing device mapper sure makes me want to work on the block layer more. DM developers have a long history of working siloed off from the rest of the block layer, building up their own crazy infrastructure (remember the old bio splitting code?) and going to extreme lengths to avoid having to work on or improve the core block layer infrastructure. It's ridiculous. You know what would be nice? What'd really make my day is if just once I got a thank you or a bit of appreciation from DM developers for the bvec iterators/bio splitting work I did that cleaned up a _lot_ of crazy hairy messes. Or getting rid of merge_bvec_fn, or trying to come up with a better solution for deadlocks due to running under generic_make_request() now.
[toc] | [prev] | [next] | [standalone]
| From | Ming Lei <ming.lei@canonical.com> |
|---|---|
| Date | 2017-02-08 03:50 +0100 |
| Subject | Re: v4.9, 4.4-final: 28 bioset threads on small notebook, 36 threads on cellphone |
| Message-ID | <t8quu-2In-15@gated-at.bofh.it> |
| In reply to | #1575338 |
On Tue, Feb 7, 2017 at 10:49 AM, Kent Overstreet
<kent.overstreet@gmail.com> wrote:
> On Mon, Feb 06, 2017 at 04:47:24PM -0900, Kent Overstreet wrote:
>> On Mon, Feb 06, 2017 at 01:53:09PM +0100, Pavel Machek wrote:
>> > Still there on v4.9, 36 threads on nokia n900 cellphone.
>> >
>> > So.. what needs to be done there?
>
>> But, I just got an idea for how to handle this that might be halfway sane, maybe
>> I'll try and come up with a patch...
>
> Ok, here's such a patch, only lightly tested:
>
> -- >8 --
> Subject: [PATCH] block: Make rescuer threads per request_queue, not per bioset
>
> Note: this patch is very lightly tested.
>
> Also, trigger rescuing whenever with bios on current->bio_list, instead
> of only when we block in bio_alloc_bioset(). This is more correct, and
> should result in fewer rescuer threads.
Looks the rescuer stuff gets simplified much with this patch.
>
> XXX: The current->bio_list plugging needs to be unified with the
> blk_plug mechanism.
Yeah, that can be another benefit, :-)
>
> TODO: If we change normal request_queue drivers to handle arbitrary size
> bios by processing requests incrementally, instead of splitting bios,
> then we can get rid of rescuer threads from those devices.
Also the rescue threads are often from some reserved block devices, such as
loop/nbd, and we should have allowed these drivers to delay allocating
the thread
just before the disk is activated. Then the thread number can get descreased
a lot.
> ---
> block/bio.c | 107 ++++---------------------------------------------
> block/blk-core.c | 58 ++++++++++++++++++++++++---
> block/blk-sysfs.c | 2 +
> include/linux/bio.h | 16 ++++----
> include/linux/blkdev.h | 10 +++++
> include/linux/sched.h | 2 +-
> kernel/sched/core.c | 4 ++
> 7 files changed, 83 insertions(+), 116 deletions(-)
>
> diff --git a/block/bio.c b/block/bio.c
> index f3b5786202..9ad54a9b12 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -336,54 +336,6 @@ void bio_chain(struct bio *bio, struct bio *parent)
> }
> EXPORT_SYMBOL(bio_chain);
>
> -static void bio_alloc_rescue(struct work_struct *work)
> -{
> - struct bio_set *bs = container_of(work, struct bio_set, rescue_work);
> - struct bio *bio;
> -
> - while (1) {
> - spin_lock(&bs->rescue_lock);
> - bio = bio_list_pop(&bs->rescue_list);
> - spin_unlock(&bs->rescue_lock);
> -
> - if (!bio)
> - break;
> -
> - generic_make_request(bio);
> - }
> -}
> -
> -static void punt_bios_to_rescuer(struct bio_set *bs)
> -{
> - struct bio_list punt, nopunt;
> - struct bio *bio;
> -
> - /*
> - * In order to guarantee forward progress we must punt only bios that
> - * were allocated from this bio_set; otherwise, if there was a bio on
> - * there for a stacking driver higher up in the stack, processing it
> - * could require allocating bios from this bio_set, and doing that from
> - * our own rescuer would be bad.
> - *
> - * Since bio lists are singly linked, pop them all instead of trying to
> - * remove from the middle of the list:
> - */
> -
> - bio_list_init(&punt);
> - bio_list_init(&nopunt);
> -
> - while ((bio = bio_list_pop(current->bio_list)))
> - bio_list_add(bio->bi_pool == bs ? &punt : &nopunt, bio);
> -
> - *current->bio_list = nopunt;
> -
> - spin_lock(&bs->rescue_lock);
> - bio_list_merge(&bs->rescue_list, &punt);
> - spin_unlock(&bs->rescue_lock);
> -
> - queue_work(bs->rescue_workqueue, &bs->rescue_work);
> -}
> -
> /**
> * bio_alloc_bioset - allocate a bio for I/O
> * @gfp_mask: the GFP_ mask given to the slab allocator
> @@ -421,54 +373,27 @@ static void punt_bios_to_rescuer(struct bio_set *bs)
> */
> struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
> {
> - gfp_t saved_gfp = gfp_mask;
> unsigned front_pad;
> unsigned inline_vecs;
> struct bio_vec *bvl = NULL;
> struct bio *bio;
> void *p;
>
> - if (!bs) {
> - if (nr_iovecs > UIO_MAXIOV)
> - return NULL;
> + WARN(current->bio_list &&
> + !current->bio_list->q->rescue_workqueue,
> + "allocating bio beneath generic_make_request() without rescuer");
>
> + if (nr_iovecs > UIO_MAXIOV)
> + return NULL;
> +
> + if (!bs) {
> p = kmalloc(sizeof(struct bio) +
> nr_iovecs * sizeof(struct bio_vec),
> gfp_mask);
> front_pad = 0;
> inline_vecs = nr_iovecs;
> } else {
> - /*
> - * generic_make_request() converts recursion to iteration; this
> - * means if we're running beneath it, any bios we allocate and
> - * submit will not be submitted (and thus freed) until after we
> - * return.
> - *
> - * This exposes us to a potential deadlock if we allocate
> - * multiple bios from the same bio_set() while running
> - * underneath generic_make_request(). If we were to allocate
> - * multiple bios (say a stacking block driver that was splitting
> - * bios), we would deadlock if we exhausted the mempool's
> - * reserve.
> - *
> - * We solve this, and guarantee forward progress, with a rescuer
> - * workqueue per bio_set. If we go to allocate and there are
> - * bios on current->bio_list, we first try the allocation
> - * without __GFP_DIRECT_RECLAIM; if that fails, we punt those
> - * bios we would be blocking to the rescuer workqueue before
> - * we retry with the original gfp_flags.
> - */
> -
> - if (current->bio_list && !bio_list_empty(current->bio_list))
> - gfp_mask &= ~__GFP_DIRECT_RECLAIM;
> -
> p = mempool_alloc(&bs->bio_pool, gfp_mask);
> - if (!p && gfp_mask != saved_gfp) {
> - punt_bios_to_rescuer(bs);
> - gfp_mask = saved_gfp;
> - p = mempool_alloc(&bs->bio_pool, gfp_mask);
> - }
> -
> front_pad = bs->front_pad;
> inline_vecs = BIO_INLINE_VECS;
> }
> @@ -483,12 +408,6 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
> unsigned long idx = 0;
>
> bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, &bs->bvec_pool);
> - if (!bvl && gfp_mask != saved_gfp) {
> - punt_bios_to_rescuer(bs);
> - gfp_mask = saved_gfp;
> - bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, &bs->bvec_pool);
> - }
> -
> if (unlikely(!bvl))
> goto err_free;
>
> @@ -1938,10 +1857,6 @@ int biovec_init_pool(mempool_t *pool, int pool_entries)
>
> void bioset_exit(struct bio_set *bs)
> {
> - if (bs->rescue_workqueue)
> - destroy_workqueue(bs->rescue_workqueue);
> - bs->rescue_workqueue = NULL;
> -
> mempool_exit(&bs->bio_pool);
> mempool_exit(&bs->bvec_pool);
>
> @@ -1968,10 +1883,6 @@ static int __bioset_init(struct bio_set *bs,
>
> bs->front_pad = front_pad;
>
> - spin_lock_init(&bs->rescue_lock);
> - bio_list_init(&bs->rescue_list);
> - INIT_WORK(&bs->rescue_work, bio_alloc_rescue);
> -
> bs->bio_slab = bio_find_or_create_slab(front_pad + back_pad);
> if (!bs->bio_slab)
> return -ENOMEM;
> @@ -1983,10 +1894,6 @@ static int __bioset_init(struct bio_set *bs,
> biovec_init_pool(&bs->bvec_pool, pool_size))
> goto bad;
>
> - bs->rescue_workqueue = alloc_workqueue("bioset", WQ_MEM_RECLAIM, 0);
> - if (!bs->rescue_workqueue)
> - goto bad;
> -
> return 0;
> bad:
> bioset_exit(bs);
> diff --git a/block/blk-core.c b/block/blk-core.c
> index 7e3cfa9c88..f716164cb3 100644
> --- a/block/blk-core.c
> +++ b/block/blk-core.c
> @@ -48,6 +48,8 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(block_unplug);
>
> DEFINE_IDA(blk_queue_ida);
>
> +static void bio_rescue_work(struct work_struct *);
> +
> /*
> * For the allocated request tables
> */
> @@ -759,11 +761,21 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
> PERCPU_REF_INIT_ATOMIC, GFP_KERNEL))
> goto fail_bdi;
>
> - if (blkcg_init_queue(q))
> + spin_lock_init(&q->rescue_lock);
> + bio_list_init(&q->rescue_list);
> + INIT_WORK(&q->rescue_work, bio_rescue_work);
> +
> + q->rescue_workqueue = alloc_workqueue("bioset", WQ_MEM_RECLAIM, 0);
> + if (!q->rescue_workqueue)
> goto fail_ref;
>
> + if (blkcg_init_queue(q))
> + goto fail_rescue;
> +
> return q;
>
> +fail_rescue:
> + destroy_workqueue(q->rescue_workqueue);
> fail_ref:
> percpu_ref_exit(&q->q_usage_counter);
> fail_bdi:
> @@ -1994,7 +2006,7 @@ generic_make_request_checks(struct bio *bio)
> */
> blk_qc_t generic_make_request(struct bio *bio)
> {
> - struct bio_list bio_list_on_stack;
> + struct bio_plug_list bio_list_on_stack;
> blk_qc_t ret = BLK_QC_T_NONE;
>
> if (!generic_make_request_checks(bio))
> @@ -2011,7 +2023,9 @@ blk_qc_t generic_make_request(struct bio *bio)
> * should be added at the tail
> */
> if (current->bio_list) {
> - bio_list_add(current->bio_list, bio);
> + WARN(!current->bio_list->q->rescue_workqueue,
> + "submitting bio beneath generic_make_request() without rescuer");
> + bio_list_add(¤t->bio_list->bios, bio);
> goto out;
> }
>
> @@ -2030,19 +2044,23 @@ blk_qc_t generic_make_request(struct bio *bio)
> * bio_list, and call into ->make_request() again.
> */
> BUG_ON(bio->bi_next);
> - bio_list_init(&bio_list_on_stack);
> + bio_list_init(&bio_list_on_stack.bios);
> current->bio_list = &bio_list_on_stack;
> +
> do {
> struct request_queue *q = bdev_get_queue(bio->bi_bdev);
>
> + current->bio_list->q = q;
> +
> if (likely(blk_queue_enter(q, false) == 0)) {
> ret = q->make_request_fn(q, bio);
>
> blk_queue_exit(q);
>
> - bio = bio_list_pop(current->bio_list);
> + bio = bio_list_pop(¤t->bio_list->bios);
> } else {
> - struct bio *bio_next = bio_list_pop(current->bio_list);
> + struct bio *bio_next =
> + bio_list_pop(¤t->bio_list->bios);
>
> bio_io_error(bio);
> bio = bio_next;
> @@ -2055,6 +2073,34 @@ blk_qc_t generic_make_request(struct bio *bio)
> }
> EXPORT_SYMBOL(generic_make_request);
>
> +static void bio_rescue_work(struct work_struct *work)
> +{
> + struct request_queue *q =
> + container_of(work, struct request_queue, rescue_work);
> + struct bio *bio;
> +
> + while (1) {
> + spin_lock(&q->rescue_lock);
> + bio = bio_list_pop(&q->rescue_list);
> + spin_unlock(&q->rescue_lock);
> +
> + if (!bio)
> + break;
> +
> + generic_make_request(bio);
> + }
> +}
> +
> +void blk_punt_blocked_bios(struct bio_plug_list *list)
> +{
> + spin_lock(&list->q->rescue_lock);
> + bio_list_merge(&list->q->rescue_list, &list->bios);
> + bio_list_init(&list->bios);
> + spin_unlock(&list->q->rescue_lock);
> +
> + queue_work(list->q->rescue_workqueue, &list->q->rescue_work);
> +}
I guess we may need to move the bios into its own queue's rescue list,
otherwise it still may deadlock in the following situation:
for example of md:
generic_make_request(bio_m0, md_q)
->.make_request(md_q)
->bio_l = mempool_alloc(md_pool)
->bio_l->q = ll_q
->generic_make_request(bio_l, ll_q)
....
mempool_alloc reclaim is triggered when allocate a new bio for low level:
-> suppose current bio_list has bio_m1, bio_l0, bio_l1,
-> all are queued into md_q's rescue_list, and handled in md_q's rescue
wq context
-> bio_m1 is handled first, and mempool_alloc() reclaimed too
-> when bio_l0 is handled in generic_make_request(), the rescue_list
is switched to ll_q's(this rescue list can be empty), then
nothing can move on
> +
> /**
> * submit_bio - submit a bio to the block device layer for I/O
> * @bio: The &struct bio which describes the I/O
> diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
> index 7f27a18cc4..77529238d1 100644
> --- a/block/blk-sysfs.c
> +++ b/block/blk-sysfs.c
> @@ -660,6 +660,8 @@ static void blk_release_queue(struct kobject *kobj)
>
> blk_trace_shutdown(q);
>
> + if (q->rescue_workqueue)
> + destroy_workqueue(q->rescue_workqueue);
> if (q->bio_split)
> bioset_free(q->bio_split);
>
> diff --git a/include/linux/bio.h b/include/linux/bio.h
> index 1ffe8e37ae..87eeec7eda 100644
> --- a/include/linux/bio.h
> +++ b/include/linux/bio.h
> @@ -658,6 +658,13 @@ static inline struct bio *bio_list_get(struct bio_list *bl)
> return bio;
> }
>
> +struct bio_plug_list {
> + struct bio_list bios;
> + struct request_queue *q;
> +};
> +
> +void blk_punt_blocked_bios(struct bio_plug_list *);
> +
> /*
> * Increment chain count for the bio. Make sure the CHAIN flag update
> * is visible before the raised count.
> @@ -687,15 +694,6 @@ struct bio_set {
> mempool_t bio_integrity_pool;
> mempool_t bvec_integrity_pool;
> #endif
> -
> - /*
> - * Deadlock avoidance for stacking block drivers: see comments in
> - * bio_alloc_bioset() for details
> - */
> - spinlock_t rescue_lock;
> - struct bio_list rescue_list;
> - struct work_struct rescue_work;
> - struct workqueue_struct *rescue_workqueue;
> };
>
> struct biovec_slab {
> diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
> index c47c358ba0..f64b886c65 100644
> --- a/include/linux/blkdev.h
> +++ b/include/linux/blkdev.h
> @@ -476,6 +476,16 @@ struct request_queue {
> struct bio_set *bio_split;
>
> bool mq_sysfs_init_done;
> +
> + /*
> + * Deadlock avoidance, to deal with the plugging in
> + * generic_make_request() that converts recursion to iteration to avoid
> + * stack overflow:
> + */
> + spinlock_t rescue_lock;
> + struct bio_list rescue_list;
> + struct work_struct rescue_work;
> + struct workqueue_struct *rescue_workqueue;
> };
>
> #define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */
> diff --git a/include/linux/sched.h b/include/linux/sched.h
> index 2865d10a28..59df7a1030 100644
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -1762,7 +1762,7 @@ struct task_struct {
> void *journal_info;
>
> /* stacked block device info */
> - struct bio_list *bio_list;
> + struct bio_plug_list *bio_list;
>
> #ifdef CONFIG_BLOCK
> /* stack plugging */
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index bd39d698cb..23b6290ba1 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -3439,6 +3439,10 @@ static inline void sched_submit_work(struct task_struct *tsk)
> {
> if (!tsk->state || tsk_is_pi_blocked(tsk))
> return;
> +
> + if (tsk->bio_list && !bio_list_empty(&tsk->bio_list->bios))
> + blk_punt_blocked_bios(tsk->bio_list);
> +
> /*
> * If we are going to sleep and we have plugged IO queued,
> * make sure to submit it to avoid deadlocks.
> --
> 2.11.0
>
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web