This patch creates the testing module for nvm pages allocator. Before this module is loaded, the super block needs to be writen into nvdimm device (like /dev/pmemX). Signed-off-by: Jianpeng Ma <jianpeng.ma@xxxxxxxxx> Signed-off-by: Qiaowei Ren <qiaowei.ren@xxxxxxxxx> --- drivers/md/bcache/Kconfig | 6 ++ drivers/md/bcache/Makefile | 2 + drivers/md/bcache/test-nvm.c | 202 +++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 drivers/md/bcache/test-nvm.c diff --git a/drivers/md/bcache/Kconfig b/drivers/md/bcache/Kconfig index fdec9905ef40..68302a9cd476 100644 --- a/drivers/md/bcache/Kconfig +++ b/drivers/md/bcache/Kconfig @@ -41,3 +41,9 @@ config BCACHE_NVM_PAGES depends on BCACHE help nvm pages allocator for bcache. + +config BCACHE_NVM_PAGES_TEST + tristate "Testing for NVM pages" + depends on BCACHE_NVM_PAGES + help + Testing module for NVM pages allocator. diff --git a/drivers/md/bcache/Makefile b/drivers/md/bcache/Makefile index 948e5ed2ca66..7b7d3535f4ef 100644 --- a/drivers/md/bcache/Makefile +++ b/drivers/md/bcache/Makefile @@ -5,3 +5,5 @@ obj-$(CONFIG_BCACHE) += bcache.o bcache-y := alloc.o bset.o btree.o closure.o debug.o extents.o\ io.o journal.o movinggc.o request.o stats.o super.o sysfs.o trace.o\ util.o writeback.o features.o nvm-pages.o + +obj-$(CONFIG_BCACHE_NVM_PAGES_TEST) += test-nvm.o diff --git a/drivers/md/bcache/test-nvm.c b/drivers/md/bcache/test-nvm.c new file mode 100644 index 000000000000..f2e5a9d69843 --- /dev/null +++ b/drivers/md/bcache/test-nvm.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nvdimm page-buddy test case + * + * Copyright (c) 2021, Intel Corporation. + * Copyright (c) 2021, Qiaowei Ren <qiaowei.ren@xxxxxxxxx>. + * Copyright (c) 2021, Jianpeng Ma <jianpeng.ma@xxxxxxxxx>. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/crc32.h> +#include <linux/uuid.h> +#include <linux/prandom.h> +#include <linux/pagemap.h> +#include <linux/pfn_t.h> +#include "nvm-pages.h" + +static char *host = "NVDIMM device name"; +module_param(host, charp, 0444); + +#define MAX_OWNER 10 + +static pgoff_t vaddr_to_nvm_pgoff(struct bch_nvm_namespace *ns, void *kaddr) +{ + return (kaddr - ns->kaddr) / PAGE_SIZE; +} + +static void print_nvm_extent(struct bch_nvm_alloced_recs *extents) +{ + struct list_head *list = extents->extent_head.next; + struct bch_nvm_namespace *ns = extents->ns; + struct bch_extent *e; + pgoff_t pgoff; + + while (list != &extents->extent_head) { + e = container_of(list, struct bch_extent, list); + pgoff = vaddr_to_nvm_pgoff(ns, e->kaddr); + pr_info(" [%ld ~ %u)", pgoff, e->nr); + list = list->next; + } + pr_info("\n"); +} + +static void print_owner_list_info(struct bch_nvm_set *nvm_set, bool print_extent) +{ + struct bch_owner_list *owner_list; + struct bch_nvm_alloced_recs *extents; + int i, j; + + for (i = 0; i < nvm_set->owner_list_used; i++) { + owner_list = nvm_set->owner_lists[i]; + pr_info("owner uuid=%pU\n", owner_list->owner_uuid); + for (j = 0; j < nvm_set->total_namespaces_nr; j++) { + if (owner_list->alloced_recs[j]) { + extents = owner_list->alloced_recs[j]; + pr_info("\t nvm uuid=%pU, allocated extents=%u\n", + extents->ns->uuid, extents->nr); + if (print_extent) + print_nvm_extent(extents); + } + } + } +} + +static void print_buddy_info(struct bch_nvm_namespace *ns) +{ + int i; + struct list_head *list; + struct page *page; + u64 expected_free_pages = 0; + + pr_info("namespace total_page=%lld, buddy_range[%lld -%lld], free_pages=%lld\n", + ns->pages_total, ns->pages_offset / ns->page_size, ns->pages_total, ns->free); + + for (i = 0; i < BCH_MAX_ORDER; i++) { + pr_info("order=%d, page range:\n", i); + list = ns->free_area[i].next; + while (list != &ns->free_area[i]) { + page = container_of((void *)list, struct page, zone_device_data); + pr_info(" [%ld -- %ld) ", page->index, page->index + (1 << page->private)); + expected_free_pages += 1 << page->private; + list = list->next; + } + } + WARN_ON(ns->free != expected_free_pages); + pr_info("expected_free_pages=%lld\n", expected_free_pages); +} + +/* alloc all page by order=0 */ +static void alloc_all_pages(struct bch_nvm_set *nvm_set) +{ + char owner_uuid[16]; + int i = 0; + int loops = 10; + u64 free_pages = nvm_set->nss[0]->free / loops; + long long pos = 0; + void **addr_array; + + generate_random_uuid(owner_uuid); + + addr_array = kvcalloc(nvm_set->nss[0]->free, sizeof(void *), GFP_KERNEL); + if (!addr_array) + return; + + pr_info("----%s----- starting\n", __func__); + for (i = 0; i < loops; i++) { + u64 alloc_pages = free_pages; + + while (alloc_pages > 0) { + addr_array[pos++] = bch_nvm_alloc_pages(0, owner_uuid); + alloc_pages--; + } + pr_info("loops=%d\n", i + 1); + } + + print_owner_list_info(nvm_set, true); + print_buddy_info(nvm_set->nss[0]); + + pr_info("----free all alloced pages--%lld-\n", pos); + pos--; + for (; pos >= 0 && (addr_array[pos] != NULL); pos--) { + struct page *page = virt_to_page(addr_array[pos]); + + bch_nvm_free_pages(addr_array[pos], page->private, owner_uuid); + + if (pos % free_pages == 0) + pr_info("current pos=%lld", pos); + } + print_owner_list_info(nvm_set, true); + print_buddy_info(nvm_set->nss[0]); + + kvfree(addr_array); +} + +static void alloc_random_order_pages(struct bch_nvm_set *nvm_set, char **owner_uuids) +{ + int i, order; + void *addr[MAX_OWNER]; + + pr_info("----%s----- starting\n", __func__); + for (i = 0; i < MAX_OWNER; i++) { + order = prandom_u32() % BCH_MAX_ORDER; + addr[i] = bch_nvm_alloc_pages(order, owner_uuids[i]); + } + + print_owner_list_info(nvm_set, true); + print_buddy_info(nvm_set->nss[0]); + + for (i = 0; i < MAX_OWNER; i++) { + struct page *page = virt_to_page(addr[i]); + + bch_nvm_free_pages(addr[i], page->private, owner_uuids[i]); + } + print_owner_list_info(nvm_set, true); + print_buddy_info(nvm_set->nss[0]); +} + +static int __init test_nvm_init(void) +{ + char **owner_uuids; + struct bch_nvm_set *nvm_set; + struct bch_nvm_namespace *ns = bch_register_namespace(host); + int i, r = 0; + + pr_info("nvm pages test enter: %s\n", host); + if (IS_ERR(ns)) { + pr_info("failed to register namespace: %s\n", host); + r = -EINVAL; + goto err; + } + + print_buddy_info(ns); + + owner_uuids = kcalloc(MAX_OWNER, sizeof(char *), GFP_KERNEL); + for (i = 0; i < MAX_OWNER; i++) { + owner_uuids[i] = kmalloc(16, GFP_KERNEL); + generate_random_uuid(owner_uuids[i]); + } + + nvm_set = ns->nvm_set; + alloc_all_pages(nvm_set); + alloc_random_order_pages(nvm_set, owner_uuids); + + for (i = 0; i < MAX_OWNER; i++) + kfree(owner_uuids[i]); + kfree(owner_uuids); + +err: + return r; +} +module_init(test_nvm_init); + +static void __exit test_nvm_exit(void) +{ + pr_info("nvm pages test exit\n"); +} +module_exit(test_nvm_exit); + +MODULE_LICENSE("GPL v2"); -- 2.17.1