block: refator submit_bio_noacct
Split out a __submit_bio_noacct helper for the actual de-recursion algorithm, and simplify the loop by using a continue when we can't enter the queue for a bio. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
ed00aabd5e
commit
566acf2daa
143
block/blk-core.c
143
block/blk-core.c
@ -1084,6 +1084,74 @@ static blk_qc_t __submit_bio(struct bio *bio)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The loop in this function may be a bit non-obvious, and so deserves some
|
||||||
|
* explanation:
|
||||||
|
*
|
||||||
|
* - Before entering the loop, bio->bi_next is NULL (as all callers ensure
|
||||||
|
* that), so we have a list with a single bio.
|
||||||
|
* - We pretend that we have just taken it off a longer list, so we assign
|
||||||
|
* bio_list to a pointer to the bio_list_on_stack, thus initialising the
|
||||||
|
* bio_list of new bios to be added. ->submit_bio() may indeed add some more
|
||||||
|
* bios through a recursive call to submit_bio_noacct. If it did, we find a
|
||||||
|
* non-NULL value in bio_list and re-enter the loop from the top.
|
||||||
|
* - In this case we really did just take the bio of the top of the list (no
|
||||||
|
* pretending) and so remove it from bio_list, and call into ->submit_bio()
|
||||||
|
* again.
|
||||||
|
*
|
||||||
|
* bio_list_on_stack[0] contains bios submitted by the current ->submit_bio.
|
||||||
|
* bio_list_on_stack[1] contains bios that were submitted before the current
|
||||||
|
* ->submit_bio_bio, but that haven't been processed yet.
|
||||||
|
*/
|
||||||
|
static blk_qc_t __submit_bio_noacct(struct bio *bio)
|
||||||
|
{
|
||||||
|
struct bio_list bio_list_on_stack[2];
|
||||||
|
blk_qc_t ret = BLK_QC_T_NONE;
|
||||||
|
|
||||||
|
BUG_ON(bio->bi_next);
|
||||||
|
|
||||||
|
bio_list_init(&bio_list_on_stack[0]);
|
||||||
|
current->bio_list = bio_list_on_stack;
|
||||||
|
|
||||||
|
do {
|
||||||
|
struct request_queue *q = bio->bi_disk->queue;
|
||||||
|
struct bio_list lower, same;
|
||||||
|
|
||||||
|
if (unlikely(bio_queue_enter(bio) != 0))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a fresh bio_list for all subordinate requests.
|
||||||
|
*/
|
||||||
|
bio_list_on_stack[1] = bio_list_on_stack[0];
|
||||||
|
bio_list_init(&bio_list_on_stack[0]);
|
||||||
|
|
||||||
|
ret = __submit_bio(bio);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sort new bios into those for a lower level and those for the
|
||||||
|
* same level.
|
||||||
|
*/
|
||||||
|
bio_list_init(&lower);
|
||||||
|
bio_list_init(&same);
|
||||||
|
while ((bio = bio_list_pop(&bio_list_on_stack[0])) != NULL)
|
||||||
|
if (q == bio->bi_disk->queue)
|
||||||
|
bio_list_add(&same, bio);
|
||||||
|
else
|
||||||
|
bio_list_add(&lower, bio);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now assemble so we handle the lowest level first.
|
||||||
|
*/
|
||||||
|
bio_list_merge(&bio_list_on_stack[0], &lower);
|
||||||
|
bio_list_merge(&bio_list_on_stack[0], &same);
|
||||||
|
bio_list_merge(&bio_list_on_stack[0], &bio_list_on_stack[1]);
|
||||||
|
} while ((bio = bio_list_pop(&bio_list_on_stack[0])));
|
||||||
|
|
||||||
|
current->bio_list = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* submit_bio_noacct - re-submit a bio to the block device layer for I/O
|
* submit_bio_noacct - re-submit a bio to the block device layer for I/O
|
||||||
* @bio: The bio describing the location in memory and on the device.
|
* @bio: The bio describing the location in memory and on the device.
|
||||||
@ -1095,82 +1163,21 @@ static blk_qc_t __submit_bio(struct bio *bio)
|
|||||||
*/
|
*/
|
||||||
blk_qc_t submit_bio_noacct(struct bio *bio)
|
blk_qc_t submit_bio_noacct(struct bio *bio)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* bio_list_on_stack[0] contains bios submitted by the current
|
|
||||||
* ->submit_bio.
|
|
||||||
* bio_list_on_stack[1] contains bios that were submitted before the
|
|
||||||
* current ->submit_bio_bio, but that haven't been processed yet.
|
|
||||||
*/
|
|
||||||
struct bio_list bio_list_on_stack[2];
|
|
||||||
blk_qc_t ret = BLK_QC_T_NONE;
|
|
||||||
|
|
||||||
if (!submit_bio_checks(bio))
|
if (!submit_bio_checks(bio))
|
||||||
goto out;
|
return BLK_QC_T_NONE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We only want one ->submit_bio to be active at a time, else
|
* We only want one ->submit_bio to be active at a time, else stack
|
||||||
* stack usage with stacked devices could be a problem. So use
|
* usage with stacked devices could be a problem. Use current->bio_list
|
||||||
* current->bio_list to keep a list of requests submited by a
|
* to collect a list of requests submited by a ->submit_bio method while
|
||||||
* ->submit_bio method. current->bio_list is also used as a
|
* it is active, and then process them after it returned.
|
||||||
* flag to say if submit_bio_noacct is currently active in this
|
|
||||||
* task or not. If it is NULL, then no make_request is active. If
|
|
||||||
* it is non-NULL, then a make_request is active, and new requests
|
|
||||||
* should be added at the tail
|
|
||||||
*/
|
*/
|
||||||
if (current->bio_list) {
|
if (current->bio_list) {
|
||||||
bio_list_add(¤t->bio_list[0], bio);
|
bio_list_add(¤t->bio_list[0], bio);
|
||||||
goto out;
|
return BLK_QC_T_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* following loop may be a bit non-obvious, and so deserves some
|
return __submit_bio_noacct(bio);
|
||||||
* explanation.
|
|
||||||
* Before entering the loop, bio->bi_next is NULL (as all callers
|
|
||||||
* ensure that) so we have a list with a single bio.
|
|
||||||
* We pretend that we have just taken it off a longer list, so
|
|
||||||
* we assign bio_list to a pointer to the bio_list_on_stack,
|
|
||||||
* thus initialising the bio_list of new bios to be
|
|
||||||
* added. ->submit_bio() may indeed add some more bios
|
|
||||||
* through a recursive call to submit_bio_noacct. If it
|
|
||||||
* did, we find a non-NULL value in bio_list and re-enter the loop
|
|
||||||
* from the top. In this case we really did just take the bio
|
|
||||||
* of the top of the list (no pretending) and so remove it from
|
|
||||||
* bio_list, and call into ->submit_bio() again.
|
|
||||||
*/
|
|
||||||
BUG_ON(bio->bi_next);
|
|
||||||
bio_list_init(&bio_list_on_stack[0]);
|
|
||||||
current->bio_list = bio_list_on_stack;
|
|
||||||
do {
|
|
||||||
struct request_queue *q = bio->bi_disk->queue;
|
|
||||||
|
|
||||||
if (likely(bio_queue_enter(bio) == 0)) {
|
|
||||||
struct bio_list lower, same;
|
|
||||||
|
|
||||||
/* Create a fresh bio_list for all subordinate requests */
|
|
||||||
bio_list_on_stack[1] = bio_list_on_stack[0];
|
|
||||||
bio_list_init(&bio_list_on_stack[0]);
|
|
||||||
ret = __submit_bio(bio);
|
|
||||||
|
|
||||||
/* sort new bios into those for a lower level
|
|
||||||
* and those for the same level
|
|
||||||
*/
|
|
||||||
bio_list_init(&lower);
|
|
||||||
bio_list_init(&same);
|
|
||||||
while ((bio = bio_list_pop(&bio_list_on_stack[0])) != NULL)
|
|
||||||
if (q == bio->bi_disk->queue)
|
|
||||||
bio_list_add(&same, bio);
|
|
||||||
else
|
|
||||||
bio_list_add(&lower, bio);
|
|
||||||
/* now assemble so we handle the lowest level first */
|
|
||||||
bio_list_merge(&bio_list_on_stack[0], &lower);
|
|
||||||
bio_list_merge(&bio_list_on_stack[0], &same);
|
|
||||||
bio_list_merge(&bio_list_on_stack[0], &bio_list_on_stack[1]);
|
|
||||||
}
|
|
||||||
bio = bio_list_pop(&bio_list_on_stack[0]);
|
|
||||||
} while (bio);
|
|
||||||
current->bio_list = NULL; /* deactivate */
|
|
||||||
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(submit_bio_noacct);
|
EXPORT_SYMBOL(submit_bio_noacct);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user