* a few cleanups to prepare for multipage bio_vecs
@ 2018-06-04 13:58 Christoph Hellwig
2018-06-04 13:58 ` [PATCH 1/3] block: simplify bio_check_pages_dirty Christoph Hellwig
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Christoph Hellwig @ 2018-06-04 13:58 UTC (permalink / raw)
To: Jens Axboe, Ming Lei; +Cc: linux-block
Clean up some bio_vec abuses to prepare for saner handling of multipage
bio_vecs.
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/3] block: simplify bio_check_pages_dirty
2018-06-04 13:58 a few cleanups to prepare for multipage bio_vecs Christoph Hellwig
@ 2018-06-04 13:58 ` Christoph Hellwig
2018-06-05 3:23 ` Ming Lei
2018-06-04 13:58 ` [PATCH 2/3] block: bio_set_pages_dirty can't see NULL bv_page in a valid bio_vec Christoph Hellwig
2018-06-04 13:58 ` [PATCH 3/3] block: use bio_add_page in bio_iov_iter_get_pages Christoph Hellwig
2 siblings, 1 reply; 8+ messages in thread
From: Christoph Hellwig @ 2018-06-04 13:58 UTC (permalink / raw)
To: Jens Axboe, Ming Lei; +Cc: linux-block
bio_check_pages_dirty currently inviolates the invariant that bv_page of
a bio_vec inside bi_vcnt shouldn't be zero, and that is going to become
really annoying with multpath biovecs. Fortunately there isn't any
all that good reason for it - once we decide to defer freeing the bio
to a workqueue holding onto a few additional pages isn't really an
issue anymore. So just check if there is a clean page that needs
dirtying in the first path, and do a second pass to free them if there
was none, while the cache is still hot.
Also use the chance to micro-optimize bio_dirty_fn a bit by not saving
irq state - we know we are called from a workqueue.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/bio.c | 56 +++++++++++++++++++++-----------------------------------
1 file changed, 21 insertions(+), 35 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index 53e0f0a1ed94..50941c1c9118 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1590,19 +1590,15 @@ static void bio_release_pages(struct bio *bio)
struct bio_vec *bvec;
int i;
- bio_for_each_segment_all(bvec, bio, i) {
- struct page *page = bvec->bv_page;
-
- if (page)
- put_page(page);
- }
+ bio_for_each_segment_all(bvec, bio, i)
+ put_page(bvec->bv_page);
}
/*
* bio_check_pages_dirty() will check that all the BIO's pages are still dirty.
* If they are, then fine. If, however, some pages are clean then they must
* have been written out during the direct-IO read. So we take another ref on
- * the BIO and the offending pages and re-dirty the pages in process context.
+ * the BIO and re-dirty the pages in process context.
*
* It is expected that bio_check_pages_dirty() will wholly own the BIO from
* here on. It will run one put_page() against each page and will run one
@@ -1620,52 +1616,42 @@ static struct bio *bio_dirty_list;
*/
static void bio_dirty_fn(struct work_struct *work)
{
- unsigned long flags;
- struct bio *bio;
+ struct bio *bio, *next;
- spin_lock_irqsave(&bio_dirty_lock, flags);
- bio = bio_dirty_list;
+ spin_lock_irq(&bio_dirty_lock);
+ next = bio_dirty_list;
bio_dirty_list = NULL;
- spin_unlock_irqrestore(&bio_dirty_lock, flags);
+ spin_unlock_irq(&bio_dirty_lock);
- while (bio) {
- struct bio *next = bio->bi_private;
+ while ((bio = next) != NULL) {
+ next = bio->bi_private;
bio_set_pages_dirty(bio);
bio_release_pages(bio);
bio_put(bio);
- bio = next;
}
}
void bio_check_pages_dirty(struct bio *bio)
{
struct bio_vec *bvec;
- int nr_clean_pages = 0;
+ unsigned long flags;
int i;
bio_for_each_segment_all(bvec, bio, i) {
- struct page *page = bvec->bv_page;
-
- if (PageDirty(page) || PageCompound(page)) {
- put_page(page);
- bvec->bv_page = NULL;
- } else {
- nr_clean_pages++;
- }
+ if (!PageDirty(bvec->bv_page) && !PageCompound(bvec->bv_page))
+ goto defer;
}
- if (nr_clean_pages) {
- unsigned long flags;
-
- spin_lock_irqsave(&bio_dirty_lock, flags);
- bio->bi_private = bio_dirty_list;
- bio_dirty_list = bio;
- spin_unlock_irqrestore(&bio_dirty_lock, flags);
- schedule_work(&bio_dirty_work);
- } else {
- bio_put(bio);
- }
+ bio_release_pages(bio);
+ bio_put(bio);
+ return;
+defer:
+ spin_lock_irqsave(&bio_dirty_lock, flags);
+ bio->bi_private = bio_dirty_list;
+ bio_dirty_list = bio;
+ spin_unlock_irqrestore(&bio_dirty_lock, flags);
+ schedule_work(&bio_dirty_work);
}
void generic_start_io_acct(struct request_queue *q, int rw,
--
2.14.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/3] block: bio_set_pages_dirty can't see NULL bv_page in a valid bio_vec
2018-06-04 13:58 a few cleanups to prepare for multipage bio_vecs Christoph Hellwig
2018-06-04 13:58 ` [PATCH 1/3] block: simplify bio_check_pages_dirty Christoph Hellwig
@ 2018-06-04 13:58 ` Christoph Hellwig
2018-06-05 3:28 ` Ming Lei
2018-06-04 13:58 ` [PATCH 3/3] block: use bio_add_page in bio_iov_iter_get_pages Christoph Hellwig
2 siblings, 1 reply; 8+ messages in thread
From: Christoph Hellwig @ 2018-06-04 13:58 UTC (permalink / raw)
To: Jens Axboe, Ming Lei; +Cc: linux-block
So don't bother handling it.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/bio.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index 50941c1c9118..d95fab72acb5 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1578,10 +1578,8 @@ void bio_set_pages_dirty(struct bio *bio)
int i;
bio_for_each_segment_all(bvec, bio, i) {
- struct page *page = bvec->bv_page;
-
- if (page && !PageCompound(page))
- set_page_dirty_lock(page);
+ if (!PageCompound(bvec->bv_page))
+ set_page_dirty_lock(bvec->bv_page);
}
}
--
2.14.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/3] block: use bio_add_page in bio_iov_iter_get_pages
2018-06-04 13:58 a few cleanups to prepare for multipage bio_vecs Christoph Hellwig
2018-06-04 13:58 ` [PATCH 1/3] block: simplify bio_check_pages_dirty Christoph Hellwig
2018-06-04 13:58 ` [PATCH 2/3] block: bio_set_pages_dirty can't see NULL bv_page in a valid bio_vec Christoph Hellwig
@ 2018-06-04 13:58 ` Christoph Hellwig
2018-06-05 3:41 ` Ming Lei
2 siblings, 1 reply; 8+ messages in thread
From: Christoph Hellwig @ 2018-06-04 13:58 UTC (permalink / raw)
To: Jens Axboe, Ming Lei; +Cc: linux-block
Replace a nasty hack with a different nasty hack to prepare for multipage
bio_vecs. By moving the temporary page array as far up as possible in
the space allocated for the bio_vec array we can iterate forward over it
and thus use bio_add_page. Using bio_add_page means we'll be able to
merge physically contiguous pages once support for multipath bio_vecs is
merged.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/bio.c | 45 +++++++++++++++++++++------------------------
1 file changed, 21 insertions(+), 24 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index d95fab72acb5..b09c133a1e24 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -871,6 +871,8 @@ int bio_add_page(struct bio *bio, struct page *page,
}
EXPORT_SYMBOL(bio_add_page);
+#define PAGE_PTRS_PER_BVEC (sizeof(struct bio_vec) / sizeof(struct page *))
+
/**
* bio_iov_iter_get_pages - pin user or kernel pages and add them to a bio
* @bio: bio to add pages to
@@ -882,38 +884,33 @@ EXPORT_SYMBOL(bio_add_page);
int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter)
{
unsigned short nr_pages = bio->bi_max_vecs - bio->bi_vcnt;
+ unsigned short entries_left = bio->bi_max_vecs - bio->bi_vcnt;
struct bio_vec *bv = bio->bi_io_vec + bio->bi_vcnt;
struct page **pages = (struct page **)bv;
- size_t offset, diff;
- ssize_t size;
+ ssize_t size, left;
+ unsigned len, i;
+ size_t offset;
+
+ /*
+ * Move page array up in the allocated memory for the bio vecs as
+ * far as possible so that we can start filling biovecs from the
+ * beginning without overwriting the temporary page array.
+ */
+ BUILD_BUG_ON(PAGE_PTRS_PER_BVEC < 2);
+ pages += entries_left * (PAGE_PTRS_PER_BVEC - 1);
size = iov_iter_get_pages(iter, pages, LONG_MAX, nr_pages, &offset);
if (unlikely(size <= 0))
return size ? size : -EFAULT;
- nr_pages = (size + offset + PAGE_SIZE - 1) / PAGE_SIZE;
- /*
- * Deep magic below: We need to walk the pinned pages backwards
- * because we are abusing the space allocated for the bio_vecs
- * for the page array. Because the bio_vecs are larger than the
- * page pointers by definition this will always work. But it also
- * means we can't use bio_add_page, so any changes to it's semantics
- * need to be reflected here as well.
- */
- bio->bi_iter.bi_size += size;
- bio->bi_vcnt += nr_pages;
-
- diff = (nr_pages * PAGE_SIZE - offset) - size;
- while (nr_pages--) {
- bv[nr_pages].bv_page = pages[nr_pages];
- bv[nr_pages].bv_len = PAGE_SIZE;
- bv[nr_pages].bv_offset = 0;
- }
+ for (left = size, i = 0; left > 0; left -= len, i++) {
+ struct page *page = pages[i];
- bv[0].bv_offset += offset;
- bv[0].bv_len -= offset;
- if (diff)
- bv[bio->bi_vcnt - 1].bv_len -= diff;
+ len = min_t(size_t, PAGE_SIZE - offset, left);
+ if (WARN_ON_ONCE(bio_add_page(bio, page, len, offset) != len))
+ return -EINVAL;
+ offset = 0;
+ }
iov_iter_advance(iter, size);
return 0;
--
2.14.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/3] block: simplify bio_check_pages_dirty
2018-06-04 13:58 ` [PATCH 1/3] block: simplify bio_check_pages_dirty Christoph Hellwig
@ 2018-06-05 3:23 ` Ming Lei
0 siblings, 0 replies; 8+ messages in thread
From: Ming Lei @ 2018-06-05 3:23 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Jens Axboe, linux-block
On Mon, Jun 04, 2018 at 03:58:51PM +0200, Christoph Hellwig wrote:
> bio_check_pages_dirty currently inviolates the invariant that bv_page of
> a bio_vec inside bi_vcnt shouldn't be zero, and that is going to become
> really annoying with multpath biovecs. Fortunately there isn't any
> all that good reason for it - once we decide to defer freeing the bio
> to a workqueue holding onto a few additional pages isn't really an
> issue anymore. So just check if there is a clean page that needs
> dirtying in the first path, and do a second pass to free them if there
> was none, while the cache is still hot.
>
> Also use the chance to micro-optimize bio_dirty_fn a bit by not saving
> irq state - we know we are called from a workqueue.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> block/bio.c | 56 +++++++++++++++++++++-----------------------------------
> 1 file changed, 21 insertions(+), 35 deletions(-)
>
> diff --git a/block/bio.c b/block/bio.c
> index 53e0f0a1ed94..50941c1c9118 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -1590,19 +1590,15 @@ static void bio_release_pages(struct bio *bio)
> struct bio_vec *bvec;
> int i;
>
> - bio_for_each_segment_all(bvec, bio, i) {
> - struct page *page = bvec->bv_page;
> -
> - if (page)
> - put_page(page);
> - }
> + bio_for_each_segment_all(bvec, bio, i)
> + put_page(bvec->bv_page);
> }
>
> /*
> * bio_check_pages_dirty() will check that all the BIO's pages are still dirty.
> * If they are, then fine. If, however, some pages are clean then they must
> * have been written out during the direct-IO read. So we take another ref on
> - * the BIO and the offending pages and re-dirty the pages in process context.
> + * the BIO and re-dirty the pages in process context.
> *
> * It is expected that bio_check_pages_dirty() will wholly own the BIO from
> * here on. It will run one put_page() against each page and will run one
> @@ -1620,52 +1616,42 @@ static struct bio *bio_dirty_list;
> */
> static void bio_dirty_fn(struct work_struct *work)
> {
> - unsigned long flags;
> - struct bio *bio;
> + struct bio *bio, *next;
>
> - spin_lock_irqsave(&bio_dirty_lock, flags);
> - bio = bio_dirty_list;
> + spin_lock_irq(&bio_dirty_lock);
> + next = bio_dirty_list;
> bio_dirty_list = NULL;
> - spin_unlock_irqrestore(&bio_dirty_lock, flags);
> + spin_unlock_irq(&bio_dirty_lock);
>
> - while (bio) {
> - struct bio *next = bio->bi_private;
> + while ((bio = next) != NULL) {
> + next = bio->bi_private;
>
> bio_set_pages_dirty(bio);
> bio_release_pages(bio);
> bio_put(bio);
> - bio = next;
> }
> }
>
> void bio_check_pages_dirty(struct bio *bio)
> {
> struct bio_vec *bvec;
> - int nr_clean_pages = 0;
> + unsigned long flags;
> int i;
>
> bio_for_each_segment_all(bvec, bio, i) {
> - struct page *page = bvec->bv_page;
> -
> - if (PageDirty(page) || PageCompound(page)) {
> - put_page(page);
> - bvec->bv_page = NULL;
> - } else {
> - nr_clean_pages++;
> - }
> + if (!PageDirty(bvec->bv_page) && !PageCompound(bvec->bv_page))
> + goto defer;
> }
>
> - if (nr_clean_pages) {
> - unsigned long flags;
> -
> - spin_lock_irqsave(&bio_dirty_lock, flags);
> - bio->bi_private = bio_dirty_list;
> - bio_dirty_list = bio;
> - spin_unlock_irqrestore(&bio_dirty_lock, flags);
> - schedule_work(&bio_dirty_work);
> - } else {
> - bio_put(bio);
> - }
> + bio_release_pages(bio);
> + bio_put(bio);
> + return;
> +defer:
> + spin_lock_irqsave(&bio_dirty_lock, flags);
> + bio->bi_private = bio_dirty_list;
> + bio_dirty_list = bio;
> + spin_unlock_irqrestore(&bio_dirty_lock, flags);
> + schedule_work(&bio_dirty_work);
> }
>
> void generic_start_io_acct(struct request_queue *q, int rw,
It is good simplification, and the only effect may be some pages
which will be freed a bit late in partial clean case.
I have run fio randread on null_blk in a 512M ram fedora 27 cloud image
based VM, not see IOPS drop, so:
Tested-by: Ming Lei <ming.lei@redhat.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Thanks,
Ming
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] block: bio_set_pages_dirty can't see NULL bv_page in a valid bio_vec
2018-06-04 13:58 ` [PATCH 2/3] block: bio_set_pages_dirty can't see NULL bv_page in a valid bio_vec Christoph Hellwig
@ 2018-06-05 3:28 ` Ming Lei
0 siblings, 0 replies; 8+ messages in thread
From: Ming Lei @ 2018-06-05 3:28 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Jens Axboe, linux-block
On Mon, Jun 04, 2018 at 03:58:52PM +0200, Christoph Hellwig wrote:
> So don't bother handling it.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> block/bio.c | 6 ++----
> 1 file changed, 2 insertions(+), 4 deletions(-)
>
> diff --git a/block/bio.c b/block/bio.c
> index 50941c1c9118..d95fab72acb5 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -1578,10 +1578,8 @@ void bio_set_pages_dirty(struct bio *bio)
> int i;
>
> bio_for_each_segment_all(bvec, bio, i) {
> - struct page *page = bvec->bv_page;
> -
> - if (page && !PageCompound(page))
> - set_page_dirty_lock(page);
> + if (!PageCompound(bvec->bv_page))
> + set_page_dirty_lock(bvec->bv_page);
> }
> }
Looks reasonable:
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Thanks,
Ming
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] block: use bio_add_page in bio_iov_iter_get_pages
2018-06-04 13:58 ` [PATCH 3/3] block: use bio_add_page in bio_iov_iter_get_pages Christoph Hellwig
@ 2018-06-05 3:41 ` Ming Lei
2018-06-05 3:42 ` Ming Lei
0 siblings, 1 reply; 8+ messages in thread
From: Ming Lei @ 2018-06-05 3:41 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Jens Axboe, linux-block
On Mon, Jun 04, 2018 at 03:58:53PM +0200, Christoph Hellwig wrote:
> Replace a nasty hack with a different nasty hack to prepare for multipage
> bio_vecs. By moving the temporary page array as far up as possible in
> the space allocated for the bio_vec array we can iterate forward over it
> and thus use bio_add_page. Using bio_add_page means we'll be able to
> merge physically contiguous pages once support for multipath bio_vecs is
> merged.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> block/bio.c | 45 +++++++++++++++++++++------------------------
> 1 file changed, 21 insertions(+), 24 deletions(-)
>
> diff --git a/block/bio.c b/block/bio.c
> index d95fab72acb5..b09c133a1e24 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -871,6 +871,8 @@ int bio_add_page(struct bio *bio, struct page *page,
> }
> EXPORT_SYMBOL(bio_add_page);
>
> +#define PAGE_PTRS_PER_BVEC (sizeof(struct bio_vec) / sizeof(struct page *))
> +
> /**
> * bio_iov_iter_get_pages - pin user or kernel pages and add them to a bio
> * @bio: bio to add pages to
> @@ -882,38 +884,33 @@ EXPORT_SYMBOL(bio_add_page);
> int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter)
> {
> unsigned short nr_pages = bio->bi_max_vecs - bio->bi_vcnt;
> + unsigned short entries_left = bio->bi_max_vecs - bio->bi_vcnt;
> struct bio_vec *bv = bio->bi_io_vec + bio->bi_vcnt;
> struct page **pages = (struct page **)bv;
> - size_t offset, diff;
> - ssize_t size;
> + ssize_t size, left;
> + unsigned len, i;
> + size_t offset;
> +
> + /*
> + * Move page array up in the allocated memory for the bio vecs as
> + * far as possible so that we can start filling biovecs from the
> + * beginning without overwriting the temporary page array.
> + */
> + BUILD_BUG_ON(PAGE_PTRS_PER_BVEC < 2);
> + pages += entries_left * (PAGE_PTRS_PER_BVEC - 1);
>
> size = iov_iter_get_pages(iter, pages, LONG_MAX, nr_pages, &offset);
> if (unlikely(size <= 0))
> return size ? size : -EFAULT;
> - nr_pages = (size + offset + PAGE_SIZE - 1) / PAGE_SIZE;
>
> - /*
> - * Deep magic below: We need to walk the pinned pages backwards
> - * because we are abusing the space allocated for the bio_vecs
> - * for the page array. Because the bio_vecs are larger than the
> - * page pointers by definition this will always work. But it also
> - * means we can't use bio_add_page, so any changes to it's semantics
> - * need to be reflected here as well.
> - */
> - bio->bi_iter.bi_size += size;
> - bio->bi_vcnt += nr_pages;
> -
> - diff = (nr_pages * PAGE_SIZE - offset) - size;
> - while (nr_pages--) {
> - bv[nr_pages].bv_page = pages[nr_pages];
> - bv[nr_pages].bv_len = PAGE_SIZE;
> - bv[nr_pages].bv_offset = 0;
> - }
> + for (left = size, i = 0; left > 0; left -= len, i++) {
> + struct page *page = pages[i];
>
> - bv[0].bv_offset += offset;
> - bv[0].bv_len -= offset;
> - if (diff)
> - bv[bio->bi_vcnt - 1].bv_len -= diff;
> + len = min_t(size_t, PAGE_SIZE - offset, left);
> + if (WARN_ON_ONCE(bio_add_page(bio, page, len, offset) != len))
> + return -EINVAL;
> + offset = 0;
> + }
One invariant is that the page index 'i' is always <= bvec index
of the added page, and the two can only be same when adding the last
page.
So this approach is correct and looks smart & clean:
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Thanks,
Ming
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] block: use bio_add_page in bio_iov_iter_get_pages
2018-06-05 3:41 ` Ming Lei
@ 2018-06-05 3:42 ` Ming Lei
0 siblings, 0 replies; 8+ messages in thread
From: Ming Lei @ 2018-06-05 3:42 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Jens Axboe, linux-block
On Tue, Jun 05, 2018 at 11:41:19AM +0800, Ming Lei wrote:
> On Mon, Jun 04, 2018 at 03:58:53PM +0200, Christoph Hellwig wrote:
> > Replace a nasty hack with a different nasty hack to prepare for multipage
> > bio_vecs. By moving the temporary page array as far up as possible in
> > the space allocated for the bio_vec array we can iterate forward over it
> > and thus use bio_add_page. Using bio_add_page means we'll be able to
> > merge physically contiguous pages once support for multipath bio_vecs is
> > merged.
> >
> > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > ---
> > block/bio.c | 45 +++++++++++++++++++++------------------------
> > 1 file changed, 21 insertions(+), 24 deletions(-)
> >
> > diff --git a/block/bio.c b/block/bio.c
> > index d95fab72acb5..b09c133a1e24 100644
> > --- a/block/bio.c
> > +++ b/block/bio.c
> > @@ -871,6 +871,8 @@ int bio_add_page(struct bio *bio, struct page *page,
> > }
> > EXPORT_SYMBOL(bio_add_page);
> >
> > +#define PAGE_PTRS_PER_BVEC (sizeof(struct bio_vec) / sizeof(struct page *))
> > +
> > /**
> > * bio_iov_iter_get_pages - pin user or kernel pages and add them to a bio
> > * @bio: bio to add pages to
> > @@ -882,38 +884,33 @@ EXPORT_SYMBOL(bio_add_page);
> > int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter)
> > {
> > unsigned short nr_pages = bio->bi_max_vecs - bio->bi_vcnt;
> > + unsigned short entries_left = bio->bi_max_vecs - bio->bi_vcnt;
> > struct bio_vec *bv = bio->bi_io_vec + bio->bi_vcnt;
> > struct page **pages = (struct page **)bv;
> > - size_t offset, diff;
> > - ssize_t size;
> > + ssize_t size, left;
> > + unsigned len, i;
> > + size_t offset;
> > +
> > + /*
> > + * Move page array up in the allocated memory for the bio vecs as
> > + * far as possible so that we can start filling biovecs from the
> > + * beginning without overwriting the temporary page array.
> > + */
> > + BUILD_BUG_ON(PAGE_PTRS_PER_BVEC < 2);
> > + pages += entries_left * (PAGE_PTRS_PER_BVEC - 1);
> >
> > size = iov_iter_get_pages(iter, pages, LONG_MAX, nr_pages, &offset);
> > if (unlikely(size <= 0))
> > return size ? size : -EFAULT;
> > - nr_pages = (size + offset + PAGE_SIZE - 1) / PAGE_SIZE;
> >
> > - /*
> > - * Deep magic below: We need to walk the pinned pages backwards
> > - * because we are abusing the space allocated for the bio_vecs
> > - * for the page array. Because the bio_vecs are larger than the
> > - * page pointers by definition this will always work. But it also
> > - * means we can't use bio_add_page, so any changes to it's semantics
> > - * need to be reflected here as well.
> > - */
> > - bio->bi_iter.bi_size += size;
> > - bio->bi_vcnt += nr_pages;
> > -
> > - diff = (nr_pages * PAGE_SIZE - offset) - size;
> > - while (nr_pages--) {
> > - bv[nr_pages].bv_page = pages[nr_pages];
> > - bv[nr_pages].bv_len = PAGE_SIZE;
> > - bv[nr_pages].bv_offset = 0;
> > - }
> > + for (left = size, i = 0; left > 0; left -= len, i++) {
> > + struct page *page = pages[i];
> >
> > - bv[0].bv_offset += offset;
> > - bv[0].bv_len -= offset;
> > - if (diff)
> > - bv[bio->bi_vcnt - 1].bv_len -= diff;
> > + len = min_t(size_t, PAGE_SIZE - offset, left);
> > + if (WARN_ON_ONCE(bio_add_page(bio, page, len, offset) != len))
> > + return -EINVAL;
> > + offset = 0;
> > + }
>
> One invariant is that the page index 'i' is always <= bvec index
oops, the above "<=" should have been ">=".
Thanks,
Ming
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2018-06-05 3:42 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-06-04 13:58 a few cleanups to prepare for multipage bio_vecs Christoph Hellwig
2018-06-04 13:58 ` [PATCH 1/3] block: simplify bio_check_pages_dirty Christoph Hellwig
2018-06-05 3:23 ` Ming Lei
2018-06-04 13:58 ` [PATCH 2/3] block: bio_set_pages_dirty can't see NULL bv_page in a valid bio_vec Christoph Hellwig
2018-06-05 3:28 ` Ming Lei
2018-06-04 13:58 ` [PATCH 3/3] block: use bio_add_page in bio_iov_iter_get_pages Christoph Hellwig
2018-06-05 3:41 ` Ming Lei
2018-06-05 3:42 ` Ming Lei
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox