aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColy Li <colyli@suse.de>2020-08-15 00:56:56 +0800
committerColy Li <colyli@suse.de>2020-08-15 00:56:56 +0800
commitda275f71e25c285fb9bea8b548f5639e8908f85b (patch)
treea7d7cd47bd64e71dbd1b7403401e6b2d14438607
parent2a48232bd4735ec50e5050175e297ecdeef78e24 (diff)
downloadbcache-patches-da275f71e25c285fb9bea8b548f5639e8908f85b.tar.gz
for-test: update patch set for removing multiple caches in cache set
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0001-bcache-rename-struct-bkey-members.patch (renamed from for-test/remove-multiple-cache-devices/0001-bcache-rename-struct-bkey-members.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0002-bcache-remove-multiple-cache-devices-support-from-PT.patch (renamed from for-test/remove-multiple-cache-devices/0002-bcache-remove-multiple-cache-devices-support-from-PT.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0003-bcache-remove-useless-for-loop-of-KEY_PTRS.patch (renamed from for-test/remove-multiple-cache-devices/0003-bcache-remove-useless-for-loop-of-KEY_PTRS.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0004-bcache-remove-ptr-index-from-PTR_CACHE.patch (renamed from for-test/remove-multiple-cache-devices/0004-bcache-remove-ptr-index-from-PTR_CACHE.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0005-bcache-remove-ptr-index-from-PTR_BUCKET-and-PTR_BUCK.patch (renamed from for-test/remove-multiple-cache-devices/0005-bcache-remove-ptr-index-from-PTR_BUCKET-and-PTR_BUCK.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0006-bcache-remove-ptr-index-from-ptr_available-and-ptr_s.patch (renamed from for-test/remove-multiple-cache-devices/0006-bcache-remove-ptr-index-from-ptr_available-and-ptr_s.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0007-bcache-remove-ptr-index-from-bch_submit_bbio.patch (renamed from for-test/remove-multiple-cache-devices/0007-bcache-remove-ptr-index-from-bch_submit_bbio.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0008-bcache-remove-ptr-index-from-bch_bkey_copy_single_pt.patch (renamed from for-test/remove-multiple-cache-devices/0008-bcache-remove-ptr-index-from-bch_bkey_copy_single_pt.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0009-bcache-remove-ptr-index-from-bch_extent_bad_expensiv.patch (renamed from for-test/remove-multiple-cache-devices/0009-bcache-remove-ptr-index-from-bch_extent_bad_expensiv.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0010-bcache-remove-for_each_cache.patch (renamed from for-test/remove-multiple-cache-devices/0010-bcache-remove-for_each_cache.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0011-bcache-do-not-use-nr_in_set-and-nr_this_dev-of-struc.patch (renamed from for-test/remove-multiple-cache-devices/0011-bcache-do-not-use-nr_in_set-and-nr_this_dev-of-struc.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0012-bcache-change-cache_set.cache-and-cache_set.cache_by.patch (renamed from for-test/remove-multiple-cache-devices/0012-bcache-change-cache_set.cache-and-cache_set.cache_by.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0013-bcache-remove-ptr-index-from-bch_bucket_alloc_set.patch (renamed from for-test/remove-multiple-cache-devices/0013-bcache-remove-ptr-index-from-bch_bucket_alloc_set.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/0014-bcache-remove-MAX_CACHES_PER_SET-from-ptr_available.patch (renamed from for-test/remove-multiple-cache-devices/0014-bcache-remove-MAX_CACHES_PER_SET-from-ptr_available.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/original_ideas/debug_info_0001.patch (renamed from for-test/remove-multiple-cache-devices/debug_info_0001.patch)0
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0000-cover-letter.patch44
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0001-bcache-remove-int-n-from-parameter-list-of-bch_bucke.patch151
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0002-bcache-explictly-make-cache_set-only-have-single-cac.patch127
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0003-bcache-remove-for_each_cache.patch894
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0004-bcache-add-set_uuid-in-struct-cache_set.patch172
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0005-bcache-only-use-block_bytes-on-struct-cache.patch257
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0006-bcache-remove-useless-alloc_bucket_pages.patch29
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0007-bcache-remove-useless-bucket_pages.patch29
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0008-bcache-only-use-bucket_bytes-on-struct-cache.patch49
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0009-bcache-avoid-data-copy-between-cache_set-sb-and-cach.patch66
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0010-bcache-don-t-check-seq-numbers-in-register_cache_set.patch51
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0011-bcache-remove-can_attach_cache.patch49
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0012-bcache-check-and-set-sync-status-on-cache-s-in-memor.patch109
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0013-bcache-remove-embedded-struct-cache_sb-from-struct-c.patch415
-rw-r--r--for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0014-bcache-move-struct-cache_sb-out-of-uapi-bcache.h.patch261
30 files changed, 2703 insertions, 0 deletions
diff --git a/for-test/remove-multiple-cache-devices/0001-bcache-rename-struct-bkey-members.patch b/for-test/remove-multiple-cache-devices/original_ideas/0001-bcache-rename-struct-bkey-members.patch
index 433fe53..433fe53 100644
--- a/for-test/remove-multiple-cache-devices/0001-bcache-rename-struct-bkey-members.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0001-bcache-rename-struct-bkey-members.patch
diff --git a/for-test/remove-multiple-cache-devices/0002-bcache-remove-multiple-cache-devices-support-from-PT.patch b/for-test/remove-multiple-cache-devices/original_ideas/0002-bcache-remove-multiple-cache-devices-support-from-PT.patch
index 5d00e83..5d00e83 100644
--- a/for-test/remove-multiple-cache-devices/0002-bcache-remove-multiple-cache-devices-support-from-PT.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0002-bcache-remove-multiple-cache-devices-support-from-PT.patch
diff --git a/for-test/remove-multiple-cache-devices/0003-bcache-remove-useless-for-loop-of-KEY_PTRS.patch b/for-test/remove-multiple-cache-devices/original_ideas/0003-bcache-remove-useless-for-loop-of-KEY_PTRS.patch
index 14e95dd..14e95dd 100644
--- a/for-test/remove-multiple-cache-devices/0003-bcache-remove-useless-for-loop-of-KEY_PTRS.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0003-bcache-remove-useless-for-loop-of-KEY_PTRS.patch
diff --git a/for-test/remove-multiple-cache-devices/0004-bcache-remove-ptr-index-from-PTR_CACHE.patch b/for-test/remove-multiple-cache-devices/original_ideas/0004-bcache-remove-ptr-index-from-PTR_CACHE.patch
index c562646..c562646 100644
--- a/for-test/remove-multiple-cache-devices/0004-bcache-remove-ptr-index-from-PTR_CACHE.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0004-bcache-remove-ptr-index-from-PTR_CACHE.patch
diff --git a/for-test/remove-multiple-cache-devices/0005-bcache-remove-ptr-index-from-PTR_BUCKET-and-PTR_BUCK.patch b/for-test/remove-multiple-cache-devices/original_ideas/0005-bcache-remove-ptr-index-from-PTR_BUCKET-and-PTR_BUCK.patch
index e121453..e121453 100644
--- a/for-test/remove-multiple-cache-devices/0005-bcache-remove-ptr-index-from-PTR_BUCKET-and-PTR_BUCK.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0005-bcache-remove-ptr-index-from-PTR_BUCKET-and-PTR_BUCK.patch
diff --git a/for-test/remove-multiple-cache-devices/0006-bcache-remove-ptr-index-from-ptr_available-and-ptr_s.patch b/for-test/remove-multiple-cache-devices/original_ideas/0006-bcache-remove-ptr-index-from-ptr_available-and-ptr_s.patch
index 5f2fccf..5f2fccf 100644
--- a/for-test/remove-multiple-cache-devices/0006-bcache-remove-ptr-index-from-ptr_available-and-ptr_s.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0006-bcache-remove-ptr-index-from-ptr_available-and-ptr_s.patch
diff --git a/for-test/remove-multiple-cache-devices/0007-bcache-remove-ptr-index-from-bch_submit_bbio.patch b/for-test/remove-multiple-cache-devices/original_ideas/0007-bcache-remove-ptr-index-from-bch_submit_bbio.patch
index 7e44295..7e44295 100644
--- a/for-test/remove-multiple-cache-devices/0007-bcache-remove-ptr-index-from-bch_submit_bbio.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0007-bcache-remove-ptr-index-from-bch_submit_bbio.patch
diff --git a/for-test/remove-multiple-cache-devices/0008-bcache-remove-ptr-index-from-bch_bkey_copy_single_pt.patch b/for-test/remove-multiple-cache-devices/original_ideas/0008-bcache-remove-ptr-index-from-bch_bkey_copy_single_pt.patch
index 2f9d956..2f9d956 100644
--- a/for-test/remove-multiple-cache-devices/0008-bcache-remove-ptr-index-from-bch_bkey_copy_single_pt.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0008-bcache-remove-ptr-index-from-bch_bkey_copy_single_pt.patch
diff --git a/for-test/remove-multiple-cache-devices/0009-bcache-remove-ptr-index-from-bch_extent_bad_expensiv.patch b/for-test/remove-multiple-cache-devices/original_ideas/0009-bcache-remove-ptr-index-from-bch_extent_bad_expensiv.patch
index 233cb94..233cb94 100644
--- a/for-test/remove-multiple-cache-devices/0009-bcache-remove-ptr-index-from-bch_extent_bad_expensiv.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0009-bcache-remove-ptr-index-from-bch_extent_bad_expensiv.patch
diff --git a/for-test/remove-multiple-cache-devices/0010-bcache-remove-for_each_cache.patch b/for-test/remove-multiple-cache-devices/original_ideas/0010-bcache-remove-for_each_cache.patch
index e2e3e76..e2e3e76 100644
--- a/for-test/remove-multiple-cache-devices/0010-bcache-remove-for_each_cache.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0010-bcache-remove-for_each_cache.patch
diff --git a/for-test/remove-multiple-cache-devices/0011-bcache-do-not-use-nr_in_set-and-nr_this_dev-of-struc.patch b/for-test/remove-multiple-cache-devices/original_ideas/0011-bcache-do-not-use-nr_in_set-and-nr_this_dev-of-struc.patch
index f5eca11..f5eca11 100644
--- a/for-test/remove-multiple-cache-devices/0011-bcache-do-not-use-nr_in_set-and-nr_this_dev-of-struc.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0011-bcache-do-not-use-nr_in_set-and-nr_this_dev-of-struc.patch
diff --git a/for-test/remove-multiple-cache-devices/0012-bcache-change-cache_set.cache-and-cache_set.cache_by.patch b/for-test/remove-multiple-cache-devices/original_ideas/0012-bcache-change-cache_set.cache-and-cache_set.cache_by.patch
index 0a46e66..0a46e66 100644
--- a/for-test/remove-multiple-cache-devices/0012-bcache-change-cache_set.cache-and-cache_set.cache_by.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0012-bcache-change-cache_set.cache-and-cache_set.cache_by.patch
diff --git a/for-test/remove-multiple-cache-devices/0013-bcache-remove-ptr-index-from-bch_bucket_alloc_set.patch b/for-test/remove-multiple-cache-devices/original_ideas/0013-bcache-remove-ptr-index-from-bch_bucket_alloc_set.patch
index b40cf78..b40cf78 100644
--- a/for-test/remove-multiple-cache-devices/0013-bcache-remove-ptr-index-from-bch_bucket_alloc_set.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0013-bcache-remove-ptr-index-from-bch_bucket_alloc_set.patch
diff --git a/for-test/remove-multiple-cache-devices/0014-bcache-remove-MAX_CACHES_PER_SET-from-ptr_available.patch b/for-test/remove-multiple-cache-devices/original_ideas/0014-bcache-remove-MAX_CACHES_PER_SET-from-ptr_available.patch
index 47a3c0e..47a3c0e 100644
--- a/for-test/remove-multiple-cache-devices/0014-bcache-remove-MAX_CACHES_PER_SET-from-ptr_available.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/0014-bcache-remove-MAX_CACHES_PER_SET-from-ptr_available.patch
diff --git a/for-test/remove-multiple-cache-devices/debug_info_0001.patch b/for-test/remove-multiple-cache-devices/original_ideas/debug_info_0001.patch
index 91b700f..91b700f 100644
--- a/for-test/remove-multiple-cache-devices/debug_info_0001.patch
+++ b/for-test/remove-multiple-cache-devices/original_ideas/debug_info_0001.patch
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0000-cover-letter.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0000-cover-letter.patch
new file mode 100644
index 0000000..8fec3cf
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0000-cover-letter.patch
@@ -0,0 +1,44 @@
+From 3821a8cd1196fdf155605f0747b1a9d4bf6d8cff Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Sat, 15 Aug 2020 00:55:48 +0800
+Subject: [PATCH 00/14] *** SUBJECT HERE ***
+
+*** BLURB HERE ***
+
+Coly Li (14):
+ bcache: remove 'int n' from parameter list of bch_bucket_alloc_set()
+ bcache: explictly make cache_set only have single cache
+ bcache: remove for_each_cache()
+ bcache: add set_uuid in struct cache_set
+ bcache: only use block_bytes() on struct cache
+ bcache: remove useless alloc_bucket_pages()
+ bcache: remove useless bucket_pages()
+ bcache: only use bucket_bytes() on struct cache
+ bcache: avoid data copy between cache_set->sb and cache->sb
+ bcache: don't check seq numbers in register_cache_set()
+ bcache: remove can_attach_cache()
+ bcache: check and set sync status on cache's in-memory super block
+ bcache: remove embedded struct cache_sb from struct cache_set
+ bcache: move struct cache_sb out of uapi bcache.h
+
+ drivers/md/bcache/alloc.c | 60 ++++-----
+ drivers/md/bcache/bcache.h | 128 +++++++++++++++---
+ drivers/md/bcache/btree.c | 144 ++++++++++----------
+ drivers/md/bcache/btree.h | 2 +-
+ drivers/md/bcache/debug.c | 10 +-
+ drivers/md/bcache/extents.c | 6 +-
+ drivers/md/bcache/features.c | 4 +-
+ drivers/md/bcache/io.c | 2 +-
+ drivers/md/bcache/journal.c | 246 ++++++++++++++++------------------
+ drivers/md/bcache/movinggc.c | 58 ++++----
+ drivers/md/bcache/request.c | 6 +-
+ drivers/md/bcache/super.c | 225 +++++++++++--------------------
+ drivers/md/bcache/sysfs.c | 10 +-
+ drivers/md/bcache/writeback.c | 2 +-
+ include/trace/events/bcache.h | 4 +-
+ include/uapi/linux/bcache.h | 98 --------------
+ 16 files changed, 445 insertions(+), 560 deletions(-)
+
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0001-bcache-remove-int-n-from-parameter-list-of-bch_bucke.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0001-bcache-remove-int-n-from-parameter-list-of-bch_bucke.patch
new file mode 100644
index 0000000..9685be9
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0001-bcache-remove-int-n-from-parameter-list-of-bch_bucke.patch
@@ -0,0 +1,151 @@
+From d10dd49dc5c8f45bfb3e2dff9a14c36b314360ed Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 00:07:05 +0800
+Subject: [PATCH 01/14] bcache: remove 'int n' from parameter list of
+ bch_bucket_alloc_set()
+
+The parameter 'int n' from bch_bucket_alloc_set() is not cleared
+defined. From the code comments n is the number of buckets to alloc, but
+from the code itself 'n' is the maximum cache to iterate. Indeed all the
+locations where bch_bucket_alloc_set() is called, 'n' is alwasy 1.
+
+This patch removes the confused and unnecessary 'int n' from parameter
+list of bch_bucket_alloc_set(), and explicitly allocates only 1 bucket
+for its caller.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/alloc.c | 35 +++++++++++++++--------------------
+ drivers/md/bcache/bcache.h | 4 ++--
+ drivers/md/bcache/btree.c | 2 +-
+ drivers/md/bcache/super.c | 2 +-
+ 4 files changed, 19 insertions(+), 24 deletions(-)
+
+diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c
+index 52035a78d836..4493ff57476d 100644
+--- a/drivers/md/bcache/alloc.c
++++ b/drivers/md/bcache/alloc.c
+@@ -49,7 +49,7 @@
+ *
+ * bch_bucket_alloc() allocates a single bucket from a specific cache.
+ *
+- * bch_bucket_alloc_set() allocates one or more buckets from different caches
++ * bch_bucket_alloc_set() allocates one bucket from different caches
+ * out of a cache set.
+ *
+ * free_some_buckets() drives all the processes described above. It's called
+@@ -488,34 +488,29 @@ void bch_bucket_free(struct cache_set *c, struct bkey *k)
+ }
+
+ int __bch_bucket_alloc_set(struct cache_set *c, unsigned int reserve,
+- struct bkey *k, int n, bool wait)
++ struct bkey *k, bool wait)
+ {
+- int i;
++ struct cache *ca;
++ long b;
+
+ /* No allocation if CACHE_SET_IO_DISABLE bit is set */
+ if (unlikely(test_bit(CACHE_SET_IO_DISABLE, &c->flags)))
+ return -1;
+
+ lockdep_assert_held(&c->bucket_lock);
+- BUG_ON(!n || n > c->caches_loaded || n > MAX_CACHES_PER_SET);
+
+ bkey_init(k);
+
+- /* sort by free space/prio of oldest data in caches */
+-
+- for (i = 0; i < n; i++) {
+- struct cache *ca = c->cache_by_alloc[i];
+- long b = bch_bucket_alloc(ca, reserve, wait);
++ ca = c->cache_by_alloc[0];
++ b = bch_bucket_alloc(ca, reserve, wait);
++ if (b == -1)
++ goto err;
+
+- if (b == -1)
+- goto err;
++ k->ptr[0] = MAKE_PTR(ca->buckets[b].gen,
++ bucket_to_sector(c, b),
++ ca->sb.nr_this_dev);
+
+- k->ptr[i] = MAKE_PTR(ca->buckets[b].gen,
+- bucket_to_sector(c, b),
+- ca->sb.nr_this_dev);
+-
+- SET_KEY_PTRS(k, i + 1);
+- }
++ SET_KEY_PTRS(k, 1);
+
+ return 0;
+ err:
+@@ -525,12 +520,12 @@ int __bch_bucket_alloc_set(struct cache_set *c, unsigned int reserve,
+ }
+
+ int bch_bucket_alloc_set(struct cache_set *c, unsigned int reserve,
+- struct bkey *k, int n, bool wait)
++ struct bkey *k, bool wait)
+ {
+ int ret;
+
+ mutex_lock(&c->bucket_lock);
+- ret = __bch_bucket_alloc_set(c, reserve, k, n, wait);
++ ret = __bch_bucket_alloc_set(c, reserve, k, wait);
+ mutex_unlock(&c->bucket_lock);
+ return ret;
+ }
+@@ -638,7 +633,7 @@ bool bch_alloc_sectors(struct cache_set *c,
+
+ spin_unlock(&c->data_bucket_lock);
+
+- if (bch_bucket_alloc_set(c, watermark, &alloc.key, 1, wait))
++ if (bch_bucket_alloc_set(c, watermark, &alloc.key, wait))
+ return false;
+
+ spin_lock(&c->data_bucket_lock);
+diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
+index 4fd03d2496d8..5ff6e9573935 100644
+--- a/drivers/md/bcache/bcache.h
++++ b/drivers/md/bcache/bcache.h
+@@ -994,9 +994,9 @@ void bch_bucket_free(struct cache_set *c, struct bkey *k);
+
+ long bch_bucket_alloc(struct cache *ca, unsigned int reserve, bool wait);
+ int __bch_bucket_alloc_set(struct cache_set *c, unsigned int reserve,
+- struct bkey *k, int n, bool wait);
++ struct bkey *k, bool wait);
+ int bch_bucket_alloc_set(struct cache_set *c, unsigned int reserve,
+- struct bkey *k, int n, bool wait);
++ struct bkey *k, bool wait);
+ bool bch_alloc_sectors(struct cache_set *c, struct bkey *k,
+ unsigned int sectors, unsigned int write_point,
+ unsigned int write_prio, bool wait);
+diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
+index 3d8bd0692af3..e2a719fed53b 100644
+--- a/drivers/md/bcache/btree.c
++++ b/drivers/md/bcache/btree.c
+@@ -1091,7 +1091,7 @@ struct btree *__bch_btree_node_alloc(struct cache_set *c, struct btree_op *op,
+
+ mutex_lock(&c->bucket_lock);
+ retry:
+- if (__bch_bucket_alloc_set(c, RESERVE_BTREE, &k.key, 1, wait))
++ if (__bch_bucket_alloc_set(c, RESERVE_BTREE, &k.key, wait))
+ goto err;
+
+ bkey_put(c, &k.key);
+diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
+index 1bbdc410ee3c..7057ec48f3d1 100644
+--- a/drivers/md/bcache/super.c
++++ b/drivers/md/bcache/super.c
+@@ -486,7 +486,7 @@ static int __uuid_write(struct cache_set *c)
+ closure_init_stack(&cl);
+ lockdep_assert_held(&bch_register_lock);
+
+- if (bch_bucket_alloc_set(c, RESERVE_BTREE, &k.key, 1, true))
++ if (bch_bucket_alloc_set(c, RESERVE_BTREE, &k.key, true))
+ return 1;
+
+ size = meta_bucket_pages(&c->sb) * PAGE_SECTORS;
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0002-bcache-explictly-make-cache_set-only-have-single-cac.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0002-bcache-explictly-make-cache_set-only-have-single-cac.patch
new file mode 100644
index 0000000..66ebff3
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0002-bcache-explictly-make-cache_set-only-have-single-cac.patch
@@ -0,0 +1,127 @@
+From e250a7b1b72a05d3a8f085ae26101a6330c559ba Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 00:30:59 +0800
+Subject: [PATCH 02/14] bcache: explictly make cache_set only have single cache
+
+Currently although the bcache code has a framework for multiple caches
+in a cache set, but indeed the multiple caches never completed and users
+use md raid1 for multiple copies of the cached data.
+
+This patch does the following change in struct cache_set, to explicitly
+make a cache_set only have single cache,
+- Change pointer array "*cache[MAX_CACHES_PER_SET]" to a single pointer
+ "*cache".
+- Remove pointer array "*cache_by_alloc[MAX_CACHES_PER_SET]".
+- Remove "caches_loaded".
+
+Now the code looks as exactly what it does in practic: only one cache is
+used in the cache set.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/alloc.c | 2 +-
+ drivers/md/bcache/bcache.h | 8 +++-----
+ drivers/md/bcache/super.c | 19 ++++++++-----------
+ 3 files changed, 12 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c
+index 4493ff57476d..3385f6add6df 100644
+--- a/drivers/md/bcache/alloc.c
++++ b/drivers/md/bcache/alloc.c
+@@ -501,7 +501,7 @@ int __bch_bucket_alloc_set(struct cache_set *c, unsigned int reserve,
+
+ bkey_init(k);
+
+- ca = c->cache_by_alloc[0];
++ ca = c->cache;
+ b = bch_bucket_alloc(ca, reserve, wait);
+ if (b == -1)
+ goto err;
+diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
+index 5ff6e9573935..aa112c1adba1 100644
+--- a/drivers/md/bcache/bcache.h
++++ b/drivers/md/bcache/bcache.h
+@@ -519,9 +519,7 @@ struct cache_set {
+
+ struct cache_sb sb;
+
+- struct cache *cache[MAX_CACHES_PER_SET];
+- struct cache *cache_by_alloc[MAX_CACHES_PER_SET];
+- int caches_loaded;
++ struct cache *cache;
+
+ struct bcache_device **devices;
+ unsigned int devices_max_used;
+@@ -808,7 +806,7 @@ static inline struct cache *PTR_CACHE(struct cache_set *c,
+ const struct bkey *k,
+ unsigned int ptr)
+ {
+- return c->cache[PTR_DEV(k, ptr)];
++ return c->cache;
+ }
+
+ static inline size_t PTR_BUCKET_NR(struct cache_set *c,
+@@ -890,7 +888,7 @@ do { \
+ /* Looping macros */
+
+ #define for_each_cache(ca, cs, iter) \
+- for (iter = 0; ca = cs->cache[iter], iter < (cs)->sb.nr_in_set; iter++)
++ for (iter = 0; ca = cs->cache, iter < 1; iter++)
+
+ #define for_each_bucket(b, ca) \
+ for (b = (ca)->buckets + (ca)->sb.first_bucket; \
+diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
+index 7057ec48f3d1..e9ccfa17beb8 100644
+--- a/drivers/md/bcache/super.c
++++ b/drivers/md/bcache/super.c
+@@ -1675,7 +1675,7 @@ static void cache_set_free(struct closure *cl)
+ for_each_cache(ca, c, i)
+ if (ca) {
+ ca->set = NULL;
+- c->cache[ca->sb.nr_this_dev] = NULL;
++ c->cache = NULL;
+ kobject_put(&ca->kobj);
+ }
+
+@@ -2166,7 +2166,7 @@ static const char *register_cache_set(struct cache *ca)
+
+ list_for_each_entry(c, &bch_cache_sets, list)
+ if (!memcmp(c->sb.set_uuid, ca->sb.set_uuid, 16)) {
+- if (c->cache[ca->sb.nr_this_dev])
++ if (c->cache)
+ return "duplicate cache set member";
+
+ if (!can_attach_cache(ca, c))
+@@ -2216,14 +2216,11 @@ static const char *register_cache_set(struct cache *ca)
+
+ kobject_get(&ca->kobj);
+ ca->set = c;
+- ca->set->cache[ca->sb.nr_this_dev] = ca;
+- c->cache_by_alloc[c->caches_loaded++] = ca;
++ ca->set->cache = ca;
+
+- if (c->caches_loaded == c->sb.nr_in_set) {
+- err = "failed to run cache set";
+- if (run_cache_set(c) < 0)
+- goto err;
+- }
++ err = "failed to run cache set";
++ if (run_cache_set(c) < 0)
++ goto err;
+
+ return NULL;
+ err:
+@@ -2240,8 +2237,8 @@ void bch_cache_release(struct kobject *kobj)
+ unsigned int i;
+
+ if (ca->set) {
+- BUG_ON(ca->set->cache[ca->sb.nr_this_dev] != ca);
+- ca->set->cache[ca->sb.nr_this_dev] = NULL;
++ BUG_ON(ca->set->cache != ca);
++ ca->set->cache = NULL;
+ }
+
+ free_pages((unsigned long) ca->disk_buckets, ilog2(meta_bucket_pages(&ca->sb)));
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0003-bcache-remove-for_each_cache.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0003-bcache-remove-for_each_cache.patch
new file mode 100644
index 0000000..5eef482
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0003-bcache-remove-for_each_cache.patch
@@ -0,0 +1,894 @@
+From 2a2cc128cca72b9d0784968f264c31e435b558f5 Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 01:26:09 +0800
+Subject: [PATCH 03/14] bcache: remove for_each_cache()
+
+Since now each cache_set explicitly has single cache, for_each_cache()
+is unnecessary. This patch removes this macro, and update all locations
+where it is used, and makes sure all code logic still being consistent.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/alloc.c | 17 ++-
+ drivers/md/bcache/bcache.h | 9 +-
+ drivers/md/bcache/btree.c | 103 +++++++---------
+ drivers/md/bcache/journal.c | 229 ++++++++++++++++-------------------
+ drivers/md/bcache/movinggc.c | 58 +++++----
+ drivers/md/bcache/super.c | 114 +++++++----------
+ 6 files changed, 236 insertions(+), 294 deletions(-)
+
+diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c
+index 3385f6add6df..1b8310992dd0 100644
+--- a/drivers/md/bcache/alloc.c
++++ b/drivers/md/bcache/alloc.c
+@@ -88,7 +88,6 @@ void bch_rescale_priorities(struct cache_set *c, int sectors)
+ struct cache *ca;
+ struct bucket *b;
+ unsigned long next = c->nbuckets * c->sb.bucket_size / 1024;
+- unsigned int i;
+ int r;
+
+ atomic_sub(sectors, &c->rescale);
+@@ -104,14 +103,14 @@ void bch_rescale_priorities(struct cache_set *c, int sectors)
+
+ c->min_prio = USHRT_MAX;
+
+- for_each_cache(ca, c, i)
+- for_each_bucket(b, ca)
+- if (b->prio &&
+- b->prio != BTREE_PRIO &&
+- !atomic_read(&b->pin)) {
+- b->prio--;
+- c->min_prio = min(c->min_prio, b->prio);
+- }
++ ca = c->cache;
++ for_each_bucket(b, ca)
++ if (b->prio &&
++ b->prio != BTREE_PRIO &&
++ !atomic_read(&b->pin)) {
++ b->prio--;
++ c->min_prio = min(c->min_prio, b->prio);
++ }
+
+ mutex_unlock(&c->bucket_lock);
+ }
+diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
+index aa112c1adba1..7ffe6b2d179b 100644
+--- a/drivers/md/bcache/bcache.h
++++ b/drivers/md/bcache/bcache.h
+@@ -887,9 +887,6 @@ do { \
+
+ /* Looping macros */
+
+-#define for_each_cache(ca, cs, iter) \
+- for (iter = 0; ca = cs->cache, iter < 1; iter++)
+-
+ #define for_each_bucket(b, ca) \
+ for (b = (ca)->buckets + (ca)->sb.first_bucket; \
+ b < (ca)->buckets + (ca)->sb.nbuckets; b++)
+@@ -931,11 +928,9 @@ static inline uint8_t bucket_gc_gen(struct bucket *b)
+
+ static inline void wake_up_allocators(struct cache_set *c)
+ {
+- struct cache *ca;
+- unsigned int i;
++ struct cache *ca = c->cache;
+
+- for_each_cache(ca, c, i)
+- wake_up_process(ca->alloc_thread);
++ wake_up_process(ca->alloc_thread);
+ }
+
+ static inline void closure_bio_submit(struct cache_set *c,
+diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
+index e2a719fed53b..0817ad510d9f 100644
+--- a/drivers/md/bcache/btree.c
++++ b/drivers/md/bcache/btree.c
+@@ -1167,19 +1167,18 @@ static void make_btree_freeing_key(struct btree *b, struct bkey *k)
+ static int btree_check_reserve(struct btree *b, struct btree_op *op)
+ {
+ struct cache_set *c = b->c;
+- struct cache *ca;
+- unsigned int i, reserve = (c->root->level - b->level) * 2 + 1;
++ struct cache *ca = c->cache;
++ unsigned int reserve = (c->root->level - b->level) * 2 + 1;
+
+ mutex_lock(&c->bucket_lock);
+
+- for_each_cache(ca, c, i)
+- if (fifo_used(&ca->free[RESERVE_BTREE]) < reserve) {
+- if (op)
+- prepare_to_wait(&c->btree_cache_wait, &op->wait,
+- TASK_UNINTERRUPTIBLE);
+- mutex_unlock(&c->bucket_lock);
+- return -EINTR;
+- }
++ if (fifo_used(&ca->free[RESERVE_BTREE]) < reserve) {
++ if (op)
++ prepare_to_wait(&c->btree_cache_wait, &op->wait,
++ TASK_UNINTERRUPTIBLE);
++ mutex_unlock(&c->bucket_lock);
++ return -EINTR;
++ }
+
+ mutex_unlock(&c->bucket_lock);
+
+@@ -1695,7 +1694,6 @@ static void btree_gc_start(struct cache_set *c)
+ {
+ struct cache *ca;
+ struct bucket *b;
+- unsigned int i;
+
+ if (!c->gc_mark_valid)
+ return;
+@@ -1705,14 +1703,14 @@ static void btree_gc_start(struct cache_set *c)
+ c->gc_mark_valid = 0;
+ c->gc_done = ZERO_KEY;
+
+- for_each_cache(ca, c, i)
+- for_each_bucket(b, ca) {
+- b->last_gc = b->gen;
+- if (!atomic_read(&b->pin)) {
+- SET_GC_MARK(b, 0);
+- SET_GC_SECTORS_USED(b, 0);
+- }
++ ca = c->cache;
++ for_each_bucket(b, ca) {
++ b->last_gc = b->gen;
++ if (!atomic_read(&b->pin)) {
++ SET_GC_MARK(b, 0);
++ SET_GC_SECTORS_USED(b, 0);
+ }
++ }
+
+ mutex_unlock(&c->bucket_lock);
+ }
+@@ -1721,7 +1719,8 @@ static void bch_btree_gc_finish(struct cache_set *c)
+ {
+ struct bucket *b;
+ struct cache *ca;
+- unsigned int i;
++ unsigned int i, j;
++ uint64_t *k;
+
+ mutex_lock(&c->bucket_lock);
+
+@@ -1739,7 +1738,6 @@ static void bch_btree_gc_finish(struct cache_set *c)
+ struct bcache_device *d = c->devices[i];
+ struct cached_dev *dc;
+ struct keybuf_key *w, *n;
+- unsigned int j;
+
+ if (!d || UUID_FLASH_ONLY(&c->uuids[i]))
+ continue;
+@@ -1756,29 +1754,27 @@ static void bch_btree_gc_finish(struct cache_set *c)
+ rcu_read_unlock();
+
+ c->avail_nbuckets = 0;
+- for_each_cache(ca, c, i) {
+- uint64_t *i;
+
+- ca->invalidate_needs_gc = 0;
++ ca = c->cache;
++ ca->invalidate_needs_gc = 0;
+
+- for (i = ca->sb.d; i < ca->sb.d + ca->sb.keys; i++)
+- SET_GC_MARK(ca->buckets + *i, GC_MARK_METADATA);
++ for (k = ca->sb.d; k < ca->sb.d + ca->sb.keys; k++)
++ SET_GC_MARK(ca->buckets + *k, GC_MARK_METADATA);
+
+- for (i = ca->prio_buckets;
+- i < ca->prio_buckets + prio_buckets(ca) * 2; i++)
+- SET_GC_MARK(ca->buckets + *i, GC_MARK_METADATA);
++ for (k = ca->prio_buckets;
++ k < ca->prio_buckets + prio_buckets(ca) * 2; k++)
++ SET_GC_MARK(ca->buckets + *k, GC_MARK_METADATA);
+
+- for_each_bucket(b, ca) {
+- c->need_gc = max(c->need_gc, bucket_gc_gen(b));
++ for_each_bucket(b, ca) {
++ c->need_gc = max(c->need_gc, bucket_gc_gen(b));
+
+- if (atomic_read(&b->pin))
+- continue;
++ if (atomic_read(&b->pin))
++ continue;
+
+- BUG_ON(!GC_MARK(b) && GC_SECTORS_USED(b));
++ BUG_ON(!GC_MARK(b) && GC_SECTORS_USED(b));
+
+- if (!GC_MARK(b) || GC_MARK(b) == GC_MARK_RECLAIMABLE)
+- c->avail_nbuckets++;
+- }
++ if (!GC_MARK(b) || GC_MARK(b) == GC_MARK_RECLAIMABLE)
++ c->avail_nbuckets++;
+ }
+
+ mutex_unlock(&c->bucket_lock);
+@@ -1830,12 +1826,10 @@ static void bch_btree_gc(struct cache_set *c)
+
+ static bool gc_should_run(struct cache_set *c)
+ {
+- struct cache *ca;
+- unsigned int i;
++ struct cache *ca = c->cache;
+
+- for_each_cache(ca, c, i)
+- if (ca->invalidate_needs_gc)
+- return true;
++ if (ca->invalidate_needs_gc)
++ return true;
+
+ if (atomic_read(&c->sectors_to_gc) < 0)
+ return true;
+@@ -2081,9 +2075,8 @@ int bch_btree_check(struct cache_set *c)
+
+ void bch_initial_gc_finish(struct cache_set *c)
+ {
+- struct cache *ca;
++ struct cache *ca = c->cache;
+ struct bucket *b;
+- unsigned int i;
+
+ bch_btree_gc_finish(c);
+
+@@ -2098,20 +2091,18 @@ void bch_initial_gc_finish(struct cache_set *c)
+ * This is only safe for buckets that have no live data in them, which
+ * there should always be some of.
+ */
+- for_each_cache(ca, c, i) {
+- for_each_bucket(b, ca) {
+- if (fifo_full(&ca->free[RESERVE_PRIO]) &&
+- fifo_full(&ca->free[RESERVE_BTREE]))
+- break;
++ for_each_bucket(b, ca) {
++ if (fifo_full(&ca->free[RESERVE_PRIO]) &&
++ fifo_full(&ca->free[RESERVE_BTREE]))
++ break;
+
+- if (bch_can_invalidate_bucket(ca, b) &&
+- !GC_MARK(b)) {
+- __bch_invalidate_one_bucket(ca, b);
+- if (!fifo_push(&ca->free[RESERVE_PRIO],
+- b - ca->buckets))
+- fifo_push(&ca->free[RESERVE_BTREE],
+- b - ca->buckets);
+- }
++ if (bch_can_invalidate_bucket(ca, b) &&
++ !GC_MARK(b)) {
++ __bch_invalidate_one_bucket(ca, b);
++ if (!fifo_push(&ca->free[RESERVE_PRIO],
++ b - ca->buckets))
++ fifo_push(&ca->free[RESERVE_BTREE],
++ b - ca->buckets);
+ }
+ }
+
+diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c
+index 77fbfd52edcf..027d0f8c4daf 100644
+--- a/drivers/md/bcache/journal.c
++++ b/drivers/md/bcache/journal.c
+@@ -179,112 +179,109 @@ int bch_journal_read(struct cache_set *c, struct list_head *list)
+ ret; \
+ })
+
+- struct cache *ca;
+- unsigned int iter;
++ struct cache *ca = c->cache;
+ int ret = 0;
++ struct journal_device *ja = &ca->journal;
++ DECLARE_BITMAP(bitmap, SB_JOURNAL_BUCKETS);
++ unsigned int i, l, r, m;
++ uint64_t seq;
+
+- for_each_cache(ca, c, iter) {
+- struct journal_device *ja = &ca->journal;
+- DECLARE_BITMAP(bitmap, SB_JOURNAL_BUCKETS);
+- unsigned int i, l, r, m;
+- uint64_t seq;
+-
+- bitmap_zero(bitmap, SB_JOURNAL_BUCKETS);
+- pr_debug("%u journal buckets\n", ca->sb.njournal_buckets);
++ bitmap_zero(bitmap, SB_JOURNAL_BUCKETS);
++ pr_debug("%u journal buckets\n", ca->sb.njournal_buckets);
+
++ /*
++ * Read journal buckets ordered by golden ratio hash to quickly
++ * find a sequence of buckets with valid journal entries
++ */
++ for (i = 0; i < ca->sb.njournal_buckets; i++) {
+ /*
+- * Read journal buckets ordered by golden ratio hash to quickly
+- * find a sequence of buckets with valid journal entries
++ * We must try the index l with ZERO first for
++ * correctness due to the scenario that the journal
++ * bucket is circular buffer which might have wrapped
+ */
+- for (i = 0; i < ca->sb.njournal_buckets; i++) {
+- /*
+- * We must try the index l with ZERO first for
+- * correctness due to the scenario that the journal
+- * bucket is circular buffer which might have wrapped
+- */
+- l = (i * 2654435769U) % ca->sb.njournal_buckets;
++ l = (i * 2654435769U) % ca->sb.njournal_buckets;
+
+- if (test_bit(l, bitmap))
+- break;
++ if (test_bit(l, bitmap))
++ break;
+
+- if (read_bucket(l))
+- goto bsearch;
+- }
++ if (read_bucket(l))
++ goto bsearch;
++ }
+
+- /*
+- * If that fails, check all the buckets we haven't checked
+- * already
+- */
+- pr_debug("falling back to linear search\n");
++ /*
++ * If that fails, check all the buckets we haven't checked
++ * already
++ */
++ pr_debug("falling back to linear search\n");
+
+- for_each_clear_bit(l, bitmap, ca->sb.njournal_buckets)
+- if (read_bucket(l))
+- goto bsearch;
++ for_each_clear_bit(l, bitmap, ca->sb.njournal_buckets)
++ if (read_bucket(l))
++ goto bsearch;
+
+- /* no journal entries on this device? */
+- if (l == ca->sb.njournal_buckets)
+- continue;
++ /* no journal entries on this device? */
++ if (l == ca->sb.njournal_buckets)
++ goto out;
+ bsearch:
+- BUG_ON(list_empty(list));
++ BUG_ON(list_empty(list));
+
+- /* Binary search */
+- m = l;
+- r = find_next_bit(bitmap, ca->sb.njournal_buckets, l + 1);
+- pr_debug("starting binary search, l %u r %u\n", l, r);
++ /* Binary search */
++ m = l;
++ r = find_next_bit(bitmap, ca->sb.njournal_buckets, l + 1);
++ pr_debug("starting binary search, l %u r %u\n", l, r);
+
+- while (l + 1 < r) {
+- seq = list_entry(list->prev, struct journal_replay,
+- list)->j.seq;
++ while (l + 1 < r) {
++ seq = list_entry(list->prev, struct journal_replay,
++ list)->j.seq;
+
+- m = (l + r) >> 1;
+- read_bucket(m);
++ m = (l + r) >> 1;
++ read_bucket(m);
+
+- if (seq != list_entry(list->prev, struct journal_replay,
+- list)->j.seq)
+- l = m;
+- else
+- r = m;
+- }
++ if (seq != list_entry(list->prev, struct journal_replay,
++ list)->j.seq)
++ l = m;
++ else
++ r = m;
++ }
+
+- /*
+- * Read buckets in reverse order until we stop finding more
+- * journal entries
+- */
+- pr_debug("finishing up: m %u njournal_buckets %u\n",
+- m, ca->sb.njournal_buckets);
+- l = m;
++ /*
++ * Read buckets in reverse order until we stop finding more
++ * journal entries
++ */
++ pr_debug("finishing up: m %u njournal_buckets %u\n",
++ m, ca->sb.njournal_buckets);
++ l = m;
+
+- while (1) {
+- if (!l--)
+- l = ca->sb.njournal_buckets - 1;
++ while (1) {
++ if (!l--)
++ l = ca->sb.njournal_buckets - 1;
+
+- if (l == m)
+- break;
++ if (l == m)
++ break;
+
+- if (test_bit(l, bitmap))
+- continue;
++ if (test_bit(l, bitmap))
++ continue;
+
+- if (!read_bucket(l))
+- break;
+- }
++ if (!read_bucket(l))
++ break;
++ }
+
+- seq = 0;
++ seq = 0;
+
+- for (i = 0; i < ca->sb.njournal_buckets; i++)
+- if (ja->seq[i] > seq) {
+- seq = ja->seq[i];
+- /*
+- * When journal_reclaim() goes to allocate for
+- * the first time, it'll use the bucket after
+- * ja->cur_idx
+- */
+- ja->cur_idx = i;
+- ja->last_idx = ja->discard_idx = (i + 1) %
+- ca->sb.njournal_buckets;
++ for (i = 0; i < ca->sb.njournal_buckets; i++)
++ if (ja->seq[i] > seq) {
++ seq = ja->seq[i];
++ /*
++ * When journal_reclaim() goes to allocate for
++ * the first time, it'll use the bucket after
++ * ja->cur_idx
++ */
++ ja->cur_idx = i;
++ ja->last_idx = ja->discard_idx = (i + 1) %
++ ca->sb.njournal_buckets;
+
+- }
+- }
++ }
+
++out:
+ if (!list_empty(list))
+ c->journal.seq = list_entry(list->prev,
+ struct journal_replay,
+@@ -342,12 +339,10 @@ void bch_journal_mark(struct cache_set *c, struct list_head *list)
+
+ static bool is_discard_enabled(struct cache_set *s)
+ {
+- struct cache *ca;
+- unsigned int i;
++ struct cache *ca = s->cache;
+
+- for_each_cache(ca, s, i)
+- if (ca->discard)
+- return true;
++ if (ca->discard)
++ return true;
+
+ return false;
+ }
+@@ -633,9 +628,10 @@ static void do_journal_discard(struct cache *ca)
+ static void journal_reclaim(struct cache_set *c)
+ {
+ struct bkey *k = &c->journal.key;
+- struct cache *ca;
++ struct cache *ca = c->cache;
+ uint64_t last_seq;
+- unsigned int iter, n = 0;
++ unsigned int next;
++ struct journal_device *ja = &ca->journal;
+ atomic_t p __maybe_unused;
+
+ atomic_long_inc(&c->reclaim);
+@@ -647,46 +643,31 @@ static void journal_reclaim(struct cache_set *c)
+
+ /* Update last_idx */
+
+- for_each_cache(ca, c, iter) {
+- struct journal_device *ja = &ca->journal;
+-
+- while (ja->last_idx != ja->cur_idx &&
+- ja->seq[ja->last_idx] < last_seq)
+- ja->last_idx = (ja->last_idx + 1) %
+- ca->sb.njournal_buckets;
+- }
++ while (ja->last_idx != ja->cur_idx &&
++ ja->seq[ja->last_idx] < last_seq)
++ ja->last_idx = (ja->last_idx + 1) %
++ ca->sb.njournal_buckets;
+
+- for_each_cache(ca, c, iter)
+- do_journal_discard(ca);
++ do_journal_discard(ca);
+
+ if (c->journal.blocks_free)
+ goto out;
+
+- /*
+- * Allocate:
+- * XXX: Sort by free journal space
+- */
+-
+- for_each_cache(ca, c, iter) {
+- struct journal_device *ja = &ca->journal;
+- unsigned int next = (ja->cur_idx + 1) % ca->sb.njournal_buckets;
++ next = (ja->cur_idx + 1) % ca->sb.njournal_buckets;
++ /* No space available on this device */
++ if (next == ja->discard_idx)
++ goto out;
+
+- /* No space available on this device */
+- if (next == ja->discard_idx)
+- continue;
++ ja->cur_idx = next;
++ k->ptr[0] = MAKE_PTR(0,
++ bucket_to_sector(c, ca->sb.d[ja->cur_idx]),
++ ca->sb.nr_this_dev);
++ atomic_long_inc(&c->reclaimed_journal_buckets);
+
+- ja->cur_idx = next;
+- k->ptr[n++] = MAKE_PTR(0,
+- bucket_to_sector(c, ca->sb.d[ja->cur_idx]),
+- ca->sb.nr_this_dev);
+- atomic_long_inc(&c->reclaimed_journal_buckets);
+- }
++ bkey_init(k);
++ SET_KEY_PTRS(k, 1);
++ c->journal.blocks_free = c->sb.bucket_size >> c->block_bits;
+
+- if (n) {
+- bkey_init(k);
+- SET_KEY_PTRS(k, n);
+- c->journal.blocks_free = c->sb.bucket_size >> c->block_bits;
+- }
+ out:
+ if (!journal_full(&c->journal))
+ __closure_wake_up(&c->journal.wait);
+@@ -750,7 +731,7 @@ static void journal_write_unlocked(struct closure *cl)
+ __releases(c->journal.lock)
+ {
+ struct cache_set *c = container_of(cl, struct cache_set, journal.io);
+- struct cache *ca;
++ struct cache *ca = c->cache;
+ struct journal_write *w = c->journal.cur;
+ struct bkey *k = &c->journal.key;
+ unsigned int i, sectors = set_blocks(w->data, block_bytes(c)) *
+@@ -780,9 +761,7 @@ static void journal_write_unlocked(struct closure *cl)
+ bkey_copy(&w->data->btree_root, &c->root->key);
+ bkey_copy(&w->data->uuid_bucket, &c->uuid_bucket);
+
+- for_each_cache(ca, c, i)
+- w->data->prio_bucket[ca->sb.nr_this_dev] = ca->prio_buckets[0];
+-
++ w->data->prio_bucket[ca->sb.nr_this_dev] = ca->prio_buckets[0];
+ w->data->magic = jset_magic(&c->sb);
+ w->data->version = BCACHE_JSET_VERSION;
+ w->data->last_seq = last_seq(&c->journal);
+diff --git a/drivers/md/bcache/movinggc.c b/drivers/md/bcache/movinggc.c
+index 5872d6470470..b9c3d27ec093 100644
+--- a/drivers/md/bcache/movinggc.c
++++ b/drivers/md/bcache/movinggc.c
+@@ -196,50 +196,48 @@ static unsigned int bucket_heap_top(struct cache *ca)
+
+ void bch_moving_gc(struct cache_set *c)
+ {
+- struct cache *ca;
++ struct cache *ca = c->cache;
+ struct bucket *b;
+- unsigned int i;
++ unsigned long sectors_to_move, reserve_sectors;
+
+ if (!c->copy_gc_enabled)
+ return;
+
+ mutex_lock(&c->bucket_lock);
+
+- for_each_cache(ca, c, i) {
+- unsigned long sectors_to_move = 0;
+- unsigned long reserve_sectors = ca->sb.bucket_size *
++ sectors_to_move = 0;
++ reserve_sectors = ca->sb.bucket_size *
+ fifo_used(&ca->free[RESERVE_MOVINGGC]);
+
+- ca->heap.used = 0;
+-
+- for_each_bucket(b, ca) {
+- if (GC_MARK(b) == GC_MARK_METADATA ||
+- !GC_SECTORS_USED(b) ||
+- GC_SECTORS_USED(b) == ca->sb.bucket_size ||
+- atomic_read(&b->pin))
+- continue;
+-
+- if (!heap_full(&ca->heap)) {
+- sectors_to_move += GC_SECTORS_USED(b);
+- heap_add(&ca->heap, b, bucket_cmp);
+- } else if (bucket_cmp(b, heap_peek(&ca->heap))) {
+- sectors_to_move -= bucket_heap_top(ca);
+- sectors_to_move += GC_SECTORS_USED(b);
+-
+- ca->heap.data[0] = b;
+- heap_sift(&ca->heap, 0, bucket_cmp);
+- }
+- }
++ ca->heap.used = 0;
++
++ for_each_bucket(b, ca) {
++ if (GC_MARK(b) == GC_MARK_METADATA ||
++ !GC_SECTORS_USED(b) ||
++ GC_SECTORS_USED(b) == ca->sb.bucket_size ||
++ atomic_read(&b->pin))
++ continue;
+
+- while (sectors_to_move > reserve_sectors) {
+- heap_pop(&ca->heap, b, bucket_cmp);
+- sectors_to_move -= GC_SECTORS_USED(b);
++ if (!heap_full(&ca->heap)) {
++ sectors_to_move += GC_SECTORS_USED(b);
++ heap_add(&ca->heap, b, bucket_cmp);
++ } else if (bucket_cmp(b, heap_peek(&ca->heap))) {
++ sectors_to_move -= bucket_heap_top(ca);
++ sectors_to_move += GC_SECTORS_USED(b);
++
++ ca->heap.data[0] = b;
++ heap_sift(&ca->heap, 0, bucket_cmp);
+ }
++ }
+
+- while (heap_pop(&ca->heap, b, bucket_cmp))
+- SET_GC_MOVE(b, 1);
++ while (sectors_to_move > reserve_sectors) {
++ heap_pop(&ca->heap, b, bucket_cmp);
++ sectors_to_move -= GC_SECTORS_USED(b);
+ }
+
++ while (heap_pop(&ca->heap, b, bucket_cmp))
++ SET_GC_MOVE(b, 1);
++
+ mutex_unlock(&c->bucket_lock);
+
+ c->moving_gc_keys.last_scanned = ZERO_KEY;
+diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
+index e9ccfa17beb8..2521be9380d6 100644
+--- a/drivers/md/bcache/super.c
++++ b/drivers/md/bcache/super.c
+@@ -343,8 +343,9 @@ static void bcache_write_super_unlock(struct closure *cl)
+ void bcache_write_super(struct cache_set *c)
+ {
+ struct closure *cl = &c->sb_write;
+- struct cache *ca;
+- unsigned int i, version = BCACHE_SB_VERSION_CDEV_WITH_UUID;
++ struct cache *ca = c->cache;
++ struct bio *bio = &ca->sb_bio;
++ unsigned int version = BCACHE_SB_VERSION_CDEV_WITH_UUID;
+
+ down(&c->sb_write_mutex);
+ closure_init(cl, &c->cl);
+@@ -354,23 +355,19 @@ void bcache_write_super(struct cache_set *c)
+ if (c->sb.version > version)
+ version = c->sb.version;
+
+- for_each_cache(ca, c, i) {
+- struct bio *bio = &ca->sb_bio;
+-
+- ca->sb.version = version;
+- ca->sb.seq = c->sb.seq;
+- ca->sb.last_mount = c->sb.last_mount;
++ ca->sb.version = version;
++ ca->sb.seq = c->sb.seq;
++ ca->sb.last_mount = c->sb.last_mount;
+
+- SET_CACHE_SYNC(&ca->sb, CACHE_SYNC(&c->sb));
++ SET_CACHE_SYNC(&ca->sb, CACHE_SYNC(&c->sb));
+
+- bio_init(bio, ca->sb_bv, 1);
+- bio_set_dev(bio, ca->bdev);
+- bio->bi_end_io = write_super_endio;
+- bio->bi_private = ca;
++ bio_init(bio, ca->sb_bv, 1);
++ bio_set_dev(bio, ca->bdev);
++ bio->bi_end_io = write_super_endio;
++ bio->bi_private = ca;
+
+- closure_get(cl);
+- __write_super(&ca->sb, ca->sb_disk, bio);
+- }
++ closure_get(cl);
++ __write_super(&ca->sb, ca->sb_disk, bio);
+
+ closure_return_with_destructor(cl, bcache_write_super_unlock);
+ }
+@@ -772,26 +769,22 @@ static void bcache_device_unlink(struct bcache_device *d)
+ lockdep_assert_held(&bch_register_lock);
+
+ if (d->c && !test_and_set_bit(BCACHE_DEV_UNLINK_DONE, &d->flags)) {
+- unsigned int i;
+- struct cache *ca;
++ struct cache *ca = d->c->cache;
+
+ sysfs_remove_link(&d->c->kobj, d->name);
+ sysfs_remove_link(&d->kobj, "cache");
+
+- for_each_cache(ca, d->c, i)
+- bd_unlink_disk_holder(ca->bdev, d->disk);
++ bd_unlink_disk_holder(ca->bdev, d->disk);
+ }
+ }
+
+ static void bcache_device_link(struct bcache_device *d, struct cache_set *c,
+ const char *name)
+ {
+- unsigned int i;
+- struct cache *ca;
++ struct cache *ca = c->cache;
+ int ret;
+
+- for_each_cache(ca, d->c, i)
+- bd_link_disk_holder(ca->bdev, d->disk);
++ bd_link_disk_holder(ca->bdev, d->disk);
+
+ snprintf(d->name, BCACHEDEVNAME_SIZE,
+ "%s%u", name, d->id);
+@@ -1663,7 +1656,6 @@ static void cache_set_free(struct closure *cl)
+ {
+ struct cache_set *c = container_of(cl, struct cache_set, cl);
+ struct cache *ca;
+- unsigned int i;
+
+ debugfs_remove(c->debug);
+
+@@ -1672,12 +1664,12 @@ static void cache_set_free(struct closure *cl)
+ bch_journal_free(c);
+
+ mutex_lock(&bch_register_lock);
+- for_each_cache(ca, c, i)
+- if (ca) {
+- ca->set = NULL;
+- c->cache = NULL;
+- kobject_put(&ca->kobj);
+- }
++ ca = c->cache;
++ if (ca) {
++ ca->set = NULL;
++ c->cache = NULL;
++ kobject_put(&ca->kobj);
++ }
+
+ bch_bset_sort_state_free(&c->sort);
+ free_pages((unsigned long) c->uuids, ilog2(meta_bucket_pages(&c->sb)));
+@@ -1703,9 +1695,8 @@ static void cache_set_free(struct closure *cl)
+ static void cache_set_flush(struct closure *cl)
+ {
+ struct cache_set *c = container_of(cl, struct cache_set, caching);
+- struct cache *ca;
++ struct cache *ca = c->cache;
+ struct btree *b;
+- unsigned int i;
+
+ bch_cache_accounting_destroy(&c->accounting);
+
+@@ -1730,9 +1721,8 @@ static void cache_set_flush(struct closure *cl)
+ mutex_unlock(&b->write_lock);
+ }
+
+- for_each_cache(ca, c, i)
+- if (ca->alloc_thread)
+- kthread_stop(ca->alloc_thread);
++ if (ca->alloc_thread)
++ kthread_stop(ca->alloc_thread);
+
+ if (c->journal.cur) {
+ cancel_delayed_work_sync(&c->journal.work);
+@@ -1973,16 +1963,14 @@ static int run_cache_set(struct cache_set *c)
+ {
+ const char *err = "cannot allocate memory";
+ struct cached_dev *dc, *t;
+- struct cache *ca;
++ struct cache *ca = c->cache;
+ struct closure cl;
+- unsigned int i;
+ LIST_HEAD(journal);
+ struct journal_replay *l;
+
+ closure_init_stack(&cl);
+
+- for_each_cache(ca, c, i)
+- c->nbuckets += ca->sb.nbuckets;
++ c->nbuckets = ca->sb.nbuckets;
+ set_gc_sectors(c);
+
+ if (CACHE_SYNC(&c->sb)) {
+@@ -2002,10 +1990,8 @@ static int run_cache_set(struct cache_set *c)
+ j = &list_entry(journal.prev, struct journal_replay, list)->j;
+
+ err = "IO error reading priorities";
+- for_each_cache(ca, c, i) {
+- if (prio_read(ca, j->prio_bucket[ca->sb.nr_this_dev]))
+- goto err;
+- }
++ if (prio_read(ca, j->prio_bucket[ca->sb.nr_this_dev]))
++ goto err;
+
+ /*
+ * If prio_read() fails it'll call cache_set_error and we'll
+@@ -2049,9 +2035,8 @@ static int run_cache_set(struct cache_set *c)
+ bch_journal_next(&c->journal);
+
+ err = "error starting allocator thread";
+- for_each_cache(ca, c, i)
+- if (bch_cache_allocator_start(ca))
+- goto err;
++ if (bch_cache_allocator_start(ca))
++ goto err;
+
+ /*
+ * First place it's safe to allocate: btree_check() and
+@@ -2070,28 +2055,23 @@ static int run_cache_set(struct cache_set *c)
+ if (bch_journal_replay(c, &journal))
+ goto err;
+ } else {
+- pr_notice("invalidating existing data\n");
++ unsigned int j;
+
+- for_each_cache(ca, c, i) {
+- unsigned int j;
+-
+- ca->sb.keys = clamp_t(int, ca->sb.nbuckets >> 7,
+- 2, SB_JOURNAL_BUCKETS);
++ pr_notice("invalidating existing data\n");
++ ca->sb.keys = clamp_t(int, ca->sb.nbuckets >> 7,
++ 2, SB_JOURNAL_BUCKETS);
+
+- for (j = 0; j < ca->sb.keys; j++)
+- ca->sb.d[j] = ca->sb.first_bucket + j;
+- }
++ for (j = 0; j < ca->sb.keys; j++)
++ ca->sb.d[j] = ca->sb.first_bucket + j;
+
+ bch_initial_gc_finish(c);
+
+ err = "error starting allocator thread";
+- for_each_cache(ca, c, i)
+- if (bch_cache_allocator_start(ca))
+- goto err;
++ if (bch_cache_allocator_start(ca))
++ goto err;
+
+ mutex_lock(&c->bucket_lock);
+- for_each_cache(ca, c, i)
+- bch_prio_write(ca, true);
++ bch_prio_write(ca, true);
+ mutex_unlock(&c->bucket_lock);
+
+ err = "cannot allocate new UUID bucket";
+@@ -2467,13 +2447,13 @@ static bool bch_is_open_backing(struct block_device *bdev)
+ static bool bch_is_open_cache(struct block_device *bdev)
+ {
+ struct cache_set *c, *tc;
+- struct cache *ca;
+- unsigned int i;
+
+- list_for_each_entry_safe(c, tc, &bch_cache_sets, list)
+- for_each_cache(ca, c, i)
+- if (ca->bdev == bdev)
+- return true;
++ list_for_each_entry_safe(c, tc, &bch_cache_sets, list) {
++ struct cache *ca = c->cache;
++ if (ca->bdev == bdev)
++ return true;
++ }
++
+ return false;
+ }
+
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0004-bcache-add-set_uuid-in-struct-cache_set.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0004-bcache-add-set_uuid-in-struct-cache_set.patch
new file mode 100644
index 0000000..2cc2238
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0004-bcache-add-set_uuid-in-struct-cache_set.patch
@@ -0,0 +1,172 @@
+From 3a40d904a9fd6946284c9b4ec157e9cbe0ac751e Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 20:12:07 +0800
+Subject: [PATCH 04/14] bcache: add set_uuid in struct cache_set
+
+This patch adds a separated set_uuid[16] in struct cache_set, to store
+the uuid of the cache set. This is the preparation to remove the embeded
+struct cache_sb from struct cache_set.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/bcache.h | 1 +
+ drivers/md/bcache/debug.c | 2 +-
+ drivers/md/bcache/super.c | 24 ++++++++++++------------
+ include/trace/events/bcache.h | 4 ++--
+ 4 files changed, 16 insertions(+), 15 deletions(-)
+
+diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
+index 7ffe6b2d179b..94a62acac4fc 100644
+--- a/drivers/md/bcache/bcache.h
++++ b/drivers/md/bcache/bcache.h
+@@ -668,6 +668,7 @@ struct cache_set {
+ struct mutex verify_lock;
+ #endif
+
++ uint8_t set_uuid[16];
+ unsigned int nr_uuids;
+ struct uuid_entry *uuids;
+ BKEY_PADDED(uuid_bucket);
+diff --git a/drivers/md/bcache/debug.c b/drivers/md/bcache/debug.c
+index 336f43910383..0ccc1b0baa42 100644
+--- a/drivers/md/bcache/debug.c
++++ b/drivers/md/bcache/debug.c
+@@ -238,7 +238,7 @@ void bch_debug_init_cache_set(struct cache_set *c)
+ if (!IS_ERR_OR_NULL(bcache_debug)) {
+ char name[50];
+
+- snprintf(name, 50, "bcache-%pU", c->sb.set_uuid);
++ snprintf(name, 50, "bcache-%pU", c->set_uuid);
+ c->debug = debugfs_create_file(name, 0400, bcache_debug, c,
+ &cache_set_debug_ops);
+ }
+diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
+index 2521be9380d6..77f5673adbbc 100644
+--- a/drivers/md/bcache/super.c
++++ b/drivers/md/bcache/super.c
+@@ -1189,8 +1189,8 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c,
+ struct cached_dev *exist_dc, *t;
+ int ret = 0;
+
+- if ((set_uuid && memcmp(set_uuid, c->sb.set_uuid, 16)) ||
+- (!set_uuid && memcmp(dc->sb.set_uuid, c->sb.set_uuid, 16)))
++ if ((set_uuid && memcmp(set_uuid, c->set_uuid, 16)) ||
++ (!set_uuid && memcmp(dc->sb.set_uuid, c->set_uuid, 16)))
+ return -ENOENT;
+
+ if (dc->disk.c) {
+@@ -1262,7 +1262,7 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c,
+ u->first_reg = u->last_reg = rtime;
+ bch_uuid_write(c);
+
+- memcpy(dc->sb.set_uuid, c->sb.set_uuid, 16);
++ memcpy(dc->sb.set_uuid, c->set_uuid, 16);
+ SET_BDEV_STATE(&dc->sb, BDEV_STATE_CLEAN);
+
+ bch_write_bdev_super(dc, &cl);
+@@ -1324,7 +1324,7 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c,
+ pr_info("Caching %s as %s on set %pU\n",
+ dc->backing_dev_name,
+ dc->disk.disk->disk_name,
+- dc->disk.c->sb.set_uuid);
++ dc->disk.c->set_uuid);
+ return 0;
+ }
+
+@@ -1632,7 +1632,7 @@ bool bch_cache_set_error(struct cache_set *c, const char *fmt, ...)
+ vaf.va = &args;
+
+ pr_err("error on %pU: %pV, disabling caching\n",
+- c->sb.set_uuid, &vaf);
++ c->set_uuid, &vaf);
+
+ va_end(args);
+
+@@ -1685,7 +1685,7 @@ static void cache_set_free(struct closure *cl)
+ list_del(&c->list);
+ mutex_unlock(&bch_register_lock);
+
+- pr_info("Cache set %pU unregistered\n", c->sb.set_uuid);
++ pr_info("Cache set %pU unregistered\n", c->set_uuid);
+ wake_up(&unregister_wait);
+
+ closure_debug_destroy(&c->cl);
+@@ -1755,7 +1755,7 @@ static void conditional_stop_bcache_device(struct cache_set *c,
+ {
+ if (dc->stop_when_cache_set_failed == BCH_CACHED_DEV_STOP_ALWAYS) {
+ pr_warn("stop_when_cache_set_failed of %s is \"always\", stop it for failed cache set %pU.\n",
+- d->disk->disk_name, c->sb.set_uuid);
++ d->disk->disk_name, c->set_uuid);
+ bcache_device_stop(d);
+ } else if (atomic_read(&dc->has_dirty)) {
+ /*
+@@ -1862,7 +1862,7 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
+
+ bch_cache_accounting_init(&c->accounting, &c->cl);
+
+- memcpy(c->sb.set_uuid, sb->set_uuid, 16);
++ memcpy(c->set_uuid, sb->set_uuid, 16);
+ c->sb.block_size = sb->block_size;
+ c->sb.bucket_size = sb->bucket_size;
+ c->sb.nr_in_set = sb->nr_in_set;
+@@ -2145,7 +2145,7 @@ static const char *register_cache_set(struct cache *ca)
+ struct cache_set *c;
+
+ list_for_each_entry(c, &bch_cache_sets, list)
+- if (!memcmp(c->sb.set_uuid, ca->sb.set_uuid, 16)) {
++ if (!memcmp(c->set_uuid, ca->sb.set_uuid, 16)) {
+ if (c->cache)
+ return "duplicate cache set member";
+
+@@ -2163,7 +2163,7 @@ static const char *register_cache_set(struct cache *ca)
+ return err;
+
+ err = "error creating kobject";
+- if (kobject_add(&c->kobj, bcache_kobj, "%pU", c->sb.set_uuid) ||
++ if (kobject_add(&c->kobj, bcache_kobj, "%pU", c->set_uuid) ||
+ kobject_add(&c->internal, &c->kobj, "internal"))
+ goto err;
+
+@@ -2188,7 +2188,7 @@ static const char *register_cache_set(struct cache *ca)
+ */
+ if (ca->sb.seq > c->sb.seq || c->sb.seq == 0) {
+ c->sb.version = ca->sb.version;
+- memcpy(c->sb.set_uuid, ca->sb.set_uuid, 16);
++ memcpy(c->set_uuid, ca->sb.set_uuid, 16);
+ c->sb.flags = ca->sb.flags;
+ c->sb.seq = ca->sb.seq;
+ pr_debug("set version = %llu\n", c->sb.version);
+@@ -2697,7 +2697,7 @@ static ssize_t bch_pending_bdevs_cleanup(struct kobject *k,
+ list_for_each_entry_safe(pdev, tpdev, &pending_devs, list) {
+ list_for_each_entry_safe(c, tc, &bch_cache_sets, list) {
+ char *pdev_set_uuid = pdev->dc->sb.set_uuid;
+- char *set_uuid = c->sb.uuid;
++ char *set_uuid = c->set_uuid;
+
+ if (!memcmp(pdev_set_uuid, set_uuid, 16)) {
+ list_del(&pdev->list);
+diff --git a/include/trace/events/bcache.h b/include/trace/events/bcache.h
+index 0bddea663b3b..e41c611d6d3b 100644
+--- a/include/trace/events/bcache.h
++++ b/include/trace/events/bcache.h
+@@ -164,7 +164,7 @@ TRACE_EVENT(bcache_write,
+ ),
+
+ TP_fast_assign(
+- memcpy(__entry->uuid, c->sb.set_uuid, 16);
++ memcpy(__entry->uuid, c->set_uuid, 16);
+ __entry->inode = inode;
+ __entry->sector = bio->bi_iter.bi_sector;
+ __entry->nr_sector = bio->bi_iter.bi_size >> 9;
+@@ -200,7 +200,7 @@ DECLARE_EVENT_CLASS(cache_set,
+ ),
+
+ TP_fast_assign(
+- memcpy(__entry->uuid, c->sb.set_uuid, 16);
++ memcpy(__entry->uuid, c->set_uuid, 16);
+ ),
+
+ TP_printk("%pU", __entry->uuid)
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0005-bcache-only-use-block_bytes-on-struct-cache.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0005-bcache-only-use-block_bytes-on-struct-cache.patch
new file mode 100644
index 0000000..ab4564b
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0005-bcache-only-use-block_bytes-on-struct-cache.patch
@@ -0,0 +1,257 @@
+From 33d50b1efdde6a712ea58356de757085fdf3fb97 Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 21:25:58 +0800
+Subject: [PATCH 05/14] bcache: only use block_bytes() on struct cache
+
+Because struct cache_set and struct cache both have struct cache_sb,
+therefore macro block_bytes() can be used on both of them. When removing
+the embedded struct cache_sb from struct cache_set, this macro won't be
+used on struct cache_set anymore.
+
+This patch unifies all block_bytes() usage only on struct cache, this is
+one of the preparation to remove the embedded struct cache_sb from
+struct cache_set.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/bcache.h | 2 +-
+ drivers/md/bcache/btree.c | 24 ++++++++++++------------
+ drivers/md/bcache/debug.c | 8 ++++----
+ drivers/md/bcache/journal.c | 8 ++++----
+ drivers/md/bcache/request.c | 2 +-
+ drivers/md/bcache/super.c | 2 +-
+ drivers/md/bcache/sysfs.c | 2 +-
+ 7 files changed, 24 insertions(+), 24 deletions(-)
+
+diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
+index 94a62acac4fc..29bec61cafbb 100644
+--- a/drivers/md/bcache/bcache.h
++++ b/drivers/md/bcache/bcache.h
+@@ -759,7 +759,7 @@ struct bbio {
+
+ #define bucket_pages(c) ((c)->sb.bucket_size / PAGE_SECTORS)
+ #define bucket_bytes(c) ((c)->sb.bucket_size << 9)
+-#define block_bytes(c) ((c)->sb.block_size << 9)
++#define block_bytes(ca) ((ca)->sb.block_size << 9)
+
+ static inline unsigned int meta_bucket_pages(struct cache_sb *sb)
+ {
+diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
+index 0817ad510d9f..c91b4d58a5b3 100644
+--- a/drivers/md/bcache/btree.c
++++ b/drivers/md/bcache/btree.c
+@@ -104,7 +104,7 @@
+
+ static inline struct bset *write_block(struct btree *b)
+ {
+- return ((void *) btree_bset_first(b)) + b->written * block_bytes(b->c);
++ return ((void *) btree_bset_first(b)) + b->written * block_bytes(b->c->cache);
+ }
+
+ static void bch_btree_init_next(struct btree *b)
+@@ -173,7 +173,7 @@ void bch_btree_node_read_done(struct btree *b)
+ goto err;
+
+ err = "bad btree header";
+- if (b->written + set_blocks(i, block_bytes(b->c)) >
++ if (b->written + set_blocks(i, block_bytes(b->c->cache)) >
+ btree_blocks(b))
+ goto err;
+
+@@ -199,13 +199,13 @@ void bch_btree_node_read_done(struct btree *b)
+
+ bch_btree_iter_push(iter, i->start, bset_bkey_last(i));
+
+- b->written += set_blocks(i, block_bytes(b->c));
++ b->written += set_blocks(i, block_bytes(b->c->cache));
+ }
+
+ err = "corrupted btree";
+ for (i = write_block(b);
+ bset_sector_offset(&b->keys, i) < KEY_SIZE(&b->key);
+- i = ((void *) i) + block_bytes(b->c))
++ i = ((void *) i) + block_bytes(b->c->cache))
+ if (i->seq == b->keys.set[0].data->seq)
+ goto err;
+
+@@ -347,7 +347,7 @@ static void do_btree_node_write(struct btree *b)
+
+ b->bio->bi_end_io = btree_node_write_endio;
+ b->bio->bi_private = cl;
+- b->bio->bi_iter.bi_size = roundup(set_bytes(i), block_bytes(b->c));
++ b->bio->bi_iter.bi_size = roundup(set_bytes(i), block_bytes(b->c->cache));
+ b->bio->bi_opf = REQ_OP_WRITE | REQ_META | REQ_FUA;
+ bch_bio_map(b->bio, i);
+
+@@ -423,10 +423,10 @@ void __bch_btree_node_write(struct btree *b, struct closure *parent)
+
+ do_btree_node_write(b);
+
+- atomic_long_add(set_blocks(i, block_bytes(b->c)) * b->c->sb.block_size,
++ atomic_long_add(set_blocks(i, block_bytes(b->c->cache)) * b->c->sb.block_size,
+ &PTR_CACHE(b->c, &b->key, 0)->btree_sectors_written);
+
+- b->written += set_blocks(i, block_bytes(b->c));
++ b->written += set_blocks(i, block_bytes(b->c->cache));
+ }
+
+ void bch_btree_node_write(struct btree *b, struct closure *parent)
+@@ -1344,7 +1344,7 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op,
+
+ if (nodes < 2 ||
+ __set_blocks(b->keys.set[0].data, keys,
+- block_bytes(b->c)) > blocks * (nodes - 1))
++ block_bytes(b->c->cache)) > blocks * (nodes - 1))
+ return 0;
+
+ for (i = 0; i < nodes; i++) {
+@@ -1378,7 +1378,7 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op,
+ k = bkey_next(k)) {
+ if (__set_blocks(n1, n1->keys + keys +
+ bkey_u64s(k),
+- block_bytes(b->c)) > blocks)
++ block_bytes(b->c->cache)) > blocks)
+ break;
+
+ last = k;
+@@ -1394,7 +1394,7 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op,
+ * though)
+ */
+ if (__set_blocks(n1, n1->keys + n2->keys,
+- block_bytes(b->c)) >
++ block_bytes(b->c->cache)) >
+ btree_blocks(new_nodes[i]))
+ goto out_unlock_nocoalesce;
+
+@@ -1403,7 +1403,7 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op,
+ last = &r->b->key;
+ }
+
+- BUG_ON(__set_blocks(n1, n1->keys + keys, block_bytes(b->c)) >
++ BUG_ON(__set_blocks(n1, n1->keys + keys, block_bytes(b->c->cache)) >
+ btree_blocks(new_nodes[i]));
+
+ if (last)
+@@ -2210,7 +2210,7 @@ static int btree_split(struct btree *b, struct btree_op *op,
+ goto err;
+
+ split = set_blocks(btree_bset_first(n1),
+- block_bytes(n1->c)) > (btree_blocks(b) * 4) / 5;
++ block_bytes(n1->c->cache)) > (btree_blocks(b) * 4) / 5;
+
+ if (split) {
+ unsigned int keys = 0;
+diff --git a/drivers/md/bcache/debug.c b/drivers/md/bcache/debug.c
+index 0ccc1b0baa42..b00fd08d696b 100644
+--- a/drivers/md/bcache/debug.c
++++ b/drivers/md/bcache/debug.c
+@@ -25,8 +25,8 @@ struct dentry *bcache_debug;
+ for (i = (start); \
+ (void *) i < (void *) (start) + (KEY_SIZE(&b->key) << 9) &&\
+ i->seq == (start)->seq; \
+- i = (void *) i + set_blocks(i, block_bytes(b->c)) * \
+- block_bytes(b->c))
++ i = (void *) i + set_blocks(i, block_bytes(b->c->cache)) * \
++ block_bytes(b->c->cache))
+
+ void bch_btree_verify(struct btree *b)
+ {
+@@ -82,14 +82,14 @@ void bch_btree_verify(struct btree *b)
+
+ for_each_written_bset(b, ondisk, i) {
+ unsigned int block = ((void *) i - (void *) ondisk) /
+- block_bytes(b->c);
++ block_bytes(b->c->cache);
+
+ pr_err("*** on disk block %u:\n", block);
+ bch_dump_bset(&b->keys, i, block);
+ }
+
+ pr_err("*** block %zu not written\n",
+- ((void *) i - (void *) ondisk) / block_bytes(b->c));
++ ((void *) i - (void *) ondisk) / block_bytes(b->c->cache));
+
+ for (j = 0; j < inmemory->keys; j++)
+ if (inmemory->d[j] != sorted->d[j])
+diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c
+index 027d0f8c4daf..ccd5de0ab0fe 100644
+--- a/drivers/md/bcache/journal.c
++++ b/drivers/md/bcache/journal.c
+@@ -98,7 +98,7 @@ reread: left = ca->sb.bucket_size - offset;
+ return ret;
+ }
+
+- blocks = set_blocks(j, block_bytes(ca->set));
++ blocks = set_blocks(j, block_bytes(ca));
+
+ /*
+ * Nodes in 'list' are in linear increasing order of
+@@ -734,7 +734,7 @@ static void journal_write_unlocked(struct closure *cl)
+ struct cache *ca = c->cache;
+ struct journal_write *w = c->journal.cur;
+ struct bkey *k = &c->journal.key;
+- unsigned int i, sectors = set_blocks(w->data, block_bytes(c)) *
++ unsigned int i, sectors = set_blocks(w->data, block_bytes(ca)) *
+ c->sb.block_size;
+
+ struct bio *bio;
+@@ -754,7 +754,7 @@ static void journal_write_unlocked(struct closure *cl)
+ return;
+ }
+
+- c->journal.blocks_free -= set_blocks(w->data, block_bytes(c));
++ c->journal.blocks_free -= set_blocks(w->data, block_bytes(ca));
+
+ w->data->btree_level = c->root->level;
+
+@@ -847,7 +847,7 @@ static struct journal_write *journal_wait_for_write(struct cache_set *c,
+ struct journal_write *w = c->journal.cur;
+
+ sectors = __set_blocks(w->data, w->data->keys + nkeys,
+- block_bytes(c)) * c->sb.block_size;
++ block_bytes(c->cache)) * c->sb.block_size;
+
+ if (sectors <= min_t(size_t,
+ c->journal.blocks_free * c->sb.block_size,
+diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
+index c7cadaafa947..02408fdbf5bb 100644
+--- a/drivers/md/bcache/request.c
++++ b/drivers/md/bcache/request.c
+@@ -99,7 +99,7 @@ static int bch_keylist_realloc(struct keylist *l, unsigned int u64s,
+ * bch_data_insert_keys() will insert the keys created so far
+ * and finish the rest when the keylist is empty.
+ */
+- if (newsize * sizeof(uint64_t) > block_bytes(c) - sizeof(struct jset))
++ if (newsize * sizeof(uint64_t) > block_bytes(c->cache) - sizeof(struct jset))
+ return -ENOMEM;
+
+ return __bch_keylist_realloc(l, u64s);
+diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
+index 77f5673adbbc..5636648902e3 100644
+--- a/drivers/md/bcache/super.c
++++ b/drivers/md/bcache/super.c
+@@ -1528,7 +1528,7 @@ static int flash_dev_run(struct cache_set *c, struct uuid_entry *u)
+
+ kobject_init(&d->kobj, &bch_flash_dev_ktype);
+
+- if (bcache_device_init(d, block_bytes(c), u->sectors,
++ if (bcache_device_init(d, block_bytes(c->cache), u->sectors,
+ NULL, &bcache_flash_ops))
+ goto err;
+
+diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
+index ac06c0bc3c0a..b9f524ab5cc8 100644
+--- a/drivers/md/bcache/sysfs.c
++++ b/drivers/md/bcache/sysfs.c
+@@ -714,7 +714,7 @@ SHOW(__bch_cache_set)
+ sysfs_print(synchronous, CACHE_SYNC(&c->sb));
+ sysfs_print(journal_delay_ms, c->journal_delay_ms);
+ sysfs_hprint(bucket_size, bucket_bytes(c));
+- sysfs_hprint(block_size, block_bytes(c));
++ sysfs_hprint(block_size, block_bytes(c->cache));
+ sysfs_print(tree_depth, c->root->level);
+ sysfs_print(root_usage_percent, bch_root_usage(c));
+
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0006-bcache-remove-useless-alloc_bucket_pages.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0006-bcache-remove-useless-alloc_bucket_pages.patch
new file mode 100644
index 0000000..ec0fcbe
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0006-bcache-remove-useless-alloc_bucket_pages.patch
@@ -0,0 +1,29 @@
+From f6600e5eb379e70ec360f241b8e475081be5d00d Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 21:28:23 +0800
+Subject: [PATCH 06/14] bcache: remove useless alloc_bucket_pages()
+
+Now no one uses alloc_bucket_pages() anymore, remove it from bcache.h.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/super.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
+index 5636648902e3..748b08ab4f11 100644
+--- a/drivers/md/bcache/super.c
++++ b/drivers/md/bcache/super.c
+@@ -1832,9 +1832,6 @@ void bch_cache_set_unregister(struct cache_set *c)
+ bch_cache_set_stop(c);
+ }
+
+-#define alloc_bucket_pages(gfp, c) \
+- ((void *) __get_free_pages(__GFP_ZERO|__GFP_COMP|gfp, ilog2(bucket_pages(c))))
+-
+ #define alloc_meta_bucket_pages(gfp, sb) \
+ ((void *) __get_free_pages(__GFP_ZERO|__GFP_COMP|gfp, ilog2(meta_bucket_pages(sb))))
+
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0007-bcache-remove-useless-bucket_pages.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0007-bcache-remove-useless-bucket_pages.patch
new file mode 100644
index 0000000..a6f7cbe
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0007-bcache-remove-useless-bucket_pages.patch
@@ -0,0 +1,29 @@
+From 003f48b7181ba8502b496953d888d958acecaa7d Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 21:15:28 +0800
+Subject: [PATCH 07/14] bcache: remove useless bucket_pages()
+
+It seems alloc_bucket_pages() is the only user of bucket_pages().
+Considering alloc_bucket_pages() is removed from bcache code, it is safe
+to remove the useless macro bucket_pages() now.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/bcache.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
+index 29bec61cafbb..48a2585b6bbb 100644
+--- a/drivers/md/bcache/bcache.h
++++ b/drivers/md/bcache/bcache.h
+@@ -757,7 +757,6 @@ struct bbio {
+ #define btree_default_blocks(c) \
+ ((unsigned int) ((PAGE_SECTORS * (c)->btree_pages) >> (c)->block_bits))
+
+-#define bucket_pages(c) ((c)->sb.bucket_size / PAGE_SECTORS)
+ #define bucket_bytes(c) ((c)->sb.bucket_size << 9)
+ #define block_bytes(ca) ((ca)->sb.block_size << 9)
+
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0008-bcache-only-use-bucket_bytes-on-struct-cache.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0008-bcache-only-use-bucket_bytes-on-struct-cache.patch
new file mode 100644
index 0000000..474ba05
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0008-bcache-only-use-bucket_bytes-on-struct-cache.patch
@@ -0,0 +1,49 @@
+From a82c66c4838aeb8843a79c341fedcd071a7cc19c Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 21:20:48 +0800
+Subject: [PATCH 08/14] bcache: only use bucket_bytes() on struct cache
+
+Because struct cache_set and struct cache both have struct cache_sb,
+macro bucket_bytes() currently are used on both of them. When removing
+the embedded struct cache_sb from struct cache_set, this macro won't be
+used on struct cache_set anymore.
+
+This patch unifies all bucket_bytes() usage only on struct cache, this is
+one of the preparation to remove the embedded struct cache_sb from
+struct cache_set.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/bcache.h | 2 +-
+ drivers/md/bcache/sysfs.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
+index 48a2585b6bbb..94d4baf4c405 100644
+--- a/drivers/md/bcache/bcache.h
++++ b/drivers/md/bcache/bcache.h
+@@ -757,7 +757,7 @@ struct bbio {
+ #define btree_default_blocks(c) \
+ ((unsigned int) ((PAGE_SECTORS * (c)->btree_pages) >> (c)->block_bits))
+
+-#define bucket_bytes(c) ((c)->sb.bucket_size << 9)
++#define bucket_bytes(ca) ((ca)->sb.bucket_size << 9)
+ #define block_bytes(ca) ((ca)->sb.block_size << 9)
+
+ static inline unsigned int meta_bucket_pages(struct cache_sb *sb)
+diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
+index b9f524ab5cc8..4bfe98faadcc 100644
+--- a/drivers/md/bcache/sysfs.c
++++ b/drivers/md/bcache/sysfs.c
+@@ -713,7 +713,7 @@ SHOW(__bch_cache_set)
+
+ sysfs_print(synchronous, CACHE_SYNC(&c->sb));
+ sysfs_print(journal_delay_ms, c->journal_delay_ms);
+- sysfs_hprint(bucket_size, bucket_bytes(c));
++ sysfs_hprint(bucket_size, bucket_bytes(c->cache));
+ sysfs_hprint(block_size, block_bytes(c->cache));
+ sysfs_print(tree_depth, c->root->level);
+ sysfs_print(root_usage_percent, bch_root_usage(c));
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0009-bcache-avoid-data-copy-between-cache_set-sb-and-cach.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0009-bcache-avoid-data-copy-between-cache_set-sb-and-cach.patch
new file mode 100644
index 0000000..c916c5f
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0009-bcache-avoid-data-copy-between-cache_set-sb-and-cach.patch
@@ -0,0 +1,66 @@
+From f0536de0e64929dfd0012937218ffce243d63313 Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 23:18:45 +0800
+Subject: [PATCH 09/14] bcache: avoid data copy between cache_set->sb and
+ cache->sb
+
+struct cache_sb embedded in struct cache_set is only partial used and
+not a real copy from cache's in-memory super block. When removing the
+embedded cache_set->sb, it is unncessary to copy data between these two
+in-memory super blocks (cache_set->sb and cache->sb), it is sufficient
+to just use cache->sb.
+
+This patch removes the data copy between these two in-memory super
+blocks in bch_cache_set_alloc() and bcache_write_super(). In future
+except for set_uuid, cache's super block will be referenced by cache
+set, no copy any more.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/super.c | 22 +++-------------------
+ 1 file changed, 3 insertions(+), 19 deletions(-)
+
+diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
+index 748b08ab4f11..05c5a7e867bb 100644
+--- a/drivers/md/bcache/super.c
++++ b/drivers/md/bcache/super.c
+@@ -350,16 +350,10 @@ void bcache_write_super(struct cache_set *c)
+ down(&c->sb_write_mutex);
+ closure_init(cl, &c->cl);
+
+- c->sb.seq++;
++ ca->sb.seq++;
+
+- if (c->sb.version > version)
+- version = c->sb.version;
+-
+- ca->sb.version = version;
+- ca->sb.seq = c->sb.seq;
+- ca->sb.last_mount = c->sb.last_mount;
+-
+- SET_CACHE_SYNC(&ca->sb, CACHE_SYNC(&c->sb));
++ if (ca->sb.version < version)
++ ca->sb.version = version;
+
+ bio_init(bio, ca->sb_bv, 1);
+ bio_set_dev(bio, ca->bdev);
+@@ -1860,16 +1854,6 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
+ bch_cache_accounting_init(&c->accounting, &c->cl);
+
+ memcpy(c->set_uuid, sb->set_uuid, 16);
+- c->sb.block_size = sb->block_size;
+- c->sb.bucket_size = sb->bucket_size;
+- c->sb.nr_in_set = sb->nr_in_set;
+- c->sb.last_mount = sb->last_mount;
+- c->sb.version = sb->version;
+- if (c->sb.version >= BCACHE_SB_VERSION_CDEV_WITH_FEATURES) {
+- c->sb.feature_compat = sb->feature_compat;
+- c->sb.feature_ro_compat = sb->feature_ro_compat;
+- c->sb.feature_incompat = sb->feature_incompat;
+- }
+
+ c->bucket_bits = ilog2(sb->bucket_size);
+ c->block_bits = ilog2(sb->block_size);
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0010-bcache-don-t-check-seq-numbers-in-register_cache_set.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0010-bcache-don-t-check-seq-numbers-in-register_cache_set.patch
new file mode 100644
index 0000000..eca2d72
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0010-bcache-don-t-check-seq-numbers-in-register_cache_set.patch
@@ -0,0 +1,51 @@
+From d9374567ee4b1e541a4492c4164acc75139824c3 Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 23:28:26 +0800
+Subject: [PATCH 10/14] bcache: don't check seq numbers in register_cache_set()
+
+In order to update the partial super block of cache set, the seq numbers
+of cache and cache set are checked in register_cache_set(). If cache's
+seq number is larger than cache set's seq number, cache set must update
+its partial super block from cache's super block. It is unncessary when
+the embedded struct cache_sb is removed from struct cache set.
+
+This patch removed the seq numbers checking from register_cache_set(),
+because later there will be no such partial super block in struct cache
+set, the cache set will directly reference in-memory super block from
+struct cache. This is a preparation patch for removing embedded struct
+cache_sb from struct cache_set.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/super.c | 15 ---------------
+ 1 file changed, 15 deletions(-)
+
+diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
+index 05c5a7e867bb..4ba713d0d9b0 100644
+--- a/drivers/md/bcache/super.c
++++ b/drivers/md/bcache/super.c
+@@ -2160,21 +2160,6 @@ static const char *register_cache_set(struct cache *ca)
+ sysfs_create_link(&c->kobj, &ca->kobj, buf))
+ goto err;
+
+- /*
+- * A special case is both ca->sb.seq and c->sb.seq are 0,
+- * such condition happens on a new created cache device whose
+- * super block is never flushed yet. In this case c->sb.version
+- * and other members should be updated too, otherwise we will
+- * have a mistaken super block version in cache set.
+- */
+- if (ca->sb.seq > c->sb.seq || c->sb.seq == 0) {
+- c->sb.version = ca->sb.version;
+- memcpy(c->set_uuid, ca->sb.set_uuid, 16);
+- c->sb.flags = ca->sb.flags;
+- c->sb.seq = ca->sb.seq;
+- pr_debug("set version = %llu\n", c->sb.version);
+- }
+-
+ kobject_get(&ca->kobj);
+ ca->set = c;
+ ca->set->cache = ca;
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0011-bcache-remove-can_attach_cache.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0011-bcache-remove-can_attach_cache.patch
new file mode 100644
index 0000000..534bd35
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0011-bcache-remove-can_attach_cache.patch
@@ -0,0 +1,49 @@
+From 5b2afd6c546f00c7debacfadbeb994450c03ae20 Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 23:36:56 +0800
+Subject: [PATCH 11/14] bcache: remove can_attach_cache()
+
+After removing the embedded struct cache_sb from struct cache_set, cache
+set will directly reference the in-memory super block of struct cache.
+It is unnecessary to compare block_size, bucket_size and nr_in_set from
+the identical in-memory super block in can_attach_cache().
+
+This is a preparation patch for latter removing cache_set->sb from
+struct cache_set.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/super.c | 10 ----------
+ 1 file changed, 10 deletions(-)
+
+diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
+index 4ba713d0d9b0..a2bba78d78e6 100644
+--- a/drivers/md/bcache/super.c
++++ b/drivers/md/bcache/super.c
+@@ -2112,13 +2112,6 @@ static int run_cache_set(struct cache_set *c)
+ return -EIO;
+ }
+
+-static bool can_attach_cache(struct cache *ca, struct cache_set *c)
+-{
+- return ca->sb.block_size == c->sb.block_size &&
+- ca->sb.bucket_size == c->sb.bucket_size &&
+- ca->sb.nr_in_set == c->sb.nr_in_set;
+-}
+-
+ static const char *register_cache_set(struct cache *ca)
+ {
+ char buf[12];
+@@ -2130,9 +2123,6 @@ static const char *register_cache_set(struct cache *ca)
+ if (c->cache)
+ return "duplicate cache set member";
+
+- if (!can_attach_cache(ca, c))
+- return "cache sb does not match set";
+-
+ if (!CACHE_SYNC(&ca->sb))
+ SET_CACHE_SYNC(&c->sb, false);
+
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0012-bcache-check-and-set-sync-status-on-cache-s-in-memor.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0012-bcache-check-and-set-sync-status-on-cache-s-in-memor.patch
new file mode 100644
index 0000000..ea5c2c2
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0012-bcache-check-and-set-sync-status-on-cache-s-in-memor.patch
@@ -0,0 +1,109 @@
+From e099c198deb5d1f2d57e2cd6f29005848260e639 Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Fri, 14 Aug 2020 23:53:52 +0800
+Subject: [PATCH 12/14] bcache: check and set sync status on cache's in-memory
+ super block
+
+Currently the cache's sync status is checked and set on cache set's in-
+memory partial super block. After removing the embedded struct cache_sb
+from cache set and reference cache's in-memory super block from struct
+cache_set, the sync status can set and check directly on cache's super
+block.
+
+This patch checks and sets the cache sync status directly on cache's
+in-memory super block. This is a preparation for later removing embedded
+struct cache_sb from struct cache_set.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/alloc.c | 2 +-
+ drivers/md/bcache/journal.c | 2 +-
+ drivers/md/bcache/super.c | 7 ++-----
+ drivers/md/bcache/sysfs.c | 6 +++---
+ 4 files changed, 7 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c
+index 1b8310992dd0..65fdbdeb5134 100644
+--- a/drivers/md/bcache/alloc.c
++++ b/drivers/md/bcache/alloc.c
+@@ -361,7 +361,7 @@ static int bch_allocator_thread(void *arg)
+ * new stuff to them:
+ */
+ allocator_wait(ca, !atomic_read(&ca->set->prio_blocked));
+- if (CACHE_SYNC(&ca->set->sb)) {
++ if (CACHE_SYNC(&ca->sb)) {
+ /*
+ * This could deadlock if an allocation with a btree
+ * node locked ever blocked - having the btree node
+diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c
+index ccd5de0ab0fe..e2810668ede3 100644
+--- a/drivers/md/bcache/journal.c
++++ b/drivers/md/bcache/journal.c
+@@ -915,7 +915,7 @@ atomic_t *bch_journal(struct cache_set *c,
+ if (unlikely(test_bit(CACHE_SET_IO_DISABLE, &c->flags)))
+ return NULL;
+
+- if (!CACHE_SYNC(&c->sb))
++ if (!CACHE_SYNC(&c->cache->sb))
+ return NULL;
+
+ w = journal_wait_for_write(c, bch_keylist_nkeys(keys));
+diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
+index a2bba78d78e6..68563ee92393 100644
+--- a/drivers/md/bcache/super.c
++++ b/drivers/md/bcache/super.c
+@@ -1954,7 +1954,7 @@ static int run_cache_set(struct cache_set *c)
+ c->nbuckets = ca->sb.nbuckets;
+ set_gc_sectors(c);
+
+- if (CACHE_SYNC(&c->sb)) {
++ if (CACHE_SYNC(&c->cache->sb)) {
+ struct bkey *k;
+ struct jset *j;
+
+@@ -2077,7 +2077,7 @@ static int run_cache_set(struct cache_set *c)
+ * everything is set up - fortunately journal entries won't be
+ * written until the SET_CACHE_SYNC() here:
+ */
+- SET_CACHE_SYNC(&c->sb, true);
++ SET_CACHE_SYNC(&c->cache->sb, true);
+
+ bch_journal_next(&c->journal);
+ bch_journal_meta(c, &cl);
+@@ -2123,9 +2123,6 @@ static const char *register_cache_set(struct cache *ca)
+ if (c->cache)
+ return "duplicate cache set member";
+
+- if (!CACHE_SYNC(&ca->sb))
+- SET_CACHE_SYNC(&c->sb, false);
+-
+ goto found;
+ }
+
+diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
+index 4bfe98faadcc..554e3afc9b68 100644
+--- a/drivers/md/bcache/sysfs.c
++++ b/drivers/md/bcache/sysfs.c
+@@ -711,7 +711,7 @@ SHOW(__bch_cache_set)
+ {
+ struct cache_set *c = container_of(kobj, struct cache_set, kobj);
+
+- sysfs_print(synchronous, CACHE_SYNC(&c->sb));
++ sysfs_print(synchronous, CACHE_SYNC(&c->cache->sb));
+ sysfs_print(journal_delay_ms, c->journal_delay_ms);
+ sysfs_hprint(bucket_size, bucket_bytes(c->cache));
+ sysfs_hprint(block_size, block_bytes(c->cache));
+@@ -812,8 +812,8 @@ STORE(__bch_cache_set)
+ if (attr == &sysfs_synchronous) {
+ bool sync = strtoul_or_return(buf);
+
+- if (sync != CACHE_SYNC(&c->sb)) {
+- SET_CACHE_SYNC(&c->sb, sync);
++ if (sync != CACHE_SYNC(&c->cache->sb)) {
++ SET_CACHE_SYNC(&c->cache->sb, sync);
+ bcache_write_super(c);
+ }
+ }
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0013-bcache-remove-embedded-struct-cache_sb-from-struct-c.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0013-bcache-remove-embedded-struct-cache_sb-from-struct-c.patch
new file mode 100644
index 0000000..b31995b
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0013-bcache-remove-embedded-struct-cache_sb-from-struct-c.patch
@@ -0,0 +1,415 @@
+From a2d4d81f595e1dd4d35e34823778a31d57b745f8 Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Sat, 15 Aug 2020 00:20:00 +0800
+Subject: [PATCH 13/14] bcache: remove embedded struct cache_sb from struct
+ cache_set
+
+Since bcache code was merged into mainline kerrnel, each cache set only
+as one single cache in it. The multiple caches framework is here but the
+code is far from completed. Considering the multiple copies of cached
+data can also be stored on e.g. md raid1 devices, it is unnecessary to
+support multiple caches in one cache set indeed.
+
+The previous preparation patches fix the dependencies of explicitly
+making a cache set only have single cache. Now we don't have to maintain
+an embedded partial super block in struct cache_set, the in-memory super
+block can be directly referenced from struct cache.
+
+This patch removes the embedded struct cache_sb from struct cache_set,
+and fixes all locations where the superb lock was referenced from this
+removed super block by referencing the in-memory super block of struct
+cache.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/alloc.c | 6 +++---
+ drivers/md/bcache/bcache.h | 4 +---
+ drivers/md/bcache/btree.c | 17 +++++++++--------
+ drivers/md/bcache/btree.h | 2 +-
+ drivers/md/bcache/extents.c | 6 +++---
+ drivers/md/bcache/features.c | 4 ++--
+ drivers/md/bcache/io.c | 2 +-
+ drivers/md/bcache/journal.c | 11 ++++++-----
+ drivers/md/bcache/request.c | 4 ++--
+ drivers/md/bcache/super.c | 19 +++++++++----------
+ drivers/md/bcache/writeback.c | 2 +-
+ 11 files changed, 38 insertions(+), 39 deletions(-)
+
+diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c
+index 65fdbdeb5134..8c371d5eef8e 100644
+--- a/drivers/md/bcache/alloc.c
++++ b/drivers/md/bcache/alloc.c
+@@ -87,7 +87,7 @@ void bch_rescale_priorities(struct cache_set *c, int sectors)
+ {
+ struct cache *ca;
+ struct bucket *b;
+- unsigned long next = c->nbuckets * c->sb.bucket_size / 1024;
++ unsigned long next = c->nbuckets * c->cache->sb.bucket_size / 1024;
+ int r;
+
+ atomic_sub(sectors, &c->rescale);
+@@ -583,7 +583,7 @@ static struct open_bucket *pick_data_bucket(struct cache_set *c,
+ struct open_bucket, list);
+ found:
+ if (!ret->sectors_free && KEY_PTRS(alloc)) {
+- ret->sectors_free = c->sb.bucket_size;
++ ret->sectors_free = c->cache->sb.bucket_size;
+ bkey_copy(&ret->key, alloc);
+ bkey_init(alloc);
+ }
+@@ -677,7 +677,7 @@ bool bch_alloc_sectors(struct cache_set *c,
+ &PTR_CACHE(c, &b->key, i)->sectors_written);
+ }
+
+- if (b->sectors_free < c->sb.block_size)
++ if (b->sectors_free < c->cache->sb.block_size)
+ b->sectors_free = 0;
+
+ /*
+diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
+index 94d4baf4c405..1d57f48307e6 100644
+--- a/drivers/md/bcache/bcache.h
++++ b/drivers/md/bcache/bcache.h
+@@ -517,8 +517,6 @@ struct cache_set {
+ atomic_t idle_counter;
+ atomic_t at_max_writeback_rate;
+
+- struct cache_sb sb;
+-
+ struct cache *cache;
+
+ struct bcache_device **devices;
+@@ -799,7 +797,7 @@ static inline sector_t bucket_to_sector(struct cache_set *c, size_t b)
+
+ static inline sector_t bucket_remainder(struct cache_set *c, sector_t s)
+ {
+- return s & (c->sb.bucket_size - 1);
++ return s & (c->cache->sb.bucket_size - 1);
+ }
+
+ static inline struct cache *PTR_CACHE(struct cache_set *c,
+diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
+index c91b4d58a5b3..d09103cc7da5 100644
+--- a/drivers/md/bcache/btree.c
++++ b/drivers/md/bcache/btree.c
+@@ -117,7 +117,7 @@ static void bch_btree_init_next(struct btree *b)
+
+ if (b->written < btree_blocks(b))
+ bch_bset_init_next(&b->keys, write_block(b),
+- bset_magic(&b->c->sb));
++ bset_magic(&b->c->cache->sb));
+
+ }
+
+@@ -155,7 +155,7 @@ void bch_btree_node_read_done(struct btree *b)
+ * See the comment arount cache_set->fill_iter.
+ */
+ iter = mempool_alloc(&b->c->fill_iter, GFP_NOIO);
+- iter->size = b->c->sb.bucket_size / b->c->sb.block_size;
++ iter->size = b->c->cache->sb.bucket_size / b->c->cache->sb.block_size;
+ iter->used = 0;
+
+ #ifdef CONFIG_BCACHE_DEBUG
+@@ -178,7 +178,7 @@ void bch_btree_node_read_done(struct btree *b)
+ goto err;
+
+ err = "bad magic";
+- if (i->magic != bset_magic(&b->c->sb))
++ if (i->magic != bset_magic(&b->c->cache->sb))
+ goto err;
+
+ err = "bad checksum";
+@@ -219,7 +219,7 @@ void bch_btree_node_read_done(struct btree *b)
+
+ if (b->written < btree_blocks(b))
+ bch_bset_init_next(&b->keys, write_block(b),
+- bset_magic(&b->c->sb));
++ bset_magic(&b->c->cache->sb));
+ out:
+ mempool_free(iter, &b->c->fill_iter);
+ return;
+@@ -423,7 +423,7 @@ void __bch_btree_node_write(struct btree *b, struct closure *parent)
+
+ do_btree_node_write(b);
+
+- atomic_long_add(set_blocks(i, block_bytes(b->c->cache)) * b->c->sb.block_size,
++ atomic_long_add(set_blocks(i, block_bytes(b->c->cache)) * b->c->cache->sb.block_size,
+ &PTR_CACHE(b->c, &b->key, 0)->btree_sectors_written);
+
+ b->written += set_blocks(i, block_bytes(b->c->cache));
+@@ -738,7 +738,7 @@ void bch_btree_cache_free(struct cache_set *c)
+ if (c->verify_data)
+ list_move(&c->verify_data->list, &c->btree_cache);
+
+- free_pages((unsigned long) c->verify_ondisk, ilog2(meta_bucket_pages(&c->sb)));
++ free_pages((unsigned long) c->verify_ondisk, ilog2(meta_bucket_pages(&c->cache->sb)));
+ #endif
+
+ list_splice(&c->btree_cache_freeable,
+@@ -785,7 +785,8 @@ int bch_btree_cache_alloc(struct cache_set *c)
+ mutex_init(&c->verify_lock);
+
+ c->verify_ondisk = (void *)
+- __get_free_pages(GFP_KERNEL|__GFP_COMP, ilog2(meta_bucket_pages(&c->sb)));
++ __get_free_pages(GFP_KERNEL|__GFP_COMP,
++ ilog2(meta_bucket_pages(&c->cache->sb)));
+ if (!c->verify_ondisk) {
+ /*
+ * Don't worry about the mca_rereserve buckets
+@@ -1108,7 +1109,7 @@ struct btree *__bch_btree_node_alloc(struct cache_set *c, struct btree_op *op,
+ }
+
+ b->parent = parent;
+- bch_bset_init_next(&b->keys, b->keys.set->data, bset_magic(&b->c->sb));
++ bch_bset_init_next(&b->keys, b->keys.set->data, bset_magic(&b->c->cache->sb));
+
+ mutex_unlock(&c->bucket_lock);
+
+diff --git a/drivers/md/bcache/btree.h b/drivers/md/bcache/btree.h
+index 257969980c49..50482107134f 100644
+--- a/drivers/md/bcache/btree.h
++++ b/drivers/md/bcache/btree.h
+@@ -194,7 +194,7 @@ static inline unsigned int bset_block_offset(struct btree *b, struct bset *i)
+
+ static inline void set_gc_sectors(struct cache_set *c)
+ {
+- atomic_set(&c->sectors_to_gc, c->sb.bucket_size * c->nbuckets / 16);
++ atomic_set(&c->sectors_to_gc, c->cache->sb.bucket_size * c->nbuckets / 16);
+ }
+
+ void bkey_put(struct cache_set *c, struct bkey *k);
+diff --git a/drivers/md/bcache/extents.c b/drivers/md/bcache/extents.c
+index 9162af5bb6ec..f4658a1f37b8 100644
+--- a/drivers/md/bcache/extents.c
++++ b/drivers/md/bcache/extents.c
+@@ -54,7 +54,7 @@ static bool __ptr_invalid(struct cache_set *c, const struct bkey *k)
+ size_t bucket = PTR_BUCKET_NR(c, k, i);
+ size_t r = bucket_remainder(c, PTR_OFFSET(k, i));
+
+- if (KEY_SIZE(k) + r > c->sb.bucket_size ||
++ if (KEY_SIZE(k) + r > c->cache->sb.bucket_size ||
+ bucket < ca->sb.first_bucket ||
+ bucket >= ca->sb.nbuckets)
+ return true;
+@@ -75,7 +75,7 @@ static const char *bch_ptr_status(struct cache_set *c, const struct bkey *k)
+ size_t bucket = PTR_BUCKET_NR(c, k, i);
+ size_t r = bucket_remainder(c, PTR_OFFSET(k, i));
+
+- if (KEY_SIZE(k) + r > c->sb.bucket_size)
++ if (KEY_SIZE(k) + r > c->cache->sb.bucket_size)
+ return "bad, length too big";
+ if (bucket < ca->sb.first_bucket)
+ return "bad, short offset";
+@@ -136,7 +136,7 @@ static void bch_bkey_dump(struct btree_keys *keys, const struct bkey *k)
+ size_t n = PTR_BUCKET_NR(b->c, k, j);
+
+ pr_cont(" bucket %zu", n);
+- if (n >= b->c->sb.first_bucket && n < b->c->sb.nbuckets)
++ if (n >= b->c->cache->sb.first_bucket && n < b->c->cache->sb.nbuckets)
+ pr_cont(" prio %i",
+ PTR_BUCKET(b->c, k, j)->prio);
+ }
+diff --git a/drivers/md/bcache/features.c b/drivers/md/bcache/features.c
+index 4442df48d28c..6469223f0b77 100644
+--- a/drivers/md/bcache/features.c
++++ b/drivers/md/bcache/features.c
+@@ -30,7 +30,7 @@ static struct feature feature_list[] = {
+ for (f = &feature_list[0]; f->compat != 0; f++) { \
+ if (f->compat != BCH_FEATURE_ ## type) \
+ continue; \
+- if (BCH_HAS_ ## type ## _FEATURE(&c->sb, f->mask)) { \
++ if (BCH_HAS_ ## type ## _FEATURE(&c->cache->sb, f->mask)) { \
+ if (first) { \
+ out += snprintf(out, buf + size - out, \
+ "["); \
+@@ -44,7 +44,7 @@ static struct feature feature_list[] = {
+ \
+ out += snprintf(out, buf + size - out, "%s", f->string);\
+ \
+- if (BCH_HAS_ ## type ## _FEATURE(&c->sb, f->mask)) \
++ if (BCH_HAS_ ## type ## _FEATURE(&c->cache->sb, f->mask)) \
+ out += snprintf(out, buf + size - out, "]"); \
+ \
+ first = false; \
+diff --git a/drivers/md/bcache/io.c b/drivers/md/bcache/io.c
+index a14a445618b4..dad71a6b7889 100644
+--- a/drivers/md/bcache/io.c
++++ b/drivers/md/bcache/io.c
+@@ -26,7 +26,7 @@ struct bio *bch_bbio_alloc(struct cache_set *c)
+ struct bbio *b = mempool_alloc(&c->bio_meta, GFP_NOIO);
+ struct bio *bio = &b->bio;
+
+- bio_init(bio, bio->bi_inline_vecs, meta_bucket_pages(&c->sb));
++ bio_init(bio, bio->bi_inline_vecs, meta_bucket_pages(&c->cache->sb));
+
+ return bio;
+ }
+diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c
+index e2810668ede3..c5526e5087ef 100644
+--- a/drivers/md/bcache/journal.c
++++ b/drivers/md/bcache/journal.c
+@@ -666,7 +666,7 @@ static void journal_reclaim(struct cache_set *c)
+
+ bkey_init(k);
+ SET_KEY_PTRS(k, 1);
+- c->journal.blocks_free = c->sb.bucket_size >> c->block_bits;
++ c->journal.blocks_free = ca->sb.bucket_size >> c->block_bits;
+
+ out:
+ if (!journal_full(&c->journal))
+@@ -735,7 +735,7 @@ static void journal_write_unlocked(struct closure *cl)
+ struct journal_write *w = c->journal.cur;
+ struct bkey *k = &c->journal.key;
+ unsigned int i, sectors = set_blocks(w->data, block_bytes(ca)) *
+- c->sb.block_size;
++ ca->sb.block_size;
+
+ struct bio *bio;
+ struct bio_list list;
+@@ -762,7 +762,7 @@ static void journal_write_unlocked(struct closure *cl)
+ bkey_copy(&w->data->uuid_bucket, &c->uuid_bucket);
+
+ w->data->prio_bucket[ca->sb.nr_this_dev] = ca->prio_buckets[0];
+- w->data->magic = jset_magic(&c->sb);
++ w->data->magic = jset_magic(&ca->sb);
+ w->data->version = BCACHE_JSET_VERSION;
+ w->data->last_seq = last_seq(&c->journal);
+ w->data->csum = csum_set(w->data);
+@@ -838,6 +838,7 @@ static struct journal_write *journal_wait_for_write(struct cache_set *c,
+ size_t sectors;
+ struct closure cl;
+ bool wait = false;
++ struct cache *ca = c->cache;
+
+ closure_init_stack(&cl);
+
+@@ -847,10 +848,10 @@ static struct journal_write *journal_wait_for_write(struct cache_set *c,
+ struct journal_write *w = c->journal.cur;
+
+ sectors = __set_blocks(w->data, w->data->keys + nkeys,
+- block_bytes(c->cache)) * c->sb.block_size;
++ block_bytes(ca)) * ca->sb.block_size;
+
+ if (sectors <= min_t(size_t,
+- c->journal.blocks_free * c->sb.block_size,
++ c->journal.blocks_free * ca->sb.block_size,
+ PAGE_SECTORS << JSET_BITS))
+ return w;
+
+diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
+index 02408fdbf5bb..37e9cf8dbfc1 100644
+--- a/drivers/md/bcache/request.c
++++ b/drivers/md/bcache/request.c
+@@ -394,8 +394,8 @@ static bool check_should_bypass(struct cached_dev *dc, struct bio *bio)
+ goto skip;
+ }
+
+- if (bio->bi_iter.bi_sector & (c->sb.block_size - 1) ||
+- bio_sectors(bio) & (c->sb.block_size - 1)) {
++ if (bio->bi_iter.bi_sector & (c->cache->sb.block_size - 1) ||
++ bio_sectors(bio) & (c->cache->sb.block_size - 1)) {
+ pr_debug("skipping unaligned io\n");
+ goto skip;
+ }
+diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
+index 68563ee92393..698a0de9170f 100644
+--- a/drivers/md/bcache/super.c
++++ b/drivers/md/bcache/super.c
+@@ -471,7 +471,7 @@ static int __uuid_write(struct cache_set *c)
+ {
+ BKEY_PADDED(key) k;
+ struct closure cl;
+- struct cache *ca;
++ struct cache *ca = c->cache;
+ unsigned int size;
+
+ closure_init_stack(&cl);
+@@ -480,13 +480,12 @@ static int __uuid_write(struct cache_set *c)
+ if (bch_bucket_alloc_set(c, RESERVE_BTREE, &k.key, true))
+ return 1;
+
+- size = meta_bucket_pages(&c->sb) * PAGE_SECTORS;
++ size = meta_bucket_pages(&ca->sb) * PAGE_SECTORS;
+ SET_KEY_SIZE(&k.key, size);
+ uuid_io(c, REQ_OP_WRITE, 0, &k.key, &cl);
+ closure_sync(&cl);
+
+ /* Only one bucket used for uuid write */
+- ca = PTR_CACHE(c, &k.key, 0);
+ atomic_long_add(ca->sb.bucket_size, &ca->meta_sectors_written);
+
+ bkey_copy(&c->uuid_bucket, &k.key);
+@@ -1199,7 +1198,7 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c,
+ return -EINVAL;
+ }
+
+- if (dc->sb.block_size < c->sb.block_size) {
++ if (dc->sb.block_size < c->cache->sb.block_size) {
+ /* Will die */
+ pr_err("Couldn't attach %s: block size less than set's block size\n",
+ dc->backing_dev_name);
+@@ -1666,7 +1665,7 @@ static void cache_set_free(struct closure *cl)
+ }
+
+ bch_bset_sort_state_free(&c->sort);
+- free_pages((unsigned long) c->uuids, ilog2(meta_bucket_pages(&c->sb)));
++ free_pages((unsigned long) c->uuids, ilog2(meta_bucket_pages(&c->cache->sb)));
+
+ if (c->moving_gc_wq)
+ destroy_workqueue(c->moving_gc_wq);
+@@ -1857,10 +1856,10 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
+
+ c->bucket_bits = ilog2(sb->bucket_size);
+ c->block_bits = ilog2(sb->block_size);
+- c->nr_uuids = meta_bucket_bytes(&c->sb) / sizeof(struct uuid_entry);
++ c->nr_uuids = meta_bucket_bytes(sb) / sizeof(struct uuid_entry);
+ c->devices_max_used = 0;
+ atomic_set(&c->attached_dev_nr, 0);
+- c->btree_pages = meta_bucket_pages(&c->sb);
++ c->btree_pages = meta_bucket_pages(sb);
+ if (c->btree_pages > BTREE_MAX_PAGES)
+ c->btree_pages = max_t(int, c->btree_pages / 4,
+ BTREE_MAX_PAGES);
+@@ -1898,7 +1897,7 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
+
+ if (mempool_init_kmalloc_pool(&c->bio_meta, 2,
+ sizeof(struct bbio) +
+- sizeof(struct bio_vec) * meta_bucket_pages(&c->sb)))
++ sizeof(struct bio_vec) * meta_bucket_pages(sb)))
+ goto err;
+
+ if (mempool_init_kmalloc_pool(&c->fill_iter, 1, iter_size))
+@@ -1908,7 +1907,7 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
+ BIOSET_NEED_BVECS|BIOSET_NEED_RESCUER))
+ goto err;
+
+- c->uuids = alloc_meta_bucket_pages(GFP_KERNEL, &c->sb);
++ c->uuids = alloc_meta_bucket_pages(GFP_KERNEL, sb);
+ if (!c->uuids)
+ goto err;
+
+@@ -2088,7 +2087,7 @@ static int run_cache_set(struct cache_set *c)
+ goto err;
+
+ closure_sync(&cl);
+- c->sb.last_mount = (u32)ktime_get_real_seconds();
++ c->cache->sb.last_mount = (u32)ktime_get_real_seconds();
+ bcache_write_super(c);
+
+ list_for_each_entry_safe(dc, t, &uncached_devices, list)
+diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c
+index 4f4ad6b3d43a..3c74996978da 100644
+--- a/drivers/md/bcache/writeback.c
++++ b/drivers/md/bcache/writeback.c
+@@ -35,7 +35,7 @@ static uint64_t __calc_target_rate(struct cached_dev *dc)
+ * This is the size of the cache, minus the amount used for
+ * flash-only devices
+ */
+- uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size -
++ uint64_t cache_sectors = c->nbuckets * c->cache->sb.bucket_size -
+ atomic_long_read(&c->flash_dev_dirty_sectors);
+
+ /*
+--
+2.26.2
+
diff --git a/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0014-bcache-move-struct-cache_sb-out-of-uapi-bcache.h.patch b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0014-bcache-move-struct-cache_sb-out-of-uapi-bcache.h.patch
new file mode 100644
index 0000000..7a9e853
--- /dev/null
+++ b/for-test/remove-multiple-cache-devices/single-cache-in-cache-set/0014-bcache-move-struct-cache_sb-out-of-uapi-bcache.h.patch
@@ -0,0 +1,261 @@
+From 3821a8cd1196fdf155605f0747b1a9d4bf6d8cff Mon Sep 17 00:00:00 2001
+From: Coly Li <colyli@suse.de>
+Date: Sat, 15 Aug 2020 00:49:17 +0800
+Subject: [PATCH 14/14] bcache: move struct cache_sb out of uapi bcache.h
+
+struct cache_sb does not exactly map to cache_sb_disk, it is only for
+in-memory super block and dosn't belong to uapi bcache.h.
+
+This patch moves the struct cache_sb definition and other depending
+macros and inline routines from include/uapi/linux/bcache.h to
+drivers/md/bcache/bcache.h, this is the proper location to have them.
+
+Signed-off-by: Coly Li <colyli@suse.de>
+---
+ drivers/md/bcache/bcache.h | 99 +++++++++++++++++++++++++++++++++++++
+ include/uapi/linux/bcache.h | 98 ------------------------------------
+ 2 files changed, 99 insertions(+), 98 deletions(-)
+
+diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
+index 1d57f48307e6..b755bf7832ac 100644
+--- a/drivers/md/bcache/bcache.h
++++ b/drivers/md/bcache/bcache.h
+@@ -279,6 +279,82 @@ struct bcache_device {
+ unsigned int cmd, unsigned long arg);
+ };
+
++/*
++ * This is for in-memory bcache super block.
++ * NOTE: cache_sb is NOT exactly mapping to cache_sb_disk, the member
++ * size, ordering and even whole struct size may be different
++ * from cache_sb_disk.
++ */
++struct cache_sb {
++ __u64 offset; /* sector where this sb was written */
++ __u64 version;
++
++ __u8 magic[16];
++
++ __u8 uuid[16];
++ union {
++ __u8 set_uuid[16];
++ __u64 set_magic;
++ };
++ __u8 label[SB_LABEL_SIZE];
++
++ __u64 flags;
++ __u64 seq;
++
++ __u64 feature_compat;
++ __u64 feature_incompat;
++ __u64 feature_ro_compat;
++
++ union {
++ struct {
++ /* Cache devices */
++ __u64 nbuckets; /* device size */
++
++ __u16 block_size; /* sectors */
++ __u16 nr_in_set;
++ __u16 nr_this_dev;
++ __u32 bucket_size; /* sectors */
++ };
++ struct {
++ /* Backing devices */
++ __u64 data_offset;
++
++ /*
++ * block_size from the cache device section is still used by
++ * backing devices, so don't add anything here until we fix
++ * things to not need it for backing devices anymore
++ */
++ };
++ };
++
++ __u32 last_mount; /* time overflow in y2106 */
++
++ __u16 first_bucket;
++ union {
++ __u16 njournal_buckets;
++ __u16 keys;
++ };
++ __u64 d[SB_JOURNAL_BUCKETS]; /* journal buckets */
++};
++
++BITMASK(CACHE_SYNC, struct cache_sb, flags, 0, 1);
++BITMASK(CACHE_DISCARD, struct cache_sb, flags, 1, 1);
++BITMASK(CACHE_REPLACEMENT, struct cache_sb, flags, 2, 3);
++#define CACHE_REPLACEMENT_LRU 0U
++#define CACHE_REPLACEMENT_FIFO 1U
++#define CACHE_REPLACEMENT_RANDOM 2U
++
++BITMASK(BDEV_CACHE_MODE, struct cache_sb, flags, 0, 4);
++#define CACHE_MODE_WRITETHROUGH 0U
++#define CACHE_MODE_WRITEBACK 1U
++#define CACHE_MODE_WRITEAROUND 2U
++#define CACHE_MODE_NONE 3U
++BITMASK(BDEV_STATE, struct cache_sb, flags, 61, 2);
++#define BDEV_STATE_NONE 0U
++#define BDEV_STATE_CLEAN 1U
++#define BDEV_STATE_DIRTY 2U
++#define BDEV_STATE_STALE 3U
++
+ struct io {
+ /* Used to track sequential IO so it can be skipped */
+ struct hlist_node hash;
+@@ -840,6 +916,13 @@ static inline bool ptr_available(struct cache_set *c, const struct bkey *k,
+ return (PTR_DEV(k, i) < MAX_CACHES_PER_SET) && PTR_CACHE(c, k, i);
+ }
+
++static inline _Bool SB_IS_BDEV(const struct cache_sb *sb)
++{
++ return sb->version == BCACHE_SB_VERSION_BDEV
++ || sb->version == BCACHE_SB_VERSION_BDEV_WITH_OFFSET
++ || sb->version == BCACHE_SB_VERSION_BDEV_WITH_FEATURES;
++}
++
+ /* Btree key macros */
+
+ /*
+@@ -958,6 +1041,22 @@ static inline void wait_for_kthread_stop(void)
+ }
+ }
+
++/* generate magic number */
++static inline __u64 jset_magic(struct cache_sb *sb)
++{
++ return sb->set_magic ^ JSET_MAGIC;
++}
++
++static inline __u64 pset_magic(struct cache_sb *sb)
++{
++ return sb->set_magic ^ PSET_MAGIC;
++}
++
++static inline __u64 bset_magic(struct cache_sb *sb)
++{
++ return sb->set_magic ^ BSET_MAGIC;
++}
++
+ /* Forward declarations */
+
+ void bch_count_backing_io_errors(struct cached_dev *dc, struct bio *bio);
+diff --git a/include/uapi/linux/bcache.h b/include/uapi/linux/bcache.h
+index 52e8bcb33981..18166a3d8503 100644
+--- a/include/uapi/linux/bcache.h
++++ b/include/uapi/linux/bcache.h
+@@ -216,89 +216,6 @@ struct cache_sb_disk {
+ __le16 bucket_size_hi;
+ };
+
+-/*
+- * This is for in-memory bcache super block.
+- * NOTE: cache_sb is NOT exactly mapping to cache_sb_disk, the member
+- * size, ordering and even whole struct size may be different
+- * from cache_sb_disk.
+- */
+-struct cache_sb {
+- __u64 offset; /* sector where this sb was written */
+- __u64 version;
+-
+- __u8 magic[16];
+-
+- __u8 uuid[16];
+- union {
+- __u8 set_uuid[16];
+- __u64 set_magic;
+- };
+- __u8 label[SB_LABEL_SIZE];
+-
+- __u64 flags;
+- __u64 seq;
+-
+- __u64 feature_compat;
+- __u64 feature_incompat;
+- __u64 feature_ro_compat;
+-
+- union {
+- struct {
+- /* Cache devices */
+- __u64 nbuckets; /* device size */
+-
+- __u16 block_size; /* sectors */
+- __u16 nr_in_set;
+- __u16 nr_this_dev;
+- __u32 bucket_size; /* sectors */
+- };
+- struct {
+- /* Backing devices */
+- __u64 data_offset;
+-
+- /*
+- * block_size from the cache device section is still used by
+- * backing devices, so don't add anything here until we fix
+- * things to not need it for backing devices anymore
+- */
+- };
+- };
+-
+- __u32 last_mount; /* time overflow in y2106 */
+-
+- __u16 first_bucket;
+- union {
+- __u16 njournal_buckets;
+- __u16 keys;
+- };
+- __u64 d[SB_JOURNAL_BUCKETS]; /* journal buckets */
+-};
+-
+-static inline _Bool SB_IS_BDEV(const struct cache_sb *sb)
+-{
+- return sb->version == BCACHE_SB_VERSION_BDEV
+- || sb->version == BCACHE_SB_VERSION_BDEV_WITH_OFFSET
+- || sb->version == BCACHE_SB_VERSION_BDEV_WITH_FEATURES;
+-}
+-
+-BITMASK(CACHE_SYNC, struct cache_sb, flags, 0, 1);
+-BITMASK(CACHE_DISCARD, struct cache_sb, flags, 1, 1);
+-BITMASK(CACHE_REPLACEMENT, struct cache_sb, flags, 2, 3);
+-#define CACHE_REPLACEMENT_LRU 0U
+-#define CACHE_REPLACEMENT_FIFO 1U
+-#define CACHE_REPLACEMENT_RANDOM 2U
+-
+-BITMASK(BDEV_CACHE_MODE, struct cache_sb, flags, 0, 4);
+-#define CACHE_MODE_WRITETHROUGH 0U
+-#define CACHE_MODE_WRITEBACK 1U
+-#define CACHE_MODE_WRITEAROUND 2U
+-#define CACHE_MODE_NONE 3U
+-BITMASK(BDEV_STATE, struct cache_sb, flags, 61, 2);
+-#define BDEV_STATE_NONE 0U
+-#define BDEV_STATE_CLEAN 1U
+-#define BDEV_STATE_DIRTY 2U
+-#define BDEV_STATE_STALE 3U
+-
+ /*
+ * Magic numbers
+ *
+@@ -310,21 +227,6 @@ BITMASK(BDEV_STATE, struct cache_sb, flags, 61, 2);
+ #define PSET_MAGIC 0x6750e15f87337f91ULL
+ #define BSET_MAGIC 0x90135c78b99e07f5ULL
+
+-static inline __u64 jset_magic(struct cache_sb *sb)
+-{
+- return sb->set_magic ^ JSET_MAGIC;
+-}
+-
+-static inline __u64 pset_magic(struct cache_sb *sb)
+-{
+- return sb->set_magic ^ PSET_MAGIC;
+-}
+-
+-static inline __u64 bset_magic(struct cache_sb *sb)
+-{
+- return sb->set_magic ^ BSET_MAGIC;
+-}
+-
+ /*
+ * Journal
+ *
+--
+2.26.2
+