]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
dm writecache: add event counters
authorMikulas Patocka <mpatocka@redhat.com>
Tue, 27 Jul 2021 13:42:21 +0000 (09:42 -0400)
committerMike Snitzer <snitzer@redhat.com>
Tue, 10 Aug 2021 17:27:49 +0000 (13:27 -0400)
Add 10 counters for various events (hit, miss, etc) and export them in
the status line (accessed from userspace with "dmsetup status"). Also
add a message "clear_stats" that resets these counters.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Documentation/admin-guide/device-mapper/writecache.rst
drivers/md/dm-writecache.c

index 65427d8dfca6d9f90fbb91f8cb5a4c92df270131..10429779a91abc598af92b9432a1ee351cc57b82 100644 (file)
@@ -78,13 +78,23 @@ Status:
 2. the number of blocks
 3. the number of free blocks
 4. the number of blocks under writeback
+5. the number of read requests
+6. the number of read requests that hit the cache
+7. the number of write requests
+8. the number of write requests that hit uncommitted block
+9. the number of write requests that hit committed block
+10. the number of write requests that bypass the cache
+11. the number of write requests that are allocated in the cache
+12. the number of write requests that are blocked on the freelist
+13. the number of flush requests
+14. the number of discard requests
 
 Messages:
        flush
-               flush the cache device. The message returns successfully
+               Flush the cache device. The message returns successfully
                if the cache device was flushed without an error
        flush_on_suspend
-               flush the cache device on next suspend. Use this message
+               Flush the cache device on next suspend. Use this message
                when you are going to remove the cache device. The proper
                sequence for removing the cache device is:
 
@@ -98,3 +108,5 @@ Messages:
                6. the cache device is now inactive and it can be deleted
        cleaner
                See above "cleaner" constructor documentation.
+       clear_stats
+               Clear the statistics that are reported on the status line
index e89868cf7d984e2f640d7f4222056908f39a8fc9..e1d7234eec3f5e745d7682baa776043abaa38967 100644 (file)
@@ -206,6 +206,19 @@ struct dm_writecache {
 
        struct bio_set bio_set;
        mempool_t copy_pool;
+
+       struct {
+               unsigned long long reads;
+               unsigned long long read_hits;
+               unsigned long long writes;
+               unsigned long long write_hits_uncommitted;
+               unsigned long long write_hits_committed;
+               unsigned long long writes_around;
+               unsigned long long writes_allocate;
+               unsigned long long writes_blocked_on_freelist;
+               unsigned long long flushes;
+               unsigned long long discards;
+       } stats;
 };
 
 #define WB_LIST_INLINE         16
@@ -1157,6 +1170,18 @@ static int process_cleaner_mesg(unsigned argc, char **argv, struct dm_writecache
        return 0;
 }
 
+static int process_clear_stats_mesg(unsigned argc, char **argv, struct dm_writecache *wc)
+{
+       if (argc != 1)
+               return -EINVAL;
+
+       wc_lock(wc);
+       memset(&wc->stats, 0, sizeof wc->stats);
+       wc_unlock(wc);
+
+       return 0;
+}
+
 static int writecache_message(struct dm_target *ti, unsigned argc, char **argv,
                              char *result, unsigned maxlen)
 {
@@ -1169,6 +1194,8 @@ static int writecache_message(struct dm_target *ti, unsigned argc, char **argv,
                r = process_flush_on_suspend_mesg(argc, argv, wc);
        else if (!strcasecmp(argv[0], "cleaner"))
                r = process_cleaner_mesg(argc, argv, wc);
+       else if (!strcasecmp(argv[0], "clear_stats"))
+               r = process_clear_stats_mesg(argc, argv, wc);
        else
                DMERR("unrecognised message received: %s", argv[0]);
 
@@ -1320,8 +1347,10 @@ static enum wc_map_op writecache_map_read(struct dm_writecache *wc, struct bio *
        struct wc_entry *e;
 
 read_next_block:
+       wc->stats.reads++;
        e = writecache_find_entry(wc, bio->bi_iter.bi_sector, WFE_RETURN_FOLLOWING);
        if (e && read_original_sector(wc, e) == bio->bi_iter.bi_sector) {
+               wc->stats.read_hits++;
                if (WC_MODE_PMEM(wc)) {
                        bio_copy_block(wc, bio, memory_data(wc, e));
                        if (bio->bi_iter.bi_size)
@@ -1400,14 +1429,17 @@ static enum wc_map_op writecache_map_write(struct dm_writecache *wc, struct bio
        do {
                bool found_entry = false;
                bool search_used = false;
+               wc->stats.writes++;
                if (writecache_has_error(wc))
                        return WC_MAP_ERROR;
                e = writecache_find_entry(wc, bio->bi_iter.bi_sector, 0);
                if (e) {
                        if (!writecache_entry_is_committed(wc, e)) {
+                               wc->stats.write_hits_uncommitted++;
                                search_used = true;
                                goto bio_copy;
                        }
+                       wc->stats.write_hits_committed++;
                        if (!WC_MODE_PMEM(wc) && !e->write_in_progress) {
                                wc->overwrote_committed = true;
                                search_used = true;
@@ -1423,15 +1455,18 @@ static enum wc_map_op writecache_map_write(struct dm_writecache *wc, struct bio
                if (unlikely(!e)) {
                        if (!WC_MODE_PMEM(wc) && !found_entry) {
 direct_write:
+                               wc->stats.writes_around++;
                                e = writecache_find_entry(wc, bio->bi_iter.bi_sector, WFE_RETURN_FOLLOWING);
                                return writecache_map_remap_origin(wc, bio, e);
                        }
+                       wc->stats.writes_blocked_on_freelist++;
                        writecache_wait_on_freelist(wc);
                        continue;
                }
                write_original_sector_seq_count(wc, e, bio->bi_iter.bi_sector, wc->seq_count);
                writecache_insert_entry(wc, e);
                wc->uncommitted_blocks++;
+               wc->stats.writes_allocate++;
 bio_copy:
                if (WC_MODE_PMEM(wc))
                        bio_copy_block(wc, bio, memory_data(wc, e));
@@ -1453,6 +1488,7 @@ static enum wc_map_op writecache_map_flush(struct dm_writecache *wc, struct bio
                return WC_MAP_ERROR;
 
        if (WC_MODE_PMEM(wc)) {
+               wc->stats.flushes++;
                writecache_flush(wc);
                if (writecache_has_error(wc))
                        return WC_MAP_ERROR;
@@ -1463,12 +1499,15 @@ static enum wc_map_op writecache_map_flush(struct dm_writecache *wc, struct bio
        /* SSD: */
        if (dm_bio_get_target_bio_nr(bio))
                return WC_MAP_REMAP_ORIGIN;
+       wc->stats.flushes++;
        writecache_offload_bio(wc, bio);
        return WC_MAP_RETURN;
 }
 
 static enum wc_map_op writecache_map_discard(struct dm_writecache *wc, struct bio *bio)
 {
+       wc->stats.discards++;
+
        if (writecache_has_error(wc))
                return WC_MAP_ERROR;
 
@@ -2618,9 +2657,20 @@ static void writecache_status(struct dm_target *ti, status_type_t type,
 
        switch (type) {
        case STATUSTYPE_INFO:
-               DMEMIT("%ld %llu %llu %llu", writecache_has_error(wc),
+               DMEMIT("%ld %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
+                      writecache_has_error(wc),
                       (unsigned long long)wc->n_blocks, (unsigned long long)wc->freelist_size,
-                      (unsigned long long)wc->writeback_size);
+                      (unsigned long long)wc->writeback_size,
+                      wc->stats.reads,
+                      wc->stats.read_hits,
+                      wc->stats.writes,
+                      wc->stats.write_hits_uncommitted,
+                      wc->stats.write_hits_committed,
+                      wc->stats.writes_around,
+                      wc->stats.writes_allocate,
+                      wc->stats.writes_blocked_on_freelist,
+                      wc->stats.flushes,
+                      wc->stats.discards);
                break;
        case STATUSTYPE_TABLE:
                DMEMIT("%c %s %s %u ", WC_MODE_PMEM(wc) ? 'p' : 's',
@@ -2678,7 +2728,7 @@ static void writecache_status(struct dm_target *ti, status_type_t type,
 
 static struct target_type writecache_target = {
        .name                   = "writecache",
-       .version                = {1, 5, 0},
+       .version                = {1, 6, 0},
        .module                 = THIS_MODULE,
        .ctr                    = writecache_ctr,
        .dtr                    = writecache_dtr,