This is the v12 effort for supporting NVDIMM for bcache journal (some
versions may not posted with version numbers).
The major change of this version is the full pointer of on-media data
structure is replaced by per-namespace offset. Now a pointer address is
calculated by namespace base mapping address + per-namespace offset.
The code logic is same as previous version, all changes are only related
to the base+offset style pointer replacement.
The nvm-pages allocator is a buddy-like allocator, which allocates size
in power-of-2 pages from the NVDIMM namespace. User space tool 'bcache'
has a new added '-M' option to format a NVDIMM namespace and register it
via sysfs interface as a bcache meta device. The nvm-pages kernel code
does a DAX mapping to map the whole namespace into system's memory
address range, and allocating the pages to requestion like typical buddy
allocator does. The major difference is nvm-pages allocator maintains
the pages allocated to each requester by an allocation list which stored
on NVDIMM too. Allocation list of different requester is tracked by a
pre-defined UUID, all the pages tracked in all allocation lists are
treated as allocated busy pages and won't be initialized into buddy
system after the system reboot.
The bcache journal code may request a block of power-of-2 size pages
from the nvm-pages allocator, normally it is a range of 256MB or 512MB
continuous pages range. During meta data journaling, the in-memory jsets
go into the calculated nvdimm pages location by kernel memcpy routine.
So the journaling I/Os won't go into block device (e.g. SSD) anymore,
the write and read for journal jsets happen on NVDIMM.
Intel developers Jianpeng Ma and Qiaowei Ren compose the initial code of
nvm-pages, the related patches are,
- 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 recs list head for allocated pages by specific uuid
All the code depends on Linux libnvdimm and dax drivers, the bcache nvm-
pages allocator can be treated as user of these two drivers.
I modify the bcache code to recognize the nvm meta device feature,
initialize journal on NVDIMM, and do journal I/Os on NVDIMM in the
following patches,
- bcache: add initial data structures for nvm pages
- bcache: use bucket index to set GC_MARK_METADATA for journal buckets
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
In this series, all previously addressed issue via code reviews are all
fixed. And all known issue during testing are fixed. The code survives
from 24+ hours smoking and I/O pressure testing among many reboots, it
works well as expected.
All the code is EXPERIMENTAL, they won't be enabled by default until we
feel the NVDIMM support is completed and stable.
Although there are some experts helped to review the code logic, but we
do appreciate if more people may help to review the code. It is quite
common that bcache patches don't have enough code reviewer, but this
time I do need help for more review or comments on this series.
Thanks in advance.
Coly Li
---
Coly Li (7):
bcache: add initial data structures for nvm pages
bcache: use bucket index to set GC_MARK_METADATA for journal buckets
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
Jianpeng Ma (5):
bcache: initialize the nvm pages allocator
bcache: initialization of the buddy
bcache: bch_nvmpg_alloc_pages() of the buddy
bcache: bch_nvmpg_free_pages() of the buddy allocator
bcache: get recs list head for allocated pages by specific uuid
drivers/md/bcache/Kconfig | 10 +
drivers/md/bcache/Makefile | 1 +
drivers/md/bcache/btree.c | 6 +-
drivers/md/bcache/features.h | 9 +
drivers/md/bcache/journal.c | 325 +++++++++--
drivers/md/bcache/journal.h | 2 +-
drivers/md/bcache/nvm-pages.c | 931 ++++++++++++++++++++++++++++++++
drivers/md/bcache/nvm-pages.h | 127 +++++
drivers/md/bcache/super.c | 53 +-
include/uapi/linux/bcache-nvm.h | 253 +++++++++
10 files changed, 1649 insertions(+), 68 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
This patch initializes the prototype data structures for nvm pages
allocator,
- struct bch_nvmpg_sb
This is the super block allocated on each nvdimm namespace for the nvm
pages allocator. A nvdimm pages allocator set may have multiple name-
spaces, bch_nvmpg_sb->set_uuid is used to mark which nvdimm set this
namespace belongs to.
- struct bch_nvmpg_header
This is a table for all heads of all allocation record lists. An allo-
cation record list traces all page(s) allocated from nvdimm namespace(s)
to a specific requester (identified by uuid). After system reboot, a
requester can retrieve all previously allocated nvdimm pages from its
record list by a pre-defined uuid.
- struct bch_nvmpg_head
This is a head of an allocation record list. Each nvdimm pages
requester (typically it's a driver) has and only has one allocation
record list, and an allocated nvdimm page only belongs to a specific
allocation record list. Member uuid[] will be set as the requester's
uuid, e.g. for bcache it is the cache set uuid. Member label is not
mandatory, it is a human-readable string for debug purpose. The nvm
offset format pointers recs_offset[] point to the location of actual
allocator record lists on each namespace of the nvdimm pages allocator
set. Each per namespace record list is represented by the following
struct bch_nvmpg_recs.
- struct bch_nvmpg_recs
This structure represents a requester's allocation record list. Member
uuid is same value as the uuid of its corresponding struct
bch_nvmpg_head. Member recs[] is a table of struct bch_pgalloc_rec
objects to trace all allocated nvmdimm pages. If the table recs[] is
full, the nvmpg format offset is a pointer points to the next struct
bch_nvmpg_recs object, nvm pages allocator will look for available free
allocation record there. All the linked struct bch_nvmpg_recs objects
compose a requester's alloction record list which is headed by the above
struct bch_nvmpg_head.
- struct bch_nvmpg_recs
This structure records a range of allocated nvdimm pages. Member pgoff
is offset in unit of page size of this allocation range. Member order
indicates size of the allocation range by (1 << order) in unit of page
size. Because the nvdimm pages allocator set may have multiple nvdimm
namespaces, member ns_id is used to identify which namespace the pgoff
belongs to.
- Bits 0 - 51: pgoff - is pages offset of the allocated pages.
- Bits 52 - 57: order - allocaed size in page_size * order-of-2
- Bits 58 - 60: ns_id - identify which namespace the pages stays on
- Bits 61 - 63: reserved.
Since each of the allocated nvm pages are power of 2, using 6 bits to
represent allocated size can have (1<<(1<<64) - 1) * PAGE_SIZE maximum
value. It can be a 76 bits width range size in byte for 4KB page size,
which is large enough currently.
All the structure members having _offset suffix are in a special fomat.
E.g. bch_nvmpg_sb.{sb_offset, pages_offset, set_header_offset},
bch_nvmpg_head.recs_offset, bch_nvmpg_recs.{head_offset, next_offset},
the offset value is 64bit, the most significant 3 bits are used to
identify which namespace this offset belongs to, and the rested 61 bits
are actual offset inside the namespace. Following patches will have
helper routines to do the conversion between memory pointer and offset.
Signed-off-by: Coly Li <redacted>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
Cc: Ying Huang <redacted>
---
include/uapi/linux/bcache-nvm.h | 253 ++++++++++++++++++++++++++++++++
1 file changed, 253 insertions(+)
create mode 100644 include/uapi/linux/bcache-nvm.h
@@ -0,0 +1,253 @@+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */++#ifndef _UAPI_BCACHE_NVM_H+#define _UAPI_BCACHE_NVM_H++/*+*BcacheonNVDIMMdatastructures+*/++/*+*-structbch_nvmpg_sb+*Thisisthesuperblockallocatedoneachnvdimmnamespaceforthenvm+*pagesallocator.Anvdimmpagesallocatorsetmayhavemultiplenamespaces,+*bch_nvmpg_sb->set_uuidisusedtomarkwhichnvdimmsetthisnamespace+*belongsto.+*+*-structbch_nvmpg_header+*Thisisatableforallheadsofallallocationrecordlists.Anallo-+*cationrecordlisttracesallpage(s)allocatedfromnvdimmnamespace(s)to+*aspecificrequester(identifiedbyuuid).Aftersystemreboot,arequester+*canretrieveallpreviouslyallocatednvdimmpagesfromitsrecordlistbya+*pre-defineduuid.+*+*-structbch_nvmpg_head+*Thisisaheadofanallocationrecordlist.Eachnvdimmpagesrequester+*(typicallyit'sadriver)hasandonlyhasoneallocationrecordlist,and+*anallocatednvdimmpageonlybedlonestoaspecificallocationrecordlist.+*Memberuuid[]willbesetastherequester'suuid,e.g.forbcacheitisthe+*cachesetuuid.Memberlabelisnotmandatory,itisahuman-readablestring+*fordebugpurpose.Thenvmoffsetformatpointersrecs_offset[]pointtothe+*locationofactualallocatorrecordlistsoneachnamespaceofthenvdimm+*pagesallocatorset.Eachpernamespacerecordlistisrepresentedbythe+*followingstructbch_nvmpg_recs.+*+*-structbch_nvmpg_recs+*Thisstructurerepresentsarequester'sallocationrecordlist.Memberuuid+*issamevalueastheuuidofitscorrespondingstructbch_nvmpg_head.Member+*recs[]isatableofstructbch_pgalloc_recobjectstotraceallallocated+*nvmdimmpages.Ifthetablerecs[]isfull,thenvmpgformatoffsetisa+*pointerpointstothenextstructbch_nvmpg_recsobject,nvmpagesallocator+*willlookforavailablefreeallocationrecordthere.Allthelinked+*structbch_nvmpg_recsobjectscomposearequester'salloctionrecordlist+*whichisheadedbytheabovestructbch_nvmpg_head.+*+*-structbch_nvmpg_rec+*Thisstructurerecordsarangeofallocatednvdimmpages.Memberpgoffis+*offsetinunitofpagesizeofthisallocationrange.Memberorderindicates+*sizeoftheallocationrangeby(1<<order)inunitofpagesize.Because+*thenvdimmpagesallocatorsetmayhavemultiplenvdimmnamespaces,member+*ns_idisusedtoidentifywhichnamespacethepgoffbelongsto.+*+*Allallocationrecordlistsarestoredonthefirstinitializednvdimmname-+*space(ns_id0).Themetadatadefaultlayoutofnvmpagesallocatoron+*namespace0is,+*+*0+---------------------------------++*||+*4KB+---------------------------------+<--BCH_NVMPG_SB_OFFSET+*|bch_nvmpg_sb|+*8KB+---------------------------------+<--BCH_NVMPG_RECLIST_HEAD_OFFSET+*|bch_nvmpg_header|+*||+*16KB+---------------------------------+<--BCH_NVMPG_SYSRECS_OFFSET+*|bch_nvmpg_recs|+*|(nvmpagesinternalusage)|+*24KB+---------------------------------++*||+*||+*16MB+---------------------------------+<--BCH_NVMPG_START+*|allocablenvmpages|+*|forbuddyallocator|+*end+---------------------------------++*+*+*+*Metadatadefaultlayoutonrestednvdimmnamespaces,+*+*0+---------------------------------++*||+*4KB+---------------------------------+<--BCH_NVMPG_SB_OFFSET+*|bch_nvmpg_sb|+*8KB+---------------------------------++*||+*||+*||+*||+*||+*||+*16MB+---------------------------------+<--BCH_NVMPG_START+*|allocablenvmpages|+*|forbuddyallocator|+*end+---------------------------------++*+*+*-Thenvmpgoffsetformatpointer+*Allmembernamesendingwith_offsetinthisheaderarenvmpgoffset+*formatpointer.Theoffsetformatis,+*[highest3bits:ns_id]+*[rested61bits:offsetinNo.ns_idnamespace]+*+*Theaboveoffsetisbyteunit,theproceduretoreferenceanvmpgoffset+*formatpointeris,+*1)Identifythenamespacerelatedin-memorystructurebyns_idfromthe+*highest3bitsofoffsetvalue.+*2)GettheDAXmappingbaseaddressfromthein-memorystructure.+*3)CalculatetheactualmemoryaddressonnvdimmbyplusingtheDAXbase+*addresswithoffsetvalueinrestedlow61bits.+*Allrelatedin-memorystructureandconversionroutinesdon'tbelongto+*userspaceapi,theyaredefinedbynvm-pagesallocatorcodein+*drivers/md/bcache/nvm-pages.{c,h}+*+*/++#include<linux/types.h>++/* In sectors */+#define BCH_NVMPG_SB_OFFSET 4096+#define BCH_NVMPG_START (16 << 20)++#define BCH_NVMPG_LBL_SIZE 32+#define BCH_NVMPG_NS_MAX 8++#define BCH_NVMPG_RECLIST_HEAD_OFFSET (8<<10)+#define BCH_NVMPG_SYSRECS_OFFSET (16<<10)++#define BCH_NVMPG_SB_VERSION 0+#define BCH_NVMPG_SB_VERSION_MAX 0++staticconst__u8bch_nvmpg_magic[]={+0x17,0xbd,0x53,0x7f,0x1b,0x23,0xd6,0x83,+0x46,0xa4,0xf8,0x28,0x17,0xda,0xec,0xa9};+staticconst__u8bch_nvmpg_recs_magic[]={+0x39,0x25,0x3f,0xf7,0x27,0x17,0xd0,0xb9,+0x10,0xe6,0xd2,0xda,0x38,0x68,0x26,0xae};++/* takes 64bit width */+structbch_nvmpg_rec{+union{+struct{+__u64pgoff:52;+__u64order:6;+__u64ns_id:3;+__u64reserved:3;+};+__u64_v;+};+};++structbch_nvmpg_recs{+union{+struct{+/*+*Anvmpgoffsetformatpointerto+*structbch_nvmpg_head+*/+__u64head_offset;+/*+*Anvmpgoffsetformatpointerto+*structbch_nvm_pgalloc_recswhichcontains+*thenextrecs[]array.+*/+__u64next_offset;+__u8magic[16];+__u8uuid[16];+__u32size;+__u32used;+__u64_pad[4];+structbch_nvmpg_recrecs[];+};+__u8pad[8192];+};+};++#define BCH_NVMPG_MAX_RECS \+((sizeof(structbch_nvmpg_recs)-\+offsetof(structbch_nvmpg_recs,recs))/\+sizeof(structbch_nvmpg_rec))++#define BCH_NVMPG_HD_STAT_FREE 0x0+#define BCH_NVMPG_HD_STAT_ALLOC 0x1+structbch_nvmpg_head{+__u8uuid[16];+__u8label[BCH_NVMPG_LBL_SIZE];+__u32state;+__u32flags;+/*+*Arrayofoffsetvaluesfromthenvmpgoffsetformat+*pointers,eachofthepointerpointstoaper-namespace+*structbch_nvmpg_recs.+*/+__u64recs_offset[BCH_NVMPG_NS_MAX];+};++/* heads[0] is always for nvm_pages internal usage */+structbch_nvmpg_set_header{+union{+struct{+__u32size;+__u32used;+__u64_pad[4];+structbch_nvmpg_headheads[];+};+__u8pad[8192];+};+};++#define BCH_NVMPG_MAX_HEADS \+((sizeof(structbch_nvmpg_set_header)-\+offsetof(structbch_nvmpg_set_header,heads))/\+sizeof(structbch_nvmpg_head))++/* The on-media bit order is local CPU order */+structbch_nvmpg_sb{+__u64csum;+__u64sb_offset;+__u64ns_start;+__u64version;+__u8magic[16];+__u8uuid[16];+__u32page_size;+__u32total_ns;+__u32this_ns;+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];++/*+*Anvmpgoffsetformatpointer,itpoints+*tostructbch_nvmpg_set_headerwhichis+*storedonlyonthefirstnamespace.+*/+__u64set_header_offset;++/* Just for csum_set() */+__u32keys;+__u64d[0];+};++#endif /* _UAPI_BCACHE_NVM_H */
From: Jianpeng Ma <redacted>
This patch define the prototype data structures in memory and
initializes the nvm pages allocator.
The nvm address space which is managed by this allocator 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 register_namespace()
to register the nvdimm device (like /dev/pmemX) into this allocator as
the instance of struct nvm_namespace.
Reported-by: Randy Dunlap <redacted>
Signed-off-by: Jianpeng Ma <redacted>
Co-developed-by: Qiaowei Ren <redacted>
Signed-off-by: Qiaowei Ren <redacted>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jens Axboe <axboe@kernel.dk>
---
drivers/md/bcache/Kconfig | 10 +
drivers/md/bcache/Makefile | 1 +
drivers/md/bcache/nvm-pages.c | 339 ++++++++++++++++++++++++++++++++++
drivers/md/bcache/nvm-pages.h | 96 ++++++++++
drivers/md/bcache/super.c | 3 +
5 files changed, 449 insertions(+)
create mode 100644 drivers/md/bcache/nvm-pages.c
create mode 100644 drivers/md/bcache/nvm-pages.h
@@ -0,0 +1,339 @@+// 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>++structbch_nvmpg_set*global_nvmpg_set;++void*bch_nvmpg_offset_to_ptr(unsignedlongoffset)+{+intns_id=BCH_NVMPG_GET_NS_ID(offset);+structbch_nvmpg_ns*ns=global_nvmpg_set->ns_tbl[ns_id];++if(offset==0)+returnNULL;++ns_id=BCH_NVMPG_GET_NS_ID(offset);+ns=global_nvmpg_set->ns_tbl[ns_id];++if(ns)+return(void*)(ns->base_addr+BCH_NVMPG_GET_OFFSET(offset));++pr_err("Invalid ns_id %u\n",ns_id);+returnNULL;+}++unsignedlongbch_nvmpg_ptr_to_offset(structbch_nvmpg_ns*ns,void*ptr)+{+intns_id=ns->ns_id;+unsignedlongoffset=(unsignedlong)(ptr-ns->base_addr);++returnBCH_NVMPG_OFFSET(ns_id,offset);+}++staticvoidrelease_ns_tbl(structbch_nvmpg_set*set)+{+inti;+structbch_nvmpg_ns*ns;++for(i=0;i<BCH_NVMPG_NS_MAX;i++){+ns=set->ns_tbl[i];+if(ns){+blkdev_put(ns->bdev,FMODE_READ|FMODE_WRITE|FMODE_EXEC);+set->ns_tbl[i]=NULL;+set->attached_ns--;+kfree(ns);+}+}++if(set->attached_ns)+pr_err("unexpected attached_ns: %u\n",set->attached_ns);+}++staticvoidrelease_nvmpg_set(structbch_nvmpg_set*set)+{+release_ns_tbl(set);+kfree(set);+}++/* Namespace 0 contains all meta data of the nvmpg allocation set */+staticintinit_nvmpg_set_header(structbch_nvmpg_ns*ns)+{+structbch_nvmpg_set_header*set_header;++if(ns->ns_id!=0){+pr_err("unexpected ns_id %u for first nvmpg namespace.\n",+ns->ns_id);+return-EINVAL;+}++set_header=bch_nvmpg_offset_to_ptr(ns->sb->set_header_offset);++mutex_lock(&global_nvmpg_set->lock);+global_nvmpg_set->set_header=set_header;+global_nvmpg_set->heads_size=set_header->size;+global_nvmpg_set->heads_used=set_header->used;+mutex_unlock(&global_nvmpg_set->lock);++return0;+}++staticintattach_nvmpg_set(structbch_nvmpg_ns*ns)+{+structbch_nvmpg_sb*sb=ns->sb;+intrc=0;++mutex_lock(&global_nvmpg_set->lock);++if(global_nvmpg_set->ns_tbl[sb->this_ns]){+pr_err("ns_id %u already attached.\n",ns->ns_id);+rc=-EEXIST;+gotounlock;+}++if(ns->ns_id!=0){+pr_err("unexpected ns_id %u for first namespace.\n",ns->ns_id);+rc=-EINVAL;+gotounlock;+}++if(global_nvmpg_set->attached_ns>0){+pr_err("multiple namespace attaching not supported yet\n");+rc=-EOPNOTSUPP;+gotounlock;+}++if((global_nvmpg_set->attached_ns+1)>sb->total_ns){+pr_err("namespace counters error: attached %u > total %u\n",+global_nvmpg_set->attached_ns,+global_nvmpg_set->total_ns);+rc=-EINVAL;+gotounlock;+}++memcpy(global_nvmpg_set->set_uuid,sb->set_uuid,16);+global_nvmpg_set->ns_tbl[sb->this_ns]=ns;+global_nvmpg_set->attached_ns++;+global_nvmpg_set->total_ns=sb->total_ns;++unlock:+mutex_unlock(&global_nvmpg_set->lock);+returnrc;+}++staticintread_nvdimm_meta_super(structblock_device*bdev,+structbch_nvmpg_ns*ns)+{+structpage*page;+structbch_nvmpg_sb*sb;+uint64_texpected_csum=0;+intr;++page=read_cache_page_gfp(bdev->bd_inode->i_mapping,+BCH_NVMPG_SB_OFFSET>>PAGE_SHIFT,GFP_KERNEL);++if(IS_ERR(page))+return-EIO;++sb=(structbch_nvmpg_sb*)+(page_address(page)+offset_in_page(BCH_NVMPG_SB_OFFSET));++r=-EINVAL;+expected_csum=csum_set(sb);+if(expected_csum!=sb->csum){+pr_info("csum is not match with expected one\n");+gotoput_page;+}++if(memcmp(sb->magic,bch_nvmpg_magic,16)){+pr_info("invalid bch_nvmpg_magic\n");+gotoput_page;+}++if(sb->sb_offset!=+BCH_NVMPG_OFFSET(sb->this_ns,BCH_NVMPG_SB_OFFSET)){+pr_info("invalid superblock offset 0x%llx\n",sb->sb_offset);+gotoput_page;+}++r=-EOPNOTSUPP;+if(sb->total_ns!=1){+pr_info("multiple name space not supported yet.\n");+gotoput_page;+}+++r=0;+/* Necessary for DAX mapping */+ns->page_size=sb->page_size;+ns->pages_total=sb->pages_total;++put_page:+put_page(page);+returnr;+}++structbch_nvmpg_ns*bch_register_namespace(constchar*dev_path)+{+structbch_nvmpg_ns*ns=NULL;+structbch_nvmpg_sb*sb=NULL;+charbuf[BDEVNAME_SIZE];+structblock_device*bdev;+pgoff_tpgoff;+intid,err;+char*path;+longdax_ret=0;++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,+global_nvmpg_set);+if(IS_ERR(bdev)){+pr_err("get %s error: %ld\n",dev_path,PTR_ERR(bdev));+kfree(path);+returnERR_PTR(PTR_ERR(bdev));+}++err=-ENOMEM;+ns=kzalloc(sizeof(structbch_nvmpg_ns),GFP_KERNEL);+if(!ns)+gotobdput;++err=-EIO;+if(read_nvdimm_meta_super(bdev,ns)){+pr_err("%s read nvdimm meta super block failed.\n",+bdevname(bdev,buf));+gotofree_ns;+}++err=-EOPNOTSUPP;+if(!bdev_dax_supported(bdev,ns->page_size)){+pr_err("%s don't support DAX\n",bdevname(bdev,buf));+gotofree_ns;+}++err=-EINVAL;+if(bdev_dax_pgoff(bdev,0,ns->page_size,&pgoff)){+pr_err("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_err("can't by dax device by %s\n",bdevname(bdev,buf));+gotofree_ns;+}++err=-EINVAL;+id=dax_read_lock();+dax_ret=dax_direct_access(ns->dax_dev,pgoff,ns->pages_total,+&ns->base_addr,&ns->start_pfn);+if(dax_ret<=0){+pr_err("dax_direct_access error\n");+dax_read_unlock(id);+gotofree_ns;+}++if(dax_ret<ns->pages_total){+pr_warn("mapped range %ld is less than ns->pages_total %lu\n",+dax_ret,ns->pages_total);+}+dax_read_unlock(id);++sb=(structbch_nvmpg_sb*)(ns->base_addr+BCH_NVMPG_SB_OFFSET);++err=-EINVAL;+/* Check magic again to make sure DAX mapping is correct */+if(memcmp(sb->magic,bch_nvmpg_magic,16)){+pr_err("invalid bch_nvmpg_magic after DAX mapping\n");+gotofree_ns;+}++if((global_nvmpg_set->attached_ns>0)&&+memcmp(sb->set_uuid,global_nvmpg_set->set_uuid,16)){+pr_err("set uuid does not match with ns_id %u\n",ns->ns_id);+gotofree_ns;+}++if(sb->set_header_offset!=+BCH_NVMPG_OFFSET(sb->this_ns,BCH_NVMPG_RECLIST_HEAD_OFFSET)){+pr_err("Invalid header offset: this_ns %u, ns_id %llu, offset 0x%llx\n",+sb->this_ns,+BCH_NVMPG_GET_NS_ID(sb->set_header_offset),+BCH_NVMPG_GET_OFFSET(sb->set_header_offset));+gotofree_ns;+}++ns->page_size=sb->page_size;+ns->pages_offset=sb->pages_offset;+ns->pages_total=sb->pages_total;+ns->sb=sb;+ns->free=0;+ns->bdev=bdev;+ns->set=global_nvmpg_set;++err=attach_nvmpg_set(ns);+if(err<0)+gotofree_ns;++mutex_init(&ns->lock);++err=init_nvmpg_set_header(ns);+if(err<0)+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_nvmpg_init(void)+{+global_nvmpg_set=kzalloc(sizeof(*global_nvmpg_set),GFP_KERNEL);+if(!global_nvmpg_set)+return-ENOMEM;++global_nvmpg_set->total_ns=0;+mutex_init(&global_nvmpg_set->lock);++pr_info("bcache nvm init\n");+return0;+}++voidbch_nvmpg_exit(void)+{+release_nvmpg_set(global_nvmpg_set);+pr_info("bcache nvm exit\n");+}
From: Jianpeng Ma <redacted>
This nvm pages allocator will implement the simple buddy allocator to
anage the nvm address space. This patch initializes this buddy allocator
for new namespace.
the unit of alloc/free of the buddy allocator 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.
Reported-by: kernel test robot <redacted>
Reported-by: Dan Carpenter <redacted>
Signed-off-by: Jianpeng Ma <redacted>
Co-developed-by: Qiaowei Ren <redacted>
Signed-off-by: Qiaowei Ren <redacted>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jens Axboe <axboe@kernel.dk>
---
drivers/md/bcache/nvm-pages.c | 212 +++++++++++++++++++++++++++++++++-
drivers/md/bcache/nvm-pages.h | 12 ++
2 files changed, 221 insertions(+), 3 deletions(-)
@@ -75,10 +109,73 @@ static void release_nvmpg_set(struct bch_nvmpg_set *set)kfree(set);}+staticintvalidate_recs(intns_id,+structbch_nvmpg_head*head,+structbch_nvmpg_recs*recs)+{+if(memcmp(recs->magic,bch_nvmpg_recs_magic,16)){+pr_err("Invalid bch_nvmpg_recs magic\n");+return-EINVAL;+}++if(memcmp(recs->uuid,head->uuid,16)){+pr_err("Invalid bch_nvmpg_recs uuid\n");+return-EINVAL;+}++if(recs->head_offset!=+bch_nvmpg_ptr_to_offset(global_nvmpg_set->ns_tbl[ns_id],head)){+pr_err("Invalid recs head_offset\n");+return-EINVAL;+}++return0;+}++staticintreserve_nvmpg_recs(structbch_nvmpg_recs*recs)+{+inti,used=0;++for(i=0;i<recs->size;i++){+structbch_nvmpg_rec*r=&recs->recs[i];+structbch_nvmpg_ns*ns;+structpage*page;+void*addr;++if(r->pgoff==0)+continue;++ns=global_nvmpg_set->ns_tbl[r->ns_id];+addr=bch_nvmpg_rec_to_ptr(r);+if(addr<ns->base_addr){+pr_err("Invalid recorded address\n");+return-EINVAL;+}++/* init struct page: index/private */+page=bch_nvmpg_va_to_pg(addr);+set_page_private(page,r->order);+page->index=r->pgoff;++reserve_nvmpg_pages(ns,r->pgoff,1L<<r->order);+used++;+}++if(used!=recs->used){+pr_err("used %d doesn't match recs->used %d\n",+used,recs->used);+return-EINVAL;+}++return0;+}+/* Namespace 0 contains all meta data of the nvmpg allocation set */staticintinit_nvmpg_set_header(structbch_nvmpg_ns*ns){structbch_nvmpg_set_header*set_header;+structbch_nvmpg_recs*sys_recs;+inti,j,used=0,rc=0;if(ns->ns_id!=0){pr_err("unexpected ns_id %u for first nvmpg namespace.\n",
@@ -92,9 +189,83 @@ static int init_nvmpg_set_header(struct bch_nvmpg_ns *ns)global_nvmpg_set->set_header=set_header;global_nvmpg_set->heads_size=set_header->size;global_nvmpg_set->heads_used=set_header->used;++/* Reserve the used space from buddy allocator */+reserve_nvmpg_pages(ns,0,div_u64(ns->pages_offset,ns->page_size));++sys_recs=ns->base_addr+BCH_NVMPG_SYSRECS_OFFSET;+for(i=0;i<set_header->size;i++){+structbch_nvmpg_head*head;++head=&set_header->heads[i];+if(head->state==BCH_NVMPG_HD_STAT_FREE)+continue;++used++;+if(used>global_nvmpg_set->heads_size){+pr_err("used heads %d > heads size %d.\n",+used,global_nvmpg_set->heads_size);+gotounlock;+}++for(j=0;j<BCH_NVMPG_NS_MAX;j++){+structbch_nvmpg_recs*recs;++recs=bch_nvmpg_offset_to_ptr(head->recs_offset[j]);++/* Iterate the recs list */+while(recs){+rc=validate_recs(j,head,recs);+if(rc<0)+gotounlock;++rc=reserve_nvmpg_recs(recs);+if(rc<0)+gotounlock;++bitmap_set(ns->recs_bitmap,recs-sys_recs,1);+recs=bch_nvmpg_offset_to_ptr(recs->next_offset);+}+}+}+unlock:mutex_unlock(&global_nvmpg_set->lock);+returnrc;+}-return0;+staticvoidbch_nvmpg_init_free_space(structbch_nvmpg_ns*ns)+{+unsignedintstart,end,pages;+inti;+structpage*page;+pgoff_tpgoff_start;++bitmap_for_each_clear_region(ns->pages_bitmap,+start,end,0,ns->pages_total){+pgoff_start=start;+pages=end-start;++while(pages){+void*addr;++for(i=BCH_MAX_ORDER-1;i>=0;i--){+if((pgoff_start%(1L<<i)==0)&&+(pages>=(1L<<i)))+break;+}++addr=bch_nvmpg_pgoff_to_ptr(ns,pgoff_start);+page=bch_nvmpg_va_to_pg(addr);+set_page_private(page,i);+page->index=pgoff_start;+__SetPageBuddy(page);+list_add((structlist_head*)&page->zone_device_data,+&ns->free_area[i]);++pgoff_start+=1L<<i;+pages-=1L<<i;+}+}}staticintattach_nvmpg_set(structbch_nvmpg_ns*ns)
@@ -68,6 +75,11 @@ struct bch_nvmpg_set {/* Indicate which field in bch_nvmpg_sb to be updated */#define BCH_NVMPG_TOTAL_NS 0 /* total_ns */+#define BCH_MAX_PGALLOC_RECS \+(min_t(unsignedint,64,\+(BCH_NVMPG_START-BCH_NVMPG_SYSRECS_OFFSET)/\+sizeof(structbch_nvmpg_recs)))+void*bch_nvmpg_offset_to_ptr(unsignedlongoffset);unsignedlongbch_nvmpg_ptr_to_offset(structbch_nvmpg_ns*ns,void*ptr);
From: Jianpeng Ma <redacted>
This patch implements the bch_nvmpg_alloc_pages() of the nvm pages buddy
allocator. In terms of function, this func is like current
page-buddy-alloc. But the differences are:
a: it need owner_uuid as parameter which record owner info. And it
make those info persistence.
b: it don't need flags like GFP_*. All allocs are the equal.
c: it don't trigger other ops etc swap/recycle.
Signed-off-by: Jianpeng Ma <redacted>
Co-developed-by: Qiaowei Ren <redacted>
Signed-off-by: Qiaowei Ren <redacted>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jens Axboe <axboe@kernel.dk>
---
drivers/md/bcache/nvm-pages.c | 210 ++++++++++++++++++++++++++++++++++
drivers/md/bcache/nvm-pages.h | 9 ++
2 files changed, 219 insertions(+)
@@ -268,6 +275,209 @@ static void bch_nvmpg_init_free_space(struct bch_nvmpg_ns *ns)}}++/* If not found, it will create if create == true */+staticstructbch_nvmpg_head*find_nvmpg_head(constchar*uuid,boolcreate)+{+structbch_nvmpg_set_header*set_header=global_nvmpg_set->set_header;+structbch_nvmpg_head*head=NULL;+inti;++if(set_header==NULL)+gotoout;++for(i=0;i<set_header->size;i++){+structbch_nvmpg_head*h=&set_header->heads[i];++if(h->state!=BCH_NVMPG_HD_STAT_ALLOC)+continue;++if(!memcmp(uuid,h->uuid,16)){+head=h;+break;+}+}++if(!head&&create){+u32used=set_header->used;++if(set_header->size>used){+head=&set_header->heads[used];+memset(head,0,sizeof(structbch_nvmpg_head));+head->state=BCH_NVMPG_HD_STAT_ALLOC;+memcpy(head->uuid,uuid,16);+global_nvmpg_set->heads_used++;+set_header->used++;+}else+pr_info("No free bch_nvmpg_head\n");+}++out:+returnhead;+}++staticstructbch_nvmpg_recs*find_empty_nvmpg_recs(void)+{+unsignedintstart;+structbch_nvmpg_ns*ns=global_nvmpg_set->ns_tbl[0];+structbch_nvmpg_recs*recs;++start=bitmap_find_next_zero_area(ns->recs_bitmap,+BCH_MAX_PGALLOC_RECS,0,1,0);+if(start>BCH_MAX_PGALLOC_RECS){+pr_info("No free struct bch_nvmpg_recs\n");+returnNULL;+}++bitmap_set(ns->recs_bitmap,start,1);+recs=(structbch_nvmpg_recs*)+bch_nvmpg_offset_to_ptr(BCH_NVMPG_SYSRECS_OFFSET)++start;++memset(recs,0,sizeof(structbch_nvmpg_recs));+returnrecs;+}+++staticstructbch_nvmpg_recs*find_nvmpg_recs(structbch_nvmpg_ns*ns,+structbch_nvmpg_head*head,+boolcreate)+{+intns_id=ns->sb->this_ns;+structbch_nvmpg_recs*prev_recs=NULL,*recs=NULL;++recs=bch_nvmpg_offset_to_ptr(head->recs_offset[ns_id]);++/* If create=false, we return recs[nr] */+if(!create)+returnrecs;++/*+*Ifcreate=true,itmeanweneedaemptystructbch_nvmpg_rec+*Soweshouldfindnon-emptystructbch_nvmpg_recsoralloc+*newstructbch_nvmpg_recs.Andreturnthisbch_nvmpg_recs+*/+while(recs&&(recs->used==recs->size)){+prev_recs=recs;+recs=bch_nvmpg_offset_to_ptr(recs->next_offset);+}++/* Found empty struct bch_nvmpg_recs */+if(recs)+returnrecs;++/* Need alloc new struct bch_nvmpg_recs */+recs=find_empty_nvmpg_recs();+if(recs){+unsignedlongoffset;++recs->next_offset=0;+recs->head_offset=bch_nvmpg_ptr_to_offset(ns,head);+memcpy(recs->magic,bch_nvmpg_recs_magic,16);+memcpy(recs->uuid,head->uuid,16);+recs->size=BCH_NVMPG_MAX_RECS;+recs->used=0;++offset=bch_nvmpg_ptr_to_offset(ns,recs);+if(prev_recs)+prev_recs->next_offset=offset;+else+head->recs_offset[ns_id]=offset;+}++returnrecs;+}++staticvoidadd_nvmpg_rec(structbch_nvmpg_ns*ns,+structbch_nvmpg_recs*recs,+void*kaddr,intorder)+{+inti;++for(i=0;i<recs->size;i++){+if(recs->recs[i].pgoff==0){+recs->recs[i].pgoff=bch_nvmpg_ptr_to_pgoff(ns,kaddr);+recs->recs[i].order=order;+recs->recs[i].ns_id=ns->sb->this_ns;+recs->used++;+break;+}+}+BUG_ON(i==recs->size);+}+++void*bch_nvmpg_alloc_pages(intorder,constchar*uuid)+{+void*kaddr=NULL;+structbch_nvmpg_head*head;+intn,o;++mutex_lock(&global_nvmpg_set->lock);+head=find_nvmpg_head(uuid,true);++if(!head){+pr_err("Cannot find bch_nvmpg_recs by uuid.\n");+gotounlock;+}++for(n=0;n<global_nvmpg_set->total_ns;n++){+structbch_nvmpg_ns*ns=global_nvmpg_set->ns_tbl[n];++if(!ns||(ns->free<(1L<<order)))+continue;++for(o=order;o<BCH_MAX_ORDER;o++){+structlist_head*list;+structpage*page,*buddy_page;++if(list_empty(&ns->free_area[o]))+continue;++list=ns->free_area[o].next;+page=container_of((void*)list,structpage,+zone_device_data);++list_del(list);++while(o!=order){+void*addr;+pgoff_tpgoff;++pgoff=page->index+(1L<<(o-1));+addr=bch_nvmpg_pgoff_to_ptr(ns,pgoff);+buddy_page=bch_nvmpg_va_to_pg(addr);+set_page_private(buddy_page,o-1);+buddy_page->index=pgoff;+__SetPageBuddy(buddy_page);+list_add((structlist_head*)&buddy_page->zone_device_data,+&ns->free_area[o-1]);+o--;+}++set_page_private(page,order);+__ClearPageBuddy(page);+ns->free-=1L<<order;+kaddr=bch_nvmpg_pgoff_to_ptr(ns,page->index);+break;+}++if(o<BCH_MAX_ORDER){+structbch_nvmpg_recs*recs;++recs=find_nvmpg_recs(ns,head,true);+/* ToDo: handle pgalloc_recs==NULL */+add_nvmpg_rec(ns,recs,kaddr,order);+break;+}+}++unlock:+mutex_unlock(&global_nvmpg_set->lock);+returnkaddr;+}+EXPORT_SYMBOL_GPL(bch_nvmpg_alloc_pages);+staticintattach_nvmpg_set(structbch_nvmpg_ns*ns){structbch_nvmpg_sb*sb=ns->sb;
@@ -75,6 +75,9 @@ struct bch_nvmpg_set {/* Indicate which field in bch_nvmpg_sb to be updated */#define BCH_NVMPG_TOTAL_NS 0 /* total_ns */+#define BCH_PGOFF_TO_KVADDR(pgoff) \+((void*)((unsignedlong)(pgoff)<<PAGE_SHIFT))+#define BCH_MAX_PGALLOC_RECS \(min_t(unsignedint,64,\(BCH_NVMPG_START-BCH_NVMPG_SYSRECS_OFFSET)/\
From: Jianpeng Ma <redacted>
This patch implements the bch_nvmpg_free_pages() of the buddy allocator.
The difference between this and page-buddy-free:
it need owner_uuid to free owner allocated pages, and must
persistent after free.
Signed-off-by: Jianpeng Ma <redacted>
Co-developed-by: Qiaowei Ren <redacted>
Signed-off-by: Qiaowei Ren <redacted>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jens Axboe <axboe@kernel.dk>
---
drivers/md/bcache/nvm-pages.c | 167 +++++++++++++++++++++++++++++++++-
drivers/md/bcache/nvm-pages.h | 3 +
2 files changed, 167 insertions(+), 3 deletions(-)
@@ -240,6 +240,51 @@ static int init_nvmpg_set_header(struct bch_nvmpg_ns *ns)returnrc;}+staticvoid__free_space(structbch_nvmpg_ns*ns,void*addr,intorder)+{+unsignedlongadd_pages=(1L<<order);+pgoff_tpgoff;+structpage*page;+void*va;++page=bch_nvmpg_va_to_pg(addr);+WARN_ON((!page)||(page->private!=order));+pgoff=page->index;++while(order<BCH_MAX_ORDER-1){+structpage*buddy_page;++pgoff_tbuddy_pgoff=pgoff^(1L<<order);+pgoff_tparent_pgoff=pgoff&~(1L<<order);++if((parent_pgoff+(1L<<(order+1))>ns->pages_total))+break;++va=bch_nvmpg_pgoff_to_ptr(ns,buddy_pgoff);+buddy_page=bch_nvmpg_va_to_pg(va);+WARN_ON(!buddy_page);++if(PageBuddy(buddy_page)&&(buddy_page->private==order)){+list_del((structlist_head*)&buddy_page->zone_device_data);+__ClearPageBuddy(buddy_page);+pgoff=parent_pgoff;+order++;+continue;+}+break;+}++va=bch_nvmpg_pgoff_to_ptr(ns,pgoff);+page=bch_nvmpg_va_to_pg(va);+WARN_ON(!page);+list_add((structlist_head*)&page->zone_device_data,+&ns->free_area[order]);+page->index=pgoff;+set_page_private(page,order);+__SetPageBuddy(page);+ns->free+=add_pages;+}+staticvoidbch_nvmpg_init_free_space(structbch_nvmpg_ns*ns){unsignedintstart,end,pages;
@@ -265,9 +310,9 @@ static void bch_nvmpg_init_free_space(struct bch_nvmpg_ns *ns)page=bch_nvmpg_va_to_pg(addr);set_page_private(page,i);page->index=pgoff_start;-__SetPageBuddy(page);-list_add((structlist_head*)&page->zone_device_data,-&ns->free_area[i]);++/* In order to update ns->free */+__free_space(ns,addr,i);pgoff_start+=1L<<i;pages-=1L<<i;
@@ -478,6 +523,121 @@ void *bch_nvmpg_alloc_pages(int order, const char *uuid)}EXPORT_SYMBOL_GPL(bch_nvmpg_alloc_pages);+staticinlinevoid*nvm_end_addr(structbch_nvmpg_ns*ns)+{+returnns->base_addr+(ns->pages_total<<PAGE_SHIFT);+}++staticinlineboolin_nvmpg_ns_range(structbch_nvmpg_ns*ns,+void*start_addr,void*end_addr)+{+return(start_addr>=ns->base_addr)&&(end_addr<nvm_end_addr(ns));+}++staticstructbch_nvmpg_ns*find_nvmpg_ns_by_addr(void*addr,intorder)+{+inti;+structbch_nvmpg_ns*ns;++for(i=0;i<global_nvmpg_set->total_ns;i++){+ns=global_nvmpg_set->ns_tbl[i];++if(ns&&in_nvmpg_ns_range(ns,addr,addr+(1L<<order)))+returnns;+}++returnNULL;+}++staticintremove_nvmpg_rec(structbch_nvmpg_recs*recs,intns_id,+void*kaddr,intorder)+{+structbch_nvmpg_head*head;+structbch_nvmpg_recs*prev_recs,*sys_recs;+structbch_nvmpg_ns*ns;+unsignedlongpgoff;+inti;++ns=global_nvmpg_set->ns_tbl[0];+pgoff=bch_nvmpg_ptr_to_pgoff(ns,kaddr);++head=bch_nvmpg_offset_to_ptr(recs->head_offset);+prev_recs=recs;+sys_recs=bch_nvmpg_offset_to_ptr(BCH_NVMPG_SYSRECS_OFFSET);+while(recs){+for(i=0;i<recs->size;i++){+structbch_nvmpg_rec*rec=&(recs->recs[i]);++if((rec->pgoff==pgoff)&&(rec->ns_id==ns_id)){+WARN_ON(rec->order!=order);+rec->_v=0;+recs->used--;++if(recs->used==0){+intrecs_pos=recs-sys_recs;++if(recs==prev_recs)+head->recs_offset[ns_id]=+recs->next_offset;+else+prev_recs->next_offset=+recs->next_offset;++recs->next_offset=0;+recs->head_offset=0;++bitmap_clear(ns->recs_bitmap,recs_pos,1);+}+gotoout;+}+}+prev_recs=recs;+recs=bch_nvmpg_offset_to_ptr(recs->next_offset);+}+out:+return(recs?0:-ENOENT);+}++voidbch_nvmpg_free_pages(void*addr,intorder,constchar*uuid)+{+structbch_nvmpg_ns*ns;+structbch_nvmpg_head*head;+structbch_nvmpg_recs*recs;+intr;++mutex_lock(&global_nvmpg_set->lock);++ns=find_nvmpg_ns_by_addr(addr,order);+if(!ns){+pr_err("can't find namespace by given kaddr from namespace\n");+gotounlock;+}++head=find_nvmpg_head(uuid,false);+if(!head){+pr_err("can't found bch_nvmpg_head by uuid\n");+gotounlock;+}++recs=find_nvmpg_recs(ns,head,false);+if(!recs){+pr_err("can't find bch_nvmpg_recs by uuid\n");+gotounlock;+}++r=remove_nvmpg_rec(recs,ns->sb->this_ns,addr,order);+if(r<0){+pr_err("can't find bch_nvmpg_rec\n");+gotounlock;+}++__free_space(ns,addr,order);++unlock:+mutex_unlock(&global_nvmpg_set->lock);+}+EXPORT_SYMBOL_GPL(bch_nvmpg_free_pages);+staticintattach_nvmpg_set(structbch_nvmpg_ns*ns){structbch_nvmpg_sb*sb=ns->sb;
From: Jianpeng Ma <redacted>
This patch implements bch_get_nvmpg_head() of the buddy allocator
to be used to get recs list head for allocated pages by specific
uuid. Then the requester (owner) can find all previous allocated
nvdimm pages by iterating the recs list.
Signed-off-by: Jianpeng Ma <redacted>
Co-developed-by: Qiaowei Ren <redacted>
Signed-off-by: Qiaowei Ren <redacted>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Jens Axboe <axboe@kernel.dk>
---
drivers/md/bcache/nvm-pages.c | 6 ++++++
drivers/md/bcache/nvm-pages.h | 6 ++++++
2 files changed, 12 insertions(+)
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>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/btree.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
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>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/features.h | 9 +++++++++
1 file changed, 9 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: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/journal.c | 117 ++++++++++++++++++++++++++++++++++
drivers/md/bcache/journal.h | 2 +-
drivers/md/bcache/nvm-pages.c | 9 +++
drivers/md/bcache/nvm-pages.h | 1 +
drivers/md/bcache/super.c | 18 +++---
5 files changed, 136 insertions(+), 11 deletions(-)
@@ -982,3 +984,118 @@ int bch_journal_alloc(struct cache_set *c)return0;}++#if defined(CONFIG_BCACHE_NVM_PAGES)++staticvoid*find_journal_nvmpg_base(structbch_nvmpg_head*nvmpg_head,+structcache*ca)+{+void*addr=NULL;+unsignedlongjnl_offset,jnl_pgoff,jnl_ns_id;+inti;++jnl_offset=(unsignedlong)ca->sb.d[0];+jnl_ns_id=BCH_NVMPG_GET_NS_ID(jnl_offset);+jnl_pgoff=BCH_NVMPG_GET_OFFSET(jnl_offset)>>PAGE_SHIFT;++for(i=0;i<BCH_NVMPG_NS_MAX;i++){+structbch_nvmpg_recs*recs;+structbch_nvmpg_rec*rec;+unsignedlongrecs_offset=0;+intj;++recs_offset=nvmpg_head->recs_offset[i];+recs=bch_nvmpg_offset_to_ptr(recs_offset);+while(recs){+for(j=0;j<recs->size;j++){+rec=&recs->recs[j];+if((rec->pgoff!=jnl_pgoff)||+(rec->ns_id!=jnl_ns_id))+continue;++addr=bch_nvmpg_offset_to_ptr(jnl_offset);+gotoout;+}+recs_offset=recs->next_offset;+recs=bch_nvmpg_offset_to_ptr(recs_offset);+}+}++out:+returnaddr;+}++staticvoid*get_journal_nvmpg_space(structcache*ca)+{+structbch_nvmpg_head*head=NULL;+void*ret=NULL;+intorder;++head=bch_get_nvmpg_head(ca->sb.set_uuid);+if(head){+ret=find_journal_nvmpg_base(head,ca);+if(ret)+gotofound;+}++order=ilog2((ca->sb.bucket_size*+ca->sb.njournal_buckets)/PAGE_SECTORS);+ret=bch_nvmpg_alloc_pages(order,ca->sb.set_uuid);+if(ret)+memset(ret,0,(1<<order)*PAGE_SIZE);+found:+returnret;+}++#endif /* CONFIG_BCACHE_NVM_PAGES */++staticint__bch_journal_nvdimm_init(structcache*ca)+{+intret=-1;++#if defined(CONFIG_BCACHE_NVM_PAGES)+inti;+void*jnl_base=NULL;++jnl_base=get_journal_nvmpg_space(ca);+if(!jnl_base){+pr_err("Failed to get journal space from nvdimm\n");+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++){+unsignedlongjnl_offset;++jnl_offset=bch_nvmpg_ptr_to_offset(bch_nvmpg_id_to_ns(0),+jnl_base+(bucket_bytes(ca)*i));+ca->sb.d[i]=jnl_offset;+}++ret=0;+out:+#endif /* CONFIG_BCACHE_NVM_PAGES */++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 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 legacy 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>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/journal.c | 120 +++++++++++++++++++++++++-----------
drivers/md/bcache/super.c | 3 +-
2 files changed, 85 insertions(+), 38 deletions(-)
@@ -793,7 +777,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 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.
The code comments added in journal_read_bucket() is noticed by kernel
test robot and Dan Carpenter, it explains why it is safe to only check
!bch_has_feature_nvdimm_meta() condition in the if() statement when
CONFIG_BCACHE_NVM_PAGES is not configured. To avoid confusion from the
bogus warning message from static checking tool.
Signed-off-by: Coly Li <redacted>
Reported-by: kernel test robot <redacted>
Reported-by: Dan Carpenter <redacted>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/journal.c | 88 ++++++++++++++++++++++++++++++-------
1 file changed, 71 insertions(+), 17 deletions(-)
@@ -55,26 +97,27 @@ static int journal_read_bucket(struct cache *ca, struct list_head *list,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_index,len,offset,&cl);+/*+*IfCONFIG_BCACHE_NVM_PAGESisnotdefined,thefeaturebit+*BCH_FEATURE_INCOMPAT_NVDIMM_METAwon'tinincompatible+*supportfeatureset,acachedeviceformatwithfeaturebit+*BCH_FEATURE_INCOMPAT_NVDIMM_METAwillfailmuchearlierin+*read_super()bybch_has_unknown_incompat_features().+*ThereforewhenCONFIG_BCACHE_NVM_PAGESisnotdefine,itis+*safetoignorethebch_has_feature_nvdimm_meta()condition.+*/+#if defined(CONFIG_BCACHE_NVM_PAGES)+else+j=__jnl_rd_nvm_bkt(ca,bucket_index,len,offset);+#endif/* 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);
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>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Jianpeng Ma <redacted>
Cc: Qiaowei Ren <redacted>
---
drivers/md/bcache/super.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
From: Dan Williams <hidden> Date: 2021-08-12 05:43:56
On Wed, Aug 11, 2021 at 10:04 AM Coly Li [off-list ref] wrote:
quoted hunk
From: Jianpeng Ma <redacted>
This patch define the prototype data structures in memory and
initializes the nvm pages allocator.
The nvm address space which is managed by this allocator 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 register_namespace()
to register the nvdimm device (like /dev/pmemX) into this allocator as
the instance of struct nvm_namespace.
Reported-by: Randy Dunlap <redacted>
Signed-off-by: Jianpeng Ma <redacted>
Co-developed-by: Qiaowei Ren <redacted>
Signed-off-by: Qiaowei Ren <redacted>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jens Axboe <axboe@kernel.dk>
---
drivers/md/bcache/Kconfig | 10 +
drivers/md/bcache/Makefile | 1 +
drivers/md/bcache/nvm-pages.c | 339 ++++++++++++++++++++++++++++++++++
drivers/md/bcache/nvm-pages.h | 96 ++++++++++
drivers/md/bcache/super.c | 3 +
5 files changed, 449 insertions(+)
create mode 100644 drivers/md/bcache/nvm-pages.c
create mode 100644 drivers/md/bcache/nvm-pages.h
@@ -0,0 +1,339 @@+// 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>++structbch_nvmpg_set*global_nvmpg_set;++void*bch_nvmpg_offset_to_ptr(unsignedlongoffset)+{+intns_id=BCH_NVMPG_GET_NS_ID(offset);+structbch_nvmpg_ns*ns=global_nvmpg_set->ns_tbl[ns_id];++if(offset==0)+returnNULL;++ns_id=BCH_NVMPG_GET_NS_ID(offset);+ns=global_nvmpg_set->ns_tbl[ns_id];++if(ns)+return(void*)(ns->base_addr+BCH_NVMPG_GET_OFFSET(offset));++pr_err("Invalid ns_id %u\n",ns_id);+returnNULL;+}++unsignedlongbch_nvmpg_ptr_to_offset(structbch_nvmpg_ns*ns,void*ptr)+{+intns_id=ns->ns_id;+unsignedlongoffset=(unsignedlong)(ptr-ns->base_addr);++returnBCH_NVMPG_OFFSET(ns_id,offset);+}++staticvoidrelease_ns_tbl(structbch_nvmpg_set*set)+{+inti;+structbch_nvmpg_ns*ns;++for(i=0;i<BCH_NVMPG_NS_MAX;i++){+ns=set->ns_tbl[i];+if(ns){+blkdev_put(ns->bdev,FMODE_READ|FMODE_WRITE|FMODE_EXEC);+set->ns_tbl[i]=NULL;+set->attached_ns--;+kfree(ns);+}+}++if(set->attached_ns)+pr_err("unexpected attached_ns: %u\n",set->attached_ns);+}++staticvoidrelease_nvmpg_set(structbch_nvmpg_set*set)+{+release_ns_tbl(set);+kfree(set);+}++/* Namespace 0 contains all meta data of the nvmpg allocation set */+staticintinit_nvmpg_set_header(structbch_nvmpg_ns*ns)+{+structbch_nvmpg_set_header*set_header;++if(ns->ns_id!=0){+pr_err("unexpected ns_id %u for first nvmpg namespace.\n",+ns->ns_id);+return-EINVAL;+}++set_header=bch_nvmpg_offset_to_ptr(ns->sb->set_header_offset);++mutex_lock(&global_nvmpg_set->lock);+global_nvmpg_set->set_header=set_header;+global_nvmpg_set->heads_size=set_header->size;+global_nvmpg_set->heads_used=set_header->used;+mutex_unlock(&global_nvmpg_set->lock);++return0;+}++staticintattach_nvmpg_set(structbch_nvmpg_ns*ns)+{+structbch_nvmpg_sb*sb=ns->sb;+intrc=0;++mutex_lock(&global_nvmpg_set->lock);++if(global_nvmpg_set->ns_tbl[sb->this_ns]){+pr_err("ns_id %u already attached.\n",ns->ns_id);+rc=-EEXIST;+gotounlock;+}++if(ns->ns_id!=0){+pr_err("unexpected ns_id %u for first namespace.\n",ns->ns_id);+rc=-EINVAL;+gotounlock;+}++if(global_nvmpg_set->attached_ns>0){+pr_err("multiple namespace attaching not supported yet\n");+rc=-EOPNOTSUPP;+gotounlock;+}++if((global_nvmpg_set->attached_ns+1)>sb->total_ns){+pr_err("namespace counters error: attached %u > total %u\n",+global_nvmpg_set->attached_ns,+global_nvmpg_set->total_ns);+rc=-EINVAL;+gotounlock;+}++memcpy(global_nvmpg_set->set_uuid,sb->set_uuid,16);+global_nvmpg_set->ns_tbl[sb->this_ns]=ns;+global_nvmpg_set->attached_ns++;+global_nvmpg_set->total_ns=sb->total_ns;++unlock:+mutex_unlock(&global_nvmpg_set->lock);+returnrc;+}++staticintread_nvdimm_meta_super(structblock_device*bdev,+structbch_nvmpg_ns*ns)+{+structpage*page;+structbch_nvmpg_sb*sb;+uint64_texpected_csum=0;+intr;++page=read_cache_page_gfp(bdev->bd_inode->i_mapping,+BCH_NVMPG_SB_OFFSET>>PAGE_SHIFT,GFP_KERNEL);++if(IS_ERR(page))+return-EIO;++sb=(structbch_nvmpg_sb*)+(page_address(page)+offset_in_page(BCH_NVMPG_SB_OFFSET));++r=-EINVAL;+expected_csum=csum_set(sb);+if(expected_csum!=sb->csum){+pr_info("csum is not match with expected one\n");+gotoput_page;+}++if(memcmp(sb->magic,bch_nvmpg_magic,16)){+pr_info("invalid bch_nvmpg_magic\n");+gotoput_page;+}++if(sb->sb_offset!=+BCH_NVMPG_OFFSET(sb->this_ns,BCH_NVMPG_SB_OFFSET)){+pr_info("invalid superblock offset 0x%llx\n",sb->sb_offset);+gotoput_page;+}++r=-EOPNOTSUPP;+if(sb->total_ns!=1){+pr_info("multiple name space not supported yet.\n");+gotoput_page;+}+++r=0;+/* Necessary for DAX mapping */+ns->page_size=sb->page_size;+ns->pages_total=sb->pages_total;++put_page:+put_page(page);+returnr;+}++structbch_nvmpg_ns*bch_register_namespace(constchar*dev_path)+{+structbch_nvmpg_ns*ns=NULL;+structbch_nvmpg_sb*sb=NULL;+charbuf[BDEVNAME_SIZE];+structblock_device*bdev;+pgoff_tpgoff;+intid,err;+char*path;+longdax_ret=0;++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,+global_nvmpg_set);+if(IS_ERR(bdev)){+pr_err("get %s error: %ld\n",dev_path,PTR_ERR(bdev));+kfree(path);+returnERR_PTR(PTR_ERR(bdev));+}++err=-ENOMEM;+ns=kzalloc(sizeof(structbch_nvmpg_ns),GFP_KERNEL);+if(!ns)+gotobdput;++err=-EIO;+if(read_nvdimm_meta_super(bdev,ns)){+pr_err("%s read nvdimm meta super block failed.\n",+bdevname(bdev,buf));+gotofree_ns;+}++err=-EOPNOTSUPP;+if(!bdev_dax_supported(bdev,ns->page_size)){+pr_err("%s don't support DAX\n",bdevname(bdev,buf));+gotofree_ns;+}++err=-EINVAL;+if(bdev_dax_pgoff(bdev,0,ns->page_size,&pgoff)){+pr_err("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_err("can't by dax device by %s\n",bdevname(bdev,buf));+gotofree_ns;+}++err=-EINVAL;+id=dax_read_lock();+dax_ret=dax_direct_access(ns->dax_dev,pgoff,ns->pages_total,+&ns->base_addr,&ns->start_pfn);+if(dax_ret<=0){+pr_err("dax_direct_access error\n");+dax_read_unlock(id);+gotofree_ns;+}++if(dax_ret<ns->pages_total){+pr_warn("mapped range %ld is less than ns->pages_total %lu\n",+dax_ret,ns->pages_total);
This failure will become a common occurrence with CXL namespaces that
will have discontiguous range support. It's already the case for
dax-devices for soft-reserved memory [1]. In the CXL case the
discontinuity will be 256MB aligned, for the soft-reserved dax-devices
the discontinuity granularity can be as small as 4K.
[1]: https://elixir.bootlin.com/linux/v5.14-rc5/source/drivers/dax/device.c#L414
On Wed, Aug 11, 2021 at 10:04 AM Coly Li [off-list ref] wrote:
quoted
From: Jianpeng Ma <redacted>
This patch define the prototype data structures in memory and
initializes the nvm pages allocator.
The nvm address space which is managed by this allocator 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 register_namespace()
to register the nvdimm device (like /dev/pmemX) into this allocator as
the instance of struct nvm_namespace.
Reported-by: Randy Dunlap <redacted>
Signed-off-by: Jianpeng Ma <redacted>
Co-developed-by: Qiaowei Ren <redacted>
Signed-off-by: Qiaowei Ren <redacted>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dan Williams <redacted>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jens Axboe <axboe@kernel.dk>
---
drivers/md/bcache/Kconfig | 10 +
drivers/md/bcache/Makefile | 1 +
drivers/md/bcache/nvm-pages.c | 339 ++++++++++++++++++++++++++++++++++
drivers/md/bcache/nvm-pages.h | 96 ++++++++++
drivers/md/bcache/super.c | 3 +
5 files changed, 449 insertions(+)
create mode 100644 drivers/md/bcache/nvm-pages.c
create mode 100644 drivers/md/bcache/nvm-pages.h
[snipped]
quoted
+
+ err = -EOPNOTSUPP;
+ if (!bdev_dax_supported(bdev, ns->page_size)) {
+ pr_err("%s don't support DAX\n", bdevname(bdev, buf));
+ goto free_ns;
+ }
+
+ err = -EINVAL;
+ if (bdev_dax_pgoff(bdev, 0, ns->page_size, &pgoff)) {
+ pr_err("invalid offset of %s\n", bdevname(bdev, buf));
+ goto free_ns;
+ }
+
+ err = -ENOMEM;
+ ns->dax_dev = fs_dax_get_by_bdev(bdev);
+ if (!ns->dax_dev) {
+ pr_err("can't by dax device by %s\n", bdevname(bdev, buf));
+ goto free_ns;
+ }
+
+ err = -EINVAL;
+ id = dax_read_lock();
+ dax_ret = dax_direct_access(ns->dax_dev, pgoff, ns->pages_total,
+ &ns->base_addr, &ns->start_pfn);
+ if (dax_ret <= 0) {
+ pr_err("dax_direct_access error\n");
+ dax_read_unlock(id);
+ goto free_ns;
+ }
+
+ if (dax_ret < ns->pages_total) {
+ pr_warn("mapped range %ld is less than ns->pages_total %lu\n",
+ dax_ret, ns->pages_total);
Hi Dan,
Many thanks for your information.
This failure will become a common occurrence with CXL namespaces that
will have discontiguous range support. It's already the case for
dax-devices for soft-reserved memory [1]. In the CXL case the
discontinuity will be 256MB aligned, for the soft-reserved dax-devices
the discontinuity granularity can be as small as 4K.
[1]: https://elixir.bootlin.com/linux/v5.14-rc5/source/drivers/dax/device.c#L414
Fortunately the on-media allocation list format works with multiple
ranges of the namespace. For the in-memory struct bch_nvmpg_ns currently
assumes the namespace is a flat continuous range. Yes, we need to
consider and support multiple ranges in struct bch_nvmpg_ns for buddy
allocation initialization to skip the discontinuous gap.
It will be in the to-do list for next work. Thanks for your comments and
hint.
Coly Li
Hi Jens,
Could you please consider take the v12 series for Linux v5.15 merge window?
In this version the full pointer in the on-media data structures are
modified to per-namespace offset, and all previous review comments are
fixed.
There is no more comments for 4 hours, and this series survives in my
smoking test for 24+ hours, as an EXPERIMENTAL code the current status
is fine IMHO.
Thanks in advance.
Coly Li
On 8/12/21 1:02 AM, Coly Li wrote:
This is the v12 effort for supporting NVDIMM for bcache journal (some
versions may not posted with version numbers).
The major change of this version is the full pointer of on-media data
structure is replaced by per-namespace offset. Now a pointer address is
calculated by namespace base mapping address + per-namespace offset.
The code logic is same as previous version, all changes are only related
to the base+offset style pointer replacement.
The nvm-pages allocator is a buddy-like allocator, which allocates size
in power-of-2 pages from the NVDIMM namespace. User space tool 'bcache'
has a new added '-M' option to format a NVDIMM namespace and register it
via sysfs interface as a bcache meta device. The nvm-pages kernel code
does a DAX mapping to map the whole namespace into system's memory
address range, and allocating the pages to requestion like typical buddy
allocator does. The major difference is nvm-pages allocator maintains
the pages allocated to each requester by an allocation list which stored
on NVDIMM too. Allocation list of different requester is tracked by a
pre-defined UUID, all the pages tracked in all allocation lists are
treated as allocated busy pages and won't be initialized into buddy
system after the system reboot.
The bcache journal code may request a block of power-of-2 size pages
from the nvm-pages allocator, normally it is a range of 256MB or 512MB
continuous pages range. During meta data journaling, the in-memory jsets
go into the calculated nvdimm pages location by kernel memcpy routine.
So the journaling I/Os won't go into block device (e.g. SSD) anymore,
the write and read for journal jsets happen on NVDIMM.
Intel developers Jianpeng Ma and Qiaowei Ren compose the initial code of
nvm-pages, the related patches are,
- 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 recs list head for allocated pages by specific uuid
All the code depends on Linux libnvdimm and dax drivers, the bcache nvm-
pages allocator can be treated as user of these two drivers.
I modify the bcache code to recognize the nvm meta device feature,
initialize journal on NVDIMM, and do journal I/Os on NVDIMM in the
following patches,
- bcache: add initial data structures for nvm pages
- bcache: use bucket index to set GC_MARK_METADATA for journal buckets
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
In this series, all previously addressed issue via code reviews are all
fixed. And all known issue during testing are fixed. The code survives
from 24+ hours smoking and I/O pressure testing among many reboots, it
works well as expected.
All the code is EXPERIMENTAL, they won't be enabled by default until we
feel the NVDIMM support is completed and stable.
Although there are some experts helped to review the code logic, but we
do appreciate if more people may help to review the code. It is quite
common that bcache patches don't have enough code reviewer, but this
time I do need help for more review or comments on this series.
Thanks in advance.
Coly Li
---
Coly Li (7):
bcache: add initial data structures for nvm pages
bcache: use bucket index to set GC_MARK_METADATA for journal buckets
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
Jianpeng Ma (5):
bcache: initialize the nvm pages allocator
bcache: initialization of the buddy
bcache: bch_nvmpg_alloc_pages() of the buddy
bcache: bch_nvmpg_free_pages() of the buddy allocator
bcache: get recs list head for allocated pages by specific uuid
drivers/md/bcache/Kconfig | 10 +
drivers/md/bcache/Makefile | 1 +
drivers/md/bcache/btree.c | 6 +-
drivers/md/bcache/features.h | 9 +
drivers/md/bcache/journal.c | 325 +++++++++--
drivers/md/bcache/journal.h | 2 +-
drivers/md/bcache/nvm-pages.c | 931 ++++++++++++++++++++++++++++++++
drivers/md/bcache/nvm-pages.h | 127 +++++
drivers/md/bcache/super.c | 53 +-
include/uapi/linux/bcache-nvm.h | 253 +++++++++
10 files changed, 1649 insertions(+), 68 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