]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
dm writecache: don't split bios when overwriting contiguous cache content
authorMikulas Patocka <mpatocka@redhat.com>
Wed, 26 May 2021 14:29:45 +0000 (10:29 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 19 Jul 2021 08:00:53 +0000 (10:00 +0200)
[ Upstream commit ee50cc19d80e9b9a8283d1fb517a778faf2f6899 ]

If dm-writecache overwrites existing cached data, it splits the
incoming bio into many block-sized bios. The I/O scheduler does merge
these bios into one large request but this needless splitting and
merging causes performance degradation.

Fix this by avoiding bio splitting if the cache target area that is
being overwritten is contiguous.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/md/dm-writecache.c

index 4f72b6f66c3aeb71b1692afbe36e5d56b07683a2..7bb4d83e90cc24b4695e789e0ab1b241292d13ca 100644 (file)
@@ -1360,14 +1360,18 @@ read_next_block:
        } else {
                do {
                        bool found_entry = false;
+                       bool search_used = false;
                        if (writecache_has_error(wc))
                                goto unlock_error;
                        e = writecache_find_entry(wc, bio->bi_iter.bi_sector, 0);
                        if (e) {
-                               if (!writecache_entry_is_committed(wc, e))
+                               if (!writecache_entry_is_committed(wc, e)) {
+                                       search_used = true;
                                        goto bio_copy;
+                               }
                                if (!WC_MODE_PMEM(wc) && !e->write_in_progress) {
                                        wc->overwrote_committed = true;
+                                       search_used = true;
                                        goto bio_copy;
                                }
                                found_entry = true;
@@ -1404,13 +1408,31 @@ bio_copy:
                                sector_t current_cache_sec = start_cache_sec + (bio_size >> SECTOR_SHIFT);
 
                                while (bio_size < bio->bi_iter.bi_size) {
-                                       struct wc_entry *f = writecache_pop_from_freelist(wc, current_cache_sec);
-                                       if (!f)
-                                               break;
-                                       write_original_sector_seq_count(wc, f, bio->bi_iter.bi_sector +
-                                                                       (bio_size >> SECTOR_SHIFT), wc->seq_count);
-                                       writecache_insert_entry(wc, f);
-                                       wc->uncommitted_blocks++;
+                                       if (!search_used) {
+                                               struct wc_entry *f = writecache_pop_from_freelist(wc, current_cache_sec);
+                                               if (!f)
+                                                       break;
+                                               write_original_sector_seq_count(wc, f, bio->bi_iter.bi_sector +
+                                                                               (bio_size >> SECTOR_SHIFT), wc->seq_count);
+                                               writecache_insert_entry(wc, f);
+                                               wc->uncommitted_blocks++;
+                                       } else {
+                                               struct wc_entry *f;
+                                               struct rb_node *next = rb_next(&e->rb_node);
+                                               if (!next)
+                                                       break;
+                                               f = container_of(next, struct wc_entry, rb_node);
+                                               if (f != e + 1)
+                                                       break;
+                                               if (read_original_sector(wc, f) !=
+                                                   read_original_sector(wc, e) + (wc->block_size >> SECTOR_SHIFT))
+                                                       break;
+                                               if (unlikely(f->write_in_progress))
+                                                       break;
+                                               if (writecache_entry_is_committed(wc, f))
+                                                       wc->overwrote_committed = true;
+                                               e = f;
+                                       }
                                        bio_size += wc->block_size;
                                        current_cache_sec += wc->block_size >> SECTOR_SHIFT;
                                }