Hi Jens,
This is the first wave bcache patches for Linux v5.12.
It is nice to see in this round we have 3 new patch contributors:
Jianpeng Ma, Qiaowei Ren and Kai Krakow.
In this series, the EXPERIMENTAL patches from Jianpeng Ma, Qiaowei Ren
and me are initial effort to store bcache meta-data on NVDIMM namespace.
The NVDIMM space is managed and mapped via DAX interface, and accessed
by linear address. In this submission we store bcache journal on NVDIMM,
in future bcache btree nodes and other meta data will be added in too,
before we remove the EXPERIMENTAL statues.
Dongdong Tao contributes a performance optimization when
bcache cache buckets are highly fregmented, Dongdong's patch makes the
dirty data writeback faster and from his benchmark reprots such changes
have recognized improvement for randome write I/O thoughput and latency
for highly fregmented buckets, and no regression for regular I/O
observed.
Kai Krakow contributes 4 patches to offload system_wq usage to separated
btree_io_wq and bch_flush_wq. In his environment the daily backup job
throughput increases from 60.2MB/s to 419MB/s and accomplished time
reduced from 14h29m to 2h13m.
Joe Perches also contributes a fine code stype fix which I pick for this
submission.
Please take them for Linux v5.12 merge window.
Thank you in advance.
Coly Li
---
Coly Li (8):
bcache: add initial data structures for nvm pages
bcache: use bucket index for SET_GC_MARK() in bch_btree_gc_finish()
bcache: add BCH_FEATURE_INCOMPAT_NVDIMM_META into incompat feature set
bcache: initialize bcache journal for NVDIMM meta device
bcache: support storing bcache journal into NVDIMM meta device
bcache: read jset from NVDIMM pages for journal replay
bcache: add sysfs interface register_nvdimm_meta to register NVDIMM
meta device
bcache: only initialize nvm-pages allocator when
CONFIG_BCACHE_NVM_PAGES configured
Jianpeng Ma (6):
bcache: initialize the nvm pages allocator
bcache: initialization of the buddy
bcache: bch_nvm_alloc_pages() of the buddy
bcache: bch_nvm_free_pages() of the buddy
bcache: get allocated pages from specific owner
bcache: persist owner info when alloc/free pages.
Joe Perches (1):
bcache: Avoid comma separated statements
Kai Krakow (4):
bcache: Fix register_device_aync typo
Revert "bcache: Kill btree_io_wq"
bcache: Give btree_io_wq correct semantics again
bcache: Move journal work to new flush wq
dongdong tao (1):
bcache: consider the fragmentation when update the writeback rate
drivers/md/bcache/Kconfig | 6 +
drivers/md/bcache/Makefile | 2 +-
drivers/md/bcache/bcache.h | 7 +
drivers/md/bcache/bset.c | 12 +-
drivers/md/bcache/btree.c | 27 +-
drivers/md/bcache/features.h | 9 +
drivers/md/bcache/journal.c | 293 ++++++++---
drivers/md/bcache/journal.h | 2 +-
drivers/md/bcache/nvm-pages.c | 853 ++++++++++++++++++++++++++++++++
drivers/md/bcache/nvm-pages.h | 112 +++++
drivers/md/bcache/super.c | 76 ++-
drivers/md/bcache/sysfs.c | 29 +-
drivers/md/bcache/writeback.c | 42 ++
drivers/md/bcache/writeback.h | 4 +
include/uapi/linux/bcache-nvm.h | 188 +++++++
15 files changed, 1579 insertions(+), 83 deletions(-)
create mode 100644 drivers/md/bcache/nvm-pages.c
create mode 100644 drivers/md/bcache/nvm-pages.h
create mode 100644 include/uapi/linux/bcache-nvm.h
--
2.26.2
@@ -2611,7 +2611,7 @@ static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr,args->sb=sb;args->sb_disk=sb_disk;args->bdev=bdev;-register_device_aync(args);+register_device_async(args);/* No wait and returns to user space */gotoasync_done;}
From: dongdong tao <redacted>
Current way to calculate the writeback rate only considered the
dirty sectors, this usually works fine when the fragmentation
is not high, but it will give us unreasonable small rate when
we are under a situation that very few dirty sectors consumed
a lot dirty buckets. In some case, the dirty bucekts can reached
to CUTOFF_WRITEBACK_SYNC while the dirty data(sectors) not even
reached the writeback_percent, the writeback rate will still
be the minimum value (4k), thus it will cause all the writes to be
stucked in a non-writeback mode because of the slow writeback.
We accelerate the rate in 3 stages with different aggressiveness,
the first stage starts when dirty buckets percent reach above
BCH_WRITEBACK_FRAGMENT_THRESHOLD_LOW (50), the second is
BCH_WRITEBACK_FRAGMENT_THRESHOLD_MID (57), the third is
BCH_WRITEBACK_FRAGMENT_THRESHOLD_HIGH (64). By default
the first stage tries to writeback the amount of dirty data
in one bucket (on average) in (1 / (dirty_buckets_percent - 50)) second,
the second stage tries to writeback the amount of dirty data in one bucket
in (1 / (dirty_buckets_percent - 57)) * 100 millisecond, the third
stage tries to writeback the amount of dirty data in one bucket in
(1 / (dirty_buckets_percent - 64)) millisecond.
the initial rate at each stage can be controlled by 3 configurable
parameters writeback_rate_fp_term_{low|mid|high}, they are by default
1, 10, 1000, the hint of IO throughput that these values are trying
to achieve is described by above paragraph, the reason that
I choose those value as default is based on the testing and the
production data, below is some details:
A. When it comes to the low stage, there is still a bit far from the 70
threshold, so we only want to give it a little bit push by setting the
term to 1, it means the initial rate will be 170 if the fragment is 6,
it is calculated by bucket_size/fragment, this rate is very small,
but still much reasonable than the minimum 8.
For a production bcache with unheavy workload, if the cache device
is bigger than 1 TB, it may take hours to consume 1% buckets,
so it is very possible to reclaim enough dirty buckets in this stage,
thus to avoid entering the next stage.
B. If the dirty buckets ratio didn't turn around during the first stage,
it comes to the mid stage, then it is necessary for mid stage
to be more aggressive than low stage, so i choose the initial rate
to be 10 times more than low stage, that means 1700 as the initial
rate if the fragment is 6. This is some normal rate
we usually see for a normal workload when writeback happens
because of writeback_percent.
C. If the dirty buckets ratio didn't turn around during the low and mid
stages, it comes to the third stage, and it is the last chance that
we can turn around to avoid the horrible cutoff writeback sync issue,
then we choose 100 times more aggressive than the mid stage, that
means 170000 as the initial rate if the fragment is 6. This is also
inferred from a production bcache, I've got one week's writeback rate
data from a production bcache which has quite heavy workloads,
again, the writeback is triggered by the writeback percent,
the highest rate area is around 100000 to 240000, so I believe this
kind aggressiveness at this stage is reasonable for production.
And it should be mostly enough because the hint is trying to reclaim
1000 bucket per second, and from that heavy production env,
it is consuming 50 bucket per second on average in one week's data.
Option writeback_consider_fragment is to control whether we want
this feature to be on or off, it's on by default.
Lastly, below is the performance data for all the testing result,
including the data from production env:
https://docs.google.com/document/d/1AmbIEa_2MhB9bqhC3rfga9tp7n9YX9PLn0jSUxscVW0/edit?usp=sharing
Signed-off-by: dongdong tao <redacted>
Signed-off-by: Coly Li <redacted>
---
drivers/md/bcache/bcache.h | 4 ++++
drivers/md/bcache/sysfs.c | 23 +++++++++++++++++++
drivers/md/bcache/writeback.c | 42 +++++++++++++++++++++++++++++++++++
drivers/md/bcache/writeback.h | 4 ++++
4 files changed, 73 insertions(+)
@@ -88,6 +88,44 @@ static void __update_writeback_rate(struct cached_dev *dc)int64_tintegral_scaled;uint32_tnew_rate;+/*+*Weneedtoconsiderthenumberofdirtybucketsaswell+*whencalculatingtheproportional_scaled,Otherwisewemight+*haveanunreasonablesmallwritebackrateatahighlyfragmentedsituation+*whenveryfewdirtysectorsconsumedalotdirtybuckets,the+*worstcaseiswhendirtybucketsreachedcutoff_writeback_syncand+*dirtydataisstillnotevenreachedtowritebackpercent,sotherate+*stillwillbeattheminimumvalue,whichwillcausethewrite+*stuckatanon-writebackmode.+*/+structcache_set*c=dc->disk.c;++int64_tdirty_buckets=c->nbuckets-c->avail_nbuckets;++if(dc->writeback_consider_fragment&&+c->gc_stats.in_use>BCH_WRITEBACK_FRAGMENT_THRESHOLD_LOW&&dirty>0){+int64_tfragment=+div_s64((dirty_buckets*c->cache->sb.bucket_size),dirty);+int64_tfp_term;+int64_tfps;++if(c->gc_stats.in_use<=BCH_WRITEBACK_FRAGMENT_THRESHOLD_MID){+fp_term=dc->writeback_rate_fp_term_low*+(c->gc_stats.in_use-BCH_WRITEBACK_FRAGMENT_THRESHOLD_LOW);+}elseif(c->gc_stats.in_use<=BCH_WRITEBACK_FRAGMENT_THRESHOLD_HIGH){+fp_term=dc->writeback_rate_fp_term_mid*+(c->gc_stats.in_use-BCH_WRITEBACK_FRAGMENT_THRESHOLD_MID);+}else{+fp_term=dc->writeback_rate_fp_term_high*+(c->gc_stats.in_use-BCH_WRITEBACK_FRAGMENT_THRESHOLD_HIGH);+}+fps=div_s64(dirty,dirty_buckets)*fp_term;+if(fragment>3&&fps>proportional_scaled){+/* Only overrite the p when fragment > 3 */+proportional_scaled=fps;+}+}+if((error<0&&dc->writeback_rate_integral>0)||(error>0&&time_before64(local_clock(),dc->writeback_rate.next+NSEC_PER_MSEC))){
From: Kai Krakow <redacted>
This reverts commit 56b30770b27d54d68ad51eccc6d888282b568cee.
With the btree using the `system_wq`, I seem to see a lot more desktop
latency than I should.
After some more investigation, it looks like the original assumption
of 56b3077 no longer is true, and bcache has a very high potential of
congesting the `system_wq`. In turn, this introduces laggy desktop
performance, IO stalls (at least with btrfs), and input events may be
delayed.
So let's revert this. It's important to note that the semantics of
using `system_wq` previously mean that `btree_io_wq` should be created
before and destroyed after other bcache wqs to keep the same
assumptions.
Cc: Coly Li <redacted>
Cc: stable@vger.kernel.org # 5.4+
Signed-off-by: Kai Krakow <redacted>
Signed-off-by: Coly Li <redacted>
---
drivers/md/bcache/bcache.h | 2 ++
drivers/md/bcache/btree.c | 21 +++++++++++++++++++--
drivers/md/bcache/super.c | 4 ++++
3 files changed, 25 insertions(+), 2 deletions(-)
From: Kai Krakow <redacted>
Before killing `btree_io_wq`, the queue was allocated using
`create_singlethread_workqueue()` which has `WQ_MEM_RECLAIM`. After
killing it, it no longer had this property but `system_wq` is not
single threaded.
Let's combine both worlds and make it multi threaded but able to
reclaim memory.
Cc: Coly Li <redacted>
Cc: stable@vger.kernel.org # 5.4+
Signed-off-by: Kai Krakow <redacted>
Signed-off-by: Coly Li <redacted>
---
drivers/md/bcache/btree.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Kai Krakow <redacted>
This is potentially long running and not latency sensitive, let's get
it out of the way of other latency sensitive events.
As observed in the previous commit, the `system_wq` comes easily
congested by bcache, and this fixes a few more stalls I was observing
every once in a while.
Let's not make this `WQ_MEM_RECLAIM` as it showed to reduce performance
of boot and file system operations in my tests. Also, without
`WQ_MEM_RECLAIM`, I no longer see desktop stalls. This matches the
previous behavior as `system_wq` also does no memory reclaim:
@@ -901,8 +903,10 @@ unsigned int bch_btree_insert_key(struct btree_keys *b, struct bkey *k,status=BTREE_INSERT_STATUS_INSERT;while(m!=bset_bkey_last(i)&&-bkey_cmp(k,b->ops->is_extents?&START_KEY(m):m)>0)-prev=m,m=bkey_next(m);+bkey_cmp(k,b->ops->is_extents?&START_KEY(m):m)>0){+prev=m;+m=bkey_next(m);+}/* prev is in the tree, if we merge we're done */status=BTREE_INSERT_STATUS_BACK_MERGE;
@@ -124,6 +124,127 @@ static inline void remove_owner_space(struct bch_nvm_namespace *ns,bitmap_set(ns->pages_bitmap,pgoff,nr);}+/* If not found, it will create if create == true */+staticstructbch_owner_list*find_owner_list(constchar*owner_uuid,boolcreate)+{+structbch_owner_list*owner_list;+inti;++for(i=0;i<only_set->owner_list_used;i++){+if(!memcmp(owner_uuid,only_set->owner_lists[i]->owner_uuid,16))+returnonly_set->owner_lists[i];+}++if(create){+owner_list=alloc_owner_list(owner_uuid,NULL,only_set->total_namespaces_nr);+only_set->owner_lists[only_set->owner_list_used++]=owner_list;+returnowner_list;+}else+returnNULL;+}++staticstructbch_nvm_alloced_recs*find_nvm_alloced_recs(structbch_owner_list*owner_list,+structbch_nvm_namespace*ns,boolcreate)+{+intposition=ns->sb.this_namespace_nr;++if(create&&!owner_list->alloced_recs[position]){+structbch_nvm_alloced_recs*alloced_recs=+kzalloc(sizeof(*alloced_recs),GFP_KERNEL|__GFP_NOFAIL);++alloced_recs->ns=ns;+INIT_LIST_HEAD(&alloced_recs->extent_head);+owner_list->alloced_recs[position]=alloced_recs;+returnalloced_recs;+}else+returnowner_list->alloced_recs[position];+}++staticinlinevoid*extent_end_addr(structbch_extent*extent)+{+returnextent->kaddr+((u64)(extent->nr)<<PAGE_SHIFT);+}++staticvoidadd_extent(structbch_nvm_alloced_recs*alloced_recs,void*addr,intorder)+{+structlist_head*list=alloced_recs->extent_head.next;+structbch_extent*extent,*tmp;+void*end_addr=addr+(((u64)1<<order)<<PAGE_SHIFT);++while(list!=&alloced_recs->extent_head){+extent=container_of(list,structbch_extent,list);+if(addr>extent->kaddr){+list=list->next;+continue;+}+break;+}++extent=kzalloc(sizeof(*extent),GFP_KERNEL);+extent->kaddr=addr;+extent->nr=1<<order;+list_add_tail(&extent->list,list);+alloced_recs->nr++;+}++void*bch_nvm_alloc_pages(intorder,constchar*owner_uuid)+{+void*kaddr=NULL;+structbch_owner_list*owner_list;+structbch_nvm_alloced_recs*alloced_recs;+inti,j;++mutex_lock(&only_set->lock);+owner_list=find_owner_list(owner_uuid,true);++for(j=0;j<only_set->total_namespaces_nr;j++){+structbch_nvm_namespace*ns=only_set->nss[j];++if(!ns||(ns->free<(1<<order)))+continue;++for(i=order;i<BCH_MAX_ORDER;i++){+structlist_head*list;+structpage*page,*buddy_page;++if(list_empty(&ns->free_area[i]))+continue;++list=ns->free_area[i].next;+page=container_of((void*)list,structpage,zone_device_data);++list_del(list);++while(i!=order){+buddy_page=nvm_vaddr_to_page(ns,+nvm_pgoff_to_vaddr(ns,page->index+(1<<(i-1))));+set_page_private(buddy_page,i-1);+buddy_page->index=page->index+(1<<(i-1));+__SetPageBuddy(buddy_page);+list_add((structlist_head*)&buddy_page->zone_device_data,+&ns->free_area[i-1]);+i--;+}++set_page_private(page,order);+__ClearPageBuddy(page);+ns->free-=1<<order;+kaddr=nvm_pgoff_to_vaddr(ns,page->index);+break;+}++if(i!=BCH_MAX_ORDER){+alloced_recs=find_nvm_alloced_recs(owner_list,ns,true);+add_extent(alloced_recs,kaddr,order);+break;+}+}++mutex_unlock(&only_set->lock);+returnkaddr;+}+EXPORT_SYMBOL_GPL(bch_nvm_alloc_pages);+staticintinit_owner_info(structbch_nvm_namespace*ns){structbch_owner_list_head*owner_list_head;
From: Jianpeng Ma <redacted>
This nvm pages allocator will implement the simple buddy to manage the
nvm address space. This patch initializes this buddy for new namespace.
the unit of alloc/free of the buddy is page. DAX device has their
struct page(in dram or PMEM).
struct { /* ZONE_DEVICE pages */
/** @pgmap: Points to the hosting device page map. */
struct dev_pagemap *pgmap;
void *zone_device_data;
/*
* ZONE_DEVICE private pages are counted as being
* mapped so the next 3 words hold the mapping, index,
* and private fields from the source anonymous or
* page cache page while the page is migrated to device
* private memory.
* ZONE_DEVICE MEMORY_DEVICE_FS_DAX pages also
* use the mapping, index, and private fields when
* pmem backed DAX files are mapped.
*/
};
ZONE_DEVICE pages only use pgmap. Other 4 words[16/32 bytes] don't use.
So the second/third word will be used as 'struct list_head ' which list
in buddy. The fourth word(that is normal struct page::index) store pgoff
which the page-offset in the dax device. And the fifth word (that is
normal struct page::private) store order of buddy. page_type will be used
to store buddy flags.
Signed-off-by: Jianpeng Ma <redacted>
Co-authored-by: Qiaowei Ren [off-list ref]
Signed-off-by: Coly Li <redacted>
---
drivers/md/bcache/nvm-pages.c | 75 ++++++++++++++++++++++++++++++++++-
drivers/md/bcache/nvm-pages.h | 5 +++
2 files changed, 78 insertions(+), 2 deletions(-)
This patch initializes the prototype data structures for nvm pages
allocator,
- struct bch_nvm_pages_sb
This is the super block allocated on each nvdimm namespace. A nvdimm
set may have multiple namespaces, bch_nvm_pages_sb->set_uuid is used
to mark which nvdimm set this name space belongs to. Normally we will
use the bcache's cache set UUID to initialize this uuid, to connect this
nvdimm set to a specified bcache cache set.
- struct bch_owner_list_head
This is a table for all heads of all owner lists. A owner list records
which page(s) allocated to which owner. After reboot from power failure,
the ownwer may find all its requested and allocated pages from the owner
list by a handler which is converted by a UUID.
- struct bch_nvm_pages_owner_head
This is a head of an owner list. Each owner only has one owner list,
and a nvm page only belongs to an specific owner. uuid[] will be set to
owner's uuid, for bcache it is the bcache's cache set uuid. label is not
mandatory, it is a human-readable string for debug purpose. The pointer
*recs references to separated nvm page which hold the table of struct
bch_nvm_pgalloc_rec.
- struct bch_nvm_pgalloc_recs
This struct occupies a whole page, owner_uuid should match the uuid
in struct bch_nvm_pages_owner_head. recs[] is the real table contains all
allocated records.
- struct bch_nvm_pgalloc_rec
Each structure records a range of allocated nvm pages. pgoff is offset
in unit of page size of this allocated nvm page range. The adjoint page
ranges of same owner can be merged into a larger one, therefore pages_nr
is NOT always power of 2.
Signed-off-by: Coly Li <redacted>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
include/uapi/linux/bcache-nvm.h | 195 ++++++++++++++++++++++++++++++++
1 file changed, 195 insertions(+)
create mode 100644 include/uapi/linux/bcache-nvm.h
@@ -0,0 +1,195 @@+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */++#ifndef _UAPI_BCACHE_NVM_H+#define _UAPI_BCACHE_NVM_H++/*+*BcacheonNVDIMMdatastructures+*/++/*+*-structbch_nvm_pages_sb+*Thisisthesuperblockallocatedoneachnvdimmnamespace.Anvdimm+*setmayhavemultiplenamespaces,bch_nvm_pages_sb->set_uuidisusedtomark+*whichnvdimmsetthisnamespacebelongsto.Normallywewillusethe+*bcache'scachesetUUIDtoinitializethisuuid,toconnectthisnvdimm+*settoaspecifiedbcachecacheset.+*+*-structbch_owner_list_head+*Thisisatableforallheadsofallownerlists.Aownerlistrecords+*whichpage(s)allocatedtowhichowner.Afterrebootfrompowerfailure,+*theownwermayfindallitsrequestedandallocatedpagesfromtheowner+*listbyahandlerwhichisconvertedbyaUUID.+*+*-structbch_nvm_pages_owner_head+*Thisisaheadofanownerlist.Eachowneronlyhasoneownerlist,+*andanvmpageonlybelongstoanspecificowner.uuid[]willbesetto+*owner'suuid,forbcacheitisthebcache'scachesetuuid.labelisnot+*mandatory,itisahuman-readablestringfordebugpurpose.Thepointer+*recsreferencestoseparatednvmpagewhichholdthetableofstruct+*bch_pgalloc_rec.+*+*-structbch_nvm_pgalloc_recs+*Thisstructureoccupiesawholepage,owner_uuidshouldmatchtheuuid+*instructbch_nvm_pages_owner_head.recs[]istherealtablecontainsall+*allocatedrecords.+*+*-structbch_pgalloc_rec+*Eachstructurerecordsarangeofallocatednvmpages.pgoffisoffset+*inunitofpagesizeofthisallocatednvmpagerange.Theadjointpage+*rangesofsameownercanbemergedintoalargerone,thereforepages_nr+*isNOTalwayspowerof2.+*+*+*Memorylayoutonnvdimmnamespace0+*+*0+---------------------------------++*||+*4KB+---------------------------------++*|bch_nvm_pages_sb|+*8KB+---------------------------------+<---bch_nvm_pages_sb.bch_owner_list_head+*|bch_owner_list_head|+*||+*16KB+---------------------------------+<---bch_owner_list_head.heads[0].recs[0]+*|bch_nvm_pgalloc_recs|+*|(nvmpagesinternalusage)|+*24KB+---------------------------------++*||+*||+*16MB+---------------------------------++*|allocablenvmpages|+*|forbuddyallocator|+*end+---------------------------------++*+*+*+*MemorylayoutonnvdimmnamespaceN+*(doesn'thaveownerlist)+*+*0+---------------------------------++*||+*4KB+---------------------------------++*|bch_nvm_pages_sb|+*8KB+---------------------------------++*||+*||+*||+*||+*||+*||+*16MB+---------------------------------++*|allocablenvmpages|+*|forbuddyallocator|+*end+---------------------------------++*+*/++#include<linux/types.h>++/* In sectors */+#define BCH_NVM_PAGES_SB_OFFSET 4096+#define BCH_NVM_PAGES_OFFSET (16 << 20)++#define BCH_NVM_PAGES_LABEL_SIZE 32+#define BCH_NVM_PAGES_NAMESPACES_MAX 8++#define BCH_NVM_PAGES_OWNER_LIST_HEAD_OFFSET (8<<10)+#define BCH_NVM_PAGES_SYS_RECS_HEAD_OFFSET (16<<10)++#define BCH_NVM_PAGES_SB_VERSION 0+#define BCH_NVM_PAGES_SB_VERSION_MAX 0++staticconstcharbch_nvm_pages_magic[]={+0x17,0xbd,0x53,0x7f,0x1b,0x23,0xd6,0x83,+0x46,0xa4,0xf8,0x28,0x17,0xda,0xec,0xa9};+staticconstcharbch_nvm_pages_pgalloc_magic[]={+0x39,0x25,0x3f,0xf7,0x27,0x17,0xd0,0xb9,+0x10,0xe6,0xd2,0xda,0x38,0x68,0x26,0xae};++structbch_pgalloc_rec{+__u32pgoff;+__u32nr;+};++structbch_nvm_pgalloc_recs{+union{+struct{+structbch_nvm_pages_owner_head*owner;+structbch_nvm_pgalloc_recs*next;+__u8magic[16];+__u8owner_uuid[16];+__u32size;+__u32used;+__u64_pad[4];+structbch_pgalloc_recrecs[];+};+__u8pad[8192];+};+};+#define BCH_MAX_RECS \+((sizeof(structbch_nvm_pgalloc_recs)-\+offsetof(structbch_nvm_pgalloc_recs,recs))/\+sizeof(structbch_pgalloc_rec))++structbch_nvm_pages_owner_head{+__u8uuid[16];+charlabel[BCH_NVM_PAGES_LABEL_SIZE];+/* Per-namespace own lists */+structbch_nvm_pgalloc_recs*recs[BCH_NVM_PAGES_NAMESPACES_MAX];+};++/* heads[0] is always for nvm_pages internal usage */+structbch_owner_list_head{+union{+struct{+__u32size;+__u32used;+__u64_pad[4];+structbch_nvm_pages_owner_headheads[];+};+__u8pad[8192];+};+};+#define BCH_MAX_OWNER_LIST \+((sizeof(structbch_owner_list_head)-\+offsetof(structbch_owner_list_head,heads))/\+sizeof(structbch_nvm_pages_owner_head))++/* The on-media bit order is local CPU order */+structbch_nvm_pages_sb{+__u64csum;+__u64ns_start;+__u64sb_offset;+__u64version;+__u8magic[16];+__u8uuid[16];+__u32page_size;+__u32total_namespaces_nr;+__u32this_namespace_nr;+union{+__u8set_uuid[16];+__u64set_magic;+};++__u64flags;+__u64seq;++__u64feature_compat;+__u64feature_incompat;+__u64feature_ro_compat;++/* For allocable nvm pages from buddy systems */+__u64pages_offset;+__u64pages_total;++__u64pad[8];++/* Only on the first name space */+structbch_owner_list_head*owner_list_head;++/* Just for csum_set() */+__u32keys;+__u64d[0];+};++#endif /* _UAPI_BCACHE_NVM_H */
From: Jianpeng Ma <redacted>
This patch implements bch_get_allocated_pages() of the buddy to be used to
get allocated pages from specific owner.
Signed-off-by: Jianpeng Ma <redacted>
Co-authored-by: Qiaowei Ren [off-list ref]
Signed-off-by: Coly Li <redacted>
---
drivers/md/bcache/nvm-pages.c | 39 +++++++++++++++++++++++++++++++++++
drivers/md/bcache/nvm-pages.h | 6 ++++++
2 files changed, 45 insertions(+)
@@ -364,9 +492,8 @@ static void init_nvm_free_space(struct bch_nvm_namespace *ns)page=nvm_vaddr_to_page(ns,nvm_pgoff_to_vaddr(ns,pgoff_start));page->index=pgoff_start;set_page_private(page,i);-__SetPageBuddy(page);-list_add((structlist_head*)&page->zone_device_data,&ns->free_area[i]);-+/* in order to update ns->free */+__free_space(ns,nvm_pgoff_to_vaddr(ns,pgoff_start),i);pgoff_start+=1<<i;pages-=1<<i;}
Currently the meta data bucket locations on cache device are reserved
after the meta data stored on NVDIMM pages, for the meta data layout
consistentcy temporarily. So these buckets are still marked as meta data
by SET_GC_MARK() in bch_btree_gc_finish().
When BCH_FEATURE_INCOMPAT_NVDIMM_META is set, the sb.d[] stores linear
address of NVDIMM pages and not bucket index anymore. Therefore we
should avoid to find bucket index from sb.d[], and directly use bucket
index from ca->sb.first_bucket to (ca->sb.first_bucket +
ca->sb.njournal_bucketsi) for setting the gc mark of journal bucket.
Signed-off-by: Coly Li <redacted>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/btree.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
From: Jianpeng Ma <redacted>
This patch define the prototype data structures in memory and initializes
the nvm pages allocator.
The nv address space which is managed by this allocatior can consist of
many nvm namespaces, and some namespaces can compose into one nvm set,
like cache set. For this initial implementation, only one set can be
supported.
The users of this nvm pages allocator need to call regiseter_namespace()
to register the nvdimm device (like /dev/pmemX) into this allocator as
the instance of struct nvm_namespace.
Signed-off-by: Jianpeng Ma <redacted>
Co-authored-by: Qiaowei Ren [off-list ref]
Signed-off-by: Coly Li <redacted>
---
drivers/md/bcache/Kconfig | 6 +
drivers/md/bcache/Makefile | 2 +-
drivers/md/bcache/nvm-pages.c | 404 ++++++++++++++++++++++++++++++++
drivers/md/bcache/nvm-pages.h | 92 ++++++++
drivers/md/bcache/super.c | 3 +
include/uapi/linux/bcache-nvm.h | 7 -
6 files changed, 506 insertions(+), 8 deletions(-)
create mode 100644 drivers/md/bcache/nvm-pages.c
create mode 100644 drivers/md/bcache/nvm-pages.h
@@ -0,0 +1,404 @@+// SPDX-License-Identifier: GPL-2.0-only+/*+*Nvdimmpage-buddyallocator+*+*Copyright(c)2021,IntelCorporation.+*Copyright(c)2021,QiaoweiRen<qiaowei.ren@intel.com>.+*Copyright(c)2021,JianpengMa<jianpeng.ma@intel.com>.+*/++#include"bcache.h"+#include"nvm-pages.h"++#include<linux/slab.h>+#include<linux/list.h>+#include<linux/mutex.h>+#include<linux/dax.h>+#include<linux/pfn_t.h>+#include<linux/libnvdimm.h>+#include<linux/mm_types.h>+#include<linux/err.h>+#include<linux/pagemap.h>+#include<linux/bitmap.h>+#include<linux/blkdev.h>++#ifdef CONFIG_BCACHE_NVM_PAGES++staticconstcharbch_nvm_pages_magic[]={+0x17,0xbd,0x53,0x7f,0x1b,0x23,0xd6,0x83,+0x46,0xa4,0xf8,0x28,0x17,0xda,0xec,0xa9};+staticconstcharbch_nvm_pages_pgalloc_magic[]={+0x39,0x25,0x3f,0xf7,0x27,0x17,0xd0,0xb9,+0x10,0xe6,0xd2,0xda,0x38,0x68,0x26,0xae};++structbch_nvm_set*only_set;++staticstructbch_owner_list*alloc_owner_list(constchar*owner_uuid,+constchar*label,inttotal_namespaces)+{+structbch_owner_list*owner_list;++owner_list=kzalloc(sizeof(*owner_list),GFP_KERNEL);+if(!owner_list)+returnNULL;++owner_list->alloced_recs=kcalloc(total_namespaces,+sizeof(structbch_nvm_alloced_recs*),GFP_KERNEL);+if(!owner_list->alloced_recs){+kfree(owner_list);+returnNULL;+}++if(owner_uuid)+memcpy(owner_list->owner_uuid,owner_uuid,16);+if(label)+memcpy(owner_list->label,label,BCH_NVM_PAGES_LABEL_SIZE);++returnowner_list;+}++staticvoidrelease_extents(structbch_nvm_alloced_recs*extents)+{+structlist_head*list=extents->extent_head.next;+structbch_extent*extent;++while(list!=&extents->extent_head){+extent=container_of(list,structbch_extent,list);+list_del(list);+kfree(extent);+list=extents->extent_head.next;+}+kfree(extents);+}++staticvoidrelease_owner_info(structbch_nvm_set*nvm_set)+{+structbch_owner_list*owner_list;+inti,j;++for(i=0;i<nvm_set->owner_list_used;i++){+owner_list=nvm_set->owner_lists[i];+for(j=0;j<nvm_set->total_namespaces_nr;j++){+if(owner_list->alloced_recs[j])+release_extents(owner_list->alloced_recs[j]);+}+kfree(owner_list->alloced_recs);+kfree(owner_list);+}+kfree(nvm_set->owner_lists);+}++staticvoidrelease_nvm_namespaces(structbch_nvm_set*nvm_set)+{+inti;++for(i=0;i<nvm_set->total_namespaces_nr;i++){+blkdev_put(nvm_set->nss[i]->bdev,FMODE_READ|FMODE_WRITE|FMODE_EXEC);+kfree(nvm_set->nss[i]);+}++kfree(nvm_set->nss);+}++staticvoidrelease_nvm_set(structbch_nvm_set*nvm_set)+{+release_nvm_namespaces(nvm_set);+release_owner_info(nvm_set);+kfree(nvm_set);+}++staticvoid*nvm_pgoff_to_vaddr(structbch_nvm_namespace*ns,pgoff_tpgoff)+{+returnns->kaddr+(pgoff<<PAGE_SHIFT);+}++staticintinit_owner_info(structbch_nvm_namespace*ns)+{+structbch_owner_list_head*owner_list_head;+structbch_nvm_pages_owner_head*owner_head;+structbch_nvm_pgalloc_recs*nvm_pgalloc_recs;+structbch_owner_list*owner_list;+structbch_nvm_alloced_recs*extents;+structbch_extent*extent;+u32i,j,k;++owner_list_head=(structbch_owner_list_head*)+(ns->kaddr+BCH_NVM_PAGES_OWNER_LIST_HEAD_OFFSET);++mutex_lock(&only_set->lock);+only_set->owner_list_size=owner_list_head->size;+only_set->owner_list_used=owner_list_head->used;++for(i=0;i<owner_list_head->used;i++){+owner_head=&owner_list_head->heads[i];+owner_list=alloc_owner_list(owner_head->uuid,owner_head->label,+only_set->total_namespaces_nr);+if(!owner_list){+mutex_unlock(&only_set->lock);+return-ENOMEM;+}++for(j=0;j<only_set->total_namespaces_nr;j++){+if(!only_set->nss[j]||!owner_head->recs[j])+continue;++nvm_pgalloc_recs=(structbch_nvm_pgalloc_recs*)+((long)owner_head->recs[j]+ns->kaddr);+if(memcmp(nvm_pgalloc_recs->magic,bch_nvm_pages_pgalloc_magic,16)){+pr_info("invalid bch_nvmpages_pgalloc_magic\n");+mutex_unlock(&only_set->lock);+return-EINVAL;+}++extents=kzalloc(sizeof(*extents),GFP_KERNEL);+if(!extents){+mutex_unlock(&only_set->lock);+return-ENOMEM;+}++extents->ns=only_set->nss[j];+INIT_LIST_HEAD(&extents->extent_head);+owner_list->alloced_recs[j]=extents;++do{+structbch_pgalloc_rec*rec;++for(k=0;k<nvm_pgalloc_recs->used;k++){+rec=&nvm_pgalloc_recs->recs[k];+extent=kzalloc(sizeof(*extent),GFP_KERNEL);+if(!extents){+mutex_unlock(&only_set->lock);+return-ENOMEM;+}+extent->kaddr=nvm_pgoff_to_vaddr(extents->ns,rec->pgoff);+extent->nr=rec->nr;+list_add_tail(&extent->list,&extents->extent_head);+}+extents->nr+=nvm_pgalloc_recs->used;++if(nvm_pgalloc_recs->next){+nvm_pgalloc_recs=(structbch_nvm_pgalloc_recs*)+((long)nvm_pgalloc_recs->next+ns->kaddr);+if(memcmp(nvm_pgalloc_recs->magic,+bch_nvm_pages_pgalloc_magic,16)){+pr_info("invalid bch_nvmpages_pgalloc_magic\n");+mutex_unlock(&only_set->lock);+return-EINVAL;+}+}else+nvm_pgalloc_recs=NULL;+}while(nvm_pgalloc_recs);+}+only_set->owner_lists[i]=owner_list;+owner_list->nvm_set=only_set;+}+mutex_unlock(&only_set->lock);++return0;+}++staticboolattach_nvm_set(structbch_nvm_namespace*ns)+{+boolrc=true;++mutex_lock(&only_set->lock);+if(only_set->nss){+if(memcmp(ns->sb.set_uuid,only_set->set_uuid,16)){+pr_info("namespace id does't match nvm set\n");+rc=false;+gotounlock;+}++if(only_set->nss[ns->sb.this_namespace_nr]){+pr_info("already has the same position(%d) nvm\n",+ns->sb.this_namespace_nr);+rc=false;+gotounlock;+}+}else{+memcpy(only_set->set_uuid,ns->sb.set_uuid,16);+only_set->total_namespaces_nr=ns->sb.total_namespaces_nr;+only_set->nss=kcalloc(only_set->total_namespaces_nr,+sizeof(structbch_nvm_namespace*),GFP_KERNEL);+only_set->owner_lists=kcalloc(BCH_MAX_OWNER_LIST,+sizeof(structnvm_pages_owner_head*),GFP_KERNEL);+if(!only_set->nss||!only_set->owner_lists){+pr_info("can't alloc nss or owner_list\n");+kfree(only_set->nss);+kfree(only_set->owner_lists);+rc=false;+gotounlock;+}+}++only_set->nss[ns->sb.this_namespace_nr]=ns;++unlock:+mutex_unlock(&only_set->lock);+returnrc;+}++staticintread_nvdimm_meta_super(structblock_device*bdev,+structbch_nvm_namespace*ns)+{+structpage*page;+structbch_nvm_pages_sb*sb;++page=read_cache_page_gfp(bdev->bd_inode->i_mapping,+BCH_NVM_PAGES_SB_OFFSET>>PAGE_SHIFT,GFP_KERNEL);++if(IS_ERR(page))+return-EIO;++sb=page_address(page)+offset_in_page(BCH_NVM_PAGES_SB_OFFSET);+memcpy(&ns->sb,sb,sizeof(structbch_nvm_pages_sb));++put_page(page);++return0;+}++structbch_nvm_namespace*bch_register_namespace(constchar*dev_path)+{+structbch_nvm_namespace*ns;+interr;+pgoff_tpgoff;+charbuf[BDEVNAME_SIZE];+structblock_device*bdev;+uint64_texpected_csum;+intid;+char*path=NULL;++path=kstrndup(dev_path,512,GFP_KERNEL);+if(!path){+pr_err("kstrndup failed\n");+returnERR_PTR(-ENOMEM);+}++bdev=blkdev_get_by_path(strim(path),+FMODE_READ|FMODE_WRITE|FMODE_EXEC,+only_set);+if(IS_ERR(bdev)){+pr_info("get %s error\n",dev_path);+kfree(path);+returnERR_PTR(PTR_ERR(bdev));+}++ns=kmalloc(sizeof(structbch_nvm_namespace),GFP_KERNEL);+if(!ns)+gotobdput;++err=-EIO;+if(read_nvdimm_meta_super(bdev,ns)){+pr_info("%s read nvdimm meta super block failed.\n",+bdevname(bdev,buf));+gotofree_ns;+}++if(memcmp(ns->sb.magic,bch_nvm_pages_magic,16)){+pr_info("invalid bch_nvm_pages_magic\n");+gotofree_ns;+}++if(ns->sb.sb_offset!=BCH_NVM_PAGES_SB_OFFSET){+pr_info("invalid superblock offset\n");+gotofree_ns;+}++if(ns->sb.total_namespaces_nr!=1){+pr_info("only one nvm device\n");+gotofree_ns;+}++expected_csum=csum_set(&ns->sb);+if(expected_csum!=ns->sb.csum){+pr_info("csum is not match with expected one\n");+gotofree_ns;+}++err=-EOPNOTSUPP;+if(!bdev_dax_supported(bdev,ns->sb.page_size)){+pr_info("%s don't support DAX\n",bdevname(bdev,buf));+gotofree_ns;+}++err=-EINVAL;+if(bdev_dax_pgoff(bdev,0,ns->sb.page_size,&pgoff)){+pr_info("invalid offset of %s\n",bdevname(bdev,buf));+gotofree_ns;+}++err=-ENOMEM;+ns->dax_dev=fs_dax_get_by_bdev(bdev);+if(!ns->dax_dev){+pr_info("can't by dax device by %s\n",bdevname(bdev,buf));+gotofree_ns;+}++err=-EINVAL;+id=dax_read_lock();+if(dax_direct_access(ns->dax_dev,pgoff,ns->sb.pages_total,+&ns->kaddr,&ns->start_pfn)<=0){+pr_info("dax_direct_access error\n");+dax_read_unlock(id);+gotofree_ns;+}+dax_read_unlock(id);+++err=-EEXIST;+if(!attach_nvm_set(ns))+gotofree_ns;++ns->page_size=ns->sb.page_size;+ns->pages_offset=ns->sb.pages_offset;+ns->pages_total=ns->sb.pages_total;+ns->free=0;+ns->bdev=bdev;+ns->nvm_set=only_set;++mutex_init(&ns->lock);++if(ns->sb.this_namespace_nr==0){+pr_info("only first namespace contain owner info\n");+err=init_owner_info(ns);+if(err<0){+pr_info("init_owner_info met error %d\n",err);+gotofree_ns;+}+}++kfree(path);+returnns;+free_ns:+kfree(ns);+bdput:+blkdev_put(bdev,FMODE_READ|FMODE_WRITE|FMODE_EXEC);+kfree(path);+returnERR_PTR(err);+}+EXPORT_SYMBOL_GPL(bch_register_namespace);++int__initbch_nvm_init(void)+{+only_set=kzalloc(sizeof(*only_set),GFP_KERNEL);+if(!only_set)+return-ENOMEM;++only_set->total_namespaces_nr=0;+only_set->owner_lists=NULL;+only_set->nss=NULL;++mutex_init(&only_set->lock);++pr_info("bcache nvm init\n");+return0;+}++voidbch_nvm_exit(void)+{+release_nvm_set(only_set);+pr_info("bcache nvm exit\n");+}++#endif
This patch adds BCH_FEATURE_INCOMPAT_NVDIMM_META (value 0x0004) into the
incompat feature set. When this bit is set by bcache-tools, it indicates
bcache meta data should be stored on specific NVDIMM meta device.
The bcache meta data mainly includes journal and btree nodes, when this
bit is set in incompat feature set, bcache will ask the nvm-pages
allocator for NVDIMM space to store the meta data.
Signed-off-by: Coly Li <redacted>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/features.h | 9 +++++++++
1 file changed, 9 insertions(+)
It is unnecessary to initialize the EXPERIMENTAL nvm-pages allocator
when CONFIG_BCACHE_NVM_PAGES is not configured. This patch uses
"#ifdef CONFIG_BCACHE_NVM_PAGES" to wrap bch_nvm_init() and
bch_nvm_exit(), and only calls them when bch_nvm_exit is configured.
Signed-off-by: Coly Li <redacted>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/super.c | 4 ++++
1 file changed, 4 insertions(+)
This patch implements two methods to store bcache journal to,
1) __journal_write_unlocked() for block interface device
The latency method to compose bio and issue the jset bio to cache
device (e.g. SSD). c->journal.key.ptr[0] indicates the LBA on cache
device to store the journal jset.
2) __journal_nvdimm_write_unlocked() for memory interface NVDIMM
Use memory interface to access NVDIMM pages and store the jset by
memcpy_flushcache(). c->journal.key.ptr[0] indicates the linear
address from the NVDIMM pages to store the journal jset.
For lagency configuration without NVDIMM meta device, journal I/O is
handled by __journal_write_unlocked() with existing code logic. If the
NVDIMM meta device is used (by bcache-tools), the journal I/O will
be handled by __journal_nvdimm_write_unlocked() and go into the NVDIMM
pages.
And when NVDIMM meta device is used, sb.d[] stores the linear addresses
from NVDIMM pages (no more bucket index), in journal_reclaim() the
journaling location in c->journal.key.ptr[0] should also be updated by
linear address from NVDIMM pages (no more LBA combined by sectors offset
and bucket index).
Signed-off-by: Coly Li <redacted>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/journal.c | 111 ++++++++++++++++++++++++------------
1 file changed, 75 insertions(+), 36 deletions(-)
@@ -793,7 +774,6 @@ static void journal_write_unlocked(struct closure *cl)ca->journal.seq[ca->journal.cur_idx]=w->data->seq;}-/* If KEY_PTRS(k) == 0, this jset gets lost in air */BUG_ON(i==0);
This patch adds a sysfs interface register_nvdimm_meta to register
NVDIMM meta device. The sysfs interface file only shows up when
CONFIG_BCACHE_NVM_PAGES=y. Then a NVDIMM name space formatted by
bcache-tools can be registered into bcache by e.g.,
echo /dev/pmem0 > /sys/fs/bcache/register_nvdimm_meta
Signed-off-by: Coly Li <redacted>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/super.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
The nvm-pages allocator may store and index the NVDIMM pages allocated
for bcache journal. This patch adds the initialization to store bcache
journal space on NVDIMM pages if BCH_FEATURE_INCOMPAT_NVDIMM_META bit is
set by bcache-tools.
If BCH_FEATURE_INCOMPAT_NVDIMM_META is set, get_nvdimm_journal_space()
will return the linear address of NVDIMM pages for bcache journal,
- If there is previously allocated space, find it from nvm-pages owner
list and return to bch_journal_init().
- If there is no previously allocated space, require a new NVDIMM range
from the nvm-pages allocator, and return it to bch_journal_init().
And in bch_journal_init(), keys in sb.d[] store the corresponding linear
address from NVDIMM into sb.d[i].ptr[0] where 'i' is the bucket index to
iterate all journal buckets.
Later when bcache journaling code stores the journaling jset, the target
NVDIMM linear address stored (and updated) in sb.d[i].ptr[0] can be used
directly in memory copy from DRAM pages into NVDIMM pages.
Signed-off-by: Coly Li <redacted>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/journal.c | 97 +++++++++++++++++++++++++++++++++++++
drivers/md/bcache/journal.h | 2 +-
drivers/md/bcache/super.c | 16 +++---
3 files changed, 107 insertions(+), 8 deletions(-)
@@ -982,3 +984,98 @@ int bch_journal_alloc(struct cache_set *c)return0;}++staticvoid*find_journal_nvm_base(structbch_extent*list,structcache*ca)+{+void*ret=NULL;+structbch_extent*cur,*next;++next=list;+do{+cur=next;+/* Match journal area's nvdimm address */+if(cur->kaddr==(void*)ca->sb.d[0]){+ret=cur->kaddr;+break;+}+next=list_entry(cur->list.next,structbch_extent,list);+}while(next!=list);++returnret;+}++staticvoidbch_release_nvm_extent_list(structbch_extent*list)+{+structbch_extent*ext;+structlist_head*cur,*next;++list_for_each_safe(cur,next,&list->list){+ext=list_entry(cur,structbch_extent,list);+kfree(ext);+}+}++staticvoid*get_nvdimm_journal_space(structcache*ca)+{+structbch_extent*allocated_list=NULL;+void*ret=NULL;++allocated_list=bch_get_allocated_pages(ca->sb.set_uuid);+if(allocated_list){+ret=find_journal_nvm_base(allocated_list,ca);+bch_release_nvm_extent_list(allocated_list);+}++if(!ret){+intorder=ilog2(ca->sb.bucket_size*ca->sb.njournal_buckets/+PAGE_SECTORS);++ret=bch_nvm_alloc_pages(order,ca->sb.set_uuid);+if(ret)+memset(ret,0,(1<<order)*PAGE_SIZE);+}++returnret;+}++staticint__bch_journal_nvdimm_init(structcache*ca)+{+inti,ret=0;+void*journal_nvm_base=NULL;++journal_nvm_base=get_nvdimm_journal_space(ca);+if(!journal_nvm_base){+pr_err("Failed to get journal space from nvdimm\n");+ret=-1;+gotoout;+}++/* Iniialized and reloaded from on-disk super block already */+if(ca->sb.d[0]!=0)+gotoout;++for(i=0;i<ca->sb.keys;i++)+ca->sb.d[i]=+(u64)(journal_nvm_base+(ca->sb.bucket_size*i));++out:+returnret;+}++intbch_journal_init(structcache_set*c)+{+inti,ret=0;+structcache*ca=c->cache;++ca->sb.keys=clamp_t(int,ca->sb.nbuckets>>7,+2,SB_JOURNAL_BUCKETS);++if(!bch_has_feature_nvdimm_meta(&ca->sb)){+for(i=0;i<ca->sb.keys;i++)+ca->sb.d[i]=ca->sb.first_bucket+i;+}else{+ret=__bch_journal_nvdimm_init(ca);+}++returnret;+}
This patch implements two methods to read jset from media for journal
replay,
- __jnl_rd_bkt() for block device
This is the legacy method to read jset via block device interface.
- __jnl_rd_nvm_bkt() for NVDIMM
This is the method to read jset from NVDIMM memory interface, a.k.a
memcopy() from NVDIMM pages to DRAM pages.
If BCH_FEATURE_INCOMPAT_NVDIMM_META is set in incompat feature set,
during running cache set, journal_read_bucket() will read the journal
content from NVDIMM by __jnl_rd_nvm_bkt(). The linear addresses of
NVDIMM pages to read jset are stored in sb.d[SB_JOURNAL_BUCKETS], which
were initialized and maintained in previous runs of the cache set.
A thing should be noticed is, when bch_journal_read() is called, the
linear address of NVDIMM pages is not loaded and initialized yet, it
is necessary to call __bch_journal_nvdimm_init() before reading the jset
from NVDIMM pages.
Signed-off-by: Coly Li <redacted>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/journal.c | 81 ++++++++++++++++++++++++++-----------
1 file changed, 57 insertions(+), 24 deletions(-)
@@ -34,60 +34,84 @@ static void journal_read_endio(struct bio *bio)closure_put(cl);}+staticstructjset*__jnl_rd_bkt(structcache*ca,unsignedintbkt_idx,+unsignedintlen,unsignedintoffset,+structclosure*cl)+{+sector_tbucket=bucket_to_sector(ca->set,ca->sb.d[bkt_idx]);+structbio*bio=&ca->journal.bio;+structjset*data=ca->set->journal.w[0].data;++bio_reset(bio);+bio->bi_iter.bi_sector=bucket+offset;+bio_set_dev(bio,ca->bdev);+bio->bi_iter.bi_size=len<<9;+bio->bi_end_io=journal_read_endio;+bio->bi_private=cl;+bio_set_op_attrs(bio,REQ_OP_READ,0);+bch_bio_map(bio,data);++closure_bio_submit(ca->set,bio,cl);+closure_sync(cl);++/* Indeed journal.w[0].data */+returndata;+}++staticstructjset*__jnl_rd_nvm_bkt(structcache*ca,unsignedintbkt_idx,+unsignedintlen,unsignedintoffset)+{+void*jset_addr=(void*)ca->sb.d[bkt_idx]+(offset<<9);+structjset*data=ca->set->journal.w[0].data;++memcpy(data,jset_addr,len<<9);++/* Indeed journal.w[0].data */+returndata;+}+staticintjournal_read_bucket(structcache*ca,structlist_head*list,-unsignedintbucket_index)+unsignedintbucket_idx){structjournal_device*ja=&ca->journal;-structbio*bio=&ja->bio;structjournal_replay*i;-structjset*j,*data=ca->set->journal.w[0].data;+structjset*j;structclosurecl;unsignedintlen,left,offset=0;intret=0;-sector_tbucket=bucket_to_sector(ca->set,ca->sb.d[bucket_index]);closure_init_stack(&cl);-pr_debug("reading %u\n",bucket_index);+pr_debug("reading %u\n",bucket_idx);while(offset<ca->sb.bucket_size){reread:left=ca->sb.bucket_size-offset;len=min_t(unsignedint,left,PAGE_SECTORS<<JSET_BITS);-bio_reset(bio);-bio->bi_iter.bi_sector=bucket+offset;-bio_set_dev(bio,ca->bdev);-bio->bi_iter.bi_size=len<<9;--bio->bi_end_io=journal_read_endio;-bio->bi_private=&cl;-bio_set_op_attrs(bio,REQ_OP_READ,0);-bch_bio_map(bio,data);--closure_bio_submit(ca->set,bio,&cl);-closure_sync(&cl);+if(!bch_has_feature_nvdimm_meta(&ca->sb))+j=__jnl_rd_bkt(ca,bucket_idx,len,offset,&cl);+else+j=__jnl_rd_nvm_bkt(ca,bucket_idx,len,offset);/* This function could be simpler now since we no longer write*journalentriesthatoverlapbucketboundaries;thismeans*thestartofabucketwillalwayshaveavalidjournalentry*ifithasanyjournalentriesatall.*/--j=data;while(len){structlist_head*where;size_tblocks,bytes=set_bytes(j);if(j->magic!=jset_magic(&ca->sb)){-pr_debug("%u: bad magic\n",bucket_index);+pr_debug("%u: bad magic\n",bucket_idx);returnret;}if(bytes>left<<9||bytes>PAGE_SIZE<<JSET_BITS){pr_info("%u: too big, %zu bytes, offset %u\n",-bucket_index,bytes,offset);+bucket_idx,bytes,offset);returnret;}
@@ -96,7 +120,7 @@ reread: left = ca->sb.bucket_size - offset;if(j->csum!=csum_set(j)){pr_info("%u: bad csum, %zu bytes, offset %u\n",-bucket_index,bytes,offset);+bucket_idx,bytes,offset);returnret;}
@@ -158,8 +182,8 @@ reread: left = ca->sb.bucket_size - offset;list_add(&i->list,where);ret=1;-if(j->seq>ja->seq[bucket_index])-ja->seq[bucket_index]=j->seq;+if(j->seq>ja->seq[bucket_idx])+ja->seq[bucket_idx]=j->seq;next_set:offset+=blocks*ca->sb.block_size;len-=blocks*ca->sb.block_size;
Hi Jens,
This is the first wave bcache patches for Linux v5.12.
It is nice to see in this round we have 3 new patch contributors:
Jianpeng Ma, Qiaowei Ren and Kai Krakow.
In this series, the EXPERIMENTAL patches from Jianpeng Ma, Qiaowei Ren
and me are initial effort to store bcache meta-data on NVDIMM namespace.
The NVDIMM space is managed and mapped via DAX interface, and accessed
by linear address. In this submission we store bcache journal on NVDIMM,
in future bcache btree nodes and other meta data will be added in too,
before we remove the EXPERIMENTAL statues.
Dongdong Tao contributes a performance optimization when
bcache cache buckets are highly fregmented, Dongdong's patch makes the
dirty data writeback faster and from his benchmark reprots such changes
have recognized improvement for randome write I/O thoughput and latency
for highly fregmented buckets, and no regression for regular I/O
observed.
Kai Krakow contributes 4 patches to offload system_wq usage to separated
btree_io_wq and bch_flush_wq. In his environment the daily backup job
throughput increases from 60.2MB/s to 419MB/s and accomplished time
reduced from 14h29m to 2h13m.
Joe Perches also contributes a fine code stype fix which I pick for this
submission.
Please take them for Linux v5.12 merge window.
Applied 1-6 for now, that weird situation with the user visible header
needs to get resolved before it can go any further.
--
Jens Axboe
This doesn't look right in a user header, any user API should be 32-bit
and 64-bit agnostic.
The above data structure is stored in NVDIMM as allocator's meta data.
It is designed to be directly accessed (in future update) as in-memory
object, but stored on non-volatiled memory like on-disk data structure.
To me, it is fine to use unsigned int/long/long long to define the
members, because nvdimm driver only works on 64bit platform. It is just
unclear to me which form/style I should use to define such data
structure. On one side they are stores as non-volatiled media, on other
side they are accessed directly as in-memory object...
For the above pointer, it is the same reason. In later version, such
object on NVDIMM will be referenced directly by an in-memory pointer
like we normally do for an in-memory object.
Therefore I do treat the data structure as in-memory object after the
DAX mapping accomplished. If not define it as an in-memory pointer, I
have to cast it into (void *) every time when I use it.
+#define BCH_MAX_OWNER_LIST \
+ ((sizeof(struct bch_owner_list_head) - \
+ offsetof(struct bch_owner_list_head, heads)) / \
+ sizeof(struct bch_nvm_pages_owner_head))
+
+/* The on-media bit order is local CPU order */
+struct bch_nvm_pages_sb {
+ __u64 csum;
+ __u64 ns_start;
+ __u64 sb_offset;
+ __u64 version;
+ __u8 magic[16];
+ __u8 uuid[16];
+ __u32 page_size;
+ __u32 total_namespaces_nr;
+ __u32 this_namespace_nr;
+ union {
+ __u8 set_uuid[16];
+ __u64 set_magic;
+ };
This doesn't look like it packs right either.
This is my mimicry from bcache code, which uses the least significant 8
bytes from the randomly generated UUID as a magic number. It is solid
and not changed during the whole life cycle for the nvm pages set.
quoted
+
+ __u64 flags;
+ __u64 seq;
+
+ __u64 feature_compat;
+ __u64 feature_incompat;
+ __u64 feature_ro_compat;
+
+ /* For allocable nvm pages from buddy systems */
+ __u64 pages_offset;
+ __u64 pages_total;
+
+ __u64 pad[8];
+
+ /* Only on the first name space */
+ struct bch_owner_list_head *owner_list_head;
And here's another pointer...
Same reason for I use it as an in-memory pointer.
The above definition is just using all the structures as in-memory
object, the difference is just they are non-volatiled after reboot.
Thanks.
Coly Li
Hi Jens,
This is the first wave bcache patches for Linux v5.12.
It is nice to see in this round we have 3 new patch contributors:
Jianpeng Ma, Qiaowei Ren and Kai Krakow.
In this series, the EXPERIMENTAL patches from Jianpeng Ma, Qiaowei Ren
and me are initial effort to store bcache meta-data on NVDIMM namespace.
The NVDIMM space is managed and mapped via DAX interface, and accessed
by linear address. In this submission we store bcache journal on NVDIMM,
in future bcache btree nodes and other meta data will be added in too,
before we remove the EXPERIMENTAL statues.
Dongdong Tao contributes a performance optimization when
bcache cache buckets are highly fregmented, Dongdong's patch makes the
dirty data writeback faster and from his benchmark reprots such changes
have recognized improvement for randome write I/O thoughput and latency
for highly fregmented buckets, and no regression for regular I/O
observed.
Kai Krakow contributes 4 patches to offload system_wq usage to separated
btree_io_wq and bch_flush_wq. In his environment the daily backup job
throughput increases from 60.2MB/s to 419MB/s and accomplished time
reduced from 14h29m to 2h13m.
Joe Perches also contributes a fine code stype fix which I pick for this
submission.
Please take them for Linux v5.12 merge window.
Applied 1-6 for now, that weird situation with the user visible header
needs to get resolved before it can go any further.
Thanks for taking care of the patches and offering your opinion. I will
ask you and other developers' suggestion for a proper form for the data
structure definition.
Coly Li