dm space map disk: cache a small number of index entries
authorJoe Thornber <ejt@redhat.com>
Tue, 13 Apr 2021 12:09:32 +0000 (13:09 +0100)
committerMike Snitzer <snitzer@redhat.com>
Fri, 4 Jun 2021 16:07:23 +0000 (12:07 -0400)
The disk space map stores it's index entries in a btree, these are
accessed very frequently, so having a few cached makes a big difference
to performance.

With this change provisioning a new block takes roughly 20% less cpu.

Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
drivers/md/persistent-data/dm-space-map-common.c
drivers/md/persistent-data/dm-space-map-common.h

index 5552941..4a6a2a9 100644 (file)
@@ -7,6 +7,7 @@
 #include "dm-space-map-common.h"
 #include "dm-transaction-manager.h"
 #include "dm-btree-internal.h"
+#include "dm-persistent-data-internal.h"
 
 #include <linux/bitops.h>
 #include <linux/device-mapper.h>
@@ -1083,28 +1084,92 @@ int sm_ll_open_metadata(struct ll_disk *ll, struct dm_transaction_manager *tm,
 
 /*----------------------------------------------------------------*/
 
+static inline int ie_cache_writeback(struct ll_disk *ll, struct ie_cache *iec)
+{
+       iec->dirty = false;
+       __dm_bless_for_disk(iec->ie);
+       return dm_btree_insert(&ll->bitmap_info, ll->bitmap_root,
+                              &iec->index, &iec->ie, &ll->bitmap_root);
+}
+
+static inline unsigned hash_index(dm_block_t index)
+{
+       return dm_hash_block(index, IE_CACHE_MASK);
+}
+
 static int disk_ll_load_ie(struct ll_disk *ll, dm_block_t index,
                           struct disk_index_entry *ie)
 {
-       return dm_btree_lookup(&ll->bitmap_info, ll->bitmap_root, &index, ie);
+       int r;
+       unsigned h = hash_index(index);
+       struct ie_cache *iec = ll->ie_cache + h;
+
+       if (iec->valid) {
+               if (iec->index == index) {
+                       memcpy(ie, &iec->ie, sizeof(*ie));
+                       return 0;
+               }
+
+               if (iec->dirty) {
+                       r = ie_cache_writeback(ll, iec);
+                       if (r)
+                               return r;
+               }
+       }
+
+       r = dm_btree_lookup(&ll->bitmap_info, ll->bitmap_root, &index, ie);
+       if (!r) {
+               iec->valid = true;
+               iec->dirty = false;
+               iec->index = index;
+               memcpy(&iec->ie, ie, sizeof(*ie));
+       }
+
+       return r;
 }
 
 static int disk_ll_save_ie(struct ll_disk *ll, dm_block_t index,
                           struct disk_index_entry *ie)
 {
-       __dm_bless_for_disk(ie);
-       return dm_btree_insert(&ll->bitmap_info, ll->bitmap_root,
-                              &index, ie, &ll->bitmap_root);
+       int r;
+       unsigned h = hash_index(index);
+       struct ie_cache *iec = ll->ie_cache + h;
+
+       ll->bitmap_index_changed = true;
+       if (iec->valid) {
+               if (iec->index == index) {
+                       memcpy(&iec->ie, ie, sizeof(*ie));
+                       iec->dirty = true;
+                       return 0;
+               }
+
+               if (iec->dirty) {
+                       r = ie_cache_writeback(ll, iec);
+                       if (r)
+                               return r;
+               }
+       }
+
+       iec->valid = true;
+       iec->dirty = true;
+       iec->index = index;
+       memcpy(&iec->ie, ie, sizeof(*ie));
+       return 0;
 }
 
 static int disk_ll_init_index(struct ll_disk *ll)
 {
+       unsigned i;
+       for (i = 0; i < IE_CACHE_SIZE; i++) {
+               struct ie_cache *iec = ll->ie_cache + i;
+               iec->valid = false;
+               iec->dirty = false;
+       }
        return dm_btree_empty(&ll->bitmap_info, &ll->bitmap_root);
 }
 
 static int disk_ll_open(struct ll_disk *ll)
 {
-       /* nothing to do */
        return 0;
 }
 
@@ -1115,7 +1180,16 @@ static dm_block_t disk_ll_max_entries(struct ll_disk *ll)
 
 static int disk_ll_commit(struct ll_disk *ll)
 {
-       return 0;
+       int r = 0;
+       unsigned i;
+
+       for (i = 0; i < IE_CACHE_SIZE; i++) {
+               struct ie_cache *iec = ll->ie_cache + i;
+               if (iec->valid && iec->dirty)
+                       r = ie_cache_writeback(ll, iec);
+       }
+
+       return r;
 }
 
 int sm_ll_new_disk(struct ll_disk *ll, struct dm_transaction_manager *tm)
index 4a22183..706ceb8 100644 (file)
@@ -54,6 +54,20 @@ typedef int (*open_index_fn)(struct ll_disk *ll);
 typedef dm_block_t (*max_index_entries_fn)(struct ll_disk *ll);
 typedef int (*commit_fn)(struct ll_disk *ll);
 
+/*
+ * A lot of time can be wasted reading and writing the same
+ * index entry.  So we cache a few entries.
+ */
+#define IE_CACHE_SIZE 64
+#define IE_CACHE_MASK (IE_CACHE_SIZE - 1)
+
+struct ie_cache {
+       bool valid;
+       bool dirty;
+       dm_block_t index;
+       struct disk_index_entry ie;
+};
+
 struct ll_disk {
        struct dm_transaction_manager *tm;
        struct dm_btree_info bitmap_info;
@@ -79,6 +93,8 @@ struct ll_disk {
        max_index_entries_fn max_entries;
        commit_fn commit;
        bool bitmap_index_changed:1;
+
+       struct ie_cache ie_cache[IE_CACHE_SIZE];
 };
 
 struct disk_sm_root {