2018-11-15 19:00:28

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 00/19] block: support multi-page bvec

Hi,

This patchset brings multi-page bvec into block layer:

1) what is multi-page bvec?

Multipage bvecs means that one 'struct bio_bvec' can hold multiple pages
which are physically contiguous instead of one single page used in linux
kernel for long time.

2) why is multi-page bvec introduced?

Kent proposed the idea[1] first.

As system's RAM becomes much bigger than before, and huge page, transparent
huge page and memory compaction are widely used, it is a bit easy now
to see physically contiguous pages from fs in I/O. On the other hand, from
block layer's view, it isn't necessary to store intermediate pages into bvec,
and it is enough to just store the physicallly contiguous 'segment' in each
io vector.

Also huge pages are being brought to filesystem and swap [2][6], we can
do IO on a hugepage each time[3], which requires that one bio can transfer
at least one huge page one time. Turns out it isn't flexiable to change
BIO_MAX_PAGES simply[3][5]. Multipage bvec can fit in this case very well.
As we saw, if CONFIG_THP_SWAP is enabled, BIO_MAX_PAGES can be configured
as much bigger, such as 512, which requires at least two 4K pages for holding
the bvec table.

With multi-page bvec:

- Inside block layer, both bio splitting and sg map can become more
efficient than before by just traversing the physically contiguous
'segment' instead of each page.

- segment handling in block layer can be improved much in future since it
should be quite easy to convert multipage bvec into segment easily. For
example, we might just store segment in each bvec directly in future.

- bio size can be increased and it should improve some high-bandwidth IO
case in theory[4].

- there is opportunity in future to improve memory footprint of bvecs.

3) how is multi-page bvec implemented in this patchset?

The patches of 1 ~ 14 implement multipage bvec in block layer:

- put all tricks into bvec/bio/rq iterators, and as far as
drivers and fs use these standard iterators, they are happy
with multipage bvec

- introduce bio_for_each_bvec() to iterate over multipage bvec for splitting
bio and mapping sg

- keep current bio_for_each_segment*() to itereate over singlepage bvec and
make sure current users won't be broken; especailly, convert to this
new helper prototype in single patch 21 given it is bascially a mechanism
conversion

- deal with iomap & xfs's sub-pagesize io vec in patch 13

- enalbe multipage bvec in patch 14

Patch 15 redefines BIO_MAX_PAGES as 256.

Patch 16 documents usages of bio iterator helpers.

Patch 17~19 kills NO_SG_MERGE.

These patches can be found in the following git tree:

git: https://github.com/ming1/linux.git for-4.21-block-mp-bvec-V10

Lots of test(blktest, xfstests, ltp io, ...) have been run with this patchset,
and not see regression.

Thanks Christoph for reviewing the early version and providing very good
suggestions, such as: introduce bio_init_with_vec_table(), remove another
unnecessary helpers for cleanup and so on.

Any comments are welcome!

V10:
- no any code change, just add more guys and list into patch's CC list,
as suggested by Christoph and Dave Chinner
V9:
- fix regression on iomap's sub-pagesize io vec, covered by patch 13
V8:
- remove prepare patches which all are merged to linus tree
- rebase on for-4.21/block
- address comments on V7
- add patches of killing NO_SG_MERGE

V7:
- include Christoph and Mike's bio_clone_bioset() patches, which is
actually prepare patches for multipage bvec
- address Christoph's comments

V6:
- avoid to introduce lots of renaming, follow Jen's suggestion of
using the name of chunk for multipage io vector
- include Christoph's three prepare patches
- decrease stack usage for using bio_for_each_chunk_segment_all()
- address Kent's comment

V5:
- remove some of prepare patches, which have been merged already
- add bio_clone_seg_bioset() to fix DM's bio clone, which
is introduced by 18a25da84354c6b (dm: ensure bio submission follows
a depth-first tree walk)
- rebase on the latest block for-v4.18

V4:
- rename bio_for_each_segment*() as bio_for_each_page*(), rename
bio_segments() as bio_pages(), rename rq_for_each_segment() as
rq_for_each_pages(), because these helpers never return real
segment, and they always return single page bvec

- introducing segment_for_each_page_all()

- introduce new bio_for_each_segment*()/rq_for_each_segment()/bio_segments()
for returning real multipage segment

- rewrite segment_last_page()

- rename bvec iterator helper as suggested by Christoph

- replace comment with applying bio helpers as suggested by Christoph

- document usage of bio iterator helpers

- redefine BIO_MAX_PAGES as 256 to make the biggest bvec table
accommodated in 4K page

- move bio_alloc_pages() into bcache as suggested by Christoph

V3:
- rebase on v4.13-rc3 with for-next of block tree
- run more xfstests: xfs/ext4 over NVMe, Sata, DM(linear),
MD(raid1), and not see regressions triggered
- add Reviewed-by on some btrfs patches
- remove two MD patches because both are merged to linus tree
already

V2:
- bvec table direct access in raid has been cleaned, so NO_MP
flag is dropped
- rebase on recent Neil Brown's change on bio and bounce code
- reorganize the patchset

V1:
- against v4.10-rc1 and some cleanup in V0 are in -linus already
- handle queue_virt_boundary() in mp bvec change and make NVMe happy
- further BTRFS cleanup
- remove QUEUE_FLAG_SPLIT_MP
- rename for two new helpers of bio_for_each_segment_all()
- fix bounce convertion
- address comments in V0

[1], http://marc.info/?l=linux-kernel&m=141680246629547&w=2
[2], https://patchwork.kernel.org/patch/9451523/
[3], http://marc.info/?t=147735447100001&r=1&w=2
[4], http://marc.info/?l=linux-mm&m=147745525801433&w=2
[5], http://marc.info/?t=149569484500007&r=1&w=2
[6], http://marc.info/?t=149820215300004&r=1&w=2


Ming Lei (19):
block: introduce multi-page page bvec helpers
block: introduce bio_for_each_bvec()
block: use bio_for_each_bvec() to compute multi-page bvec count
block: use bio_for_each_bvec() to map sg
block: introduce bvec_last_segment()
fs/buffer.c: use bvec iterator to truncate the bio
btrfs: use bvec_last_segment to get bio's last page
btrfs: move bio_pages_all() to btrfs
block: introduce bio_bvecs()
block: loop: pass multi-page bvec to iov_iter
bcache: avoid to use bio_for_each_segment_all() in
bch_bio_alloc_pages()
block: allow bio_for_each_segment_all() to iterate over multi-page
bvec
iomap & xfs: only account for new added page
block: enable multipage bvecs
block: always define BIO_MAX_PAGES as 256
block: document usage of bio iterator helpers
block: don't use bio->bi_vcnt to figure out segment number
block: kill QUEUE_FLAG_NO_SG_MERGE
block: kill BLK_MQ_F_SG_MERGE

Documentation/block/biovecs.txt | 26 +++++
block/bio.c | 51 +++++++---
block/blk-merge.c | 199 +++++++++++++++++++++++++-------------
block/blk-mq-debugfs.c | 2 -
block/blk-mq.c | 3 -
block/blk-zoned.c | 1 +
block/bounce.c | 6 +-
drivers/block/loop.c | 25 ++---
drivers/block/nbd.c | 2 +-
drivers/block/rbd.c | 2 +-
drivers/block/skd_main.c | 1 -
drivers/block/xen-blkfront.c | 2 +-
drivers/md/bcache/btree.c | 3 +-
drivers/md/bcache/util.c | 2 +-
drivers/md/dm-crypt.c | 3 +-
drivers/md/dm-rq.c | 2 +-
drivers/md/dm-table.c | 13 ---
drivers/md/raid1.c | 3 +-
drivers/mmc/core/queue.c | 3 +-
drivers/scsi/scsi_lib.c | 2 +-
drivers/staging/erofs/data.c | 3 +-
drivers/staging/erofs/unzip_vle.c | 3 +-
fs/block_dev.c | 6 +-
fs/btrfs/compression.c | 8 +-
fs/btrfs/disk-io.c | 3 +-
fs/btrfs/extent_io.c | 29 ++++--
fs/btrfs/inode.c | 6 +-
fs/btrfs/raid56.c | 3 +-
fs/buffer.c | 5 +-
fs/crypto/bio.c | 3 +-
fs/direct-io.c | 4 +-
fs/exofs/ore.c | 3 +-
fs/exofs/ore_raid.c | 3 +-
fs/ext4/page-io.c | 3 +-
fs/ext4/readpage.c | 3 +-
fs/f2fs/data.c | 9 +-
fs/gfs2/lops.c | 6 +-
fs/gfs2/meta_io.c | 3 +-
fs/iomap.c | 28 ++++--
fs/mpage.c | 3 +-
fs/xfs/xfs_aops.c | 15 ++-
include/linux/bio.h | 94 ++++++++++++++----
include/linux/blk-mq.h | 1 -
include/linux/blkdev.h | 1 -
include/linux/bvec.h | 155 +++++++++++++++++++++++++++--
45 files changed, 556 insertions(+), 195 deletions(-)

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]


--
2.9.5


2018-11-19 18:42:44

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 08/19] btrfs: move bio_pages_all() to btrfs

On Fri, Nov 16, 2018 at 02:38:45PM +0100, Christoph Hellwig wrote:
> On Thu, Nov 15, 2018 at 04:52:55PM +0800, Ming Lei wrote:
> > BTRFS is the only user of this helper, so move this helper into
> > BTRFS, and implement it via bio_for_each_segment_all(), since
> > bio->bi_vcnt may not equal to number of pages after multipage bvec
> > is enabled.
>
> btrfs only uses the value to check if it is larger than 1. No amount
> of multipage bio merging should ever make bi_vcnt go from 0 to 1 or
> vice versa.

Could you explain a bit why?

Suppose 2 physically continuous pages are added to this bio, .bi_vcnt
can be 1 in case of multi-page bvec, but it is 2 in case of single-page
bvec.

Thanks,
Ming

2018-11-15 19:01:19

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 04/19] block: use bio_for_each_bvec() to map sg

It is more efficient to use bio_for_each_bvec() to map sg, meantime
we have to consider splitting multipage bvec as done in blk_bio_segment_split().

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
block/blk-merge.c | 72 +++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 52 insertions(+), 20 deletions(-)

diff --git a/block/blk-merge.c b/block/blk-merge.c
index 6f7deb94a23f..cb9f49bcfd36 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -473,6 +473,56 @@ static int blk_phys_contig_segment(struct request_queue *q, struct bio *bio,
return biovec_phys_mergeable(q, &end_bv, &nxt_bv);
}

+static struct scatterlist *blk_next_sg(struct scatterlist **sg,
+ struct scatterlist *sglist)
+{
+ if (!*sg)
+ return sglist;
+ else {
+ /*
+ * If the driver previously mapped a shorter
+ * list, we could see a termination bit
+ * prematurely unless it fully inits the sg
+ * table on each mapping. We KNOW that there
+ * must be more entries here or the driver
+ * would be buggy, so force clear the
+ * termination bit to avoid doing a full
+ * sg_init_table() in drivers for each command.
+ */
+ sg_unmark_end(*sg);
+ return sg_next(*sg);
+ }
+}
+
+static unsigned blk_bvec_map_sg(struct request_queue *q,
+ struct bio_vec *bvec, struct scatterlist *sglist,
+ struct scatterlist **sg)
+{
+ unsigned nbytes = bvec->bv_len;
+ unsigned nsegs = 0, total = 0;
+
+ while (nbytes > 0) {
+ unsigned seg_size;
+ struct page *pg;
+ unsigned offset, idx;
+
+ *sg = blk_next_sg(sg, sglist);
+
+ seg_size = min(nbytes, queue_max_segment_size(q));
+ offset = (total + bvec->bv_offset) % PAGE_SIZE;
+ idx = (total + bvec->bv_offset) / PAGE_SIZE;
+ pg = nth_page(bvec->bv_page, idx);
+
+ sg_set_page(*sg, pg, seg_size, offset);
+
+ total += seg_size;
+ nbytes -= seg_size;
+ nsegs++;
+ }
+
+ return nsegs;
+}
+
static inline void
__blk_segment_map_sg(struct request_queue *q, struct bio_vec *bvec,
struct scatterlist *sglist, struct bio_vec *bvprv,
@@ -490,25 +540,7 @@ __blk_segment_map_sg(struct request_queue *q, struct bio_vec *bvec,
(*sg)->length += nbytes;
} else {
new_segment:
- if (!*sg)
- *sg = sglist;
- else {
- /*
- * If the driver previously mapped a shorter
- * list, we could see a termination bit
- * prematurely unless it fully inits the sg
- * table on each mapping. We KNOW that there
- * must be more entries here or the driver
- * would be buggy, so force clear the
- * termination bit to avoid doing a full
- * sg_init_table() in drivers for each command.
- */
- sg_unmark_end(*sg);
- *sg = sg_next(*sg);
- }
-
- sg_set_page(*sg, bvec->bv_page, nbytes, bvec->bv_offset);
- (*nsegs)++;
+ (*nsegs) += blk_bvec_map_sg(q, bvec, sglist, sg);
}
*bvprv = *bvec;
}
@@ -530,7 +562,7 @@ static int __blk_bios_map_sg(struct request_queue *q, struct bio *bio,
int cluster = blk_queue_cluster(q), nsegs = 0;

for_each_bio(bio)
- bio_for_each_segment(bvec, bio, iter)
+ bio_for_each_bvec(bvec, bio, iter)
__blk_segment_map_sg(q, &bvec, sglist, &bvprv, sg,
&nsegs, &cluster);

--
2.9.5

2018-11-16 11:33:06

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 12/19] block: allow bio_for_each_segment_all() to iterate over multi-page bvec

On Thu, Nov 15, 2018 at 04:52:59PM +0800, Ming Lei wrote:
> This patch introduces one extra iterator variable to bio_for_each_segment_all(),
> then we can allow bio_for_each_segment_all() to iterate over multi-page bvec.
>
> Given it is just one mechannical & simple change on all bio_for_each_segment_all()
> users, this patch does tree-wide change in one single patch, so that we can
> avoid to use a temporary helper for this conversion.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: Darrick J. Wong <[email protected]>
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]
> Signed-off-by: Ming Lei <[email protected]>
> ---
> block/bio.c | 27 ++++++++++++++++++---------
> block/blk-zoned.c | 1 +
> block/bounce.c | 6 ++++--
> drivers/md/bcache/btree.c | 3 ++-
> drivers/md/dm-crypt.c | 3 ++-
> drivers/md/raid1.c | 3 ++-
> drivers/staging/erofs/data.c | 3 ++-
> drivers/staging/erofs/unzip_vle.c | 3 ++-
> fs/block_dev.c | 6 ++++--
> fs/btrfs/compression.c | 3 ++-
> fs/btrfs/disk-io.c | 3 ++-
> fs/btrfs/extent_io.c | 12 ++++++++----
> fs/btrfs/inode.c | 6 ++++--
> fs/btrfs/raid56.c | 3 ++-
> fs/crypto/bio.c | 3 ++-
> fs/direct-io.c | 4 +++-
> fs/exofs/ore.c | 3 ++-
> fs/exofs/ore_raid.c | 3 ++-
> fs/ext4/page-io.c | 3 ++-
> fs/ext4/readpage.c | 3 ++-
> fs/f2fs/data.c | 9 ++++++---
> fs/gfs2/lops.c | 6 ++++--
> fs/gfs2/meta_io.c | 3 ++-
> fs/iomap.c | 6 ++++--
> fs/mpage.c | 3 ++-
> fs/xfs/xfs_aops.c | 5 +++--
> include/linux/bio.h | 11 +++++++++--
> include/linux/bvec.h | 31 +++++++++++++++++++++++++++++++
> 28 files changed, 129 insertions(+), 46 deletions(-)
>

[snip]

> diff --git a/include/linux/bio.h b/include/linux/bio.h
> index 3496c816946e..1a2430a8b89d 100644
> --- a/include/linux/bio.h
> +++ b/include/linux/bio.h
> @@ -131,12 +131,19 @@ static inline bool bio_full(struct bio *bio)
> return bio->bi_vcnt >= bio->bi_max_vecs;
> }
>
> +#define bvec_for_each_segment(bv, bvl, i, iter_all) \
> + for (bv = bvec_init_iter_all(&iter_all); \
> + (iter_all.done < (bvl)->bv_len) && \
> + ((bvec_next_segment((bvl), &iter_all)), 1); \

The parentheses around (bvec_next_segment((bvl), &iter_all)) are
unnecessary.

> + iter_all.done += bv->bv_len, i += 1)
> +
> /*
> * drivers should _never_ use the all version - the bio may have been split
> * before it got to the driver and the driver won't own all of it
> */
> -#define bio_for_each_segment_all(bvl, bio, i) \
> - for (i = 0, bvl = (bio)->bi_io_vec; i < (bio)->bi_vcnt; i++, bvl++)
> +#define bio_for_each_segment_all(bvl, bio, i, iter_all) \
> + for (i = 0, iter_all.idx = 0; iter_all.idx < (bio)->bi_vcnt; iter_all.idx++) \
> + bvec_for_each_segment(bvl, &((bio)->bi_io_vec[iter_all.idx]), i, iter_all)

Would it be possible to move i into iter_all to streamline this a bit?

> static inline void __bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
> unsigned bytes, bool mp)
> diff --git a/include/linux/bvec.h b/include/linux/bvec.h
> index 01616a0b6220..02f26d2b59ad 100644
> --- a/include/linux/bvec.h
> +++ b/include/linux/bvec.h
> @@ -82,6 +82,12 @@ struct bvec_iter {
> current bvec */
> };
>
> +struct bvec_iter_all {
> + struct bio_vec bv;
> + int idx;
> + unsigned done;
> +};
> +
> /*
> * various member access, note that bio_data should of course not be used
> * on highmem page vectors
> @@ -216,6 +222,31 @@ static inline bool mp_bvec_iter_advance(const struct bio_vec *bv,
> .bi_bvec_done = 0, \
> }
>
> +static inline struct bio_vec *bvec_init_iter_all(struct bvec_iter_all *iter_all)
> +{
> + iter_all->bv.bv_page = NULL;
> + iter_all->done = 0;
> +
> + return &iter_all->bv;
> +}
> +
> +/* used for chunk_for_each_segment */
> +static inline void bvec_next_segment(const struct bio_vec *bvec,
> + struct bvec_iter_all *iter_all)

Indentation.

> +{
> + struct bio_vec *bv = &iter_all->bv;
> +
> + if (bv->bv_page) {
> + bv->bv_page += 1;
> + bv->bv_offset = 0;
> + } else {
> + bv->bv_page = bvec->bv_page;
> + bv->bv_offset = bvec->bv_offset;
> + }
> + bv->bv_len = min_t(unsigned int, PAGE_SIZE - bv->bv_offset,
> + bvec->bv_len - iter_all->done);

Indentation.

> +}
> +
> /*
> * Get the last singlepage segment from the multipage bvec and store it
> * in @seg
> --
> 2.9.5
>

2018-11-16 12:10:04

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 15/19] block: always define BIO_MAX_PAGES as 256

On Thu, Nov 15, 2018 at 04:53:02PM +0800, Ming Lei wrote:
> Now multi-page bvec can cover CONFIG_THP_SWAP, so we don't need to
> increase BIO_MAX_PAGES for it.

You mentioned to it in the cover letter, but this needs more explanation
in the commit message. Why did CONFIG_THP_SWAP require > 256? Why does
multipage bvecs remove that requirement?

> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]
> Signed-off-by: Ming Lei <[email protected]>
> ---
> include/linux/bio.h | 8 --------
> 1 file changed, 8 deletions(-)
>
> diff --git a/include/linux/bio.h b/include/linux/bio.h
> index 5040e9a2eb09..277921ad42e7 100644
> --- a/include/linux/bio.h
> +++ b/include/linux/bio.h
> @@ -34,15 +34,7 @@
> #define BIO_BUG_ON
> #endif
>
> -#ifdef CONFIG_THP_SWAP
> -#if HPAGE_PMD_NR > 256
> -#define BIO_MAX_PAGES HPAGE_PMD_NR
> -#else
> #define BIO_MAX_PAGES 256
> -#endif
> -#else
> -#define BIO_MAX_PAGES 256
> -#endif
>
> #define bio_prio(bio) (bio)->bi_ioprio
> #define bio_set_prio(bio, prio) ((bio)->bi_ioprio = prio)
> --
> 2.9.5
>

2018-11-19 13:32:29

by Jens Axboe

[permalink] [raw]
Subject: Re: [PATCH V10 01/19] block: introduce multi-page page bvec helpers

On 11/18/18 7:23 PM, Ming Lei wrote:
> On Fri, Nov 16, 2018 at 02:13:05PM +0100, Christoph Hellwig wrote:
>>> -#define bvec_iter_page(bvec, iter) \
>>> +#define mp_bvec_iter_page(bvec, iter) \
>>> (__bvec_iter_bvec((bvec), (iter))->bv_page)
>>>
>>> -#define bvec_iter_len(bvec, iter) \
>>> +#define mp_bvec_iter_len(bvec, iter) \
>>
>> I'd much prefer if we would stick to the segment naming that
>> we also use in the higher level helper.
>>
>> So segment_iter_page, segment_iter_len, etc.
>
> We discussed the naming problem before, one big problem is that the 'segment'
> in bio_for_each_segment*() means one single page segment actually.
>
> If we use segment_iter_page() here for multi-page segment, it may
> confuse people.
>
> Of course, I prefer to the naming of segment/page,
>
> And Jens didn't agree to rename bio_for_each_segment*() before.

I didn't like frivolous renaming (and I still don't), but mp_
is horrible imho. Don't name these after the fact that they
are done in conjunction with supporting multipage bvecs. That
very fact will be irrelevant very soon

--
Jens Axboe

2018-11-19 18:49:11

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 10/19] block: loop: pass multi-page bvec to iov_iter

On Thu, Nov 15, 2018 at 04:40:22PM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:52:57PM +0800, Ming Lei wrote:
> > iov_iter is implemented with bvec itererator, so it is safe to pass
> > multipage bvec to it, and this way is much more efficient than
> > passing one page in each bvec.
> >
> > Cc: Dave Chinner <[email protected]>
> > Cc: Kent Overstreet <[email protected]>
> > Cc: Mike Snitzer <[email protected]>
> > Cc: [email protected]
> > Cc: Alexander Viro <[email protected]>
> > Cc: [email protected]
> > Cc: Shaohua Li <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: David Sterba <[email protected]>
> > Cc: [email protected]
> > Cc: Darrick J. Wong <[email protected]>
> > Cc: [email protected]
> > Cc: Gao Xiang <[email protected]>
> > Cc: Christoph Hellwig <[email protected]>
> > Cc: Theodore Ts'o <[email protected]>
> > Cc: [email protected]
> > Cc: Coly Li <[email protected]>
> > Cc: [email protected]
> > Cc: Boaz Harrosh <[email protected]>
> > Cc: Bob Peterson <[email protected]>
> > Cc: [email protected]
>
> Reviewed-by: Omar Sandoval <[email protected]>
>
> Comments below.
>
> > Signed-off-by: Ming Lei <[email protected]>
> > ---
> > drivers/block/loop.c | 23 ++++++++++++-----------
> > 1 file changed, 12 insertions(+), 11 deletions(-)
> >
> > diff --git a/drivers/block/loop.c b/drivers/block/loop.c
> > index bf6bc35aaf88..a3fd418ec637 100644
> > --- a/drivers/block/loop.c
> > +++ b/drivers/block/loop.c
> > @@ -515,16 +515,16 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
> > struct bio *bio = rq->bio;
> > struct file *file = lo->lo_backing_file;
> > unsigned int offset;
> > - int segments = 0;
> > + int nr_bvec = 0;
> > int ret;
> >
> > if (rq->bio != rq->biotail) {
> > - struct req_iterator iter;
> > + struct bvec_iter iter;
> > struct bio_vec tmp;
> >
> > __rq_for_each_bio(bio, rq)
> > - segments += bio_segments(bio);
> > - bvec = kmalloc_array(segments, sizeof(struct bio_vec),
> > + nr_bvec += bio_bvecs(bio);
> > + bvec = kmalloc_array(nr_bvec, sizeof(struct bio_vec),
> > GFP_NOIO);
> > if (!bvec)
> > return -EIO;
> > @@ -533,13 +533,14 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
> > /*
> > * The bios of the request may be started from the middle of
> > * the 'bvec' because of bio splitting, so we can't directly
> > - * copy bio->bi_iov_vec to new bvec. The rq_for_each_segment
> > + * copy bio->bi_iov_vec to new bvec. The bio_for_each_bvec
> > * API will take care of all details for us.
> > */
> > - rq_for_each_segment(tmp, rq, iter) {
> > - *bvec = tmp;
> > - bvec++;
> > - }
> > + __rq_for_each_bio(bio, rq)
> > + bio_for_each_bvec(tmp, bio, iter) {
> > + *bvec = tmp;
> > + bvec++;
> > + }
>
> Even if they're not strictly necessary, could you please include the
> curly braces for __rq_for_each_bio() here?

Sure, will do it.

>
> > bvec = cmd->bvec;
> > offset = 0;
> > } else {
> > @@ -550,11 +551,11 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
> > */
> > offset = bio->bi_iter.bi_bvec_done;
> > bvec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
> > - segments = bio_segments(bio);
> > + nr_bvec = bio_bvecs(bio);
>
> This scared me for a second, but it's fine to do here because we haven't
> actually enabled multipage bvecs yet, right?

Well, it is fine, all helpers supporting multi-page bvec actually works
well when it isn't enabled, cause single-page bvec is one special case in
which multi-page bevc helpers have to deal with.

Thanks,
Ming

2018-11-17 00:05:41

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 14/19] block: enable multipage bvecs

> -
> - if (page == bv->bv_page && off == bv->bv_offset + bv->bv_len) {
> - bv->bv_len += len;
> - bio->bi_iter.bi_size += len;
> - return true;
> - }
> + struct request_queue *q = NULL;
> +
> + if (page == bv->bv_page && off == (bv->bv_offset + bv->bv_len)
> + && (off + len) <= PAGE_SIZE)

How could the page struct be the same, but the range beyond PAGE_SIZE
(at least with the existing callers)?

Also no need for the inner btraces, and the && always goes on the
first line.

> + if (bio->bi_disk)
> + q = bio->bi_disk->queue;
> +
> + /* disable multi-page bvec too if cluster isn't enabled */
> + if (!q || !blk_queue_cluster(q) ||
> + ((page_to_phys(bv->bv_page) + bv->bv_offset + bv->bv_len) !=
> + (page_to_phys(page) + off)))
> + return false;
> + merge:
> + bv->bv_len += len;
> + bio->bi_iter.bi_size += len;
> + return true;

Ok, this is scary, as it will give differen results depending on when
bi_disk is assigned. But then again we shouldn't really do the cluster
check here, but rather when splitting the bio for the actual low-level
driver.

(and eventually we should kill this clustering setting off in favor
of our normal segment limits).

2018-11-17 12:57:43

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 00/19] block: support multi-page bvec

On Fri, Nov 16, 2018 at 03:03:14PM +0100, Christoph Hellwig wrote:
> It seems like bi_phys_segments is still around of this series.
> Shouldn't it be superflous now?

Even though multi-page bvec is supported, the segment number doesn't
equal to the actual bvec count yet, for example, one bvec may be too
bigger to be held in one single segment.


Thanks,
Ming

2018-11-16 12:16:03

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 16/19] block: document usage of bio iterator helpers

On Thu, Nov 15, 2018 at 04:53:03PM +0800, Ming Lei wrote:
> Now multi-page bvec is supported, some helpers may return page by
> page, meantime some may return segment by segment, this patch
> documents the usage.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]
> Signed-off-by: Ming Lei <[email protected]>
> ---
> Documentation/block/biovecs.txt | 26 ++++++++++++++++++++++++++
> 1 file changed, 26 insertions(+)
>
> diff --git a/Documentation/block/biovecs.txt b/Documentation/block/biovecs.txt
> index 25689584e6e0..bfafb70d0d9e 100644
> --- a/Documentation/block/biovecs.txt
> +++ b/Documentation/block/biovecs.txt
> @@ -117,3 +117,29 @@ Other implications:
> size limitations and the limitations of the underlying devices. Thus
> there's no need to define ->merge_bvec_fn() callbacks for individual block
> drivers.
> +
> +Usage of helpers:
> +=================
> +
> +* The following helpers whose names have the suffix of "_all" can only be used
> +on non-BIO_CLONED bio, and usually they are used by filesystem code, and driver
> +shouldn't use them because bio may have been split before they got to the driver:

Putting an english teacher hat on, this is quite the run-on sentence.
How about:

* The following helpers whose names have the suffix of "_all" can only be used
on non-BIO_CLONED bio. They are usually used by filesystem code. Drivers
shouldn't use them because the bio may have been split before it reached the
driver.

Maybe also an explanation of why the filesystem would want to use these?

> + bio_for_each_segment_all()
> + bio_first_bvec_all()
> + bio_first_page_all()
> + bio_last_bvec_all()
> +
> +* The following helpers iterate over single-page bvec, and the local
> +variable of 'struct bio_vec' or the reference records single-page IO
> +vector during the itearation:


* The following helpers iterate over single-page bvecs. The passed 'struct
bio_vec' will contain a single-page IO vector during the iteration.

> + bio_for_each_segment()
> + bio_for_each_segment_all()
> +
> +* The following helper iterates over multi-page bvec, and each bvec may
> +include multiple physically contiguous pages, and the local variable of
> +'struct bio_vec' or the reference records multi-page IO vector during the
> +itearation:

* The following helper iterates over multi-page bvecs. Each bvec may
include multiple physically contiguous pages. The passed 'struct bio_vec' will
contain a multi-page IO vector during the iteration.

> + bio_for_each_bvec()
> --
> 2.9.5
>

2018-11-21 11:31:38

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()

On Tue, Nov 20, 2018 at 12:11:35PM -0800, Sagi Grimberg wrote:
>
> > > > The only user in your final tree seems to be the loop driver, and
> > > > even that one only uses the helper for read/write bios.
> > > >
> > > > I think something like this would be much simpler in the end:
> > >
> > > The recently submitted nvme-tcp host driver should also be a user
> > > of this. Does it make sense to keep it as a helper then?
> >
> > I did take a brief look at the code, and I really don't understand
> > why the heck it even deals with bios to start with. Like all the
> > other nvme transports it is a blk-mq driver and should iterate
> > over segments in a request and more or less ignore bios. Something
> > is horribly wrong in the design.
>
> Can you explain a little more? I'm more than happy to change that but
> I'm not completely clear how...
>
> Before we begin a data transfer, we need to set our own iterator that
> will advance with the progression of the data transfer. We also need to
> keep in mind that all the data transfer (both send and recv) are
> completely non blocking (and zero-copy when we send).
>
> That means that every data movement needs to be able to suspend
> and resume asynchronously. i.e. we cannot use the following pattern:
> rq_for_each_segment(bvec, rq, rq_iter) {
> iov_iter_bvec(&iov_iter, WRITE, &bvec, 1, bvec.bv_len);
> send(sock, iov_iter);
> }

Not sure I understand the 'blocking' problem in this case.

We can build a bvec table from this req, and send them all
in send(), can this way avoid your blocking issue? You may see this
example in branch 'rq->bio != rq->biotail' of lo_rw_aio().

If this way is what you need, I think you are right, even we may
introduce the following helpers:

rq_for_each_bvec()
rq_bvecs()

So looks nvme-tcp host driver might be the 2nd driver which benefits
from multi-page bvec directly.

The multi-page bvec V11 has passed my tests and addressed almost
all the comments during review on V10. I removed bio_vecs() in V11,
but it won't be big deal, we can introduce them anytime when there
is the requirement.

Thanks,
Ming

2018-11-16 10:36:35

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()

On Thu, Nov 15, 2018 at 04:52:56PM +0800, Ming Lei wrote:
> There are still cases in which we need to use bio_bvecs() for get the
> number of multi-page segment, so introduce it.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]

Reviewed-by: Omar Sandoval <[email protected]>

> Signed-off-by: Ming Lei <[email protected]>
> ---
> include/linux/bio.h | 30 +++++++++++++++++++++++++-----
> 1 file changed, 25 insertions(+), 5 deletions(-)
>
> diff --git a/include/linux/bio.h b/include/linux/bio.h
> index 1f0dcf109841..3496c816946e 100644
> --- a/include/linux/bio.h
> +++ b/include/linux/bio.h
> @@ -196,7 +196,6 @@ static inline unsigned bio_segments(struct bio *bio)
> * We special case discard/write same/write zeroes, because they
> * interpret bi_size differently:
> */
> -
> switch (bio_op(bio)) {
> case REQ_OP_DISCARD:
> case REQ_OP_SECURE_ERASE:
> @@ -205,13 +204,34 @@ static inline unsigned bio_segments(struct bio *bio)
> case REQ_OP_WRITE_SAME:
> return 1;
> default:
> - break;
> + bio_for_each_segment(bv, bio, iter)
> + segs++;
> + return segs;
> }
> +}
>
> - bio_for_each_segment(bv, bio, iter)
> - segs++;
> +static inline unsigned bio_bvecs(struct bio *bio)
> +{
> + unsigned bvecs = 0;
> + struct bio_vec bv;
> + struct bvec_iter iter;
>
> - return segs;
> + /*
> + * We special case discard/write same/write zeroes, because they
> + * interpret bi_size differently:
> + */
> + switch (bio_op(bio)) {
> + case REQ_OP_DISCARD:
> + case REQ_OP_SECURE_ERASE:
> + case REQ_OP_WRITE_ZEROES:
> + return 0;
> + case REQ_OP_WRITE_SAME:
> + return 1;
> + default:
> + bio_for_each_bvec(bv, bio, iter)
> + bvecs++;
> + return bvecs;
> + }
> }
>
> /*
> --
> 2.9.5
>

2018-11-16 04:34:57

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 01/19] block: introduce multi-page page bvec helpers

On Thu, Nov 15, 2018 at 04:52:48PM +0800, Ming Lei wrote:
> This patch introduces helpers of 'mp_bvec_iter_*' for multipage
> bvec support.
>
> The introduced helpers treate one bvec as real multi-page segment,
> which may include more than one pages.
>
> The existed helpers of bvec_iter_* are interfaces for supporting current
> bvec iterator which is thought as single-page by drivers, fs, dm and
> etc. These introduced helpers will build single-page bvec in flight, so
> this way won't break current bio/bvec users, which needn't any change.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]

Reviewed-by: Omar Sandoval <[email protected]>

But a couple of comments below.

> Signed-off-by: Ming Lei <[email protected]>
> ---
> include/linux/bvec.h | 63 +++++++++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 60 insertions(+), 3 deletions(-)
>
> diff --git a/include/linux/bvec.h b/include/linux/bvec.h
> index 02c73c6aa805..8ef904a50577 100644
> --- a/include/linux/bvec.h
> +++ b/include/linux/bvec.h
> @@ -23,6 +23,44 @@
> #include <linux/kernel.h>
> #include <linux/bug.h>
> #include <linux/errno.h>
> +#include <linux/mm.h>
> +
> +/*
> + * What is multi-page bvecs?
> + *
> + * - bvecs stored in bio->bi_io_vec is always multi-page(mp) style
> + *
> + * - bvec(struct bio_vec) represents one physically contiguous I/O
> + * buffer, now the buffer may include more than one pages after
> + * multi-page(mp) bvec is supported, and all these pages represented
> + * by one bvec is physically contiguous. Before mp support, at most
> + * one page is included in one bvec, we call it single-page(sp)
> + * bvec.
> + *
> + * - .bv_page of the bvec represents the 1st page in the mp bvec
> + *
> + * - .bv_offset of the bvec represents offset of the buffer in the bvec
> + *
> + * The effect on the current drivers/filesystem/dm/bcache/...:
> + *
> + * - almost everyone supposes that one bvec only includes one single
> + * page, so we keep the sp interface not changed, for example,
> + * bio_for_each_segment() still returns bvec with single page
> + *
> + * - bio_for_each_segment*() will be changed to return single-page
> + * bvec too
> + *
> + * - during iterating, iterator variable(struct bvec_iter) is always
> + * updated in multipage bvec style and that means bvec_iter_advance()
> + * is kept not changed
> + *
> + * - returned(copied) single-page bvec is built in flight by bvec
> + * helpers from the stored multipage bvec
> + *
> + * - In case that some components(such as iov_iter) need to support
> + * multi-page bvec, we introduce new helpers(mp_bvec_iter_*) for
> + * them.
> + */

This comment sounds more like a commit message (i.e., how were things
before, and how are we changing them). In a couple of years when I read
this code, I probably won't care how it was changed, just how it works.
So I think a comment explaining the concepts of multi-page and
single-page bvecs is very useful, but please move all of the "foo was
changed" and "before mp support" type stuff to the commit message.

> /*
> * was unsigned short, but we might as well be ready for > 64kB I/O pages
> @@ -50,16 +88,35 @@ struct bvec_iter {
> */
> #define __bvec_iter_bvec(bvec, iter) (&(bvec)[(iter).bi_idx])
>
> -#define bvec_iter_page(bvec, iter) \
> +#define mp_bvec_iter_page(bvec, iter) \
> (__bvec_iter_bvec((bvec), (iter))->bv_page)
>
> -#define bvec_iter_len(bvec, iter) \
> +#define mp_bvec_iter_len(bvec, iter) \
> min((iter).bi_size, \
> __bvec_iter_bvec((bvec), (iter))->bv_len - (iter).bi_bvec_done)
>
> -#define bvec_iter_offset(bvec, iter) \
> +#define mp_bvec_iter_offset(bvec, iter) \
> (__bvec_iter_bvec((bvec), (iter))->bv_offset + (iter).bi_bvec_done)
>
> +#define mp_bvec_iter_page_idx(bvec, iter) \
> + (mp_bvec_iter_offset((bvec), (iter)) / PAGE_SIZE)
> +
> +/*
> + * <page, offset,length> of single-page(sp) segment.
> + *
> + * This helpers are for building sp bvec in flight.
> + */
> +#define bvec_iter_offset(bvec, iter) \
> + (mp_bvec_iter_offset((bvec), (iter)) % PAGE_SIZE)
> +
> +#define bvec_iter_len(bvec, iter) \
> + min_t(unsigned, mp_bvec_iter_len((bvec), (iter)), \
> + (PAGE_SIZE - (bvec_iter_offset((bvec), (iter)))))

The parentheses around (bvec_iter_offset((bvec), (iter))) and
(PAGE_SIZE - (bvec_iter_offset((bvec), (iter)))) are unnecessary
clutter. This looks easier to read to me:

#define bvec_iter_len(bvec, iter) \
min_t(unsigned, mp_bvec_iter_len((bvec), (iter)), \
PAGE_SIZE - bvec_iter_offset((bvec), (iter)))

> +
> +#define bvec_iter_page(bvec, iter) \
> + nth_page(mp_bvec_iter_page((bvec), (iter)), \
> + mp_bvec_iter_page_idx((bvec), (iter)))
> +
> #define bvec_iter_bvec(bvec, iter) \
> ((struct bio_vec) { \
> .bv_page = bvec_iter_page((bvec), (iter)), \
> --
> 2.9.5
>

2018-11-15 19:02:11

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 07/19] btrfs: use bvec_last_segment to get bio's last page

Preparing for supporting multi-page bvec.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
fs/btrfs/compression.c | 5 ++++-
fs/btrfs/extent_io.c | 5 +++--
2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 2955a4ea2fa8..161e14b8b180 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -400,8 +400,11 @@ blk_status_t btrfs_submit_compressed_write(struct inode *inode, u64 start,
static u64 bio_end_offset(struct bio *bio)
{
struct bio_vec *last = bio_last_bvec_all(bio);
+ struct bio_vec bv;

- return page_offset(last->bv_page) + last->bv_len + last->bv_offset;
+ bvec_last_segment(last, &bv);
+
+ return page_offset(bv.bv_page) + bv.bv_len + bv.bv_offset;
}

static noinline int add_ra_bio_pages(struct inode *inode,
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index d228f706ff3e..5d5965297e7e 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2720,11 +2720,12 @@ static int __must_check submit_one_bio(struct bio *bio, int mirror_num,
{
blk_status_t ret = 0;
struct bio_vec *bvec = bio_last_bvec_all(bio);
- struct page *page = bvec->bv_page;
+ struct bio_vec bv;
struct extent_io_tree *tree = bio->bi_private;
u64 start;

- start = page_offset(page) + bvec->bv_offset;
+ bvec_last_segment(bvec, &bv);
+ start = page_offset(bv.bv_page) + bv.bv_offset;

bio->bi_private = NULL;

--
2.9.5

2018-11-15 19:05:18

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 18/19] block: kill QUEUE_FLAG_NO_SG_MERGE

Since bdced438acd83ad83a6c ("block: setup bi_phys_segments after splitting"),
physical segment number is mainly figured out in blk_queue_split() for
fast path, and the flag of BIO_SEG_VALID is set there too.

Now only blk_recount_segments() and blk_recalc_rq_segments() use this
flag.

Basically blk_recount_segments() is bypassed in fast path given BIO_SEG_VALID
is set in blk_queue_split().

For another user of blk_recalc_rq_segments():

- run in partial completion branch of blk_update_request, which is an unusual case

- run in blk_cloned_rq_check_limits(), still not a big problem if the flag is killed
since dm-rq is the only user.

Multi-page bvec is enabled now, QUEUE_FLAG_NO_SG_MERGE doesn't make sense any more.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
block/blk-merge.c | 31 ++++++-------------------------
block/blk-mq-debugfs.c | 1 -
block/blk-mq.c | 3 ---
drivers/md/dm-table.c | 13 -------------
include/linux/blkdev.h | 1 -
5 files changed, 6 insertions(+), 43 deletions(-)

diff --git a/block/blk-merge.c b/block/blk-merge.c
index 153a659fde74..06be298be332 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -351,8 +351,7 @@ void blk_queue_split(struct request_queue *q, struct bio **bio)
EXPORT_SYMBOL(blk_queue_split);

static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
- struct bio *bio,
- bool no_sg_merge)
+ struct bio *bio)
{
struct bio_vec bv, bvprv = { NULL };
int cluster, prev = 0;
@@ -379,13 +378,6 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
nr_phys_segs = 0;
for_each_bio(bio) {
bio_for_each_bvec(bv, bio, iter) {
- /*
- * If SG merging is disabled, each bio vector is
- * a segment
- */
- if (no_sg_merge)
- goto new_segment;
-
if (prev && cluster) {
if (seg_size + bv.bv_len
> queue_max_segment_size(q))
@@ -420,27 +412,16 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,

void blk_recalc_rq_segments(struct request *rq)
{
- bool no_sg_merge = !!test_bit(QUEUE_FLAG_NO_SG_MERGE,
- &rq->q->queue_flags);
-
- rq->nr_phys_segments = __blk_recalc_rq_segments(rq->q, rq->bio,
- no_sg_merge);
+ rq->nr_phys_segments = __blk_recalc_rq_segments(rq->q, rq->bio);
}

void blk_recount_segments(struct request_queue *q, struct bio *bio)
{
- unsigned short seg_cnt = bio_segments(bio);
-
- if (test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags) &&
- (seg_cnt < queue_max_segments(q)))
- bio->bi_phys_segments = seg_cnt;
- else {
- struct bio *nxt = bio->bi_next;
+ struct bio *nxt = bio->bi_next;

- bio->bi_next = NULL;
- bio->bi_phys_segments = __blk_recalc_rq_segments(q, bio, false);
- bio->bi_next = nxt;
- }
+ bio->bi_next = NULL;
+ bio->bi_phys_segments = __blk_recalc_rq_segments(q, bio);
+ bio->bi_next = nxt;

bio_set_flag(bio, BIO_SEG_VALID);
}
diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c
index f021f4817b80..e188b1090759 100644
--- a/block/blk-mq-debugfs.c
+++ b/block/blk-mq-debugfs.c
@@ -128,7 +128,6 @@ static const char *const blk_queue_flag_name[] = {
QUEUE_FLAG_NAME(SAME_FORCE),
QUEUE_FLAG_NAME(DEAD),
QUEUE_FLAG_NAME(INIT_DONE),
- QUEUE_FLAG_NAME(NO_SG_MERGE),
QUEUE_FLAG_NAME(POLL),
QUEUE_FLAG_NAME(WC),
QUEUE_FLAG_NAME(FUA),
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 411be60d0cb6..ed484af5744b 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -2755,9 +2755,6 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,

q->queue_flags |= QUEUE_FLAG_MQ_DEFAULT;

- if (!(set->flags & BLK_MQ_F_SG_MERGE))
- queue_flag_set_unlocked(QUEUE_FLAG_NO_SG_MERGE, q);
-
q->sg_reserved_size = INT_MAX;

INIT_DELAYED_WORK(&q->requeue_work, blk_mq_requeue_work);
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 9038c302d5c2..22fed6987aea 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -1698,14 +1698,6 @@ static int device_is_not_random(struct dm_target *ti, struct dm_dev *dev,
return q && !blk_queue_add_random(q);
}

-static int queue_supports_sg_merge(struct dm_target *ti, struct dm_dev *dev,
- sector_t start, sector_t len, void *data)
-{
- struct request_queue *q = bdev_get_queue(dev->bdev);
-
- return q && !test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags);
-}
-
static bool dm_table_all_devices_attribute(struct dm_table *t,
iterate_devices_callout_fn func)
{
@@ -1902,11 +1894,6 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
if (!dm_table_supports_write_zeroes(t))
q->limits.max_write_zeroes_sectors = 0;

- if (dm_table_all_devices_attribute(t, queue_supports_sg_merge))
- blk_queue_flag_clear(QUEUE_FLAG_NO_SG_MERGE, q);
- else
- blk_queue_flag_set(QUEUE_FLAG_NO_SG_MERGE, q);
-
dm_table_verify_integrity(t);

/*
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index e67ad2dd025e..c5c7799e88c2 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -604,7 +604,6 @@ struct request_queue {
#define QUEUE_FLAG_SAME_FORCE 15 /* force complete on same CPU */
#define QUEUE_FLAG_DEAD 16 /* queue tear-down finished */
#define QUEUE_FLAG_INIT_DONE 17 /* queue is initialized */
-#define QUEUE_FLAG_NO_SG_MERGE 18 /* don't attempt to merge SG segments*/
#define QUEUE_FLAG_POLL 19 /* IO polling enabled if set */
#define QUEUE_FLAG_WC 20 /* Write back caching */
#define QUEUE_FLAG_FUA 21 /* device supports FUA writes */
--
2.9.5

2018-11-20 13:11:58

by Huang, Ying

[permalink] [raw]
Subject: Re: [PATCH V10 15/19] block: always define BIO_MAX_PAGES as 256

Ming Lei <[email protected]> writes:

> On Thu, Nov 15, 2018 at 05:59:36PM -0800, Omar Sandoval wrote:
>> On Thu, Nov 15, 2018 at 04:53:02PM +0800, Ming Lei wrote:
>> > Now multi-page bvec can cover CONFIG_THP_SWAP, so we don't need to
>> > increase BIO_MAX_PAGES for it.
>>
>> You mentioned to it in the cover letter, but this needs more explanation
>> in the commit message. Why did CONFIG_THP_SWAP require > 256? Why does
>> multipage bvecs remove that requirement?
>
> CONFIG_THP_SWAP needs to split one TH page into normal pages and adds
> them all to one bio. With multipage-bvec, it just takes one bvec to
> hold them all.

Yes. CONFIG_THP_SWAP needs to put 512 normal sub-pages into one bio to
write the 512 sub-pages together. With the help of multipage-bvec, it
needs just bvect to hold 512 normal sub-pages.

Best Regards,
Huang, Ying

> thanks,
> Ming

2018-11-19 18:38:27

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 08/19] btrfs: move bio_pages_all() to btrfs

On Thu, Nov 15, 2018 at 04:23:56PM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:52:55PM +0800, Ming Lei wrote:
> > BTRFS is the only user of this helper, so move this helper into
> > BTRFS, and implement it via bio_for_each_segment_all(), since
> > bio->bi_vcnt may not equal to number of pages after multipage bvec
> > is enabled.
>
> Shouldn't you also get rid of bio_pages_all() in this patch?

Good catch!

thanks,
Ming

2018-11-21 15:37:16

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()

On Tue, Nov 20, 2018 at 08:42:04PM -0800, Sagi Grimberg wrote:
>
> > > Yeah, that is the most common example, given merge is enabled
> > > in most of cases. If the driver or device doesn't care merge,
> > > you can disable it and always get single bio request, then the
> > > bio's bvec table can be reused for send().
> >
> > Does bvec_iter span bvecs with your patches? I didn't see that change?
>
> Wait, I see that the bvec is still a single array per bio. When you said
> a table I thought you meant a 2-dimentional array...

I mean a new 1-d table A has to be created for multiple bios in one rq,
and build it in the following way

rq_for_each_bvec(tmp, rq, rq_iter)
*A = tmp;

Then you can pass A to iov_iter_bvec() & send().

Given it is over TCP, I guess it should be doable for you to preallocate one
256-bvec table in one page for each request, then sets the max segment size as
(unsigned int)-1, and max segment number as 256, the preallocated table
should work anytime.


Thanks,
Ming

2018-11-17 00:15:49

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 00/19] block: support multi-page bvec

It seems like bi_phys_segments is still around of this series.
Shouldn't it be superflous now?

2018-11-15 19:03:21

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 11/19] bcache: avoid to use bio_for_each_segment_all() in bch_bio_alloc_pages()

bch_bio_alloc_pages() is always called on one new bio, so it is safe
to access the bvec table directly. Given it is the only kind of this
case, open code the bvec table access since bio_for_each_segment_all()
will be changed to support for iterating over multipage bvec.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Acked-by: Coly Li <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
drivers/md/bcache/util.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
index 20eddeac1531..8517aebcda2d 100644
--- a/drivers/md/bcache/util.c
+++ b/drivers/md/bcache/util.c
@@ -270,7 +270,7 @@ int bch_bio_alloc_pages(struct bio *bio, gfp_t gfp_mask)
int i;
struct bio_vec *bv;

- bio_for_each_segment_all(bv, bio, i) {
+ for (i = 0, bv = bio->bi_io_vec; i < bio->bi_vcnt; bv++) {
bv->bv_page = alloc_page(gfp_mask);
if (!bv->bv_page) {
while (--bv >= bio->bi_io_vec)
--
2.9.5

2018-11-16 23:51:13

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 08/19] btrfs: move bio_pages_all() to btrfs

On Thu, Nov 15, 2018 at 04:52:55PM +0800, Ming Lei wrote:
> BTRFS is the only user of this helper, so move this helper into
> BTRFS, and implement it via bio_for_each_segment_all(), since
> bio->bi_vcnt may not equal to number of pages after multipage bvec
> is enabled.

btrfs only uses the value to check if it is larger than 1. No amount
of multipage bio merging should ever make bi_vcnt go from 0 to 1 or
vice versa.

2018-11-15 19:01:35

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 05/19] block: introduce bvec_last_segment()

BTRFS and guard_bio_eod() need to get the last singlepage segment
from one multipage bvec, so introduce this helper to make them happy.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
include/linux/bvec.h | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/include/linux/bvec.h b/include/linux/bvec.h
index 3d61352cd8cf..01616a0b6220 100644
--- a/include/linux/bvec.h
+++ b/include/linux/bvec.h
@@ -216,4 +216,29 @@ static inline bool mp_bvec_iter_advance(const struct bio_vec *bv,
.bi_bvec_done = 0, \
}

+/*
+ * Get the last singlepage segment from the multipage bvec and store it
+ * in @seg
+ */
+static inline void bvec_last_segment(const struct bio_vec *bvec,
+ struct bio_vec *seg)
+{
+ unsigned total = bvec->bv_offset + bvec->bv_len;
+ unsigned last_page = total / PAGE_SIZE;
+
+ if (last_page * PAGE_SIZE == total)
+ last_page--;
+
+ seg->bv_page = nth_page(bvec->bv_page, last_page);
+
+ /* the whole segment is inside the last page */
+ if (bvec->bv_offset >= last_page * PAGE_SIZE) {
+ seg->bv_offset = bvec->bv_offset % PAGE_SIZE;
+ seg->bv_len = bvec->bv_len;
+ } else {
+ seg->bv_offset = 0;
+ seg->bv_len = total - last_page * PAGE_SIZE;
+ }
+}
+
#endif /* __LINUX_BVEC_ITER_H */
--
2.9.5

2018-11-15 19:04:40

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 16/19] block: document usage of bio iterator helpers

Now multi-page bvec is supported, some helpers may return page by
page, meantime some may return segment by segment, this patch
documents the usage.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
Documentation/block/biovecs.txt | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)

diff --git a/Documentation/block/biovecs.txt b/Documentation/block/biovecs.txt
index 25689584e6e0..bfafb70d0d9e 100644
--- a/Documentation/block/biovecs.txt
+++ b/Documentation/block/biovecs.txt
@@ -117,3 +117,29 @@ Other implications:
size limitations and the limitations of the underlying devices. Thus
there's no need to define ->merge_bvec_fn() callbacks for individual block
drivers.
+
+Usage of helpers:
+=================
+
+* The following helpers whose names have the suffix of "_all" can only be used
+on non-BIO_CLONED bio, and usually they are used by filesystem code, and driver
+shouldn't use them because bio may have been split before they got to the driver:
+
+ bio_for_each_segment_all()
+ bio_first_bvec_all()
+ bio_first_page_all()
+ bio_last_bvec_all()
+
+* The following helpers iterate over single-page bvec, and the local
+variable of 'struct bio_vec' or the reference records single-page IO
+vector during the itearation:
+
+ bio_for_each_segment()
+ bio_for_each_segment_all()
+
+* The following helper iterates over multi-page bvec, and each bvec may
+include multiple physically contiguous pages, and the local variable of
+'struct bio_vec' or the reference records multi-page IO vector during the
+itearation:
+
+ bio_for_each_bvec()
--
2.9.5

2018-11-16 04:37:50

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 02/19] block: introduce bio_for_each_bvec()

On Thu, Nov 15, 2018 at 04:52:49PM +0800, Ming Lei wrote:
> This helper is used for iterating over multi-page bvec for bio
> split & merge code.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]

Reviewed-by: Omar Sandoval <[email protected]>

One comment below.

> Signed-off-by: Ming Lei <[email protected]>
> ---
> include/linux/bio.h | 34 +++++++++++++++++++++++++++++++---
> include/linux/bvec.h | 36 ++++++++++++++++++++++++++++++++----
> 2 files changed, 63 insertions(+), 7 deletions(-)
>
> diff --git a/include/linux/bio.h b/include/linux/bio.h
> index 056fb627edb3..1f0dcf109841 100644
> --- a/include/linux/bio.h
> +++ b/include/linux/bio.h
> @@ -76,6 +76,9 @@
> #define bio_data_dir(bio) \
> (op_is_write(bio_op(bio)) ? WRITE : READ)
>
> +#define bio_iter_mp_iovec(bio, iter) \
> + mp_bvec_iter_bvec((bio)->bi_io_vec, (iter))
> +
> /*
> * Check whether this bio carries any data or not. A NULL bio is allowed.
> */
> @@ -135,18 +138,33 @@ static inline bool bio_full(struct bio *bio)
> #define bio_for_each_segment_all(bvl, bio, i) \
> for (i = 0, bvl = (bio)->bi_io_vec; i < (bio)->bi_vcnt; i++, bvl++)
>
> -static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
> - unsigned bytes)
> +static inline void __bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
> + unsigned bytes, bool mp)
> {
> iter->bi_sector += bytes >> 9;
>
> if (bio_no_advance_iter(bio))
> iter->bi_size -= bytes;
> else
> - bvec_iter_advance(bio->bi_io_vec, iter, bytes);
> + if (!mp)
> + bvec_iter_advance(bio->bi_io_vec, iter, bytes);
> + else
> + mp_bvec_iter_advance(bio->bi_io_vec, iter, bytes);

if (!foo) {} else {} hurts my brain, please do

if (mp)
mp_bvec_iter_advance(bio->bi_io_vec, iter, bytes);
else
bvec_iter_advance(bio->bi_io_vec, iter, bytes);

2018-11-16 06:29:49

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 03/19] block: use bio_for_each_bvec() to compute multi-page bvec count

On Thu, Nov 15, 2018 at 04:52:50PM +0800, Ming Lei wrote:
> First it is more efficient to use bio_for_each_bvec() in both
> blk_bio_segment_split() and __blk_recalc_rq_segments() to compute how
> many multi-page bvecs there are in the bio.
>
> Secondly once bio_for_each_bvec() is used, the bvec may need to be
> splitted because its length can be very longer than max segment size,
> so we have to split the big bvec into several segments.
>
> Thirdly when splitting multi-page bvec into segments, the max segment
> limit may be reached, so the bio split need to be considered under
> this situation too.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]
> Signed-off-by: Ming Lei <[email protected]>
> ---
> block/blk-merge.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++---------
> 1 file changed, 76 insertions(+), 14 deletions(-)
>
> diff --git a/block/blk-merge.c b/block/blk-merge.c
> index 91b2af332a84..6f7deb94a23f 100644
> --- a/block/blk-merge.c
> +++ b/block/blk-merge.c
> @@ -160,6 +160,62 @@ static inline unsigned get_max_io_size(struct request_queue *q,
> return sectors;
> }
>
> +/*
> + * Split the bvec @bv into segments, and update all kinds of
> + * variables.
> + */
> +static bool bvec_split_segs(struct request_queue *q, struct bio_vec *bv,
> + unsigned *nsegs, unsigned *last_seg_size,
> + unsigned *front_seg_size, unsigned *sectors)
> +{
> + bool need_split = false;
> + unsigned len = bv->bv_len;
> + unsigned total_len = 0;
> + unsigned new_nsegs = 0, seg_size = 0;

"unsigned int" here and everywhere else.

> + if ((*nsegs >= queue_max_segments(q)) || !len)
> + return need_split;
> +
> + /*
> + * Multipage bvec may be too big to hold in one segment,
> + * so the current bvec has to be splitted as multiple
> + * segments.
> + */
> + while (new_nsegs + *nsegs < queue_max_segments(q)) {
> + seg_size = min(queue_max_segment_size(q), len);
> +
> + new_nsegs++;
> + total_len += seg_size;
> + len -= seg_size;
> +
> + if ((queue_virt_boundary(q) && ((bv->bv_offset +
> + total_len) & queue_virt_boundary(q))) || !len)
> + break;

Checking queue_virt_boundary(q) != 0 is superfluous, and the len check
could just control the loop, i.e.,

while (len && new_nsegs + *nsegs < queue_max_segments(q)) {
seg_size = min(queue_max_segment_size(q), len);

new_nsegs++;
total_len += seg_size;
len -= seg_size;

if ((bv->bv_offset + total_len) & queue_virt_boundary(q))
break;
}

And if you rewrite it this way, I _think_ you can get rid of this
special case:

if ((*nsegs >= queue_max_segments(q)) || !len)
return need_split;

above.

> + }
> +
> + /* split in the middle of the bvec */
> + if (len)
> + need_split = true;

need_split is unnecessary, just return len != 0.

> +
> + /* update front segment size */
> + if (!*nsegs) {
> + unsigned first_seg_size = seg_size;
> +
> + if (new_nsegs > 1)
> + first_seg_size = queue_max_segment_size(q);
> + if (*front_seg_size < first_seg_size)
> + *front_seg_size = first_seg_size;
> + }
> +
> + /* update other varibles */
> + *last_seg_size = seg_size;
> + *nsegs += new_nsegs;
> + if (sectors)
> + *sectors += total_len >> 9;
> +
> + return need_split;
> +}
> +
> static struct bio *blk_bio_segment_split(struct request_queue *q,
> struct bio *bio,
> struct bio_set *bs,
> @@ -173,7 +229,7 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
> struct bio *new = NULL;
> const unsigned max_sectors = get_max_io_size(q, bio);
>
> - bio_for_each_segment(bv, bio, iter) {
> + bio_for_each_bvec(bv, bio, iter) {
> /*
> * If the queue doesn't support SG gaps and adding this
> * offset would create a gap, disallow it.
> @@ -188,8 +244,12 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
> */
> if (nsegs < queue_max_segments(q) &&
> sectors < max_sectors) {
> - nsegs++;
> - sectors = max_sectors;
> + /* split in the middle of bvec */
> + bv.bv_len = (max_sectors - sectors) << 9;
> + bvec_split_segs(q, &bv, &nsegs,
> + &seg_size,
> + &front_seg_size,
> + &sectors);
> }
> goto split;
> }
> @@ -214,11 +274,12 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
> if (nsegs == 1 && seg_size > front_seg_size)
> front_seg_size = seg_size;

Hm, do we still need to check this here now that we're updating
front_seg_size inside of bvec_split_segs()?

>
> - nsegs++;
> bvprv = bv;
> bvprvp = &bvprv;
> - seg_size = bv.bv_len;
> - sectors += bv.bv_len >> 9;
> +
> + if (bvec_split_segs(q, &bv, &nsegs, &seg_size,
> + &front_seg_size, &sectors))

What happened to the indent alignment here?

> + goto split;
>
> }
>
> @@ -296,6 +357,7 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
> struct bio_vec bv, bvprv = { NULL };
> int cluster, prev = 0;
> unsigned int seg_size, nr_phys_segs;
> + unsigned front_seg_size = bio->bi_seg_front_size;
> struct bio *fbio, *bbio;
> struct bvec_iter iter;
>
> @@ -316,7 +378,7 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
> seg_size = 0;
> nr_phys_segs = 0;
> for_each_bio(bio) {
> - bio_for_each_segment(bv, bio, iter) {
> + bio_for_each_bvec(bv, bio, iter) {
> /*
> * If SG merging is disabled, each bio vector is
> * a segment
> @@ -336,20 +398,20 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
> continue;
> }
> new_segment:
> - if (nr_phys_segs == 1 && seg_size >
> - fbio->bi_seg_front_size)
> - fbio->bi_seg_front_size = seg_size;
> + if (nr_phys_segs == 1 && seg_size > front_seg_size)
> + front_seg_size = seg_size;

Same comment as in blk_bio_segment_split(), do we still need to check
this if we're updating front_seg_size in bvec_split_segs()?

>
> - nr_phys_segs++;
> bvprv = bv;
> prev = 1;
> - seg_size = bv.bv_len;
> + bvec_split_segs(q, &bv, &nr_phys_segs, &seg_size,
> + &front_seg_size, NULL);
> }
> bbio = bio;
> }
>
> - if (nr_phys_segs == 1 && seg_size > fbio->bi_seg_front_size)
> - fbio->bi_seg_front_size = seg_size;
> + if (nr_phys_segs == 1 && seg_size > front_seg_size)
> + front_seg_size = seg_size;
> + fbio->bi_seg_front_size = front_seg_size;
> if (seg_size > bbio->bi_seg_back_size)
> bbio->bi_seg_back_size = seg_size;
>
> --
> 2.9.5
>

2018-11-16 23:59:15

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 11/19] bcache: avoid to use bio_for_each_segment_all() in bch_bio_alloc_pages()

> - bio_for_each_segment_all(bv, bio, i) {
> + for (i = 0, bv = bio->bi_io_vec; i < bio->bi_vcnt; bv++) {

This really needs a comment. Otherwise it looks fine to me.

2018-11-19 19:44:07

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 18/19] block: kill QUEUE_FLAG_NO_SG_MERGE

On Fri, Nov 16, 2018 at 02:58:03PM +0100, Christoph Hellwig wrote:
> On Thu, Nov 15, 2018 at 04:53:05PM +0800, Ming Lei wrote:
> > Since bdced438acd83ad83a6c ("block: setup bi_phys_segments after splitting"),
> > physical segment number is mainly figured out in blk_queue_split() for
> > fast path, and the flag of BIO_SEG_VALID is set there too.
> >
> > Now only blk_recount_segments() and blk_recalc_rq_segments() use this
> > flag.
> >
> > Basically blk_recount_segments() is bypassed in fast path given BIO_SEG_VALID
> > is set in blk_queue_split().
> >
> > For another user of blk_recalc_rq_segments():
> >
> > - run in partial completion branch of blk_update_request, which is an unusual case
> >
> > - run in blk_cloned_rq_check_limits(), still not a big problem if the flag is killed
> > since dm-rq is the only user.
> >
> > Multi-page bvec is enabled now, QUEUE_FLAG_NO_SG_MERGE doesn't make sense any more.
> >
> > Cc: Dave Chinner <[email protected]>
> > Cc: Kent Overstreet <[email protected]>
> > Cc: Mike Snitzer <[email protected]>
> > Cc: [email protected]
> > Cc: Alexander Viro <[email protected]>
> > Cc: [email protected]
> > Cc: Shaohua Li <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: David Sterba <[email protected]>
> > Cc: [email protected]
> > Cc: Darrick J. Wong <[email protected]>
> > Cc: [email protected]
> > Cc: Gao Xiang <[email protected]>
> > Cc: Christoph Hellwig <[email protected]>
> > Cc: Theodore Ts'o <[email protected]>
> > Cc: [email protected]
> > Cc: Coly Li <[email protected]>
> > Cc: [email protected]
> > Cc: Boaz Harrosh <[email protected]>
> > Cc: Bob Peterson <[email protected]>
> > Cc: [email protected]
> > Signed-off-by: Ming Lei <[email protected]>
> > ---
> > block/blk-merge.c | 31 ++++++-------------------------
> > block/blk-mq-debugfs.c | 1 -
> > block/blk-mq.c | 3 ---
> > drivers/md/dm-table.c | 13 -------------
> > include/linux/blkdev.h | 1 -
> > 5 files changed, 6 insertions(+), 43 deletions(-)
> >
> > diff --git a/block/blk-merge.c b/block/blk-merge.c
> > index 153a659fde74..06be298be332 100644
> > --- a/block/blk-merge.c
> > +++ b/block/blk-merge.c
> > @@ -351,8 +351,7 @@ void blk_queue_split(struct request_queue *q, struct bio **bio)
> > EXPORT_SYMBOL(blk_queue_split);
> >
> > static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
> > - struct bio *bio,
> > - bool no_sg_merge)
> > + struct bio *bio)
> > {
> > struct bio_vec bv, bvprv = { NULL };
> > int cluster, prev = 0;
> > @@ -379,13 +378,6 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
> > nr_phys_segs = 0;
> > for_each_bio(bio) {
> > bio_for_each_bvec(bv, bio, iter) {
> > - /*
> > - * If SG merging is disabled, each bio vector is
> > - * a segment
> > - */
> > - if (no_sg_merge)
> > - goto new_segment;
> > -
> > if (prev && cluster) {
> > if (seg_size + bv.bv_len
> > > queue_max_segment_size(q))
> > @@ -420,27 +412,16 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
> >
> > void blk_recalc_rq_segments(struct request *rq)
> > {
> > - bool no_sg_merge = !!test_bit(QUEUE_FLAG_NO_SG_MERGE,
> > - &rq->q->queue_flags);
> > -
> > - rq->nr_phys_segments = __blk_recalc_rq_segments(rq->q, rq->bio,
> > - no_sg_merge);
> > + rq->nr_phys_segments = __blk_recalc_rq_segments(rq->q, rq->bio);
>
> Can we rename __blk_recalc_rq_segments to blk_recalc_rq_segments
> can kill the old blk_recalc_rq_segments now?

Sure.

Thanks,
Ming

2018-11-21 16:08:10

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()


>> Wait, I see that the bvec is still a single array per bio. When you said
>> a table I thought you meant a 2-dimentional array...
>
> I mean a new 1-d table A has to be created for multiple bios in one rq,
> and build it in the following way
>
> rq_for_each_bvec(tmp, rq, rq_iter)
> *A = tmp;
>
> Then you can pass A to iov_iter_bvec() & send().
>
> Given it is over TCP, I guess it should be doable for you to preallocate one
> 256-bvec table in one page for each request, then sets the max segment size as
> (unsigned int)-1, and max segment number as 256, the preallocated table
> should work anytime.

256 bvec table is really a lot to preallocate, especially when its not
needed, I can easily initialize the bvec_iter on the bio bvec. If this
involves preallocation of the worst-case than I don't consider this to
be an improvement.

2018-11-16 11:57:24

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 13/19] iomap & xfs: only account for new added page

On Thu, Nov 15, 2018 at 04:53:00PM +0800, Ming Lei wrote:
> After multi-page is enabled, one new page may be merged to a segment
> even though it is a new added page.
>
> This patch deals with this issue by post-check in case of merge, and
> only a freshly new added page need to be dealt with for iomap & xfs.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]
> Signed-off-by: Ming Lei <[email protected]>
> ---
> fs/iomap.c | 22 ++++++++++++++--------
> fs/xfs/xfs_aops.c | 10 ++++++++--
> include/linux/bio.h | 11 +++++++++++
> 3 files changed, 33 insertions(+), 10 deletions(-)
>
> diff --git a/fs/iomap.c b/fs/iomap.c
> index df0212560b36..a1b97a5c726a 100644
> --- a/fs/iomap.c
> +++ b/fs/iomap.c
> @@ -288,6 +288,7 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
> loff_t orig_pos = pos;
> unsigned poff, plen;
> sector_t sector;
> + bool need_account = false;
>
> if (iomap->type == IOMAP_INLINE) {
> WARN_ON_ONCE(pos);
> @@ -313,18 +314,15 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
> */
> sector = iomap_sector(iomap, pos);
> if (ctx->bio && bio_end_sector(ctx->bio) == sector) {
> - if (__bio_try_merge_page(ctx->bio, page, plen, poff))
> + if (__bio_try_merge_page(ctx->bio, page, plen, poff)) {
> + need_account = iop && bio_is_last_segment(ctx->bio,
> + page, plen, poff);

It's redundant to make this iop && ... since you already check
iop && need_account below. Maybe rename it to added_page? Also, this
indentation is wack.

> goto done;
> + }
> is_contig = true;
> }
>
> - /*
> - * If we start a new segment we need to increase the read count, and we
> - * need to do so before submitting any previous full bio to make sure
> - * that we don't prematurely unlock the page.
> - */
> - if (iop)
> - atomic_inc(&iop->read_count);
> + need_account = true;
>
> if (!ctx->bio || !is_contig || bio_full(ctx->bio)) {
> gfp_t gfp = mapping_gfp_constraint(page->mapping, GFP_KERNEL);
> @@ -347,6 +345,14 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
> __bio_add_page(ctx->bio, page, plen, poff);
> done:
> /*
> + * If we add a new page we need to increase the read count, and we
> + * need to do so before submitting any previous full bio to make sure
> + * that we don't prematurely unlock the page.
> + */
> + if (iop && need_account)
> + atomic_inc(&iop->read_count);
> +
> + /*
> * Move the caller beyond our range so that it keeps making progress.
> * For that we have to include any leading non-uptodate ranges, but
> * we can skip trailing ones as they will be handled in the next
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index 1f1829e506e8..d8e9cc9f751a 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -603,6 +603,7 @@ xfs_add_to_ioend(
> unsigned len = i_blocksize(inode);
> unsigned poff = offset & (PAGE_SIZE - 1);
> sector_t sector;
> + bool need_account;
>
> sector = xfs_fsb_to_db(ip, wpc->imap.br_startblock) +
> ((offset - XFS_FSB_TO_B(mp, wpc->imap.br_startoff)) >> 9);
> @@ -617,13 +618,18 @@ xfs_add_to_ioend(
> }
>
> if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff)) {
> - if (iop)
> - atomic_inc(&iop->write_count);
> + need_account = true;
> if (bio_full(wpc->ioend->io_bio))
> xfs_chain_bio(wpc->ioend, wbc, bdev, sector);
> __bio_add_page(wpc->ioend->io_bio, page, len, poff);
> + } else {
> + need_account = iop && bio_is_last_segment(wpc->ioend->io_bio,
> + page, len, poff);

Same here, no need for iop &&, rename it added_page, indentation is off.

> }
>
> + if (iop && need_account)
> + atomic_inc(&iop->write_count);
> +
> wpc->ioend->io_size += len;
> }
>
> diff --git a/include/linux/bio.h b/include/linux/bio.h
> index 1a2430a8b89d..5040e9a2eb09 100644
> --- a/include/linux/bio.h
> +++ b/include/linux/bio.h
> @@ -341,6 +341,17 @@ static inline struct bio_vec *bio_last_bvec_all(struct bio *bio)
> return &bio->bi_io_vec[bio->bi_vcnt - 1];
> }
>
> +/* iomap needs this helper to deal with sub-pagesize bvec */
> +static inline bool bio_is_last_segment(struct bio *bio, struct page *page,
> + unsigned int len, unsigned int off)

Indentation.

> +{
> + struct bio_vec bv;
> +
> + bvec_last_segment(bio_last_bvec_all(bio), &bv);
> +
> + return bv.bv_page == page && bv.bv_len == len && bv.bv_offset == off;
> +}
> +
> enum bip_flags {
> BIP_BLOCK_INTEGRITY = 1 << 0, /* block layer owns integrity data */
> BIP_MAPPED_INTEGRITY = 1 << 1, /* ref tag has been remapped */
> --
> 2.9.5
>

2018-11-17 00:11:56

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 18/19] block: kill QUEUE_FLAG_NO_SG_MERGE

On Thu, Nov 15, 2018 at 06:18:11PM -0800, Omar Sandoval wrote:
> This commit message wasn't very clear. Is it the case that
> QUEUE_FLAG_NO_SG_MERGE is no longer set by any drivers?

I think he wants to say that not doing S/G merging is rather pointless
with the current setup of the I/O path, as it isn't going to save
you a significant amount of cycles.

2018-11-15 19:03:31

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 12/19] block: allow bio_for_each_segment_all() to iterate over multi-page bvec

This patch introduces one extra iterator variable to bio_for_each_segment_all(),
then we can allow bio_for_each_segment_all() to iterate over multi-page bvec.

Given it is just one mechannical & simple change on all bio_for_each_segment_all()
users, this patch does tree-wide change in one single patch, so that we can
avoid to use a temporary helper for this conversion.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: Darrick J. Wong <[email protected]>
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
block/bio.c | 27 ++++++++++++++++++---------
block/blk-zoned.c | 1 +
block/bounce.c | 6 ++++--
drivers/md/bcache/btree.c | 3 ++-
drivers/md/dm-crypt.c | 3 ++-
drivers/md/raid1.c | 3 ++-
drivers/staging/erofs/data.c | 3 ++-
drivers/staging/erofs/unzip_vle.c | 3 ++-
fs/block_dev.c | 6 ++++--
fs/btrfs/compression.c | 3 ++-
fs/btrfs/disk-io.c | 3 ++-
fs/btrfs/extent_io.c | 12 ++++++++----
fs/btrfs/inode.c | 6 ++++--
fs/btrfs/raid56.c | 3 ++-
fs/crypto/bio.c | 3 ++-
fs/direct-io.c | 4 +++-
fs/exofs/ore.c | 3 ++-
fs/exofs/ore_raid.c | 3 ++-
fs/ext4/page-io.c | 3 ++-
fs/ext4/readpage.c | 3 ++-
fs/f2fs/data.c | 9 ++++++---
fs/gfs2/lops.c | 6 ++++--
fs/gfs2/meta_io.c | 3 ++-
fs/iomap.c | 6 ++++--
fs/mpage.c | 3 ++-
fs/xfs/xfs_aops.c | 5 +++--
include/linux/bio.h | 11 +++++++++--
include/linux/bvec.h | 31 +++++++++++++++++++++++++++++++
28 files changed, 129 insertions(+), 46 deletions(-)

diff --git a/block/bio.c b/block/bio.c
index d5368a445561..6486722d4d4b 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1072,8 +1072,9 @@ static int bio_copy_from_iter(struct bio *bio, struct iov_iter *iter)
{
int i;
struct bio_vec *bvec;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
ssize_t ret;

ret = copy_page_from_iter(bvec->bv_page,
@@ -1103,8 +1104,9 @@ static int bio_copy_to_iter(struct bio *bio, struct iov_iter iter)
{
int i;
struct bio_vec *bvec;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
ssize_t ret;

ret = copy_page_to_iter(bvec->bv_page,
@@ -1126,8 +1128,9 @@ void bio_free_pages(struct bio *bio)
{
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i)
+ bio_for_each_segment_all(bvec, bio, i, iter_all)
__free_page(bvec->bv_page);
}
EXPORT_SYMBOL(bio_free_pages);
@@ -1293,6 +1296,7 @@ struct bio *bio_map_user_iov(struct request_queue *q,
struct bio *bio;
int ret;
struct bio_vec *bvec;
+ struct bvec_iter_all iter_all;

if (!iov_iter_count(iter))
return ERR_PTR(-EINVAL);
@@ -1366,7 +1370,7 @@ struct bio *bio_map_user_iov(struct request_queue *q,
return bio;

out_unmap:
- bio_for_each_segment_all(bvec, bio, j) {
+ bio_for_each_segment_all(bvec, bio, j, iter_all) {
put_page(bvec->bv_page);
}
bio_put(bio);
@@ -1377,11 +1381,12 @@ static void __bio_unmap_user(struct bio *bio)
{
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

/*
* make sure we dirty pages we wrote to
*/
- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
if (bio_data_dir(bio) == READ)
set_page_dirty_lock(bvec->bv_page);

@@ -1473,8 +1478,9 @@ static void bio_copy_kern_endio_read(struct bio *bio)
char *p = bio->bi_private;
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
memcpy(p, page_address(bvec->bv_page), bvec->bv_len);
p += bvec->bv_len;
}
@@ -1583,8 +1589,9 @@ void bio_set_pages_dirty(struct bio *bio)
{
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
if (!PageCompound(bvec->bv_page))
set_page_dirty_lock(bvec->bv_page);
}
@@ -1595,8 +1602,9 @@ static void bio_release_pages(struct bio *bio)
{
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i)
+ bio_for_each_segment_all(bvec, bio, i, iter_all)
put_page(bvec->bv_page);
}

@@ -1643,8 +1651,9 @@ void bio_check_pages_dirty(struct bio *bio)
struct bio_vec *bvec;
unsigned long flags;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
if (!PageDirty(bvec->bv_page) && !PageCompound(bvec->bv_page))
goto defer;
}
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 13ba2011a306..789b09ae402a 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -123,6 +123,7 @@ static int blk_report_zones(struct gendisk *disk, sector_t sector,
unsigned int z = 0, n, nrz = *nr_zones;
sector_t capacity = get_capacity(disk);
int ret;
+ struct bvec_iter_all iter_all;

while (z < nrz && sector < capacity) {
n = nrz - z;
diff --git a/block/bounce.c b/block/bounce.c
index 36869afc258c..aee79b3e4777 100644
--- a/block/bounce.c
+++ b/block/bounce.c
@@ -165,11 +165,12 @@ static void bounce_end_io(struct bio *bio, mempool_t *pool)
struct bio_vec *bvec, orig_vec;
int i;
struct bvec_iter orig_iter = bio_orig->bi_iter;
+ struct bvec_iter_all iter_all;

/*
* free up bounce indirect pages used
*/
- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
orig_vec = bio_iter_iovec(bio_orig, orig_iter);
if (bvec->bv_page != orig_vec.bv_page) {
dec_zone_page_state(bvec->bv_page, NR_BOUNCE);
@@ -292,6 +293,7 @@ static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig,
bool bounce = false;
int sectors = 0;
bool passthrough = bio_is_passthrough(*bio_orig);
+ struct bvec_iter_all iter_all;

bio_for_each_segment(from, *bio_orig, iter) {
if (i++ < BIO_MAX_PAGES)
@@ -311,7 +313,7 @@ static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig,
bio = bounce_clone_bio(*bio_orig, GFP_NOIO, passthrough ? NULL :
&bounce_bio_set);

- bio_for_each_segment_all(to, bio, i) {
+ bio_for_each_segment_all(to, bio, i, iter_all) {
struct page *page = to->bv_page;

if (page_to_pfn(page) <= q->limits.bounce_pfn)
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
index 3f4211b5cd33..6242ae4e2127 100644
--- a/drivers/md/bcache/btree.c
+++ b/drivers/md/bcache/btree.c
@@ -427,8 +427,9 @@ static void do_btree_node_write(struct btree *b)
int j;
struct bio_vec *bv;
void *base = (void *) ((unsigned long) i & ~(PAGE_SIZE - 1));
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bv, b->bio, j)
+ bio_for_each_segment_all(bv, b->bio, j, iter_all)
memcpy(page_address(bv->bv_page),
base + j * PAGE_SIZE, PAGE_SIZE);

diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index b8eec515a003..a0dcf28c01b5 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1447,8 +1447,9 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone)
{
unsigned int i;
struct bio_vec *bv;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bv, clone, i) {
+ bio_for_each_segment_all(bv, clone, i, iter_all) {
BUG_ON(!bv->bv_page);
mempool_free(bv->bv_page, &cc->page_pool);
}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 1d54109071cc..6f74a3b06c7e 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2114,13 +2114,14 @@ static void process_checks(struct r1bio *r1_bio)
struct page **spages = get_resync_pages(sbio)->pages;
struct bio_vec *bi;
int page_len[RESYNC_PAGES] = { 0 };
+ struct bvec_iter_all iter_all;

if (sbio->bi_end_io != end_sync_read)
continue;
/* Now we can 'fixup' the error value */
sbio->bi_status = 0;

- bio_for_each_segment_all(bi, sbio, j)
+ bio_for_each_segment_all(bi, sbio, j, iter_all)
page_len[j] = bi->bv_len;

if (!status) {
diff --git a/drivers/staging/erofs/data.c b/drivers/staging/erofs/data.c
index 6384f73e5418..96240ceca02a 100644
--- a/drivers/staging/erofs/data.c
+++ b/drivers/staging/erofs/data.c
@@ -20,8 +20,9 @@ static inline void read_endio(struct bio *bio)
int i;
struct bio_vec *bvec;
const blk_status_t err = bio->bi_status;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
struct page *page = bvec->bv_page;

/* page is already locked */
diff --git a/drivers/staging/erofs/unzip_vle.c b/drivers/staging/erofs/unzip_vle.c
index 79d3ba62b298..41a8a9399863 100644
--- a/drivers/staging/erofs/unzip_vle.c
+++ b/drivers/staging/erofs/unzip_vle.c
@@ -731,11 +731,12 @@ static inline void z_erofs_vle_read_endio(struct bio *bio)
const blk_status_t err = bio->bi_status;
unsigned int i;
struct bio_vec *bvec;
+ struct bvec_iter_all iter_all;
#ifdef EROFS_FS_HAS_MANAGED_CACHE
struct address_space *mngda = NULL;
#endif

- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
struct page *page = bvec->bv_page;
bool cachemngd = false;

diff --git a/fs/block_dev.c b/fs/block_dev.c
index c039abfb2052..0fcb5515dca7 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -197,6 +197,7 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter,
ssize_t ret;
blk_qc_t qc;
int i;
+ struct bvec_iter_all iter_all;

if ((pos | iov_iter_alignment(iter)) &
(bdev_logical_block_size(bdev) - 1))
@@ -246,7 +247,7 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter,
}
__set_current_state(TASK_RUNNING);

- bio_for_each_segment_all(bvec, &bio, i) {
+ bio_for_each_segment_all(bvec, &bio, i, iter_all) {
if (should_dirty && !PageCompound(bvec->bv_page))
set_page_dirty_lock(bvec->bv_page);
put_page(bvec->bv_page);
@@ -314,8 +315,9 @@ static void blkdev_bio_end_io(struct bio *bio)
} else {
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i)
+ bio_for_each_segment_all(bvec, bio, i, iter_all)
put_page(bvec->bv_page);
bio_put(bio);
}
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 161e14b8b180..ac3c201377e1 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -162,13 +162,14 @@ static void end_compressed_bio_read(struct bio *bio)
} else {
int i;
struct bio_vec *bvec;
+ struct bvec_iter_all iter_all;

/*
* we have verified the checksum already, set page
* checked so the end_io handlers know about it
*/
ASSERT(!bio_flagged(bio, BIO_CLONED));
- bio_for_each_segment_all(bvec, cb->orig_bio, i)
+ bio_for_each_segment_all(bvec, cb->orig_bio, i, iter_all)
SetPageChecked(bvec->bv_page);

bio_endio(cb->orig_bio);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index b0ab41da91d1..834efe6e3137 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -819,9 +819,10 @@ static blk_status_t btree_csum_one_bio(struct bio *bio)
struct bio_vec *bvec;
struct btrfs_root *root;
int i, ret = 0;
+ struct bvec_iter_all iter_all;

ASSERT(!bio_flagged(bio, BIO_CLONED));
- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
root = BTRFS_I(bvec->bv_page->mapping->host)->root;
ret = csum_dirty_buffer(root->fs_info, bvec->bv_page);
if (ret)
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 874bb9aeebdc..9373eb8ade06 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2352,10 +2352,11 @@ static unsigned btrfs_bio_pages_all(struct bio *bio)
{
unsigned i;
struct bio_vec *bv;
+ struct bvec_iter_all iter_all;

WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED));

- bio_for_each_segment_all(bv, bio, i)
+ bio_for_each_segment_all(bv, bio, i, iter_all)
;
return i;
}
@@ -2457,9 +2458,10 @@ static void end_bio_extent_writepage(struct bio *bio)
u64 start;
u64 end;
int i;
+ struct bvec_iter_all iter_all;

ASSERT(!bio_flagged(bio, BIO_CLONED));
- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
struct page *page = bvec->bv_page;
struct inode *inode = page->mapping->host;
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
@@ -2528,9 +2530,10 @@ static void end_bio_extent_readpage(struct bio *bio)
int mirror;
int ret;
int i;
+ struct bvec_iter_all iter_all;

ASSERT(!bio_flagged(bio, BIO_CLONED));
- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
struct page *page = bvec->bv_page;
struct inode *inode = page->mapping->host;
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
@@ -3682,9 +3685,10 @@ static void end_bio_extent_buffer_writepage(struct bio *bio)
struct bio_vec *bvec;
struct extent_buffer *eb;
int i, done;
+ struct bvec_iter_all iter_all;

ASSERT(!bio_flagged(bio, BIO_CLONED));
- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
struct page *page = bvec->bv_page;

eb = (struct extent_buffer *)page->private;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index d3df5b52278c..0ec8c1dd328f 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7811,6 +7811,7 @@ static void btrfs_retry_endio_nocsum(struct bio *bio)
struct bio_vec *bvec;
struct extent_io_tree *io_tree, *failure_tree;
int i;
+ struct bvec_iter_all iter_all;

if (bio->bi_status)
goto end;
@@ -7822,7 +7823,7 @@ static void btrfs_retry_endio_nocsum(struct bio *bio)

done->uptodate = 1;
ASSERT(!bio_flagged(bio, BIO_CLONED));
- bio_for_each_segment_all(bvec, bio, i)
+ bio_for_each_segment_all(bvec, bio, i, iter_all)
clean_io_failure(BTRFS_I(inode)->root->fs_info, failure_tree,
io_tree, done->start, bvec->bv_page,
btrfs_ino(BTRFS_I(inode)), 0);
@@ -7901,6 +7902,7 @@ static void btrfs_retry_endio(struct bio *bio)
int uptodate;
int ret;
int i;
+ struct bvec_iter_all iter_all;

if (bio->bi_status)
goto end;
@@ -7914,7 +7916,7 @@ static void btrfs_retry_endio(struct bio *bio)
failure_tree = &BTRFS_I(inode)->io_failure_tree;

ASSERT(!bio_flagged(bio, BIO_CLONED));
- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
ret = __readpage_endio_check(inode, io_bio, i, bvec->bv_page,
bvec->bv_offset, done->start,
bvec->bv_len);
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index df41d7049936..e33a99871d60 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1443,10 +1443,11 @@ static void set_bio_pages_uptodate(struct bio *bio)
{
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

ASSERT(!bio_flagged(bio, BIO_CLONED));

- bio_for_each_segment_all(bvec, bio, i)
+ bio_for_each_segment_all(bvec, bio, i, iter_all)
SetPageUptodate(bvec->bv_page);
}

diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c
index 0959044c5cee..5759bcd018cd 100644
--- a/fs/crypto/bio.c
+++ b/fs/crypto/bio.c
@@ -30,8 +30,9 @@ static void __fscrypt_decrypt_bio(struct bio *bio, bool done)
{
struct bio_vec *bv;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bv, bio, i) {
+ bio_for_each_segment_all(bv, bio, i, iter_all) {
struct page *page = bv->bv_page;
int ret = fscrypt_decrypt_page(page->mapping->host, page,
PAGE_SIZE, 0, page->index);
diff --git a/fs/direct-io.c b/fs/direct-io.c
index ea07d5a34317..5904fc2e180c 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -551,7 +551,9 @@ static blk_status_t dio_bio_complete(struct dio *dio, struct bio *bio)
if (dio->is_async && dio->op == REQ_OP_READ && dio->should_dirty) {
bio_check_pages_dirty(bio); /* transfers ownership */
} else {
- bio_for_each_segment_all(bvec, bio, i) {
+ struct bvec_iter_all iter_all;
+
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
struct page *page = bvec->bv_page;

if (dio->op == REQ_OP_READ && !PageCompound(page) &&
diff --git a/fs/exofs/ore.c b/fs/exofs/ore.c
index 5331a15a61f1..24a8e34882e9 100644
--- a/fs/exofs/ore.c
+++ b/fs/exofs/ore.c
@@ -420,8 +420,9 @@ static void _clear_bio(struct bio *bio)
{
struct bio_vec *bv;
unsigned i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bv, bio, i) {
+ bio_for_each_segment_all(bv, bio, i, iter_all) {
unsigned this_count = bv->bv_len;

if (likely(PAGE_SIZE == this_count))
diff --git a/fs/exofs/ore_raid.c b/fs/exofs/ore_raid.c
index 199590f36203..e83bab54b03e 100644
--- a/fs/exofs/ore_raid.c
+++ b/fs/exofs/ore_raid.c
@@ -468,11 +468,12 @@ static void _mark_read4write_pages_uptodate(struct ore_io_state *ios, int ret)
/* loop on all devices all pages */
for (d = 0; d < ios->numdevs; d++) {
struct bio *bio = ios->per_dev[d].bio;
+ struct bvec_iter_all iter_all;

if (!bio)
continue;

- bio_for_each_segment_all(bv, bio, i) {
+ bio_for_each_segment_all(bv, bio, i, iter_all) {
struct page *page = bv->bv_page;

SetPageUptodate(page);
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index db7590178dfc..0644b4e7d6d4 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -63,8 +63,9 @@ static void ext4_finish_bio(struct bio *bio)
{
int i;
struct bio_vec *bvec;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
struct page *page = bvec->bv_page;
#ifdef CONFIG_EXT4_FS_ENCRYPTION
struct page *data_page = NULL;
diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
index f461d75ac049..b0d9537bc797 100644
--- a/fs/ext4/readpage.c
+++ b/fs/ext4/readpage.c
@@ -72,6 +72,7 @@ static void mpage_end_io(struct bio *bio)
{
struct bio_vec *bv;
int i;
+ struct bvec_iter_all iter_all;

if (ext4_bio_encrypted(bio)) {
if (bio->bi_status) {
@@ -81,7 +82,7 @@ static void mpage_end_io(struct bio *bio)
return;
}
}
- bio_for_each_segment_all(bv, bio, i) {
+ bio_for_each_segment_all(bv, bio, i, iter_all) {
struct page *page = bv->bv_page;

if (!bio->bi_status) {
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index b293cb3e27a2..d28f482a0d52 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -87,8 +87,9 @@ static void __read_end_io(struct bio *bio)
struct page *page;
struct bio_vec *bv;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bv, bio, i) {
+ bio_for_each_segment_all(bv, bio, i, iter_all) {
page = bv->bv_page;

/* PG_error was set if any post_read step failed */
@@ -164,13 +165,14 @@ static void f2fs_write_end_io(struct bio *bio)
struct f2fs_sb_info *sbi = bio->bi_private;
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

if (time_to_inject(sbi, FAULT_WRITE_IO)) {
f2fs_show_injection_info(FAULT_WRITE_IO);
bio->bi_status = BLK_STS_IOERR;
}

- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
struct page *page = bvec->bv_page;
enum count_type type = WB_DATA_TYPE(page);

@@ -347,6 +349,7 @@ static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode,
struct bio_vec *bvec;
struct page *target;
int i;
+ struct bvec_iter_all iter_all;

if (!io->bio)
return false;
@@ -354,7 +357,7 @@ static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode,
if (!inode && !page && !ino)
return true;

- bio_for_each_segment_all(bvec, io->bio, i) {
+ bio_for_each_segment_all(bvec, io->bio, i, iter_all) {

if (bvec->bv_page->mapping)
target = bvec->bv_page;
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 4c7069b8f3c1..f2f165620161 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -168,7 +168,8 @@ u64 gfs2_log_bmap(struct gfs2_sbd *sdp)
* that is pinned in the pagecache.
*/

-static void gfs2_end_log_write_bh(struct gfs2_sbd *sdp, struct bio_vec *bvec,
+static void gfs2_end_log_write_bh(struct gfs2_sbd *sdp,
+ struct bio_vec *bvec,
blk_status_t error)
{
struct buffer_head *bh, *next;
@@ -207,6 +208,7 @@ static void gfs2_end_log_write(struct bio *bio)
struct bio_vec *bvec;
struct page *page;
int i;
+ struct bvec_iter_all iter_all;

if (bio->bi_status) {
fs_err(sdp, "Error %d writing to journal, jid=%u\n",
@@ -214,7 +216,7 @@ static void gfs2_end_log_write(struct bio *bio)
wake_up(&sdp->sd_logd_waitq);
}

- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
page = bvec->bv_page;
if (page_has_buffers(page))
gfs2_end_log_write_bh(sdp, bvec, bio->bi_status);
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index be9c0bf697fe..3201342404a7 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -190,8 +190,9 @@ static void gfs2_meta_read_endio(struct bio *bio)
{
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i) {
+ bio_for_each_segment_all(bvec, bio, i, iter_all) {
struct page *page = bvec->bv_page;
struct buffer_head *bh = page_buffers(page);
unsigned int len = bvec->bv_len;
diff --git a/fs/iomap.c b/fs/iomap.c
index f61d13dfdf09..df0212560b36 100644
--- a/fs/iomap.c
+++ b/fs/iomap.c
@@ -262,8 +262,9 @@ iomap_read_end_io(struct bio *bio)
int error = blk_status_to_errno(bio->bi_status);
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i)
+ bio_for_each_segment_all(bvec, bio, i, iter_all)
iomap_read_page_end_io(bvec, error);
bio_put(bio);
}
@@ -1541,8 +1542,9 @@ static void iomap_dio_bio_end_io(struct bio *bio)
} else {
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bvec, bio, i)
+ bio_for_each_segment_all(bvec, bio, i, iter_all)
put_page(bvec->bv_page);
bio_put(bio);
}
diff --git a/fs/mpage.c b/fs/mpage.c
index c820dc9bebab..3f19da75178b 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -48,8 +48,9 @@ static void mpage_end_io(struct bio *bio)
{
struct bio_vec *bv;
int i;
+ struct bvec_iter_all iter_all;

- bio_for_each_segment_all(bv, bio, i) {
+ bio_for_each_segment_all(bv, bio, i, iter_all) {
struct page *page = bv->bv_page;
page_endio(page, bio_op(bio),
blk_status_to_errno(bio->bi_status));
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 338b9d9984e0..1f1829e506e8 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -62,7 +62,7 @@ xfs_find_daxdev_for_inode(
static void
xfs_finish_page_writeback(
struct inode *inode,
- struct bio_vec *bvec,
+ struct bio_vec *bvec,
int error)
{
struct iomap_page *iop = to_iomap_page(bvec->bv_page);
@@ -98,6 +98,7 @@ xfs_destroy_ioend(
for (bio = &ioend->io_inline_bio; bio; bio = next) {
struct bio_vec *bvec;
int i;
+ struct bvec_iter_all iter_all;

/*
* For the last bio, bi_private points to the ioend, so we
@@ -109,7 +110,7 @@ xfs_destroy_ioend(
next = bio->bi_private;

/* walk each page on bio, ending page IO on them */
- bio_for_each_segment_all(bvec, bio, i)
+ bio_for_each_segment_all(bvec, bio, i, iter_all)
xfs_finish_page_writeback(inode, bvec, error);
bio_put(bio);
}
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 3496c816946e..1a2430a8b89d 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -131,12 +131,19 @@ static inline bool bio_full(struct bio *bio)
return bio->bi_vcnt >= bio->bi_max_vecs;
}

+#define bvec_for_each_segment(bv, bvl, i, iter_all) \
+ for (bv = bvec_init_iter_all(&iter_all); \
+ (iter_all.done < (bvl)->bv_len) && \
+ ((bvec_next_segment((bvl), &iter_all)), 1); \
+ iter_all.done += bv->bv_len, i += 1)
+
/*
* drivers should _never_ use the all version - the bio may have been split
* before it got to the driver and the driver won't own all of it
*/
-#define bio_for_each_segment_all(bvl, bio, i) \
- for (i = 0, bvl = (bio)->bi_io_vec; i < (bio)->bi_vcnt; i++, bvl++)
+#define bio_for_each_segment_all(bvl, bio, i, iter_all) \
+ for (i = 0, iter_all.idx = 0; iter_all.idx < (bio)->bi_vcnt; iter_all.idx++) \
+ bvec_for_each_segment(bvl, &((bio)->bi_io_vec[iter_all.idx]), i, iter_all)

static inline void __bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
unsigned bytes, bool mp)
diff --git a/include/linux/bvec.h b/include/linux/bvec.h
index 01616a0b6220..02f26d2b59ad 100644
--- a/include/linux/bvec.h
+++ b/include/linux/bvec.h
@@ -82,6 +82,12 @@ struct bvec_iter {
current bvec */
};

+struct bvec_iter_all {
+ struct bio_vec bv;
+ int idx;
+ unsigned done;
+};
+
/*
* various member access, note that bio_data should of course not be used
* on highmem page vectors
@@ -216,6 +222,31 @@ static inline bool mp_bvec_iter_advance(const struct bio_vec *bv,
.bi_bvec_done = 0, \
}

+static inline struct bio_vec *bvec_init_iter_all(struct bvec_iter_all *iter_all)
+{
+ iter_all->bv.bv_page = NULL;
+ iter_all->done = 0;
+
+ return &iter_all->bv;
+}
+
+/* used for chunk_for_each_segment */
+static inline void bvec_next_segment(const struct bio_vec *bvec,
+ struct bvec_iter_all *iter_all)
+{
+ struct bio_vec *bv = &iter_all->bv;
+
+ if (bv->bv_page) {
+ bv->bv_page += 1;
+ bv->bv_offset = 0;
+ } else {
+ bv->bv_page = bvec->bv_page;
+ bv->bv_offset = bvec->bv_offset;
+ }
+ bv->bv_len = min_t(unsigned int, PAGE_SIZE - bv->bv_offset,
+ bvec->bv_len - iter_all->done);
+}
+
/*
* Get the last singlepage segment from the multipage bvec and store it
* in @seg
--
2.9.5

2018-11-19 18:32:15

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 07/19] btrfs: use bvec_last_segment to get bio's last page

On Fri, Nov 16, 2018 at 02:37:10PM +0100, Christoph Hellwig wrote:
> On Thu, Nov 15, 2018 at 04:52:54PM +0800, Ming Lei wrote:
> > index 2955a4ea2fa8..161e14b8b180 100644
> > --- a/fs/btrfs/compression.c
> > +++ b/fs/btrfs/compression.c
> > @@ -400,8 +400,11 @@ blk_status_t btrfs_submit_compressed_write(struct inode *inode, u64 start,
> > static u64 bio_end_offset(struct bio *bio)
> > {
> > struct bio_vec *last = bio_last_bvec_all(bio);
> > + struct bio_vec bv;
> >
> > - return page_offset(last->bv_page) + last->bv_len + last->bv_offset;
> > + bvec_last_segment(last, &bv);
> > +
> > + return page_offset(bv.bv_page) + bv.bv_len + bv.bv_offset;
>
> I don't think we need this. If last is a multi-page bvec bv_offset
> will already contain the correct offset from the first page.

Yeah, it is true for this specific case, looks we can drop this patch.


thanks,
Ming

2018-11-15 19:03:38

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 13/19] iomap & xfs: only account for new added page

After multi-page is enabled, one new page may be merged to a segment
even though it is a new added page.

This patch deals with this issue by post-check in case of merge, and
only a freshly new added page need to be dealt with for iomap & xfs.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
fs/iomap.c | 22 ++++++++++++++--------
fs/xfs/xfs_aops.c | 10 ++++++++--
include/linux/bio.h | 11 +++++++++++
3 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/fs/iomap.c b/fs/iomap.c
index df0212560b36..a1b97a5c726a 100644
--- a/fs/iomap.c
+++ b/fs/iomap.c
@@ -288,6 +288,7 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
loff_t orig_pos = pos;
unsigned poff, plen;
sector_t sector;
+ bool need_account = false;

if (iomap->type == IOMAP_INLINE) {
WARN_ON_ONCE(pos);
@@ -313,18 +314,15 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
*/
sector = iomap_sector(iomap, pos);
if (ctx->bio && bio_end_sector(ctx->bio) == sector) {
- if (__bio_try_merge_page(ctx->bio, page, plen, poff))
+ if (__bio_try_merge_page(ctx->bio, page, plen, poff)) {
+ need_account = iop && bio_is_last_segment(ctx->bio,
+ page, plen, poff);
goto done;
+ }
is_contig = true;
}

- /*
- * If we start a new segment we need to increase the read count, and we
- * need to do so before submitting any previous full bio to make sure
- * that we don't prematurely unlock the page.
- */
- if (iop)
- atomic_inc(&iop->read_count);
+ need_account = true;

if (!ctx->bio || !is_contig || bio_full(ctx->bio)) {
gfp_t gfp = mapping_gfp_constraint(page->mapping, GFP_KERNEL);
@@ -347,6 +345,14 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
__bio_add_page(ctx->bio, page, plen, poff);
done:
/*
+ * If we add a new page we need to increase the read count, and we
+ * need to do so before submitting any previous full bio to make sure
+ * that we don't prematurely unlock the page.
+ */
+ if (iop && need_account)
+ atomic_inc(&iop->read_count);
+
+ /*
* Move the caller beyond our range so that it keeps making progress.
* For that we have to include any leading non-uptodate ranges, but
* we can skip trailing ones as they will be handled in the next
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 1f1829e506e8..d8e9cc9f751a 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -603,6 +603,7 @@ xfs_add_to_ioend(
unsigned len = i_blocksize(inode);
unsigned poff = offset & (PAGE_SIZE - 1);
sector_t sector;
+ bool need_account;

sector = xfs_fsb_to_db(ip, wpc->imap.br_startblock) +
((offset - XFS_FSB_TO_B(mp, wpc->imap.br_startoff)) >> 9);
@@ -617,13 +618,18 @@ xfs_add_to_ioend(
}

if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff)) {
- if (iop)
- atomic_inc(&iop->write_count);
+ need_account = true;
if (bio_full(wpc->ioend->io_bio))
xfs_chain_bio(wpc->ioend, wbc, bdev, sector);
__bio_add_page(wpc->ioend->io_bio, page, len, poff);
+ } else {
+ need_account = iop && bio_is_last_segment(wpc->ioend->io_bio,
+ page, len, poff);
}

+ if (iop && need_account)
+ atomic_inc(&iop->write_count);
+
wpc->ioend->io_size += len;
}

diff --git a/include/linux/bio.h b/include/linux/bio.h
index 1a2430a8b89d..5040e9a2eb09 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -341,6 +341,17 @@ static inline struct bio_vec *bio_last_bvec_all(struct bio *bio)
return &bio->bi_io_vec[bio->bi_vcnt - 1];
}

+/* iomap needs this helper to deal with sub-pagesize bvec */
+static inline bool bio_is_last_segment(struct bio *bio, struct page *page,
+ unsigned int len, unsigned int off)
+{
+ struct bio_vec bv;
+
+ bvec_last_segment(bio_last_bvec_all(bio), &bv);
+
+ return bv.bv_page == page && bv.bv_len == len && bv.bv_offset == off;
+}
+
enum bip_flags {
BIP_BLOCK_INTEGRITY = 1 << 0, /* block layer owns integrity data */
BIP_MAPPED_INTEGRITY = 1 << 1, /* ref tag has been remapped */
--
2.9.5

2018-11-19 19:27:41

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 15/19] block: always define BIO_MAX_PAGES as 256

On Thu, Nov 15, 2018 at 05:59:36PM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:53:02PM +0800, Ming Lei wrote:
> > Now multi-page bvec can cover CONFIG_THP_SWAP, so we don't need to
> > increase BIO_MAX_PAGES for it.
>
> You mentioned to it in the cover letter, but this needs more explanation
> in the commit message. Why did CONFIG_THP_SWAP require > 256? Why does
> multipage bvecs remove that requirement?

CONFIG_THP_SWAP needs to split one TH page into normal pages and adds
them all to one bio. With multipage-bvec, it just takes one bvec to
hold them all.

thanks,
Ming

2018-11-16 12:22:10

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 17/19] block: don't use bio->bi_vcnt to figure out segment number

On Thu, Nov 15, 2018 at 04:53:04PM +0800, Ming Lei wrote:
> It is wrong to use bio->bi_vcnt to figure out how many segments
> there are in the bio even though CLONED flag isn't set on this bio,
> because this bio may be splitted or advanced.
>
> So always use bio_segments() in blk_recount_segments(), and it shouldn't
> cause any performance loss now because the physical segment number is figured
> out in blk_queue_split() and BIO_SEG_VALID is set meantime since
> bdced438acd83ad83a6c ("block: setup bi_phys_segments after splitting").
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Fixes: 7f60dcaaf91 ("block: blk-merge: fix blk_recount_segments()")

>From what I can tell, the problem was originally introduced by
76d8137a3113 ("blk-merge: recaculate segment if it isn't less than max segments")

Is that right?

> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]
> Signed-off-by: Ming Lei <[email protected]>
> ---
> block/blk-merge.c | 8 +-------
> 1 file changed, 1 insertion(+), 7 deletions(-)
>
> diff --git a/block/blk-merge.c b/block/blk-merge.c
> index cb9f49bcfd36..153a659fde74 100644
> --- a/block/blk-merge.c
> +++ b/block/blk-merge.c
> @@ -429,13 +429,7 @@ void blk_recalc_rq_segments(struct request *rq)
>
> void blk_recount_segments(struct request_queue *q, struct bio *bio)
> {
> - unsigned short seg_cnt;
> -
> - /* estimate segment number by bi_vcnt for non-cloned bio */
> - if (bio_flagged(bio, BIO_CLONED))
> - seg_cnt = bio_segments(bio);
> - else
> - seg_cnt = bio->bi_vcnt;
> + unsigned short seg_cnt = bio_segments(bio);
>
> if (test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags) &&
> (seg_cnt < queue_max_segments(q)))
> --
> 2.9.5
>

2018-11-21 15:14:55

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()


>> Yeah, that is the most common example, given merge is enabled
>> in most of cases. If the driver or device doesn't care merge,
>> you can disable it and always get single bio request, then the
>> bio's bvec table can be reused for send().
>
> Does bvec_iter span bvecs with your patches? I didn't see that change?

Wait, I see that the bvec is still a single array per bio. When you said
a table I thought you meant a 2-dimentional array...

Unless I'm not mistaken, I think that the change is pretty simple then.
However, nvme-tcp still needs to be bio aware unless we have some
abstraction in place.. Which will mean that nvme-tcp will need to
open-code bio_bvecs.

2018-11-16 23:25:28

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 01/19] block: introduce multi-page page bvec helpers

> -#define bvec_iter_page(bvec, iter) \
> +#define mp_bvec_iter_page(bvec, iter) \
> (__bvec_iter_bvec((bvec), (iter))->bv_page)
>
> -#define bvec_iter_len(bvec, iter) \
> +#define mp_bvec_iter_len(bvec, iter) \

I'd much prefer if we would stick to the segment naming that
we also use in the higher level helper.

So segment_iter_page, segment_iter_len, etc.

> + * This helpers are for building sp bvec in flight.

Please spell out single page, sp is not easy understandable.

2018-11-16 12:06:54

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 14/19] block: enable multipage bvecs

On Thu, Nov 15, 2018 at 04:53:01PM +0800, Ming Lei wrote:
> This patch pulls the trigger for multi-page bvecs.
>
> Now any request queue which supports queue cluster will see multi-page
> bvecs.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]
> Signed-off-by: Ming Lei <[email protected]>
> ---
> block/bio.c | 24 ++++++++++++++++++------
> 1 file changed, 18 insertions(+), 6 deletions(-)
>
> diff --git a/block/bio.c b/block/bio.c
> index 6486722d4d4b..ed6df6f8e63d 100644
> --- a/block/bio.c
> +++ b/block/bio.c

This comment above __bio_try_merge_page() doesn't make sense after this
change:

This is a
a useful optimisation for file systems with a block size smaller than the
page size.

Can you please get rid of it in this patch?

> @@ -767,12 +767,24 @@ bool __bio_try_merge_page(struct bio *bio, struct page *page,
>
> if (bio->bi_vcnt > 0) {
> struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1];
> -
> - if (page == bv->bv_page && off == bv->bv_offset + bv->bv_len) {
> - bv->bv_len += len;
> - bio->bi_iter.bi_size += len;
> - return true;
> - }
> + struct request_queue *q = NULL;
> +
> + if (page == bv->bv_page && off == (bv->bv_offset + bv->bv_len)
> + && (off + len) <= PAGE_SIZE)
> + goto merge;

The parentheses around (bv->bv_offset + bv->bv_len) and (off + len) are
unnecessary noise.

What's the point of the new (off + len) <= PAGE_SIZE check?

> +
> + if (bio->bi_disk)
> + q = bio->bi_disk->queue;
> +
> + /* disable multi-page bvec too if cluster isn't enabled */
> + if (!q || !blk_queue_cluster(q) ||
> + ((page_to_phys(bv->bv_page) + bv->bv_offset + bv->bv_len) !=
> + (page_to_phys(page) + off)))

More unnecessary parentheses here.

> + return false;
> + merge:
> + bv->bv_len += len;
> + bio->bi_iter.bi_size += len;
> + return true;
> }
> return false;
> }
> --
> 2.9.5
>

2018-11-15 19:05:47

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 19/19] block: kill BLK_MQ_F_SG_MERGE

QUEUE_FLAG_NO_SG_MERGE has been killed, so kill BLK_MQ_F_SG_MERGE too.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
block/blk-mq-debugfs.c | 1 -
drivers/block/loop.c | 2 +-
drivers/block/nbd.c | 2 +-
drivers/block/rbd.c | 2 +-
drivers/block/skd_main.c | 1 -
drivers/block/xen-blkfront.c | 2 +-
drivers/md/dm-rq.c | 2 +-
drivers/mmc/core/queue.c | 3 +--
drivers/scsi/scsi_lib.c | 2 +-
include/linux/blk-mq.h | 1 -
10 files changed, 7 insertions(+), 11 deletions(-)

diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c
index e188b1090759..e1c12358391a 100644
--- a/block/blk-mq-debugfs.c
+++ b/block/blk-mq-debugfs.c
@@ -250,7 +250,6 @@ static const char *const alloc_policy_name[] = {
static const char *const hctx_flag_name[] = {
HCTX_FLAG_NAME(SHOULD_MERGE),
HCTX_FLAG_NAME(TAG_SHARED),
- HCTX_FLAG_NAME(SG_MERGE),
HCTX_FLAG_NAME(BLOCKING),
HCTX_FLAG_NAME(NO_SCHED),
};
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index a3fd418ec637..d509902a8046 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1907,7 +1907,7 @@ static int loop_add(struct loop_device **l, int i)
lo->tag_set.queue_depth = 128;
lo->tag_set.numa_node = NUMA_NO_NODE;
lo->tag_set.cmd_size = sizeof(struct loop_cmd);
- lo->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+ lo->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
lo->tag_set.driver_data = lo;

err = blk_mq_alloc_tag_set(&lo->tag_set);
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 08696f5f00bb..999c94de78e5 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -1570,7 +1570,7 @@ static int nbd_dev_add(int index)
nbd->tag_set.numa_node = NUMA_NO_NODE;
nbd->tag_set.cmd_size = sizeof(struct nbd_cmd);
nbd->tag_set.flags = BLK_MQ_F_SHOULD_MERGE |
- BLK_MQ_F_SG_MERGE | BLK_MQ_F_BLOCKING;
+ BLK_MQ_F_BLOCKING;
nbd->tag_set.driver_data = nbd;

err = blk_mq_alloc_tag_set(&nbd->tag_set);
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 8e5140bbf241..3dfd300b5283 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -3988,7 +3988,7 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
rbd_dev->tag_set.ops = &rbd_mq_ops;
rbd_dev->tag_set.queue_depth = rbd_dev->opts->queue_depth;
rbd_dev->tag_set.numa_node = NUMA_NO_NODE;
- rbd_dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+ rbd_dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
rbd_dev->tag_set.nr_hw_queues = 1;
rbd_dev->tag_set.cmd_size = sizeof(struct work_struct);

diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c
index a10d5736d8f7..a7040f9a1b1b 100644
--- a/drivers/block/skd_main.c
+++ b/drivers/block/skd_main.c
@@ -2843,7 +2843,6 @@ static int skd_cons_disk(struct skd_device *skdev)
skdev->sgs_per_request * sizeof(struct scatterlist);
skdev->tag_set.numa_node = NUMA_NO_NODE;
skdev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE |
- BLK_MQ_F_SG_MERGE |
BLK_ALLOC_POLICY_TO_MQ_FLAG(BLK_TAG_ALLOC_FIFO);
skdev->tag_set.driver_data = skdev;
rc = blk_mq_alloc_tag_set(&skdev->tag_set);
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 56452cabce5b..297412bf23e1 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -977,7 +977,7 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size,
} else
info->tag_set.queue_depth = BLK_RING_SIZE(info);
info->tag_set.numa_node = NUMA_NO_NODE;
- info->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+ info->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
info->tag_set.cmd_size = sizeof(struct blkif_req);
info->tag_set.driver_data = info;

diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c
index 7cd36e4d1310..140ada0b99fc 100644
--- a/drivers/md/dm-rq.c
+++ b/drivers/md/dm-rq.c
@@ -536,7 +536,7 @@ int dm_mq_init_request_queue(struct mapped_device *md, struct dm_table *t)
md->tag_set->ops = &dm_mq_ops;
md->tag_set->queue_depth = dm_get_blk_mq_queue_depth();
md->tag_set->numa_node = md->numa_node_id;
- md->tag_set->flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+ md->tag_set->flags = BLK_MQ_F_SHOULD_MERGE;
md->tag_set->nr_hw_queues = dm_get_blk_mq_nr_hw_queues();
md->tag_set->driver_data = md;

diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
index 6edffeed9953..6a0e9f6b6412 100644
--- a/drivers/mmc/core/queue.c
+++ b/drivers/mmc/core/queue.c
@@ -387,8 +387,7 @@ static int mmc_mq_init_queue(struct mmc_queue *mq, int q_depth,
mq->tag_set.ops = mq_ops;
mq->tag_set.queue_depth = q_depth;
mq->tag_set.numa_node = NUMA_NO_NODE;
- mq->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE |
- BLK_MQ_F_BLOCKING;
+ mq->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING;
mq->tag_set.nr_hw_queues = 1;
mq->tag_set.cmd_size = sizeof(struct mmc_queue_req);
mq->tag_set.driver_data = mq;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 5d83a162d03b..c7b0e4ff5e6d 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1883,7 +1883,7 @@ int scsi_mq_setup_tags(struct Scsi_Host *shost)
shost->tag_set.queue_depth = shost->can_queue;
shost->tag_set.cmd_size = cmd_size;
shost->tag_set.numa_node = NUMA_NO_NODE;
- shost->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE;
+ shost->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
shost->tag_set.flags |=
BLK_ALLOC_POLICY_TO_MQ_FLAG(shost->hostt->tag_alloc_policy);
shost->tag_set.driver_data = shost;
diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h
index 929e8abc5535..ca7389d7e04f 100644
--- a/include/linux/blk-mq.h
+++ b/include/linux/blk-mq.h
@@ -211,7 +211,6 @@ struct blk_mq_ops {
enum {
BLK_MQ_F_SHOULD_MERGE = 1 << 0,
BLK_MQ_F_TAG_SHARED = 1 << 1,
- BLK_MQ_F_SG_MERGE = 1 << 2,
BLK_MQ_F_BLOCKING = 1 << 5,
BLK_MQ_F_NO_SCHED = 1 << 6,
BLK_MQ_F_ALLOC_POLICY_START_BIT = 8,
--
2.9.5

2018-11-15 19:01:52

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 06/19] fs/buffer.c: use bvec iterator to truncate the bio

Once multi-page bvec is enabled, the last bvec may include more than one
page, this patch use bvec_last_segment() to truncate the bio.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
fs/buffer.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fs/buffer.c b/fs/buffer.c
index 1286c2b95498..fa37ad52e962 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -3032,7 +3032,10 @@ void guard_bio_eod(int op, struct bio *bio)

/* ..and clear the end of the buffer for reads */
if (op == REQ_OP_READ) {
- zero_user(bvec->bv_page, bvec->bv_offset + bvec->bv_len,
+ struct bio_vec bv;
+
+ bvec_last_segment(bvec, &bv);
+ zero_user(bv.bv_page, bv.bv_offset + bv.bv_len,
truncated_bytes);
}
}
--
2.9.5

2018-11-19 13:53:56

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 02/19] block: introduce bio_for_each_bvec()

On Fri, Nov 16, 2018 at 02:30:28PM +0100, Christoph Hellwig wrote:
> > +static inline void __bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
> > + unsigned bytes, bool mp)
>
> I think these magic 'bool np' arguments and wrappers over wrapper
> don't help anyone to actually understand the code. I'd vote for
> removing as many wrappers as we really don't need, and passing the
> actual segment limit instead of the magic bool flag. Something like
> this untested patch:

I think this way is fine, just a little comment.

>
> diff --git a/include/linux/bio.h b/include/linux/bio.h
> index 277921ad42e7..dcad0b69f57a 100644
> --- a/include/linux/bio.h
> +++ b/include/linux/bio.h
> @@ -138,30 +138,21 @@ static inline bool bio_full(struct bio *bio)
> bvec_for_each_segment(bvl, &((bio)->bi_io_vec[iter_all.idx]), i, iter_all)
>
> static inline void __bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
> - unsigned bytes, bool mp)
> + unsigned bytes, unsigned max_segment)

The new parameter should have been named as 'max_segment_len' or
'max_seg_len'.

> {
> iter->bi_sector += bytes >> 9;
>
> if (bio_no_advance_iter(bio))
> iter->bi_size -= bytes;
> else
> - if (!mp)
> - bvec_iter_advance(bio->bi_io_vec, iter, bytes);
> - else
> - mp_bvec_iter_advance(bio->bi_io_vec, iter, bytes);
> + __bvec_iter_advance(bio->bi_io_vec, iter, bytes, max_segment);
> /* TODO: It is reasonable to complete bio with error here. */
> }
>
> static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
> unsigned bytes)
> {
> - __bio_advance_iter(bio, iter, bytes, false);
> -}
> -
> -static inline void bio_advance_mp_iter(struct bio *bio, struct bvec_iter *iter,
> - unsigned bytes)
> -{
> - __bio_advance_iter(bio, iter, bytes, true);
> + __bio_advance_iter(bio, iter, bytes, PAGE_SIZE);
> }
>
> #define __bio_for_each_segment(bvl, bio, iter, start) \
> @@ -177,7 +168,7 @@ static inline void bio_advance_mp_iter(struct bio *bio, struct bvec_iter *iter,
> for (iter = (start); \
> (iter).bi_size && \
> ((bvl = bio_iter_mp_iovec((bio), (iter))), 1); \
> - bio_advance_mp_iter((bio), &(iter), (bvl).bv_len))
> + __bio_advance_iter((bio), &(iter), (bvl).bv_len, 0))

Even we might pass '-1' for multi-page segment.

>
> /* returns one real segment(multipage bvec) each time */
> #define bio_for_each_bvec(bvl, bio, iter) \
> diff --git a/include/linux/bvec.h b/include/linux/bvec.h
> index 02f26d2b59ad..5e2ed46c1c88 100644
> --- a/include/linux/bvec.h
> +++ b/include/linux/bvec.h
> @@ -138,8 +138,7 @@ struct bvec_iter_all {
> })
>
> static inline bool __bvec_iter_advance(const struct bio_vec *bv,
> - struct bvec_iter *iter,
> - unsigned bytes, bool mp)
> + struct bvec_iter *iter, unsigned bytes, unsigned max_segment)
> {
> if (WARN_ONCE(bytes > iter->bi_size,
> "Attempted to advance past end of bvec iter\n")) {
> @@ -148,18 +147,18 @@ static inline bool __bvec_iter_advance(const struct bio_vec *bv,
> }
>
> while (bytes) {
> - unsigned len;
> + unsigned segment_len = mp_bvec_iter_len(bv, *iter);
>
> - if (mp)
> - len = mp_bvec_iter_len(bv, *iter);
> - else
> - len = bvec_iter_len(bv, *iter);
> + if (max_segment) {
> + max_segment -= bvec_iter_offset(bv, *iter);
> + segment_len = min(segment_len, max_segment);

Looks 'max_segment' needs to be constant, shouldn't be updated.

If '-1' is passed for multipage case, the above change may become:

segment_len = min_t(segment_len, max_seg_len - bvec_iter_offset(bv, *iter));

This way is more clean, but with extra cost of the above line for multipage
case.

Thanks,
Ming

2018-11-16 08:43:45

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 04/19] block: use bio_for_each_bvec() to map sg

On Thu, Nov 15, 2018 at 04:52:51PM +0800, Ming Lei wrote:
> It is more efficient to use bio_for_each_bvec() to map sg, meantime
> we have to consider splitting multipage bvec as done in blk_bio_segment_split().
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]

Reviewed-by: Omar Sandoval <[email protected]>

> Signed-off-by: Ming Lei <[email protected]>
> ---
> block/blk-merge.c | 72 +++++++++++++++++++++++++++++++++++++++----------------
> 1 file changed, 52 insertions(+), 20 deletions(-)
>
> diff --git a/block/blk-merge.c b/block/blk-merge.c
> index 6f7deb94a23f..cb9f49bcfd36 100644
> --- a/block/blk-merge.c
> +++ b/block/blk-merge.c
> @@ -473,6 +473,56 @@ static int blk_phys_contig_segment(struct request_queue *q, struct bio *bio,
> return biovec_phys_mergeable(q, &end_bv, &nxt_bv);
> }
>
> +static struct scatterlist *blk_next_sg(struct scatterlist **sg,
> + struct scatterlist *sglist)
> +{
> + if (!*sg)
> + return sglist;
> + else {
> + /*
> + * If the driver previously mapped a shorter
> + * list, we could see a termination bit
> + * prematurely unless it fully inits the sg
> + * table on each mapping. We KNOW that there
> + * must be more entries here or the driver
> + * would be buggy, so force clear the
> + * termination bit to avoid doing a full
> + * sg_init_table() in drivers for each command.
> + */
> + sg_unmark_end(*sg);
> + return sg_next(*sg);
> + }
> +}
> +
> +static unsigned blk_bvec_map_sg(struct request_queue *q,
> + struct bio_vec *bvec, struct scatterlist *sglist,
> + struct scatterlist **sg)
> +{
> + unsigned nbytes = bvec->bv_len;
> + unsigned nsegs = 0, total = 0;
> +
> + while (nbytes > 0) {
> + unsigned seg_size;
> + struct page *pg;
> + unsigned offset, idx;
> +
> + *sg = blk_next_sg(sg, sglist);
> +
> + seg_size = min(nbytes, queue_max_segment_size(q));
> + offset = (total + bvec->bv_offset) % PAGE_SIZE;
> + idx = (total + bvec->bv_offset) / PAGE_SIZE;
> + pg = nth_page(bvec->bv_page, idx);
> +
> + sg_set_page(*sg, pg, seg_size, offset);
> +
> + total += seg_size;
> + nbytes -= seg_size;
> + nsegs++;
> + }
> +
> + return nsegs;
> +}
> +
> static inline void
> __blk_segment_map_sg(struct request_queue *q, struct bio_vec *bvec,
> struct scatterlist *sglist, struct bio_vec *bvprv,
> @@ -490,25 +540,7 @@ __blk_segment_map_sg(struct request_queue *q, struct bio_vec *bvec,
> (*sg)->length += nbytes;
> } else {
> new_segment:
> - if (!*sg)
> - *sg = sglist;
> - else {
> - /*
> - * If the driver previously mapped a shorter
> - * list, we could see a termination bit
> - * prematurely unless it fully inits the sg
> - * table on each mapping. We KNOW that there
> - * must be more entries here or the driver
> - * would be buggy, so force clear the
> - * termination bit to avoid doing a full
> - * sg_init_table() in drivers for each command.
> - */
> - sg_unmark_end(*sg);
> - *sg = sg_next(*sg);
> - }
> -
> - sg_set_page(*sg, bvec->bv_page, nbytes, bvec->bv_offset);
> - (*nsegs)++;
> + (*nsegs) += blk_bvec_map_sg(q, bvec, sglist, sg);
> }
> *bvprv = *bvec;
> }
> @@ -530,7 +562,7 @@ static int __blk_bios_map_sg(struct request_queue *q, struct bio *bio,
> int cluster = blk_queue_cluster(q), nsegs = 0;
>
> for_each_bio(bio)
> - bio_for_each_segment(bvec, bio, iter)
> + bio_for_each_bvec(bvec, bio, iter)
> __blk_segment_map_sg(q, &bvec, sglist, &bvprv, sg,
> &nsegs, &cluster);
>
> --
> 2.9.5
>

2018-11-16 10:50:36

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 10/19] block: loop: pass multi-page bvec to iov_iter

On Thu, Nov 15, 2018 at 04:52:57PM +0800, Ming Lei wrote:
> iov_iter is implemented with bvec itererator, so it is safe to pass
> multipage bvec to it, and this way is much more efficient than
> passing one page in each bvec.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]

Reviewed-by: Omar Sandoval <[email protected]>

Comments below.

> Signed-off-by: Ming Lei <[email protected]>
> ---
> drivers/block/loop.c | 23 ++++++++++++-----------
> 1 file changed, 12 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/block/loop.c b/drivers/block/loop.c
> index bf6bc35aaf88..a3fd418ec637 100644
> --- a/drivers/block/loop.c
> +++ b/drivers/block/loop.c
> @@ -515,16 +515,16 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
> struct bio *bio = rq->bio;
> struct file *file = lo->lo_backing_file;
> unsigned int offset;
> - int segments = 0;
> + int nr_bvec = 0;
> int ret;
>
> if (rq->bio != rq->biotail) {
> - struct req_iterator iter;
> + struct bvec_iter iter;
> struct bio_vec tmp;
>
> __rq_for_each_bio(bio, rq)
> - segments += bio_segments(bio);
> - bvec = kmalloc_array(segments, sizeof(struct bio_vec),
> + nr_bvec += bio_bvecs(bio);
> + bvec = kmalloc_array(nr_bvec, sizeof(struct bio_vec),
> GFP_NOIO);
> if (!bvec)
> return -EIO;
> @@ -533,13 +533,14 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
> /*
> * The bios of the request may be started from the middle of
> * the 'bvec' because of bio splitting, so we can't directly
> - * copy bio->bi_iov_vec to new bvec. The rq_for_each_segment
> + * copy bio->bi_iov_vec to new bvec. The bio_for_each_bvec
> * API will take care of all details for us.
> */
> - rq_for_each_segment(tmp, rq, iter) {
> - *bvec = tmp;
> - bvec++;
> - }
> + __rq_for_each_bio(bio, rq)
> + bio_for_each_bvec(tmp, bio, iter) {
> + *bvec = tmp;
> + bvec++;
> + }

Even if they're not strictly necessary, could you please include the
curly braces for __rq_for_each_bio() here?

> bvec = cmd->bvec;
> offset = 0;
> } else {
> @@ -550,11 +551,11 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
> */
> offset = bio->bi_iter.bi_bvec_done;
> bvec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
> - segments = bio_segments(bio);
> + nr_bvec = bio_bvecs(bio);

This scared me for a second, but it's fine to do here because we haven't
actually enabled multipage bvecs yet, right?

> }
> atomic_set(&cmd->ref, 2);
>
> - iov_iter_bvec(&iter, rw, bvec, segments, blk_rq_bytes(rq));
> + iov_iter_bvec(&iter, rw, bvec, nr_bvec, blk_rq_bytes(rq));
> iter.iov_offset = offset;
>
> cmd->iocb.ki_pos = pos;
> --
> 2.9.5
>

2018-11-16 23:45:41

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 04/19] block: use bio_for_each_bvec() to map sg

> + if (!*sg)
> + return sglist;
> + else {

No need for an else after an early return.

2018-11-17 00:07:44

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 17/19] block: don't use bio->bi_vcnt to figure out segment number

On Thu, Nov 15, 2018 at 04:53:04PM +0800, Ming Lei wrote:
> It is wrong to use bio->bi_vcnt to figure out how many segments
> there are in the bio even though CLONED flag isn't set on this bio,
> because this bio may be splitted or advanced.
>
> So always use bio_segments() in blk_recount_segments(), and it shouldn't
> cause any performance loss now because the physical segment number is figured
> out in blk_queue_split() and BIO_SEG_VALID is set meantime since
> bdced438acd83ad83a6c ("block: setup bi_phys_segments after splitting").

Looks good, but shouldn't this go to the beginning of the series?

Reviewed-by: Christoph Hellwig <[email protected]>

2018-11-21 06:42:34

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()


>>> The only user in your final tree seems to be the loop driver, and
>>> even that one only uses the helper for read/write bios.
>>>
>>> I think something like this would be much simpler in the end:
>>
>> The recently submitted nvme-tcp host driver should also be a user
>> of this. Does it make sense to keep it as a helper then?
>
> I did take a brief look at the code, and I really don't understand
> why the heck it even deals with bios to start with. Like all the
> other nvme transports it is a blk-mq driver and should iterate
> over segments in a request and more or less ignore bios. Something
> is horribly wrong in the design.

Can you explain a little more? I'm more than happy to change that but
I'm not completely clear how...

Before we begin a data transfer, we need to set our own iterator that
will advance with the progression of the data transfer. We also need to
keep in mind that all the data transfer (both send and recv) are
completely non blocking (and zero-copy when we send).

That means that every data movement needs to be able to suspend
and resume asynchronously. i.e. we cannot use the following pattern:
rq_for_each_segment(bvec, rq, rq_iter) {
iov_iter_bvec(&iov_iter, WRITE, &bvec, 1, bvec.bv_len);
send(sock, iov_iter);
}

Given that a request can hold more than a single bio, I'm not clear on
how we can achieve that without iterating over the bios in the request
ourselves.

Any useful insight?

2018-11-21 20:53:55

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()

On Tue, Nov 20, 2018 at 09:35:07PM -0800, Sagi Grimberg wrote:
>
> > > Wait, I see that the bvec is still a single array per bio. When you said
> > > a table I thought you meant a 2-dimentional array...
> >
> > I mean a new 1-d table A has to be created for multiple bios in one rq,
> > and build it in the following way
> >
> > rq_for_each_bvec(tmp, rq, rq_iter)
> > *A = tmp;
> >
> > Then you can pass A to iov_iter_bvec() & send().
> >
> > Given it is over TCP, I guess it should be doable for you to preallocate one
> > 256-bvec table in one page for each request, then sets the max segment size as
> > (unsigned int)-1, and max segment number as 256, the preallocated table
> > should work anytime.
>
> 256 bvec table is really a lot to preallocate, especially when its not
> needed, I can easily initialize the bvec_iter on the bio bvec. If this
> involves preallocation of the worst-case than I don't consider this to
> be an improvement.

If you don't provide one single bvec table, I understand you may not send
this req via one send().

The bvec_iter initialization is easy to do:

bvec_iter = bio->bi_iter

when you move to a new a bio, please refer to __bio_for_each_bvec() or
__bio_for_each_segment().

Thanks,
Ming

2018-11-15 19:04:26

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 15/19] block: always define BIO_MAX_PAGES as 256

Now multi-page bvec can cover CONFIG_THP_SWAP, so we don't need to
increase BIO_MAX_PAGES for it.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
include/linux/bio.h | 8 --------
1 file changed, 8 deletions(-)

diff --git a/include/linux/bio.h b/include/linux/bio.h
index 5040e9a2eb09..277921ad42e7 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -34,15 +34,7 @@
#define BIO_BUG_ON
#endif

-#ifdef CONFIG_THP_SWAP
-#if HPAGE_PMD_NR > 256
-#define BIO_MAX_PAGES HPAGE_PMD_NR
-#else
#define BIO_MAX_PAGES 256
-#endif
-#else
-#define BIO_MAX_PAGES 256
-#endif

#define bio_prio(bio) (bio)->bi_ioprio
#define bio_set_prio(bio, prio) ((bio)->bi_ioprio = prio)
--
2.9.5

2018-11-15 19:02:52

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 10/19] block: loop: pass multi-page bvec to iov_iter

iov_iter is implemented with bvec itererator, so it is safe to pass
multipage bvec to it, and this way is much more efficient than
passing one page in each bvec.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
drivers/block/loop.c | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index bf6bc35aaf88..a3fd418ec637 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -515,16 +515,16 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
struct bio *bio = rq->bio;
struct file *file = lo->lo_backing_file;
unsigned int offset;
- int segments = 0;
+ int nr_bvec = 0;
int ret;

if (rq->bio != rq->biotail) {
- struct req_iterator iter;
+ struct bvec_iter iter;
struct bio_vec tmp;

__rq_for_each_bio(bio, rq)
- segments += bio_segments(bio);
- bvec = kmalloc_array(segments, sizeof(struct bio_vec),
+ nr_bvec += bio_bvecs(bio);
+ bvec = kmalloc_array(nr_bvec, sizeof(struct bio_vec),
GFP_NOIO);
if (!bvec)
return -EIO;
@@ -533,13 +533,14 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
/*
* The bios of the request may be started from the middle of
* the 'bvec' because of bio splitting, so we can't directly
- * copy bio->bi_iov_vec to new bvec. The rq_for_each_segment
+ * copy bio->bi_iov_vec to new bvec. The bio_for_each_bvec
* API will take care of all details for us.
*/
- rq_for_each_segment(tmp, rq, iter) {
- *bvec = tmp;
- bvec++;
- }
+ __rq_for_each_bio(bio, rq)
+ bio_for_each_bvec(tmp, bio, iter) {
+ *bvec = tmp;
+ bvec++;
+ }
bvec = cmd->bvec;
offset = 0;
} else {
@@ -550,11 +551,11 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
*/
offset = bio->bi_iter.bi_bvec_done;
bvec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
- segments = bio_segments(bio);
+ nr_bvec = bio_bvecs(bio);
}
atomic_set(&cmd->ref, 2);

- iov_iter_bvec(&iter, rw, bvec, segments, blk_rq_bytes(rq));
+ iov_iter_bvec(&iter, rw, bvec, nr_bvec, blk_rq_bytes(rq));
iter.iov_offset = offset;

cmd->iocb.ki_pos = pos;
--
2.9.5

2018-11-16 12:28:43

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 18/19] block: kill QUEUE_FLAG_NO_SG_MERGE

On Thu, Nov 15, 2018 at 04:53:05PM +0800, Ming Lei wrote:
> Since bdced438acd83ad83a6c ("block: setup bi_phys_segments after splitting"),
> physical segment number is mainly figured out in blk_queue_split() for
> fast path, and the flag of BIO_SEG_VALID is set there too.
>
> Now only blk_recount_segments() and blk_recalc_rq_segments() use this
> flag.
>
> Basically blk_recount_segments() is bypassed in fast path given BIO_SEG_VALID
> is set in blk_queue_split().
>
> For another user of blk_recalc_rq_segments():
>
> - run in partial completion branch of blk_update_request, which is an unusual case
>
> - run in blk_cloned_rq_check_limits(), still not a big problem if the flag is killed
> since dm-rq is the only user.
>
> Multi-page bvec is enabled now, QUEUE_FLAG_NO_SG_MERGE doesn't make sense any more.

This commit message wasn't very clear. Is it the case that
QUEUE_FLAG_NO_SG_MERGE is no longer set by any drivers?

2018-11-15 19:01:01

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 02/19] block: introduce bio_for_each_bvec()

This helper is used for iterating over multi-page bvec for bio
split & merge code.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
include/linux/bio.h | 34 +++++++++++++++++++++++++++++++---
include/linux/bvec.h | 36 ++++++++++++++++++++++++++++++++----
2 files changed, 63 insertions(+), 7 deletions(-)

diff --git a/include/linux/bio.h b/include/linux/bio.h
index 056fb627edb3..1f0dcf109841 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -76,6 +76,9 @@
#define bio_data_dir(bio) \
(op_is_write(bio_op(bio)) ? WRITE : READ)

+#define bio_iter_mp_iovec(bio, iter) \
+ mp_bvec_iter_bvec((bio)->bi_io_vec, (iter))
+
/*
* Check whether this bio carries any data or not. A NULL bio is allowed.
*/
@@ -135,18 +138,33 @@ static inline bool bio_full(struct bio *bio)
#define bio_for_each_segment_all(bvl, bio, i) \
for (i = 0, bvl = (bio)->bi_io_vec; i < (bio)->bi_vcnt; i++, bvl++)

-static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
- unsigned bytes)
+static inline void __bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
+ unsigned bytes, bool mp)
{
iter->bi_sector += bytes >> 9;

if (bio_no_advance_iter(bio))
iter->bi_size -= bytes;
else
- bvec_iter_advance(bio->bi_io_vec, iter, bytes);
+ if (!mp)
+ bvec_iter_advance(bio->bi_io_vec, iter, bytes);
+ else
+ mp_bvec_iter_advance(bio->bi_io_vec, iter, bytes);
/* TODO: It is reasonable to complete bio with error here. */
}

+static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
+ unsigned bytes)
+{
+ __bio_advance_iter(bio, iter, bytes, false);
+}
+
+static inline void bio_advance_mp_iter(struct bio *bio, struct bvec_iter *iter,
+ unsigned bytes)
+{
+ __bio_advance_iter(bio, iter, bytes, true);
+}
+
#define __bio_for_each_segment(bvl, bio, iter, start) \
for (iter = (start); \
(iter).bi_size && \
@@ -156,6 +174,16 @@ static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
#define bio_for_each_segment(bvl, bio, iter) \
__bio_for_each_segment(bvl, bio, iter, (bio)->bi_iter)

+#define __bio_for_each_bvec(bvl, bio, iter, start) \
+ for (iter = (start); \
+ (iter).bi_size && \
+ ((bvl = bio_iter_mp_iovec((bio), (iter))), 1); \
+ bio_advance_mp_iter((bio), &(iter), (bvl).bv_len))
+
+/* returns one real segment(multipage bvec) each time */
+#define bio_for_each_bvec(bvl, bio, iter) \
+ __bio_for_each_bvec(bvl, bio, iter, (bio)->bi_iter)
+
#define bio_iter_last(bvec, iter) ((iter).bi_size == (bvec).bv_len)

static inline unsigned bio_segments(struct bio *bio)
diff --git a/include/linux/bvec.h b/include/linux/bvec.h
index 8ef904a50577..3d61352cd8cf 100644
--- a/include/linux/bvec.h
+++ b/include/linux/bvec.h
@@ -124,8 +124,16 @@ struct bvec_iter {
.bv_offset = bvec_iter_offset((bvec), (iter)), \
})

-static inline bool bvec_iter_advance(const struct bio_vec *bv,
- struct bvec_iter *iter, unsigned bytes)
+#define mp_bvec_iter_bvec(bvec, iter) \
+((struct bio_vec) { \
+ .bv_page = mp_bvec_iter_page((bvec), (iter)), \
+ .bv_len = mp_bvec_iter_len((bvec), (iter)), \
+ .bv_offset = mp_bvec_iter_offset((bvec), (iter)), \
+})
+
+static inline bool __bvec_iter_advance(const struct bio_vec *bv,
+ struct bvec_iter *iter,
+ unsigned bytes, bool mp)
{
if (WARN_ONCE(bytes > iter->bi_size,
"Attempted to advance past end of bvec iter\n")) {
@@ -134,8 +142,14 @@ static inline bool bvec_iter_advance(const struct bio_vec *bv,
}

while (bytes) {
- unsigned iter_len = bvec_iter_len(bv, *iter);
- unsigned len = min(bytes, iter_len);
+ unsigned len;
+
+ if (mp)
+ len = mp_bvec_iter_len(bv, *iter);
+ else
+ len = bvec_iter_len(bv, *iter);
+
+ len = min(bytes, len);

bytes -= len;
iter->bi_size -= len;
@@ -173,6 +187,20 @@ static inline bool bvec_iter_rewind(const struct bio_vec *bv,
return true;
}

+static inline bool bvec_iter_advance(const struct bio_vec *bv,
+ struct bvec_iter *iter,
+ unsigned bytes)
+{
+ return __bvec_iter_advance(bv, iter, bytes, false);
+}
+
+static inline bool mp_bvec_iter_advance(const struct bio_vec *bv,
+ struct bvec_iter *iter,
+ unsigned bytes)
+{
+ return __bvec_iter_advance(bv, iter, bytes, true);
+}
+
#define for_each_bvec(bvl, bio_vec, iter, start) \
for (iter = (start); \
(iter).bi_size && \
--
2.9.5

2018-11-17 02:53:59

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 19/19] block: kill BLK_MQ_F_SG_MERGE

On Thu, Nov 15, 2018 at 04:53:06PM +0800, Ming Lei wrote:
> QUEUE_FLAG_NO_SG_MERGE has been killed, so kill BLK_MQ_F_SG_MERGE too.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]

Reviewed-by: Omar Sandoval <[email protected]>

> Signed-off-by: Ming Lei <[email protected]>
> ---
> block/blk-mq-debugfs.c | 1 -
> drivers/block/loop.c | 2 +-
> drivers/block/nbd.c | 2 +-
> drivers/block/rbd.c | 2 +-
> drivers/block/skd_main.c | 1 -
> drivers/block/xen-blkfront.c | 2 +-
> drivers/md/dm-rq.c | 2 +-
> drivers/mmc/core/queue.c | 3 +--
> drivers/scsi/scsi_lib.c | 2 +-
> include/linux/blk-mq.h | 1 -
> 10 files changed, 7 insertions(+), 11 deletions(-)

[snip]

2018-11-16 23:42:55

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 02/19] block: introduce bio_for_each_bvec()

> +static inline void __bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
> + unsigned bytes, bool mp)

I think these magic 'bool np' arguments and wrappers over wrapper
don't help anyone to actually understand the code. I'd vote for
removing as many wrappers as we really don't need, and passing the
actual segment limit instead of the magic bool flag. Something like
this untested patch:

diff --git a/include/linux/bio.h b/include/linux/bio.h
index 277921ad42e7..dcad0b69f57a 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -138,30 +138,21 @@ static inline bool bio_full(struct bio *bio)
bvec_for_each_segment(bvl, &((bio)->bi_io_vec[iter_all.idx]), i, iter_all)

static inline void __bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
- unsigned bytes, bool mp)
+ unsigned bytes, unsigned max_segment)
{
iter->bi_sector += bytes >> 9;

if (bio_no_advance_iter(bio))
iter->bi_size -= bytes;
else
- if (!mp)
- bvec_iter_advance(bio->bi_io_vec, iter, bytes);
- else
- mp_bvec_iter_advance(bio->bi_io_vec, iter, bytes);
+ __bvec_iter_advance(bio->bi_io_vec, iter, bytes, max_segment);
/* TODO: It is reasonable to complete bio with error here. */
}

static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
unsigned bytes)
{
- __bio_advance_iter(bio, iter, bytes, false);
-}
-
-static inline void bio_advance_mp_iter(struct bio *bio, struct bvec_iter *iter,
- unsigned bytes)
-{
- __bio_advance_iter(bio, iter, bytes, true);
+ __bio_advance_iter(bio, iter, bytes, PAGE_SIZE);
}

#define __bio_for_each_segment(bvl, bio, iter, start) \
@@ -177,7 +168,7 @@ static inline void bio_advance_mp_iter(struct bio *bio, struct bvec_iter *iter,
for (iter = (start); \
(iter).bi_size && \
((bvl = bio_iter_mp_iovec((bio), (iter))), 1); \
- bio_advance_mp_iter((bio), &(iter), (bvl).bv_len))
+ __bio_advance_iter((bio), &(iter), (bvl).bv_len, 0))

/* returns one real segment(multipage bvec) each time */
#define bio_for_each_bvec(bvl, bio, iter) \
diff --git a/include/linux/bvec.h b/include/linux/bvec.h
index 02f26d2b59ad..5e2ed46c1c88 100644
--- a/include/linux/bvec.h
+++ b/include/linux/bvec.h
@@ -138,8 +138,7 @@ struct bvec_iter_all {
})

static inline bool __bvec_iter_advance(const struct bio_vec *bv,
- struct bvec_iter *iter,
- unsigned bytes, bool mp)
+ struct bvec_iter *iter, unsigned bytes, unsigned max_segment)
{
if (WARN_ONCE(bytes > iter->bi_size,
"Attempted to advance past end of bvec iter\n")) {
@@ -148,18 +147,18 @@ static inline bool __bvec_iter_advance(const struct bio_vec *bv,
}

while (bytes) {
- unsigned len;
+ unsigned segment_len = mp_bvec_iter_len(bv, *iter);

- if (mp)
- len = mp_bvec_iter_len(bv, *iter);
- else
- len = bvec_iter_len(bv, *iter);
+ if (max_segment) {
+ max_segment -= bvec_iter_offset(bv, *iter);
+ segment_len = min(segment_len, max_segment);
+ }

- len = min(bytes, len);
+ segment_len = min(bytes, segment_len);

- bytes -= len;
- iter->bi_size -= len;
- iter->bi_bvec_done += len;
+ bytes -= segment_len;
+ iter->bi_size -= segment_len;
+ iter->bi_bvec_done += segment_len;

if (iter->bi_bvec_done == __bvec_iter_bvec(bv, *iter)->bv_len) {
iter->bi_bvec_done = 0;
@@ -197,14 +196,7 @@ static inline bool bvec_iter_advance(const struct bio_vec *bv,
struct bvec_iter *iter,
unsigned bytes)
{
- return __bvec_iter_advance(bv, iter, bytes, false);
-}
-
-static inline bool mp_bvec_iter_advance(const struct bio_vec *bv,
- struct bvec_iter *iter,
- unsigned bytes)
-{
- return __bvec_iter_advance(bv, iter, bytes, true);
+ return __bvec_iter_advance(bv, iter, bytes, PAGE_SIZE);
}

#define for_each_bvec(bvl, bio_vec, iter, start) \

2018-11-21 14:17:21

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()

On Tue, Nov 20, 2018 at 07:20:45PM -0800, Sagi Grimberg wrote:
>
> > Not sure I understand the 'blocking' problem in this case.
> >
> > We can build a bvec table from this req, and send them all
> > in send(),
>
> I would like to avoid growing bvec tables and keep everything
> preallocated. Plus, a bvec_iter operates on a bvec which means
> we'll need a table there as well... Not liking it so far...

In case of bios in one request, we can't know how many bvecs there
are except for calling rq_bvecs(), so it may not be suitable to
preallocate the table. If you have to send the IO request in one send(),
runtime allocation may be inevitable.

If you don't require to send the IO request in one send(), you may send
one bio in one time, and just uses the bio's bvec table directly,
such as the single bio case in lo_rw_aio().

>
> > can this way avoid your blocking issue? You may see this
> > example in branch 'rq->bio != rq->biotail' of lo_rw_aio().
>
> This is exactly an example of not ignoring the bios...

Yeah, that is the most common example, given merge is enabled
in most of cases. If the driver or device doesn't care merge,
you can disable it and always get single bio request, then the
bio's bvec table can be reused for send().

>
> > If this way is what you need, I think you are right, even we may
> > introduce the following helpers:
> >
> > rq_for_each_bvec()
> > rq_bvecs()
>
> I'm not sure how this helps me either. Unless we can set a bvec_iter to
> span bvecs or have an abstract bio crossing when we re-initialize the
> bvec_iter I don't see how I can ignore bios completely...

rq_for_each_bvec() will iterate over all bvecs from all bios, so you
needn't to see any bio in this req.

rq_bvecs() will return how many bvecs there are in this request(cover
all bios in this req)

>
> > So looks nvme-tcp host driver might be the 2nd driver which benefits
> > from multi-page bvec directly.
> >
> > The multi-page bvec V11 has passed my tests and addressed almost
> > all the comments during review on V10. I removed bio_vecs() in V11,
> > but it won't be big deal, we can introduce them anytime when there
> > is the requirement.
>
> multipage-bvecs and nvme-tcp are going to conflict, so it would be good
> to coordinate on this. I think that nvme-tcp host needs some adjustments
> as setting a bvec_iter. I'm under the impression that the change is rather
> small and self-contained, but I'm not sure I have the full
> picture here.

I guess I may not get your exact requirement on block io iterator from nvme-tcp
too, :-(

thanks,
Ming

2018-11-15 22:50:57

by David Sterba

[permalink] [raw]
Subject: Re: [PATCH V10 12/19] block: allow bio_for_each_segment_all() to iterate over multi-page bvec

On Thu, Nov 15, 2018 at 04:52:59PM +0800, Ming Lei wrote:
> diff --git a/block/blk-zoned.c b/block/blk-zoned.c
> index 13ba2011a306..789b09ae402a 100644
> --- a/block/blk-zoned.c
> +++ b/block/blk-zoned.c
> @@ -123,6 +123,7 @@ static int blk_report_zones(struct gendisk *disk, sector_t sector,
> unsigned int z = 0, n, nrz = *nr_zones;
> sector_t capacity = get_capacity(disk);
> int ret;
> + struct bvec_iter_all iter_all;
>
> while (z < nrz && sector < capacity) {
> n = nrz - z;

iter_all is added but not used and I don't see any
bio_for_each_segment_all for conversion in this function.

2018-11-19 19:08:56

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 14/19] block: enable multipage bvecs

On Thu, Nov 15, 2018 at 05:56:27PM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:53:01PM +0800, Ming Lei wrote:
> > This patch pulls the trigger for multi-page bvecs.
> >
> > Now any request queue which supports queue cluster will see multi-page
> > bvecs.
> >
> > Cc: Dave Chinner <[email protected]>
> > Cc: Kent Overstreet <[email protected]>
> > Cc: Mike Snitzer <[email protected]>
> > Cc: [email protected]
> > Cc: Alexander Viro <[email protected]>
> > Cc: [email protected]
> > Cc: Shaohua Li <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: David Sterba <[email protected]>
> > Cc: [email protected]
> > Cc: Darrick J. Wong <[email protected]>
> > Cc: [email protected]
> > Cc: Gao Xiang <[email protected]>
> > Cc: Christoph Hellwig <[email protected]>
> > Cc: Theodore Ts'o <[email protected]>
> > Cc: [email protected]
> > Cc: Coly Li <[email protected]>
> > Cc: [email protected]
> > Cc: Boaz Harrosh <[email protected]>
> > Cc: Bob Peterson <[email protected]>
> > Cc: [email protected]
> > Signed-off-by: Ming Lei <[email protected]>
> > ---
> > block/bio.c | 24 ++++++++++++++++++------
> > 1 file changed, 18 insertions(+), 6 deletions(-)
> >
> > diff --git a/block/bio.c b/block/bio.c
> > index 6486722d4d4b..ed6df6f8e63d 100644
> > --- a/block/bio.c
> > +++ b/block/bio.c
>
> This comment above __bio_try_merge_page() doesn't make sense after this
> change:
>
> This is a
> a useful optimisation for file systems with a block size smaller than the
> page size.
>
> Can you please get rid of it in this patch?

I understand __bio_try_merge_page() still works for original cases, so
looks the optimization for sub-pagesize is still there too, isn't it?

>
> > @@ -767,12 +767,24 @@ bool __bio_try_merge_page(struct bio *bio, struct page *page,
> >
> > if (bio->bi_vcnt > 0) {
> > struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1];
> > -
> > - if (page == bv->bv_page && off == bv->bv_offset + bv->bv_len) {
> > - bv->bv_len += len;
> > - bio->bi_iter.bi_size += len;
> > - return true;
> > - }
> > + struct request_queue *q = NULL;
> > +
> > + if (page == bv->bv_page && off == (bv->bv_offset + bv->bv_len)
> > + && (off + len) <= PAGE_SIZE)
> > + goto merge;
>
> The parentheses around (bv->bv_offset + bv->bv_len) and (off + len) are
> unnecessary noise.
>
> What's the point of the new (off + len) <= PAGE_SIZE check?

Yeah, I don't know why I did it, :-(, the check is absolutely always true.

>
> > +
> > + if (bio->bi_disk)
> > + q = bio->bi_disk->queue;
> > +
> > + /* disable multi-page bvec too if cluster isn't enabled */
> > + if (!q || !blk_queue_cluster(q) ||
> > + ((page_to_phys(bv->bv_page) + bv->bv_offset + bv->bv_len) !=
> > + (page_to_phys(page) + off)))
>
> More unnecessary parentheses here.

OK.

Thanks,
Ming

2018-11-19 18:51:41

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 11/19] bcache: avoid to use bio_for_each_segment_all() in bch_bio_alloc_pages()

On Fri, Nov 16, 2018 at 02:46:45PM +0100, Christoph Hellwig wrote:
> > - bio_for_each_segment_all(bv, bio, i) {
> > + for (i = 0, bv = bio->bi_io_vec; i < bio->bi_vcnt; bv++) {
>
> This really needs a comment. Otherwise it looks fine to me.

OK, will do it in next version.

Thanks,
Ming

2018-11-17 02:17:33

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 03/19] block: use bio_for_each_bvec() to compute multi-page bvec count

On Fri, Nov 16, 2018 at 10:19:56AM +0100, Christoph Hellwig wrote:
> On Thu, Nov 15, 2018 at 02:18:47PM -0800, Omar Sandoval wrote:
> > My only reason to prefer unsigned int is consistency. unsigned int is
> > much more common in the kernel:
> >
> > $ ag --cc -s 'unsigned\s+int' | wc -l
> > 129632
> > $ ag --cc -s 'unsigned\s+(?!char|short|int|long)' | wc -l
> > 22435
> >
> > checkpatch also warns on plain unsigned.
>
> Talk about chicken and egg. unsigned is perfectly valid C, and being
> shorter often helps being more readable. checkpath is as so often
> wrongly opinionated..

Fair enough. Since enough people don't mind bare unsigned in the block
code, I retract my comment :)

2018-11-19 19:30:12

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 17/19] block: don't use bio->bi_vcnt to figure out segment number

On Thu, Nov 15, 2018 at 06:11:40PM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:53:04PM +0800, Ming Lei wrote:
> > It is wrong to use bio->bi_vcnt to figure out how many segments
> > there are in the bio even though CLONED flag isn't set on this bio,
> > because this bio may be splitted or advanced.
> >
> > So always use bio_segments() in blk_recount_segments(), and it shouldn't
> > cause any performance loss now because the physical segment number is figured
> > out in blk_queue_split() and BIO_SEG_VALID is set meantime since
> > bdced438acd83ad83a6c ("block: setup bi_phys_segments after splitting").
> >
> > Cc: Dave Chinner <[email protected]>
> > Cc: Kent Overstreet <[email protected]>
> > Fixes: 7f60dcaaf91 ("block: blk-merge: fix blk_recount_segments()")
>
> From what I can tell, the problem was originally introduced by
> 76d8137a3113 ("blk-merge: recaculate segment if it isn't less than max segments")
>
> Is that right?

Indeed, will update it in next version.

Thanks,
Ming

2018-11-17 00:06:13

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 15/19] block: always define BIO_MAX_PAGES as 256

> diff --git a/include/linux/bio.h b/include/linux/bio.h
> index 5040e9a2eb09..277921ad42e7 100644
> --- a/include/linux/bio.h
> +++ b/include/linux/bio.h
> @@ -34,15 +34,7 @@
> #define BIO_BUG_ON
> #endif
>
> -#ifdef CONFIG_THP_SWAP
> -#if HPAGE_PMD_NR > 256
> -#define BIO_MAX_PAGES HPAGE_PMD_NR
> -#else
> #define BIO_MAX_PAGES 256
> -#endif
> -#else
> -#define BIO_MAX_PAGES 256
> -#endif

Looks good. This mess should have never gone in.

Reviewed-by: Christoph Hellwig <[email protected]>

2018-11-21 13:53:25

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()


> Not sure I understand the 'blocking' problem in this case.
>
> We can build a bvec table from this req, and send them all
> in send(),

I would like to avoid growing bvec tables and keep everything
preallocated. Plus, a bvec_iter operates on a bvec which means
we'll need a table there as well... Not liking it so far...

> can this way avoid your blocking issue? You may see this
> example in branch 'rq->bio != rq->biotail' of lo_rw_aio().

This is exactly an example of not ignoring the bios...

> If this way is what you need, I think you are right, even we may
> introduce the following helpers:
>
> rq_for_each_bvec()
> rq_bvecs()

I'm not sure how this helps me either. Unless we can set a bvec_iter to
span bvecs or have an abstract bio crossing when we re-initialize the
bvec_iter I don't see how I can ignore bios completely...

> So looks nvme-tcp host driver might be the 2nd driver which benefits
> from multi-page bvec directly.
>
> The multi-page bvec V11 has passed my tests and addressed almost
> all the comments during review on V10. I removed bio_vecs() in V11,
> but it won't be big deal, we can introduce them anytime when there
> is the requirement.

multipage-bvecs and nvme-tcp are going to conflict, so it would be good
to coordinate on this. I think that nvme-tcp host needs some adjustments
as setting a bvec_iter. I'm under the impression that the change is
rather small and self-contained, but I'm not sure I have the full
picture here.

2018-11-16 07:14:48

by Mike Snitzer

[permalink] [raw]
Subject: Re: [PATCH V10 03/19] block: use bio_for_each_bvec() to compute multi-page bvec count

On Thu, Nov 15 2018 at 3:20pm -0500,
Omar Sandoval <[email protected]> wrote:

> On Thu, Nov 15, 2018 at 04:52:50PM +0800, Ming Lei wrote:
> > First it is more efficient to use bio_for_each_bvec() in both
> > blk_bio_segment_split() and __blk_recalc_rq_segments() to compute how
> > many multi-page bvecs there are in the bio.
> >
> > Secondly once bio_for_each_bvec() is used, the bvec may need to be
> > splitted because its length can be very longer than max segment size,
> > so we have to split the big bvec into several segments.
> >
> > Thirdly when splitting multi-page bvec into segments, the max segment
> > limit may be reached, so the bio split need to be considered under
> > this situation too.
> >
> > Cc: Dave Chinner <[email protected]>
> > Cc: Kent Overstreet <[email protected]>
> > Cc: Mike Snitzer <[email protected]>
> > Cc: [email protected]
> > Cc: Alexander Viro <[email protected]>
> > Cc: [email protected]
> > Cc: Shaohua Li <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: David Sterba <[email protected]>
> > Cc: [email protected]
> > Cc: Darrick J. Wong <[email protected]>
> > Cc: [email protected]
> > Cc: Gao Xiang <[email protected]>
> > Cc: Christoph Hellwig <[email protected]>
> > Cc: Theodore Ts'o <[email protected]>
> > Cc: [email protected]
> > Cc: Coly Li <[email protected]>
> > Cc: [email protected]
> > Cc: Boaz Harrosh <[email protected]>
> > Cc: Bob Peterson <[email protected]>
> > Cc: [email protected]
> > Signed-off-by: Ming Lei <[email protected]>
> > ---
> > block/blk-merge.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++---------
> > 1 file changed, 76 insertions(+), 14 deletions(-)
> >
> > diff --git a/block/blk-merge.c b/block/blk-merge.c
> > index 91b2af332a84..6f7deb94a23f 100644
> > --- a/block/blk-merge.c
> > +++ b/block/blk-merge.c
> > @@ -160,6 +160,62 @@ static inline unsigned get_max_io_size(struct request_queue *q,
> > return sectors;
> > }
> >
> > +/*
> > + * Split the bvec @bv into segments, and update all kinds of
> > + * variables.
> > + */
> > +static bool bvec_split_segs(struct request_queue *q, struct bio_vec *bv,
> > + unsigned *nsegs, unsigned *last_seg_size,
> > + unsigned *front_seg_size, unsigned *sectors)
> > +{
> > + bool need_split = false;
> > + unsigned len = bv->bv_len;
> > + unsigned total_len = 0;
> > + unsigned new_nsegs = 0, seg_size = 0;
>
> "unsigned int" here and everywhere else.

Curious why? I've wondered what govens use of "unsigned" vs "unsigned
int" recently and haven't found _the_ reason to pick one over the other.

Thanks,
Mike

2018-11-16 10:30:30

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 06/19] fs/buffer.c: use bvec iterator to truncate the bio

On Thu, Nov 15, 2018 at 04:52:53PM +0800, Ming Lei wrote:
> Once multi-page bvec is enabled, the last bvec may include more than one
> page, this patch use bvec_last_segment() to truncate the bio.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]

Reviewed-by: Omar Sandoval <[email protected]>

> Signed-off-by: Ming Lei <[email protected]>
> ---
> fs/buffer.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/fs/buffer.c b/fs/buffer.c
> index 1286c2b95498..fa37ad52e962 100644
> --- a/fs/buffer.c
> +++ b/fs/buffer.c
> @@ -3032,7 +3032,10 @@ void guard_bio_eod(int op, struct bio *bio)
>
> /* ..and clear the end of the buffer for reads */
> if (op == REQ_OP_READ) {
> - zero_user(bvec->bv_page, bvec->bv_offset + bvec->bv_len,
> + struct bio_vec bv;
> +
> + bvec_last_segment(bvec, &bv);
> + zero_user(bv.bv_page, bv.bv_offset + bv.bv_len,
> truncated_bytes);
> }
> }
> --
> 2.9.5
>

2018-11-21 02:46:47

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()

On Mon, Nov 19, 2018 at 04:49:27PM -0800, Sagi Grimberg wrote:
>
>> The only user in your final tree seems to be the loop driver, and
>> even that one only uses the helper for read/write bios.
>>
>> I think something like this would be much simpler in the end:
>
> The recently submitted nvme-tcp host driver should also be a user
> of this. Does it make sense to keep it as a helper then?

I did take a brief look at the code, and I really don't understand
why the heck it even deals with bios to start with. Like all the
other nvme transports it is a blk-mq driver and should iterate
over segments in a request and more or less ignore bios. Something
is horribly wrong in the design.

2018-11-15 19:01:06

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 03/19] block: use bio_for_each_bvec() to compute multi-page bvec count

First it is more efficient to use bio_for_each_bvec() in both
blk_bio_segment_split() and __blk_recalc_rq_segments() to compute how
many multi-page bvecs there are in the bio.

Secondly once bio_for_each_bvec() is used, the bvec may need to be
splitted because its length can be very longer than max segment size,
so we have to split the big bvec into several segments.

Thirdly when splitting multi-page bvec into segments, the max segment
limit may be reached, so the bio split need to be considered under
this situation too.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
block/blk-merge.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 76 insertions(+), 14 deletions(-)

diff --git a/block/blk-merge.c b/block/blk-merge.c
index 91b2af332a84..6f7deb94a23f 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -160,6 +160,62 @@ static inline unsigned get_max_io_size(struct request_queue *q,
return sectors;
}

+/*
+ * Split the bvec @bv into segments, and update all kinds of
+ * variables.
+ */
+static bool bvec_split_segs(struct request_queue *q, struct bio_vec *bv,
+ unsigned *nsegs, unsigned *last_seg_size,
+ unsigned *front_seg_size, unsigned *sectors)
+{
+ bool need_split = false;
+ unsigned len = bv->bv_len;
+ unsigned total_len = 0;
+ unsigned new_nsegs = 0, seg_size = 0;
+
+ if ((*nsegs >= queue_max_segments(q)) || !len)
+ return need_split;
+
+ /*
+ * Multipage bvec may be too big to hold in one segment,
+ * so the current bvec has to be splitted as multiple
+ * segments.
+ */
+ while (new_nsegs + *nsegs < queue_max_segments(q)) {
+ seg_size = min(queue_max_segment_size(q), len);
+
+ new_nsegs++;
+ total_len += seg_size;
+ len -= seg_size;
+
+ if ((queue_virt_boundary(q) && ((bv->bv_offset +
+ total_len) & queue_virt_boundary(q))) || !len)
+ break;
+ }
+
+ /* split in the middle of the bvec */
+ if (len)
+ need_split = true;
+
+ /* update front segment size */
+ if (!*nsegs) {
+ unsigned first_seg_size = seg_size;
+
+ if (new_nsegs > 1)
+ first_seg_size = queue_max_segment_size(q);
+ if (*front_seg_size < first_seg_size)
+ *front_seg_size = first_seg_size;
+ }
+
+ /* update other varibles */
+ *last_seg_size = seg_size;
+ *nsegs += new_nsegs;
+ if (sectors)
+ *sectors += total_len >> 9;
+
+ return need_split;
+}
+
static struct bio *blk_bio_segment_split(struct request_queue *q,
struct bio *bio,
struct bio_set *bs,
@@ -173,7 +229,7 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
struct bio *new = NULL;
const unsigned max_sectors = get_max_io_size(q, bio);

- bio_for_each_segment(bv, bio, iter) {
+ bio_for_each_bvec(bv, bio, iter) {
/*
* If the queue doesn't support SG gaps and adding this
* offset would create a gap, disallow it.
@@ -188,8 +244,12 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
*/
if (nsegs < queue_max_segments(q) &&
sectors < max_sectors) {
- nsegs++;
- sectors = max_sectors;
+ /* split in the middle of bvec */
+ bv.bv_len = (max_sectors - sectors) << 9;
+ bvec_split_segs(q, &bv, &nsegs,
+ &seg_size,
+ &front_seg_size,
+ &sectors);
}
goto split;
}
@@ -214,11 +274,12 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
if (nsegs == 1 && seg_size > front_seg_size)
front_seg_size = seg_size;

- nsegs++;
bvprv = bv;
bvprvp = &bvprv;
- seg_size = bv.bv_len;
- sectors += bv.bv_len >> 9;
+
+ if (bvec_split_segs(q, &bv, &nsegs, &seg_size,
+ &front_seg_size, &sectors))
+ goto split;

}

@@ -296,6 +357,7 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
struct bio_vec bv, bvprv = { NULL };
int cluster, prev = 0;
unsigned int seg_size, nr_phys_segs;
+ unsigned front_seg_size = bio->bi_seg_front_size;
struct bio *fbio, *bbio;
struct bvec_iter iter;

@@ -316,7 +378,7 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
seg_size = 0;
nr_phys_segs = 0;
for_each_bio(bio) {
- bio_for_each_segment(bv, bio, iter) {
+ bio_for_each_bvec(bv, bio, iter) {
/*
* If SG merging is disabled, each bio vector is
* a segment
@@ -336,20 +398,20 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
continue;
}
new_segment:
- if (nr_phys_segs == 1 && seg_size >
- fbio->bi_seg_front_size)
- fbio->bi_seg_front_size = seg_size;
+ if (nr_phys_segs == 1 && seg_size > front_seg_size)
+ front_seg_size = seg_size;

- nr_phys_segs++;
bvprv = bv;
prev = 1;
- seg_size = bv.bv_len;
+ bvec_split_segs(q, &bv, &nr_phys_segs, &seg_size,
+ &front_seg_size, NULL);
}
bbio = bio;
}

- if (nr_phys_segs == 1 && seg_size > fbio->bi_seg_front_size)
- fbio->bi_seg_front_size = seg_size;
+ if (nr_phys_segs == 1 && seg_size > front_seg_size)
+ front_seg_size = seg_size;
+ fbio->bi_seg_front_size = front_seg_size;
if (seg_size > bbio->bi_seg_back_size)
bbio->bi_seg_back_size = seg_size;

--
2.9.5

2018-11-19 18:44:36

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()

On Fri, Nov 16, 2018 at 02:45:41PM +0100, Christoph Hellwig wrote:
> On Thu, Nov 15, 2018 at 04:52:56PM +0800, Ming Lei wrote:
> > There are still cases in which we need to use bio_bvecs() for get the
> > number of multi-page segment, so introduce it.
>
> The only user in your final tree seems to be the loop driver, and
> even that one only uses the helper for read/write bios.
>
> I think something like this would be much simpler in the end:
>
> diff --git a/drivers/block/loop.c b/drivers/block/loop.c
> index d509902a8046..712511815ac6 100644
> --- a/drivers/block/loop.c
> +++ b/drivers/block/loop.c
> @@ -514,16 +514,18 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
> struct request *rq = blk_mq_rq_from_pdu(cmd);
> struct bio *bio = rq->bio;
> struct file *file = lo->lo_backing_file;
> + struct bvec_iter bvec_iter;
> + struct bio_vec tmp;
> unsigned int offset;
> int nr_bvec = 0;
> int ret;
>
> + __rq_for_each_bio(bio, rq)
> + bio_for_each_bvec(tmp, bio, bvec_iter)
> + nr_bvec++;
> +
> if (rq->bio != rq->biotail) {
> - struct bvec_iter iter;
> - struct bio_vec tmp;
>
> - __rq_for_each_bio(bio, rq)
> - nr_bvec += bio_bvecs(bio);
> bvec = kmalloc_array(nr_bvec, sizeof(struct bio_vec),
> GFP_NOIO);
> if (!bvec)
> @@ -537,7 +539,7 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
> * API will take care of all details for us.
> */
> __rq_for_each_bio(bio, rq)
> - bio_for_each_bvec(tmp, bio, iter) {
> + bio_for_each_bvec(tmp, bio, bvec_iter) {
> *bvec = tmp;
> bvec++;
> }
> @@ -551,7 +553,6 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
> */
> offset = bio->bi_iter.bi_bvec_done;
> bvec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
> - nr_bvec = bio_bvecs(bio);
> }
> atomic_set(&cmd->ref, 2);
>
> diff --git a/include/linux/bio.h b/include/linux/bio.h
> index dcad0b69f57a..379440d1ced0 100644
> --- a/include/linux/bio.h
> +++ b/include/linux/bio.h
> @@ -200,30 +200,6 @@ static inline unsigned bio_segments(struct bio *bio)
> }
> }
>
> -static inline unsigned bio_bvecs(struct bio *bio)
> -{
> - unsigned bvecs = 0;
> - struct bio_vec bv;
> - struct bvec_iter iter;
> -
> - /*
> - * We special case discard/write same/write zeroes, because they
> - * interpret bi_size differently:
> - */
> - switch (bio_op(bio)) {
> - case REQ_OP_DISCARD:
> - case REQ_OP_SECURE_ERASE:
> - case REQ_OP_WRITE_ZEROES:
> - return 0;
> - case REQ_OP_WRITE_SAME:
> - return 1;
> - default:
> - bio_for_each_bvec(bv, bio, iter)
> - bvecs++;
> - return bvecs;
> - }
> -}
> -
> /*
> * get a reference to a bio, so it won't disappear. the intended use is
> * something like:

OK, will do it in next version.

Thanks,
Ming

2018-11-16 19:31:27

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 03/19] block: use bio_for_each_bvec() to compute multi-page bvec count

On Thu, Nov 15, 2018 at 02:18:47PM -0800, Omar Sandoval wrote:
> My only reason to prefer unsigned int is consistency. unsigned int is
> much more common in the kernel:
>
> $ ag --cc -s 'unsigned\s+int' | wc -l
> 129632
> $ ag --cc -s 'unsigned\s+(?!char|short|int|long)' | wc -l
> 22435
>
> checkpatch also warns on plain unsigned.

Talk about chicken and egg. unsigned is perfectly valid C, and being
shorter often helps being more readable. checkpath is as so often
wrongly opinionated..

2018-11-19 18:47:11

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 08/19] btrfs: move bio_pages_all() to btrfs

On Mon, Nov 19, 2018 at 04:19:24PM +0800, Ming Lei wrote:
> On Fri, Nov 16, 2018 at 02:38:45PM +0100, Christoph Hellwig wrote:
> > On Thu, Nov 15, 2018 at 04:52:55PM +0800, Ming Lei wrote:
> > > BTRFS is the only user of this helper, so move this helper into
> > > BTRFS, and implement it via bio_for_each_segment_all(), since
> > > bio->bi_vcnt may not equal to number of pages after multipage bvec
> > > is enabled.
> >
> > btrfs only uses the value to check if it is larger than 1. No amount
> > of multipage bio merging should ever make bi_vcnt go from 0 to 1 or
> > vice versa.
>
> Could you explain a bit why?
>
> Suppose 2 physically continuous pages are added to this bio, .bi_vcnt
> can be 1 in case of multi-page bvec, but it is 2 in case of single-page
> bvec.

True, I did think of 0 vs 1.

The magic here in btrfs still doesn't make much sense. The comments
down in btrfs_check_repairable talk about sectors, so it might be a
leftover from the blocksize == PAGE_SIZE days (assuming those patches
actually got merged). I'd like to have the btrfs folks chime in,
but in the end we should probably check if the bio was larger than
a single sector here.

2018-11-21 14:58:34

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()


>> I would like to avoid growing bvec tables and keep everything
>> preallocated. Plus, a bvec_iter operates on a bvec which means
>> we'll need a table there as well... Not liking it so far...
>
> In case of bios in one request, we can't know how many bvecs there
> are except for calling rq_bvecs(), so it may not be suitable to
> preallocate the table. If you have to send the IO request in one send(),
> runtime allocation may be inevitable.

I don't want to do that, I want to work on a single bvec at a time like
the current implementation does.

> If you don't require to send the IO request in one send(), you may send
> one bio in one time, and just uses the bio's bvec table directly,
> such as the single bio case in lo_rw_aio().

we'd need some indication that we need to reinit my iter with the
new bvec, today we do:

static inline void nvme_tcp_advance_req(struct nvme_tcp_request *req,
int len)
{
req->snd.data_sent += len;
req->pdu_sent += len;
iov_iter_advance(&req->snd.iter, len);
if (!iov_iter_count(&req->snd.iter) &&
req->snd.data_sent < req->data_len) {
req->snd.curr_bio = req->snd.curr_bio->bi_next;
nvme_tcp_init_send_iter(req);
}
}

and initialize the send iter. I imagine that now I will need to
switch to the next bvec and only if I'm on the last I need to
use the next bio...

Do you offer an API for that?


>>> can this way avoid your blocking issue? You may see this
>>> example in branch 'rq->bio != rq->biotail' of lo_rw_aio().
>>
>> This is exactly an example of not ignoring the bios...
>
> Yeah, that is the most common example, given merge is enabled
> in most of cases. If the driver or device doesn't care merge,
> you can disable it and always get single bio request, then the
> bio's bvec table can be reused for send().

Does bvec_iter span bvecs with your patches? I didn't see that change?

>> I'm not sure how this helps me either. Unless we can set a bvec_iter to
>> span bvecs or have an abstract bio crossing when we re-initialize the
>> bvec_iter I don't see how I can ignore bios completely...
>
> rq_for_each_bvec() will iterate over all bvecs from all bios, so you
> needn't to see any bio in this req.

But I don't need this iteration, I need a transparent API like;
bvec2 = rq_bvec_next(rq, bvec)

This way I can simply always reinit my iter without thinking about how
the request/bios/bvecs are constructed...

> rq_bvecs() will return how many bvecs there are in this request(cover
> all bios in this req)

Still not very useful given that I don't want to use a table...

>>> So looks nvme-tcp host driver might be the 2nd driver which benefits
>>> from multi-page bvec directly.
>>>
>>> The multi-page bvec V11 has passed my tests and addressed almost
>>> all the comments during review on V10. I removed bio_vecs() in V11,
>>> but it won't be big deal, we can introduce them anytime when there
>>> is the requirement.
>>
>> multipage-bvecs and nvme-tcp are going to conflict, so it would be good
>> to coordinate on this. I think that nvme-tcp host needs some adjustments
>> as setting a bvec_iter. I'm under the impression that the change is rather
>> small and self-contained, but I'm not sure I have the full
>> picture here.
>
> I guess I may not get your exact requirement on block io iterator from nvme-tcp
> too, :-(

They are pretty much listed above. Today nvme-tcp sets an iterator with:

vec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
nsegs = bio_segments(bio);
size = bio->bi_iter.bi_size;
offset = bio->bi_iter.bi_bvec_done;
iov_iter_bvec(&req->snd.iter, WRITE, vec, nsegs, size);

and when done, iterate to the next bio and do the same.

With multipage bvec it would be great if we can simply have
something like rq_bvec_next() that would pretty much satisfy
the requirements from the nvme-tcp side...

2018-11-16 10:54:17

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 11/19] bcache: avoid to use bio_for_each_segment_all() in bch_bio_alloc_pages()

On Thu, Nov 15, 2018 at 04:52:58PM +0800, Ming Lei wrote:
> bch_bio_alloc_pages() is always called on one new bio, so it is safe
> to access the bvec table directly. Given it is the only kind of this
> case, open code the bvec table access since bio_for_each_segment_all()
> will be changed to support for iterating over multipage bvec.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Acked-by: Coly Li <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]
> Signed-off-by: Ming Lei <[email protected]>
> ---
> drivers/md/bcache/util.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
> index 20eddeac1531..8517aebcda2d 100644
> --- a/drivers/md/bcache/util.c
> +++ b/drivers/md/bcache/util.c
> @@ -270,7 +270,7 @@ int bch_bio_alloc_pages(struct bio *bio, gfp_t gfp_mask)
> int i;
> struct bio_vec *bv;
>
> - bio_for_each_segment_all(bv, bio, i) {
> + for (i = 0, bv = bio->bi_io_vec; i < bio->bi_vcnt; bv++) {

This is missing an i++.

2018-11-16 10:34:08

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 08/19] btrfs: move bio_pages_all() to btrfs

On Thu, Nov 15, 2018 at 04:52:55PM +0800, Ming Lei wrote:
> BTRFS is the only user of this helper, so move this helper into
> BTRFS, and implement it via bio_for_each_segment_all(), since
> bio->bi_vcnt may not equal to number of pages after multipage bvec
> is enabled.

Shouldn't you also get rid of bio_pages_all() in this patch?

> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]
> Signed-off-by: Ming Lei <[email protected]>
> ---
> fs/btrfs/extent_io.c | 14 +++++++++++++-
> 1 file changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
> index 5d5965297e7e..874bb9aeebdc 100644
> --- a/fs/btrfs/extent_io.c
> +++ b/fs/btrfs/extent_io.c
> @@ -2348,6 +2348,18 @@ struct bio *btrfs_create_repair_bio(struct inode *inode, struct bio *failed_bio,
> return bio;
> }
>
> +static unsigned btrfs_bio_pages_all(struct bio *bio)
> +{
> + unsigned i;
> + struct bio_vec *bv;
> +
> + WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED));
> +
> + bio_for_each_segment_all(bv, bio, i)
> + ;
> + return i;
> +}
> +
> /*
> * this is a generic handler for readpage errors (default
> * readpage_io_failed_hook). if other copies exist, read those and write back
> @@ -2368,7 +2380,7 @@ static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
> int read_mode = 0;
> blk_status_t status;
> int ret;
> - unsigned failed_bio_pages = bio_pages_all(failed_bio);
> + unsigned failed_bio_pages = btrfs_bio_pages_all(failed_bio);
>
> BUG_ON(bio_op(failed_bio) == REQ_OP_WRITE);
>
> --
> 2.9.5
>

2018-11-20 11:15:56

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()


> The only user in your final tree seems to be the loop driver, and
> even that one only uses the helper for read/write bios.
>
> I think something like this would be much simpler in the end:

The recently submitted nvme-tcp host driver should also be a user
of this. Does it make sense to keep it as a helper then?

2018-11-16 08:28:30

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 03/19] block: use bio_for_each_bvec() to compute multi-page bvec count

On Thu, Nov 15, 2018 at 04:05:10PM -0500, Mike Snitzer wrote:
> On Thu, Nov 15 2018 at 3:20pm -0500,
> Omar Sandoval <[email protected]> wrote:
>
> > On Thu, Nov 15, 2018 at 04:52:50PM +0800, Ming Lei wrote:
> > > First it is more efficient to use bio_for_each_bvec() in both
> > > blk_bio_segment_split() and __blk_recalc_rq_segments() to compute how
> > > many multi-page bvecs there are in the bio.
> > >
> > > Secondly once bio_for_each_bvec() is used, the bvec may need to be
> > > splitted because its length can be very longer than max segment size,
> > > so we have to split the big bvec into several segments.
> > >
> > > Thirdly when splitting multi-page bvec into segments, the max segment
> > > limit may be reached, so the bio split need to be considered under
> > > this situation too.
> > >
> > > Cc: Dave Chinner <[email protected]>
> > > Cc: Kent Overstreet <[email protected]>
> > > Cc: Mike Snitzer <[email protected]>
> > > Cc: [email protected]
> > > Cc: Alexander Viro <[email protected]>
> > > Cc: [email protected]
> > > Cc: Shaohua Li <[email protected]>
> > > Cc: [email protected]
> > > Cc: [email protected]
> > > Cc: David Sterba <[email protected]>
> > > Cc: [email protected]
> > > Cc: Darrick J. Wong <[email protected]>
> > > Cc: [email protected]
> > > Cc: Gao Xiang <[email protected]>
> > > Cc: Christoph Hellwig <[email protected]>
> > > Cc: Theodore Ts'o <[email protected]>
> > > Cc: [email protected]
> > > Cc: Coly Li <[email protected]>
> > > Cc: [email protected]
> > > Cc: Boaz Harrosh <[email protected]>
> > > Cc: Bob Peterson <[email protected]>
> > > Cc: [email protected]
> > > Signed-off-by: Ming Lei <[email protected]>
> > > ---
> > > block/blk-merge.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++---------
> > > 1 file changed, 76 insertions(+), 14 deletions(-)
> > >
> > > diff --git a/block/blk-merge.c b/block/blk-merge.c
> > > index 91b2af332a84..6f7deb94a23f 100644
> > > --- a/block/blk-merge.c
> > > +++ b/block/blk-merge.c
> > > @@ -160,6 +160,62 @@ static inline unsigned get_max_io_size(struct request_queue *q,
> > > return sectors;
> > > }
> > >
> > > +/*
> > > + * Split the bvec @bv into segments, and update all kinds of
> > > + * variables.
> > > + */
> > > +static bool bvec_split_segs(struct request_queue *q, struct bio_vec *bv,
> > > + unsigned *nsegs, unsigned *last_seg_size,
> > > + unsigned *front_seg_size, unsigned *sectors)
> > > +{
> > > + bool need_split = false;
> > > + unsigned len = bv->bv_len;
> > > + unsigned total_len = 0;
> > > + unsigned new_nsegs = 0, seg_size = 0;
> >
> > "unsigned int" here and everywhere else.
>
> Curious why? I've wondered what govens use of "unsigned" vs "unsigned
> int" recently and haven't found _the_ reason to pick one over the other.

My only reason to prefer unsigned int is consistency. unsigned int is
much more common in the kernel:

$ ag --cc -s 'unsigned\s+int' | wc -l
129632
$ ag --cc -s 'unsigned\s+(?!char|short|int|long)' | wc -l
22435

checkpatch also warns on plain unsigned.

2018-11-16 09:33:55

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 05/19] block: introduce bvec_last_segment()

On Thu, Nov 15, 2018 at 04:52:52PM +0800, Ming Lei wrote:
> BTRFS and guard_bio_eod() need to get the last singlepage segment
> from one multipage bvec, so introduce this helper to make them happy.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]

Reviewed-by: Omar Sandoval <[email protected]>

Minor comments below.

> Signed-off-by: Ming Lei <[email protected]>
> ---
> include/linux/bvec.h | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
>
> diff --git a/include/linux/bvec.h b/include/linux/bvec.h
> index 3d61352cd8cf..01616a0b6220 100644
> --- a/include/linux/bvec.h
> +++ b/include/linux/bvec.h
> @@ -216,4 +216,29 @@ static inline bool mp_bvec_iter_advance(const struct bio_vec *bv,
> .bi_bvec_done = 0, \
> }
>
> +/*
> + * Get the last singlepage segment from the multipage bvec and store it
> + * in @seg
> + */
> +static inline void bvec_last_segment(const struct bio_vec *bvec,
> + struct bio_vec *seg)

Indentation is all messed up here.

> +{
> + unsigned total = bvec->bv_offset + bvec->bv_len;
> + unsigned last_page = total / PAGE_SIZE;
> +
> + if (last_page * PAGE_SIZE == total)
> + last_page--;

I think this could just be

unsigned int last_page = (total - 1) / PAGE_SIZE;

> + seg->bv_page = nth_page(bvec->bv_page, last_page);
> +
> + /* the whole segment is inside the last page */
> + if (bvec->bv_offset >= last_page * PAGE_SIZE) {
> + seg->bv_offset = bvec->bv_offset % PAGE_SIZE;
> + seg->bv_len = bvec->bv_len;
> + } else {
> + seg->bv_offset = 0;
> + seg->bv_len = total - last_page * PAGE_SIZE;
> + }
> +}
> +
> #endif /* __LINUX_BVEC_ITER_H */
> --
> 2.9.5
>

2018-11-19 18:58:42

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 13/19] iomap & xfs: only account for new added page

On Thu, Nov 15, 2018 at 05:46:58PM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:53:00PM +0800, Ming Lei wrote:
> > After multi-page is enabled, one new page may be merged to a segment
> > even though it is a new added page.
> >
> > This patch deals with this issue by post-check in case of merge, and
> > only a freshly new added page need to be dealt with for iomap & xfs.
> >
> > Cc: Dave Chinner <[email protected]>
> > Cc: Kent Overstreet <[email protected]>
> > Cc: Mike Snitzer <[email protected]>
> > Cc: [email protected]
> > Cc: Alexander Viro <[email protected]>
> > Cc: [email protected]
> > Cc: Shaohua Li <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: David Sterba <[email protected]>
> > Cc: [email protected]
> > Cc: Darrick J. Wong <[email protected]>
> > Cc: [email protected]
> > Cc: Gao Xiang <[email protected]>
> > Cc: Christoph Hellwig <[email protected]>
> > Cc: Theodore Ts'o <[email protected]>
> > Cc: [email protected]
> > Cc: Coly Li <[email protected]>
> > Cc: [email protected]
> > Cc: Boaz Harrosh <[email protected]>
> > Cc: Bob Peterson <[email protected]>
> > Cc: [email protected]
> > Signed-off-by: Ming Lei <[email protected]>
> > ---
> > fs/iomap.c | 22 ++++++++++++++--------
> > fs/xfs/xfs_aops.c | 10 ++++++++--
> > include/linux/bio.h | 11 +++++++++++
> > 3 files changed, 33 insertions(+), 10 deletions(-)
> >
> > diff --git a/fs/iomap.c b/fs/iomap.c
> > index df0212560b36..a1b97a5c726a 100644
> > --- a/fs/iomap.c
> > +++ b/fs/iomap.c
> > @@ -288,6 +288,7 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
> > loff_t orig_pos = pos;
> > unsigned poff, plen;
> > sector_t sector;
> > + bool need_account = false;
> >
> > if (iomap->type == IOMAP_INLINE) {
> > WARN_ON_ONCE(pos);
> > @@ -313,18 +314,15 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
> > */
> > sector = iomap_sector(iomap, pos);
> > if (ctx->bio && bio_end_sector(ctx->bio) == sector) {
> > - if (__bio_try_merge_page(ctx->bio, page, plen, poff))
> > + if (__bio_try_merge_page(ctx->bio, page, plen, poff)) {
> > + need_account = iop && bio_is_last_segment(ctx->bio,
> > + page, plen, poff);
>
> It's redundant to make this iop && ... since you already check
> iop && need_account below. Maybe rename it to added_page? Also, this
> indentation is wack.

We may avoid to call bio_is_last_segment() in case of !iop, and will
fix the indentation.

Looks added_page is one better name.

>
> > goto done;
> > + }
> > is_contig = true;
> > }
> >
> > - /*
> > - * If we start a new segment we need to increase the read count, and we
> > - * need to do so before submitting any previous full bio to make sure
> > - * that we don't prematurely unlock the page.
> > - */
> > - if (iop)
> > - atomic_inc(&iop->read_count);
> > + need_account = true;
> >
> > if (!ctx->bio || !is_contig || bio_full(ctx->bio)) {
> > gfp_t gfp = mapping_gfp_constraint(page->mapping, GFP_KERNEL);
> > @@ -347,6 +345,14 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
> > __bio_add_page(ctx->bio, page, plen, poff);
> > done:
> > /*
> > + * If we add a new page we need to increase the read count, and we
> > + * need to do so before submitting any previous full bio to make sure
> > + * that we don't prematurely unlock the page.
> > + */
> > + if (iop && need_account)
> > + atomic_inc(&iop->read_count);
> > +
> > + /*
> > * Move the caller beyond our range so that it keeps making progress.
> > * For that we have to include any leading non-uptodate ranges, but
> > * we can skip trailing ones as they will be handled in the next
> > diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> > index 1f1829e506e8..d8e9cc9f751a 100644
> > --- a/fs/xfs/xfs_aops.c
> > +++ b/fs/xfs/xfs_aops.c
> > @@ -603,6 +603,7 @@ xfs_add_to_ioend(
> > unsigned len = i_blocksize(inode);
> > unsigned poff = offset & (PAGE_SIZE - 1);
> > sector_t sector;
> > + bool need_account;
> >
> > sector = xfs_fsb_to_db(ip, wpc->imap.br_startblock) +
> > ((offset - XFS_FSB_TO_B(mp, wpc->imap.br_startoff)) >> 9);
> > @@ -617,13 +618,18 @@ xfs_add_to_ioend(
> > }
> >
> > if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff)) {
> > - if (iop)
> > - atomic_inc(&iop->write_count);
> > + need_account = true;
> > if (bio_full(wpc->ioend->io_bio))
> > xfs_chain_bio(wpc->ioend, wbc, bdev, sector);
> > __bio_add_page(wpc->ioend->io_bio, page, len, poff);
> > + } else {
> > + need_account = iop && bio_is_last_segment(wpc->ioend->io_bio,
> > + page, len, poff);
>
> Same here, no need for iop &&, rename it added_page, indentation is off.
>
> > }
> >
> > + if (iop && need_account)
> > + atomic_inc(&iop->write_count);
> > +
> > wpc->ioend->io_size += len;
> > }
> >
> > diff --git a/include/linux/bio.h b/include/linux/bio.h
> > index 1a2430a8b89d..5040e9a2eb09 100644
> > --- a/include/linux/bio.h
> > +++ b/include/linux/bio.h
> > @@ -341,6 +341,17 @@ static inline struct bio_vec *bio_last_bvec_all(struct bio *bio)
> > return &bio->bi_io_vec[bio->bi_vcnt - 1];
> > }
> >
> > +/* iomap needs this helper to deal with sub-pagesize bvec */
> > +static inline bool bio_is_last_segment(struct bio *bio, struct page *page,
> > + unsigned int len, unsigned int off)
>
> Indentation.

OK.

Thanks,
Ming

2018-11-19 12:47:56

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 01/19] block: introduce multi-page page bvec helpers

On Thu, Nov 15, 2018 at 10:25:59AM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:52:48PM +0800, Ming Lei wrote:
> > This patch introduces helpers of 'mp_bvec_iter_*' for multipage
> > bvec support.
> >
> > The introduced helpers treate one bvec as real multi-page segment,
> > which may include more than one pages.
> >
> > The existed helpers of bvec_iter_* are interfaces for supporting current
> > bvec iterator which is thought as single-page by drivers, fs, dm and
> > etc. These introduced helpers will build single-page bvec in flight, so
> > this way won't break current bio/bvec users, which needn't any change.
> >
> > Cc: Dave Chinner <[email protected]>
> > Cc: Kent Overstreet <[email protected]>
> > Cc: Mike Snitzer <[email protected]>
> > Cc: [email protected]
> > Cc: Alexander Viro <[email protected]>
> > Cc: [email protected]
> > Cc: Shaohua Li <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: David Sterba <[email protected]>
> > Cc: [email protected]
> > Cc: Darrick J. Wong <[email protected]>
> > Cc: [email protected]
> > Cc: Gao Xiang <[email protected]>
> > Cc: Christoph Hellwig <[email protected]>
> > Cc: Theodore Ts'o <[email protected]>
> > Cc: [email protected]
> > Cc: Coly Li <[email protected]>
> > Cc: [email protected]
> > Cc: Boaz Harrosh <[email protected]>
> > Cc: Bob Peterson <[email protected]>
> > Cc: [email protected]
>
> Reviewed-by: Omar Sandoval <[email protected]>
>
> But a couple of comments below.
>
> > Signed-off-by: Ming Lei <[email protected]>
> > ---
> > include/linux/bvec.h | 63 +++++++++++++++++++++++++++++++++++++++++++++++++---
> > 1 file changed, 60 insertions(+), 3 deletions(-)
> >
> > diff --git a/include/linux/bvec.h b/include/linux/bvec.h
> > index 02c73c6aa805..8ef904a50577 100644
> > --- a/include/linux/bvec.h
> > +++ b/include/linux/bvec.h
> > @@ -23,6 +23,44 @@
> > #include <linux/kernel.h>
> > #include <linux/bug.h>
> > #include <linux/errno.h>
> > +#include <linux/mm.h>
> > +
> > +/*
> > + * What is multi-page bvecs?
> > + *
> > + * - bvecs stored in bio->bi_io_vec is always multi-page(mp) style
> > + *
> > + * - bvec(struct bio_vec) represents one physically contiguous I/O
> > + * buffer, now the buffer may include more than one pages after
> > + * multi-page(mp) bvec is supported, and all these pages represented
> > + * by one bvec is physically contiguous. Before mp support, at most
> > + * one page is included in one bvec, we call it single-page(sp)
> > + * bvec.
> > + *
> > + * - .bv_page of the bvec represents the 1st page in the mp bvec
> > + *
> > + * - .bv_offset of the bvec represents offset of the buffer in the bvec
> > + *
> > + * The effect on the current drivers/filesystem/dm/bcache/...:
> > + *
> > + * - almost everyone supposes that one bvec only includes one single
> > + * page, so we keep the sp interface not changed, for example,
> > + * bio_for_each_segment() still returns bvec with single page
> > + *
> > + * - bio_for_each_segment*() will be changed to return single-page
> > + * bvec too
> > + *
> > + * - during iterating, iterator variable(struct bvec_iter) is always
> > + * updated in multipage bvec style and that means bvec_iter_advance()
> > + * is kept not changed
> > + *
> > + * - returned(copied) single-page bvec is built in flight by bvec
> > + * helpers from the stored multipage bvec
> > + *
> > + * - In case that some components(such as iov_iter) need to support
> > + * multi-page bvec, we introduce new helpers(mp_bvec_iter_*) for
> > + * them.
> > + */
>
> This comment sounds more like a commit message (i.e., how were things
> before, and how are we changing them). In a couple of years when I read
> this code, I probably won't care how it was changed, just how it works.
> So I think a comment explaining the concepts of multi-page and
> single-page bvecs is very useful, but please move all of the "foo was
> changed" and "before mp support" type stuff to the commit message.

OK.

>
> > /*
> > * was unsigned short, but we might as well be ready for > 64kB I/O pages
> > @@ -50,16 +88,35 @@ struct bvec_iter {
> > */
> > #define __bvec_iter_bvec(bvec, iter) (&(bvec)[(iter).bi_idx])
> >
> > -#define bvec_iter_page(bvec, iter) \
> > +#define mp_bvec_iter_page(bvec, iter) \
> > (__bvec_iter_bvec((bvec), (iter))->bv_page)
> >
> > -#define bvec_iter_len(bvec, iter) \
> > +#define mp_bvec_iter_len(bvec, iter) \
> > min((iter).bi_size, \
> > __bvec_iter_bvec((bvec), (iter))->bv_len - (iter).bi_bvec_done)
> >
> > -#define bvec_iter_offset(bvec, iter) \
> > +#define mp_bvec_iter_offset(bvec, iter) \
> > (__bvec_iter_bvec((bvec), (iter))->bv_offset + (iter).bi_bvec_done)
> >
> > +#define mp_bvec_iter_page_idx(bvec, iter) \
> > + (mp_bvec_iter_offset((bvec), (iter)) / PAGE_SIZE)
> > +
> > +/*
> > + * <page, offset,length> of single-page(sp) segment.
> > + *
> > + * This helpers are for building sp bvec in flight.
> > + */
> > +#define bvec_iter_offset(bvec, iter) \
> > + (mp_bvec_iter_offset((bvec), (iter)) % PAGE_SIZE)
> > +
> > +#define bvec_iter_len(bvec, iter) \
> > + min_t(unsigned, mp_bvec_iter_len((bvec), (iter)), \
> > + (PAGE_SIZE - (bvec_iter_offset((bvec), (iter)))))
>
> The parentheses around (bvec_iter_offset((bvec), (iter))) and
> (PAGE_SIZE - (bvec_iter_offset((bvec), (iter)))) are unnecessary
> clutter. This looks easier to read to me:

Good catch!

Thanks,
Ming

2018-11-19 18:55:36

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 12/19] block: allow bio_for_each_segment_all() to iterate over multi-page bvec

On Thu, Nov 15, 2018 at 05:22:45PM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:52:59PM +0800, Ming Lei wrote:
> > This patch introduces one extra iterator variable to bio_for_each_segment_all(),
> > then we can allow bio_for_each_segment_all() to iterate over multi-page bvec.
> >
> > Given it is just one mechannical & simple change on all bio_for_each_segment_all()
> > users, this patch does tree-wide change in one single patch, so that we can
> > avoid to use a temporary helper for this conversion.
> >
> > Cc: Dave Chinner <[email protected]>
> > Cc: Kent Overstreet <[email protected]>
> > Cc: [email protected]
> > Cc: Alexander Viro <[email protected]>
> > Cc: Shaohua Li <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: David Sterba <[email protected]>
> > Cc: Darrick J. Wong <[email protected]>
> > Cc: Gao Xiang <[email protected]>
> > Cc: Christoph Hellwig <[email protected]>
> > Cc: Theodore Ts'o <[email protected]>
> > Cc: [email protected]
> > Cc: Coly Li <[email protected]>
> > Cc: [email protected]
> > Cc: Boaz Harrosh <[email protected]>
> > Cc: Bob Peterson <[email protected]>
> > Cc: [email protected]
> > Signed-off-by: Ming Lei <[email protected]>
> > ---
> > block/bio.c | 27 ++++++++++++++++++---------
> > block/blk-zoned.c | 1 +
> > block/bounce.c | 6 ++++--
> > drivers/md/bcache/btree.c | 3 ++-
> > drivers/md/dm-crypt.c | 3 ++-
> > drivers/md/raid1.c | 3 ++-
> > drivers/staging/erofs/data.c | 3 ++-
> > drivers/staging/erofs/unzip_vle.c | 3 ++-
> > fs/block_dev.c | 6 ++++--
> > fs/btrfs/compression.c | 3 ++-
> > fs/btrfs/disk-io.c | 3 ++-
> > fs/btrfs/extent_io.c | 12 ++++++++----
> > fs/btrfs/inode.c | 6 ++++--
> > fs/btrfs/raid56.c | 3 ++-
> > fs/crypto/bio.c | 3 ++-
> > fs/direct-io.c | 4 +++-
> > fs/exofs/ore.c | 3 ++-
> > fs/exofs/ore_raid.c | 3 ++-
> > fs/ext4/page-io.c | 3 ++-
> > fs/ext4/readpage.c | 3 ++-
> > fs/f2fs/data.c | 9 ++++++---
> > fs/gfs2/lops.c | 6 ++++--
> > fs/gfs2/meta_io.c | 3 ++-
> > fs/iomap.c | 6 ++++--
> > fs/mpage.c | 3 ++-
> > fs/xfs/xfs_aops.c | 5 +++--
> > include/linux/bio.h | 11 +++++++++--
> > include/linux/bvec.h | 31 +++++++++++++++++++++++++++++++
> > 28 files changed, 129 insertions(+), 46 deletions(-)
> >
>
> [snip]
>
> > diff --git a/include/linux/bio.h b/include/linux/bio.h
> > index 3496c816946e..1a2430a8b89d 100644
> > --- a/include/linux/bio.h
> > +++ b/include/linux/bio.h
> > @@ -131,12 +131,19 @@ static inline bool bio_full(struct bio *bio)
> > return bio->bi_vcnt >= bio->bi_max_vecs;
> > }
> >
> > +#define bvec_for_each_segment(bv, bvl, i, iter_all) \
> > + for (bv = bvec_init_iter_all(&iter_all); \
> > + (iter_all.done < (bvl)->bv_len) && \
> > + ((bvec_next_segment((bvl), &iter_all)), 1); \
>
> The parentheses around (bvec_next_segment((bvl), &iter_all)) are
> unnecessary.

OK.

>
> > + iter_all.done += bv->bv_len, i += 1)
> > +
> > /*
> > * drivers should _never_ use the all version - the bio may have been split
> > * before it got to the driver and the driver won't own all of it
> > */
> > -#define bio_for_each_segment_all(bvl, bio, i) \
> > - for (i = 0, bvl = (bio)->bi_io_vec; i < (bio)->bi_vcnt; i++, bvl++)
> > +#define bio_for_each_segment_all(bvl, bio, i, iter_all) \
> > + for (i = 0, iter_all.idx = 0; iter_all.idx < (bio)->bi_vcnt; iter_all.idx++) \
> > + bvec_for_each_segment(bvl, &((bio)->bi_io_vec[iter_all.idx]), i, iter_all)
>
> Would it be possible to move i into iter_all to streamline this a bit?

That may may cause unnecessary conversion work for us, because the local
variable 'i' is defined in external function.

>
> > static inline void __bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
> > unsigned bytes, bool mp)
> > diff --git a/include/linux/bvec.h b/include/linux/bvec.h
> > index 01616a0b6220..02f26d2b59ad 100644
> > --- a/include/linux/bvec.h
> > +++ b/include/linux/bvec.h
> > @@ -82,6 +82,12 @@ struct bvec_iter {
> > current bvec */
> > };
> >
> > +struct bvec_iter_all {
> > + struct bio_vec bv;
> > + int idx;
> > + unsigned done;
> > +};
> > +
> > /*
> > * various member access, note that bio_data should of course not be used
> > * on highmem page vectors
> > @@ -216,6 +222,31 @@ static inline bool mp_bvec_iter_advance(const struct bio_vec *bv,
> > .bi_bvec_done = 0, \
> > }
> >
> > +static inline struct bio_vec *bvec_init_iter_all(struct bvec_iter_all *iter_all)
> > +{
> > + iter_all->bv.bv_page = NULL;
> > + iter_all->done = 0;
> > +
> > + return &iter_all->bv;
> > +}
> > +
> > +/* used for chunk_for_each_segment */
> > +static inline void bvec_next_segment(const struct bio_vec *bvec,
> > + struct bvec_iter_all *iter_all)
>
> Indentation.

OK.

>
> > +{
> > + struct bio_vec *bv = &iter_all->bv;
> > +
> > + if (bv->bv_page) {
> > + bv->bv_page += 1;
> > + bv->bv_offset = 0;
> > + } else {
> > + bv->bv_page = bvec->bv_page;
> > + bv->bv_offset = bvec->bv_offset;
> > + }
> > + bv->bv_len = min_t(unsigned int, PAGE_SIZE - bv->bv_offset,
> > + bvec->bv_len - iter_all->done);
>
> Indentation.

OK.

--
Ming

2018-11-19 18:15:11

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 04/19] block: use bio_for_each_bvec() to map sg

On Fri, Nov 16, 2018 at 02:33:14PM +0100, Christoph Hellwig wrote:
> > + if (!*sg)
> > + return sglist;
> > + else {
>
> No need for an else after an early return.

OK, good catch!

Thanks,
Ming

2018-11-19 18:50:44

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 11/19] bcache: avoid to use bio_for_each_segment_all() in bch_bio_alloc_pages()

On Thu, Nov 15, 2018 at 04:44:02PM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:52:58PM +0800, Ming Lei wrote:
> > bch_bio_alloc_pages() is always called on one new bio, so it is safe
> > to access the bvec table directly. Given it is the only kind of this
> > case, open code the bvec table access since bio_for_each_segment_all()
> > will be changed to support for iterating over multipage bvec.
> >
> > Cc: Dave Chinner <[email protected]>
> > Cc: Kent Overstreet <[email protected]>
> > Acked-by: Coly Li <[email protected]>
> > Cc: Mike Snitzer <[email protected]>
> > Cc: [email protected]
> > Cc: Alexander Viro <[email protected]>
> > Cc: [email protected]
> > Cc: Shaohua Li <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: David Sterba <[email protected]>
> > Cc: [email protected]
> > Cc: Darrick J. Wong <[email protected]>
> > Cc: [email protected]
> > Cc: Gao Xiang <[email protected]>
> > Cc: Christoph Hellwig <[email protected]>
> > Cc: Theodore Ts'o <[email protected]>
> > Cc: [email protected]
> > Cc: Coly Li <[email protected]>
> > Cc: [email protected]
> > Cc: Boaz Harrosh <[email protected]>
> > Cc: Bob Peterson <[email protected]>
> > Cc: [email protected]
> > Signed-off-by: Ming Lei <[email protected]>
> > ---
> > drivers/md/bcache/util.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
> > index 20eddeac1531..8517aebcda2d 100644
> > --- a/drivers/md/bcache/util.c
> > +++ b/drivers/md/bcache/util.c
> > @@ -270,7 +270,7 @@ int bch_bio_alloc_pages(struct bio *bio, gfp_t gfp_mask)
> > int i;
> > struct bio_vec *bv;
> >
> > - bio_for_each_segment_all(bv, bio, i) {
> > + for (i = 0, bv = bio->bi_io_vec; i < bio->bi_vcnt; bv++) {
>
> This is missing an i++.

Good catch, will fix it in next version.

thanks,
Ming

2018-11-16 19:53:30

by Gao Xiang

[permalink] [raw]
Subject: Re: [PATCH V10 03/19] block: use bio_for_each_bvec() to compute multi-page bvec count


On 2018/11/16 17:19, Christoph Hellwig wrote:
> On Thu, Nov 15, 2018 at 02:18:47PM -0800, Omar Sandoval wrote:
>> My only reason to prefer unsigned int is consistency. unsigned int is
>> much more common in the kernel:
>>
>> $ ag --cc -s 'unsigned\s+int' | wc -l
>> 129632
>> $ ag --cc -s 'unsigned\s+(?!char|short|int|long)' | wc -l
>> 22435
>>
>> checkpatch also warns on plain unsigned.
>
> Talk about chicken and egg. unsigned is perfectly valid C, and being
> shorter often helps being more readable. checkpath is as so often
> wrongly opinionated..
>

sigh...I personally tend to use "unsigned" instead of "unsigned int" as well,
but checkpatch.pl also suggests erofs to use "unsigned int" :-(

Thanks,
Gao Xiang

2018-11-19 18:52:49

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 12/19] block: allow bio_for_each_segment_all() to iterate over multi-page bvec

On Thu, Nov 15, 2018 at 01:42:52PM +0100, David Sterba wrote:
> On Thu, Nov 15, 2018 at 04:52:59PM +0800, Ming Lei wrote:
> > diff --git a/block/blk-zoned.c b/block/blk-zoned.c
> > index 13ba2011a306..789b09ae402a 100644
> > --- a/block/blk-zoned.c
> > +++ b/block/blk-zoned.c
> > @@ -123,6 +123,7 @@ static int blk_report_zones(struct gendisk *disk, sector_t sector,
> > unsigned int z = 0, n, nrz = *nr_zones;
> > sector_t capacity = get_capacity(disk);
> > int ret;
> > + struct bvec_iter_all iter_all;
> >
> > while (z < nrz && sector < capacity) {
> > n = nrz - z;
>
> iter_all is added but not used and I don't see any
> bio_for_each_segment_all for conversion in this function.

Good catch, will fix it in next version.

Thanks,
Ming

2018-11-17 02:53:37

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 18/19] block: kill QUEUE_FLAG_NO_SG_MERGE

On Fri, Nov 16, 2018 at 02:59:22PM +0100, Christoph Hellwig wrote:
> On Thu, Nov 15, 2018 at 06:18:11PM -0800, Omar Sandoval wrote:
> > This commit message wasn't very clear. Is it the case that
> > QUEUE_FLAG_NO_SG_MERGE is no longer set by any drivers?
>
> I think he wants to say that not doing S/G merging is rather pointless
> with the current setup of the I/O path, as it isn't going to save
> you a significant amount of cycles.

Okay, that makes sense. Ming, you can add

Reviewed-by: Omar Sandoval <[email protected]>

2018-11-19 18:13:45

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 03/19] block: use bio_for_each_bvec() to compute multi-page bvec count

On Thu, Nov 15, 2018 at 12:20:28PM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:52:50PM +0800, Ming Lei wrote:
> > First it is more efficient to use bio_for_each_bvec() in both
> > blk_bio_segment_split() and __blk_recalc_rq_segments() to compute how
> > many multi-page bvecs there are in the bio.
> >
> > Secondly once bio_for_each_bvec() is used, the bvec may need to be
> > splitted because its length can be very longer than max segment size,
> > so we have to split the big bvec into several segments.
> >
> > Thirdly when splitting multi-page bvec into segments, the max segment
> > limit may be reached, so the bio split need to be considered under
> > this situation too.
> >
> > Cc: Dave Chinner <[email protected]>
> > Cc: Kent Overstreet <[email protected]>
> > Cc: Mike Snitzer <[email protected]>
> > Cc: [email protected]
> > Cc: Alexander Viro <[email protected]>
> > Cc: [email protected]
> > Cc: Shaohua Li <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: David Sterba <[email protected]>
> > Cc: [email protected]
> > Cc: Darrick J. Wong <[email protected]>
> > Cc: [email protected]
> > Cc: Gao Xiang <[email protected]>
> > Cc: Christoph Hellwig <[email protected]>
> > Cc: Theodore Ts'o <[email protected]>
> > Cc: [email protected]
> > Cc: Coly Li <[email protected]>
> > Cc: [email protected]
> > Cc: Boaz Harrosh <[email protected]>
> > Cc: Bob Peterson <[email protected]>
> > Cc: [email protected]
> > Signed-off-by: Ming Lei <[email protected]>
> > ---
> > block/blk-merge.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++---------
> > 1 file changed, 76 insertions(+), 14 deletions(-)
> >
> > diff --git a/block/blk-merge.c b/block/blk-merge.c
> > index 91b2af332a84..6f7deb94a23f 100644
> > --- a/block/blk-merge.c
> > +++ b/block/blk-merge.c
> > @@ -160,6 +160,62 @@ static inline unsigned get_max_io_size(struct request_queue *q,
> > return sectors;
> > }
> >
> > +/*
> > + * Split the bvec @bv into segments, and update all kinds of
> > + * variables.
> > + */
> > +static bool bvec_split_segs(struct request_queue *q, struct bio_vec *bv,
> > + unsigned *nsegs, unsigned *last_seg_size,
> > + unsigned *front_seg_size, unsigned *sectors)
> > +{
> > + bool need_split = false;
> > + unsigned len = bv->bv_len;
> > + unsigned total_len = 0;
> > + unsigned new_nsegs = 0, seg_size = 0;
>
> "unsigned int" here and everywhere else.
>
> > + if ((*nsegs >= queue_max_segments(q)) || !len)
> > + return need_split;
> > +
> > + /*
> > + * Multipage bvec may be too big to hold in one segment,
> > + * so the current bvec has to be splitted as multiple
> > + * segments.
> > + */
> > + while (new_nsegs + *nsegs < queue_max_segments(q)) {
> > + seg_size = min(queue_max_segment_size(q), len);
> > +
> > + new_nsegs++;
> > + total_len += seg_size;
> > + len -= seg_size;
> > +
> > + if ((queue_virt_boundary(q) && ((bv->bv_offset +
> > + total_len) & queue_virt_boundary(q))) || !len)
> > + break;
>
> Checking queue_virt_boundary(q) != 0 is superfluous, and the len check
> could just control the loop, i.e.,
>
> while (len && new_nsegs + *nsegs < queue_max_segments(q)) {
> seg_size = min(queue_max_segment_size(q), len);
>
> new_nsegs++;
> total_len += seg_size;
> len -= seg_size;
>
> if ((bv->bv_offset + total_len) & queue_virt_boundary(q))
> break;
> }
>
> And if you rewrite it this way, I _think_ you can get rid of this
> special case:
>
> if ((*nsegs >= queue_max_segments(q)) || !len)
> return need_split;
>
> above.

Good point, will do in next version.

>
> > + }
> > +
> > + /* split in the middle of the bvec */
> > + if (len)
> > + need_split = true;
>
> need_split is unnecessary, just return len != 0.

OK.

>
> > +
> > + /* update front segment size */
> > + if (!*nsegs) {
> > + unsigned first_seg_size = seg_size;
> > +
> > + if (new_nsegs > 1)
> > + first_seg_size = queue_max_segment_size(q);
> > + if (*front_seg_size < first_seg_size)
> > + *front_seg_size = first_seg_size;
> > + }
> > +
> > + /* update other varibles */
> > + *last_seg_size = seg_size;
> > + *nsegs += new_nsegs;
> > + if (sectors)
> > + *sectors += total_len >> 9;
> > +
> > + return need_split;
> > +}
> > +
> > static struct bio *blk_bio_segment_split(struct request_queue *q,
> > struct bio *bio,
> > struct bio_set *bs,
> > @@ -173,7 +229,7 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
> > struct bio *new = NULL;
> > const unsigned max_sectors = get_max_io_size(q, bio);
> >
> > - bio_for_each_segment(bv, bio, iter) {
> > + bio_for_each_bvec(bv, bio, iter) {
> > /*
> > * If the queue doesn't support SG gaps and adding this
> > * offset would create a gap, disallow it.
> > @@ -188,8 +244,12 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
> > */
> > if (nsegs < queue_max_segments(q) &&
> > sectors < max_sectors) {
> > - nsegs++;
> > - sectors = max_sectors;
> > + /* split in the middle of bvec */
> > + bv.bv_len = (max_sectors - sectors) << 9;
> > + bvec_split_segs(q, &bv, &nsegs,
> > + &seg_size,
> > + &front_seg_size,
> > + &sectors);
> > }
> > goto split;
> > }
> > @@ -214,11 +274,12 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
> > if (nsegs == 1 && seg_size > front_seg_size)
> > front_seg_size = seg_size;
>
> Hm, do we still need to check this here now that we're updating
> front_seg_size inside of bvec_split_segs()?

Right, the check & update can be removed.

>
> >
> > - nsegs++;
> > bvprv = bv;
> > bvprvp = &bvprv;
> > - seg_size = bv.bv_len;
> > - sectors += bv.bv_len >> 9;
> > +
> > + if (bvec_split_segs(q, &bv, &nsegs, &seg_size,
> > + &front_seg_size, &sectors))
>
> What happened to the indent alignment here?

Will fix it in next version.

>
> > + goto split;
> >
> > }
> >
> > @@ -296,6 +357,7 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
> > struct bio_vec bv, bvprv = { NULL };
> > int cluster, prev = 0;
> > unsigned int seg_size, nr_phys_segs;
> > + unsigned front_seg_size = bio->bi_seg_front_size;
> > struct bio *fbio, *bbio;
> > struct bvec_iter iter;
> >
> > @@ -316,7 +378,7 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
> > seg_size = 0;
> > nr_phys_segs = 0;
> > for_each_bio(bio) {
> > - bio_for_each_segment(bv, bio, iter) {
> > + bio_for_each_bvec(bv, bio, iter) {
> > /*
> > * If SG merging is disabled, each bio vector is
> > * a segment
> > @@ -336,20 +398,20 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
> > continue;
> > }
> > new_segment:
> > - if (nr_phys_segs == 1 && seg_size >
> > - fbio->bi_seg_front_size)
> > - fbio->bi_seg_front_size = seg_size;
> > + if (nr_phys_segs == 1 && seg_size > front_seg_size)
> > + front_seg_size = seg_size;
>
> Same comment as in blk_bio_segment_split(), do we still need to check
> this if we're updating front_seg_size in bvec_split_segs()?

I think we can remove it too.


Thanks,
Ming

2018-11-16 23:58:11

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()

On Thu, Nov 15, 2018 at 04:52:56PM +0800, Ming Lei wrote:
> There are still cases in which we need to use bio_bvecs() for get the
> number of multi-page segment, so introduce it.

The only user in your final tree seems to be the loop driver, and
even that one only uses the helper for read/write bios.

I think something like this would be much simpler in the end:

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index d509902a8046..712511815ac6 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -514,16 +514,18 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
struct request *rq = blk_mq_rq_from_pdu(cmd);
struct bio *bio = rq->bio;
struct file *file = lo->lo_backing_file;
+ struct bvec_iter bvec_iter;
+ struct bio_vec tmp;
unsigned int offset;
int nr_bvec = 0;
int ret;

+ __rq_for_each_bio(bio, rq)
+ bio_for_each_bvec(tmp, bio, bvec_iter)
+ nr_bvec++;
+
if (rq->bio != rq->biotail) {
- struct bvec_iter iter;
- struct bio_vec tmp;

- __rq_for_each_bio(bio, rq)
- nr_bvec += bio_bvecs(bio);
bvec = kmalloc_array(nr_bvec, sizeof(struct bio_vec),
GFP_NOIO);
if (!bvec)
@@ -537,7 +539,7 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
* API will take care of all details for us.
*/
__rq_for_each_bio(bio, rq)
- bio_for_each_bvec(tmp, bio, iter) {
+ bio_for_each_bvec(tmp, bio, bvec_iter) {
*bvec = tmp;
bvec++;
}
@@ -551,7 +553,6 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
*/
offset = bio->bi_iter.bi_bvec_done;
bvec = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
- nr_bvec = bio_bvecs(bio);
}
atomic_set(&cmd->ref, 2);

diff --git a/include/linux/bio.h b/include/linux/bio.h
index dcad0b69f57a..379440d1ced0 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -200,30 +200,6 @@ static inline unsigned bio_segments(struct bio *bio)
}
}

-static inline unsigned bio_bvecs(struct bio *bio)
-{
- unsigned bvecs = 0;
- struct bio_vec bv;
- struct bvec_iter iter;
-
- /*
- * We special case discard/write same/write zeroes, because they
- * interpret bi_size differently:
- */
- switch (bio_op(bio)) {
- case REQ_OP_DISCARD:
- case REQ_OP_SECURE_ERASE:
- case REQ_OP_WRITE_ZEROES:
- return 0;
- case REQ_OP_WRITE_SAME:
- return 1;
- default:
- bio_for_each_bvec(bv, bio, iter)
- bvecs++;
- return bvecs;
- }
-}
-
/*
* get a reference to a bio, so it won't disappear. the intended use is
* something like:

2018-11-19 18:20:36

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 05/19] block: introduce bvec_last_segment()

On Thu, Nov 15, 2018 at 03:23:56PM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:52:52PM +0800, Ming Lei wrote:
> > BTRFS and guard_bio_eod() need to get the last singlepage segment
> > from one multipage bvec, so introduce this helper to make them happy.
> >
> > Cc: Dave Chinner <[email protected]>
> > Cc: Kent Overstreet <[email protected]>
> > Cc: Mike Snitzer <[email protected]>
> > Cc: [email protected]
> > Cc: Alexander Viro <[email protected]>
> > Cc: [email protected]
> > Cc: Shaohua Li <[email protected]>
> > Cc: [email protected]
> > Cc: [email protected]
> > Cc: David Sterba <[email protected]>
> > Cc: [email protected]
> > Cc: Darrick J. Wong <[email protected]>
> > Cc: [email protected]
> > Cc: Gao Xiang <[email protected]>
> > Cc: Christoph Hellwig <[email protected]>
> > Cc: Theodore Ts'o <[email protected]>
> > Cc: [email protected]
> > Cc: Coly Li <[email protected]>
> > Cc: [email protected]
> > Cc: Boaz Harrosh <[email protected]>
> > Cc: Bob Peterson <[email protected]>
> > Cc: [email protected]
>
> Reviewed-by: Omar Sandoval <[email protected]>
>
> Minor comments below.
>
> > Signed-off-by: Ming Lei <[email protected]>
> > ---
> > include/linux/bvec.h | 25 +++++++++++++++++++++++++
> > 1 file changed, 25 insertions(+)
> >
> > diff --git a/include/linux/bvec.h b/include/linux/bvec.h
> > index 3d61352cd8cf..01616a0b6220 100644
> > --- a/include/linux/bvec.h
> > +++ b/include/linux/bvec.h
> > @@ -216,4 +216,29 @@ static inline bool mp_bvec_iter_advance(const struct bio_vec *bv,
> > .bi_bvec_done = 0, \
> > }
> >
> > +/*
> > + * Get the last singlepage segment from the multipage bvec and store it
> > + * in @seg
> > + */
> > +static inline void bvec_last_segment(const struct bio_vec *bvec,
> > + struct bio_vec *seg)
>
> Indentation is all messed up here.

Will fix it.

>
> > +{
> > + unsigned total = bvec->bv_offset + bvec->bv_len;
> > + unsigned last_page = total / PAGE_SIZE;
> > +
> > + if (last_page * PAGE_SIZE == total)
> > + last_page--;
>
> I think this could just be
>
> unsigned int last_page = (total - 1) / PAGE_SIZE;

This way is really elegant.

Thanks,
Ming

2018-11-19 19:41:19

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 18/19] block: kill QUEUE_FLAG_NO_SG_MERGE

On Thu, Nov 15, 2018 at 06:18:11PM -0800, Omar Sandoval wrote:
> On Thu, Nov 15, 2018 at 04:53:05PM +0800, Ming Lei wrote:
> > Since bdced438acd83ad83a6c ("block: setup bi_phys_segments after splitting"),
> > physical segment number is mainly figured out in blk_queue_split() for
> > fast path, and the flag of BIO_SEG_VALID is set there too.
> >
> > Now only blk_recount_segments() and blk_recalc_rq_segments() use this
> > flag.
> >
> > Basically blk_recount_segments() is bypassed in fast path given BIO_SEG_VALID
> > is set in blk_queue_split().
> >
> > For another user of blk_recalc_rq_segments():
> >
> > - run in partial completion branch of blk_update_request, which is an unusual case
> >
> > - run in blk_cloned_rq_check_limits(), still not a big problem if the flag is killed
> > since dm-rq is the only user.
> >
> > Multi-page bvec is enabled now, QUEUE_FLAG_NO_SG_MERGE doesn't make sense any more.
>
> This commit message wasn't very clear. Is it the case that
> QUEUE_FLAG_NO_SG_MERGE is no longer set by any drivers?

OK, I will add the explanation to commit log in next version.

05f1dd53152173 (block: add queue flag for disabling SG merging) introduces this
flag for NVMe performance purpose only, so that merging to segment can
be bypassed for NVMe.

Actually this optimization was bypassed by 54efd50bfd873e2d (block: make
generic_make_request handle arbitrarily sized bios) and bdced438acd83ad83a6c
("block: setup bi_phys_segments after splitting").

Now segment computation can be very quick, given most of times one bvec
can be thought as one segment, so we can remove the flag.

thanks,
Ming

2018-11-19 19:23:50

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 14/19] block: enable multipage bvecs

On Fri, Nov 16, 2018 at 02:53:08PM +0100, Christoph Hellwig wrote:
> > -
> > - if (page == bv->bv_page && off == bv->bv_offset + bv->bv_len) {
> > - bv->bv_len += len;
> > - bio->bi_iter.bi_size += len;
> > - return true;
> > - }
> > + struct request_queue *q = NULL;
> > +
> > + if (page == bv->bv_page && off == (bv->bv_offset + bv->bv_len)
> > + && (off + len) <= PAGE_SIZE)
>
> How could the page struct be the same, but the range beyond PAGE_SIZE
> (at least with the existing callers)?
>
> Also no need for the inner btraces, and the && always goes on the
> first line.

OK.

>
> > + if (bio->bi_disk)
> > + q = bio->bi_disk->queue;
> > +
> > + /* disable multi-page bvec too if cluster isn't enabled */
> > + if (!q || !blk_queue_cluster(q) ||
> > + ((page_to_phys(bv->bv_page) + bv->bv_offset + bv->bv_len) !=
> > + (page_to_phys(page) + off)))
> > + return false;
> > + merge:
> > + bv->bv_len += len;
> > + bio->bi_iter.bi_size += len;
> > + return true;
>
> Ok, this is scary, as it will give differen results depending on when
> bi_disk is assigned.

It is just merge or not, both can be handled well now.

> But then again we shouldn't really do the cluster
> check here, but rather when splitting the bio for the actual low-level
> driver.

Yeah, I thought of this way too, but it may cause tons of bio split for
no-clustering, and there are quite a few scsi devices which require
to disable clustering.

[linux]$ git grep -n DISABLE_CLUSTERING ./drivers/scsi/ | wc -l
28

Or we may introduce bio_split_to_single_page_bvec() to allocate &
convert to single-page bvec table for non-clustering, will try this
approach in next version.

>
> (and eventually we should kill this clustering setting off in favor
> of our normal segment limits).

Yeah, it has been in my post-multi-page todo list already, :-)

thanks,
Ming

2018-11-19 13:57:58

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 01/19] block: introduce multi-page page bvec helpers

On Sun, Nov 18, 2018 at 08:10:14PM -0700, Jens Axboe wrote:
> On 11/18/18 7:23 PM, Ming Lei wrote:
> > On Fri, Nov 16, 2018 at 02:13:05PM +0100, Christoph Hellwig wrote:
> >>> -#define bvec_iter_page(bvec, iter) \
> >>> +#define mp_bvec_iter_page(bvec, iter) \
> >>> (__bvec_iter_bvec((bvec), (iter))->bv_page)
> >>>
> >>> -#define bvec_iter_len(bvec, iter) \
> >>> +#define mp_bvec_iter_len(bvec, iter) \
> >>
> >> I'd much prefer if we would stick to the segment naming that
> >> we also use in the higher level helper.
> >>
> >> So segment_iter_page, segment_iter_len, etc.
> >
> > We discussed the naming problem before, one big problem is that the 'segment'
> > in bio_for_each_segment*() means one single page segment actually.
> >
> > If we use segment_iter_page() here for multi-page segment, it may
> > confuse people.
> >
> > Of course, I prefer to the naming of segment/page,
> >
> > And Jens didn't agree to rename bio_for_each_segment*() before.
>
> I didn't like frivolous renaming (and I still don't), but mp_
> is horrible imho. Don't name these after the fact that they
> are done in conjunction with supporting multipage bvecs. That
> very fact will be irrelevant very soon

OK, so what is your suggestion for the naming issue?

Are you fine to use segment_iter_page() here? Then the term of 'segment'
may be interpreted as multi-page segment here, but as single-page in
bio_for_each_segment*().

thanks
Ming

2018-11-15 19:02:28

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 08/19] btrfs: move bio_pages_all() to btrfs

BTRFS is the only user of this helper, so move this helper into
BTRFS, and implement it via bio_for_each_segment_all(), since
bio->bi_vcnt may not equal to number of pages after multipage bvec
is enabled.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
fs/btrfs/extent_io.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 5d5965297e7e..874bb9aeebdc 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2348,6 +2348,18 @@ struct bio *btrfs_create_repair_bio(struct inode *inode, struct bio *failed_bio,
return bio;
}

+static unsigned btrfs_bio_pages_all(struct bio *bio)
+{
+ unsigned i;
+ struct bio_vec *bv;
+
+ WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED));
+
+ bio_for_each_segment_all(bv, bio, i)
+ ;
+ return i;
+}
+
/*
* this is a generic handler for readpage errors (default
* readpage_io_failed_hook). if other copies exist, read those and write back
@@ -2368,7 +2380,7 @@ static int bio_readpage_error(struct bio *failed_bio, u64 phy_offset,
int read_mode = 0;
blk_status_t status;
int ret;
- unsigned failed_bio_pages = bio_pages_all(failed_bio);
+ unsigned failed_bio_pages = btrfs_bio_pages_all(failed_bio);

BUG_ON(bio_op(failed_bio) == REQ_OP_WRITE);

--
2.9.5

2018-11-16 10:32:08

by Omar Sandoval

[permalink] [raw]
Subject: Re: [PATCH V10 07/19] btrfs: use bvec_last_segment to get bio's last page

On Thu, Nov 15, 2018 at 04:52:54PM +0800, Ming Lei wrote:
> Preparing for supporting multi-page bvec.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]

Reviewed-by: Omar Sandoval <[email protected]>

> Signed-off-by: Ming Lei <[email protected]>
> ---
> fs/btrfs/compression.c | 5 ++++-
> fs/btrfs/extent_io.c | 5 +++--
> 2 files changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
> index 2955a4ea2fa8..161e14b8b180 100644
> --- a/fs/btrfs/compression.c
> +++ b/fs/btrfs/compression.c
> @@ -400,8 +400,11 @@ blk_status_t btrfs_submit_compressed_write(struct inode *inode, u64 start,
> static u64 bio_end_offset(struct bio *bio)
> {
> struct bio_vec *last = bio_last_bvec_all(bio);
> + struct bio_vec bv;
>
> - return page_offset(last->bv_page) + last->bv_len + last->bv_offset;
> + bvec_last_segment(last, &bv);
> +
> + return page_offset(bv.bv_page) + bv.bv_len + bv.bv_offset;
> }
>
> static noinline int add_ra_bio_pages(struct inode *inode,
> diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
> index d228f706ff3e..5d5965297e7e 100644
> --- a/fs/btrfs/extent_io.c
> +++ b/fs/btrfs/extent_io.c
> @@ -2720,11 +2720,12 @@ static int __must_check submit_one_bio(struct bio *bio, int mirror_num,
> {
> blk_status_t ret = 0;
> struct bio_vec *bvec = bio_last_bvec_all(bio);
> - struct page *page = bvec->bv_page;
> + struct bio_vec bv;
> struct extent_io_tree *tree = bio->bi_private;
> u64 start;
>
> - start = page_offset(page) + bvec->bv_offset;
> + bvec_last_segment(bvec, &bv);
> + start = page_offset(bv.bv_page) + bv.bv_offset;
>
> bio->bi_private = NULL;
>
> --
> 2.9.5
>

2018-11-17 00:10:37

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 18/19] block: kill QUEUE_FLAG_NO_SG_MERGE

On Thu, Nov 15, 2018 at 04:53:05PM +0800, Ming Lei wrote:
> Since bdced438acd83ad83a6c ("block: setup bi_phys_segments after splitting"),
> physical segment number is mainly figured out in blk_queue_split() for
> fast path, and the flag of BIO_SEG_VALID is set there too.
>
> Now only blk_recount_segments() and blk_recalc_rq_segments() use this
> flag.
>
> Basically blk_recount_segments() is bypassed in fast path given BIO_SEG_VALID
> is set in blk_queue_split().
>
> For another user of blk_recalc_rq_segments():
>
> - run in partial completion branch of blk_update_request, which is an unusual case
>
> - run in blk_cloned_rq_check_limits(), still not a big problem if the flag is killed
> since dm-rq is the only user.
>
> Multi-page bvec is enabled now, QUEUE_FLAG_NO_SG_MERGE doesn't make sense any more.
>
> Cc: Dave Chinner <[email protected]>
> Cc: Kent Overstreet <[email protected]>
> Cc: Mike Snitzer <[email protected]>
> Cc: [email protected]
> Cc: Alexander Viro <[email protected]>
> Cc: [email protected]
> Cc: Shaohua Li <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: David Sterba <[email protected]>
> Cc: [email protected]
> Cc: Darrick J. Wong <[email protected]>
> Cc: [email protected]
> Cc: Gao Xiang <[email protected]>
> Cc: Christoph Hellwig <[email protected]>
> Cc: Theodore Ts'o <[email protected]>
> Cc: [email protected]
> Cc: Coly Li <[email protected]>
> Cc: [email protected]
> Cc: Boaz Harrosh <[email protected]>
> Cc: Bob Peterson <[email protected]>
> Cc: [email protected]
> Signed-off-by: Ming Lei <[email protected]>
> ---
> block/blk-merge.c | 31 ++++++-------------------------
> block/blk-mq-debugfs.c | 1 -
> block/blk-mq.c | 3 ---
> drivers/md/dm-table.c | 13 -------------
> include/linux/blkdev.h | 1 -
> 5 files changed, 6 insertions(+), 43 deletions(-)
>
> diff --git a/block/blk-merge.c b/block/blk-merge.c
> index 153a659fde74..06be298be332 100644
> --- a/block/blk-merge.c
> +++ b/block/blk-merge.c
> @@ -351,8 +351,7 @@ void blk_queue_split(struct request_queue *q, struct bio **bio)
> EXPORT_SYMBOL(blk_queue_split);
>
> static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
> - struct bio *bio,
> - bool no_sg_merge)
> + struct bio *bio)
> {
> struct bio_vec bv, bvprv = { NULL };
> int cluster, prev = 0;
> @@ -379,13 +378,6 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
> nr_phys_segs = 0;
> for_each_bio(bio) {
> bio_for_each_bvec(bv, bio, iter) {
> - /*
> - * If SG merging is disabled, each bio vector is
> - * a segment
> - */
> - if (no_sg_merge)
> - goto new_segment;
> -
> if (prev && cluster) {
> if (seg_size + bv.bv_len
> > queue_max_segment_size(q))
> @@ -420,27 +412,16 @@ static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
>
> void blk_recalc_rq_segments(struct request *rq)
> {
> - bool no_sg_merge = !!test_bit(QUEUE_FLAG_NO_SG_MERGE,
> - &rq->q->queue_flags);
> -
> - rq->nr_phys_segments = __blk_recalc_rq_segments(rq->q, rq->bio,
> - no_sg_merge);
> + rq->nr_phys_segments = __blk_recalc_rq_segments(rq->q, rq->bio);

Can we rename __blk_recalc_rq_segments to blk_recalc_rq_segments
can kill the old blk_recalc_rq_segments now?

Otherwise looks fine:

Reviewed-by: Christoph Hellwig <[email protected]>

2018-11-16 23:46:50

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 05/19] block: introduce bvec_last_segment()

> + unsigned last_page = total / PAGE_SIZE;
> +
> + if (last_page * PAGE_SIZE == total)

Not sure it matters much with modern compilers, but generally
we shit by PAGE_SHIFT instead of multiplying with or dividing
by PAGE_SIZE.

2018-11-15 19:02:41

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 09/19] block: introduce bio_bvecs()

There are still cases in which we need to use bio_bvecs() for get the
number of multi-page segment, so introduce it.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
include/linux/bio.h | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/include/linux/bio.h b/include/linux/bio.h
index 1f0dcf109841..3496c816946e 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -196,7 +196,6 @@ static inline unsigned bio_segments(struct bio *bio)
* We special case discard/write same/write zeroes, because they
* interpret bi_size differently:
*/
-
switch (bio_op(bio)) {
case REQ_OP_DISCARD:
case REQ_OP_SECURE_ERASE:
@@ -205,13 +204,34 @@ static inline unsigned bio_segments(struct bio *bio)
case REQ_OP_WRITE_SAME:
return 1;
default:
- break;
+ bio_for_each_segment(bv, bio, iter)
+ segs++;
+ return segs;
}
+}

- bio_for_each_segment(bv, bio, iter)
- segs++;
+static inline unsigned bio_bvecs(struct bio *bio)
+{
+ unsigned bvecs = 0;
+ struct bio_vec bv;
+ struct bvec_iter iter;

- return segs;
+ /*
+ * We special case discard/write same/write zeroes, because they
+ * interpret bi_size differently:
+ */
+ switch (bio_op(bio)) {
+ case REQ_OP_DISCARD:
+ case REQ_OP_SECURE_ERASE:
+ case REQ_OP_WRITE_ZEROES:
+ return 0;
+ case REQ_OP_WRITE_SAME:
+ return 1;
+ default:
+ bio_for_each_bvec(bv, bio, iter)
+ bvecs++;
+ return bvecs;
+ }
}

/*
--
2.9.5

2018-11-16 23:49:38

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 07/19] btrfs: use bvec_last_segment to get bio's last page

On Thu, Nov 15, 2018 at 04:52:54PM +0800, Ming Lei wrote:
> index 2955a4ea2fa8..161e14b8b180 100644
> --- a/fs/btrfs/compression.c
> +++ b/fs/btrfs/compression.c
> @@ -400,8 +400,11 @@ blk_status_t btrfs_submit_compressed_write(struct inode *inode, u64 start,
> static u64 bio_end_offset(struct bio *bio)
> {
> struct bio_vec *last = bio_last_bvec_all(bio);
> + struct bio_vec bv;
>
> - return page_offset(last->bv_page) + last->bv_len + last->bv_offset;
> + bvec_last_segment(last, &bv);
> +
> + return page_offset(bv.bv_page) + bv.bv_len + bv.bv_offset;

I don't think we need this. If last is a multi-page bvec bv_offset
will already contain the correct offset from the first page.

2018-11-17 00:02:08

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 13/19] iomap & xfs: only account for new added page

I'd much rather have __bio_try_merge_page only do merges in
the same page, and have a new __bio_try_merge_segment that does
multi-page merges. This will keep the accounting a lot simpler.

2018-11-15 19:05:08

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 17/19] block: don't use bio->bi_vcnt to figure out segment number

It is wrong to use bio->bi_vcnt to figure out how many segments
there are in the bio even though CLONED flag isn't set on this bio,
because this bio may be splitted or advanced.

So always use bio_segments() in blk_recount_segments(), and it shouldn't
cause any performance loss now because the physical segment number is figured
out in blk_queue_split() and BIO_SEG_VALID is set meantime since
bdced438acd83ad83a6c ("block: setup bi_phys_segments after splitting").

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Fixes: 7f60dcaaf91 ("block: blk-merge: fix blk_recount_segments()")
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
block/blk-merge.c | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/block/blk-merge.c b/block/blk-merge.c
index cb9f49bcfd36..153a659fde74 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -429,13 +429,7 @@ void blk_recalc_rq_segments(struct request *rq)

void blk_recount_segments(struct request_queue *q, struct bio *bio)
{
- unsigned short seg_cnt;
-
- /* estimate segment number by bi_vcnt for non-cloned bio */
- if (bio_flagged(bio, BIO_CLONED))
- seg_cnt = bio_segments(bio);
- else
- seg_cnt = bio->bi_vcnt;
+ unsigned short seg_cnt = bio_segments(bio);

if (test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags) &&
(seg_cnt < queue_max_segments(q)))
--
2.9.5

2018-11-21 19:19:47

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 09/19] block: introduce bio_bvecs()

On Tue, Nov 20, 2018 at 09:35:07PM -0800, Sagi Grimberg wrote:
>> Given it is over TCP, I guess it should be doable for you to preallocate one
>> 256-bvec table in one page for each request, then sets the max segment size as
>> (unsigned int)-1, and max segment number as 256, the preallocated table
>> should work anytime.
>
> 256 bvec table is really a lot to preallocate, especially when its not
> needed, I can easily initialize the bvec_iter on the bio bvec. If this
> involves preallocation of the worst-case than I don't consider this to
> be an improvement.

Ok, I took a look at the nvme-tcp code and it seems you care about
bios because you want a contiguos bio chunk for sending it down
the networking code. Yes, in that case we sort of need to iterate
over bios. But you already have a special case for discard, so you
don't really need any of the magic in the bio_bvecs() helper either
can can just count bi_vcnt in the bio.

2018-11-15 19:00:44

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 01/19] block: introduce multi-page page bvec helpers

This patch introduces helpers of 'mp_bvec_iter_*' for multipage
bvec support.

The introduced helpers treate one bvec as real multi-page segment,
which may include more than one pages.

The existed helpers of bvec_iter_* are interfaces for supporting current
bvec iterator which is thought as single-page by drivers, fs, dm and
etc. These introduced helpers will build single-page bvec in flight, so
this way won't break current bio/bvec users, which needn't any change.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
include/linux/bvec.h | 63 +++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 60 insertions(+), 3 deletions(-)

diff --git a/include/linux/bvec.h b/include/linux/bvec.h
index 02c73c6aa805..8ef904a50577 100644
--- a/include/linux/bvec.h
+++ b/include/linux/bvec.h
@@ -23,6 +23,44 @@
#include <linux/kernel.h>
#include <linux/bug.h>
#include <linux/errno.h>
+#include <linux/mm.h>
+
+/*
+ * What is multi-page bvecs?
+ *
+ * - bvecs stored in bio->bi_io_vec is always multi-page(mp) style
+ *
+ * - bvec(struct bio_vec) represents one physically contiguous I/O
+ * buffer, now the buffer may include more than one pages after
+ * multi-page(mp) bvec is supported, and all these pages represented
+ * by one bvec is physically contiguous. Before mp support, at most
+ * one page is included in one bvec, we call it single-page(sp)
+ * bvec.
+ *
+ * - .bv_page of the bvec represents the 1st page in the mp bvec
+ *
+ * - .bv_offset of the bvec represents offset of the buffer in the bvec
+ *
+ * The effect on the current drivers/filesystem/dm/bcache/...:
+ *
+ * - almost everyone supposes that one bvec only includes one single
+ * page, so we keep the sp interface not changed, for example,
+ * bio_for_each_segment() still returns bvec with single page
+ *
+ * - bio_for_each_segment*() will be changed to return single-page
+ * bvec too
+ *
+ * - during iterating, iterator variable(struct bvec_iter) is always
+ * updated in multipage bvec style and that means bvec_iter_advance()
+ * is kept not changed
+ *
+ * - returned(copied) single-page bvec is built in flight by bvec
+ * helpers from the stored multipage bvec
+ *
+ * - In case that some components(such as iov_iter) need to support
+ * multi-page bvec, we introduce new helpers(mp_bvec_iter_*) for
+ * them.
+ */

/*
* was unsigned short, but we might as well be ready for > 64kB I/O pages
@@ -50,16 +88,35 @@ struct bvec_iter {
*/
#define __bvec_iter_bvec(bvec, iter) (&(bvec)[(iter).bi_idx])

-#define bvec_iter_page(bvec, iter) \
+#define mp_bvec_iter_page(bvec, iter) \
(__bvec_iter_bvec((bvec), (iter))->bv_page)

-#define bvec_iter_len(bvec, iter) \
+#define mp_bvec_iter_len(bvec, iter) \
min((iter).bi_size, \
__bvec_iter_bvec((bvec), (iter))->bv_len - (iter).bi_bvec_done)

-#define bvec_iter_offset(bvec, iter) \
+#define mp_bvec_iter_offset(bvec, iter) \
(__bvec_iter_bvec((bvec), (iter))->bv_offset + (iter).bi_bvec_done)

+#define mp_bvec_iter_page_idx(bvec, iter) \
+ (mp_bvec_iter_offset((bvec), (iter)) / PAGE_SIZE)
+
+/*
+ * <page, offset,length> of single-page(sp) segment.
+ *
+ * This helpers are for building sp bvec in flight.
+ */
+#define bvec_iter_offset(bvec, iter) \
+ (mp_bvec_iter_offset((bvec), (iter)) % PAGE_SIZE)
+
+#define bvec_iter_len(bvec, iter) \
+ min_t(unsigned, mp_bvec_iter_len((bvec), (iter)), \
+ (PAGE_SIZE - (bvec_iter_offset((bvec), (iter)))))
+
+#define bvec_iter_page(bvec, iter) \
+ nth_page(mp_bvec_iter_page((bvec), (iter)), \
+ mp_bvec_iter_page_idx((bvec), (iter)))
+
#define bvec_iter_bvec(bvec, iter) \
((struct bio_vec) { \
.bv_page = bvec_iter_page((bvec), (iter)), \
--
2.9.5

2018-11-19 19:02:36

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 13/19] iomap & xfs: only account for new added page

On Fri, Nov 16, 2018 at 02:49:36PM +0100, Christoph Hellwig wrote:
> I'd much rather have __bio_try_merge_page only do merges in
> the same page, and have a new __bio_try_merge_segment that does
> multi-page merges. This will keep the accounting a lot simpler.

Looks this way is clever, will do it in next version.

Thanks,
Ming

2018-11-15 19:04:07

by Ming Lei

[permalink] [raw]
Subject: [PATCH V10 14/19] block: enable multipage bvecs

This patch pulls the trigger for multi-page bvecs.

Now any request queue which supports queue cluster will see multi-page
bvecs.

Cc: Dave Chinner <[email protected]>
Cc: Kent Overstreet <[email protected]>
Cc: Mike Snitzer <[email protected]>
Cc: [email protected]
Cc: Alexander Viro <[email protected]>
Cc: [email protected]
Cc: Shaohua Li <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: David Sterba <[email protected]>
Cc: [email protected]
Cc: Darrick J. Wong <[email protected]>
Cc: [email protected]
Cc: Gao Xiang <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: [email protected]
Cc: Coly Li <[email protected]>
Cc: [email protected]
Cc: Boaz Harrosh <[email protected]>
Cc: Bob Peterson <[email protected]>
Cc: [email protected]
Signed-off-by: Ming Lei <[email protected]>
---
block/bio.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/block/bio.c b/block/bio.c
index 6486722d4d4b..ed6df6f8e63d 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -767,12 +767,24 @@ bool __bio_try_merge_page(struct bio *bio, struct page *page,

if (bio->bi_vcnt > 0) {
struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1];
-
- if (page == bv->bv_page && off == bv->bv_offset + bv->bv_len) {
- bv->bv_len += len;
- bio->bi_iter.bi_size += len;
- return true;
- }
+ struct request_queue *q = NULL;
+
+ if (page == bv->bv_page && off == (bv->bv_offset + bv->bv_len)
+ && (off + len) <= PAGE_SIZE)
+ goto merge;
+
+ if (bio->bi_disk)
+ q = bio->bi_disk->queue;
+
+ /* disable multi-page bvec too if cluster isn't enabled */
+ if (!q || !blk_queue_cluster(q) ||
+ ((page_to_phys(bv->bv_page) + bv->bv_offset + bv->bv_len) !=
+ (page_to_phys(page) + off)))
+ return false;
+ merge:
+ bv->bv_len += len;
+ bio->bi_iter.bi_size += len;
+ return true;
}
return false;
}
--
2.9.5

2018-11-19 12:46:01

by Ming Lei

[permalink] [raw]
Subject: Re: [PATCH V10 01/19] block: introduce multi-page page bvec helpers

On Fri, Nov 16, 2018 at 02:13:05PM +0100, Christoph Hellwig wrote:
> > -#define bvec_iter_page(bvec, iter) \
> > +#define mp_bvec_iter_page(bvec, iter) \
> > (__bvec_iter_bvec((bvec), (iter))->bv_page)
> >
> > -#define bvec_iter_len(bvec, iter) \
> > +#define mp_bvec_iter_len(bvec, iter) \
>
> I'd much prefer if we would stick to the segment naming that
> we also use in the higher level helper.
>
> So segment_iter_page, segment_iter_len, etc.

We discussed the naming problem before, one big problem is that the 'segment'
in bio_for_each_segment*() means one single page segment actually.

If we use segment_iter_page() here for multi-page segment, it may
confuse people.

Of course, I prefer to the naming of segment/page,

And Jens didn't agree to rename bio_for_each_segment*() before.

So what is the solution we should take for moving on?

>
> > + * This helpers are for building sp bvec in flight.
>
> Please spell out single page, sp is not easy understandable.

OK.

Thanks,
Ming

2018-11-17 00:12:11

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH V10 19/19] block: kill BLK_MQ_F_SG_MERGE

On Thu, Nov 15, 2018 at 04:53:06PM +0800, Ming Lei wrote:
> QUEUE_FLAG_NO_SG_MERGE has been killed, so kill BLK_MQ_F_SG_MERGE too.

Looks fine,

Reviewed-by: Christoph Hellwig <[email protected]>