diff mbox

[46/60] block: deal with dirtying pages for multipage bvec

Message ID 1477728600-12938-47-git-send-email-tom.leiming@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ming Lei Oct. 29, 2016, 8:08 a.m. UTC
In bio_check_pages_dirty(), bvec->bv_page is used as flag
for marking if the page has been dirtied & released, and if
no, it will be dirtied in deferred workqueue.

With multipage bvec, we can't do that any more, so change
the logic into checking all pages in one mp bvec, and only
release all these pages if all are dirtied, otherwise dirty
them all in deferred wrokqueue.

Signed-off-by: Ming Lei <tom.leiming@gmail.com>
---
 block/bio.c | 43 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 36 insertions(+), 7 deletions(-)

Comments

Christoph Hellwig Oct. 31, 2016, 3:40 p.m. UTC | #1
On Sat, Oct 29, 2016 at 04:08:45PM +0800, Ming Lei wrote:
> In bio_check_pages_dirty(), bvec->bv_page is used as flag
> for marking if the page has been dirtied & released, and if
> no, it will be dirtied in deferred workqueue.
> 
> With multipage bvec, we can't do that any more, so change
> the logic into checking all pages in one mp bvec, and only
> release all these pages if all are dirtied, otherwise dirty
> them all in deferred wrokqueue.

Just defer the whole bio to the workqueue if we need to redirty any,
that avoids having all these complex iteratations.
--
To unsubscribe from this list: send the line "unsubscribe linux-block" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ming Lei Nov. 1, 2016, 12:19 a.m. UTC | #2
On Mon, Oct 31, 2016 at 11:40 PM, Christoph Hellwig <hch@infradead.org> wrote:
> On Sat, Oct 29, 2016 at 04:08:45PM +0800, Ming Lei wrote:
>> In bio_check_pages_dirty(), bvec->bv_page is used as flag
>> for marking if the page has been dirtied & released, and if
>> no, it will be dirtied in deferred workqueue.
>>
>> With multipage bvec, we can't do that any more, so change
>> the logic into checking all pages in one mp bvec, and only
>> release all these pages if all are dirtied, otherwise dirty
>> them all in deferred wrokqueue.
>
> Just defer the whole bio to the workqueue if we need to redirty any,
> that avoids having all these complex iteratations.

For dio READ, it is always dirtied before submitting, so there should be
little possibity that pages in the bio becomes non-dirty after completion,
it should hurt performance if each direct-read bio is deferred to the block
wq.


thanks,
Ming Lei
--
To unsubscribe from this list: send the line "unsubscribe linux-block" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/block/bio.c b/block/bio.c
index a9bf01784f37..8e5af6e8bba3 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1597,8 +1597,9 @@  void bio_set_pages_dirty(struct bio *bio)
 {
 	struct bio_vec *bvec;
 	int i;
+	struct bvec_iter_all bia;
 
-	bio_for_each_segment_all(bvec, bio, i) {
+	bio_for_each_segment_all_rd(bvec, bio, i, bia) {
 		struct page *page = bvec->bv_page;
 
 		if (page && !PageCompound(page))
@@ -1606,16 +1607,26 @@  void bio_set_pages_dirty(struct bio *bio)
 	}
 }
 
+static inline void release_mp_bvec_pages(struct bio_vec *bvec)
+{
+	struct bio_vec bv;
+	struct bvec_iter iter;
+
+	bvec_for_each_sp_bvec(bv, bvec, iter)
+		put_page(bv.bv_page);
+}
+
 static void bio_release_pages(struct bio *bio)
 {
 	struct bio_vec *bvec;
 	int i;
 
+	/* iterate each mp bvec */
 	bio_for_each_segment_all(bvec, bio, i) {
 		struct page *page = bvec->bv_page;
 
 		if (page)
-			put_page(page);
+			release_mp_bvec_pages(bvec);
 	}
 }
 
@@ -1659,20 +1670,38 @@  static void bio_dirty_fn(struct work_struct *work)
 	}
 }
 
+static inline void check_mp_bvec_pages(struct bio_vec *bvec,
+		int *nr_dirty, int *nr_pages)
+{
+	struct bio_vec bv;
+	struct bvec_iter iter;
+
+	bvec_for_each_sp_bvec(bv, bvec, iter) {
+		struct page *page = bv.bv_page;
+
+		if (PageDirty(page) || PageCompound(page))
+			(*nr_dirty)++;
+		(*nr_pages)++;
+	}
+}
+
 void bio_check_pages_dirty(struct bio *bio)
 {
 	struct bio_vec *bvec;
 	int nr_clean_pages = 0;
 	int i;
 
-	bio_for_each_segment_all(bvec, bio, i) {
-		struct page *page = bvec->bv_page;
+	bio_for_each_segment_all_wt(bvec, bio, i) {
+		int nr_dirty = 0, nr_pages = 0;
+
+		check_mp_bvec_pages(bvec, &nr_dirty, &nr_pages);
 
-		if (PageDirty(page) || PageCompound(page)) {
-			put_page(page);
+		/* release all pages in the mp bvec if all are dirtied */
+		if (nr_dirty == nr_pages) {
+			release_mp_bvec_pages(bvec);
 			bvec->bv_page = NULL;
 		} else {
-			nr_clean_pages++;
+			nr_clean_pages += nr_pages;
 		}
 	}