]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
dm: fix double accounting of flush with data
authorMike Snitzer <snitzer@redhat.com>
Fri, 18 Feb 2022 04:39:57 +0000 (23:39 -0500)
committerStefan Bader <stefan.bader@canonical.com>
Wed, 27 Apr 2022 09:56:39 +0000 (11:56 +0200)
BugLink: https://bugs.launchpad.net/bugs/1969110
commit 8d394bc4adf588ca4a0650745167cb83f86c18c9 upstream.

DM handles a flush with data by first issuing an empty flush and then
once it completes the REQ_PREFLUSH flag is removed and the payload is
issued.  The problem fixed by this commit is that both the empty flush
bio and the data payload will account the full extent of the data
payload.

Fix this by factoring out dm_io_acct() and having it wrap all IO
accounting to set the size of  bio with REQ_PREFLUSH to 0, account the
IO, and then restore the original size.

Cc: stable@vger.kernel.org
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 737d5e25e28d4b06b30bb5f42c1125c0877c4c94)
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
drivers/md/dm-stats.c
drivers/md/dm-stats.h
drivers/md/dm.c

index 24383398b990e059af74ab2d15d8f24e56821348..0e039a8c0bf2e8f60f1175a5e5a74c81e3030d11 100644 (file)
@@ -644,13 +644,14 @@ static void __dm_stat_bio(struct dm_stat *s, int bi_rw,
 
 void dm_stats_account_io(struct dm_stats *stats, unsigned long bi_rw,
                         sector_t bi_sector, unsigned bi_sectors, bool end,
-                        unsigned long duration_jiffies,
+                        unsigned long start_time,
                         struct dm_stats_aux *stats_aux)
 {
        struct dm_stat *s;
        sector_t end_sector;
        struct dm_stats_last_position *last;
        bool got_precise_time;
+       unsigned long duration_jiffies = 0;
 
        if (unlikely(!bi_sectors))
                return;
@@ -670,7 +671,8 @@ void dm_stats_account_io(struct dm_stats *stats, unsigned long bi_rw,
                                       ));
                WRITE_ONCE(last->last_sector, end_sector);
                WRITE_ONCE(last->last_rw, bi_rw);
-       }
+       } else
+               duration_jiffies = jiffies - start_time;
 
        rcu_read_lock();
 
index 943894ecee5b0a84d1b96c8a3e189254f0b8e093..09c81a1ec057db452ad32eaf21dec7f339ca5f8f 100644 (file)
@@ -31,7 +31,7 @@ int dm_stats_message(struct mapped_device *md, unsigned argc, char **argv,
 
 void dm_stats_account_io(struct dm_stats *stats, unsigned long bi_rw,
                         sector_t bi_sector, unsigned bi_sectors, bool end,
-                        unsigned long duration_jiffies,
+                        unsigned long start_time,
                         struct dm_stats_aux *aux);
 
 static inline bool dm_stats_used(struct dm_stats *st)
index 3b927c26d62aac60925d92b670356a9bcb537769..443478a0885782f5bc43a0c797767e85b1745df3 100644 (file)
@@ -484,29 +484,48 @@ u64 dm_start_time_ns_from_clone(struct bio *bio)
 }
 EXPORT_SYMBOL_GPL(dm_start_time_ns_from_clone);
 
-static void start_io_acct(struct dm_io *io)
+static bool bio_is_flush_with_data(struct bio *bio)
 {
-       struct mapped_device *md = io->md;
-       struct bio *bio = io->orig_bio;
+       return ((bio->bi_opf & REQ_PREFLUSH) && bio->bi_iter.bi_size);
+}
+
+static void dm_io_acct(bool end, struct mapped_device *md, struct bio *bio,
+                      unsigned long start_time, struct dm_stats_aux *stats_aux)
+{
+       bool is_flush_with_data;
+       unsigned int bi_size;
+
+       /* If REQ_PREFLUSH set save any payload but do not account it */
+       is_flush_with_data = bio_is_flush_with_data(bio);
+       if (is_flush_with_data) {
+               bi_size = bio->bi_iter.bi_size;
+               bio->bi_iter.bi_size = 0;
+       }
+
+       if (!end)
+               bio_start_io_acct_time(bio, start_time);
+       else
+               bio_end_io_acct(bio, start_time);
 
-       bio_start_io_acct_time(bio, io->start_time);
        if (unlikely(dm_stats_used(&md->stats)))
                dm_stats_account_io(&md->stats, bio_data_dir(bio),
                                    bio->bi_iter.bi_sector, bio_sectors(bio),
-                                   false, 0, &io->stats_aux);
+                                   end, start_time, stats_aux);
+
+       /* Restore bio's payload so it does get accounted upon requeue */
+       if (is_flush_with_data)
+               bio->bi_iter.bi_size = bi_size;
+}
+
+static void start_io_acct(struct dm_io *io)
+{
+       dm_io_acct(false, io->md, io->orig_bio, io->start_time, &io->stats_aux);
 }
 
 static void end_io_acct(struct mapped_device *md, struct bio *bio,
                        unsigned long start_time, struct dm_stats_aux *stats_aux)
 {
-       unsigned long duration = jiffies - start_time;
-
-       bio_end_io_acct(bio, start_time);
-
-       if (unlikely(dm_stats_used(&md->stats)))
-               dm_stats_account_io(&md->stats, bio_data_dir(bio),
-                                   bio->bi_iter.bi_sector, bio_sectors(bio),
-                                   true, duration, stats_aux);
+       dm_io_acct(true, md, bio, start_time, stats_aux);
 }
 
 static struct dm_io *alloc_io(struct mapped_device *md, struct bio *bio)
@@ -835,7 +854,7 @@ void dm_io_dec_pending(struct dm_io *io, blk_status_t error)
                if (io_error == BLK_STS_DM_REQUEUE)
                        return;
 
-               if ((bio->bi_opf & REQ_PREFLUSH) && bio->bi_iter.bi_size) {
+               if (bio_is_flush_with_data(bio)) {
                        /*
                         * Preflush done for flush with data, reissue
                         * without REQ_PREFLUSH.