From e0018bec9ee0d387c76decb4b599ac1d62f59d90 Mon Sep 17 00:00:00 2001 From: Anita Zhang Date: Aug 05 2023 07:09:24 +0000 Subject: 71.1-8.1: Sync c9 updates and latest elake patches - Sync 71.1-8 from c9 branch. - Patch bafc29a0..df411ebe from elake/ndctl fork for hs+fb --- diff --git a/SOURCES/0003-ndctl-test-Fix-btt-expect-table-compile-warning.patch b/SOURCES/0003-ndctl-test-Fix-btt-expect-table-compile-warning.patch new file mode 100644 index 0000000..5ec6ca1 --- /dev/null +++ b/SOURCES/0003-ndctl-test-Fix-btt-expect-table-compile-warning.patch @@ -0,0 +1,51 @@ +From ee7fabed859d07809dc3cfe6b23b7ad3b0c6cd73 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Tue, 12 Jan 2021 23:14:58 -0800 +Subject: [PATCH 003/217] ndctl/test: Fix btt expect table compile warning + +../test/libndctl.c:989:2: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] + 989 | unsigned long long expect_table[][2] = { + | ^~~~~~~~ + +...just move the declaration a few lines up. + +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +Link: https://lore.kernel.org/r/161052209839.1804207.11951679046842122849.stgit@dwillia2-desk3.amr.corp.intel.com +--- + test/libndctl.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/test/libndctl.c b/test/libndctl.c +index 24d72b3..fc65149 100644 +--- a/test/libndctl.c ++++ b/test/libndctl.c +@@ -980,12 +980,6 @@ static int check_btt_size(struct ndctl_btt *btt) + struct ndctl_ctx *ctx = ndctl_btt_get_ctx(btt); + struct ndctl_test *test = ndctl_get_private_data(ctx); + struct ndctl_namespace *ndns = ndctl_btt_get_namespace(btt); +- +- if (!ndns) +- return -ENXIO; +- +- ns_size = ndctl_namespace_get_size(ndns); +- sect_size = ndctl_btt_get_sector_size(btt); + unsigned long long expect_table[][2] = { + [0] = { + [0] = 0x11b5400, +@@ -1001,6 +995,12 @@ static int check_btt_size(struct ndctl_btt *btt) + }, + }; + ++ if (!ndns) ++ return -ENXIO; ++ ++ ns_size = ndctl_namespace_get_size(ndns); ++ sect_size = ndctl_btt_get_sector_size(btt); ++ + if (sect_size >= SZ_4K) + sect_select = 1; + else if (sect_size >= 512) +-- +2.27.0 + diff --git a/SOURCES/0004-ndctl-test-Cleanup-unnecessary-out-label.patch b/SOURCES/0004-ndctl-test-Cleanup-unnecessary-out-label.patch new file mode 100644 index 0000000..848d018 --- /dev/null +++ b/SOURCES/0004-ndctl-test-Cleanup-unnecessary-out-label.patch @@ -0,0 +1,57 @@ +From ff4030e88da2cdcaf52c0d7457cd30264ea8915b Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Tue, 12 Jan 2021 23:15:03 -0800 +Subject: [PATCH 004/217] ndctl/test: Cleanup unnecessary out label + +There are no cleanup actions to take in test_dax_remap(), and it is already +inconsistent for having a single return point, so remove the out label. + +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +Link: https://lore.kernel.org/r/161052210395.1804207.7318263492906073721.stgit@dwillia2-desk3.amr.corp.intel.com +--- + test/dax-pmd.c | 12 ++++-------- + 1 file changed, 4 insertions(+), 8 deletions(-) + +diff --git a/test/dax-pmd.c b/test/dax-pmd.c +index 401826d..b1251db 100644 +--- a/test/dax-pmd.c ++++ b/test/dax-pmd.c +@@ -83,20 +83,18 @@ int test_dax_remap(struct ndctl_test *test, int dax_fd, unsigned long align, voi + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGBUS, &act, 0)) { + perror("sigaction"); +- rc = EXIT_FAILURE; +- goto out; ++ return EXIT_FAILURE; + } + + /* test fault after device-dax instance disabled */ + if (sigsetjmp(sj_env, 1)) { + if (!fsdax && align > SZ_4K) { + fprintf(stderr, "got expected SIGBUS after mremap() of device-dax\n"); +- rc = 0; ++ return 0; + } else { + fprintf(stderr, "unpexpected SIGBUS after mremap()\n"); +- rc = -EIO; ++ return -EIO; + } +- goto out; + } + + *(int *) anon = 0xAA; +@@ -107,9 +105,7 @@ int test_dax_remap(struct ndctl_test *test, int dax_fd, unsigned long align, voi + return -ENXIO; + } + +- rc = 0; +-out: +- return rc; ++ return 0; + } + + int test_dax_directio(int dax_fd, unsigned long align, void *dax_addr, off_t offset) +-- +2.27.0 + diff --git a/SOURCES/0005-ndctl-test-Fix-device-dax-mremap-test.patch b/SOURCES/0005-ndctl-test-Fix-device-dax-mremap-test.patch new file mode 100644 index 0000000..6fb688c --- /dev/null +++ b/SOURCES/0005-ndctl-test-Fix-device-dax-mremap-test.patch @@ -0,0 +1,40 @@ +From 6694afe31dd67d186199a58d2252be5ea3472692 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Tue, 12 Jan 2021 23:15:09 -0800 +Subject: [PATCH 005/217] ndctl/test: Fix device-dax mremap() test + +The test_dax_remap() test is a regression check for mishandling of mremap() +in the presence of pmd_devmap(). My understanding is that it was a fuzzing +condition not something an application would want to do in practice. + +On recent kernels with commit 73d5e0629919 ("mremap: check if it's possible +to split original vma"), the test fails for device-dax. That seems an +equally acceptable result of attempting this remap, so update the test +rather than ask the kernel to preserve the old behaviour. + +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +Link: https://lore.kernel.org/r/161052210936.1804207.17896246772670985157.stgit@dwillia2-desk3.amr.corp.intel.com +--- + test/dax-pmd.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/test/dax-pmd.c b/test/dax-pmd.c +index b1251db..7648e34 100644 +--- a/test/dax-pmd.c ++++ b/test/dax-pmd.c +@@ -69,6 +69,11 @@ int test_dax_remap(struct ndctl_test *test, int dax_fd, unsigned long align, voi + + remap = mremap(addr, REMAP_SIZE, REMAP_SIZE, MREMAP_MAYMOVE|MREMAP_FIXED, anon); + ++ if (remap == MAP_FAILED) { ++ fprintf(stderr, "%s: mremap failed, that's ok too\n", __func__); ++ return 0; ++ } ++ + if (remap != anon) { + rc = -ENXIO; + perror("mremap"); +-- +2.27.0 + diff --git a/SOURCES/0006-ndctl-test-Exercise-soft_offline_page-corner-cases.patch b/SOURCES/0006-ndctl-test-Exercise-soft_offline_page-corner-cases.patch new file mode 100644 index 0000000..b46bfbf --- /dev/null +++ b/SOURCES/0006-ndctl-test-Exercise-soft_offline_page-corner-cases.patch @@ -0,0 +1,134 @@ +From 940acf65a61595e8c0db3aebe1c74307acbbef68 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Tue, 12 Jan 2021 23:15:14 -0800 +Subject: [PATCH 006/217] ndctl/test: Exercise soft_offline_page() corner cases + +Test soft-offline injection into PMEM namespace metadata and user mapped +space. Both attempts should fail on kernels with a pfn_to_online_page() +implementation that considers subsection ZONE_DEVICE ranges. + +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +Link: https://lore.kernel.org/r/161052211455.1804207.13884321454837200896.stgit@dwillia2-desk3.amr.corp.intel.com +--- + test/dax-poison.c | 19 +++++++++++++++++++ + test/device-dax.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 64 insertions(+) + +diff --git a/test/dax-poison.c b/test/dax-poison.c +index a4ef12e..4e09761 100644 +--- a/test/dax-poison.c ++++ b/test/dax-poison.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -49,6 +50,7 @@ int test_dax_poison(struct ndctl_test *test, int dax_fd, unsigned long align, + unsigned char *addr = MAP_FAILED; + struct sigaction act; + unsigned x = x; ++ FILE *smaps; + void *buf; + int rc; + +@@ -94,6 +96,9 @@ int test_dax_poison(struct ndctl_test *test, int dax_fd, unsigned long align, + goto out; + } + ++ fprintf(stderr, "%s: mmap got %p align: %ld offset: %zd\n", ++ __func__, addr, align, offset); ++ + if (sigsetjmp(sj_env, 1)) { + if (sig_mcerr_ar) { + fprintf(stderr, "madvise triggered 'action required' sigbus\n"); +@@ -104,6 +109,20 @@ int test_dax_poison(struct ndctl_test *test, int dax_fd, unsigned long align, + } + } + ++ rc = madvise(addr + align / 2, 4096, MADV_SOFT_OFFLINE); ++ if (rc == 0) { ++ fprintf(stderr, "softoffline should always fail for dax\n"); ++ smaps = fopen("/proc/self/smaps", "r"); ++ do { ++ rc = fread(buf, 1, 4096, smaps); ++ fwrite(buf, 1, rc, stderr); ++ } while (rc); ++ fclose(smaps); ++ fail(); ++ rc = -ENXIO; ++ goto out; ++ } ++ + rc = madvise(addr + align / 2, 4096, MADV_HWPOISON); + if (rc) { + fail(); +diff --git a/test/device-dax.c b/test/device-dax.c +index 5f0da29..aad8fa5 100644 +--- a/test/device-dax.c ++++ b/test/device-dax.c +@@ -128,6 +128,44 @@ static int verify_data(struct daxctl_dev *dev, char *dax_buf, + return 0; + } + ++static int test_dax_soft_offline(struct ndctl_test *test, struct ndctl_namespace *ndns) ++{ ++ unsigned long long resource = ndctl_namespace_get_resource(ndns); ++ int fd, rc; ++ char *buf; ++ ++ if (resource == ULLONG_MAX) { ++ fprintf(stderr, "failed to get resource: %s\n", ++ ndctl_namespace_get_devname(ndns)); ++ return -ENXIO; ++ } ++ ++ fd = open("/sys/devices/system/memory/soft_offline_page", O_WRONLY); ++ if (fd < 0) { ++ fprintf(stderr, "failed to open soft_offline_page\n"); ++ return -ENOENT; ++ } ++ ++ rc = asprintf(&buf, "%#llx\n", resource); ++ if (rc < 0) { ++ fprintf(stderr, "failed to alloc resource\n"); ++ close(fd); ++ return -ENOMEM; ++ } ++ ++ fprintf(stderr, "%s: try to offline page @%#llx\n", __func__, resource); ++ rc = write(fd, buf, rc); ++ free(buf); ++ close(fd); ++ ++ if (rc >= 0) { ++ fprintf(stderr, "%s: should have failed\n", __func__); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ + static int __test_device_dax(unsigned long align, int loglevel, + struct ndctl_test *test, struct ndctl_ctx *ctx) + { +@@ -278,6 +316,13 @@ static int __test_device_dax(unsigned long align, int loglevel, + goto out; + } + ++ rc = test_dax_soft_offline(test, ndns); ++ if (rc) { ++ fprintf(stderr, "%s: failed dax soft offline\n", ++ ndctl_namespace_get_devname(ndns)); ++ goto out; ++ } ++ + rc = test_dax_poison(test, fd, align, NULL, 0, devdax); + if (rc) { + fprintf(stderr, "%s: failed dax poison\n", +-- +2.27.0 + diff --git a/SOURCES/0007-msft-Add-xlat_firmware_status-for-JEDEC-Byte-Address.patch b/SOURCES/0007-msft-Add-xlat_firmware_status-for-JEDEC-Byte-Address.patch new file mode 100644 index 0000000..aa0420e --- /dev/null +++ b/SOURCES/0007-msft-Add-xlat_firmware_status-for-JEDEC-Byte-Address.patch @@ -0,0 +1,73 @@ +From 11357d68b77392e4360ae2824e75bf8397a84885 Mon Sep 17 00:00:00 2001 +From: Redhairer Li +Date: Sat, 9 Jan 2021 23:36:33 +0800 +Subject: [PATCH 007/217] msft: Add xlat_firmware_status for JEDEC Byte + Addressable Energy Backed DSM + +Translate the status codes of the result of JEDEC Byte Addressable Energy Backed +DSM to generic errno style error codes. + +Signed-off-by: Li Redhairer +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +Link: https://lore.kernel.org/r/20210109153633.8493-1-redhairer.li@intel.com +--- + ndctl/lib/msft.c | 22 ++++++++++++++++++++++ + ndctl/lib/msft.h | 6 ++++++ + 2 files changed, 28 insertions(+) + +diff --git a/ndctl/lib/msft.c b/ndctl/lib/msft.c +index 145872c..3112799 100644 +--- a/ndctl/lib/msft.c ++++ b/ndctl/lib/msft.c +@@ -149,10 +149,32 @@ static unsigned int msft_cmd_smart_get_life_used(struct ndctl_cmd *cmd) + return 100 - CMD_MSFT_SMART(cmd)->nvm_lifetime; + } + ++static int msft_cmd_xlat_firmware_status(struct ndctl_cmd *cmd) ++{ ++ unsigned int status; ++ ++ status = cmd->get_firmware_status(cmd) & NDN_MSFT_STATUS_MASK; ++ ++ /* Common statuses */ ++ switch (status) { ++ case NDN_MSFT_STATUS_SUCCESS: ++ return 0; ++ case NDN_MSFT_STATUS_NOTSUPP: ++ return -EOPNOTSUPP; ++ case NDN_MSFT_STATUS_INVALPARM: ++ return -EINVAL; ++ case NDN_MSFT_STATUS_I2CERR: ++ return -EIO; ++ } ++ ++ return -ENOMSG; ++} ++ + struct ndctl_dimm_ops * const msft_dimm_ops = &(struct ndctl_dimm_ops) { + .new_smart = msft_dimm_cmd_new_smart, + .smart_get_flags = msft_cmd_smart_get_flags, + .smart_get_health = msft_cmd_smart_get_health, + .smart_get_media_temperature = msft_cmd_smart_get_media_temperature, + .smart_get_life_used = msft_cmd_smart_get_life_used, ++ .xlat_firmware_status = msft_cmd_xlat_firmware_status, + }; +diff --git a/ndctl/lib/msft.h b/ndctl/lib/msft.h +index 7cfd26f..978cc11 100644 +--- a/ndctl/lib/msft.h ++++ b/ndctl/lib/msft.h +@@ -50,4 +50,10 @@ struct ndn_pkg_msft { + union ndn_msft_cmd u; + } __attribute__((packed)); + ++#define NDN_MSFT_STATUS_MASK 0xffff ++#define NDN_MSFT_STATUS_SUCCESS 0 ++#define NDN_MSFT_STATUS_NOTSUPP 1 ++#define NDN_MSFT_STATUS_INVALPARM 2 ++#define NDN_MSFT_STATUS_I2CERR 3 ++ + #endif /* __NDCTL_MSFT_H__ */ +-- +2.27.0 + diff --git a/SOURCES/0008-ndctl-namespace-Fix-disable-namespace-accounting-rel.patch b/SOURCES/0008-ndctl-namespace-Fix-disable-namespace-accounting-rel.patch new file mode 100644 index 0000000..76b77cf --- /dev/null +++ b/SOURCES/0008-ndctl-namespace-Fix-disable-namespace-accounting-rel.patch @@ -0,0 +1,140 @@ +From fe626a8a8a1b1bc94ea95c693ec672109909e3dc Mon Sep 17 00:00:00 2001 +From: Redhairer Li +Date: Thu, 28 Jan 2021 22:03:39 +0800 +Subject: [PATCH 008/217] ndctl/namespace: Fix disable-namespace accounting + relative to seed devices + +Seed namespaces are included in "ndctl disable-namespace all". However +since the user never "creates" them it is surprising to see +"disable-namespace" report 1 more namespace relative to the number that +have been created. Catch attempts to disable a zero-sized namespace: + +Before: +{ + "dev":"namespace1.0", + "size":"492.00 MiB (515.90 MB)", + "blockdev":"pmem1" +} +{ + "dev":"namespace1.1", + "size":"492.00 MiB (515.90 MB)", + "blockdev":"pmem1.1" +} +{ + "dev":"namespace1.2", + "size":"492.00 MiB (515.90 MB)", + "blockdev":"pmem1.2" +} +disabled 4 namespaces + +After: +{ + "dev":"namespace1.0", + "size":"492.00 MiB (515.90 MB)", + "blockdev":"pmem1" +} +{ + "dev":"namespace1.3", + "size":"492.00 MiB (515.90 MB)", + "blockdev":"pmem1.3" +} +{ + "dev":"namespace1.1", + "size":"492.00 MiB (515.90 MB)", + "blockdev":"pmem1.1" +} +disabled 3 namespaces + +Signed-off-by: Redhairer Li +Signed-off-by: Vishal Verma +Link: https://lore.kernel.org/linux-nvdimm/20210128140339.3080-1-redhairer.li@intel.com/ +--- + ndctl/lib/libndctl.c | 10 ++++++++-- + ndctl/namespace.c | 8 ++++---- + ndctl/region.c | 2 +- + 3 files changed, 13 insertions(+), 7 deletions(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index 36fb6fe..2f6d806 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -4602,6 +4602,7 @@ NDCTL_EXPORT int ndctl_namespace_disable_safe(struct ndctl_namespace *ndns) + const char *bdev = NULL; + char path[50]; + int fd; ++ unsigned long long size = ndctl_namespace_get_size(ndns); + + if (pfn && ndctl_pfn_is_enabled(pfn)) + bdev = ndctl_pfn_get_block_device(pfn); +@@ -4631,8 +4632,13 @@ NDCTL_EXPORT int ndctl_namespace_disable_safe(struct ndctl_namespace *ndns) + devname, bdev, strerror(errno)); + return -errno; + } +- } else +- ndctl_namespace_disable_invalidate(ndns); ++ } else { ++ if (size == 0) ++ /* No disable necessary due to no capacity allocated */ ++ return 1; ++ else ++ ndctl_namespace_disable_invalidate(ndns); ++ } + + return 0; + } +diff --git a/ndctl/namespace.c b/ndctl/namespace.c +index 0c8df9f..1feb74d 100644 +--- a/ndctl/namespace.c ++++ b/ndctl/namespace.c +@@ -1125,7 +1125,7 @@ static int namespace_prep_reconfig(struct ndctl_region *region, + } + + rc = ndctl_namespace_disable_safe(ndns); +- if (rc) ++ if (rc < 0) + return rc; + + ndctl_namespace_set_enforce_mode(ndns, NDCTL_NS_MODE_RAW); +@@ -1431,7 +1431,7 @@ static int dax_clear_badblocks(struct ndctl_dax *dax) + return -ENXIO; + + rc = ndctl_namespace_disable_safe(ndns); +- if (rc) { ++ if (rc < 0) { + error("%s: unable to disable namespace: %s\n", devname, + strerror(-rc)); + return rc; +@@ -1455,7 +1455,7 @@ static int pfn_clear_badblocks(struct ndctl_pfn *pfn) + return -ENXIO; + + rc = ndctl_namespace_disable_safe(ndns); +- if (rc) { ++ if (rc < 0) { + error("%s: unable to disable namespace: %s\n", devname, + strerror(-rc)); + return rc; +@@ -1478,7 +1478,7 @@ static int raw_clear_badblocks(struct ndctl_namespace *ndns) + return -ENXIO; + + rc = ndctl_namespace_disable_safe(ndns); +- if (rc) { ++ if (rc < 0) { + error("%s: unable to disable namespace: %s\n", devname, + strerror(-rc)); + return rc; +diff --git a/ndctl/region.c b/ndctl/region.c +index 3edb9b3..4552c4a 100644 +--- a/ndctl/region.c ++++ b/ndctl/region.c +@@ -70,7 +70,7 @@ static int region_action(struct ndctl_region *region, enum device_action mode) + case ACTION_DISABLE: + ndctl_namespace_foreach(region, ndns) { + rc = ndctl_namespace_disable_safe(ndns); +- if (rc) ++ if (rc < 0) + return rc; + } + rc = ndctl_region_disable_invalidate(region); +-- +2.27.0 + diff --git a/SOURCES/0009-zero_info_block-skip-seed-devices.patch b/SOURCES/0009-zero_info_block-skip-seed-devices.patch new file mode 100644 index 0000000..12d36c0 --- /dev/null +++ b/SOURCES/0009-zero_info_block-skip-seed-devices.patch @@ -0,0 +1,50 @@ +From fb13dfb8d84c4f0a749665c8f07179450b199f3e Mon Sep 17 00:00:00 2001 +From: Jeff Moyer +Date: Tue, 9 Feb 2021 16:51:53 -0500 +Subject: [PATCH 009/217] zero_info_block: skip seed devices + +Currently, ndctl destroy-namespace -f all will output errors of the +form: + + Error: destroy namespace: namespace0.0 failed to enable for zeroing, continuing + +for any zero-sized namespace. That particular namespace looks like this: + + { + "dev":"namespace0.0", + "mode":"raw", + "size":0, + "uuid":"00000000-0000-0000-0000-000000000000", + "sector_size":512, + "state":"disabled" + } + +This patch skips over namespaces with size=0 when zeroing out info +blocks. + +Fixes: 46654c2d60b70 ("ndctl/namespace: Always zero info-blocks") +Reported-by: Zhang Yi +Signed-off-by: Jeff Moyer +Signed-off-by: Vishal Verma +Link: https://lore.kernel.org/r/x49r1lohpty.fsf@segfault.boston.devel.redhat.com +--- + ndctl/namespace.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/ndctl/namespace.c b/ndctl/namespace.c +index 1feb74d..1e8a2cd 100644 +--- a/ndctl/namespace.c ++++ b/ndctl/namespace.c +@@ -1052,6 +1052,9 @@ static int zero_info_block(struct ndctl_namespace *ndns) + void *buf = NULL, *read_buf = NULL; + char path[50]; + ++ if (ndctl_namespace_get_size(ndns) == 0) ++ return 1; ++ + ndctl_namespace_set_raw_mode(ndns, 1); + rc = ndctl_namespace_enable(ndns); + if (rc < 0) { +-- +2.27.0 + diff --git a/SOURCES/0010-ndctl-update-.gitignore.patch b/SOURCES/0010-ndctl-update-.gitignore.patch new file mode 100644 index 0000000..b7432cf --- /dev/null +++ b/SOURCES/0010-ndctl-update-.gitignore.patch @@ -0,0 +1,51 @@ +From 5d0d4dc5ae1de82de92212c98297b24fbba09227 Mon Sep 17 00:00:00 2001 +From: QI Fuli +Date: Tue, 2 Feb 2021 22:02:06 +0900 +Subject: [PATCH 010/217] ndctl: update .gitignore + +Add Documentation/ndctl/attrs.adoc and *.lo to .gitignore. + +Signed-off-by: QI Fuli +Signed-off-by: Vishal Verma +Link: https://lore.kernel.org/r/20210202130206.3761-1-qi.fuli@fujitsu.com +--- + .gitignore | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/.gitignore b/.gitignore +index 3ef9ff7..53512b2 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -1,4 +1,5 @@ + *.o ++*.lo + *.xml + .deps/ + .libs/ +@@ -15,13 +16,13 @@ Makefile.in + *.1 + Documentation/daxctl/asciidoc.conf + Documentation/ndctl/asciidoc.conf ++Documentation/ndctl/attrs.adoc + Documentation/daxctl/asciidoctor-extensions.rb + Documentation/ndctl/asciidoctor-extensions.rb + .dirstamp + daxctl/config.h + daxctl/daxctl + daxctl/lib/libdaxctl.la +-daxctl/lib/libdaxctl.lo + daxctl/lib/libdaxctl.pc + *.a + ndctl/config.h +@@ -29,8 +30,6 @@ ndctl/lib/libndctl.pc + ndctl/ndctl + rhel/ + sles/ndctl.spec +-util/log.lo +-util/sysfs.lo + version.m4 + *.swp + cscope.files +-- +2.27.0 + diff --git a/SOURCES/0011-ndctl-test-add-checking-the-presence-of-jq-command-a.patch b/SOURCES/0011-ndctl-test-add-checking-the-presence-of-jq-command-a.patch new file mode 100644 index 0000000..cacd0a6 --- /dev/null +++ b/SOURCES/0011-ndctl-test-add-checking-the-presence-of-jq-command-a.patch @@ -0,0 +1,127 @@ +From 7ce2fddfa3f108036a2d81de4d2e66ac29e4631e Mon Sep 17 00:00:00 2001 +From: QI Fuli +Date: Wed, 3 Feb 2021 22:21:08 +0900 +Subject: [PATCH 011/217] ndctl/test: add checking the presence of jq command + ahead + +Due to the lack of jq command, the result of the test will be 'fail'. +This patch adds checking the presence of jq commmand ahead. +If there is no jq command in the system, the test will be marked as 'skip'. + +Signed-off-by: QI Fuli +Link: https://github.com/pmem/ndctl/issues/141 +Signed-off-by: Vishal Verma +Link: https://lore.kernel.org/r/20210203132108.6246-1-qi.fuli@fujitsu.com +--- + test/daxdev-errors.sh | 1 + + test/inject-error.sh | 2 ++ + test/inject-smart.sh | 1 + + test/label-compat.sh | 1 + + test/max_available_extent_ns.sh | 1 + + test/monitor.sh | 2 ++ + test/multi-dax.sh | 1 + + test/sector-mode.sh | 2 ++ + 8 files changed, 11 insertions(+) + +diff --git a/test/daxdev-errors.sh b/test/daxdev-errors.sh +index 6281f32..9547d78 100755 +--- a/test/daxdev-errors.sh ++++ b/test/daxdev-errors.sh +@@ -9,6 +9,7 @@ rc=77 + . $(dirname $0)/common + + check_min_kver "4.12" || do_skip "lacks dax dev error handling" ++check_prereq "jq" + + trap 'err $LINENO' ERR + +diff --git a/test/inject-error.sh b/test/inject-error.sh +index c636033..7d0b826 100755 +--- a/test/inject-error.sh ++++ b/test/inject-error.sh +@@ -11,6 +11,8 @@ err_count=8 + + . $(dirname $0)/common + ++check_prereq "jq" ++ + trap 'err $LINENO' ERR + + # sample json: +diff --git a/test/inject-smart.sh b/test/inject-smart.sh +index 94705df..4ca83b8 100755 +--- a/test/inject-smart.sh ++++ b/test/inject-smart.sh +@@ -166,6 +166,7 @@ do_tests() + } + + check_min_kver "4.19" || do_skip "kernel $KVER may not support smart (un)injection" ++check_prereq "jq" + modprobe nfit_test + rc=1 + +diff --git a/test/label-compat.sh b/test/label-compat.sh +index 340b93d..8ab2858 100755 +--- a/test/label-compat.sh ++++ b/test/label-compat.sh +@@ -10,6 +10,7 @@ BASE=$(dirname $0) + . $BASE/common + + check_min_kver "4.11" || do_skip "may not provide reliable isetcookie values" ++check_prereq "jq" + + trap 'err $LINENO' ERR + +diff --git a/test/max_available_extent_ns.sh b/test/max_available_extent_ns.sh +index 14d741d..343f3c9 100755 +--- a/test/max_available_extent_ns.sh ++++ b/test/max_available_extent_ns.sh +@@ -9,6 +9,7 @@ rc=77 + trap 'err $LINENO' ERR + + check_min_kver "4.19" || do_skip "kernel $KVER may not support max_available_size" ++check_prereq "jq" + + init() + { +diff --git a/test/monitor.sh b/test/monitor.sh +index cdab5e1..28c5541 100755 +--- a/test/monitor.sh ++++ b/test/monitor.sh +@@ -13,6 +13,8 @@ smart_supported_bus="" + + . $(dirname $0)/common + ++check_prereq "jq" ++ + trap 'err $LINENO' ERR + + check_min_kver "4.15" || do_skip "kernel $KVER may not support monitor service" +diff --git a/test/multi-dax.sh b/test/multi-dax.sh +index e932569..8496619 100755 +--- a/test/multi-dax.sh ++++ b/test/multi-dax.sh +@@ -9,6 +9,7 @@ rc=77 + . $(dirname $0)/common + + check_min_kver "4.13" || do_skip "may lack multi-dax support" ++check_prereq "jq" + + trap 'err $LINENO' ERR + +diff --git a/test/sector-mode.sh b/test/sector-mode.sh +index dd7013e..54fa806 100755 +--- a/test/sector-mode.sh ++++ b/test/sector-mode.sh +@@ -6,6 +6,8 @@ rc=77 + + . $(dirname $0)/common + ++check_prereq "jq" ++ + set -e + trap 'err $LINENO' ERR + +-- +2.27.0 + diff --git a/SOURCES/0012-Expose-ndctl_bus_nfit_translate_spa-as-a-public-func.patch b/SOURCES/0012-Expose-ndctl_bus_nfit_translate_spa-as-a-public-func.patch new file mode 100644 index 0000000..e873924 --- /dev/null +++ b/SOURCES/0012-Expose-ndctl_bus_nfit_translate_spa-as-a-public-func.patch @@ -0,0 +1,78 @@ +From c81fa15bafb1295aaa7d7f09500c3fbdd68b0011 Mon Sep 17 00:00:00 2001 +From: "Tsaur, Erwin" +Date: Thu, 4 Mar 2021 17:18:04 -0800 +Subject: [PATCH 012/217] Expose ndctl_bus_nfit_translate_spa as a public + function. + +The motivation is to allow access to ACPI defined NVDIMM Root Device +_DSM Function Index 5(Translate SPA). The rest of the _DSM functions, +which are mostly ARS related, are already public. + +Basically move ndctl_bus_nfit_translate_spa declaration from private.h +to libndctl.h. + +Reviewed-by: Dan Williams +Signed-off-by: "Tsaur, Erwin" +Signed-off-by: Vishal Verma +Link: https://lore.kernel.org/r/20210305011804.3573-1-erwin.tsaur@intel.com +--- + ndctl/lib/libndctl.sym | 4 ++++ + ndctl/lib/nfit.c | 2 +- + ndctl/lib/private.h | 2 -- + ndctl/libndctl.h | 2 ++ + 4 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/ndctl/lib/libndctl.sym b/ndctl/lib/libndctl.sym +index 0a82616..58afb74 100644 +--- a/ndctl/lib/libndctl.sym ++++ b/ndctl/lib/libndctl.sym +@@ -451,3 +451,7 @@ LIBNDCTL_25 { + ndctl_bus_clear_fw_activate_nosuspend; + ndctl_bus_activate_firmware; + } LIBNDCTL_24; ++ ++LIBNDCTL_26 { ++ ndctl_bus_nfit_translate_spa; ++} LIBNDCTL_25; +diff --git a/ndctl/lib/nfit.c b/ndctl/lib/nfit.c +index 6f68fcf..d85682f 100644 +--- a/ndctl/lib/nfit.c ++++ b/ndctl/lib/nfit.c +@@ -114,7 +114,7 @@ static int is_valid_spa(struct ndctl_bus *bus, unsigned long long spa) + * + * If success, returns zero, store dimm's @handle, and @dpa. + */ +-int ndctl_bus_nfit_translate_spa(struct ndctl_bus *bus, ++NDCTL_EXPORT int ndctl_bus_nfit_translate_spa(struct ndctl_bus *bus, + unsigned long long address, unsigned int *handle, unsigned long long *dpa) + { + +diff --git a/ndctl/lib/private.h b/ndctl/lib/private.h +index ede1300..8f4510e 100644 +--- a/ndctl/lib/private.h ++++ b/ndctl/lib/private.h +@@ -370,8 +370,6 @@ static inline int check_kmod(struct kmod_ctx *kmod_ctx) + return kmod_ctx ? 0 : -ENXIO; + } + +-int ndctl_bus_nfit_translate_spa(struct ndctl_bus *bus, unsigned long long addr, +- unsigned int *handle, unsigned long long *dpa); + struct ndctl_cmd *ndctl_bus_cmd_new_err_inj(struct ndctl_bus *bus); + struct ndctl_cmd *ndctl_bus_cmd_new_err_inj_clr(struct ndctl_bus *bus); + struct ndctl_cmd *ndctl_bus_cmd_new_err_inj_stat(struct ndctl_bus *bus, +diff --git a/ndctl/libndctl.h b/ndctl/libndctl.h +index 60e1288..87d07b7 100644 +--- a/ndctl/libndctl.h ++++ b/ndctl/libndctl.h +@@ -152,6 +152,8 @@ int ndctl_bus_clear_fw_activate_noidle(struct ndctl_bus *bus); + int ndctl_bus_set_fw_activate_nosuspend(struct ndctl_bus *bus); + int ndctl_bus_clear_fw_activate_nosuspend(struct ndctl_bus *bus); + int ndctl_bus_activate_firmware(struct ndctl_bus *bus, enum ndctl_fwa_method method); ++int ndctl_bus_nfit_translate_spa(struct ndctl_bus *bus, unsigned long long addr, ++ unsigned int *handle, unsigned long long *dpa); + + struct ndctl_dimm; + struct ndctl_dimm *ndctl_dimm_get_first(struct ndctl_bus *bus); +-- +2.27.0 + diff --git a/SOURCES/0013-test-libndctl-Use-ndctl_region_set_ro-to-change-disk.patch b/SOURCES/0013-test-libndctl-Use-ndctl_region_set_ro-to-change-disk.patch new file mode 100644 index 0000000..596947c --- /dev/null +++ b/SOURCES/0013-test-libndctl-Use-ndctl_region_set_ro-to-change-disk.patch @@ -0,0 +1,72 @@ +From 43e48c0d2f271cba4237f6eefc3e4912a74c102b Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Tue, 9 Mar 2021 22:09:49 -0800 +Subject: [PATCH 013/217] test/libndctl: Use ndctl_region_set_ro() to change + disk read-only state + +Kernel commit 52f019d43c22 ("block: add a hard-readonly flag to struct +gendisk") broke the read-only management test, by fixing the broken +behavior that BLKROSET could make a block device read-write even when the +disk is read-only. The fix [1] propagates changes of the region +read-only state to the underlying disk. Add ndctl_region_set_ro() ahead of +BLKROSET so that BLKROSET does not conflict the block_device state with the +disk state. + +[1]: http://lore.kernel.org/r/161534060720.528671.2341213328968989192.stgit@dwillia2-desk3.amr.corp.intel.com + +Reported-by: kernel test robot +Reported-by: Vishal Verma +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +Link: https://lore.kernel.org/r/161535658913.530219.12194565167385663385.stgit@dwillia2-desk3.amr.corp.intel.com +--- + test/libndctl.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/test/libndctl.c b/test/libndctl.c +index fc65149..c42f785 100644 +--- a/test/libndctl.c ++++ b/test/libndctl.c +@@ -1541,6 +1541,7 @@ static int validate_bdev(const char *devname, struct ndctl_btt *btt, + struct ndctl_pfn *pfn, struct ndctl_namespace *ndns, + struct namespace *namespace, void *buf) + { ++ struct ndctl_region *region = ndctl_namespace_get_region(ndns); + char bdevpath[50]; + int fd, rc, ro; + +@@ -1578,6 +1579,13 @@ static int validate_bdev(const char *devname, struct ndctl_btt *btt, + } + + ro = 0; ++ rc = ndctl_region_set_ro(region, ro); ++ if (rc < 0) { ++ fprintf(stderr, "%s: ndctl_region_set_ro failed\n", devname); ++ rc = -errno; ++ goto out; ++ } ++ + rc = ioctl(fd, BLKROSET, &ro); + if (rc < 0) { + fprintf(stderr, "%s: BLKROSET failed\n", +@@ -1605,8 +1613,16 @@ static int validate_bdev(const char *devname, struct ndctl_btt *btt, + rc = -ENXIO; + goto out; + } ++ ++ rc = ndctl_region_set_ro(region, namespace->ro); ++ if (rc < 0) { ++ fprintf(stderr, "%s: ndctl_region_set_ro reset failed\n", devname); ++ rc = -errno; ++ goto out; ++ } ++ + rc = 0; +- out: ++out: + close(fd); + return rc; + } +-- +2.27.0 + diff --git a/SOURCES/0014-daxctl-fail-reconfigure-device-based-on-kernel-onlin.patch b/SOURCES/0014-daxctl-fail-reconfigure-device-based-on-kernel-onlin.patch new file mode 100644 index 0000000..3470017 --- /dev/null +++ b/SOURCES/0014-daxctl-fail-reconfigure-device-based-on-kernel-onlin.patch @@ -0,0 +1,221 @@ +From 99415dfc7c5167c49a5732f577836f68872645b2 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Wed, 24 Mar 2021 12:09:29 -0700 +Subject: [PATCH 014/217] daxctl: fail reconfigure-device based on kernel + onlining policy + +If the kernel has a policy set to auto-online any new memory blocks, we +know that an attempt to reconfigure a device either in ZONE_MOVABLE, or +with the --no-online is going to fail. While we detect this race after +the fact, and print a warning, that is often insufficient as the user +may be forced to reboot to get out of the situation, resulting in an +unpleasant experience. + +Detect whether the kernel policy is set to auto-online. If so, fail +device reconfigure operations that we know can't be satisfied. Allow +for overriding this safety check via the -f (--force) option. Update the +man page to talk about this, and the unit test to test for an expected +failure by enabling auto-onlining. + +Cc: Dave Hansen +Reported-by: Chunye Xu +Reported-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .../daxctl/daxctl-reconfigure-device.txt | 12 ++++++- + daxctl/device.c | 10 ++++++ + daxctl/lib/libdaxctl-private.h | 1 + + daxctl/lib/libdaxctl.c | 21 +++++++++++ + daxctl/lib/libdaxctl.sym | 5 +++ + daxctl/libdaxctl.h | 1 + + test/daxctl-devices.sh | 36 +++++++++++++++++++ + 7 files changed, 85 insertions(+), 1 deletion(-) + +diff --git a/Documentation/daxctl/daxctl-reconfigure-device.txt b/Documentation/daxctl/daxctl-reconfigure-device.txt +index ad33eda..f112b3c 100644 +--- a/Documentation/daxctl/daxctl-reconfigure-device.txt ++++ b/Documentation/daxctl/daxctl-reconfigure-device.txt +@@ -119,6 +119,10 @@ recommended to use the --no-online option described below. This will abridge + the device reconfiguration operation to just hotplugging the memory, and + refrain from then onlining it. + ++In case daxctl detects that there is a kernel policy to auto-online blocks ++(via /sys/devices/system/memory/auto_online_blocks), then reconfiguring to ++system-ram will result in a failure. This can be overridden with '--force'. ++ + OPTIONS + ------- + include::region-option.txt[] +@@ -162,12 +166,18 @@ include::movable-options.txt[] + + -f:: + --force:: +- When converting from "system-ram" mode to "devdax", it is expected ++ - When converting from "system-ram" mode to "devdax", it is expected + that all the memory sections are first made offline. By default, + daxctl won't touch online memory. However with this option, attempt + to offline the memory on the NUMA node associated with the dax device + before converting it back to "devdax" mode. + ++ - Additionally, if a kernel policy to auto-online blocks is detected, ++ reconfiguration to system-ram fails. With this option, the failure can ++ be overridden to allow reconfiguration regardless of kernel policy. ++ Doing this may result in a successful reconfiguration, but it may ++ not be possible to subsequently offline the memory without a reboot. ++ + + include::human-option.txt[] + +diff --git a/daxctl/device.c b/daxctl/device.c +index 0721a57..a427b7d 100644 +--- a/daxctl/device.c ++++ b/daxctl/device.c +@@ -541,8 +541,18 @@ static int disable_devdax_device(struct daxctl_dev *dev) + + static int reconfig_mode_system_ram(struct daxctl_dev *dev) + { ++ const char *devname = daxctl_dev_get_devname(dev); + int rc, skip_enable = 0; + ++ if (param.no_online || !param.no_movable) { ++ if (!param.force && daxctl_dev_will_auto_online_memory(dev)) { ++ fprintf(stderr, ++ "%s: error: kernel policy will auto-online memory, aborting\n", ++ devname); ++ return -EBUSY; ++ } ++ } ++ + if (daxctl_dev_is_enabled(dev)) { + rc = disable_devdax_device(dev); + if (rc < 0) +diff --git a/daxctl/lib/libdaxctl-private.h b/daxctl/lib/libdaxctl-private.h +index af257fd..ae45311 100644 +--- a/daxctl/lib/libdaxctl-private.h ++++ b/daxctl/lib/libdaxctl-private.h +@@ -111,6 +111,7 @@ struct daxctl_memory { + char *node_path; + unsigned long block_size; + enum memory_zones zone; ++ bool auto_online; + }; + + +diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c +index 479e8f6..879f7e6 100644 +--- a/daxctl/lib/libdaxctl.c ++++ b/daxctl/lib/libdaxctl.c +@@ -1644,3 +1644,24 @@ DAXCTL_EXPORT int daxctl_memory_is_movable(struct daxctl_memory *mem) + return rc; + return (mem->zone == MEM_ZONE_MOVABLE) ? 1 : 0; + } ++ ++DAXCTL_EXPORT int daxctl_dev_will_auto_online_memory(struct daxctl_dev *dev) ++{ ++ const char *auto_path = "/sys/devices/system/memory/auto_online_blocks"; ++ const char *devname = daxctl_dev_get_devname(dev); ++ struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); ++ char buf[SYSFS_ATTR_SIZE]; ++ ++ /* ++ * If we can't read the policy for some reason, don't fail yet. Assume ++ * the auto-onlining policy is absent, and carry on. If onlining blocks ++ * does result in the memory being in an inconsistent state, we have a ++ * check and warning for it after the fact ++ */ ++ if (sysfs_read_attr(ctx, auto_path, buf) != 0) ++ err(ctx, "%s: Unable to determine auto-online policy: %s\n", ++ devname, strerror(errno)); ++ ++ /* match both "online" and "online_movable" */ ++ return !strncmp(buf, "online", 6); ++} +diff --git a/daxctl/lib/libdaxctl.sym b/daxctl/lib/libdaxctl.sym +index a4e1684..892e393 100644 +--- a/daxctl/lib/libdaxctl.sym ++++ b/daxctl/lib/libdaxctl.sym +@@ -91,3 +91,8 @@ global: + daxctl_mapping_get_size; + daxctl_dev_set_mapping; + } LIBDAXCTL_7; ++ ++LIBDAXCTL_9 { ++global: ++ daxctl_dev_will_auto_online_memory; ++} LIBDAXCTL_8; +diff --git a/daxctl/libdaxctl.h b/daxctl/libdaxctl.h +index e82b274..30ab51a 100644 +--- a/daxctl/libdaxctl.h ++++ b/daxctl/libdaxctl.h +@@ -71,6 +71,7 @@ int daxctl_dev_disable(struct daxctl_dev *dev); + int daxctl_dev_enable_devdax(struct daxctl_dev *dev); + int daxctl_dev_enable_ram(struct daxctl_dev *dev); + int daxctl_dev_get_target_node(struct daxctl_dev *dev); ++int daxctl_dev_will_auto_online_memory(struct daxctl_dev *dev); + + struct daxctl_memory; + struct daxctl_memory *daxctl_dev_get_memory(struct daxctl_dev *dev); +diff --git a/test/daxctl-devices.sh b/test/daxctl-devices.sh +index 496e4f2..eed5906 100755 +--- a/test/daxctl-devices.sh ++++ b/test/daxctl-devices.sh +@@ -64,6 +64,26 @@ daxctl_get_mode() + "$DAXCTL" list -d "$1" | jq -er '.[].mode' + } + ++set_online_policy() ++{ ++ echo "online" > /sys/devices/system/memory/auto_online_blocks ++} ++ ++unset_online_policy() ++{ ++ echo "offline" > /sys/devices/system/memory/auto_online_blocks ++} ++ ++save_online_policy() ++{ ++ saved_policy="$(cat /sys/devices/system/memory/auto_online_blocks)" ++} ++ ++restore_online_policy() ++{ ++ echo "$saved_policy" > /sys/devices/system/memory/auto_online_blocks ++} ++ + daxctl_test() + { + local daxdev +@@ -71,6 +91,9 @@ daxctl_test() + daxdev=$(daxctl_get_dev "$testdev") + test -n "$daxdev" + ++ # these tests need to run with kernel onlining policy turned off ++ save_online_policy ++ unset_online_policy + "$DAXCTL" reconfigure-device -N -m system-ram "$daxdev" + [[ $(daxctl_get_mode "$daxdev") == "system-ram" ]] + "$DAXCTL" online-memory "$daxdev" +@@ -81,6 +104,19 @@ daxctl_test() + [[ $(daxctl_get_mode "$daxdev") == "system-ram" ]] + "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] ++ ++ # this tests for reconfiguration failure if an online-policy is set ++ set_online_policy ++ : "This command is expected to fail:" ++ if ! "$DAXCTL" reconfigure-device -N -m system-ram "$daxdev"; then ++ echo "reconfigure failed as expected" ++ else ++ echo "reconfigure succeded, expected failure" ++ restore_online_policy ++ return 1 ++ fi ++ ++ restore_online_policy + } + + find_testdev +-- +2.27.0 + diff --git a/SOURCES/0015-libdaxctl-add-an-API-to-check-if-a-device-is-active.patch b/SOURCES/0015-libdaxctl-add-an-API-to-check-if-a-device-is-active.patch new file mode 100644 index 0000000..72ba1f4 --- /dev/null +++ b/SOURCES/0015-libdaxctl-add-an-API-to-check-if-a-device-is-active.patch @@ -0,0 +1,63 @@ +From e563e6a7c55e65c554e07db6215f8bcb2d411d3b Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Tue, 30 Mar 2021 20:50:37 -0600 +Subject: [PATCH 015/217] libdaxctl: add an API to check if a device is active + +Add an API to check whether a daxctl device is active in system-ram +mode. This would be used from libndctl during +ndctl_namespace_disable_safe(), so that we don't disable/destroy an +underlying namespace while the memory is active and online. + +Reported-by: Chunye Xu +Cc: Dan Williams +Cc: Dave Hansen +Signed-off-by: Vishal Verma +--- + daxctl/lib/libdaxctl.c | 10 ++++++++++ + daxctl/lib/libdaxctl.sym | 1 + + daxctl/libdaxctl.h | 1 + + 3 files changed, 12 insertions(+) + +diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c +index 879f7e6..860bd9c 100644 +--- a/daxctl/lib/libdaxctl.c ++++ b/daxctl/lib/libdaxctl.c +@@ -1665,3 +1665,13 @@ DAXCTL_EXPORT int daxctl_dev_will_auto_online_memory(struct daxctl_dev *dev) + /* match both "online" and "online_movable" */ + return !strncmp(buf, "online", 6); + } ++ ++DAXCTL_EXPORT int daxctl_dev_has_online_memory(struct daxctl_dev *dev) ++{ ++ struct daxctl_memory *mem = daxctl_dev_get_memory(dev); ++ ++ if (mem) ++ return daxctl_memory_is_online(mem); ++ else ++ return 0; ++} +diff --git a/daxctl/lib/libdaxctl.sym b/daxctl/lib/libdaxctl.sym +index 892e393..a13e93d 100644 +--- a/daxctl/lib/libdaxctl.sym ++++ b/daxctl/lib/libdaxctl.sym +@@ -95,4 +95,5 @@ global: + LIBDAXCTL_9 { + global: + daxctl_dev_will_auto_online_memory; ++ daxctl_dev_has_online_memory; + } LIBDAXCTL_8; +diff --git a/daxctl/libdaxctl.h b/daxctl/libdaxctl.h +index 30ab51a..683ae9c 100644 +--- a/daxctl/libdaxctl.h ++++ b/daxctl/libdaxctl.h +@@ -72,6 +72,7 @@ int daxctl_dev_enable_devdax(struct daxctl_dev *dev); + int daxctl_dev_enable_ram(struct daxctl_dev *dev); + int daxctl_dev_get_target_node(struct daxctl_dev *dev); + int daxctl_dev_will_auto_online_memory(struct daxctl_dev *dev); ++int daxctl_dev_has_online_memory(struct daxctl_dev *dev); + + struct daxctl_memory; + struct daxctl_memory *daxctl_dev_get_memory(struct daxctl_dev *dev); +-- +2.27.0 + diff --git a/SOURCES/0016-libndctl-check-for-active-system-ram-before-disablin.patch b/SOURCES/0016-libndctl-check-for-active-system-ram-before-disablin.patch new file mode 100644 index 0000000..3f6587f --- /dev/null +++ b/SOURCES/0016-libndctl-check-for-active-system-ram-before-disablin.patch @@ -0,0 +1,113 @@ +From 573f0d46cff15fff2804b3fb444d1e34f482e788 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Tue, 30 Mar 2021 20:54:55 -0600 +Subject: [PATCH 016/217] libndctl: check for active system-ram before + disabling daxctl devices + +Teach ndctl_namespace_disable_safe() to look at the state of a +daxctl_dev with respect to whether it is active in 'system-ram' mode +before disabling it. This is similar to checking whether a filesystem is +actively mounted on a namespace before disabling it. + +Without this, libndctl would happily disable a devdax namespace while the +device was active in system-ram mode. If the namespace was subsequently +also destroyed, this would leave the memory without any sort of a +'handle' to perform any subsequent operation on it, and the system would +have to be rebooted to get out of this situation. + +Reported-by: Chunye Xu +Cc: Dan Williams +Cc: Dave Hansen +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 25 ++++++++++++++++++++++++- + test/daxctl-devices.sh | 16 ++++++++++++++++ + 2 files changed, 40 insertions(+), 1 deletion(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index 2f6d806..2eda56c 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -4593,21 +4593,40 @@ NDCTL_EXPORT int ndctl_namespace_disable_invalidate(struct ndctl_namespace *ndns + return ndctl_namespace_disable(ndns); + } + ++static int ndctl_dax_has_active_memory(struct ndctl_dax *dax) ++{ ++ struct daxctl_region *dax_region; ++ struct daxctl_dev *dax_dev; ++ ++ dax_region = ndctl_dax_get_daxctl_region(dax); ++ if (!dax_region) ++ return 0; ++ ++ daxctl_dev_foreach(dax_region, dax_dev) ++ if (daxctl_dev_has_online_memory(dax_dev)) ++ return 1; ++ ++ return 0; ++} ++ + NDCTL_EXPORT int ndctl_namespace_disable_safe(struct ndctl_namespace *ndns) + { + const char *devname = ndctl_namespace_get_devname(ndns); + struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); + struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); + struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); ++ struct ndctl_dax *dax = ndctl_namespace_get_dax(ndns); + const char *bdev = NULL; ++ int fd, active = 0; + char path[50]; +- int fd; + unsigned long long size = ndctl_namespace_get_size(ndns); + + if (pfn && ndctl_pfn_is_enabled(pfn)) + bdev = ndctl_pfn_get_block_device(pfn); + else if (btt && ndctl_btt_is_enabled(btt)) + bdev = ndctl_btt_get_block_device(btt); ++ else if (dax && ndctl_dax_is_enabled(dax)) ++ active = ndctl_dax_has_active_memory(dax); + else if (ndctl_namespace_is_enabled(ndns)) + bdev = ndctl_namespace_get_block_device(ndns); + +@@ -4632,6 +4651,10 @@ NDCTL_EXPORT int ndctl_namespace_disable_safe(struct ndctl_namespace *ndns) + devname, bdev, strerror(errno)); + return -errno; + } ++ } else if (active) { ++ dbg(ctx, "%s: active as system-ram, refusing to disable\n", ++ devname); ++ return -EBUSY; + } else { + if (size == 0) + /* No disable necessary due to no capacity allocated */ +diff --git a/test/daxctl-devices.sh b/test/daxctl-devices.sh +index eed5906..56c9691 100755 +--- a/test/daxctl-devices.sh ++++ b/test/daxctl-devices.sh +@@ -105,6 +105,22 @@ daxctl_test() + "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] + ++ # fail 'ndctl-disable-namespace' while the devdax namespace is active ++ # as system-ram. If this test fails, a reboot will be required to ++ # recover from the resulting state. ++ test -n "$testdev" ++ "$DAXCTL" reconfigure-device -m system-ram "$daxdev" ++ [[ $(daxctl_get_mode "$daxdev") == "system-ram" ]] ++ if ! "$NDCTL" disable-namespace "$testdev"; then ++ echo "disable-namespace failed as expected" ++ else ++ echo "disable-namespace succeded, expected failure" ++ echo "reboot required to recover from this state" ++ return 1 ++ fi ++ "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" ++ [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] ++ + # this tests for reconfiguration failure if an online-policy is set + set_online_policy + : "This command is expected to fail:" +-- +2.27.0 + diff --git a/SOURCES/0017-daxctl-emit-counts-of-total-and-online-memblocks.patch b/SOURCES/0017-daxctl-emit-counts-of-total-and-online-memblocks.patch new file mode 100644 index 0000000..28f1ee6 --- /dev/null +++ b/SOURCES/0017-daxctl-emit-counts-of-total-and-online-memblocks.patch @@ -0,0 +1,49 @@ +From e81f890c7ae1c940c7f52b8984e8728706489728 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Wed, 31 Mar 2021 13:51:35 -0600 +Subject: [PATCH 017/217] daxctl: emit counts of total and online memblocks + +Fir daxctl device listings, if in 'system-ram' mode, it is useful to +know whether the memory associated with the device is online or not. +Since the memory is comprised of a number of 'memblocks', and it is +possible (albeit rare) to have a subset of them online, and the rest +offline, we can't just use a boolean online-or-offline flag for the +state. + +Add a couple of counts, one for the total number of memblocks associated +with the device, and another for the ones that are online. + +Link: https://github.com/pmem/ndctl/issues/139 +Cc: Dan Williams +Cc: Dave Hansen +Reported-by: Steve Scargall +Signed-off-by: Vishal Verma +--- + util/json.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/util/json.c b/util/json.c +index ca0167b..a8d2412 100644 +--- a/util/json.c ++++ b/util/json.c +@@ -482,6 +482,17 @@ struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, + json_object_object_add(jdev, "mode", jobj); + + if (mem && daxctl_dev_get_resource(dev) != 0) { ++ int num_sections = daxctl_memory_num_sections(mem); ++ int num_online = daxctl_memory_is_online(mem); ++ ++ jobj = json_object_new_int(num_online); ++ if (jobj) ++ json_object_object_add(jdev, "online_memblocks", jobj); ++ ++ jobj = json_object_new_int(num_sections); ++ if (jobj) ++ json_object_object_add(jdev, "total_memblocks", jobj); ++ + movable = daxctl_memory_is_movable(mem); + if (movable == 1) + jobj = json_object_new_boolean(true); +-- +2.27.0 + diff --git a/SOURCES/0018-libndctl-Unify-adding-dimms-for-papr-and-nfit-famili.patch b/SOURCES/0018-libndctl-Unify-adding-dimms-for-papr-and-nfit-famili.patch new file mode 100644 index 0000000..3e499c2 --- /dev/null +++ b/SOURCES/0018-libndctl-Unify-adding-dimms-for-papr-and-nfit-famili.patch @@ -0,0 +1,220 @@ +From daef3a386a9c45105a2c045ddee46600e265939f Mon Sep 17 00:00:00 2001 +From: Santosh Sivaraj +Date: Thu, 13 May 2021 11:42:15 +0530 +Subject: [PATCH 018/217] libndctl: Unify adding dimms for papr and nfit + families + +In preparation for enabling tests on non-nfit devices, unify both, already very +similar, functions into one. This will help in adding all attributes needed for +the unit tests. Since the function doesn't fail if some of the dimm attributes +are missing, this will work fine on PAPR platforms though only part of the DIMM +attributes are provided (This doesn't mean that all of the DIMM attributes can +be missing). + +Link: https://lore.kernel.org/r/20210513061218.760322-1-santosh@fossix.org +Signed-off-by: Santosh Sivaraj +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 103 ++++++++++++++++--------------------------- + 1 file changed, 38 insertions(+), 65 deletions(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index 2f6d806..e45353f 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -1646,41 +1646,9 @@ static int ndctl_bind(struct ndctl_ctx *ctx, struct kmod_module *module, + static int ndctl_unbind(struct ndctl_ctx *ctx, const char *devpath); + static struct kmod_module *to_module(struct ndctl_ctx *ctx, const char *alias); + +-static int add_papr_dimm(struct ndctl_dimm *dimm, const char *dimm_base) +-{ +- int rc = -ENODEV; +- char buf[SYSFS_ATTR_SIZE]; +- struct ndctl_ctx *ctx = dimm->bus->ctx; +- char *path = calloc(1, strlen(dimm_base) + 100); +- const char * const devname = ndctl_dimm_get_devname(dimm); +- +- dbg(ctx, "%s: Probing of_pmem dimm at %s\n", devname, dimm_base); +- +- if (!path) +- return -ENOMEM; +- +- /* construct path to the papr compatible dimm flags file */ +- sprintf(path, "%s/papr/flags", dimm_base); +- +- if (ndctl_bus_is_papr_scm(dimm->bus) && +- sysfs_read_attr(ctx, path, buf) == 0) { +- +- dbg(ctx, "%s: Adding papr-scm dimm flags:\"%s\"\n", devname, buf); +- dimm->cmd_family = NVDIMM_FAMILY_PAPR; +- +- /* Parse dimm flags */ +- parse_papr_flags(dimm, buf); +- +- /* Allocate monitor mode fd */ +- dimm->health_eventfd = open(path, O_RDONLY|O_CLOEXEC); +- rc = 0; +- } +- +- free(path); +- return rc; +-} +- +-static int add_nfit_dimm(struct ndctl_dimm *dimm, const char *dimm_base) ++static int populate_dimm_attributes(struct ndctl_dimm *dimm, ++ const char *dimm_base, ++ const char *bus_prefix) + { + int i, rc = -1; + char buf[SYSFS_ATTR_SIZE]; +@@ -1694,7 +1662,7 @@ static int add_nfit_dimm(struct ndctl_dimm *dimm, const char *dimm_base) + * 'unique_id' may not be available on older kernels, so don't + * fail if the read fails. + */ +- sprintf(path, "%s/nfit/id", dimm_base); ++ sprintf(path, "%s/%s/id", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) { + unsigned int b[9]; + +@@ -1709,68 +1677,74 @@ static int add_nfit_dimm(struct ndctl_dimm *dimm, const char *dimm_base) + } + } + +- sprintf(path, "%s/nfit/handle", dimm_base); ++ sprintf(path, "%s/%s/handle", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) < 0) + goto err_read; + dimm->handle = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/phys_id", dimm_base); ++ sprintf(path, "%s/%s/phys_id", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) < 0) + goto err_read; + dimm->phys_id = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/serial", dimm_base); ++ sprintf(path, "%s/%s/serial", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->serial = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/vendor", dimm_base); ++ sprintf(path, "%s/%s/vendor", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->vendor_id = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/device", dimm_base); ++ sprintf(path, "%s/%s/device", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->device_id = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/rev_id", dimm_base); ++ sprintf(path, "%s/%s/rev_id", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->revision_id = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/dirty_shutdown", dimm_base); ++ sprintf(path, "%s/%s/dirty_shutdown", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->dirty_shutdown = strtoll(buf, NULL, 0); + +- sprintf(path, "%s/nfit/subsystem_vendor", dimm_base); ++ sprintf(path, "%s/%s/subsystem_vendor", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->subsystem_vendor_id = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/subsystem_device", dimm_base); ++ sprintf(path, "%s/%s/subsystem_device", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->subsystem_device_id = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/subsystem_rev_id", dimm_base); ++ sprintf(path, "%s/%s/subsystem_rev_id", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->subsystem_revision_id = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/family", dimm_base); ++ sprintf(path, "%s/%s/family", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->cmd_family = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/dsm_mask", dimm_base); ++ sprintf(path, "%s/%s/dsm_mask", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->nfit_dsm_mask = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/format", dimm_base); ++ sprintf(path, "%s/%s/format", dimm_base, bus_prefix); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->format[0] = strtoul(buf, NULL, 0); + for (i = 1; i < dimm->formats; i++) { +- sprintf(path, "%s/nfit/format%d", dimm_base, i); ++ sprintf(path, "%s/%s/format%d", dimm_base, bus_prefix, i); + if (sysfs_read_attr(ctx, path, buf) == 0) + dimm->format[i] = strtoul(buf, NULL, 0); + } + +- sprintf(path, "%s/nfit/flags", dimm_base); +- if (sysfs_read_attr(ctx, path, buf) == 0) +- parse_nfit_mem_flags(dimm, buf); ++ sprintf(path, "%s/%s/flags", dimm_base, bus_prefix); ++ if (sysfs_read_attr(ctx, path, buf) == 0) { ++ if (ndctl_bus_has_nfit(dimm->bus)) ++ parse_nfit_mem_flags(dimm, buf); ++ else if (ndctl_bus_is_papr_scm(dimm->bus)) { ++ dimm->cmd_family = NVDIMM_FAMILY_PAPR; ++ parse_papr_flags(dimm, buf); ++ } ++ } + + dimm->health_eventfd = open(path, O_RDONLY|O_CLOEXEC); + rc = 0; +@@ -1792,7 +1766,8 @@ static void *add_dimm(void *parent, int id, const char *dimm_base) + if (!path) + return NULL; + +- sprintf(path, "%s/nfit/formats", dimm_base); ++ sprintf(path, "%s/%s/formats", dimm_base, ++ ndctl_bus_has_nfit(bus) ? "nfit" : "papr"); + if (sysfs_read_attr(ctx, path, buf) < 0) + formats = 1; + else +@@ -1866,13 +1841,12 @@ static void *add_dimm(void *parent, int id, const char *dimm_base) + else + dimm->fwa_result = fwa_result_to_result(buf); + ++ dimm->formats = formats; + /* Check if the given dimm supports nfit */ + if (ndctl_bus_has_nfit(bus)) { +- dimm->formats = formats; +- rc = add_nfit_dimm(dimm, dimm_base); +- } else if (ndctl_bus_has_of_node(bus)) { +- rc = add_papr_dimm(dimm, dimm_base); +- } ++ rc = populate_dimm_attributes(dimm, dimm_base, "nfit"); ++ } else if (ndctl_bus_has_of_node(bus)) ++ rc = populate_dimm_attributes(dimm, dimm_base, "papr"); + + if (rc == -ENODEV) { + /* Unprobed dimm with no family */ +@@ -2531,13 +2505,12 @@ static void *add_region(void *parent, int id, const char *region_base) + goto err_read; + region->num_mappings = strtoul(buf, NULL, 0); + +- sprintf(path, "%s/nfit/range_index", region_base); +- if (ndctl_bus_has_nfit(bus)) { +- if (sysfs_read_attr(ctx, path, buf) < 0) +- goto err_read; +- region->range_index = strtoul(buf, NULL, 0); +- } else ++ sprintf(path, "%s/%s/range_index", region_base, ++ ndctl_bus_has_nfit(bus) ? "nfit": "papr"); ++ if (sysfs_read_attr(ctx, path, buf) < 0) + region->range_index = -1; ++ else ++ region->range_index = strtoul(buf, NULL, 0); + + sprintf(path, "%s/read_only", region_base); + if (sysfs_read_attr(ctx, path, buf) < 0) +-- +2.27.0 + diff --git a/SOURCES/0019-test-Don-t-skip-tests-if-nfit-modules-are-missing.patch b/SOURCES/0019-test-Don-t-skip-tests-if-nfit-modules-are-missing.patch new file mode 100644 index 0000000..573a272 --- /dev/null +++ b/SOURCES/0019-test-Don-t-skip-tests-if-nfit-modules-are-missing.patch @@ -0,0 +1,211 @@ +From 1649ad9c3e2c6e9c47870c8d3b54f10b24177bc7 Mon Sep 17 00:00:00 2001 +From: Santosh Sivaraj +Date: Thu, 13 May 2021 11:42:16 +0530 +Subject: [PATCH 019/217] test: Don't skip tests if nfit modules are missing + +For NFIT to be available ACPI is a must, so don't fail when nfit modules +are missing on a platform that doesn't support ACPI. + +Link: https://lore.kernel.org/r/20210513061218.760322-2-santosh@fossix.org +Signed-off-by: Santosh Sivaraj +Signed-off-by: Vishal Verma +--- + test.h | 2 +- + test/ack-shutdown-count-set.c | 2 +- + test/blk_namespaces.c | 2 +- + test/core.c | 28 ++++++++++++++++++++++++++-- + test/dpa-alloc.c | 2 +- + test/dsm-fail.c | 2 +- + test/libndctl.c | 2 +- + test/multi-pmem.c | 2 +- + test/parent-uuid.c | 2 +- + test/pmem_namespaces.c | 2 +- + 10 files changed, 35 insertions(+), 11 deletions(-) + +diff --git a/test.h b/test.h +index cba8d41..7de13fe 100644 +--- a/test.h ++++ b/test.h +@@ -20,7 +20,7 @@ void builtin_xaction_namespace_reset(void); + + struct kmod_ctx; + struct kmod_module; +-int nfit_test_init(struct kmod_ctx **ctx, struct kmod_module **mod, ++int ndctl_test_init(struct kmod_ctx **ctx, struct kmod_module **mod, + struct ndctl_ctx *nd_ctx, int log_level, + struct ndctl_test *test); + +diff --git a/test/ack-shutdown-count-set.c b/test/ack-shutdown-count-set.c +index fb1d82b..c561ff3 100644 +--- a/test/ack-shutdown-count-set.c ++++ b/test/ack-shutdown-count-set.c +@@ -99,7 +99,7 @@ static int test_ack_shutdown_count_set(int loglevel, struct ndctl_test *test, + int result = EXIT_FAILURE, err; + + ndctl_set_log_priority(ctx, loglevel); +- err = nfit_test_init(&kmod_ctx, &mod, NULL, loglevel, test); ++ err = ndctl_test_init(&kmod_ctx, &mod, NULL, loglevel, test); + if (err < 0) { + result = 77; + ndctl_test_skip(test); +diff --git a/test/blk_namespaces.c b/test/blk_namespaces.c +index d7f00cb..f076e85 100644 +--- a/test/blk_namespaces.c ++++ b/test/blk_namespaces.c +@@ -228,7 +228,7 @@ int test_blk_namespaces(int log_level, struct ndctl_test *test, + + if (!bus) { + fprintf(stderr, "ACPI.NFIT unavailable falling back to nfit_test\n"); +- rc = nfit_test_init(&kmod_ctx, &mod, NULL, log_level, test); ++ rc = ndctl_test_init(&kmod_ctx, &mod, NULL, log_level, test); + ndctl_invalidate(ctx); + bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); + if (rc < 0 || !bus) { +diff --git a/test/core.c b/test/core.c +index cc7d8d9..2b03aa9 100644 +--- a/test/core.c ++++ b/test/core.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + + #define KVER_STRLEN 20 +@@ -106,11 +107,11 @@ int ndctl_test_get_skipped(struct ndctl_test *test) + return test->skip; + } + +-int nfit_test_init(struct kmod_ctx **ctx, struct kmod_module **mod, ++int ndctl_test_init(struct kmod_ctx **ctx, struct kmod_module **mod, + struct ndctl_ctx *nd_ctx, int log_level, + struct ndctl_test *test) + { +- int rc; ++ int rc, family = -1; + unsigned int i; + const char *name; + struct ndctl_bus *bus; +@@ -127,10 +128,28 @@ int nfit_test_init(struct kmod_ctx **ctx, struct kmod_module **mod, + "nd_e820", + "nd_pmem", + }; ++ char *test_env; + + log_init(&log_ctx, "test/init", "NDCTL_TEST"); + log_ctx.log_priority = log_level; + ++ /* ++ * The following two checks determine the platform family. For ++ * Intel/platforms which support ACPI, check sysfs; for other platforms ++ * determine from the environment variable NVDIMM_TEST_FAMILY ++ */ ++ if (access("/sys/bus/acpi", F_OK) == 0) ++ family = NVDIMM_FAMILY_INTEL; ++ ++ test_env = getenv("NDCTL_TEST_FAMILY"); ++ if (test_env && strcmp(test_env, "PAPR") == 0) ++ family = NVDIMM_FAMILY_PAPR; ++ ++ if (family == -1) { ++ log_err(&log_ctx, "Cannot determine NVDIMM family\n"); ++ return -ENOTSUP; ++ } ++ + *ctx = kmod_new(NULL, NULL); + if (!*ctx) + return -ENXIO; +@@ -185,6 +204,11 @@ retry: + + path = kmod_module_get_path(*mod); + if (!path) { ++ if (family != NVDIMM_FAMILY_INTEL && ++ (strcmp(name, "nfit") == 0 || ++ strcmp(name, "nd_e820") == 0)) ++ continue; ++ + log_err(&log_ctx, "%s.ko: failed to get path\n", name); + break; + } +diff --git a/test/dpa-alloc.c b/test/dpa-alloc.c +index e922009..0b3bb7a 100644 +--- a/test/dpa-alloc.c ++++ b/test/dpa-alloc.c +@@ -289,7 +289,7 @@ int test_dpa_alloc(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) + return 77; + + ndctl_set_log_priority(ctx, loglevel); +- err = nfit_test_init(&kmod_ctx, &mod, NULL, loglevel, test); ++ err = ndctl_test_init(&kmod_ctx, &mod, NULL, loglevel, test); + if (err < 0) { + ndctl_test_skip(test); + fprintf(stderr, "nfit_test unavailable skipping tests\n"); +diff --git a/test/dsm-fail.c b/test/dsm-fail.c +index 9dfd8b0..0a6383d 100644 +--- a/test/dsm-fail.c ++++ b/test/dsm-fail.c +@@ -346,7 +346,7 @@ int test_dsm_fail(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) + int result = EXIT_FAILURE, err; + + ndctl_set_log_priority(ctx, loglevel); +- err = nfit_test_init(&kmod_ctx, &mod, NULL, loglevel, test); ++ err = ndctl_test_init(&kmod_ctx, &mod, NULL, loglevel, test); + if (err < 0) { + result = 77; + ndctl_test_skip(test); +diff --git a/test/libndctl.c b/test/libndctl.c +index c42f785..d9b50f4 100644 +--- a/test/libndctl.c ++++ b/test/libndctl.c +@@ -2708,7 +2708,7 @@ int test_libndctl(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) + daxctl_set_log_priority(daxctl_ctx, loglevel); + ndctl_set_private_data(ctx, test); + +- err = nfit_test_init(&kmod_ctx, &mod, ctx, loglevel, test); ++ err = ndctl_test_init(&kmod_ctx, &mod, ctx, loglevel, test); + if (err < 0) { + ndctl_test_skip(test); + fprintf(stderr, "nfit_test unavailable skipping tests\n"); +diff --git a/test/multi-pmem.c b/test/multi-pmem.c +index 3d10952..3ea08cc 100644 +--- a/test/multi-pmem.c ++++ b/test/multi-pmem.c +@@ -249,7 +249,7 @@ int test_multi_pmem(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx + + ndctl_set_log_priority(ctx, loglevel); + +- err = nfit_test_init(&kmod_ctx, &mod, NULL, loglevel, test); ++ err = ndctl_test_init(&kmod_ctx, &mod, NULL, loglevel, test); + if (err < 0) { + result = 77; + ndctl_test_skip(test); +diff --git a/test/parent-uuid.c b/test/parent-uuid.c +index 6424e9f..bded33a 100644 +--- a/test/parent-uuid.c ++++ b/test/parent-uuid.c +@@ -218,7 +218,7 @@ int test_parent_uuid(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ct + return 77; + + ndctl_set_log_priority(ctx, loglevel); +- err = nfit_test_init(&kmod_ctx, &mod, NULL, loglevel, test); ++ err = ndctl_test_init(&kmod_ctx, &mod, NULL, loglevel, test); + if (err < 0) { + ndctl_test_skip(test); + fprintf(stderr, "nfit_test unavailable skipping tests\n"); +diff --git a/test/pmem_namespaces.c b/test/pmem_namespaces.c +index f0f2edd..a4db1ae 100644 +--- a/test/pmem_namespaces.c ++++ b/test/pmem_namespaces.c +@@ -191,7 +191,7 @@ int test_pmem_namespaces(int log_level, struct ndctl_test *test, + + if (!bus) { + fprintf(stderr, "ACPI.NFIT unavailable falling back to nfit_test\n"); +- rc = nfit_test_init(&kmod_ctx, &mod, NULL, log_level, test); ++ rc = ndctl_test_init(&kmod_ctx, &mod, NULL, log_level, test); + ndctl_invalidate(ctx); + bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); + if (rc < 0 || !bus) { +-- +2.27.0 + diff --git a/SOURCES/0020-papr-Add-support-to-parse-save_fail-flag-for-dimm.patch b/SOURCES/0020-papr-Add-support-to-parse-save_fail-flag-for-dimm.patch new file mode 100644 index 0000000..7994e22 --- /dev/null +++ b/SOURCES/0020-papr-Add-support-to-parse-save_fail-flag-for-dimm.patch @@ -0,0 +1,41 @@ +From f081f302505209430df46908775a3cffb875a5c7 Mon Sep 17 00:00:00 2001 +From: Santosh Sivaraj +Date: Thu, 13 May 2021 11:42:17 +0530 +Subject: [PATCH 020/217] papr: Add support to parse save_fail flag for dimm + +This will help in getting the dimm fail tests to run on papr family too. +Also add nvdimm_test compatibility string for recognizing the test module. + +Link: https://lore.kernel.org/r/20210513061218.760322-3-santosh@fossix.org +Signed-off-by: Santosh Sivaraj +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index e45353f..a8b99ea 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -805,6 +805,8 @@ static void parse_papr_flags(struct ndctl_dimm *dimm, char *flags) + dimm->flags.f_restore = 1; + else if (strcmp(start, "smart_notify") == 0) + dimm->flags.f_smart = 1; ++ else if (strcmp(start, "save_fail") == 0) ++ dimm->flags.f_save = 1; + start = end + 1; + } + if (end != start) +@@ -1035,7 +1037,8 @@ NDCTL_EXPORT int ndctl_bus_is_papr_scm(struct ndctl_bus *bus) + if (sysfs_read_attr(bus->ctx, bus->bus_buf, buf) < 0) + return 0; + +- return (strcmp(buf, "ibm,pmemory") == 0); ++ return (strcmp(buf, "ibm,pmemory") == 0 || ++ strcmp(buf, "nvdimm_test") == 0); + } + + /** +-- +2.27.0 + diff --git a/SOURCES/0021-Use-page-size-as-alignment-value.patch b/SOURCES/0021-Use-page-size-as-alignment-value.patch new file mode 100644 index 0000000..c1000b0 --- /dev/null +++ b/SOURCES/0021-Use-page-size-as-alignment-value.patch @@ -0,0 +1,129 @@ +From fe831b526b88f6ca7a27fdb149b8a7d2ecddbc55 Mon Sep 17 00:00:00 2001 +From: Santosh Sivaraj +Date: Thu, 13 May 2021 11:42:18 +0530 +Subject: [PATCH 021/217] Use page size as alignment value + +The alignment sizes passed to ndctl in the tests are all hardcoded to 4k, +the default page size on x86. Change those to the default page size on that +architecture (sysconf/getconf). No functional changes otherwise. + +Link: https://lore.kernel.org/r/20210513061218.760322-4-santosh@fossix.org +Signed-off-by: Santosh Sivaraj +Signed-off-by: Vishal Verma +--- + test/dpa-alloc.c | 15 ++++++++------- + test/multi-dax.sh | 6 ++++-- + test/sector-mode.sh | 4 +++- + 3 files changed, 15 insertions(+), 10 deletions(-) + +diff --git a/test/dpa-alloc.c b/test/dpa-alloc.c +index 0b3bb7a..59185cf 100644 +--- a/test/dpa-alloc.c ++++ b/test/dpa-alloc.c +@@ -38,12 +38,13 @@ static int do_test(struct ndctl_ctx *ctx, struct ndctl_test *test) + struct ndctl_region *region, *blk_region = NULL; + struct ndctl_namespace *ndns; + struct ndctl_dimm *dimm; +- unsigned long size; ++ unsigned long size, page_size; + struct ndctl_bus *bus; + char uuid_str[40]; + int round; + int rc; + ++ page_size = sysconf(_SC_PAGESIZE); + /* disable nfit_test.1, not used in this test */ + bus = ndctl_bus_get_by_provider(ctx, NFIT_PROVIDER1); + if (!bus) +@@ -124,11 +125,11 @@ static int do_test(struct ndctl_ctx *ctx, struct ndctl_test *test) + return rc; + } + ndctl_namespace_disable_invalidate(ndns); +- rc = ndctl_namespace_set_size(ndns, SZ_4K); ++ rc = ndctl_namespace_set_size(ndns, page_size); + if (rc) { +- fprintf(stderr, "failed to init %s to size: %d\n", ++ fprintf(stderr, "failed to init %s to size: %lu\n", + ndctl_namespace_get_devname(ndns), +- SZ_4K); ++ page_size); + return rc; + } + namespaces[i].ndns = ndns; +@@ -150,7 +151,7 @@ static int do_test(struct ndctl_ctx *ctx, struct ndctl_test *test) + ndns = namespaces[i % ARRAY_SIZE(namespaces)].ndns; + if (i % ARRAY_SIZE(namespaces) == 0) + round++; +- size = SZ_4K * round; ++ size = page_size * round; + rc = ndctl_namespace_set_size(ndns, size); + if (rc) { + fprintf(stderr, "%s: set_size: %lx failed: %d\n", +@@ -166,7 +167,7 @@ static int do_test(struct ndctl_ctx *ctx, struct ndctl_test *test) + i--; + round++; + ndns = namespaces[i % ARRAY_SIZE(namespaces)].ndns; +- size = SZ_4K * round; ++ size = page_size * round; + rc = ndctl_namespace_set_size(ndns, size); + if (rc) { + fprintf(stderr, "%s failed to update while labels full\n", +@@ -175,7 +176,7 @@ static int do_test(struct ndctl_ctx *ctx, struct ndctl_test *test) + } + + round--; +- size = SZ_4K * round; ++ size = page_size * round; + rc = ndctl_namespace_set_size(ndns, size); + if (rc) { + fprintf(stderr, "%s failed to reduce size while labels full\n", +diff --git a/test/multi-dax.sh b/test/multi-dax.sh +index 8496619..b343a38 100755 +--- a/test/multi-dax.sh ++++ b/test/multi-dax.sh +@@ -13,6 +13,8 @@ check_prereq "jq" + + trap 'err $LINENO' ERR + ++ALIGN_SIZE=`getconf PAGESIZE` ++ + # setup (reset nfit_test dimms) + modprobe nfit_test + $NDCTL disable-region -b $NFIT_TEST_BUS0 all +@@ -23,9 +25,9 @@ rc=1 + query=". | sort_by(.available_size) | reverse | .[0].dev" + region=$($NDCTL list -b $NFIT_TEST_BUS0 -t pmem -Ri | jq -r "$query") + +-json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -r $region -t pmem -m devdax -a 4096 -s 16M) ++json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -r $region -t pmem -m devdax -a $ALIGN_SIZE -s 16M) + chardev1=$(echo $json | jq ". | select(.mode == \"devdax\") | .daxregion.devices[0].chardev") +-json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -r $region -t pmem -m devdax -a 4096 -s 16M) ++json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -r $region -t pmem -m devdax -a $ALIGN_SIZE -s 16M) + chardev2=$(echo $json | jq ". | select(.mode == \"devdax\") | .daxregion.devices[0].chardev") + + _cleanup +diff --git a/test/sector-mode.sh b/test/sector-mode.sh +index 54fa806..7a2faea 100755 +--- a/test/sector-mode.sh ++++ b/test/sector-mode.sh +@@ -11,6 +11,8 @@ check_prereq "jq" + set -e + trap 'err $LINENO' ERR + ++ALIGN_SIZE=`getconf PAGESIZE` ++ + # setup (reset nfit_test dimms) + modprobe nfit_test + $NDCTL disable-region -b $NFIT_TEST_BUS0 all +@@ -27,7 +29,7 @@ NAMESPACE=$($NDCTL list -b $NFIT_TEST_BUS1 -N | jq -r "$query") + REGION=$($NDCTL list -R --namespace=$NAMESPACE | jq -r "(.[]) | .dev") + echo 0 > /sys/bus/nd/devices/$REGION/read_only + $NDCTL create-namespace --no-autolabel -e $NAMESPACE -m sector -f -l 4K +-$NDCTL create-namespace --no-autolabel -e $NAMESPACE -m dax -f -a 4K ++$NDCTL create-namespace --no-autolabel -e $NAMESPACE -m dax -f -a $ALIGN_SIZE + $NDCTL create-namespace --no-autolabel -e $NAMESPACE -m sector -f -l 4K + + _cleanup +-- +2.27.0 + diff --git a/SOURCES/0022-libndctl-Remove-redundant-checks-and-assignments.patch b/SOURCES/0022-libndctl-Remove-redundant-checks-and-assignments.patch new file mode 100644 index 0000000..bcc65b8 --- /dev/null +++ b/SOURCES/0022-libndctl-Remove-redundant-checks-and-assignments.patch @@ -0,0 +1,38 @@ +From 561af4f919b3f1b3d1a213137f1d024420996d56 Mon Sep 17 00:00:00 2001 +From: Santosh Sivaraj +Date: Sun, 28 Mar 2021 05:43:51 +0530 +Subject: [PATCH 022/217] libndctl: Remove redundant checks and assignments + +check_udev already checks for udev allocation failure, remove the redundant +check. + +Link: https://lore.kernel.org/r/20210328001351.2245032-1-santosh@fossix.org +Signed-off-by: Santosh Sivaraj +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index bf0968c..3a496ed 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -323,12 +323,9 @@ NDCTL_EXPORT int ndctl_new(struct ndctl_ctx **ctx) + dbg(c, "timeout = %ld\n", tmo); + } + +- if (udev) { +- c->udev = udev; +- c->udev_queue = udev_queue_new(udev); +- if (!c->udev_queue) +- err(c, "failed to retrieve udev queue\n"); +- } ++ c->udev_queue = udev_queue_new(udev); ++ if (!c->udev_queue) ++ err(c, "failed to retrieve udev queue\n"); + + c->kmod_ctx = kmod_ctx; + c->daxctl_ctx = daxctl_ctx; +-- +2.27.0 + diff --git a/SOURCES/0023-ndctl-Update-nvdimm-mailing-list-address.patch b/SOURCES/0023-ndctl-Update-nvdimm-mailing-list-address.patch new file mode 100644 index 0000000..4217357 --- /dev/null +++ b/SOURCES/0023-ndctl-Update-nvdimm-mailing-list-address.patch @@ -0,0 +1,56 @@ +From 063af7c447d257397a925df81897da2c71e31653 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Tue, 18 May 2021 16:25:27 -0600 +Subject: [PATCH 023/217] ndctl: Update nvdimm mailing list address + +The 'nvdimm' mailing list has moved from lists.01.org to +lists.linux.dev. Update CONTRIBUTING.md and configure.ac to reflect +this. + +Link: https://lore.kernel.org/r/20210518222527.550730-1-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + CONTRIBUTING.md | 7 ++++--- + configure.ac | 2 +- + 2 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md +index 4c29d31..4f4865d 100644 +--- a/CONTRIBUTING.md ++++ b/CONTRIBUTING.md +@@ -6,13 +6,14 @@ The following is a set of guidelines that we adhere to, and request that + contributors follow. + + 1. The libnvdimm (kernel subsystem) and ndctl developers primarily use +- the [linux-nvdimm](https://lists.01.org/postorius/lists/linux-nvdimm.lists.01.org/) ++ the [nvdimm](https://subspace.kernel.org/lists.linux.dev.html) + mailing list for everything. It is recommended to send patches to +- **```linux-nvdimm@lists.01.org```** ++ **```nvdimm@lists.linux.dev```** ++ An archive is available on [lore](https://lore.kernel.org/nvdimm/) + + 1. Github [issues](https://github.com/pmem/ndctl/issues) are an acceptable + way to report a problem, but if you just have a question, +- [email](mailto:linux-nvdimm@lists.01.org) the above list. ++ [email](mailto:nvdimm@lists.linux.dev) the above list. + + 1. We follow the Linux Kernel [Coding Style Guide][cs] as applicable. + +diff --git a/configure.ac b/configure.ac +index 5ec8d2f..dc39dbe 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2,7 +2,7 @@ AC_PREREQ(2.60) + m4_include([version.m4]) + AC_INIT([ndctl], + GIT_VERSION, +- [linux-nvdimm@lists.01.org], ++ [nvdimm@lists.linux.dev], + [ndctl], + [https://github.com/pmem/ndctl]) + AC_CONFIG_SRCDIR([ndctl/lib/libndctl.c]) +-- +2.27.0 + diff --git a/SOURCES/0024-libndctl-papr-Fix-probe-for-papr-scm-compatible-nvdi.patch b/SOURCES/0024-libndctl-papr-Fix-probe-for-papr-scm-compatible-nvdi.patch new file mode 100644 index 0000000..01b34fe --- /dev/null +++ b/SOURCES/0024-libndctl-papr-Fix-probe-for-papr-scm-compatible-nvdi.patch @@ -0,0 +1,122 @@ +From e086106b4d81a2079141c848db7695451c04e877 Mon Sep 17 00:00:00 2001 +From: Vaibhav Jain +Date: Mon, 17 May 2021 21:18:24 +0530 +Subject: [PATCH 024/217] libndctl/papr: Fix probe for papr-scm compatible + nvdimms + +With recent changes introduced for unification of PAPR and NFIT +families the probe for papr-scm nvdimms is broken since they don't +expose 'handle' or 'phys_id' sysfs attributes. These attributes are +only exposed by NFIT and 'nvdimm_test' nvdimms. Since 'unable to read' +these sysfs attributes is a non-recoverable error hence this prevents +probing of 'PAPR-SCM' nvdimms and ndctl reports following error: + +$ sudo NDCTL_LOG=debug ndctl list -DH +libndctl: ndctl_new: ctx 0x10015342c70 created +libndctl: add_dimm: nmem1: probe failed: Operation not permitted +libndctl: __sysfs_device_parse: nmem1: add_dev() failed +libndctl: add_dimm: nmem0: probe failed: Operation not permitted +libndctl: __sysfs_device_parse: nmem0: add_dev() failed + +Fixing this bug is complicated by the fact these attributes are needed +for by the 'nvdimm_test' nvdimms which also uses the +NVDIMM_FAMILY_PAPR. Adding a two way comparison for these two +attributes in populate_dimm_attributes() to distinguish between +'nvdimm_test' and papr-scm nvdimms will be clunky and make future +updates to populate_dimm_attributes() error prone. + +So, this patch proposes to fix the issue by re-introducing +add_papr_dimm() to probe both papr-scm and 'nvdimm_test' nvdimms. The +'compatible' sysfs attribute associated with the PAPR device is used +to distinguish between the two nvdimm types and in case an +'nvdimm_test' device is detected then forward its probe to +populate_dimm_attributes(). + +families") + +Link: https://lore.kernel.org/r/20210517154824.142237-1-vaibhav@linux.ibm.com +Fixes: daef3a386a9c("libndctl: Unify adding dimms for papr and nfit +Signed-off-by: Vaibhav Jain +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 57 ++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 55 insertions(+), 2 deletions(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index 3a496ed..aa36a3c 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -1754,6 +1754,58 @@ static int populate_dimm_attributes(struct ndctl_dimm *dimm, + return rc; + } + ++static int add_papr_dimm(struct ndctl_dimm *dimm, const char *dimm_base) ++{ ++ int rc = -ENODEV; ++ char buf[SYSFS_ATTR_SIZE]; ++ struct ndctl_ctx *ctx = dimm->bus->ctx; ++ char *path = calloc(1, strlen(dimm_base) + 100); ++ const char * const devname = ndctl_dimm_get_devname(dimm); ++ ++ dbg(ctx, "%s: Probing of_pmem dimm at %s\n", devname, dimm_base); ++ ++ if (!path) ++ return -ENOMEM; ++ ++ /* Check the compatibility of the probed nvdimm */ ++ sprintf(path, "%s/../of_node/compatible", dimm_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) { ++ dbg(ctx, "%s: Unable to read compatible field\n", devname); ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ dbg(ctx, "%s:Compatible of_pmem = '%s'\n", devname, buf); ++ ++ /* Probe for papr-scm memory */ ++ if (strcmp(buf, "ibm,pmemory") == 0) { ++ /* Read the dimm flags file */ ++ sprintf(path, "%s/papr/flags", dimm_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) { ++ rc = -errno; ++ err(ctx, "%s: Unable to read dimm-flags\n", devname); ++ goto out; ++ } ++ ++ dbg(ctx, "%s: Adding papr-scm dimm flags:\"%s\"\n", devname, buf); ++ dimm->cmd_family = NVDIMM_FAMILY_PAPR; ++ ++ /* Parse dimm flags */ ++ parse_papr_flags(dimm, buf); ++ ++ /* Allocate monitor mode fd */ ++ dimm->health_eventfd = open(path, O_RDONLY|O_CLOEXEC); ++ rc = 0; ++ ++ } else if (strcmp(buf, "nvdimm_test") == 0) { ++ /* probe via common populate_dimm_attributes() */ ++ rc = populate_dimm_attributes(dimm, dimm_base, "papr"); ++ } ++out: ++ free(path); ++ return rc; ++} ++ + static void *add_dimm(void *parent, int id, const char *dimm_base) + { + int formats, i, rc = -ENODEV; +@@ -1845,8 +1897,9 @@ static void *add_dimm(void *parent, int id, const char *dimm_base) + /* Check if the given dimm supports nfit */ + if (ndctl_bus_has_nfit(bus)) { + rc = populate_dimm_attributes(dimm, dimm_base, "nfit"); +- } else if (ndctl_bus_has_of_node(bus)) +- rc = populate_dimm_attributes(dimm, dimm_base, "papr"); ++ } else if (ndctl_bus_has_of_node(bus)) { ++ rc = add_papr_dimm(dimm, dimm_base); ++ } + + if (rc == -ENODEV) { + /* Unprobed dimm with no family */ +-- +2.27.0 + diff --git a/SOURCES/0025-ndctl-scrub-Stop-translating-return-values.patch b/SOURCES/0025-ndctl-scrub-Stop-translating-return-values.patch new file mode 100644 index 0000000..1123cf8 --- /dev/null +++ b/SOURCES/0025-ndctl-scrub-Stop-translating-return-values.patch @@ -0,0 +1,38 @@ +From c52109355b715bbe21e284090435bee7563863cc Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 26 May 2021 16:33:04 -0700 +Subject: [PATCH 025/217] ndctl/scrub: Stop translating return values + +In preparation for triggering a poll loop within ndctl_bus_start_scrub(), +stop translating return values into -EOPNOTSUPP. + +Link: https://lore.kernel.org/r/162207198482.3715490.5994844104395495686.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index aa36a3c..e5641fe 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -1354,14 +1354,8 @@ static int __ndctl_bus_get_scrub_state(struct ndctl_bus *bus, + NDCTL_EXPORT int ndctl_bus_start_scrub(struct ndctl_bus *bus) + { + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); +- int rc; + +- rc = sysfs_write_attr(ctx, bus->scrub_path, "1\n"); +- if (rc == -EBUSY) +- return rc; +- else if (rc < 0) +- return -EOPNOTSUPP; +- return 0; ++ return sysfs_write_attr(ctx, bus->scrub_path, "1\n"); + } + + NDCTL_EXPORT int ndctl_bus_get_scrub_state(struct ndctl_bus *bus) +-- +2.27.0 + diff --git a/SOURCES/0026-ndctl-scrub-Reread-scrub-engine-status-at-start.patch b/SOURCES/0026-ndctl-scrub-Reread-scrub-engine-status-at-start.patch new file mode 100644 index 0000000..ec7035f --- /dev/null +++ b/SOURCES/0026-ndctl-scrub-Reread-scrub-engine-status-at-start.patch @@ -0,0 +1,48 @@ +From 4e646fa490ba4b782afa188dd8818b94c419924e Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 26 May 2021 16:33:10 -0700 +Subject: [PATCH 026/217] ndctl/scrub: Reread scrub-engine status at start + +Given that the kernel has exponential backoff to cover the lack of +interrupts for scrub completion status there is a reasonable likelihood +that 'ndctl start-scrub' is issued while the hardware/platform scrub-state +is idle, but the kernel engine poll timer has not fired. + +Trigger at least one poll cycle for the kernel to re-read the scrub-state +before reporting that ARS is busy. + +Link: https://lore.kernel.org/r/162207199057.3715490.2469820075085914776.stgit@dwillia2-desk3.amr.corp.intel.com +Reported-by: Krzysztof Rusocki +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index e5641fe..536e142 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -1354,8 +1354,18 @@ static int __ndctl_bus_get_scrub_state(struct ndctl_bus *bus, + NDCTL_EXPORT int ndctl_bus_start_scrub(struct ndctl_bus *bus) + { + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); ++ int rc; ++ ++ rc = sysfs_write_attr(ctx, bus->scrub_path, "1\n"); + +- return sysfs_write_attr(ctx, bus->scrub_path, "1\n"); ++ /* ++ * Try at least 1 poll cycle before reporting busy in case this ++ * request hits the kernel's exponential backoff while the ++ * hardware/platform scrub state is idle. ++ */ ++ if (rc == -EBUSY && ndctl_bus_poll_scrub_completion(bus, 1, 1) == 0) ++ return sysfs_write_attr(ctx, bus->scrub_path, "1\n"); ++ return rc; + } + + NDCTL_EXPORT int ndctl_bus_get_scrub_state(struct ndctl_bus *bus) +-- +2.27.0 + diff --git a/SOURCES/0027-ndctl-dimm-Fix-label-index-block-calculations.patch b/SOURCES/0027-ndctl-dimm-Fix-label-index-block-calculations.patch new file mode 100644 index 0000000..ad2e6c2 --- /dev/null +++ b/SOURCES/0027-ndctl-dimm-Fix-label-index-block-calculations.patch @@ -0,0 +1,167 @@ +From 7e98977c10ad5f4baf5e3bc4d5b4b2fd733a8b7e Mon Sep 17 00:00:00 2001 +From: Jingqi Liu +Date: Thu, 8 Jul 2021 16:14:46 +0800 +Subject: [PATCH 027/217] ndctl/dimm: Fix label index block calculations + +The following bug is caused by setting the size of Label Index Block +to a fixed 256 bytes. + +Use the following Qemu command to start a Guest with 2MB label-size: + -object memory-backend-file,id=mem1,share=on,mem-path=/dev/dax1.1,size=14G,align=2M + -device nvdimm,memdev=mem1,id=nv1,label-size=2M + +There is a namespace in the Guest as follows: + $ ndctl list + [ + { + "dev":"namespace0.0", + "mode":"devdax", + "map":"dev", + "size":14780727296, + "uuid":"58ad5282-5a16-404f-b8ee-e28b4c784eb8", + "chardev":"dax0.0", + "align":2097152, + "name":"namespace0.0" + } + ] + +Fail to read labels. The result is as follows: + $ ndctl read-labels -u nmem0 + [ + ] + read 0 nmem + +If using the following Qemu command to start the Guest with 128K +label-size, this label can be read correctly. + -object memory-backend-file,id=mem1,share=on,mem-path=/dev/dax1.1,size=14G,align=2M + -device nvdimm,memdev=mem1,id=nv1,label-size=128K + +The size of a Label Index Block depends on how many label slots fit into +the label storage area. The minimum size of an index block is 256 bytes +and the size must be a multiple of 256 bytes. For a storage area of 128KB, +the corresponding Label Index Block size is 256 bytes. But if the label +storage area is not 128KB, the Label Index Block size should not be 256 bytes. + +Namespace Label Index Block appears twice at the top of the label storage area. +Following the two index blocks, an array for storing labels takes up the +remainder of the label storage area. + +For obtaining the size of Namespace Index Block, we also cannot rely on +the field of 'mysize' in this index block since it might be corrupted. +Similar to the linux kernel, we use sizeof_namespace_index() to get the size +of Namespace Index Block. Then we can also correctly calculate the starting +offset of the following namespace labels. + +Link: https://lore.kernel.org/r/20210708081446.14323-1-jingqi.liu@intel.com +Suggested-by: Dan Williams +Reviewed-by: Dan Williams +Reviewed-by: Vishal Verma +Signed-off-by: Jingqi Liu +Signed-off-by: Vishal Verma +--- + ndctl/dimm.c | 19 +++++++++++++++---- + ndctl/lib/dimm.c | 5 +++++ + ndctl/lib/libndctl.sym | 1 + + ndctl/libndctl.h | 1 + + 4 files changed, 22 insertions(+), 4 deletions(-) + +diff --git a/ndctl/dimm.c b/ndctl/dimm.c +index 09ce49e..1d2d9a2 100644 +--- a/ndctl/dimm.c ++++ b/ndctl/dimm.c +@@ -94,13 +94,18 @@ static struct json_object *dump_label_json(struct ndctl_dimm *dimm, + struct json_object *jarray = json_object_new_array(); + struct json_object *jlabel = NULL; + struct namespace_label nslabel; ++ unsigned int nsindex_size; + unsigned int slot = -1; + ssize_t offset; + + if (!jarray) + return NULL; + +- for (offset = NSINDEX_ALIGN * 2; offset < size; ++ nsindex_size = ndctl_dimm_sizeof_namespace_index(dimm); ++ if (nsindex_size == 0) ++ return NULL; ++ ++ for (offset = nsindex_size * 2; offset < size; + offset += ndctl_dimm_sizeof_namespace_label(dimm)) { + ssize_t len = min_t(ssize_t, + ndctl_dimm_sizeof_namespace_label(dimm), +@@ -204,17 +209,23 @@ static struct json_object *dump_label_json(struct ndctl_dimm *dimm, + return jarray; + } + +-static struct json_object *dump_index_json(struct ndctl_cmd *cmd_read, ssize_t size) ++static struct json_object *dump_index_json(struct ndctl_dimm *dimm, ++ struct ndctl_cmd *cmd_read, ssize_t size) + { + struct json_object *jarray = json_object_new_array(); + struct json_object *jindex = NULL; + struct namespace_index nsindex; ++ unsigned int nsindex_size; + ssize_t offset; + + if (!jarray) + return NULL; + +- for (offset = 0; offset < NSINDEX_ALIGN * 2; offset += NSINDEX_ALIGN) { ++ nsindex_size = ndctl_dimm_sizeof_namespace_index(dimm); ++ if (nsindex_size == 0) ++ return NULL; ++ ++ for (offset = 0; offset < nsindex_size * 2; offset += nsindex_size) { + ssize_t len = min_t(ssize_t, sizeof(nsindex), size - offset); + struct json_object *jobj; + +@@ -288,7 +299,7 @@ static struct json_object *dump_json(struct ndctl_dimm *dimm, + goto err; + json_object_object_add(jdimm, "dev", jobj); + +- jindex = dump_index_json(cmd_read, size); ++ jindex = dump_index_json(dimm, cmd_read, size); + if (!jindex) + goto err; + json_object_object_add(jdimm, "index", jindex); +diff --git a/ndctl/lib/dimm.c b/ndctl/lib/dimm.c +index c045cbe..9e36e28 100644 +--- a/ndctl/lib/dimm.c ++++ b/ndctl/lib/dimm.c +@@ -256,6 +256,11 @@ static int __label_validate(struct nvdimm_data *ndd) + return -EINVAL; + } + ++NDCTL_EXPORT unsigned int ndctl_dimm_sizeof_namespace_index(struct ndctl_dimm *dimm) ++{ ++ return sizeof_namespace_index(&dimm->ndd); ++} ++ + /* + * If the dimm labels have not been previously validated this routine + * will make up a default size. Otherwise, it will pick the size based +diff --git a/ndctl/lib/libndctl.sym b/ndctl/lib/libndctl.sym +index 58afb74..5ee73b7 100644 +--- a/ndctl/lib/libndctl.sym ++++ b/ndctl/lib/libndctl.sym +@@ -454,4 +454,5 @@ LIBNDCTL_25 { + + LIBNDCTL_26 { + ndctl_bus_nfit_translate_spa; ++ ndctl_dimm_sizeof_namespace_index; + } LIBNDCTL_25; +diff --git a/ndctl/libndctl.h b/ndctl/libndctl.h +index 87d07b7..df109bb 100644 +--- a/ndctl/libndctl.h ++++ b/ndctl/libndctl.h +@@ -337,6 +337,7 @@ int ndctl_dimm_init_labels(struct ndctl_dimm *dimm, + enum ndctl_namespace_version v); + unsigned long ndctl_dimm_get_available_labels(struct ndctl_dimm *dimm); + unsigned int ndctl_dimm_sizeof_namespace_label(struct ndctl_dimm *dimm); ++unsigned int ndctl_dimm_sizeof_namespace_index(struct ndctl_dimm *dimm); + unsigned int ndctl_cmd_cfg_size_get_size(struct ndctl_cmd *cfg_size); + ssize_t ndctl_cmd_cfg_read_get_data(struct ndctl_cmd *cfg_read, void *buf, + unsigned int len, unsigned int offset); +-- +2.27.0 + diff --git a/SOURCES/0028-ndctl-namespace-Skip-seed-namespaces-when-processing.patch b/SOURCES/0028-ndctl-namespace-Skip-seed-namespaces-when-processing.patch new file mode 100644 index 0000000..5d2ebd0 --- /dev/null +++ b/SOURCES/0028-ndctl-namespace-Skip-seed-namespaces-when-processing.patch @@ -0,0 +1,58 @@ +From 9bd2994f91bb77604521cbe09a76a51d092c2cfd Mon Sep 17 00:00:00 2001 +From: Michal Suchanek +Date: Wed, 6 Jan 2021 14:17:40 +0100 +Subject: [PATCH 028/217] ndctl/namespace: Skip seed namespaces when processing + all namespaces. + +The seed namespaces are exposed by the kernel but most operations are +not valid on seed namespaces. + +When processing all namespaces the user gets confusing errors from ndctl +trying to process seed namespaces. The kernel does not provide any way +to tell that a namspace is seed namespace but skipping namespaces with +zero size and UUID is a good heuristic. + +The user can still specify the namespace by name directly in case +processing it is desirable. + +Link: https://patchwork.kernel.org/patch/11473645/ +Link: https://lore.kernel.org/r/e55ae2c17b8b9c3288491efe6214338118e8c5ae.1609938610.git.msuchanek@suse.de +Fixes: #41 +Tested-by: Harish Sriram +Reviewed-by: Santosh S +Signed-off-by: Michal Suchanek +Signed-off-by: Vishal Verma +--- + ndctl/namespace.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/ndctl/namespace.c b/ndctl/namespace.c +index 1e8a2cd..5e65ed5 100644 +--- a/ndctl/namespace.c ++++ b/ndctl/namespace.c +@@ -2210,9 +2210,19 @@ static int do_xaction_namespace(const char *namespace, + ndctl_namespace_foreach_safe(region, ndns, _n) { + ndns_name = ndctl_namespace_get_devname(ndns); + +- if (strcmp(namespace, "all") != 0 +- && strcmp(namespace, ndns_name) != 0) +- continue; ++ if (strcmp(namespace, "all") == 0) { ++ static const uuid_t zero_uuid; ++ uuid_t uuid; ++ ++ ndctl_namespace_get_uuid(ndns, uuid); ++ if (!ndctl_namespace_get_size(ndns) && ++ !memcmp(uuid, zero_uuid, sizeof(uuid_t))) ++ continue; ++ } else { ++ if (strcmp(namespace, ndns_name) != 0) ++ continue; ++ } ++ + switch (action) { + case ACTION_DISABLE: + rc = ndctl_namespace_disable_safe(ndns); +-- +2.27.0 + diff --git a/SOURCES/0029-ndctl-namespace-Suppress-ENXIO-when-processing-all-n.patch b/SOURCES/0029-ndctl-namespace-Suppress-ENXIO-when-processing-all-n.patch new file mode 100644 index 0000000..75b7c3f --- /dev/null +++ b/SOURCES/0029-ndctl-namespace-Suppress-ENXIO-when-processing-all-n.patch @@ -0,0 +1,82 @@ +From 07011a334fd1e4b641cdbfaf5de7500f7bdc941d Mon Sep 17 00:00:00 2001 +From: Michal Suchanek +Date: Wed, 6 Jan 2021 14:17:41 +0100 +Subject: [PATCH 029/217] ndctl/namespace: Suppress -ENXIO when processing all + namespaces. + +When processing all namespaces and no namespaces exist user gets the +default -ENXIO. Set default rc to 0 when processing all namespaces. +This avoids confusing error message printed in addition to the message +saying 0 namespaces were affected. + +Before: + + # ndctl check-namespace all +namespace0.0: namespace_check: namespace0.0: check aborted, namespace online +error checking namespaces: Device or resource busy +checked 0 namespaces + # ndctl disable-namespace all +disabled 1 namespace + # ndctl check-namespace all +namespace0.0: namespace_check: Unable to recover any BTT info blocks +error checking namespaces: No such device or address +checked 0 namespaces + # ndctl destroy-namespace all +destroyed 1 namespace + # ndctl check-namespace all +error checking namespaces: No such device or address +checked 0 namespaces + # ndctl destroy-namespace all +error destroying namespaces: No such device or address +destroyed 0 namespaces + +After: + + # ndctl check-namespace all +namespace0.0: namespace_check: namespace0.0: check aborted, namespace online +error checking namespaces: Device or resource busy +checked 0 namespaces + # ndctl disable-namespace namespace0.0 +disabled 1 namespace + # ndctl check-namespace all +namespace0.0: namespace_check: Unable to recover any BTT info blocks +error checking namespaces: No such device or address +checked 0 namespaces + # ndctl destroy-namespace all +destroyed 1 namespace + # ndctl check-namespace all +checked 0 namespaces + # ndctl destroy-namespace all +destroyed 0 namespaces + # ndctl destroy-namespace all +destroyed 0 namespaces + +Note: this does change the return value from -ENXIO to 0 in the cases +when no namespaces exist and processing all namespaces was requested. + +Link: https://patchwork.kernel.org/patch/11681431/ +Link: https://lore.kernel.org/r/32c8cd8d2716f5e52aebea4e4d303eeb4e0550f9.1609938610.git.msuchanek@suse.de +Reviewed-by: Santosh S +Signed-off-by: Michal Suchanek +Signed-off-by: Vishal Verma +--- + ndctl/namespace.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/ndctl/namespace.c b/ndctl/namespace.c +index 5e65ed5..cd822b3 100644 +--- a/ndctl/namespace.c ++++ b/ndctl/namespace.c +@@ -2151,6 +2151,9 @@ static int do_xaction_namespace(const char *namespace, + if (!namespace && action != ACTION_CREATE) + return rc; + ++ if (namespace && (strcmp(namespace, "all") == 0)) ++ rc = 0; ++ + if (verbose) + ndctl_set_log_priority(ctx, LOG_DEBUG); + +-- +2.27.0 + diff --git a/SOURCES/0030-namespace-action-Drop-zero-namespace-checks.patch b/SOURCES/0030-namespace-action-Drop-zero-namespace-checks.patch new file mode 100644 index 0000000..dde5c63 --- /dev/null +++ b/SOURCES/0030-namespace-action-Drop-zero-namespace-checks.patch @@ -0,0 +1,85 @@ +From 80e0d88c3098bd419e26146a8cb3b693fdd06417 Mon Sep 17 00:00:00 2001 +From: Santosh Sivaraj +Date: Wed, 6 Jan 2021 14:17:42 +0100 +Subject: [PATCH 030/217] namespace-action: Drop zero namespace checks. + +With seed namespaces catched early on these checks for sizes in enable +and destroy namespace code path are not needed. + +Reverts commit b9cb03f6d5a8 ("ndctl/namespace: Fix enable-namespace +error for seed namespaces") + +Reverts commit e01045e58ad5 ("ndctl/namespace: Fix destroy-namespace +accounting relative to seed devices") + +Link: https://patchwork.kernel.org/patch/11739975/ +Link: https://lore.kernel.org/r/eb4bc7885708fa13e3d37286bc4a4219b1e4e5b6.1609938610.git.msuchanek@suse.de +Fixes: b9cb03f6d5a8 ("ndctl/namespace: Fix enable-namespace error for seed namespaces") +Fixes: e01045e58ad5 ("ndctl/namespace: Fix destroy-namespace accounting relative to seed devices") +Signed-off-by: Santosh Sivaraj +[rebased on top of the previous patches] +Signed-off-by: Michal Suchanek +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 5 ----- + ndctl/namespace.c | 10 ---------- + 2 files changed, 15 deletions(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index 536e142..87f60b9 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -4531,16 +4531,11 @@ NDCTL_EXPORT int ndctl_namespace_enable(struct ndctl_namespace *ndns) + const char *devname = ndctl_namespace_get_devname(ndns); + struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); + struct ndctl_region *region = ndns->region; +- unsigned long long size = ndctl_namespace_get_size(ndns); + int rc; + + if (ndctl_namespace_is_enabled(ndns)) + return 0; + +- /* Don't try to enable idle namespace (no capacity allocated) */ +- if (size == 0) +- return -ENXIO; +- + rc = ndctl_bind(ctx, ndns->module, devname); + + /* +diff --git a/ndctl/namespace.c b/ndctl/namespace.c +index cd822b3..c67c086 100644 +--- a/ndctl/namespace.c ++++ b/ndctl/namespace.c +@@ -1164,15 +1164,12 @@ static int namespace_destroy(struct ndctl_region *region, + struct ndctl_namespace *ndns) + { + const char *devname = ndctl_namespace_get_devname(ndns); +- unsigned long long size; + int rc; + + rc = namespace_prep_reconfig(region, ndns); + if (rc < 0) + return rc; + +- size = ndctl_namespace_get_size(ndns); +- + /* Labeled namespace, destroy label / allocation */ + if (rc == 2) { + rc = ndctl_namespace_delete(ndns); +@@ -1180,13 +1177,6 @@ static int namespace_destroy(struct ndctl_region *region, + debug("%s: failed to reclaim\n", devname); + } + +- /* +- * Don't report a destroyed namespace when no capacity was +- * allocated. +- */ +- if (size == 0 && rc == 0) +- rc = 1; +- + return rc; + } + +-- +2.27.0 + diff --git a/SOURCES/0031-ndctl-add-.clang-format.patch b/SOURCES/0031-ndctl-add-.clang-format.patch new file mode 100644 index 0000000..a447ec3 --- /dev/null +++ b/SOURCES/0031-ndctl-add-.clang-format.patch @@ -0,0 +1,187 @@ +From dc712e47c39f4dbc5f089831fd50dd1a2752c8fc Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:23 -0600 +Subject: [PATCH 031/217] ndctl: add .clang-format + +Copy the Linux kernel's .clang-format and modify it for ndctl. Only the +'ForEachMacros' section has been modified from the original kernel copy. + +Cc: Dan Williams +Reported-by: Ben Widawsky +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .clang-format | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 161 insertions(+) + create mode 100644 .clang-format + +diff --git a/.clang-format b/.clang-format +new file mode 100644 +index 0000000..4e00fff +--- /dev/null ++++ b/.clang-format +@@ -0,0 +1,161 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# clang-format configuration file. Intended for clang-format >= 4. ++# Copied from Linux's .clang-format ++# ++# For more information, see: ++# ++# https://clang.llvm.org/docs/ClangFormat.html ++# https://clang.llvm.org/docs/ClangFormatStyleOptions.html ++# ++--- ++AccessModifierOffset: -4 ++AlignAfterOpenBracket: Align ++AlignConsecutiveAssignments: false ++AlignConsecutiveDeclarations: false ++#AlignEscapedNewlines: Left # Unknown to clang-format-4.0 ++AlignOperands: true ++AlignTrailingComments: false ++AllowAllParametersOfDeclarationOnNextLine: false ++AllowShortBlocksOnASingleLine: false ++AllowShortCaseLabelsOnASingleLine: false ++AllowShortFunctionsOnASingleLine: None ++AllowShortIfStatementsOnASingleLine: false ++AllowShortLoopsOnASingleLine: false ++AlwaysBreakAfterDefinitionReturnType: None ++AlwaysBreakAfterReturnType: None ++AlwaysBreakBeforeMultilineStrings: false ++AlwaysBreakTemplateDeclarations: false ++BinPackArguments: true ++BinPackParameters: true ++BraceWrapping: ++ AfterClass: false ++ AfterControlStatement: false ++ AfterEnum: false ++ AfterFunction: true ++ AfterNamespace: true ++ AfterObjCDeclaration: false ++ AfterStruct: false ++ AfterUnion: false ++ #AfterExternBlock: false # Unknown to clang-format-5.0 ++ BeforeCatch: false ++ BeforeElse: false ++ IndentBraces: false ++ #SplitEmptyFunction: true # Unknown to clang-format-4.0 ++ #SplitEmptyRecord: true # Unknown to clang-format-4.0 ++ #SplitEmptyNamespace: true # Unknown to clang-format-4.0 ++BreakBeforeBinaryOperators: None ++BreakBeforeBraces: Custom ++#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 ++BreakBeforeTernaryOperators: false ++BreakConstructorInitializersBeforeComma: false ++#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 ++BreakAfterJavaFieldAnnotations: false ++BreakStringLiterals: false ++ColumnLimit: 80 ++CommentPragmas: '^ IWYU pragma:' ++#CompactNamespaces: false # Unknown to clang-format-4.0 ++ConstructorInitializerAllOnOneLineOrOnePerLine: false ++ConstructorInitializerIndentWidth: 8 ++ContinuationIndentWidth: 8 ++Cpp11BracedListStyle: false ++DerivePointerAlignment: false ++DisableFormat: false ++ExperimentalAutoDetectBinPacking: false ++#FixNamespaceComments: false # Unknown to clang-format-4.0 ++ ++# Taken from: ++# while read -r sym; do ++# printf " - '%s'\n" "$sym"; ++# done < \ ++# <(cscope -dL6 "foreach|for_each" \ ++# | awk '{ print $4 $5 }' | grep -E 'foreach|for_each' \ ++# | sed -e 's/#define//' \ ++# -e 's/*//' \ ++# -e 's/://' \ ++# -e 's/\(.*for_each.*\)(.*/\1/' \ ++# -e 's/\(.*foreach.*\)(.*/\1/' \ ++# | sort -u) ++ForEachMacros: ++ - 'daxctl_dev_foreach' ++ - 'daxctl_mapping_foreach' ++ - 'daxctl_region_foreach' ++ - 'kmod_list_foreach' ++ - 'kmod_list_foreach_reverse' ++ - 'list_for_each' ++ - 'list_for_each_off' ++ - 'list_for_each_rev' ++ - 'list_for_each_safe' ++ - 'list_for_each_safe_off' ++ - 'ndctl_btt_foreach' ++ - 'ndctl_btt_foreach_safe' ++ - 'ndctl_bus_foreach' ++ - 'ndctl_dax_foreach' ++ - 'ndctl_dax_foreach_safe' ++ - 'ndctl_dimm_foreach' ++ - 'ndctl_dimm_foreach_in_interleave_set' ++ - 'ndctl_dimm_foreach_in_region' ++ - 'ndctl_interleave_set_foreach' ++ - 'ndctl_mapping_foreach' ++ - 'ndctl_namespace_badblock_foreach' ++ - 'ndctl_namespace_bb_foreach' ++ - 'ndctl_namespace_foreach' ++ - 'ndctl_namespace_foreach_safe' ++ - 'ndctl_pfn_foreach' ++ - 'ndctl_pfn_foreach_safe' ++ - 'ndctl_region_badblock_foreach' ++ - 'ndctl_region_foreach' ++ - 'udev_list_entry_foreach' ++ ++#IncludeBlocks: Preserve # Unknown to clang-format-5.0 ++IncludeCategories: ++ - Regex: '.*' ++ Priority: 1 ++IncludeIsMainRegex: '(Test)?$' ++IndentCaseLabels: false ++#IndentPPDirectives: None # Unknown to clang-format-5.0 ++IndentWidth: 8 ++IndentWrappedFunctionNames: false ++JavaScriptQuotes: Leave ++JavaScriptWrapImports: true ++KeepEmptyLinesAtTheStartOfBlocks: false ++MacroBlockBegin: '' ++MacroBlockEnd: '' ++MaxEmptyLinesToKeep: 1 ++NamespaceIndentation: None ++#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 ++ObjCBlockIndentWidth: 8 ++ObjCSpaceAfterProperty: true ++ObjCSpaceBeforeProtocolList: true ++ ++# Taken from git's rules ++#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 ++PenaltyBreakBeforeFirstCallParameter: 30 ++PenaltyBreakComment: 10 ++PenaltyBreakFirstLessLess: 0 ++PenaltyBreakString: 10 ++PenaltyExcessCharacter: 100 ++PenaltyReturnTypeOnItsOwnLine: 60 ++ ++PointerAlignment: Right ++ReflowComments: false ++SortIncludes: false ++#SortUsingDeclarations: false # Unknown to clang-format-4.0 ++SpaceAfterCStyleCast: false ++SpaceAfterTemplateKeyword: true ++SpaceBeforeAssignmentOperators: true ++#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 ++#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 ++SpaceBeforeParens: ControlStatements ++#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 ++SpaceInEmptyParentheses: false ++SpacesBeforeTrailingComments: 1 ++SpacesInAngles: false ++SpacesInContainerLiterals: false ++SpacesInCStyleCastParentheses: false ++SpacesInParentheses: false ++SpacesInSquareBrackets: false ++Standard: Cpp03 ++TabWidth: 8 ++UseTab: Always ++... +-- +2.27.0 + diff --git a/SOURCES/0032-cxl-add-a-cxl-utility-and-libcxl-library.patch b/SOURCES/0032-cxl-add-a-cxl-utility-and-libcxl-library.patch new file mode 100644 index 0000000..a283fd4 --- /dev/null +++ b/SOURCES/0032-cxl-add-a-cxl-utility-and-libcxl-library.patch @@ -0,0 +1,1277 @@ +From 6cd8155dd5a600f1bb435a49ebddc1fae2f60e73 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:24 -0600 +Subject: [PATCH 032/217] cxl: add a cxl utility and libcxl library + +CXL - or Compute eXpress Link - is a new interconnect that extends PCIe +to support a wide range of devices, including cache coherent memory +expanders. As such, these devices can be new sources of 'persistent +memory', and the 'ndctl' umbrella of tools and libraries needs to be able +to interact with them. + +Add a new utility and library for managing these CXL memory devices. This +is an initial bring-up for interacting with CXL devices, and only includes +adding the utility and library infrastructure, parsing device information +from sysfs for CXL devices, and providing a 'cxl-list' command to +display this information in JSON formatted output. + +Cc: Ben Widawsky +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .clang-format | 1 + + .gitignore | 4 +- + Documentation/cxl/Makefile.am | 58 +++++ + Documentation/cxl/cxl-list.txt | 59 +++++ + Documentation/cxl/cxl.txt | 34 +++ + Documentation/cxl/human-option.txt | 8 + + Documentation/cxl/verbose-option.txt | 5 + + Makefile.am | 8 +- + Makefile.am.in | 4 + + configure.ac | 3 + + cxl/Makefile.am | 21 ++ + cxl/builtin.h | 8 + + cxl/cxl.c | 96 ++++++++ + cxl/lib/Makefile.am | 32 +++ + cxl/lib/libcxl.c | 345 +++++++++++++++++++++++++++ + cxl/lib/libcxl.pc.in | 11 + + cxl/lib/libcxl.sym | 25 ++ + cxl/lib/private.h | 29 +++ + cxl/libcxl.h | 55 +++++ + cxl/list.c | 121 ++++++++++ + util/filter.c | 20 ++ + util/filter.h | 2 + + util/json.c | 26 ++ + util/json.h | 3 + + util/main.h | 3 + + 25 files changed, 977 insertions(+), 4 deletions(-) + create mode 100644 Documentation/cxl/Makefile.am + create mode 100644 Documentation/cxl/cxl-list.txt + create mode 100644 Documentation/cxl/cxl.txt + create mode 100644 Documentation/cxl/human-option.txt + create mode 100644 Documentation/cxl/verbose-option.txt + create mode 100644 cxl/Makefile.am + create mode 100644 cxl/builtin.h + create mode 100644 cxl/cxl.c + create mode 100644 cxl/lib/Makefile.am + create mode 100644 cxl/lib/libcxl.c + create mode 100644 cxl/lib/libcxl.pc.in + create mode 100644 cxl/lib/libcxl.sym + create mode 100644 cxl/lib/private.h + create mode 100644 cxl/libcxl.h + create mode 100644 cxl/list.c + +diff --git a/.clang-format b/.clang-format +index 4e00fff..d2e77d0 100644 +--- a/.clang-format ++++ b/.clang-format +@@ -77,6 +77,7 @@ ExperimentalAutoDetectBinPacking: false + # -e 's/\(.*foreach.*\)(.*/\1/' \ + # | sort -u) + ForEachMacros: ++ - 'cxl_memdev_foreach' + - 'daxctl_dev_foreach' + - 'daxctl_mapping_foreach' + - 'daxctl_region_foreach' +diff --git a/.gitignore b/.gitignore +index 53512b2..6a97b92 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -16,9 +16,11 @@ Makefile.in + *.1 + Documentation/daxctl/asciidoc.conf + Documentation/ndctl/asciidoc.conf +-Documentation/ndctl/attrs.adoc ++Documentation/cxl/asciidoc.conf + Documentation/daxctl/asciidoctor-extensions.rb + Documentation/ndctl/asciidoctor-extensions.rb ++Documentation/cxl/asciidoctor-extensions.rb ++Documentation/ndctl/attrs.adoc + .dirstamp + daxctl/config.h + daxctl/daxctl +diff --git a/Documentation/cxl/Makefile.am b/Documentation/cxl/Makefile.am +new file mode 100644 +index 0000000..db98dd7 +--- /dev/null ++++ b/Documentation/cxl/Makefile.am +@@ -0,0 +1,58 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (C) 2020-2021 Intel Corporation. All rights reserved. ++ ++if USE_ASCIIDOCTOR ++ ++do_subst = sed -e 's,@Utility@,Cxl,g' -e's,@utility@,cxl,g' ++CONFFILE = asciidoctor-extensions.rb ++asciidoctor-extensions.rb: ../asciidoctor-extensions.rb.in ++ $(AM_V_GEN) $(do_subst) < $< > $@ ++ ++else ++ ++do_subst = sed -e 's,UTILITY,cxl,g' ++CONFFILE = asciidoc.conf ++asciidoc.conf: ../asciidoc.conf.in ++ $(AM_V_GEN) $(do_subst) < $< > $@ ++ ++endif ++ ++man1_MANS = \ ++ cxl.1 \ ++ cxl-list.1 ++ ++EXTRA_DIST = $(man1_MANS) ++ ++CLEANFILES = $(man1_MANS) ++ ++XML_DEPS = \ ++ ../../version.m4 \ ++ ../copyright.txt \ ++ Makefile \ ++ $(CONFFILE) ++ ++RM ?= rm -f ++ ++if USE_ASCIIDOCTOR ++ ++%.1: %.txt $(XML_DEPS) ++ $(AM_V_GEN)$(RM) $@+ $@ && \ ++ $(ASCIIDOC) -b manpage -d manpage -acompat-mode \ ++ -I. -rasciidoctor-extensions \ ++ -amansource=cxl -amanmanual="cxl Manual" \ ++ -andctl_version=$(VERSION) -o $@+ $< && \ ++ mv $@+ $@ ++ ++else ++ ++%.xml: %.txt $(XML_DEPS) ++ $(AM_V_GEN)$(RM) $@+ $@ && \ ++ $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ ++ --unsafe -acxl_version=$(VERSION) -o $@+ $< && \ ++ mv $@+ $@ ++ ++%.1: %.xml $(XML_DEPS) ++ $(AM_V_GEN)$(RM) $@ && \ ++ $(XMLTO) -o . -m ../manpage-normal.xsl man $< ++ ++endif +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +new file mode 100644 +index 0000000..370d5b8 +--- /dev/null ++++ b/Documentation/cxl/cxl-list.txt +@@ -0,0 +1,59 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-list(1) ++=========== ++ ++NAME ++---- ++cxl-list - List CXL capable memory devices, and their attributes in json. ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl list' [] ++ ++Walk the CXL capable device hierarchy in the system and list all device ++instances along with some of their major attributes. ++ ++EXAMPLE ++------- ++---- ++# cxl list --memdevs ++{ ++ "memdev":"mem0", ++ "pmem_size":268435456, ++ "ram_size":0, ++} ++---- ++ ++OPTIONS ++------- ++-m:: ++--memdev=:: ++ Specify a cxl memory device name to filter the listing. For example: ++---- ++# cxl list --memdev=mem0 ++{ ++ "memdev":"mem0", ++ "pmem_size":268435456, ++ "ram_size":0, ++} ++---- ++ ++-M:: ++--memdevs:: ++ Include CXL memory devices in the listing ++ ++-i:: ++--idle:: ++ Include idle (not enabled / zero-sized) devices in the listing ++ ++include::human-option.txt[] ++ ++include::verbose-option.txt[] ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:ndctl-list[1] +diff --git a/Documentation/cxl/cxl.txt b/Documentation/cxl/cxl.txt +new file mode 100644 +index 0000000..41a51c7 +--- /dev/null ++++ b/Documentation/cxl/cxl.txt +@@ -0,0 +1,34 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl(1) ++====== ++ ++NAME ++---- ++cxl - Provides enumeration and provisioning commands for CXL platforms ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl' [--version] [--help] COMMAND [ARGS] ++ ++OPTIONS ++------- ++-v:: ++--version:: ++ Display the version of the 'cxl' utility. ++ ++-h:: ++--help:: ++ Run the 'cxl help' command. ++ ++DESCRIPTION ++----------- ++The cxl utility provides enumeration and provisioning commands for ++the CXL devices managed by the Linux kernel. ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:ndctl[1] +diff --git a/Documentation/cxl/human-option.txt b/Documentation/cxl/human-option.txt +new file mode 100644 +index 0000000..2f4de7a +--- /dev/null ++++ b/Documentation/cxl/human-option.txt +@@ -0,0 +1,8 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++-u:: ++--human:: ++ By default the command will output machine-friendly raw-integer ++ data. Instead, with this flag, numbers representing storage size ++ will be formatted as human readable strings with units, other ++ fields are converted to hexadecimal strings. +diff --git a/Documentation/cxl/verbose-option.txt b/Documentation/cxl/verbose-option.txt +new file mode 100644 +index 0000000..cb62c8e +--- /dev/null ++++ b/Documentation/cxl/verbose-option.txt +@@ -0,0 +1,5 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++-v:: ++--verbose:: ++ Emit more debug messages +diff --git a/Makefile.am b/Makefile.am +index 60a1998..428fd40 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1,9 +1,9 @@ + include Makefile.am.in + + ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} +-SUBDIRS = . daxctl/lib ndctl/lib ndctl daxctl ++SUBDIRS = . cxl/lib daxctl/lib ndctl/lib cxl ndctl daxctl + if ENABLE_DOCS +-SUBDIRS += Documentation/ndctl Documentation/daxctl ++SUBDIRS += Documentation/ndctl Documentation/daxctl Documentation/cxl + endif + SUBDIRS += test + +@@ -87,4 +87,6 @@ libutil_a_SOURCES = \ + util/filter.h \ + util/bitmap.h + +-nobase_include_HEADERS = daxctl/libdaxctl.h ++nobase_include_HEADERS = \ ++ daxctl/libdaxctl.h \ ++ cxl/libcxl.h +diff --git a/Makefile.am.in b/Makefile.am.in +index bdceda9..aaeee53 100644 +--- a/Makefile.am.in ++++ b/Makefile.am.in +@@ -42,3 +42,7 @@ LIBNDCTL_AGE=19 + LIBDAXCTL_CURRENT=6 + LIBDAXCTL_REVISION=0 + LIBDAXCTL_AGE=5 ++ ++LIBCXL_CURRENT=1 ++LIBCXL_REVISION=0 ++LIBCXL_AGE=0 +diff --git a/configure.ac b/configure.ac +index dc39dbe..dadae0a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -222,12 +222,15 @@ AC_CONFIG_HEADERS(config.h) + AC_CONFIG_FILES([ + Makefile + daxctl/lib/Makefile ++ cxl/lib/Makefile + ndctl/lib/Makefile + ndctl/Makefile + daxctl/Makefile ++ cxl/Makefile + test/Makefile + Documentation/ndctl/Makefile + Documentation/daxctl/Makefile ++ Documentation/cxl/Makefile + ]) + + AC_OUTPUT +diff --git a/cxl/Makefile.am b/cxl/Makefile.am +new file mode 100644 +index 0000000..98606b9 +--- /dev/null ++++ b/cxl/Makefile.am +@@ -0,0 +1,21 @@ ++include $(top_srcdir)/Makefile.am.in ++ ++bin_PROGRAMS = cxl ++ ++DISTCLEANFILES = config.h ++BUILT_SOURCES = config.h ++config.h: $(srcdir)/Makefile.am ++ $(AM_V_GEN) echo "/* Autogenerated by cxl/Makefile.am */" >$@ ++ ++cxl_SOURCES =\ ++ cxl.c \ ++ list.c \ ++ ../util/json.c \ ++ builtin.h ++ ++cxl_LDADD =\ ++ lib/libcxl.la \ ++ ../libutil.a \ ++ $(UUID_LIBS) \ ++ $(KMOD_LIBS) \ ++ $(JSON_LIBS) +diff --git a/cxl/builtin.h b/cxl/builtin.h +new file mode 100644 +index 0000000..3797f98 +--- /dev/null ++++ b/cxl/builtin.h +@@ -0,0 +1,8 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2020-2021 Intel Corporation. All rights reserved. */ ++#ifndef _CXL_BUILTIN_H_ ++#define _CXL_BUILTIN_H_ ++ ++struct cxl_ctx; ++int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx); ++#endif /* _CXL_BUILTIN_H_ */ +diff --git a/cxl/cxl.c b/cxl/cxl.c +new file mode 100644 +index 0000000..a7725f8 +--- /dev/null ++++ b/cxl/cxl.c +@@ -0,0 +1,96 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2020-2021 Intel Corporation. All rights reserved. */ ++/* Copyright (C) 2005 Andreas Ericsson. All rights reserved. */ ++ ++/* originally copied from perf and git */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++const char cxl_usage_string[] = "cxl [--version] [--help] COMMAND [ARGS]"; ++const char cxl_more_info_string[] = ++ "See 'cxl help COMMAND' for more information on a specific command.\n" ++ " cxl --list-cmds to see all available commands"; ++ ++static int cmd_version(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ printf("%s\n", VERSION); ++ return 0; ++} ++ ++static int cmd_help(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ const char * const builtin_help_subcommands[] = { ++ "list", ++ NULL, ++ }; ++ struct option builtin_help_options[] = { ++ OPT_END(), ++ }; ++ const char *builtin_help_usage[] = { ++ "cxl help [command]", ++ NULL ++ }; ++ ++ argc = parse_options_subcommand(argc, argv, builtin_help_options, ++ builtin_help_subcommands, builtin_help_usage, 0); ++ ++ if (!argv[0]) { ++ printf("\n usage: %s\n\n", cxl_usage_string); ++ printf("\n %s\n\n", cxl_more_info_string); ++ return 0; ++ } ++ ++ return help_show_man_page(argv[0], "cxl", "CXL_MAN_VIEWER"); ++} ++ ++static struct cmd_struct commands[] = { ++ { "version", .c_fn = cmd_version }, ++ { "list", .c_fn = cmd_list }, ++ { "help", .c_fn = cmd_help }, ++}; ++ ++int main(int argc, const char **argv) ++{ ++ struct cxl_ctx *ctx; ++ int rc; ++ ++ /* Look for flags.. */ ++ argv++; ++ argc--; ++ main_handle_options(&argv, &argc, cxl_usage_string, commands, ++ ARRAY_SIZE(commands)); ++ ++ if (argc > 0) { ++ if (!prefixcmp(argv[0], "--")) ++ argv[0] += 2; ++ } else { ++ /* The user didn't specify a command; give them help */ ++ printf("\n usage: %s\n\n", cxl_usage_string); ++ printf("\n %s\n\n", cxl_more_info_string); ++ goto out; ++ } ++ ++ rc = cxl_new(&ctx); ++ if (rc) ++ goto out; ++ main_handle_internal_command(argc, argv, ctx, commands, ++ ARRAY_SIZE(commands), PROG_CXL); ++ cxl_unref(ctx); ++ fprintf(stderr, "Unknown command: '%s'\n", argv[0]); ++out: ++ return 1; ++} +diff --git a/cxl/lib/Makefile.am b/cxl/lib/Makefile.am +new file mode 100644 +index 0000000..277f0cd +--- /dev/null ++++ b/cxl/lib/Makefile.am +@@ -0,0 +1,32 @@ ++include $(top_srcdir)/Makefile.am.in ++ ++%.pc: %.pc.in Makefile ++ $(SED_PROCESS) ++ ++pkginclude_HEADERS = ../libcxl.h ++lib_LTLIBRARIES = libcxl.la ++ ++libcxl_la_SOURCES =\ ++ ../libcxl.h \ ++ private.h \ ++ ../../util/sysfs.c \ ++ ../../util/sysfs.h \ ++ ../../util/log.c \ ++ ../../util/log.h \ ++ libcxl.c ++ ++libcxl_la_LIBADD =\ ++ $(UUID_LIBS) \ ++ $(KMOD_LIBS) ++ ++EXTRA_DIST += libcxl.sym ++ ++libcxl_la_LDFLAGS = $(AM_LDFLAGS) \ ++ -version-info $(LIBCXL_CURRENT):$(LIBCXL_REVISION):$(LIBCXL_AGE) \ ++ -Wl,--version-script=$(top_srcdir)/cxl/lib/libcxl.sym ++libcxl_la_DEPENDENCIES = libcxl.sym ++ ++pkgconfigdir = $(libdir)/pkgconfig ++pkgconfig_DATA = libcxl.pc ++EXTRA_DIST += libcxl.pc.in ++CLEANFILES += libcxl.pc +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +new file mode 100644 +index 0000000..c15e987 +--- /dev/null ++++ b/cxl/lib/libcxl.c +@@ -0,0 +1,345 @@ ++// SPDX-License-Identifier: LGPL-2.1 ++// Copyright (C) 2020-2021, Intel Corporation. All rights reserved. ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include "private.h" ++ ++/** ++ * struct cxl_ctx - library user context to find "nd" instances ++ * ++ * Instantiate with cxl_new(), which takes an initial reference. Free ++ * the context by dropping the reference count to zero with ++ * cxl_unref(), or take additional references with cxl_ref() ++ * @timeout: default library timeout in milliseconds ++ */ ++struct cxl_ctx { ++ /* log_ctx must be first member for cxl_set_log_fn compat */ ++ struct log_ctx ctx; ++ int refcount; ++ void *userdata; ++ int memdevs_init; ++ struct list_head memdevs; ++ struct kmod_ctx *kmod_ctx; ++ void *private_data; ++}; ++ ++static void free_memdev(struct cxl_memdev *memdev, struct list_head *head) ++{ ++ if (head) ++ list_del_from(head, &memdev->list); ++ kmod_module_unref(memdev->module); ++ free(memdev->firmware_version); ++ free(memdev->dev_buf); ++ free(memdev->dev_path); ++ free(memdev); ++} ++ ++/** ++ * cxl_get_userdata - retrieve stored data pointer from library context ++ * @ctx: cxl library context ++ * ++ * This might be useful to access from callbacks like a custom logging ++ * function. ++ */ ++CXL_EXPORT void *cxl_get_userdata(struct cxl_ctx *ctx) ++{ ++ if (ctx == NULL) ++ return NULL; ++ return ctx->userdata; ++} ++ ++/** ++ * cxl_set_userdata - store custom @userdata in the library context ++ * @ctx: cxl library context ++ * @userdata: data pointer ++ */ ++CXL_EXPORT void cxl_set_userdata(struct cxl_ctx *ctx, void *userdata) ++{ ++ if (ctx == NULL) ++ return; ++ ctx->userdata = userdata; ++} ++ ++CXL_EXPORT void cxl_set_private_data(struct cxl_ctx *ctx, void *data) ++{ ++ ctx->private_data = data; ++} ++ ++CXL_EXPORT void *cxl_get_private_data(struct cxl_ctx *ctx) ++{ ++ return ctx->private_data; ++} ++ ++/** ++ * cxl_new - instantiate a new library context ++ * @ctx: context to establish ++ * ++ * Returns zero on success and stores an opaque pointer in ctx. The ++ * context is freed by cxl_unref(), i.e. cxl_new() implies an ++ * internal cxl_ref(). ++ */ ++CXL_EXPORT int cxl_new(struct cxl_ctx **ctx) ++{ ++ struct kmod_ctx *kmod_ctx; ++ struct cxl_ctx *c; ++ int rc = 0; ++ ++ c = calloc(1, sizeof(struct cxl_ctx)); ++ if (!c) ++ return -ENOMEM; ++ ++ kmod_ctx = kmod_new(NULL, NULL); ++ if (check_kmod(kmod_ctx) != 0) { ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ c->refcount = 1; ++ log_init(&c->ctx, "libcxl", "CXL_LOG"); ++ info(c, "ctx %p created\n", c); ++ dbg(c, "log_priority=%d\n", c->ctx.log_priority); ++ *ctx = c; ++ list_head_init(&c->memdevs); ++ c->kmod_ctx = kmod_ctx; ++ ++ return 0; ++out: ++ free(c); ++ return rc; ++} ++ ++/** ++ * cxl_ref - take an additional reference on the context ++ * @ctx: context established by cxl_new() ++ */ ++CXL_EXPORT struct cxl_ctx *cxl_ref(struct cxl_ctx *ctx) ++{ ++ if (ctx == NULL) ++ return NULL; ++ ctx->refcount++; ++ return ctx; ++} ++ ++/** ++ * cxl_unref - drop a context reference count ++ * @ctx: context established by cxl_new() ++ * ++ * Drop a reference and if the resulting reference count is 0 destroy ++ * the context. ++ */ ++CXL_EXPORT void cxl_unref(struct cxl_ctx *ctx) ++{ ++ struct cxl_memdev *memdev, *_d; ++ ++ if (ctx == NULL) ++ return; ++ ctx->refcount--; ++ if (ctx->refcount > 0) ++ return; ++ ++ list_for_each_safe(&ctx->memdevs, memdev, _d, list) ++ free_memdev(memdev, &ctx->memdevs); ++ ++ kmod_unref(ctx->kmod_ctx); ++ info(ctx, "context %p released\n", ctx); ++ free(ctx); ++} ++ ++/** ++ * cxl_set_log_fn - override default log routine ++ * @ctx: cxl library context ++ * @log_fn: function to be called for logging messages ++ * ++ * The built-in logging writes to stderr. It can be overridden by a ++ * custom function, to plug log messages into the user's logging ++ * functionality. ++ */ ++CXL_EXPORT void cxl_set_log_fn(struct cxl_ctx *ctx, ++ void (*cxl_log_fn)(struct cxl_ctx *ctx, int priority, ++ const char *file, int line, const char *fn, ++ const char *format, va_list args)) ++{ ++ ctx->ctx.log_fn = (log_fn) cxl_log_fn; ++ info(ctx, "custom logging function %p registered\n", cxl_log_fn); ++} ++ ++/** ++ * cxl_get_log_priority - retrieve current library loglevel (syslog) ++ * @ctx: cxl library context ++ */ ++CXL_EXPORT int cxl_get_log_priority(struct cxl_ctx *ctx) ++{ ++ return ctx->ctx.log_priority; ++} ++ ++/** ++ * cxl_set_log_priority - set log verbosity ++ * @priority: from syslog.h, LOG_ERR, LOG_INFO, LOG_DEBUG ++ * ++ * Note: LOG_DEBUG requires library be built with "configure --enable-debug" ++ */ ++CXL_EXPORT void cxl_set_log_priority(struct cxl_ctx *ctx, int priority) ++{ ++ ctx->ctx.log_priority = priority; ++} ++ ++static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) ++{ ++ const char *devname = devpath_to_devname(cxlmem_base); ++ char *path = calloc(1, strlen(cxlmem_base) + 100); ++ struct cxl_ctx *ctx = parent; ++ struct cxl_memdev *memdev, *memdev_dup; ++ char buf[SYSFS_ATTR_SIZE]; ++ struct stat st; ++ ++ if (!path) ++ return NULL; ++ dbg(ctx, "%s: base: \'%s\'\n", devname, cxlmem_base); ++ ++ memdev = calloc(1, sizeof(*memdev)); ++ if (!memdev) ++ goto err_dev; ++ memdev->id = id; ++ memdev->ctx = ctx; ++ ++ sprintf(path, "/dev/cxl/%s", devname); ++ if (stat(path, &st) < 0) ++ goto err_read; ++ memdev->major = major(st.st_rdev); ++ memdev->minor = minor(st.st_rdev); ++ ++ sprintf(path, "%s/pmem/size", cxlmem_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ goto err_read; ++ memdev->pmem_size = strtoull(buf, NULL, 0); ++ ++ sprintf(path, "%s/ram/size", cxlmem_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ goto err_read; ++ memdev->ram_size = strtoull(buf, NULL, 0); ++ ++ sprintf(path, "%s/payload_max", cxlmem_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ goto err_read; ++ memdev->payload_max = strtoull(buf, NULL, 0); ++ if (memdev->payload_max < 0) ++ goto err_read; ++ ++ memdev->dev_path = strdup(cxlmem_base); ++ if (!memdev->dev_path) ++ goto err_read; ++ ++ sprintf(path, "%s/firmware_version", cxlmem_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ goto err_read; ++ ++ memdev->firmware_version = strdup(buf); ++ if (!memdev->firmware_version) ++ goto err_read; ++ ++ memdev->dev_buf = calloc(1, strlen(cxlmem_base) + 50); ++ if (!memdev->dev_buf) ++ goto err_read; ++ memdev->buf_len = strlen(cxlmem_base) + 50; ++ ++ cxl_memdev_foreach(ctx, memdev_dup) ++ if (memdev_dup->id == memdev->id) { ++ free_memdev(memdev, NULL); ++ free(path); ++ return memdev_dup; ++ } ++ ++ list_add(&ctx->memdevs, &memdev->list); ++ free(path); ++ return memdev; ++ ++ err_read: ++ free(memdev->firmware_version); ++ free(memdev->dev_buf); ++ free(memdev->dev_path); ++ free(memdev); ++ err_dev: ++ free(path); ++ return NULL; ++} ++ ++static void cxl_memdevs_init(struct cxl_ctx *ctx) ++{ ++ if (ctx->memdevs_init) ++ return; ++ ++ ctx->memdevs_init = 1; ++ ++ sysfs_device_parse(ctx, "/sys/bus/cxl/devices", "mem", ctx, ++ add_cxl_memdev); ++} ++ ++CXL_EXPORT struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev) ++{ ++ return memdev->ctx; ++} ++ ++CXL_EXPORT struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx) ++{ ++ cxl_memdevs_init(ctx); ++ ++ return list_top(&ctx->memdevs, struct cxl_memdev, list); ++} ++ ++CXL_EXPORT struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev) ++{ ++ struct cxl_ctx *ctx = memdev->ctx; ++ ++ return list_next(&ctx->memdevs, memdev, list); ++} ++ ++CXL_EXPORT int cxl_memdev_get_id(struct cxl_memdev *memdev) ++{ ++ return memdev->id; ++} ++ ++CXL_EXPORT const char *cxl_memdev_get_devname(struct cxl_memdev *memdev) ++{ ++ return devpath_to_devname(memdev->dev_path); ++} ++ ++CXL_EXPORT int cxl_memdev_get_major(struct cxl_memdev *memdev) ++{ ++ return memdev->major; ++} ++ ++CXL_EXPORT int cxl_memdev_get_minor(struct cxl_memdev *memdev) ++{ ++ return memdev->minor; ++} ++ ++CXL_EXPORT unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev) ++{ ++ return memdev->pmem_size; ++} ++ ++CXL_EXPORT unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev) ++{ ++ return memdev->ram_size; ++} ++ ++CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev) ++{ ++ return memdev->firmware_version; ++} +diff --git a/cxl/lib/libcxl.pc.in b/cxl/lib/libcxl.pc.in +new file mode 100644 +index 0000000..949fcdc +--- /dev/null ++++ b/cxl/lib/libcxl.pc.in +@@ -0,0 +1,11 @@ ++prefix=@prefix@ ++exec_prefix=@exec_prefix@ ++libdir=@libdir@ ++includedir=@includedir@ ++ ++Name: libcxl ++Description: Manage CXL devices ++Version: @VERSION@ ++Libs: -L${libdir} -lcxl ++Libs.private: ++Cflags: -I${includedir} +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +new file mode 100644 +index 0000000..2616e5c +--- /dev/null ++++ b/cxl/lib/libcxl.sym +@@ -0,0 +1,25 @@ ++LIBCXL_1 { ++global: ++ cxl_get_userdata; ++ cxl_set_userdata; ++ cxl_get_private_data; ++ cxl_set_private_data; ++ cxl_ref; ++ cxl_get_log_priority; ++ cxl_set_log_fn; ++ cxl_unref; ++ cxl_set_log_priority; ++ cxl_new; ++ cxl_memdev_get_first; ++ cxl_memdev_get_next; ++ cxl_memdev_get_id; ++ cxl_memdev_get_devname; ++ cxl_memdev_get_major; ++ cxl_memdev_get_minor; ++ cxl_memdev_get_ctx; ++ cxl_memdev_get_pmem_size; ++ cxl_memdev_get_ram_size; ++ cxl_memdev_get_firmware_verison; ++local: ++ *; ++}; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +new file mode 100644 +index 0000000..fc88fa1 +--- /dev/null ++++ b/cxl/lib/private.h +@@ -0,0 +1,29 @@ ++/* SPDX-License-Identifier: LGPL-2.1 */ ++/* Copyright (C) 2020-2021, Intel Corporation. All rights reserved. */ ++#ifndef _LIBCXL_PRIVATE_H_ ++#define _LIBCXL_PRIVATE_H_ ++ ++#include ++ ++#define CXL_EXPORT __attribute__ ((visibility("default"))) ++ ++struct cxl_memdev { ++ int id, major, minor; ++ void *dev_buf; ++ size_t buf_len; ++ char *dev_path; ++ char *firmware_version; ++ struct cxl_ctx *ctx; ++ struct list_node list; ++ unsigned long long pmem_size; ++ unsigned long long ram_size; ++ int payload_max; ++ struct kmod_module *module; ++}; ++ ++static inline int check_kmod(struct kmod_ctx *kmod_ctx) ++{ ++ return kmod_ctx ? 0 : -ENXIO; ++} ++ ++#endif /* _LIBCXL_PRIVATE_H_ */ +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +new file mode 100644 +index 0000000..fd06790 +--- /dev/null ++++ b/cxl/libcxl.h +@@ -0,0 +1,55 @@ ++/* SPDX-License-Identifier: LGPL-2.1 */ ++/* Copyright (C) 2020-2021, Intel Corporation. All rights reserved. */ ++#ifndef _LIBCXL_H_ ++#define _LIBCXL_H_ ++ ++#include ++#include ++ ++#ifdef HAVE_UUID ++#include ++#else ++typedef unsigned char uuid_t[16]; ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct cxl_ctx; ++struct cxl_ctx *cxl_ref(struct cxl_ctx *ctx); ++void cxl_unref(struct cxl_ctx *ctx); ++int cxl_new(struct cxl_ctx **ctx); ++void cxl_set_log_fn(struct cxl_ctx *ctx, ++ void (*log_fn)(struct cxl_ctx *ctx, int priority, ++ const char *file, int line, const char *fn, ++ const char *format, va_list args)); ++int cxl_get_log_priority(struct cxl_ctx *ctx); ++void cxl_set_log_priority(struct cxl_ctx *ctx, int priority); ++void cxl_set_userdata(struct cxl_ctx *ctx, void *userdata); ++void *cxl_get_userdata(struct cxl_ctx *ctx); ++void cxl_set_private_data(struct cxl_ctx *ctx, void *data); ++void *cxl_get_private_data(struct cxl_ctx *ctx); ++ ++struct cxl_memdev; ++struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx); ++struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev); ++int cxl_memdev_get_id(struct cxl_memdev *memdev); ++const char *cxl_memdev_get_devname(struct cxl_memdev *memdev); ++int cxl_memdev_get_major(struct cxl_memdev *memdev); ++int cxl_memdev_get_minor(struct cxl_memdev *memdev); ++struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev); ++unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev); ++unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev); ++const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev); ++ ++#define cxl_memdev_foreach(ctx, memdev) \ ++ for (memdev = cxl_memdev_get_first(ctx); \ ++ memdev != NULL; \ ++ memdev = cxl_memdev_get_next(memdev)) ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif +diff --git a/cxl/list.c b/cxl/list.c +new file mode 100644 +index 0000000..043d20c +--- /dev/null ++++ b/cxl/list.c +@@ -0,0 +1,121 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2020-2021 Intel Corporation. All rights reserved. */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct { ++ bool memdevs; ++ bool idle; ++ bool human; ++} list; ++ ++static unsigned long listopts_to_flags(void) ++{ ++ unsigned long flags = 0; ++ ++ if (list.idle) ++ flags |= UTIL_JSON_IDLE; ++ if (list.human) ++ flags |= UTIL_JSON_HUMAN; ++ return flags; ++} ++ ++static struct { ++ const char *memdev; ++} param; ++ ++static int did_fail; ++ ++#define fail(fmt, ...) \ ++do { \ ++ did_fail = 1; \ ++ fprintf(stderr, "cxl-%s:%s:%d: " fmt, \ ++ VERSION, __func__, __LINE__, ##__VA_ARGS__); \ ++} while (0) ++ ++static int num_list_flags(void) ++{ ++ return list.memdevs; ++} ++ ++int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ const struct option options[] = { ++ OPT_STRING('m', "memdev", ¶m.memdev, "memory device name", ++ "filter by CXL memory device name"), ++ OPT_BOOLEAN('M', "memdevs", &list.memdevs, ++ "include CXL memory device info"), ++ OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"), ++ OPT_BOOLEAN('u', "human", &list.human, ++ "use human friendly number formats "), ++ OPT_END(), ++ }; ++ const char * const u[] = { ++ "cxl list []", ++ NULL ++ }; ++ struct json_object *jdevs = NULL; ++ unsigned long list_flags; ++ struct cxl_memdev *memdev; ++ int i; ++ ++ argc = parse_options(argc, argv, options, u, 0); ++ for (i = 0; i < argc; i++) ++ error("unknown parameter \"%s\"\n", argv[i]); ++ ++ if (argc) ++ usage_with_options(u, options); ++ ++ if (num_list_flags() == 0) { ++ /* ++ * TODO: We likely want to list regions by default if nothing ++ * was explicitly asked for. But until we have region support, ++ * print this error asking for devices explicitly. ++ * Once region support is added, this TODO can be removed. ++ */ ++ error("please specify entities to list, e.g. using -m/-M\n"); ++ usage_with_options(u, options); ++ } ++ ++ list_flags = listopts_to_flags(); ++ ++ cxl_memdev_foreach(ctx, memdev) { ++ struct json_object *jdev = NULL; ++ ++ if (!util_cxl_memdev_filter(memdev, param.memdev)) ++ continue; ++ ++ if (list.memdevs) { ++ if (!jdevs) { ++ jdevs = json_object_new_array(); ++ if (!jdevs) { ++ fail("\n"); ++ continue; ++ } ++ } ++ ++ jdev = util_cxl_memdev_to_json(memdev, list_flags); ++ if (!jdev) { ++ fail("\n"); ++ continue; ++ } ++ json_object_array_add(jdevs, jdev); ++ } ++ } ++ ++ if (jdevs) ++ util_display_json_array(stdout, jdevs, list_flags); ++ ++ if (did_fail) ++ return -ENOMEM; ++ return 0; ++} +diff --git a/util/filter.c b/util/filter.c +index 8b4aad3..d81dade 100644 +--- a/util/filter.c ++++ b/util/filter.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + struct ndctl_bus *util_bus_filter(struct ndctl_bus *bus, const char *__ident) + { +@@ -339,6 +340,25 @@ struct daxctl_region *util_daxctl_region_filter(struct daxctl_region *region, + return NULL; + } + ++struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, ++ const char *ident) ++{ ++ int memdev_id; ++ ++ if (!ident || strcmp(ident, "all") == 0) ++ return memdev; ++ ++ if (strcmp(ident, cxl_memdev_get_devname(memdev)) == 0) ++ return memdev; ++ ++ if ((sscanf(ident, "%d", &memdev_id) == 1 ++ || sscanf(ident, "mem%d", &memdev_id) == 1) ++ && cxl_memdev_get_id(memdev) == memdev_id) ++ return memdev; ++ ++ return NULL; ++} ++ + enum ndctl_namespace_mode util_nsmode(const char *mode) + { + if (!mode) +diff --git a/util/filter.h b/util/filter.h +index 1e1a41c..9a80d65 100644 +--- a/util/filter.h ++++ b/util/filter.h +@@ -29,6 +29,8 @@ struct daxctl_dev *util_daxctl_dev_filter(struct daxctl_dev *dev, + const char *ident); + struct daxctl_region *util_daxctl_region_filter(struct daxctl_region *region, + const char *ident); ++struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, ++ const char *ident); + + enum ndctl_namespace_mode util_nsmode(const char *mode); + const char *util_nsmode_name(enum ndctl_namespace_mode mode); +diff --git a/util/json.c b/util/json.c +index a8d2412..3be3a92 100644 +--- a/util/json.c ++++ b/util/json.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1440,3 +1441,28 @@ struct json_object *util_badblock_rec_to_json(u64 block, u64 count, + json_object_put(jerr); + return NULL; + } ++ ++struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, ++ unsigned long flags) ++{ ++ const char *devname = cxl_memdev_get_devname(memdev); ++ struct json_object *jdev, *jobj; ++ ++ jdev = json_object_new_object(); ++ if (!devname || !jdev) ++ return NULL; ++ ++ jobj = json_object_new_string(devname); ++ if (jobj) ++ json_object_object_add(jdev, "memdev", jobj); ++ ++ jobj = util_json_object_size(cxl_memdev_get_pmem_size(memdev), flags); ++ if (jobj) ++ json_object_object_add(jdev, "pmem_size", jobj); ++ ++ jobj = util_json_object_size(cxl_memdev_get_ram_size(memdev), flags); ++ if (jobj) ++ json_object_object_add(jdev, "ram_size", jobj); ++ ++ return jdev; ++} +diff --git a/util/json.h b/util/json.h +index 0f09e36..91918c8 100644 +--- a/util/json.h ++++ b/util/json.h +@@ -55,4 +55,7 @@ struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm); + struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm, + unsigned long flags); + struct json_object *util_region_capabilities_to_json(struct ndctl_region *region); ++struct cxl_memdev; ++struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, ++ unsigned long flags); + #endif /* __NDCTL_JSON_H__ */ +diff --git a/util/main.h b/util/main.h +index c89a843..80b55c4 100644 +--- a/util/main.h ++++ b/util/main.h +@@ -10,16 +10,19 @@ + enum program { + PROG_NDCTL, + PROG_DAXCTL, ++ PROG_CXL, + }; + + struct ndctl_ctx; + struct daxctl_ctx; ++struct cxl_ctx; + + struct cmd_struct { + const char *cmd; + union { + int (*n_fn)(int, const char **, struct ndctl_ctx *ctx); + int (*d_fn)(int, const char **, struct daxctl_ctx *ctx); ++ int (*c_fn)(int, const char **, struct cxl_ctx *ctx); + }; + }; + +-- +2.27.0 + diff --git a/SOURCES/0033-cxl-add-a-local-copy-of-the-cxl_mem-UAPI-header.patch b/SOURCES/0033-cxl-add-a-local-copy-of-the-cxl_mem-UAPI-header.patch new file mode 100644 index 0000000..54d2230 --- /dev/null +++ b/SOURCES/0033-cxl-add-a-local-copy-of-the-cxl_mem-UAPI-header.patch @@ -0,0 +1,256 @@ +From 894fb9b2b59364f7f5683ea68c8bd765223a4ca8 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:25 -0600 +Subject: [PATCH 033/217] cxl: add a local copy of the cxl_mem UAPI header + +While CXL functionality is under development, it is useful to have a +local copy of the UAPI header for cxl_mem definitions. This allows +building cxl and libcxl on systems where the appropriate kernel headers +are not installed in the usual locations. + +Cc: Ben Widawsky +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Makefile.am | 3 +- + Makefile.am.in | 1 + + cxl/cxl_mem.h | 189 ++++++++++++++++++++++++++++++++++++++++++++ + cxl/lib/Makefile.am | 2 +- + 4 files changed, 193 insertions(+), 2 deletions(-) + create mode 100644 cxl/cxl_mem.h + +diff --git a/Makefile.am b/Makefile.am +index 428fd40..4904ee7 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -89,4 +89,5 @@ libutil_a_SOURCES = \ + + nobase_include_HEADERS = \ + daxctl/libdaxctl.h \ +- cxl/libcxl.h ++ cxl/libcxl.h \ ++ cxl/cxl_mem.h +diff --git a/Makefile.am.in b/Makefile.am.in +index aaeee53..a748128 100644 +--- a/Makefile.am.in ++++ b/Makefile.am.in +@@ -11,6 +11,7 @@ AM_CPPFLAGS = \ + -DNDCTL_MAN_PATH=\""$(mandir)"\" \ + -I${top_srcdir}/ndctl/lib \ + -I${top_srcdir}/ndctl \ ++ -I${top_srcdir}/cxl \ + -I${top_srcdir}/ \ + $(KMOD_CFLAGS) \ + $(UDEV_CFLAGS) \ +diff --git a/cxl/cxl_mem.h b/cxl/cxl_mem.h +new file mode 100644 +index 0000000..d38cc9c +--- /dev/null ++++ b/cxl/cxl_mem.h +@@ -0,0 +1,189 @@ ++/* SPDX-License-Identifier: LGPL-2.1 */ ++/* Copyright (C) 2020-2021, Intel Corporation. All rights reserved. */ ++/* ++ * CXL IOCTLs for Memory Devices ++ */ ++ ++#ifndef _UAPI_CXL_MEM_H_ ++#define _UAPI_CXL_MEM_H_ ++ ++#include ++#include ++#include ++ ++#define __user ++ ++/** ++ * DOC: UAPI ++ * ++ * Not all of all commands that the driver supports are always available for use ++ * by userspace. Userspace must check the results from the QUERY command in ++ * order to determine the live set of commands. ++ */ ++ ++#define CXL_MEM_QUERY_COMMANDS _IOR(0xCE, 1, struct cxl_mem_query_commands) ++#define CXL_MEM_SEND_COMMAND _IOWR(0xCE, 2, struct cxl_send_command) ++ ++#define CXL_CMDS \ ++ ___C(INVALID, "Invalid Command"), \ ++ ___C(IDENTIFY, "Identify Command"), \ ++ ___C(RAW, "Raw device command"), \ ++ ___C(GET_SUPPORTED_LOGS, "Get Supported Logs"), \ ++ ___C(GET_FW_INFO, "Get FW Info"), \ ++ ___C(GET_PARTITION_INFO, "Get Partition Information"), \ ++ ___C(GET_LSA, "Get Label Storage Area"), \ ++ ___C(GET_HEALTH_INFO, "Get Health Info"), \ ++ ___C(GET_LOG, "Get Log"), \ ++ ___C(SET_PARTITION_INFO, "Set Partition Information"), \ ++ ___C(SET_LSA, "Set Label Storage Area"), \ ++ ___C(GET_ALERT_CONFIG, "Get Alert Configuration"), \ ++ ___C(SET_ALERT_CONFIG, "Set Alert Configuration"), \ ++ ___C(GET_SHUTDOWN_STATE, "Get Shutdown State"), \ ++ ___C(SET_SHUTDOWN_STATE, "Set Shutdown State"), \ ++ ___C(GET_POISON, "Get Poison List"), \ ++ ___C(INJECT_POISON, "Inject Poison"), \ ++ ___C(CLEAR_POISON, "Clear Poison"), \ ++ ___C(GET_SCAN_MEDIA_CAPS, "Get Scan Media Capabilities"), \ ++ ___C(SCAN_MEDIA, "Scan Media"), \ ++ ___C(GET_SCAN_MEDIA, "Get Scan Media Results"), \ ++ ___C(MAX, "invalid / last command") ++ ++#define ___C(a, b) CXL_MEM_COMMAND_ID_##a ++enum { CXL_CMDS }; ++ ++#undef ___C ++#define ___C(a, b) { b } ++static const struct { ++ const char *name; ++} cxl_command_names[] = { CXL_CMDS }; ++ ++/* ++ * Here's how this actually breaks out: ++ * cxl_command_names[] = { ++ * [CXL_MEM_COMMAND_ID_INVALID] = { "Invalid Command" }, ++ * [CXL_MEM_COMMAND_ID_IDENTIFY] = { "Identify Command" }, ++ * ... ++ * [CXL_MEM_COMMAND_ID_MAX] = { "invalid / last command" }, ++ * }; ++ */ ++ ++#undef ___C ++ ++/** ++ * struct cxl_command_info - Command information returned from a query. ++ * @id: ID number for the command. ++ * @flags: Flags that specify command behavior. ++ * @size_in: Expected input size, or -1 if variable length. ++ * @size_out: Expected output size, or -1 if variable length. ++ * ++ * Represents a single command that is supported by both the driver and the ++ * hardware. This is returned as part of an array from the query ioctl. The ++ * following would be a command that takes a variable length input and returns 0 ++ * bytes of output. ++ * ++ * - @id = 10 ++ * - @flags = 0 ++ * - @size_in = -1 ++ * - @size_out = 0 ++ * ++ * See struct cxl_mem_query_commands. ++ */ ++struct cxl_command_info { ++ __u32 id; ++ ++ __u32 flags; ++#define CXL_MEM_COMMAND_FLAG_MASK GENMASK(0, 0) ++ ++ __s32 size_in; ++ __s32 size_out; ++}; ++ ++/** ++ * struct cxl_mem_query_commands - Query supported commands. ++ * @n_commands: In/out parameter. When @n_commands is > 0, the driver will ++ * return min(num_support_commands, n_commands). When @n_commands ++ * is 0, driver will return the number of total supported commands. ++ * @rsvd: Reserved for future use. ++ * @commands: Output array of supported commands. This array must be allocated ++ * by userspace to be at least min(num_support_commands, @n_commands) ++ * ++ * Allow userspace to query the available commands supported by both the driver, ++ * and the hardware. Commands that aren't supported by either the driver, or the ++ * hardware are not returned in the query. ++ * ++ * Examples: ++ * ++ * - { .n_commands = 0 } // Get number of supported commands ++ * - { .n_commands = 15, .commands = buf } // Return first 15 (or less) ++ * supported commands ++ * ++ * See struct cxl_command_info. ++ */ ++struct cxl_mem_query_commands { ++ /* ++ * Input: Number of commands to return (space allocated by user) ++ * Output: Number of commands supported by the driver/hardware ++ * ++ * If n_commands is 0, kernel will only return number of commands and ++ * not try to populate commands[], thus allowing userspace to know how ++ * much space to allocate ++ */ ++ __u32 n_commands; ++ __u32 rsvd; ++ ++ struct cxl_command_info __user commands[]; /* out: supported commands */ ++}; ++ ++/** ++ * struct cxl_send_command - Send a command to a memory device. ++ * @id: The command to send to the memory device. This must be one of the ++ * commands returned by the query command. ++ * @flags: Flags for the command (input). ++ * @raw: Special fields for raw commands ++ * @raw.opcode: Opcode passed to hardware when using the RAW command. ++ * @raw.rsvd: Must be zero. ++ * @rsvd: Must be zero. ++ * @retval: Return value from the memory device (output). ++ * @in: Parameters associated with input payload. ++ * @in.size: Size of the payload to provide to the device (input). ++ * @in.rsvd: Must be zero. ++ * @in.payload: Pointer to memory for payload input, payload is little endian. ++ * @out: Parameters associated with output payload. ++ * @out.size: Size of the payload received from the device (input/output). This ++ * field is filled in by userspace to let the driver know how much ++ * space was allocated for output. It is populated by the driver to ++ * let userspace know how large the output payload actually was. ++ * @out.rsvd: Must be zero. ++ * @out.payload: Pointer to memory for payload output, payload is little endian. ++ * ++ * Mechanism for userspace to send a command to the hardware for processing. The ++ * driver will do basic validation on the command sizes. In some cases even the ++ * payload may be introspected. Userspace is required to allocate large enough ++ * buffers for size_out which can be variable length in certain situations. ++ */ ++struct cxl_send_command { ++ __u32 id; ++ __u32 flags; ++ union { ++ struct { ++ __u16 opcode; ++ __u16 rsvd; ++ } raw; ++ __u32 rsvd; ++ }; ++ __u32 retval; ++ ++ struct { ++ __s32 size; ++ __u32 rsvd; ++ __u64 payload; ++ } in; ++ ++ struct { ++ __s32 size; ++ __u32 rsvd; ++ __u64 payload; ++ } out; ++}; ++ ++#endif +diff --git a/cxl/lib/Makefile.am b/cxl/lib/Makefile.am +index 277f0cd..72c9ccd 100644 +--- a/cxl/lib/Makefile.am ++++ b/cxl/lib/Makefile.am +@@ -3,7 +3,7 @@ include $(top_srcdir)/Makefile.am.in + %.pc: %.pc.in Makefile + $(SED_PROCESS) + +-pkginclude_HEADERS = ../libcxl.h ++pkginclude_HEADERS = ../libcxl.h ../cxl_mem.h + lib_LTLIBRARIES = libcxl.la + + libcxl_la_SOURCES =\ +-- +2.27.0 + diff --git a/SOURCES/0034-util-add-the-struct_size-helper-from-the-kernel.patch b/SOURCES/0034-util-add-the-struct_size-helper-from-the-kernel.patch new file mode 100644 index 0000000..e2291d5 --- /dev/null +++ b/SOURCES/0034-util-add-the-struct_size-helper-from-the-kernel.patch @@ -0,0 +1,114 @@ +From 7aa7c7be6e803de267a165237e23577ab496e792 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:26 -0600 +Subject: [PATCH 034/217] util: add the struct_size() helper from the kernel + +Add struct_size() from include/linux/overflow.h which calculates the +size of a struct with a trailing variable length array. + +Suggested-by: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + util/size.h | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + util/util.h | 6 ++++++ + 2 files changed, 68 insertions(+) + +diff --git a/util/size.h b/util/size.h +index 646edae..a0f3593 100644 +--- a/util/size.h ++++ b/util/size.h +@@ -4,6 +4,8 @@ + #ifndef _NDCTL_SIZE_H_ + #define _NDCTL_SIZE_H_ + #include ++#include ++#include + + #define SZ_1K 0x00000400 + #define SZ_4K 0x00001000 +@@ -30,4 +32,64 @@ static inline bool is_power_of_2(unsigned long long v) + #define BITS_PER_LONG (sizeof(unsigned long) * 8) + #define HPAGE_SIZE (2 << 20) + ++/* ++ * Helpers for struct_size() copied from include/linux/overflow.h (GPL-2.0) ++ * ++ * For simplicity and code hygiene, the fallback code below insists on ++ * a, b and *d having the same type (similar to the min() and max() ++ * macros), whereas gcc's type-generic overflow checkers accept ++ * different types. Hence we don't just make check_add_overflow an ++ * alias for __builtin_add_overflow, but add type checks similar to ++ * below. ++ */ ++#define check_add_overflow(a, b, d) (({ \ ++ typeof(a) __a = (a); \ ++ typeof(b) __b = (b); \ ++ typeof(d) __d = (d); \ ++ (void) (&__a == &__b); \ ++ (void) (&__a == __d); \ ++ __builtin_add_overflow(__a, __b, __d); \ ++})) ++ ++#define check_mul_overflow(a, b, d) (({ \ ++ typeof(a) __a = (a); \ ++ typeof(b) __b = (b); \ ++ typeof(d) __d = (d); \ ++ (void) (&__a == &__b); \ ++ (void) (&__a == __d); \ ++ __builtin_mul_overflow(__a, __b, __d); \ ++})) ++ ++/* ++ * Compute a*b+c, returning SIZE_MAX on overflow. Internal helper for ++ * struct_size() below. ++ */ ++static inline size_t __ab_c_size(size_t a, size_t b, size_t c) ++{ ++ size_t bytes; ++ ++ if (check_mul_overflow(a, b, &bytes)) ++ return SIZE_MAX; ++ if (check_add_overflow(bytes, c, &bytes)) ++ return SIZE_MAX; ++ ++ return bytes; ++} ++ ++/** ++ * struct_size() - Calculate size of structure with trailing array. ++ * @p: Pointer to the structure. ++ * @member: Name of the array member. ++ * @count: Number of elements in the array. ++ * ++ * Calculates size of memory needed for structure @p followed by an ++ * array of @count number of @member elements. ++ * ++ * Return: number of bytes needed or SIZE_MAX on overflow. ++ */ ++#define struct_size(p, member, count) \ ++ __ab_c_size(count, \ ++ sizeof(*(p)->member) + __must_be_array((p)->member),\ ++ sizeof(*(p))) ++ + #endif /* _NDCTL_SIZE_H_ */ +diff --git a/util/util.h b/util/util.h +index ae0e4e1..b2b4ae6 100644 +--- a/util/util.h ++++ b/util/util.h +@@ -63,6 +63,12 @@ + #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) + #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) + ++/* Are two types/vars the same type (ignoring qualifiers)? */ ++#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) ++ ++/* &a[0] degrades to a pointer: a different type from an array */ ++#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0])) ++ + enum { + READ, WRITE, + }; +-- +2.27.0 + diff --git a/SOURCES/0035-libcxl-add-support-for-command-query-and-submission.patch b/SOURCES/0035-libcxl-add-support-for-command-query-and-submission.patch new file mode 100644 index 0000000..f2af1f0 --- /dev/null +++ b/SOURCES/0035-libcxl-add-support-for-command-query-and-submission.patch @@ -0,0 +1,535 @@ +From 96afebd1b32ff839129f3bc0ba323ab5f04674ea Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:27 -0600 +Subject: [PATCH 035/217] libcxl: add support for command query and submission + +Add a set of APIs around 'cxl_cmd' for querying the kernel for supported +commands, allocating and validating command structures against the +supported set, and submitting the commands. + +'Query Commands' and 'Send Command' are implemented as IOCTLs in the +kernel. 'Query Commands' returns information about each supported +command, such as flags governing its use, or input and output payload +sizes. This information is used to validate command support, as well as +set up input and output buffers for command submission. + +Cc: Ben Widawsky +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 390 +++++++++++++++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 9 ++ + cxl/lib/private.h | 33 ++++ + cxl/libcxl.h | 11 ++ + 4 files changed, 443 insertions(+) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index c15e987..727d599 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -9,14 +9,17 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + + #include ++#include + #include + #include ++#include + #include + #include "private.h" + +@@ -343,3 +346,390 @@ CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev + { + return memdev->firmware_version; + } ++ ++CXL_EXPORT void cxl_cmd_unref(struct cxl_cmd *cmd) ++{ ++ if (!cmd) ++ return; ++ if (--cmd->refcount == 0) { ++ free(cmd->query_cmd); ++ free(cmd->send_cmd); ++ free(cmd->input_payload); ++ free(cmd->output_payload); ++ free(cmd); ++ } ++} ++ ++CXL_EXPORT void cxl_cmd_ref(struct cxl_cmd *cmd) ++{ ++ cmd->refcount++; ++} ++ ++static int cxl_cmd_alloc_query(struct cxl_cmd *cmd, int num_cmds) ++{ ++ size_t size; ++ ++ if (!cmd) ++ return -EINVAL; ++ ++ if (cmd->query_cmd != NULL) ++ free(cmd->query_cmd); ++ ++ size = struct_size(cmd->query_cmd, commands, num_cmds); ++ if (size == SIZE_MAX) ++ return -EOVERFLOW; ++ ++ cmd->query_cmd = calloc(1, size); ++ if (!cmd->query_cmd) ++ return -ENOMEM; ++ ++ cmd->query_cmd->n_commands = num_cmds; ++ ++ return 0; ++} ++ ++static struct cxl_cmd *cxl_cmd_new(struct cxl_memdev *memdev) ++{ ++ struct cxl_cmd *cmd; ++ size_t size; ++ ++ size = sizeof(*cmd); ++ cmd = calloc(1, size); ++ if (!cmd) ++ return NULL; ++ ++ cxl_cmd_ref(cmd); ++ cmd->memdev = memdev; ++ ++ return cmd; ++} ++ ++static int __do_cmd(struct cxl_cmd *cmd, int ioctl_cmd, int fd) ++{ ++ void *cmd_buf; ++ int rc; ++ ++ switch (ioctl_cmd) { ++ case CXL_MEM_QUERY_COMMANDS: ++ cmd_buf = cmd->query_cmd; ++ break; ++ case CXL_MEM_SEND_COMMAND: ++ cmd_buf = cmd->send_cmd; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ rc = ioctl(fd, ioctl_cmd, cmd_buf); ++ if (rc < 0) ++ rc = -errno; ++ ++ return rc; ++} ++ ++static int do_cmd(struct cxl_cmd *cmd, int ioctl_cmd) ++{ ++ char *path; ++ struct stat st; ++ unsigned int major, minor; ++ int rc = 0, fd; ++ struct cxl_memdev *memdev = cmd->memdev; ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ const char *devname = cxl_memdev_get_devname(memdev); ++ ++ major = cxl_memdev_get_major(memdev); ++ minor = cxl_memdev_get_minor(memdev); ++ ++ if (asprintf(&path, "/dev/cxl/%s", devname) < 0) ++ return -ENOMEM; ++ ++ fd = open(path, O_RDWR); ++ if (fd < 0) { ++ err(ctx, "failed to open %s: %s\n", path, strerror(errno)); ++ rc = -errno; ++ goto out; ++ } ++ ++ if (fstat(fd, &st) >= 0 && S_ISCHR(st.st_mode) ++ && major(st.st_rdev) == major ++ && minor(st.st_rdev) == minor) { ++ rc = __do_cmd(cmd, ioctl_cmd, fd); ++ } else { ++ err(ctx, "failed to validate %s as a CXL memdev node\n", path); ++ rc = -ENXIO; ++ } ++ close(fd); ++out: ++ free(path); ++ return rc; ++} ++ ++static int alloc_do_query(struct cxl_cmd *cmd, int num_cmds) ++{ ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(cmd->memdev); ++ int rc; ++ ++ rc = cxl_cmd_alloc_query(cmd, num_cmds); ++ if (rc) ++ return rc; ++ ++ rc = do_cmd(cmd, CXL_MEM_QUERY_COMMANDS); ++ if (rc < 0) ++ err(ctx, "%s: query commands failed: %s\n", ++ cxl_memdev_get_devname(cmd->memdev), ++ strerror(-rc)); ++ return rc; ++} ++ ++static int cxl_cmd_do_query(struct cxl_cmd *cmd) ++{ ++ struct cxl_memdev *memdev = cmd->memdev; ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ const char *devname = cxl_memdev_get_devname(memdev); ++ int rc, n_commands; ++ ++ switch (cmd->query_status) { ++ case CXL_CMD_QUERY_OK: ++ return 0; ++ case CXL_CMD_QUERY_UNSUPPORTED: ++ return -EOPNOTSUPP; ++ case CXL_CMD_QUERY_NOT_RUN: ++ break; ++ default: ++ err(ctx, "%s: Unknown query_status %d\n", ++ devname, cmd->query_status); ++ return -EINVAL; ++ } ++ ++ rc = alloc_do_query(cmd, 0); ++ if (rc) ++ return rc; ++ ++ n_commands = cmd->query_cmd->n_commands; ++ dbg(ctx, "%s: supports %d commands\n", devname, n_commands); ++ ++ return alloc_do_query(cmd, n_commands); ++} ++ ++static int cxl_cmd_validate(struct cxl_cmd *cmd, u32 cmd_id) ++{ ++ struct cxl_memdev *memdev = cmd->memdev; ++ struct cxl_mem_query_commands *query = cmd->query_cmd; ++ const char *devname = cxl_memdev_get_devname(memdev); ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ u32 i; ++ ++ for (i = 0; i < query->n_commands; i++) { ++ struct cxl_command_info *cinfo = &query->commands[i]; ++ const char *cmd_name = cxl_command_names[cinfo->id].name; ++ ++ if (cinfo->id != cmd_id) ++ continue; ++ ++ dbg(ctx, "%s: %s: in: %d, out %d, flags: %#08x\n", ++ devname, cmd_name, cinfo->size_in, ++ cinfo->size_out, cinfo->flags); ++ ++ cmd->query_idx = i; ++ cmd->query_status = CXL_CMD_QUERY_OK; ++ return 0; ++ } ++ cmd->query_status = CXL_CMD_QUERY_UNSUPPORTED; ++ return -EOPNOTSUPP; ++} ++ ++CXL_EXPORT int cxl_cmd_set_input_payload(struct cxl_cmd *cmd, void *buf, ++ int size) ++{ ++ struct cxl_memdev *memdev = cmd->memdev; ++ ++ if (size > memdev->payload_max || size < 0) ++ return -EINVAL; ++ ++ if (!buf) { ++ ++ /* If the user didn't supply a buffer, allocate it */ ++ cmd->input_payload = calloc(1, size); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ } else { ++ /* ++ * Use user-buffer as is. If an automatic allocation was ++ * previously made (based on a fixed size from query), ++ * it will get freed during unref. ++ */ ++ cmd->send_cmd->in.payload = (u64)buf; ++ } ++ cmd->send_cmd->in.size = size; ++ ++ return 0; ++} ++ ++CXL_EXPORT int cxl_cmd_set_output_payload(struct cxl_cmd *cmd, void *buf, ++ int size) ++{ ++ struct cxl_memdev *memdev = cmd->memdev; ++ ++ if (size > memdev->payload_max || size < 0) ++ return -EINVAL; ++ ++ if (!buf) { ++ ++ /* If the user didn't supply a buffer, allocate it */ ++ cmd->output_payload = calloc(1, size); ++ if (!cmd->output_payload) ++ return -ENOMEM; ++ cmd->send_cmd->out.payload = (u64)cmd->output_payload; ++ } else { ++ /* ++ * Use user-buffer as is. If an automatic allocation was ++ * previously made (based on a fixed size from query), ++ * it will get freed during unref. ++ */ ++ cmd->send_cmd->out.payload = (u64)buf; ++ } ++ cmd->send_cmd->out.size = size; ++ ++ return 0; ++} ++ ++static int cxl_cmd_alloc_send(struct cxl_cmd *cmd, u32 cmd_id) ++{ ++ struct cxl_mem_query_commands *query = cmd->query_cmd; ++ struct cxl_command_info *cinfo = &query->commands[cmd->query_idx]; ++ size_t size; ++ ++ if (!query) ++ return -EINVAL; ++ ++ size = sizeof(struct cxl_send_command); ++ cmd->send_cmd = calloc(1, size); ++ if (!cmd->send_cmd) ++ return -ENOMEM; ++ ++ if (cinfo->id != cmd_id) ++ return -EINVAL; ++ ++ cmd->send_cmd->id = cmd_id; ++ ++ if (cinfo->size_in > 0) { ++ cmd->input_payload = calloc(1, cinfo->size_in); ++ if (!cmd->input_payload) ++ return -ENOMEM; ++ cmd->send_cmd->in.payload = (u64)cmd->input_payload; ++ cmd->send_cmd->in.size = cinfo->size_in; ++ } ++ if (cinfo->size_out > 0) { ++ cmd->output_payload = calloc(1, cinfo->size_out); ++ if (!cmd->output_payload) ++ return -ENOMEM; ++ cmd->send_cmd->out.payload = (u64)cmd->output_payload; ++ cmd->send_cmd->out.size = cinfo->size_out; ++ } ++ ++ return 0; ++} ++ ++static struct cxl_cmd *cxl_cmd_new_generic(struct cxl_memdev *memdev, ++ u32 cmd_id) ++{ ++ const char *devname = cxl_memdev_get_devname(memdev); ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ struct cxl_cmd *cmd; ++ int rc; ++ ++ cmd = cxl_cmd_new(memdev); ++ if (!cmd) ++ return NULL; ++ ++ rc = cxl_cmd_do_query(cmd); ++ if (rc) { ++ err(ctx, "%s: query returned: %s\n", devname, strerror(-rc)); ++ goto fail; ++ } ++ ++ rc = cxl_cmd_validate(cmd, cmd_id); ++ if (rc) { ++ errno = -rc; ++ goto fail; ++ } ++ ++ rc = cxl_cmd_alloc_send(cmd, cmd_id); ++ if (rc) { ++ errno = -rc; ++ goto fail; ++ } ++ ++ cmd->status = 1; ++ return cmd; ++ ++fail: ++ cxl_cmd_unref(cmd); ++ return NULL; ++} ++ ++CXL_EXPORT const char *cxl_cmd_get_devname(struct cxl_cmd *cmd) ++{ ++ return cxl_memdev_get_devname(cmd->memdev); ++} ++ ++CXL_EXPORT struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, ++ int opcode) ++{ ++ struct cxl_cmd *cmd; ++ ++ /* opcode '0' is reserved */ ++ if (opcode <= 0) { ++ errno = EINVAL; ++ return NULL; ++ } ++ ++ cmd = cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_RAW); ++ if (!cmd) ++ return NULL; ++ ++ cmd->send_cmd->raw.opcode = opcode; ++ return cmd; ++} ++ ++CXL_EXPORT int cxl_cmd_submit(struct cxl_cmd *cmd) ++{ ++ struct cxl_memdev *memdev = cmd->memdev; ++ const char *devname = cxl_memdev_get_devname(memdev); ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ int rc; ++ ++ switch (cmd->query_status) { ++ case CXL_CMD_QUERY_OK: ++ break; ++ case CXL_CMD_QUERY_UNSUPPORTED: ++ return -EOPNOTSUPP; ++ case CXL_CMD_QUERY_NOT_RUN: ++ return -EINVAL; ++ default: ++ err(ctx, "%s: Unknown query_status %d\n", ++ devname, cmd->query_status); ++ return -EINVAL; ++ } ++ ++ dbg(ctx, "%s: submitting SEND cmd: in: %d, out: %d\n", devname, ++ cmd->send_cmd->in.size, cmd->send_cmd->out.size); ++ rc = do_cmd(cmd, CXL_MEM_SEND_COMMAND); ++ cmd->status = cmd->send_cmd->retval; ++ dbg(ctx, "%s: got SEND cmd: in: %d, out: %d, retval: %d, status: %d\n", ++ devname, cmd->send_cmd->in.size, cmd->send_cmd->out.size, ++ rc, cmd->status); ++ ++ return rc; ++} ++ ++CXL_EXPORT int cxl_cmd_get_mbox_status(struct cxl_cmd *cmd) ++{ ++ return cmd->status; ++} ++ ++CXL_EXPORT int cxl_cmd_get_out_size(struct cxl_cmd *cmd) ++{ ++ return cmd->send_cmd->out.size; ++} +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 2616e5c..3900f90 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -20,6 +20,15 @@ global: + cxl_memdev_get_pmem_size; + cxl_memdev_get_ram_size; + cxl_memdev_get_firmware_verison; ++ cxl_cmd_get_devname; ++ cxl_cmd_new_raw; ++ cxl_cmd_set_input_payload; ++ cxl_cmd_set_output_payload; ++ cxl_cmd_ref; ++ cxl_cmd_unref; ++ cxl_cmd_submit; ++ cxl_cmd_get_mbox_status; ++ cxl_cmd_get_out_size; + local: + *; + }; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index fc88fa1..87ca17e 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -4,6 +4,9 @@ + #define _LIBCXL_PRIVATE_H_ + + #include ++#include ++#include ++#include + + #define CXL_EXPORT __attribute__ ((visibility("default"))) + +@@ -21,6 +24,36 @@ struct cxl_memdev { + struct kmod_module *module; + }; + ++enum cxl_cmd_query_status { ++ CXL_CMD_QUERY_NOT_RUN = 0, ++ CXL_CMD_QUERY_OK, ++ CXL_CMD_QUERY_UNSUPPORTED, ++}; ++ ++/** ++ * struct cxl_cmd - CXL memdev command ++ * @memdev: the memory device to which the command is being sent ++ * @query_cmd: structure for the Linux 'Query commands' ioctl ++ * @send_cmd: structure for the Linux 'Send command' ioctl ++ * @input_payload: buffer for input payload managed by libcxl ++ * @output_payload: buffer for output payload managed by libcxl ++ * @refcount: reference for passing command buffer around ++ * @query_status: status from query_commands ++ * @query_idx: index of 'this' command in the query_commands array ++ * @status: command return status from the device ++ */ ++struct cxl_cmd { ++ struct cxl_memdev *memdev; ++ struct cxl_mem_query_commands *query_cmd; ++ struct cxl_send_command *send_cmd; ++ void *input_payload; ++ void *output_payload; ++ int refcount; ++ int query_status; ++ int query_idx; ++ int status; ++}; ++ + static inline int check_kmod(struct kmod_ctx *kmod_ctx) + { + return kmod_ctx ? 0 : -ENXIO; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index fd06790..6e87b80 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -48,6 +48,17 @@ const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev); + memdev != NULL; \ + memdev = cxl_memdev_get_next(memdev)) + ++struct cxl_cmd; ++const char *cxl_cmd_get_devname(struct cxl_cmd *cmd); ++struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode); ++int cxl_cmd_set_input_payload(struct cxl_cmd *cmd, void *in, int size); ++int cxl_cmd_set_output_payload(struct cxl_cmd *cmd, void *out, int size); ++void cxl_cmd_ref(struct cxl_cmd *cmd); ++void cxl_cmd_unref(struct cxl_cmd *cmd); ++int cxl_cmd_submit(struct cxl_cmd *cmd); ++int cxl_cmd_get_mbox_status(struct cxl_cmd *cmd); ++int cxl_cmd_get_out_size(struct cxl_cmd *cmd); ++ + #ifdef __cplusplus + } /* extern "C" */ + #endif +-- +2.27.0 + diff --git a/SOURCES/0036-libcxl-add-support-for-the-Identify-Device-command.patch b/SOURCES/0036-libcxl-add-support-for-the-Identify-Device-command.patch new file mode 100644 index 0000000..e134c74 --- /dev/null +++ b/SOURCES/0036-libcxl-add-support-for-the-Identify-Device-command.patch @@ -0,0 +1,157 @@ +From 244862cbbfecda9b6b638eccaca526f4daba2795 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:28 -0600 +Subject: [PATCH 036/217] libcxl: add support for the 'Identify Device' command + +Add APIs to allocate and send an 'Identify Device' command, and +accessors to retrieve some of the fields from the resulting data. + +Only add a handful accessor functions; more can be added as the need +arises. The fields added are fw_revision, partition_align, and +lsa_size. + +Cc: Ben Widawsky +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 4 ++++ + cxl/lib/private.h | 19 +++++++++++++++++ + cxl/libcxl.h | 4 ++++ + 4 files changed, 79 insertions(+) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 727d599..ed21670 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -13,7 +13,10 @@ + #include + #include + #include ++#include ++#include + #include ++#include + + #include + #include +@@ -674,6 +677,55 @@ CXL_EXPORT const char *cxl_cmd_get_devname(struct cxl_cmd *cmd) + return cxl_memdev_get_devname(cmd->memdev); + } + ++CXL_EXPORT struct cxl_cmd *cxl_cmd_new_identify(struct cxl_memdev *memdev) ++{ ++ return cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_IDENTIFY); ++} ++ ++CXL_EXPORT int cxl_cmd_identify_get_fw_rev(struct cxl_cmd *cmd, char *fw_rev, ++ int fw_len) ++{ ++ struct cxl_cmd_identify *id = ++ (struct cxl_cmd_identify *)cmd->send_cmd->out.payload; ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_IDENTIFY) ++ return -EINVAL; ++ if (cmd->status < 0) ++ return cmd->status; ++ ++ if (fw_len > 0) ++ memcpy(fw_rev, id->fw_revision, ++ min(fw_len, CXL_CMD_IDENTIFY_FW_REV_LENGTH)); ++ return 0; ++} ++ ++CXL_EXPORT unsigned long long cxl_cmd_identify_get_partition_align( ++ struct cxl_cmd *cmd) ++{ ++ struct cxl_cmd_identify *id = ++ (struct cxl_cmd_identify *)cmd->send_cmd->out.payload; ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_IDENTIFY) ++ return -EINVAL; ++ if (cmd->status < 0) ++ return cmd->status; ++ ++ return le64_to_cpu(id->partition_align); ++} ++ ++CXL_EXPORT unsigned int cxl_cmd_identify_get_label_size(struct cxl_cmd *cmd) ++{ ++ struct cxl_cmd_identify *id = ++ (struct cxl_cmd_identify *)cmd->send_cmd->out.payload; ++ ++ if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_IDENTIFY) ++ return -EINVAL; ++ if (cmd->status < 0) ++ return cmd->status; ++ ++ return le32_to_cpu(id->lsa_size); ++} ++ + CXL_EXPORT struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, + int opcode) + { +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 3900f90..1dc45f4 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -29,6 +29,10 @@ global: + cxl_cmd_submit; + cxl_cmd_get_mbox_status; + cxl_cmd_get_out_size; ++ cxl_cmd_new_identify; ++ cxl_cmd_identify_get_fw_rev; ++ cxl_cmd_identify_get_partition_align; ++ cxl_cmd_identify_get_label_size; + local: + *; + }; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 87ca17e..3273f21 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -54,6 +54,25 @@ struct cxl_cmd { + int status; + }; + ++#define CXL_CMD_IDENTIFY_FW_REV_LENGTH 0x10 ++ ++struct cxl_cmd_identify { ++ char fw_revision[CXL_CMD_IDENTIFY_FW_REV_LENGTH]; ++ le64 total_capacity; ++ le64 volatile_capacity; ++ le64 persistent_capacity; ++ le64 partition_align; ++ le16 info_event_log_size; ++ le16 warning_event_log_size; ++ le16 failure_event_log_size; ++ le16 fatal_event_log_size; ++ le32 lsa_size; ++ u8 poison_list_max_mer[3]; ++ le16 inject_poison_limit; ++ u8 poison_caps; ++ u8 qos_telemetry_caps; ++} __attribute__((packed)); ++ + static inline int check_kmod(struct kmod_ctx *kmod_ctx) + { + return kmod_ctx ? 0 : -ENXIO; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 6e87b80..0f2d5e9 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -58,6 +58,10 @@ void cxl_cmd_unref(struct cxl_cmd *cmd); + int cxl_cmd_submit(struct cxl_cmd *cmd); + int cxl_cmd_get_mbox_status(struct cxl_cmd *cmd); + int cxl_cmd_get_out_size(struct cxl_cmd *cmd); ++struct cxl_cmd *cxl_cmd_new_identify(struct cxl_memdev *memdev); ++int cxl_cmd_identify_get_fw_rev(struct cxl_cmd *cmd, char *fw_rev, int fw_len); ++unsigned long long cxl_cmd_identify_get_partition_align(struct cxl_cmd *cmd); ++unsigned int cxl_cmd_identify_get_label_size(struct cxl_cmd *cmd); + + #ifdef __cplusplus + } /* extern "C" */ +-- +2.27.0 + diff --git a/SOURCES/0037-libcxl-add-GET_HEALTH_INFO-mailbox-command-and-acces.patch b/SOURCES/0037-libcxl-add-GET_HEALTH_INFO-mailbox-command-and-acces.patch new file mode 100644 index 0000000..60a3fd6 --- /dev/null +++ b/SOURCES/0037-libcxl-add-GET_HEALTH_INFO-mailbox-command-and-acces.patch @@ -0,0 +1,575 @@ +From 55ada0eab92d52826c9be0186db664ba9eeba749 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:29 -0600 +Subject: [PATCH 037/217] libcxl: add GET_HEALTH_INFO mailbox command and + accessors + +Add libcxl APIs to create a new GET_HEALTH_INFO mailbox command, the +command output data structure (privately), and accessor APIs to return +the different fields in the health info output. + +Cc: Ben Widawsky +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 291 +++++++++++++++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 29 +++++ + cxl/lib/private.h | 47 ++++++++ + cxl/libcxl.h | 33 +++++ + util/bitmap.h | 85 +++++++++++++ + 5 files changed, 485 insertions(+) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index ed21670..065824d 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -677,6 +677,297 @@ CXL_EXPORT const char *cxl_cmd_get_devname(struct cxl_cmd *cmd) + return cxl_memdev_get_devname(cmd->memdev); + } + ++static int cxl_cmd_validate_status(struct cxl_cmd *cmd, u32 id) ++{ ++ if (cmd->send_cmd->id != id) ++ return -EINVAL; ++ if (cmd->status < 0) ++ return cmd->status; ++ return 0; ++} ++ ++/* Helpers for health_info fields (no endian conversion) */ ++#define cmd_get_field_u8(cmd, n, N, field) \ ++do { \ ++ struct cxl_cmd_##n *c = \ ++ (struct cxl_cmd_##n *)cmd->send_cmd->out.payload; \ ++ int rc = cxl_cmd_validate_status(cmd, CXL_MEM_COMMAND_ID_##N); \ ++ if (rc) \ ++ return rc; \ ++ return c->field; \ ++} while(0) ++ ++#define cmd_get_field_u16(cmd, n, N, field) \ ++do { \ ++ struct cxl_cmd_##n *c = \ ++ (struct cxl_cmd_##n *)cmd->send_cmd->out.payload; \ ++ int rc = cxl_cmd_validate_status(cmd, CXL_MEM_COMMAND_ID_##N); \ ++ if (rc) \ ++ return rc; \ ++ return le16_to_cpu(c->field); \ ++} while(0) ++ ++ ++#define cmd_get_field_u32(cmd, n, N, field) \ ++do { \ ++ struct cxl_cmd_##n *c = \ ++ (struct cxl_cmd_##n *)cmd->send_cmd->out.payload; \ ++ int rc = cxl_cmd_validate_status(cmd, CXL_MEM_COMMAND_ID_##N); \ ++ if (rc) \ ++ return rc; \ ++ return le32_to_cpu(c->field); \ ++} while(0) ++ ++ ++#define cmd_get_field_u8_mask(cmd, n, N, field, mask) \ ++do { \ ++ struct cxl_cmd_##n *c = \ ++ (struct cxl_cmd_##n *)cmd->send_cmd->out.payload; \ ++ int rc = cxl_cmd_validate_status(cmd, CXL_MEM_COMMAND_ID_##N); \ ++ if (rc) \ ++ return rc; \ ++ return !!(c->field & mask); \ ++} while(0) ++ ++CXL_EXPORT struct cxl_cmd *cxl_cmd_new_get_health_info( ++ struct cxl_memdev *memdev) ++{ ++ return cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_GET_HEALTH_INFO); ++} ++ ++#define cmd_health_get_status_field(c, m) \ ++ cmd_get_field_u8_mask(c, get_health_info, GET_HEALTH_INFO, health_status, m) ++ ++CXL_EXPORT int cxl_cmd_health_info_get_maintenance_needed(struct cxl_cmd *cmd) ++{ ++ cmd_health_get_status_field(cmd, ++ CXL_CMD_HEALTH_INFO_STATUS_MAINTENANCE_NEEDED_MASK); ++} ++ ++CXL_EXPORT int cxl_cmd_health_info_get_performance_degraded(struct cxl_cmd *cmd) ++{ ++ cmd_health_get_status_field(cmd, ++ CXL_CMD_HEALTH_INFO_STATUS_PERFORMANCE_DEGRADED_MASK); ++} ++ ++CXL_EXPORT int cxl_cmd_health_info_get_hw_replacement_needed(struct cxl_cmd *cmd) ++{ ++ cmd_health_get_status_field(cmd, ++ CXL_CMD_HEALTH_INFO_STATUS_HW_REPLACEMENT_NEEDED_MASK); ++} ++ ++#define cmd_health_check_media_field(cmd, f) \ ++do { \ ++ struct cxl_cmd_get_health_info *c = \ ++ (struct cxl_cmd_get_health_info *)cmd->send_cmd->out.payload; \ ++ int rc = cxl_cmd_validate_status(cmd, \ ++ CXL_MEM_COMMAND_ID_GET_HEALTH_INFO); \ ++ if (rc) \ ++ return rc; \ ++ return (c->media_status == f); \ ++} while(0) ++ ++CXL_EXPORT int cxl_cmd_health_info_get_media_normal(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_media_field(cmd, ++ CXL_CMD_HEALTH_INFO_MEDIA_STATUS_NORMAL); ++} ++ ++CXL_EXPORT int cxl_cmd_health_info_get_media_not_ready(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_media_field(cmd, ++ CXL_CMD_HEALTH_INFO_MEDIA_STATUS_NOT_READY); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_media_persistence_lost(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_media_field(cmd, ++ CXL_CMD_HEALTH_INFO_MEDIA_STATUS_PERSISTENCE_LOST); ++} ++ ++CXL_EXPORT int cxl_cmd_health_info_get_media_data_lost(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_media_field(cmd, ++ CXL_CMD_HEALTH_INFO_MEDIA_STATUS_DATA_LOST); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_media_powerloss_persistence_loss(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_media_field(cmd, ++ CXL_CMD_HEALTH_INFO_MEDIA_STATUS_POWERLOSS_PERSISTENCE_LOSS); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_media_shutdown_persistence_loss(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_media_field(cmd, ++ CXL_CMD_HEALTH_INFO_MEDIA_STATUS_SHUTDOWN_PERSISTENCE_LOSS); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_media_persistence_loss_imminent(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_media_field(cmd, ++ CXL_CMD_HEALTH_INFO_MEDIA_STATUS_PERSISTENCE_LOSS_IMMINENT); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_media_powerloss_data_loss(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_media_field(cmd, ++ CXL_CMD_HEALTH_INFO_MEDIA_STATUS_POWERLOSS_DATA_LOSS); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_media_shutdown_data_loss(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_media_field(cmd, ++ CXL_CMD_HEALTH_INFO_MEDIA_STATUS_SHUTDOWN_DATA_LOSS); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_media_data_loss_imminent(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_media_field(cmd, ++ CXL_CMD_HEALTH_INFO_MEDIA_STATUS_DATA_LOSS_IMMINENT); ++} ++ ++#define cmd_health_check_ext_field(cmd, fname, type) \ ++do { \ ++ struct cxl_cmd_get_health_info *c = \ ++ (struct cxl_cmd_get_health_info *)cmd->send_cmd->out.payload; \ ++ int rc = cxl_cmd_validate_status(cmd, \ ++ CXL_MEM_COMMAND_ID_GET_HEALTH_INFO); \ ++ if (rc) \ ++ return rc; \ ++ return (FIELD_GET(fname##_MASK, c->ext_status) == \ ++ fname##_##type); \ ++} while(0) ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_ext_life_used_normal(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_ext_field(cmd, ++ CXL_CMD_HEALTH_INFO_EXT_LIFE_USED, NORMAL); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_ext_life_used_warning(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_ext_field(cmd, ++ CXL_CMD_HEALTH_INFO_EXT_LIFE_USED, WARNING); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_ext_life_used_critical(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_ext_field(cmd, ++ CXL_CMD_HEALTH_INFO_EXT_LIFE_USED, CRITICAL); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_ext_temperature_normal(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_ext_field(cmd, ++ CXL_CMD_HEALTH_INFO_EXT_TEMPERATURE, NORMAL); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_ext_temperature_warning(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_ext_field(cmd, ++ CXL_CMD_HEALTH_INFO_EXT_TEMPERATURE, WARNING); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_ext_temperature_critical(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_ext_field(cmd, ++ CXL_CMD_HEALTH_INFO_EXT_TEMPERATURE, CRITICAL); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_ext_corrected_volatile_normal(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_ext_field(cmd, ++ CXL_CMD_HEALTH_INFO_EXT_CORRECTED_VOLATILE, NORMAL); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_ext_corrected_volatile_warning(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_ext_field(cmd, ++ CXL_CMD_HEALTH_INFO_EXT_CORRECTED_VOLATILE, WARNING); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_ext_corrected_persistent_normal(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_ext_field(cmd, ++ CXL_CMD_HEALTH_INFO_EXT_CORRECTED_PERSISTENT, NORMAL); ++} ++ ++CXL_EXPORT int ++cxl_cmd_health_info_get_ext_corrected_persistent_warning(struct cxl_cmd *cmd) ++{ ++ cmd_health_check_ext_field(cmd, ++ CXL_CMD_HEALTH_INFO_EXT_CORRECTED_PERSISTENT, WARNING); ++} ++ ++static int health_info_get_life_used_raw(struct cxl_cmd *cmd) ++{ ++ cmd_get_field_u8(cmd, get_health_info, GET_HEALTH_INFO, ++ life_used); ++} ++ ++CXL_EXPORT int cxl_cmd_health_info_get_life_used(struct cxl_cmd *cmd) ++{ ++ int rc = health_info_get_life_used_raw(cmd); ++ ++ if (rc < 0) ++ return rc; ++ if (rc == CXL_CMD_HEALTH_INFO_LIFE_USED_NOT_IMPL) ++ return -EOPNOTSUPP; ++ return rc; ++} ++ ++static int health_info_get_temperature_raw(struct cxl_cmd *cmd) ++{ ++ cmd_get_field_u16(cmd, get_health_info, GET_HEALTH_INFO, ++ temperature); ++} ++ ++CXL_EXPORT int cxl_cmd_health_info_get_temperature(struct cxl_cmd *cmd) ++{ ++ int rc = health_info_get_temperature_raw(cmd); ++ ++ if (rc < 0) ++ return rc; ++ if (rc == CXL_CMD_HEALTH_INFO_TEMPERATURE_NOT_IMPL) ++ return -EOPNOTSUPP; ++ return rc; ++} ++ ++CXL_EXPORT int cxl_cmd_health_info_get_dirty_shutdowns(struct cxl_cmd *cmd) ++{ ++ cmd_get_field_u32(cmd, get_health_info, GET_HEALTH_INFO, ++ dirty_shutdowns); ++} ++ ++CXL_EXPORT int cxl_cmd_health_info_get_volatile_errors(struct cxl_cmd *cmd) ++{ ++ cmd_get_field_u32(cmd, get_health_info, GET_HEALTH_INFO, ++ volatile_errors); ++} ++ ++CXL_EXPORT int cxl_cmd_health_info_get_pmem_errors(struct cxl_cmd *cmd) ++{ ++ cmd_get_field_u32(cmd, get_health_info, GET_HEALTH_INFO, ++ pmem_errors); ++} ++ + CXL_EXPORT struct cxl_cmd *cxl_cmd_new_identify(struct cxl_memdev *memdev) + { + return cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_IDENTIFY); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 1dc45f4..c83bc28 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -33,6 +33,35 @@ global: + cxl_cmd_identify_get_fw_rev; + cxl_cmd_identify_get_partition_align; + cxl_cmd_identify_get_label_size; ++ cxl_cmd_new_get_health_info; ++ cxl_cmd_health_info_get_maintenance_needed; ++ cxl_cmd_health_info_get_performance_degraded; ++ cxl_cmd_health_info_get_hw_replacement_needed; ++ cxl_cmd_health_info_get_media_normal; ++ cxl_cmd_health_info_get_media_not_ready; ++ cxl_cmd_health_info_get_media_persistence_lost; ++ cxl_cmd_health_info_get_media_data_lost; ++ cxl_cmd_health_info_get_media_powerloss_persistence_loss; ++ cxl_cmd_health_info_get_media_shutdown_persistence_loss; ++ cxl_cmd_health_info_get_media_persistence_loss_imminent; ++ cxl_cmd_health_info_get_media_powerloss_data_loss; ++ cxl_cmd_health_info_get_media_shutdown_data_loss; ++ cxl_cmd_health_info_get_media_data_loss_imminent; ++ cxl_cmd_health_info_get_ext_life_used_normal; ++ cxl_cmd_health_info_get_ext_life_used_warning; ++ cxl_cmd_health_info_get_ext_life_used_critical; ++ cxl_cmd_health_info_get_ext_temperature_normal; ++ cxl_cmd_health_info_get_ext_temperature_warning; ++ cxl_cmd_health_info_get_ext_temperature_critical; ++ cxl_cmd_health_info_get_ext_corrected_volatile_normal; ++ cxl_cmd_health_info_get_ext_corrected_volatile_warning; ++ cxl_cmd_health_info_get_ext_corrected_persistent_normal; ++ cxl_cmd_health_info_get_ext_corrected_persistent_warning; ++ cxl_cmd_health_info_get_life_used; ++ cxl_cmd_health_info_get_temperature; ++ cxl_cmd_health_info_get_dirty_shutdowns; ++ cxl_cmd_health_info_get_volatile_errors; ++ cxl_cmd_health_info_get_pmem_errors; + local: + *; + }; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 3273f21..885553a 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -73,6 +73,53 @@ struct cxl_cmd_identify { + u8 qos_telemetry_caps; + } __attribute__((packed)); + ++struct cxl_cmd_get_health_info { ++ u8 health_status; ++ u8 media_status; ++ u8 ext_status; ++ u8 life_used; ++ le16 temperature; ++ le32 dirty_shutdowns; ++ le32 volatile_errors; ++ le32 pmem_errors; ++} __attribute__((packed)); ++ ++/* CXL 2.0 8.2.9.5.3 Byte 0 Health Status */ ++#define CXL_CMD_HEALTH_INFO_STATUS_MAINTENANCE_NEEDED_MASK BIT(0) ++#define CXL_CMD_HEALTH_INFO_STATUS_PERFORMANCE_DEGRADED_MASK BIT(1) ++#define CXL_CMD_HEALTH_INFO_STATUS_HW_REPLACEMENT_NEEDED_MASK BIT(2) ++ ++/* CXL 2.0 8.2.9.5.3 Byte 1 Media Status */ ++#define CXL_CMD_HEALTH_INFO_MEDIA_STATUS_NORMAL 0x0 ++#define CXL_CMD_HEALTH_INFO_MEDIA_STATUS_NOT_READY 0x1 ++#define CXL_CMD_HEALTH_INFO_MEDIA_STATUS_PERSISTENCE_LOST 0x2 ++#define CXL_CMD_HEALTH_INFO_MEDIA_STATUS_DATA_LOST 0x3 ++#define CXL_CMD_HEALTH_INFO_MEDIA_STATUS_POWERLOSS_PERSISTENCE_LOSS 0x4 ++#define CXL_CMD_HEALTH_INFO_MEDIA_STATUS_SHUTDOWN_PERSISTENCE_LOSS 0x5 ++#define CXL_CMD_HEALTH_INFO_MEDIA_STATUS_PERSISTENCE_LOSS_IMMINENT 0x6 ++#define CXL_CMD_HEALTH_INFO_MEDIA_STATUS_POWERLOSS_DATA_LOSS 0x7 ++#define CXL_CMD_HEALTH_INFO_MEDIA_STATUS_SHUTDOWN_DATA_LOSS 0x8 ++#define CXL_CMD_HEALTH_INFO_MEDIA_STATUS_DATA_LOSS_IMMINENT 0x9 ++ ++/* CXL 2.0 8.2.9.5.3 Byte 2 Additional Status */ ++#define CXL_CMD_HEALTH_INFO_EXT_LIFE_USED_MASK GENMASK(1, 0) ++#define CXL_CMD_HEALTH_INFO_EXT_LIFE_USED_NORMAL (0) ++#define CXL_CMD_HEALTH_INFO_EXT_LIFE_USED_WARNING (1) ++#define CXL_CMD_HEALTH_INFO_EXT_LIFE_USED_CRITICAL (2) ++#define CXL_CMD_HEALTH_INFO_EXT_TEMPERATURE_MASK GENMASK(3, 2) ++#define CXL_CMD_HEALTH_INFO_EXT_TEMPERATURE_NORMAL (0) ++#define CXL_CMD_HEALTH_INFO_EXT_TEMPERATURE_WARNING (1) ++#define CXL_CMD_HEALTH_INFO_EXT_TEMPERATURE_CRITICAL (2) ++#define CXL_CMD_HEALTH_INFO_EXT_CORRECTED_VOLATILE_MASK BIT(4) ++#define CXL_CMD_HEALTH_INFO_EXT_CORRECTED_VOLATILE_NORMAL (0) ++#define CXL_CMD_HEALTH_INFO_EXT_CORRECTED_VOLATILE_WARNING (1) ++#define CXL_CMD_HEALTH_INFO_EXT_CORRECTED_PERSISTENT_MASK BIT(5) ++#define CXL_CMD_HEALTH_INFO_EXT_CORRECTED_PERSISTENT_NORMAL (0) ++#define CXL_CMD_HEALTH_INFO_EXT_CORRECTED_PERSISTENT_WARNING (1) ++ ++#define CXL_CMD_HEALTH_INFO_LIFE_USED_NOT_IMPL 0xff ++#define CXL_CMD_HEALTH_INFO_TEMPERATURE_NOT_IMPL 0xffff ++ + static inline int check_kmod(struct kmod_ctx *kmod_ctx) + { + return kmod_ctx ? 0 : -ENXIO; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 0f2d5e9..eae2db8 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -62,6 +62,39 @@ struct cxl_cmd *cxl_cmd_new_identify(struct cxl_memdev *memdev); + int cxl_cmd_identify_get_fw_rev(struct cxl_cmd *cmd, char *fw_rev, int fw_len); + unsigned long long cxl_cmd_identify_get_partition_align(struct cxl_cmd *cmd); + unsigned int cxl_cmd_identify_get_label_size(struct cxl_cmd *cmd); ++struct cxl_cmd *cxl_cmd_new_get_health_info(struct cxl_memdev *memdev); ++int cxl_cmd_health_info_get_maintenance_needed(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_performance_degraded(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_hw_replacement_needed(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_normal(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_not_ready(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_persistence_lost(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_data_lost(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_normal(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_not_ready(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_persistence_lost(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_data_lost(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_powerloss_persistence_loss(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_shutdown_persistence_loss(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_persistence_loss_imminent(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_powerloss_data_loss(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_shutdown_data_loss(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_media_data_loss_imminent(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_ext_life_used_normal(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_ext_life_used_warning(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_ext_life_used_critical(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_ext_temperature_normal(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_ext_temperature_warning(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_ext_temperature_critical(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_ext_corrected_volatile_normal(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_ext_corrected_volatile_warning(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_ext_corrected_persistent_normal(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_ext_corrected_persistent_warning(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_life_used(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_temperature(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_dirty_shutdowns(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_volatile_errors(struct cxl_cmd *cmd); ++int cxl_cmd_health_info_get_pmem_errors(struct cxl_cmd *cmd); + + #ifdef __cplusplus + } /* extern "C" */ +diff --git a/util/bitmap.h b/util/bitmap.h +index 490f3f0..04b3429 100644 +--- a/util/bitmap.h ++++ b/util/bitmap.h +@@ -3,10 +3,33 @@ + #ifndef _NDCTL_BITMAP_H_ + #define _NDCTL_BITMAP_H_ + ++#include + #include ++#include + #include + ++#ifndef _UL ++#define _UL(x) (_AC(x, UL)) ++#endif ++#ifndef _ULL ++#define _ULL(x) (_AC(x, ULL)) ++#endif ++ + #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) ++#define UL(x) (_UL(x)) ++#define ULL(x) (_ULL(x)) ++ ++/* GENMASK() and its dependencies copied from include/linux/{bits.h, const.h} */ ++#define __is_constexpr(x) \ ++ (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) ++#define GENMASK_INPUT_CHECK(h, l) \ ++ (BUILD_BUG_ON_ZERO(__builtin_choose_expr( \ ++ __is_constexpr((l) > (h)), (l) > (h), 0))) ++#define __GENMASK(h, l) \ ++ (((~UL(0)) - (UL(1) << (l)) + 1) & \ ++ (~UL(0) >> (BITS_PER_LONG - 1 - (h)))) ++#define GENMASK(h, l) \ ++ (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l)) + + #define BIT(nr) (1UL << (nr)) + #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +@@ -30,5 +53,67 @@ unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, + unsigned long offset); + int bitmap_full(const unsigned long *src, unsigned int nbits); + ++/* ++ * Bitfield access macros ++ * (Copied from Linux's include/linux/bitfield.h) ++ * ++ * FIELD_{GET,PREP} macros take as first parameter shifted mask ++ * from which they extract the base mask and shift amount. ++ * Mask must be a compilation time constant. ++ * ++ * Example: ++ * ++ * #define REG_FIELD_A GENMASK(6, 0) ++ * #define REG_FIELD_B BIT(7) ++ * #define REG_FIELD_C GENMASK(15, 8) ++ * #define REG_FIELD_D GENMASK(31, 16) ++ * ++ * Get: ++ * a = FIELD_GET(REG_FIELD_A, reg); ++ * b = FIELD_GET(REG_FIELD_B, reg); ++ * ++ * Set: ++ * reg = FIELD_PREP(REG_FIELD_A, 1) | ++ * FIELD_PREP(REG_FIELD_B, 0) | ++ * FIELD_PREP(REG_FIELD_C, c) | ++ * FIELD_PREP(REG_FIELD_D, 0x40); ++ * ++ * Modify: ++ * reg &= ~REG_FIELD_C; ++ * reg |= FIELD_PREP(REG_FIELD_C, c); ++ */ ++ ++/* Force a compilation error if a constant expression is not a power of 2 */ ++#define __BUILD_BUG_ON_NOT_POWER_OF_2(n) \ ++ BUILD_BUG_ON(((n) & ((n) - 1)) != 0) ++#define BUILD_BUG_ON_NOT_POWER_OF_2(n) \ ++ BUILD_BUG_ON((n) == 0 || (((n) & ((n) - 1)) != 0)) ++ ++#define __bf_shf(x) (__builtin_ffsll(x) - 1) ++ ++#define __BF_FIELD_CHECK(_mask, _reg, _val) \ ++ ({ \ ++ BUILD_BUG_ON(!__builtin_constant_p(_mask)); \ ++ BUILD_BUG_ON((_mask) == 0); \ ++ BUILD_BUG_ON(__builtin_constant_p(_val) ? \ ++ ~((_mask) >> __bf_shf(_mask)) & (_val) : 0); \ ++ BUILD_BUG_ON((_mask) > (typeof(_reg))~0ull); \ ++ __BUILD_BUG_ON_NOT_POWER_OF_2((_mask) + \ ++ (1ULL << __bf_shf(_mask))); \ ++ }) ++ ++/** ++ * FIELD_GET() - extract a bitfield element ++ * @_mask: shifted mask defining the field's length and position ++ * @_reg: value of entire bitfield ++ * ++ * FIELD_GET() extracts the field specified by @_mask from the ++ * bitfield passed in as @_reg by masking and shifting it down. ++ */ ++#define FIELD_GET(_mask, _reg) \ ++ ({ \ ++ __BF_FIELD_CHECK(_mask, _reg, 0U); \ ++ (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ ++ }) + + #endif /* _NDCTL_BITMAP_H_ */ +-- +2.27.0 + diff --git a/SOURCES/0038-libcxl-add-support-for-the-GET_LSA-command.patch b/SOURCES/0038-libcxl-add-support-for-the-GET_LSA-command.patch new file mode 100644 index 0000000..d9db687 --- /dev/null +++ b/SOURCES/0038-libcxl-add-support-for-the-GET_LSA-command.patch @@ -0,0 +1,113 @@ +From c7ae078f1050ed54e254377404af2ae0879f2a39 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:30 -0600 +Subject: [PATCH 038/217] libcxl: add support for the 'GET_LSA' command + +Add a command allocator and accessor APIs for the 'GET_LSA' mailbox +command. + +Cc: Ben Widawsky +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 36 ++++++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 2 ++ + cxl/lib/private.h | 5 +++++ + cxl/libcxl.h | 4 ++++ + 4 files changed, 47 insertions(+) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 065824d..76913a2 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -1036,6 +1036,42 @@ CXL_EXPORT struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, + return cmd; + } + ++CXL_EXPORT struct cxl_cmd *cxl_cmd_new_read_label(struct cxl_memdev *memdev, ++ unsigned int offset, unsigned int length) ++{ ++ struct cxl_cmd_get_lsa_in *get_lsa; ++ struct cxl_cmd *cmd; ++ ++ cmd = cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_GET_LSA); ++ if (!cmd) ++ return NULL; ++ ++ get_lsa = (struct cxl_cmd_get_lsa_in *)cmd->send_cmd->in.payload; ++ get_lsa->offset = cpu_to_le32(offset); ++ get_lsa->length = cpu_to_le32(length); ++ return cmd; ++} ++ ++CXL_EXPORT ssize_t cxl_cmd_read_label_get_payload(struct cxl_cmd *cmd, ++ void *buf, unsigned int length) ++{ ++ struct cxl_cmd_get_lsa_in *get_lsa; ++ void *payload; ++ int rc; ++ ++ rc = cxl_cmd_validate_status(cmd, CXL_MEM_COMMAND_ID_GET_LSA); ++ if (rc) ++ return rc; ++ ++ get_lsa = (struct cxl_cmd_get_lsa_in *)cmd->send_cmd->in.payload; ++ if (length > le32_to_cpu(get_lsa->length)) ++ return -EINVAL; ++ ++ payload = (void *)cmd->send_cmd->out.payload; ++ memcpy(buf, payload, length); ++ return length; ++} ++ + CXL_EXPORT int cxl_cmd_submit(struct cxl_cmd *cmd) + { + struct cxl_memdev *memdev = cmd->memdev; +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index c83bc28..629322c 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -62,6 +62,8 @@ global: + cxl_cmd_health_info_get_dirty_shutdowns; + cxl_cmd_health_info_get_volatile_errors; + cxl_cmd_health_info_get_pmem_errors; ++ cxl_cmd_new_read_label; ++ cxl_cmd_read_label_get_payload; + local: + *; + }; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 885553a..bf3a897 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -73,6 +73,11 @@ struct cxl_cmd_identify { + u8 qos_telemetry_caps; + } __attribute__((packed)); + ++struct cxl_cmd_get_lsa_in { ++ le32 offset; ++ le32 length; ++} __attribute__((packed)); ++ + struct cxl_cmd_get_health_info { + u8 health_status; + u8 media_status; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index eae2db8..7408745 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -95,6 +95,10 @@ int cxl_cmd_health_info_get_temperature(struct cxl_cmd *cmd); + int cxl_cmd_health_info_get_dirty_shutdowns(struct cxl_cmd *cmd); + int cxl_cmd_health_info_get_volatile_errors(struct cxl_cmd *cmd); + int cxl_cmd_health_info_get_pmem_errors(struct cxl_cmd *cmd); ++struct cxl_cmd *cxl_cmd_new_read_label(struct cxl_memdev *memdev, ++ unsigned int offset, unsigned int length); ++ssize_t cxl_cmd_read_label_get_payload(struct cxl_cmd *cmd, void *buf, ++ unsigned int length); + + #ifdef __cplusplus + } /* extern "C" */ +-- +2.27.0 + diff --git a/SOURCES/0039-libcxl-add-label_size-to-cxl_memdev-and-an-API-to-re.patch b/SOURCES/0039-libcxl-add-label_size-to-cxl_memdev-and-an-API-to-re.patch new file mode 100644 index 0000000..7a0eb25 --- /dev/null +++ b/SOURCES/0039-libcxl-add-label_size-to-cxl_memdev-and-an-API-to-re.patch @@ -0,0 +1,89 @@ +From 101966ed3e4a73a6e0e1c269306e976040e068a9 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:32 -0600 +Subject: [PATCH 039/217] libcxl: add label_size to cxl_memdev, and an API to + retrieve it + +Size of the Label Storage Area (LSA) is available as a sysfs attribute +called 'label_storage_size'. Add that to libcxl's memdev so that it is available +for label related commands. + +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 12 ++++++++++++ + cxl/lib/libcxl.sym | 1 + + cxl/lib/private.h | 1 + + cxl/libcxl.h | 1 + + 4 files changed, 15 insertions(+) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 76913a2..def3a97 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -247,6 +247,13 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + if (memdev->payload_max < 0) + goto err_read; + ++ sprintf(path, "%s/label_storage_size", cxlmem_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ goto err_read; ++ memdev->lsa_size = strtoull(buf, NULL, 0); ++ if (memdev->lsa_size == ULLONG_MAX) ++ goto err_read; ++ + memdev->dev_path = strdup(cxlmem_base); + if (!memdev->dev_path) + goto err_read; +@@ -350,6 +357,11 @@ CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev + return memdev->firmware_version; + } + ++CXL_EXPORT size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev) ++{ ++ return memdev->lsa_size; ++} ++ + CXL_EXPORT void cxl_cmd_unref(struct cxl_cmd *cmd) + { + if (!cmd) +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 629322c..858e953 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -64,6 +64,7 @@ global: + cxl_cmd_health_info_get_pmem_errors; + cxl_cmd_new_read_label; + cxl_cmd_read_label_get_payload; ++ cxl_memdev_get_label_size; + local: + *; + }; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index bf3a897..c4ed741 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -21,6 +21,7 @@ struct cxl_memdev { + unsigned long long pmem_size; + unsigned long long ram_size; + int payload_max; ++ size_t lsa_size; + struct kmod_module *module; + }; + +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 7408745..d3b97a1 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -42,6 +42,7 @@ struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev); + unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev); + unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev); + const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev); ++size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev); + + #define cxl_memdev_foreach(ctx, memdev) \ + for (memdev = cxl_memdev_get_first(ctx); \ +-- +2.27.0 + diff --git a/SOURCES/0040-libcxl-add-representation-for-an-nvdimm-bridge-objec.patch b/SOURCES/0040-libcxl-add-representation-for-an-nvdimm-bridge-objec.patch new file mode 100644 index 0000000..533f398 --- /dev/null +++ b/SOURCES/0040-libcxl-add-representation-for-an-nvdimm-bridge-objec.patch @@ -0,0 +1,184 @@ +From cd1aed6cefe8f4f2043349e1a614876f67743439 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:33 -0600 +Subject: [PATCH 040/217] libcxl: add representation for an nvdimm bridge + object + +Add an nvdimm bridge object representation internal to libcxl. A bridge +object is tied to its parent memdev object, and this patch adds its +first interface, which checks whether a bridge is 'active' - i.e. +implying the label space on the memdev is owned by the kernel. + +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 1 + + cxl/lib/private.h | 8 +++++ + cxl/libcxl.h | 1 + + 4 files changed, 83 insertions(+) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index def3a97..60ed646 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -45,11 +45,19 @@ struct cxl_ctx { + void *private_data; + }; + ++static void free_bridge(struct cxl_nvdimm_bridge *bridge) ++{ ++ free(bridge->dev_buf); ++ free(bridge->dev_path); ++ free(bridge); ++} ++ + static void free_memdev(struct cxl_memdev *memdev, struct list_head *head) + { + if (head) + list_del_from(head, &memdev->list); + kmod_module_unref(memdev->module); ++ free_bridge(memdev->bridge); + free(memdev->firmware_version); + free(memdev->dev_buf); + free(memdev->dev_path); +@@ -205,6 +213,40 @@ CXL_EXPORT void cxl_set_log_priority(struct cxl_ctx *ctx, int priority) + ctx->ctx.log_priority = priority; + } + ++static void *add_cxl_bridge(void *parent, int id, const char *br_base) ++{ ++ const char *devname = devpath_to_devname(br_base); ++ struct cxl_memdev *memdev = parent; ++ struct cxl_ctx *ctx = memdev->ctx; ++ struct cxl_nvdimm_bridge *bridge; ++ ++ dbg(ctx, "%s: bridge_base: \'%s\'\n", devname, br_base); ++ ++ bridge = calloc(1, sizeof(*bridge)); ++ if (!bridge) ++ goto err_dev; ++ bridge->id = id; ++ ++ bridge->dev_path = strdup(br_base); ++ if (!bridge->dev_path) ++ goto err_read; ++ ++ bridge->dev_buf = calloc(1, strlen(br_base) + 50); ++ if (!bridge->dev_buf) ++ goto err_read; ++ bridge->buf_len = strlen(br_base) + 50; ++ ++ memdev->bridge = bridge; ++ return bridge; ++ ++ err_read: ++ free(bridge->dev_buf); ++ free(bridge->dev_path); ++ free(bridge); ++ err_dev: ++ return NULL; ++} ++ + static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + { + const char *devname = devpath_to_devname(cxlmem_base); +@@ -271,6 +313,8 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + goto err_read; + memdev->buf_len = strlen(cxlmem_base) + 50; + ++ sysfs_device_parse(ctx, cxlmem_base, "pmem", memdev, add_cxl_bridge); ++ + cxl_memdev_foreach(ctx, memdev_dup) + if (memdev_dup->id == memdev->id) { + free_memdev(memdev, NULL); +@@ -362,6 +406,35 @@ CXL_EXPORT size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev) + return memdev->lsa_size; + } + ++static int is_enabled(const char *drvpath) ++{ ++ struct stat st; ++ ++ if (lstat(drvpath, &st) < 0 || !S_ISLNK(st.st_mode)) ++ return 0; ++ else ++ return 1; ++} ++ ++CXL_EXPORT int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev) ++{ ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ struct cxl_nvdimm_bridge *bridge = memdev->bridge; ++ char *path = bridge->dev_buf; ++ int len = bridge->buf_len; ++ ++ if (!bridge) ++ return 0; ++ ++ if (snprintf(path, len, "%s/driver", bridge->dev_path) >= len) { ++ err(ctx, "%s: nvdimm bridge buffer too small!\n", ++ cxl_memdev_get_devname(memdev)); ++ return 0; ++ } ++ ++ return is_enabled(path); ++} ++ + CXL_EXPORT void cxl_cmd_unref(struct cxl_cmd *cmd) + { + if (!cmd) +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 858e953..f3b0c63 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -65,6 +65,7 @@ global: + cxl_cmd_new_read_label; + cxl_cmd_read_label_get_payload; + cxl_memdev_get_label_size; ++ cxl_memdev_nvdimm_bridge_active; + local: + *; + }; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index c4ed741..525c41e 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -10,6 +10,13 @@ + + #define CXL_EXPORT __attribute__ ((visibility("default"))) + ++struct cxl_nvdimm_bridge { ++ int id; ++ void *dev_buf; ++ size_t buf_len; ++ char *dev_path; ++}; ++ + struct cxl_memdev { + int id, major, minor; + void *dev_buf; +@@ -23,6 +30,7 @@ struct cxl_memdev { + int payload_max; + size_t lsa_size; + struct kmod_module *module; ++ struct cxl_nvdimm_bridge *bridge; + }; + + enum cxl_cmd_query_status { +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index d3b97a1..535e349 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -43,6 +43,7 @@ unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev); + unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev); + const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev); + size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev); ++int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev); + + #define cxl_memdev_foreach(ctx, memdev) \ + for (memdev = cxl_memdev_get_first(ctx); \ +-- +2.27.0 + diff --git a/SOURCES/0041-libcxl-add-interfaces-for-label-operations.patch b/SOURCES/0041-libcxl-add-interfaces-for-label-operations.patch new file mode 100644 index 0000000..ff7e247 --- /dev/null +++ b/SOURCES/0041-libcxl-add-interfaces-for-label-operations.patch @@ -0,0 +1,247 @@ +From 6255d23452809ddc6d48083c35fc935e4fa420d8 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:34 -0600 +Subject: [PATCH 041/217] libcxl: add interfaces for label operations + +Add libcxl interfaces to allow performinfg label (LSA) manipulations. +Add a 'cxl_cmd_new_set_lsa' interface to create a 'Set LSA' mailbox +command payload, and interfaces to read, write, and zero the LSA area on +a memdev. + +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 158 +++++++++++++++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 4 ++ + cxl/lib/private.h | 6 ++ + cxl/libcxl.h | 8 +++ + 4 files changed, 176 insertions(+) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 60ed646..f0664be 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -1197,3 +1197,161 @@ CXL_EXPORT int cxl_cmd_get_out_size(struct cxl_cmd *cmd) + { + return cmd->send_cmd->out.size; + } ++ ++CXL_EXPORT struct cxl_cmd *cxl_cmd_new_write_label(struct cxl_memdev *memdev, ++ void *lsa_buf, unsigned int offset, unsigned int length) ++{ ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ struct cxl_cmd_set_lsa *set_lsa; ++ struct cxl_cmd *cmd; ++ int rc; ++ ++ cmd = cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_SET_LSA); ++ if (!cmd) ++ return NULL; ++ ++ /* this will allocate 'in.payload' */ ++ rc = cxl_cmd_set_input_payload(cmd, NULL, sizeof(*set_lsa) + length); ++ if (rc) { ++ err(ctx, "%s: cmd setup failed: %s\n", ++ cxl_memdev_get_devname(memdev), strerror(-rc)); ++ goto out_fail; ++ } ++ set_lsa = (struct cxl_cmd_set_lsa *)cmd->send_cmd->in.payload; ++ set_lsa->offset = cpu_to_le32(offset); ++ memcpy(set_lsa->lsa_data, lsa_buf, length); ++ ++ return cmd; ++ ++out_fail: ++ cxl_cmd_unref(cmd); ++ return NULL; ++} ++ ++enum lsa_op { ++ LSA_OP_GET, ++ LSA_OP_SET, ++ LSA_OP_ZERO, ++}; ++ ++static int __lsa_op(struct cxl_memdev *memdev, int op, void *buf, ++ size_t length, size_t offset) ++{ ++ const char *devname = cxl_memdev_get_devname(memdev); ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ void *zero_buf = NULL; ++ struct cxl_cmd *cmd; ++ ssize_t ret_len; ++ int rc = 0; ++ ++ switch (op) { ++ case LSA_OP_GET: ++ cmd = cxl_cmd_new_read_label(memdev, offset, length); ++ if (!cmd) ++ return -ENOMEM; ++ rc = cxl_cmd_set_output_payload(cmd, buf, length); ++ if (rc) { ++ err(ctx, "%s: cmd setup failed: %s\n", ++ cxl_memdev_get_devname(memdev), strerror(-rc)); ++ goto out; ++ } ++ break; ++ case LSA_OP_ZERO: ++ zero_buf = calloc(1, length); ++ if (!zero_buf) ++ return -ENOMEM; ++ buf = zero_buf; ++ /* fall through */ ++ case LSA_OP_SET: ++ cmd = cxl_cmd_new_write_label(memdev, buf, offset, length); ++ if (!cmd) { ++ rc = -ENOMEM; ++ goto out_free; ++ } ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ err(ctx, "%s: cmd submission failed: %s\n", ++ devname, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ err(ctx, "%s: firmware status: %d\n", ++ devname, rc); ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ if (op == LSA_OP_GET) { ++ ret_len = cxl_cmd_read_label_get_payload(cmd, buf, length); ++ if (ret_len < 0) { ++ rc = ret_len; ++ goto out; ++ } ++ } ++ ++out: ++ cxl_cmd_unref(cmd); ++out_free: ++ free(zero_buf); ++ return rc; ++ ++} ++ ++static int lsa_op(struct cxl_memdev *memdev, int op, void *buf, ++ size_t length, size_t offset) ++{ ++ const char *devname = cxl_memdev_get_devname(memdev); ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ size_t remaining = length, cur_len, cur_off = 0; ++ int label_iter_max, rc = 0; ++ ++ if (op != LSA_OP_ZERO && buf == NULL) { ++ err(ctx, "%s: LSA buffer cannot be NULL\n", devname); ++ return -EINVAL; ++ } ++ ++ if (length == 0) ++ return 0; ++ ++ label_iter_max = memdev->payload_max - sizeof(struct cxl_cmd_set_lsa); ++ while (remaining) { ++ cur_len = min((size_t)label_iter_max, remaining); ++ rc = __lsa_op(memdev, op, buf + cur_off, ++ cur_len, offset + cur_off); ++ if (rc) ++ break; ++ ++ remaining -= cur_len; ++ cur_off += cur_len; ++ } ++ ++ if (rc && (op == LSA_OP_SET)) ++ err(ctx, "%s: labels may be in an inconsistent state\n", ++ devname); ++ return rc; ++} ++ ++CXL_EXPORT int cxl_memdev_zero_label(struct cxl_memdev *memdev, size_t length, ++ size_t offset) ++{ ++ return lsa_op(memdev, LSA_OP_ZERO, NULL, length, offset); ++} ++ ++CXL_EXPORT int cxl_memdev_write_label(struct cxl_memdev *memdev, void *buf, ++ size_t length, size_t offset) ++{ ++ return lsa_op(memdev, LSA_OP_SET, buf, length, offset); ++} ++ ++CXL_EXPORT int cxl_memdev_read_label(struct cxl_memdev *memdev, void *buf, ++ size_t length, size_t offset) ++{ ++ return lsa_op(memdev, LSA_OP_GET, buf, length, offset); ++} +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index f3b0c63..077d104 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -66,6 +66,10 @@ global: + cxl_cmd_read_label_get_payload; + cxl_memdev_get_label_size; + cxl_memdev_nvdimm_bridge_active; ++ cxl_cmd_new_write_label; ++ cxl_memdev_zero_label; ++ cxl_memdev_write_label; ++ cxl_memdev_read_label; + local: + *; + }; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 525c41e..a1b8b50 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -87,6 +87,12 @@ struct cxl_cmd_get_lsa_in { + le32 length; + } __attribute__((packed)); + ++struct cxl_cmd_set_lsa { ++ le32 offset; ++ le32 rsvd; ++ unsigned char lsa_data[0]; ++} __attribute__ ((packed)); ++ + struct cxl_cmd_get_health_info { + u8 health_status; + u8 media_status; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 535e349..89d35ba 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -44,6 +44,12 @@ unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev); + const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev); + size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev); + int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev); ++int cxl_memdev_zero_label(struct cxl_memdev *memdev, size_t length, ++ size_t offset); ++int cxl_memdev_read_label(struct cxl_memdev *memdev, void *buf, size_t length, ++ size_t offset); ++int cxl_memdev_write_label(struct cxl_memdev *memdev, void *buf, size_t length, ++ size_t offset); + + #define cxl_memdev_foreach(ctx, memdev) \ + for (memdev = cxl_memdev_get_first(ctx); \ +@@ -101,6 +107,8 @@ struct cxl_cmd *cxl_cmd_new_read_label(struct cxl_memdev *memdev, + unsigned int offset, unsigned int length); + ssize_t cxl_cmd_read_label_get_payload(struct cxl_cmd *cmd, void *buf, + unsigned int length); ++struct cxl_cmd *cxl_cmd_new_write_label(struct cxl_memdev *memdev, ++ void *buf, unsigned int offset, unsigned int length); + + #ifdef __cplusplus + } /* extern "C" */ +-- +2.27.0 + diff --git a/SOURCES/0042-cxl-add-commands-to-read-write-and-zero-labels.patch b/SOURCES/0042-cxl-add-commands-to-read-write-and-zero-labels.patch new file mode 100644 index 0000000..b98f75b --- /dev/null +++ b/SOURCES/0042-cxl-add-commands-to-read-write-and-zero-labels.patch @@ -0,0 +1,583 @@ +From c415cebe4b5ca50e06db78a4719f312e33106936 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:35 -0600 +Subject: [PATCH 042/217] cxl: add commands to read, write, and zero labels + +Add the following cxl-cli commands: read-labels, write-labels, +zero-labels. They operate on a CXL memdev, or a set of memdevs, and +allow interacting with the label storage area (LSA) on the device. + +Add man pages for the above cxl-cli commands. + +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/Makefile.am | 5 +- + Documentation/cxl/cxl-read-labels.txt | 33 +++ + Documentation/cxl/cxl-write-labels.txt | 32 +++ + Documentation/cxl/cxl-zero-labels.txt | 29 ++ + Documentation/cxl/labels-description.txt | 8 + + Documentation/cxl/labels-options.txt | 17 ++ + Documentation/cxl/memdev-option.txt | 4 + + cxl/Makefile.am | 1 + + cxl/builtin.h | 5 + + cxl/cxl.c | 3 + + cxl/memdev.c | 324 +++++++++++++++++++++++ + 11 files changed, 460 insertions(+), 1 deletion(-) + create mode 100644 Documentation/cxl/cxl-read-labels.txt + create mode 100644 Documentation/cxl/cxl-write-labels.txt + create mode 100644 Documentation/cxl/cxl-zero-labels.txt + create mode 100644 Documentation/cxl/labels-description.txt + create mode 100644 Documentation/cxl/labels-options.txt + create mode 100644 Documentation/cxl/memdev-option.txt + create mode 100644 cxl/memdev.c + +diff --git a/Documentation/cxl/Makefile.am b/Documentation/cxl/Makefile.am +index db98dd7..efabaa3 100644 +--- a/Documentation/cxl/Makefile.am ++++ b/Documentation/cxl/Makefile.am +@@ -19,7 +19,10 @@ endif + + man1_MANS = \ + cxl.1 \ +- cxl-list.1 ++ cxl-list.1 \ ++ cxl-read-labels.1 \ ++ cxl-write-labels.1 \ ++ cxl-zero-labels.1 + + EXTRA_DIST = $(man1_MANS) + +diff --git a/Documentation/cxl/cxl-read-labels.txt b/Documentation/cxl/cxl-read-labels.txt +new file mode 100644 +index 0000000..143f296 +--- /dev/null ++++ b/Documentation/cxl/cxl-read-labels.txt +@@ -0,0 +1,33 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-read-labels(1) ++================== ++ ++NAME ++---- ++cxl-read-labels - read out the label area on a CXL memdev ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl read-labels' [..] [] ++ ++include::labels-description.txt[] ++This command dumps the raw binary data in a memdev's label area to stdout or a ++file. In the multi-memdev case the data is concatenated. ++ ++OPTIONS ++------- ++include::labels-options.txt[] ++ ++-o:: ++--output:: ++ output file ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-write-labels[1], ++linkcxl:cxl-zero-labels[1], ++CXL-2.0 9.13.2 +diff --git a/Documentation/cxl/cxl-write-labels.txt b/Documentation/cxl/cxl-write-labels.txt +new file mode 100644 +index 0000000..75f42a5 +--- /dev/null ++++ b/Documentation/cxl/cxl-write-labels.txt +@@ -0,0 +1,32 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-write-labels(1) ++=================== ++ ++NAME ++---- ++cxl-write-labels - write data to the label area on a memdev ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl write-labels [-i ]' ++ ++include::labels-description.txt[] ++Read data from the input filename, or stdin, and write it to the given ++ device. Note that the device must not be active in any region, or ++actively registered with the nvdimm subsystem. If it is, the kernel will ++not allow write access to the device's label data area. ++ ++OPTIONS ++------- ++include::labels-options.txt[] ++-i:: ++--input:: ++ input file ++ ++SEE ALSO ++-------- ++linkcxl:cxl-read-labels[1], ++linkcxl:cxl-zero-labels[1], ++CXL-2.0 9.13.2 +diff --git a/Documentation/cxl/cxl-zero-labels.txt b/Documentation/cxl/cxl-zero-labels.txt +new file mode 100644 +index 0000000..bf95b24 +--- /dev/null ++++ b/Documentation/cxl/cxl-zero-labels.txt +@@ -0,0 +1,29 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-zero-labels(1) ++================== ++ ++NAME ++---- ++cxl-zero-labels - zero out the label area on a set of memdevs ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl zero-labels' [..] [] ++ ++include::labels-description.txt[] ++This command resets the device to its default state by ++deleting all labels. ++ ++OPTIONS ++------- ++include::labels-options.txt[] ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-read-labels[1], ++linkcxl:cxl-write-labels[1], ++CXL-2.0 9.13.2 +diff --git a/Documentation/cxl/labels-description.txt b/Documentation/cxl/labels-description.txt +new file mode 100644 +index 0000000..f60bd5d +--- /dev/null ++++ b/Documentation/cxl/labels-description.txt +@@ -0,0 +1,8 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++DESCRIPTION ++----------- ++The region label area is a small persistent partition of capacity ++available on some CXL memory devices. The label area is used to ++and configure or determine the set of memory devices participating ++in different interleave sets. +diff --git a/Documentation/cxl/labels-options.txt b/Documentation/cxl/labels-options.txt +new file mode 100644 +index 0000000..06fbac3 +--- /dev/null ++++ b/Documentation/cxl/labels-options.txt +@@ -0,0 +1,17 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++:: ++include::memdev-option.txt[] ++ ++-s:: ++--size=:: ++ Limit the operation to the given number of bytes. A size of 0 ++ indicates to operate over the entire label capacity. ++ ++-O:: ++--offset=:: ++ Begin the operation at the given offset into the label area. ++ ++-v:: ++ Turn on verbose debug messages in the library (if libcxl was built with ++ logging and debug enabled). +diff --git a/Documentation/cxl/memdev-option.txt b/Documentation/cxl/memdev-option.txt +new file mode 100644 +index 0000000..e778582 +--- /dev/null ++++ b/Documentation/cxl/memdev-option.txt +@@ -0,0 +1,4 @@ ++// SPDX-License-Identifier: GPL-2.0 ++A 'memX' device name, or a memdev id number. Restrict the operation to ++the specified memdev(s). The keyword 'all' can be specified to indicate ++the lack of any restriction. +diff --git a/cxl/Makefile.am b/cxl/Makefile.am +index 98606b9..da9f91d 100644 +--- a/cxl/Makefile.am ++++ b/cxl/Makefile.am +@@ -10,6 +10,7 @@ config.h: $(srcdir)/Makefile.am + cxl_SOURCES =\ + cxl.c \ + list.c \ ++ memdev.c \ + ../util/json.c \ + builtin.h + +diff --git a/cxl/builtin.h b/cxl/builtin.h +index 3797f98..78eca6e 100644 +--- a/cxl/builtin.h ++++ b/cxl/builtin.h +@@ -5,4 +5,9 @@ + + struct cxl_ctx; + int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_write_labels(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_read_labels(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_zero_labels(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_init_labels(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_check_labels(int argc, const char **argv, struct cxl_ctx *ctx); + #endif /* _CXL_BUILTIN_H_ */ +diff --git a/cxl/cxl.c b/cxl/cxl.c +index a7725f8..4b1661d 100644 +--- a/cxl/cxl.c ++++ b/cxl/cxl.c +@@ -61,6 +61,9 @@ static struct cmd_struct commands[] = { + { "version", .c_fn = cmd_version }, + { "list", .c_fn = cmd_list }, + { "help", .c_fn = cmd_help }, ++ { "zero-labels", .c_fn = cmd_zero_labels }, ++ { "read-labels", .c_fn = cmd_read_labels }, ++ { "write-labels", .c_fn = cmd_write_labels }, + }; + + int main(int argc, const char **argv) +diff --git a/cxl/memdev.c b/cxl/memdev.c +new file mode 100644 +index 0000000..5ee38e5 +--- /dev/null ++++ b/cxl/memdev.c +@@ -0,0 +1,324 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2020-2021 Intel Corporation. All rights reserved. */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct action_context { ++ FILE *f_out; ++ FILE *f_in; ++}; ++ ++static struct parameters { ++ const char *outfile; ++ const char *infile; ++ unsigned len; ++ unsigned offset; ++ bool verbose; ++} param; ++ ++#define fail(fmt, ...) \ ++do { \ ++ fprintf(stderr, "cxl-%s:%s:%d: " fmt, \ ++ VERSION, __func__, __LINE__, ##__VA_ARGS__); \ ++} while (0) ++ ++#define BASE_OPTIONS() \ ++OPT_BOOLEAN('v',"verbose", ¶m.verbose, "turn on debug") ++ ++#define READ_OPTIONS() \ ++OPT_STRING('o', "output", ¶m.outfile, "output-file", \ ++ "filename to write label area contents") ++ ++#define WRITE_OPTIONS() \ ++OPT_STRING('i', "input", ¶m.infile, "input-file", \ ++ "filename to read label area data") ++ ++#define LABEL_OPTIONS() \ ++OPT_UINTEGER('s', "size", ¶m.len, "number of label bytes to operate"), \ ++OPT_UINTEGER('O', "offset", ¶m.offset, \ ++ "offset into the label area to start operation") ++ ++static const struct option read_options[] = { ++ BASE_OPTIONS(), ++ LABEL_OPTIONS(), ++ READ_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option write_options[] = { ++ BASE_OPTIONS(), ++ LABEL_OPTIONS(), ++ WRITE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option zero_options[] = { ++ BASE_OPTIONS(), ++ LABEL_OPTIONS(), ++ OPT_END(), ++}; ++ ++static int action_zero(struct cxl_memdev *memdev, struct action_context *actx) ++{ ++ size_t size; ++ int rc; ++ ++ if (param.len) ++ size = param.len; ++ else ++ size = cxl_memdev_get_label_size(memdev); ++ ++ if (cxl_memdev_nvdimm_bridge_active(memdev)) { ++ fprintf(stderr, ++ "%s: has active nvdimm bridge, abort label write\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ rc = cxl_memdev_zero_label(memdev, size, param.offset); ++ if (rc < 0) ++ fprintf(stderr, "%s: label zeroing failed: %s\n", ++ cxl_memdev_get_devname(memdev), strerror(-rc)); ++ ++ return rc; ++} ++ ++static int action_write(struct cxl_memdev *memdev, struct action_context *actx) ++{ ++ size_t size = param.len, read_len; ++ unsigned char *buf; ++ int rc; ++ ++ if (cxl_memdev_nvdimm_bridge_active(memdev)) { ++ fprintf(stderr, ++ "%s: has active nvdimm bridge, abort label write\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ if (!size) { ++ size_t label_size = cxl_memdev_get_label_size(memdev); ++ ++ fseek(actx->f_in, 0L, SEEK_END); ++ size = ftell(actx->f_in); ++ fseek(actx->f_in, 0L, SEEK_SET); ++ ++ if (size > label_size) { ++ fprintf(stderr, ++ "File size (%zu) greater than label area size (%zu), aborting\n", ++ size, label_size); ++ return -EINVAL; ++ } ++ } ++ ++ buf = calloc(1, size); ++ if (!buf) ++ return -ENOMEM; ++ ++ read_len = fread(buf, 1, size, actx->f_in); ++ if (read_len != size) { ++ rc = -ENXIO; ++ goto out; ++ } ++ ++ rc = cxl_memdev_write_label(memdev, buf, size, param.offset); ++ if (rc < 0) ++ fprintf(stderr, "%s: label write failed: %s\n", ++ cxl_memdev_get_devname(memdev), strerror(-rc)); ++ ++out: ++ free(buf); ++ return rc; ++} ++ ++static int action_read(struct cxl_memdev *memdev, struct action_context *actx) ++{ ++ size_t size, write_len; ++ char *buf; ++ int rc; ++ ++ if (param.len) ++ size = param.len; ++ else ++ size = cxl_memdev_get_label_size(memdev); ++ ++ buf = calloc(1, size); ++ if (!buf) ++ return -ENOMEM; ++ ++ rc = cxl_memdev_read_label(memdev, buf, size, param.offset); ++ if (rc < 0) { ++ fprintf(stderr, "%s: label read failed: %s\n", ++ cxl_memdev_get_devname(memdev), strerror(-rc)); ++ goto out; ++ } ++ ++ write_len = fwrite(buf, 1, size, actx->f_out); ++ if (write_len != size) { ++ rc = -ENXIO; ++ goto out; ++ } ++ fflush(actx->f_out); ++ ++out: ++ free(buf); ++ return rc; ++} ++ ++static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, ++ int (*action)(struct cxl_memdev *memdev, struct action_context *actx), ++ const struct option *options, const char *usage) ++{ ++ struct cxl_memdev *memdev, *single = NULL; ++ struct action_context actx = { 0 }; ++ int i, rc = 0, count = 0, err = 0; ++ const char * const u[] = { ++ usage, ++ NULL ++ }; ++ unsigned long id; ++ ++ argc = parse_options(argc, argv, options, u, 0); ++ ++ if (argc == 0) ++ usage_with_options(u, options); ++ for (i = 0; i < argc; i++) { ++ if (strcmp(argv[i], "all") == 0) { ++ argv[0] = "all"; ++ argc = 1; ++ break; ++ } ++ ++ if (sscanf(argv[i], "mem%lu", &id) != 1) { ++ fprintf(stderr, "'%s' is not a valid memdev name\n", ++ argv[i]); ++ err++; ++ } ++ } ++ ++ if (err == argc) { ++ usage_with_options(u, options); ++ return -EINVAL; ++ } ++ ++ if (!param.outfile) ++ actx.f_out = stdout; ++ else { ++ actx.f_out = fopen(param.outfile, "w+"); ++ if (!actx.f_out) { ++ fprintf(stderr, "failed to open: %s: (%s)\n", ++ param.outfile, strerror(errno)); ++ rc = -errno; ++ goto out; ++ } ++ } ++ ++ if (!param.infile) { ++ actx.f_in = stdin; ++ } else { ++ actx.f_in = fopen(param.infile, "r"); ++ if (!actx.f_in) { ++ fprintf(stderr, "failed to open: %s: (%s)\n", ++ param.infile, strerror(errno)); ++ rc = -errno; ++ goto out_close_fout; ++ } ++ } ++ ++ if (param.verbose) ++ cxl_set_log_priority(ctx, LOG_DEBUG); ++ ++ rc = 0; ++ err = 0; ++ count = 0; ++ ++ for (i = 0; i < argc; i++) { ++ if (sscanf(argv[i], "mem%lu", &id) != 1 ++ && strcmp(argv[i], "all") != 0) ++ continue; ++ ++ cxl_memdev_foreach (ctx, memdev) { ++ if (!util_cxl_memdev_filter(memdev, argv[i])) ++ continue; ++ ++ if (action == action_write) { ++ single = memdev; ++ rc = 0; ++ } else ++ rc = action(memdev, &actx); ++ ++ if (rc == 0) ++ count++; ++ else if (rc && !err) ++ err = rc; ++ } ++ } ++ rc = err; ++ ++ if (action == action_write) { ++ if (count > 1) { ++ error("write-labels only supports writing a single memdev\n"); ++ usage_with_options(u, options); ++ return -EINVAL; ++ } else if (single) { ++ rc = action(single, &actx); ++ if (rc) ++ count = 0; ++ } ++ } ++ ++ if (actx.f_in != stdin) ++ fclose(actx.f_in); ++ ++ out_close_fout: ++ if (actx.f_out != stdout) ++ fclose(actx.f_out); ++ ++ out: ++ /* ++ * count if some actions succeeded, 0 if none were attempted, ++ * negative error code otherwise. ++ */ ++ if (count > 0) ++ return count; ++ return rc; ++} ++ ++int cmd_write_labels(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int count = memdev_action(argc, argv, ctx, action_write, write_options, ++ "cxl write-labels [-i ]"); ++ ++ fprintf(stderr, "wrote %d mem%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); ++ return count >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_read_labels(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int count = memdev_action(argc, argv, ctx, action_read, read_options, ++ "cxl read-labels [..] [-o ]"); ++ ++ fprintf(stderr, "read %d mem%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); ++ return count >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_zero_labels(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int count = memdev_action(argc, argv, ctx, action_zero, zero_options, ++ "cxl zero-labels [..] []"); ++ ++ fprintf(stderr, "zeroed %d mem%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); ++ return count >= 0 ? 0 : EXIT_FAILURE; ++} +-- +2.27.0 + diff --git a/SOURCES/0043-Documentation-cxl-add-library-API-documentation.patch b/SOURCES/0043-Documentation-cxl-add-library-API-documentation.patch new file mode 100644 index 0000000..4645a63 --- /dev/null +++ b/SOURCES/0043-Documentation-cxl-add-library-API-documentation.patch @@ -0,0 +1,248 @@ +From 62fe17528d362110e258d56d8a2f44f2798f3f45 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:36 -0600 +Subject: [PATCH 043/217] Documentation/cxl: add library API documentation + +Add library API documentation for libcxl(3) using the existing +asciidoc(tor) build system. Add a section 3 man page for 'libcxl' that +provides an overview of the library and its usage, and a man page for +the 'cxl_new()' API. + +Cc: Ben Widawsky +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .gitignore | 3 ++ + Documentation/cxl/lib/Makefile.am | 58 +++++++++++++++++++++++++++++++ + Documentation/cxl/lib/cxl_new.txt | 43 +++++++++++++++++++++++ + Documentation/cxl/lib/libcxl.txt | 56 +++++++++++++++++++++++++++++ + Makefile.am | 1 + + configure.ac | 1 + + 6 files changed, 162 insertions(+) + create mode 100644 Documentation/cxl/lib/Makefile.am + create mode 100644 Documentation/cxl/lib/cxl_new.txt + create mode 100644 Documentation/cxl/lib/libcxl.txt + +diff --git a/.gitignore b/.gitignore +index 6a97b92..6468c7a 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -14,12 +14,15 @@ Makefile.in + /libtool + /stamp-h1 + *.1 ++*.3 + Documentation/daxctl/asciidoc.conf + Documentation/ndctl/asciidoc.conf + Documentation/cxl/asciidoc.conf ++Documentation/cxl/lib/asciidoc.conf + Documentation/daxctl/asciidoctor-extensions.rb + Documentation/ndctl/asciidoctor-extensions.rb + Documentation/cxl/asciidoctor-extensions.rb ++Documentation/cxl/lib/asciidoctor-extensions.rb + Documentation/ndctl/attrs.adoc + .dirstamp + daxctl/config.h +diff --git a/Documentation/cxl/lib/Makefile.am b/Documentation/cxl/lib/Makefile.am +new file mode 100644 +index 0000000..41e3a5f +--- /dev/null ++++ b/Documentation/cxl/lib/Makefile.am +@@ -0,0 +1,58 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (C) 2020-2021 Intel Corporation. All rights reserved. ++ ++if USE_ASCIIDOCTOR ++ ++do_subst = sed -e 's,@Utility@,Libcxl,g' -e's,@utility@,libcxl,g' ++CONFFILE = asciidoctor-extensions.rb ++asciidoctor-extensions.rb: ../../asciidoctor-extensions.rb.in ++ $(AM_V_GEN) $(do_subst) < $< > $@ ++ ++else ++ ++do_subst = sed -e 's,UTILITY,libcxl,g' ++CONFFILE = asciidoc.conf ++asciidoc.conf: ../../asciidoc.conf.in ++ $(AM_V_GEN) $(do_subst) < $< > $@ ++ ++endif ++ ++man3_MANS = \ ++ libcxl.3 \ ++ cxl_new.3 ++ ++EXTRA_DIST = $(man3_MANS) ++ ++CLEANFILES = $(man3_MANS) ++ ++XML_DEPS = \ ++ ../../../version.m4 \ ++ ../../copyright.txt \ ++ Makefile \ ++ $(CONFFILE) ++ ++RM ?= rm -f ++ ++if USE_ASCIIDOCTOR ++ ++%.3: %.txt $(XML_DEPS) ++ $(AM_V_GEN)$(RM) $@+ $@ && \ ++ $(ASCIIDOC) -b manpage -d manpage -acompat-mode \ ++ -I. -rasciidoctor-extensions \ ++ -amansource=libcxl -amanmanual="libcxl Manual" \ ++ -andctl_version=$(VERSION) -o $@+ $< && \ ++ mv $@+ $@ ++ ++else ++ ++%.xml: %.txt $(XML_DEPS) ++ $(AM_V_GEN)$(RM) $@+ $@ && \ ++ $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ ++ --unsafe -alibcxl_version=$(VERSION) -o $@+ $< && \ ++ mv $@+ $@ ++ ++%.3: %.xml $(XML_DEPS) ++ $(AM_V_GEN)$(RM) $@ && \ ++ $(XMLTO) -o . -m ../../manpage-normal.xsl man $< ++ ++endif +diff --git a/Documentation/cxl/lib/cxl_new.txt b/Documentation/cxl/lib/cxl_new.txt +new file mode 100644 +index 0000000..147d4e0 +--- /dev/null ++++ b/Documentation/cxl/lib/cxl_new.txt +@@ -0,0 +1,43 @@ ++// SPDX-License-Identifier: LGPL-2.0 ++ ++cxl_new(3) ++========== ++ ++NAME ++---- ++cxl_new - Create a new library context object that acts as a handle for all ++library operations ++ ++SYNOPSIS ++-------- ++[verse] ++---- ++#include ++ ++int cxl_new(struct cxl_ctx **ctx); ++---- ++ ++DESCRIPTION ++----------- ++Instantiates a new library context, and stores an opaque pointer in ctx. The ++context is freed by linklibcxl:cxl_unref[3], i.e. cxl_new(3) implies an ++internal linklibcxl:cxl_ref[3]. ++ ++ ++RETURN VALUE ++------------ ++Returns 0 on success, and a negative errno on failure. ++Possible error codes are: ++ ++ * -ENOMEM ++ * -ENXIO ++ ++EXAMPLE ++------- ++See example usage in test/libcxl.c ++ ++include::../../copyright.txt[] ++ ++SEE ALSO ++-------- ++linklibcxl:cxl_ref[3], linklibcxl:cxl_unref[3] +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +new file mode 100644 +index 0000000..2539369 +--- /dev/null ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -0,0 +1,56 @@ ++// SPDX-License-Identifier: LGPL-2.0 ++ ++libcxl(3) ++========= ++ ++NAME ++---- ++libcxl - A library to interact with CXL devices through sysfs(5) ++and ioctl(2) interfaces ++ ++SYNOPSIS ++-------- ++[verse] ++#include ++cc ... -lcxl ++ ++DESCRIPTION ++----------- ++libcxl provides interfaces to interact with CXL devices in Linux, using sysfs ++interfaces for most kernel interactions, and the ioctl() interface for command ++submission. ++ ++The starting point for all library interfaces is a 'cxl_ctx' object, returned ++by linklibcxl:cxl_new[3]. CXL 'Type 3' memory devices are children of the ++cxl_ctx object, and can be iterated through using an iterator API. ++ ++Library level interfaces that are agnostic to any device, or a specific ++subclass of operations have the prefix 'cxl_' ++ ++The object representing a CXL Type 3 device is 'cxl_memdev'. Library interfaces ++related to these devices have the prefix 'cxl_memdev_'. These interfaces are ++mostly associated with sysfs interactions (unless otherwise noted in their ++respective documentation pages). They are typically used to retrieve data ++published by the kernel, or to send data or trigger kernel operations for a ++given device. ++ ++A 'cxl_cmd' is a reference counted object which is used to perform 'Mailbox' ++commands as described in the CXL Specification. A 'cxl_cmd' object is tied to a ++'cxl_memdev'. Associated library interfaces have the prefix 'cxl_cmd_'. Within ++this sub-class of interfaces, there are: ++ ++ * 'cxl_cmd_new_*' interfaces that allocate a new cxl_cmd object for a given ++ command type. ++ ++ * 'cxl_cmd_submit' which submits the command via ioctl() ++ ++ * 'cxl_cmd__get_' interfaces that get specific fields out of the ++ command response ++ ++ * 'cxl_cmd_get_*' interfaces to get general command related information. ++ ++include::../../copyright.txt[] ++ ++SEE ALSO ++-------- ++linklibcxl:cxl[1] +diff --git a/Makefile.am b/Makefile.am +index 4904ee7..e2f6bef 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -4,6 +4,7 @@ ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} + SUBDIRS = . cxl/lib daxctl/lib ndctl/lib cxl ndctl daxctl + if ENABLE_DOCS + SUBDIRS += Documentation/ndctl Documentation/daxctl Documentation/cxl ++SUBDIRS += Documentation/cxl/lib + endif + SUBDIRS += test + +diff --git a/configure.ac b/configure.ac +index dadae0a..00497ae 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -231,6 +231,7 @@ AC_CONFIG_FILES([ + Documentation/ndctl/Makefile + Documentation/daxctl/Makefile + Documentation/cxl/Makefile ++ Documentation/cxl/lib/Makefile + ]) + + AC_OUTPUT +-- +2.27.0 + diff --git a/SOURCES/0044-ndctl-Add-CXL-packages-to-the-RPM-spec.patch b/SOURCES/0044-ndctl-Add-CXL-packages-to-the-RPM-spec.patch new file mode 100644 index 0000000..89de3d6 --- /dev/null +++ b/SOURCES/0044-ndctl-Add-CXL-packages-to-the-RPM-spec.patch @@ -0,0 +1,147 @@ +From 57b1484fa427228afd52cdfa4fa3916a7a5878bf Mon Sep 17 00:00:00 2001 +From: Ira Weiny +Date: Thu, 7 Oct 2021 02:21:37 -0600 +Subject: [PATCH 044/217] ndctl: Add CXL packages to the RPM spec + +Add CXL related packages - the cxl-cli utility, the libcxl library, and +development headers to respective RPM packages in the main spec file. + +Reviewed-by: Dan Williams +Signed-off-by: Ira Weiny +Signed-off-by: Vishal Verma +--- + Makefile.am | 4 ++++ + ndctl.spec.in | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 53 insertions(+) + +diff --git a/Makefile.am b/Makefile.am +index e2f6bef..fa2010a 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -23,17 +23,21 @@ CLEANFILES += $(noinst_SCRIPTS) + + do_rhel_subst = sed -e 's,VERSION,$(VERSION),g' \ + -e 's,DAX_DNAME,daxctl-devel,g' \ ++ -e 's,CXL_DNAME,cxl-devel,g' \ + -e 's,DNAME,ndctl-devel,g' \ + -e '/^%defattr.*/d' \ + -e 's,DAX_LNAME,daxctl-libs,g' \ ++ -e 's,CXL_LNAME,cxl-libs,g' \ + -e 's,LNAME,ndctl-libs,g' + + do_sles_subst = sed -e 's,VERSION,$(VERSION),g' \ + -e 's,DAX_DNAME,libdaxctl-devel,g' \ ++ -e 's,CXL_DNAME,libcxl-devel,g' \ + -e 's,DNAME,libndctl-devel,g' \ + -e 's,%license,%doc,g' \ + -e 's,\(^License:.*GPL\)v2,\1-2.0,g' \ + -e "s,DAX_LNAME,libdaxctl$$(($(LIBDAXCTL_CURRENT) - $(LIBDAXCTL_AGE))),g" \ ++ -e "s,CXL_LNAME,libcxl$$(($(LIBCXL_CURRENT) - $(LIBCXL_AGE))),g" \ + -e "s,LNAME,libndctl$$(($(LIBNDCTL_CURRENT) - $(LIBNDCTL_AGE))),g" + + rhel/ndctl.spec: ndctl.spec.in Makefile.am version.m4 +diff --git a/ndctl.spec.in b/ndctl.spec.in +index 0563b2d..4b08c05 100644 +--- a/ndctl.spec.in ++++ b/ndctl.spec.in +@@ -8,6 +8,7 @@ Source0: https://github.com/pmem/%{name}/archive/v%{version}.tar.gz#/%{name}-%{v + + Requires: LNAME%{?_isa} = %{version}-%{release} + Requires: DAX_LNAME%{?_isa} = %{version}-%{release} ++Requires: CXL_LNAME%{?_isa} = %{version}-%{release} + BuildRequires: autoconf + %if 0%{?rhel} < 9 + BuildRequires: asciidoc +@@ -54,6 +55,24 @@ the Linux kernel Device-DAX facility. This facility enables DAX mappings + of performance / feature differentiated memory without need of a + filesystem. + ++%package -n cxl-cli ++Summary: Manage CXL devices ++License: GPLv2 ++Requires: CXL_LNAME%{?_isa} = %{version}-%{release} ++ ++%description -n cxl-cli ++The cxl utility provides enumeration and provisioning commands for ++the Linux kernel CXL devices. ++ ++%package -n CXL_DNAME ++Summary: Development files for libcxl ++License: LGPLv2 ++Requires: CXL_LNAME%{?_isa} = %{version}-%{release} ++ ++%description -n CXL_DNAME ++This package contains libraries and header files for developing applications ++that use libcxl, a library for enumerating and communicating with CXL devices. ++ + %package -n DAX_DNAME + Summary: Development files for libdaxctl + License: LGPLv2 +@@ -84,6 +103,13 @@ Device DAX is a facility for establishing DAX mappings of performance / + feature-differentiated memory. DAX_LNAME provides an enumeration / + control API for these devices. + ++%package -n CXL_LNAME ++Summary: Management library for CXL devices ++License: LGPLv2 ++ ++%description -n CXL_LNAME ++libcxl is a library for enumerating and communicating with CXL devices. ++ + + %prep + %setup -q ndctl-%{version} +@@ -105,6 +131,8 @@ make check + + %ldconfig_scriptlets -n DAX_LNAME + ++%ldconfig_scriptlets -n CXL_LNAME ++ + %define bashcompdir %(pkg-config --variable=completionsdir bash-completion) + + %files +@@ -126,6 +154,12 @@ make check + %{_mandir}/man1/daxctl* + %{_datadir}/daxctl/daxctl.conf + ++%files -n cxl-cli ++%defattr(-,root,root) ++%license LICENSES/preferred/GPL-2.0 LICENSES/other/MIT LICENSES/other/CC0-1.0 ++%{_bindir}/cxl ++%{_mandir}/man1/cxl* ++ + %files -n LNAME + %defattr(-,root,root) + %doc README.md +@@ -138,6 +172,12 @@ make check + %license LICENSES/preferred/LGPL-2.1 LICENSES/other/MIT LICENSES/other/CC0-1.0 + %{_libdir}/libdaxctl.so.* + ++%files -n CXL_LNAME ++%defattr(-,root,root) ++%doc README.md ++%license LICENSES/preferred/LGPL-2.1 LICENSES/other/MIT LICENSES/other/CC0-1.0 ++%{_libdir}/libcxl.so.* ++ + %files -n DNAME + %defattr(-,root,root) + %license LICENSES/preferred/LGPL-2.1 +@@ -152,6 +192,15 @@ make check + %{_libdir}/libdaxctl.so + %{_libdir}/pkgconfig/libdaxctl.pc + ++%files -n CXL_DNAME ++%defattr(-,root,root) ++%license LICENSES/preferred/LGPL-2.1 ++%{_includedir}/cxl/ ++%{_libdir}/libcxl.so ++%{_libdir}/pkgconfig/libcxl.pc ++%{_mandir}/man3/cxl* ++%{_mandir}/man3/libcxl.3.gz ++ + + %changelog + * Fri May 27 2016 Dan Williams - 53-1 +-- +2.27.0 + diff --git a/SOURCES/0045-cxl-cli-add-bash-completion.patch b/SOURCES/0045-cxl-cli-add-bash-completion.patch new file mode 100644 index 0000000..a99e567 --- /dev/null +++ b/SOURCES/0045-cxl-cli-add-bash-completion.patch @@ -0,0 +1,139 @@ +From 02c40b971bd4d092b3612fcb5e9ddd57548e6dbb Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:38 -0600 +Subject: [PATCH 045/217] cxl-cli: add bash completion + +Add bash completion for the cxl-cli commands implemented so far: + cxl-list + cxl-read-labels + cxl-write-labels + cxl-zero-labels + +Acked-by: Dan Williams +Signed-off-by: Vishal Verma +--- + contrib/ndctl | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 109 insertions(+) + +diff --git a/contrib/ndctl b/contrib/ndctl +index 680fe6a..cae4b1b 100755 +--- a/contrib/ndctl ++++ b/contrib/ndctl +@@ -647,5 +647,114 @@ _daxctl() + __daxctl_main + } + ++### cxl-cli ### ++ ++__cxl_get_devs() ++{ ++ local opts=("--memdevs" "$*") ++ cxl list "${opts[@]}" | grep -E "^\s*\"memdev\":" | cut -d'"' -f4 ++} ++ ++__cxlcomp() ++{ ++ local i=0 ++ ++ COMPREPLY=( $( compgen -W "$1" -- "$2" ) ) ++ for cword in "${COMPREPLY[@]}"; do ++ if [[ "$cword" == @(--memdev|--offset|--size|--input|--output) ]]; then ++ COMPREPLY[$i]="${cword}=" ++ else ++ COMPREPLY[$i]="${cword} " ++ fi ++ ((i++)) ++ done ++} ++ ++__cxl_comp_options() ++{ ++ ++ local cur=$1 ++ local opts ++ ++ if [[ "$cur" == *=* ]]; then ++ local cur_subopt=${cur%%=*} ++ local cur_arg=${cur##*=} ++ case $cur_subopt in ++ --memdev) ++ opts="$(__cxl_get_devs -i)" ++ ;; ++ *) ++ return ++ ;; ++ esac ++ __cxlcomp "$opts" "$cur_arg" ++ fi ++} ++ ++__cxl_comp_non_option_args() ++{ ++ local subcmd=$1 ++ local cur=$2 ++ local opts ++ ++ case $subcmd in ++ read-labels) ++ ;& ++ write-labels) ++ ;& ++ zero-labels) ++ opts="$(__cxl_get_devs -i) all" ++ ;; ++ *) ++ return ++ ;; ++ esac ++ __cxlcomp "$opts" "$cur" ++} ++ ++__cxl_main() ++{ ++ local cmd subcmd ++ ++ cmd=${words[0]} ++ COMPREPLY=() ++ ++ # Skip options backward and find the last cxl command ++ __nd_common_prev_skip_opts ++ subcmd=$prev_skip_opts ++ # List cxl subcommands or long options ++ if [ -z $subcmd ]; then ++ if [[ $cur == --* ]]; then ++ cmds="--version --help --list-cmds" ++ else ++ cmds=$($cmd --list-cmds) ++ fi ++ __cxlcomp "$cmds" "$cur" ++ else ++ # List long option names ++ if [[ $cur == --* ]]; then ++ opts=$($cmd $subcmd --list-opts) ++ __cxlcomp "$opts" "$cur" ++ __cxl_comp_options "$cur" ++ else ++ [ -z "$subcmd" ] && return ++ __cxl_comp_non_option_args "$subcmd" "$cur" ++ fi ++ fi ++} ++ ++type cxl &>/dev/null && ++_cxl() ++{ ++ local cur words cword prev ++ if [ $preload_get_comp_words_by_ref = "true" ]; then ++ _get_comp_words_by_ref -n =: cur words cword prev ++ else ++ __nd_common_get_comp_words_by_ref -n =: cur words cword prev ++ fi ++ __cxl_main ++} ++ + complete -o nospace -F _ndctl ndctl + complete -o nospace -F _daxctl daxctl ++complete -o nospace -F _cxl cxl +-- +2.27.0 + diff --git a/SOURCES/0046-cxl-add-health-information-to-cxl-list.patch b/SOURCES/0046-cxl-add-health-information-to-cxl-list.patch new file mode 100644 index 0000000..1bd79bb --- /dev/null +++ b/SOURCES/0046-cxl-add-health-information-to-cxl-list.patch @@ -0,0 +1,308 @@ +From f5d1e2133c54c1f420a0c3cf45fa633f097823be Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 7 Oct 2021 02:21:39 -0600 +Subject: [PATCH 046/217] cxl: add health information to cxl-list + +Add JSON output for fields from the 'GET_HEALTH_INFO' mailbox command +to memory device listings. + +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 38 +++++++ + cxl/list.c | 5 + + util/json.c | 179 +++++++++++++++++++++++++++++++++ + util/json.h | 1 + + 4 files changed, 223 insertions(+) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 370d5b8..c8d10fb 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -48,6 +48,44 @@ OPTIONS + --idle:: + Include idle (not enabled / zero-sized) devices in the listing + ++-H:: ++--health:: ++ Include health information in the memdev listing. Example listing: ++---- ++# cxl list -m mem0 -H ++[ ++ { ++ "memdev":"mem0", ++ "pmem_size":268435456, ++ "ram_size":268435456, ++ "health":{ ++ "maintenance_needed":true, ++ "performance_degraded":true, ++ "hw_replacement_needed":true, ++ "media_normal":false, ++ "media_not_ready":false, ++ "media_persistence_lost":false, ++ "media_data_lost":true, ++ "media_powerloss_persistence_loss":false, ++ "media_shutdown_persistence_loss":false, ++ "media_persistence_loss_imminent":false, ++ "media_powerloss_data_loss":false, ++ "media_shutdown_data_loss":false, ++ "media_data_loss_imminent":false, ++ "ext_life_used":"normal", ++ "ext_temperature":"critical", ++ "ext_corrected_volatile":"warning", ++ "ext_corrected_persistent":"normal", ++ "life_used_percent":15, ++ "temperature":25, ++ "dirty_shutdowns":10, ++ "volatile_errors":20, ++ "pmem_errors":30 ++ } ++ } ++] ++---- ++ + include::human-option.txt[] + + include::verbose-option.txt[] +diff --git a/cxl/list.c b/cxl/list.c +index 043d20c..b1468b7 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -16,6 +16,7 @@ static struct { + bool memdevs; + bool idle; + bool human; ++ bool health; + } list; + + static unsigned long listopts_to_flags(void) +@@ -26,6 +27,8 @@ static unsigned long listopts_to_flags(void) + flags |= UTIL_JSON_IDLE; + if (list.human) + flags |= UTIL_JSON_HUMAN; ++ if (list.health) ++ flags |= UTIL_JSON_HEALTH; + return flags; + } + +@@ -57,6 +60,8 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"), + OPT_BOOLEAN('u', "human", &list.human, + "use human friendly number formats "), ++ OPT_BOOLEAN('H', "health", &list.health, ++ "include memory device health information "), + OPT_END(), + }; + const char * const u[] = { +diff --git a/util/json.c b/util/json.c +index 3be3a92..f97cf07 100644 +--- a/util/json.c ++++ b/util/json.c +@@ -1442,6 +1442,180 @@ struct json_object *util_badblock_rec_to_json(u64 block, u64 count, + return NULL; + } + ++static struct json_object *util_cxl_memdev_health_to_json( ++ struct cxl_memdev *memdev, unsigned long flags) ++{ ++ struct json_object *jhealth; ++ struct json_object *jobj; ++ struct cxl_cmd *cmd; ++ u32 field; ++ int rc; ++ ++ jhealth = json_object_new_object(); ++ if (!jhealth) ++ return NULL; ++ if (!memdev) ++ goto err_jobj; ++ ++ cmd = cxl_cmd_new_get_health_info(memdev); ++ if (!cmd) ++ goto err_jobj; ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) ++ goto err_cmd; ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) ++ goto err_cmd; ++ ++ /* health_status fields */ ++ rc = cxl_cmd_health_info_get_maintenance_needed(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "maintenance_needed", jobj); ++ ++ rc = cxl_cmd_health_info_get_performance_degraded(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "performance_degraded", jobj); ++ ++ rc = cxl_cmd_health_info_get_hw_replacement_needed(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "hw_replacement_needed", jobj); ++ ++ /* media_status fields */ ++ rc = cxl_cmd_health_info_get_media_normal(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_normal", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_not_ready(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_not_ready", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_persistence_lost(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_persistence_lost", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_data_lost(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_data_lost", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_powerloss_persistence_loss(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_powerloss_persistence_loss", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_shutdown_persistence_loss(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_shutdown_persistence_loss", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_persistence_loss_imminent(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_persistence_loss_imminent", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_powerloss_data_loss(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_powerloss_data_loss", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_shutdown_data_loss(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_shutdown_data_loss", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_data_loss_imminent(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_data_loss_imminent", jobj); ++ ++ /* ext_status fields */ ++ if (cxl_cmd_health_info_get_ext_life_used_normal(cmd)) ++ jobj = json_object_new_string("normal"); ++ else if (cxl_cmd_health_info_get_ext_life_used_warning(cmd)) ++ jobj = json_object_new_string("warning"); ++ else if (cxl_cmd_health_info_get_ext_life_used_critical(cmd)) ++ jobj = json_object_new_string("critical"); ++ else ++ jobj = json_object_new_string("unknown"); ++ if (jobj) ++ json_object_object_add(jhealth, "ext_life_used", jobj); ++ ++ if (cxl_cmd_health_info_get_ext_temperature_normal(cmd)) ++ jobj = json_object_new_string("normal"); ++ else if (cxl_cmd_health_info_get_ext_temperature_warning(cmd)) ++ jobj = json_object_new_string("warning"); ++ else if (cxl_cmd_health_info_get_ext_temperature_critical(cmd)) ++ jobj = json_object_new_string("critical"); ++ else ++ jobj = json_object_new_string("unknown"); ++ if (jobj) ++ json_object_object_add(jhealth, "ext_temperature", jobj); ++ ++ if (cxl_cmd_health_info_get_ext_corrected_volatile_normal(cmd)) ++ jobj = json_object_new_string("normal"); ++ else if (cxl_cmd_health_info_get_ext_corrected_volatile_warning(cmd)) ++ jobj = json_object_new_string("warning"); ++ else ++ jobj = json_object_new_string("unknown"); ++ if (jobj) ++ json_object_object_add(jhealth, "ext_corrected_volatile", jobj); ++ ++ if (cxl_cmd_health_info_get_ext_corrected_persistent_normal(cmd)) ++ jobj = json_object_new_string("normal"); ++ else if (cxl_cmd_health_info_get_ext_corrected_persistent_warning(cmd)) ++ jobj = json_object_new_string("warning"); ++ else ++ jobj = json_object_new_string("unknown"); ++ if (jobj) ++ json_object_object_add(jhealth, "ext_corrected_persistent", jobj); ++ ++ /* other fields */ ++ field = cxl_cmd_health_info_get_life_used(cmd); ++ if (field != 0xff) { ++ jobj = json_object_new_int(field); ++ if (jobj) ++ json_object_object_add(jhealth, "life_used_percent", jobj); ++ } ++ ++ field = cxl_cmd_health_info_get_temperature(cmd); ++ if (field != 0xffff) { ++ jobj = json_object_new_int(field); ++ if (jobj) ++ json_object_object_add(jhealth, "temperature", jobj); ++ } ++ ++ field = cxl_cmd_health_info_get_dirty_shutdowns(cmd); ++ jobj = json_object_new_int64(field); ++ if (jobj) ++ json_object_object_add(jhealth, "dirty_shutdowns", jobj); ++ ++ field = cxl_cmd_health_info_get_volatile_errors(cmd); ++ jobj = json_object_new_int64(field); ++ if (jobj) ++ json_object_object_add(jhealth, "volatile_errors", jobj); ++ ++ field = cxl_cmd_health_info_get_pmem_errors(cmd); ++ jobj = json_object_new_int64(field); ++ if (jobj) ++ json_object_object_add(jhealth, "pmem_errors", jobj); ++ ++ cxl_cmd_unref(cmd); ++ return jhealth; ++ ++err_cmd: ++ cxl_cmd_unref(cmd); ++err_jobj: ++ json_object_put(jhealth); ++ return NULL; ++} ++ + struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + unsigned long flags) + { +@@ -1464,5 +1638,10 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + if (jobj) + json_object_object_add(jdev, "ram_size", jobj); + ++ if (flags & UTIL_JSON_HEALTH) { ++ jobj = util_cxl_memdev_health_to_json(memdev, flags); ++ if (jobj) ++ json_object_object_add(jdev, "health", jobj); ++ } + return jdev; + } +diff --git a/util/json.h b/util/json.h +index 91918c8..ce575e6 100644 +--- a/util/json.h ++++ b/util/json.h +@@ -19,6 +19,7 @@ enum util_json_flags { + UTIL_JSON_CONFIGURED = (1 << 7), + UTIL_JSON_FIRMWARE = (1 << 8), + UTIL_JSON_DAX_MAPPINGS = (1 << 9), ++ UTIL_JSON_HEALTH = (1 << 10), + }; + + struct json_object; +-- +2.27.0 + diff --git a/SOURCES/0047-ndctl-install-bash-completion-symlinks.patch b/SOURCES/0047-ndctl-install-bash-completion-symlinks.patch new file mode 100644 index 0000000..09109eb --- /dev/null +++ b/SOURCES/0047-ndctl-install-bash-completion-symlinks.patch @@ -0,0 +1,75 @@ +From ac46d00d7d4d555a238bb898e2ff4af0c444bebe Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Tue, 16 Nov 2021 17:51:31 -0700 +Subject: [PATCH 047/217] ndctl: install bash-completion symlinks + +Install symlinks for other utilities (daxctl, cxl-cli) in the +completions directory so that the dynamic completion loader can pick up +their respective names. Without this, completions for daxctl and cxl +would only work after a prior invocation of ndctl's completion. + +Reported-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Makefile.am | 3 +++ + configure.ac | 1 + + ndctl.spec.in | 4 +++- + 3 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/Makefile.am b/Makefile.am +index fa2010a..bd0037e 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -49,6 +49,9 @@ sles/ndctl.spec: sles/header ndctl.spec.in Makefile.am version.m4 + if ENABLE_BASH_COMPLETION + bashcompletiondir = $(BASH_COMPLETION_DIR) + dist_bashcompletion_DATA = contrib/ndctl ++install-data-hook: ++ $(LN_S) -f $(BASH_COMPLETION_DIR)/ndctl $(DESTDIR)/$(BASH_COMPLETION_DIR)/daxctl ++ $(LN_S) -f $(BASH_COMPLETION_DIR)/ndctl $(DESTDIR)/$(BASH_COMPLETION_DIR)/cxl + endif + + modprobe_file = contrib/nvdimm-security.conf +diff --git a/configure.ac b/configure.ac +index 00497ae..9ac785f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -31,6 +31,7 @@ AC_PREFIX_DEFAULT([/usr]) + + AC_PROG_SED + AC_PROG_MKDIR_P ++AC_PROG_LN_S + + AC_ARG_ENABLE([docs], + AS_HELP_STRING([--disable-docs], +diff --git a/ndctl.spec.in b/ndctl.spec.in +index 4b08c05..27ca097 100644 +--- a/ndctl.spec.in ++++ b/ndctl.spec.in +@@ -140,7 +140,7 @@ make check + %license LICENSES/preferred/GPL-2.0 LICENSES/other/MIT LICENSES/other/CC0-1.0 + %{_bindir}/ndctl + %{_mandir}/man1/ndctl* +-%{bashcompdir}/ ++%{bashcompdir}/ndctl + %{_unitdir}/ndctl-monitor.service + %{_sysconfdir}/ndctl/keys/keys.readme + %{_sysconfdir}/modprobe.d/nvdimm-security.conf +@@ -153,12 +153,14 @@ make check + %{_bindir}/daxctl + %{_mandir}/man1/daxctl* + %{_datadir}/daxctl/daxctl.conf ++%{bashcompdir}/daxctl + + %files -n cxl-cli + %defattr(-,root,root) + %license LICENSES/preferred/GPL-2.0 LICENSES/other/MIT LICENSES/other/CC0-1.0 + %{_bindir}/cxl + %{_mandir}/man1/cxl* ++%{bashcompdir}/cxl + + %files -n LNAME + %defattr(-,root,root) +-- +2.27.0 + diff --git a/SOURCES/0048-scripts-Add-a-man-page-template-generator.patch b/SOURCES/0048-scripts-Add-a-man-page-template-generator.patch new file mode 100644 index 0000000..712ed42 --- /dev/null +++ b/SOURCES/0048-scripts-Add-a-man-page-template-generator.patch @@ -0,0 +1,400 @@ +From 706a418798633ccb550b114eca7cc11038ab2695 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Tue, 14 Dec 2021 19:01:02 -0700 +Subject: [PATCH 048/217] scripts: Add a man page template generator + +Add a script to generate man page templates for the utils and libraries +under ndctl - including cxl, libcxl, ndctl, and daxctl. + +The script can control certain include options depending on the options +supplied, and either dump the templates to stdout, or write the actual +files in their eventual directories, and open up an editor to further +edit them (unless --no-edit is used). + +Link: https://lore.kernel.org/r/20211215020102.97880-1-vishal.l.verma@intel.com +Cc: Dan Williams +Signed-off-by: Vishal Verma +--- + scripts/docsurgeon | 339 +++++++++++++++++++++++++ + scripts/docsurgeon_parser_generator.m4 | 23 ++ + 2 files changed, 362 insertions(+) + create mode 100755 scripts/docsurgeon + create mode 100644 scripts/docsurgeon_parser_generator.m4 + +diff --git a/scripts/docsurgeon b/scripts/docsurgeon +new file mode 100755 +index 0000000..ca0ad78 +--- /dev/null ++++ b/scripts/docsurgeon +@@ -0,0 +1,339 @@ ++#!/bin/bash -eE ++ ++this_script="docsurgeon" ++script_dir="$(cd "$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")" && pwd)" ++env_file="${script_dir}/.env" ++if [ -e "$env_file" ]; then ++ # shellcheck source=.env ++ . "$env_file" ++fi ++ ++sources_file="${script_dir}/.sources" ++ ++parser_generator="${script_dir}/${this_script}_parser_generator.m4" ++parser_lib="${script_dir}/${this_script}_parser.sh" ++if [ ! -e "$parser_lib" ] || [ "$parser_generator" -nt "$parser_lib" ]; then ++ if command -V argbash > /dev/null; then ++ argbash --strip user-content "$parser_generator" -o "$parser_lib" ++ else ++ echo "error: please install argbash" >&2 ++ exit 1 ++ fi ++fi ++ ++if [[ $1 != "bin" ]]; then ++ # shellcheck source=docsurgeon_parser.sh ++ . "${script_dir}/${this_script}_parser.sh" || { echo "Couldn't find $parser_lib" >&2; exit 1; } ++fi ++ ++# some script defaults - override using '.env' ++docbase="Documentation" ++copyright_cli="// SPDX-License-Identifier: GPL-2.0" ++copyright_footer_cli="include::../copyright.txt[]" ++copyright_lib="// SPDX-License-Identifier: LGPL-2.0" ++copyright_footer_lib="include::../../copyright.txt[]" ++ ++# List of files we're creating, to be edited/renamed later ++# This starts out blank, and is filled in as we go by gen_*() functions ++declare -a outfiles ++ ++cleanup() ++{ ++ if [ ${#outfiles[@]} -gt 0 ]; then ++ rm -f "${outfiles[@]}" ++ fi ++ set +x ++} ++ ++trap cleanup EXIT ++ ++auto_detect_params() ++{ ++ fs="" ++ module="" ++ section="" ++ ++ # if module and section were explicitly specified, respect them ++ if [[ $_arg_module ]] && [[ $_arg_section ]]; then ++ return ++ fi ++ ++ # check if names are self-consistent, and determine 'fs' ++ for name in ${_arg_name[@]}; do ++ if [[ ! $fs ]]; then ++ if [[ $name == *-* ]]; then ++ fs="-" ++ elif [[ $name == *_* ]]; then ++ fs="_" ++ else ++ # can't autodetect section ++ return ++ fi ++ fi ++ if [[ $fs == "-" ]] && [[ $name == *_* ]]; then ++ die "can't auto-detect params with mixed-style names" ++ fi ++ if [[ $fs == "_" ]] && [[ $name == *-* ]]; then ++ die "can't auto-detect params with mixed-style names" ++ fi ++ done ++ ++ # try to detect module name ++ for name in ${_arg_name[@]}; do ++ str=${name%%$fs*} ++ if [[ $module ]]; then ++ if [[ $str != $module ]]; then ++ die "Can't autodetect module because of mixed names ($str and $module)" ++ fi ++ else ++ module="$str" ++ fi ++ done ++ ++ # try to detect section number ++ case "$fs" in ++ -) ++ section=1 ++ ;; ++ _) ++ section=3 ++ ;; ++ *) ++ die "Unknown fs, can't autodetect section number" ++ ;; ++ esac ++ ++ if [[ $module ]]; then ++ _arg_module="$module" ++ fi ++ if [[ $section ]]; then ++ _arg_section="$section" ++ fi ++} ++ ++process_options_logic() ++{ ++ if [[ $_arg_debug == "on" ]]; then ++ set -x ++ fi ++ ++ auto_detect_params ++} ++ ++gen_underline() ++{ ++ name="$1" ++ char="$2" ++ num="${#name}" ++ ++ printf -v tmpstring "%-${num}s" " " ++ echo "${tmpstring// /$char}" ++} ++ ++gen_header() ++{ ++ printf "\n%s\n%s\n" "$1" "$(gen_underline "$1" "=")" ++} ++ ++gen_section() ++{ ++ printf "\n%s\n%s\n" "$1" "$(gen_underline "$1" "-")" ++} ++ ++gen_section_name() ++{ ++ name="$1" ++ ++ gen_section "NAME" ++ cat <<- EOF ++ $name - ++ EOF ++} ++ ++gen_section_synopsis_1() ++{ ++ name="$1" ++ ++ gen_section "SYNOPSIS" ++ cat <<- EOF ++ [verse] ++ '$_arg_module ${name#*-} []' ++ EOF ++} ++ ++gen_section_synopsis_3() ++{ ++ name="$1" ++ ++ gen_section "SYNOPSIS" ++ cat <<- EOF ++ [verse] ++ ---- ++ #include <$_arg_module/lib$_arg_module.h> ++ ++ $name(); ++ ---- ++ EOF ++} ++ ++gen_section_example_1() ++{ ++ name="$1" ++ ++ gen_section "EXAMPLE" ++ cat <<- EOF ++ ---- ++ # $_arg_module ${name#*-} ++ ---- ++ EOF ++} ++ ++gen_section_example_3() ++{ ++ name="$1" ++ ++ gen_section "EXAMPLE" ++ cat <<- EOF ++ See example usage in test/lib$_arg_module.c ++ EOF ++} ++ ++gen_section_options_1() ++{ ++ gen_section "OPTIONS" ++cat << EOF ++-o:: ++--option:: ++ Description ++EOF ++ ++ if [[ $_arg_human_option == "on" ]]; then ++ printf "\n%s\n" "include::human-option.txt[]" ++ fi ++ if [[ $_arg_verbose_option == "on" ]]; then ++ printf "\n%s\n" "include::verbose-option.txt[]" ++ fi ++} ++ ++gen_section_seealso_1() ++{ ++ gen_section "SEE ALSO" ++ cat <<- EOF ++ link$_arg_module:$_arg_module-list[$_arg_section], ++ EOF ++} ++ ++gen_section_seealso_3() ++{ ++ gen_section "SEE ALSO" ++ cat <<- EOF ++ linklib$_arg_module:${_arg_module}_other_API[$_arg_section], ++ EOF ++} ++ ++gen_cli() ++{ ++ name="$1" ++ path="$docbase/$_arg_module" ++ if [ ! -d "$path" ]; then ++ die "Not found: $path" ++ fi ++ ++ tmp="$(mktemp -p "$path" "$name.txt.XXXX")" ++ outfiles+=("$tmp") ++ ++ # Start template generation ++ printf "%s\n" "$copyright_cli" > "$tmp" ++ gen_header "$name" >> "$tmp" ++ gen_section_name "$name" >> "$tmp" ++ gen_section_synopsis_1 "$name" >> "$tmp" ++ gen_section "DESCRIPTION" >> "$tmp" ++ gen_section_example_1 "$name" >> "$tmp" ++ gen_section_options_1 >> "$tmp" ++ printf "\n%s\n" "$copyright_footer_cli" >> "$tmp" ++ gen_section_seealso_1 >> "$tmp" ++} ++ ++gen_lib() ++{ ++ name="$1" ++ path="$docbase/$_arg_module/lib" ++ if [ ! -d "$path" ]; then ++ die "Not found: $path" ++ fi ++ ++ tmp="$(mktemp -p "$path" "$name.txt.XXXX")" ++ outfiles+=("$tmp") ++ ++ # Start template generation ++ printf "%s\n" "$copyright_lib" > "$tmp" ++ gen_header "$name($_arg_section)" >> "$tmp" ++ gen_section_name "$name" >> "$tmp" ++ gen_section_synopsis_3 "$name" >> "$tmp" ++ gen_section "DESCRIPTION" >> "$tmp" ++ gen_section "RETURN VALUE" >> "$tmp" ++ gen_section_example_3 "$name" >> "$tmp" ++ printf "\n%s\n" "$copyright_footer_lib" >> "$tmp" ++ gen_section_seealso_3 >> "$tmp" ++} ++ ++gen_man() ++{ ++ name="$1" ++ case "$_arg_section" in ++ 1) ++ gen_cli "$name" ++ ;; ++ 3) ++ gen_lib "$name" ++ ;; ++ *) ++ die "Unknown section: $_arg_section" ++ ;; ++ esac ++} ++ ++gen_include() ++{ ++ echo "in gen_include" ++} ++ ++main() ++{ ++ process_options_logic ++ ++ cmd="$_arg_command" ++ case "$cmd" in ++ gen-man) ++ for name in ${_arg_name[@]}; do ++ gen_man "$name" ++ done ++ ;; ++ gen-include) ++ for name in ${_arg_name[@]}; do ++ gen_include ++ done ++ ;; ++ *) ++ die "Unknown command: $cmd" ++ ;; ++ esac ++ ++ if [[ $_arg_dump == "on" ]]; then ++ for file in ${outfiles[@]}; do ++ echo "${file##*/}" ++ cat "$file" ++ rm "$file" ++ done ++ elif [ ${#outfiles[@]} -gt 0 ]; then ++ if [[ $_arg_edit = "on" ]]; then ++ vim -p "${outfiles[@]}" ++ fi ++ ++ for file in ${outfiles[@]}; do ++ mv "$file" "${file%.*}" ++ done ++ fi ++} ++ ++main "$@" +diff --git a/scripts/docsurgeon_parser_generator.m4 b/scripts/docsurgeon_parser_generator.m4 +new file mode 100644 +index 0000000..9283c7c +--- /dev/null ++++ b/scripts/docsurgeon_parser_generator.m4 +@@ -0,0 +1,23 @@ ++#!/bin/bash ++ ++# m4_ignore( ++echo "This is just a parsing library template, not the library - pass this file to 'argbash' to fix this." >&2 ++exit 11 #)Created by argbash-init v2.9.0 ++# Rearrange the order of options below according to what you would like to see in the help message. ++# ARG_OPTIONAL_REPEATED([name], [n], [Command or function name to generate a template for.\n Can be repeated for multiple names. ], []) ++# ARG_OPTIONAL_BOOLEAN([edit], [e], [Edit template files after creation], [on]) ++# ARG_OPTIONAL_BOOLEAN([debug], [], [Debug script problems (enables set -x)], ) ++# ARG_OPTIONAL_BOOLEAN([dump], [], [Write generated file to stdout instead of a file], ) ++# ARG_OPTIONAL_SINGLE([module], [m], [Module (Docs subdir) in which to create the template], []) ++# ARG_OPTIONAL_SINGLE([section], [s], [man section for which to create the template], []) ++# ARG_OPTIONAL_BOOLEAN([human-option], [u], [Include the human option in 'OPTIONS'], ) ++# ARG_OPTIONAL_BOOLEAN([verbose-option], [V], [Include the verbose option in 'OPTIONS'], ) ++# ARG_POSITIONAL_DOUBLEDASH() ++# ARG_POSITIONAL_SINGLE([command], [Operation to perform:\n gen-man\n gen-include], []) ++# ARGBASH_SET_DELIM([ =]) ++# ARG_OPTION_STACKING([getopt]) ++# ARG_RESTRICT_VALUES([no-local-options]) ++# ARG_DEFAULTS_POS ++# ARG_HELP([Tool to aid in creating and managing man page templates]) ++# ARG_VERSION([echo "docsurgeon 0.1"]) ++# ARGBASH_GO +-- +2.27.0 + diff --git a/SOURCES/0049-daxctl-Add-Soft-Reservation-theory-of-operation.patch b/SOURCES/0049-daxctl-Add-Soft-Reservation-theory-of-operation.patch new file mode 100644 index 0000000..54dfad0 --- /dev/null +++ b/SOURCES/0049-daxctl-Add-Soft-Reservation-theory-of-operation.patch @@ -0,0 +1,167 @@ +From 8f4e42c0c526e85b045fd0329df7cb904f511c98 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 7 Oct 2021 14:59:53 -0700 +Subject: [PATCH 049/217] daxctl: Add "Soft Reservation" theory of operation + +As systems are starting to ship memory with the EFI "Special Purpose" +attribute that Linux optionally turns into "Soft Reserved" ranges one of +the immediate first questions is "where is my special memory, and how do +access it". Add some documentation to explain the default behaviour of +"Soft Reserved". + +Link: https://lore.kernel.org/r/163364399303.201290.6835215953983673447.stgit@dwillia2-desk3.amr.corp.intel.com +Reported-by: John Groves +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .../daxctl/daxctl-reconfigure-device.txt | 127 ++++++++++++------ + 1 file changed, 88 insertions(+), 39 deletions(-) + +diff --git a/Documentation/daxctl/daxctl-reconfigure-device.txt b/Documentation/daxctl/daxctl-reconfigure-device.txt +index f112b3c..132684c 100644 +--- a/Documentation/daxctl/daxctl-reconfigure-device.txt ++++ b/Documentation/daxctl/daxctl-reconfigure-device.txt +@@ -12,6 +12,94 @@ SYNOPSIS + [verse] + 'daxctl reconfigure-device' [...] [] + ++DESCRIPTION ++----------- ++ ++Reconfigure the operational mode of a dax device. This can be used to convert ++a regular 'devdax' mode device to the 'system-ram' mode which arranges for the ++dax range to be hot-plugged into the system as regular memory. ++ ++NOTE: This is a destructive operation. Any data on the dax device *will* be ++lost. ++ ++NOTE: Device reconfiguration depends on the dax-bus device model. See ++linkdaxctl:daxctl-migrate-device-model[1] for more information. If dax-class is ++in use (via the dax_pmem_compat driver), the reconfiguration will fail with an ++error such as the following: ++---- ++# daxctl reconfigure-device --mode=system-ram --region=0 all ++libdaxctl: daxctl_dev_disable: dax3.0: error: device model is dax-class ++dax3.0: disable failed: Operation not supported ++error reconfiguring devices: Operation not supported ++reconfigured 0 devices ++---- ++ ++'daxctl-reconfigure-device' nominally expects that it will online new memory ++blocks as 'movable', so that kernel data doesn't make it into this memory. ++However, there are other potential agents that may be configured to ++automatically online new hot-plugged memory as it appears. Most notably, ++these are the '/sys/devices/system/memory/auto_online_blocks' configuration, ++or system udev rules. If such an agent races to online memory sections, daxctl ++checks if the blocks were onlined as 'movable' memory. If this was not the ++case, and the memory blocks are found to be in a different zone, then a ++warning is displayed. If it is desired that a different agent control the ++onlining of memory blocks, and the associated memory zone, then it is ++recommended to use the --no-online option described below. This will abridge ++the device reconfiguration operation to just hotplugging the memory, and ++refrain from then onlining it. ++ ++In case daxctl detects that there is a kernel policy to auto-online blocks ++(via /sys/devices/system/memory/auto_online_blocks), then reconfiguring to ++system-ram will result in a failure. This can be overridden with '--force'. ++ ++ ++THEORY OF OPERATION ++------------------- ++The kernel device-dax subsystem surfaces character devices ++that provide DAX-access (direct mappings sans page-cache buffering) to a ++given memory region. The devices are named /dev/daxX.Y where X is a ++region-id and Y is an instance-id within that region. There are 2 ++mechanisms that trigger device-dax instances to appear: ++ ++1. Persistent Memory (PMEM) namespace configured in "devdax" mode. See ++"ndctl create-namspace --help" and ++https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/dax/Kconfig[CONFIG_DEV_DAX_PMEM]. ++In this case the device-dax instance is statically sized to its host ++memory region which is bounded to the physical address range of the host ++namespace. ++ ++2. Soft Reserved memory enumerated by platform firmware. On EFI systems ++this is communicated via the so called EFI_MEMORY_SP "Special Purpose" ++attribute. See ++https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/dax/Kconfig[CONFIG_DEV_DAX_HMEM]. ++In this case the device-dax instance(s) associated with the given memory ++region can be resized and divided into multiple devices. ++ ++In the Soft Reservation case the expectation for EFI + ACPI based ++platforms is that in addition to the EFI_MEMORY_SP attribute the ++firmware also creates distinct ACPI proximity domains for any address ++range that has different performance characteristics than default ++"System RAM". So, the SRAT will define the proximity domain, the SLIT ++communicates relative distance to other proximity domains, and the HMAT ++is populated with nominal read/write latency and read/write bandwidth ++data. That HMAT data is emitted to the kernel log on bootup, and also ++exported to sysfs. See ++https://www.kernel.org/doc/html/latest/admin-guide/mm/numaperf.html[NUMAPERF], ++for the runtime representation of CPU to Memory node performance ++details. ++ ++Outside of the NUMA performance details linked above the other method to ++detect the presence of "Soft Reserved" memory is to dump /proc/iomem and ++look for "Soft Reserved" ranges. If the kernel was not built with ++CONFIG_EFI_SOFTRESERVE, predates the introduction of ++CONFIG_EFI_SOFTRESERVE (v5.5), or was booted with the efi=nosoftreserve ++command line then device-dax will not attach and the expectation is that ++the memory shows up as a memory-only NUMA node. Otherwise the memory ++shows up as a device-dax instance and DAXCTL(1) can be used to ++optionally partition it and assign the memory back to the kernel as ++"System RAM", or the device can be mapped directly as the back end of a ++userspace memory allocator like https://pmem.io/vmem/libvmem/[LIBVMEM]. ++ + EXAMPLES + -------- + +@@ -83,45 +171,6 @@ reconfigured 1 device + reconfigured 1 device + ---- + +-DESCRIPTION +------------ +- +-Reconfigure the operational mode of a dax device. This can be used to convert +-a regular 'devdax' mode device to the 'system-ram' mode which arranges for the +-dax range to be hot-plugged into the system as regular memory. +- +-NOTE: This is a destructive operation. Any data on the dax device *will* be +-lost. +- +-NOTE: Device reconfiguration depends on the dax-bus device model. See +-linkdaxctl:daxctl-migrate-device-model[1] for more information. If dax-class is +-in use (via the dax_pmem_compat driver), the reconfiguration will fail with an +-error such as the following: +----- +-# daxctl reconfigure-device --mode=system-ram --region=0 all +-libdaxctl: daxctl_dev_disable: dax3.0: error: device model is dax-class +-dax3.0: disable failed: Operation not supported +-error reconfiguring devices: Operation not supported +-reconfigured 0 devices +----- +- +-'daxctl-reconfigure-device' nominally expects that it will online new memory +-blocks as 'movable', so that kernel data doesn't make it into this memory. +-However, there are other potential agents that may be configured to +-automatically online new hot-plugged memory as it appears. Most notably, +-these are the '/sys/devices/system/memory/auto_online_blocks' configuration, +-or system udev rules. If such an agent races to online memory sections, daxctl +-checks if the blocks were onlined as 'movable' memory. If this was not the +-case, and the memory blocks are found to be in a different zone, then a +-warning is displayed. If it is desired that a different agent control the +-onlining of memory blocks, and the associated memory zone, then it is +-recommended to use the --no-online option described below. This will abridge +-the device reconfiguration operation to just hotplugging the memory, and +-refrain from then onlining it. +- +-In case daxctl detects that there is a kernel policy to auto-online blocks +-(via /sys/devices/system/memory/auto_online_blocks), then reconfiguring to +-system-ram will result in a failure. This can be overridden with '--force'. + + OPTIONS + ------- +-- +2.27.0 + diff --git a/SOURCES/0061-libcxl-fix-potential-NULL-dereference-in-cxl_memdev_.patch b/SOURCES/0061-libcxl-fix-potential-NULL-dereference-in-cxl_memdev_.patch new file mode 100644 index 0000000..00d8bd5 --- /dev/null +++ b/SOURCES/0061-libcxl-fix-potential-NULL-dereference-in-cxl_memdev_.patch @@ -0,0 +1,46 @@ +From c55b18181281b2fffadb9e0e8955d74b8b719349 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Fri, 17 Dec 2021 19:25:11 -0700 +Subject: [PATCH 061/217] libcxl: fix potential NULL dereference in + cxl_memdev_nvdimm_bridge_active() + +Static analysis points out that the function above has a check for +'if (!bridge)', implying that bridge maybe NULL, but it is dereferenced +before the check, which could result in a NULL dereference. + +Fix this by moving any accesses to the bridge structure after the NULL +check. + +Link: https://lore.kernel.org/r/20211218022511.314928-1-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index f0664be..3390eb9 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -420,12 +420,15 @@ CXL_EXPORT int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev) + { + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); + struct cxl_nvdimm_bridge *bridge = memdev->bridge; +- char *path = bridge->dev_buf; +- int len = bridge->buf_len; ++ char *path; ++ int len; + + if (!bridge) + return 0; + ++ path = bridge->dev_buf; ++ len = bridge->buf_len; ++ + if (snprintf(path, len, "%s/driver", bridge->dev_path) >= len) { + err(ctx, "%s: nvdimm bridge buffer too small!\n", + cxl_memdev_get_devname(memdev)); +-- +2.27.0 + diff --git a/SOURCES/0064-ndctl-release-v72.patch b/SOURCES/0064-ndctl-release-v72.patch new file mode 100644 index 0000000..c81b9f4 --- /dev/null +++ b/SOURCES/0064-ndctl-release-v72.patch @@ -0,0 +1,142 @@ +From 25062cf34c70012f5d42ce1fef7e2dc129807c10 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Fri, 17 Dec 2021 21:14:37 -0700 +Subject: [PATCH 064/217] ndctl: release v72 + +This release incorporates functionality up to the 5.16 kernel. + +Highlights include a new utility and library for interfacing with the +'CXL' drivers and devices, a policy based configuration mechanism for +ndctl and daxctl, fixes, test updates, and general additions for the +PAPR family of NVDIMMs, more seed device accounting fixes, misc unit +test and documentation updates, and fixes to NVDIMM bus scrubbing. + +Commands: + cxl-cli: new utility providing list, {read,write,zero}-label commands + daxctl-reconfigure-device: new --check-config option + ndctl-monitor: add support for a new unified config file format + ndctl-*-namespace: seed namespace accounting fixes + +Tests: + Fix device-dax mremap() test + Exercise soft_offline_page() corner cases + Fix btt expect table compile warning + monitor.sh: add checking the presence of jq command ahead of time + +APIs: + ndctl_bus_nfit_translate_spa + ndctl_dimm_sizeof_namespace_index + ndctl_get_config_path + ndctl_set_config_path + daxctl_dev_has_online_memory + daxctl_dev_will_auto_online_memory + daxctl_get_config_path + daxctl_set_config_path + cxl_cmd_get_devname + cxl_cmd_get_mbox_status + cxl_cmd_get_out_size + cxl_cmd_health_info_get_dirty_shutdowns + cxl_cmd_health_info_get_ext_corrected_persistent_normal + cxl_cmd_health_info_get_ext_corrected_persistent_warning + cxl_cmd_health_info_get_ext_corrected_volatile_normal + cxl_cmd_health_info_get_ext_corrected_volatile_warning + cxl_cmd_health_info_get_ext_life_used_critical + cxl_cmd_health_info_get_ext_life_used_normal + cxl_cmd_health_info_get_ext_life_used_warning + cxl_cmd_health_info_get_ext_temperature_critical + cxl_cmd_health_info_get_ext_temperature_normal + cxl_cmd_health_info_get_ext_temperature_warning + cxl_cmd_health_info_get_hw_replacement_needed + cxl_cmd_health_info_get_life_used + cxl_cmd_health_info_get_maintenance_needed + cxl_cmd_health_info_get_media_data_loss_imminent + cxl_cmd_health_info_get_media_data_lost + cxl_cmd_health_info_get_media_normal + cxl_cmd_health_info_get_media_not_ready + cxl_cmd_health_info_get_media_persistence_loss_imminent + cxl_cmd_health_info_get_media_persistence_lost + cxl_cmd_health_info_get_media_powerloss_data_loss + cxl_cmd_health_info_get_media_powerloss_persistence_loss + cxl_cmd_health_info_get_media_shutdown_data_loss + cxl_cmd_health_info_get_media_shutdown_persistence_loss + cxl_cmd_health_info_get_performance_degraded + cxl_cmd_health_info_get_pmem_errors + cxl_cmd_health_info_get_temperature + cxl_cmd_health_info_get_volatile_errors + cxl_cmd_identify_get_fw_rev + cxl_cmd_identify_get_label_size + cxl_cmd_identify_get_partition_align + cxl_cmd_new_get_health_info + cxl_cmd_new_identify + cxl_cmd_new_raw + cxl_cmd_new_read_label + cxl_cmd_new_write_label + cxl_cmd_read_label_get_payload + cxl_cmd_ref + cxl_cmd_set_input_payload + cxl_cmd_set_output_payload + cxl_cmd_submit + cxl_cmd_unref + cxl_get_log_priority + cxl_get_private_data + cxl_get_userdata + cxl_memdev_get_ctx + cxl_memdev_get_devname + cxl_memdev_get_firmware_verison + cxl_memdev_get_first + cxl_memdev_get_id + cxl_memdev_get_label_size + cxl_memdev_get_major + cxl_memdev_get_minor + cxl_memdev_get_next + cxl_memdev_get_pmem_size + cxl_memdev_get_ram_size + cxl_memdev_nvdimm_bridge_active + cxl_memdev_read_label + cxl_memdev_write_label + cxl_memdev_zero_label + cxl_new + cxl_ref + cxl_set_log_fn + cxl_set_log_priority + cxl_set_private_data + cxl_set_userdata + cxl_unref +--- + Makefile.am.in | 8 ++++---- + git-version | 2 +- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff -up ndctl-71.1/Makefile.am.in.orig ndctl-71.1/Makefile.am.in +--- ndctl-71.1/Makefile.am.in.orig 2022-10-07 15:20:47.982353042 -0400 ++++ ndctl-71.1/Makefile.am.in 2022-10-07 15:21:02.174401362 -0400 +@@ -36,13 +36,13 @@ SED_PROCESS = \ + -e 's,@includedir\@,$(includedir),g' \ + < $< > $@ || rm $@ + +-LIBNDCTL_CURRENT=25 ++LIBNDCTL_CURRENT=26 + LIBNDCTL_REVISION=1 +-LIBNDCTL_AGE=19 ++LIBNDCTL_AGE=20 + +-LIBDAXCTL_CURRENT=6 ++LIBDAXCTL_CURRENT=7 + LIBDAXCTL_REVISION=0 +-LIBDAXCTL_AGE=5 ++LIBDAXCTL_AGE=6 + + LIBCXL_CURRENT=1 + LIBCXL_REVISION=0 +diff -up ndctl-71.1/git-version.orig ndctl-71.1/git-version +--- ndctl-71.1/git-version.orig 2022-10-07 15:21:02.175401365 -0400 ++++ ndctl-71.1/git-version 2022-10-07 15:21:27.267486796 -0400 +@@ -19,7 +19,7 @@ dirty() { + fi + } + +-DEF_VER=71.1 ++DEF_VER=72 + + LF=' + ' diff --git a/SOURCES/0067-ndctl-add-repology-graphic-to-README.md.patch b/SOURCES/0067-ndctl-add-repology-graphic-to-README.md.patch new file mode 100644 index 0000000..3e66ade --- /dev/null +++ b/SOURCES/0067-ndctl-add-repology-graphic-to-README.md.patch @@ -0,0 +1,32 @@ +From 20a714fe89a7941ef99898821719a46f04f7488b Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Tue, 4 Jan 2022 17:18:23 -0700 +Subject: [PATCH 067/217] ndctl: add repology graphic to README.md + +Add a graphic/badge from repology showing the packaging status of ndctl +with various distros. + +Link: https://lore.kernel.org/r/20220105001823.299797-1-vishal.l.verma@intel.com +Reviewed-by: Ira Weiny +Signed-off-by: Vishal Verma +--- + README.md | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/README.md b/README.md +index 89dfc87..4ab4523 100644 +--- a/README.md ++++ b/README.md +@@ -4,6 +4,9 @@ + Utility library for managing the libnvdimm (non-volatile memory device) + sub-system in the Linux kernel + ++ ++ Packaging status ++ + + Build + ===== +-- +2.27.0 + diff --git a/SOURCES/0068-Documentation-ndctl-fix-self-reference-of-ndctl-disa.patch b/SOURCES/0068-Documentation-ndctl-fix-self-reference-of-ndctl-disa.patch new file mode 100644 index 0000000..20eccea --- /dev/null +++ b/SOURCES/0068-Documentation-ndctl-fix-self-reference-of-ndctl-disa.patch @@ -0,0 +1,29 @@ +From 3f2a2973b23fa86c810aab49eeb8ff5f97a02720 Mon Sep 17 00:00:00 2001 +From: Yasunori Goto +Date: Wed, 3 Feb 2021 19:17:07 +0900 +Subject: [PATCH 068/217] Documentation/ndctl: fix self-reference of ndctl + disable-namespace + +The man manual of ndctl disable-namespace link to itself at See +Also section. It should be enable-namespace instead of it. + +Signed-off-by: Yasunori Goto +Signed-off-by: Vishal Verma +Link: https://github.com/pmem/ndctl/pull/160 +--- + Documentation/ndctl/ndctl-disable-namespace.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Documentation/ndctl/ndctl-disable-namespace.txt b/Documentation/ndctl/ndctl-disable-namespace.txt +index 5d3a8be..187348f 100644 +--- a/Documentation/ndctl/ndctl-disable-namespace.txt ++++ b/Documentation/ndctl/ndctl-disable-namespace.txt +@@ -22,4 +22,4 @@ include::../copyright.txt[] + + SEE ALSO + -------- +-linkndctl:ndctl-disable-namespace[1] ++linkndctl:ndctl-enable-namespace[1] +-- +2.27.0 + diff --git a/SOURCES/0069-ndctl-docs-Clarify-update-firwmware-activation-overf.patch b/SOURCES/0069-ndctl-docs-Clarify-update-firwmware-activation-overf.patch new file mode 100644 index 0000000..a88e013 --- /dev/null +++ b/SOURCES/0069-ndctl-docs-Clarify-update-firwmware-activation-overf.patch @@ -0,0 +1,97 @@ +From 475cb041a97d3c7140efd1b0cda820fb22b69d11 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:31:44 -0800 +Subject: [PATCH 069/217] ndctl/docs: Clarify update-firwmware activation + 'overflow' conditions + +Give examples and remediation for "overflow" events, i.e. where the +estimated time to complete activation exceeds the platform advertised +maximum. When that happens forced activation can lead to undefined results. + +Link: https://lore.kernel.org/r/164141830490.3990253.6263569501446070716.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/ndctl/ndctl-update-firmware.txt | 64 ++++++++++++++++++- + 1 file changed, 63 insertions(+), 1 deletion(-) + +diff --git a/Documentation/ndctl/ndctl-update-firmware.txt b/Documentation/ndctl/ndctl-update-firmware.txt +index 1080d62..6166457 100644 +--- a/Documentation/ndctl/ndctl-update-firmware.txt ++++ b/Documentation/ndctl/ndctl-update-firmware.txt +@@ -58,7 +58,69 @@ include::xable-bus-options.txt[] + Arm a device for firmware activation. This is enabled by default + when a firmware image is specified. Specify --no-arm to disable + this default. Otherwise, without a firmware image, this option can be +- used to manually arm a device for firmware activate. ++ used to manually arm a device for firmware activate. When a ++ device transitions from unarmed to armed the platform recalculates the ++ firmware activation time and compares it against the maximum platform ++ supported time. If the activation time would exceed the platform maximum the ++ arm attempt is aborted: ++ ++[verse] ++ndctl update-firmware --arm --bus=nfit_test.0 all ++ Error: update firmware: nmem4: arm aborted, tripped overflow ++[ ++ { ++ "dev":"nmem1", ++ "id":"cdab-0a-07e0-ffffffff", ++ "handle":"0", ++ "phys_id":"0", ++ "security":"disabled", ++ "firmware":{ ++ "current_version":"0", ++ "can_update":true ++ } ++ }, ++ { ++ "dev":"nmem3", ++ "id":"cdab-0a-07e0-fffeffff", ++ "handle":"0x100", ++ "phys_id":"0x2", ++ "security":"disabled", ++ "firmware":{ ++ "current_version":"0", ++ "can_update":true ++ } ++ }, ++ { ++ "dev":"nmem2", ++ "id":"cdab-0a-07e0-feffffff", ++ "handle":"0x1", ++ "phys_id":"0x1", ++ "security":"disabled", ++ "firmware":{ ++ "current_version":"0", ++ "can_update":true ++ } ++ } ++] ++updated 3 nmems. ++ ++ It is possible, but not recommended, to ignore timeout overflows ++ with the --force option. At any point to view the 'armed' state of the ++ bus do: ++ ++[verse] ++ndctl list -BF -b nfit_test.0 ++[ ++ { ++ "provider":"nfit_test.0", ++ "dev":"ndbus2", ++ "scrub_state":"idle", ++ "firmware":{ ++ "activate_method":"suspend", ++ "activate_state":"overflow" ++ } ++ } ++] + + -D:: + --disarm:: +-- +2.27.0 + diff --git a/SOURCES/0070-ndctl-test-Prepare-for-BLK-aperture-support-removal.patch b/SOURCES/0070-ndctl-test-Prepare-for-BLK-aperture-support-removal.patch new file mode 100644 index 0000000..697f46c --- /dev/null +++ b/SOURCES/0070-ndctl-test-Prepare-for-BLK-aperture-support-removal.patch @@ -0,0 +1,171 @@ +From e423b467e10e3405e6e09260b7669e7022b5f5f7 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:31:50 -0800 +Subject: [PATCH 070/217] ndctl/test: Prepare for BLK-aperture support removal + +The kernel is dropping its support for the BLK-aperture access method. The +primary side effect of this for nfit_test is that NVDIMM namespace labeling +will not be enabled by default. Update the unit tests to initialize the +label index area in this scenario. + +Link: https://lore.kernel.org/r/164141830999.3990253.5021445352398348657.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/core.c | 31 ++++++++++++++++++++++++++++--- + test/libndctl.c | 49 +++++++++++++++++++++++++++++++++++-------------- + 2 files changed, 63 insertions(+), 17 deletions(-) + +diff --git a/test/core.c b/test/core.c +index 2b03aa9..93e1dae 100644 +--- a/test/core.c ++++ b/test/core.c +@@ -261,8 +261,8 @@ retry: + ndctl_bus_foreach(nd_ctx, bus) { + struct ndctl_region *region; + +- if (strncmp(ndctl_bus_get_provider(bus), +- "nfit_test", 9) != 0) ++ if (strcmp(ndctl_bus_get_provider(bus), ++ "nfit_test.0") != 0) + continue; + ndctl_region_foreach(bus, region) + ndctl_region_disable_invalidate(region); +@@ -280,5 +280,30 @@ retry: + NULL, NULL, NULL, NULL); + if (rc) + kmod_unref(*ctx); +- return rc; ++ ++ if (!nd_ctx) ++ return rc; ++ ++ ndctl_bus_foreach (nd_ctx, bus) { ++ struct ndctl_region *region; ++ struct ndctl_dimm *dimm; ++ ++ if (strcmp(ndctl_bus_get_provider(bus), "nfit_test.0") != 0) ++ continue; ++ ++ ndctl_region_foreach (bus, region) ++ ndctl_region_disable_invalidate(region); ++ ++ ndctl_dimm_foreach (bus, dimm) { ++ ndctl_dimm_read_label_index(dimm); ++ ndctl_dimm_init_labels(dimm, NDCTL_NS_VERSION_1_2); ++ ndctl_dimm_disable(dimm); ++ ndctl_dimm_enable(dimm); ++ } ++ ++ ndctl_region_foreach (bus, region) ++ ndctl_region_enable(region); ++ } ++ ++ return 0; + } +diff --git a/test/libndctl.c b/test/libndctl.c +index d9b50f4..c0e4b4c 100644 +--- a/test/libndctl.c ++++ b/test/libndctl.c +@@ -2587,17 +2587,41 @@ static int check_dimms(struct ndctl_bus *bus, struct dimm *dimms, int n, + return 0; + } + +-static void reset_bus(struct ndctl_bus *bus) ++enum dimm_reset { ++ DIMM_INIT, ++ DIMM_ZERO, ++}; ++ ++static int reset_dimms(struct ndctl_bus *bus, enum dimm_reset reset) + { +- struct ndctl_region *region; + struct ndctl_dimm *dimm; ++ int rc = 0; ++ ++ ndctl_dimm_foreach(bus, dimm) { ++ if (reset == DIMM_ZERO) ++ ndctl_dimm_zero_labels(dimm); ++ else { ++ ndctl_dimm_read_label_index(dimm); ++ ndctl_dimm_init_labels(dimm, NDCTL_NS_VERSION_1_2); ++ } ++ ndctl_dimm_disable(dimm); ++ rc = ndctl_dimm_enable(dimm); ++ if (rc) ++ break; ++ } ++ ++ return rc; ++} ++ ++static void reset_bus(struct ndctl_bus *bus, enum dimm_reset reset) ++{ ++ struct ndctl_region *region; + + /* disable all regions so that set_config_data commands are permitted */ + ndctl_region_foreach(bus, region) + ndctl_region_disable_invalidate(region); + +- ndctl_dimm_foreach(bus, dimm) +- ndctl_dimm_zero_labels(dimm); ++ reset_dimms(bus, reset); + + /* set regions back to their default state */ + ndctl_region_foreach(bus, region) +@@ -2608,7 +2632,6 @@ static int do_test0(struct ndctl_ctx *ctx, struct ndctl_test *test) + { + struct ndctl_bus *bus = ndctl_bus_get_by_provider(ctx, NFIT_PROVIDER0); + struct ndctl_region *region; +- struct ndctl_dimm *dimm; + int rc; + + if (!bus) +@@ -2625,13 +2648,10 @@ static int do_test0(struct ndctl_ctx *ctx, struct ndctl_test *test) + if (rc) + return rc; + +- ndctl_dimm_foreach(bus, dimm) { +- rc = ndctl_dimm_zero_labels(dimm); +- if (rc < 0) { +- fprintf(stderr, "failed to zero %s\n", +- ndctl_dimm_get_devname(dimm)); +- return rc; +- } ++ rc = reset_dimms(bus, DIMM_INIT); ++ if (rc < 0) { ++ fprintf(stderr, "failed to reset dimms\n"); ++ return rc; + } + + /* +@@ -2649,14 +2669,14 @@ static int do_test0(struct ndctl_ctx *ctx, struct ndctl_test *test) + rc = check_regions(bus, regions0, ARRAY_SIZE(regions0), DAX); + if (rc) + return rc; +- reset_bus(bus); ++ reset_bus(bus, DIMM_INIT); + } + + if (ndctl_test_attempt(test, KERNEL_VERSION(4, 8, 0))) { + rc = check_regions(bus, regions0, ARRAY_SIZE(regions0), PFN); + if (rc) + return rc; +- reset_bus(bus); ++ reset_bus(bus, DIMM_INIT); + } + + return check_regions(bus, regions0, ARRAY_SIZE(regions0), BTT); +@@ -2671,6 +2691,7 @@ static int do_test1(struct ndctl_ctx *ctx, struct ndctl_test *test) + return -ENXIO; + + ndctl_bus_wait_probe(bus); ++ reset_bus(bus, DIMM_ZERO); + + /* + * Starting with v4.10 the dimm on nfit_test.1 gets a unique +-- +2.27.0 + diff --git a/SOURCES/0071-ndctl-test-Move-reset-to-function-in-common.patch b/SOURCES/0071-ndctl-test-Move-reset-to-function-in-common.patch new file mode 100644 index 0000000..e61cfd2 --- /dev/null +++ b/SOURCES/0071-ndctl-test-Move-reset-to-function-in-common.patch @@ -0,0 +1,428 @@ +From 6538529be5738f06543a0d7178f97e0b0e6b63c2 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:31:55 -0800 +Subject: [PATCH 071/217] ndctl/test: Move 'reset()' to function in 'common' + +When BLK mode is removed, tests that expect the nfit_test region to allow +pmem namespace creation will need to 'init' rather than 'zero' labels. In +preparation, take the time opportunity to move reset() to a common +function. So that 'ndctl zero-labels' can be replaced with 'ndctl +init-labels' in one central location. + +Link: https://lore.kernel.org/r/164141831509.3990253.14783946910211635678.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/blk-exhaust.sh | 4 +--- + test/btt-check.sh | 7 ------- + test/btt-errors.sh | 12 +++--------- + test/btt-pad-compat.sh | 7 ------- + test/clear.sh | 4 +--- + test/common | 15 +++++++++++++++ + test/create.sh | 4 +--- + test/daxctl-create.sh | 4 ++-- + test/daxdev-errors.sh | 4 +--- + test/firmware-update.sh | 8 +++----- + test/inject-error.sh | 7 ------- + test/max_available_extent_ns.sh | 9 +-------- + test/monitor.sh | 11 ++--------- + test/multi-dax.sh | 4 +--- + test/pfn-meta-errors.sh | 4 +--- + test/pmem-errors.sh | 4 +--- + test/rescan-partitions.sh | 7 ------- + test/sector-mode.sh | 9 ++------- + test/track-uuid.sh | 4 +--- + 19 files changed, 36 insertions(+), 92 deletions(-) + +diff --git a/test/blk-exhaust.sh b/test/blk-exhaust.sh +index 09c4aae..b6d3808 100755 +--- a/test/blk-exhaust.sh ++++ b/test/blk-exhaust.sh +@@ -14,9 +14,7 @@ trap 'err $LINENO' ERR + + # setup (reset nfit_test dimms) + modprobe nfit_test +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all ++reset + + # if the kernel accounting is correct we should be able to create two + # pmem and two blk namespaces on nfit_test.0 +diff --git a/test/btt-check.sh b/test/btt-check.sh +index 8e0b489..65b5c58 100755 +--- a/test/btt-check.sh ++++ b/test/btt-check.sh +@@ -39,13 +39,6 @@ create() + [ $size -gt 0 ] || err "$LINENO" + } + +-reset() +-{ +- $NDCTL disable-region -b $NFIT_TEST_BUS0 all +- $NDCTL zero-labels -b $NFIT_TEST_BUS0 all +- $NDCTL enable-region -b $NFIT_TEST_BUS0 all +-} +- + # re-enable the BTT namespace, and do IO to it in an attempt to + # verify it still comes up ok, and functions as expected + post_repair_test() +diff --git a/test/btt-errors.sh b/test/btt-errors.sh +index 4e59f57..5a20d26 100755 +--- a/test/btt-errors.sh ++++ b/test/btt-errors.sh +@@ -45,9 +45,7 @@ trap 'err $LINENO cleanup' ERR + + # setup (reset nfit_test dimms) + modprobe nfit_test +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all ++reset + + rc=1 + +@@ -126,9 +124,7 @@ dd if=$MNT/$FILE of=/dev/null iflag=direct bs=4096 count=1 + + # reset everything to get a clean log + if grep -q "$MNT" /proc/mounts; then umount $MNT; fi +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all ++reset + dev="x" + json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem -m sector) + eval "$(echo "$json" | json2var)" +@@ -148,9 +144,7 @@ force_raw 0 + dd if=/dev/$blockdev of=/dev/null iflag=direct bs=4096 count=1 && err $LINENO || true + + # done, exit +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all ++reset + cleanup + _cleanup + exit 0 +diff --git a/test/btt-pad-compat.sh b/test/btt-pad-compat.sh +index bf1ea54..be538b7 100755 +--- a/test/btt-pad-compat.sh ++++ b/test/btt-pad-compat.sh +@@ -37,13 +37,6 @@ create() + fi + } + +-reset() +-{ +- $NDCTL disable-region -b $NFIT_TEST_BUS0 all +- $NDCTL zero-labels -b $NFIT_TEST_BUS0 all +- $NDCTL enable-region -b $NFIT_TEST_BUS0 all +-} +- + verify_idx() + { + idx0="$1" +diff --git a/test/clear.sh b/test/clear.sh +index fb9d52c..c4d02d5 100755 +--- a/test/clear.sh ++++ b/test/clear.sh +@@ -14,9 +14,7 @@ trap 'err $LINENO' ERR + + # setup (reset nfit_test dimms) + modprobe nfit_test +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all ++reset + + rc=1 + +diff --git a/test/common b/test/common +index 6bcefca..3c54d63 100644 +--- a/test/common ++++ b/test/common +@@ -46,6 +46,21 @@ err() + exit $rc + } + ++reset() ++{ ++ $NDCTL disable-region -b $NFIT_TEST_BUS0 all ++ $NDCTL zero-labels -b $NFIT_TEST_BUS0 all ++ $NDCTL enable-region -b $NFIT_TEST_BUS0 all ++} ++ ++reset1() ++{ ++ $NDCTL disable-region -b $NFIT_TEST_BUS1 all ++ $NDCTL zero-labels -b $NFIT_TEST_BUS1 all ++ $NDCTL enable-region -b $NFIT_TEST_BUS1 all ++} ++ ++ + # check_min_kver + # $1: Supported kernel version. format: X.Y + # +diff --git a/test/create.sh b/test/create.sh +index b0fd99f..e9baaa0 100755 +--- a/test/create.sh ++++ b/test/create.sh +@@ -15,9 +15,7 @@ trap 'err $LINENO' ERR + + # setup (reset nfit_test dimms) + modprobe nfit_test +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all ++reset + + rc=1 + +diff --git a/test/daxctl-create.sh b/test/daxctl-create.sh +index 198779a..d319a39 100755 +--- a/test/daxctl-create.sh ++++ b/test/daxctl-create.sh +@@ -10,7 +10,7 @@ trap 'cleanup $LINENO' ERR + cleanup() + { + printf "Error at line %d\n" "$1" +- [[ $testdev ]] && reset ++ [[ $testdev ]] && reset_dax + exit $rc + } + +@@ -70,7 +70,7 @@ reset_dev() + "$DAXCTL" enable-device "$testdev" + } + +-reset() ++reset_dax() + { + test -n "$testdev" + +diff --git a/test/daxdev-errors.sh b/test/daxdev-errors.sh +index 9547d78..e13453d 100755 +--- a/test/daxdev-errors.sh ++++ b/test/daxdev-errors.sh +@@ -15,9 +15,7 @@ trap 'err $LINENO' ERR + + # setup (reset nfit_test dimms) + modprobe nfit_test +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all ++reset + + rc=1 + +diff --git a/test/firmware-update.sh b/test/firmware-update.sh +index 8cc9c41..93ce166 100755 +--- a/test/firmware-update.sh ++++ b/test/firmware-update.sh +@@ -10,11 +10,9 @@ image="update-fw.img" + + trap 'err $LINENO' ERR + +-reset() ++fwupd_reset() + { +- $NDCTL disable-region -b $NFIT_TEST_BUS0 all +- $NDCTL zero-labels -b $NFIT_TEST_BUS0 all +- $NDCTL enable-region -b $NFIT_TEST_BUS0 all ++ reset + if [ -f $image ]; then + rm -f $image + fi +@@ -73,7 +71,7 @@ do_tests() + check_min_kver "4.16" || do_skip "may lack firmware update test handling" + + modprobe nfit_test +-reset ++fwupd_reset + detect + rc=1 + do_tests +diff --git a/test/inject-error.sh b/test/inject-error.sh +index 7d0b826..fd823b6 100755 +--- a/test/inject-error.sh ++++ b/test/inject-error.sh +@@ -37,13 +37,6 @@ create() + [ $size -gt 0 ] || err "$LINENO" + } + +-reset() +-{ +- $NDCTL disable-region -b $NFIT_TEST_BUS0 all +- $NDCTL zero-labels -b $NFIT_TEST_BUS0 all +- $NDCTL enable-region -b $NFIT_TEST_BUS0 all +-} +- + check_status() + { + local sector="$1" +diff --git a/test/max_available_extent_ns.sh b/test/max_available_extent_ns.sh +index 343f3c9..47a921f 100755 +--- a/test/max_available_extent_ns.sh ++++ b/test/max_available_extent_ns.sh +@@ -11,13 +11,6 @@ trap 'err $LINENO' ERR + check_min_kver "4.19" || do_skip "kernel $KVER may not support max_available_size" + check_prereq "jq" + +-init() +-{ +- $NDCTL disable-region -b $NFIT_TEST_BUS0 all +- $NDCTL zero-labels -b $NFIT_TEST_BUS0 all +- $NDCTL enable-region -b $NFIT_TEST_BUS0 all +-} +- + do_test() + { + region=$($NDCTL list -b $NFIT_TEST_BUS0 -R -t pmem | jq -r 'sort_by(-.size) | .[].dev' | head -1) +@@ -40,7 +33,7 @@ do_test() + + modprobe nfit_test + rc=1 +-init ++reset + do_test + _cleanup + exit 0 +diff --git a/test/monitor.sh b/test/monitor.sh +index c015c11..6aa4196 100755 +--- a/test/monitor.sh ++++ b/test/monitor.sh +@@ -19,13 +19,6 @@ trap 'err $LINENO' ERR + + check_min_kver "4.15" || do_skip "kernel $KVER may not support monitor service" + +-init() +-{ +- $NDCTL disable-region -b $NFIT_TEST_BUS0 all +- $NDCTL zero-labels -b $NFIT_TEST_BUS0 all +- $NDCTL enable-region -b $NFIT_TEST_BUS0 all +-} +- + start_monitor() + { + logfile=$(mktemp) +@@ -112,7 +105,7 @@ test_filter_region() + + test_filter_namespace() + { +- init ++ reset + monitor_namespace=$($NDCTL create-namespace -b $smart_supported_bus | jq -r .dev) + monitor_dimms=$(get_monitor_dimm "-n $monitor_namespace") + start_monitor "-n $monitor_namespace" +@@ -170,7 +163,7 @@ do_tests() + + modprobe nfit_test + rc=1 +-init ++reset + set_smart_supported_bus + do_tests + _cleanup +diff --git a/test/multi-dax.sh b/test/multi-dax.sh +index b343a38..04070ad 100755 +--- a/test/multi-dax.sh ++++ b/test/multi-dax.sh +@@ -17,9 +17,7 @@ ALIGN_SIZE=`getconf PAGESIZE` + + # setup (reset nfit_test dimms) + modprobe nfit_test +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all ++reset + rc=1 + + query=". | sort_by(.available_size) | reverse | .[0].dev" +diff --git a/test/pfn-meta-errors.sh b/test/pfn-meta-errors.sh +index 0ade2e5..6314897 100755 +--- a/test/pfn-meta-errors.sh ++++ b/test/pfn-meta-errors.sh +@@ -29,9 +29,7 @@ trap 'err $LINENO' ERR + + # setup (reset nfit_test dimms) + modprobe nfit_test +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all ++reset + + rc=1 + +diff --git a/test/pmem-errors.sh b/test/pmem-errors.sh +index 4225c3b..2065780 100755 +--- a/test/pmem-errors.sh ++++ b/test/pmem-errors.sh +@@ -28,9 +28,7 @@ trap 'err $LINENO cleanup' ERR + + # setup (reset nfit_test dimms) + modprobe nfit_test +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all ++reset + + rc=1 + +diff --git a/test/rescan-partitions.sh b/test/rescan-partitions.sh +index 1686de3..51bbd73 100755 +--- a/test/rescan-partitions.sh ++++ b/test/rescan-partitions.sh +@@ -25,13 +25,6 @@ check_min_kver "4.16" || do_skip "may not contain fixes for partition rescanning + check_prereq "parted" + check_prereq "blockdev" + +-reset() +-{ +- $NDCTL disable-region -b $NFIT_TEST_BUS0 all +- $NDCTL zero-labels -b $NFIT_TEST_BUS0 all +- $NDCTL enable-region -b $NFIT_TEST_BUS0 all +-} +- + test_mode() + { + local mode="$1" +diff --git a/test/sector-mode.sh b/test/sector-mode.sh +index 7a2faea..439ef33 100755 +--- a/test/sector-mode.sh ++++ b/test/sector-mode.sh +@@ -15,13 +15,8 @@ ALIGN_SIZE=`getconf PAGESIZE` + + # setup (reset nfit_test dimms) + modprobe nfit_test +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all +- +-$NDCTL disable-region -b $NFIT_TEST_BUS1 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS1 all +-$NDCTL enable-region -b $NFIT_TEST_BUS1 all ++reset ++reset1 + + rc=1 + query=". | sort_by(.size) | reverse | .[0].dev" +diff --git a/test/track-uuid.sh b/test/track-uuid.sh +index be3cf9c..3bacd2c 100755 +--- a/test/track-uuid.sh ++++ b/test/track-uuid.sh +@@ -12,9 +12,7 @@ trap 'err $LINENO' ERR + + # setup (reset nfit_test dimms) + modprobe nfit_test +-$NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all +-$NDCTL enable-region -b $NFIT_TEST_BUS0 all ++reset + + rc=1 + +-- +2.27.0 + diff --git a/SOURCES/0072-ndctl-test-Initialize-the-label-area-by-default.patch b/SOURCES/0072-ndctl-test-Initialize-the-label-area-by-default.patch new file mode 100644 index 0000000..ba3de4d --- /dev/null +++ b/SOURCES/0072-ndctl-test-Initialize-the-label-area-by-default.patch @@ -0,0 +1,104 @@ +From fe37c85f1ffb0b2d04ef60e8ece6a9a44a145cc5 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:00 -0800 +Subject: [PATCH 072/217] ndctl/test: Initialize the label area by default + +The removal of BLK-mode support causes nfit_test regions to not be +'aliased' by default, which means that the only way to enable labels is to +initialize the namespace label index block. In support of that the common +'reset()' helper is updated to initialize v1.1 labels instead of zero them. +Additionally, it highlighted that some btt tests have silent assumptions of +v1.1 vs v1.2 label support. Add a 'resetV()' alternative to the common +'reset()' function that initializes the label area to v1.2. + +Link: https://lore.kernel.org/r/164141832017.3990253.10383328274835531066.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/btt-errors.sh | 4 ++-- + test/btt-pad-compat.sh | 2 +- + test/common | 11 +++++++++-- + test/label-compat.sh | 2 +- + 4 files changed, 13 insertions(+), 6 deletions(-) + +diff --git a/test/btt-errors.sh b/test/btt-errors.sh +index 5a20d26..6e69178 100755 +--- a/test/btt-errors.sh ++++ b/test/btt-errors.sh +@@ -45,7 +45,7 @@ trap 'err $LINENO cleanup' ERR + + # setup (reset nfit_test dimms) + modprobe nfit_test +-reset ++resetV + + rc=1 + +@@ -124,7 +124,7 @@ dd if=$MNT/$FILE of=/dev/null iflag=direct bs=4096 count=1 + + # reset everything to get a clean log + if grep -q "$MNT" /proc/mounts; then umount $MNT; fi +-reset ++resetV + dev="x" + json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem -m sector) + eval "$(echo "$json" | json2var)" +diff --git a/test/btt-pad-compat.sh b/test/btt-pad-compat.sh +index be538b7..005316a 100755 +--- a/test/btt-pad-compat.sh ++++ b/test/btt-pad-compat.sh +@@ -148,7 +148,7 @@ do_tests() + verify_idx 0 1 + + # do the same with an old format namespace +- reset ++ resetV + create_oldfmt_ns + verify_idx 0 2 + +diff --git a/test/common b/test/common +index 3c54d63..b6d4712 100644 +--- a/test/common ++++ b/test/common +@@ -49,14 +49,21 @@ err() + reset() + { + $NDCTL disable-region -b $NFIT_TEST_BUS0 all +- $NDCTL zero-labels -b $NFIT_TEST_BUS0 all ++ $NDCTL init-labels -f -b $NFIT_TEST_BUS0 all ++ $NDCTL enable-region -b $NFIT_TEST_BUS0 all ++} ++ ++resetV() ++{ ++ $NDCTL disable-region -b $NFIT_TEST_BUS0 all ++ $NDCTL init-labels -f -V 1.2 -b $NFIT_TEST_BUS0 all + $NDCTL enable-region -b $NFIT_TEST_BUS0 all + } + + reset1() + { + $NDCTL disable-region -b $NFIT_TEST_BUS1 all +- $NDCTL zero-labels -b $NFIT_TEST_BUS1 all ++ $NDCTL init-labels -f -b $NFIT_TEST_BUS1 all + $NDCTL enable-region -b $NFIT_TEST_BUS1 all + } + +diff --git a/test/label-compat.sh b/test/label-compat.sh +index 8ab2858..7ae4d5e 100755 +--- a/test/label-compat.sh ++++ b/test/label-compat.sh +@@ -17,7 +17,7 @@ trap 'err $LINENO' ERR + # setup (reset nfit_test dimms) + modprobe nfit_test + $NDCTL disable-region -b $NFIT_TEST_BUS0 all +-$NDCTL zero-labels -b $NFIT_TEST_BUS0 all ++$NDCTL init-labels -f -b $NFIT_TEST_BUS0 all + + # grab the largest pmem region on -b $NFIT_TEST_BUS0 + query=". | sort_by(.available_size) | reverse | .[0].dev" +-- +2.27.0 + diff --git a/SOURCES/0073-ndctl-test-Skip-BLK-flags-checks.patch b/SOURCES/0073-ndctl-test-Skip-BLK-flags-checks.patch new file mode 100644 index 0000000..6933080 --- /dev/null +++ b/SOURCES/0073-ndctl-test-Skip-BLK-flags-checks.patch @@ -0,0 +1,43 @@ +From 756a6598a0fa6cebdd0e98564af089ca6b463fb1 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:05 -0800 +Subject: [PATCH 073/217] ndctl/test: Skip BLK flags checks + +With the removal of BLK-mode support, test/libndctl will fail to detect the +JEDEC format on the nfit_test bus. Report + skip that check rather than +fail the test when that happens. + +Link: https://lore.kernel.org/r/164141832529.3990253.16538298357542644310.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/libndctl.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/test/libndctl.c b/test/libndctl.c +index c0e4b4c..1e97926 100644 +--- a/test/libndctl.c ++++ b/test/libndctl.c +@@ -2535,7 +2535,7 @@ static int check_dimms(struct ndctl_bus *bus, struct dimm *dimms, int n, + fprintf(stderr, "dimm%d expected formats: %d got: %d\n", + i, dimms[i].formats, + ndctl_dimm_get_formats(dimm)); +- return -ENXIO; ++ fprintf(stderr, "continuing...\n"); + } + for (j = 0; j < dimms[i].formats; j++) { + if (ndctl_dimm_get_formatN(dimm, j) != dimms[i].format[j]) { +@@ -2543,7 +2543,7 @@ static int check_dimms(struct ndctl_bus *bus, struct dimm *dimms, int n, + "dimm%d expected format[%d]: %d got: %d\n", + i, j, dimms[i].format[j], + ndctl_dimm_get_formatN(dimm, j)); +- return -ENXIO; ++ fprintf(stderr, "continuing...\n"); + } + } + } +-- +2.27.0 + diff --git a/SOURCES/0074-ndctl-test-Move-sector-mode-to-a-different-region.patch b/SOURCES/0074-ndctl-test-Move-sector-mode-to-a-different-region.patch new file mode 100644 index 0000000..d69249e --- /dev/null +++ b/SOURCES/0074-ndctl-test-Move-sector-mode-to-a-different-region.patch @@ -0,0 +1,41 @@ +From b787320498508192f1e04ac38d39da4eb3ca26e9 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:10 -0800 +Subject: [PATCH 074/217] ndctl/test: Move sector-mode to a different region + +Previously the largest region on the nfit_test.1 bus belonged to a BLK-mode +region. With the removal of BLK-mode support update the test to instead +find a suitable PMEM region to perform the checkout. + +Link: https://lore.kernel.org/r/164141833068.3990253.15694496866707006837.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/sector-mode.sh | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/test/sector-mode.sh b/test/sector-mode.sh +index 439ef33..f70b0f1 100755 +--- a/test/sector-mode.sh ++++ b/test/sector-mode.sh +@@ -19,11 +19,11 @@ reset + reset1 + + rc=1 +-query=". | sort_by(.size) | reverse | .[0].dev" +-NAMESPACE=$($NDCTL list -b $NFIT_TEST_BUS1 -N | jq -r "$query") +-REGION=$($NDCTL list -R --namespace=$NAMESPACE | jq -r "(.[]) | .dev") ++query=". | sort_by(.available_size) | reverse | .[0].dev" ++REGION=$($NDCTL list -R -b $NFIT_TEST_BUS1 | jq -r "$query") + echo 0 > /sys/bus/nd/devices/$REGION/read_only +-$NDCTL create-namespace --no-autolabel -e $NAMESPACE -m sector -f -l 4K ++echo $ALIGN_SIZE > /sys/bus/nd/devices/$REGION/align ++NAMESPACE=$($NDCTL create-namespace --no-autolabel -r $REGION -m sector -f -l 4K | jq -r ".dev") + $NDCTL create-namespace --no-autolabel -e $NAMESPACE -m dax -f -a $ALIGN_SIZE + $NDCTL create-namespace --no-autolabel -e $NAMESPACE -m sector -f -l 4K + +-- +2.27.0 + diff --git a/SOURCES/0075-ndctl-Deprecate-BLK-aperture-support.patch b/SOURCES/0075-ndctl-Deprecate-BLK-aperture-support.patch new file mode 100644 index 0000000..556f887 --- /dev/null +++ b/SOURCES/0075-ndctl-Deprecate-BLK-aperture-support.patch @@ -0,0 +1,1966 @@ +From 2e05cd6b4aee656f69e11cf8b5e280dd47a14b1b Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:15 -0800 +Subject: [PATCH 075/217] ndctl: Deprecate BLK aperture support + +The kernel is dropping its BLK aperture support, so deprecate the same in +ndctl. The options will still be supported, and the library calls will not +be deleted in case code needs them to compile. However the documentation +and the tests for BLK mode can be removed. + +Link: https://lore.kernel.org/r/164141833579.3990253.17885822648406789915.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .gitignore | 4 - + Documentation/ndctl/labels-description.txt | 5 +- + .../ndctl/ndctl-create-namespace.txt | 29 +- + Documentation/ndctl/ndctl-init-labels.txt | 7 +- + Documentation/ndctl/ndctl-list.txt | 4 - + Documentation/ndctl/region-description.txt | 10 +- + README.md | 1 - + contrib/nfit_test_depmod.conf | 1 - + ndctl/Makefile.am | 6 +- + ndctl/bat.c | 5 - + ndctl/test.c | 11 - + test.h | 3 - + test/Makefile.am | 31 -- + test/blk-exhaust.sh | 30 -- + test/blk_namespaces.c | 357 ------------------ + test/core.c | 1 - + test/create.sh | 13 - + test/dpa-alloc.c | 326 ---------------- + test/libndctl.c | 198 ++-------- + test/multi-pmem.c | 285 -------------- + test/parent-uuid.c | 254 ------------- + 21 files changed, 51 insertions(+), 1530 deletions(-) + delete mode 100755 test/blk-exhaust.sh + delete mode 100644 test/blk_namespaces.c + delete mode 100644 test/dpa-alloc.c + delete mode 100644 test/multi-pmem.c + delete mode 100644 test/parent-uuid.c + +diff --git a/.gitignore b/.gitignore +index 6468c7a..6b19d90 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -42,20 +42,16 @@ cscope*.out + tags + test/*.log + test/*.trs +-test/blk-ns + test/dax-dev + test/dax-errors + test/dax-pmd + test/daxdev-errors + test/device-dax +-test/dpa-alloc + test/dsm-fail + test/hugetlb + test/image + test/libndctl + test/mmap +-test/multi-pmem +-test/parent-uuid + test/pmem-ns + test/smart-listen + test/smart-notify +diff --git a/Documentation/ndctl/labels-description.txt b/Documentation/ndctl/labels-description.txt +index 6244a49..a246edc 100644 +--- a/Documentation/ndctl/labels-description.txt ++++ b/Documentation/ndctl/labels-description.txt +@@ -3,6 +3,5 @@ + DESCRIPTION + ----------- + The namespace label area is a small persistent partition of capacity +-available on some NVDIMM devices. The label area is used to resolve +-aliasing between 'pmem' and 'blk' capacity by delineating namespace +-boundaries. ++available on some NVDIMM devices. The label area is used to provision ++one, or more, namespaces from regions. +diff --git a/Documentation/ndctl/ndctl-create-namespace.txt b/Documentation/ndctl/ndctl-create-namespace.txt +index 92a89dd..afb085e 100644 +--- a/Documentation/ndctl/ndctl-create-namespace.txt ++++ b/Documentation/ndctl/ndctl-create-namespace.txt +@@ -28,27 +28,17 @@ ndctl create-namespace -f -e namespace0.0 --mode=sector + + OPTIONS + ------- +--t:: +---type=:: +- Create a 'pmem' or 'blk' namespace (subject to available +- capacity). A pmem namespace supports the dax (direct access) +- capability to linkndctl:mmap[2] persistent memory directly into +- a process address space. A blk namespace access persistent +- memory through a block-window-aperture. Compared to pmem it +- supports a traditional storage error model (EIO on error rather +- than a cpu exception on a bad memory access), but it does not +- support dax. +- + -m:: + --mode=:: + - "raw": expose the namespace capacity directly with +- limitations. Neither a raw pmem namepace nor raw blk +- namespace support sector atomicity by default (see "sector" +- mode below). A raw pmem namespace may have limited to no dax +- support depending the kernel. In other words operations like +- direct-I/O targeting a dax buffer may fail for a pmem +- namespace in raw mode or indirect through a page-cache buffer. +- See "fsdax" and "devdax" mode for dax operation. ++ limitations. A raw pmem namepace namespace does not support ++ sector atomicity (see "sector" mode below). A raw pmem ++ namespace may have limited to no dax support depending the ++ kernel. In other words operations like direct-I/O targeting a ++ dax buffer may fail for a pmem namespace in raw mode or ++ indirect through a page-cache buffer. See "fsdax" and ++ "devdax" mode for dax operation. ++ + + - "sector": persistent memory, given that it is byte + addressable, does not support sector atomicity. The +@@ -206,8 +196,7 @@ OPTIONS + * NVDIMM does not support labels + + * The NVDIMM supports labels, but the Label Index Block (see +- UEFI 2.7) is not present and there is no capacity aliasing +- between 'blk' and 'pmem' regions. ++ UEFI 2.7) is not present. + + - In the latter case the configuration can be upgraded to + labelled operation by writing an index block on all DIMMs in a +diff --git a/Documentation/ndctl/ndctl-init-labels.txt b/Documentation/ndctl/ndctl-init-labels.txt +index 733ef0e..73685b3 100644 +--- a/Documentation/ndctl/ndctl-init-labels.txt ++++ b/Documentation/ndctl/ndctl-init-labels.txt +@@ -13,10 +13,9 @@ SYNOPSIS + 'ndctl init-labels' [..] [] + + include::labels-description.txt[] +-By default, and in kernels prior to v4.10, the kernel only honors labels +-when a DIMM aliases PMEM and BLK capacity. Starting with v4.10 the +-kernel will honor labels for sub-dividing PMEM if all the DIMMs in an +-interleave set / region have a valid namespace index block. ++Starting with v4.10 the kernel will honor labels for sub-dividing PMEM ++if all the DIMMs in an interleave set / region have a valid namespace ++index block. + + This command can be used to initialize the namespace index block if it + is missing or reinitialize it if it is damaged. Note that +diff --git a/Documentation/ndctl/ndctl-list.txt b/Documentation/ndctl/ndctl-list.txt +index b8d517d..2922f10 100644 +--- a/Documentation/ndctl/ndctl-list.txt ++++ b/Documentation/ndctl/ndctl-list.txt +@@ -82,10 +82,6 @@ include::xable-bus-options.txt[] + 'X.Y'. Limit the namespace list to the single identified device + if present. + +--t:: +---type=:: +- Filter listing by region type ('pmem' or 'blk') +- + -m:: + --mode=:: + Filter listing by the mode ('raw', 'fsdax', 'sector' or 'devdax') +diff --git a/Documentation/ndctl/region-description.txt b/Documentation/ndctl/region-description.txt +index c14416a..ce268a0 100644 +--- a/Documentation/ndctl/region-description.txt ++++ b/Documentation/ndctl/region-description.txt +@@ -2,9 +2,7 @@ + + DESCRIPTION + ----------- +-A generic REGION device is registered for each PMEM range or +-BLK-aperture set. LIBNVDIMM provides a built-in driver for these REGION +-devices. This driver is responsible for reconciling the aliased DPA +-mappings across all regions, parsing the LABEL, if present, and then +-emitting NAMESPACE devices with the resolved/exclusive DPA-boundaries +-for the nd_pmem or nd_blk device driver to consume. ++A generic REGION device is registered for each PMEM range / ++interleave-set. LIBNVDIMM provides a built-in driver for these REGION ++devices. This driver is responsible for parsing namespace labels and ++instantiating PMEM namespaces for each coherent set of labels. +diff --git a/README.md b/README.md +index 89dfc87..6f36a6d 100644 +--- a/README.md ++++ b/README.md +@@ -110,7 +110,6 @@ override dax_pmem * extra + override dax_pmem_core * extra + override dax_pmem_compat * extra + override libnvdimm * extra +-override nd_blk * extra + override nd_btt * extra + override nd_e820 * extra + override nd_pmem * extra +diff --git a/contrib/nfit_test_depmod.conf b/contrib/nfit_test_depmod.conf +index 9f8498e..0e0574e 100644 +--- a/contrib/nfit_test_depmod.conf ++++ b/contrib/nfit_test_depmod.conf +@@ -5,7 +5,6 @@ override dax_pmem * extra + override dax_pmem_core * extra + override dax_pmem_compat * extra + override libnvdimm * extra +-override nd_blk * extra + override nd_btt * extra + override nd_e820 * extra + override nd_pmem * extra +diff --git a/ndctl/Makefile.am b/ndctl/Makefile.am +index 4e99510..93b682e 100644 +--- a/ndctl/Makefile.am ++++ b/ndctl/Makefile.am +@@ -45,8 +45,7 @@ endif + EXTRA_DIST += keys.readme monitor.conf ndctl-monitor.service + + if ENABLE_DESTRUCTIVE +-ndctl_SOURCES += ../test/blk_namespaces.c \ +- ../test/pmem_namespaces.c ++ndctl_SOURCES += ../test/pmem_namespaces.c + ndctl_SOURCES += bat.c + endif + +@@ -67,9 +66,6 @@ if ENABLE_TEST + ndctl_SOURCES += ../test/libndctl.c \ + ../test/dsm-fail.c \ + ../util/sysfs.c \ +- ../test/dpa-alloc.c \ +- ../test/parent-uuid.c \ +- ../test/multi-pmem.c \ + ../test/core.c \ + test.c + endif +diff --git a/ndctl/bat.c b/ndctl/bat.c +index ef00a3b..13e964d 100644 +--- a/ndctl/bat.c ++++ b/ndctl/bat.c +@@ -41,11 +41,6 @@ int cmd_bat(int argc, const char **argv, struct ndctl_ctx *ctx) + return EXIT_FAILURE; + } + +- rc = test_blk_namespaces(loglevel, test, ctx); +- fprintf(stderr, "test_blk_namespaces: %s\n", rc ? "FAIL" : "PASS"); +- if (rc && rc != 77) +- return rc; +- + rc = test_pmem_namespaces(loglevel, test, ctx); + fprintf(stderr, "test_pmem_namespaces: %s\n", rc ? "FAIL" : "PASS"); + return ndctl_test_result(test, rc); +diff --git a/ndctl/test.c b/ndctl/test.c +index 6a05d8d..a0f5bc9 100644 +--- a/ndctl/test.c ++++ b/ndctl/test.c +@@ -58,16 +58,5 @@ int cmd_test(int argc, const char **argv, struct ndctl_ctx *ctx) + if (rc && rc != 77) + return rc; + +- rc = test_dpa_alloc(loglevel, test, ctx); +- fprintf(stderr, "test-dpa-alloc: %s\n", result(rc)); +- if (rc && rc != 77) +- return rc; +- +- rc = test_parent_uuid(loglevel, test, ctx); +- fprintf(stderr, "test-parent-uuid: %s\n", result(rc)); +- +- rc = test_multi_pmem(loglevel, test, ctx); +- fprintf(stderr, "test-multi-pmem: %s\n", result(rc)); +- + return ndctl_test_result(test, rc); + } +diff --git a/test.h b/test.h +index 7de13fe..b2267e6 100644 +--- a/test.h ++++ b/test.h +@@ -26,7 +26,6 @@ int ndctl_test_init(struct kmod_ctx **ctx, struct kmod_module **mod, + + struct ndctl_ctx; + int test_parent_uuid(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); +-int test_multi_pmem(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); + int test_dax_directio(int dax_fd, unsigned long align, void *dax_addr, off_t offset); + int test_dax_remap(struct ndctl_test *test, int dax_fd, unsigned long align, void *dax_addr, + off_t offset, bool fsdax); +@@ -40,9 +39,7 @@ static inline int test_dax_poison(struct ndctl_test *test, int dax_fd, + return 0; + } + #endif +-int test_dpa_alloc(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); + int test_dsm_fail(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); + int test_libndctl(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); +-int test_blk_namespaces(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); + int test_pmem_namespaces(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx); + #endif /* __TEST_H__ */ +diff --git a/test/Makefile.am b/test/Makefile.am +index c5b8764..a5a54df 100644 +--- a/test/Makefile.am ++++ b/test/Makefile.am +@@ -3,9 +3,6 @@ include $(top_srcdir)/Makefile.am.in + TESTS =\ + libndctl \ + dsm-fail \ +- dpa-alloc \ +- parent-uuid \ +- multi-pmem \ + create.sh \ + clear.sh \ + pmem-errors.sh \ +@@ -13,7 +10,6 @@ TESTS =\ + multi-dax.sh \ + btt-check.sh \ + label-compat.sh \ +- blk-exhaust.sh \ + sector-mode.sh \ + inject-error.sh \ + btt-errors.sh \ +@@ -35,9 +31,6 @@ EXTRA_DIST += $(TESTS) common \ + check_PROGRAMS =\ + libndctl \ + dsm-fail \ +- dpa-alloc \ +- parent-uuid \ +- multi-pmem \ + dax-errors \ + smart-notify \ + smart-listen \ +@@ -48,7 +41,6 @@ check_PROGRAMS =\ + + if ENABLE_DESTRUCTIVE + TESTS +=\ +- blk-ns \ + pmem-ns \ + sub-section.sh \ + dax-dev \ +@@ -68,7 +60,6 @@ TESTS += security.sh + endif + + check_PROGRAMS +=\ +- blk-ns \ + pmem-ns \ + dax-dev \ + dax-pmd \ +@@ -108,18 +99,9 @@ ack_shutdown_count_set_SOURCES =\ + + ack_shutdown_count_set_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS) + +-blk_ns_SOURCES = blk_namespaces.c $(testcore) +-blk_ns_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS) $(UUID_LIBS) +- + pmem_ns_SOURCES = pmem_namespaces.c $(testcore) + pmem_ns_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS) $(UUID_LIBS) + +-dpa_alloc_SOURCES = dpa-alloc.c $(testcore) +-dpa_alloc_LDADD = $(LIBNDCTL_LIB) $(UUID_LIBS) $(KMOD_LIBS) +- +-parent_uuid_SOURCES = parent-uuid.c $(testcore) +-parent_uuid_LDADD = $(LIBNDCTL_LIB) $(UUID_LIBS) $(KMOD_LIBS) +- + dax_dev_SOURCES = dax-dev.c $(testcore) + dax_dev_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS) + +@@ -169,19 +151,6 @@ smart_notify_LDADD = $(LIBNDCTL_LIB) + smart_listen_SOURCES = smart-listen.c + smart_listen_LDADD = $(LIBNDCTL_LIB) + +-multi_pmem_SOURCES = \ +- multi-pmem.c \ +- $(testcore) \ +- ../ndctl/namespace.c \ +- ../ndctl/check.c \ +- ../util/json.c +-multi_pmem_LDADD = \ +- $(LIBNDCTL_LIB) \ +- $(JSON_LIBS) \ +- $(UUID_LIBS) \ +- $(KMOD_LIBS) \ +- ../libutil.a +- + list_smart_dimm_SOURCES = \ + list-smart-dimm.c \ + ../util/json.c +diff --git a/test/blk-exhaust.sh b/test/blk-exhaust.sh +deleted file mode 100755 +index b6d3808..0000000 +--- a/test/blk-exhaust.sh ++++ /dev/null +@@ -1,30 +0,0 @@ +-#!/bin/bash -x +-# SPDX-License-Identifier: GPL-2.0 +-# Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +- +-set -e +- +-rc=77 +- +-. $(dirname $0)/common +- +-check_min_kver "4.11" || do_skip "may lack blk-exhaustion fix" +- +-trap 'err $LINENO' ERR +- +-# setup (reset nfit_test dimms) +-modprobe nfit_test +-reset +- +-# if the kernel accounting is correct we should be able to create two +-# pmem and two blk namespaces on nfit_test.0 +-rc=1 +-$NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem +-$NDCTL create-namespace -b $NFIT_TEST_BUS0 -t pmem +-$NDCTL create-namespace -b $NFIT_TEST_BUS0 -t blk -m raw +-$NDCTL create-namespace -b $NFIT_TEST_BUS0 -t blk -m raw +- +-# clearnup and exit +-_cleanup +- +-exit 0 +diff --git a/test/blk_namespaces.c b/test/blk_namespaces.c +deleted file mode 100644 +index f076e85..0000000 +--- a/test/blk_namespaces.c ++++ /dev/null +@@ -1,357 +0,0 @@ +-// SPDX-License-Identifier: LGPL-2.1 +-// Copyright (C) 2015-2020, Intel Corporation. All rights reserved. +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* The purpose of this test is to verify that we can successfully do I/O to +- * multiple nd_blk namespaces that have discontiguous segments. It first +- * sets up two namespaces, each 1/2 the total size of the NVDIMM and each with +- * two discontiguous segments, arranged like this: +- * +- * +-------+-------+-------+-------+ +- * | nd0 | nd1 | nd0 | nd1 | +- * +-------+-------+-------+-------+ +- * +- * It then runs some I/O to the beginning, middle and end of each of these +- * namespaces, checking data integrity. The I/O to the middle of the +- * namespace will hit two pages, one on either side of the segment boundary. +- */ +-#define err(msg)\ +- fprintf(stderr, "%s:%d: %s (%s)\n", __func__, __LINE__, msg, strerror(errno)) +- +-static struct ndctl_namespace *create_blk_namespace(int region_fraction, +- struct ndctl_region *region) +-{ +- struct ndctl_namespace *ndns, *seed_ns = NULL; +- unsigned long long size; +- uuid_t uuid; +- +- ndctl_region_set_align(region, sysconf(_SC_PAGESIZE)); +- ndctl_namespace_foreach(region, ndns) +- if (ndctl_namespace_get_size(ndns) == 0) { +- seed_ns = ndns; +- break; +- } +- +- if (!seed_ns) +- return NULL; +- +- uuid_generate(uuid); +- size = ndctl_region_get_size(region)/region_fraction; +- +- if (ndctl_namespace_set_uuid(seed_ns, uuid) < 0) +- return NULL; +- +- if (ndctl_namespace_set_size(seed_ns, size) < 0) +- return NULL; +- +- if (ndctl_namespace_set_sector_size(seed_ns, 512) < 0) +- return NULL; +- +- if (ndctl_namespace_enable(seed_ns) < 0) +- return NULL; +- +- return seed_ns; +-} +- +-static int disable_blk_namespace(struct ndctl_namespace *ndns) +-{ +- if (ndctl_namespace_disable_invalidate(ndns) < 0) +- return -ENODEV; +- +- if (ndctl_namespace_delete(ndns) < 0) +- return -ENODEV; +- +- return 0; +-} +- +-static int ns_do_io(const char *bdev) +-{ +- int fd, i; +- int rc = 0; +- const int page_size = 4096; +- const int num_pages = 4; +- unsigned long num_dev_pages, num_blocks; +- off_t addr; +- +- void *random_page[num_pages]; +- void *blk_page[num_pages]; +- +- rc = posix_memalign(random_page, page_size, page_size * num_pages); +- if (rc) { +- fprintf(stderr, "posix_memalign failure\n"); +- return rc; +- } +- +- rc = posix_memalign(blk_page, page_size, page_size * num_pages); +- if (rc) { +- fprintf(stderr, "posix_memalign failure\n"); +- goto err_free_blk; +- } +- +- for (i = 1; i < num_pages; i++) { +- random_page[i] = (char*)random_page[0] + page_size * i; +- blk_page[i] = (char*)blk_page[0] + page_size * i; +- } +- +- /* read random data into random_page */ +- if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { +- err("open"); +- rc = -ENODEV; +- goto err_free_all; +- } +- +- rc = read(fd, random_page[0], page_size * num_pages); +- if (rc < 0) { +- err("read"); +- close(fd); +- goto err_free_all; +- } +- +- close(fd); +- +- if ((fd = open(bdev, O_RDWR|O_DIRECT)) < 0) { +- err("open"); +- rc = -ENODEV; +- goto err_free_all; +- } +- +- ioctl(fd, BLKGETSIZE, &num_blocks); +- num_dev_pages = num_blocks / 8; +- +- /* write the random data out to each of the segments */ +- rc = pwrite(fd, random_page[0], page_size, 0); +- if (rc < 0) { +- err("write"); +- goto err_close; +- } +- +- /* two pages that span the region discontinuity */ +- addr = page_size * (num_dev_pages/2 - 1); +- rc = pwrite(fd, random_page[1], page_size*2, addr); +- if (rc < 0) { +- err("write"); +- goto err_close; +- } +- +- addr = page_size * (num_dev_pages - 1); +- rc = pwrite(fd, random_page[3], page_size, addr); +- if (rc < 0) { +- err("write"); +- goto err_close; +- } +- +- /* read back the random data into blk_page */ +- rc = pread(fd, blk_page[0], page_size, 0); +- if (rc < 0) { +- err("read"); +- goto err_close; +- } +- +- /* two pages that span the region discontinuity */ +- addr = page_size * (num_dev_pages/2 - 1); +- rc = pread(fd, blk_page[1], page_size*2, addr); +- if (rc < 0) { +- err("read"); +- goto err_close; +- } +- +- addr = page_size * (num_dev_pages - 1); +- rc = pread(fd, blk_page[3], page_size, addr); +- if (rc < 0) { +- err("read"); +- goto err_close; +- } +- +- /* verify the data */ +- if (memcmp(random_page[0], blk_page[0], page_size * num_pages)) { +- fprintf(stderr, "Block data miscompare\n"); +- rc = -EIO; +- goto err_close; +- } +- +- rc = 0; +- err_close: +- close(fd); +- err_free_all: +- free(random_page[0]); +- err_free_blk: +- free(blk_page[0]); +- return rc; +-} +- +-static const char *comm = "test-blk-namespaces"; +- +-int test_blk_namespaces(int log_level, struct ndctl_test *test, +- struct ndctl_ctx *ctx) +-{ +- char bdev[50]; +- int rc = -ENXIO; +- struct ndctl_bus *bus; +- struct ndctl_dimm *dimm; +- struct kmod_module *mod = NULL; +- struct kmod_ctx *kmod_ctx = NULL; +- struct ndctl_namespace *ndns[2]; +- struct ndctl_region *region, *blk_region = NULL; +- +- if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 2, 0))) +- return 77; +- +- ndctl_set_log_priority(ctx, log_level); +- +- bus = ndctl_bus_get_by_provider(ctx, "ACPI.NFIT"); +- if (bus) { +- /* skip this bus if no BLK regions */ +- ndctl_region_foreach(bus, region) +- if (ndctl_region_get_nstype(region) +- == ND_DEVICE_NAMESPACE_BLK) +- break; +- if (!region) +- bus = NULL; +- } +- +- if (!bus) { +- fprintf(stderr, "ACPI.NFIT unavailable falling back to nfit_test\n"); +- rc = ndctl_test_init(&kmod_ctx, &mod, NULL, log_level, test); +- ndctl_invalidate(ctx); +- bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); +- if (rc < 0 || !bus) { +- ndctl_test_skip(test); +- fprintf(stderr, "nfit_test unavailable skipping tests\n"); +- return 77; +- } +- } +- +- fprintf(stderr, "%s: found provider: %s\n", comm, +- ndctl_bus_get_provider(bus)); +- +- /* get the system to a clean state */ +- ndctl_region_foreach(bus, region) +- ndctl_region_disable_invalidate(region); +- +- ndctl_dimm_foreach(bus, dimm) { +- rc = ndctl_dimm_zero_labels(dimm); +- if (rc < 0) { +- fprintf(stderr, "failed to zero %s\n", +- ndctl_dimm_get_devname(dimm)); +- goto err_module; +- } +- } +- +- /* create our config */ +- ndctl_region_foreach(bus, region) +- if (strcmp(ndctl_region_get_type_name(region), "blk") == 0) { +- blk_region = region; +- break; +- } +- +- if (!blk_region || ndctl_region_enable(blk_region) < 0) { +- fprintf(stderr, "%s: failed to find block region\n", comm); +- rc = -ENODEV; +- goto err_cleanup; +- } +- +- rc = -ENODEV; +- ndns[0] = create_blk_namespace(4, blk_region); +- if (!ndns[0]) { +- fprintf(stderr, "%s: failed to create block namespace\n", comm); +- goto err_cleanup; +- } +- +- ndns[1] = create_blk_namespace(4, blk_region); +- if (!ndns[1]) { +- fprintf(stderr, "%s: failed to create block namespace\n", comm); +- goto err_cleanup; +- } +- +- rc = disable_blk_namespace(ndns[0]); +- if (rc < 0) { +- fprintf(stderr, "%s: failed to disable block namespace\n", comm); +- goto err_cleanup; +- } +- +- ndns[0] = create_blk_namespace(2, blk_region); +- if (!ndns[0]) { +- fprintf(stderr, "%s: failed to create block namespace\n", comm); +- rc = -ENODEV; +- goto err_cleanup; +- } +- +- rc = disable_blk_namespace(ndns[1]); +- if (rc < 0) { +- fprintf(stderr, "%s: failed to disable block namespace\n", comm); +- goto err_cleanup; +- } +- +- rc = -ENODEV; +- ndns[1] = create_blk_namespace(2, blk_region); +- if (!ndns[1]) { +- fprintf(stderr, "%s: failed to create block namespace\n", comm); +- goto err_cleanup; +- } +- +- /* okay, all set up, do some I/O */ +- rc = -EIO; +- sprintf(bdev, "/dev/%s", ndctl_namespace_get_block_device(ndns[0])); +- if (ns_do_io(bdev)) +- goto err_cleanup; +- sprintf(bdev, "/dev/%s", ndctl_namespace_get_block_device(ndns[1])); +- if (ns_do_io(bdev)) +- goto err_cleanup; +- rc = 0; +- +- err_cleanup: +- /* unload nfit_test */ +- bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); +- if (bus) +- ndctl_region_foreach(bus, region) +- ndctl_region_disable_invalidate(region); +- bus = ndctl_bus_get_by_provider(ctx, "nfit_test.1"); +- if (bus) +- ndctl_region_foreach(bus, region) +- ndctl_region_disable_invalidate(region); +- if (mod) +- kmod_module_remove_module(mod, 0); +- +- err_module: +- if (kmod_ctx) +- kmod_unref(kmod_ctx); +- return rc; +-} +- +-int __attribute__((weak)) main(int argc, char *argv[]) +-{ +- struct ndctl_test *test = ndctl_test_new(0); +- struct ndctl_ctx *ctx; +- int rc; +- +- comm = argv[0]; +- if (!test) { +- fprintf(stderr, "failed to initialize test\n"); +- return EXIT_FAILURE; +- } +- +- rc = ndctl_new(&ctx); +- if (rc) +- return ndctl_test_result(test, rc); +- +- rc = test_blk_namespaces(LOG_DEBUG, test, ctx); +- ndctl_unref(ctx); +- return ndctl_test_result(test, rc); +-} +diff --git a/test/core.c b/test/core.c +index 93e1dae..dc1405d 100644 +--- a/test/core.c ++++ b/test/core.c +@@ -123,7 +123,6 @@ int ndctl_test_init(struct kmod_ctx **ctx, struct kmod_module **mod, + "dax_pmem_core", + "dax_pmem_compat", + "libnvdimm", +- "nd_blk", + "nd_btt", + "nd_e820", + "nd_pmem", +diff --git a/test/create.sh b/test/create.sh +index e9baaa0..9a6f373 100755 +--- a/test/create.sh ++++ b/test/create.sh +@@ -40,19 +40,6 @@ eval $(echo $json | json2var) + # free capacity for blk creation + $NDCTL destroy-namespace -f $dev + +-# create blk +-dev="x" +-json=$($NDCTL create-namespace -b $NFIT_TEST_BUS0 -t blk -m raw -v) +-eval $(echo $json | json2var) +-[ $dev = "x" ] && echo "fail: $LINENO" && exit 1 +-[ $mode != "raw" ] && echo "fail: $LINENO" && exit 1 +- +-# convert blk to sector mode +-json=$($NDCTL create-namespace -m sector -l $SECTOR_SIZE -f -e $dev) +-eval $(echo $json | json2var) +-[ $sector_size != $SECTOR_SIZE ] && echo "fail: $LINENO" && exit 1 +-[ $mode != "sector" ] && echo "fail: $LINENO" && exit 1 +- + _cleanup + + exit 0 +diff --git a/test/dpa-alloc.c b/test/dpa-alloc.c +deleted file mode 100644 +index 59185cf..0000000 +--- a/test/dpa-alloc.c ++++ /dev/null +@@ -1,326 +0,0 @@ +-// SPDX-License-Identifier: LGPL-2.1 +-// Copyright (C) 2014-2020, Intel Corporation. All rights reserved. +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-static const char *NFIT_PROVIDER0 = "nfit_test.0"; +-static const char *NFIT_PROVIDER1 = "nfit_test.1"; +-#define NUM_NAMESPACES 4 +- +-struct test_dpa_namespace { +- struct ndctl_namespace *ndns; +- unsigned long long size; +- uuid_t uuid; +-} namespaces[NUM_NAMESPACES]; +- +-#define MIN_SIZE SZ_4M +- +-static int do_test(struct ndctl_ctx *ctx, struct ndctl_test *test) +-{ +- unsigned int default_available_slots, available_slots, i; +- struct ndctl_region *region, *blk_region = NULL; +- struct ndctl_namespace *ndns; +- struct ndctl_dimm *dimm; +- unsigned long size, page_size; +- struct ndctl_bus *bus; +- char uuid_str[40]; +- int round; +- int rc; +- +- page_size = sysconf(_SC_PAGESIZE); +- /* disable nfit_test.1, not used in this test */ +- bus = ndctl_bus_get_by_provider(ctx, NFIT_PROVIDER1); +- if (!bus) +- return -ENXIO; +- ndctl_region_foreach(bus, region) { +- ndctl_region_disable_invalidate(region); +- ndctl_region_set_align(region, sysconf(_SC_PAGESIZE) +- * ndctl_region_get_interleave_ways(region)); +- } +- +- /* init nfit_test.0 */ +- bus = ndctl_bus_get_by_provider(ctx, NFIT_PROVIDER0); +- if (!bus) +- return -ENXIO; +- ndctl_region_foreach(bus, region) { +- ndctl_region_disable_invalidate(region); +- ndctl_region_set_align(region, sysconf(_SC_PAGESIZE) +- * ndctl_region_get_interleave_ways(region)); +- } +- +- ndctl_dimm_foreach(bus, dimm) { +- rc = ndctl_dimm_zero_labels(dimm); +- if (rc < 0) { +- fprintf(stderr, "failed to zero %s\n", +- ndctl_dimm_get_devname(dimm)); +- return rc; +- } +- } +- +- /* +- * Find a guineapig BLK region, we know that the dimm with +- * handle==0 from nfit_test.0 always allocates from highest DPA +- * to lowest with no excursions into BLK only ranges. +- */ +- ndctl_region_foreach(bus, region) { +- if (ndctl_region_get_type(region) != ND_DEVICE_REGION_BLK) +- continue; +- dimm = ndctl_region_get_first_dimm(region); +- if (!dimm) +- continue; +- if (ndctl_dimm_get_handle(dimm) == 0) { +- blk_region = region; +- break; +- } +- } +- if (!blk_region || ndctl_region_enable(blk_region) < 0) { +- fprintf(stderr, "failed to find a usable BLK region\n"); +- return -ENXIO; +- } +- region = blk_region; +- +- if (ndctl_region_get_available_size(region) / MIN_SIZE < NUM_NAMESPACES) { +- fprintf(stderr, "%s insufficient available_size\n", +- ndctl_region_get_devname(region)); +- return -ENXIO; +- } +- +- default_available_slots = ndctl_dimm_get_available_labels(dimm); +- +- /* grow namespaces */ +- for (i = 0; i < ARRAY_SIZE(namespaces); i++) { +- uuid_t uuid; +- +- ndns = ndctl_region_get_namespace_seed(region); +- if (!ndns) { +- fprintf(stderr, "%s: failed to get seed: %d\n", +- ndctl_region_get_devname(region), i); +- return -ENXIO; +- } +- uuid_generate_random(uuid); +- ndctl_namespace_set_uuid(ndns, uuid); +- ndctl_namespace_set_sector_size(ndns, 512); +- ndctl_namespace_set_size(ndns, MIN_SIZE); +- rc = ndctl_namespace_enable(ndns); +- if (rc) { +- fprintf(stderr, "failed to enable %s: %d\n", +- ndctl_namespace_get_devname(ndns), rc); +- return rc; +- } +- ndctl_namespace_disable_invalidate(ndns); +- rc = ndctl_namespace_set_size(ndns, page_size); +- if (rc) { +- fprintf(stderr, "failed to init %s to size: %lu\n", +- ndctl_namespace_get_devname(ndns), +- page_size); +- return rc; +- } +- namespaces[i].ndns = ndns; +- ndctl_namespace_get_uuid(ndns, namespaces[i].uuid); +- } +- +- available_slots = ndctl_dimm_get_available_labels(dimm); +- if (available_slots != default_available_slots +- - ARRAY_SIZE(namespaces)) { +- fprintf(stderr, "expected %ld slots available\n", +- default_available_slots +- - ARRAY_SIZE(namespaces)); +- return -ENOSPC; +- } +- +- /* exhaust label space, by round-robin allocating 4K */ +- round = 1; +- for (i = 0; i < available_slots; i++) { +- ndns = namespaces[i % ARRAY_SIZE(namespaces)].ndns; +- if (i % ARRAY_SIZE(namespaces) == 0) +- round++; +- size = page_size * round; +- rc = ndctl_namespace_set_size(ndns, size); +- if (rc) { +- fprintf(stderr, "%s: set_size: %lx failed: %d\n", +- ndctl_namespace_get_devname(ndns), size, rc); +- return rc; +- } +- } +- +- /* +- * The last namespace we updated should still be modifiable via +- * the kernel's reserve label +- */ +- i--; +- round++; +- ndns = namespaces[i % ARRAY_SIZE(namespaces)].ndns; +- size = page_size * round; +- rc = ndctl_namespace_set_size(ndns, size); +- if (rc) { +- fprintf(stderr, "%s failed to update while labels full\n", +- ndctl_namespace_get_devname(ndns)); +- return rc; +- } +- +- round--; +- size = page_size * round; +- rc = ndctl_namespace_set_size(ndns, size); +- if (rc) { +- fprintf(stderr, "%s failed to reduce size while labels full\n", +- ndctl_namespace_get_devname(ndns)); +- return rc; +- } +- +- /* do the allocations survive a region cycle? */ +- for (i = 0; i < ARRAY_SIZE(namespaces); i++) { +- ndns = namespaces[i].ndns; +- namespaces[i].size = ndctl_namespace_get_size(ndns); +- namespaces[i].ndns = NULL; +- } +- +- ndctl_region_disable_invalidate(region); +- rc = ndctl_region_enable(region); +- if (rc) { +- fprintf(stderr, "failed to re-enable %s: %d\n", +- ndctl_region_get_devname(region), rc); +- return rc; +- } +- +- ndctl_namespace_foreach(region, ndns) { +- uuid_t uuid; +- +- ndctl_namespace_get_uuid(ndns, uuid); +- for (i = 0; i < ARRAY_SIZE(namespaces); i++) { +- if (uuid_compare(uuid, namespaces[i].uuid) == 0) { +- namespaces[i].ndns = ndns; +- break; +- } +- } +- } +- +- /* validate that they all came back */ +- for (i = 0; i < ARRAY_SIZE(namespaces); i++) { +- ndns = namespaces[i].ndns; +- size = ndns ? ndctl_namespace_get_size(ndns) : 0; +- +- if (ndns && size == namespaces[i].size) +- continue; +- uuid_unparse(namespaces[i].uuid, uuid_str); +- fprintf(stderr, "failed to recover %s\n", uuid_str); +- return -ENODEV; +- } +- +- /* test deletion and merging */ +- ndns = namespaces[0].ndns; +- for (i = 1; i < ARRAY_SIZE(namespaces); i++) { +- struct ndctl_namespace *victim = namespaces[i].ndns; +- +- uuid_unparse(namespaces[i].uuid, uuid_str); +- size = ndctl_namespace_get_size(victim); +- rc = ndctl_namespace_disable(victim); +- if (rc) { +- fprintf(stderr, "failed to disable %s\n", uuid_str); +- return rc; +- } +- rc = ndctl_namespace_delete(victim); +- if (rc) { +- fprintf(stderr, "failed to delete %s\n", uuid_str); +- return rc; +- } +- size += ndctl_namespace_get_size(ndns); +- rc = ndctl_namespace_set_size(ndns, size); +- if (rc) { +- fprintf(stderr, "failed to merge %s\n", uuid_str); +- return rc; +- } +- } +- +- /* there can be only one */ +- i = 0; +- ndctl_namespace_foreach(region, ndns) { +- unsigned long long sz = ndctl_namespace_get_size(ndns); +- +- if (sz) { +- i++; +- if (sz == size) +- continue; +- fprintf(stderr, "%s size: %llx expected %lx\n", +- ndctl_namespace_get_devname(ndns), +- sz, size); +- return -ENXIO; +- } +- } +- if (i != 1) { +- fprintf(stderr, "failed to delete namespaces\n"); +- return -ENXIO; +- } +- +- available_slots = ndctl_dimm_get_available_labels(dimm); +- if (available_slots != default_available_slots - 1) { +- fprintf(stderr, "mishandled slot count\n"); +- return -ENXIO; +- } +- +- ndctl_region_foreach(bus, region) +- ndctl_region_disable_invalidate(region); +- +- return 0; +-} +- +-int test_dpa_alloc(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) +-{ +- struct kmod_module *mod; +- struct kmod_ctx *kmod_ctx; +- int err, result = EXIT_FAILURE; +- +- if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 2, 0))) +- return 77; +- +- ndctl_set_log_priority(ctx, loglevel); +- err = ndctl_test_init(&kmod_ctx, &mod, NULL, loglevel, test); +- if (err < 0) { +- ndctl_test_skip(test); +- fprintf(stderr, "nfit_test unavailable skipping tests\n"); +- return 77; +- } +- +- err = do_test(ctx, test); +- if (err == 0) +- result = EXIT_SUCCESS; +- kmod_module_remove_module(mod, 0); +- kmod_unref(kmod_ctx); +- return result; +-} +- +-int __attribute__((weak)) main(int argc, char *argv[]) +-{ +- struct ndctl_test *test = ndctl_test_new(0); +- struct ndctl_ctx *ctx; +- int rc; +- +- if (!test) { +- fprintf(stderr, "failed to initialize test\n"); +- return EXIT_FAILURE; +- } +- +- rc = ndctl_new(&ctx); +- if (rc) +- return ndctl_test_result(test, rc); +- +- rc = test_dpa_alloc(LOG_DEBUG, test, ctx); +- ndctl_unref(ctx); +- return ndctl_test_result(test, rc); +-} +diff --git a/test/libndctl.c b/test/libndctl.c +index 1e97926..35e7cfa 100644 +--- a/test/libndctl.c ++++ b/test/libndctl.c +@@ -30,46 +30,35 @@ + /* + * Kernel provider "nfit_test.0" produces an NFIT with the following attributes: + * +- * (a) (b) DIMM BLK-REGION +- * +-------------------+--------+--------+--------+ +- * +------+ | pm0.0 | blk2.0 | pm1.0 | blk2.1 | 0 region2 +- * | imc0 +--+- - - region0- - - +--------+ +--------+ +- * +--+---+ | pm0.0 | blk3.0 | pm1.0 | blk3.1 | 1 region3 +- * | +-------------------+--------v v--------+ +- * +--+---+ | | +- * | cpu0 | region1 +- * +--+---+ | | +- * | +----------------------------^ ^--------+ +- * +--+---+ | blk4.0 | pm1.0 | blk4.0 | 2 region4 +- * | imc1 +--+----------------------------| +--------+ +- * +------+ | blk5.0 | pm1.0 | blk5.0 | 3 region5 +- * +----------------------------+--------+--------+ ++ * (a) (b) DIMM ++ * +-------------------+--------+--------+--------+ ++ * +------+ | pm0.0 | free | pm1.0 | free | 0 ++ * | imc0 +--+- - - region0- - - +--------+ +--------+ ++ * +--+---+ | pm0.0 | free | pm1.0 | free | 1 ++ * | +-------------------+--------v v--------+ ++ * +--+---+ | | ++ * | cpu0 | region1 ++ * +--+---+ | | ++ * | +----------------------------^ ^--------+ ++ * +--+---+ | free | pm1.0 | free | 2 ++ * | imc1 +--+----------------------------| +--------+ ++ * +------+ | free | pm1.0 | free | 3 ++ * +----------------------------+--------+--------+ + * +- * *) In this layout we have four dimms and two memory controllers in one +- * socket. Each unique interface ("blk" or "pmem") to DPA space +- * is identified by a region device with a dynamically assigned id. ++ * In this platform we have four DIMMs and two memory controllers in one ++ * socket. Each PMEM interleave set is identified by a region device with ++ * a dynamically assigned id. + * +- * *) The first portion of dimm0 and dimm1 are interleaved as REGION0. +- * A single "pmem" namespace is created in the REGION0-"spa"-range +- * that spans dimm0 and dimm1 with a user-specified name of "pm0.0". +- * Some of that interleaved "spa" range is reclaimed as "bdw" +- * accessed space starting at offset (a) into each dimm. In that +- * reclaimed space we create two "bdw" "namespaces" from REGION2 and +- * REGION3 where "blk2.0" and "blk3.0" are just human readable names +- * that could be set to any user-desired name in the label. ++ * 1. The first portion of DIMM0 and DIMM1 are interleaved as REGION0. A ++ * single PMEM namespace is created in the REGION0-SPA-range that spans most ++ * of DIMM0 and DIMM1 with a user-specified name of "pm0.0". Some of that ++ * interleaved system-physical-address range is left free for ++ * another PMEM namespace to be defined. + * +- * *) In the last portion of dimm0 and dimm1 we have an interleaved +- * "spa" range, REGION1, that spans those two dimms as well as dimm2 +- * and dimm3. Some of REGION1 allocated to a "pmem" namespace named +- * "pm1.0" the rest is reclaimed in 4 "bdw" namespaces (for each +- * dimm in the interleave set), "blk2.1", "blk3.1", "blk4.0", and +- * "blk5.0". +- * +- * *) The portion of dimm2 and dimm3 that do not participate in the +- * REGION1 interleaved "spa" range (i.e. the DPA address below +- * offset (b) are also included in the "blk4.0" and "blk5.0" +- * namespaces. Note, that this example shows that "bdw" namespaces +- * don't need to be contiguous in DPA-space. ++ * 2. In the last portion of DIMM0 and DIMM1 we have an interleaved ++ * system-physical-address range, REGION1, that spans those two DIMMs as ++ * well as DIMM2 and DIMM3. Some of REGION1 is allocated to a PMEM namespace ++ * named "pm1.0". + * + * Kernel provider "nfit_test.1" produces an NFIT with the following attributes: + * +@@ -127,10 +116,10 @@ struct dimm { + (((n & 0xfff) << 16) | ((s & 0xf) << 12) | ((i & 0xf) << 8) \ + | ((c & 0xf) << 4) | (d & 0xf)) + static struct dimm dimms0[] = { +- { DIMM_HANDLE(0, 0, 0, 0, 0), 0, 0, 2016, 10, 42, { 0 }, 2, { 0x201, 0x301, }, }, +- { DIMM_HANDLE(0, 0, 0, 0, 1), 1, 0, 2016, 10, 42, { 0 }, 2, { 0x201, 0x301, }, }, +- { DIMM_HANDLE(0, 0, 1, 0, 0), 2, 0, 2016, 10, 42, { 0 }, 2, { 0x201, 0x301, }, }, +- { DIMM_HANDLE(0, 0, 1, 0, 1), 3, 0, 2016, 10, 42, { 0 }, 2, { 0x201, 0x301, }, }, ++ { DIMM_HANDLE(0, 0, 0, 0, 0), 0, 0, 2016, 10, 42, { 0 }, 1, { 0x201, }, }, ++ { DIMM_HANDLE(0, 0, 0, 0, 1), 1, 0, 2016, 10, 42, { 0 }, 1, { 0x201, }, }, ++ { DIMM_HANDLE(0, 0, 1, 0, 0), 2, 0, 2016, 10, 42, { 0 }, 1, { 0x201, }, }, ++ { DIMM_HANDLE(0, 0, 1, 0, 1), 3, 0, 2016, 10, 42, { 0 }, 1, { 0x201, }, }, + }; + + static struct dimm dimms1[] = { +@@ -240,7 +229,6 @@ struct namespace { + }; + + static uuid_t null_uuid; +-static unsigned long blk_sector_sizes[] = { 512, 520, 528, 4096, 4104, 4160, 4224, }; + static unsigned long pmem_sector_sizes[] = { 512, 4096 }; + static unsigned long io_sector_sizes[] = { 0 }; + +@@ -262,60 +250,6 @@ static struct namespace namespace1_pmem0 = { + ARRAY_SIZE(pmem_sector_sizes), pmem_sector_sizes, + }; + +-static struct namespace namespace2_blk0 = { +- 0, "namespace_blk", NULL, NULL, NULL, SZ_7M, +- { 3, 3, 3, 3, +- 3, 3, 3, 3, +- 3, 3, 3, 3, +- 3, 3, 3, 3, }, 1, 1, 0, +- ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, +-}; +- +-static struct namespace namespace2_blk1 = { +- 1, "namespace_blk", NULL, NULL, NULL, SZ_11M, +- { 4, 4, 4, 4, +- 4, 4, 4, 4, +- 4, 4, 4, 4, +- 4, 4, 4, 4, }, 1, 1, 0, +- ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, +-}; +- +-static struct namespace namespace3_blk0 = { +- 0, "namespace_blk", NULL, NULL, NULL, SZ_7M, +- { 5, 5, 5, 5, +- 5, 5, 5, 5, +- 5, 5, 5, 5, +- 5, 5, 5, 5, }, 1, 1, 0, +- ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, +-}; +- +-static struct namespace namespace3_blk1 = { +- 1, "namespace_blk", NULL, NULL, NULL, SZ_11M, +- { 6, 6, 6, 6, +- 6, 6, 6, 6, +- 6, 6, 6, 6, +- 6, 6, 6, 6, }, 1, 1, 0, +- ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, +-}; +- +-static struct namespace namespace4_blk0 = { +- 0, "namespace_blk", &btt_settings, NULL, NULL, SZ_27M, +- { 7, 7, 7, 7, +- 7, 7, 7, 7, +- 7, 7, 7, 7, +- 7, 7, 7, 7, }, 1, 1, 0, +- ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, +-}; +- +-static struct namespace namespace5_blk0 = { +- 0, "namespace_blk", &btt_settings, NULL, NULL, SZ_27M, +- { 8, 8, 8, 8, +- 8, 8, 8, 8, +- 8, 8, 8, 8, +- 8, 8, 8, 8, }, 1, 1, 0, +- ARRAY_SIZE(blk_sector_sizes), blk_sector_sizes, +-}; +- + static struct region regions0[] = { + { { 1 }, 2, 1, "pmem", SZ_32M, SZ_32M, { 1 }, + .namespaces = { +@@ -339,40 +273,6 @@ static struct region regions0[] = { + [0] = &default_pfn, + }, + }, +- { { DIMM_HANDLE(0, 0, 0, 0, 0) }, 1, 1, "blk", SZ_18M, SZ_32M, +- .namespaces = { +- [0] = &namespace2_blk0, +- [1] = &namespace2_blk1, +- }, +- .btts = { +- [0] = &default_btt, +- }, +- }, +- { { DIMM_HANDLE(0, 0, 0, 0, 1) }, 1, 1, "blk", SZ_18M, SZ_32M, +- .namespaces = { +- [0] = &namespace3_blk0, +- [1] = &namespace3_blk1, +- }, +- .btts = { +- [0] = &default_btt, +- }, +- }, +- { { DIMM_HANDLE(0, 0, 1, 0, 0) }, 1, 1, "blk", SZ_27M, SZ_32M, +- .namespaces = { +- [0] = &namespace4_blk0, +- }, +- .btts = { +- [0] = &default_btt, +- }, +- }, +- { { DIMM_HANDLE(0, 0, 1, 0, 1) }, 1, 1, "blk", SZ_27M, SZ_32M, +- .namespaces = { +- [0] = &namespace5_blk0, +- }, +- .btts = { +- [0] = &default_btt, +- }, +- }, + }; + + static struct namespace namespace1 = { +@@ -485,26 +385,6 @@ static struct ndctl_region *get_pmem_region_by_range_index(struct ndctl_bus *bus + return NULL; + } + +-static struct ndctl_region *get_blk_region_by_dimm_handle(struct ndctl_bus *bus, +- unsigned int handle) +-{ +- struct ndctl_region *region; +- +- ndctl_region_foreach(bus, region) { +- struct ndctl_mapping *map; +- +- if (ndctl_region_get_type(region) != ND_DEVICE_REGION_BLK) +- continue; +- ndctl_mapping_foreach(region, map) { +- struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(map); +- +- if (ndctl_dimm_get_handle(dimm) == handle) +- return region; +- } +- } +- return NULL; +-} +- + enum ns_mode { + BTT, PFN, DAX, + }; +@@ -522,11 +402,8 @@ static int check_regions(struct ndctl_bus *bus, struct region *regions, int n, + struct ndctl_interleave_set *iset; + char devname[50]; + +- if (strcmp(regions[i].type, "pmem") == 0) +- region = get_pmem_region_by_range_index(bus, regions[i].range_index); +- else +- region = get_blk_region_by_dimm_handle(bus, regions[i].handle); +- ++ region = get_pmem_region_by_range_index(bus, ++ regions[i].range_index); + if (!region) { + fprintf(stderr, "failed to find region type: %s ident: %x\n", + regions[i].type, regions[i].handle); +@@ -1065,7 +942,6 @@ static int check_btt_create(struct ndctl_region *region, struct ndctl_namespace + return -ENXIO; + + for (i = 0; i < btt_s->num_sector_sizes; i++) { +- struct ndctl_namespace *ns_seed = ndctl_region_get_namespace_seed(region); + struct ndctl_btt *btt_seed = ndctl_region_get_btt_seed(region); + enum ndctl_namespace_mode mode; + +@@ -1115,16 +991,6 @@ static int check_btt_create(struct ndctl_region *region, struct ndctl_namespace + goto err; + } + +- /* check new seed creation for BLK regions */ +- if (ndctl_region_get_type(region) == ND_DEVICE_REGION_BLK) { +- if (ns_seed == ndctl_region_get_namespace_seed(region) +- && ndns == ns_seed) { +- fprintf(stderr, "%s: failed to advance namespace seed\n", +- ndctl_region_get_devname(region)); +- goto err; +- } +- } +- + if (namespace->ro) { + ndctl_region_set_ro(region, 0); + rc = ndctl_btt_enable(btt); +diff --git a/test/multi-pmem.c b/test/multi-pmem.c +deleted file mode 100644 +index 3ea08cc..0000000 +--- a/test/multi-pmem.c ++++ /dev/null +@@ -1,285 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#define NUM_NAMESPACES 4 +-#define SZ_NAMESPACE SZ_16M +- +-static int setup_namespace(struct ndctl_region *region) +-{ +- struct ndctl_ctx *ctx = ndctl_region_get_ctx(region); +- const char *argv[] = { +- "__func__", "-v", "-m", "raw", "-s", "16M", "-r", "", +- }; +- int argc = ARRAY_SIZE(argv); +- +- argv[argc - 1] = ndctl_region_get_devname(region); +- builtin_xaction_namespace_reset(); +- return cmd_create_namespace(argc, argv, ctx); +-} +- +-static void destroy_namespace(struct ndctl_namespace *ndns) +-{ +- struct ndctl_ctx *ctx = ndctl_namespace_get_ctx(ndns); +- const char *argv[] = { +- "__func__", "-v", "-f", "", +- }; +- int argc = ARRAY_SIZE(argv); +- +- argv[argc - 1] = ndctl_namespace_get_devname(ndns); +- builtin_xaction_namespace_reset(); +- cmd_destroy_namespace(argc, argv, ctx); +-} +- +-/* Check that the namespace device is gone (if it wasn't the seed) */ +-static int check_deleted(struct ndctl_region *region, const char *devname, +- struct ndctl_test *test) +-{ +- struct ndctl_namespace *ndns; +- +- if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 10, 0))) +- return 0; +- +- ndctl_namespace_foreach(region, ndns) { +- if (strcmp(devname, ndctl_namespace_get_devname(ndns))) +- continue; +- if (ndns == ndctl_region_get_namespace_seed(region)) +- continue; +- fprintf(stderr, "multi-pmem: expected %s to be deleted\n", +- devname); +- return -ENXIO; +- } +- +- return 0; +-} +- +-static int do_multi_pmem(struct ndctl_ctx *ctx, struct ndctl_test *test) +-{ +- int i; +- char devname[100]; +- struct ndctl_bus *bus; +- uuid_t uuid[NUM_NAMESPACES]; +- struct ndctl_namespace *ndns; +- struct ndctl_dimm *dimm_target, *dimm; +- struct ndctl_region *region, *target = NULL; +- struct ndctl_namespace *namespaces[NUM_NAMESPACES]; +- unsigned long long blk_avail, blk_avail_orig, expect; +- +- if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 9, 0))) { +- ndctl_test_skip(test); +- return 77; +- } +- +- bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0"); +- if (!bus) +- return -ENXIO; +- +- /* disable all regions so that set_config_data commands are permitted */ +- ndctl_region_foreach(bus, region) +- ndctl_region_disable_invalidate(region); +- +- ndctl_dimm_foreach(bus, dimm) { +- int rc = ndctl_dimm_zero_labels(dimm); +- +- if (rc < 0) { +- fprintf(stderr, "failed to zero %s\n", +- ndctl_dimm_get_devname(dimm)); +- return rc; +- } +- } +- +- /* +- * Set regions back to their default state and find our target +- * region. +- */ +- ndctl_region_foreach(bus, region) { +- ndctl_region_enable(region); +- if (ndctl_region_get_available_size(region) +- == SZ_NAMESPACE * NUM_NAMESPACES) +- target = region; +- } +- +- if (!target) { +- fprintf(stderr, "multi-pmem: failed to find target region\n"); +- return -ENXIO; +- } +- region = target; +- +- for (i = 0; i < (int) ARRAY_SIZE(uuid); i++) { +- if (setup_namespace(region) != 0) { +- fprintf(stderr, "multi-pmem: failed to setup namespace: %d\n", i); +- return -ENXIO; +- } +- sprintf(devname, "namespace%d.%d", +- ndctl_region_get_id(region), i); +- ndctl_namespace_foreach(region, ndns) +- if (strcmp(ndctl_namespace_get_devname(ndns), devname) == 0 +- && ndctl_namespace_is_enabled(ndns)) +- break; +- if (!ndns) { +- fprintf(stderr, "multi-pmem: failed to find namespace: %s\n", +- devname); +- return -ENXIO; +- } +- ndctl_namespace_get_uuid(ndns, uuid[i]); +- } +- +- /* bounce the region and verify everything came back as expected */ +- ndctl_region_disable_invalidate(region); +- ndctl_region_enable(region); +- +- for (i = 0; i < (int) ARRAY_SIZE(uuid); i++) { +- char uuid_str1[40], uuid_str2[40]; +- uuid_t uuid_check; +- +- sprintf(devname, "namespace%d.%d", +- ndctl_region_get_id(region), i); +- ndctl_namespace_foreach(region, ndns) +- if (strcmp(ndctl_namespace_get_devname(ndns), devname) == 0 +- && ndctl_namespace_is_enabled(ndns)) +- break; +- if (!ndns) { +- fprintf(stderr, "multi-pmem: failed to restore namespace: %s\n", +- devname); +- return -ENXIO; +- } +- +- ndctl_namespace_get_uuid(ndns, uuid_check); +- uuid_unparse(uuid_check, uuid_str2); +- uuid_unparse(uuid[i], uuid_str1); +- if (uuid_compare(uuid_check, uuid[i]) != 0) { +- fprintf(stderr, "multi-pmem: expected uuid[%d]: %s, got %s\n", +- i, uuid_str1, uuid_str2); +- return -ENXIO; +- } +- namespaces[i] = ndns; +- } +- +- /* +- * Check that aliased blk capacity does not increase until the +- * highest dpa pmem-namespace is deleted. +- */ +- dimm_target = ndctl_region_get_first_dimm(region); +- if (!dimm_target) { +- fprintf(stderr, "multi-pmem: failed to retrieve dimm from %s\n", +- ndctl_region_get_devname(region)); +- return -ENXIO; +- } +- +- dimm = NULL; +- ndctl_region_foreach(bus, region) { +- if (ndctl_region_get_type(region) != ND_DEVICE_REGION_BLK) +- continue; +- ndctl_dimm_foreach_in_region(region, dimm) +- if (dimm == dimm_target) +- break; +- if (dimm) +- break; +- } +- +- blk_avail_orig = ndctl_region_get_available_size(region); +- for (i = 1; i < NUM_NAMESPACES - 1; i++) { +- ndns = namespaces[i]; +- sprintf(devname, "%s", ndctl_namespace_get_devname(ndns)); +- destroy_namespace(ndns); +- blk_avail = ndctl_region_get_available_size(region); +- if (blk_avail != blk_avail_orig) { +- fprintf(stderr, "multi-pmem: destroy %s %llx avail, expect %llx\n", +- devname, blk_avail, blk_avail_orig); +- return -ENXIO; +- } +- +- if (check_deleted(target, devname, test) != 0) +- return -ENXIO; +- } +- +- ndns = namespaces[NUM_NAMESPACES - 1]; +- sprintf(devname, "%s", ndctl_namespace_get_devname(ndns)); +- destroy_namespace(ndns); +- blk_avail = ndctl_region_get_available_size(region); +- expect = (SZ_NAMESPACE / ndctl_region_get_interleave_ways(target)) +- * (NUM_NAMESPACES - 1) + blk_avail_orig; +- if (blk_avail != expect) { +- fprintf(stderr, "multi-pmem: destroy %s %llx avail, expect %llx\n", +- devname, blk_avail, expect); +- return -ENXIO; +- } +- +- if (check_deleted(target, devname, test) != 0) +- return -ENXIO; +- +- ndctl_bus_foreach(ctx, bus) { +- if (strncmp(ndctl_bus_get_provider(bus), "nfit_test", 9) != 0) +- continue; +- ndctl_region_foreach(bus, region) +- ndctl_region_disable_invalidate(region); +- } +- +- return 0; +-} +- +-int test_multi_pmem(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) +-{ +- struct kmod_module *mod; +- struct kmod_ctx *kmod_ctx; +- int err, result = EXIT_FAILURE; +- +- if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 2, 0))) +- return 77; +- +- ndctl_set_log_priority(ctx, loglevel); +- +- err = ndctl_test_init(&kmod_ctx, &mod, NULL, loglevel, test); +- if (err < 0) { +- result = 77; +- ndctl_test_skip(test); +- fprintf(stderr, "%s unavailable skipping tests\n", +- "nfit_test"); +- return result; +- } +- +- result = do_multi_pmem(ctx, test); +- +- kmod_module_remove_module(mod, 0); +- kmod_unref(kmod_ctx); +- return result; +-} +- +-int __attribute__((weak)) main(int argc, char *argv[]) +-{ +- struct ndctl_test *test = ndctl_test_new(0); +- struct ndctl_ctx *ctx; +- int rc; +- +- if (!test) { +- fprintf(stderr, "failed to initialize test\n"); +- return EXIT_FAILURE; +- } +- +- rc = ndctl_new(&ctx); +- if (rc) +- return ndctl_test_result(test, rc); +- rc = test_multi_pmem(LOG_DEBUG, test, ctx); +- ndctl_unref(ctx); +- return ndctl_test_result(test, rc); +-} +diff --git a/test/parent-uuid.c b/test/parent-uuid.c +deleted file mode 100644 +index bded33a..0000000 +--- a/test/parent-uuid.c ++++ /dev/null +@@ -1,254 +0,0 @@ +-// SPDX-License-Identifier: LGPL-2.1 +-// Copyright (C) 2015-2020, Intel Corporation. All rights reserved. +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-static const char *PROVIDER = "nfit_test.0"; +- +-static struct ndctl_bus *get_bus_by_provider(struct ndctl_ctx *ctx, +- const char *provider) +-{ +- struct ndctl_bus *bus; +- +- ndctl_bus_foreach(ctx, bus) +- if (strcmp(provider, ndctl_bus_get_provider(bus)) == 0) +- return bus; +- +- return NULL; +-} +- +-static struct ndctl_btt *get_idle_btt(struct ndctl_region *region) +-{ +- struct ndctl_btt *btt; +- +- ndctl_btt_foreach(region, btt) +- if (!ndctl_btt_is_enabled(btt) +- && !ndctl_btt_is_configured(btt)) +- return btt; +- return NULL; +-} +- +-static struct ndctl_namespace *create_blk_namespace(int region_fraction, +- struct ndctl_region *region, unsigned long long req_size, +- uuid_t uuid) +-{ +- struct ndctl_namespace *ndns, *seed_ns = NULL; +- unsigned long long size; +- +- ndctl_region_set_align(region, sysconf(_SC_PAGESIZE)); +- ndctl_namespace_foreach(region, ndns) +- if (ndctl_namespace_get_size(ndns) == 0) { +- seed_ns = ndns; +- break; +- } +- +- if (!seed_ns) +- return NULL; +- +- size = ndctl_region_get_size(region)/region_fraction; +- if (req_size) +- size = req_size; +- +- if (ndctl_namespace_set_uuid(seed_ns, uuid) < 0) +- return NULL; +- +- if (ndctl_namespace_set_size(seed_ns, size) < 0) +- return NULL; +- +- if (ndctl_namespace_set_sector_size(seed_ns, 512) < 0) +- return NULL; +- +- if (ndctl_namespace_enable(seed_ns) < 0) +- return NULL; +- +- return seed_ns; +-} +- +-static int disable_blk_namespace(struct ndctl_namespace *ndns) +-{ +- if (ndctl_namespace_disable_invalidate(ndns) < 0) +- return -ENODEV; +- +- if (ndctl_namespace_delete(ndns) < 0) +- return -ENODEV; +- +- return 0; +-} +- +-static struct ndctl_btt *check_valid_btt(struct ndctl_region *region, +- struct ndctl_namespace *ndns, uuid_t btt_uuid) +-{ +- struct ndctl_btt *btt = NULL; +- ndctl_btt_foreach(region, btt) { +- struct ndctl_namespace *btt_ndns; +- uuid_t uu; +- +- ndctl_btt_get_uuid(btt, uu); +- if (uuid_compare(uu, btt_uuid) != 0) +- continue; +- if (!ndctl_btt_is_enabled(btt)) +- continue; +- btt_ndns = ndctl_btt_get_namespace(btt); +- if (!btt_ndns || strcmp(ndctl_namespace_get_devname(btt_ndns), +- ndctl_namespace_get_devname(ndns)) != 0) +- continue; +- return btt; +- } +- return NULL; +-} +- +-static int do_test(struct ndctl_ctx *ctx) +-{ +- int rc; +- struct ndctl_bus *bus; +- struct ndctl_btt *btt, *found = NULL, *_btt; +- struct ndctl_region *region, *blk_region = NULL; +- struct ndctl_namespace *ndns, *_ndns; +- unsigned long long ns_size = 18874368; +- uuid_t uuid = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}; +- uuid_t btt_uuid; +- +- bus = get_bus_by_provider(ctx, PROVIDER); +- if (!bus) { +- fprintf(stderr, "failed to find NFIT-provider: %s\n", PROVIDER); +- return -ENODEV; +- } +- +- ndctl_region_foreach(bus, region) +- if (strcmp(ndctl_region_get_type_name(region), "blk") == 0) { +- blk_region = region; +- break; +- } +- +- if (!blk_region) { +- fprintf(stderr, "failed to find block region\n"); +- return -ENODEV; +- } +- +- /* create a blk namespace */ +- ndns = create_blk_namespace(1, blk_region, ns_size, uuid); +- if (!ndns) { +- fprintf(stderr, "failed to create block namespace\n"); +- return -ENXIO; +- } +- +- /* create a btt for this namespace */ +- uuid_generate(btt_uuid); +- btt = get_idle_btt(region); +- if (!btt) +- return -ENXIO; +- +- ndctl_namespace_disable_invalidate(ndns); +- ndctl_btt_set_uuid(btt, btt_uuid); +- ndctl_btt_set_sector_size(btt, 512); +- ndctl_btt_set_namespace(btt, ndns); +- rc = ndctl_btt_enable(btt); +- if (rc) { +- fprintf(stderr, "failed to create btt 0\n"); +- return rc; +- } +- +- /* re-create the namespace - this should auto-enable the btt */ +- disable_blk_namespace(ndns); +- ndns = create_blk_namespace(1, blk_region, ns_size, uuid); +- if (!ndns) { +- fprintf(stderr, "failed to re-create block namespace\n"); +- return -ENXIO; +- } +- +- /* Verify btt was auto-created */ +- found = check_valid_btt(blk_region, ndns, btt_uuid); +- if (!found) +- return -ENXIO; +- btt = found; +- +- /*disable the btt and namespace again */ +- ndctl_btt_delete(btt); +- disable_blk_namespace(ndns); +- +- /* recreate the namespace with a different uuid */ +- uuid_generate(uuid); +- ndns = create_blk_namespace(1, blk_region, ns_size, uuid); +- if (!ndns) { +- fprintf(stderr, "failed to re-create block namespace\n"); +- return -ENXIO; +- } +- +- /* make sure there is no btt on this namespace */ +- found = check_valid_btt(blk_region, ndns, btt_uuid); +- if (found) { +- fprintf(stderr, "found a stale btt\n"); +- return -ENXIO; +- } +- +- ndctl_btt_foreach_safe(blk_region, btt, _btt) +- ndctl_btt_delete(btt); +- +- ndctl_namespace_foreach_safe(blk_region, ndns, _ndns) +- if (ndctl_namespace_get_size(ndns) != 0) +- disable_blk_namespace(ndns); +- +- ndctl_region_foreach(bus, region) +- ndctl_region_disable_invalidate(region); +- +- return 0; +-} +- +-int test_parent_uuid(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) +-{ +- struct kmod_module *mod; +- struct kmod_ctx *kmod_ctx; +- int err, result = EXIT_FAILURE; +- +- if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 3, 0))) +- return 77; +- +- ndctl_set_log_priority(ctx, loglevel); +- err = ndctl_test_init(&kmod_ctx, &mod, NULL, loglevel, test); +- if (err < 0) { +- ndctl_test_skip(test); +- fprintf(stderr, "nfit_test unavailable skipping tests\n"); +- return 77; +- } +- +- err = do_test(ctx); +- if (err == 0) +- result = EXIT_SUCCESS; +- kmod_module_remove_module(mod, 0); +- kmod_unref(kmod_ctx); +- return result; +-} +- +-int __attribute__((weak)) main(int argc, char *argv[]) +-{ +- struct ndctl_test *test = ndctl_test_new(0); +- struct ndctl_ctx *ctx; +- int rc; +- +- if (!test) { +- fprintf(stderr, "failed to initialize test\n"); +- return EXIT_FAILURE; +- } +- +- rc = ndctl_new(&ctx); +- if (rc) +- return ndctl_test_result(test, rc); +- +- rc = test_parent_uuid(LOG_DEBUG, test, ctx); +- ndctl_unref(ctx); +- return ndctl_test_result(test, rc); +-} +-- +2.27.0 + diff --git a/SOURCES/0076-ndctl-test-Fix-support-for-missing-dax_pmem_compat-m.patch b/SOURCES/0076-ndctl-test-Fix-support-for-missing-dax_pmem_compat-m.patch new file mode 100644 index 0000000..027c0ab --- /dev/null +++ b/SOURCES/0076-ndctl-test-Fix-support-for-missing-dax_pmem_compat-m.patch @@ -0,0 +1,78 @@ +From 9bfb567715d1b45e6598e6b38bef531312c72db3 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:21 -0800 +Subject: [PATCH 076/217] ndctl/test: Fix support for missing dax_pmem_compat + module + +The kernel is moving to drop CONFIG_DEV_DAX_PMEM_COMPAT. Update +ndctl_test_init() to not error out if dax_pmem_compat is missing. It seems +that the original implementation of support for missing dax_pmem_compat was +broken, or since that time newer versions of kmod_module_new_from_name() no +longer fail when the module is missing. + +Link: https://lore.kernel.org/r/164141834155.3990253.5388773351209410262.stgit@dwillia2-desk3.amr.corp.intel.com +Fixes: b7991dbc22f3 ("ndctl/test: Relax dax_pmem_compat requirement") +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/core.c | 25 +++++++++++-------------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +diff --git a/test/core.c b/test/core.c +index dc1405d..5d1aa23 100644 +--- a/test/core.c ++++ b/test/core.c +@@ -120,7 +120,6 @@ int ndctl_test_init(struct kmod_ctx **ctx, struct kmod_module **mod, + "nfit", + "device_dax", + "dax_pmem", +- "dax_pmem_core", + "dax_pmem_compat", + "libnvdimm", + "nd_btt", +@@ -180,29 +179,27 @@ int ndctl_test_init(struct kmod_ctx **ctx, struct kmod_module **mod, + /* + * Skip device-dax bus-model modules on pre-v5.1 + */ +- if ((strcmp(name, "dax_pmem_core") == 0 +- || strcmp(name, "dax_pmem_compat") == 0) +- && !ndctl_test_attempt(test, +- KERNEL_VERSION(5, 1, 0))) ++ if ((strcmp(name, "dax_pmem_compat") == 0) && ++ !ndctl_test_attempt(test, KERNEL_VERSION(5, 1, 0))) + continue; + + retry: + rc = kmod_module_new_from_name(*ctx, name, mod); +- +- /* +- * dax_pmem_compat is not required, missing is ok, +- * present-but-production is not ok. +- */ +- if (rc && strcmp(name, "dax_pmem_compat") == 0) +- continue; +- + if (rc) { +- log_err(&log_ctx, "%s.ko: missing\n", name); ++ log_err(&log_ctx, "failed to interrogate %s.ko\n", ++ name); + break; + } + + path = kmod_module_get_path(*mod); + if (!path) { ++ /* ++ * dax_pmem_compat is not required, missing is ++ * ok, present-but-production is not ok. ++ */ ++ if (strcmp(name, "dax_pmem_compat") == 0) ++ continue; ++ + if (family != NVDIMM_FAMILY_INTEL && + (strcmp(name, "nfit") == 0 || + strcmp(name, "nd_e820") == 0)) +-- +2.27.0 + diff --git a/SOURCES/0077-util-Distribute-filter-and-json-helpers-to-per-tool-.patch b/SOURCES/0077-util-Distribute-filter-and-json-helpers-to-per-tool-.patch new file mode 100644 index 0000000..dc8a474 --- /dev/null +++ b/SOURCES/0077-util-Distribute-filter-and-json-helpers-to-per-tool-.patch @@ -0,0 +1,5595 @@ +From 965fa02e372fe3a05acc9ae8379f6dd796b7346d Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:26 -0800 +Subject: [PATCH 077/217] util: Distribute 'filter' and 'json' helpers to + per-tool objects + +In preparation for switching build systems, fix the long standing wart +of mixing ndctl, daxctl, and cxl 'filter' and 'json' utilities in the +top-level util/filter.[ch]. Distribute them to their respective +{ndctl,daxctl,cxl}/filter.{c,h} locations. + +This also removes the naming collisions for util/json.h between util/ +and ndct/util/. I.e. is no longer ambiguous or subject to +being shadowed by the tool local "util" directory. + +Unfortunately unwinding this caused a lot of code to move all at once. +The benefit is that now it is clear that ndctl is the only tool that +reaches across into the 'filter' and 'json' functionality of another +tool (daxctl). + +Link: https://lore.kernel.org/r/164141834691.3990253.16681105368577841032.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Makefile.am | 1 - + Makefile.am.in | 3 - + cxl/Makefile.am | 3 + + cxl/filter.c | 25 + + cxl/filter.h | 7 + + cxl/json.c | 214 +++++ + cxl/json.h | 8 + + cxl/list.c | 4 +- + cxl/memdev.c | 3 +- + daxctl/Makefile.am | 5 + + daxctl/device.c | 4 +- + daxctl/filter.c | 43 + + daxctl/filter.h | 12 + + daxctl/json.c | 245 ++++++ + daxctl/json.h | 18 + + daxctl/list.c | 4 +- + ndctl/Makefile.am | 16 +- + ndctl/bus.c | 4 +- + ndctl/check.c | 2 +- + ndctl/dimm.c | 6 +- + {util => ndctl}/filter.c | 60 +- + {util => ndctl}/filter.h | 12 +- + ndctl/inject-error.c | 6 +- + ndctl/inject-smart.c | 6 +- + ndctl/{util => }/json-smart.c | 5 +- + ndctl/json.c | 1114 ++++++++++++++++++++++++ + ndctl/json.h | 24 + + ndctl/{util => }/keys.c | 5 +- + ndctl/{util => }/keys.h | 0 + ndctl/lib/libndctl.c | 2 +- + ndctl/lib/papr.c | 4 +- + ndctl/lib/private.h | 4 +- + ndctl/list.c | 5 +- + ndctl/load-keys.c | 7 +- + ndctl/monitor.c | 4 +- + ndctl/namespace.c | 6 +- + ndctl/region.c | 3 +- + test/Makefile.am | 22 +- + test/ack-shutdown-count-set.c | 2 +- + test/daxdev-errors.c | 2 +- + test/device-dax.c | 2 +- + test/dsm-fail.c | 4 +- + test/libndctl.c | 2 +- + test/list-smart-dimm.c | 6 +- + test/pmem_namespaces.c | 2 +- + test/revoke-devmem.c | 2 +- + util/help.c | 2 +- + util/json.c | 1542 +-------------------------------- + util/json.h | 39 +- + 49 files changed, 1820 insertions(+), 1701 deletions(-) + create mode 100644 cxl/filter.c + create mode 100644 cxl/filter.h + create mode 100644 cxl/json.c + create mode 100644 cxl/json.h + create mode 100644 daxctl/filter.c + create mode 100644 daxctl/filter.h + create mode 100644 daxctl/json.c + create mode 100644 daxctl/json.h + rename {util => ndctl}/filter.c (88%) + rename {util => ndctl}/filter.h (89%) + rename ndctl/{util => }/json-smart.c (99%) + create mode 100644 ndctl/json.c + create mode 100644 ndctl/json.h + rename ndctl/{util => }/keys.c (99%) + rename ndctl/{util => }/keys.h (100%) + +diff -up ndctl-71.1/Makefile.am.in.orig ndctl-71.1/Makefile.am.in +--- ndctl-71.1/Makefile.am.in.orig 2022-10-07 15:50:40.071454550 -0400 ++++ ndctl-71.1/Makefile.am.in 2022-10-07 15:51:03.900535680 -0400 +@@ -9,9 +9,6 @@ AM_CPPFLAGS = \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DPREFIX=\""$(prefix)"\" \ + -DNDCTL_MAN_PATH=\""$(mandir)"\" \ +- -I${top_srcdir}/ndctl/lib \ +- -I${top_srcdir}/ndctl \ +- -I${top_srcdir}/cxl \ + -I${top_srcdir}/ \ + $(KMOD_CFLAGS) \ + $(UDEV_CFLAGS) \ +diff -up ndctl-71.1/Makefile.am.orig ndctl-71.1/Makefile.am +--- ndctl-71.1/Makefile.am.orig 2022-10-07 15:50:40.071454550 -0400 ++++ ndctl-71.1/Makefile.am 2022-10-07 15:51:03.900535680 -0400 +@@ -84,7 +84,6 @@ libutil_a_SOURCES = \ + util/help.c \ + util/strbuf.c \ + util/wrapper.c \ +- util/filter.c \ + util/bitmap.c \ + util/abspath.c \ + util/iomem.c \ +diff -up ndctl-71.1/cxl/Makefile.am.orig ndctl-71.1/cxl/Makefile.am +--- ndctl-71.1/cxl/Makefile.am.orig 2022-10-07 15:50:40.071454550 -0400 ++++ ndctl-71.1/cxl/Makefile.am 2022-10-07 15:51:03.900535680 -0400 +@@ -12,6 +12,9 @@ cxl_SOURCES =\ + list.c \ + memdev.c \ + ../util/json.c \ ++ json.c \ ++ filter.c \ ++ filter.h \ + builtin.h + + cxl_LDADD =\ +diff -up ndctl-71.1/cxl/filter.c.orig ndctl-71.1/cxl/filter.c +--- ndctl-71.1/cxl/filter.c.orig 2022-10-07 15:51:03.900535680 -0400 ++++ ndctl-71.1/cxl/filter.c 2022-10-07 15:51:03.900535680 -0400 +@@ -0,0 +1,25 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. ++#include ++#include ++#include ++#include "filter.h" ++ ++struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, ++ const char *ident) ++{ ++ int memdev_id; ++ ++ if (!ident || strcmp(ident, "all") == 0) ++ return memdev; ++ ++ if (strcmp(ident, cxl_memdev_get_devname(memdev)) == 0) ++ return memdev; ++ ++ if ((sscanf(ident, "%d", &memdev_id) == 1 ++ || sscanf(ident, "mem%d", &memdev_id) == 1) ++ && cxl_memdev_get_id(memdev) == memdev_id) ++ return memdev; ++ ++ return NULL; ++} +diff -up ndctl-71.1/cxl/filter.h.orig ndctl-71.1/cxl/filter.h +--- ndctl-71.1/cxl/filter.h.orig 2022-10-07 15:51:03.901535684 -0400 ++++ ndctl-71.1/cxl/filter.h 2022-10-07 15:51:03.901535684 -0400 +@@ -0,0 +1,7 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ ++#ifndef _CXL_UTIL_FILTER_H_ ++#define _CXL_UTIL_FILTER_H_ ++struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, ++ const char *ident); ++#endif /* _CXL_UTIL_FILTER_H_ */ +diff -up ndctl-71.1/cxl/json.c.orig ndctl-71.1/cxl/json.c +--- ndctl-71.1/cxl/json.c.orig 2022-10-07 15:51:03.901535684 -0400 ++++ ndctl-71.1/cxl/json.c 2022-10-07 15:51:03.901535684 -0400 +@@ -0,0 +1,214 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "json.h" ++ ++static struct json_object *util_cxl_memdev_health_to_json( ++ struct cxl_memdev *memdev, unsigned long flags) ++{ ++ struct json_object *jhealth; ++ struct json_object *jobj; ++ struct cxl_cmd *cmd; ++ u32 field; ++ int rc; ++ ++ jhealth = json_object_new_object(); ++ if (!jhealth) ++ return NULL; ++ if (!memdev) ++ goto err_jobj; ++ ++ cmd = cxl_cmd_new_get_health_info(memdev); ++ if (!cmd) ++ goto err_jobj; ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) ++ goto err_cmd; ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) ++ goto err_cmd; ++ ++ /* health_status fields */ ++ rc = cxl_cmd_health_info_get_maintenance_needed(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "maintenance_needed", jobj); ++ ++ rc = cxl_cmd_health_info_get_performance_degraded(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "performance_degraded", jobj); ++ ++ rc = cxl_cmd_health_info_get_hw_replacement_needed(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "hw_replacement_needed", jobj); ++ ++ /* media_status fields */ ++ rc = cxl_cmd_health_info_get_media_normal(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_normal", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_not_ready(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_not_ready", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_persistence_lost(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_persistence_lost", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_data_lost(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_data_lost", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_powerloss_persistence_loss(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_powerloss_persistence_loss", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_shutdown_persistence_loss(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_shutdown_persistence_loss", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_persistence_loss_imminent(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_persistence_loss_imminent", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_powerloss_data_loss(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_powerloss_data_loss", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_shutdown_data_loss(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_shutdown_data_loss", jobj); ++ ++ rc = cxl_cmd_health_info_get_media_data_loss_imminent(cmd); ++ jobj = json_object_new_boolean(rc); ++ if (jobj) ++ json_object_object_add(jhealth, "media_data_loss_imminent", jobj); ++ ++ /* ext_status fields */ ++ if (cxl_cmd_health_info_get_ext_life_used_normal(cmd)) ++ jobj = json_object_new_string("normal"); ++ else if (cxl_cmd_health_info_get_ext_life_used_warning(cmd)) ++ jobj = json_object_new_string("warning"); ++ else if (cxl_cmd_health_info_get_ext_life_used_critical(cmd)) ++ jobj = json_object_new_string("critical"); ++ else ++ jobj = json_object_new_string("unknown"); ++ if (jobj) ++ json_object_object_add(jhealth, "ext_life_used", jobj); ++ ++ if (cxl_cmd_health_info_get_ext_temperature_normal(cmd)) ++ jobj = json_object_new_string("normal"); ++ else if (cxl_cmd_health_info_get_ext_temperature_warning(cmd)) ++ jobj = json_object_new_string("warning"); ++ else if (cxl_cmd_health_info_get_ext_temperature_critical(cmd)) ++ jobj = json_object_new_string("critical"); ++ else ++ jobj = json_object_new_string("unknown"); ++ if (jobj) ++ json_object_object_add(jhealth, "ext_temperature", jobj); ++ ++ if (cxl_cmd_health_info_get_ext_corrected_volatile_normal(cmd)) ++ jobj = json_object_new_string("normal"); ++ else if (cxl_cmd_health_info_get_ext_corrected_volatile_warning(cmd)) ++ jobj = json_object_new_string("warning"); ++ else ++ jobj = json_object_new_string("unknown"); ++ if (jobj) ++ json_object_object_add(jhealth, "ext_corrected_volatile", jobj); ++ ++ if (cxl_cmd_health_info_get_ext_corrected_persistent_normal(cmd)) ++ jobj = json_object_new_string("normal"); ++ else if (cxl_cmd_health_info_get_ext_corrected_persistent_warning(cmd)) ++ jobj = json_object_new_string("warning"); ++ else ++ jobj = json_object_new_string("unknown"); ++ if (jobj) ++ json_object_object_add(jhealth, "ext_corrected_persistent", jobj); ++ ++ /* other fields */ ++ field = cxl_cmd_health_info_get_life_used(cmd); ++ if (field != 0xff) { ++ jobj = json_object_new_int(field); ++ if (jobj) ++ json_object_object_add(jhealth, "life_used_percent", jobj); ++ } ++ ++ field = cxl_cmd_health_info_get_temperature(cmd); ++ if (field != 0xffff) { ++ jobj = json_object_new_int(field); ++ if (jobj) ++ json_object_object_add(jhealth, "temperature", jobj); ++ } ++ ++ field = cxl_cmd_health_info_get_dirty_shutdowns(cmd); ++ jobj = json_object_new_int64(field); ++ if (jobj) ++ json_object_object_add(jhealth, "dirty_shutdowns", jobj); ++ ++ field = cxl_cmd_health_info_get_volatile_errors(cmd); ++ jobj = json_object_new_int64(field); ++ if (jobj) ++ json_object_object_add(jhealth, "volatile_errors", jobj); ++ ++ field = cxl_cmd_health_info_get_pmem_errors(cmd); ++ jobj = json_object_new_int64(field); ++ if (jobj) ++ json_object_object_add(jhealth, "pmem_errors", jobj); ++ ++ cxl_cmd_unref(cmd); ++ return jhealth; ++ ++err_cmd: ++ cxl_cmd_unref(cmd); ++err_jobj: ++ json_object_put(jhealth); ++ return NULL; ++} ++ ++struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, ++ unsigned long flags) ++{ ++ const char *devname = cxl_memdev_get_devname(memdev); ++ struct json_object *jdev, *jobj; ++ ++ jdev = json_object_new_object(); ++ if (!devname || !jdev) ++ return NULL; ++ ++ jobj = json_object_new_string(devname); ++ if (jobj) ++ json_object_object_add(jdev, "memdev", jobj); ++ ++ jobj = util_json_object_size(cxl_memdev_get_pmem_size(memdev), flags); ++ if (jobj) ++ json_object_object_add(jdev, "pmem_size", jobj); ++ ++ jobj = util_json_object_size(cxl_memdev_get_ram_size(memdev), flags); ++ if (jobj) ++ json_object_object_add(jdev, "ram_size", jobj); ++ ++ if (flags & UTIL_JSON_HEALTH) { ++ jobj = util_cxl_memdev_health_to_json(memdev, flags); ++ if (jobj) ++ json_object_object_add(jdev, "health", jobj); ++ } ++ return jdev; ++} +diff -up ndctl-71.1/cxl/json.h.orig ndctl-71.1/cxl/json.h +--- ndctl-71.1/cxl/json.h.orig 2022-10-07 15:51:03.901535684 -0400 ++++ ndctl-71.1/cxl/json.h 2022-10-07 15:51:03.901535684 -0400 +@@ -0,0 +1,8 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ ++#ifndef __CXL_UTIL_JSON_H__ ++#define __CXL_UTIL_JSON_H__ ++struct cxl_memdev; ++struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, ++ unsigned long flags); ++#endif /* __CXL_UTIL_JSON_H__ */ +diff -up ndctl-71.1/cxl/list.c.orig ndctl-71.1/cxl/list.c +--- ndctl-71.1/cxl/list.c.orig 2022-10-07 15:50:40.072454553 -0400 ++++ ndctl-71.1/cxl/list.c 2022-10-07 15:51:03.901535684 -0400 +@@ -6,12 +6,14 @@ + #include + #include + #include +-#include + #include + #include + #include + #include + ++#include "json.h" ++#include "filter.h" ++ + static struct { + bool memdevs; + bool idle; +diff -up ndctl-71.1/cxl/memdev.c.orig ndctl-71.1/cxl/memdev.c +--- ndctl-71.1/cxl/memdev.c.orig 2022-10-07 15:50:40.072454553 -0400 ++++ ndctl-71.1/cxl/memdev.c 2022-10-07 15:51:03.901535684 -0400 +@@ -6,12 +6,13 @@ + #include + #include + #include +-#include + #include + #include + #include + #include + ++#include "filter.h" ++ + struct action_context { + FILE *f_out; + FILE *f_in; +diff -up ndctl-71.1/daxctl/Makefile.am.orig ndctl-71.1/daxctl/Makefile.am +--- ndctl-71.1/daxctl/Makefile.am.orig 2022-10-07 15:50:40.073454556 -0400 ++++ ndctl-71.1/daxctl/Makefile.am 2022-10-07 15:51:03.902535687 -0400 +@@ -18,6 +18,11 @@ daxctl_SOURCES =\ + migrate.c \ + device.c \ + ../util/json.c \ ++ ../util/json.h \ ++ json.c \ ++ json.h \ ++ filter.c \ ++ filter.h \ + builtin.h + + daxctl_LDADD =\ +diff -up ndctl-71.1/daxctl/device.c.orig ndctl-71.1/daxctl/device.c +--- ndctl-71.1/daxctl/device.c.orig 2022-10-07 15:50:40.074454560 -0400 ++++ ndctl-71.1/daxctl/device.c 2022-10-07 15:51:03.902535687 -0400 +@@ -11,13 +11,15 @@ + #include + #include + #include +-#include + #include + #include + #include + #include + #include + ++#include "filter.h" ++#include "json.h" ++ + static struct { + const char *dev; + const char *mode; +diff -up ndctl-71.1/daxctl/filter.c.orig ndctl-71.1/daxctl/filter.c +--- ndctl-71.1/daxctl/filter.c.orig 2022-10-07 15:51:03.902535687 -0400 ++++ ndctl-71.1/daxctl/filter.c 2022-10-07 15:51:03.902535687 -0400 +@@ -0,0 +1,43 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. ++#include ++#include ++#include ++ ++#include "filter.h" ++ ++struct daxctl_dev *util_daxctl_dev_filter(struct daxctl_dev *dev, ++ const char *ident) ++{ ++ struct daxctl_region *region = daxctl_dev_get_region(dev); ++ int region_id, dev_id; ++ ++ if (!ident || strcmp(ident, "all") == 0) ++ return dev; ++ ++ if (strcmp(ident, daxctl_dev_get_devname(dev)) == 0) ++ return dev; ++ ++ if (sscanf(ident, "%d.%d", ®ion_id, &dev_id) == 2 && ++ daxctl_region_get_id(region) == region_id && ++ daxctl_dev_get_id(dev) == dev_id) ++ return dev; ++ ++ return NULL; ++} ++ ++struct daxctl_region *util_daxctl_region_filter(struct daxctl_region *region, ++ const char *ident) ++{ ++ int region_id; ++ ++ if (!ident || strcmp(ident, "all") == 0) ++ return region; ++ ++ if ((sscanf(ident, "%d", ®ion_id) == 1 || ++ sscanf(ident, "region%d", ®ion_id) == 1) && ++ daxctl_region_get_id(region) == region_id) ++ return region; ++ ++ return NULL; ++} +diff -up ndctl-71.1/daxctl/filter.h.orig ndctl-71.1/daxctl/filter.h +--- ndctl-71.1/daxctl/filter.h.orig 2022-10-07 15:51:03.902535687 -0400 ++++ ndctl-71.1/daxctl/filter.h 2022-10-07 15:51:03.902535687 -0400 +@@ -0,0 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ ++#ifndef _DAXCTL_UTIL_FILTER_H_ ++#define _DAXCTL_UTIL_FILTER_H_ ++#include ++#include ++ ++struct daxctl_dev *util_daxctl_dev_filter(struct daxctl_dev *dev, ++ const char *ident); ++struct daxctl_region *util_daxctl_region_filter(struct daxctl_region *region, ++ const char *ident); ++#endif /* _DAXCTL_UTIL_FILTER_H_ */ +diff -up ndctl-71.1/daxctl/json.c.orig ndctl-71.1/daxctl/json.c +--- ndctl-71.1/daxctl/json.c.orig 2022-10-07 15:51:03.903535690 -0400 ++++ ndctl-71.1/daxctl/json.c 2022-10-07 15:51:03.902535687 -0400 +@@ -0,0 +1,245 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "filter.h" ++#include "json.h" ++ ++struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, ++ unsigned long flags) ++{ ++ struct daxctl_memory *mem = daxctl_dev_get_memory(dev); ++ const char *devname = daxctl_dev_get_devname(dev); ++ struct json_object *jdev, *jobj, *jmappings = NULL; ++ struct daxctl_mapping *mapping = NULL; ++ int node, movable, align; ++ ++ jdev = json_object_new_object(); ++ if (!devname || !jdev) ++ return NULL; ++ ++ jobj = json_object_new_string(devname); ++ if (jobj) ++ json_object_object_add(jdev, "chardev", jobj); ++ ++ jobj = util_json_object_size(daxctl_dev_get_size(dev), flags); ++ if (jobj) ++ json_object_object_add(jdev, "size", jobj); ++ ++ node = daxctl_dev_get_target_node(dev); ++ if (node >= 0) { ++ jobj = json_object_new_int(node); ++ if (jobj) ++ json_object_object_add(jdev, "target_node", jobj); ++ } ++ ++ align = daxctl_dev_get_align(dev); ++ if (align > 0) { ++ jobj = util_json_object_size(daxctl_dev_get_align(dev), flags); ++ if (jobj) ++ json_object_object_add(jdev, "align", jobj); ++ } ++ ++ if (mem) ++ jobj = json_object_new_string("system-ram"); ++ else ++ jobj = json_object_new_string("devdax"); ++ if (jobj) ++ json_object_object_add(jdev, "mode", jobj); ++ ++ if (mem && daxctl_dev_get_resource(dev) != 0) { ++ int num_sections = daxctl_memory_num_sections(mem); ++ int num_online = daxctl_memory_is_online(mem); ++ ++ jobj = json_object_new_int(num_online); ++ if (jobj) ++ json_object_object_add(jdev, "online_memblocks", jobj); ++ ++ jobj = json_object_new_int(num_sections); ++ if (jobj) ++ json_object_object_add(jdev, "total_memblocks", jobj); ++ ++ movable = daxctl_memory_is_movable(mem); ++ if (movable == 1) ++ jobj = json_object_new_boolean(true); ++ else if (movable == 0) ++ jobj = json_object_new_boolean(false); ++ else ++ jobj = NULL; ++ if (jobj) ++ json_object_object_add(jdev, "movable", jobj); ++ } ++ ++ if (!daxctl_dev_is_enabled(dev)) { ++ jobj = json_object_new_string("disabled"); ++ if (jobj) ++ json_object_object_add(jdev, "state", jobj); ++ } ++ ++ if (!(flags & UTIL_JSON_DAX_MAPPINGS)) ++ return jdev; ++ ++ daxctl_mapping_foreach(dev, mapping) { ++ struct json_object *jmapping; ++ ++ if (!jmappings) { ++ jmappings = json_object_new_array(); ++ if (!jmappings) ++ continue; ++ ++ json_object_object_add(jdev, "mappings", jmappings); ++ } ++ ++ jmapping = util_daxctl_mapping_to_json(mapping, flags); ++ if (!jmapping) ++ continue; ++ json_object_array_add(jmappings, jmapping); ++ } ++ return jdev; ++} ++ ++struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region, ++ struct json_object *jdevs, const char *ident, ++ unsigned long flags) ++{ ++ struct daxctl_dev *dev; ++ ++ daxctl_dev_foreach(region, dev) { ++ struct json_object *jdev; ++ ++ if (!util_daxctl_dev_filter(dev, ident)) ++ continue; ++ ++ if (!(flags & (UTIL_JSON_IDLE|UTIL_JSON_CONFIGURED)) ++ && !daxctl_dev_get_size(dev)) ++ continue; ++ ++ if (!jdevs) { ++ jdevs = json_object_new_array(); ++ if (!jdevs) ++ return NULL; ++ } ++ ++ jdev = util_daxctl_dev_to_json(dev, flags); ++ if (!jdev) { ++ json_object_put(jdevs); ++ return NULL; ++ } ++ ++ json_object_array_add(jdevs, jdev); ++ } ++ ++ return jdevs; ++} ++ ++struct json_object *util_daxctl_region_to_json(struct daxctl_region *region, ++ const char *ident, unsigned long flags) ++{ ++ unsigned long align; ++ struct json_object *jregion, *jobj; ++ unsigned long long available_size, size; ++ ++ jregion = json_object_new_object(); ++ if (!jregion) ++ return NULL; ++ ++ /* ++ * The flag indicates when we are being called by an agent that ++ * already knows about the parent device information. ++ */ ++ if (!(flags & UTIL_JSON_DAX)) { ++ /* trim off the redundant /sys/devices prefix */ ++ const char *path = daxctl_region_get_path(region); ++ int len = strlen("/sys/devices"); ++ const char *trim = &path[len]; ++ ++ if (strncmp(path, "/sys/devices", len) != 0) ++ goto err; ++ jobj = json_object_new_string(trim); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jregion, "path", jobj); ++ } ++ ++ jobj = json_object_new_int(daxctl_region_get_id(region)); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jregion, "id", jobj); ++ ++ size = daxctl_region_get_size(region); ++ if (size < ULLONG_MAX) { ++ jobj = util_json_object_size(size, flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jregion, "size", jobj); ++ } ++ ++ available_size = daxctl_region_get_available_size(region); ++ if (available_size) { ++ jobj = util_json_object_size(available_size, flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jregion, "available_size", jobj); ++ } ++ ++ align = daxctl_region_get_align(region); ++ if (align < ULONG_MAX) { ++ jobj = json_object_new_int64(align); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jregion, "align", jobj); ++ } ++ ++ if (!(flags & UTIL_JSON_DAX_DEVS)) ++ return jregion; ++ ++ jobj = util_daxctl_devs_to_list(region, NULL, ident, flags); ++ if (jobj) ++ json_object_object_add(jregion, "devices", jobj); ++ ++ return jregion; ++ err: ++ json_object_put(jregion); ++ return NULL; ++} ++ ++struct json_object *util_daxctl_mapping_to_json(struct daxctl_mapping *mapping, ++ unsigned long flags) ++{ ++ struct json_object *jmapping = json_object_new_object(); ++ struct json_object *jobj; ++ ++ if (!jmapping) ++ return NULL; ++ ++ jobj = util_json_object_hex(daxctl_mapping_get_offset(mapping), flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jmapping, "page_offset", jobj); ++ ++ jobj = util_json_object_hex(daxctl_mapping_get_start(mapping), flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jmapping, "start", jobj); ++ ++ jobj = util_json_object_hex(daxctl_mapping_get_end(mapping), flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jmapping, "end", jobj); ++ ++ jobj = util_json_object_size(daxctl_mapping_get_size(mapping), flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jmapping, "size", jobj); ++ ++ return jmapping; ++ err: ++ json_object_put(jmapping); ++ return NULL; ++} +diff -up ndctl-71.1/daxctl/json.h.orig ndctl-71.1/daxctl/json.h +--- ndctl-71.1/daxctl/json.h.orig 2022-10-07 15:51:03.905535697 -0400 ++++ ndctl-71.1/daxctl/json.h 2022-10-07 15:51:03.904535694 -0400 +@@ -0,0 +1,18 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ ++#ifndef __DAXCTL_JSON_H__ ++#define __DAXCTL_JSON_H__ ++#include ++ ++struct json_object *util_daxctl_mapping_to_json(struct daxctl_mapping *mapping, ++ unsigned long flags); ++struct daxctl_region; ++struct daxctl_dev; ++struct json_object *util_daxctl_region_to_json(struct daxctl_region *region, ++ const char *ident, unsigned long flags); ++struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, ++ unsigned long flags); ++struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region, ++ struct json_object *jdevs, const char *ident, ++ unsigned long flags); ++#endif /* __CXL_UTIL_JSON_H__ */ +diff -up ndctl-71.1/daxctl/list.c.orig ndctl-71.1/daxctl/list.c +--- ndctl-71.1/daxctl/list.c.orig 2022-10-07 15:50:40.075454563 -0400 ++++ ndctl-71.1/daxctl/list.c 2022-10-07 15:51:03.905535697 -0400 +@@ -6,12 +6,14 @@ + #include + #include + #include +-#include + #include + #include + #include + #include + ++#include "filter.h" ++#include "json.h" ++ + static struct { + bool devs; + bool regions; +diff -up ndctl-71.1/ndctl/Makefile.am.orig ndctl-71.1/ndctl/Makefile.am +--- ndctl-71.1/ndctl/Makefile.am.orig 2022-10-07 15:50:40.076454567 -0400 ++++ ndctl-71.1/ndctl/Makefile.am 2022-10-07 15:51:03.905535697 -0400 +@@ -19,13 +19,19 @@ ndctl_SOURCES = ndctl.c \ + region.c \ + dimm.c \ + ../util/log.c \ +- ../util/filter.c \ +- ../util/filter.h \ ++ ../daxctl/filter.c \ ++ ../daxctl/filter.h \ ++ filter.c \ ++ filter.h \ + list.c \ + ../util/json.c \ + ../util/json.h \ +- util/json-smart.c \ +- util/keys.h \ ++ ../daxctl/json.c \ ++ ../daxctl/json.h \ ++ json.c \ ++ json.h \ ++ json-smart.c \ ++ keys.h \ + inject-error.c \ + inject-smart.c \ + monitor.c \ +@@ -36,7 +42,7 @@ ndctl_SOURCES = ndctl.c \ + firmware-update.h + + if ENABLE_KEYUTILS +-ndctl_SOURCES += util/keys.c \ ++ndctl_SOURCES += keys.c \ + load-keys.c + keys_configdir = $(ndctl_keysdir) + keys_config_DATA = $(ndctl_keysreadme) +diff -up ndctl-71.1/ndctl/bus.c.orig ndctl-71.1/ndctl/bus.c +--- ndctl-71.1/ndctl/bus.c.orig 2022-10-07 15:50:40.077454570 -0400 ++++ ndctl-71.1/ndctl/bus.c 2022-10-07 15:51:03.905535697 -0400 +@@ -8,12 +8,14 @@ + #include + #include + #include +-#include + #include + #include + #include + #include + ++#include "filter.h" ++#include "json.h" ++ + static struct { + bool verbose; + bool force; +diff -up ndctl-71.1/ndctl/check.c.orig ndctl-71.1/ndctl/check.c +--- ndctl-71.1/ndctl/check.c.orig 2022-10-07 15:50:40.078454573 -0400 ++++ ndctl-71.1/ndctl/check.c 2022-10-07 15:51:03.906535700 -0400 +@@ -8,7 +8,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -20,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff -up ndctl-71.1/ndctl/dimm.c.orig ndctl-71.1/ndctl/dimm.c +--- ndctl-71.1/ndctl/dimm.c.orig 2022-10-07 15:50:40.078454573 -0400 ++++ ndctl-71.1/ndctl/dimm.c 2022-10-07 15:51:03.907535704 -0400 +@@ -11,7 +11,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -20,7 +19,10 @@ + #include + #include + #include +-#include ++ ++#include "filter.h" ++#include "json.h" ++#include "keys.h" + + static const char *cmd_name = "dimm"; + static int err_count; +diff -up ndctl-71.1/ndctl/filter.c.orig ndctl-71.1/ndctl/filter.c +--- ndctl-71.1/ndctl/filter.c.orig 2022-10-07 15:51:03.928535775 -0400 ++++ ndctl-71.1/ndctl/filter.c 2022-10-07 15:51:03.908535707 -0400 +@@ -0,0 +1,468 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "filter.h" ++ ++struct ndctl_bus *util_bus_filter(struct ndctl_bus *bus, const char *__ident) ++{ ++ char *end = NULL, *ident, *save; ++ unsigned long bus_id, id; ++ const char *provider, *devname, *name; ++ ++ if (!__ident) ++ return bus; ++ ++ ident = strdup(__ident); ++ if (!ident) ++ return NULL; ++ ++ for (name = strtok_r(ident, " ", &save); name; ++ name = strtok_r(NULL, " ", &save)) { ++ if (strcmp(name, "all") == 0) ++ break; ++ ++ bus_id = strtoul(ident, &end, 0); ++ if (end == ident || end[0]) ++ bus_id = ULONG_MAX; ++ ++ provider = ndctl_bus_get_provider(bus); ++ devname = ndctl_bus_get_devname(bus); ++ id = ndctl_bus_get_id(bus); ++ ++ if (bus_id < ULONG_MAX && bus_id == id) ++ break; ++ ++ if (bus_id == ULONG_MAX && (strcmp(provider, name) == 0 ++ || strcmp(devname, name) == 0)) ++ break; ++ } ++ free(ident); ++ ++ if (name) ++ return bus; ++ return NULL; ++} ++ ++struct ndctl_region *util_region_filter(struct ndctl_region *region, ++ const char *__ident) ++{ ++ char *end = NULL, *ident, *save; ++ const char *name, *region_name; ++ unsigned long region_id, id; ++ ++ if (!__ident) ++ return region; ++ ++ ident = strdup(__ident); ++ if (!ident) ++ return NULL; ++ ++ for (name = strtok_r(ident, " ", &save); name; ++ name = strtok_r(NULL, " ", &save)) { ++ if (strcmp(name, "all") == 0) ++ break; ++ ++ region_id = strtoul(ident, &end, 0); ++ if (end == ident || end[0]) ++ region_id = ULONG_MAX; ++ ++ region_name = ndctl_region_get_devname(region); ++ id = ndctl_region_get_id(region); ++ ++ if (region_id < ULONG_MAX && region_id == id) ++ break; ++ ++ if (region_id == ULONG_MAX && strcmp(region_name, name) == 0) ++ break; ++ } ++ free(ident); ++ ++ if (name) ++ return region; ++ return NULL; ++} ++ ++struct ndctl_namespace *util_namespace_filter(struct ndctl_namespace *ndns, ++ const char *__ident) ++{ ++ struct ndctl_region *region = ndctl_namespace_get_region(ndns); ++ unsigned long region_id, ndns_id; ++ const char *name; ++ char *ident, *save; ++ ++ if (!__ident) ++ return ndns; ++ ++ ident = strdup(__ident); ++ if (!ident) ++ return NULL; ++ ++ for (name = strtok_r(ident, " ", &save); name; ++ name = strtok_r(NULL, " ", &save)) { ++ if (strcmp(name, "all") == 0) ++ break; ++ ++ if (strcmp(name, ndctl_namespace_get_devname(ndns)) == 0) ++ break; ++ ++ if (sscanf(name, "%ld.%ld", ®ion_id, &ndns_id) == 2 ++ && ndctl_region_get_id(region) == region_id ++ && ndctl_namespace_get_id(ndns) == ndns_id) ++ break; ++ } ++ free(ident); ++ ++ if (name) ++ return ndns; ++ return NULL; ++} ++ ++struct ndctl_dimm *util_dimm_filter(struct ndctl_dimm *dimm, ++ const char *__ident) ++{ ++ char *end = NULL, *ident, *save; ++ const char *name, *dimm_name; ++ unsigned long dimm_id, id; ++ ++ if (!__ident) ++ return dimm; ++ ++ ident = strdup(__ident); ++ if (!ident) ++ return NULL; ++ ++ for (name = strtok_r(ident, " ", &save); name; ++ name = strtok_r(NULL, " ", &save)) { ++ if (strcmp(name, "all") == 0) ++ break; ++ ++ dimm_id = strtoul(ident, &end, 0); ++ if (end == ident || end[0]) ++ dimm_id = ULONG_MAX; ++ ++ dimm_name = ndctl_dimm_get_devname(dimm); ++ id = ndctl_dimm_get_id(dimm); ++ ++ if (dimm_id < ULONG_MAX && dimm_id == id) ++ break; ++ ++ if (dimm_id == ULONG_MAX && strcmp(dimm_name, name) == 0) ++ break; ++ } ++ free(ident); ++ ++ if (name) ++ return dimm; ++ return NULL; ++} ++ ++struct ndctl_bus *util_bus_filter_by_dimm(struct ndctl_bus *bus, ++ const char *ident) ++{ ++ struct ndctl_dimm *dimm; ++ ++ if (!ident || strcmp(ident, "all") == 0) ++ return bus; ++ ++ ndctl_dimm_foreach(bus, dimm) ++ if (util_dimm_filter(dimm, ident)) ++ return bus; ++ return NULL; ++} ++ ++struct ndctl_bus *util_bus_filter_by_region(struct ndctl_bus *bus, ++ const char *ident) ++{ ++ struct ndctl_region *region; ++ ++ if (!ident || strcmp(ident, "all") == 0) ++ return bus; ++ ++ ndctl_region_foreach(bus, region) ++ if (util_region_filter(region, ident)) ++ return bus; ++ return NULL; ++} ++ ++struct ndctl_bus *util_bus_filter_by_namespace(struct ndctl_bus *bus, ++ const char *ident) ++{ ++ struct ndctl_region *region; ++ struct ndctl_namespace *ndns; ++ ++ if (!ident || strcmp(ident, "all") == 0) ++ return bus; ++ ++ ndctl_region_foreach(bus, region) ++ ndctl_namespace_foreach(region, ndns) ++ if (util_namespace_filter(ndns, ident)) ++ return bus; ++ return NULL; ++} ++ ++struct ndctl_region *util_region_filter_by_dimm(struct ndctl_region *region, ++ const char *ident) ++{ ++ struct ndctl_dimm *dimm; ++ ++ if (!ident || strcmp(ident, "all") == 0) ++ return region; ++ ++ ndctl_dimm_foreach_in_region(region, dimm) ++ if (util_dimm_filter(dimm, ident)) ++ return region; ++ ++ return NULL; ++} ++ ++struct ndctl_dimm *util_dimm_filter_by_region(struct ndctl_dimm *dimm, ++ const char *ident) ++{ ++ struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); ++ struct ndctl_region *region; ++ struct ndctl_dimm *check; ++ ++ if (!ident || strcmp(ident, "all") == 0) ++ return dimm; ++ ++ ndctl_region_foreach(bus, region) { ++ if (!util_region_filter(region, ident)) ++ continue; ++ ndctl_dimm_foreach_in_region(region, check) ++ if (check == dimm) ++ return dimm; ++ } ++ ++ return NULL; ++} ++ ++struct ndctl_dimm *util_dimm_filter_by_namespace(struct ndctl_dimm *dimm, ++ const char *ident) ++{ ++ struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); ++ struct ndctl_namespace *ndns; ++ struct ndctl_region *region; ++ struct ndctl_dimm *check; ++ ++ if (!ident || strcmp(ident, "all") == 0) ++ return dimm; ++ ++ ndctl_region_foreach(bus, region) { ++ ndctl_namespace_foreach(region, ndns) { ++ if (!util_namespace_filter(ndns, ident)) ++ continue; ++ ndctl_dimm_foreach_in_region(region, check) ++ if (check == dimm) ++ return dimm; ++ } ++ } ++ ++ return NULL; ++} ++ ++struct ndctl_dimm *util_dimm_filter_by_numa_node(struct ndctl_dimm *dimm, ++ int numa_node) ++{ ++ struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm); ++ struct ndctl_region *region; ++ struct ndctl_dimm *check; ++ ++ if (numa_node == NUMA_NO_NODE) ++ return dimm; ++ ++ ndctl_region_foreach(bus, region) ++ ndctl_dimm_foreach_in_region(region, check) ++ if (check == dimm && ++ ndctl_region_get_numa_node(region) == numa_node) ++ return dimm; ++ ++ return NULL; ++} ++ ++struct ndctl_region *util_region_filter_by_namespace(struct ndctl_region *region, ++ const char *ident) ++{ ++ struct ndctl_namespace *ndns; ++ ++ if (!ident || strcmp(ident, "all") == 0) ++ return region; ++ ++ ndctl_namespace_foreach(region, ndns) ++ if (util_namespace_filter(ndns, ident)) ++ return region; ++ return NULL; ++} ++ ++enum ndctl_namespace_mode util_nsmode(const char *mode) ++{ ++ if (!mode) ++ return NDCTL_NS_MODE_UNKNOWN; ++ if (strcasecmp(mode, "memory") == 0) ++ return NDCTL_NS_MODE_FSDAX; ++ if (strcasecmp(mode, "fsdax") == 0) ++ return NDCTL_NS_MODE_FSDAX; ++ if (strcasecmp(mode, "sector") == 0) ++ return NDCTL_NS_MODE_SECTOR; ++ if (strcasecmp(mode, "safe") == 0) ++ return NDCTL_NS_MODE_SECTOR; ++ if (strcasecmp(mode, "dax") == 0) ++ return NDCTL_NS_MODE_DEVDAX; ++ if (strcasecmp(mode, "devdax") == 0) ++ return NDCTL_NS_MODE_DEVDAX; ++ if (strcasecmp(mode, "raw") == 0) ++ return NDCTL_NS_MODE_RAW; ++ ++ return NDCTL_NS_MODE_UNKNOWN; ++} ++ ++const char *util_nsmode_name(enum ndctl_namespace_mode mode) ++{ ++ static const char * const modes[] = { ++ [NDCTL_NS_MODE_FSDAX] = "fsdax", ++ [NDCTL_NS_MODE_DEVDAX] = "devdax", ++ [NDCTL_NS_MODE_RAW] = "raw", ++ [NDCTL_NS_MODE_SECTOR] = "sector", ++ [NDCTL_NS_MODE_UNKNOWN] = "unknown", ++ }; ++ ++ return modes[mode]; ++} ++ ++int util_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx, ++ struct util_filter_params *param) ++{ ++ struct ndctl_bus *bus; ++ unsigned int type = 0; ++ int numa_node = NUMA_NO_NODE; ++ char *end = NULL; ++ ++ if (param->type && (strcmp(param->type, "pmem") != 0 ++ && strcmp(param->type, "blk") != 0)) { ++ error("unknown type \"%s\" must be \"pmem\" or \"blk\"\n", ++ param->type); ++ return -EINVAL; ++ } ++ ++ if (param->type) { ++ if (strcmp(param->type, "pmem") == 0) ++ type = ND_DEVICE_REGION_PMEM; ++ else ++ type = ND_DEVICE_REGION_BLK; ++ } ++ ++ if (param->mode && util_nsmode(param->mode) == NDCTL_NS_MODE_UNKNOWN) { ++ error("invalid mode: '%s'\n", param->mode); ++ return -EINVAL; ++ } ++ ++ if (param->numa_node && strcmp(param->numa_node, "all") != 0) { ++ struct stat st; ++ ++ if (stat("/sys/devices/system/node", &st) != 0) { ++ error("This system does not support NUMA"); ++ return -EINVAL; ++ } ++ ++ numa_node = strtol(param->numa_node, &end, 0); ++ if (end == param->numa_node || end[0]) { ++ error("invalid numa_node: '%s'\n", param->numa_node); ++ return -EINVAL; ++ } ++ } ++ ++ ndctl_bus_foreach(ctx, bus) { ++ struct ndctl_region *region; ++ struct ndctl_dimm *dimm; ++ ++ if (!util_bus_filter(bus, param->bus) ++ || !util_bus_filter_by_dimm(bus, param->dimm) ++ || !util_bus_filter_by_region(bus, param->region) ++ || !util_bus_filter_by_namespace(bus, param->namespace)) ++ continue; ++ ++ if (!fctx->filter_bus(bus, fctx)) ++ continue; ++ ++ ndctl_dimm_foreach(bus, dimm) { ++ if (!fctx->filter_dimm) ++ break; ++ ++ if (!util_dimm_filter(dimm, param->dimm) ++ || !util_dimm_filter_by_region(dimm, ++ param->region) ++ || !util_dimm_filter_by_namespace(dimm, ++ param->namespace) ++ || !util_dimm_filter_by_numa_node(dimm, ++ numa_node)) ++ continue; ++ ++ fctx->filter_dimm(dimm, fctx); ++ } ++ ++ ndctl_region_foreach(bus, region) { ++ struct ndctl_namespace *ndns; ++ ++ if (!util_region_filter(region, param->region) ++ || !util_region_filter_by_dimm(region, ++ param->dimm) ++ || !util_region_filter_by_namespace(region, ++ param->namespace)) ++ continue; ++ ++ /* ++ * if numa_node attribute is not available for regions ++ * (which is true for pre 5.4 kernels), don't skip the ++ * region if namespace is also requested, let the ++ * namespace filter handle the NUMA node filtering. ++ */ ++ if (numa_node != NUMA_NO_NODE && ++ !ndctl_region_has_numa(region) && ++ !fctx->filter_namespace) { ++ fprintf(stderr, ++ "This kernel does not provide NUMA node information per-region\n"); ++ continue; ++ } ++ ++ if (ndctl_region_has_numa(region) && ++ numa_node != NUMA_NO_NODE && ++ ndctl_region_get_numa_node(region) != numa_node) ++ continue; ++ ++ if (type && ndctl_region_get_type(region) != type) ++ continue; ++ ++ if (!fctx->filter_region(region, fctx)) ++ continue; ++ ++ ndctl_namespace_foreach(region, ndns) { ++ enum ndctl_namespace_mode mode; ++ ++ if (!fctx->filter_namespace) ++ break; ++ if (!util_namespace_filter(ndns, param->namespace)) ++ continue; ++ ++ mode = ndctl_namespace_get_mode(ndns); ++ if (param->mode && util_nsmode(param->mode) != mode) ++ continue; ++ ++ if (numa_node != NUMA_NO_NODE && ++ ndctl_namespace_get_numa_node(ndns) != numa_node) ++ continue; ++ ++ fctx->filter_namespace(ndns, fctx); ++ } ++ } ++ } ++ return 0; ++} +diff -up ndctl-71.1/ndctl/filter.h.orig ndctl-71.1/ndctl/filter.h +--- ndctl-71.1/ndctl/filter.h.orig 2022-10-07 15:51:03.929535779 -0400 ++++ ndctl-71.1/ndctl/filter.h 2022-10-07 15:51:03.908535707 -0400 +@@ -0,0 +1,86 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ ++#ifndef _NDCTL_UTIL_FILTER_H_ ++#define _NDCTL_UTIL_FILTER_H_ ++#include ++#include ++ ++struct ndctl_bus *util_bus_filter(struct ndctl_bus *bus, const char *ident); ++struct ndctl_region *util_region_filter(struct ndctl_region *region, ++ const char *ident); ++struct ndctl_namespace *util_namespace_filter(struct ndctl_namespace *ndns, ++ const char *ident); ++struct ndctl_dimm *util_dimm_filter(struct ndctl_dimm *dimm, const char *ident); ++struct ndctl_bus *util_bus_filter_by_dimm(struct ndctl_bus *bus, ++ const char *ident); ++struct ndctl_bus *util_bus_filter_by_region(struct ndctl_bus *bus, ++ const char *ident); ++struct ndctl_bus *util_bus_filter_by_namespace(struct ndctl_bus *bus, ++ const char *ident); ++struct ndctl_region *util_region_filter_by_dimm(struct ndctl_region *region, ++ const char *ident); ++struct ndctl_dimm *util_dimm_filter_by_region(struct ndctl_dimm *dimm, ++ const char *ident); ++struct ndctl_dimm *util_dimm_filter_by_namespace(struct ndctl_dimm *dimm, ++ const char *ident); ++struct ndctl_region *util_region_filter_by_namespace(struct ndctl_region *region, ++ const char *ident); ++ ++enum ndctl_namespace_mode util_nsmode(const char *mode); ++const char *util_nsmode_name(enum ndctl_namespace_mode mode); ++ ++struct json_object; ++ ++/* json object hierarchy for the util_filter_walk() performed by cmd_list() */ ++struct list_filter_arg { ++ struct json_object *jnamespaces; ++ struct json_object *jregions; ++ struct json_object *jdimms; ++ struct json_object *jbuses; ++ struct json_object *jregion; ++ struct json_object *jbus; ++ unsigned long flags; ++}; ++ ++struct monitor_filter_arg { ++ struct list_head dimms; ++ int maxfd_dimm; ++ int num_dimm; ++ unsigned long flags; ++}; ++ ++/* ++ * struct util_filter_ctx - control and callbacks for util_filter_walk() ++ * ->filter_bus() and ->filter_region() return bool because the ++ * child-object filter routines can not be called if the parent context ++ * is not established. ->filter_dimm() and ->filter_namespace() are leaf ++ * objects, so no child dependencies to check. ++ */ ++struct util_filter_ctx { ++ bool (*filter_bus)(struct ndctl_bus *bus, struct util_filter_ctx *ctx); ++ void (*filter_dimm)(struct ndctl_dimm *dimm, struct util_filter_ctx *ctx); ++ bool (*filter_region)(struct ndctl_region *region, ++ struct util_filter_ctx *ctx); ++ void (*filter_namespace)(struct ndctl_namespace *ndns, ++ struct util_filter_ctx *ctx); ++ union { ++ void *arg; ++ struct list_filter_arg *list; ++ struct monitor_filter_arg *monitor; ++ }; ++}; ++ ++struct util_filter_params { ++ const char *bus; ++ const char *region; ++ const char *type; ++ const char *dimm; ++ const char *mode; ++ const char *namespace; ++ const char *numa_node; ++}; ++ ++struct ndctl_ctx; ++int util_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx, ++ struct util_filter_params *param); ++#endif /* _NDCTL_UTIL_FILTER_H_ */ +diff -up ndctl-71.1/ndctl/inject-error.c.orig ndctl-71.1/ndctl/inject-error.c +--- ndctl-71.1/ndctl/inject-error.c.orig 2022-10-07 15:50:40.081454584 -0400 ++++ ndctl-71.1/ndctl/inject-error.c 2022-10-07 15:51:03.909535711 -0400 +@@ -12,12 +12,11 @@ + #include + #include + +-#include + #include + #include + #include + #include +-#include ++#include + #include + #include + #include +@@ -26,6 +25,9 @@ + #include + #include + ++#include "filter.h" ++#include "json.h" ++ + static bool verbose; + static struct parameters { + const char *bus; +diff -up ndctl-71.1/ndctl/inject-smart.c.orig ndctl-71.1/ndctl/inject-smart.c +--- ndctl-71.1/ndctl/inject-smart.c.orig 2022-10-07 15:50:40.081454584 -0400 ++++ ndctl-71.1/ndctl/inject-smart.c 2022-10-07 15:51:03.910535714 -0400 +@@ -13,12 +13,11 @@ + #include + #include + +-#include + #include + #include + #include + #include +-#include ++#include + #include + #include + #include +@@ -27,6 +26,9 @@ + #include + #include + ++#include "filter.h" ++#include "json.h" ++ + static struct parameters { + const char *bus; + const char *dimm; +diff -up ndctl-71.1/ndctl/json-smart.c.orig ndctl-71.1/ndctl/json-smart.c +--- ndctl-71.1/ndctl/json-smart.c.orig 2022-10-07 15:51:03.929535779 -0400 ++++ ndctl-71.1/ndctl/json-smart.c 2022-10-07 15:51:03.910535714 -0400 +@@ -0,0 +1,214 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "json.h" ++ ++static void smart_threshold_to_json(struct ndctl_dimm *dimm, ++ struct json_object *jhealth) ++{ ++ unsigned int alarm_control; ++ struct json_object *jobj; ++ struct ndctl_cmd *cmd; ++ int rc; ++ ++ cmd = ndctl_dimm_cmd_new_smart_threshold(dimm); ++ if (!cmd) ++ return; ++ ++ rc = ndctl_cmd_submit_xlat(cmd); ++ if (rc < 0) ++ goto out; ++ ++ alarm_control = ndctl_cmd_smart_threshold_get_alarm_control(cmd); ++ if (alarm_control & ND_SMART_TEMP_TRIP) { ++ unsigned int temp; ++ double t; ++ ++ jobj = json_object_new_boolean(true); ++ if (jobj) ++ json_object_object_add(jhealth, ++ "alarm_enabled_media_temperature", jobj); ++ temp = ndctl_cmd_smart_threshold_get_temperature(cmd); ++ t = ndctl_decode_smart_temperature(temp); ++ jobj = json_object_new_double(t); ++ if (jobj) ++ json_object_object_add(jhealth, ++ "temperature_threshold", jobj); ++ } else { ++ jobj = json_object_new_boolean(false); ++ if (jobj) ++ json_object_object_add(jhealth, ++ "alarm_enabled_media_temperature", jobj); ++ } ++ ++ if (alarm_control & ND_SMART_CTEMP_TRIP) { ++ unsigned int temp; ++ double t; ++ ++ jobj = json_object_new_boolean(true); ++ if (jobj) ++ json_object_object_add(jhealth, ++ "alarm_enabled_ctrl_temperature", jobj); ++ temp = ndctl_cmd_smart_threshold_get_ctrl_temperature(cmd); ++ t = ndctl_decode_smart_temperature(temp); ++ jobj = json_object_new_double(t); ++ if (jobj) ++ json_object_object_add(jhealth, ++ "controller_temperature_threshold", jobj); ++ } else { ++ jobj = json_object_new_boolean(false); ++ if (jobj) ++ json_object_object_add(jhealth, ++ "alarm_enabled_ctrl_temperature", jobj); ++ } ++ ++ if (alarm_control & ND_SMART_SPARE_TRIP) { ++ unsigned int spares; ++ ++ jobj = json_object_new_boolean(true); ++ if (jobj) ++ json_object_object_add(jhealth, ++ "alarm_enabled_spares", jobj); ++ spares = ndctl_cmd_smart_threshold_get_spares(cmd); ++ jobj = json_object_new_int(spares); ++ if (jobj) ++ json_object_object_add(jhealth, ++ "spares_threshold", jobj); ++ } else { ++ jobj = json_object_new_boolean(false); ++ if (jobj) ++ json_object_object_add(jhealth, ++ "alarm_enabled_spares", jobj); ++ } ++ ++ out: ++ ndctl_cmd_unref(cmd); ++} ++ ++struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm) ++{ ++ struct json_object *jhealth = json_object_new_object(); ++ struct json_object *jobj; ++ struct ndctl_cmd *cmd; ++ unsigned int flags; ++ int rc; ++ ++ if (!jhealth) ++ return NULL; ++ ++ cmd = ndctl_dimm_cmd_new_smart(dimm); ++ if (!cmd) ++ goto err; ++ ++ rc = ndctl_cmd_submit_xlat(cmd); ++ if (rc < 0) { ++ jobj = json_object_new_string("unknown"); ++ if (jobj) ++ json_object_object_add(jhealth, "health_state", jobj); ++ goto out; ++ } ++ ++ flags = ndctl_cmd_smart_get_flags(cmd); ++ if (flags & ND_SMART_HEALTH_VALID) { ++ unsigned int health = ndctl_cmd_smart_get_health(cmd); ++ ++ if (health & ND_SMART_FATAL_HEALTH) ++ jobj = json_object_new_string("fatal"); ++ else if (health & ND_SMART_CRITICAL_HEALTH) ++ jobj = json_object_new_string("critical"); ++ else if (health & ND_SMART_NON_CRITICAL_HEALTH) ++ jobj = json_object_new_string("non-critical"); ++ else ++ jobj = json_object_new_string("ok"); ++ if (jobj) ++ json_object_object_add(jhealth, "health_state", jobj); ++ } ++ ++ if (flags & ND_SMART_TEMP_VALID) { ++ unsigned int temp = ndctl_cmd_smart_get_temperature(cmd); ++ double t = ndctl_decode_smart_temperature(temp); ++ ++ jobj = json_object_new_double(t); ++ if (jobj) ++ json_object_object_add(jhealth, "temperature_celsius", jobj); ++ } ++ ++ if (flags & ND_SMART_CTEMP_VALID) { ++ unsigned int temp = ndctl_cmd_smart_get_ctrl_temperature(cmd); ++ double t = ndctl_decode_smart_temperature(temp); ++ ++ jobj = json_object_new_double(t); ++ if (jobj) ++ json_object_object_add(jhealth, ++ "controller_temperature_celsius", jobj); ++ } ++ ++ if (flags & ND_SMART_SPARES_VALID) { ++ unsigned int spares = ndctl_cmd_smart_get_spares(cmd); ++ ++ jobj = json_object_new_int(spares); ++ if (jobj) ++ json_object_object_add(jhealth, "spares_percentage", jobj); ++ } ++ ++ if (flags & ND_SMART_ALARM_VALID) { ++ unsigned int alarm_flags = ndctl_cmd_smart_get_alarm_flags(cmd); ++ bool temp_flag = !!(alarm_flags & ND_SMART_TEMP_TRIP); ++ bool ctrl_temp_flag = !!(alarm_flags & ND_SMART_CTEMP_TRIP); ++ bool spares_flag = !!(alarm_flags & ND_SMART_SPARE_TRIP); ++ ++ jobj = json_object_new_boolean(temp_flag); ++ if (jobj) ++ json_object_object_add(jhealth, "alarm_temperature", jobj); ++ ++ jobj = json_object_new_boolean(ctrl_temp_flag); ++ if (jobj) ++ json_object_object_add(jhealth, "alarm_controller_temperature", jobj); ++ ++ jobj = json_object_new_boolean(spares_flag); ++ if (jobj) ++ json_object_object_add(jhealth, "alarm_spares", jobj); ++ } ++ ++ smart_threshold_to_json(dimm, jhealth); ++ ++ if (flags & ND_SMART_USED_VALID) { ++ unsigned int life_used = ndctl_cmd_smart_get_life_used(cmd); ++ ++ jobj = json_object_new_int(life_used); ++ if (jobj) ++ json_object_object_add(jhealth, "life_used_percentage", jobj); ++ } ++ ++ if (flags & ND_SMART_SHUTDOWN_VALID) { ++ unsigned int shutdown = ndctl_cmd_smart_get_shutdown_state(cmd); ++ ++ jobj = json_object_new_string(shutdown ? "dirty" : "clean"); ++ if (jobj) ++ json_object_object_add(jhealth, "shutdown_state", jobj); ++ } ++ ++ if (flags & ND_SMART_SHUTDOWN_COUNT_VALID) { ++ unsigned int shutdown = ndctl_cmd_smart_get_shutdown_count(cmd); ++ ++ jobj = json_object_new_int(shutdown); ++ if (jobj) ++ json_object_object_add(jhealth, "shutdown_count", jobj); ++ } ++ ++ ndctl_cmd_unref(cmd); ++ return jhealth; ++ err: ++ json_object_put(jhealth); ++ jhealth = NULL; ++ out: ++ if (cmd) ++ ndctl_cmd_unref(cmd); ++ return jhealth; ++} +diff -up ndctl-71.1/ndctl/json.c.orig ndctl-71.1/ndctl/json.c +--- ndctl-71.1/ndctl/json.c.orig 2022-10-07 15:51:03.912535721 -0400 ++++ ndctl-71.1/ndctl/json.c 2022-10-07 15:51:03.912535721 -0400 +@@ -0,0 +1,1114 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "json.h" ++#include "ndctl.h" ++#include "../daxctl/json.h" ++ ++struct json_object *util_bus_to_json(struct ndctl_bus *bus, unsigned long flags) ++{ ++ struct json_object *jbus = json_object_new_object(); ++ struct json_object *jobj, *fw_obj = NULL; ++ int scrub; ++ ++ if (!jbus) ++ return NULL; ++ ++ jobj = json_object_new_string(ndctl_bus_get_provider(bus)); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jbus, "provider", jobj); ++ ++ jobj = json_object_new_string(ndctl_bus_get_devname(bus)); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jbus, "dev", jobj); ++ ++ scrub = ndctl_bus_get_scrub_state(bus); ++ if (scrub < 0) ++ return jbus; ++ ++ jobj = json_object_new_string(scrub ? "active" : "idle"); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jbus, "scrub_state", jobj); ++ ++ if (flags & UTIL_JSON_FIRMWARE) { ++ struct ndctl_dimm *dimm; ++ ++ /* ++ * Skip displaying firmware activation capability if no ++ * DIMMs support firmware update. ++ */ ++ ndctl_dimm_foreach(bus, dimm) ++ if (ndctl_dimm_fw_update_supported(dimm) == 0) { ++ fw_obj = json_object_new_object(); ++ break; ++ } ++ } ++ ++ if (fw_obj) { ++ enum ndctl_fwa_state state; ++ enum ndctl_fwa_method method; ++ ++ jobj = NULL; ++ method = ndctl_bus_get_fw_activate_method(bus); ++ if (method == NDCTL_FWA_METHOD_RESET) ++ jobj = json_object_new_string("reset"); ++ if (method == NDCTL_FWA_METHOD_SUSPEND) ++ jobj = json_object_new_string("suspend"); ++ if (method == NDCTL_FWA_METHOD_LIVE) ++ jobj = json_object_new_string("live"); ++ if (jobj) ++ json_object_object_add(fw_obj, "activate_method", jobj); ++ ++ jobj = NULL; ++ state = ndctl_bus_get_fw_activate_state(bus); ++ if (state == NDCTL_FWA_ARMED) ++ jobj = json_object_new_string("armed"); ++ if (state == NDCTL_FWA_IDLE) ++ jobj = json_object_new_string("idle"); ++ if (state == NDCTL_FWA_ARM_OVERFLOW) ++ jobj = json_object_new_string("overflow"); ++ if (jobj) ++ json_object_object_add(fw_obj, "activate_state", jobj); ++ ++ json_object_object_add(jbus, "firmware", fw_obj); ++ } ++ ++ return jbus; ++ err: ++ json_object_put(jbus); ++ return NULL; ++} ++ ++ ++ ++struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm, ++ unsigned long flags) ++{ ++ struct json_object *jfirmware = json_object_new_object(); ++ bool can_update, need_powercycle; ++ enum ndctl_fwa_result result; ++ enum ndctl_fwa_state state; ++ struct json_object *jobj; ++ struct ndctl_cmd *cmd; ++ uint64_t run, next; ++ int rc; ++ ++ if (!jfirmware) ++ return NULL; ++ ++ cmd = ndctl_dimm_cmd_new_fw_get_info(dimm); ++ if (!cmd) ++ goto err; ++ ++ rc = ndctl_cmd_submit(cmd); ++ if ((rc < 0) || ndctl_cmd_fw_xlat_firmware_status(cmd) != FW_SUCCESS) { ++ jobj = util_json_object_hex(-1, flags); ++ if (jobj) ++ json_object_object_add(jfirmware, "current_version", ++ jobj); ++ goto out; ++ } ++ ++ run = ndctl_cmd_fw_info_get_run_version(cmd); ++ if (run == ULLONG_MAX) { ++ jobj = util_json_object_hex(-1, flags); ++ if (jobj) ++ json_object_object_add(jfirmware, "current_version", ++ jobj); ++ goto out; ++ } ++ ++ jobj = util_json_object_hex(run, flags); ++ if (jobj) ++ json_object_object_add(jfirmware, "current_version", jobj); ++ ++ rc = ndctl_dimm_fw_update_supported(dimm); ++ can_update = rc == 0; ++ jobj = json_object_new_boolean(can_update); ++ if (jobj) ++ json_object_object_add(jfirmware, "can_update", jobj); ++ ++ ++ next = ndctl_cmd_fw_info_get_updated_version(cmd); ++ if (next == ULLONG_MAX) { ++ jobj = util_json_object_hex(-1, flags); ++ if (jobj) ++ json_object_object_add(jfirmware, "next_version", ++ jobj); ++ goto out; ++ } ++ ++ if (!next) ++ goto out; ++ ++ jobj = util_json_object_hex(next, flags); ++ if (jobj) ++ json_object_object_add(jfirmware, ++ "next_version", jobj); ++ ++ state = ndctl_dimm_get_fw_activate_state(dimm); ++ switch (state) { ++ case NDCTL_FWA_IDLE: ++ jobj = json_object_new_string("idle"); ++ break; ++ case NDCTL_FWA_ARMED: ++ jobj = json_object_new_string("armed"); ++ break; ++ case NDCTL_FWA_BUSY: ++ jobj = json_object_new_string("busy"); ++ break; ++ default: ++ jobj = NULL; ++ break; ++ } ++ if (jobj) ++ json_object_object_add(jfirmware, "activate_state", jobj); ++ ++ result = ndctl_dimm_get_fw_activate_result(dimm); ++ switch (result) { ++ case NDCTL_FWA_RESULT_NONE: ++ case NDCTL_FWA_RESULT_SUCCESS: ++ case NDCTL_FWA_RESULT_NOTSTAGED: ++ /* ++ * If a 'next' firmware version is staged then this ++ * result is stale, if the activation succeeds that is ++ * indicated by not finding a 'next' entry. ++ */ ++ need_powercycle = false; ++ break; ++ case NDCTL_FWA_RESULT_NEEDRESET: ++ case NDCTL_FWA_RESULT_FAIL: ++ default: ++ /* ++ * If the last activation failed, or if the activation ++ * result is unavailable it is always the case that the ++ * only remediation is powercycle. ++ */ ++ need_powercycle = true; ++ break; ++ } ++ ++ if (need_powercycle) { ++ jobj = json_object_new_boolean(true); ++ if (!jobj) ++ goto out; ++ json_object_object_add(jfirmware, "need_powercycle", jobj); ++ } ++ ++ ndctl_cmd_unref(cmd); ++ return jfirmware; ++ ++err: ++ json_object_put(jfirmware); ++ jfirmware = NULL; ++out: ++ if (cmd) ++ ndctl_cmd_unref(cmd); ++ return jfirmware; ++} ++ ++ ++ ++struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm, ++ unsigned long flags) ++{ ++ struct json_object *jdimm = json_object_new_object(); ++ const char *id = ndctl_dimm_get_unique_id(dimm); ++ unsigned int handle = ndctl_dimm_get_handle(dimm); ++ unsigned short phys_id = ndctl_dimm_get_phys_id(dimm); ++ struct json_object *jobj; ++ enum ndctl_security_state sstate; ++ ++ if (!jdimm) ++ return NULL; ++ ++ jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jdimm, "dev", jobj); ++ ++ if (id) { ++ jobj = json_object_new_string(id); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jdimm, "id", jobj); ++ } ++ ++ if (handle < UINT_MAX) { ++ jobj = util_json_object_hex(handle, flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jdimm, "handle", jobj); ++ } ++ ++ if (phys_id < USHRT_MAX) { ++ jobj = util_json_object_hex(phys_id, flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jdimm, "phys_id", jobj); ++ } ++ ++ if (!ndctl_dimm_is_enabled(dimm)) { ++ jobj = json_object_new_string("disabled"); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jdimm, "state", jobj); ++ } ++ ++ if (ndctl_dimm_failed_map(dimm)) { ++ jobj = json_object_new_boolean(true); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jdimm, "flag_failed_map", jobj); ++ } ++ ++ if (ndctl_dimm_failed_save(dimm)) { ++ jobj = json_object_new_boolean(true); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jdimm, "flag_failed_save", jobj); ++ } ++ ++ if (ndctl_dimm_failed_arm(dimm)) { ++ jobj = json_object_new_boolean(true); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jdimm, "flag_failed_arm", jobj); ++ } ++ ++ if (ndctl_dimm_failed_restore(dimm)) { ++ jobj = json_object_new_boolean(true); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jdimm, "flag_failed_restore", jobj); ++ } ++ ++ if (ndctl_dimm_failed_flush(dimm)) { ++ jobj = json_object_new_boolean(true); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jdimm, "flag_failed_flush", jobj); ++ } ++ ++ if (ndctl_dimm_smart_pending(dimm)) { ++ jobj = json_object_new_boolean(true); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jdimm, "flag_smart_event", jobj); ++ } ++ ++ sstate = ndctl_dimm_get_security(dimm); ++ if (sstate == NDCTL_SECURITY_DISABLED) ++ jobj = json_object_new_string("disabled"); ++ else if (sstate == NDCTL_SECURITY_UNLOCKED) ++ jobj = json_object_new_string("unlocked"); ++ else if (sstate == NDCTL_SECURITY_LOCKED) ++ jobj = json_object_new_string("locked"); ++ else if (sstate == NDCTL_SECURITY_FROZEN) ++ jobj = json_object_new_string("frozen"); ++ else if (sstate == NDCTL_SECURITY_OVERWRITE) ++ jobj = json_object_new_string("overwrite"); ++ else ++ jobj = NULL; ++ if (jobj) ++ json_object_object_add(jdimm, "security", jobj); ++ ++ if (ndctl_dimm_security_is_frozen(dimm)) { ++ jobj = json_object_new_boolean(true); ++ if (jobj) ++ json_object_object_add(jdimm, "security_frozen", jobj); ++ } ++ ++ if (flags & UTIL_JSON_FIRMWARE) { ++ struct json_object *jfirmware; ++ ++ jfirmware = util_dimm_firmware_to_json(dimm, flags); ++ if (jfirmware) ++ json_object_object_add(jdimm, "firmware", jfirmware); ++ } ++ ++ return jdimm; ++ err: ++ json_object_put(jdimm); ++ return NULL; ++} ++ ++#define _SZ(get_max, get_elem, type) \ ++static struct json_object *util_##type##_build_size_array(struct ndctl_##type *arg) \ ++{ \ ++ struct json_object *arr = json_object_new_array(); \ ++ int i; \ ++ \ ++ if (!arr) \ ++ return NULL; \ ++ \ ++ for (i = 0; i < get_max(arg); i++) { \ ++ struct json_object *jobj; \ ++ int64_t align; \ ++ \ ++ align = get_elem(arg, i); \ ++ jobj = json_object_new_int64(align); \ ++ if (!jobj) \ ++ goto err; \ ++ json_object_array_add(arr, jobj); \ ++ } \ ++ \ ++ return arr; \ ++err: \ ++ json_object_put(arr); \ ++ return NULL; \ ++} ++#define SZ(type, kind) _SZ(ndctl_##type##_get_num_##kind##s, \ ++ ndctl_##type##_get_supported_##kind, type) ++SZ(pfn, alignment) ++SZ(dax, alignment) ++SZ(btt, sector_size) ++ ++struct json_object *util_region_capabilities_to_json(struct ndctl_region *region) ++{ ++ struct json_object *jcaps, *jcap, *jobj; ++ struct ndctl_btt *btt = ndctl_region_get_btt_seed(region); ++ struct ndctl_pfn *pfn = ndctl_region_get_pfn_seed(region); ++ struct ndctl_dax *dax = ndctl_region_get_dax_seed(region); ++ ++ if (!btt || !pfn || !dax) ++ return NULL; ++ ++ jcaps = json_object_new_array(); ++ if (!jcaps) ++ return NULL; ++ ++ if (btt) { ++ jcap = json_object_new_object(); ++ if (!jcap) ++ goto err; ++ json_object_array_add(jcaps, jcap); ++ ++ jobj = json_object_new_string("sector"); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jcap, "mode", jobj); ++ jobj = util_btt_build_size_array(btt); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jcap, "sector_sizes", jobj); ++ } ++ ++ if (pfn) { ++ jcap = json_object_new_object(); ++ if (!jcap) ++ goto err; ++ json_object_array_add(jcaps, jcap); ++ ++ jobj = json_object_new_string("fsdax"); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jcap, "mode", jobj); ++ jobj = util_pfn_build_size_array(pfn); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jcap, "alignments", jobj); ++ } ++ ++ if (dax) { ++ jcap = json_object_new_object(); ++ if (!jcap) ++ goto err; ++ json_object_array_add(jcaps, jcap); ++ ++ jobj = json_object_new_string("devdax"); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jcap, "mode", jobj); ++ jobj = util_dax_build_size_array(dax); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jcap, "alignments", jobj); ++ } ++ ++ return jcaps; ++err: ++ json_object_put(jcaps); ++ return NULL; ++} ++ ++ ++static int compare_dimm_number(const void *p1, const void *p2) ++{ ++ struct ndctl_dimm *dimm1 = *(struct ndctl_dimm **)p1; ++ struct ndctl_dimm *dimm2 = *(struct ndctl_dimm **)p2; ++ const char *dimm1_name = ndctl_dimm_get_devname(dimm1); ++ const char *dimm2_name = ndctl_dimm_get_devname(dimm2); ++ int num1, num2; ++ ++ if (sscanf(dimm1_name, "nmem%d", &num1) != 1) ++ num1 = 0; ++ if (sscanf(dimm2_name, "nmem%d", &num2) != 1) ++ num2 = 0; ++ ++ return num1 - num2; ++} ++ ++static struct json_object *badblocks_to_jdimms(struct ndctl_region *region, ++ unsigned long long addr, unsigned long len) ++{ ++ struct ndctl_bus *bus = ndctl_region_get_bus(region); ++ int count = ndctl_region_get_interleave_ways(region); ++ unsigned long long end = addr + len; ++ struct json_object *jdimms, *jobj; ++ struct ndctl_dimm **dimms, *dimm; ++ int found, i; ++ ++ jdimms = json_object_new_array(); ++ if (!jdimms) ++ return NULL; ++ ++ dimms = calloc(count, sizeof(struct ndctl_dimm *)); ++ if (!dimms) ++ goto err_dimms; ++ ++ for (found = 0; found < count && addr < end; addr += 512) { ++ dimm = ndctl_bus_get_dimm_by_physical_address(bus, addr); ++ if (!dimm) ++ continue; ++ ++ for (i = 0; i < count; i++) ++ if (dimms[i] == dimm) ++ break; ++ if (i >= count) ++ dimms[found++] = dimm; ++ } ++ ++ if (!found) ++ goto err_found; ++ ++ qsort(dimms, found, sizeof(dimm), compare_dimm_number); ++ ++ for (i = 0; i < found; i++) { ++ const char *devname = ndctl_dimm_get_devname(dimms[i]); ++ ++ jobj = json_object_new_string(devname); ++ if (!jobj) ++ break; ++ json_object_array_add(jdimms, jobj); ++ } ++ ++ if (!i) ++ goto err_found; ++ free(dimms); ++ return jdimms; ++ ++err_found: ++ free(dimms); ++err_dimms: ++ json_object_put(jdimms); ++ return NULL; ++} ++ ++struct json_object *util_region_badblocks_to_json(struct ndctl_region *region, ++ unsigned int *bb_count, unsigned long flags) ++{ ++ struct json_object *jbb = NULL, *jbbs = NULL, *jobj; ++ struct badblock *bb; ++ int bbs = 0; ++ ++ if (flags & UTIL_JSON_MEDIA_ERRORS) { ++ jbbs = json_object_new_array(); ++ if (!jbbs) ++ return NULL; ++ } ++ ++ ndctl_region_badblock_foreach(region, bb) { ++ struct json_object *jdimms; ++ unsigned long long addr; ++ ++ bbs += bb->len; ++ ++ /* recheck so we can still get the badblocks_count from above */ ++ if (!(flags & UTIL_JSON_MEDIA_ERRORS)) ++ continue; ++ ++ /* get start address of region */ ++ addr = ndctl_region_get_resource(region); ++ if (addr == ULLONG_MAX) ++ goto err_array; ++ ++ /* get address of bad block */ ++ addr += bb->offset << 9; ++ ++ jbb = json_object_new_object(); ++ if (!jbb) ++ goto err_array; ++ ++ jobj = json_object_new_int64(bb->offset); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jbb, "offset", jobj); ++ ++ jobj = json_object_new_int(bb->len); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jbb, "length", jobj); ++ ++ jdimms = badblocks_to_jdimms(region, addr, bb->len << 9); ++ if (jdimms) ++ json_object_object_add(jbb, "dimms", jdimms); ++ json_object_array_add(jbbs, jbb); ++ } ++ ++ *bb_count = bbs; ++ ++ if (bbs) ++ return jbbs; ++ ++ err: ++ json_object_put(jbb); ++ err_array: ++ json_object_put(jbbs); ++ return NULL; ++} ++ ++static struct json_object *util_namespace_badblocks_to_json( ++ struct ndctl_namespace *ndns, ++ unsigned int *bb_count, unsigned long flags) ++{ ++ struct json_object *jbb = NULL, *jbbs = NULL, *jobj; ++ struct badblock *bb; ++ int bbs = 0; ++ ++ if (flags & UTIL_JSON_MEDIA_ERRORS) { ++ jbbs = json_object_new_array(); ++ if (!jbbs) ++ return NULL; ++ } else ++ return NULL; ++ ++ ndctl_namespace_badblock_foreach(ndns, bb) { ++ bbs += bb->len; ++ ++ /* recheck so we can still get the badblocks_count from above */ ++ if (!(flags & UTIL_JSON_MEDIA_ERRORS)) ++ continue; ++ ++ jbb = json_object_new_object(); ++ if (!jbb) ++ goto err_array; ++ ++ jobj = json_object_new_int64(bb->offset); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jbb, "offset", jobj); ++ ++ jobj = json_object_new_int(bb->len); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jbb, "length", jobj); ++ json_object_array_add(jbbs, jbb); ++ } ++ ++ *bb_count = bbs; ++ ++ if (bbs) ++ return jbbs; ++ ++ err: ++ json_object_put(jbb); ++ err_array: ++ json_object_put(jbbs); ++ return NULL; ++} ++ ++static struct json_object *dev_badblocks_to_json(struct ndctl_region *region, ++ unsigned long long dev_begin, unsigned long long dev_size, ++ unsigned int *bb_count, unsigned long flags) ++{ ++ struct json_object *jbb = NULL, *jbbs = NULL, *jobj; ++ unsigned long long region_begin, dev_end, offset; ++ unsigned int len, bbs = 0; ++ struct badblock *bb; ++ ++ region_begin = ndctl_region_get_resource(region); ++ if (region_begin == ULLONG_MAX) ++ return NULL; ++ ++ dev_end = dev_begin + dev_size - 1; ++ ++ if (flags & UTIL_JSON_MEDIA_ERRORS) { ++ jbbs = json_object_new_array(); ++ if (!jbbs) ++ return NULL; ++ } ++ ++ ndctl_region_badblock_foreach(region, bb) { ++ unsigned long long bb_begin, bb_end, begin, end; ++ struct json_object *jdimms; ++ ++ bb_begin = region_begin + (bb->offset << 9); ++ bb_end = bb_begin + (bb->len << 9) - 1; ++ ++ if (bb_end <= dev_begin || bb_begin >= dev_end) ++ continue; ++ ++ if (bb_begin < dev_begin) ++ begin = dev_begin; ++ else ++ begin = bb_begin; ++ ++ if (bb_end > dev_end) ++ end = dev_end; ++ else ++ end = bb_end; ++ ++ offset = (begin - dev_begin) >> 9; ++ len = (end - begin + 1) >> 9; ++ ++ bbs += len; ++ ++ /* recheck so we can still get the badblocks_count from above */ ++ if (!(flags & UTIL_JSON_MEDIA_ERRORS)) ++ continue; ++ ++ jbb = json_object_new_object(); ++ if (!jbb) ++ goto err_array; ++ ++ jobj = json_object_new_int64(offset); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jbb, "offset", jobj); ++ ++ jobj = json_object_new_int(len); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jbb, "length", jobj); ++ ++ jdimms = badblocks_to_jdimms(region, begin, len << 9); ++ if (jdimms) ++ json_object_object_add(jbb, "dimms", jdimms); ++ ++ json_object_array_add(jbbs, jbb); ++ } ++ ++ *bb_count = bbs; ++ ++ if (bbs) ++ return jbbs; ++ ++ err: ++ json_object_put(jbb); ++ err_array: ++ json_object_put(jbbs); ++ return NULL; ++} ++ ++static struct json_object *util_pfn_badblocks_to_json(struct ndctl_pfn *pfn, ++ unsigned int *bb_count, unsigned long flags) ++{ ++ struct ndctl_region *region = ndctl_pfn_get_region(pfn); ++ unsigned long long pfn_begin, pfn_size; ++ ++ pfn_begin = ndctl_pfn_get_resource(pfn); ++ if (pfn_begin == ULLONG_MAX) { ++ struct ndctl_namespace *ndns = ndctl_pfn_get_namespace(pfn); ++ ++ return util_namespace_badblocks_to_json(ndns, bb_count, flags); ++ } ++ ++ pfn_size = ndctl_pfn_get_size(pfn); ++ if (pfn_size == ULLONG_MAX) ++ return NULL; ++ ++ return dev_badblocks_to_json(region, pfn_begin, pfn_size, ++ bb_count, flags); ++} ++ ++static void util_btt_badblocks_to_json(struct ndctl_btt *btt, ++ unsigned int *bb_count) ++{ ++ struct ndctl_region *region = ndctl_btt_get_region(btt); ++ struct ndctl_namespace *ndns = ndctl_btt_get_namespace(btt); ++ unsigned long long begin, size; ++ ++ if (!ndns) ++ return; ++ ++ begin = ndctl_namespace_get_resource(ndns); ++ if (begin == ULLONG_MAX) ++ return; ++ ++ size = ndctl_namespace_get_size(ndns); ++ if (size == ULLONG_MAX) ++ return; ++ ++ /* ++ * The dev_badblocks_to_json() for BTT is not accurate with ++ * respect to data vs metadata badblocks, and is only useful for ++ * a potential bb_count. ++ * ++ * FIXME: switch to native BTT badblocks representation ++ * when / if the kernel provides it. ++ */ ++ dev_badblocks_to_json(region, begin, size, bb_count, 0); ++} ++static struct json_object *util_dax_badblocks_to_json(struct ndctl_dax *dax, ++ unsigned int *bb_count, unsigned long flags) ++{ ++ struct ndctl_region *region = ndctl_dax_get_region(dax); ++ unsigned long long dax_begin, dax_size; ++ ++ dax_begin = ndctl_dax_get_resource(dax); ++ if (dax_begin == ULLONG_MAX) ++ return NULL; ++ ++ dax_size = ndctl_dax_get_size(dax); ++ if (dax_size == ULLONG_MAX) ++ return NULL; ++ ++ return dev_badblocks_to_json(region, dax_begin, dax_size, ++ bb_count, flags); ++} ++ ++static struct json_object *util_raw_uuid(struct ndctl_namespace *ndns) ++{ ++ char buf[40]; ++ uuid_t raw_uuid; ++ ++ ndctl_namespace_get_uuid(ndns, raw_uuid); ++ if (uuid_is_null(raw_uuid)) ++ return NULL; ++ uuid_unparse(raw_uuid, buf); ++ return json_object_new_string(buf); ++} ++ ++static void util_raw_uuid_to_json(struct ndctl_namespace *ndns, ++ unsigned long flags, ++ struct json_object *jndns) ++{ ++ struct json_object *jobj; ++ ++ if (!(flags & UTIL_JSON_VERBOSE)) ++ return; ++ ++ jobj = util_raw_uuid(ndns); ++ if (!jobj) ++ return; ++ json_object_object_add(jndns, "raw_uuid", jobj); ++} ++ ++struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns, ++ unsigned long flags) ++{ ++ struct json_object *jndns = json_object_new_object(); ++ enum ndctl_pfn_loc loc = NDCTL_PFN_LOC_NONE; ++ struct json_object *jobj, *jbbs = NULL; ++ const char *locations[] = { ++ [NDCTL_PFN_LOC_NONE] = "none", ++ [NDCTL_PFN_LOC_RAM] = "mem", ++ [NDCTL_PFN_LOC_PMEM] = "dev", ++ }; ++ unsigned long long size = ULLONG_MAX; ++ unsigned int sector_size = UINT_MAX; ++ enum ndctl_namespace_mode mode; ++ const char *bdev = NULL, *name; ++ unsigned int bb_count = 0; ++ struct ndctl_btt *btt; ++ struct ndctl_pfn *pfn; ++ struct ndctl_dax *dax; ++ unsigned long align = 0; ++ char buf[40]; ++ uuid_t uuid; ++ int numa, target; ++ ++ if (!jndns) ++ return NULL; ++ ++ jobj = json_object_new_string(ndctl_namespace_get_devname(ndns)); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jndns, "dev", jobj); ++ ++ btt = ndctl_namespace_get_btt(ndns); ++ dax = ndctl_namespace_get_dax(ndns); ++ pfn = ndctl_namespace_get_pfn(ndns); ++ mode = ndctl_namespace_get_mode(ndns); ++ switch (mode) { ++ case NDCTL_NS_MODE_MEMORY: ++ if (pfn) { /* dynamic memory mode */ ++ size = ndctl_pfn_get_size(pfn); ++ loc = ndctl_pfn_get_location(pfn); ++ } else { /* native/static memory mode */ ++ size = ndctl_namespace_get_size(ndns); ++ loc = NDCTL_PFN_LOC_RAM; ++ } ++ jobj = json_object_new_string("fsdax"); ++ break; ++ case NDCTL_NS_MODE_DAX: ++ if (!dax) ++ goto err; ++ size = ndctl_dax_get_size(dax); ++ jobj = json_object_new_string("devdax"); ++ loc = ndctl_dax_get_location(dax); ++ break; ++ case NDCTL_NS_MODE_SECTOR: ++ if (!btt) ++ goto err; ++ jobj = json_object_new_string("sector"); ++ size = ndctl_btt_get_size(btt); ++ break; ++ case NDCTL_NS_MODE_RAW: ++ size = ndctl_namespace_get_size(ndns); ++ jobj = json_object_new_string("raw"); ++ break; ++ default: ++ jobj = NULL; ++ } ++ if (jobj) ++ json_object_object_add(jndns, "mode", jobj); ++ ++ if ((mode != NDCTL_NS_MODE_SECTOR) && (mode != NDCTL_NS_MODE_RAW)) { ++ jobj = json_object_new_string(locations[loc]); ++ if (jobj) ++ json_object_object_add(jndns, "map", jobj); ++ } ++ ++ if (size < ULLONG_MAX) { ++ jobj = util_json_object_size(size, flags); ++ if (jobj) ++ json_object_object_add(jndns, "size", jobj); ++ } ++ ++ if (btt) { ++ ndctl_btt_get_uuid(btt, uuid); ++ uuid_unparse(uuid, buf); ++ jobj = json_object_new_string(buf); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jndns, "uuid", jobj); ++ util_raw_uuid_to_json(ndns, flags, jndns); ++ bdev = ndctl_btt_get_block_device(btt); ++ } else if (pfn) { ++ align = ndctl_pfn_get_align(pfn); ++ ndctl_pfn_get_uuid(pfn, uuid); ++ uuid_unparse(uuid, buf); ++ jobj = json_object_new_string(buf); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jndns, "uuid", jobj); ++ util_raw_uuid_to_json(ndns, flags, jndns); ++ bdev = ndctl_pfn_get_block_device(pfn); ++ } else if (dax) { ++ struct daxctl_region *dax_region; ++ ++ dax_region = ndctl_dax_get_daxctl_region(dax); ++ align = ndctl_dax_get_align(dax); ++ ndctl_dax_get_uuid(dax, uuid); ++ uuid_unparse(uuid, buf); ++ jobj = json_object_new_string(buf); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jndns, "uuid", jobj); ++ util_raw_uuid_to_json(ndns, flags, jndns); ++ if ((flags & UTIL_JSON_DAX) && dax_region) { ++ jobj = util_daxctl_region_to_json(dax_region, NULL, ++ flags); ++ if (jobj) ++ json_object_object_add(jndns, "daxregion", jobj); ++ } else if (dax_region) { ++ struct daxctl_dev *dev; ++ ++ /* ++ * We can only find/list these device-dax ++ * details when the instance is enabled. ++ */ ++ dev = daxctl_dev_get_first(dax_region); ++ if (dev) { ++ name = daxctl_dev_get_devname(dev); ++ jobj = json_object_new_string(name); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jndns, "chardev", jobj); ++ } ++ } ++ } else if (ndctl_namespace_get_type(ndns) != ND_DEVICE_NAMESPACE_IO) { ++ ndctl_namespace_get_uuid(ndns, uuid); ++ uuid_unparse(uuid, buf); ++ jobj = json_object_new_string(buf); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jndns, "uuid", jobj); ++ bdev = ndctl_namespace_get_block_device(ndns); ++ } else ++ bdev = ndctl_namespace_get_block_device(ndns); ++ ++ if (btt) ++ sector_size = ndctl_btt_get_sector_size(btt); ++ else if (!dax) { ++ sector_size = ndctl_namespace_get_sector_size(ndns); ++ if (!sector_size || sector_size == UINT_MAX) ++ sector_size = 512; ++ } ++ ++ /* ++ * The kernel will default to a 512 byte sector size on PMEM ++ * namespaces that don't explicitly have a sector size. This ++ * happens because they use pre-v1.2 labels or because they ++ * don't have a label space (devtype=nd_namespace_io). ++ */ ++ if (sector_size < UINT_MAX) { ++ jobj = json_object_new_int(sector_size); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jndns, "sector_size", jobj); ++ } ++ ++ if (align) { ++ jobj = json_object_new_int64(align); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jndns, "align", jobj); ++ } ++ ++ if (bdev && bdev[0]) { ++ jobj = json_object_new_string(bdev); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jndns, "blockdev", jobj); ++ } ++ ++ if (!ndctl_namespace_is_active(ndns)) { ++ jobj = json_object_new_string("disabled"); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jndns, "state", jobj); ++ } ++ ++ name = ndctl_namespace_get_alt_name(ndns); ++ if (name && name[0]) { ++ jobj = json_object_new_string(name); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jndns, "name", jobj); ++ } ++ ++ numa = ndctl_namespace_get_numa_node(ndns); ++ if (numa >= 0 && flags & UTIL_JSON_VERBOSE) { ++ jobj = json_object_new_int(numa); ++ if (jobj) ++ json_object_object_add(jndns, "numa_node", jobj); ++ } ++ ++ target = ndctl_namespace_get_target_node(ndns); ++ if (target >= 0 && flags & UTIL_JSON_VERBOSE) { ++ jobj = json_object_new_int(target); ++ if (jobj) ++ json_object_object_add(jndns, "target_node", jobj); ++ } ++ ++ if (pfn) ++ jbbs = util_pfn_badblocks_to_json(pfn, &bb_count, flags); ++ else if (dax) ++ jbbs = util_dax_badblocks_to_json(dax, &bb_count, flags); ++ else if (btt) ++ util_btt_badblocks_to_json(btt, &bb_count); ++ else { ++ jbbs = util_region_badblocks_to_json( ++ ndctl_namespace_get_region(ndns), &bb_count, ++ flags); ++ if (!jbbs) ++ jbbs = util_namespace_badblocks_to_json(ndns, &bb_count, ++ flags); ++ } ++ ++ if (bb_count) { ++ jobj = json_object_new_int(bb_count); ++ if (!jobj) { ++ json_object_put(jbbs); ++ goto err; ++ } ++ json_object_object_add(jndns, "badblock_count", jobj); ++ } ++ ++ if ((flags & UTIL_JSON_MEDIA_ERRORS) && jbbs) ++ json_object_object_add(jndns, "badblocks", jbbs); ++ ++ return jndns; ++ err: ++ json_object_put(jndns); ++ return NULL; ++} ++ ++ ++struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping, ++ unsigned long flags) ++{ ++ struct json_object *jmapping = json_object_new_object(); ++ struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(mapping); ++ struct json_object *jobj; ++ int position; ++ ++ if (!jmapping) ++ return NULL; ++ ++ jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jmapping, "dimm", jobj); ++ ++ jobj = util_json_object_hex(ndctl_mapping_get_offset(mapping), flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jmapping, "offset", jobj); ++ ++ jobj = util_json_object_hex(ndctl_mapping_get_length(mapping), flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jmapping, "length", jobj); ++ ++ position = ndctl_mapping_get_position(mapping); ++ if (position >= 0) { ++ jobj = json_object_new_int(position); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jmapping, "position", jobj); ++ } ++ ++ return jmapping; ++ err: ++ json_object_put(jmapping); ++ return NULL; ++} ++ ++struct json_object *util_badblock_rec_to_json(u64 block, u64 count, ++ unsigned long flags) ++{ ++ struct json_object *jerr = json_object_new_object(); ++ struct json_object *jobj; ++ ++ if (!jerr) ++ return NULL; ++ ++ jobj = util_json_object_hex(block, flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jerr, "block", jobj); ++ ++ jobj = util_json_object_hex(count, flags); ++ if (!jobj) ++ goto err; ++ json_object_object_add(jerr, "count", jobj); ++ ++ return jerr; ++ err: ++ json_object_put(jerr); ++ return NULL; ++} +diff -up ndctl-71.1/ndctl/json.h.orig ndctl-71.1/ndctl/json.h +--- ndctl-71.1/ndctl/json.h.orig 2022-10-07 15:51:03.913535724 -0400 ++++ ndctl-71.1/ndctl/json.h 2022-10-07 15:51:03.913535724 -0400 +@@ -0,0 +1,24 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ ++#ifndef __NDCTL_UTIL_JSON_H__ ++#define __NDCTL_UTIL_JSON_H__ ++#include ++#include ++ ++struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns, ++ unsigned long flags); ++struct json_object *util_badblock_rec_to_json(u64 block, u64 count, ++ unsigned long flags); ++struct json_object *util_region_badblocks_to_json(struct ndctl_region *region, ++ unsigned int *bb_count, unsigned long flags); ++struct json_object *util_bus_to_json(struct ndctl_bus *bus, ++ unsigned long flags); ++struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm, ++ unsigned long flags); ++struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping, ++ unsigned long flags); ++struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm); ++struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm, ++ unsigned long flags); ++struct json_object *util_region_capabilities_to_json(struct ndctl_region *region); ++#endif /* __NDCTL_UTIL_JSON_H__ */ +diff -up ndctl-71.1/ndctl/keys.c.orig ndctl-71.1/ndctl/keys.c +--- ndctl-71.1/ndctl/keys.c.orig 2022-10-07 15:51:03.930535782 -0400 ++++ ndctl-71.1/ndctl/keys.c 2022-10-07 15:51:03.913535724 -0400 +@@ -0,0 +1,667 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2018-2020 Intel Corporation. All rights reserved. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "keys.h" ++ ++static int get_key_path(struct ndctl_dimm *dimm, char *path, ++ enum ndctl_key_type key_type) ++{ ++ char hostname[HOST_NAME_MAX]; ++ int rc; ++ ++ rc = gethostname(hostname, HOST_NAME_MAX); ++ if (rc < 0) { ++ fprintf(stderr, "gethostname: %s\n", strerror(errno)); ++ return -errno; ++ } ++ ++ switch (key_type) { ++ case ND_USER_OLD_KEY: ++ rc = sprintf(path, "%s/nvdimm-old_%s_%s.blob", ++ NDCTL_KEYS_DIR, ++ ndctl_dimm_get_unique_id(dimm), ++ hostname); ++ break; ++ case ND_USER_KEY: ++ rc = sprintf(path, "%s/nvdimm_%s_%s.blob", ++ NDCTL_KEYS_DIR, ++ ndctl_dimm_get_unique_id(dimm), ++ hostname); ++ break; ++ case ND_MASTER_OLD_KEY: ++ rc = sprintf(path, "%s/nvdimm-master-old_%s_%s.blob", ++ NDCTL_KEYS_DIR, ++ ndctl_dimm_get_unique_id(dimm), ++ hostname); ++ break; ++ case ND_MASTER_KEY: ++ rc = sprintf(path, "%s/nvdimm-master_%s_%s.blob", ++ NDCTL_KEYS_DIR, ++ ndctl_dimm_get_unique_id(dimm), ++ hostname); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (rc < 0) { ++ fprintf(stderr, "error setting path: %s\n", strerror(errno)); ++ return -errno; ++ } ++ ++ return 0; ++} ++ ++static int get_key_desc(struct ndctl_dimm *dimm, char *desc, ++ enum ndctl_key_type key_type) ++{ ++ int rc; ++ ++ switch (key_type) { ++ case ND_USER_OLD_KEY: ++ rc = sprintf(desc, "nvdimm-old:%s", ++ ndctl_dimm_get_unique_id(dimm)); ++ break; ++ case ND_USER_KEY: ++ rc = sprintf(desc, "nvdimm:%s", ++ ndctl_dimm_get_unique_id(dimm)); ++ break; ++ case ND_MASTER_OLD_KEY: ++ rc = sprintf(desc, "nvdimm-master-old:%s", ++ ndctl_dimm_get_unique_id(dimm)); ++ break; ++ case ND_MASTER_KEY: ++ rc = sprintf(desc, "nvdimm-master:%s", ++ ndctl_dimm_get_unique_id(dimm)); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (rc < 0) { ++ fprintf(stderr, "error setting key description: %s\n", ++ strerror(errno)); ++ return -errno; ++ } ++ ++ return 0; ++} ++ ++char *ndctl_load_key_blob(const char *path, int *size, const char *postfix, ++ int dirfd, enum key_type key_type) ++{ ++ struct stat st; ++ ssize_t read_bytes = 0; ++ int rc, fd; ++ char *blob, *pl, *rdptr; ++ char prefix[] = "load "; ++ bool need_prefix = false; ++ ++ if (key_type == KEY_ENCRYPTED || key_type == KEY_TRUSTED) ++ need_prefix = true; ++ ++ fd = openat(dirfd, path, O_RDONLY); ++ if (fd < 0) { ++ fprintf(stderr, "failed to open file %s: %s\n", ++ path, strerror(errno)); ++ return NULL; ++ } ++ ++ rc = fstat(fd, &st); ++ if (rc < 0) { ++ fprintf(stderr, "stat: %s\n", strerror(errno)); ++ return NULL; ++ } ++ if ((st.st_mode & S_IFMT) != S_IFREG) { ++ fprintf(stderr, "%s not a regular file\n", path); ++ return NULL; ++ } ++ ++ if (st.st_size == 0 || st.st_size > 4096) { ++ fprintf(stderr, "Invalid blob file size\n"); ++ return NULL; ++ } ++ ++ *size = st.st_size; ++ if (need_prefix) ++ *size += strlen(prefix); ++ ++ /* ++ * We need to increment postfix and space. ++ * "keyhandle=" is 10 bytes, plus null termination. ++ */ ++ if (postfix) ++ *size += strlen(postfix) + 10 + 1; ++ blob = malloc(*size); ++ if (!blob) { ++ fprintf(stderr, "Unable to allocate memory for blob\n"); ++ return NULL; ++ } ++ ++ if (need_prefix) { ++ memcpy(blob, prefix, strlen(prefix)); ++ pl = blob + strlen(prefix); ++ } else ++ pl = blob; ++ ++ rdptr = pl; ++ do { ++ rc = read(fd, rdptr, st.st_size - read_bytes); ++ if (rc < 0) { ++ fprintf(stderr, "Failed to read from blob file: %s\n", ++ strerror(errno)); ++ free(blob); ++ close(fd); ++ return NULL; ++ } ++ read_bytes += rc; ++ rdptr += rc; ++ } while (read_bytes != st.st_size); ++ ++ close(fd); ++ ++ if (postfix) { ++ pl += read_bytes; ++ *pl = ' '; ++ pl++; ++ rc = sprintf(pl, "keyhandle=%s", postfix); ++ } ++ ++ return blob; ++} ++ ++static key_serial_t dimm_check_key(struct ndctl_dimm *dimm, ++ enum ndctl_key_type key_type) ++{ ++ char desc[ND_KEY_DESC_SIZE]; ++ int rc; ++ ++ rc = get_key_desc(dimm, desc, key_type); ++ if (rc < 0) ++ return rc; ++ ++ return keyctl_search(KEY_SPEC_USER_KEYRING, "encrypted", desc, 0); ++} ++ ++static key_serial_t dimm_create_key(struct ndctl_dimm *dimm, ++ const char *kek, enum ndctl_key_type key_type) ++{ ++ char desc[ND_KEY_DESC_SIZE]; ++ char path[PATH_MAX]; ++ char cmd[ND_KEY_CMD_SIZE]; ++ key_serial_t key; ++ void *buffer; ++ int rc; ++ ssize_t size; ++ FILE *fp; ++ ssize_t wrote; ++ struct stat st; ++ ++ if (ndctl_dimm_is_active(dimm)) { ++ fprintf(stderr, "regions active on %s, op failed\n", ++ ndctl_dimm_get_devname(dimm)); ++ return -EBUSY; ++ } ++ ++ rc = get_key_desc(dimm, desc, key_type); ++ if (rc < 0) ++ return rc; ++ ++ /* make sure it's not already in the key ring */ ++ key = keyctl_search(KEY_SPEC_USER_KEYRING, "encrypted", desc, 0); ++ if (key > 0) { ++ fprintf(stderr, "Error: key already present in user keyring\n"); ++ return -EEXIST; ++ } ++ ++ rc = get_key_path(dimm, path, key_type); ++ if (rc < 0) ++ return rc; ++ ++ rc = stat(path, &st); ++ if (rc == 0) { ++ fprintf(stderr, "%s already exists!\n", path); ++ return -EEXIST; ++ } ++ ++ rc = sprintf(cmd, "new enc32 %s 32", kek); ++ if (rc < 0) { ++ fprintf(stderr, "sprintf: %s\n", strerror(errno)); ++ return -errno; ++ } ++ ++ key = add_key("encrypted", desc, cmd, strlen(cmd), ++ KEY_SPEC_USER_KEYRING); ++ if (key < 0) { ++ fprintf(stderr, "add_key failed: %s\n", strerror(errno)); ++ return -errno; ++ } ++ ++ size = keyctl_read_alloc(key, &buffer); ++ if (size < 0) { ++ fprintf(stderr, "keyctl_read_alloc failed: %s\n", strerror(errno)); ++ keyctl_unlink(key, KEY_SPEC_USER_KEYRING); ++ return rc; ++ } ++ ++ fp = fopen(path, "w"); ++ if (!fp) { ++ rc = -errno; ++ fprintf(stderr, "Unable to open file %s: %s\n", ++ path, strerror(errno)); ++ free(buffer); ++ return rc; ++ } ++ ++ wrote = fwrite(buffer, 1, size, fp); ++ if (wrote != size) { ++ if (wrote == -1) ++ rc = -errno; ++ else ++ rc = -EIO; ++ fprintf(stderr, "Failed to write to %s: %s\n", ++ path, strerror(-rc)); ++ fclose(fp); ++ free(buffer); ++ return rc; ++ } ++ ++ fclose(fp); ++ free(buffer); ++ return key; ++} ++ ++static key_serial_t dimm_load_key(struct ndctl_dimm *dimm, ++ enum ndctl_key_type key_type) ++{ ++ key_serial_t key; ++ char desc[ND_KEY_DESC_SIZE]; ++ char path[PATH_MAX]; ++ int rc; ++ char *blob; ++ int size; ++ ++ if (ndctl_dimm_is_active(dimm)) { ++ fprintf(stderr, "regions active on %s, op failed\n", ++ ndctl_dimm_get_devname(dimm)); ++ return -EBUSY; ++ } ++ ++ rc = get_key_desc(dimm, desc, key_type); ++ if (rc < 0) ++ return rc; ++ ++ rc = get_key_path(dimm, path, key_type); ++ if (rc < 0) ++ return rc; ++ ++ blob = ndctl_load_key_blob(path, &size, NULL, -1, KEY_ENCRYPTED); ++ if (!blob) ++ return -ENOMEM; ++ ++ key = add_key("encrypted", desc, blob, size, KEY_SPEC_USER_KEYRING); ++ free(blob); ++ if (key < 0) { ++ fprintf(stderr, "add_key failed: %s\n", strerror(errno)); ++ return -errno; ++ } ++ ++ return key; ++} ++ ++/* ++ * The function will check to see if the existing key is there and remove ++ * from user key ring if it is. Rename the existing key blob to old key ++ * blob, and then attempt to inject the key as old key into the user key ++ * ring. ++ */ ++static key_serial_t move_key_to_old(struct ndctl_dimm *dimm, ++ enum ndctl_key_type key_type) ++{ ++ int rc; ++ key_serial_t key; ++ char old_path[PATH_MAX]; ++ char new_path[PATH_MAX]; ++ enum ndctl_key_type okey_type; ++ ++ if (ndctl_dimm_is_active(dimm)) { ++ fprintf(stderr, "regions active on %s, op failed\n", ++ ndctl_dimm_get_devname(dimm)); ++ return -EBUSY; ++ } ++ ++ key = dimm_check_key(dimm, key_type); ++ if (key > 0) ++ keyctl_unlink(key, KEY_SPEC_USER_KEYRING); ++ ++ if (key_type == ND_USER_KEY) ++ okey_type = ND_USER_OLD_KEY; ++ else if (key_type == ND_MASTER_KEY) ++ okey_type = ND_MASTER_OLD_KEY; ++ else ++ return -EINVAL; ++ ++ rc = get_key_path(dimm, old_path, key_type); ++ if (rc < 0) ++ return rc; ++ ++ rc = get_key_path(dimm, new_path, okey_type); ++ if (rc < 0) ++ return rc; ++ ++ rc = rename(old_path, new_path); ++ if (rc < 0) { ++ fprintf(stderr, "rename failed from %s to %s: %s\n", ++ old_path, new_path, strerror(errno)); ++ return -errno; ++ } ++ ++ return dimm_load_key(dimm, okey_type); ++} ++ ++static int dimm_remove_key(struct ndctl_dimm *dimm, ++ enum ndctl_key_type key_type) ++{ ++ key_serial_t key; ++ char path[PATH_MAX]; ++ int rc; ++ ++ key = dimm_check_key(dimm, key_type); ++ if (key > 0) ++ keyctl_unlink(key, KEY_SPEC_USER_KEYRING); ++ ++ rc = get_key_path(dimm, path, key_type); ++ if (rc < 0) ++ return rc; ++ ++ rc = unlink(path); ++ if (rc < 0) { ++ fprintf(stderr, "delete file %s failed: %s\n", ++ path, strerror(errno)); ++ return -errno; ++ } ++ ++ return 0; ++} ++ ++static int verify_kek(struct ndctl_dimm *dimm, const char *kek) ++{ ++ char *type, *desc, *key_handle; ++ key_serial_t key; ++ int rc = 0; ++ ++ key_handle = strdup(kek); ++ if (!key_handle) ++ return -ENOMEM; ++ ++ type = strtok(key_handle, ":"); ++ if (!type) { ++ fprintf(stderr, "No key type found for kek handle\n"); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ if (strcmp(type, "trusted") != 0 && ++ strcmp(type, "user") != 0) { ++ fprintf(stderr, "No such key type: %s", type); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ desc = strtok(NULL, ":"); ++ if (!desc) { ++ fprintf(stderr, "No description found for kek handle\n"); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ key = keyctl_search(KEY_SPEC_USER_KEYRING, type, desc, 0); ++ if (key < 0) { ++ fprintf(stderr, "No key encryption key found\n"); ++ rc = key; ++ goto out; ++ } ++ ++out: ++ free(key_handle); ++ return rc; ++} ++ ++int ndctl_dimm_setup_key(struct ndctl_dimm *dimm, const char *kek, ++ enum ndctl_key_type key_type) ++{ ++ key_serial_t key; ++ int rc; ++ ++ rc = verify_kek(dimm, kek); ++ if (rc < 0) ++ return rc; ++ ++ key = dimm_create_key(dimm, kek, key_type); ++ if (key < 0) ++ return key; ++ ++ if (key_type == ND_MASTER_KEY) ++ rc = ndctl_dimm_update_master_passphrase(dimm, 0, key); ++ else ++ rc = ndctl_dimm_update_passphrase(dimm, 0, key); ++ if (rc < 0) { ++ dimm_remove_key(dimm, key_type); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static char *get_current_kek(struct ndctl_dimm *dimm, ++ enum ndctl_key_type key_type) ++{ ++ key_serial_t key; ++ char *key_buf; ++ long rc; ++ char *type, *desc; ++ ++ key = dimm_check_key(dimm, key_type); ++ if (key < 0) ++ return NULL; ++ ++ rc = keyctl_read_alloc(key, (void **)&key_buf); ++ if (rc < 0) ++ return NULL; ++ ++ rc = sscanf(key_buf, "%ms %ms", &type, &desc); ++ if (rc < 0) ++ return NULL; ++ ++ free(key_buf); ++ free(type); ++ ++ return desc; ++} ++ ++int ndctl_dimm_update_key(struct ndctl_dimm *dimm, const char *kek, ++ enum ndctl_key_type key_type) ++{ ++ int rc; ++ key_serial_t old_key, new_key; ++ char *current_kek = NULL; ++ enum ndctl_key_type okey_type; ++ ++ if (kek) { ++ rc = verify_kek(dimm, kek); ++ if (rc < 0) ++ return rc; ++ } else { /* find current kek */ ++ current_kek = get_current_kek(dimm, key_type); ++ if (!current_kek) ++ return -ENOKEY; ++ } ++ ++ if (key_type == ND_USER_KEY) ++ okey_type = ND_USER_OLD_KEY; ++ else if (key_type == ND_MASTER_KEY) ++ okey_type = ND_MASTER_OLD_KEY; ++ else ++ return -EINVAL; ++ ++ /* ++ * 1. check if current key is loaded and remove ++ * 2. move current key blob to old key blob ++ * 3. load old key blob ++ * 4. trigger change key with old and new key ++ * 5. remove old key ++ * 6. remove old key blob ++ */ ++ old_key = move_key_to_old(dimm, key_type); ++ if (old_key < 0) ++ return old_key; ++ ++ new_key = dimm_create_key(dimm, current_kek ? current_kek : kek, ++ key_type); ++ free(current_kek); ++ /* need to create new key here */ ++ if (new_key < 0) { ++ new_key = dimm_load_key(dimm, key_type); ++ if (new_key < 0) ++ return new_key; ++ } ++ ++ if (key_type == ND_MASTER_KEY) ++ rc = ndctl_dimm_update_master_passphrase(dimm, ++ old_key, new_key); ++ else ++ rc = ndctl_dimm_update_passphrase(dimm, old_key, new_key); ++ if (rc < 0) ++ return rc; ++ ++ rc = dimm_remove_key(dimm, okey_type); ++ if (rc < 0) ++ return rc; ++ ++ return 0; ++} ++ ++static key_serial_t check_dimm_key(struct ndctl_dimm *dimm, bool need_key, ++ enum ndctl_key_type key_type) ++{ ++ key_serial_t key; ++ ++ key = dimm_check_key(dimm, key_type); ++ if (key < 0) { ++ key = dimm_load_key(dimm, key_type); ++ if (key < 0 && need_key) { ++ fprintf(stderr, "Unable to load key\n"); ++ return -ENOKEY; ++ } else ++ key = 0; ++ } ++ return key; ++} ++ ++static int run_key_op(struct ndctl_dimm *dimm, ++ key_serial_t key, ++ int (*run_op)(struct ndctl_dimm *, long), const char *name) ++{ ++ int rc; ++ ++ rc = run_op(dimm, key); ++ if (rc < 0) { ++ fprintf(stderr, "Failed %s for %s\n", name, ++ ndctl_dimm_get_devname(dimm)); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int discard_key(struct ndctl_dimm *dimm) ++{ ++ int rc; ++ ++ rc = dimm_remove_key(dimm, ND_USER_KEY); ++ if (rc < 0) { ++ fprintf(stderr, "Unable to cleanup key.\n"); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int ndctl_dimm_remove_key(struct ndctl_dimm *dimm) ++{ ++ key_serial_t key; ++ int rc; ++ ++ key = check_dimm_key(dimm, true, ND_USER_KEY); ++ if (key < 0) ++ return key; ++ ++ rc = run_key_op(dimm, key, ndctl_dimm_disable_passphrase, ++ "remove passphrase"); ++ if (rc < 0) ++ return rc; ++ ++ return discard_key(dimm); ++} ++ ++int ndctl_dimm_secure_erase_key(struct ndctl_dimm *dimm, ++ enum ndctl_key_type key_type) ++{ ++ key_serial_t key = 0; ++ int rc; ++ ++ if (key_type != ND_ZERO_KEY) { ++ key = check_dimm_key(dimm, true, key_type); ++ if (key < 0) ++ return key; ++ } ++ ++ if (key_type == ND_MASTER_KEY) ++ rc = run_key_op(dimm, key, ndctl_dimm_master_secure_erase, ++ "master crypto erase"); ++ else if (key_type == ND_USER_KEY || key_type == ND_ZERO_KEY) ++ rc = run_key_op(dimm, key, ndctl_dimm_secure_erase, ++ "crypto erase"); ++ else ++ rc = -EINVAL; ++ if (rc < 0) ++ return rc; ++ ++ if (key_type == ND_USER_KEY) ++ return discard_key(dimm); ++ ++ return 0; ++} ++ ++int ndctl_dimm_overwrite_key(struct ndctl_dimm *dimm) ++{ ++ key_serial_t key; ++ int rc; ++ ++ key = check_dimm_key(dimm, false, ND_USER_KEY); ++ if (key < 0) ++ return key; ++ ++ rc = run_key_op(dimm, key, ndctl_dimm_overwrite, ++ "overwrite"); ++ if (rc < 0) ++ return rc; ++ ++ return 0; ++} +diff -up ndctl-71.1/ndctl/keys.h.orig ndctl-71.1/ndctl/keys.h +--- ndctl-71.1/ndctl/keys.h.orig 2022-10-07 15:51:03.930535782 -0400 ++++ ndctl-71.1/ndctl/keys.h 2022-10-07 15:51:03.914535728 -0400 +@@ -0,0 +1,67 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2019-2020 Intel Corporation. All rights reserved. */ ++ ++#ifndef _NDCTL_UTIL_KEYS_H_ ++#define _NDCTL_UTIL_KEYS_H_ ++ ++enum ndctl_key_type { ++ ND_USER_KEY, ++ ND_USER_OLD_KEY, ++ ND_MASTER_KEY, ++ ND_MASTER_OLD_KEY, ++ ND_ZERO_KEY, ++}; ++ ++enum key_type { ++ KEY_USER = 0, ++ KEY_TRUSTED, ++ KEY_ENCRYPTED, ++}; ++ ++#ifdef ENABLE_KEYUTILS ++char *ndctl_load_key_blob(const char *path, int *size, const char *postfix, ++ int dirfd, enum key_type key_type); ++int ndctl_dimm_setup_key(struct ndctl_dimm *dimm, const char *kek, ++ enum ndctl_key_type key_type); ++int ndctl_dimm_update_key(struct ndctl_dimm *dimm, const char *kek, ++ enum ndctl_key_type key_type); ++int ndctl_dimm_remove_key(struct ndctl_dimm *dimm); ++int ndctl_dimm_secure_erase_key(struct ndctl_dimm *dimm, ++ enum ndctl_key_type key_type); ++int ndctl_dimm_overwrite_key(struct ndctl_dimm *dimm); ++#else ++char *ndctl_load_key_blob(const char *path, int *size, const char *postfix, ++ int dirfd, enum key_type key_type) ++{ ++ return NULL; ++} ++static inline int ndctl_dimm_setup_key(struct ndctl_dimm *dimm, ++ const char *kek, enum ndctl_key_type key_type) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int ndctl_dimm_update_key(struct ndctl_dimm *dimm, ++ const char *kek, enum ndctl_key_type key_type) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int ndctl_dimm_remove_key(struct ndctl_dimm *dimm) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int ndctl_dimm_secure_erase_key(struct ndctl_dimm *dimm, ++ enum ndctl_key_type key_type) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int ndctl_dimm_overwrite_key(struct ndctl_dimm *dimm) ++{ ++ return -EOPNOTSUPP; ++} ++#endif /* ENABLE_KEYUTILS */ ++ ++#endif +diff -up ndctl-71.1/ndctl/lib/libndctl.c.orig ndctl-71.1/ndctl/lib/libndctl.c +--- ndctl-71.1/ndctl/lib/libndctl.c.orig 2022-10-07 15:50:40.085454597 -0400 ++++ ndctl-71.1/ndctl/lib/libndctl.c 2022-10-07 15:51:03.915535731 -0400 +@@ -20,10 +20,10 @@ + #include + #include + +-#include + #include + #include + #include ++#include + #include + #include + #include +diff -up ndctl-71.1/ndctl/lib/papr.c.orig ndctl-71.1/ndctl/lib/papr.c +--- ndctl-71.1/ndctl/lib/papr.c.orig 2022-10-07 15:50:40.086454601 -0400 ++++ ndctl-71.1/ndctl/lib/papr.c 2022-10-07 15:51:03.916535735 -0400 +@@ -10,9 +10,9 @@ + #include + #include + #include +-#include ++#include + #include +-#include ++#include "private.h" + #include "papr.h" + + /* Utility logging maros for simplify logging */ +diff -up ndctl-71.1/ndctl/lib/private.h.orig ndctl-71.1/ndctl/lib/private.h +--- ndctl-71.1/ndctl/lib/private.h.orig 2022-10-07 15:50:40.086454601 -0400 ++++ ndctl-71.1/ndctl/lib/private.h 2022-10-07 15:51:03.917535738 -0400 +@@ -14,8 +14,9 @@ + #include + #include + +-#include ++#include + #include ++#include + #include + #include + #include "intel.h" +@@ -23,7 +24,6 @@ + #include "msft.h" + #include "hyperv.h" + #include "papr.h" +-#include "libndctl-nfit.h" + + struct nvdimm_data { + struct ndctl_cmd *cmd_read; +diff -up ndctl-71.1/ndctl/list.c.orig ndctl-71.1/ndctl/list.c +--- ndctl-71.1/ndctl/list.c.orig 2022-10-07 15:50:40.087454604 -0400 ++++ ndctl-71.1/ndctl/list.c 2022-10-07 15:51:03.917535738 -0400 +@@ -7,13 +7,14 @@ + #include + + #include +-#include + #include + #include + #include + #include + +-#include ++#include "ndctl.h" ++#include "filter.h" ++#include "json.h" + + static struct { + bool buses; +diff -up ndctl-71.1/ndctl/load-keys.c.orig ndctl-71.1/ndctl/load-keys.c +--- ndctl-71.1/ndctl/load-keys.c.orig 2022-10-07 15:50:40.087454604 -0400 ++++ ndctl-71.1/ndctl/load-keys.c 2022-10-07 15:51:03.917535738 -0400 +@@ -12,13 +12,14 @@ + #include + #include + #include +-#include + #include ++#include + #include + #include + #include +-#include +-#include ++ ++#include "filter.h" ++#include "keys.h" + + static struct parameters { + const char *key_path; +diff -up ndctl-71.1/ndctl/monitor.c.orig ndctl-71.1/ndctl/monitor.c +--- ndctl-71.1/ndctl/monitor.c.orig 2022-10-07 15:50:40.087454604 -0400 ++++ ndctl-71.1/ndctl/monitor.c 2022-10-07 15:51:03.917535738 -0400 +@@ -7,7 +7,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -26,6 +25,9 @@ + #endif + #include + ++#include "filter.h" ++#include "json.h" ++ + static struct monitor { + const char *log; + const char *config_file; +diff -up ndctl-71.1/ndctl/namespace.c.orig ndctl-71.1/ndctl/namespace.c +--- ndctl-71.1/ndctl/namespace.c.orig 2022-10-07 15:50:40.088454607 -0400 ++++ ndctl-71.1/ndctl/namespace.c 2022-10-07 15:51:03.918535741 -0400 +@@ -9,7 +9,6 @@ + #include + #include + +-#include + #include "action.h" + #include "namespace.h" + #include +@@ -20,11 +19,14 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include + ++#include "filter.h" ++#include "json.h" ++ + static bool verbose; + static bool force; + static bool repair; +diff -up ndctl-71.1/ndctl/region.c.orig ndctl-71.1/ndctl/region.c +--- ndctl-71.1/ndctl/region.c.orig 2022-10-07 15:50:40.088454607 -0400 ++++ ndctl-71.1/ndctl/region.c 2022-10-07 15:51:03.918535741 -0400 +@@ -5,10 +5,11 @@ + #include + #include + #include "action.h" +-#include + #include + #include + ++#include "filter.h" ++ + static struct { + const char *bus; + const char *type; +diff -up ndctl-71.1/ndctl/util/json-smart.c.orig ndctl-71.1/ndctl/util/json-smart.c +diff -up ndctl-71.1/ndctl/util/keys.c.orig ndctl-71.1/ndctl/util/keys.c +diff -up ndctl-71.1/ndctl/util/keys.h.orig ndctl-71.1/ndctl/util/keys.h +diff -up ndctl-71.1/test/Makefile.am.orig ndctl-71.1/test/Makefile.am +--- ndctl-71.1/test/Makefile.am.orig 2022-10-07 15:50:40.088454607 -0400 ++++ ndctl-71.1/test/Makefile.am 2022-10-07 15:51:03.919535745 -0400 +@@ -80,12 +80,19 @@ testcore =\ + libndctl_SOURCES = libndctl.c $(testcore) + libndctl_LDADD = $(LIBNDCTL_LIB) $(UUID_LIBS) $(KMOD_LIBS) + ++namespace_core =\ ++ ../ndctl/namespace.c \ ++ ../ndctl/filter.c \ ++ ../ndctl/check.c \ ++ ../util/json.c \ ++ ../ndctl/json.c \ ++ ../daxctl/filter.c \ ++ ../daxctl/json.c ++ + dsm_fail_SOURCES =\ + dsm-fail.c \ + $(testcore) \ +- ../ndctl/namespace.c \ +- ../ndctl/check.c \ +- ../util/json.c ++ $(namespace_core) + + dsm_fail_LDADD = $(LIBNDCTL_LIB) \ + $(KMOD_LIBS) \ +@@ -122,9 +129,7 @@ device_dax_SOURCES = \ + dax-dev.c \ + dax-pmd.c \ + $(testcore) \ +- ../ndctl/namespace.c \ +- ../ndctl/check.c \ +- ../util/json.c ++ $(namespace_core) + + if ENABLE_POISON + dax_pmd_SOURCES += dax-poison.c +@@ -153,7 +158,10 @@ smart_listen_LDADD = $(LIBNDCTL_LIB) + + list_smart_dimm_SOURCES = \ + list-smart-dimm.c \ +- ../util/json.c ++ ../ndctl/filter.c \ ++ ../util/json.c \ ++ ../ndctl/json.c ++ + list_smart_dimm_LDADD = \ + $(LIBNDCTL_LIB) \ + $(JSON_LIBS) \ +diff -up ndctl-71.1/test/ack-shutdown-count-set.c.orig ndctl-71.1/test/ack-shutdown-count-set.c +--- ndctl-71.1/test/ack-shutdown-count-set.c.orig 2022-10-07 15:50:40.089454611 -0400 ++++ ndctl-71.1/test/ack-shutdown-count-set.c 2022-10-07 15:51:03.919535745 -0400 +@@ -15,7 +15,7 @@ + + #include + #include +-#include ++#include + #include + + static int test_dimm(struct ndctl_dimm *dimm) +diff -up ndctl-71.1/test/daxdev-errors.c.orig ndctl-71.1/test/daxdev-errors.c +--- ndctl-71.1/test/daxdev-errors.c.orig 2022-10-07 15:50:40.090454614 -0400 ++++ ndctl-71.1/test/daxdev-errors.c 2022-10-07 15:51:03.920535748 -0400 +@@ -23,7 +23,7 @@ + #include + #include + #include +-#include ++#include + + #define fail() fprintf(stderr, "%s: failed at: %d\n", __func__, __LINE__) + +diff -up ndctl-71.1/test/device-dax.c.orig ndctl-71.1/test/device-dax.c +--- ndctl-71.1/test/device-dax.c.orig 2022-10-07 15:50:40.090454614 -0400 ++++ ndctl-71.1/test/device-dax.c 2022-10-07 15:51:03.921535752 -0400 +@@ -20,7 +20,7 @@ + #include + #include + +-#include ++#include + #include + + static sigjmp_buf sj_env; +diff -up ndctl-71.1/test/dsm-fail.c.orig ndctl-71.1/test/dsm-fail.c +--- ndctl-71.1/test/dsm-fail.c.orig 2022-10-07 15:50:40.091454617 -0400 ++++ ndctl-71.1/test/dsm-fail.c 2022-10-07 15:51:03.921535752 -0400 +@@ -14,8 +14,8 @@ + + #include + #include +-#include +-#include ++#include ++#include + #include + + #define DIMM_PATH "/sys/devices/platform/nfit_test.0/nfit_test_dimm/test_dimm0" +diff -up ndctl-71.1/test/libndctl.c.orig ndctl-71.1/test/libndctl.c +--- ndctl-71.1/test/libndctl.c.orig 2022-10-07 15:50:40.092454621 -0400 ++++ ndctl-71.1/test/libndctl.c 2022-10-07 15:51:03.922535755 -0400 +@@ -21,7 +21,7 @@ + #include + #include + #include +-#include ++#include + #include + + #define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */ +diff -up ndctl-71.1/test/list-smart-dimm.c.orig ndctl-71.1/test/list-smart-dimm.c +--- ndctl-71.1/test/list-smart-dimm.c.orig 2022-10-07 15:50:40.093454624 -0400 ++++ ndctl-71.1/test/list-smart-dimm.c 2022-10-07 15:51:03.923535758 -0400 +@@ -3,11 +3,13 @@ + #include + #include + #include +-#include + #include + #include + #include +-#include ++ ++#include ++#include ++#include + + struct util_filter_params param; + static int did_fail; +diff -up ndctl-71.1/test/pmem_namespaces.c.orig ndctl-71.1/test/pmem_namespaces.c +--- ndctl-71.1/test/pmem_namespaces.c.orig 2022-10-07 15:50:40.093454624 -0400 ++++ ndctl-71.1/test/pmem_namespaces.c 2022-10-07 15:51:03.923535758 -0400 +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -18,7 +19,6 @@ + #include + + #include +-#include + + #define err(msg)\ + fprintf(stderr, "%s:%d: %s (%s)\n", __func__, __LINE__, msg, strerror(errno)) +diff -up ndctl-71.1/test/revoke-devmem.c.orig ndctl-71.1/test/revoke-devmem.c +--- ndctl-71.1/test/revoke-devmem.c.orig 2022-10-07 15:50:40.094454628 -0400 ++++ ndctl-71.1/test/revoke-devmem.c 2022-10-07 15:51:03.924535762 -0400 +@@ -19,7 +19,7 @@ + #include + #include + +-#include ++#include + #include + + static sigjmp_buf sj_env; +diff -up ndctl-71.1/util/filter.c.orig ndctl-71.1/util/filter.c +diff -up ndctl-71.1/util/filter.h.orig ndctl-71.1/util/filter.h +diff -up ndctl-71.1/util/help.c.orig ndctl-71.1/util/help.c +--- ndctl-71.1/util/help.c.orig 2022-10-07 15:50:40.095454631 -0400 ++++ ndctl-71.1/util/help.c 2022-10-07 15:51:03.925535765 -0400 +@@ -14,7 +14,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + +diff -up ndctl-71.1/util/json.c.orig ndctl-71.1/util/json.c +--- ndctl-71.1/util/json.c.orig 2022-10-07 15:50:40.096454635 -0400 ++++ ndctl-71.1/util/json.c 2022-10-07 15:51:03.926535769 -0400 +@@ -2,17 +2,10 @@ + // Copyright (C) 2015-2020 Intel Corporation. All rights reserved. + #include + #include ++#include + #include +-#include +-#include + #include + #include +-#include +-#include +-#include +-#include +-#include +-#include + + /* adapted from mdadm::human_size_brief() */ + static int display_size(struct json_object *jobj, struct printbuf *pbuf, +@@ -112,1536 +105,3 @@ void util_display_json_array(FILE *f_out + } + json_object_put(jarray); + } +- +-struct json_object *util_bus_to_json(struct ndctl_bus *bus, unsigned long flags) +-{ +- struct json_object *jbus = json_object_new_object(); +- struct json_object *jobj, *fw_obj = NULL; +- int scrub; +- +- if (!jbus) +- return NULL; +- +- jobj = json_object_new_string(ndctl_bus_get_provider(bus)); +- if (!jobj) +- goto err; +- json_object_object_add(jbus, "provider", jobj); +- +- jobj = json_object_new_string(ndctl_bus_get_devname(bus)); +- if (!jobj) +- goto err; +- json_object_object_add(jbus, "dev", jobj); +- +- scrub = ndctl_bus_get_scrub_state(bus); +- if (scrub < 0) +- return jbus; +- +- jobj = json_object_new_string(scrub ? "active" : "idle"); +- if (!jobj) +- goto err; +- json_object_object_add(jbus, "scrub_state", jobj); +- +- if (flags & UTIL_JSON_FIRMWARE) { +- struct ndctl_dimm *dimm; +- +- /* +- * Skip displaying firmware activation capability if no +- * DIMMs support firmware update. +- */ +- ndctl_dimm_foreach(bus, dimm) +- if (ndctl_dimm_fw_update_supported(dimm) == 0) { +- fw_obj = json_object_new_object(); +- break; +- } +- } +- +- if (fw_obj) { +- enum ndctl_fwa_state state; +- enum ndctl_fwa_method method; +- +- jobj = NULL; +- method = ndctl_bus_get_fw_activate_method(bus); +- if (method == NDCTL_FWA_METHOD_RESET) +- jobj = json_object_new_string("reset"); +- if (method == NDCTL_FWA_METHOD_SUSPEND) +- jobj = json_object_new_string("suspend"); +- if (method == NDCTL_FWA_METHOD_LIVE) +- jobj = json_object_new_string("live"); +- if (jobj) +- json_object_object_add(fw_obj, "activate_method", jobj); +- +- jobj = NULL; +- state = ndctl_bus_get_fw_activate_state(bus); +- if (state == NDCTL_FWA_ARMED) +- jobj = json_object_new_string("armed"); +- if (state == NDCTL_FWA_IDLE) +- jobj = json_object_new_string("idle"); +- if (state == NDCTL_FWA_ARM_OVERFLOW) +- jobj = json_object_new_string("overflow"); +- if (jobj) +- json_object_object_add(fw_obj, "activate_state", jobj); +- +- json_object_object_add(jbus, "firmware", fw_obj); +- } +- +- return jbus; +- err: +- json_object_put(jbus); +- return NULL; +-} +- +-struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm, +- unsigned long flags) +-{ +- struct json_object *jfirmware = json_object_new_object(); +- bool can_update, need_powercycle; +- enum ndctl_fwa_result result; +- enum ndctl_fwa_state state; +- struct json_object *jobj; +- struct ndctl_cmd *cmd; +- uint64_t run, next; +- int rc; +- +- if (!jfirmware) +- return NULL; +- +- cmd = ndctl_dimm_cmd_new_fw_get_info(dimm); +- if (!cmd) +- goto err; +- +- rc = ndctl_cmd_submit(cmd); +- if ((rc < 0) || ndctl_cmd_fw_xlat_firmware_status(cmd) != FW_SUCCESS) { +- jobj = util_json_object_hex(-1, flags); +- if (jobj) +- json_object_object_add(jfirmware, "current_version", +- jobj); +- goto out; +- } +- +- run = ndctl_cmd_fw_info_get_run_version(cmd); +- if (run == ULLONG_MAX) { +- jobj = util_json_object_hex(-1, flags); +- if (jobj) +- json_object_object_add(jfirmware, "current_version", +- jobj); +- goto out; +- } +- +- jobj = util_json_object_hex(run, flags); +- if (jobj) +- json_object_object_add(jfirmware, "current_version", jobj); +- +- rc = ndctl_dimm_fw_update_supported(dimm); +- can_update = rc == 0; +- jobj = json_object_new_boolean(can_update); +- if (jobj) +- json_object_object_add(jfirmware, "can_update", jobj); +- +- +- next = ndctl_cmd_fw_info_get_updated_version(cmd); +- if (next == ULLONG_MAX) { +- jobj = util_json_object_hex(-1, flags); +- if (jobj) +- json_object_object_add(jfirmware, "next_version", +- jobj); +- goto out; +- } +- +- if (!next) +- goto out; +- +- jobj = util_json_object_hex(next, flags); +- if (jobj) +- json_object_object_add(jfirmware, +- "next_version", jobj); +- +- state = ndctl_dimm_get_fw_activate_state(dimm); +- switch (state) { +- case NDCTL_FWA_IDLE: +- jobj = json_object_new_string("idle"); +- break; +- case NDCTL_FWA_ARMED: +- jobj = json_object_new_string("armed"); +- break; +- case NDCTL_FWA_BUSY: +- jobj = json_object_new_string("busy"); +- break; +- default: +- jobj = NULL; +- break; +- } +- if (jobj) +- json_object_object_add(jfirmware, "activate_state", jobj); +- +- result = ndctl_dimm_get_fw_activate_result(dimm); +- switch (result) { +- case NDCTL_FWA_RESULT_NONE: +- case NDCTL_FWA_RESULT_SUCCESS: +- case NDCTL_FWA_RESULT_NOTSTAGED: +- /* +- * If a 'next' firmware version is staged then this +- * result is stale, if the activation succeeds that is +- * indicated by not finding a 'next' entry. +- */ +- need_powercycle = false; +- break; +- case NDCTL_FWA_RESULT_NEEDRESET: +- case NDCTL_FWA_RESULT_FAIL: +- default: +- /* +- * If the last activation failed, or if the activation +- * result is unavailable it is always the case that the +- * only remediation is powercycle. +- */ +- need_powercycle = true; +- break; +- } +- +- if (need_powercycle) { +- jobj = json_object_new_boolean(true); +- if (!jobj) +- goto out; +- json_object_object_add(jfirmware, "need_powercycle", jobj); +- } +- +- ndctl_cmd_unref(cmd); +- return jfirmware; +- +-err: +- json_object_put(jfirmware); +- jfirmware = NULL; +-out: +- if (cmd) +- ndctl_cmd_unref(cmd); +- return jfirmware; +-} +- +-struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm, +- unsigned long flags) +-{ +- struct json_object *jdimm = json_object_new_object(); +- const char *id = ndctl_dimm_get_unique_id(dimm); +- unsigned int handle = ndctl_dimm_get_handle(dimm); +- unsigned short phys_id = ndctl_dimm_get_phys_id(dimm); +- struct json_object *jobj; +- enum ndctl_security_state sstate; +- +- if (!jdimm) +- return NULL; +- +- jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); +- if (!jobj) +- goto err; +- json_object_object_add(jdimm, "dev", jobj); +- +- if (id) { +- jobj = json_object_new_string(id); +- if (!jobj) +- goto err; +- json_object_object_add(jdimm, "id", jobj); +- } +- +- if (handle < UINT_MAX) { +- jobj = util_json_object_hex(handle, flags); +- if (!jobj) +- goto err; +- json_object_object_add(jdimm, "handle", jobj); +- } +- +- if (phys_id < USHRT_MAX) { +- jobj = util_json_object_hex(phys_id, flags); +- if (!jobj) +- goto err; +- json_object_object_add(jdimm, "phys_id", jobj); +- } +- +- if (!ndctl_dimm_is_enabled(dimm)) { +- jobj = json_object_new_string("disabled"); +- if (!jobj) +- goto err; +- json_object_object_add(jdimm, "state", jobj); +- } +- +- if (ndctl_dimm_failed_map(dimm)) { +- jobj = json_object_new_boolean(true); +- if (!jobj) +- goto err; +- json_object_object_add(jdimm, "flag_failed_map", jobj); +- } +- +- if (ndctl_dimm_failed_save(dimm)) { +- jobj = json_object_new_boolean(true); +- if (!jobj) +- goto err; +- json_object_object_add(jdimm, "flag_failed_save", jobj); +- } +- +- if (ndctl_dimm_failed_arm(dimm)) { +- jobj = json_object_new_boolean(true); +- if (!jobj) +- goto err; +- json_object_object_add(jdimm, "flag_failed_arm", jobj); +- } +- +- if (ndctl_dimm_failed_restore(dimm)) { +- jobj = json_object_new_boolean(true); +- if (!jobj) +- goto err; +- json_object_object_add(jdimm, "flag_failed_restore", jobj); +- } +- +- if (ndctl_dimm_failed_flush(dimm)) { +- jobj = json_object_new_boolean(true); +- if (!jobj) +- goto err; +- json_object_object_add(jdimm, "flag_failed_flush", jobj); +- } +- +- if (ndctl_dimm_smart_pending(dimm)) { +- jobj = json_object_new_boolean(true); +- if (!jobj) +- goto err; +- json_object_object_add(jdimm, "flag_smart_event", jobj); +- } +- +- sstate = ndctl_dimm_get_security(dimm); +- if (sstate == NDCTL_SECURITY_DISABLED) +- jobj = json_object_new_string("disabled"); +- else if (sstate == NDCTL_SECURITY_UNLOCKED) +- jobj = json_object_new_string("unlocked"); +- else if (sstate == NDCTL_SECURITY_LOCKED) +- jobj = json_object_new_string("locked"); +- else if (sstate == NDCTL_SECURITY_FROZEN) +- jobj = json_object_new_string("frozen"); +- else if (sstate == NDCTL_SECURITY_OVERWRITE) +- jobj = json_object_new_string("overwrite"); +- else +- jobj = NULL; +- if (jobj) +- json_object_object_add(jdimm, "security", jobj); +- +- if (ndctl_dimm_security_is_frozen(dimm)) { +- jobj = json_object_new_boolean(true); +- if (jobj) +- json_object_object_add(jdimm, "security_frozen", jobj); +- } +- +- if (flags & UTIL_JSON_FIRMWARE) { +- struct json_object *jfirmware; +- +- jfirmware = util_dimm_firmware_to_json(dimm, flags); +- if (jfirmware) +- json_object_object_add(jdimm, "firmware", jfirmware); +- } +- +- return jdimm; +- err: +- json_object_put(jdimm); +- return NULL; +-} +- +-struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, +- unsigned long flags) +-{ +- struct daxctl_memory *mem = daxctl_dev_get_memory(dev); +- const char *devname = daxctl_dev_get_devname(dev); +- struct json_object *jdev, *jobj, *jmappings = NULL; +- struct daxctl_mapping *mapping = NULL; +- int node, movable, align; +- +- jdev = json_object_new_object(); +- if (!devname || !jdev) +- return NULL; +- +- jobj = json_object_new_string(devname); +- if (jobj) +- json_object_object_add(jdev, "chardev", jobj); +- +- jobj = util_json_object_size(daxctl_dev_get_size(dev), flags); +- if (jobj) +- json_object_object_add(jdev, "size", jobj); +- +- node = daxctl_dev_get_target_node(dev); +- if (node >= 0) { +- jobj = json_object_new_int(node); +- if (jobj) +- json_object_object_add(jdev, "target_node", jobj); +- } +- +- align = daxctl_dev_get_align(dev); +- if (align > 0) { +- jobj = util_json_object_size(daxctl_dev_get_align(dev), flags); +- if (jobj) +- json_object_object_add(jdev, "align", jobj); +- } +- +- if (mem) +- jobj = json_object_new_string("system-ram"); +- else +- jobj = json_object_new_string("devdax"); +- if (jobj) +- json_object_object_add(jdev, "mode", jobj); +- +- if (mem && daxctl_dev_get_resource(dev) != 0) { +- int num_sections = daxctl_memory_num_sections(mem); +- int num_online = daxctl_memory_is_online(mem); +- +- jobj = json_object_new_int(num_online); +- if (jobj) +- json_object_object_add(jdev, "online_memblocks", jobj); +- +- jobj = json_object_new_int(num_sections); +- if (jobj) +- json_object_object_add(jdev, "total_memblocks", jobj); +- +- movable = daxctl_memory_is_movable(mem); +- if (movable == 1) +- jobj = json_object_new_boolean(true); +- else if (movable == 0) +- jobj = json_object_new_boolean(false); +- else +- jobj = NULL; +- if (jobj) +- json_object_object_add(jdev, "movable", jobj); +- } +- +- if (!daxctl_dev_is_enabled(dev)) { +- jobj = json_object_new_string("disabled"); +- if (jobj) +- json_object_object_add(jdev, "state", jobj); +- } +- +- if (!(flags & UTIL_JSON_DAX_MAPPINGS)) +- return jdev; +- +- daxctl_mapping_foreach(dev, mapping) { +- struct json_object *jmapping; +- +- if (!jmappings) { +- jmappings = json_object_new_array(); +- if (!jmappings) +- continue; +- +- json_object_object_add(jdev, "mappings", jmappings); +- } +- +- jmapping = util_daxctl_mapping_to_json(mapping, flags); +- if (!jmapping) +- continue; +- json_object_array_add(jmappings, jmapping); +- } +- return jdev; +-} +- +-struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region, +- struct json_object *jdevs, const char *ident, +- unsigned long flags) +-{ +- struct daxctl_dev *dev; +- +- daxctl_dev_foreach(region, dev) { +- struct json_object *jdev; +- +- if (!util_daxctl_dev_filter(dev, ident)) +- continue; +- +- if (!(flags & (UTIL_JSON_IDLE|UTIL_JSON_CONFIGURED)) +- && !daxctl_dev_get_size(dev)) +- continue; +- +- if (!jdevs) { +- jdevs = json_object_new_array(); +- if (!jdevs) +- return NULL; +- } +- +- jdev = util_daxctl_dev_to_json(dev, flags); +- if (!jdev) { +- json_object_put(jdevs); +- return NULL; +- } +- +- json_object_array_add(jdevs, jdev); +- } +- +- return jdevs; +-} +- +-#define _SZ(get_max, get_elem, type) \ +-static struct json_object *util_##type##_build_size_array(struct ndctl_##type *arg) \ +-{ \ +- struct json_object *arr = json_object_new_array(); \ +- int i; \ +- \ +- if (!arr) \ +- return NULL; \ +- \ +- for (i = 0; i < get_max(arg); i++) { \ +- struct json_object *jobj; \ +- int64_t align; \ +- \ +- align = get_elem(arg, i); \ +- jobj = json_object_new_int64(align); \ +- if (!jobj) \ +- goto err; \ +- json_object_array_add(arr, jobj); \ +- } \ +- \ +- return arr; \ +-err: \ +- json_object_put(arr); \ +- return NULL; \ +-} +-#define SZ(type, kind) _SZ(ndctl_##type##_get_num_##kind##s, \ +- ndctl_##type##_get_supported_##kind, type) +-SZ(pfn, alignment) +-SZ(dax, alignment) +-SZ(btt, sector_size) +- +-struct json_object *util_region_capabilities_to_json(struct ndctl_region *region) +-{ +- struct json_object *jcaps, *jcap, *jobj; +- struct ndctl_btt *btt = ndctl_region_get_btt_seed(region); +- struct ndctl_pfn *pfn = ndctl_region_get_pfn_seed(region); +- struct ndctl_dax *dax = ndctl_region_get_dax_seed(region); +- +- if (!btt || !pfn || !dax) +- return NULL; +- +- jcaps = json_object_new_array(); +- if (!jcaps) +- return NULL; +- +- if (btt) { +- jcap = json_object_new_object(); +- if (!jcap) +- goto err; +- json_object_array_add(jcaps, jcap); +- +- jobj = json_object_new_string("sector"); +- if (!jobj) +- goto err; +- json_object_object_add(jcap, "mode", jobj); +- jobj = util_btt_build_size_array(btt); +- if (!jobj) +- goto err; +- json_object_object_add(jcap, "sector_sizes", jobj); +- } +- +- if (pfn) { +- jcap = json_object_new_object(); +- if (!jcap) +- goto err; +- json_object_array_add(jcaps, jcap); +- +- jobj = json_object_new_string("fsdax"); +- if (!jobj) +- goto err; +- json_object_object_add(jcap, "mode", jobj); +- jobj = util_pfn_build_size_array(pfn); +- if (!jobj) +- goto err; +- json_object_object_add(jcap, "alignments", jobj); +- } +- +- if (dax) { +- jcap = json_object_new_object(); +- if (!jcap) +- goto err; +- json_object_array_add(jcaps, jcap); +- +- jobj = json_object_new_string("devdax"); +- if (!jobj) +- goto err; +- json_object_object_add(jcap, "mode", jobj); +- jobj = util_dax_build_size_array(dax); +- if (!jobj) +- goto err; +- json_object_object_add(jcap, "alignments", jobj); +- } +- +- return jcaps; +-err: +- json_object_put(jcaps); +- return NULL; +-} +- +-struct json_object *util_daxctl_region_to_json(struct daxctl_region *region, +- const char *ident, unsigned long flags) +-{ +- unsigned long align; +- struct json_object *jregion, *jobj; +- unsigned long long available_size, size; +- +- jregion = json_object_new_object(); +- if (!jregion) +- return NULL; +- +- /* +- * The flag indicates when we are being called by an agent that +- * already knows about the parent device information. +- */ +- if (!(flags & UTIL_JSON_DAX)) { +- /* trim off the redundant /sys/devices prefix */ +- const char *path = daxctl_region_get_path(region); +- int len = strlen("/sys/devices"); +- const char *trim = &path[len]; +- +- if (strncmp(path, "/sys/devices", len) != 0) +- goto err; +- jobj = json_object_new_string(trim); +- if (!jobj) +- goto err; +- json_object_object_add(jregion, "path", jobj); +- } +- +- jobj = json_object_new_int(daxctl_region_get_id(region)); +- if (!jobj) +- goto err; +- json_object_object_add(jregion, "id", jobj); +- +- size = daxctl_region_get_size(region); +- if (size < ULLONG_MAX) { +- jobj = util_json_object_size(size, flags); +- if (!jobj) +- goto err; +- json_object_object_add(jregion, "size", jobj); +- } +- +- available_size = daxctl_region_get_available_size(region); +- if (available_size) { +- jobj = util_json_object_size(available_size, flags); +- if (!jobj) +- goto err; +- json_object_object_add(jregion, "available_size", jobj); +- } +- +- align = daxctl_region_get_align(region); +- if (align < ULONG_MAX) { +- jobj = json_object_new_int64(align); +- if (!jobj) +- goto err; +- json_object_object_add(jregion, "align", jobj); +- } +- +- if (!(flags & UTIL_JSON_DAX_DEVS)) +- return jregion; +- +- jobj = util_daxctl_devs_to_list(region, NULL, ident, flags); +- if (jobj) +- json_object_object_add(jregion, "devices", jobj); +- +- return jregion; +- err: +- json_object_put(jregion); +- return NULL; +-} +- +-static int compare_dimm_number(const void *p1, const void *p2) +-{ +- struct ndctl_dimm *dimm1 = *(struct ndctl_dimm **)p1; +- struct ndctl_dimm *dimm2 = *(struct ndctl_dimm **)p2; +- const char *dimm1_name = ndctl_dimm_get_devname(dimm1); +- const char *dimm2_name = ndctl_dimm_get_devname(dimm2); +- int num1, num2; +- +- if (sscanf(dimm1_name, "nmem%d", &num1) != 1) +- num1 = 0; +- if (sscanf(dimm2_name, "nmem%d", &num2) != 1) +- num2 = 0; +- +- return num1 - num2; +-} +- +-static struct json_object *badblocks_to_jdimms(struct ndctl_region *region, +- unsigned long long addr, unsigned long len) +-{ +- struct ndctl_bus *bus = ndctl_region_get_bus(region); +- int count = ndctl_region_get_interleave_ways(region); +- unsigned long long end = addr + len; +- struct json_object *jdimms, *jobj; +- struct ndctl_dimm **dimms, *dimm; +- int found, i; +- +- jdimms = json_object_new_array(); +- if (!jdimms) +- return NULL; +- +- dimms = calloc(count, sizeof(struct ndctl_dimm *)); +- if (!dimms) +- goto err_dimms; +- +- for (found = 0; found < count && addr < end; addr += 512) { +- dimm = ndctl_bus_get_dimm_by_physical_address(bus, addr); +- if (!dimm) +- continue; +- +- for (i = 0; i < count; i++) +- if (dimms[i] == dimm) +- break; +- if (i >= count) +- dimms[found++] = dimm; +- } +- +- if (!found) +- goto err_found; +- +- qsort(dimms, found, sizeof(dimm), compare_dimm_number); +- +- for (i = 0; i < found; i++) { +- const char *devname = ndctl_dimm_get_devname(dimms[i]); +- +- jobj = json_object_new_string(devname); +- if (!jobj) +- break; +- json_object_array_add(jdimms, jobj); +- } +- +- if (!i) +- goto err_found; +- free(dimms); +- return jdimms; +- +-err_found: +- free(dimms); +-err_dimms: +- json_object_put(jdimms); +- return NULL; +-} +- +-struct json_object *util_region_badblocks_to_json(struct ndctl_region *region, +- unsigned int *bb_count, unsigned long flags) +-{ +- struct json_object *jbb = NULL, *jbbs = NULL, *jobj; +- struct badblock *bb; +- int bbs = 0; +- +- if (flags & UTIL_JSON_MEDIA_ERRORS) { +- jbbs = json_object_new_array(); +- if (!jbbs) +- return NULL; +- } +- +- ndctl_region_badblock_foreach(region, bb) { +- struct json_object *jdimms; +- unsigned long long addr; +- +- bbs += bb->len; +- +- /* recheck so we can still get the badblocks_count from above */ +- if (!(flags & UTIL_JSON_MEDIA_ERRORS)) +- continue; +- +- /* get start address of region */ +- addr = ndctl_region_get_resource(region); +- if (addr == ULLONG_MAX) +- goto err_array; +- +- /* get address of bad block */ +- addr += bb->offset << 9; +- +- jbb = json_object_new_object(); +- if (!jbb) +- goto err_array; +- +- jobj = json_object_new_int64(bb->offset); +- if (!jobj) +- goto err; +- json_object_object_add(jbb, "offset", jobj); +- +- jobj = json_object_new_int(bb->len); +- if (!jobj) +- goto err; +- json_object_object_add(jbb, "length", jobj); +- +- jdimms = badblocks_to_jdimms(region, addr, bb->len << 9); +- if (jdimms) +- json_object_object_add(jbb, "dimms", jdimms); +- json_object_array_add(jbbs, jbb); +- } +- +- *bb_count = bbs; +- +- if (bbs) +- return jbbs; +- +- err: +- json_object_put(jbb); +- err_array: +- json_object_put(jbbs); +- return NULL; +-} +- +-static struct json_object *util_namespace_badblocks_to_json( +- struct ndctl_namespace *ndns, +- unsigned int *bb_count, unsigned long flags) +-{ +- struct json_object *jbb = NULL, *jbbs = NULL, *jobj; +- struct badblock *bb; +- int bbs = 0; +- +- if (flags & UTIL_JSON_MEDIA_ERRORS) { +- jbbs = json_object_new_array(); +- if (!jbbs) +- return NULL; +- } else +- return NULL; +- +- ndctl_namespace_badblock_foreach(ndns, bb) { +- bbs += bb->len; +- +- /* recheck so we can still get the badblocks_count from above */ +- if (!(flags & UTIL_JSON_MEDIA_ERRORS)) +- continue; +- +- jbb = json_object_new_object(); +- if (!jbb) +- goto err_array; +- +- jobj = json_object_new_int64(bb->offset); +- if (!jobj) +- goto err; +- json_object_object_add(jbb, "offset", jobj); +- +- jobj = json_object_new_int(bb->len); +- if (!jobj) +- goto err; +- json_object_object_add(jbb, "length", jobj); +- json_object_array_add(jbbs, jbb); +- } +- +- *bb_count = bbs; +- +- if (bbs) +- return jbbs; +- +- err: +- json_object_put(jbb); +- err_array: +- json_object_put(jbbs); +- return NULL; +-} +- +-static struct json_object *dev_badblocks_to_json(struct ndctl_region *region, +- unsigned long long dev_begin, unsigned long long dev_size, +- unsigned int *bb_count, unsigned long flags) +-{ +- struct json_object *jbb = NULL, *jbbs = NULL, *jobj; +- unsigned long long region_begin, dev_end, offset; +- unsigned int len, bbs = 0; +- struct badblock *bb; +- +- region_begin = ndctl_region_get_resource(region); +- if (region_begin == ULLONG_MAX) +- return NULL; +- +- dev_end = dev_begin + dev_size - 1; +- +- if (flags & UTIL_JSON_MEDIA_ERRORS) { +- jbbs = json_object_new_array(); +- if (!jbbs) +- return NULL; +- } +- +- ndctl_region_badblock_foreach(region, bb) { +- unsigned long long bb_begin, bb_end, begin, end; +- struct json_object *jdimms; +- +- bb_begin = region_begin + (bb->offset << 9); +- bb_end = bb_begin + (bb->len << 9) - 1; +- +- if (bb_end <= dev_begin || bb_begin >= dev_end) +- continue; +- +- if (bb_begin < dev_begin) +- begin = dev_begin; +- else +- begin = bb_begin; +- +- if (bb_end > dev_end) +- end = dev_end; +- else +- end = bb_end; +- +- offset = (begin - dev_begin) >> 9; +- len = (end - begin + 1) >> 9; +- +- bbs += len; +- +- /* recheck so we can still get the badblocks_count from above */ +- if (!(flags & UTIL_JSON_MEDIA_ERRORS)) +- continue; +- +- jbb = json_object_new_object(); +- if (!jbb) +- goto err_array; +- +- jobj = json_object_new_int64(offset); +- if (!jobj) +- goto err; +- json_object_object_add(jbb, "offset", jobj); +- +- jobj = json_object_new_int(len); +- if (!jobj) +- goto err; +- json_object_object_add(jbb, "length", jobj); +- +- jdimms = badblocks_to_jdimms(region, begin, len << 9); +- if (jdimms) +- json_object_object_add(jbb, "dimms", jdimms); +- +- json_object_array_add(jbbs, jbb); +- } +- +- *bb_count = bbs; +- +- if (bbs) +- return jbbs; +- +- err: +- json_object_put(jbb); +- err_array: +- json_object_put(jbbs); +- return NULL; +-} +- +-static struct json_object *util_pfn_badblocks_to_json(struct ndctl_pfn *pfn, +- unsigned int *bb_count, unsigned long flags) +-{ +- struct ndctl_region *region = ndctl_pfn_get_region(pfn); +- unsigned long long pfn_begin, pfn_size; +- +- pfn_begin = ndctl_pfn_get_resource(pfn); +- if (pfn_begin == ULLONG_MAX) { +- struct ndctl_namespace *ndns = ndctl_pfn_get_namespace(pfn); +- +- return util_namespace_badblocks_to_json(ndns, bb_count, flags); +- } +- +- pfn_size = ndctl_pfn_get_size(pfn); +- if (pfn_size == ULLONG_MAX) +- return NULL; +- +- return dev_badblocks_to_json(region, pfn_begin, pfn_size, +- bb_count, flags); +-} +- +-static void util_btt_badblocks_to_json(struct ndctl_btt *btt, +- unsigned int *bb_count) +-{ +- struct ndctl_region *region = ndctl_btt_get_region(btt); +- struct ndctl_namespace *ndns = ndctl_btt_get_namespace(btt); +- unsigned long long begin, size; +- +- if (!ndns) +- return; +- +- begin = ndctl_namespace_get_resource(ndns); +- if (begin == ULLONG_MAX) +- return; +- +- size = ndctl_namespace_get_size(ndns); +- if (size == ULLONG_MAX) +- return; +- +- /* +- * The dev_badblocks_to_json() for BTT is not accurate with +- * respect to data vs metadata badblocks, and is only useful for +- * a potential bb_count. +- * +- * FIXME: switch to native BTT badblocks representation +- * when / if the kernel provides it. +- */ +- dev_badblocks_to_json(region, begin, size, bb_count, 0); +-} +- +-static struct json_object *util_dax_badblocks_to_json(struct ndctl_dax *dax, +- unsigned int *bb_count, unsigned long flags) +-{ +- struct ndctl_region *region = ndctl_dax_get_region(dax); +- unsigned long long dax_begin, dax_size; +- +- dax_begin = ndctl_dax_get_resource(dax); +- if (dax_begin == ULLONG_MAX) +- return NULL; +- +- dax_size = ndctl_dax_get_size(dax); +- if (dax_size == ULLONG_MAX) +- return NULL; +- +- return dev_badblocks_to_json(region, dax_begin, dax_size, +- bb_count, flags); +-} +- +-static struct json_object *util_raw_uuid(struct ndctl_namespace *ndns) +-{ +- char buf[40]; +- uuid_t raw_uuid; +- +- ndctl_namespace_get_uuid(ndns, raw_uuid); +- if (uuid_is_null(raw_uuid)) +- return NULL; +- uuid_unparse(raw_uuid, buf); +- return json_object_new_string(buf); +-} +- +-static void util_raw_uuid_to_json(struct ndctl_namespace *ndns, +- unsigned long flags, +- struct json_object *jndns) +-{ +- struct json_object *jobj; +- +- if (!(flags & UTIL_JSON_VERBOSE)) +- return; +- +- jobj = util_raw_uuid(ndns); +- if (!jobj) +- return; +- json_object_object_add(jndns, "raw_uuid", jobj); +-} +- +-struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns, +- unsigned long flags) +-{ +- struct json_object *jndns = json_object_new_object(); +- enum ndctl_pfn_loc loc = NDCTL_PFN_LOC_NONE; +- struct json_object *jobj, *jbbs = NULL; +- const char *locations[] = { +- [NDCTL_PFN_LOC_NONE] = "none", +- [NDCTL_PFN_LOC_RAM] = "mem", +- [NDCTL_PFN_LOC_PMEM] = "dev", +- }; +- unsigned long long size = ULLONG_MAX; +- unsigned int sector_size = UINT_MAX; +- enum ndctl_namespace_mode mode; +- const char *bdev = NULL, *name; +- unsigned int bb_count = 0; +- struct ndctl_btt *btt; +- struct ndctl_pfn *pfn; +- struct ndctl_dax *dax; +- unsigned long align = 0; +- char buf[40]; +- uuid_t uuid; +- int numa, target; +- +- if (!jndns) +- return NULL; +- +- jobj = json_object_new_string(ndctl_namespace_get_devname(ndns)); +- if (!jobj) +- goto err; +- json_object_object_add(jndns, "dev", jobj); +- +- btt = ndctl_namespace_get_btt(ndns); +- dax = ndctl_namespace_get_dax(ndns); +- pfn = ndctl_namespace_get_pfn(ndns); +- mode = ndctl_namespace_get_mode(ndns); +- switch (mode) { +- case NDCTL_NS_MODE_MEMORY: +- if (pfn) { /* dynamic memory mode */ +- size = ndctl_pfn_get_size(pfn); +- loc = ndctl_pfn_get_location(pfn); +- } else { /* native/static memory mode */ +- size = ndctl_namespace_get_size(ndns); +- loc = NDCTL_PFN_LOC_RAM; +- } +- jobj = json_object_new_string("fsdax"); +- break; +- case NDCTL_NS_MODE_DAX: +- if (!dax) +- goto err; +- size = ndctl_dax_get_size(dax); +- jobj = json_object_new_string("devdax"); +- loc = ndctl_dax_get_location(dax); +- break; +- case NDCTL_NS_MODE_SECTOR: +- if (!btt) +- goto err; +- jobj = json_object_new_string("sector"); +- size = ndctl_btt_get_size(btt); +- break; +- case NDCTL_NS_MODE_RAW: +- size = ndctl_namespace_get_size(ndns); +- jobj = json_object_new_string("raw"); +- break; +- default: +- jobj = NULL; +- } +- if (jobj) +- json_object_object_add(jndns, "mode", jobj); +- +- if ((mode != NDCTL_NS_MODE_SECTOR) && (mode != NDCTL_NS_MODE_RAW)) { +- jobj = json_object_new_string(locations[loc]); +- if (jobj) +- json_object_object_add(jndns, "map", jobj); +- } +- +- if (size < ULLONG_MAX) { +- jobj = util_json_object_size(size, flags); +- if (jobj) +- json_object_object_add(jndns, "size", jobj); +- } +- +- if (btt) { +- ndctl_btt_get_uuid(btt, uuid); +- uuid_unparse(uuid, buf); +- jobj = json_object_new_string(buf); +- if (!jobj) +- goto err; +- json_object_object_add(jndns, "uuid", jobj); +- util_raw_uuid_to_json(ndns, flags, jndns); +- bdev = ndctl_btt_get_block_device(btt); +- } else if (pfn) { +- align = ndctl_pfn_get_align(pfn); +- ndctl_pfn_get_uuid(pfn, uuid); +- uuid_unparse(uuid, buf); +- jobj = json_object_new_string(buf); +- if (!jobj) +- goto err; +- json_object_object_add(jndns, "uuid", jobj); +- util_raw_uuid_to_json(ndns, flags, jndns); +- bdev = ndctl_pfn_get_block_device(pfn); +- } else if (dax) { +- struct daxctl_region *dax_region; +- +- dax_region = ndctl_dax_get_daxctl_region(dax); +- align = ndctl_dax_get_align(dax); +- ndctl_dax_get_uuid(dax, uuid); +- uuid_unparse(uuid, buf); +- jobj = json_object_new_string(buf); +- if (!jobj) +- goto err; +- json_object_object_add(jndns, "uuid", jobj); +- util_raw_uuid_to_json(ndns, flags, jndns); +- if ((flags & UTIL_JSON_DAX) && dax_region) { +- jobj = util_daxctl_region_to_json(dax_region, NULL, +- flags); +- if (jobj) +- json_object_object_add(jndns, "daxregion", jobj); +- } else if (dax_region) { +- struct daxctl_dev *dev; +- +- /* +- * We can only find/list these device-dax +- * details when the instance is enabled. +- */ +- dev = daxctl_dev_get_first(dax_region); +- if (dev) { +- name = daxctl_dev_get_devname(dev); +- jobj = json_object_new_string(name); +- if (!jobj) +- goto err; +- json_object_object_add(jndns, "chardev", jobj); +- } +- } +- } else if (ndctl_namespace_get_type(ndns) != ND_DEVICE_NAMESPACE_IO) { +- ndctl_namespace_get_uuid(ndns, uuid); +- uuid_unparse(uuid, buf); +- jobj = json_object_new_string(buf); +- if (!jobj) +- goto err; +- json_object_object_add(jndns, "uuid", jobj); +- bdev = ndctl_namespace_get_block_device(ndns); +- } else +- bdev = ndctl_namespace_get_block_device(ndns); +- +- if (btt) +- sector_size = ndctl_btt_get_sector_size(btt); +- else if (!dax) { +- sector_size = ndctl_namespace_get_sector_size(ndns); +- if (!sector_size || sector_size == UINT_MAX) +- sector_size = 512; +- } +- +- /* +- * The kernel will default to a 512 byte sector size on PMEM +- * namespaces that don't explicitly have a sector size. This +- * happens because they use pre-v1.2 labels or because they +- * don't have a label space (devtype=nd_namespace_io). +- */ +- if (sector_size < UINT_MAX) { +- jobj = json_object_new_int(sector_size); +- if (!jobj) +- goto err; +- json_object_object_add(jndns, "sector_size", jobj); +- } +- +- if (align) { +- jobj = json_object_new_int64(align); +- if (!jobj) +- goto err; +- json_object_object_add(jndns, "align", jobj); +- } +- +- if (bdev && bdev[0]) { +- jobj = json_object_new_string(bdev); +- if (!jobj) +- goto err; +- json_object_object_add(jndns, "blockdev", jobj); +- } +- +- if (!ndctl_namespace_is_active(ndns)) { +- jobj = json_object_new_string("disabled"); +- if (!jobj) +- goto err; +- json_object_object_add(jndns, "state", jobj); +- } +- +- name = ndctl_namespace_get_alt_name(ndns); +- if (name && name[0]) { +- jobj = json_object_new_string(name); +- if (!jobj) +- goto err; +- json_object_object_add(jndns, "name", jobj); +- } +- +- numa = ndctl_namespace_get_numa_node(ndns); +- if (numa >= 0 && flags & UTIL_JSON_VERBOSE) { +- jobj = json_object_new_int(numa); +- if (jobj) +- json_object_object_add(jndns, "numa_node", jobj); +- } +- +- target = ndctl_namespace_get_target_node(ndns); +- if (target >= 0 && flags & UTIL_JSON_VERBOSE) { +- jobj = json_object_new_int(target); +- if (jobj) +- json_object_object_add(jndns, "target_node", jobj); +- } +- +- if (pfn) +- jbbs = util_pfn_badblocks_to_json(pfn, &bb_count, flags); +- else if (dax) +- jbbs = util_dax_badblocks_to_json(dax, &bb_count, flags); +- else if (btt) +- util_btt_badblocks_to_json(btt, &bb_count); +- else { +- jbbs = util_region_badblocks_to_json( +- ndctl_namespace_get_region(ndns), &bb_count, +- flags); +- if (!jbbs) +- jbbs = util_namespace_badblocks_to_json(ndns, &bb_count, +- flags); +- } +- +- if (bb_count) { +- jobj = json_object_new_int(bb_count); +- if (!jobj) { +- json_object_put(jbbs); +- goto err; +- } +- json_object_object_add(jndns, "badblock_count", jobj); +- } +- +- if ((flags & UTIL_JSON_MEDIA_ERRORS) && jbbs) +- json_object_object_add(jndns, "badblocks", jbbs); +- +- return jndns; +- err: +- json_object_put(jndns); +- return NULL; +-} +- +-struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping, +- unsigned long flags) +-{ +- struct json_object *jmapping = json_object_new_object(); +- struct ndctl_dimm *dimm = ndctl_mapping_get_dimm(mapping); +- struct json_object *jobj; +- int position; +- +- if (!jmapping) +- return NULL; +- +- jobj = json_object_new_string(ndctl_dimm_get_devname(dimm)); +- if (!jobj) +- goto err; +- json_object_object_add(jmapping, "dimm", jobj); +- +- jobj = util_json_object_hex(ndctl_mapping_get_offset(mapping), flags); +- if (!jobj) +- goto err; +- json_object_object_add(jmapping, "offset", jobj); +- +- jobj = util_json_object_hex(ndctl_mapping_get_length(mapping), flags); +- if (!jobj) +- goto err; +- json_object_object_add(jmapping, "length", jobj); +- +- position = ndctl_mapping_get_position(mapping); +- if (position >= 0) { +- jobj = json_object_new_int(position); +- if (!jobj) +- goto err; +- json_object_object_add(jmapping, "position", jobj); +- } +- +- return jmapping; +- err: +- json_object_put(jmapping); +- return NULL; +-} +- +-struct json_object *util_daxctl_mapping_to_json(struct daxctl_mapping *mapping, +- unsigned long flags) +-{ +- struct json_object *jmapping = json_object_new_object(); +- struct json_object *jobj; +- +- if (!jmapping) +- return NULL; +- +- jobj = util_json_object_hex(daxctl_mapping_get_offset(mapping), flags); +- if (!jobj) +- goto err; +- json_object_object_add(jmapping, "page_offset", jobj); +- +- jobj = util_json_object_hex(daxctl_mapping_get_start(mapping), flags); +- if (!jobj) +- goto err; +- json_object_object_add(jmapping, "start", jobj); +- +- jobj = util_json_object_hex(daxctl_mapping_get_end(mapping), flags); +- if (!jobj) +- goto err; +- json_object_object_add(jmapping, "end", jobj); +- +- jobj = util_json_object_size(daxctl_mapping_get_size(mapping), flags); +- if (!jobj) +- goto err; +- json_object_object_add(jmapping, "size", jobj); +- +- return jmapping; +- err: +- json_object_put(jmapping); +- return NULL; +-} +- +-struct json_object *util_badblock_rec_to_json(u64 block, u64 count, +- unsigned long flags) +-{ +- struct json_object *jerr = json_object_new_object(); +- struct json_object *jobj; +- +- if (!jerr) +- return NULL; +- +- jobj = util_json_object_hex(block, flags); +- if (!jobj) +- goto err; +- json_object_object_add(jerr, "block", jobj); +- +- jobj = util_json_object_hex(count, flags); +- if (!jobj) +- goto err; +- json_object_object_add(jerr, "count", jobj); +- +- return jerr; +- err: +- json_object_put(jerr); +- return NULL; +-} +- +-static struct json_object *util_cxl_memdev_health_to_json( +- struct cxl_memdev *memdev, unsigned long flags) +-{ +- struct json_object *jhealth; +- struct json_object *jobj; +- struct cxl_cmd *cmd; +- u32 field; +- int rc; +- +- jhealth = json_object_new_object(); +- if (!jhealth) +- return NULL; +- if (!memdev) +- goto err_jobj; +- +- cmd = cxl_cmd_new_get_health_info(memdev); +- if (!cmd) +- goto err_jobj; +- +- rc = cxl_cmd_submit(cmd); +- if (rc < 0) +- goto err_cmd; +- rc = cxl_cmd_get_mbox_status(cmd); +- if (rc != 0) +- goto err_cmd; +- +- /* health_status fields */ +- rc = cxl_cmd_health_info_get_maintenance_needed(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "maintenance_needed", jobj); +- +- rc = cxl_cmd_health_info_get_performance_degraded(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "performance_degraded", jobj); +- +- rc = cxl_cmd_health_info_get_hw_replacement_needed(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "hw_replacement_needed", jobj); +- +- /* media_status fields */ +- rc = cxl_cmd_health_info_get_media_normal(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "media_normal", jobj); +- +- rc = cxl_cmd_health_info_get_media_not_ready(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "media_not_ready", jobj); +- +- rc = cxl_cmd_health_info_get_media_persistence_lost(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "media_persistence_lost", jobj); +- +- rc = cxl_cmd_health_info_get_media_data_lost(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "media_data_lost", jobj); +- +- rc = cxl_cmd_health_info_get_media_powerloss_persistence_loss(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "media_powerloss_persistence_loss", jobj); +- +- rc = cxl_cmd_health_info_get_media_shutdown_persistence_loss(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "media_shutdown_persistence_loss", jobj); +- +- rc = cxl_cmd_health_info_get_media_persistence_loss_imminent(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "media_persistence_loss_imminent", jobj); +- +- rc = cxl_cmd_health_info_get_media_powerloss_data_loss(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "media_powerloss_data_loss", jobj); +- +- rc = cxl_cmd_health_info_get_media_shutdown_data_loss(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "media_shutdown_data_loss", jobj); +- +- rc = cxl_cmd_health_info_get_media_data_loss_imminent(cmd); +- jobj = json_object_new_boolean(rc); +- if (jobj) +- json_object_object_add(jhealth, "media_data_loss_imminent", jobj); +- +- /* ext_status fields */ +- if (cxl_cmd_health_info_get_ext_life_used_normal(cmd)) +- jobj = json_object_new_string("normal"); +- else if (cxl_cmd_health_info_get_ext_life_used_warning(cmd)) +- jobj = json_object_new_string("warning"); +- else if (cxl_cmd_health_info_get_ext_life_used_critical(cmd)) +- jobj = json_object_new_string("critical"); +- else +- jobj = json_object_new_string("unknown"); +- if (jobj) +- json_object_object_add(jhealth, "ext_life_used", jobj); +- +- if (cxl_cmd_health_info_get_ext_temperature_normal(cmd)) +- jobj = json_object_new_string("normal"); +- else if (cxl_cmd_health_info_get_ext_temperature_warning(cmd)) +- jobj = json_object_new_string("warning"); +- else if (cxl_cmd_health_info_get_ext_temperature_critical(cmd)) +- jobj = json_object_new_string("critical"); +- else +- jobj = json_object_new_string("unknown"); +- if (jobj) +- json_object_object_add(jhealth, "ext_temperature", jobj); +- +- if (cxl_cmd_health_info_get_ext_corrected_volatile_normal(cmd)) +- jobj = json_object_new_string("normal"); +- else if (cxl_cmd_health_info_get_ext_corrected_volatile_warning(cmd)) +- jobj = json_object_new_string("warning"); +- else +- jobj = json_object_new_string("unknown"); +- if (jobj) +- json_object_object_add(jhealth, "ext_corrected_volatile", jobj); +- +- if (cxl_cmd_health_info_get_ext_corrected_persistent_normal(cmd)) +- jobj = json_object_new_string("normal"); +- else if (cxl_cmd_health_info_get_ext_corrected_persistent_warning(cmd)) +- jobj = json_object_new_string("warning"); +- else +- jobj = json_object_new_string("unknown"); +- if (jobj) +- json_object_object_add(jhealth, "ext_corrected_persistent", jobj); +- +- /* other fields */ +- field = cxl_cmd_health_info_get_life_used(cmd); +- if (field != 0xff) { +- jobj = json_object_new_int(field); +- if (jobj) +- json_object_object_add(jhealth, "life_used_percent", jobj); +- } +- +- field = cxl_cmd_health_info_get_temperature(cmd); +- if (field != 0xffff) { +- jobj = json_object_new_int(field); +- if (jobj) +- json_object_object_add(jhealth, "temperature", jobj); +- } +- +- field = cxl_cmd_health_info_get_dirty_shutdowns(cmd); +- jobj = json_object_new_int64(field); +- if (jobj) +- json_object_object_add(jhealth, "dirty_shutdowns", jobj); +- +- field = cxl_cmd_health_info_get_volatile_errors(cmd); +- jobj = json_object_new_int64(field); +- if (jobj) +- json_object_object_add(jhealth, "volatile_errors", jobj); +- +- field = cxl_cmd_health_info_get_pmem_errors(cmd); +- jobj = json_object_new_int64(field); +- if (jobj) +- json_object_object_add(jhealth, "pmem_errors", jobj); +- +- cxl_cmd_unref(cmd); +- return jhealth; +- +-err_cmd: +- cxl_cmd_unref(cmd); +-err_jobj: +- json_object_put(jhealth); +- return NULL; +-} +- +-struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, +- unsigned long flags) +-{ +- const char *devname = cxl_memdev_get_devname(memdev); +- struct json_object *jdev, *jobj; +- +- jdev = json_object_new_object(); +- if (!devname || !jdev) +- return NULL; +- +- jobj = json_object_new_string(devname); +- if (jobj) +- json_object_object_add(jdev, "memdev", jobj); +- +- jobj = util_json_object_size(cxl_memdev_get_pmem_size(memdev), flags); +- if (jobj) +- json_object_object_add(jdev, "pmem_size", jobj); +- +- jobj = util_json_object_size(cxl_memdev_get_ram_size(memdev), flags); +- if (jobj) +- json_object_object_add(jdev, "ram_size", jobj); +- +- if (flags & UTIL_JSON_HEALTH) { +- jobj = util_cxl_memdev_health_to_json(memdev, flags); +- if (jobj) +- json_object_object_add(jdev, "health", jobj); +- } +- return jdev; +-} +diff -up ndctl-71.1/util/json.h.orig ndctl-71.1/util/json.h +--- ndctl-71.1/util/json.h.orig 2022-10-07 15:50:40.097454638 -0400 ++++ ndctl-71.1/util/json.h 2022-10-07 15:51:03.927535772 -0400 +@@ -1,12 +1,9 @@ + /* SPDX-License-Identifier: GPL-2.0 */ + /* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ +-#ifndef __NDCTL_JSON_H__ +-#define __NDCTL_JSON_H__ ++#ifndef __UTIL_JSON_H__ ++#define __UTIL_JSON_H__ + #include + #include +-#include +-#include +-#include + + enum util_json_flags { + UTIL_JSON_IDLE = (1 << 0), +@@ -25,38 +22,8 @@ enum util_json_flags { + struct json_object; + void util_display_json_array(FILE *f_out, struct json_object *jarray, + unsigned long flags); +-struct json_object *util_bus_to_json(struct ndctl_bus *bus, +- unsigned long flags); +-struct json_object *util_dimm_to_json(struct ndctl_dimm *dimm, +- unsigned long flags); +-struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping, +- unsigned long flags); +-struct json_object *util_daxctl_mapping_to_json(struct daxctl_mapping *mapping, +- unsigned long flags); +-struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns, +- unsigned long flags); +-struct json_object *util_badblock_rec_to_json(u64 block, u64 count, +- unsigned long flags); +-struct daxctl_region; +-struct daxctl_dev; +-struct json_object *util_region_badblocks_to_json(struct ndctl_region *region, +- unsigned int *bb_count, unsigned long flags); +-struct json_object *util_daxctl_region_to_json(struct daxctl_region *region, +- const char *ident, unsigned long flags); +-struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, +- unsigned long flags); +-struct json_object *util_daxctl_devs_to_list(struct daxctl_region *region, +- struct json_object *jdevs, const char *ident, +- unsigned long flags); + struct json_object *util_json_object_size(unsigned long long size, + unsigned long flags); + struct json_object *util_json_object_hex(unsigned long long val, + unsigned long flags); +-struct json_object *util_dimm_health_to_json(struct ndctl_dimm *dimm); +-struct json_object *util_dimm_firmware_to_json(struct ndctl_dimm *dimm, +- unsigned long flags); +-struct json_object *util_region_capabilities_to_json(struct ndctl_region *region); +-struct cxl_memdev; +-struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, +- unsigned long flags); +-#endif /* __NDCTL_JSON_H__ */ ++#endif /* __UTIL_JSON_H__ */ diff --git a/SOURCES/0078-Documentation-Drop-attrs.adoc-include.patch b/SOURCES/0078-Documentation-Drop-attrs.adoc-include.patch new file mode 100644 index 0000000..5233185 --- /dev/null +++ b/SOURCES/0078-Documentation-Drop-attrs.adoc-include.patch @@ -0,0 +1,104 @@ +From d1b966de2b32f6152bc3b9c3f5d842ba12407a87 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:32 -0800 +Subject: [PATCH 078/217] Documentation: Drop attrs.adoc include + +In preparation for switching build systems, drop the attrs.adoc include for +communicating variables to asciidoc. Simply add the necessary variable +values to the invocation of the command using the --attribute argument. + +Link: https://lore.kernel.org/r/164141835217.3990253.17678912974035740752.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .gitignore | 1 - + Documentation/daxctl/Makefile.am | 17 +++++++---------- + .../daxctl/daxctl-reconfigure-device.txt | 2 -- + Documentation/ndctl/Makefile.am | 17 +++++++---------- + Documentation/ndctl/intel-nvdimm-security.txt | 2 -- + Documentation/ndctl/ndctl-load-keys.txt | 2 -- + Documentation/ndctl/ndctl-monitor.txt | 2 -- + Documentation/ndctl/ndctl-sanitize-dimm.txt | 2 -- + Documentation/ndctl/ndctl-setup-passphrase.txt | 2 -- + Documentation/ndctl/ndctl-update-passphrase.txt | 2 -- + 10 files changed, 14 insertions(+), 35 deletions(-) + +diff -up ndctl-71.1/.gitignore.orig ndctl-71.1/.gitignore +--- ndctl-71.1/.gitignore.orig 2022-10-07 15:58:15.663005697 -0400 ++++ ndctl-71.1/.gitignore 2022-10-07 15:58:57.238147247 -0400 +@@ -23,7 +23,6 @@ Documentation/daxctl/asciidoctor-extensi + Documentation/ndctl/asciidoctor-extensions.rb + Documentation/cxl/asciidoctor-extensions.rb + Documentation/cxl/lib/asciidoctor-extensions.rb +-Documentation/ndctl/attrs.adoc + .dirstamp + daxctl/config.h + daxctl/daxctl +diff -up ndctl-71.1/Documentation/ndctl/intel-nvdimm-security.txt.orig ndctl-71.1/Documentation/ndctl/intel-nvdimm-security.txt +--- ndctl-71.1/Documentation/ndctl/intel-nvdimm-security.txt.orig 2020-12-22 16:44:57.000000000 -0500 ++++ ndctl-71.1/Documentation/ndctl/intel-nvdimm-security.txt 2022-10-07 15:59:06.192177733 -0400 +@@ -1,7 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 + +-include::attrs.adoc[] +- + THEORY OF OPERATION + ------------------- + The Intel Device Specific Methods (DSM) specification v1.7 and v1.8 [1] +diff -up ndctl-71.1/Documentation/ndctl/ndctl-load-keys.txt.orig ndctl-71.1/Documentation/ndctl/ndctl-load-keys.txt +--- ndctl-71.1/Documentation/ndctl/ndctl-load-keys.txt.orig 2020-12-22 16:44:57.000000000 -0500 ++++ ndctl-71.1/Documentation/ndctl/ndctl-load-keys.txt 2022-10-07 15:59:06.192177733 -0400 +@@ -1,7 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 + +-include::attrs.adoc[] +- + ndctl-load-keys(1) + ================== + +diff -up ndctl-71.1/Documentation/ndctl/ndctl-monitor.txt.orig ndctl-71.1/Documentation/ndctl/ndctl-monitor.txt +--- ndctl-71.1/Documentation/ndctl/ndctl-monitor.txt.orig 2020-12-22 16:44:57.000000000 -0500 ++++ ndctl-71.1/Documentation/ndctl/ndctl-monitor.txt 2022-10-07 15:59:06.192177733 -0400 +@@ -1,7 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 + +-include::attrs.adoc[] +- + ndctl-monitor(1) + ================ + +diff -up ndctl-71.1/Documentation/ndctl/ndctl-sanitize-dimm.txt.orig ndctl-71.1/Documentation/ndctl/ndctl-sanitize-dimm.txt +--- ndctl-71.1/Documentation/ndctl/ndctl-sanitize-dimm.txt.orig 2020-12-22 16:44:57.000000000 -0500 ++++ ndctl-71.1/Documentation/ndctl/ndctl-sanitize-dimm.txt 2022-10-07 15:59:06.192177733 -0400 +@@ -1,7 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 + +-include::attrs.adoc[] +- + ndctl-sanitize-dimm(1) + ====================== + +diff -up ndctl-71.1/Documentation/ndctl/ndctl-setup-passphrase.txt.orig ndctl-71.1/Documentation/ndctl/ndctl-setup-passphrase.txt +--- ndctl-71.1/Documentation/ndctl/ndctl-setup-passphrase.txt.orig 2020-12-22 16:44:57.000000000 -0500 ++++ ndctl-71.1/Documentation/ndctl/ndctl-setup-passphrase.txt 2022-10-07 15:59:06.193177737 -0400 +@@ -1,7 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 + +-include::attrs.adoc[] +- + ndctl-setup-passphrase(1) + ========================= + +diff -up ndctl-71.1/Documentation/ndctl/ndctl-update-passphrase.txt.orig ndctl-71.1/Documentation/ndctl/ndctl-update-passphrase.txt +--- ndctl-71.1/Documentation/ndctl/ndctl-update-passphrase.txt.orig 2020-12-22 16:44:57.000000000 -0500 ++++ ndctl-71.1/Documentation/ndctl/ndctl-update-passphrase.txt 2022-10-07 15:59:06.193177737 -0400 +@@ -1,7 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 + +-include::attrs.adoc[] +- + ndctl-update-passphrase(1) + ========================== + diff --git a/SOURCES/0079-build-Drop-unnecessary-tool-config.h-includes.patch b/SOURCES/0079-build-Drop-unnecessary-tool-config.h-includes.patch new file mode 100644 index 0000000..ee5daad --- /dev/null +++ b/SOURCES/0079-build-Drop-unnecessary-tool-config.h-includes.patch @@ -0,0 +1,53 @@ +From 3297995248081d31d282fc9a339894989ff94e23 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:37 -0800 +Subject: [PATCH 079/217] build: Drop unnecessary $tool/config.h includes + +In preparation for support for meson as the build infrastructure remove +some explicit config.h includes that will be replaced by a unified config.h +at the top of the project. + +Link: https://lore.kernel.org/r/164141835727.3990253.12971738434561351928.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + daxctl/migrate.c | 1 - + ndctl/keys.c | 1 - + ndctl/monitor.c | 1 - + 3 files changed, 3 deletions(-) + +diff -up ndctl-71.1/daxctl/migrate.c.orig ndctl-71.1/daxctl/migrate.c +--- ndctl-71.1/daxctl/migrate.c.orig 2020-12-22 16:44:57.000000000 -0500 ++++ ndctl-71.1/daxctl/migrate.c 2022-10-07 16:01:20.316634385 -0400 +@@ -5,7 +5,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -up ndctl-71.1/ndctl/keys.c.orig ndctl-71.1/ndctl/keys.c +--- ndctl-71.1/ndctl/keys.c.orig 2022-10-07 16:01:03.161575978 -0400 ++++ ndctl-71.1/ndctl/keys.c 2022-10-07 16:01:20.317634389 -0400 +@@ -13,7 +13,6 @@ + #include + #include + +-#include + #include + #include + +diff -up ndctl-71.1/ndctl/monitor.c.orig ndctl-71.1/ndctl/monitor.c +--- ndctl-71.1/ndctl/monitor.c.orig 2022-10-07 16:01:03.164575988 -0400 ++++ ndctl-71.1/ndctl/monitor.c 2022-10-07 16:01:20.318634392 -0400 +@@ -10,7 +10,6 @@ + #include + #include + #include +-#include + #include + #include + #include diff --git a/SOURCES/0080-test-Prepare-out-of-line-builds.patch b/SOURCES/0080-test-Prepare-out-of-line-builds.patch new file mode 100644 index 0000000..cdc8ba8 --- /dev/null +++ b/SOURCES/0080-test-Prepare-out-of-line-builds.patch @@ -0,0 +1,355 @@ +From d12d5f82755db50277e50c8daa97be15107f924d Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:42 -0800 +Subject: [PATCH 080/217] test: Prepare out of line builds + +In preparation for converting to meson prepare the unit tests to run out of +a build directory rather than out of the source directory. Introduce +TEST_PATH for the location of the test executables. + +Link: https://lore.kernel.org/r/164141836235.3990253.5237538466465550643.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/btt-errors.sh | 4 +--- + test/common | 37 +++++++++++++++++++++---------------- + test/dax-pmd.c | 11 +++++++++-- + test/dax.sh | 6 +++--- + test/daxdev-errors.sh | 4 ++-- + test/device-dax-fio.sh | 2 +- + test/dm.sh | 4 ++-- + test/inject-smart.sh | 2 +- + test/mmap.sh | 6 +++--- + test/monitor.sh | 6 +++--- + test/pmem-errors.sh | 8 +++----- + test/sub-section.sh | 4 ++-- + test/track-uuid.sh | 2 +- + 13 files changed, 52 insertions(+), 44 deletions(-) + +diff --git a/test/btt-errors.sh b/test/btt-errors.sh +index 6e69178..18518d5 100755 +--- a/test/btt-errors.sh ++++ b/test/btt-errors.sh +@@ -11,14 +11,12 @@ rc=77 + + cleanup() + { +- rm -f $FILE +- rm -f $MNT/$FILE + if grep -q "$MNT" /proc/mounts; then + umount $MNT + else + rc=77 + fi +- rmdir $MNT ++ rm -rf $MNT + } + + force_raw() +diff --git a/test/common b/test/common +index b6d4712..fb48795 100644 +--- a/test/common ++++ b/test/common +@@ -4,27 +4,32 @@ + # Global variables + + # NDCTL +-# +-if [ -f "../ndctl/ndctl" ] && [ -x "../ndctl/ndctl" ]; then +- export NDCTL=../ndctl/ndctl +-elif [ -f "./ndctl/ndctl" ] && [ -x "./ndctl/ndctl" ]; then +- export NDCTL=./ndctl/ndctl +-else +- echo "Couldn't find an ndctl binary" +- exit 1 ++if [ -z $NDCTL ]; then ++ if [ -f "../ndctl/ndctl" ] && [ -x "../ndctl/ndctl" ]; then ++ export NDCTL=../ndctl/ndctl ++ elif [ -f "./ndctl/ndctl" ] && [ -x "./ndctl/ndctl" ]; then ++ export NDCTL=./ndctl/ndctl ++ else ++ echo "Couldn't find an ndctl binary" ++ exit 1 ++ fi + fi + + # DAXCTL +-# +-if [ -f "../daxctl/daxctl" ] && [ -x "../daxctl/daxctl" ]; then +- export DAXCTL=../daxctl/daxctl +-elif [ -f "./daxctl/daxctl" ] && [ -x "./daxctl/daxctl" ]; then +- export DAXCTL=./daxctl/daxctl +-else +- echo "Couldn't find an daxctl binary" +- exit 1 ++if [ -z $DAXCTL ]; then ++ if [ -f "../daxctl/daxctl" ] && [ -x "../daxctl/daxctl" ]; then ++ export DAXCTL=../daxctl/daxctl ++ elif [ -f "./daxctl/daxctl" ] && [ -x "./daxctl/daxctl" ]; then ++ export DAXCTL=./daxctl/daxctl ++ else ++ echo "Couldn't find an daxctl binary" ++ exit 1 ++ fi + fi + ++if [ -z $TEST_PATH ]; then ++ export TEST_PATH=. ++fi + + # NFIT_TEST_BUS[01] + # +diff --git a/test/dax-pmd.c b/test/dax-pmd.c +index 7648e34..f840875 100644 +--- a/test/dax-pmd.c ++++ b/test/dax-pmd.c +@@ -24,7 +24,8 @@ + __func__, __LINE__, strerror(errno)) + #define faili(i) fprintf(stderr, "%s: failed at: %d: %d (%s)\n", \ + __func__, __LINE__, i, strerror(errno)) +-#define TEST_FILE "test_dax_data" ++#define TEST_DIR "test_dax_mnt" ++#define TEST_FILE TEST_DIR "/test_dax_data" + + #define REGION_MEM_SIZE 4096*4 + #define REGION_PM_SIZE 4096*512 +@@ -171,8 +172,14 @@ int test_dax_directio(int dax_fd, unsigned long align, void *dax_addr, off_t off + } + rc = -ENXIO; + ++ rc = mkdir(TEST_DIR, 0600); ++ if (rc < 0 && errno != EEXIST) { ++ faili(i); ++ munmap(addr, 2 * align); ++ break; ++ } + fd2 = open(TEST_FILE, O_CREAT|O_TRUNC|O_DIRECT|O_RDWR, +- DEFFILEMODE); ++ 0600); + if (fd2 < 0) { + faili(i); + munmap(addr, 2*align); +diff --git a/test/dax.sh b/test/dax.sh +index bcdd4e9..bb9848b 100755 +--- a/test/dax.sh ++++ b/test/dax.sh +@@ -15,13 +15,13 @@ cleanup() { + else + rc=77 + fi +- rmdir $MNT ++ rm -rf $MNT + exit $rc + } + + run_test() { + rc=0 +- if ! trace-cmd record -e fs_dax:dax_pmd_fault_done ./dax-pmd $MNT/$FILE; then ++ if ! trace-cmd record -e fs_dax:dax_pmd_fault_done $TEST_PATH/dax-pmd $MNT/$FILE; then + rc=$? + if [ "$rc" -ne 77 ] && [ "$rc" -ne 0 ]; then + cleanup "$1" +@@ -104,7 +104,7 @@ set -e + mkdir -p $MNT + trap 'err $LINENO cleanup' ERR + +-dev=$(./dax-dev) ++dev=$($TEST_PATH/dax-dev) + json=$($NDCTL list -N -n $dev) + eval $(json2var <<< "$json") + rc=1 +diff --git a/test/daxdev-errors.sh b/test/daxdev-errors.sh +index e13453d..7f79718 100755 +--- a/test/daxdev-errors.sh ++++ b/test/daxdev-errors.sh +@@ -62,8 +62,8 @@ read sector len < /sys/bus/nd/devices/$region/badblocks + echo "sector: $sector len: $len" + + # run the daxdev-errors test +-test -x ./daxdev-errors +-./daxdev-errors $busdev $region ++test -x $TEST_PATH/daxdev-errors ++$TEST_PATH/daxdev-errors $busdev $region + + # check badblocks, should be empty + if read sector len < /sys/bus/platform/devices/nfit_test.0/$busdev/$region/badblocks; then +diff --git a/test/device-dax-fio.sh b/test/device-dax-fio.sh +index f57a9d2..c43ac05 100755 +--- a/test/device-dax-fio.sh ++++ b/test/device-dax-fio.sh +@@ -18,7 +18,7 @@ if ! fio --enghelp | grep -q "dev-dax"; then + exit 77 + fi + +-dev=$(./dax-dev) ++dev=$($TEST_PATH/dax-dev) + for align in 4k 2m 1g + do + json=$($NDCTL create-namespace -m devdax -a $align -f -e $dev) +diff --git a/test/dm.sh b/test/dm.sh +index 4656e5b..b780a65 100755 +--- a/test/dm.sh ++++ b/test/dm.sh +@@ -8,7 +8,7 @@ SKIP=77 + FAIL=1 + SUCCESS=0 + +-. ./common ++. $(dirname $0)/common + + MNT=test_dax_mnt + TEST_DM_PMEM=/dev/mapper/test_pmem +@@ -30,7 +30,7 @@ cleanup() { + if [ -L $TEST_DM_PMEM ]; then + dmsetup remove $TEST_DM_PMEM + fi +- rmdir $MNT ++ rm -rf $MNT + # opportunistic cleanup, not fatal if these fail + namespaces=$($NDCTL list -N | jq -r ".[] | select(.name==\"$NAME\") | .dev") + for i in $namespaces +diff --git a/test/inject-smart.sh b/test/inject-smart.sh +index 4ca83b8..8b91360 100755 +--- a/test/inject-smart.sh ++++ b/test/inject-smart.sh +@@ -170,7 +170,7 @@ check_prereq "jq" + modprobe nfit_test + rc=1 + +-jlist=$(./list-smart-dimm -b $bus) ++jlist=$($TEST_PATH/list-smart-dimm -b $bus) + dimm="$(jq '.[]."dev"?, ."dev"?' <<< $jlist | sort | head -1 | xargs)" + test -n "$dimm" + +diff --git a/test/mmap.sh b/test/mmap.sh +index 50a1d34..760257d 100755 +--- a/test/mmap.sh ++++ b/test/mmap.sh +@@ -7,7 +7,7 @@ + MNT=test_mmap_mnt + FILE=image + DEV="" +-TEST=./mmap ++TEST=$TEST_PATH/mmap + rc=77 + + cleanup() { +@@ -17,7 +17,7 @@ cleanup() { + else + rc=77 + fi +- rmdir $MNT ++ rm -rf $MNT + exit $rc + } + +@@ -49,7 +49,7 @@ set -e + mkdir -p $MNT + trap 'err $LINENO cleanup' ERR + +-dev=$(./dax-dev) ++dev=$($TEST_PATH/dax-dev) + json=$($NDCTL list -N -n $dev) + eval $(json2var <<< "$json") + DEV="/dev/${blockdev}" +diff --git a/test/monitor.sh b/test/monitor.sh +index 6aa4196..e58c908 100755 +--- a/test/monitor.sh ++++ b/test/monitor.sh +@@ -31,7 +31,7 @@ start_monitor() + set_smart_supported_bus() + { + smart_supported_bus=$NFIT_TEST_BUS0 +- monitor_dimms=$(./list-smart-dimm -b $smart_supported_bus | jq -r .[0].dev) ++ monitor_dimms=$($TEST_PATH/list-smart-dimm -b $smart_supported_bus | jq -r .[0].dev) + if [ -z $monitor_dimms ]; then + smart_supported_bus=$NFIT_TEST_BUS1 + fi +@@ -39,14 +39,14 @@ set_smart_supported_bus() + + get_monitor_dimm() + { +- jlist=$(./list-smart-dimm -b $smart_supported_bus $1) ++ jlist=$($TEST_PATH/list-smart-dimm -b $smart_supported_bus $1) + monitor_dimms=$(jq '.[]."dev"?, ."dev"?' <<<$jlist | sort | uniq | xargs) + echo $monitor_dimms + } + + call_notify() + { +- ./smart-notify $smart_supported_bus ++ $TEST_PATH/smart-notify $smart_supported_bus + sync; sleep 3 + } + +diff --git a/test/pmem-errors.sh b/test/pmem-errors.sh +index 2065780..9a59c25 100755 +--- a/test/pmem-errors.sh ++++ b/test/pmem-errors.sh +@@ -10,14 +10,12 @@ rc=77 + + cleanup() + { +- rm -f $FILE +- rm -f $MNT/$FILE + if [ -n "$blockdev" ]; then + umount /dev/$blockdev + else + rc=77 + fi +- rmdir $MNT ++ rm -rf $MNT + } + + check_min_kver "4.7" || do_skip "may lack dax error handling" +@@ -82,8 +80,8 @@ echo $start_sect 8 > /sys/block/$blockdev/badblocks + dd if=$MNT/$FILE of=/dev/null iflag=direct bs=4096 count=1 && err $LINENO || true + + # run the dax-errors test +-test -x ./dax-errors +-./dax-errors $MNT/$FILE ++test -x $TEST_PATH/dax-errors ++$TEST_PATH/dax-errors $MNT/$FILE + + # TODO: disable this check till we have clear-on-write in the kernel + #if read sector len < /sys/block/$blockdev/badblocks; then +diff --git a/test/sub-section.sh b/test/sub-section.sh +index 92ae816..77b9633 100755 +--- a/test/sub-section.sh ++++ b/test/sub-section.sh +@@ -8,7 +8,7 @@ SKIP=77 + FAIL=1 + SUCCESS=0 + +-. ./common ++. $(dirname $0)/common + + check_min_kver "5.3" || do_skip "may lack align sub-section hotplug support" + +@@ -30,7 +30,7 @@ cleanup() { + if mountpoint -q $MNT; then + umount $MNT + fi +- rmdir $MNT ++ rm -rf $MNT + # opportunistic cleanup, not fatal if these fail + namespaces=$($NDCTL list -N | jq -r ".[] | select(.name==\"$NAME\") | .dev") + for i in $namespaces +diff --git a/test/track-uuid.sh b/test/track-uuid.sh +index 3bacd2c..a967d0e 100755 +--- a/test/track-uuid.sh ++++ b/test/track-uuid.sh +@@ -5,7 +5,7 @@ + blockdev="" + rc=77 + +-. ./common ++. $(dirname $0)/common + + set -e + trap 'err $LINENO' ERR +-- +2.27.0 + diff --git a/SOURCES/0081-ndctl-Drop-executable-bit-for-bash-completion-script.patch b/SOURCES/0081-ndctl-Drop-executable-bit-for-bash-completion-script.patch new file mode 100644 index 0000000..d062f20 --- /dev/null +++ b/SOURCES/0081-ndctl-Drop-executable-bit-for-bash-completion-script.patch @@ -0,0 +1,28 @@ +From 7912cb0d19b5d17321439d118d41e57236b5484b Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:47 -0800 +Subject: [PATCH 081/217] ndctl: Drop executable bit for bash-completion script + +The rpm build process warns: + +*** WARNING: ./usr/share/bash-completion/completions/ndctl is executable but has no shebang, removing executable bit + +Clear the unnecessary executable bit since completion helpers are sourced, +not executed. + +Link: https://lore.kernel.org/r/164141836772.3990253.4996882214531720931.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + contrib/ndctl | 0 + 1 file changed, 0 insertions(+), 0 deletions(-) + mode change 100755 => 100644 contrib/ndctl + +diff --git a/contrib/ndctl b/contrib/ndctl +old mode 100755 +new mode 100644 +-- +2.27.0 + diff --git a/SOURCES/0082-build-Add-meson-build-infrastructure.patch b/SOURCES/0082-build-Add-meson-build-infrastructure.patch new file mode 100644 index 0000000..72348d7 --- /dev/null +++ b/SOURCES/0082-build-Add-meson-build-infrastructure.patch @@ -0,0 +1,1577 @@ +From 4e5faa1726d22e8ef51e860d56f4300077a3e9ba Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:53 -0800 +Subject: [PATCH 082/217] build: Add meson build infrastructure + +Build times improve from 10s of seconds to sub-second builds especially +when ccache gets involved and the only change is a git version bump. Recall +that every time the version changes with autotools it does a reconfigure. +With meson only the objects that depend on the version string are rebuilt. +So the primary motivation is to make the ndctl project more enjoyable to +develop. + +Tools, libraries, documentation, and tests all seem to be working. The +remaining work is to redo the rpm build infrastructure, and validate that +installation is working as expected. + +Given the long standing momentum on the old build system it is still kept +functional for now. The only compatibility hack when moving from an +autotools build to a meson build is to delete the config.h files generated +by the old system in favor of the unified configuration header build from +the config.h.meson template. + +Link: https://lore.kernel.org/r/164141837329.3990253.11664056350173402543.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Vaibhav Jain +Tested-by: Alison Schofield +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .gitignore | 5 +- + Documentation/cxl/lib/meson.build | 79 +++++++++ + Documentation/cxl/meson.build | 84 +++++++++ + Documentation/daxctl/meson.build | 94 ++++++++++ + Documentation/ndctl/meson.build | 124 +++++++++++++ + clean_config.sh | 2 + + config.h.meson | 151 ++++++++++++++++ + contrib/meson.build | 28 +++ + cxl/lib/meson.build | 35 ++++ + cxl/meson.build | 25 +++ + daxctl/device.c | 1 + + daxctl/lib/meson.build | 44 +++++ + daxctl/meson.build | 35 ++++ + meson.build | 280 ++++++++++++++++++++++++++++++ + meson_options.txt | 25 +++ + ndctl/lib/meson.build | 48 +++++ + ndctl/meson.build | 82 +++++++++ + test/meson.build | 237 +++++++++++++++++++++++++ + tools/meson-vcs-tag.sh | 18 ++ + util/meson.build | 16 ++ + version.h.in | 2 + + 21 files changed, 1414 insertions(+), 1 deletion(-) + create mode 100644 Documentation/cxl/lib/meson.build + create mode 100644 Documentation/cxl/meson.build + create mode 100644 Documentation/daxctl/meson.build + create mode 100644 Documentation/ndctl/meson.build + create mode 100755 clean_config.sh + create mode 100644 config.h.meson + create mode 100644 contrib/meson.build + create mode 100644 cxl/lib/meson.build + create mode 100644 cxl/meson.build + create mode 100644 daxctl/lib/meson.build + create mode 100644 daxctl/meson.build + create mode 100644 meson.build + create mode 100644 meson_options.txt + create mode 100644 ndctl/lib/meson.build + create mode 100644 ndctl/meson.build + create mode 100644 test/meson.build + create mode 100755 tools/meson-vcs-tag.sh + create mode 100644 util/meson.build + create mode 100644 version.h.in + +diff -up ndctl-71.1/.gitignore.orig ndctl-71.1/.gitignore +--- ndctl-71.1/.gitignore.orig 2022-10-07 17:29:42.924688214 -0400 ++++ ndctl-71.1/.gitignore 2022-10-07 17:29:51.566717638 -0400 +@@ -9,7 +9,9 @@ Makefile.in + /aclocal.m4 + /autom4te.cache + /build-aux +-/config.* ++/config.h ++/config.log ++/config.status + /configure + /libtool + /stamp-h1 +@@ -24,6 +26,7 @@ Documentation/ndctl/asciidoctor-extensio + Documentation/cxl/asciidoctor-extensions.rb + Documentation/cxl/lib/asciidoctor-extensions.rb + .dirstamp ++build/ + daxctl/config.h + daxctl/daxctl + daxctl/lib/libdaxctl.la +diff -up ndctl-71.1/Documentation/cxl/lib/meson.build.orig ndctl-71.1/Documentation/cxl/lib/meson.build +--- ndctl-71.1/Documentation/cxl/lib/meson.build.orig 2022-10-07 17:29:51.567717641 -0400 ++++ ndctl-71.1/Documentation/cxl/lib/meson.build 2022-10-07 17:29:51.567717641 -0400 +@@ -0,0 +1,79 @@ ++if get_option('asciidoctor').enabled() ++ asciidoc_conf = custom_target('asciidoctor-extensions.rb', ++ command : [ ++ 'sed', '-e', 's,@Utility@,Libcxl,g', '-e', 's,@utility@,cxl,g', '@INPUT@' ++ ], ++ input : '../../asciidoctor-extensions.rb.in', ++ output : 'asciidoctor-extensions.rb', ++ capture : true, ++ ) ++else ++ asciidoc_conf = custom_target('asciidoc.conf', ++ command : [ ++ 'sed', '-e', 's,UTILITY,libcxl,g', ++ ], ++ input : '../../asciidoc.conf.in', ++ output : 'asciidoc.conf', ++ capture : true, ++ ) ++endif ++ ++filedeps = [ ++ '../../copyright.txt', ++] ++ ++libcxl_manpages = [ ++ 'libcxl.txt', ++ 'cxl_new.txt', ++] ++ ++foreach man : libcxl_manpages ++ name = man.split('.')[0] ++ output = name + '.3' ++ output_xml = name + '.xml' ++ if get_option('asciidoctor').enabled() ++ custom_target(name, ++ command : [ ++ asciidoc, ++ '-b', 'manpage', '-d', 'manpage', '-acompat-mode', '-I', '@OUTDIR@', ++ '-rasciidoctor-extensions', '-amansource=libcxl', ++ '-amanmanual=libcxl Manual', ++ '-andctl_version=@0@'.format(meson.project_version()), ++ '-o', '@OUTPUT@', '@INPUT@' ++ ], ++ input : man, ++ output : output, ++ depend_files : filedeps, ++ depends : asciidoc_conf, ++ install : get_option('docs').enabled(), ++ install_dir : join_paths(get_option('mandir'), 'man3'), ++ ) ++ else ++ xml = custom_target(output_xml, ++ command : [ ++ asciidoc, ++ '-b', 'docbook', '-d', 'manpage', '-f', asciidoc_conf, '--unsafe', ++ '-andctl_version=@0@'.format(meson.project_version()), ++ '-o', '@OUTPUT@', '@INPUT@', ++ ], ++ input : man, ++ output : output_xml, ++ depend_files : filedeps, ++ depends : asciidoc_conf, ++ ) ++ ++ xsl = files('../../manpage-normal.xsl') ++ ++ custom_target(name, ++ command : [ ++ xmlto, '-o', '@OUTDIR@', '-m', xsl, 'man', '@INPUT@' ++ ], ++ depends : xml, ++ depend_files : xsl, ++ input : xml, ++ output : output, ++ install : get_option('docs').enabled(), ++ install_dir : join_paths(get_option('mandir'), 'man3'), ++ ) ++ endif ++endforeach +diff -up ndctl-71.1/Documentation/cxl/meson.build.orig ndctl-71.1/Documentation/cxl/meson.build +--- ndctl-71.1/Documentation/cxl/meson.build.orig 2022-10-07 17:29:51.568717645 -0400 ++++ ndctl-71.1/Documentation/cxl/meson.build 2022-10-07 17:29:51.568717645 -0400 +@@ -0,0 +1,84 @@ ++if get_option('asciidoctor').enabled() ++ asciidoc_conf = custom_target('asciidoctor-extensions.rb', ++ command : [ ++ 'sed', '-e', 's,@Utility@,Cxl,g', '-e', 's,@utility@,cxl,g', '@INPUT@' ++ ], ++ input : '../asciidoctor-extensions.rb.in', ++ output : 'asciidoctor-extensions.rb', ++ capture : true, ++ ) ++else ++ asciidoc_conf = custom_target('asciidoc.conf', ++ command : [ ++ 'sed', '-e', 's,UTILITY,cxl,g', ++ ], ++ input : '../asciidoc.conf.in', ++ output : 'asciidoc.conf', ++ capture : true, ++ ) ++endif ++ ++filedeps = [ ++ '../copyright.txt', ++] ++ ++cxl_manpages = [ ++ 'cxl.txt', ++ 'cxl-list.txt', ++ 'cxl-read-labels.txt', ++ 'cxl-write-labels.txt', ++ 'cxl-zero-labels.txt', ++] ++ ++foreach man : cxl_manpages ++ name = man.split('.')[0] ++ output = name + '.1' ++ output_xml = name + '.xml' ++ if get_option('asciidoctor').enabled() ++ custom_target(name, ++ command : [ ++ asciidoc, ++ '-b', 'manpage', '-d', 'manpage', '-acompat-mode', '-I', '@OUTDIR@', ++ '-rasciidoctor-extensions', '-amansource=cxl', ++ '-amanmanual=cxl Manual', ++ '-andctl_version=@0@'.format(meson.project_version()), ++ '-o', '@OUTPUT@', '@INPUT@' ++ ], ++ input : man, ++ output : output, ++ depend_files : filedeps, ++ depends : asciidoc_conf, ++ install : get_option('docs').enabled(), ++ install_dir : join_paths(get_option('mandir'), 'man1'), ++ ) ++ else ++ xml = custom_target(output_xml, ++ command : [ ++ asciidoc, ++ '-b', 'docbook', '-d', 'manpage', '-f', asciidoc_conf, '--unsafe', ++ '-andctl_version=@0@'.format(meson.project_version()), ++ '-o', '@OUTPUT@', '@INPUT@', ++ ], ++ input : man, ++ output : output_xml, ++ depend_files : filedeps, ++ depends : asciidoc_conf, ++ ) ++ ++ xsl = files('../manpage-normal.xsl') ++ ++ custom_target(name, ++ command : [ ++ xmlto, '-o', '@OUTDIR@', '-m', xsl, 'man', '@INPUT@' ++ ], ++ depends : xml, ++ depend_files : xsl, ++ input : xml, ++ output : output, ++ install : get_option('docs').enabled(), ++ install_dir : join_paths(get_option('mandir'), 'man1'), ++ ) ++ endif ++endforeach ++ ++subdir('lib') +diff -up ndctl-71.1/Documentation/daxctl/meson.build.orig ndctl-71.1/Documentation/daxctl/meson.build +--- ndctl-71.1/Documentation/daxctl/meson.build.orig 2022-10-07 17:29:51.569717648 -0400 ++++ ndctl-71.1/Documentation/daxctl/meson.build 2022-10-07 17:29:51.568717645 -0400 +@@ -0,0 +1,94 @@ ++if get_option('asciidoctor').enabled() ++ asciidoc_conf = custom_target('asciidoctor-extensions.rb', ++ command : [ ++ 'sed', '-e', 's,@Utility@,Daxctl,g', '-e', 's,@utility@,daxctl,g', '@INPUT@' ++ ], ++ input : '../asciidoctor-extensions.rb.in', ++ output : 'asciidoctor-extensions.rb', ++ capture : true, ++ ) ++else ++ asciidoc_conf = custom_target('asciidoc.conf', ++ command : [ ++ 'sed', '-e', 's,UTILITY,daxctl,g', ++ ], ++ input : '../asciidoc.conf.in', ++ output : 'asciidoc.conf', ++ capture : true, ++ ) ++endif ++ ++filedeps = [ ++ 'human-option.txt', ++ '../copyright.txt', ++] ++ ++daxctl_manpages = [ ++ 'daxctl.txt', ++ 'daxctl-list.txt', ++ 'daxctl-migrate-device-model.txt', ++ 'daxctl-reconfigure-device.txt', ++ 'daxctl-online-memory.txt', ++ 'daxctl-offline-memory.txt', ++ 'daxctl-disable-device.txt', ++ 'daxctl-enable-device.txt', ++ 'daxctl-create-device.txt', ++ 'daxctl-destroy-device.txt', ++] ++ ++foreach man : daxctl_manpages ++ name = man.split('.')[0] ++ output = name + '.1' ++ output_xml = name + '.xml' ++ if get_option('asciidoctor').enabled() ++ custom_target(name, ++ command : [ ++ asciidoc, ++ '-b', 'manpage', '-d', 'manpage', '-acompat-mode', '-I', '@OUTDIR@', ++ '-rasciidoctor-extensions', '-amansource=daxctl', ++ '-amanmanual=daxctl Manual', ++ '-adaxctl_confdir=@0@'.format(daxctlconf_dir), ++ '-adaxctl_conf=@0@'.format(daxctlconf), ++ '-andctl_keysdir=@0@'.format(ndctlkeys_dir), ++ '-andctl_version=@0@'.format(meson.project_version()), ++ '-o', '@OUTPUT@', '@INPUT@' ++ ], ++ input : man, ++ output : output, ++ depend_files : filedeps, ++ depends : asciidoc_conf, ++ install : get_option('docs').enabled(), ++ install_dir : join_paths(get_option('mandir'), 'man1'), ++ ) ++ else ++ xml = custom_target(output_xml, ++ command : [ ++ asciidoc, ++ '-b', 'docbook', '-d', 'manpage', '-f', asciidoc_conf, '--unsafe', ++ '-adaxctl_confdir=@0@'.format(daxctlconf_dir), ++ '-adaxctl_conf=@0@'.format(daxctlconf), ++ '-andctl_keysdir=@0@'.format(ndctlkeys_dir), ++ '-andctl_version=@0@'.format(meson.project_version()), ++ '-o', '@OUTPUT@', '@INPUT@', ++ ], ++ input : man, ++ output : output_xml, ++ depend_files : filedeps, ++ depends : asciidoc_conf, ++ ) ++ ++ xsl = files('../manpage-normal.xsl') ++ ++ custom_target(name, ++ command : [ ++ xmlto, '-o', '@OUTDIR@', '-m', xsl, 'man', '@INPUT@' ++ ], ++ depends : xml, ++ depend_files : xsl, ++ input : xml, ++ output : output, ++ install : get_option('docs').enabled(), ++ install_dir : join_paths(get_option('mandir'), 'man1'), ++ ) ++ endif ++endforeach +diff -up ndctl-71.1/Documentation/ndctl/meson.build.orig ndctl-71.1/Documentation/ndctl/meson.build +--- ndctl-71.1/Documentation/ndctl/meson.build.orig 2022-10-07 17:29:51.570717651 -0400 ++++ ndctl-71.1/Documentation/ndctl/meson.build 2022-10-07 17:29:51.570717651 -0400 +@@ -0,0 +1,124 @@ ++if get_option('asciidoctor').enabled() ++ asciidoc_conf = custom_target('asciidoctor-extensions.rb', ++ command : [ ++ 'sed', '-e', 's,@Utility@,Ndctl,g', '-e', 's,@utility@,ndctl,g', '@INPUT@' ++ ], ++ input : '../asciidoctor-extensions.rb.in', ++ output : 'asciidoctor-extensions.rb', ++ capture : true, ++ ) ++else ++ asciidoc_conf = custom_target('asciidoc.conf', ++ command : [ ++ 'sed', '-e', 's,UTILITY,ndctl,g', ++ ], ++ input : '../asciidoc.conf.in', ++ output : 'asciidoc.conf', ++ capture : true, ++ ) ++endif ++ ++filedeps = [ ++ '../copyright.txt', ++ 'region-description.txt', ++ 'xable-region-options.txt', ++ 'dimm-description.txt', ++ 'xable-dimm-options.txt', ++ 'xable-namespace-options.txt', ++ 'ars-description.txt', ++ 'labels-description.txt', ++ 'labels-options.txt', ++] ++ ++ndctl_manpages = [ ++ 'ndctl.txt', ++ 'ndctl-wait-scrub.txt', ++ 'ndctl-start-scrub.txt', ++ 'ndctl-zero-labels.txt', ++ 'ndctl-read-labels.txt', ++ 'ndctl-write-labels.txt', ++ 'ndctl-init-labels.txt', ++ 'ndctl-check-labels.txt', ++ 'ndctl-enable-region.txt', ++ 'ndctl-disable-region.txt', ++ 'ndctl-enable-dimm.txt', ++ 'ndctl-disable-dimm.txt', ++ 'ndctl-enable-namespace.txt', ++ 'ndctl-disable-namespace.txt', ++ 'ndctl-create-namespace.txt', ++ 'ndctl-destroy-namespace.txt', ++ 'ndctl-check-namespace.txt', ++ 'ndctl-clear-errors.txt', ++ 'ndctl-inject-error.txt', ++ 'ndctl-inject-smart.txt', ++ 'ndctl-update-firmware.txt', ++ 'ndctl-list.txt', ++ 'ndctl-monitor.txt', ++ 'ndctl-setup-passphrase.txt', ++ 'ndctl-update-passphrase.txt', ++ 'ndctl-remove-passphrase.txt', ++ 'ndctl-freeze-security.txt', ++ 'ndctl-sanitize-dimm.txt', ++ 'ndctl-load-keys.txt', ++ 'ndctl-wait-overwrite.txt', ++ 'ndctl-read-infoblock.txt', ++ 'ndctl-write-infoblock.txt', ++ 'ndctl-activate-firmware.txt', ++] ++ ++foreach man : ndctl_manpages ++ name = man.split('.')[0] ++ output = name + '.1' ++ output_xml = name + '.xml' ++ if get_option('asciidoctor').enabled() ++ custom_target(name, ++ command : [ ++ asciidoc, ++ '-b', 'manpage', '-d', 'manpage', '-acompat-mode', '-I', '@OUTDIR@', ++ '-rasciidoctor-extensions', '-amansource=ndctl', ++ '-amanmanual=ndctl Manual', ++ '-andctl_confdir=@0@'.format(ndctlconf_dir), ++ '-andctl_monitorconf=@0@'.format(ndctlconf), ++ '-andctl_keysdir=@0@'.format(ndctlkeys_dir), ++ '-andctl_version=@0@'.format(meson.project_version()), ++ '-o', '@OUTPUT@', '@INPUT@' ++ ], ++ input : man, ++ output : output, ++ depend_files : filedeps, ++ depends : asciidoc_conf, ++ install : get_option('docs').enabled(), ++ install_dir : join_paths(get_option('mandir'), 'man1'), ++ ) ++ else ++ xml = custom_target(output_xml, ++ command : [ ++ asciidoc, ++ '-b', 'docbook', '-d', 'manpage', '-f', asciidoc_conf, '--unsafe', ++ '-andctl_version=@0@'.format(meson.project_version()), ++ '-andctl_confdir=@0@'.format(ndctlconf_dir), ++ '-andctl_monitorconf=@0@'.format(ndctlconf), ++ '-andctl_keysdir=@0@'.format(ndctlkeys_dir), ++ '-o', '@OUTPUT@', '@INPUT@', ++ ], ++ input : man, ++ output : output_xml, ++ depend_files : filedeps, ++ depends : asciidoc_conf, ++ ) ++ ++ xsl = files('../manpage-normal.xsl') ++ ++ custom_target(name, ++ command : [ ++ xmlto, '-o', '@OUTDIR@', '-m', xsl, 'man', '@INPUT@' ++ ], ++ depends : xml, ++ depend_files : xsl, ++ input : xml, ++ output : output, ++ install : get_option('docs').enabled(), ++ install_dir : join_paths(get_option('mandir'), 'man1'), ++ ) ++ endif ++endforeach +diff -up ndctl-71.1/clean_config.sh.orig ndctl-71.1/clean_config.sh +--- ndctl-71.1/clean_config.sh.orig 2022-10-07 17:29:51.570717651 -0400 ++++ ndctl-71.1/clean_config.sh 2022-10-07 17:29:51.570717651 -0400 +@@ -0,0 +1,2 @@ ++#!/bin/bash ++git ls-files -o --exclude build | grep config.h\$ | xargs rm +diff -up ndctl-71.1/config.h.meson.orig ndctl-71.1/config.h.meson +--- ndctl-71.1/config.h.meson.orig 2022-10-07 17:29:51.570717651 -0400 ++++ ndctl-71.1/config.h.meson 2022-10-07 17:29:51.570717651 -0400 +@@ -0,0 +1,151 @@ ++/* Debug messages. */ ++#mesondefine ENABLE_DEBUG ++ ++/* destructive functional tests support */ ++#mesondefine ENABLE_DESTRUCTIVE ++ ++/* Documentation / man pages. */ ++#mesondefine ENABLE_DOCS ++ ++/* Enable keyutils support */ ++#mesondefine ENABLE_KEYUTILS ++ ++/* System logging. */ ++#mesondefine ENABLE_LOGGING ++ ++/* ndctl test poison support */ ++#mesondefine ENABLE_POISON ++ ++/* ndctl test support */ ++#mesondefine ENABLE_TEST ++ ++/* Define to 1 if big-endian-arch */ ++#mesondefine HAVE_BIG_ENDIAN ++ ++/* Define to 1 if you have the declaration of `BUS_MCEERR_AR', and to 0 if you ++ don't. */ ++#mesondefine HAVE_DECL_BUS_MCEERR_AR ++ ++/* Define to 1 if you have the declaration of `MAP_SHARED_VALIDATE', and to 0 ++ if you don't. */ ++#mesondefine HAVE_DECL_MAP_SHARED_VALIDATE ++ ++/* Define to 1 if you have the declaration of `MAP_SYNC', and to 0 if you ++ don't. */ ++#mesondefine HAVE_DECL_MAP_SYNC ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_DLFCN_H ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_INTTYPES_H ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_KEYUTILS_H ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_LINUX_VERSION_H ++ ++/* Define to 1 if little-endian-arch */ ++#mesondefine HAVE_LITTLE_ENDIAN ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_MEMORY_H ++ ++/* Define to 1 if you have the `secure_getenv' function. */ ++#mesondefine HAVE_SECURE_GETENV ++ ++/* Define to 1 if you have statement expressions. */ ++#mesondefine HAVE_STATEMENT_EXPR ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_STDINT_H ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_STDLIB_H ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_STRINGS_H ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_STRING_H ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_SYS_STAT_H ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_SYS_TYPES_H ++ ++/* Define to 1 if typeof works with your compiler. */ ++#mesondefine HAVE_TYPEOF ++ ++/* Define to 1 if you have the header file. */ ++#mesondefine HAVE_UNISTD_H ++ ++/* Define to 1 if using libuuid */ ++#mesondefine HAVE_UUID ++ ++/* Define to 1 if you have the `__secure_getenv' function. */ ++#mesondefine HAVE___SECURE_GETENV ++ ++/* Define to the sub-directory where libtool stores uninstalled libraries. */ ++#mesondefine LT_OBJDIR ++ ++/* Name of package */ ++#mesondefine PACKAGE ++ ++/* Define to the address where bug reports for this package should be sent. */ ++#mesondefine PACKAGE_BUGREPORT ++ ++/* Define to the full name of this package. */ ++#mesondefine PACKAGE_NAME ++ ++/* Define to the full name and version of this package. */ ++#mesondefine PACKAGE_STRING ++ ++/* Define to the one symbol short name of this package. */ ++#mesondefine PACKAGE_TARNAME ++ ++/* Define to the home page for this package. */ ++#mesondefine PACKAGE_URL ++ ++/* Define to the version of this package. */ ++#mesondefine PACKAGE_VERSION ++ ++/* Define to 1 if you have the ANSI C header files. */ ++#mesondefine STDC_HEADERS ++ ++/* Version number of package */ ++#mesondefine VERSION ++ ++/* Number of bits in a file offset, on hosts where this is settable. */ ++#mesondefine _FILE_OFFSET_BITS ++ ++/* Define for large files, on AIX-style hosts. */ ++#mesondefine _LARGE_FILES ++ ++/* Define to 1 if on MINIX. */ ++#mesondefine _MINIX ++ ++/* Define to 2 if the system does not provide POSIX.1 features except with ++ this defined. */ ++#mesondefine _POSIX_1_SOURCE ++ ++/* Define to 1 if you need to in order for `stat' and other things to work. */ ++#mesondefine _POSIX_SOURCE ++ ++/* Define to __typeof__ if your compiler spells it that way. */ ++#mesondefine typeof ++ ++/* Define to enable GNU Source Extensions */ ++#mesondefine _GNU_SOURCE ++ ++/* Locations to install configuration files, key config, man pages, etc.. */ ++#mesondefine NDCTL_CONF_FILE ++#mesondefine NDCTL_CONF_DIR ++#mesondefine DAXCTL_CONF_DIR ++#mesondefine NDCTL_KEYS_DIR ++#mesondefine NDCTL_MAN_PATH ++#mesondefine DAXCTL_MODPROBE_DATA ++#mesondefine DAXCTL_MODPROBE_INSTALL ++#mesondefine PREFIX +diff -up ndctl-71.1/contrib/meson.build.orig ndctl-71.1/contrib/meson.build +--- ndctl-71.1/contrib/meson.build.orig 2022-10-07 17:29:51.571717655 -0400 ++++ ndctl-71.1/contrib/meson.build 2022-10-07 17:29:51.570717651 -0400 +@@ -0,0 +1,28 @@ ++bashcompletiondir = get_option('bashcompletiondir') ++if bashcompletiondir == '' ++ bash_completion = dependency('bash-completion', required : false) ++ if bash_completion.found() ++ bashcompletiondir = bash_completion.get_pkgconfig_variable('completionsdir') ++ else ++ bashcompletiondir = datadir / 'bash-completion/completions' ++ endif ++endif ++ ++if bashcompletiondir != 'no' ++ install_data('ndctl', install_dir : bashcompletiondir) ++ ++# TODO Switch to symlinks once 0.61.0 is more widely available ++# install_symlink('daxctl', ++# install_dir : bashcompletiondir, ++# pointing_to : 'ndctl' ++# ) ++# install_symlink('cxl', ++# install_dir : bashcompletiondir, ++# pointing_to : 'ndctl' ++# ) ++ install_data('ndctl', rename : 'daxctl', install_dir : bashcompletiondir) ++ install_data('ndctl', rename : 'cxl', install_dir : bashcompletiondir) ++endif ++ ++modprobedatadir = get_option('sysconfdir') + '/modprobe.d/' ++install_data('nvdimm-security.conf', install_dir : modprobedatadir) +diff -up ndctl-71.1/cxl/lib/meson.build.orig ndctl-71.1/cxl/lib/meson.build +--- ndctl-71.1/cxl/lib/meson.build.orig 2022-10-07 17:29:51.571717655 -0400 ++++ ndctl-71.1/cxl/lib/meson.build 2022-10-07 17:29:51.571717655 -0400 +@@ -0,0 +1,35 @@ ++libcxl_version = '@0@.@1@.@2@'.format( ++ LIBCXL_CURRENT - LIBCXL_AGE, ++ LIBCXL_REVISION, ++ LIBCXL_AGE) ++ ++mapfile = files('libcxl.sym') ++vflag = '-Wl,--version-script,@0@/@1@'.format(project_source_root, mapfile[0]) ++ ++cxl = library('cxl', ++ '../../util/sysfs.c', ++ '../../util/log.c', ++ '../../util/log.h', ++ 'libcxl.c', ++ include_directories : root_inc, ++ dependencies : [ ++ uuid, ++ kmod, ++ ], ++ version : libcxl_version, ++ install : true, ++ install_dir : rootlibdir, ++ link_args : vflag, ++ link_depends : mapfile, ++) ++cxl_dep = declare_dependency(link_with : cxl) ++ ++custom_target( ++ 'libcxl.pc', ++ command : pkgconfig_script + [ '@INPUT@' ], ++ input : 'libcxl.pc.in', ++ output : 'libcxl.pc', ++ capture : true, ++ install : true, ++ install_dir : pkgconfiglibdir, ++) +diff -up ndctl-71.1/cxl/meson.build.orig ndctl-71.1/cxl/meson.build +--- ndctl-71.1/cxl/meson.build.orig 2022-10-07 17:29:51.571717655 -0400 ++++ ndctl-71.1/cxl/meson.build 2022-10-07 17:29:51.571717655 -0400 +@@ -0,0 +1,25 @@ ++cxl_src = [ ++ 'cxl.c', ++ 'list.c', ++ 'memdev.c', ++ '../util/json.c', ++ 'json.c', ++ 'filter.c', ++] ++ ++cxl_tool = executable('cxl', ++ cxl_src, ++ include_directories : root_inc, ++ dependencies : [ ++ cxl_dep, ++ util_dep, ++ uuid, ++ kmod, ++ json, ++ versiondep, ++ ], ++ install : true, ++ install_dir : rootbindir, ++) ++ ++install_headers('libcxl.h', subdir : 'cxl') +diff -up ndctl-71.1/daxctl/device.c.orig ndctl-71.1/daxctl/device.c +--- ndctl-71.1/daxctl/device.c.orig 2022-10-07 17:29:42.909688163 -0400 ++++ ndctl-71.1/daxctl/device.c 2022-10-07 17:29:51.572717658 -0400 +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff -up ndctl-71.1/daxctl/lib/meson.build.orig ndctl-71.1/daxctl/lib/meson.build +--- ndctl-71.1/daxctl/lib/meson.build.orig 2022-10-07 17:29:51.573717662 -0400 ++++ ndctl-71.1/daxctl/lib/meson.build 2022-10-07 17:29:51.573717662 -0400 +@@ -0,0 +1,44 @@ ++libdaxctl_version = '@0@.@1@.@2@'.format( ++ LIBDAXCTL_CURRENT - LIBDAXCTL_AGE, ++ LIBDAXCTL_REVISION, ++ LIBDAXCTL_AGE, ++) ++ ++mapfile = files('libdaxctl.sym') ++vflag = '-Wl,--version-script,@0@/@1@'.format(project_source_root, mapfile[0]) ++ ++libdaxctl_src = [ ++ '../../util/iomem.c', ++ '../../util/sysfs.c', ++ '../../util/log.c', ++ 'libdaxctl.c', ++] ++ ++daxctl = library( ++ 'daxctl', ++ libdaxctl_src, ++ version : libdaxctl_version, ++ include_directories : root_inc, ++ dependencies : [ ++ uuid, ++ kmod, ++ ], ++ install : true, ++ install_dir : rootlibdir, ++ link_args : vflag, ++ link_depends : mapfile, ++) ++ ++daxctl_dep = declare_dependency(link_with : daxctl) ++ ++custom_target( ++ 'libdaxctl.pc', ++ command : pkgconfig_script + [ '@INPUT@' ], ++ input : 'libdaxctl.pc.in', ++ output : 'libdaxctl.pc', ++ capture : true, ++ install : true, ++ install_dir : pkgconfiglibdir, ++) ++ ++install_data('daxctl.conf', install_dir : datadir / 'daxctl') +diff -up ndctl-71.1/daxctl/meson.build.orig ndctl-71.1/daxctl/meson.build +--- ndctl-71.1/daxctl/meson.build.orig 2022-10-07 17:29:51.574717665 -0400 ++++ ndctl-71.1/daxctl/meson.build 2022-10-07 17:29:51.574717665 -0400 +@@ -0,0 +1,28 @@ ++daxctl_src = [ ++ 'daxctl.c', ++ 'acpi.c', ++ 'list.c', ++ 'migrate.c', ++ 'device.c', ++ '../util/json.c', ++ 'json.c', ++ 'filter.c', ++] ++ ++daxctl_tool = executable('daxctl', ++ daxctl_src, ++ include_directories : root_inc, ++ dependencies : [ ++ daxctl_dep, ++ ndctl_dep, ++ util_dep, ++ uuid, ++ kmod, ++ json, ++ versiondep, ++ ], ++ install : true, ++ install_dir : rootbindir, ++) ++ ++install_headers('libdaxctl.h', subdir : 'daxctl') +diff -up ndctl-71.1/meson.build.orig ndctl-71.1/meson.build +--- ndctl-71.1/meson.build.orig 2022-10-07 17:29:51.575717669 -0400 ++++ ndctl-71.1/meson.build 2022-10-07 17:33:21.562432608 -0400 +@@ -0,0 +1,279 @@ ++project('ndctl', 'c', ++ version : '72', ++ license : [ ++ 'GPL-2.0', ++ 'LGPL-2.1', ++ 'CC0-1.0', ++ 'MIT', ++ ], ++ default_options : [ ++ 'c_std=gnu99', ++ 'prefix=/usr', ++ 'sysconfdir=/etc', ++ 'localstatedir=/var', ++ ], ++) ++ ++# rootprefixdir and rootlibdir setup copied from systemd: ++rootprefixdir = get_option('rootprefix') ++rootprefix_default = '/usr' ++if rootprefixdir == '' ++ rootprefixdir = rootprefix_default ++endif ++rootbindir = join_paths(rootprefixdir, 'bin') ++ ++# join_paths ignores the preceding arguments if an absolute component is ++# encountered, so this should canonicalize various paths when they are ++# absolute or relative. ++prefixdir = get_option('prefix') ++if not prefixdir.startswith('/') ++ error('Prefix is not absolute: "@0@"'.format(prefixdir)) ++endif ++if prefixdir != rootprefixdir and rootprefixdir != '/' and not prefixdir.strip('/').startswith(rootprefixdir.strip('/') + '/') ++ error('Prefix is not below root prefix (now rootprefix=@0@ prefix=@1@)'.format( ++ rootprefixdir, prefixdir)) ++endif ++ ++libdir = join_paths(prefixdir, get_option('libdir')) ++rootlibdir = get_option('rootlibdir') ++if rootlibdir == '' ++ rootlibdir = join_paths(rootprefixdir, libdir.split('/')[-1]) ++endif ++datadir = prefixdir / get_option('datadir') ++includedir = prefixdir / get_option('includedir') ++ ++pkgconfiglibdir = get_option('pkgconfiglibdir') != '' ? get_option('pkgconfiglibdir') : libdir / 'pkgconfig' ++ ++datadir = prefixdir / get_option('datadir') ++includedir = prefixdir / get_option('includedir') ++sysconfdir = get_option('sysconfdir') ++ ++pkgconfig_script = ''' ++sed -e s,@VERSION@,@0@,g ++ -e s,@prefix@,@1@,g ++ -e s,@exec_prefix@,@1@,g ++ -e s,@libdir@,@2@,g ++ -e s,@includedir@,@3@,g ++'''.format(meson.project_version(), prefixdir, libdir, includedir).split() ++ ++cc_flags = [ ++ '-Wall', ++ '-Wchar-subscripts', ++ '-Wformat-security', ++ '-Wmissing-declarations', ++ '-Wmissing-prototypes', ++ '-Wnested-externs ', ++ '-Wshadow', ++ '-Wsign-compare', ++ '-Wstrict-prototypes', ++ '-Wtype-limits', ++ '-Wmaybe-uninitialized', ++ '-Wdeclaration-after-statement', ++ '-Wunused-result', ++ '-D_FORTIFY_SOURCE=2', ++ '-O2', ++] ++cc = meson.get_compiler('c') ++add_project_arguments(cc.get_supported_arguments(cc_flags), language : 'c') ++ ++project_source_root = meson.current_source_dir() ++ ++# Remove this after the conversion to meson has been completed ++# Cleanup the leftover config.h files to avoid conflicts with the meson ++# generated config.h ++git = find_program('git', required : false) ++if git.found() ++ run_command('clean_config.sh', ++ env : 'GIT_DIR=@0@/.git'.format(project_source_root), ++ ) ++endif ++ ++version_tag = get_option('version-tag') ++if version_tag != '' ++ vcs_data = configuration_data() ++ vcs_data.set('VCS_TAG', version_tag) ++ version_h = configure_file( ++ configuration : vcs_data, ++ input : 'version.h.in', ++ output : 'version.h' ++ ) ++else ++ vcs_tagger = [ ++ project_source_root + '/tools/meson-vcs-tag.sh', ++ project_source_root, ++ meson.project_version() ++ ] ++ ++ version_h = vcs_tag( ++ input : 'version.h.in', ++ output : 'version.h', ++ command: vcs_tagger ++ ) ++endif ++ ++versiondep = declare_dependency( ++ compile_args: ['-include', 'version.h'], ++ sources: version_h ++) ++ ++kmod = dependency('libkmod') ++libudev = dependency('libudev') ++uuid = dependency('uuid') ++json = dependency('json-c') ++if get_option('docs').enabled() ++ if get_option('asciidoctor').enabled() ++ asciidoc = find_program('asciidoctor', required : true) ++ else ++ asciidoc = find_program('asciidoc', required : true) ++ xmlto = find_program('xmlto', required : true) ++ endif ++endif ++ ++if get_option('systemd').enabled() ++ systemd = dependency('systemd', required : true) ++ systemdunitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir') ++ udev = dependency('udev', required : true) ++ udevdir = udev.get_pkgconfig_variable('udevdir') ++ udevrulesdir = udevdir / 'rules.d' ++endif ++ ++cc = meson.get_compiler('c') ++ ++# keyutils and iniparser lack pkgconfig ++keyutils = cc.find_library('keyutils', required : get_option('keyutils')) ++ ++conf = configuration_data() ++check_headers = [ ++ ['HAVE_DLFCN_H', 'dlfcn.h'], ++ ['HAVE_INTTYPES_H', 'inttypes.h'], ++ ['HAVE_KEYUTILS_H', 'keyutils.h'], ++ ['HAVE_LINUX_VERSION_H', 'linux/version.h'], ++ ['HAVE_MEMORY_H', 'memory.h'], ++ ['HAVE_STDINT_H', 'stdint.h'], ++ ['HAVE_STDLIB_H', 'stdlib.h'], ++ ['HAVE_STRINGS_H', 'strings.h'], ++ ['HAVE_STRING_H', 'string.h'], ++ ['HAVE_SYS_STAT_H', 'sys/stat.h'], ++ ['HAVE_SYS_TYPES_H', 'sys/types.h'], ++ ['HAVE_UNISTD_H', 'unistd.h'], ++] ++ ++foreach h : check_headers ++ if cc.has_header(h.get(1)) ++ conf.set(h.get(0), 1) ++ endif ++endforeach ++ ++map_sync_symbols = [ ++ [ 'signal.h', 'BUS_MCEERR_AR' ], ++ [ 'linux/mman.h', 'MAP_SHARED_VALIDATE' ], ++ [ 'linux/mman.h', 'MAP_SYNC' ], ++] ++ ++count = 0 ++foreach symbol : map_sync_symbols ++ if cc.has_header_symbol(symbol[0], symbol[1]) ++ conf.set('HAVE_DECL_@0@'.format(symbol[1].to_upper()), 1) ++ count = count + 1 ++ endif ++endforeach ++ ++poison_enabled = false ++if get_option('poison').enabled() and count == 3 ++ poison_enabled = true ++endif ++ ++conf.set('ENABLE_POISON', poison_enabled) ++conf.set('ENABLE_KEYUTILS', get_option('keyutils').enabled()) ++conf.set('ENABLE_TEST', get_option('test').enabled()) ++conf.set('ENABLE_DESTRUCTIVE', get_option('destructive').enabled()) ++conf.set('ENABLE_LOGGING', get_option('logging').enabled()) ++conf.set('ENABLE_DEBUG', get_option('dbg').enabled()) ++ ++typeof = cc.run(''' ++ int main() { ++ struct { ++ char a[16]; ++ } x; ++ typeof(x) y; ++ ++ return sizeof(x) == sizeof(y); ++ } ++ ''' ++) ++ ++if typeof.compiled() and typeof.returncode() == 1 ++ conf.set('HAVE_TYPEOF', 1) ++ conf.set('HAVE_STATEMENT_EXPR', 1) ++endif ++ ++if target_machine.endian() == 'big' ++ conf.set('HAVE_BIG_ENDIAN', 1) ++else ++ conf.set('HAVE_LITTLE_ENDIAN', 1) ++endif ++ ++conf.set('_GNU_SOURCE', true) ++conf.set_quoted('PREFIX', get_option('prefix')) ++conf.set_quoted('NDCTL_MAN_PATH', get_option('mandir')) ++ ++foreach ident : ['secure_getenv', '__secure_getenv'] ++ conf.set10('HAVE_' + ident.to_upper(), cc.has_function(ident)) ++endforeach ++ ++ ++ndctlconf_dir = sysconfdir / 'ndctl' ++ndctlconf = ndctlconf_dir / 'monitor.conf' ++conf.set_quoted('NDCTL_CONF_FILE', ndctlconf) ++conf.set_quoted('NDCTL_CONF_DIR', ndctlconf_dir) ++ ++ndctlkeys_dir = sysconfdir / 'ndctl' / 'keys' ++conf.set_quoted('NDCTL_KEYS_DIR', ndctlkeys_dir) ++ ++daxctlconf_dir = sysconfdir / 'daxctl.conf.d' ++daxctlconf = daxctlconf_dir / 'dax.conf' ++conf.set_quoted('DAXCTL_CONF_DIR', daxctlconf_dir) ++ ++conf.set_quoted('DAXCTL_MODPROBE_DATA', datadir / 'daxctl/daxctl.conf') ++conf.set_quoted('DAXCTL_MODPROBE_INSTALL', sysconfdir / 'modprobe.d/daxctl.conf') ++ ++config_h = configure_file( ++ input : 'config.h.meson', ++ output : 'config.h', ++ configuration : conf ++) ++add_project_arguments('-include', 'config.h', language : 'c') ++ ++LIBNDCTL_CURRENT=25 ++LIBNDCTL_REVISION=1 ++LIBNDCTL_AGE=19 ++ ++LIBDAXCTL_CURRENT=6 ++LIBDAXCTL_REVISION=0 ++LIBDAXCTL_AGE=5 ++ ++LIBCXL_CURRENT=1 ++LIBCXL_REVISION=0 ++LIBCXL_AGE=0 ++ ++root_inc = include_directories(['.', 'ndctl', ]) ++ ++ccan = static_library('ccan', ++ [ 'ccan/str/str.c', 'ccan/list/list.c' ], ++) ++ccan_dep = declare_dependency(link_with : ccan) ++ ++subdir('daxctl/lib') ++subdir('ndctl/lib') ++subdir('cxl/lib') ++subdir('util') ++subdir('ndctl') ++subdir('daxctl') ++subdir('cxl') ++if get_option('docs').enabled() ++ subdir('Documentation/ndctl') ++ subdir('Documentation/daxctl') ++ subdir('Documentation/cxl') ++endif ++subdir('test') ++subdir('contrib') +diff -up ndctl-71.1/meson_options.txt.orig ndctl-71.1/meson_options.txt +--- ndctl-71.1/meson_options.txt.orig 2022-10-07 17:29:51.575717669 -0400 ++++ ndctl-71.1/meson_options.txt 2022-10-07 17:29:51.575717669 -0400 +@@ -0,0 +1,25 @@ ++option('version-tag', type : 'string', ++ description : 'override the git version string') ++option('docs', type : 'feature', value : 'enabled') ++option('asciidoctor', type : 'feature', value : 'disabled') ++option('systemd', type : 'feature', value : 'enabled') ++option('keyutils', type : 'feature', value : 'enabled', ++ description : 'enable nvdimm device passphrase management') ++option('test', type : 'feature', value : 'disabled', ++ description : 'enable shipping tests in ndctl') ++option('destructive', type : 'feature', value : 'disabled', ++ description : 'enable tests that may clobber live system resources') ++option('poison', type : 'feature', value : 'enabled', ++ description : 'enable tests that inject poison / memory-failure') ++option('logging', type : 'feature', value : 'enabled', ++ description : 'enable log infrastructure') ++option('dbg', type : 'feature', value : 'enabled', ++ description : 'enable dbg messages') ++option('rootprefix', type : 'string', ++ description : '''override the root prefix [default '/' if split-usr and '/usr' otherwise]''') ++option('rootlibdir', type : 'string', ++ description : '''[/usr]/lib/x86_64-linux-gnu or such''') ++option('pkgconfiglibdir', type : 'string', value : '', ++ description : 'directory for standard pkg-config files') ++option('bashcompletiondir', type : 'string', ++ description : '''${datadir}/bash-completion/completions''') +diff -up ndctl-71.1/ndctl/lib/meson.build.orig ndctl-71.1/ndctl/lib/meson.build +--- ndctl-71.1/ndctl/lib/meson.build.orig 2022-10-07 17:29:51.576717672 -0400 ++++ ndctl-71.1/ndctl/lib/meson.build 2022-10-07 17:29:51.576717672 -0400 +@@ -0,0 +1,48 @@ ++libndctl_version = '@0@.@1@.@2@'.format( ++ LIBNDCTL_CURRENT - LIBNDCTL_AGE, ++ LIBNDCTL_REVISION, ++ LIBNDCTL_AGE) ++ ++mapfile = files('libndctl.sym') ++vflag = '-Wl,--version-script,@0@/@1@'.format(project_source_root, mapfile[0]) ++ ++ndctl = library( ++ 'ndctl', ++ '../../util/log.c', ++ '../../util/sysfs.c', ++ 'dimm.c', ++ 'inject.c', ++ 'nfit.c', ++ 'smart.c', ++ 'intel.c', ++ 'hpe1.c', ++ 'msft.c', ++ 'hyperv.c', ++ 'papr.c', ++ 'ars.c', ++ 'firmware.c', ++ 'libndctl.c', ++ dependencies : [ ++ daxctl_dep, ++ libudev, ++ uuid, ++ kmod, ++ ], ++ include_directories : root_inc, ++ version : libndctl_version, ++ install : true, ++ install_dir : rootlibdir, ++ link_args : vflag, ++ link_depends : mapfile, ++) ++ndctl_dep = declare_dependency(link_with : ndctl) ++ ++custom_target( ++ 'libndctl.pc', ++ command : pkgconfig_script + [ '@INPUT@' ], ++ input : 'libndctl.pc.in', ++ output : 'libndctl.pc', ++ capture : true, ++ install : true, ++ install_dir : pkgconfiglibdir, ++) +diff -up ndctl-71.1/ndctl/meson.build.orig ndctl-71.1/ndctl/meson.build +--- ndctl-71.1/ndctl/meson.build.orig 2022-10-07 17:29:51.576717672 -0400 ++++ ndctl-71.1/ndctl/meson.build 2022-10-07 17:29:51.576717672 -0400 +@@ -0,0 +1,80 @@ ++ndctl_src = [ ++ 'ndctl.c', ++ 'bus.c', ++ 'create-nfit.c', ++ 'namespace.c', ++ 'check.c', ++ 'region.c', ++ 'dimm.c', ++ '../util/log.c', ++ '../daxctl/filter.c', ++ 'filter.c', ++ 'list.c', ++ '../util/json.c', ++ '../daxctl/json.c', ++ 'json.c', ++ 'json-smart.c', ++ 'inject-error.c', ++ 'inject-smart.c', ++ 'monitor.c', ++] ++ ++deps = [ ++ util_dep, ++ ndctl_dep, ++ daxctl_dep, ++ cxl_dep, ++ uuid, ++ kmod, ++ json, ++ versiondep, ++] ++ ++if get_option('keyutils').enabled() ++ ndctl_src += [ ++ 'keys.c', ++ 'load-keys.c', ++ ] ++ deps += keyutils ++endif ++ ++if get_option('test').enabled() ++ ndctl_src += [ ++ '../test/libndctl.c', ++ '../test/dsm-fail.c', ++ '../util/sysfs.c', ++ '../test/core.c', ++ 'test.c', ++] ++endif ++ ++if get_option('destructive').enabled() ++ if get_option('test').disabled() ++ error('\'-D=destructive=enabled\' requires \'-Dtest=enabled\'\n') ++ endif ++ ndctl_src += [ ++ '../test/pmem_namespaces.c', ++ 'bat.c', ++ ] ++endif ++ ++if get_option('systemd').enabled() ++ install_data('ndctl-monitor.service', install_dir : systemdunitdir) ++endif ++install_data('monitor.conf', install_dir : ndctlconf_dir) ++install_data('keys.readme', install_dir : ndctlkeys_dir) ++ ++ndctl_tool = executable('ndctl', ndctl_src, ++ dependencies : deps, ++ install : true, ++ install_dir : rootbindir, ++ include_directories : root_inc, ++) ++ ++install_headers( ++ [ ++ 'libndctl.h', ++ 'ndctl.h' ++ ], ++ subdir : 'ndctl' ++) +diff -up ndctl-71.1/test/meson.build.orig ndctl-71.1/test/meson.build +--- ndctl-71.1/test/meson.build.orig 2022-10-07 17:29:51.577717675 -0400 ++++ ndctl-71.1/test/meson.build 2022-10-07 17:29:51.577717675 -0400 +@@ -0,0 +1,237 @@ ++testcore = [ ++ 'core.c', ++ '../util/log.c', ++ '../util/sysfs.c', ++] ++ ++libndctl_deps = [ ++ ndctl_dep, ++ daxctl_dep, ++ uuid, ++ kmod, ++] ++ ++ndctl_deps = libndctl_deps + [ ++ json, ++ util_dep, ++ versiondep, ++] ++ ++libndctl = executable('libndctl', testcore + [ 'libndctl.c'], ++ dependencies : libndctl_deps, ++ include_directories : root_inc, ++) ++ ++namespace_core = [ ++ '../ndctl/namespace.c', ++ '../ndctl/filter.c', ++ '../ndctl/check.c', ++ '../util/json.c', ++ '../ndctl/json.c', ++ '../daxctl/filter.c', ++ '../daxctl/json.c', ++] ++ ++dsm_fail = executable('dsm-fail', testcore + namespace_core + [ 'dsm-fail.c' ], ++ dependencies : ndctl_deps, ++ include_directories : root_inc, ++) ++ ++hugetlb_src = testcore + [ 'hugetlb.c', 'dax-pmd.c' ] ++if poison_enabled ++ hugetlb_src += [ 'dax-poison.c' ] ++endif ++hugetlb = executable('hugetlb', hugetlb_src, ++ dependencies : libndctl_deps, ++ include_directories : root_inc, ++) ++ ++ack_shutdown_count = executable('ack-shutdown-count-set', ++ testcore + [ 'ack-shutdown-count-set.c' ], ++ dependencies : libndctl_deps, ++ include_directories : root_inc, ++) ++ ++dax_errors = executable('dax-errors', ++ 'dax-errors.c', ++) ++ ++smart_notify = executable('smart-notify', 'smart-notify.c', ++ dependencies : libndctl_deps, ++ include_directories : root_inc, ++) ++ ++smart_listen = executable('smart-listen', 'smart-listen.c', ++ dependencies : libndctl_deps, ++ include_directories : root_inc, ++) ++ ++daxdev_errors = executable('daxdev-errors', [ ++ 'daxdev-errors.c', ++ '../util/log.c', ++ '../util/sysfs.c', ++ ], ++ dependencies : libndctl_deps, ++ include_directories : root_inc, ++) ++ ++list_smart_dimm = executable('list-smart-dimm', [ ++ 'list-smart-dimm.c', ++ '../ndctl/filter.c', ++ '../util/json.c', ++ '../ndctl/json.c', ++ '../daxctl/json.c', ++ '../daxctl/filter.c', ++ ], ++ dependencies : ndctl_deps, ++ include_directories : root_inc, ++) ++ ++pmem_ns = executable('pmem-ns', testcore + [ 'pmem_namespaces.c' ], ++ dependencies : libndctl_deps, ++ include_directories : root_inc, ++) ++ ++dax_dev = executable('dax-dev', testcore + [ 'dax-dev.c' ], ++ dependencies : libndctl_deps, ++ include_directories : root_inc, ++) ++ ++dax_pmd_src = testcore + [ 'dax-pmd.c' ] ++if poison_enabled ++ dax_pmd_src += [ 'dax-poison.c' ] ++endif ++ ++dax_pmd = executable('dax-pmd', dax_pmd_src, ++ dependencies : libndctl_deps, ++ include_directories : root_inc, ++) ++ ++device_dax_src = testcore + namespace_core + [ ++ 'device-dax.c', ++ 'dax-dev.c', ++ 'dax-pmd.c', ++] ++ ++if poison_enabled ++ device_dax_src += 'dax-poison.c' ++endif ++ ++device_dax = executable('device-dax', device_dax_src, ++ dependencies : ndctl_deps, ++ include_directories : root_inc, ++) ++ ++revoke_devmem = executable('revoke_devmem', testcore + [ ++ 'revoke-devmem.c', ++ 'dax-dev.c', ++ ], ++ dependencies : libndctl_deps, ++ include_directories : root_inc, ++) ++ ++mmap = executable('mmap', 'mmap.c',) ++ ++create = find_program('create.sh') ++clear = find_program('clear.sh') ++pmem_errors = find_program('pmem-errors.sh') ++daxdev_errors_sh = find_program('daxdev-errors.sh') ++multi_dax = find_program('multi-dax.sh') ++btt_check = find_program('btt-check.sh') ++label_compat = find_program('label-compat.sh') ++sector_mode = find_program('sector-mode.sh') ++inject_error = find_program('inject-error.sh') ++btt_errors = find_program('btt-errors.sh') ++btt_pad_compat = find_program('btt-pad-compat.sh') ++firmware_update = find_program('firmware-update.sh') ++rescan_partitions = find_program('rescan-partitions.sh') ++inject_smart = find_program('inject-smart.sh') ++monitor = find_program('monitor.sh') ++max_extent = find_program('max_available_extent_ns.sh') ++pfn_meta_errors = find_program('pfn-meta-errors.sh') ++track_uuid = find_program('track-uuid.sh') ++ ++tests = [ ++ [ 'libndctl', libndctl ], ++ [ 'dsm-fail', dsm_fail ], ++ [ 'create.sh', create ], ++ [ 'clear.sh', clear ], ++ [ 'pmem-errors.sh', pmem_errors ], ++ [ 'daxdev-errors.sh', daxdev_errors_sh ], ++ [ 'multi-dax.sh', multi_dax ], ++ [ 'btt-check.sh', btt_check ], ++ [ 'label-compat.sh', label_compat ], ++ [ 'sector-mode.sh', sector_mode ], ++ [ 'inject-error.sh', inject_error ], ++ [ 'btt-errors.sh', btt_errors ], ++ [ 'hugetlb', hugetlb ], ++ [ 'btt-pad-compat.sh', btt_pad_compat ], ++ [ 'firmware-update.sh', firmware_update ], ++ [ 'ack-shutdown-count-set', ack_shutdown_count ], ++ [ 'rescan-partitions.sh', rescan_partitions ], ++ [ 'inject-smart.sh', inject_smart ], ++ [ 'monitor.sh', monitor ], ++ [ 'max_extent_ns', max_extent ], ++ [ 'pfn-meta-errors.sh', pfn_meta_errors ], ++ [ 'track-uuid.sh', track_uuid ], ++] ++ ++if get_option('destructive').enabled() ++ sub_section = find_program('sub-section.sh') ++ dax_ext4 = find_program('dax-ext4.sh') ++ dax_xfs = find_program('dax-xfs.sh') ++ align = find_program('align.sh') ++ device_dax_fio = find_program('device-dax-fio.sh') ++ daxctl_devices = find_program('daxctl-devices.sh') ++ daxctl_create = find_program('daxctl-create.sh') ++ dm = find_program('dm.sh') ++ mmap_test = find_program('mmap.sh') ++ ++ tests += [ ++ [ 'pmem-ns', pmem_ns ], ++ [ 'sub-section.sh', sub_section ], ++ [ 'dax-dev', dax_dev ], ++ [ 'dax-ext4.sh', dax_ext4 ], ++ [ 'dax-xfs.sh', dax_xfs ], ++ [ 'align.sh', align ], ++ [ 'device-dax', device_dax ], ++ [ 'revoke-devmem', revoke_devmem ], ++ [ 'device-dax-fio.sh', device_dax_fio ], ++ [ 'daxctl-devices.sh', daxctl_devices ], ++ [ 'daxctl-create.sh', daxctl_create ], ++ [ 'dm.sh', dm ], ++ [ 'mmap.sh', mmap_test ], ++ ] ++endif ++ ++if get_option('keyutils').enabled() ++ security = find_program('security.sh') ++ tests += [ ++ [ 'security.sh', security ] ++ ] ++endif ++ ++foreach t : tests ++ test(t[0], t[1], ++ is_parallel : false, ++ depends : [ ++ ndctl_tool, ++ daxctl_tool, ++ cxl_tool, ++ smart_notify, ++ list_smart_dimm, ++ dax_pmd, ++ dax_errors, ++ daxdev_errors, ++ dax_dev, ++ mmap, ++ ], ++ timeout : 0, ++ env : [ ++ 'NDCTL=@0@'.format(ndctl_tool.full_path()), ++ 'DAXCTL=@0@'.format(daxctl_tool.full_path()), ++ 'TEST_PATH=@0@'.format(meson.current_build_dir()), ++ 'DATA_PATH=@0@'.format(meson.current_source_dir()), ++ ], ++ ) ++endforeach +diff -up ndctl-71.1/tools/meson-vcs-tag.sh.orig ndctl-71.1/tools/meson-vcs-tag.sh +--- ndctl-71.1/tools/meson-vcs-tag.sh.orig 2022-10-07 17:29:51.577717675 -0400 ++++ ndctl-71.1/tools/meson-vcs-tag.sh 2022-10-07 17:29:51.577717675 -0400 +@@ -0,0 +1,18 @@ ++#!/usr/bin/env bash ++# SPDX-License-Identifier: LGPL-2.1-or-later ++ ++set -eu ++set -o pipefail ++ ++dir="${1:?}" ++fallback="${2:?}" ++ ++# Apparently git describe has a bug where it always considers the work-tree ++# dirty when invoked with --git-dir (even though 'git status' is happy). Work ++# around this issue by cd-ing to the source directory. ++cd "$dir" ++# Check that we have either .git/ (a normal clone) or a .git file (a work-tree) ++# and that we don't get confused if a tarball is extracted in a higher-level ++# git repository. ++[ -e .git ] && git describe --abbrev=7 --dirty=+ 2>/dev/null | \ ++ sed -e 's/^v//' -e 's/-/./g' || echo "$fallback" +diff -up ndctl-71.1/util/meson.build.orig ndctl-71.1/util/meson.build +--- ndctl-71.1/util/meson.build.orig 2022-10-07 17:29:51.578717679 -0400 ++++ ndctl-71.1/util/meson.build 2022-10-07 17:29:51.578717679 -0400 +@@ -0,0 +1,15 @@ ++util = static_library('util', [ ++ 'parse-options.c', ++ 'usage.c', ++ 'size.c', ++ 'main.c', ++ 'help.c', ++ 'strbuf.c', ++ 'wrapper.c', ++ 'bitmap.c', ++ 'abspath.c', ++ 'iomem.c', ++ ], ++ include_directories : root_inc, ++) ++util_dep = declare_dependency(link_with : util) +diff -up ndctl-71.1/version.h.in.orig ndctl-71.1/version.h.in +--- ndctl-71.1/version.h.in.orig 2022-10-07 17:29:51.578717679 -0400 ++++ ndctl-71.1/version.h.in 2022-10-07 17:29:51.578717679 -0400 +@@ -0,0 +1,2 @@ ++/* SPDX-License-Identifier: LGPL-2.1 */ ++#define VERSION "@VCS_TAG@" diff --git a/SOURCES/0083-build-Add-meson-rpmbuild-support.patch b/SOURCES/0083-build-Add-meson-rpmbuild-support.patch new file mode 100644 index 0000000..4000682 --- /dev/null +++ b/SOURCES/0083-build-Add-meson-rpmbuild-support.patch @@ -0,0 +1,223 @@ +From 8b5b941093521dd18fcc99659b3e3b1b9e9456b7 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:32:58 -0800 +Subject: [PATCH 083/217] build: Add meson rpmbuild support + +Beyond being a prerequisite for removing autotools support, this capability +served as validation that the meson conversion generated all the same files +as autotools and installed them to the same expected locations. + +The procedure to use the rpmbuild.sh script is: + + meson setup build + meson compile -C build rhel/ndctl.spec + ./rpmbuild.sh build/rhel/ndctl.spec + +Link: https://lore.kernel.org/r/164141837841.3990253.11379060834465142446.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .gitignore | 2 +- + Makefile.am | 2 ++ + meson.build | 6 ++++++ + ndctl.spec.in | 23 +++++++++++++++++++++++ + rhel/meson.build | 23 +++++++++++++++++++++++ + rpmbuild.sh | 5 ++++- + sles/meson.build | 36 ++++++++++++++++++++++++++++++++++++ + 7 files changed, 95 insertions(+), 2 deletions(-) + create mode 100644 rhel/meson.build + create mode 100644 sles/meson.build + +diff -up ndctl-71.1/.gitignore.orig ndctl-71.1/.gitignore +--- ndctl-71.1/.gitignore.orig 2022-10-07 16:34:40.712445112 -0400 ++++ ndctl-71.1/.gitignore 2022-10-07 16:34:52.832486377 -0400 +@@ -35,7 +35,7 @@ daxctl/lib/libdaxctl.pc + ndctl/config.h + ndctl/lib/libndctl.pc + ndctl/ndctl +-rhel/ ++rhel/ndctl.spec + sles/ndctl.spec + version.m4 + *.swp +diff -up ndctl-71.1/Makefile.am.orig ndctl-71.1/Makefile.am +--- ndctl-71.1/Makefile.am.orig 2022-10-07 16:34:40.687445027 -0400 ++++ ndctl-71.1/Makefile.am 2022-10-07 16:34:52.832486377 -0400 +@@ -22,6 +22,7 @@ noinst_SCRIPTS = rhel/ndctl.spec sles/nd + CLEANFILES += $(noinst_SCRIPTS) + + do_rhel_subst = sed -e 's,VERSION,$(VERSION),g' \ ++ -e 's,MESON,0,g' \ + -e 's,DAX_DNAME,daxctl-devel,g' \ + -e 's,CXL_DNAME,cxl-devel,g' \ + -e 's,DNAME,ndctl-devel,g' \ +@@ -31,6 +32,7 @@ do_rhel_subst = sed -e 's,VERSION,$(VERS + -e 's,LNAME,ndctl-libs,g' + + do_sles_subst = sed -e 's,VERSION,$(VERSION),g' \ ++ -e 's,MESON,0,g' \ + -e 's,DAX_DNAME,libdaxctl-devel,g' \ + -e 's,CXL_DNAME,libcxl-devel,g' \ + -e 's,DNAME,libndctl-devel,g' \ +diff -up ndctl-71.1/meson.build.orig ndctl-71.1/meson.build +--- ndctl-71.1/meson.build.orig 2022-10-07 16:34:40.715445122 -0400 ++++ ndctl-71.1/meson.build 2022-10-07 16:34:52.833486380 -0400 +@@ -278,3 +278,9 @@ if get_option('docs').enabled() + endif + subdir('test') + subdir('contrib') ++ ++# only support spec file generation from git builds ++if version_tag == '' ++ subdir('rhel') ++ subdir('sles') ++endif +diff -up ndctl-71.1/ndctl.spec.in.orig ndctl-71.1/ndctl.spec.in +--- ndctl-71.1/ndctl.spec.in.orig 2022-10-07 16:34:40.645444884 -0400 ++++ ndctl-71.1/ndctl.spec.in 2022-10-07 16:34:52.833486380 -0400 +@@ -6,14 +6,20 @@ License: GPLv2 + Url: https://github.com/pmem/ndctl + Source0: https://github.com/pmem/%{name}/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz + ++%define with_meson MESON + Requires: LNAME%{?_isa} = %{version}-%{release} + Requires: DAX_LNAME%{?_isa} = %{version}-%{release} + Requires: CXL_LNAME%{?_isa} = %{version}-%{release} + BuildRequires: autoconf + %if 0%{?rhel} < 9 + BuildRequires: asciidoc ++%if !%{with_meson} + %define asciidoc --disable-asciidoctor ++%endif + %else ++%if %{with_meson} ++%define asciidoctor -Dasciidoctor=enabled ++%endif + BuildRequires: rubygem-asciidoctor + %endif + BuildRequires: xmlto +@@ -28,6 +34,10 @@ BuildRequires: pkgconfig(bash-completion + BuildRequires: pkgconfig(systemd) + BuildRequires: keyutils-libs-devel + ++%if %{with_meson} ++BuildRequires: meson ++%endif ++ + %description + Utility library for managing the "libnvdimm" subsystem. The "libnvdimm" + subsystem defines a kernel device model and control message interface for +@@ -115,17 +125,30 @@ libcxl is a library for enumerating and + %setup -q ndctl-%{version} + + %build ++%if %{with_meson} ++%meson %{?asciidoctor} -Dversion-tag=%{version} ++%meson_build ++%else + echo %{version} > version + ./autogen.sh + %configure --disable-static --disable-silent-rules %{?asciidoc} + make %{?_smp_mflags} ++%endif + + %install ++%if %{with_meson} ++%meson_install ++%else + %make_install + find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' ++%endif + + %check ++%if %{with_meson} ++%meson_test ++%else + make check ++%endif + + %ldconfig_scriptlets -n LNAME + +diff -up ndctl-71.1/rhel/meson.build.orig ndctl-71.1/rhel/meson.build +--- ndctl-71.1/rhel/meson.build.orig 2022-10-07 16:34:52.834486384 -0400 ++++ ndctl-71.1/rhel/meson.build 2022-10-07 16:34:52.834486384 -0400 +@@ -0,0 +1,23 @@ ++rhel_spec1 = vcs_tag( ++ input : '../ndctl.spec.in', ++ output : 'ndctl.spec.in', ++ command: vcs_tagger, ++ replace_string : 'VERSION', ++) ++ ++rhel_spec2 = custom_target('ndctl.spec', ++ command : [ ++ 'sed', '-e', 's,MESON,1,g', ++ '-e', 's,DAX_DNAME,daxctl-devel,g', ++ '-e', 's,CXL_DNAME,cxl-devel,g', ++ '-e', 's,DNAME,ndctl-devel,g', ++ '-e', '/^%defattr.*/d', ++ '-e', 's,DAX_LNAME,daxctl-libs,g', ++ '-e', 's,CXL_LNAME,cxl-libs,g', ++ '-e', 's,LNAME,ndctl-libs,g', ++ '@INPUT@' ++ ], ++ input : rhel_spec1, ++ output : 'ndctl.spec', ++ capture : true, ++) +diff -up ndctl-71.1/rpmbuild.sh.orig ndctl-71.1/rpmbuild.sh +--- ndctl-71.1/rpmbuild.sh.orig 2020-12-22 16:44:57.000000000 -0500 ++++ ndctl-71.1/rpmbuild.sh 2022-10-07 16:34:52.834486384 -0400 +@@ -1,6 +1,9 @@ + #!/bin/bash ++ ++spec=${1:-$(dirname $0)/rhel/ndctl.spec)} ++ + pushd $(dirname $0) >/dev/null + [ ! -d ~/rpmbuild/SOURCES ] && echo "rpmdev tree not found" && exit 1 + ./make-git-snapshot.sh + popd > /dev/null +-rpmbuild -ba $(dirname $0)/rhel/ndctl.spec ++rpmbuild --nocheck -ba $spec +diff -up ndctl-71.1/sles/meson.build.orig ndctl-71.1/sles/meson.build +--- ndctl-71.1/sles/meson.build.orig 2022-10-07 16:34:52.836486391 -0400 ++++ ndctl-71.1/sles/meson.build 2022-10-07 16:34:52.835486387 -0400 +@@ -0,0 +1,36 @@ ++sles_spec1 = vcs_tag( ++ input : '../ndctl.spec.in', ++ output : 'ndctl.spec.sles.in', ++ command: vcs_tagger, ++ replace_string : 'VERSION', ++) ++ ++header = files('header') ++ ++sles_spec2 = custom_target('ndctl.spec.in', ++ command : [ ++ 'cat', header, '@INPUT@', ++ ], ++ input : sles_spec1, ++ output : 'ndctl.spec.in', ++ capture : true, ++) ++ ++sles_spec3 = custom_target('ndctl.spec', ++ command : [ ++ 'sed', '-e', 's,MESON,1,g', ++ '-e', 's,DAX_DNAME,libdaxctl-devel,g', ++ '-e', 's,CXL_DNAME,libcxl-devel,g', ++ '-e', 's,DNAME,libndctl-devel,g', ++ '-e', 's,%license,%doc,g', ++ '-e', 's,\(^License:.*GPL\)v2,\1-2.0,g', ++ '-e', 's,DAX_LNAME,libdaxctl@0@,g'.format(LIBDAXCTL_CURRENT - LIBDAXCTL_AGE), ++ '-e', 's,CXL_LNAME,libcxl@0@,g'.format(LIBCXL_CURRENT - LIBCXL_AGE), ++ '-e', 's,LNAME,libndctl@0@,g'.format(LIBNDCTL_CURRENT - LIBNDCTL_AGE), ++ '@INPUT@' ++ ], ++ ++ input : sles_spec2, ++ output : 'ndctl.spec', ++ capture : true, ++) diff --git a/SOURCES/0084-ndctl-Jettison-autotools.patch b/SOURCES/0084-ndctl-Jettison-autotools.patch new file mode 100644 index 0000000..2b83e0e --- /dev/null +++ b/SOURCES/0084-ndctl-Jettison-autotools.patch @@ -0,0 +1,1423 @@ +From 44f2f06c688fec84db41b26b47c2de45040906eb Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 5 Jan 2022 13:33:03 -0800 +Subject: [PATCH 084/217] ndctl: Jettison autotools + +Similar to several other projects, ndctl has run its course with autotools +and sees a better path forward with Meson. Now that the Meson conversion is +complete, remove the autotools infrastructure. + +Link: https://lore.kernel.org/r/164141838349.3990253.14745993061779737304.stgit@dwillia2-desk3.amr.corp.intel.com +Tested-by: Alison Schofield +Tested-by: Vaibhav Jain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .gitignore | 58 ------- + Documentation/cxl/Makefile.am | 61 ------- + Documentation/cxl/lib/Makefile.am | 58 ------- + Documentation/daxctl/Makefile.am | 72 -------- + Documentation/ndctl/Makefile.am | 103 ------------ + Makefile.am | 104 ------------ + Makefile.am.in | 46 ----- + autogen.sh | 28 ---- + configure.ac | 270 ------------------------------ + cxl/Makefile.am | 25 --- + cxl/lib/Makefile.am | 32 ---- + daxctl/Makefile.am | 45 ----- + daxctl/lib/Makefile.am | 42 ----- + ndctl.spec.in | 28 +--- + ndctl/Makefile.am | 86 ---------- + ndctl/lib/Makefile.am | 58 ------- + rhel/meson.build | 3 +- + sles/meson.build | 3 +- + test/Makefile.am | 169 ------------------- + 19 files changed, 4 insertions(+), 1287 deletions(-) + delete mode 100644 Documentation/cxl/Makefile.am + delete mode 100644 Documentation/cxl/lib/Makefile.am + delete mode 100644 Documentation/daxctl/Makefile.am + delete mode 100644 Documentation/ndctl/Makefile.am + delete mode 100644 Makefile.am + delete mode 100644 Makefile.am.in + delete mode 100755 autogen.sh + delete mode 100644 configure.ac + delete mode 100644 cxl/Makefile.am + delete mode 100644 cxl/lib/Makefile.am + delete mode 100644 daxctl/Makefile.am + delete mode 100644 daxctl/lib/Makefile.am + delete mode 100644 ndctl/Makefile.am + delete mode 100644 ndctl/lib/Makefile.am + delete mode 100644 test/Makefile.am + +diff -up ndctl-71.1/.gitignore.orig ndctl-71.1/.gitignore +--- ndctl-71.1/.gitignore.orig 2022-10-07 18:39:13.182886649 -0400 ++++ ndctl-71.1/.gitignore 2022-10-07 18:39:43.934991351 -0400 +@@ -1,63 +1,5 @@ +-*.o +-*.lo +-*.xml +-.deps/ +-.libs/ +-Makefile +-!contrib/Makefile +-Makefile.in +-/aclocal.m4 +-/autom4te.cache +-/build-aux +-/config.h +-/config.log +-/config.status +-/configure +-/libtool +-/stamp-h1 +-*.1 +-*.3 +-Documentation/daxctl/asciidoc.conf +-Documentation/ndctl/asciidoc.conf +-Documentation/cxl/asciidoc.conf +-Documentation/cxl/lib/asciidoc.conf +-Documentation/daxctl/asciidoctor-extensions.rb +-Documentation/ndctl/asciidoctor-extensions.rb +-Documentation/cxl/asciidoctor-extensions.rb +-Documentation/cxl/lib/asciidoctor-extensions.rb +-.dirstamp + build/ +-daxctl/config.h +-daxctl/daxctl +-daxctl/lib/libdaxctl.la +-daxctl/lib/libdaxctl.pc +-*.a +-ndctl/config.h +-ndctl/lib/libndctl.pc +-ndctl/ndctl + rhel/ndctl.spec + sles/ndctl.spec +-version.m4 + *.swp +-cscope.files +-cscope*.out + tags +-test/*.log +-test/*.trs +-test/dax-dev +-test/dax-errors +-test/dax-pmd +-test/daxdev-errors +-test/device-dax +-test/dsm-fail +-test/hugetlb +-test/image +-test/libndctl +-test/mmap +-test/pmem-ns +-test/smart-listen +-test/smart-notify +-test/fio.job +-test/local-write-0-verify.state +-test/ack-shutdown-count-set +-test/list-smart-dimm +diff -up ndctl-71.1/Documentation/cxl/Makefile.am.orig /dev/null +--- ndctl-71.1/Documentation/cxl/Makefile.am 2022-10-07 18:39:13.067886258 -0400 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,61 +0,0 @@ +-# SPDX-License-Identifier: GPL-2.0 +-# Copyright (C) 2020-2021 Intel Corporation. All rights reserved. +- +-if USE_ASCIIDOCTOR +- +-do_subst = sed -e 's,@Utility@,Cxl,g' -e's,@utility@,cxl,g' +-CONFFILE = asciidoctor-extensions.rb +-asciidoctor-extensions.rb: ../asciidoctor-extensions.rb.in +- $(AM_V_GEN) $(do_subst) < $< > $@ +- +-else +- +-do_subst = sed -e 's,UTILITY,cxl,g' +-CONFFILE = asciidoc.conf +-asciidoc.conf: ../asciidoc.conf.in +- $(AM_V_GEN) $(do_subst) < $< > $@ +- +-endif +- +-man1_MANS = \ +- cxl.1 \ +- cxl-list.1 \ +- cxl-read-labels.1 \ +- cxl-write-labels.1 \ +- cxl-zero-labels.1 +- +-EXTRA_DIST = $(man1_MANS) +- +-CLEANFILES = $(man1_MANS) +- +-XML_DEPS = \ +- ../../version.m4 \ +- ../copyright.txt \ +- Makefile \ +- $(CONFFILE) +- +-RM ?= rm -f +- +-if USE_ASCIIDOCTOR +- +-%.1: %.txt $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@+ $@ && \ +- $(ASCIIDOC) -b manpage -d manpage -acompat-mode \ +- -I. -rasciidoctor-extensions \ +- -amansource=cxl -amanmanual="cxl Manual" \ +- -andctl_version=$(VERSION) -o $@+ $< && \ +- mv $@+ $@ +- +-else +- +-%.xml: %.txt $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@+ $@ && \ +- $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ +- --unsafe -acxl_version=$(VERSION) -o $@+ $< && \ +- mv $@+ $@ +- +-%.1: %.xml $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@ && \ +- $(XMLTO) -o . -m ../manpage-normal.xsl man $< +- +-endif +diff -up ndctl-71.1/Documentation/cxl/lib/Makefile.am /dev/null +--- ndctl-71.1/Documentation/cxl/lib/Makefile.am 2022-10-07 18:39:13.071886271 -0400 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,58 +0,0 @@ +-# SPDX-License-Identifier: GPL-2.0 +-# Copyright (C) 2020-2021 Intel Corporation. All rights reserved. +- +-if USE_ASCIIDOCTOR +- +-do_subst = sed -e 's,@Utility@,Libcxl,g' -e's,@utility@,libcxl,g' +-CONFFILE = asciidoctor-extensions.rb +-asciidoctor-extensions.rb: ../../asciidoctor-extensions.rb.in +- $(AM_V_GEN) $(do_subst) < $< > $@ +- +-else +- +-do_subst = sed -e 's,UTILITY,libcxl,g' +-CONFFILE = asciidoc.conf +-asciidoc.conf: ../../asciidoc.conf.in +- $(AM_V_GEN) $(do_subst) < $< > $@ +- +-endif +- +-man3_MANS = \ +- libcxl.3 \ +- cxl_new.3 +- +-EXTRA_DIST = $(man3_MANS) +- +-CLEANFILES = $(man3_MANS) +- +-XML_DEPS = \ +- ../../../version.m4 \ +- ../../copyright.txt \ +- Makefile \ +- $(CONFFILE) +- +-RM ?= rm -f +- +-if USE_ASCIIDOCTOR +- +-%.3: %.txt $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@+ $@ && \ +- $(ASCIIDOC) -b manpage -d manpage -acompat-mode \ +- -I. -rasciidoctor-extensions \ +- -amansource=libcxl -amanmanual="libcxl Manual" \ +- -andctl_version=$(VERSION) -o $@+ $< && \ +- mv $@+ $@ +- +-else +- +-%.xml: %.txt $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@+ $@ && \ +- $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ +- --unsafe -alibcxl_version=$(VERSION) -o $@+ $< && \ +- mv $@+ $@ +- +-%.3: %.xml $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@ && \ +- $(XMLTO) -o . -m ../../manpage-normal.xsl man $< +- +-endif +diff -up ndctl-71.1/Documentation/daxctl/Makefile.am /dev/null +--- ndctl-71.1/Documentation/daxctl/Makefile.am 2022-10-07 18:42:19.341520462 -0400 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,66 +0,0 @@ +-# SPDX-License-Identifier: GPL-2.0 +-# Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +- +-if USE_ASCIIDOCTOR +- +-do_subst = sed -e 's,@Utility@,Daxctl,g' -e's,@utility@,daxctl,g' +-CONFFILE = asciidoctor-extensions.rb +-asciidoctor-extensions.rb: ../asciidoctor-extensions.rb.in +- $(AM_V_GEN) $(do_subst) < $< > $@ +- +-else +- +-do_subst = sed -e 's,UTILITY,daxctl,g' +-CONFFILE = asciidoc.conf +-asciidoc.conf: ../asciidoc.conf.in +- $(AM_V_GEN) $(do_subst) < $< > $@ +- +-endif +- +-man1_MANS = \ +- daxctl.1 \ +- daxctl-list.1 \ +- daxctl-migrate-device-model.1 \ +- daxctl-reconfigure-device.1 \ +- daxctl-online-memory.1 \ +- daxctl-offline-memory.1 \ +- daxctl-disable-device.1 \ +- daxctl-enable-device.1 \ +- daxctl-create-device.1 \ +- daxctl-destroy-device.1 +- +-EXTRA_DIST = $(man1_MANS) +- +-CLEANFILES = $(man1_MANS) +- +-XML_DEPS = \ +- ../../version.m4 \ +- ../copyright.txt \ +- Makefile \ +- $(CONFFILE) +- +-RM ?= rm -f +- +-if USE_ASCIIDOCTOR +- +-%.1: %.txt $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@+ $@ && \ +- $(ASCIIDOC) -b manpage -d manpage -acompat-mode \ +- -I. -rasciidoctor-extensions \ +- -amansource=daxctl -amanmanual="daxctl Manual" \ +- -andctl_version=$(VERSION) -o $@+ $< && \ +- mv $@+ $@ +- +-else +- +-%.xml: %.txt $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@+ $@ && \ +- $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ +- --unsafe -adaxctl_version=$(VERSION) -o $@+ $< && \ +- mv $@+ $@ +- +-%.1: %.xml $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@ && \ +- $(XMLTO) -o . -m ../manpage-normal.xsl man $< +- +-endif +diff -up ndctl-71.1/Documentation/ndctl/Makefile.am /dev/null +--- ndctl-71.1/Documentation/ndctl/Makefile.am 2020-12-22 16:44:57.000000000 -0500 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,106 +0,0 @@ +-# SPDX-License-Identifier: GPL-2.0 +-# Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +- +-if USE_ASCIIDOCTOR +- +-do_subst = sed -e 's,@Utility@,Ndctl,g' -e's,@utility@,ndctl,g' +-CONFFILE = asciidoctor-extensions.rb +-asciidoctor-extensions.rb: ../asciidoctor-extensions.rb.in +- $(AM_V_GEN) $(do_subst) < $< > $@ +- +-else +- +-do_subst = sed -e 's,UTILITY,ndctl,g' +-CONFFILE = asciidoc.conf +-asciidoc.conf: ../asciidoc.conf.in +- $(AM_V_GEN) $(do_subst) < $< > $@ +- +-endif +- +-man1_MANS = \ +- ndctl.1 \ +- ndctl-wait-scrub.1 \ +- ndctl-start-scrub.1 \ +- ndctl-zero-labels.1 \ +- ndctl-read-labels.1 \ +- ndctl-write-labels.1 \ +- ndctl-init-labels.1 \ +- ndctl-check-labels.1 \ +- ndctl-enable-region.1 \ +- ndctl-disable-region.1 \ +- ndctl-enable-dimm.1 \ +- ndctl-disable-dimm.1 \ +- ndctl-enable-namespace.1 \ +- ndctl-disable-namespace.1 \ +- ndctl-create-namespace.1 \ +- ndctl-destroy-namespace.1 \ +- ndctl-check-namespace.1 \ +- ndctl-clear-errors.1 \ +- ndctl-inject-error.1 \ +- ndctl-inject-smart.1 \ +- ndctl-update-firmware.1 \ +- ndctl-list.1 \ +- ndctl-monitor.1 \ +- ndctl-setup-passphrase.1 \ +- ndctl-update-passphrase.1 \ +- ndctl-remove-passphrase.1 \ +- ndctl-freeze-security.1 \ +- ndctl-sanitize-dimm.1 \ +- ndctl-load-keys.1 \ +- ndctl-wait-overwrite.1 \ +- ndctl-read-infoblock.1 \ +- ndctl-write-infoblock.1 \ +- ndctl-activate-firmware.1 +- +-EXTRA_DIST = $(man1_MANS) +- +-CLEANFILES = $(man1_MANS) +- +-.ONESHELL: +-attrs.adoc: $(srcdir)/Makefile.am +- $(AM_V_GEN) cat <<- EOF >$@ +- :ndctl_monitorconfdir: $(ndctl_monitorconfdir) +- :ndctl_monitorconf: $(ndctl_monitorconf) +- :ndctl_keysdir: $(ndctl_keysdir) +- EOF +- +-XML_DEPS = \ +- ../../version.m4 \ +- Makefile \ +- $(CONFFILE) \ +- ../copyright.txt \ +- region-description.txt \ +- xable-region-options.txt \ +- dimm-description.txt \ +- xable-dimm-options.txt \ +- xable-namespace-options.txt \ +- ars-description.txt \ +- labels-description.txt \ +- labels-options.txt \ +- attrs.adoc +- +-RM ?= rm -f +- +-if USE_ASCIIDOCTOR +- +-%.1: %.txt $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@+ $@ && \ +- $(ASCIIDOC) -b manpage -d manpage -acompat-mode \ +- -I. -rasciidoctor-extensions \ +- -amansource=ndctl -amanmanual="ndctl Manual" \ +- -andctl_version=$(VERSION) -o $@+ $< && \ +- mv $@+ $@ +- +-else +- +-%.xml: %.txt $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@+ $@ && \ +- $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ +- --unsafe -andctl_version=$(VERSION) -o $@+ $< && \ +- mv $@+ $@ +- +-%.1: %.xml $(XML_DEPS) +- $(AM_V_GEN)$(RM) $@ && \ +- $(XMLTO) -o . -m ../manpage-normal.xsl man $< +- +-endif +diff -up ndctl-71.1/Makefile.am.in /dev/null +--- ndctl-71.1/Makefile.am.in 2022-10-07 18:39:13.130886472 -0400 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,46 +0,0 @@ +-EXTRA_DIST = +-CLEANFILES = +- +-AM_MAKEFLAGS = --no-print-directory +- +-AM_CPPFLAGS = \ +- -include $(top_builddir)/config.h \ +- -DSYSCONFDIR=\""$(sysconfdir)"\" \ +- -DLIBEXECDIR=\""$(libexecdir)"\" \ +- -DPREFIX=\""$(prefix)"\" \ +- -DNDCTL_MAN_PATH=\""$(mandir)"\" \ +- -I${top_srcdir}/ \ +- $(KMOD_CFLAGS) \ +- $(UDEV_CFLAGS) \ +- $(UUID_CFLAGS) \ +- $(JSON_CFLAGS) +- +-AM_CFLAGS = ${my_CFLAGS} \ +- -fvisibility=hidden \ +- -ffunction-sections \ +- -fdata-sections +- +-AM_LDFLAGS = \ +- -Wl,--gc-sections \ +- -Wl,--as-needed +- +-SED_PROCESS = \ +- $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(SED) \ +- -e 's,@VERSION\@,$(VERSION),g' \ +- -e 's,@prefix\@,$(prefix),g' \ +- -e 's,@exec_prefix\@,$(exec_prefix),g' \ +- -e 's,@libdir\@,$(libdir),g' \ +- -e 's,@includedir\@,$(includedir),g' \ +- < $< > $@ || rm $@ +- +-LIBNDCTL_CURRENT=26 +-LIBNDCTL_REVISION=1 +-LIBNDCTL_AGE=20 +- +-LIBDAXCTL_CURRENT=7 +-LIBDAXCTL_REVISION=0 +-LIBDAXCTL_AGE=6 +- +-LIBCXL_CURRENT=1 +-LIBCXL_REVISION=0 +-LIBCXL_AGE=0 +diff -up ndctl-71.1/Makefile.am /dev/null +--- ndctl-71.1/Makefile.am 2022-10-07 18:39:13.182886649 -0400 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,102 +0,0 @@ +-include Makefile.am.in +- +-ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} +-SUBDIRS = . cxl/lib daxctl/lib ndctl/lib cxl ndctl daxctl +-if ENABLE_DOCS +-SUBDIRS += Documentation/ndctl Documentation/daxctl Documentation/cxl +-SUBDIRS += Documentation/cxl/lib +-endif +-SUBDIRS += test +- +-BUILT_SOURCES = version.m4 +-version.m4: FORCE +- $(AM_V_GEN)$(top_srcdir)/git-version-gen +- +-FORCE: +- +-EXTRA_DIST += ndctl.spec.in \ +- sles/header \ +- contrib/nvdimm-security.conf +- +-noinst_SCRIPTS = rhel/ndctl.spec sles/ndctl.spec +-CLEANFILES += $(noinst_SCRIPTS) +- +-do_rhel_subst = sed -e 's,VERSION,$(VERSION),g' \ +- -e 's,MESON,0,g' \ +- -e 's,DAX_DNAME,daxctl-devel,g' \ +- -e 's,CXL_DNAME,cxl-devel,g' \ +- -e 's,DNAME,ndctl-devel,g' \ +- -e '/^%defattr.*/d' \ +- -e 's,DAX_LNAME,daxctl-libs,g' \ +- -e 's,CXL_LNAME,cxl-libs,g' \ +- -e 's,LNAME,ndctl-libs,g' +- +-do_sles_subst = sed -e 's,VERSION,$(VERSION),g' \ +- -e 's,MESON,0,g' \ +- -e 's,DAX_DNAME,libdaxctl-devel,g' \ +- -e 's,CXL_DNAME,libcxl-devel,g' \ +- -e 's,DNAME,libndctl-devel,g' \ +- -e 's,%license,%doc,g' \ +- -e 's,\(^License:.*GPL\)v2,\1-2.0,g' \ +- -e "s,DAX_LNAME,libdaxctl$$(($(LIBDAXCTL_CURRENT) - $(LIBDAXCTL_AGE))),g" \ +- -e "s,CXL_LNAME,libcxl$$(($(LIBCXL_CURRENT) - $(LIBCXL_AGE))),g" \ +- -e "s,LNAME,libndctl$$(($(LIBNDCTL_CURRENT) - $(LIBNDCTL_AGE))),g" +- +-rhel/ndctl.spec: ndctl.spec.in Makefile.am version.m4 +- $(AM_V_GEN)$(MKDIR_P) rhel; $(do_rhel_subst) < $< > $@ +- +-sles/ndctl.spec: sles/header ndctl.spec.in Makefile.am version.m4 +- $(AM_V_GEN)$(MKDIR_P) sles; cat sles/header $< | $(do_sles_subst) > $@ +- +-if ENABLE_BASH_COMPLETION +-bashcompletiondir = $(BASH_COMPLETION_DIR) +-dist_bashcompletion_DATA = contrib/ndctl +-install-data-hook: +- $(LN_S) -f $(BASH_COMPLETION_DIR)/ndctl $(DESTDIR)/$(BASH_COMPLETION_DIR)/daxctl +- $(LN_S) -f $(BASH_COMPLETION_DIR)/ndctl $(DESTDIR)/$(BASH_COMPLETION_DIR)/cxl +-endif +- +-modprobe_file = contrib/nvdimm-security.conf +-modprobedir = $(sysconfdir)/modprobe.d/ +-modprobe_DATA = $(modprobe_file) +- +-noinst_LIBRARIES = libccan.a +-libccan_a_SOURCES = \ +- ccan/str/str.h \ +- ccan/str/str_debug.h \ +- ccan/str/str.c \ +- ccan/str/debug.c \ +- ccan/list/list.h \ +- ccan/list/list.c \ +- ccan/container_of/container_of.h \ +- ccan/check_type/check_type.h \ +- ccan/build_assert/build_assert.h \ +- ccan/array_size/array_size.h \ +- ccan/minmax/minmax.h \ +- ccan/short_types/short_types.h \ +- ccan/endian/endian.h +- +-noinst_LIBRARIES += libutil.a +-libutil_a_SOURCES = \ +- util/parse-options.c \ +- util/parse-options.h \ +- util/usage.c \ +- util/size.c \ +- util/main.c \ +- util/help.c \ +- util/strbuf.c \ +- util/wrapper.c \ +- util/bitmap.c \ +- util/abspath.c \ +- util/iomem.c \ +- util/util.h \ +- util/strbuf.h \ +- util/size.h \ +- util/main.h \ +- util/filter.h \ +- util/bitmap.h +- +-nobase_include_HEADERS = \ +- daxctl/libdaxctl.h \ +- cxl/libcxl.h \ +- cxl/cxl_mem.h +diff -up ndctl-71.1/autogen.sh /dev/null +--- ndctl-71.1/autogen.sh 2020-12-22 16:44:57.000000000 -0500 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,28 +0,0 @@ +-#!/bin/sh -e +- +-if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then +- cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \ +- chmod +x .git/hooks/pre-commit && \ +- echo "Activated pre-commit hook." +-fi +- +-$(dirname $0)/git-version-gen +-reconf_args='' +-[ -n "$*" ] && reconf_args="$*" +-autoreconf --install --symlink $reconf_args +- +-libdir() { +- echo $(cd $1/$(gcc -print-multi-os-directory); pwd) +-} +- +-args="--prefix=/usr \ +---sysconfdir=/etc \ +---libdir=$(libdir /usr/lib)" +- +-echo +-echo "----------------------------------------------------------------" +-echo "Initialized build system. For a common configuration please run:" +-echo "----------------------------------------------------------------" +-echo +-echo "./configure CFLAGS='-g -O2' $args" +-echo +diff -up ndctl-71.1/configure.ac /dev/null +--- ndctl-71.1/configure.ac 2022-10-07 18:39:13.083886312 -0400 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,254 +0,0 @@ +-AC_PREREQ(2.60) +-m4_include([version.m4]) +-AC_INIT([ndctl], +- GIT_VERSION, +- [nvdimm@lists.linux.dev], +- [ndctl], +- [https://github.com/pmem/ndctl]) +-AC_CONFIG_SRCDIR([ndctl/lib/libndctl.c]) +-AC_CONFIG_AUX_DIR([build-aux]) +-AM_INIT_AUTOMAKE([ +- foreign +- 1.11 +- -Wall +- -Wno-portability +- silent-rules +- tar-pax +- no-dist-gzip +- dist-xz +- subdir-objects +-]) +-AC_PROG_CC_STDC +-AC_USE_SYSTEM_EXTENSIONS +-AC_SYS_LARGEFILE +-AC_CONFIG_MACRO_DIR([m4]) +-AM_SILENT_RULES([yes]) +-LT_INIT([ +- disable-static +- pic-only +-]) +-AC_PREFIX_DEFAULT([/usr]) +- +-AC_PROG_SED +-AC_PROG_MKDIR_P +-AC_PROG_LN_S +- +-AC_ARG_ENABLE([docs], +- AS_HELP_STRING([--disable-docs], +- [disable documentation build @<:@default=enabled@:>@]), +- [], enable_docs=yes) +-AS_IF([test "x$enable_docs" = "xyes"], [ +- AC_DEFINE(ENABLE_DOCS, [1], [Documentation / man pages.]) +-]) +-AM_CONDITIONAL([ENABLE_DOCS], [test "x$enable_docs" = "xyes"]) +- +-AC_ARG_ENABLE([asciidoctor], +- AS_HELP_STRING([--enable-asciidoctor], +- [use asciidoctor for documentation build]), +- [], enable_asciidoctor=yes) +-AM_CONDITIONAL([USE_ASCIIDOCTOR], [test "x$enable_asciidoctor" = "xyes"]) +-if test "x$enable_asciidoctor" = "xyes"; then +- asciidoc="asciidoctor" +-else +- asciidoc="asciidoc" +-fi +-AC_CHECK_PROG(ASCIIDOC, [$asciidoc], [$(which $asciidoc)], [missing]) +-if test "x$ASCIIDOC" = xmissing -a "x$enable_docs" = "xyes"; then +- AC_MSG_ERROR([$asciidoc needed to build documentation]) +-fi +-AC_SUBST([ASCIIDOC]) +- +-if test x"$asciidoc" = x"asciidoc"; then +-AC_CHECK_PROG(XMLTO, [xmlto], [$(which xmlto)], [missing]) +-if test "x$XMLTO" = xmissing -a "x$enable_docs" = "xyes"; then +- AC_MSG_ERROR([xmlto needed to build documentation]) +-fi +-AC_SUBST([XMLTO]) +-fi +- +-AC_C_TYPEOF +-AC_DEFINE([HAVE_STATEMENT_EXPR], 1, [Define to 1 if you have statement expressions.]) +- +-AC_C_BIGENDIAN( +- AC_DEFINE(HAVE_BIG_ENDIAN, 1, [Define to 1 if big-endian-arch]), +- AC_DEFINE(HAVE_LITTLE_ENDIAN, 1, [Define to 1 if little-endian-arch]), +- [], []) +- +-AC_ARG_ENABLE([logging], +- AS_HELP_STRING([--disable-logging], [disable system logging @<:@default=enabled@:>@]), +- [], enable_logging=yes) +-AS_IF([test "x$enable_logging" = "xyes"], [ +- AC_DEFINE(ENABLE_LOGGING, [1], [System logging.]) +-]) +- +-AC_ARG_ENABLE([debug], +- AS_HELP_STRING([--enable-debug], [enable debug messages @<:@default=disabled@:>@]), +- [], [enable_debug=no]) +-AS_IF([test "x$enable_debug" = "xyes"], [ +- AC_DEFINE(ENABLE_DEBUG, [1], [Debug messages.]) +-]) +- +-AC_ARG_ENABLE([destructive], +- AS_HELP_STRING([--enable-destructive], [enable destructive functional tests @<:@default=disabled@:>@]), +- [], [enable_destructive=no]) +-AS_IF([test "x$enable_destructive" = "xyes"], +- [AC_DEFINE([ENABLE_DESTRUCTIVE], [1], [destructive functional tests support])]) +-AM_CONDITIONAL([ENABLE_DESTRUCTIVE], [test "x$enable_destructive" = "xyes"]) +- +-AC_ARG_ENABLE([test], +- AS_HELP_STRING([--enable-test], [enable ndctl test command @<:@default=disabled@:>@]), +- [], [enable_test=$enable_destructive]) +-AS_IF([test "x$enable_test" = "xyes"], +- [AC_DEFINE([ENABLE_TEST], [1], [ndctl test support])]) +-AM_CONDITIONAL([ENABLE_TEST], [test "x$enable_test" = "xyes"]) +- +-AC_CHECK_DECLS([BUS_MCEERR_AR], [enable_bus_mc_err=yes], [], [[#include ]]) +-AC_CHECK_DECLS([MAP_SHARED_VALIDATE], [kernel_map_shared_validate=yes], [], [[#include ]]) +-AC_CHECK_DECLS([MAP_SYNC], [kernel_map_sync=yes], [], [[#include ]]) +-AS_UNSET([ac_cv_have_decl_MAP_SHARED_VALIDATE]) +-AS_UNSET([ac_cv_have_decl_MAP_SYNC]) +-AC_CHECK_DECLS([MAP_SHARED_VALIDATE], [enable_map_shared_validate=yes], [], [[#include ]]) +-AC_CHECK_DECLS([MAP_SYNC], [enable_map_sync=yes], [], [[#include ]]) +- +-if test "x$kernel_map_shared_validate" = "xyes" -a "x$enable_map_shared_validate" != "xyes" ; then +- AC_MSG_WARN([MAP_SHARED_VALIDATE supported by kernel but not by , consider installing glibc-2.28 or later.]) +-fi +-if test "x$kernel_map_shared_validate" != "xyes" -a "x$enable_map_shared_validate" != "xyes" ; then +- AC_MSG_WARN([MAP_SHARED_VALIDATE not supported by kernel, consider installing kernel-4.15 or later.]) +-fi +-if test "x$kernel_map_sync" = "xyes" -a "x$enable_map_sync" != "xyes" ; then +- AC_MSG_WARN([MAP_SYNC supported by kernel but not by , consider installing glibc-2.28 or later.]) +-fi +-if test "x$kernel_map_sync" != "xyes" -a "x$enable_map_sync" != "xyes" ; then +- AC_MSG_WARN([MAP_SYNC not supported by kernel or architecture, consider installing kernel-4.15 or later.]) +-fi +- +-AS_IF([test "x$enable_bus_mc_err" = "xyes" -a "x$enable_map_sync" = "xyes" -a "x$enable_map_shared_validate" = "xyes"], +- [AC_DEFINE([ENABLE_POISON], [1], [ndctl test poison support])]) +-AM_CONDITIONAL([ENABLE_POISON], +- [test "x$enable_bus_mc_err" = "xyes" -a "x$enable_map_sync" = "xyes" -a "x$enable_map_shared_validate" = "xyes"]) +- +-PKG_CHECK_MODULES([KMOD], [libkmod]) +-PKG_CHECK_MODULES([UDEV], [libudev]) +-PKG_CHECK_MODULES([UUID], [uuid], +- [AC_DEFINE([HAVE_UUID], [1], [Define to 1 if using libuuid])]) +-PKG_CHECK_MODULES([JSON], [json-c]) +- +-AC_ARG_WITH([bash], +- AS_HELP_STRING([--with-bash], +- [Enable bash auto-completion. @<:@default=yes@:>@]), +- [], +- [with_bash=yes]) +- +-if test "x$with_bash" = "xyes"; then +- PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], +- [BASH_COMPLETION_DIR=$($PKG_CONFIG --variable=completionsdir bash-completion)], []) +-fi +- +-AC_SUBST([BASH_COMPLETION_DIR]) +-AM_CONDITIONAL([ENABLE_BASH_COMPLETION], [test "x$with_bash" = "xyes"]) +- +-AC_ARG_ENABLE([local], +- AS_HELP_STRING([--disable-local], [build against kernel ndctl.h @<:@default=system@:>@]), +- [], [enable_local=yes]) +- +-AC_CHECK_HEADERS_ONCE([linux/version.h]) +- +-AC_CHECK_FUNCS([ \ +- __secure_getenv \ +- secure_getenv\ +-]) +- +-AC_ARG_WITH([systemd], +- AS_HELP_STRING([--with-systemd], +- [Enable systemd functionality (monitor). @<:@default=yes@:>@]), +- [], [with_systemd=yes]) +- +-if test "x$with_systemd" = "xyes"; then +- PKG_CHECK_MODULES([SYSTEMD], [systemd], +- [systemd_unitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)], []) +-fi +- +-AC_SUBST([systemd_unitdir]) +-AM_CONDITIONAL([ENABLE_SYSTEMD_UNITS], [test "x$with_systemd" = "xyes"]) +- +-ndctl_monitorconfdir=${sysconfdir}/ndctl +-ndctl_monitorconf=monitor.conf +-AC_SUBST([ndctl_monitorconfdir]) +-AC_SUBST([ndctl_monitorconf]) +- +-daxctl_modprobe_datadir=${datadir}/daxctl +-daxctl_modprobe_data=daxctl.conf +-AC_SUBST([daxctl_modprobe_datadir]) +-AC_SUBST([daxctl_modprobe_data]) +- +-AC_ARG_WITH([keyutils], +- AS_HELP_STRING([--with-keyutils], +- [Enable keyutils functionality (security). @<:@default=yes@:>@]), [], [with_keyutils=yes]) +- +-if test "x$with_keyutils" = "xyes"; then +- AC_CHECK_HEADERS([keyutils.h],,[ +- AC_MSG_ERROR([keyutils.h not found, consider installing the keyutils library development package (variously named keyutils-libs-devel, keyutils-devel, or libkeyutils-dev).]) +- ]) +-fi +-AS_IF([test "x$with_keyutils" = "xyes"], +- [AC_DEFINE([ENABLE_KEYUTILS], [1], [Enable keyutils support])]) +-AM_CONDITIONAL([ENABLE_KEYUTILS], [test "x$with_keyutils" = "xyes"]) +- +-ndctl_keysdir=${sysconfdir}/ndctl/keys +-ndctl_keysreadme=keys.readme +-AC_SUBST([ndctl_keysdir]) +-AC_SUBST([ndctl_keysreadme]) +- +-my_CFLAGS="\ +--Wall \ +--Wchar-subscripts \ +--Wformat-security \ +--Wmissing-declarations \ +--Wmissing-prototypes \ +--Wnested-externs \ +--Wshadow \ +--Wsign-compare \ +--Wstrict-prototypes \ +--Wtype-limits \ +--Wmaybe-uninitialized \ +--Wdeclaration-after-statement \ +--Wunused-result \ +--D_FORTIFY_SOURCE=2 \ +--O2 +-" +-AC_SUBST([my_CFLAGS]) +- +-AC_CONFIG_HEADERS(config.h) +-AC_CONFIG_FILES([ +- Makefile +- daxctl/lib/Makefile +- cxl/lib/Makefile +- ndctl/lib/Makefile +- ndctl/Makefile +- daxctl/Makefile +- cxl/Makefile +- test/Makefile +- Documentation/ndctl/Makefile +- Documentation/daxctl/Makefile +- Documentation/cxl/Makefile +- Documentation/cxl/lib/Makefile +-]) +- +-AC_OUTPUT +-AC_MSG_RESULT([ +- $PACKAGE $VERSION +- ===== +- +- prefix: ${prefix} +- sysconfdir: ${sysconfdir} +- libdir: ${libdir} +- includedir: ${includedir} +- +- compiler: ${CC} +- cflags: ${CFLAGS} +- ldflags: ${LDFLAGS} +- +- logging: ${enable_logging} +- debug: ${enable_debug} +-]) +diff -up ndctl-71.1/cxl/Makefile.am /dev/null +--- ndctl-71.1/cxl/Makefile.am 2022-10-07 18:39:13.131886476 -0400 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,25 +0,0 @@ +-include $(top_srcdir)/Makefile.am.in +- +-bin_PROGRAMS = cxl +- +-DISTCLEANFILES = config.h +-BUILT_SOURCES = config.h +-config.h: $(srcdir)/Makefile.am +- $(AM_V_GEN) echo "/* Autogenerated by cxl/Makefile.am */" >$@ +- +-cxl_SOURCES =\ +- cxl.c \ +- list.c \ +- memdev.c \ +- ../util/json.c \ +- json.c \ +- filter.c \ +- filter.h \ +- builtin.h +- +-cxl_LDADD =\ +- lib/libcxl.la \ +- ../libutil.a \ +- $(UUID_LIBS) \ +- $(KMOD_LIBS) \ +- $(JSON_LIBS) +diff -up ndctl-71.1/cxl/lib/Makefile.am /dev/null +--- ndctl-71.1/cxl/lib/Makefile.am 2022-10-07 18:39:13.041886169 -0400 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,32 +0,0 @@ +-include $(top_srcdir)/Makefile.am.in +- +-%.pc: %.pc.in Makefile +- $(SED_PROCESS) +- +-pkginclude_HEADERS = ../libcxl.h ../cxl_mem.h +-lib_LTLIBRARIES = libcxl.la +- +-libcxl_la_SOURCES =\ +- ../libcxl.h \ +- private.h \ +- ../../util/sysfs.c \ +- ../../util/sysfs.h \ +- ../../util/log.c \ +- ../../util/log.h \ +- libcxl.c +- +-libcxl_la_LIBADD =\ +- $(UUID_LIBS) \ +- $(KMOD_LIBS) +- +-EXTRA_DIST += libcxl.sym +- +-libcxl_la_LDFLAGS = $(AM_LDFLAGS) \ +- -version-info $(LIBCXL_CURRENT):$(LIBCXL_REVISION):$(LIBCXL_AGE) \ +- -Wl,--version-script=$(top_srcdir)/cxl/lib/libcxl.sym +-libcxl_la_DEPENDENCIES = libcxl.sym +- +-pkgconfigdir = $(libdir)/pkgconfig +-pkgconfig_DATA = libcxl.pc +-EXTRA_DIST += libcxl.pc.in +-CLEANFILES += libcxl.pc +diff -up ndctl-71.1/daxctl/Makefile.am /dev/null +--- ndctl-71.1/daxctl/Makefile.am 2022-10-07 18:39:13.132886479 -0400 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,33 +0,0 @@ +-include $(top_srcdir)/Makefile.am.in +- +-bin_PROGRAMS = daxctl +- +-DISTCLEANFILES = config.h +-BUILT_SOURCES = config.h +-config.h: $(srcdir)/Makefile.am +- $(AM_V_GEN) echo "/* Autogenerated by daxctl/Makefile.am */" >$@ && \ +- echo '#define DAXCTL_MODPROBE_DATA \ +- "$(daxctl_modprobe_datadir)/$(daxctl_modprobe_data)"' >>$@ && \ +- echo '#define DAXCTL_MODPROBE_INSTALL \ +- "$(sysconfdir)/modprobe.d/$(daxctl_modprobe_data)"' >>$@ +- +-daxctl_SOURCES =\ +- daxctl.c \ +- acpi.c \ +- list.c \ +- migrate.c \ +- device.c \ +- ../util/json.c \ +- ../util/json.h \ +- json.c \ +- json.h \ +- filter.c \ +- filter.h \ +- builtin.h +- +-daxctl_LDADD =\ +- lib/libdaxctl.la \ +- ../libutil.a \ +- $(UUID_LIBS) \ +- $(KMOD_LIBS) \ +- $(JSON_LIBS) +diff -up ndctl-71.1/daxctl/lib/Makefile.am /dev/null +--- ndctl-71.1/daxctl/lib/Makefile.am 2020-12-22 16:44:57.000000000 -0500 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,36 +0,0 @@ +-include $(top_srcdir)/Makefile.am.in +- +-%.pc: %.pc.in Makefile +- $(SED_PROCESS) +- +-pkginclude_HEADERS = ../libdaxctl.h +-lib_LTLIBRARIES = libdaxctl.la +- +-libdaxctl_la_SOURCES =\ +- ../libdaxctl.h \ +- libdaxctl-private.h \ +- ../../util/iomem.c \ +- ../../util/iomem.h \ +- ../../util/sysfs.c \ +- ../../util/sysfs.h \ +- ../../util/log.c \ +- ../../util/log.h \ +- libdaxctl.c +- +-libdaxctl_la_LIBADD =\ +- $(UUID_LIBS) \ +- $(KMOD_LIBS) +- +-daxctl_modprobe_data_DATA = daxctl.conf +- +-EXTRA_DIST += libdaxctl.sym daxctl.conf +- +-libdaxctl_la_LDFLAGS = $(AM_LDFLAGS) \ +- -version-info $(LIBDAXCTL_CURRENT):$(LIBDAXCTL_REVISION):$(LIBDAXCTL_AGE) \ +- -Wl,--version-script=$(top_srcdir)/daxctl/lib/libdaxctl.sym +-libdaxctl_la_DEPENDENCIES = libdaxctl.sym +- +-pkgconfigdir = $(libdir)/pkgconfig +-pkgconfig_DATA = libdaxctl.pc +-EXTRA_DIST += libdaxctl.pc.in +-CLEANFILES += libdaxctl.pc +diff -up ndctl-71.1/ndctl.spec.in.orig ndctl-71.1/ndctl.spec.in +--- ndctl-71.1/ndctl.spec.in.orig 2022-10-07 18:39:13.183886653 -0400 ++++ ndctl-71.1/ndctl.spec.in 2022-10-07 18:44:39.333997092 -0400 +@@ -6,21 +6,16 @@ License: GPLv2 + Url: https://github.com/pmem/ndctl + Source0: https://github.com/pmem/%{name}/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz + +-%define with_meson MESON + Requires: LNAME%{?_isa} = %{version}-%{release} + Requires: DAX_LNAME%{?_isa} = %{version}-%{release} + Requires: CXL_LNAME%{?_isa} = %{version}-%{release} + BuildRequires: autoconf + %if 0%{?rhel} < 9 + BuildRequires: asciidoc +-%if !%{with_meson} +-%define asciidoc --disable-asciidoctor +-%endif ++%define asciidoctor -Dasciidoctor=disabled + %else +-%if %{with_meson} +-%define asciidoctor -Dasciidoctor=enabled +-%endif + BuildRequires: rubygem-asciidoctor ++%define asciidoctor -Dasciidoctor=enabled + %endif + BuildRequires: xmlto + BuildRequires: automake +@@ -34,9 +29,7 @@ BuildRequires: pkgconfig(bash-completion + BuildRequires: pkgconfig(systemd) + BuildRequires: keyutils-libs-devel + +-%if %{with_meson} + BuildRequires: meson +-%endif + + %description + Utility library for managing the "libnvdimm" subsystem. The "libnvdimm" +@@ -125,30 +118,14 @@ libcxl is a library for enumerating and + %setup -q ndctl-%{version} + + %build +-%if %{with_meson} + %meson %{?asciidoctor} -Dversion-tag=%{version} + %meson_build +-%else +-echo %{version} > version +-./autogen.sh +-%configure --disable-static --disable-silent-rules %{?asciidoc} +-make %{?_smp_mflags} +-%endif + + %install +-%if %{with_meson} + %meson_install +-%else +-%make_install +-find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' +-%endif + + %check +-%if %{with_meson} + %meson_test +-%else +-make check +-%endif + + %ldconfig_scriptlets -n LNAME + +diff -up ndctl-71.1/ndctl/Makefile.am /dev/null +--- ndctl-71.1/ndctl/Makefile.am 2022-10-07 18:39:13.135886489 -0400 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,83 +0,0 @@ +-include $(top_srcdir)/Makefile.am.in +- +-bin_PROGRAMS = ndctl +- +-DISTCLEANFILES = config.h +-BUILT_SOURCES = config.h +-config.h: $(srcdir)/Makefile.am +- $(AM_V_GEN) echo "/* Autogenerated by ndctl/Makefile.am */" >$@ && \ +- echo '#define NDCTL_CONF_FILE \ +- "$(ndctl_monitorconfdir)/$(ndctl_monitorconf)"' >>$@ +- $(AM_V_GEN) echo '#define NDCTL_KEYS_DIR "$(ndctl_keysdir)"' >>$@ +- +-ndctl_SOURCES = ndctl.c \ +- builtin.h \ +- bus.c \ +- create-nfit.c \ +- namespace.c \ +- check.c \ +- region.c \ +- dimm.c \ +- ../util/log.c \ +- ../daxctl/filter.c \ +- ../daxctl/filter.h \ +- filter.c \ +- filter.h \ +- list.c \ +- ../util/json.c \ +- ../util/json.h \ +- ../daxctl/json.c \ +- ../daxctl/json.h \ +- json.c \ +- json.h \ +- json-smart.c \ +- keys.h \ +- inject-error.c \ +- inject-smart.c \ +- monitor.c \ +- namespace.h \ +- action.h \ +- ../nfit.h \ +- ../test.h \ +- firmware-update.h +- +-if ENABLE_KEYUTILS +-ndctl_SOURCES += keys.c \ +- load-keys.c +-keys_configdir = $(ndctl_keysdir) +-keys_config_DATA = $(ndctl_keysreadme) +-endif +- +-EXTRA_DIST += keys.readme monitor.conf ndctl-monitor.service +- +-if ENABLE_DESTRUCTIVE +-ndctl_SOURCES += ../test/pmem_namespaces.c +-ndctl_SOURCES += bat.c +-endif +- +-ndctl_LDADD =\ +- lib/libndctl.la \ +- ../daxctl/lib/libdaxctl.la \ +- ../libutil.a \ +- $(UUID_LIBS) \ +- $(KMOD_LIBS) \ +- $(JSON_LIBS) +- +-if ENABLE_KEYUTILS +-ndctl_LDADD += -lkeyutils +-endif +- +-if ENABLE_TEST +-ndctl_SOURCES += ../test/libndctl.c \ +- ../test/dsm-fail.c \ +- ../util/sysfs.c \ +- ../test/core.c \ +- test.c +-endif +- +-monitor_configdir = $(ndctl_monitorconfdir) +-monitor_config_DATA = $(ndctl_monitorconf) +- +-if ENABLE_SYSTEMD_UNITS +-systemd_unit_DATA = ndctl-monitor.service +-endif +diff -up ndctl-71.1/ndctl/lib/Makefile.am /dev/null +--- ndctl-71.1/ndctl/lib/Makefile.am 2020-12-22 16:44:57.000000000 -0500 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,52 +0,0 @@ +-include $(top_srcdir)/Makefile.am.in +- +-%.pc: %.pc.in Makefile +- $(SED_PROCESS) +- +-pkginclude_HEADERS = ../libndctl.h ../ndctl.h +-lib_LTLIBRARIES = libndctl.la +- +-libndctl_la_SOURCES =\ +- ../libndctl.h \ +- private.h \ +- ../../util/list.h \ +- ../../util/log.c \ +- ../../util/log.h \ +- ../../util/sysfs.c \ +- ../../util/sysfs.h \ +- ../../util/fletcher.h \ +- dimm.c \ +- inject.c \ +- nfit.c \ +- smart.c \ +- intel.c \ +- hpe1.c \ +- msft.c \ +- hyperv.c \ +- papr.c \ +- ars.c \ +- firmware.c \ +- libndctl.c \ +- intel.h \ +- hpe1.h \ +- msft.h \ +- hyperv.h \ +- ../../ndctl/libndctl-nfit.h +- +-libndctl_la_LIBADD =\ +- ../../daxctl/lib/libdaxctl.la \ +- $(UDEV_LIBS) \ +- $(UUID_LIBS) \ +- $(KMOD_LIBS) +- +-EXTRA_DIST += libndctl.sym +- +-libndctl_la_LDFLAGS = $(AM_LDFLAGS) \ +- -version-info $(LIBNDCTL_CURRENT):$(LIBNDCTL_REVISION):$(LIBNDCTL_AGE) \ +- -Wl,--version-script=$(top_srcdir)/ndctl/lib/libndctl.sym +-libndctl_la_DEPENDENCIES = libndctl.sym +- +-pkgconfigdir = $(libdir)/pkgconfig +-pkgconfig_DATA = libndctl.pc +-EXTRA_DIST += libndctl.pc.in +-CLEANFILES += libndctl.pc +diff -up ndctl-71.1/rhel/meson.build.orig ndctl-71.1/rhel/meson.build +--- ndctl-71.1/rhel/meson.build.orig 2022-10-07 18:39:13.184886656 -0400 ++++ ndctl-71.1/rhel/meson.build 2022-10-07 18:41:13.892297627 -0400 +@@ -7,8 +7,7 @@ rhel_spec1 = vcs_tag( + + rhel_spec2 = custom_target('ndctl.spec', + command : [ +- 'sed', '-e', 's,MESON,1,g', +- '-e', 's,DAX_DNAME,daxctl-devel,g', ++ 'sed', '-e', 's,DAX_DNAME,daxctl-devel,g', + '-e', 's,CXL_DNAME,cxl-devel,g', + '-e', 's,DNAME,ndctl-devel,g', + '-e', '/^%defattr.*/d', +diff -up ndctl-71.1/sles/meson.build.orig ndctl-71.1/sles/meson.build +--- ndctl-71.1/sles/meson.build.orig 2022-10-07 18:39:13.184886656 -0400 ++++ ndctl-71.1/sles/meson.build 2022-10-07 18:41:13.893297631 -0400 +@@ -18,8 +18,7 @@ sles_spec2 = custom_target('ndctl.spec.i + + sles_spec3 = custom_target('ndctl.spec', + command : [ +- 'sed', '-e', 's,MESON,1,g', +- '-e', 's,DAX_DNAME,libdaxctl-devel,g', ++ 'sed', '-e', 's,DAX_DNAME,libdaxctl-devel,g', + '-e', 's,CXL_DNAME,libcxl-devel,g', + '-e', 's,DNAME,libndctl-devel,g', + '-e', 's,%license,%doc,g', +diff -up ndctl-71.1/test/Makefile.am /dev/null +--- ndctl-71.1/test/Makefile.am 2022-10-07 18:39:13.146886527 -0400 ++++ /dev/null 2022-07-27 11:30:34.495212588 -0400 +@@ -1,169 +0,0 @@ +-include $(top_srcdir)/Makefile.am.in +- +-TESTS =\ +- libndctl \ +- dsm-fail \ +- create.sh \ +- clear.sh \ +- pmem-errors.sh \ +- daxdev-errors.sh \ +- multi-dax.sh \ +- btt-check.sh \ +- label-compat.sh \ +- sector-mode.sh \ +- inject-error.sh \ +- btt-errors.sh \ +- hugetlb \ +- btt-pad-compat.sh \ +- firmware-update.sh \ +- ack-shutdown-count-set \ +- rescan-partitions.sh \ +- inject-smart.sh \ +- monitor.sh \ +- max_available_extent_ns.sh \ +- pfn-meta-errors.sh \ +- track-uuid.sh +- +-EXTRA_DIST += $(TESTS) common \ +- btt-pad-compat.xxd \ +- nmem1.bin nmem2.bin nmem3.bin nmem4.bin +- +-check_PROGRAMS =\ +- libndctl \ +- dsm-fail \ +- dax-errors \ +- smart-notify \ +- smart-listen \ +- hugetlb \ +- daxdev-errors \ +- ack-shutdown-count-set \ +- list-smart-dimm +- +-if ENABLE_DESTRUCTIVE +-TESTS +=\ +- pmem-ns \ +- sub-section.sh \ +- dax-dev \ +- dax-ext4.sh \ +- dax-xfs.sh \ +- align.sh \ +- device-dax \ +- revoke-devmem \ +- device-dax-fio.sh \ +- daxctl-devices.sh \ +- daxctl-create.sh \ +- dm.sh \ +- mmap.sh +- +-if ENABLE_KEYUTILS +-TESTS += security.sh +-endif +- +-check_PROGRAMS +=\ +- pmem-ns \ +- dax-dev \ +- dax-pmd \ +- device-dax \ +- revoke-devmem \ +- mmap +-endif +- +-LIBNDCTL_LIB =\ +- ../ndctl/lib/libndctl.la \ +- ../daxctl/lib/libdaxctl.la +- +-testcore =\ +- core.c \ +- ../util/log.c \ +- ../util/sysfs.c +- +-libndctl_SOURCES = libndctl.c $(testcore) +-libndctl_LDADD = $(LIBNDCTL_LIB) $(UUID_LIBS) $(KMOD_LIBS) +- +-namespace_core =\ +- ../ndctl/namespace.c \ +- ../ndctl/filter.c \ +- ../ndctl/check.c \ +- ../util/json.c \ +- ../ndctl/json.c \ +- ../daxctl/filter.c \ +- ../daxctl/json.c +- +-dsm_fail_SOURCES =\ +- dsm-fail.c \ +- $(testcore) \ +- $(namespace_core) +- +-dsm_fail_LDADD = $(LIBNDCTL_LIB) \ +- $(KMOD_LIBS) \ +- $(JSON_LIBS) \ +- $(UUID_LIBS) \ +- ../libutil.a +- +-ack_shutdown_count_set_SOURCES =\ +- ack-shutdown-count-set.c \ +- $(testcore) +- +-ack_shutdown_count_set_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS) +- +-pmem_ns_SOURCES = pmem_namespaces.c $(testcore) +-pmem_ns_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS) $(UUID_LIBS) +- +-dax_dev_SOURCES = dax-dev.c $(testcore) +-dax_dev_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS) +- +-dax_pmd_SOURCES = dax-pmd.c \ +- $(testcore) +- +-hugetlb_SOURCES = hugetlb.c \ +- dax-pmd.c +- +-mmap_SOURCES = mmap.c +-dax_errors_SOURCES = dax-errors.c +-daxdev_errors_SOURCES = daxdev-errors.c \ +- ../util/log.c \ +- ../util/sysfs.c +-daxdev_errors_LDADD = $(LIBNDCTL_LIB) +-device_dax_SOURCES = \ +- device-dax.c \ +- dax-dev.c \ +- dax-pmd.c \ +- $(testcore) \ +- $(namespace_core) +- +-if ENABLE_POISON +-dax_pmd_SOURCES += dax-poison.c +-hugetlb_SOURCES += dax-poison.c +-device_dax_SOURCES += dax-poison.c +-endif +- +-device_dax_LDADD = \ +- $(LIBNDCTL_LIB) \ +- $(KMOD_LIBS) \ +- $(JSON_LIBS) \ +- $(UUID_LIBS) \ +- ../libutil.a +- +-revoke_devmem_SOURCES = \ +- revoke-devmem.c \ +- dax-dev.c \ +- $(testcore) +- +-revoke_devmem_LDADD = $(LIBNDCTL_LIB) +- +-smart_notify_SOURCES = smart-notify.c +-smart_notify_LDADD = $(LIBNDCTL_LIB) +-smart_listen_SOURCES = smart-listen.c +-smart_listen_LDADD = $(LIBNDCTL_LIB) +- +-list_smart_dimm_SOURCES = \ +- list-smart-dimm.c \ +- ../ndctl/filter.c \ +- ../util/json.c \ +- ../ndctl/json.c +- +-list_smart_dimm_LDADD = \ +- $(LIBNDCTL_LIB) \ +- $(JSON_LIBS) \ +- $(UUID_LIBS) \ +- ../libutil.a diff --git a/SOURCES/0085-ndctl-build-Default-asciidoctor-to-enabled.patch b/SOURCES/0085-ndctl-build-Default-asciidoctor-to-enabled.patch new file mode 100644 index 0000000..da73810 --- /dev/null +++ b/SOURCES/0085-ndctl-build-Default-asciidoctor-to-enabled.patch @@ -0,0 +1,32 @@ +From 3a8d6e4bc90e899f751b881dc949e79daeeb04bb Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Fri, 7 Jan 2022 11:31:06 -0800 +Subject: [PATCH 085/217] ndctl/build: Default asciidoctor to enabled + +The autotools build infra previously defaulted asciidoctor to enabled, do +the same for Meson. + +Link: https://lore.kernel.org/r/164158386600.302694.5479584050156277551.stgit@dwillia2-desk3.amr.corp.intel.com +Reported-by: Vishal Verma +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + meson_options.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/meson_options.txt b/meson_options.txt +index 95312bf..aa4a6dc 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -1,7 +1,7 @@ + option('version-tag', type : 'string', + description : 'override the git version string') + option('docs', type : 'feature', value : 'enabled') +-option('asciidoctor', type : 'feature', value : 'disabled') ++option('asciidoctor', type : 'feature', value : 'enabled') + option('systemd', type : 'feature', value : 'enabled') + option('keyutils', type : 'feature', value : 'enabled', + description : 'enable nvdimm device passphrase management') +-- +2.27.0 + diff --git a/SOURCES/0086-ndctl-update-README.md-for-meson-build.patch b/SOURCES/0086-ndctl-update-README.md-for-meson-build.patch new file mode 100644 index 0000000..5daeb48 --- /dev/null +++ b/SOURCES/0086-ndctl-update-README.md-for-meson-build.patch @@ -0,0 +1,88 @@ +From 351badda9e5b6454e56f31992e9325c4656680bd Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Fri, 14 Jan 2022 18:32:29 -0700 +Subject: [PATCH 086/217] ndctl: update README.md for meson build + +Update the README to replace the autotools build and test instructions +with meson equivalents. Also provide an example for setting meson +configuration options by illustrating the destructive unit tests use +case. + +Link: https://lore.kernel.org/r/20220115013229.1604139-1-vishal.l.verma@intel.com +Cc: Dan Williams +Reported-by: Alison Schofield +Reported-by: Jane Chu +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + README.md | 33 ++++++++++++++++++++++----------- + 1 file changed, 22 insertions(+), 11 deletions(-) + +diff --git a/README.md b/README.md +index 6f36a6d..f3fe65b 100644 +--- a/README.md ++++ b/README.md +@@ -9,11 +9,14 @@ Build + ===== + + ``` +-./autogen.sh +-./configure CFLAGS='-g -O2' --prefix=/usr --sysconfdir=/etc --libdir=/usr/lib64 +-make +-make check +-sudo make install ++meson setup build; ++meson compile -C build; ++``` ++ ++Optionally, to install: ++ ++``` ++meson install -C build + ``` + + There are a number of packages required for the build steps that may not +@@ -34,7 +37,7 @@ https://nvdimm.wiki.kernel.org/start + + Unit Tests + ========== +-The unit tests run by `make check` require the nfit_test.ko module to be ++The unit tests run by `meson test` require the nfit_test.ko module to be + loaded. To build and install nfit_test.ko: + + 1. Obtain the kernel source. For example, +@@ -78,8 +81,16 @@ loaded. To build and install nfit_test.ko: + sudo make modules_install + ``` + +-1. Now run `make check` in the ndctl source directory, or `ndctl test`, +- if ndctl was built with `--enable-test`. ++1. Now run `meson test -C build` in the ndctl source directory, or `ndctl test`, ++ if ndctl was built with `-Dtest=enabled` as a configuration option to meson. ++ ++1. To run the 'destructive' set of tests that may clobber existing pmem ++ configurations and data, configure meson with the destructive option after the ++ `meson setup` step: ++ ++ ``` ++ meson configure -Dtest=enabled -Ddestructive=enabled build; ++ ``` + + Troubleshooting + =============== +@@ -87,9 +98,9 @@ Troubleshooting + The unit tests will validate that the environment is set up correctly + before they try to run. If the platform is misconfigured, i.e. the unit + test modules are not available, or the test versions of the modules are +-superseded by the "in-tree/production" version of the modules `make +-check` will skip tests and report a message like the following in +-test/test-suite.log: ++superseded by the "in-tree/production" version of the modules `meson ++test` will skip tests and report a message like the following in ++`build/meson-logs/testlog.txt` + + ``` + SKIP: libndctl +-- +2.27.0 + diff --git a/SOURCES/0087-test-Add-suite-identifiers-to-tests.patch b/SOURCES/0087-test-Add-suite-identifiers-to-tests.patch new file mode 100644 index 0000000..685d590 --- /dev/null +++ b/SOURCES/0087-test-Add-suite-identifiers-to-tests.patch @@ -0,0 +1,122 @@ +From a61377ecf015929de27a665d0b5c937315f9e4aa Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:51:54 -0800 +Subject: [PATCH 087/217] test: Add 'suite' identifiers to tests + +In preparation for adding CXL tests, and in anticipation of wanting to only +run the CXL tests, label each test with a suite id. + +Link: https://lore.kernel.org/r/164298551461.3021641.4591877842309963514.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/meson.build | 73 ++++++++++++++++++++++++------------------------ + 1 file changed, 37 insertions(+), 36 deletions(-) + +diff --git a/test/meson.build b/test/meson.build +index 94287aa..07a5bb6 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -152,28 +152,28 @@ pfn_meta_errors = find_program('pfn-meta-errors.sh') + track_uuid = find_program('track-uuid.sh') + + tests = [ +- [ 'libndctl', libndctl ], +- [ 'dsm-fail', dsm_fail ], +- [ 'create.sh', create ], +- [ 'clear.sh', clear ], +- [ 'pmem-errors.sh', pmem_errors ], +- [ 'daxdev-errors.sh', daxdev_errors_sh ], +- [ 'multi-dax.sh', multi_dax ], +- [ 'btt-check.sh', btt_check ], +- [ 'label-compat.sh', label_compat ], +- [ 'sector-mode.sh', sector_mode ], +- [ 'inject-error.sh', inject_error ], +- [ 'btt-errors.sh', btt_errors ], +- [ 'hugetlb', hugetlb ], +- [ 'btt-pad-compat.sh', btt_pad_compat ], +- [ 'firmware-update.sh', firmware_update ], +- [ 'ack-shutdown-count-set', ack_shutdown_count ], +- [ 'rescan-partitions.sh', rescan_partitions ], +- [ 'inject-smart.sh', inject_smart ], +- [ 'monitor.sh', monitor ], +- [ 'max_extent_ns', max_extent ], +- [ 'pfn-meta-errors.sh', pfn_meta_errors ], +- [ 'track-uuid.sh', track_uuid ], ++ [ 'libndctl', libndctl, 'ndctl' ], ++ [ 'dsm-fail', dsm_fail, 'ndctl' ], ++ [ 'create.sh', create, 'ndctl' ], ++ [ 'clear.sh', clear, 'ndctl' ], ++ [ 'pmem-errors.sh', pmem_errors, 'ndctl' ], ++ [ 'daxdev-errors.sh', daxdev_errors_sh, 'dax' ], ++ [ 'multi-dax.sh', multi_dax, 'dax' ], ++ [ 'btt-check.sh', btt_check, 'ndctl' ], ++ [ 'label-compat.sh', label_compat, 'ndctl' ], ++ [ 'sector-mode.sh', sector_mode, 'ndctl' ], ++ [ 'inject-error.sh', inject_error, 'ndctl' ], ++ [ 'btt-errors.sh', btt_errors, 'ndctl' ], ++ [ 'hugetlb', hugetlb, 'ndctl' ], ++ [ 'btt-pad-compat.sh', btt_pad_compat, 'ndctl' ], ++ [ 'firmware-update.sh', firmware_update, 'ndctl' ], ++ [ 'ack-shutdown-count-set', ack_shutdown_count, 'ndctl' ], ++ [ 'rescan-partitions.sh', rescan_partitions, 'ndctl' ], ++ [ 'inject-smart.sh', inject_smart, 'ndctl' ], ++ [ 'monitor.sh', monitor, 'ndctl' ], ++ [ 'max_extent_ns', max_extent, 'ndctl' ], ++ [ 'pfn-meta-errors.sh', pfn_meta_errors, 'ndctl' ], ++ [ 'track-uuid.sh', track_uuid, 'ndctl' ], + ] + + if get_option('destructive').enabled() +@@ -188,26 +188,26 @@ if get_option('destructive').enabled() + mmap_test = find_program('mmap.sh') + + tests += [ +- [ 'pmem-ns', pmem_ns ], +- [ 'sub-section.sh', sub_section ], +- [ 'dax-dev', dax_dev ], +- [ 'dax-ext4.sh', dax_ext4 ], +- [ 'dax-xfs.sh', dax_xfs ], +- [ 'align.sh', align ], +- [ 'device-dax', device_dax ], +- [ 'revoke-devmem', revoke_devmem ], +- [ 'device-dax-fio.sh', device_dax_fio ], +- [ 'daxctl-devices.sh', daxctl_devices ], +- [ 'daxctl-create.sh', daxctl_create ], +- [ 'dm.sh', dm ], +- [ 'mmap.sh', mmap_test ], ++ [ 'pmem-ns', pmem_ns, 'ndctl' ], ++ [ 'sub-section.sh', sub_section, 'dax' ], ++ [ 'dax-dev', dax_dev, 'dax' ], ++ [ 'dax-ext4.sh', dax_ext4, 'dax' ], ++ [ 'dax-xfs.sh', dax_xfs, 'dax' ], ++ [ 'align.sh', align, 'ndctl' ], ++ [ 'device-dax', device_dax, 'dax' ], ++ [ 'revoke-devmem', revoke_devmem, 'dax' ], ++ [ 'device-dax-fio.sh', device_dax_fio, 'dax' ], ++ [ 'daxctl-devices.sh', daxctl_devices, 'dax' ], ++ [ 'daxctl-create.sh', daxctl_create, 'dax' ], ++ [ 'dm.sh', dm, 'dax' ], ++ [ 'mmap.sh', mmap_test, 'dax' ], + ] + endif + + if get_option('keyutils').enabled() + security = find_program('security.sh') + tests += [ +- [ 'security.sh', security ] ++ [ 'security.sh', security, 'ndctl' ] + ] + endif + +@@ -226,6 +226,7 @@ foreach t : tests + dax_dev, + mmap, + ], ++ suite: t[2], + timeout : 0, + env : [ + 'NDCTL=@0@'.format(ndctl_tool.full_path()), +-- +2.27.0 + diff --git a/SOURCES/0088-ndctl-Rename-util_filter-to-ndctl_filter.patch b/SOURCES/0088-ndctl-Rename-util_filter-to-ndctl_filter.patch new file mode 100644 index 0000000..8f05501 --- /dev/null +++ b/SOURCES/0088-ndctl-Rename-util_filter-to-ndctl_filter.patch @@ -0,0 +1,287 @@ +From d7c5fa695a91d66485ca1febd6f29c3a483e20f6 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:00 -0800 +Subject: [PATCH 088/217] ndctl: Rename util_filter to ndctl_filter + +In preparation for introducing a cxl_filter_walk() implementation rename +the current filter_walk infrastructure with an ndctl_ prefix. + +Link: https://lore.kernel.org/r/164298552014.3021641.16369576632179722489.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + ndctl/filter.c | 4 ++-- + ndctl/filter.h | 21 +++++++++++---------- + ndctl/list.c | 14 +++++++------- + ndctl/monitor.c | 12 ++++++------ + test/list-smart-dimm.c | 12 ++++++------ + 5 files changed, 32 insertions(+), 31 deletions(-) + +diff -up ndctl-71.1/ndctl/filter.c.orig ndctl-71.1/ndctl/filter.c +--- ndctl-71.1/ndctl/filter.c.orig 2022-10-07 17:54:40.799788014 -0400 ++++ ndctl-71.1/ndctl/filter.c 2022-10-07 17:54:52.592828166 -0400 +@@ -338,8 +338,8 @@ const char *util_nsmode_name(enum ndctl_ + return modes[mode]; + } + +-int util_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx, +- struct util_filter_params *param) ++int ndctl_filter_walk(struct ndctl_ctx *ctx, struct ndctl_filter_ctx *fctx, ++ struct ndctl_filter_params *param) + { + struct ndctl_bus *bus; + unsigned int type = 0; +diff -up ndctl-71.1/ndctl/filter.h.orig ndctl-71.1/ndctl/filter.h +--- ndctl-71.1/ndctl/filter.h.orig 2022-10-07 17:54:40.799788014 -0400 ++++ ndctl-71.1/ndctl/filter.h 2022-10-07 17:54:52.593828169 -0400 +@@ -31,7 +31,7 @@ const char *util_nsmode_name(enum ndctl_ + + struct json_object; + +-/* json object hierarchy for the util_filter_walk() performed by cmd_list() */ ++/* json object hierarchy for the ndctl_filter_walk() performed by cmd_list() */ + struct list_filter_arg { + struct json_object *jnamespaces; + struct json_object *jregions; +@@ -50,19 +50,20 @@ struct monitor_filter_arg { + }; + + /* +- * struct util_filter_ctx - control and callbacks for util_filter_walk() ++ * struct ndctl_filter_ctx - control and callbacks for ndctl_filter_walk() + * ->filter_bus() and ->filter_region() return bool because the + * child-object filter routines can not be called if the parent context + * is not established. ->filter_dimm() and ->filter_namespace() are leaf + * objects, so no child dependencies to check. + */ +-struct util_filter_ctx { +- bool (*filter_bus)(struct ndctl_bus *bus, struct util_filter_ctx *ctx); +- void (*filter_dimm)(struct ndctl_dimm *dimm, struct util_filter_ctx *ctx); ++struct ndctl_filter_ctx { ++ bool (*filter_bus)(struct ndctl_bus *bus, struct ndctl_filter_ctx *ctx); ++ void (*filter_dimm)(struct ndctl_dimm *dimm, ++ struct ndctl_filter_ctx *ctx); + bool (*filter_region)(struct ndctl_region *region, +- struct util_filter_ctx *ctx); ++ struct ndctl_filter_ctx *ctx); + void (*filter_namespace)(struct ndctl_namespace *ndns, +- struct util_filter_ctx *ctx); ++ struct ndctl_filter_ctx *ctx); + union { + void *arg; + struct list_filter_arg *list; +@@ -70,7 +71,7 @@ struct util_filter_ctx { + }; + }; + +-struct util_filter_params { ++struct ndctl_filter_params { + const char *bus; + const char *region; + const char *type; +@@ -81,6 +82,6 @@ struct util_filter_params { + }; + + struct ndctl_ctx; +-int util_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx, +- struct util_filter_params *param); ++int ndctl_filter_walk(struct ndctl_ctx *ctx, struct ndctl_filter_ctx *fctx, ++ struct ndctl_filter_params *param); + #endif /* _NDCTL_UTIL_FILTER_H_ */ +diff -up ndctl-71.1/ndctl/list.c.orig ndctl-71.1/ndctl/list.c +--- ndctl-71.1/ndctl/list.c.orig 2022-10-07 17:54:40.806788038 -0400 ++++ ndctl-71.1/ndctl/list.c 2022-10-07 17:54:52.593828169 -0400 +@@ -55,7 +55,7 @@ static unsigned long listopts_to_flags(v + return flags; + } + +-static struct util_filter_params param; ++static struct ndctl_filter_params param; + + static int did_fail; + +@@ -234,7 +234,7 @@ static struct json_object *region_to_jso + } + + static void filter_namespace(struct ndctl_namespace *ndns, +- struct util_filter_ctx *ctx) ++ struct ndctl_filter_ctx *ctx) + { + struct json_object *jndns; + struct list_filter_arg *lfa = ctx->list; +@@ -272,7 +272,7 @@ static void filter_namespace(struct ndct + } + + static bool filter_region(struct ndctl_region *region, +- struct util_filter_ctx *ctx) ++ struct ndctl_filter_ctx *ctx) + { + struct list_filter_arg *lfa = ctx->list; + struct json_object *jbus = lfa->jbus; +@@ -318,7 +318,7 @@ static bool filter_region(struct ndctl_r + return true; + } + +-static void filter_dimm(struct ndctl_dimm *dimm, struct util_filter_ctx *ctx) ++static void filter_dimm(struct ndctl_dimm *dimm, struct ndctl_filter_ctx *ctx) + { + struct list_filter_arg *lfa = ctx->list; + struct json_object *jdimm; +@@ -367,7 +367,7 @@ static void filter_dimm(struct ndctl_dim + json_object_array_add(lfa->jdimms, jdimm); + } + +-static bool filter_bus(struct ndctl_bus *bus, struct util_filter_ctx *ctx) ++static bool filter_bus(struct ndctl_bus *bus, struct ndctl_filter_ctx *ctx) + { + struct list_filter_arg *lfa = ctx->list; + +@@ -489,7 +489,7 @@ int cmd_list(int argc, const char **argv + NULL + }; + bool lint = !!secure_getenv("NDCTL_LIST_LINT"); +- struct util_filter_ctx fctx = { 0 }; ++ struct ndctl_filter_ctx fctx = { 0 }; + struct list_filter_arg lfa = { 0 }; + int i, rc; + +@@ -544,7 +544,7 @@ int cmd_list(int argc, const char **argv + fctx.list = &lfa; + lfa.flags = listopts_to_flags(); + +- rc = util_filter_walk(ctx, &fctx, ¶m); ++ rc = ndctl_filter_walk(ctx, &fctx, ¶m); + if (rc) + return rc; + +diff -up ndctl-71.1/ndctl/monitor.c.orig ndctl-71.1/ndctl/monitor.c +--- ndctl-71.1/ndctl/monitor.c.orig 2022-10-07 17:54:40.824788100 -0400 ++++ ndctl-71.1/ndctl/monitor.c 2022-10-07 17:55:08.025880711 -0400 +@@ -48,7 +48,7 @@ struct monitor_dimm { + struct list_node list; + }; + +-static struct util_filter_params param; ++static struct ndctl_filter_params param; + + static int did_fail; + +@@ -263,12 +263,12 @@ out: + } + + static bool filter_region(struct ndctl_region *region, +- struct util_filter_ctx *fctx) ++ struct ndctl_filter_ctx *fctx) + { + return true; + } + +-static void filter_dimm(struct ndctl_dimm *dimm, struct util_filter_ctx *fctx) ++static void filter_dimm(struct ndctl_dimm *dimm, struct ndctl_filter_ctx *fctx) + { + struct monitor_dimm *mdimm; + struct monitor_filter_arg *mfa = fctx->monitor; +@@ -316,7 +316,7 @@ static void filter_dimm(struct ndctl_dim + return; + } + +-static bool filter_bus(struct ndctl_bus *bus, struct util_filter_ctx *fctx) ++static bool filter_bus(struct ndctl_bus *bus, struct ndctl_filter_ctx *fctx) + { + return true; + } +@@ -481,7 +481,7 @@ static void parse_config(const char **ar + } + + static int read_config_file(struct ndctl_ctx *ctx, struct monitor *_monitor, +- struct util_filter_params *_param) ++ struct ndctl_filter_params *_param) + { + FILE *f; + size_t len = 0; +@@ -603,7 +603,7 @@ int cmd_monitor(int argc, const char **a + NULL + }; + const char *prefix = "./"; +- struct util_filter_ctx fctx = { 0 }; ++ struct ndctl_filter_ctx fctx = { 0 }; + struct monitor_filter_arg mfa = { 0 }; + int i, rc; + +@@ -667,7 +667,7 @@ int cmd_monitor(int argc, const char **a + mfa.maxfd_dimm = -1; + mfa.flags = 0; + +- rc = util_filter_walk(ctx, &fctx, ¶m); ++ rc = ndctl_filter_walk(ctx, &fctx, ¶m); + if (rc) + goto out; + +diff -up ndctl-71.1/test/list-smart-dimm.c.orig ndctl-71.1/test/list-smart-dimm.c +--- ndctl-71.1/test/list-smart-dimm.c.orig 2022-10-07 17:54:40.813788062 -0400 ++++ ndctl-71.1/test/list-smart-dimm.c 2022-10-07 17:54:52.595828176 -0400 +@@ -11,7 +11,7 @@ + #include + #include + +-struct util_filter_params param; ++struct ndctl_filter_params param; + static int did_fail; + static int jflag = JSON_C_TO_STRING_PRETTY; + +@@ -23,12 +23,12 @@ do { \ + } while (0) + + static bool filter_region(struct ndctl_region *region, +- struct util_filter_ctx *ctx) ++ struct ndctl_filter_ctx *ctx) + { + return true; + } + +-static void filter_dimm(struct ndctl_dimm *dimm, struct util_filter_ctx *ctx) ++static void filter_dimm(struct ndctl_dimm *dimm, struct ndctl_filter_ctx *ctx) + { + struct list_filter_arg *lfa = ctx->list; + struct json_object *jdimm; +@@ -57,7 +57,7 @@ static void filter_dimm(struct ndctl_dim + json_object_array_add(lfa->jdimms, jdimm); + } + +-static bool filter_bus(struct ndctl_bus *bus, struct util_filter_ctx *ctx) ++static bool filter_bus(struct ndctl_bus *bus, struct ndctl_filter_ctx *ctx) + { + return true; + } +@@ -89,7 +89,7 @@ int main(int argc, const char *argv[]) + "list-smart-dimm []", + NULL + }; +- struct util_filter_ctx fctx = { 0 }; ++ struct ndctl_filter_ctx fctx = { 0 }; + struct list_filter_arg lfa = { 0 }; + + rc = ndctl_new(&ctx); +@@ -108,7 +108,7 @@ int main(int argc, const char *argv[]) + fctx.list = &lfa; + lfa.flags = 0; + +- rc = util_filter_walk(ctx, &fctx, ¶m); ++ rc = ndctl_filter_walk(ctx, &fctx, ¶m); + if (rc) + return rc; + +diff -up ndctl-71.1/util/filter.c.orig ndctl-71.1/util/filter.c +--- ndctl-71.1/util/filter.c.orig 2022-10-07 17:55:48.192017464 -0400 ++++ ndctl-71.1/util/filter.c 2022-10-07 17:55:54.474038852 -0400 +@@ -394,8 +394,8 @@ const char *util_nsmode_name(enum ndctl_ + return modes[mode]; + } + +-int util_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx, +- struct util_filter_params *param) ++int ndctl_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx, ++ struct util_filter_params *param) + { + struct ndctl_bus *bus; + unsigned int type = 0; diff --git a/SOURCES/0089-build-Add-tags.patch b/SOURCES/0089-build-Add-tags.patch new file mode 100644 index 0000000..322fc83 --- /dev/null +++ b/SOURCES/0089-build-Add-tags.patch @@ -0,0 +1,54 @@ +From 6dafb0baf8fda14f25e0a764fe8f89d8a4727b0c Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:05 -0800 +Subject: [PATCH 089/217] build: Add tags + +Copy the systemd approach to generating tags with a file listing from git. + +Link: https://lore.kernel.org/r/164298552547.3021641.2951502977152843738.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + meson.build | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/meson.build b/meson.build +index b22fb2e..68f3d0c 100644 +--- a/meson.build ++++ b/meson.build +@@ -82,6 +82,7 @@ project_source_root = meson.current_source_dir() + # Cleanup the leftover config.h files to avoid conflicts with the meson + # generated config.h + git = find_program('git', required : false) ++env = find_program('env') + if git.found() + run_command('clean_config.sh', + env : 'GIT_DIR=@0@/.git'.format(project_source_root), +@@ -111,6 +112,24 @@ else + ) + endif + ++if git.found() ++ all_files = run_command( ++ env, '-u', 'GIT_WORK_TREE', ++ git, '--git-dir=@0@/.git'.format(project_source_root), ++ 'ls-files', ':/*.[ch]', ++ check : false) ++ if all_files.returncode() == 0 ++ all_files = files(all_files.stdout().split()) ++ custom_target( ++ 'tags', ++ output : 'tags', ++ command : [env, 'etags', '-o', '@0@/TAGS'.format(project_source_root)] + all_files) ++ run_target( ++ 'ctags', ++ command : [env, 'ctags', '-o', '@0@/tags'.format(project_source_root)] + all_files) ++ endif ++endif ++ + versiondep = declare_dependency( + compile_args: ['-include', 'version.h'], + sources: version_h +-- +2.27.0 + diff --git a/SOURCES/0090-json-Add-support-for-json_object_new_uint64.patch b/SOURCES/0090-json-Add-support-for-json_object_new_uint64.patch new file mode 100644 index 0000000..23d8d39 --- /dev/null +++ b/SOURCES/0090-json-Add-support-for-json_object_new_uint64.patch @@ -0,0 +1,194 @@ +From 691cd249750b505753680d2a766280698ce25b75 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:10 -0800 +Subject: [PATCH 090/217] json: Add support for json_object_new_uint64() + +Recent versions of json-c add a proper u64 type. However since ndctl still +needs to build against older json-c add build infrastructure to fallback to +s64. + +Link: https://lore.kernel.org/r/164298553057.3021641.17232869374733997747.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + config.h.meson | 3 +++ + cxl/json.c | 6 +++--- + daxctl/json.c | 2 +- + meson.build | 6 ++++++ + ndctl/dimm.c | 2 +- + ndctl/json.c | 10 +++++----- + util/json.c | 2 +- + util/json.h | 13 ++++++++++++- + 8 files changed, 32 insertions(+), 12 deletions(-) + +diff -up ndctl-71.1/config.h.meson.orig ndctl-71.1/config.h.meson +--- ndctl-71.1/config.h.meson.orig 2022-10-07 17:40:36.698914113 -0400 ++++ ndctl-71.1/config.h.meson 2022-10-07 17:41:24.043075305 -0400 +@@ -88,6 +88,9 @@ + /* Define to 1 if you have the `__secure_getenv' function. */ + #mesondefine HAVE___SECURE_GETENV + ++/* Define to 1 if you have json_object_new_uint64 in json-c */ ++#mesondefine HAVE_JSON_U64 ++ + /* Define to the sub-directory where libtool stores uninstalled libraries. */ + #mesondefine LT_OBJDIR + +diff -up ndctl-71.1/cxl/json.c.orig ndctl-71.1/cxl/json.c +--- ndctl-71.1/cxl/json.c.orig 2022-10-07 17:40:36.668914011 -0400 ++++ ndctl-71.1/cxl/json.c 2022-10-07 17:41:24.043075305 -0400 +@@ -159,17 +159,17 @@ static struct json_object *util_cxl_memd + } + + field = cxl_cmd_health_info_get_dirty_shutdowns(cmd); +- jobj = json_object_new_int64(field); ++ jobj = util_json_new_u64(field); + if (jobj) + json_object_object_add(jhealth, "dirty_shutdowns", jobj); + + field = cxl_cmd_health_info_get_volatile_errors(cmd); +- jobj = json_object_new_int64(field); ++ jobj = util_json_new_u64(field); + if (jobj) + json_object_object_add(jhealth, "volatile_errors", jobj); + + field = cxl_cmd_health_info_get_pmem_errors(cmd); +- jobj = json_object_new_int64(field); ++ jobj = util_json_new_u64(field); + if (jobj) + json_object_object_add(jhealth, "pmem_errors", jobj); + +diff -up ndctl-71.1/daxctl/json.c.orig ndctl-71.1/daxctl/json.c +--- ndctl-71.1/daxctl/json.c.orig 2022-10-07 17:40:36.671914021 -0400 ++++ ndctl-71.1/daxctl/json.c 2022-10-07 17:41:24.043075305 -0400 +@@ -190,7 +190,7 @@ struct json_object *util_daxctl_region_t + + align = daxctl_region_get_align(region); + if (align < ULONG_MAX) { +- jobj = json_object_new_int64(align); ++ jobj = util_json_new_u64(align); + if (!jobj) + goto err; + json_object_object_add(jregion, "align", jobj); +diff -up ndctl-71.1/meson.build.orig ndctl-71.1/meson.build +--- ndctl-71.1/meson.build.orig 2022-10-07 17:40:36.720914188 -0400 ++++ ndctl-71.1/meson.build 2022-10-07 17:41:24.044075308 -0400 +@@ -240,6 +240,12 @@ foreach ident : ['secure_getenv', '__sec + conf.set10('HAVE_' + ident.to_upper(), cc.has_function(ident)) + endforeach + ++conf.set10('HAVE_JSON_U64', ++ cc.has_function('json_object_new_uint64', ++ prefix : '''#include ''', ++ dependencies : json, ++ ) ++) + + ndctlconf_dir = sysconfdir / 'ndctl' + ndctlconf = ndctlconf_dir / 'monitor.conf' +diff -up ndctl-71.1/ndctl/dimm.c.orig ndctl-71.1/ndctl/dimm.c +--- ndctl-71.1/ndctl/dimm.c.orig 2022-10-07 17:40:36.673914028 -0400 ++++ ndctl-71.1/ndctl/dimm.c 2022-10-07 17:41:24.044075308 -0400 +@@ -168,7 +168,7 @@ static struct json_object *dump_label_js + break; + json_object_object_add(jlabel, "isetcookie", jobj); + +- jobj = json_object_new_int64(le64_to_cpu(nslabel.lbasize)); ++ jobj = util_json_new_u64(le64_to_cpu(nslabel.lbasize)); + if (!jobj) + break; + json_object_object_add(jlabel, "lbasize", jobj); +diff -up ndctl-71.1/ndctl/json.c.orig ndctl-71.1/ndctl/json.c +--- ndctl-71.1/ndctl/json.c.orig 2022-10-07 17:40:36.675914034 -0400 ++++ ndctl-71.1/ndctl/json.c 2022-10-07 17:41:24.044075308 -0400 +@@ -357,7 +357,7 @@ static struct json_object *util_##type## + int64_t align; \ + \ + align = get_elem(arg, i); \ +- jobj = json_object_new_int64(align); \ ++ jobj = util_json_new_u64(align); \ + if (!jobj) \ + goto err; \ + json_object_array_add(arr, jobj); \ +@@ -550,7 +550,7 @@ struct json_object *util_region_badblock + if (!jbb) + goto err_array; + +- jobj = json_object_new_int64(bb->offset); ++ jobj = util_json_new_u64(bb->offset); + if (!jobj) + goto err; + json_object_object_add(jbb, "offset", jobj); +@@ -604,7 +604,7 @@ static struct json_object *util_namespac + if (!jbb) + goto err_array; + +- jobj = json_object_new_int64(bb->offset); ++ jobj = util_json_new_u64(bb->offset); + if (!jobj) + goto err; + json_object_object_add(jbb, "offset", jobj); +@@ -682,7 +682,7 @@ static struct json_object *dev_badblocks + if (!jbb) + goto err_array; + +- jobj = json_object_new_int64(offset); ++ jobj = util_json_new_u64(offset); + if (!jobj) + goto err; + json_object_object_add(jbb, "offset", jobj); +@@ -972,7 +972,7 @@ struct json_object *util_namespace_to_js + } + + if (align) { +- jobj = json_object_new_int64(align); ++ jobj = util_json_new_u64(align); + if (!jobj) + goto err; + json_object_object_add(jndns, "align", jobj); +diff -up ndctl-71.1/util/json.c.orig ndctl-71.1/util/json.c +--- ndctl-71.1/util/json.c.orig 2022-10-07 17:40:36.682914058 -0400 ++++ ndctl-71.1/util/json.c 2022-10-07 17:41:24.045075312 -0400 +@@ -82,7 +82,7 @@ struct json_object *util_json_object_siz + struct json_object *util_json_object_hex(unsigned long long val, + unsigned long flags) + { +- struct json_object *jobj = json_object_new_int64(val); ++ struct json_object *jobj = util_json_new_u64(val); + + if (jobj && (flags & UTIL_JSON_HUMAN)) + json_object_set_serializer(jobj, display_hex, NULL, NULL); +diff -up ndctl-71.1/util/json.h.orig ndctl-71.1/util/json.h +--- ndctl-71.1/util/json.h.orig 2022-10-07 17:40:36.683914062 -0400 ++++ ndctl-71.1/util/json.h 2022-10-07 17:41:24.046075315 -0400 +@@ -4,6 +4,7 @@ + #define __UTIL_JSON_H__ + #include + #include ++#include + + enum util_json_flags { + UTIL_JSON_IDLE = (1 << 0), +@@ -19,11 +20,21 @@ enum util_json_flags { + UTIL_JSON_HEALTH = (1 << 10), + }; + +-struct json_object; + void util_display_json_array(FILE *f_out, struct json_object *jarray, + unsigned long flags); + struct json_object *util_json_object_size(unsigned long long size, + unsigned long flags); + struct json_object *util_json_object_hex(unsigned long long val, + unsigned long flags); ++#if HAVE_JSON_U64 ++static inline struct json_object *util_json_new_u64(unsigned long long val) ++{ ++ return json_object_new_uint64(val); ++} ++#else /* fallback to signed */ ++static inline struct json_object *util_json_new_u64(unsigned long long val) ++{ ++ return json_object_new_int64(val); ++} ++#endif /* HAVE_JSON_U64 */ + #endif /* __UTIL_JSON_H__ */ diff --git a/SOURCES/0091-cxl-json-Cleanup-object-leak-false-positive.patch b/SOURCES/0091-cxl-json-Cleanup-object-leak-false-positive.patch new file mode 100644 index 0000000..d48fa1a --- /dev/null +++ b/SOURCES/0091-cxl-json-Cleanup-object-leak-false-positive.patch @@ -0,0 +1,32 @@ +From 8f457dc414ec27178828c86533910958542ce73d Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:15 -0800 +Subject: [PATCH 091/217] cxl/json: Cleanup object leak false positive + +As written it is a leak of the json object to return if devname is NULL. +However, the devname can not be NULL because the memdev would not have been +enumerated. Drop the error checking. + +Link: https://lore.kernel.org/r/164298553566.3021641.11858634436119663877.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/json.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/cxl/json.c b/cxl/json.c +index 97ed76b..3ef9f76 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -190,7 +190,7 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + struct json_object *jdev, *jobj; + + jdev = json_object_new_object(); +- if (!devname || !jdev) ++ if (!jdev) + return NULL; + + jobj = json_object_new_string(devname); +-- +2.27.0 + diff --git a/SOURCES/0092-cxl-list-Support-multiple-memdev-device-name-filter-.patch b/SOURCES/0092-cxl-list-Support-multiple-memdev-device-name-filter-.patch new file mode 100644 index 0000000..b9680b2 --- /dev/null +++ b/SOURCES/0092-cxl-list-Support-multiple-memdev-device-name-filter-.patch @@ -0,0 +1,118 @@ +From 0ce0152d8e29f85325a3a59f94051228540abf6a Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:20 -0800 +Subject: [PATCH 092/217] cxl/list: Support multiple memdev device name filter + arguments + +Similar to 'ndctl list', allow for a syntax like: + + cxl list -m "$(seq -s ' ' 2 5)" + +...to filter the output to just those 4 memdevs. + +Link: https://lore.kernel.org/r/164298554075.3021641.17678360870961637912.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 21 ++++++++++++++++++- + cxl/filter.c | 38 ++++++++++++++++++++++++---------- + 2 files changed, 47 insertions(+), 12 deletions(-) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index c8d10fb..686e0ea 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -30,7 +30,7 @@ OPTIONS + ------- + -m:: + --memdev=:: +- Specify a cxl memory device name to filter the listing. For example: ++ Specify CXL memory device name(s), or device id(s), to filter the listing. For example: + ---- + # cxl list --memdev=mem0 + { +@@ -38,6 +38,25 @@ OPTIONS + "pmem_size":268435456, + "ram_size":0, + } ++ ++# cxl list -m "0 mem1 2" ++[ ++ { ++ "memdev":"mem0", ++ "pmem_size":268435456, ++ "ram_size":0 ++ }, ++ { ++ "memdev":"mem2", ++ "pmem_size":268435456, ++ "ram_size":268435456 ++ }, ++ { ++ "memdev":"mem1", ++ "pmem_size":268435456, ++ "ram_size":268435456 ++ } ++] + ---- + + -M:: +diff --git a/cxl/filter.c b/cxl/filter.c +index 21322ed..efafaf5 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -2,24 +2,40 @@ + // Copyright (C) 2015-2020 Intel Corporation. All rights reserved. + #include + #include ++#include + #include + #include "filter.h" + + struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, +- const char *ident) ++ const char *__ident) + { +- int memdev_id; ++ char *ident, *save; ++ const char *name; ++ int memdev_id; + +- if (!ident || strcmp(ident, "all") == 0) +- return memdev; ++ if (!__ident) ++ return memdev; + +- if (strcmp(ident, cxl_memdev_get_devname(memdev)) == 0) +- return memdev; ++ ident = strdup(__ident); ++ if (!ident) ++ return NULL; + +- if ((sscanf(ident, "%d", &memdev_id) == 1 +- || sscanf(ident, "mem%d", &memdev_id) == 1) +- && cxl_memdev_get_id(memdev) == memdev_id) +- return memdev; ++ for (name = strtok_r(ident, " ", &save); name; ++ name = strtok_r(NULL, " ", &save)) { ++ if (strcmp(name, "all") == 0) ++ break; + +- return NULL; ++ if ((sscanf(name, "%d", &memdev_id) == 1 || ++ sscanf(name, "mem%d", &memdev_id) == 1) && ++ cxl_memdev_get_id(memdev) == memdev_id) ++ break; ++ ++ if (strcmp(name, cxl_memdev_get_devname(memdev)) == 0) ++ break; ++ } ++ ++ free(ident); ++ if (name) ++ return memdev; ++ return NULL; + } +-- +2.27.0 + diff --git a/SOURCES/0093-cxl-list-Support-comma-separated-lists.patch b/SOURCES/0093-cxl-list-Support-comma-separated-lists.patch new file mode 100644 index 0000000..6b55952 --- /dev/null +++ b/SOURCES/0093-cxl-list-Support-comma-separated-lists.patch @@ -0,0 +1,55 @@ +From a36b8b815d2e8bfd8438b44d4775bdf3ffc3a6d8 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:26 -0800 +Subject: [PATCH 093/217] cxl/list: Support comma separated lists + +In addition to supporting a syntax like: + + cxl list -m "0 1 2" + +...support: + + cxl list -m 0,1,2 + +Link: https://lore.kernel.org/r/164298554612.3021641.3315920699556984273.stgit@dwillia2-desk3.amr.corp.intel.com +Reported-by: Vishal Verma +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/filter.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/cxl/filter.c b/cxl/filter.c +index efafaf5..405b653 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -6,6 +6,15 @@ + #include + #include "filter.h" + ++static const char *which_sep(const char *filter) ++{ ++ if (strchr(filter, ' ')) ++ return " "; ++ if (strchr(filter, ',')) ++ return ","; ++ return " "; ++} ++ + struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + const char *__ident) + { +@@ -20,8 +29,8 @@ struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + if (!ident) + return NULL; + +- for (name = strtok_r(ident, " ", &save); name; +- name = strtok_r(NULL, " ", &save)) { ++ for (name = strtok_r(ident, which_sep(__ident), &save); name; ++ name = strtok_r(NULL, which_sep(__ident), &save)) { + if (strcmp(name, "all") == 0) + break; + +-- +2.27.0 + diff --git a/SOURCES/0094-cxl-list-Introduce-cxl_filter_walk.patch b/SOURCES/0094-cxl-list-Introduce-cxl_filter_walk.patch new file mode 100644 index 0000000..b3aec86 --- /dev/null +++ b/SOURCES/0094-cxl-list-Introduce-cxl_filter_walk.patch @@ -0,0 +1,292 @@ +From f833845ce72490e4c80b3ccc9972d5329f69a381 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:31 -0800 +Subject: [PATCH 094/217] cxl/list: Introduce cxl_filter_walk() + +In preparation for introducing more objects and filtering options for 'cxl +list' introduce cxl_filter_walk() to centralize CXL topology walks. It +fills the same role as ndctl_filter_walk() as a way to distribute topology +interrogation beyond 'cxl list' to other commands, and serve as the +template for CXL object hierarchy in JSON output payloads. + +Use the common dbg() logger for log messages. + +Link: https://lore.kernel.org/r/164298555121.3021641.16127840206319352254.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 2 + + cxl/filter.c | 50 ++++++++++++++++ + cxl/filter.h | 18 +++++- + cxl/list.c | 102 +++++++-------------------------- + cxl/meson.build | 1 + + 5 files changed, 90 insertions(+), 83 deletions(-) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 686e0ea..4d409ba 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -15,6 +15,8 @@ SYNOPSIS + Walk the CXL capable device hierarchy in the system and list all device + instances along with some of their major attributes. + ++Options can be specified to limit the output to specific objects. ++ + EXAMPLE + ------- + ---- +diff --git a/cxl/filter.c b/cxl/filter.c +index 405b653..d1ff4b6 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -1,10 +1,16 @@ + // SPDX-License-Identifier: GPL-2.0 + // Copyright (C) 2015-2020 Intel Corporation. All rights reserved. ++#include + #include + #include + #include ++#include ++#include + #include ++#include ++ + #include "filter.h" ++#include "json.h" + + static const char *which_sep(const char *filter) + { +@@ -48,3 +54,47 @@ struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + return memdev; + return NULL; + } ++ ++static unsigned long params_to_flags(struct cxl_filter_params *param) ++{ ++ unsigned long flags = 0; ++ ++ if (param->idle) ++ flags |= UTIL_JSON_IDLE; ++ if (param->human) ++ flags |= UTIL_JSON_HUMAN; ++ if (param->health) ++ flags |= UTIL_JSON_HEALTH; ++ return flags; ++} ++ ++int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) ++{ ++ struct json_object *jplatform = json_object_new_array(); ++ unsigned long flags = params_to_flags(p); ++ struct cxl_memdev *memdev; ++ ++ if (!jplatform) { ++ dbg(p, "platform object allocation failure\n"); ++ return -ENOMEM; ++ } ++ ++ cxl_memdev_foreach(ctx, memdev) { ++ struct json_object *jdev; ++ ++ if (!util_cxl_memdev_filter(memdev, p->memdev_filter)) ++ continue; ++ if (p->memdevs) { ++ jdev = util_cxl_memdev_to_json(memdev, flags); ++ if (!jdev) { ++ dbg(p, "memdev object allocation failure\n"); ++ continue; ++ } ++ json_object_array_add(jplatform, jdev); ++ } ++ } ++ ++ util_display_json_array(stdout, jplatform, flags); ++ ++ return 0; ++} +diff --git a/cxl/filter.h b/cxl/filter.h +index da80033..664b74b 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -1,7 +1,21 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ ++/* Copyright (C) 2021 Intel Corporation. All rights reserved. */ + #ifndef _CXL_UTIL_FILTER_H_ + #define _CXL_UTIL_FILTER_H_ ++ ++#include ++#include ++ ++struct cxl_filter_params { ++ const char *memdev_filter; ++ bool memdevs; ++ bool idle; ++ bool human; ++ bool health; ++ struct log_ctx ctx; ++}; ++ + struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, +- const char *ident); ++ const char *ident); ++int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *param); + #endif /* _CXL_UTIL_FILTER_H_ */ +diff --git a/cxl/list.c b/cxl/list.c +index 7f7a04d..1730307 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -9,60 +9,27 @@ + #include + #include + #include +-#include + +-#include "json.h" + #include "filter.h" + +-static struct { +- bool memdevs; +- bool idle; +- bool human; +- bool health; +-} list; +- +-static unsigned long listopts_to_flags(void) +-{ +- unsigned long flags = 0; +- +- if (list.idle) +- flags |= UTIL_JSON_IDLE; +- if (list.human) +- flags |= UTIL_JSON_HUMAN; +- if (list.health) +- flags |= UTIL_JSON_HEALTH; +- return flags; +-} +- +-static struct { +- const char *memdev; +-} param; +- +-static int did_fail; +- +-#define fail(fmt, ...) \ +-do { \ +- did_fail = 1; \ +- fprintf(stderr, "cxl-%s:%s:%d: " fmt, \ +- VERSION, __func__, __LINE__, ##__VA_ARGS__); \ +-} while (0) ++static struct cxl_filter_params param; + + static int num_list_flags(void) + { +- return list.memdevs; ++ return param.memdevs; + } + + int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + { + const struct option options[] = { +- OPT_STRING('m', "memdev", ¶m.memdev, "memory device name", ++ OPT_STRING('m', "memdev", ¶m.memdev_filter, "memory device name", + "filter by CXL memory device name"), +- OPT_BOOLEAN('M', "memdevs", &list.memdevs, ++ OPT_BOOLEAN('M', "memdevs", ¶m.memdevs, + "include CXL memory device info"), +- OPT_BOOLEAN('i', "idle", &list.idle, "include idle devices"), +- OPT_BOOLEAN('u', "human", &list.human, ++ OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), ++ OPT_BOOLEAN('u', "human", ¶m.human, + "use human friendly number formats "), +- OPT_BOOLEAN('H', "health", &list.health, ++ OPT_BOOLEAN('H', "health", ¶m.health, + "include memory device health information "), + OPT_END(), + }; +@@ -70,9 +37,6 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + "cxl list []", + NULL + }; +- struct json_object *jdevs = NULL; +- unsigned long list_flags; +- struct cxl_memdev *memdev; + int i; + + argc = parse_options(argc, argv, options, u, 0); +@@ -83,46 +47,22 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + usage_with_options(u, options); + + if (num_list_flags() == 0) { +- /* +- * TODO: We likely want to list regions by default if nothing +- * was explicitly asked for. But until we have region support, +- * print this error asking for devices explicitly. +- * Once region support is added, this TODO can be removed. +- */ +- error("please specify entities to list, e.g. using -m/-M\n"); +- usage_with_options(u, options); +- } +- +- list_flags = listopts_to_flags(); +- +- cxl_memdev_foreach(ctx, memdev) { +- struct json_object *jdev = NULL; +- +- if (!util_cxl_memdev_filter(memdev, param.memdev)) +- continue; +- +- if (list.memdevs) { +- if (!jdevs) { +- jdevs = json_object_new_array(); +- if (!jdevs) { +- fail("\n"); +- continue; +- } +- } +- +- jdev = util_cxl_memdev_to_json(memdev, list_flags); +- if (!jdev) { +- fail("\n"); +- continue; +- } +- json_object_array_add(jdevs, jdev); ++ if (param.memdev_filter) ++ param.memdevs = true; ++ else { ++ /* ++ * TODO: We likely want to list regions by default if ++ * nothing was explicitly asked for. But until we have ++ * region support, print this error asking for devices ++ * explicitly. Once region support is added, this TODO ++ * can be removed. ++ */ ++ error("please specify entities to list, e.g. using -m/-M\n"); ++ usage_with_options(u, options); + } + } + +- if (jdevs) +- util_display_json_array(stdout, jdevs, list_flags); ++ log_init(¶m.ctx, "cxl list", "CXL_LIST_LOG"); + +- if (did_fail) +- return -ENOMEM; +- return 0; ++ return cxl_filter_walk(ctx, ¶m); + } +diff --git a/cxl/meson.build b/cxl/meson.build +index 805924b..fc7ee71 100644 +--- a/cxl/meson.build ++++ b/cxl/meson.build +@@ -3,6 +3,7 @@ cxl_src = [ + 'list.c', + 'memdev.c', + '../util/json.c', ++ '../util/log.c', + 'json.c', + 'filter.c', + ] +-- +2.27.0 + diff --git a/SOURCES/0095-cxl-list-Emit-device-serial-numbers.patch b/SOURCES/0095-cxl-list-Emit-device-serial-numbers.patch new file mode 100644 index 0000000..78899ef --- /dev/null +++ b/SOURCES/0095-cxl-list-Emit-device-serial-numbers.patch @@ -0,0 +1,159 @@ +From 2d1b8cea119ca2bb0eec8ebb2dfb1b6c4d844ddd Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:36 -0800 +Subject: [PATCH 095/217] cxl/list: Emit device serial numbers + +Starting with the v5.17 kernel the CXL driver emits the mandatory device +serial number for each memory device. Include it in the memory device +listing. + +Link: https://lore.kernel.org/r/164298555630.3021641.3246226448369816200.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 15 +++++++++------ + cxl/json.c | 11 ++++++++++- + cxl/lib/libcxl.c | 11 +++++++++++ + cxl/lib/libcxl.sym | 5 +++++ + cxl/lib/private.h | 1 + + cxl/libcxl.h | 1 + + 6 files changed, 37 insertions(+), 7 deletions(-) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 4d409ba..bd0207e 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -41,22 +41,25 @@ OPTIONS + "ram_size":0, + } + +-# cxl list -m "0 mem1 2" ++# cxl list -M --memdev="0 mem3 5" + [ + { + "memdev":"mem0", + "pmem_size":268435456, +- "ram_size":0 ++ "ram_size":0, ++ "serial":0 + }, + { +- "memdev":"mem2", ++ "memdev":"mem3", + "pmem_size":268435456, +- "ram_size":268435456 ++ "ram_size":268435456, ++ "serial":2 + }, + { +- "memdev":"mem1", ++ "memdev":"mem5", + "pmem_size":268435456, +- "ram_size":268435456 ++ "ram_size":268435456, ++ "serial":4 + } + ] + ---- +diff --git a/cxl/json.c b/cxl/json.c +index 3ef9f76..d8e65df 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -1,5 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. ++// Copyright (C) 2015-2021 Intel Corporation. All rights reserved. ++#include + #include + #include + #include +@@ -188,6 +189,7 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + { + const char *devname = cxl_memdev_get_devname(memdev); + struct json_object *jdev, *jobj; ++ unsigned long long serial; + + jdev = json_object_new_object(); + if (!jdev) +@@ -210,5 +212,12 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + if (jobj) + json_object_object_add(jdev, "health", jobj); + } ++ ++ serial = cxl_memdev_get_serial(memdev); ++ if (serial < ULLONG_MAX) { ++ jobj = util_json_object_hex(serial, flags); ++ if (jobj) ++ json_object_object_add(jdev, "serial", jobj); ++ } + return jdev; + } +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 3390eb9..8d3cf80 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -296,6 +296,12 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + if (memdev->lsa_size == ULLONG_MAX) + goto err_read; + ++ sprintf(path, "%s/serial", cxlmem_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ memdev->serial = ULLONG_MAX; ++ else ++ memdev->serial = strtoull(buf, NULL, 0); ++ + memdev->dev_path = strdup(cxlmem_base); + if (!memdev->dev_path) + goto err_read; +@@ -371,6 +377,11 @@ CXL_EXPORT int cxl_memdev_get_id(struct cxl_memdev *memdev) + return memdev->id; + } + ++CXL_EXPORT unsigned long long cxl_memdev_get_serial(struct cxl_memdev *memdev) ++{ ++ return memdev->serial; ++} ++ + CXL_EXPORT const char *cxl_memdev_get_devname(struct cxl_memdev *memdev) + { + return devpath_to_devname(memdev->dev_path); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 077d104..4411035 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -73,3 +73,8 @@ global: + local: + *; + }; ++ ++LIBCXL_2 { ++global: ++ cxl_memdev_get_serial; ++} LIBCXL_1; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index a1b8b50..28f7e16 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -31,6 +31,7 @@ struct cxl_memdev { + size_t lsa_size; + struct kmod_module *module; + struct cxl_nvdimm_bridge *bridge; ++ unsigned long long serial; + }; + + enum cxl_cmd_query_status { +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 89d35ba..bcdede8 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -35,6 +35,7 @@ struct cxl_memdev; + struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx); + struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev); + int cxl_memdev_get_id(struct cxl_memdev *memdev); ++unsigned long long cxl_memdev_get_serial(struct cxl_memdev *memdev); + const char *cxl_memdev_get_devname(struct cxl_memdev *memdev); + int cxl_memdev_get_major(struct cxl_memdev *memdev); + int cxl_memdev_get_minor(struct cxl_memdev *memdev); +-- +2.27.0 + diff --git a/SOURCES/0096-cxl-list-Add-filter-by-serial-support.patch b/SOURCES/0096-cxl-list-Add-filter-by-serial-support.patch new file mode 100644 index 0000000..c11c22d --- /dev/null +++ b/SOURCES/0096-cxl-list-Add-filter-by-serial-support.patch @@ -0,0 +1,163 @@ +From d7854adcd1e517d2372ec51f4a1ede2d549975e8 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:41 -0800 +Subject: [PATCH 096/217] cxl/list: Add filter by serial support + +Given that serial numbers are intended to be unique device identifiers, +enable them as a memdev filter option. + +Link: https://lore.kernel.org/r/164298556167.3021641.5470955268978068465.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 4 ++++ + cxl/filter.c | 38 ++++++++++++++++++++++++++++++---- + cxl/filter.h | 4 +++- + cxl/list.c | 4 +++- + cxl/memdev.c | 2 +- + 5 files changed, 45 insertions(+), 7 deletions(-) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index bd0207e..224c972 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -64,6 +64,10 @@ OPTIONS + ] + ---- + ++-s:: ++--serial=:: ++ Specify CXL memory device serial number(s) to filter the listing ++ + -M:: + --memdevs:: + Include CXL memory devices in the listing +diff --git a/cxl/filter.c b/cxl/filter.c +index d1ff4b6..26efc65 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -21,15 +21,45 @@ static const char *which_sep(const char *filter) + return " "; + } + ++static struct cxl_memdev * ++util_cxl_memdev_serial_filter(struct cxl_memdev *memdev, const char *__serials) ++{ ++ unsigned long long serial = 0; ++ char *serials, *save, *end; ++ const char *arg; ++ ++ if (!__serials) ++ return memdev; ++ ++ serials = strdup(__serials); ++ if (!serials) ++ return NULL; ++ ++ for (arg = strtok_r(serials, which_sep(__serials), &save); arg; ++ arg = strtok_r(NULL, which_sep(__serials), &save)) { ++ serial = strtoull(arg, &end, 0); ++ if (!arg[0] || end[0] != 0) ++ continue; ++ if (cxl_memdev_get_serial(memdev) == serial) ++ break; ++ } ++ ++ free(serials); ++ if (arg) ++ return memdev; ++ return NULL; ++} ++ + struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, +- const char *__ident) ++ const char *__ident, ++ const char *serials) + { + char *ident, *save; + const char *name; + int memdev_id; + + if (!__ident) +- return memdev; ++ return util_cxl_memdev_serial_filter(memdev, serials); + + ident = strdup(__ident); + if (!ident) +@@ -51,7 +81,7 @@ struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + + free(ident); + if (name) +- return memdev; ++ return util_cxl_memdev_serial_filter(memdev, serials); + return NULL; + } + +@@ -82,7 +112,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + cxl_memdev_foreach(ctx, memdev) { + struct json_object *jdev; + +- if (!util_cxl_memdev_filter(memdev, p->memdev_filter)) ++ if (!util_cxl_memdev_filter(memdev, p->memdev_filter, p->serial_filter)) + continue; + if (p->memdevs) { + jdev = util_cxl_memdev_to_json(memdev, flags); +diff --git a/cxl/filter.h b/cxl/filter.h +index 664b74b..12d9344 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -8,6 +8,7 @@ + + struct cxl_filter_params { + const char *memdev_filter; ++ const char *serial_filter; + bool memdevs; + bool idle; + bool human; +@@ -16,6 +17,7 @@ struct cxl_filter_params { + }; + + struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, +- const char *ident); ++ const char *__ident, ++ const char *serials); + int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *param); + #endif /* _CXL_UTIL_FILTER_H_ */ +diff --git a/cxl/list.c b/cxl/list.c +index 1730307..6bc48df 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -24,6 +24,8 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + const struct option options[] = { + OPT_STRING('m', "memdev", ¶m.memdev_filter, "memory device name", + "filter by CXL memory device name"), ++ OPT_STRING('s', "serial", ¶m.serial_filter, "memory device serial", ++ "filter by CXL memory device serial number"), + OPT_BOOLEAN('M', "memdevs", ¶m.memdevs, + "include CXL memory device info"), + OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), +@@ -47,7 +49,7 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + usage_with_options(u, options); + + if (num_list_flags() == 0) { +- if (param.memdev_filter) ++ if (param.memdev_filter || param.serial_filter) + param.memdevs = true; + else { + /* +diff --git a/cxl/memdev.c b/cxl/memdev.c +index d063d51..b9141be 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -248,7 +248,7 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + continue; + + cxl_memdev_foreach (ctx, memdev) { +- if (!util_cxl_memdev_filter(memdev, argv[i])) ++ if (!util_cxl_memdev_filter(memdev, argv[i], NULL)) + continue; + + if (action == action_write) { +-- +2.27.0 + diff --git a/SOURCES/0097-cxl-lib-Rename-nvdimm-bridge-to-pmem.patch b/SOURCES/0097-cxl-lib-Rename-nvdimm-bridge-to-pmem.patch new file mode 100644 index 0000000..f9431b1 --- /dev/null +++ b/SOURCES/0097-cxl-lib-Rename-nvdimm-bridge-to-pmem.patch @@ -0,0 +1,164 @@ +From 0be46d9c6638903978d16388c765a1907d5970bc Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:47 -0800 +Subject: [PATCH 097/217] cxl/lib: Rename nvdimm bridge to pmem + +The kernel has 2 object classes for connecting CXL to NVDIMM. There is an +'nvdimm-bridge' object (one per root CXL port) that represents a CXL NVDIMM +Bus, and there are 'pmem' object that represent CXL NVDIMM DIMM devices. +The object that the library is currently calling an nvdimm-bridge is +actually the 'pmem' object. Rename accordingly. + +The exported function cxl_memdev_nvdimm_bridge_active() is not renamed, but +since it is a cxl_memdev operation and 'struct cxl_pmem' is an +implementation detail it is fine as is. + +Link: https://lore.kernel.org/r/164298556712.3021641.15612755067301105130.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 56 +++++++++++++++++++++++------------------------ + cxl/lib/private.h | 4 ++-- + 2 files changed, 30 insertions(+), 30 deletions(-) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 8d3cf80..9839f26 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -45,11 +45,11 @@ struct cxl_ctx { + void *private_data; + }; + +-static void free_bridge(struct cxl_nvdimm_bridge *bridge) ++static void free_pmem(struct cxl_pmem *pmem) + { +- free(bridge->dev_buf); +- free(bridge->dev_path); +- free(bridge); ++ free(pmem->dev_buf); ++ free(pmem->dev_path); ++ free(pmem); + } + + static void free_memdev(struct cxl_memdev *memdev, struct list_head *head) +@@ -57,7 +57,7 @@ static void free_memdev(struct cxl_memdev *memdev, struct list_head *head) + if (head) + list_del_from(head, &memdev->list); + kmod_module_unref(memdev->module); +- free_bridge(memdev->bridge); ++ free_pmem(memdev->pmem); + free(memdev->firmware_version); + free(memdev->dev_buf); + free(memdev->dev_path); +@@ -213,36 +213,36 @@ CXL_EXPORT void cxl_set_log_priority(struct cxl_ctx *ctx, int priority) + ctx->ctx.log_priority = priority; + } + +-static void *add_cxl_bridge(void *parent, int id, const char *br_base) ++static void *add_cxl_pmem(void *parent, int id, const char *br_base) + { + const char *devname = devpath_to_devname(br_base); + struct cxl_memdev *memdev = parent; + struct cxl_ctx *ctx = memdev->ctx; +- struct cxl_nvdimm_bridge *bridge; ++ struct cxl_pmem *pmem; + +- dbg(ctx, "%s: bridge_base: \'%s\'\n", devname, br_base); ++ dbg(ctx, "%s: pmem_base: \'%s\'\n", devname, br_base); + +- bridge = calloc(1, sizeof(*bridge)); +- if (!bridge) ++ pmem = calloc(1, sizeof(*pmem)); ++ if (!pmem) + goto err_dev; +- bridge->id = id; ++ pmem->id = id; + +- bridge->dev_path = strdup(br_base); +- if (!bridge->dev_path) ++ pmem->dev_path = strdup(br_base); ++ if (!pmem->dev_path) + goto err_read; + +- bridge->dev_buf = calloc(1, strlen(br_base) + 50); +- if (!bridge->dev_buf) ++ pmem->dev_buf = calloc(1, strlen(br_base) + 50); ++ if (!pmem->dev_buf) + goto err_read; +- bridge->buf_len = strlen(br_base) + 50; ++ pmem->buf_len = strlen(br_base) + 50; + +- memdev->bridge = bridge; +- return bridge; ++ memdev->pmem = pmem; ++ return pmem; + + err_read: +- free(bridge->dev_buf); +- free(bridge->dev_path); +- free(bridge); ++ free(pmem->dev_buf); ++ free(pmem->dev_path); ++ free(pmem); + err_dev: + return NULL; + } +@@ -319,7 +319,7 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + goto err_read; + memdev->buf_len = strlen(cxlmem_base) + 50; + +- sysfs_device_parse(ctx, cxlmem_base, "pmem", memdev, add_cxl_bridge); ++ sysfs_device_parse(ctx, cxlmem_base, "pmem", memdev, add_cxl_pmem); + + cxl_memdev_foreach(ctx, memdev_dup) + if (memdev_dup->id == memdev->id) { +@@ -430,18 +430,18 @@ static int is_enabled(const char *drvpath) + CXL_EXPORT int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev) + { + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); +- struct cxl_nvdimm_bridge *bridge = memdev->bridge; ++ struct cxl_pmem *pmem = memdev->pmem; + char *path; + int len; + +- if (!bridge) ++ if (!pmem) + return 0; + +- path = bridge->dev_buf; +- len = bridge->buf_len; ++ path = pmem->dev_buf; ++ len = pmem->buf_len; + +- if (snprintf(path, len, "%s/driver", bridge->dev_path) >= len) { +- err(ctx, "%s: nvdimm bridge buffer too small!\n", ++ if (snprintf(path, len, "%s/driver", pmem->dev_path) >= len) { ++ err(ctx, "%s: nvdimm pmem buffer too small!\n", + cxl_memdev_get_devname(memdev)); + return 0; + } +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 28f7e16..7c81e24 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -10,7 +10,7 @@ + + #define CXL_EXPORT __attribute__ ((visibility("default"))) + +-struct cxl_nvdimm_bridge { ++struct cxl_pmem { + int id; + void *dev_buf; + size_t buf_len; +@@ -30,7 +30,7 @@ struct cxl_memdev { + int payload_max; + size_t lsa_size; + struct kmod_module *module; +- struct cxl_nvdimm_bridge *bridge; ++ struct cxl_pmem *pmem; + unsigned long long serial; + }; + +-- +2.27.0 + diff --git a/SOURCES/0098-cxl-list-Cleanup-options-definitions.patch b/SOURCES/0098-cxl-list-Cleanup-options-definitions.patch new file mode 100644 index 0000000..f8cebbe --- /dev/null +++ b/SOURCES/0098-cxl-list-Cleanup-options-definitions.patch @@ -0,0 +1,61 @@ +From 5d20a4d2cca923e63cb1604da51788c0fd078ce1 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:52 -0800 +Subject: [PATCH 098/217] cxl/list: Cleanup options definitions + +Clarify which options take lists by adding a "(s)" to the object name, and +move the option block out of cmd_list() to reduce the column-80 collisions. + +Link: https://lore.kernel.org/r/164298557263.3021641.8121105326167408001.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/list.c | 30 ++++++++++++++++-------------- + 1 file changed, 16 insertions(+), 14 deletions(-) + +diff --git a/cxl/list.c b/cxl/list.c +index 6bc48df..7e2744d 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -19,22 +19,24 @@ static int num_list_flags(void) + return param.memdevs; + } + ++static const struct option options[] = { ++ OPT_STRING('m', "memdev", ¶m.memdev_filter, "memory device name(s)", ++ "filter by CXL memory device name(s)"), ++ OPT_STRING('s', "serial", ¶m.serial_filter, ++ "memory device serial(s)", ++ "filter by CXL memory device serial number(s)"), ++ OPT_BOOLEAN('M', "memdevs", ¶m.memdevs, ++ "include CXL memory device info"), ++ OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), ++ OPT_BOOLEAN('u', "human", ¶m.human, ++ "use human friendly number formats "), ++ OPT_BOOLEAN('H', "health", ¶m.health, ++ "include memory device health information "), ++ OPT_END(), ++}; ++ + int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + { +- const struct option options[] = { +- OPT_STRING('m', "memdev", ¶m.memdev_filter, "memory device name", +- "filter by CXL memory device name"), +- OPT_STRING('s', "serial", ¶m.serial_filter, "memory device serial", +- "filter by CXL memory device serial number"), +- OPT_BOOLEAN('M', "memdevs", ¶m.memdevs, +- "include CXL memory device info"), +- OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), +- OPT_BOOLEAN('u', "human", ¶m.human, +- "use human friendly number formats "), +- OPT_BOOLEAN('H', "health", ¶m.health, +- "include memory device health information "), +- OPT_END(), +- }; + const char * const u[] = { + "cxl list []", + NULL +-- +2.27.0 + diff --git a/SOURCES/0099-Documentation-Enhance-libcxl-memdev-API-documentatio.patch b/SOURCES/0099-Documentation-Enhance-libcxl-memdev-API-documentatio.patch new file mode 100644 index 0000000..c989e88 --- /dev/null +++ b/SOURCES/0099-Documentation-Enhance-libcxl-memdev-API-documentatio.patch @@ -0,0 +1,170 @@ +From 7b9ed7e065c6de029385d40de1f7cb0aed3a9108 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:52:57 -0800 +Subject: [PATCH 099/217] Documentation: Enhance libcxl memdev API + documentation + +In preparation for adding documentation for more objects, organize the +current into subsections and flesh out descriptions for the current APIs. + +Link: https://lore.kernel.org/r/164298557771.3021641.14904324834528700206.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/copyright.txt | 2 +- + Documentation/cxl/lib/libcxl.txt | 111 +++++++++++++++++++++++++++---- + 2 files changed, 99 insertions(+), 14 deletions(-) + +diff --git a/Documentation/copyright.txt b/Documentation/copyright.txt +index a9380e1..af9caf7 100644 +--- a/Documentation/copyright.txt ++++ b/Documentation/copyright.txt +@@ -2,7 +2,7 @@ + + COPYRIGHT + --------- +-Copyright (C) 2016 - 2020, Intel Corporation. License GPLv2: GNU GPL ++Copyright (C) 2016 - 2022, Intel Corporation. License GPLv2: GNU GPL + version 2 . This is free software: + you are free to change and redistribute it. There is NO WARRANTY, to + the extent permitted by law. +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 2539369..c127326 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -20,27 +20,100 @@ libcxl provides interfaces to interact with CXL devices in Linux, using sysfs + interfaces for most kernel interactions, and the ioctl() interface for command + submission. + +-The starting point for all library interfaces is a 'cxl_ctx' object, returned +-by linklibcxl:cxl_new[3]. CXL 'Type 3' memory devices are children of the +-cxl_ctx object, and can be iterated through using an iterator API. ++The starting point for all library interfaces is a 'cxl_ctx' object, ++returned by linklibcxl:cxl_new[3]. CXL 'Type 3' memory devices and other ++CXL device objects are descendants of the cxl_ctx object, and can be ++iterated via an object an iterator API of the form ++cxl__foreach(, ). + +-Library level interfaces that are agnostic to any device, or a specific +-subclass of operations have the prefix 'cxl_' ++MEMDEVS ++------- ++The object representing a CXL memory expander (Type 3 device) is 'struct ++cxl_memdev'. Library interfaces related to these devices have the prefix ++'cxl_memdev_'. These interfaces are mostly associated with sysfs ++interactions (unless otherwise noted in their respective documentation ++sections). They are typically used to retrieve data published by the ++kernel, or to send data or trigger kernel operations for a given device. + +-The object representing a CXL Type 3 device is 'cxl_memdev'. Library interfaces +-related to these devices have the prefix 'cxl_memdev_'. These interfaces are +-mostly associated with sysfs interactions (unless otherwise noted in their +-respective documentation pages). They are typically used to retrieve data +-published by the kernel, or to send data or trigger kernel operations for a +-given device. ++=== MEMDEV: Enumeration ++---- ++struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx); ++struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev); ++struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev); ++ ++#define cxl_memdev_foreach(ctx, memdev) \ ++ for (memdev = cxl_memdev_get_first(ctx); \ ++ memdev != NULL; \ ++ memdev = cxl_memdev_get_next(memdev)) ++ ++---- ++ ++CXL memdev instances are enumerated from the global library context ++'struct cxl_ctx'. By default a memdev only offers a portal to submit ++memory device commands, see the port, decoder, and endpoint APIs to ++determine what if any CXL Memory Resources are reachable given a ++specific memdev. ++ ++=== MEMDEV: Attributes ++---- ++int cxl_memdev_get_id(struct cxl_memdev *memdev); ++unsigned long long cxl_memdev_get_serial(struct cxl_memdev *memdev); ++const char *cxl_memdev_get_devname(struct cxl_memdev *memdev); ++int cxl_memdev_get_major(struct cxl_memdev *memdev); ++int cxl_memdev_get_minor(struct cxl_memdev *memdev); ++unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev); ++unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev); ++const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev); ++size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev); ++int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev); ++---- ++ ++A memdev is given a kernel device name of the form "mem%d" where an id ++(cxl_memdev_get_id()) is dynamically allocated as devices are ++discovered. Note that there are no guarantees that ids / kernel device ++names for memdevs are stable from one boot to the next, devices are ++enumerated asynchronously. If a stable identifier is use ++cxl_memdev_get_serial() which returns a value according to the 'Device ++Serial Number Extended Capability' in the PCIe 5.0 Base Specification. ++ ++The character device node for command submission can be found by default ++at /dev/cxl/mem%d, or created with a major / minor returned from ++cxl_memdev_get_{major,minor}(). ++ ++The 'pmem_size' and 'ram_size' attributes return the current ++provisioning of DPA (Device Physical Address / local capacity) in the ++device. ++ ++=== MEMDEV: Commands ++---- ++struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode); ++struct cxl_cmd *cxl_cmd_new_identify(struct cxl_memdev *memdev); ++struct cxl_cmd *cxl_cmd_new_get_health_info(struct cxl_memdev *memdev); ++struct cxl_cmd *cxl_cmd_new_read_label(struct cxl_memdev *memdev, ++ unsigned int offset, unsigned int length); ++struct cxl_cmd *cxl_cmd_new_write_label(struct cxl_memdev *memdev, void *buf, ++ unsigned int offset, unsigned int length); ++int cxl_memdev_zero_label(struct cxl_memdev *memdev, size_t length, ++ size_t offset); ++int cxl_memdev_read_label(struct cxl_memdev *memdev, void *buf, size_t length, ++ size_t offset); ++int cxl_memdev_write_label(struct cxl_memdev *memdev, void *buf, size_t length, ++ size_t offset); ++ ++---- + + A 'cxl_cmd' is a reference counted object which is used to perform 'Mailbox' + commands as described in the CXL Specification. A 'cxl_cmd' object is tied to a + 'cxl_memdev'. Associated library interfaces have the prefix 'cxl_cmd_'. Within + this sub-class of interfaces, there are: + +- * 'cxl_cmd_new_*' interfaces that allocate a new cxl_cmd object for a given +- command type. ++ * 'cxl_cmd_new_*()' interfaces that allocate a new cxl_cmd object for a given ++ command type targeted at a given memdev. As part of the command ++ instantiation process the library validates that the command is ++ supported by the memory device, otherwise it returns NULL to indicate ++ 'no support'. The libcxl command id is translated by the kernel into ++ a CXL standard opcode. See the potential command ids in ++ /usr/include/linux/cxl_mem.h. + + * 'cxl_cmd_submit' which submits the command via ioctl() + +@@ -49,6 +122,18 @@ this sub-class of interfaces, there are: + + * 'cxl_cmd_get_*' interfaces to get general command related information. + ++cxl_cmd_new_raw() supports so called 'RAW' commands where the command id ++is 'RAW' and it carries an unmodified CXL memory device command payload ++associated with the 'opcode' argument. Given the kernel does minimal ++input validation on these commands typically raw commands are not ++supported by the kernel outside debug build scenarios. libcxl is limited ++to supporting commands that appear in the CXL standard / public ++specifications. ++ ++cxl_memdev{read,write,zero}_label() are helpers for marshaling multiple ++label access commands over an arbitrary extent of the device's label ++area. ++ + include::../../copyright.txt[] + + SEE ALSO +-- +2.27.0 + diff --git a/SOURCES/0100-cxl-list-Add-bus-objects.patch b/SOURCES/0100-cxl-list-Add-bus-objects.patch new file mode 100644 index 0000000..25dd088 --- /dev/null +++ b/SOURCES/0100-cxl-list-Add-bus-objects.patch @@ -0,0 +1,739 @@ +From 9dce91c303720a336c55ecdc2e01e423589b85b2 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:53:02 -0800 +Subject: [PATCH 100/217] cxl/list: Add bus objects + +A 'struct cxl_bus' represents a CXL.mem domain. It is the root of a +Host-managed Device Memory (HDM) hierarchy. When memory devices are enabled +for CXL operation they appear underneath a bus in a 'cxl list -BM' listing, +otherwise they display as disconnected. + +A 'bus' is identical to the kernel's CXL root port object, but given the +confusion between CXL root ports, and PCIe root ports, the 'bus' name is +less ambiguous. It also serves a similar role in the object hierarchy as a +'struct ndctl_bus' object. It is also the case that the "root" name will +appear as the kernel device-name, so the association will be clear. + +Link: https://lore.kernel.org/r/164298558278.3021641.16323855851736615358.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .clang-format | 1 + + Documentation/cxl/cxl-list.txt | 88 ++++++++++++++++--- + Documentation/cxl/lib/libcxl.txt | 30 +++++++ + cxl/filter.c | 117 ++++++++++++++++++++++++- + cxl/filter.h | 2 + + cxl/json.c | 21 +++++ + cxl/json.h | 5 +- + cxl/lib/libcxl.c | 142 +++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 5 ++ + cxl/lib/private.h | 14 +++ + cxl/libcxl.h | 11 +++ + cxl/list.c | 19 +++-- + 12 files changed, 431 insertions(+), 24 deletions(-) + +diff --git a/.clang-format b/.clang-format +index d2e77d0..1154c76 100644 +--- a/.clang-format ++++ b/.clang-format +@@ -78,6 +78,7 @@ ExperimentalAutoDetectBinPacking: false + # | sort -u) + ForEachMacros: + - 'cxl_memdev_foreach' ++ - 'cxl_bus_foreach' + - 'daxctl_dev_foreach' + - 'daxctl_mapping_foreach' + - 'daxctl_region_foreach' +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 224c972..be131ae 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -15,17 +15,60 @@ SYNOPSIS + Walk the CXL capable device hierarchy in the system and list all device + instances along with some of their major attributes. + +-Options can be specified to limit the output to specific objects. ++Options can be specified to limit the output to specific objects. When a ++single object type is specified the return json object is an array of ++just those objects, when multiple objects types are specified the ++returned the returned object may be an array of arrays with the inner ++array named for the given object type. ++ ++Filters can by specifed as either a single identidier, a space separated ++quoted string, or a comma separated list. When multiple filter ++identifiers are specified within a filter string, like "-m ++mem0,mem1,mem2", they are combined as an 'OR' filter. When multiple ++filter string types are specified, like "-m mem0,mem1,mem2 -p port10", ++they are combined as an 'AND' filter. So, "-m mem0,mem1,mem2 -p port10" ++would only list objects that are beneath port10 AND map mem0, mem1, OR ++mem2. ++ ++The --human option in addition to reformatting some fields to more human ++friendly strings also unwraps the array to reduce the number of lines of ++output. + + EXAMPLE + ------- + ---- + # cxl list --memdevs +-{ +- "memdev":"mem0", +- "pmem_size":268435456, +- "ram_size":0, +-} ++[ ++ { ++ "memdev":"mem0", ++ "pmem_size":268435456, ++ "ram_size":0, ++ "serial":0 ++ } ++] ++ ++# cxl list -BMu ++[ ++ { ++ "anon memdevs":[ ++ { ++ "memdev":"mem0", ++ "pmem_size":"256.00 MiB (268.44 MB)", ++ "ram_size":0, ++ "serial":"0" ++ } ++ ] ++ }, ++ { ++ "buses":[ ++ { ++ "bus":"root0", ++ "provider":"ACPI.CXL" ++ } ++ ] ++ } ++] ++ + ---- + + OPTIONS +@@ -34,13 +77,6 @@ OPTIONS + --memdev=:: + Specify CXL memory device name(s), or device id(s), to filter the listing. For example: + ---- +-# cxl list --memdev=mem0 +-{ +- "memdev":"mem0", +- "pmem_size":268435456, +- "ram_size":0, +-} +- + # cxl list -M --memdev="0 mem3 5" + [ + { +@@ -114,6 +150,32 @@ OPTIONS + ] + ---- + ++-B:: ++--buses:: ++ Include 'bus' / CXL root object(s) in the listing. Typically, on ACPI ++ systems the bus object is a singleton associated with the ACPI0017 ++ device, but there are test scenerios where there may be multiple CXL ++ memory hierarchies. ++---- ++# cxl list -B ++[ ++ { ++ "bus":"root3", ++ "provider":"cxl_test" ++ }, ++ { ++ "bus":"root0", ++ "provider":"ACPI.CXL" ++ } ++] ++---- ++ ++-b:: ++--bus=:: ++ Specify CXL root device name(s), device id(s), and / or CXL bus provider ++ names to filter the listing. The supported provider names are "ACPI.CXL" ++ and "cxl_test". ++ + include::human-option.txt[] + + include::verbose-option.txt[] +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index c127326..84af66a 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -134,6 +134,36 @@ cxl_memdev{read,write,zero}_label() are helpers for marshaling multiple + label access commands over an arbitrary extent of the device's label + area. + ++BUSES ++----- ++The CXL Memory space is CPU and Device coherent. The address ranges that ++support coherent access are described by platform firmware and ++communicated to the operating system via a CXL root object 'struct ++cxl_bus'. ++ ++=== BUS: Enumeration ++---- ++struct cxl_bus *cxl_bus_get_first(struct cxl_ctx *ctx); ++struct cxl_bus *cxl_bus_get_next(struct cxl_bus *bus); ++ ++#define cxl_bus_foreach(ctx, bus) \ ++ for (bus = cxl_bus_get_first(ctx); bus != NULL; \ ++ bus = cxl_bus_get_next(bus)) ++---- ++ ++=== BUS: Attributes ++---- ++const char *cxl_bus_get_provider(struct cxl_bus *bus); ++const char *cxl_bus_get_devname(struct cxl_bus *bus); ++int cxl_bus_get_id(struct cxl_bus *bus); ++---- ++ ++The provider name of a bus is a persistent name that is independent of ++discovery order. The possible provider names are 'ACPI.CXL' and ++'cxl_test'. The devname and id attributes, like other objects, are just ++the kernel device names that are subject to change based on discovery ++order. ++ + include::../../copyright.txt[] + + SEE ALSO +diff --git a/cxl/filter.c b/cxl/filter.c +index 26efc65..5f4844b 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -1,5 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (C) 2015-2020 Intel Corporation. All rights reserved. ++// Copyright (C) 2015-2022 Intel Corporation. All rights reserved. + #include + #include + #include +@@ -21,6 +21,43 @@ static const char *which_sep(const char *filter) + return " "; + } + ++static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, ++ const char *__ident) ++{ ++ char *ident, *save; ++ const char *arg; ++ int bus_id; ++ ++ if (!__ident) ++ return bus; ++ ++ ident = strdup(__ident); ++ if (!ident) ++ return NULL; ++ ++ for (arg = strtok_r(ident, which_sep(__ident), &save); arg; ++ arg = strtok_r(NULL, which_sep(__ident), &save)) { ++ if (strcmp(arg, "all") == 0) ++ break; ++ ++ if ((sscanf(arg, "%d", &bus_id) == 1 || ++ sscanf(arg, "root%d", &bus_id) == 1) && ++ cxl_bus_get_id(bus) == bus_id) ++ break; ++ ++ if (strcmp(arg, cxl_bus_get_devname(bus)) == 0) ++ break; ++ ++ if (strcmp(arg, cxl_bus_get_provider(bus)) == 0) ++ break; ++ } ++ ++ free(ident); ++ if (arg) ++ return bus; ++ return NULL; ++} ++ + static struct cxl_memdev * + util_cxl_memdev_serial_filter(struct cxl_memdev *memdev, const char *__serials) + { +@@ -98,21 +135,67 @@ static unsigned long params_to_flags(struct cxl_filter_params *param) + return flags; + } + ++static void splice_array(struct cxl_filter_params *p, struct json_object *jobjs, ++ struct json_object *platform, ++ const char *container_name, bool do_container) ++{ ++ size_t count; ++ ++ if (!json_object_array_length(jobjs)) { ++ json_object_put(jobjs); ++ return; ++ } ++ ++ if (do_container) { ++ struct json_object *container = json_object_new_object(); ++ ++ if (!container) { ++ err(p, "failed to list: %s\n", container_name); ++ return; ++ } ++ ++ json_object_object_add(container, container_name, jobjs); ++ json_object_array_add(platform, container); ++ return; ++ } ++ ++ for (count = json_object_array_length(jobjs); count; count--) { ++ struct json_object *jobj = json_object_array_get_idx(jobjs, 0); ++ ++ json_object_get(jobj); ++ json_object_array_del_idx(jobjs, 0, 1); ++ json_object_array_add(platform, jobj); ++ } ++ json_object_put(jobjs); ++} ++ + int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + { + struct json_object *jplatform = json_object_new_array(); ++ struct json_object *jdevs = NULL, *jbuses = NULL; + unsigned long flags = params_to_flags(p); + struct cxl_memdev *memdev; ++ int top_level_objs = 0; ++ struct cxl_bus *bus; + + if (!jplatform) { + dbg(p, "platform object allocation failure\n"); + return -ENOMEM; + } + ++ jdevs = json_object_new_array(); ++ if (!jdevs) ++ goto err; ++ ++ jbuses = json_object_new_array(); ++ if (!jbuses) ++ goto err; ++ + cxl_memdev_foreach(ctx, memdev) { + struct json_object *jdev; + +- if (!util_cxl_memdev_filter(memdev, p->memdev_filter, p->serial_filter)) ++ if (!util_cxl_memdev_filter(memdev, p->memdev_filter, ++ p->serial_filter)) + continue; + if (p->memdevs) { + jdev = util_cxl_memdev_to_json(memdev, flags); +@@ -120,11 +203,39 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + dbg(p, "memdev object allocation failure\n"); + continue; + } +- json_object_array_add(jplatform, jdev); ++ json_object_array_add(jdevs, jdev); ++ } ++ } ++ ++ cxl_bus_foreach(ctx, bus) { ++ struct json_object *jbus; ++ ++ if (!util_cxl_bus_filter(bus, p->bus_filter)) ++ continue; ++ if (p->buses) { ++ jbus = util_cxl_bus_to_json(bus, flags); ++ if (!jbus) { ++ dbg(p, "bus object allocation failure\n"); ++ continue; ++ } ++ json_object_array_add(jbuses, jbus); + } + } + ++ if (json_object_array_length(jdevs)) ++ top_level_objs++; ++ if (json_object_array_length(jbuses)) ++ top_level_objs++; ++ ++ splice_array(p, jdevs, jplatform, "anon memdevs", top_level_objs > 1); ++ splice_array(p, jbuses, jplatform, "buses", top_level_objs > 1); ++ + util_display_json_array(stdout, jplatform, flags); + + return 0; ++err: ++ json_object_put(jdevs); ++ json_object_put(jbuses); ++ json_object_put(jplatform); ++ return -ENOMEM; + } +diff --git a/cxl/filter.h b/cxl/filter.h +index 12d9344..d41e757 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -9,7 +9,9 @@ + struct cxl_filter_params { + const char *memdev_filter; + const char *serial_filter; ++ const char *bus_filter; + bool memdevs; ++ bool buses; + bool idle; + bool human; + bool health; +diff --git a/cxl/json.c b/cxl/json.c +index d8e65df..a584594 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -221,3 +221,24 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + } + return jdev; + } ++ ++struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus, ++ unsigned long flags) ++{ ++ const char *devname = cxl_bus_get_devname(bus); ++ struct json_object *jbus, *jobj; ++ ++ jbus = json_object_new_object(); ++ if (!jbus) ++ return NULL; ++ ++ jobj = json_object_new_string(devname); ++ if (jobj) ++ json_object_object_add(jbus, "bus", jobj); ++ ++ jobj = json_object_new_string(cxl_bus_get_provider(bus)); ++ if (jobj) ++ json_object_object_add(jbus, "provider", jobj); ++ ++ return jbus; ++} +diff --git a/cxl/json.h b/cxl/json.h +index 3abcfe6..4abf6e5 100644 +--- a/cxl/json.h ++++ b/cxl/json.h +@@ -1,8 +1,11 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ ++/* Copyright (C) 2015-2022 Intel Corporation. All rights reserved. */ + #ifndef __CXL_UTIL_JSON_H__ + #define __CXL_UTIL_JSON_H__ + struct cxl_memdev; + struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + unsigned long flags); ++struct cxl_bus; ++struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus, ++ unsigned long flags); + #endif /* __CXL_UTIL_JSON_H__ */ +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 9839f26..8548a45 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -40,7 +40,9 @@ struct cxl_ctx { + int refcount; + void *userdata; + int memdevs_init; ++ int buses_init; + struct list_head memdevs; ++ struct list_head buses; + struct kmod_ctx *kmod_ctx; + void *private_data; + }; +@@ -64,6 +66,21 @@ static void free_memdev(struct cxl_memdev *memdev, struct list_head *head) + free(memdev); + } + ++static void __free_port(struct cxl_port *port, struct list_head *head) ++{ ++ if (head) ++ list_del_from(head, &port->list); ++ free(port->dev_buf); ++ free(port->dev_path); ++ free(port->uport); ++} ++ ++static void free_bus(struct cxl_bus *bus, struct list_head *head) ++{ ++ __free_port(&bus->port, head); ++ free(bus); ++} ++ + /** + * cxl_get_userdata - retrieve stored data pointer from library context + * @ctx: cxl library context +@@ -130,6 +147,7 @@ CXL_EXPORT int cxl_new(struct cxl_ctx **ctx) + dbg(c, "log_priority=%d\n", c->ctx.log_priority); + *ctx = c; + list_head_init(&c->memdevs); ++ list_head_init(&c->buses); + c->kmod_ctx = kmod_ctx; + + return 0; +@@ -160,6 +178,7 @@ CXL_EXPORT struct cxl_ctx *cxl_ref(struct cxl_ctx *ctx) + CXL_EXPORT void cxl_unref(struct cxl_ctx *ctx) + { + struct cxl_memdev *memdev, *_d; ++ struct cxl_bus *bus, *_b; + + if (ctx == NULL) + return; +@@ -170,6 +189,9 @@ CXL_EXPORT void cxl_unref(struct cxl_ctx *ctx) + list_for_each_safe(&ctx->memdevs, memdev, _d, list) + free_memdev(memdev, &ctx->memdevs); + ++ list_for_each_safe(&ctx->buses, bus, _b, port.list) ++ free_bus(bus, &ctx->buses); ++ + kmod_unref(ctx->kmod_ctx); + info(ctx, "context %p released\n", ctx); + free(ctx); +@@ -449,6 +471,126 @@ CXL_EXPORT int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev) + return is_enabled(path); + } + ++static int cxl_port_init(struct cxl_port *port, struct cxl_ctx *ctx, int id, ++ const char *cxlport_base) ++{ ++ char *path = calloc(1, strlen(cxlport_base) + 100); ++ size_t rc; ++ ++ if (!path) ++ return -ENOMEM; ++ ++ port->id = id; ++ port->ctx = ctx; ++ ++ port->dev_path = strdup(cxlport_base); ++ if (!port->dev_path) ++ goto err; ++ ++ port->dev_buf = calloc(1, strlen(cxlport_base) + 50); ++ if (!port->dev_buf) ++ goto err; ++ port->buf_len = strlen(cxlport_base) + 50; ++ ++ rc = snprintf(port->dev_buf, port->buf_len, "%s/uport", cxlport_base); ++ if (rc >= port->buf_len) ++ goto err; ++ port->uport = realpath(port->dev_buf, NULL); ++ if (!port->uport) ++ goto err; ++ ++ return 0; ++err: ++ free(port->dev_path); ++ free(port->dev_buf); ++ free(path); ++ return -ENOMEM; ++} ++ ++static void *add_cxl_bus(void *parent, int id, const char *cxlbus_base) ++{ ++ const char *devname = devpath_to_devname(cxlbus_base); ++ struct cxl_bus *bus, *bus_dup; ++ struct cxl_ctx *ctx = parent; ++ struct cxl_port *port; ++ int rc; ++ ++ dbg(ctx, "%s: base: \'%s\'\n", devname, cxlbus_base); ++ ++ bus = calloc(1, sizeof(*bus)); ++ if (!bus) ++ return NULL; ++ ++ port = &bus->port; ++ rc = cxl_port_init(port, ctx, id, cxlbus_base); ++ if (rc) ++ goto err; ++ ++ cxl_bus_foreach(ctx, bus_dup) ++ if (bus_dup->port.id == bus->port.id) { ++ free_bus(bus, NULL); ++ return bus_dup; ++ } ++ ++ list_add(&ctx->buses, &port->list); ++ return bus; ++ ++err: ++ free(bus); ++ return NULL; ++} ++ ++static void cxl_buses_init(struct cxl_ctx *ctx) ++{ ++ if (ctx->buses_init) ++ return; ++ ++ ctx->buses_init = 1; ++ ++ sysfs_device_parse(ctx, "/sys/bus/cxl/devices", "root", ctx, ++ add_cxl_bus); ++} ++ ++CXL_EXPORT struct cxl_bus *cxl_bus_get_first(struct cxl_ctx *ctx) ++{ ++ cxl_buses_init(ctx); ++ ++ return list_top(&ctx->buses, struct cxl_bus, port.list); ++} ++ ++CXL_EXPORT struct cxl_bus *cxl_bus_get_next(struct cxl_bus *bus) ++{ ++ struct cxl_ctx *ctx = bus->port.ctx; ++ ++ return list_next(&ctx->buses, bus, port.list); ++} ++ ++CXL_EXPORT const char *cxl_bus_get_devname(struct cxl_bus *bus) ++{ ++ struct cxl_port *port = &bus->port; ++ ++ return devpath_to_devname(port->dev_path); ++} ++ ++CXL_EXPORT int cxl_bus_get_id(struct cxl_bus *bus) ++{ ++ struct cxl_port *port = &bus->port; ++ ++ return port->id; ++} ++ ++CXL_EXPORT const char *cxl_bus_get_provider(struct cxl_bus *bus) ++{ ++ struct cxl_port *port = &bus->port; ++ const char *devname = devpath_to_devname(port->uport); ++ ++ if (strcmp(devname, "ACPI0017:00") == 0) ++ return "ACPI.CXL"; ++ if (strcmp(devname, "cxl_acpi.0") == 0) ++ return "cxl_test"; ++ return devname; ++} ++ + CXL_EXPORT void cxl_cmd_unref(struct cxl_cmd *cmd) + { + if (!cmd) +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 4411035..781ff99 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -77,4 +77,9 @@ local: + LIBCXL_2 { + global: + cxl_memdev_get_serial; ++ cxl_bus_get_first; ++ cxl_bus_get_next; ++ cxl_bus_get_provider; ++ cxl_bus_get_devname; ++ cxl_bus_get_id; + } LIBCXL_1; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 7c81e24..0758d05 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -34,6 +34,20 @@ struct cxl_memdev { + unsigned long long serial; + }; + ++struct cxl_port { ++ int id; ++ void *dev_buf; ++ size_t buf_len; ++ char *dev_path; ++ char *uport; ++ struct cxl_ctx *ctx; ++ struct list_node list; ++}; ++ ++struct cxl_bus { ++ struct cxl_port port; ++}; ++ + enum cxl_cmd_query_status { + CXL_CMD_QUERY_NOT_RUN = 0, + CXL_CMD_QUERY_OK, +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index bcdede8..da66eb2 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -57,6 +57,17 @@ int cxl_memdev_write_label(struct cxl_memdev *memdev, void *buf, size_t length, + memdev != NULL; \ + memdev = cxl_memdev_get_next(memdev)) + ++struct cxl_bus; ++struct cxl_bus *cxl_bus_get_first(struct cxl_ctx *ctx); ++struct cxl_bus *cxl_bus_get_next(struct cxl_bus *bus); ++const char *cxl_bus_get_provider(struct cxl_bus *bus); ++const char *cxl_bus_get_devname(struct cxl_bus *bus); ++int cxl_bus_get_id(struct cxl_bus *bus); ++ ++#define cxl_bus_foreach(ctx, bus) \ ++ for (bus = cxl_bus_get_first(ctx); bus != NULL; \ ++ bus = cxl_bus_get_next(bus)) ++ + struct cxl_cmd; + const char *cxl_cmd_get_devname(struct cxl_cmd *cmd); + struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode); +diff --git a/cxl/list.c b/cxl/list.c +index 7e2744d..9500e61 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -1,5 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 +-/* Copyright (C) 2020-2021 Intel Corporation. All rights reserved. */ ++/* Copyright (C) 2020-2022 Intel Corporation. All rights reserved. */ + #include + #include + #include +@@ -14,11 +14,6 @@ + + static struct cxl_filter_params param; + +-static int num_list_flags(void) +-{ +- return param.memdevs; +-} +- + static const struct option options[] = { + OPT_STRING('m', "memdev", ¶m.memdev_filter, "memory device name(s)", + "filter by CXL memory device name(s)"), +@@ -27,6 +22,9 @@ static const struct option options[] = { + "filter by CXL memory device serial number(s)"), + OPT_BOOLEAN('M', "memdevs", ¶m.memdevs, + "include CXL memory device info"), ++ OPT_STRING('b', "bus", ¶m.bus_filter, "bus device name", ++ "filter by CXL bus device name(s)"), ++ OPT_BOOLEAN('B', "buses", ¶m.buses, "include CXL bus info"), + OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), + OPT_BOOLEAN('u', "human", ¶m.human, + "use human friendly number formats "), +@@ -35,6 +33,11 @@ static const struct option options[] = { + OPT_END(), + }; + ++static int num_list_flags(void) ++{ ++ return !!param.memdevs + !!param.buses; ++} ++ + int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + { + const char * const u[] = { +@@ -53,7 +56,9 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + if (num_list_flags() == 0) { + if (param.memdev_filter || param.serial_filter) + param.memdevs = true; +- else { ++ if (param.bus_filter) ++ param.buses = true; ++ if (num_list_flags() == 0) { + /* + * TODO: We likely want to list regions by default if + * nothing was explicitly asked for. But until we have +-- +2.27.0 + diff --git a/SOURCES/0101-util-json-Warn-on-stderr-about-empty-list-results.patch b/SOURCES/0101-util-json-Warn-on-stderr-about-empty-list-results.patch new file mode 100644 index 0000000..b8fcc87 --- /dev/null +++ b/SOURCES/0101-util-json-Warn-on-stderr-about-empty-list-results.patch @@ -0,0 +1,44 @@ +From 91f78bbcda7fc644041dfabfa679c6a627f90e76 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:53:08 -0800 +Subject: [PATCH 101/217] util/json: Warn on stderr about empty list results + +Help interactive users notice something is wrong with the list parameters +by warning that no devices matched the specified filter settings. + +Link: https://lore.kernel.org/r/164298558814.3021641.13051269428355986099.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + util/json.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/util/json.c b/util/json.c +index bd5f8fc..f8cc81f 100644 +--- a/util/json.c ++++ b/util/json.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -95,9 +96,11 @@ void util_display_json_array(FILE *f_out, struct json_object *jarray, + int len = json_object_array_length(jarray); + int jflag = JSON_C_TO_STRING_PRETTY; + +- if (json_object_array_length(jarray) > 1 || !(flags & UTIL_JSON_HUMAN)) ++ if (len > 1 || !(flags & UTIL_JSON_HUMAN)) { ++ if (len == 0) ++ warning("no matching devices found\n"); + fprintf(f_out, "%s\n", json_object_to_json_string_ext(jarray, jflag)); +- else if (len) { ++ } else if (len) { + struct json_object *jobj; + + jobj = json_object_array_get_idx(jarray, 0); +-- +2.27.0 + diff --git a/SOURCES/0102-util-sysfs-Uplevel-modalias-lookup-helper-to-util.patch b/SOURCES/0102-util-sysfs-Uplevel-modalias-lookup-helper-to-util.patch new file mode 100644 index 0000000..6d8dd93 --- /dev/null +++ b/SOURCES/0102-util-sysfs-Uplevel-modalias-lookup-helper-to-util.patch @@ -0,0 +1,165 @@ +From ecd7e6e7aabfa2592f3f739a725d135eb43d6314 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:53:13 -0800 +Subject: [PATCH 102/217] util/sysfs: Uplevel modalias lookup helper to util/ + +The to_module() helper looks up modules relative to a modalias. Uplevel +this to share with libcxl. + +Link: https://lore.kernel.org/r/164298559346.3021641.11059026790676662837.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 33 +++++---------------------------- + util/sysfs.c | 27 +++++++++++++++++++++++++++ + util/sysfs.h | 8 ++++++++ + 3 files changed, 40 insertions(+), 28 deletions(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index 47a234c..1374ad9 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -1668,7 +1668,6 @@ static enum ndctl_fwa_result fwa_result_to_result(const char *result) + static int ndctl_bind(struct ndctl_ctx *ctx, struct kmod_module *module, + const char *devname); + static int ndctl_unbind(struct ndctl_ctx *ctx, const char *devpath); +-static struct kmod_module *to_module(struct ndctl_ctx *ctx, const char *alias); + + static int populate_dimm_attributes(struct ndctl_dimm *dimm, + const char *dimm_base, +@@ -1878,7 +1877,7 @@ static void *add_dimm(void *parent, int id, const char *dimm_base) + sprintf(path, "%s/modalias", dimm_base); + if (sysfs_read_attr(ctx, path, buf) < 0) + goto err_read; +- dimm->module = to_module(ctx, buf); ++ dimm->module = util_modalias_to_module(ctx, buf); + + dimm->handle = -1; + dimm->phys_id = -1; +@@ -2597,7 +2596,7 @@ static void *add_region(void *parent, int id, const char *region_base) + sprintf(path, "%s/modalias", region_base); + if (sysfs_read_attr(ctx, path, buf) < 0) + goto err_read; +- region->module = to_module(ctx, buf); ++ region->module = util_modalias_to_module(ctx, buf); + + sprintf(path, "%s/numa_node", region_base); + if ((rc = sysfs_read_attr(ctx, path, buf)) == 0) +@@ -3885,28 +3884,6 @@ NDCTL_EXPORT struct ndctl_ctx *ndctl_mapping_get_ctx( + return ndctl_mapping_get_bus(mapping)->ctx; + } + +-static struct kmod_module *to_module(struct ndctl_ctx *ctx, const char *alias) +-{ +- struct kmod_list *list = NULL; +- struct kmod_module *mod; +- int rc; +- +- if (!ctx->kmod_ctx) +- return NULL; +- +- rc = kmod_module_new_from_lookup(ctx->kmod_ctx, alias, &list); +- if (rc < 0 || !list) { +- dbg(ctx, "failed to find module for alias: %s %d list: %s\n", +- alias, rc, list ? "populated" : "empty"); +- return NULL; +- } +- mod = kmod_module_get_module(list); +- dbg(ctx, "alias: %s module: %s\n", alias, kmod_module_get_name(mod)); +- kmod_module_unref_list(list); +- +- return mod; +-} +- + static char *get_block_device(struct ndctl_ctx *ctx, const char *block_path) + { + char *bdev_name = NULL; +@@ -4069,7 +4046,7 @@ static void *add_namespace(void *parent, int id, const char *ndns_base) + sprintf(path, "%s/modalias", ndns_base); + if (sysfs_read_attr(ctx, path, buf) < 0) + goto err_read; +- ndns->module = to_module(ctx, buf); ++ ndns->module = util_modalias_to_module(ctx, buf); + + ndctl_namespace_foreach(region, ndns_dup) + if (ndns_dup->id == ndns->id) { +@@ -5182,7 +5159,7 @@ static void *add_btt(void *parent, int id, const char *btt_base) + sprintf(path, "%s/modalias", btt_base); + if (sysfs_read_attr(ctx, path, buf) < 0) + goto err_read; +- btt->module = to_module(ctx, buf); ++ btt->module = util_modalias_to_module(ctx, buf); + + sprintf(path, "%s/uuid", btt_base); + if (sysfs_read_attr(ctx, path, buf) < 0) +@@ -5533,7 +5510,7 @@ static void *__add_pfn(struct ndctl_pfn *pfn, const char *pfn_base) + sprintf(path, "%s/modalias", pfn_base); + if (sysfs_read_attr(ctx, path, buf) < 0) + goto err_read; +- pfn->module = to_module(ctx, buf); ++ pfn->module = util_modalias_to_module(ctx, buf); + + sprintf(path, "%s/uuid", pfn_base); + if (sysfs_read_attr(ctx, path, buf) < 0) +diff --git a/util/sysfs.c b/util/sysfs.c +index cfbab7d..23330cb 100644 +--- a/util/sysfs.c ++++ b/util/sysfs.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -118,3 +119,29 @@ int __sysfs_device_parse(struct log_ctx *ctx, const char *base_path, + + return add_errors; + } ++ ++struct kmod_module *__util_modalias_to_module(struct kmod_ctx *kmod_ctx, ++ const char *alias, ++ struct log_ctx *log) ++{ ++ struct kmod_list *list = NULL; ++ struct kmod_module *mod; ++ int rc; ++ ++ if (!kmod_ctx) ++ return NULL; ++ ++ rc = kmod_module_new_from_lookup(kmod_ctx, alias, &list); ++ if (rc < 0 || !list) { ++ log_dbg(log, ++ "failed to find module for alias: %s %d list: %s\n", ++ alias, rc, list ? "populated" : "empty"); ++ return NULL; ++ } ++ mod = kmod_module_get_module(list); ++ log_dbg(log, "alias: %s module: %s\n", alias, ++ kmod_module_get_name(mod)); ++ kmod_module_unref_list(list); ++ ++ return mod; ++} +diff --git a/util/sysfs.h b/util/sysfs.h +index 6485a73..bdee4f5 100644 +--- a/util/sysfs.h ++++ b/util/sysfs.h +@@ -27,4 +27,12 @@ static inline const char *devpath_to_devname(const char *devpath) + { + return strrchr(devpath, '/') + 1; + } ++ ++struct kmod_ctx; ++struct kmod_module; ++struct kmod_module *__util_modalias_to_module(struct kmod_ctx *kmod_ctx, ++ const char *alias, ++ struct log_ctx *log); ++#define util_modalias_to_module(ctx, buf) \ ++ __util_modalias_to_module((ctx)->kmod_ctx, buf, &(ctx)->ctx) + #endif /* __UTIL_SYSFS_H__ */ +-- +2.27.0 + diff --git a/SOURCES/0103-cxl-list-Add-port-enumeration.patch b/SOURCES/0103-cxl-list-Add-port-enumeration.patch new file mode 100644 index 0000000..832d485 --- /dev/null +++ b/SOURCES/0103-cxl-list-Add-port-enumeration.patch @@ -0,0 +1,862 @@ +From fef3f05ca8cdfd8d783162042d5cf20325c8b64b Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:53:18 -0800 +Subject: [PATCH 103/217] cxl/list: Add port enumeration + +Between a cxl_bus (root port) and an endpoint there can be an arbitrary +level of switches. Add enumeration for these ports at each level of the +hierarchy. + +However, given the CXL root ports are also "ports" infer that if the port +filter argument is the word "root" or "root%d" then include root ports in +the listing. The keyword "switch" is also provided to filter only the ports +beneath the root that are not endpoint ports. + +Link: https://lore.kernel.org/r/164298559854.3021641.17724828997703051001.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .clang-format | 1 + + Documentation/cxl/cxl-list.txt | 24 ++++ + Documentation/cxl/lib/libcxl.txt | 42 ++++++ + cxl/filter.c | 224 ++++++++++++++++++++++++++++++- + cxl/filter.h | 4 + + cxl/json.c | 23 ++++ + cxl/json.h | 3 + + cxl/lib/libcxl.c | 160 +++++++++++++++++++++- + cxl/lib/libcxl.sym | 12 ++ + cxl/lib/private.h | 11 ++ + cxl/libcxl.h | 19 +++ + cxl/list.c | 17 ++- + 12 files changed, 534 insertions(+), 6 deletions(-) + +diff --git a/.clang-format b/.clang-format +index 1154c76..391cd34 100644 +--- a/.clang-format ++++ b/.clang-format +@@ -79,6 +79,7 @@ ExperimentalAutoDetectBinPacking: false + ForEachMacros: + - 'cxl_memdev_foreach' + - 'cxl_bus_foreach' ++ - 'cxl_port_foreach' + - 'daxctl_dev_foreach' + - 'daxctl_mapping_foreach' + - 'daxctl_region_foreach' +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index be131ae..3076deb 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -176,6 +176,30 @@ OPTIONS + names to filter the listing. The supported provider names are "ACPI.CXL" + and "cxl_test". + ++-P:: ++--ports:: ++ Include port objects (CXL / PCIe root ports + Upstream Switch Ports) in ++ the listing. ++ ++-p:: ++--port=:: ++ Specify CXL Port device name(s), device id(s), and or port type ++ names to filter the listing. The supported port type names are "root" ++ and "switch". Note that since a bus object is also a port, the following ++ two syntaxes are equivalent: ++---- ++# cxl list -B ++# cxl list -P -p root ++---- ++ By default, only 'switch' ports are listed. ++ ++-S:: ++--single:: ++ Specify whether the listing should emit all the objects that are ++ descendants of a port that matches the port filter, or only direct ++ descendants of the individual ports that match the filter. By default ++ all descendant objects are listed. ++ + include::human-option.txt[] + + include::verbose-option.txt[] +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 84af66a..804e9ca 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -164,6 +164,48 @@ discovery order. The possible provider names are 'ACPI.CXL' and + the kernel device names that are subject to change based on discovery + order. + ++PORTS ++----- ++CXL ports track the PCIe hierarchy between a platform firmware CXL root ++object, through CXL / PCIe Host Bridges, CXL / PCIe Root Ports, and CXL ++/ PCIe Switch Ports. ++ ++=== PORT: Enumeration ++---- ++struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus); ++struct cxl_port *cxl_port_get_first(struct cxl_port *parent); ++struct cxl_port *cxl_port_get_next(struct cxl_port *port); ++struct cxl_port *cxl_port_get_parent(struct cxl_port *port); ++struct cxl_bus *cxl_port_get_bus(struct cxl_port *port); ++struct cxl_ctx *cxl_port_get_ctx(struct cxl_port *port); ++ ++#define cxl_port_foreach(parent, port) \ ++ for (port = cxl_port_get_first(parent); port != NULL; \ ++ port = cxl_port_get_next(port)) ++---- ++A bus object encapsulates a CXL port object. Use cxl_bus_get_port() to ++use generic port APIs on root objects. ++ ++Ports are hierarchical. All but the a root object have another CXL port ++as a parent object retrievable via cxl_port_get_parent(). ++ ++The root port of a hiearchy can be retrieved via any port instance in ++that hierarchy via cxl_port_get_bus(). ++ ++=== PORT: Attributes ++---- ++const char *cxl_port_get_devname(struct cxl_port *port); ++int cxl_port_get_id(struct cxl_port *port); ++int cxl_port_is_enabled(struct cxl_port *port); ++bool cxl_port_is_root(struct cxl_port *port); ++bool cxl_port_is_switch(struct cxl_port *port); ++---- ++The port type is communicated via cxl_port_is_(). An 'enabled' port ++is one that has succeeded in discovering the CXL component registers in ++the host device and has enumerated its downstream ports. In order for a ++memdev to be enabled for CXL memory operation all CXL ports in its ++ancestry must also be enabled. ++ + include::../../copyright.txt[] + + SEE ALSO +diff --git a/cxl/filter.c b/cxl/filter.c +index 5f4844b..8b79db3 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -21,6 +21,101 @@ static const char *which_sep(const char *filter) + return " "; + } + ++bool cxl_filter_has(const char *__filter, const char *needle) ++{ ++ char *filter, *save; ++ const char *arg; ++ ++ if (!needle) ++ return true; ++ ++ if (!__filter) ++ return false; ++ ++ filter = strdup(__filter); ++ if (!filter) ++ return false; ++ ++ for (arg = strtok_r(filter, which_sep(__filter), &save); arg; ++ arg = strtok_r(NULL, which_sep(__filter), &save)) ++ if (strstr(arg, needle)) ++ break; ++ ++ free(filter); ++ if (arg) ++ return true; ++ return false; ++} ++ ++static struct cxl_port *__util_cxl_port_filter(struct cxl_port *port, ++ const char *__ident) ++{ ++ char *ident, *save; ++ const char *arg; ++ int port_id; ++ ++ if (!__ident) ++ return port; ++ ++ ident = strdup(__ident); ++ if (!ident) ++ return NULL; ++ ++ for (arg = strtok_r(ident, which_sep(__ident), &save); arg; ++ arg = strtok_r(NULL, which_sep(__ident), &save)) { ++ if (strcmp(arg, "all") == 0) ++ break; ++ ++ if (strcmp(arg, "root") == 0 && cxl_port_is_root(port)) ++ break; ++ ++ if (strcmp(arg, "switch") == 0 && cxl_port_is_switch(port)) ++ break; ++ ++ if ((sscanf(arg, "%d", &port_id) == 1 || ++ sscanf(arg, "port%d", &port_id) == 1) && ++ cxl_port_get_id(port) == port_id) ++ break; ++ ++ if (strcmp(arg, cxl_port_get_devname(port)) == 0) ++ break; ++ } ++ ++ free(ident); ++ if (arg) ++ return port; ++ return NULL; ++} ++ ++enum cxl_port_filter_mode { ++ CXL_PF_SINGLE, ++ CXL_PF_ANCESTRY, ++}; ++ ++static enum cxl_port_filter_mode pf_mode(struct cxl_filter_params *p) ++{ ++ if (p->single) ++ return CXL_PF_SINGLE; ++ return CXL_PF_ANCESTRY; ++} ++ ++static struct cxl_port *util_cxl_port_filter(struct cxl_port *port, ++ const char *ident, ++ enum cxl_port_filter_mode mode) ++{ ++ struct cxl_port *iter = port; ++ ++ while (iter) { ++ if (__util_cxl_port_filter(iter, ident)) ++ return port; ++ if (mode == CXL_PF_SINGLE) ++ return NULL; ++ iter = cxl_port_get_parent(iter); ++ } ++ ++ return NULL; ++} ++ + static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, + const char *__ident) + { +@@ -58,6 +153,31 @@ static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, + return NULL; + } + ++static struct cxl_port *util_cxl_port_filter_by_bus(struct cxl_port *port, ++ const char *__ident) ++{ ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ struct cxl_bus *bus; ++ ++ if (!__ident) ++ return port; ++ ++ if (cxl_port_is_root(port)) { ++ bus = cxl_port_to_bus(port); ++ bus = util_cxl_bus_filter(bus, __ident); ++ return bus ? port : NULL; ++ } ++ ++ cxl_bus_foreach(ctx, bus) { ++ if (!util_cxl_bus_filter(bus, __ident)) ++ continue; ++ if (bus == cxl_port_get_bus(port)) ++ return port; ++ } ++ ++ return NULL; ++} ++ + static struct cxl_memdev * + util_cxl_memdev_serial_filter(struct cxl_memdev *memdev, const char *__serials) + { +@@ -169,10 +289,82 @@ static void splice_array(struct cxl_filter_params *p, struct json_object *jobjs, + json_object_put(jobjs); + } + ++static bool cond_add_put_array(struct json_object *jobj, const char *key, ++ struct json_object *array) ++{ ++ if (jobj && array && json_object_array_length(array) > 0) { ++ json_object_object_add(jobj, key, array); ++ return true; ++ } else { ++ json_object_put(array); ++ return false; ++ } ++} ++ ++static bool cond_add_put_array_suffix(struct json_object *jobj, const char *key, ++ const char *suffix, ++ struct json_object *array) ++{ ++ char *name; ++ bool rc; ++ ++ if (asprintf(&name, "%s:%s", key, suffix) < 0) ++ return false; ++ rc = cond_add_put_array(jobj, name, array); ++ free(name); ++ return rc; ++} ++ ++static struct json_object *pick_array(struct json_object *child, ++ struct json_object *container) ++{ ++ if (child) ++ return child; ++ if (container) ++ return container; ++ return NULL; ++} ++ ++static void walk_child_ports(struct cxl_port *parent_port, ++ struct cxl_filter_params *p, ++ struct json_object *jports, ++ unsigned long flags) ++{ ++ struct cxl_port *port; ++ ++ cxl_port_foreach(parent_port, port) { ++ const char *devname = cxl_port_get_devname(port); ++ struct json_object *jport = NULL; ++ struct json_object *jchildports = NULL; ++ ++ if (!util_cxl_port_filter(port, p->port_filter, pf_mode(p))) ++ goto walk_children; ++ if (!util_cxl_port_filter_by_bus(port, p->bus_filter)) ++ goto walk_children; ++ if (!p->idle && !cxl_port_is_enabled(port)) ++ continue; ++ if (p->ports) ++ jport = util_cxl_port_to_json(port, flags); ++ if (!jport) ++ continue; ++ json_object_array_add(jports, jport); ++ jchildports = json_object_new_array(); ++ if (!jchildports) { ++ err(p, "%s: failed to enumerate child ports\n", ++ devname); ++ continue; ++ } ++walk_children: ++ walk_child_ports(port, p, pick_array(jchildports, jports), ++ flags); ++ cond_add_put_array_suffix(jport, "ports", devname, jchildports); ++ } ++} ++ + int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + { ++ struct json_object *jdevs = NULL, *jbuses = NULL, *jports = NULL; + struct json_object *jplatform = json_object_new_array(); +- struct json_object *jdevs = NULL, *jbuses = NULL; + unsigned long flags = params_to_flags(p); + struct cxl_memdev *memdev; + int top_level_objs = 0; +@@ -191,6 +383,10 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + if (!jbuses) + goto err; + ++ jports = json_object_new_array(); ++ if (!jports) ++ goto err; ++ + cxl_memdev_foreach(ctx, memdev) { + struct json_object *jdev; + +@@ -208,10 +404,15 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + } + + cxl_bus_foreach(ctx, bus) { +- struct json_object *jbus; ++ struct json_object *jbus = NULL; ++ struct json_object *jchildports = NULL; ++ struct cxl_port *port = cxl_bus_get_port(bus); ++ const char *devname = cxl_bus_get_devname(bus); + + if (!util_cxl_bus_filter(bus, p->bus_filter)) +- continue; ++ goto walk_children; ++ if (!util_cxl_port_filter(port, p->port_filter, pf_mode(p))) ++ goto walk_children; + if (p->buses) { + jbus = util_cxl_bus_to_json(bus, flags); + if (!jbus) { +@@ -219,16 +420,32 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + continue; + } + json_object_array_add(jbuses, jbus); ++ if (p->ports) { ++ jchildports = json_object_new_array(); ++ if (!jchildports) { ++ err(p, ++ "%s: failed to enumerate child ports\n", ++ devname); ++ continue; ++ } ++ } + } ++walk_children: ++ walk_child_ports(port, p, pick_array(jchildports, jports), ++ flags); ++ cond_add_put_array_suffix(jbus, "ports", devname, jchildports); + } + + if (json_object_array_length(jdevs)) + top_level_objs++; + if (json_object_array_length(jbuses)) + top_level_objs++; ++ if (json_object_array_length(jports)) ++ top_level_objs++; + + splice_array(p, jdevs, jplatform, "anon memdevs", top_level_objs > 1); + splice_array(p, jbuses, jplatform, "buses", top_level_objs > 1); ++ splice_array(p, jports, jplatform, "ports", top_level_objs > 1); + + util_display_json_array(stdout, jplatform, flags); + +@@ -236,6 +453,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + err: + json_object_put(jdevs); + json_object_put(jbuses); ++ json_object_put(jports); + json_object_put(jplatform); + return -ENOMEM; + } +diff --git a/cxl/filter.h b/cxl/filter.h +index d41e757..0d83304 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -10,7 +10,10 @@ struct cxl_filter_params { + const char *memdev_filter; + const char *serial_filter; + const char *bus_filter; ++ const char *port_filter; ++ bool single; + bool memdevs; ++ bool ports; + bool buses; + bool idle; + bool human; +@@ -22,4 +25,5 @@ struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + const char *__ident, + const char *serials); + int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *param); ++bool cxl_filter_has(const char *needle, const char *__filter); + #endif /* _CXL_UTIL_FILTER_H_ */ +diff --git a/cxl/json.c b/cxl/json.c +index a584594..d9f864e 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -242,3 +242,26 @@ struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus, + + return jbus; + } ++ ++struct json_object *util_cxl_port_to_json(struct cxl_port *port, ++ unsigned long flags) ++{ ++ const char *devname = cxl_port_get_devname(port); ++ struct json_object *jport, *jobj; ++ ++ jport = json_object_new_object(); ++ if (!jport) ++ return NULL; ++ ++ jobj = json_object_new_string(devname); ++ if (jobj) ++ json_object_object_add(jport, "port", jobj); ++ ++ if (!cxl_port_is_enabled(port)) { ++ jobj = json_object_new_string("disabled"); ++ if (jobj) ++ json_object_object_add(jport, "state", jobj); ++ } ++ ++ return jport; ++} +diff --git a/cxl/json.h b/cxl/json.h +index 4abf6e5..36653db 100644 +--- a/cxl/json.h ++++ b/cxl/json.h +@@ -8,4 +8,7 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + struct cxl_bus; + struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus, + unsigned long flags); ++struct cxl_port; ++struct json_object *util_cxl_port_to_json(struct cxl_port *port, ++ unsigned long flags); + #endif /* __CXL_UTIL_JSON_H__ */ +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 8548a45..03eff3c 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -66,15 +66,27 @@ static void free_memdev(struct cxl_memdev *memdev, struct list_head *head) + free(memdev); + } + ++static void free_port(struct cxl_port *port, struct list_head *head); + static void __free_port(struct cxl_port *port, struct list_head *head) + { ++ struct cxl_port *child, *_c; ++ + if (head) + list_del_from(head, &port->list); ++ list_for_each_safe(&port->child_ports, child, _c, list) ++ free_port(child, &port->child_ports); ++ kmod_module_unref(port->module); + free(port->dev_buf); + free(port->dev_path); + free(port->uport); + } + ++static void free_port(struct cxl_port *port, struct list_head *head) ++{ ++ __free_port(port, head); ++ free(port); ++} ++ + static void free_bus(struct cxl_bus *bus, struct list_head *head) + { + __free_port(&bus->port, head); +@@ -471,10 +483,12 @@ CXL_EXPORT int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev) + return is_enabled(path); + } + +-static int cxl_port_init(struct cxl_port *port, struct cxl_ctx *ctx, int id, ++static int cxl_port_init(struct cxl_port *port, struct cxl_port *parent_port, ++ enum cxl_port_type type, struct cxl_ctx *ctx, int id, + const char *cxlport_base) + { + char *path = calloc(1, strlen(cxlport_base) + 100); ++ char buf[SYSFS_ATTR_SIZE]; + size_t rc; + + if (!path) +@@ -482,6 +496,10 @@ static int cxl_port_init(struct cxl_port *port, struct cxl_ctx *ctx, int id, + + port->id = id; + port->ctx = ctx; ++ port->type = type; ++ port->parent = parent_port; ++ ++ list_head_init(&port->child_ports); + + port->dev_path = strdup(cxlport_base); + if (!port->dev_path) +@@ -499,6 +517,10 @@ static int cxl_port_init(struct cxl_port *port, struct cxl_ctx *ctx, int id, + if (!port->uport) + goto err; + ++ sprintf(path, "%s/modalias", cxlport_base); ++ if (sysfs_read_attr(ctx, path, buf) == 0) ++ port->module = util_modalias_to_module(ctx, buf); ++ + return 0; + err: + free(port->dev_path); +@@ -507,6 +529,135 @@ err: + return -ENOMEM; + } + ++static void *add_cxl_port(void *parent, int id, const char *cxlport_base) ++{ ++ const char *devname = devpath_to_devname(cxlport_base); ++ struct cxl_port *port, *port_dup; ++ struct cxl_port *parent_port = parent; ++ struct cxl_ctx *ctx = cxl_port_get_ctx(parent_port); ++ int rc; ++ ++ dbg(ctx, "%s: base: \'%s\'\n", devname, cxlport_base); ++ ++ port = calloc(1, sizeof(*port)); ++ if (!port) ++ return NULL; ++ ++ rc = cxl_port_init(port, parent_port, CXL_PORT_SWITCH, ctx, id, ++ cxlport_base); ++ if (rc) ++ goto err; ++ ++ cxl_port_foreach(parent_port, port_dup) ++ if (port_dup->id == port->id) { ++ free_port(port, NULL); ++ return port_dup; ++ } ++ ++ list_add(&parent_port->child_ports, &port->list); ++ return port; ++ ++err: ++ free(port); ++ return NULL; ++ ++} ++ ++static void cxl_ports_init(struct cxl_port *port) ++{ ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ ++ if (port->ports_init) ++ return; ++ ++ port->ports_init = 1; ++ ++ sysfs_device_parse(ctx, port->dev_path, "port", port, add_cxl_port); ++} ++ ++CXL_EXPORT struct cxl_ctx *cxl_port_get_ctx(struct cxl_port *port) ++{ ++ return port->ctx; ++} ++ ++CXL_EXPORT struct cxl_port *cxl_port_get_first(struct cxl_port *port) ++{ ++ cxl_ports_init(port); ++ ++ return list_top(&port->child_ports, struct cxl_port, list); ++} ++ ++CXL_EXPORT struct cxl_port *cxl_port_get_next(struct cxl_port *port) ++{ ++ struct cxl_port *parent_port = port->parent; ++ ++ return list_next(&parent_port->child_ports, port, list); ++} ++ ++CXL_EXPORT const char *cxl_port_get_devname(struct cxl_port *port) ++{ ++ return devpath_to_devname(port->dev_path); ++} ++ ++CXL_EXPORT int cxl_port_get_id(struct cxl_port *port) ++{ ++ return port->id; ++} ++ ++CXL_EXPORT struct cxl_port *cxl_port_get_parent(struct cxl_port *port) ++{ ++ return port->parent; ++} ++ ++CXL_EXPORT bool cxl_port_is_root(struct cxl_port *port) ++{ ++ return port->type == CXL_PORT_ROOT; ++} ++ ++CXL_EXPORT bool cxl_port_is_switch(struct cxl_port *port) ++{ ++ return port->type == CXL_PORT_SWITCH; ++} ++ ++CXL_EXPORT struct cxl_bus *cxl_port_get_bus(struct cxl_port *port) ++{ ++ struct cxl_bus *bus; ++ ++ if (!cxl_port_is_enabled(port)) ++ return NULL; ++ ++ if (port->bus) ++ return port->bus; ++ ++ while (port->parent) ++ port = port->parent; ++ ++ bus = container_of(port, typeof(*bus), port); ++ port->bus = bus; ++ return bus; ++} ++ ++CXL_EXPORT int cxl_port_is_enabled(struct cxl_port *port) ++{ ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ char *path = port->dev_buf; ++ int len = port->buf_len; ++ ++ if (snprintf(path, len, "%s/driver", port->dev_path) >= len) { ++ err(ctx, "%s: buffer too small!\n", cxl_port_get_devname(port)); ++ return 0; ++ } ++ ++ return is_enabled(path); ++} ++ ++CXL_EXPORT struct cxl_bus *cxl_port_to_bus(struct cxl_port *port) ++{ ++ if (!cxl_port_is_root(port)) ++ return NULL; ++ return container_of(port, struct cxl_bus, port); ++} ++ + static void *add_cxl_bus(void *parent, int id, const char *cxlbus_base) + { + const char *devname = devpath_to_devname(cxlbus_base); +@@ -522,7 +673,7 @@ static void *add_cxl_bus(void *parent, int id, const char *cxlbus_base) + return NULL; + + port = &bus->port; +- rc = cxl_port_init(port, ctx, id, cxlbus_base); ++ rc = cxl_port_init(port, NULL, CXL_PORT_ROOT, ctx, id, cxlbus_base); + if (rc) + goto err; + +@@ -579,6 +730,11 @@ CXL_EXPORT int cxl_bus_get_id(struct cxl_bus *bus) + return port->id; + } + ++CXL_EXPORT struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus) ++{ ++ return &bus->port; ++} ++ + CXL_EXPORT const char *cxl_bus_get_provider(struct cxl_bus *bus) + { + struct cxl_port *port = &bus->port; +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 781ff99..a7e923f 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -82,4 +82,16 @@ global: + cxl_bus_get_provider; + cxl_bus_get_devname; + cxl_bus_get_id; ++ cxl_bus_get_port; ++ cxl_port_get_first; ++ cxl_port_get_next; ++ cxl_port_get_devname; ++ cxl_port_get_id; ++ cxl_port_get_ctx; ++ cxl_port_is_enabled; ++ cxl_port_get_parent; ++ cxl_port_is_root; ++ cxl_port_is_switch; ++ cxl_port_to_bus; ++ cxl_port_get_bus; + } LIBCXL_1; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 0758d05..637f90d 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -34,14 +34,25 @@ struct cxl_memdev { + unsigned long long serial; + }; + ++enum cxl_port_type { ++ CXL_PORT_ROOT, ++ CXL_PORT_SWITCH, ++}; ++ + struct cxl_port { + int id; + void *dev_buf; + size_t buf_len; + char *dev_path; + char *uport; ++ int ports_init; + struct cxl_ctx *ctx; ++ struct cxl_bus *bus; ++ enum cxl_port_type type; ++ struct cxl_port *parent; ++ struct kmod_module *module; + struct list_node list; ++ struct list_head child_ports; + }; + + struct cxl_bus { +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index da66eb2..efbb397 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -5,6 +5,7 @@ + + #include + #include ++#include + + #ifdef HAVE_UUID + #include +@@ -63,11 +64,29 @@ struct cxl_bus *cxl_bus_get_next(struct cxl_bus *bus); + const char *cxl_bus_get_provider(struct cxl_bus *bus); + const char *cxl_bus_get_devname(struct cxl_bus *bus); + int cxl_bus_get_id(struct cxl_bus *bus); ++struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus); + + #define cxl_bus_foreach(ctx, bus) \ + for (bus = cxl_bus_get_first(ctx); bus != NULL; \ + bus = cxl_bus_get_next(bus)) + ++struct cxl_port; ++struct cxl_port *cxl_port_get_first(struct cxl_port *parent); ++struct cxl_port *cxl_port_get_next(struct cxl_port *port); ++const char *cxl_port_get_devname(struct cxl_port *port); ++int cxl_port_get_id(struct cxl_port *port); ++struct cxl_ctx *cxl_port_get_ctx(struct cxl_port *port); ++int cxl_port_is_enabled(struct cxl_port *port); ++struct cxl_port *cxl_port_get_parent(struct cxl_port *port); ++bool cxl_port_is_root(struct cxl_port *port); ++bool cxl_port_is_switch(struct cxl_port *port); ++struct cxl_bus *cxl_port_to_bus(struct cxl_port *port); ++struct cxl_bus *cxl_port_get_bus(struct cxl_port *port); ++ ++#define cxl_port_foreach(parent, port) \ ++ for (port = cxl_port_get_first(parent); port != NULL; \ ++ port = cxl_port_get_next(port)) ++ + struct cxl_cmd; + const char *cxl_cmd_get_devname(struct cxl_cmd *cmd); + struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode); +diff --git a/cxl/list.c b/cxl/list.c +index 9500e61..1ef91b4 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -25,6 +25,11 @@ static const struct option options[] = { + OPT_STRING('b', "bus", ¶m.bus_filter, "bus device name", + "filter by CXL bus device name(s)"), + OPT_BOOLEAN('B', "buses", ¶m.buses, "include CXL bus info"), ++ OPT_STRING('p', "port", ¶m.port_filter, "port device name", ++ "filter by CXL port device name(s)"), ++ OPT_BOOLEAN('P', "ports", ¶m.ports, "include CXL port info"), ++ OPT_BOOLEAN('S', "single", ¶m.single, ++ "skip listing descendant objects"), + OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), + OPT_BOOLEAN('u', "human", ¶m.human, + "use human friendly number formats "), +@@ -35,7 +40,7 @@ static const struct option options[] = { + + static int num_list_flags(void) + { +- return !!param.memdevs + !!param.buses; ++ return !!param.memdevs + !!param.buses + !!param.ports; + } + + int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) +@@ -53,11 +58,18 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + if (argc) + usage_with_options(u, options); + ++ if (param.single && !param.port_filter) { ++ error("-S/--single expects a port filter: -p/--port=\n"); ++ usage_with_options(u, options); ++ } ++ + if (num_list_flags() == 0) { + if (param.memdev_filter || param.serial_filter) + param.memdevs = true; + if (param.bus_filter) + param.buses = true; ++ if (param.port_filter) ++ param.ports = true; + if (num_list_flags() == 0) { + /* + * TODO: We likely want to list regions by default if +@@ -73,5 +85,8 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + + log_init(¶m.ctx, "cxl list", "CXL_LIST_LOG"); + ++ if (cxl_filter_has(param.port_filter, "root") && param.ports) ++ param.buses = true; ++ + return cxl_filter_walk(ctx, ¶m); + } +-- +2.27.0 + diff --git a/SOURCES/0104-cxl-list-Add-debug-option.patch b/SOURCES/0104-cxl-list-Add-debug-option.patch new file mode 100644 index 0000000..8cfb023 --- /dev/null +++ b/SOURCES/0104-cxl-list-Add-debug-option.patch @@ -0,0 +1,103 @@ +From 2a43dce3913b392a13a5ee918c8ee831a25d720e Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:53:24 -0800 +Subject: [PATCH 104/217] cxl/list: Add --debug option + +Add an option to turn on libray and cxl_filter_walk() messages. Gate it +based on the global ENABLE_DEBUG configuration setting. + +Link: https://lore.kernel.org/r/164298560409.3021641.11040422738199381922.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 6 ++++-- + cxl/filter.c | 3 +++ + cxl/list.c | 9 +++++++++ + 3 files changed, 16 insertions(+), 2 deletions(-) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 3076deb..42b6de6 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -200,9 +200,11 @@ OPTIONS + descendants of the individual ports that match the filter. By default + all descendant objects are listed. + +-include::human-option.txt[] ++--debug:: ++ If the cxl tool was built with debug enabled, turn on debug ++ messages. + +-include::verbose-option.txt[] ++include::human-option.txt[] + + include::../copyright.txt[] + +diff --git a/cxl/filter.c b/cxl/filter.c +index 8b79db3..32171a4 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -387,6 +387,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + if (!jports) + goto err; + ++ dbg(p, "walk memdevs\n"); + cxl_memdev_foreach(ctx, memdev) { + struct json_object *jdev; + +@@ -403,6 +404,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + } + } + ++ dbg(p, "walk buses\n"); + cxl_bus_foreach(ctx, bus) { + struct json_object *jbus = NULL; + struct json_object *jchildports = NULL; +@@ -431,6 +433,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + } + } + walk_children: ++ dbg(p, "walk ports\n"); + walk_child_ports(port, p, pick_array(jchildports, jports), + flags); + cond_add_put_array_suffix(jbus, "ports", devname, jchildports); +diff --git a/cxl/list.c b/cxl/list.c +index 1ef91b4..01ab19b 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -13,6 +13,7 @@ + #include "filter.h" + + static struct cxl_filter_params param; ++static bool debug; + + static const struct option options[] = { + OPT_STRING('m', "memdev", ¶m.memdev_filter, "memory device name(s)", +@@ -35,6 +36,9 @@ static const struct option options[] = { + "use human friendly number formats "), + OPT_BOOLEAN('H', "health", ¶m.health, + "include memory device health information "), ++#ifdef ENABLE_DEBUG ++ OPT_BOOLEAN(0, "debug", &debug, "debug list walk"), ++#endif + OPT_END(), + }; + +@@ -84,9 +88,14 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + } + + log_init(¶m.ctx, "cxl list", "CXL_LIST_LOG"); ++ if (debug) { ++ cxl_set_log_priority(ctx, LOG_DEBUG); ++ param.ctx.log_priority = LOG_DEBUG; ++ } + + if (cxl_filter_has(param.port_filter, "root") && param.ports) + param.buses = true; + ++ dbg(¶m, "walk topology\n"); + return cxl_filter_walk(ctx, ¶m); + } +-- +2.27.0 + diff --git a/SOURCES/0105-cxl-list-Add-endpoints.patch b/SOURCES/0105-cxl-list-Add-endpoints.patch new file mode 100644 index 0000000..959ca13 --- /dev/null +++ b/SOURCES/0105-cxl-list-Add-endpoints.patch @@ -0,0 +1,746 @@ +From 7eb06a5293531854e7a28666e955106094d3552b Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:53:29 -0800 +Subject: [PATCH 105/217] cxl/list: Add endpoints + +Endpoints are port-like objects that represent the HDM decoders at terminal +end of a decode chain. Unlike port decoders that route to downstream ports, +endpoint decoders route to endpoint DPA (Device Physical Address) ranges. + +Link: https://lore.kernel.org/r/164298560917.3021641.13753578554905796298.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .clang-format | 1 + + Documentation/cxl/cxl-list.txt | 16 ++++ + Documentation/cxl/lib/libcxl.txt | 31 ++++++- + cxl/filter.c | 147 ++++++++++++++++++++++++++++--- + cxl/filter.h | 2 + + cxl/json.c | 20 ++++- + cxl/json.h | 2 + + cxl/lib/libcxl.c | 107 ++++++++++++++++++++++ + cxl/lib/libcxl.sym | 9 ++ + cxl/lib/private.h | 10 +++ + cxl/libcxl.h | 15 ++++ + cxl/list.c | 13 ++- + 12 files changed, 355 insertions(+), 18 deletions(-) + +diff --git a/.clang-format b/.clang-format +index 391cd34..106bc5e 100644 +--- a/.clang-format ++++ b/.clang-format +@@ -80,6 +80,7 @@ ForEachMacros: + - 'cxl_memdev_foreach' + - 'cxl_bus_foreach' + - 'cxl_port_foreach' ++ - 'cxl_endpoint_foreach' + - 'daxctl_dev_foreach' + - 'daxctl_mapping_foreach' + - 'daxctl_region_foreach' +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 42b6de6..d342da2 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -190,6 +190,12 @@ OPTIONS + ---- + # cxl list -B + # cxl list -P -p root ++---- ++ Additionally, endpoint objects are also ports so the following commands ++ are also equivalent. ++---- ++# cxl list -E ++# cxl list -P -p endpoint + ---- + By default, only 'switch' ports are listed. + +@@ -200,6 +206,16 @@ OPTIONS + descendants of the individual ports that match the filter. By default + all descendant objects are listed. + ++-E:: ++--endpoints:: ++ Include endpoint objects (CXL Memory Device decoders) in the ++ listing. ++ ++-e:: ++--endpoint:: ++ Specify CXL endpoint device name(s), or device id(s) to filter ++ the emitted endpoint(s). ++ + --debug:: + If the cxl tool was built with debug enabled, turn on debug + messages. +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 804e9ca..eebab37 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -199,12 +199,41 @@ int cxl_port_get_id(struct cxl_port *port); + int cxl_port_is_enabled(struct cxl_port *port); + bool cxl_port_is_root(struct cxl_port *port); + bool cxl_port_is_switch(struct cxl_port *port); ++bool cxl_port_is_endpoint(struct cxl_port *port); + ---- + The port type is communicated via cxl_port_is_(). An 'enabled' port + is one that has succeeded in discovering the CXL component registers in + the host device and has enumerated its downstream ports. In order for a + memdev to be enabled for CXL memory operation all CXL ports in its +-ancestry must also be enabled. ++ancestry must also be enabled including a root port, an arbitrary number ++of intervening switch ports, and a terminal endpoint port. ++ ++ENDPOINTS ++--------- ++CXL endpoint objects encapsulate the set of host-managed device-memory ++(HDM) decoders in a physical memory device. The endpoint is the last hop ++in a decoder chain that translate SPA to DPA (system-physical-address to ++device-local-physical-address). ++ ++=== ENDPOINT: Enumeration ++---- ++struct cxl_endpoint *cxl_endpoint_get_first(struct cxl_port *parent); ++struct cxl_endpoint *cxl_endpoint_get_next(struct cxl_endpoint *endpoint); ++struct cxl_ctx *cxl_endpoint_get_ctx(struct cxl_endpoint *endpoint); ++struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint); ++struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint); ++ ++#define cxl_endpoint_foreach(port, endpoint) \ ++ for (endpoint = cxl_endpoint_get_first(port); endpoint != NULL; \ ++ endpoint = cxl_endpoint_get_next(endpoint)) ++---- ++ ++=== ENDPOINT: Attributes ++---- ++const char *cxl_endpoint_get_devname(struct cxl_endpoint *endpoint); ++int cxl_endpoint_get_id(struct cxl_endpoint *endpoint); ++int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint); ++---- + + include::../../copyright.txt[] + +diff --git a/cxl/filter.c b/cxl/filter.c +index 32171a4..5d80d1b 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -47,8 +47,42 @@ bool cxl_filter_has(const char *__filter, const char *needle) + return false; + } + ++static struct cxl_endpoint * ++util_cxl_endpoint_filter(struct cxl_endpoint *endpoint, const char *__ident) ++{ ++ char *ident, *save; ++ const char *arg; ++ int endpoint_id; ++ ++ if (!__ident) ++ return endpoint; ++ ++ ident = strdup(__ident); ++ if (!ident) ++ return NULL; ++ ++ for (arg = strtok_r(ident, which_sep(__ident), &save); arg; ++ arg = strtok_r(NULL, which_sep(__ident), &save)) { ++ if (strcmp(arg, "all") == 0) ++ break; ++ ++ if ((sscanf(arg, "%d", &endpoint_id) == 1 || ++ sscanf(arg, "endpoint%d", &endpoint_id) == 1) && ++ cxl_endpoint_get_id(endpoint) == endpoint_id) ++ break; ++ ++ if (strcmp(arg, cxl_endpoint_get_devname(endpoint)) == 0) ++ break; ++ } ++ ++ free(ident); ++ if (arg) ++ return endpoint; ++ return NULL; ++} ++ + static struct cxl_port *__util_cxl_port_filter(struct cxl_port *port, +- const char *__ident) ++ const char *__ident) + { + char *ident, *save; + const char *arg; +@@ -72,6 +106,9 @@ static struct cxl_port *__util_cxl_port_filter(struct cxl_port *port, + if (strcmp(arg, "switch") == 0 && cxl_port_is_switch(port)) + break; + ++ if (strcmp(arg, "endpoint") == 0 && cxl_port_is_endpoint(port)) ++ break; ++ + if ((sscanf(arg, "%d", &port_id) == 1 || + sscanf(arg, "port%d", &port_id) == 1) && + cxl_port_get_id(port) == port_id) +@@ -116,6 +153,24 @@ static struct cxl_port *util_cxl_port_filter(struct cxl_port *port, + return NULL; + } + ++static struct cxl_endpoint * ++util_cxl_endpoint_filter_by_port(struct cxl_endpoint *endpoint, ++ const char *ident, ++ enum cxl_port_filter_mode mode) ++{ ++ struct cxl_port *iter = cxl_endpoint_get_port(endpoint); ++ ++ if (util_cxl_port_filter(iter, ident, CXL_PF_SINGLE)) ++ return endpoint; ++ iter = cxl_port_get_parent(iter); ++ if (!iter) ++ return NULL; ++ if (util_cxl_port_filter(iter, ident, mode)) ++ return endpoint; ++ ++ return NULL; ++} ++ + static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, + const char *__ident) + { +@@ -325,10 +380,34 @@ static struct json_object *pick_array(struct json_object *child, + return NULL; + } + ++static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p, ++ struct json_object *jeps, unsigned long flags) ++{ ++ struct cxl_endpoint *endpoint; ++ ++ cxl_endpoint_foreach(port, endpoint) { ++ struct cxl_port *ep_port = cxl_endpoint_get_port(endpoint); ++ struct json_object *jendpoint; ++ ++ if (!util_cxl_endpoint_filter(endpoint, p->endpoint_filter)) ++ continue; ++ if (!util_cxl_port_filter_by_bus(ep_port, p->bus_filter)) ++ continue; ++ if (!util_cxl_endpoint_filter_by_port(endpoint, p->port_filter, ++ pf_mode(p))) ++ continue; ++ if (!p->idle && !cxl_endpoint_is_enabled(endpoint)) ++ continue; ++ jendpoint = util_cxl_endpoint_to_json(endpoint, flags); ++ if (jendpoint) ++ json_object_array_add(jeps, jendpoint); ++ } ++} ++ + static void walk_child_ports(struct cxl_port *parent_port, + struct cxl_filter_params *p, + struct json_object *jports, +- unsigned long flags) ++ struct json_object *jeps, unsigned long flags) + { + struct cxl_port *port; + +@@ -336,6 +415,7 @@ static void walk_child_ports(struct cxl_port *parent_port, + const char *devname = cxl_port_get_devname(port); + struct json_object *jport = NULL; + struct json_object *jchildports = NULL; ++ struct json_object *jchildendpoints = NULL; + + if (!util_cxl_port_filter(port, p->port_filter, pf_mode(p))) + goto walk_children; +@@ -343,21 +423,41 @@ static void walk_child_ports(struct cxl_port *parent_port, + goto walk_children; + if (!p->idle && !cxl_port_is_enabled(port)) + continue; +- if (p->ports) ++ if (p->ports) { + jport = util_cxl_port_to_json(port, flags); +- if (!jport) +- continue; +- json_object_array_add(jports, jport); +- jchildports = json_object_new_array(); +- if (!jchildports) { +- err(p, "%s: failed to enumerate child ports\n", +- devname); +- continue; ++ if (!jport) { ++ err(p, "%s: failed to list\n", devname); ++ continue; ++ } ++ json_object_array_add(jports, jport); ++ jchildports = json_object_new_array(); ++ if (!jchildports) { ++ err(p, "%s: failed to enumerate child ports\n", ++ devname); ++ continue; ++ } ++ } ++ ++ if (p->ports && p->endpoints) { ++ jchildendpoints = json_object_new_array(); ++ if (!jchildendpoints) { ++ err(p, ++ "%s: failed to enumerate child endpoints\n", ++ devname); ++ continue; ++ } + } ++ + walk_children: ++ if (p->endpoints) ++ walk_endpoints(port, p, pick_array(jchildendpoints, jeps), ++ flags); ++ + walk_child_ports(port, p, pick_array(jchildports, jports), +- flags); ++ pick_array(jchildendpoints, jeps), flags); + cond_add_put_array_suffix(jport, "ports", devname, jchildports); ++ cond_add_put_array_suffix(jport, "endpoints", devname, ++ jchildendpoints); + } + } + +@@ -366,6 +466,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + struct json_object *jdevs = NULL, *jbuses = NULL, *jports = NULL; + struct json_object *jplatform = json_object_new_array(); + unsigned long flags = params_to_flags(p); ++ struct json_object *jeps = NULL; + struct cxl_memdev *memdev; + int top_level_objs = 0; + struct cxl_bus *bus; +@@ -387,6 +488,10 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + if (!jports) + goto err; + ++ jeps = json_object_new_array(); ++ if (!jeps) ++ goto err; ++ + dbg(p, "walk memdevs\n"); + cxl_memdev_foreach(ctx, memdev) { + struct json_object *jdev; +@@ -408,6 +513,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + cxl_bus_foreach(ctx, bus) { + struct json_object *jbus = NULL; + struct json_object *jchildports = NULL; ++ struct json_object *jchildeps = NULL; + struct cxl_port *port = cxl_bus_get_port(bus); + const char *devname = cxl_bus_get_devname(bus); + +@@ -431,12 +537,23 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + continue; + } + } ++ if (p->endpoints) { ++ jchildeps = json_object_new_array(); ++ if (!jchildeps) { ++ err(p, ++ "%s: failed to enumerate child endpoints\n", ++ devname); ++ continue; ++ } ++ } + } + walk_children: + dbg(p, "walk ports\n"); + walk_child_ports(port, p, pick_array(jchildports, jports), +- flags); ++ pick_array(jchildeps, jeps), flags); + cond_add_put_array_suffix(jbus, "ports", devname, jchildports); ++ cond_add_put_array_suffix(jbus, "endpoints", devname, ++ jchildeps); + } + + if (json_object_array_length(jdevs)) +@@ -445,10 +562,13 @@ walk_children: + top_level_objs++; + if (json_object_array_length(jports)) + top_level_objs++; ++ if (json_object_array_length(jeps)) ++ top_level_objs++; + + splice_array(p, jdevs, jplatform, "anon memdevs", top_level_objs > 1); + splice_array(p, jbuses, jplatform, "buses", top_level_objs > 1); + splice_array(p, jports, jplatform, "ports", top_level_objs > 1); ++ splice_array(p, jeps, jplatform, "endpoints", top_level_objs > 1); + + util_display_json_array(stdout, jplatform, flags); + +@@ -457,6 +577,7 @@ err: + json_object_put(jdevs); + json_object_put(jbuses); + json_object_put(jports); ++ json_object_put(jeps); + json_object_put(jplatform); + return -ENOMEM; + } +diff --git a/cxl/filter.h b/cxl/filter.h +index 0d83304..bbd341c 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -11,7 +11,9 @@ struct cxl_filter_params { + const char *serial_filter; + const char *bus_filter; + const char *port_filter; ++ const char *endpoint_filter; + bool single; ++ bool endpoints; + bool memdevs; + bool ports; + bool buses; +diff --git a/cxl/json.c b/cxl/json.c +index d9f864e..08f6192 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -243,8 +243,9 @@ struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus, + return jbus; + } + +-struct json_object *util_cxl_port_to_json(struct cxl_port *port, +- unsigned long flags) ++static struct json_object *__util_cxl_port_to_json(struct cxl_port *port, ++ const char *name_key, ++ unsigned long flags) + { + const char *devname = cxl_port_get_devname(port); + struct json_object *jport, *jobj; +@@ -255,7 +256,7 @@ struct json_object *util_cxl_port_to_json(struct cxl_port *port, + + jobj = json_object_new_string(devname); + if (jobj) +- json_object_object_add(jport, "port", jobj); ++ json_object_object_add(jport, name_key, jobj); + + if (!cxl_port_is_enabled(port)) { + jobj = json_object_new_string("disabled"); +@@ -265,3 +266,16 @@ struct json_object *util_cxl_port_to_json(struct cxl_port *port, + + return jport; + } ++ ++struct json_object *util_cxl_port_to_json(struct cxl_port *port, ++ unsigned long flags) ++{ ++ return __util_cxl_port_to_json(port, "port", flags); ++} ++ ++struct json_object *util_cxl_endpoint_to_json(struct cxl_endpoint *endpoint, ++ unsigned long flags) ++{ ++ return __util_cxl_port_to_json(cxl_endpoint_get_port(endpoint), ++ "endpoint", flags); ++} +diff --git a/cxl/json.h b/cxl/json.h +index 36653db..8f45190 100644 +--- a/cxl/json.h ++++ b/cxl/json.h +@@ -11,4 +11,6 @@ struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus, + struct cxl_port; + struct json_object *util_cxl_port_to_json(struct cxl_port *port, + unsigned long flags); ++struct json_object *util_cxl_endpoint_to_json(struct cxl_endpoint *endpoint, ++ unsigned long flags); + #endif /* __CXL_UTIL_JSON_H__ */ +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 03eff3c..a25e715 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -67,14 +67,18 @@ static void free_memdev(struct cxl_memdev *memdev, struct list_head *head) + } + + static void free_port(struct cxl_port *port, struct list_head *head); ++static void free_endpoint(struct cxl_endpoint *endpoint, struct list_head *head); + static void __free_port(struct cxl_port *port, struct list_head *head) + { + struct cxl_port *child, *_c; ++ struct cxl_endpoint *endpoint, *_e; + + if (head) + list_del_from(head, &port->list); + list_for_each_safe(&port->child_ports, child, _c, list) + free_port(child, &port->child_ports); ++ list_for_each_safe(&port->endpoints, endpoint, _e, port.list) ++ free_endpoint(endpoint, &port->endpoints); + kmod_module_unref(port->module); + free(port->dev_buf); + free(port->dev_path); +@@ -87,6 +91,12 @@ static void free_port(struct cxl_port *port, struct list_head *head) + free(port); + } + ++static void free_endpoint(struct cxl_endpoint *endpoint, struct list_head *head) ++{ ++ __free_port(&endpoint->port, head); ++ free(endpoint); ++} ++ + static void free_bus(struct cxl_bus *bus, struct list_head *head) + { + __free_port(&bus->port, head); +@@ -500,6 +510,7 @@ static int cxl_port_init(struct cxl_port *port, struct cxl_port *parent_port, + port->parent = parent_port; + + list_head_init(&port->child_ports); ++ list_head_init(&port->endpoints); + + port->dev_path = strdup(cxlport_base); + if (!port->dev_path) +@@ -529,6 +540,97 @@ err: + return -ENOMEM; + } + ++static void *add_cxl_endpoint(void *parent, int id, const char *cxlep_base) ++{ ++ const char *devname = devpath_to_devname(cxlep_base); ++ struct cxl_endpoint *endpoint, *endpoint_dup; ++ struct cxl_port *port = parent; ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ int rc; ++ ++ dbg(ctx, "%s: base: \'%s\'\n", devname, cxlep_base); ++ ++ endpoint = calloc(1, sizeof(*endpoint)); ++ if (!endpoint) ++ return NULL; ++ ++ rc = cxl_port_init(&endpoint->port, port, CXL_PORT_ENDPOINT, ctx, id, ++ cxlep_base); ++ if (rc) ++ goto err; ++ ++ cxl_endpoint_foreach(port, endpoint_dup) ++ if (endpoint_dup->port.id == endpoint->port.id) { ++ free_endpoint(endpoint, NULL); ++ return endpoint_dup; ++ } ++ ++ list_add(&port->endpoints, &endpoint->port.list); ++ return endpoint; ++ ++err: ++ free(endpoint); ++ return NULL; ++ ++} ++ ++static void cxl_endpoints_init(struct cxl_port *port) ++{ ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ ++ if (port->endpoints_init) ++ return; ++ ++ port->endpoints_init = 1; ++ ++ sysfs_device_parse(ctx, port->dev_path, "endpoint", port, ++ add_cxl_endpoint); ++} ++ ++CXL_EXPORT struct cxl_ctx *cxl_endpoint_get_ctx(struct cxl_endpoint *endpoint) ++{ ++ return endpoint->port.ctx; ++} ++ ++CXL_EXPORT struct cxl_endpoint *cxl_endpoint_get_first(struct cxl_port *port) ++{ ++ cxl_endpoints_init(port); ++ ++ return list_top(&port->endpoints, struct cxl_endpoint, port.list); ++} ++ ++CXL_EXPORT struct cxl_endpoint *cxl_endpoint_get_next(struct cxl_endpoint *endpoint) ++{ ++ struct cxl_port *port = endpoint->port.parent; ++ ++ return list_next(&port->endpoints, endpoint, port.list); ++} ++ ++CXL_EXPORT const char *cxl_endpoint_get_devname(struct cxl_endpoint *endpoint) ++{ ++ return devpath_to_devname(endpoint->port.dev_path); ++} ++ ++CXL_EXPORT int cxl_endpoint_get_id(struct cxl_endpoint *endpoint) ++{ ++ return endpoint->port.id; ++} ++ ++CXL_EXPORT struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint) ++{ ++ return endpoint->port.parent; ++} ++ ++CXL_EXPORT struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint) ++{ ++ return &endpoint->port; ++} ++ ++CXL_EXPORT int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint) ++{ ++ return cxl_port_is_enabled(&endpoint->port); ++} ++ + static void *add_cxl_port(void *parent, int id, const char *cxlport_base) + { + const char *devname = devpath_to_devname(cxlport_base); +@@ -619,6 +721,11 @@ CXL_EXPORT bool cxl_port_is_switch(struct cxl_port *port) + return port->type == CXL_PORT_SWITCH; + } + ++CXL_EXPORT bool cxl_port_is_endpoint(struct cxl_port *port) ++{ ++ return port->type == CXL_PORT_ENDPOINT; ++} ++ + CXL_EXPORT struct cxl_bus *cxl_port_get_bus(struct cxl_port *port) + { + struct cxl_bus *bus; +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index a7e923f..7a51a0c 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -93,5 +93,14 @@ global: + cxl_port_is_root; + cxl_port_is_switch; + cxl_port_to_bus; ++ cxl_port_is_endpoint; + cxl_port_get_bus; ++ cxl_endpoint_get_first; ++ cxl_endpoint_get_next; ++ cxl_endpoint_get_devname; ++ cxl_endpoint_get_id; ++ cxl_endpoint_get_ctx; ++ cxl_endpoint_is_enabled; ++ cxl_endpoint_get_parent; ++ cxl_endpoint_get_port; + } LIBCXL_1; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 637f90d..cedd2f2 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -17,6 +17,7 @@ struct cxl_pmem { + char *dev_path; + }; + ++struct cxl_endpoint; + struct cxl_memdev { + int id, major, minor; + void *dev_buf; +@@ -32,11 +33,13 @@ struct cxl_memdev { + struct kmod_module *module; + struct cxl_pmem *pmem; + unsigned long long serial; ++ struct cxl_endpoint *endpoint; + }; + + enum cxl_port_type { + CXL_PORT_ROOT, + CXL_PORT_SWITCH, ++ CXL_PORT_ENDPOINT, + }; + + struct cxl_port { +@@ -46,6 +49,7 @@ struct cxl_port { + char *dev_path; + char *uport; + int ports_init; ++ int endpoints_init; + struct cxl_ctx *ctx; + struct cxl_bus *bus; + enum cxl_port_type type; +@@ -53,12 +57,18 @@ struct cxl_port { + struct kmod_module *module; + struct list_node list; + struct list_head child_ports; ++ struct list_head endpoints; + }; + + struct cxl_bus { + struct cxl_port port; + }; + ++struct cxl_endpoint { ++ struct cxl_port port; ++ struct cxl_memdev *memdev; ++}; ++ + enum cxl_cmd_query_status { + CXL_CMD_QUERY_NOT_RUN = 0, + CXL_CMD_QUERY_OK, +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index efbb397..f6ba9a1 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -81,12 +81,27 @@ struct cxl_port *cxl_port_get_parent(struct cxl_port *port); + bool cxl_port_is_root(struct cxl_port *port); + bool cxl_port_is_switch(struct cxl_port *port); + struct cxl_bus *cxl_port_to_bus(struct cxl_port *port); ++bool cxl_port_is_endpoint(struct cxl_port *port); + struct cxl_bus *cxl_port_get_bus(struct cxl_port *port); + + #define cxl_port_foreach(parent, port) \ + for (port = cxl_port_get_first(parent); port != NULL; \ + port = cxl_port_get_next(port)) + ++struct cxl_endpoint; ++struct cxl_endpoint *cxl_endpoint_get_first(struct cxl_port *parent); ++struct cxl_endpoint *cxl_endpoint_get_next(struct cxl_endpoint *endpoint); ++const char *cxl_endpoint_get_devname(struct cxl_endpoint *endpoint); ++int cxl_endpoint_get_id(struct cxl_endpoint *endpoint); ++struct cxl_ctx *cxl_endpoint_get_ctx(struct cxl_endpoint *endpoint); ++int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint); ++struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint); ++struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint); ++ ++#define cxl_endpoint_foreach(port, endpoint) \ ++ for (endpoint = cxl_endpoint_get_first(port); endpoint != NULL; \ ++ endpoint = cxl_endpoint_get_next(endpoint)) ++ + struct cxl_cmd; + const char *cxl_cmd_get_devname(struct cxl_cmd *cmd); + struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode); +diff --git a/cxl/list.c b/cxl/list.c +index 01ab19b..b15e01c 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -31,6 +31,11 @@ static const struct option options[] = { + OPT_BOOLEAN('P', "ports", ¶m.ports, "include CXL port info"), + OPT_BOOLEAN('S', "single", ¶m.single, + "skip listing descendant objects"), ++ OPT_STRING('e', "endpoint", ¶m.endpoint_filter, ++ "endpoint device name", ++ "filter by CXL endpoint device name(s)"), ++ OPT_BOOLEAN('E', "endpoints", ¶m.endpoints, ++ "include CXL endpoint info"), + OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), + OPT_BOOLEAN('u', "human", ¶m.human, + "use human friendly number formats "), +@@ -44,7 +49,8 @@ static const struct option options[] = { + + static int num_list_flags(void) + { +- return !!param.memdevs + !!param.buses + !!param.ports; ++ return !!param.memdevs + !!param.buses + !!param.ports + ++ !!param.endpoints; + } + + int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) +@@ -74,6 +80,8 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + param.buses = true; + if (param.port_filter) + param.ports = true; ++ if (param.endpoint_filter) ++ param.endpoints = true; + if (num_list_flags() == 0) { + /* + * TODO: We likely want to list regions by default if +@@ -96,6 +104,9 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + if (cxl_filter_has(param.port_filter, "root") && param.ports) + param.buses = true; + ++ if (cxl_filter_has(param.port_filter, "endpoint") && param.ports) ++ param.endpoints = true; ++ + dbg(¶m, "walk topology\n"); + return cxl_filter_walk(ctx, ¶m); + } +-- +2.27.0 + diff --git a/SOURCES/0106-cxl-list-Add-host-entries-for-port-like-objects.patch b/SOURCES/0106-cxl-list-Add-host-entries-for-port-like-objects.patch new file mode 100644 index 0000000..313b105 --- /dev/null +++ b/SOURCES/0106-cxl-list-Add-host-entries-for-port-like-objects.patch @@ -0,0 +1,154 @@ +From f39735be3c1157fdfa7dd5c781048a411ebe4dc5 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:53:34 -0800 +Subject: [PATCH 106/217] cxl/list: Add 'host' entries for port-like objects + +Add the device name of the "host" device for a given CXL port object. The +kernel calls this the 'uport' attribute. + +Link: https://lore.kernel.org/r/164298561473.3021641.16508989603599026269.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 9 +++++++++ + Documentation/cxl/lib/libcxl.txt | 5 +++++ + cxl/json.c | 4 ++++ + cxl/lib/libcxl.c | 10 ++++++++++ + cxl/lib/libcxl.sym | 2 ++ + cxl/libcxl.h | 2 ++ + 6 files changed, 32 insertions(+) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index d342da2..30b6161 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -210,6 +210,15 @@ OPTIONS + --endpoints:: + Include endpoint objects (CXL Memory Device decoders) in the + listing. ++---- ++# cxl list -E ++[ ++ { ++ "endpoint":"endpoint2", ++ "host":"mem0" ++ } ++] ++---- + + -e:: + --endpoint:: +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index eebab37..e4b372d 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -178,6 +178,7 @@ struct cxl_port *cxl_port_get_next(struct cxl_port *port); + struct cxl_port *cxl_port_get_parent(struct cxl_port *port); + struct cxl_bus *cxl_port_get_bus(struct cxl_port *port); + struct cxl_ctx *cxl_port_get_ctx(struct cxl_port *port); ++const char *cxl_port_get_host(struct cxl_port *port); + + #define cxl_port_foreach(parent, port) \ + for (port = cxl_port_get_first(parent); port != NULL; \ +@@ -192,6 +193,9 @@ as a parent object retrievable via cxl_port_get_parent(). + The root port of a hiearchy can be retrieved via any port instance in + that hierarchy via cxl_port_get_bus(). + ++The host of a port is the corresponding device name of the PCIe Root ++Port, or Switch Upstream Port with CXL capabilities. ++ + === PORT: Attributes + ---- + const char *cxl_port_get_devname(struct cxl_port *port); +@@ -222,6 +226,7 @@ struct cxl_endpoint *cxl_endpoint_get_next(struct cxl_endpoint *endpoint); + struct cxl_ctx *cxl_endpoint_get_ctx(struct cxl_endpoint *endpoint); + struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint); + struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint); ++const char *cxl_endpoint_get_host(struct cxl_endpoint *endpoint); + + #define cxl_endpoint_foreach(port, endpoint) \ + for (endpoint = cxl_endpoint_get_first(port); endpoint != NULL; \ +diff --git a/cxl/json.c b/cxl/json.c +index 08f6192..af3b4fe 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -258,6 +258,10 @@ static struct json_object *__util_cxl_port_to_json(struct cxl_port *port, + if (jobj) + json_object_object_add(jport, name_key, jobj); + ++ jobj = json_object_new_string(cxl_port_get_host(port)); ++ if (jobj) ++ json_object_object_add(jport, "host", jobj); ++ + if (!cxl_port_is_enabled(port)) { + jobj = json_object_new_string("disabled"); + if (jobj) +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index a25e715..5f48202 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -626,6 +626,11 @@ CXL_EXPORT struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint) + return &endpoint->port; + } + ++CXL_EXPORT const char *cxl_endpoint_get_host(struct cxl_endpoint *endpoint) ++{ ++ return cxl_port_get_host(&endpoint->port); ++} ++ + CXL_EXPORT int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint) + { + return cxl_port_is_enabled(&endpoint->port); +@@ -744,6 +749,11 @@ CXL_EXPORT struct cxl_bus *cxl_port_get_bus(struct cxl_port *port) + return bus; + } + ++CXL_EXPORT const char *cxl_port_get_host(struct cxl_port *port) ++{ ++ return devpath_to_devname(port->uport); ++} ++ + CXL_EXPORT int cxl_port_is_enabled(struct cxl_port *port) + { + struct cxl_ctx *ctx = cxl_port_get_ctx(port); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 7a51a0c..dc2863e 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -95,6 +95,7 @@ global: + cxl_port_to_bus; + cxl_port_is_endpoint; + cxl_port_get_bus; ++ cxl_port_get_host; + cxl_endpoint_get_first; + cxl_endpoint_get_next; + cxl_endpoint_get_devname; +@@ -103,4 +104,5 @@ global: + cxl_endpoint_is_enabled; + cxl_endpoint_get_parent; + cxl_endpoint_get_port; ++ cxl_endpoint_get_host; + } LIBCXL_1; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index f6ba9a1..a60777e 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -83,6 +83,7 @@ bool cxl_port_is_switch(struct cxl_port *port); + struct cxl_bus *cxl_port_to_bus(struct cxl_port *port); + bool cxl_port_is_endpoint(struct cxl_port *port); + struct cxl_bus *cxl_port_get_bus(struct cxl_port *port); ++const char *cxl_port_get_host(struct cxl_port *port); + + #define cxl_port_foreach(parent, port) \ + for (port = cxl_port_get_first(parent); port != NULL; \ +@@ -97,6 +98,7 @@ struct cxl_ctx *cxl_endpoint_get_ctx(struct cxl_endpoint *endpoint); + int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint); + struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint); + struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint); ++const char *cxl_endpoint_get_host(struct cxl_endpoint *endpoint); + + #define cxl_endpoint_foreach(port, endpoint) \ + for (endpoint = cxl_endpoint_get_first(port); endpoint != NULL; \ +-- +2.27.0 + diff --git a/SOURCES/0107-cxl-list-Add-host-entries-for-memdevs.patch b/SOURCES/0107-cxl-list-Add-host-entries-for-memdevs.patch new file mode 100644 index 0000000..244428c --- /dev/null +++ b/SOURCES/0107-cxl-list-Add-host-entries-for-memdevs.patch @@ -0,0 +1,175 @@ +From eec8c953a840a1cbdca63352c64cec3e48e86afe Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:53:39 -0800 +Subject: [PATCH 107/217] cxl/list: Add 'host' entries for memdevs + +For debugging CXL port connectivity issues it will be useful to have the +PCI device name for the memory expander in the 'memdev' listing. + +Link: https://lore.kernel.org/r/164298561980.3021641.9636572507721689266.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 3 ++- + Documentation/cxl/lib/libcxl.txt | 4 ++++ + cxl/json.c | 5 +++++ + cxl/lib/libcxl.c | 24 ++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 1 + + cxl/lib/private.h | 1 + + cxl/libcxl.h | 1 + + 7 files changed, 38 insertions(+), 1 deletion(-) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 30b6161..9c21ab7 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -43,7 +43,8 @@ EXAMPLE + "memdev":"mem0", + "pmem_size":268435456, + "ram_size":0, +- "serial":0 ++ "serial":0, ++ "host":"0000:35:00.0" + } + ] + +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index e4b372d..91fd33e 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -40,6 +40,7 @@ kernel, or to send data or trigger kernel operations for a given device. + struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx); + struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev); + struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev); ++const char *cxl_memdev_get_host(struct cxl_memdev *memdev) + + #define cxl_memdev_foreach(ctx, memdev) \ + for (memdev = cxl_memdev_get_first(ctx); \ +@@ -54,6 +55,9 @@ memory device commands, see the port, decoder, and endpoint APIs to + determine what if any CXL Memory Resources are reachable given a + specific memdev. + ++The host of a memdev is the PCIe Endpoint device that registered its CXL ++capabilities with the Linux CXL core. ++ + === MEMDEV: Attributes + ---- + int cxl_memdev_get_id(struct cxl_memdev *memdev); +diff --git a/cxl/json.c b/cxl/json.c +index af3b4fe..1868686 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -219,6 +219,11 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + if (jobj) + json_object_object_add(jdev, "serial", jobj); + } ++ ++ jobj = json_object_new_string(cxl_memdev_get_host(memdev)); ++ if (jobj) ++ json_object_object_add(jdev, "host", jobj); ++ + return jdev; + } + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 5f48202..c4ddc7d 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -63,6 +63,7 @@ static void free_memdev(struct cxl_memdev *memdev, struct list_head *head) + free(memdev->firmware_version); + free(memdev->dev_buf); + free(memdev->dev_path); ++ free(memdev->host); + free(memdev); + } + +@@ -297,6 +298,7 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + char *path = calloc(1, strlen(cxlmem_base) + 100); + struct cxl_ctx *ctx = parent; + struct cxl_memdev *memdev, *memdev_dup; ++ char *host, *rpath = NULL; + char buf[SYSFS_ATTR_SIZE]; + struct stat st; + +@@ -350,6 +352,22 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + if (!memdev->dev_path) + goto err_read; + ++ rpath = realpath(cxlmem_base, NULL); ++ if (!rpath) ++ goto err_read; ++ host = strrchr(rpath, '/'); ++ if (host) { ++ host[0] = '\0'; ++ host = strrchr(rpath, '/'); ++ } ++ if (!host) ++ goto err_read; ++ memdev->host = strdup(host + 1); ++ if (!memdev->host) ++ goto err_read; ++ free(rpath); ++ rpath = NULL; ++ + sprintf(path, "%s/firmware_version", cxlmem_base); + if (sysfs_read_attr(ctx, path, buf) < 0) + goto err_read; +@@ -381,6 +399,7 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + free(memdev->dev_buf); + free(memdev->dev_path); + free(memdev); ++ free(rpath); + err_dev: + free(path); + return NULL; +@@ -431,6 +450,11 @@ CXL_EXPORT const char *cxl_memdev_get_devname(struct cxl_memdev *memdev) + return devpath_to_devname(memdev->dev_path); + } + ++CXL_EXPORT const char *cxl_memdev_get_host(struct cxl_memdev *memdev) ++{ ++ return memdev->host; ++} ++ + CXL_EXPORT int cxl_memdev_get_major(struct cxl_memdev *memdev) + { + return memdev->major; +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index dc2863e..8f0688a 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -77,6 +77,7 @@ local: + LIBCXL_2 { + global: + cxl_memdev_get_serial; ++ cxl_memdev_get_host; + cxl_bus_get_first; + cxl_bus_get_next; + cxl_bus_get_provider; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index cedd2f2..b097bdf 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -22,6 +22,7 @@ struct cxl_memdev { + int id, major, minor; + void *dev_buf; + size_t buf_len; ++ char *host; + char *dev_path; + char *firmware_version; + struct cxl_ctx *ctx; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index a60777e..5487b55 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -38,6 +38,7 @@ struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev); + int cxl_memdev_get_id(struct cxl_memdev *memdev); + unsigned long long cxl_memdev_get_serial(struct cxl_memdev *memdev); + const char *cxl_memdev_get_devname(struct cxl_memdev *memdev); ++const char *cxl_memdev_get_host(struct cxl_memdev *memdev); + int cxl_memdev_get_major(struct cxl_memdev *memdev); + int cxl_memdev_get_minor(struct cxl_memdev *memdev); + struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev); +-- +2.27.0 + diff --git a/SOURCES/0108-cxl-list-Move-enabled-memdevs-underneath-their-endpo.patch b/SOURCES/0108-cxl-list-Move-enabled-memdevs-underneath-their-endpo.patch new file mode 100644 index 0000000..aa72462 --- /dev/null +++ b/SOURCES/0108-cxl-list-Move-enabled-memdevs-underneath-their-endpo.patch @@ -0,0 +1,499 @@ +From 41d6769393f449008abf934e815f137360889633 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:53:45 -0800 +Subject: [PATCH 108/217] cxl/list: Move enabled memdevs underneath their + endpoint + +When a memdev is enabled it means that the kernel was able to validate a +CXL connection from the CXL root, through intervening switches, and to the +endpoint. Reflect that state by listing memdevs as child objects of +endpoints, or aggregated into an array if individual endpoints are not +listed. + +Link: https://lore.kernel.org/r/164298562531.3021641.10620937879296964476.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 11 ++- + Documentation/cxl/lib/libcxl.txt | 2 + + cxl/filter.c | 130 ++++++++++++++++++++++++------- + cxl/json.c | 6 ++ + cxl/lib/libcxl.c | 97 +++++++++++++++++++++++ + cxl/lib/libcxl.sym | 3 + + cxl/libcxl.h | 4 + + 7 files changed, 223 insertions(+), 30 deletions(-) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 9c21ab7..1751868 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -19,7 +19,16 @@ Options can be specified to limit the output to specific objects. When a + single object type is specified the return json object is an array of + just those objects, when multiple objects types are specified the + returned the returned object may be an array of arrays with the inner +-array named for the given object type. ++array named for the given object type. The top-level arrays are ellided ++when the objects can nest under a higher object-type in the hierararchy. ++The potential top-level array names and their nesting properties are: ++ ++"anon memdevs":: (disabled memory devices) do not nest ++"buses":: do not nest ++"ports":: nest under buses ++"endpoints":: nest under ports or buses (if ports are not emitted) ++"memdevs":: nest under endpoints or ports (if endpoints are not ++ emitted) or buses (if endpoints and ports are not emitted) + + Filters can by specifed as either a single identidier, a space separated + quoted string, or a comma separated list. When multiple filter +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 91fd33e..73b0fb9 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -41,6 +41,7 @@ struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx); + struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev); + struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev); + const char *cxl_memdev_get_host(struct cxl_memdev *memdev) ++struct cxl_memdev *cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint); + + #define cxl_memdev_foreach(ctx, memdev) \ + for (memdev = cxl_memdev_get_first(ctx); \ +@@ -231,6 +232,7 @@ struct cxl_ctx *cxl_endpoint_get_ctx(struct cxl_endpoint *endpoint); + struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint); + struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint); + const char *cxl_endpoint_get_host(struct cxl_endpoint *endpoint); ++struct cxl_endpoint *cxl_memdev_get_endpoint(struct cxl_memdev *memdev); + + #define cxl_endpoint_foreach(port, endpoint) \ + for (endpoint = cxl_endpoint_get_first(port); endpoint != NULL; \ +diff --git a/cxl/filter.c b/cxl/filter.c +index 5d80d1b..2130816 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -381,13 +381,16 @@ static struct json_object *pick_array(struct json_object *child, + } + + static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p, +- struct json_object *jeps, unsigned long flags) ++ struct json_object *jeps, struct json_object *jdevs, ++ unsigned long flags) + { + struct cxl_endpoint *endpoint; + + cxl_endpoint_foreach(port, endpoint) { + struct cxl_port *ep_port = cxl_endpoint_get_port(endpoint); +- struct json_object *jendpoint; ++ const char *devname = cxl_endpoint_get_devname(endpoint); ++ struct json_object *jendpoint = NULL; ++ struct cxl_memdev *memdev; + + if (!util_cxl_endpoint_filter(endpoint, p->endpoint_filter)) + continue; +@@ -398,24 +401,54 @@ static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p, + continue; + if (!p->idle && !cxl_endpoint_is_enabled(endpoint)) + continue; +- jendpoint = util_cxl_endpoint_to_json(endpoint, flags); +- if (jendpoint) ++ if (p->endpoints) { ++ jendpoint = util_cxl_endpoint_to_json(endpoint, flags); ++ if (!jendpoint) { ++ err(p, "%s: failed to list\n", devname); ++ continue; ++ } + json_object_array_add(jeps, jendpoint); ++ } ++ if (p->memdevs) { ++ struct json_object *jobj; ++ ++ memdev = cxl_endpoint_get_memdev(endpoint); ++ if (!memdev) ++ continue; ++ if (!util_cxl_memdev_filter(memdev, p->memdev_filter, ++ p->serial_filter)) ++ continue; ++ if (!p->idle && !cxl_memdev_is_enabled(memdev)) ++ continue; ++ jobj = util_cxl_memdev_to_json(memdev, flags); ++ if (!jobj) { ++ err(p, "failed to json serialize %s\n", ++ cxl_memdev_get_devname(memdev)); ++ continue; ++ } ++ if (p->endpoints) ++ json_object_object_add(jendpoint, "memdev", ++ jobj); ++ else ++ json_object_array_add(jdevs, jobj); ++ } + } + } + + static void walk_child_ports(struct cxl_port *parent_port, + struct cxl_filter_params *p, + struct json_object *jports, +- struct json_object *jeps, unsigned long flags) ++ struct json_object *jeps, ++ struct json_object *jdevs, unsigned long flags) + { + struct cxl_port *port; + + cxl_port_foreach(parent_port, port) { + const char *devname = cxl_port_get_devname(port); + struct json_object *jport = NULL; ++ struct json_object *jchilddevs = NULL; + struct json_object *jchildports = NULL; +- struct json_object *jchildendpoints = NULL; ++ struct json_object *jchildeps = NULL; + + if (!util_cxl_port_filter(port, p->port_filter, pf_mode(p))) + goto walk_children; +@@ -436,28 +469,41 @@ static void walk_child_ports(struct cxl_port *parent_port, + devname); + continue; + } +- } + +- if (p->ports && p->endpoints) { +- jchildendpoints = json_object_new_array(); +- if (!jchildendpoints) { +- err(p, +- "%s: failed to enumerate child endpoints\n", +- devname); +- continue; ++ if (p->memdevs && !p->endpoints) { ++ jchilddevs = json_object_new_array(); ++ if (!jchilddevs) { ++ err(p, ++ "%s: failed to enumerate child memdevs\n", ++ devname); ++ continue; ++ } ++ } ++ ++ if (p->endpoints) { ++ jchildeps = json_object_new_array(); ++ if (!jchildeps) { ++ err(p, ++ "%s: failed to enumerate child endpoints\n", ++ devname); ++ continue; ++ } + } + } + + walk_children: +- if (p->endpoints) +- walk_endpoints(port, p, pick_array(jchildendpoints, jeps), +- flags); ++ if (p->endpoints || p->memdevs) ++ walk_endpoints(port, p, pick_array(jchildeps, jeps), ++ pick_array(jchilddevs, jdevs), flags); + + walk_child_ports(port, p, pick_array(jchildports, jports), +- pick_array(jchildendpoints, jeps), flags); ++ pick_array(jchildeps, jeps), ++ pick_array(jchilddevs, jdevs), flags); + cond_add_put_array_suffix(jport, "ports", devname, jchildports); + cond_add_put_array_suffix(jport, "endpoints", devname, +- jchildendpoints); ++ jchildeps); ++ cond_add_put_array_suffix(jport, "memdevs", devname, ++ jchilddevs); + } + } + +@@ -466,6 +512,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + struct json_object *jdevs = NULL, *jbuses = NULL, *jports = NULL; + struct json_object *jplatform = json_object_new_array(); + unsigned long flags = params_to_flags(p); ++ struct json_object *janondevs = NULL; + struct json_object *jeps = NULL; + struct cxl_memdev *memdev; + int top_level_objs = 0; +@@ -476,8 +523,8 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + return -ENOMEM; + } + +- jdevs = json_object_new_array(); +- if (!jdevs) ++ janondevs = json_object_new_array(); ++ if (!janondevs) + goto err; + + jbuses = json_object_new_array(); +@@ -492,20 +539,28 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + if (!jeps) + goto err; + ++ jdevs = json_object_new_array(); ++ if (!jdevs) ++ goto err; ++ + dbg(p, "walk memdevs\n"); + cxl_memdev_foreach(ctx, memdev) { +- struct json_object *jdev; ++ struct json_object *janondev; + + if (!util_cxl_memdev_filter(memdev, p->memdev_filter, + p->serial_filter)) + continue; ++ if (cxl_memdev_is_enabled(memdev)) ++ continue; ++ if (!p->idle) ++ continue; + if (p->memdevs) { +- jdev = util_cxl_memdev_to_json(memdev, flags); +- if (!jdev) { ++ janondev = util_cxl_memdev_to_json(memdev, flags); ++ if (!janondev) { + dbg(p, "memdev object allocation failure\n"); + continue; + } +- json_object_array_add(jdevs, jdev); ++ json_object_array_add(janondevs, janondev); + } + } + +@@ -513,6 +568,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + cxl_bus_foreach(ctx, bus) { + struct json_object *jbus = NULL; + struct json_object *jchildports = NULL; ++ struct json_object *jchilddevs = NULL; + struct json_object *jchildeps = NULL; + struct cxl_port *port = cxl_bus_get_port(bus); + const char *devname = cxl_bus_get_devname(bus); +@@ -546,17 +602,29 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + continue; + } + } ++ ++ if (p->memdevs && !p->ports && !p->endpoints) { ++ jchilddevs = json_object_new_array(); ++ if (!jchilddevs) { ++ err(p, ++ "%s: failed to enumerate child memdevs\n", ++ devname); ++ continue; ++ } ++ } + } + walk_children: + dbg(p, "walk ports\n"); + walk_child_ports(port, p, pick_array(jchildports, jports), +- pick_array(jchildeps, jeps), flags); ++ pick_array(jchildeps, jeps), ++ pick_array(jchilddevs, jdevs), flags); + cond_add_put_array_suffix(jbus, "ports", devname, jchildports); + cond_add_put_array_suffix(jbus, "endpoints", devname, + jchildeps); ++ cond_add_put_array_suffix(jbus, "memdevs", devname, jchilddevs); + } + +- if (json_object_array_length(jdevs)) ++ if (json_object_array_length(janondevs)) + top_level_objs++; + if (json_object_array_length(jbuses)) + top_level_objs++; +@@ -564,20 +632,24 @@ walk_children: + top_level_objs++; + if (json_object_array_length(jeps)) + top_level_objs++; ++ if (json_object_array_length(jdevs)) ++ top_level_objs++; + +- splice_array(p, jdevs, jplatform, "anon memdevs", top_level_objs > 1); ++ splice_array(p, janondevs, jplatform, "anon memdevs", top_level_objs > 1); + splice_array(p, jbuses, jplatform, "buses", top_level_objs > 1); + splice_array(p, jports, jplatform, "ports", top_level_objs > 1); + splice_array(p, jeps, jplatform, "endpoints", top_level_objs > 1); ++ splice_array(p, jdevs, jplatform, "memdevs", top_level_objs > 1); + + util_display_json_array(stdout, jplatform, flags); + + return 0; + err: +- json_object_put(jdevs); ++ json_object_put(janondevs); + json_object_put(jbuses); + json_object_put(jports); + json_object_put(jeps); ++ json_object_put(jdevs); + json_object_put(jplatform); + return -ENOMEM; + } +diff --git a/cxl/json.c b/cxl/json.c +index 1868686..b809332 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -224,6 +224,12 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + if (jobj) + json_object_object_add(jdev, "host", jobj); + ++ if (!cxl_memdev_is_enabled(memdev)) { ++ jobj = json_object_new_string("disabled"); ++ if (jobj) ++ json_object_object_add(jdev, "state", jobj); ++ } ++ + return jdev; + } + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index c4ddc7d..4523ca6 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -480,6 +480,60 @@ CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev + return memdev->firmware_version; + } + ++static struct cxl_endpoint *cxl_port_find_endpoint(struct cxl_port *parent_port, ++ struct cxl_memdev *memdev) ++{ ++ struct cxl_endpoint *endpoint; ++ struct cxl_port *port; ++ ++ cxl_port_foreach(parent_port, port) { ++ cxl_endpoint_foreach(port, endpoint) ++ if (strcmp(cxl_endpoint_get_host(endpoint), ++ cxl_memdev_get_devname(memdev)) == 0) ++ return endpoint; ++ endpoint = cxl_port_find_endpoint(port, memdev); ++ if (endpoint) ++ return endpoint; ++ } ++ ++ return NULL; ++} ++ ++CXL_EXPORT struct cxl_endpoint * ++cxl_memdev_get_endpoint(struct cxl_memdev *memdev) ++{ ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ struct cxl_endpoint *endpoint = NULL; ++ struct cxl_bus *bus; ++ ++ if (memdev->endpoint) ++ return memdev->endpoint; ++ ++ if (!cxl_memdev_is_enabled(memdev)) ++ return NULL; ++ ++ cxl_bus_foreach (ctx, bus) { ++ struct cxl_port *port = cxl_bus_get_port(bus); ++ ++ endpoint = cxl_port_find_endpoint(port, memdev); ++ if (endpoint) ++ break; ++ } ++ ++ if (!endpoint) ++ return NULL; ++ ++ if (endpoint->memdev && endpoint->memdev != memdev) ++ err(ctx, "%s assigned to %s not %s\n", ++ cxl_endpoint_get_devname(endpoint), ++ cxl_memdev_get_devname(endpoint->memdev), ++ cxl_memdev_get_devname(memdev)); ++ memdev->endpoint = endpoint; ++ endpoint->memdev = memdev; ++ ++ return endpoint; ++} ++ + CXL_EXPORT size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev) + { + return memdev->lsa_size; +@@ -495,6 +549,21 @@ static int is_enabled(const char *drvpath) + return 1; + } + ++CXL_EXPORT int cxl_memdev_is_enabled(struct cxl_memdev *memdev) ++{ ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ char *path = memdev->dev_buf; ++ int len = memdev->buf_len; ++ ++ if (snprintf(path, len, "%s/driver", memdev->dev_path) >= len) { ++ err(ctx, "%s: buffer too small!\n", ++ cxl_memdev_get_devname(memdev)); ++ return 0; ++ } ++ ++ return is_enabled(path); ++} ++ + CXL_EXPORT int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev) + { + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); +@@ -660,6 +729,34 @@ CXL_EXPORT int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint) + return cxl_port_is_enabled(&endpoint->port); + } + ++CXL_EXPORT struct cxl_memdev * ++cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint) ++{ ++ struct cxl_ctx *ctx = cxl_endpoint_get_ctx(endpoint); ++ struct cxl_memdev *memdev; ++ ++ if (endpoint->memdev) ++ return endpoint->memdev; ++ ++ if (!cxl_endpoint_is_enabled(endpoint)) ++ return NULL; ++ ++ cxl_memdev_foreach(ctx, memdev) ++ if (strcmp(cxl_memdev_get_devname(memdev), ++ cxl_endpoint_get_host(endpoint)) == 0) { ++ if (memdev->endpoint && memdev->endpoint != endpoint) ++ err(ctx, "%s assigned to %s not %s\n", ++ cxl_memdev_get_devname(memdev), ++ cxl_endpoint_get_devname(memdev->endpoint), ++ cxl_endpoint_get_devname(endpoint)); ++ endpoint->memdev = memdev; ++ memdev->endpoint = endpoint; ++ return memdev; ++ } ++ ++ return NULL; ++} ++ + static void *add_cxl_port(void *parent, int id, const char *cxlport_base) + { + const char *devname = devpath_to_devname(cxlport_base); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 8f0688a..321acac 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -106,4 +106,7 @@ global: + cxl_endpoint_get_parent; + cxl_endpoint_get_port; + cxl_endpoint_get_host; ++ cxl_endpoint_get_memdev; ++ cxl_memdev_get_endpoint; ++ cxl_memdev_is_enabled; + } LIBCXL_1; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 5487b55..790ece8 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -46,6 +46,8 @@ unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev); + unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev); + const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev); + size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev); ++struct cxl_endpoint; ++struct cxl_endpoint *cxl_memdev_get_endpoint(struct cxl_memdev *memdev); + int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev); + int cxl_memdev_zero_label(struct cxl_memdev *memdev, size_t length, + size_t offset); +@@ -100,6 +102,8 @@ int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint); + struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint); + struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint); + const char *cxl_endpoint_get_host(struct cxl_endpoint *endpoint); ++struct cxl_memdev *cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint); ++int cxl_memdev_is_enabled(struct cxl_memdev *memdev); + + #define cxl_endpoint_foreach(port, endpoint) \ + for (endpoint = cxl_endpoint_get_first(port); endpoint != NULL; \ +-- +2.27.0 + diff --git a/SOURCES/0109-cxl-list-Filter-memdev-by-ancestry.patch b/SOURCES/0109-cxl-list-Filter-memdev-by-ancestry.patch new file mode 100644 index 0000000..f6f94da --- /dev/null +++ b/SOURCES/0109-cxl-list-Filter-memdev-by-ancestry.patch @@ -0,0 +1,329 @@ +From b90fc91e1034668cfde06f0fd8a7293df8b7690d Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:53:50 -0800 +Subject: [PATCH 109/217] cxl/list: Filter memdev by ancestry + +Whenever a memdev filter is specified limit output of buses, ports and +endpoints to those that are in the memdev's ancestry. + +Link: https://lore.kernel.org/r/164298563039.3021641.5253222797042241091.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 19 +++++++++ + Documentation/cxl/lib/libcxl.txt | 11 +++++ + cxl/filter.c | 69 ++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.c | 36 +++++++++++++++++ + cxl/lib/libcxl.sym | 5 +++ + cxl/libcxl.h | 4 ++ + 6 files changed, 144 insertions(+) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 1751868..bac27c7 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -39,6 +39,25 @@ they are combined as an 'AND' filter. So, "-m mem0,mem1,mem2 -p port10" + would only list objects that are beneath port10 AND map mem0, mem1, OR + mem2. + ++Given that many topology queries seek to answer questions relative to a ++given memdev, buses, ports, and endpoints can be filtered by one or more ++memdevs. For example: ++---- ++# cxl list -P -p switch,endpoint -m mem0 ++[ ++ { ++ "port":"port1", ++ "host":"ACPI0016:00", ++ "endpoints:port1":[ ++ { ++ "endpoint":"endpoint2", ++ "host":"mem0" ++ } ++ ] ++ } ++] ++---- ++ + The --human option in addition to reformatting some fields to more human + friendly strings also unwraps the array to reduce the number of lines of + output. +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 73b0fb9..b0253d7 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -150,11 +150,18 @@ cxl_bus'. + ---- + struct cxl_bus *cxl_bus_get_first(struct cxl_ctx *ctx); + struct cxl_bus *cxl_bus_get_next(struct cxl_bus *bus); ++struct cxl_ctx *cxl_bus_get_ctx(struct cxl_bus *bus); ++struct cxl_bus *cxl_memdev_get_bus(struct cxl_memdev *memdev); ++struct cxl_bus *cxl_endpoint_get_bus(struct cxl_endpoint *endpoint); + + #define cxl_bus_foreach(ctx, bus) \ + for (bus = cxl_bus_get_first(ctx); bus != NULL; \ + bus = cxl_bus_get_next(bus)) + ---- ++When a memdev is active it has established a CXL port hierarchy between ++itself and the root of its associated CXL topology. The ++cxl_{memdev,endpoint}_get_bus() helpers walk that topology to retrieve ++the associated bus object. + + === BUS: Attributes + ---- +@@ -209,6 +216,7 @@ int cxl_port_is_enabled(struct cxl_port *port); + bool cxl_port_is_root(struct cxl_port *port); + bool cxl_port_is_switch(struct cxl_port *port); + bool cxl_port_is_endpoint(struct cxl_port *port); ++bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev); + ---- + The port type is communicated via cxl_port_is_(). An 'enabled' port + is one that has succeeded in discovering the CXL component registers in +@@ -217,6 +225,9 @@ memdev to be enabled for CXL memory operation all CXL ports in its + ancestry must also be enabled including a root port, an arbitrary number + of intervening switch ports, and a terminal endpoint port. + ++cxl_port_hosts_memdev() returns true if the port's host appears in the ++memdev host's device topology ancestry. ++ + ENDPOINTS + --------- + CXL endpoint objects encapsulate the set of host-managed device-memory +diff --git a/cxl/filter.c b/cxl/filter.c +index 2130816..6dc61a1 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -297,6 +297,66 @@ struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + return NULL; + } + ++static struct cxl_bus *util_cxl_bus_filter_by_memdev(struct cxl_bus *bus, ++ const char *ident, ++ const char *serial) ++{ ++ struct cxl_ctx *ctx = cxl_bus_get_ctx(bus); ++ struct cxl_memdev *memdev; ++ ++ if (!ident && !serial) ++ return bus; ++ ++ cxl_memdev_foreach(ctx, memdev) { ++ if (!util_cxl_memdev_filter(memdev, ident, serial)) ++ continue; ++ if (cxl_memdev_get_bus(memdev) == bus) ++ return bus; ++ } ++ ++ return NULL; ++} ++ ++static struct cxl_endpoint * ++util_cxl_endpoint_filter_by_memdev(struct cxl_endpoint *endpoint, ++ const char *ident, const char *serial) ++{ ++ struct cxl_ctx *ctx = cxl_endpoint_get_ctx(endpoint); ++ struct cxl_memdev *memdev; ++ ++ if (!ident && !serial) ++ return endpoint; ++ ++ cxl_memdev_foreach(ctx, memdev) { ++ if (!util_cxl_memdev_filter(memdev, ident, serial)) ++ continue; ++ if (cxl_memdev_get_endpoint(memdev) == endpoint) ++ return endpoint; ++ } ++ ++ return NULL; ++} ++ ++static struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port, ++ const char *ident, ++ const char *serial) ++{ ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ struct cxl_memdev *memdev; ++ ++ if (!ident && !serial) ++ return port; ++ ++ cxl_memdev_foreach(ctx, memdev) { ++ if (!util_cxl_memdev_filter(memdev, ident, serial)) ++ continue; ++ if (cxl_port_hosts_memdev(port, memdev)) ++ return port; ++ } ++ ++ return NULL; ++} ++ + static unsigned long params_to_flags(struct cxl_filter_params *param) + { + unsigned long flags = 0; +@@ -399,6 +459,9 @@ static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p, + if (!util_cxl_endpoint_filter_by_port(endpoint, p->port_filter, + pf_mode(p))) + continue; ++ if (!util_cxl_endpoint_filter_by_memdev( ++ endpoint, p->memdev_filter, p->serial_filter)) ++ continue; + if (!p->idle && !cxl_endpoint_is_enabled(endpoint)) + continue; + if (p->endpoints) { +@@ -450,6 +513,9 @@ static void walk_child_ports(struct cxl_port *parent_port, + struct json_object *jchildports = NULL; + struct json_object *jchildeps = NULL; + ++ if (!util_cxl_port_filter_by_memdev(port, p->memdev_filter, ++ p->serial_filter)) ++ continue; + if (!util_cxl_port_filter(port, p->port_filter, pf_mode(p))) + goto walk_children; + if (!util_cxl_port_filter_by_bus(port, p->bus_filter)) +@@ -573,6 +639,9 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + struct cxl_port *port = cxl_bus_get_port(bus); + const char *devname = cxl_bus_get_devname(bus); + ++ if (!util_cxl_bus_filter_by_memdev(bus, p->memdev_filter, ++ p->serial_filter)) ++ continue; + if (!util_cxl_bus_filter(bus, p->bus_filter)) + goto walk_children; + if (!util_cxl_port_filter(port, p->port_filter, pf_mode(p))) +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 4523ca6..0065f6b 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -455,6 +455,15 @@ CXL_EXPORT const char *cxl_memdev_get_host(struct cxl_memdev *memdev) + return memdev->host; + } + ++CXL_EXPORT struct cxl_bus *cxl_memdev_get_bus(struct cxl_memdev *memdev) ++{ ++ struct cxl_endpoint *endpoint = cxl_memdev_get_endpoint(memdev); ++ ++ if (!endpoint) ++ return NULL; ++ return cxl_endpoint_get_bus(endpoint); ++} ++ + CXL_EXPORT int cxl_memdev_get_major(struct cxl_memdev *memdev) + { + return memdev->major; +@@ -724,6 +733,13 @@ CXL_EXPORT const char *cxl_endpoint_get_host(struct cxl_endpoint *endpoint) + return cxl_port_get_host(&endpoint->port); + } + ++CXL_EXPORT struct cxl_bus *cxl_endpoint_get_bus(struct cxl_endpoint *endpoint) ++{ ++ struct cxl_port *port = &endpoint->port; ++ ++ return cxl_port_get_bus(port); ++} ++ + CXL_EXPORT int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint) + { + return cxl_port_is_enabled(&endpoint->port); +@@ -875,6 +891,21 @@ CXL_EXPORT const char *cxl_port_get_host(struct cxl_port *port) + return devpath_to_devname(port->uport); + } + ++CXL_EXPORT bool cxl_port_hosts_memdev(struct cxl_port *port, ++ struct cxl_memdev *memdev) ++{ ++ struct cxl_endpoint *endpoint = cxl_memdev_get_endpoint(memdev); ++ struct cxl_port *iter; ++ ++ if (!endpoint) ++ return false; ++ ++ iter = cxl_endpoint_get_port(endpoint); ++ while (iter && iter != port) ++ iter = iter->parent; ++ return iter != NULL; ++} ++ + CXL_EXPORT int cxl_port_is_enabled(struct cxl_port *port) + { + struct cxl_ctx *ctx = cxl_port_get_ctx(port); +@@ -985,6 +1016,11 @@ CXL_EXPORT const char *cxl_bus_get_provider(struct cxl_bus *bus) + return devname; + } + ++CXL_EXPORT struct cxl_ctx *cxl_bus_get_ctx(struct cxl_bus *bus) ++{ ++ return cxl_port_get_ctx(&bus->port); ++} ++ + CXL_EXPORT void cxl_cmd_unref(struct cxl_cmd *cmd) + { + if (!cmd) +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 321acac..29f3498 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -84,6 +84,7 @@ global: + cxl_bus_get_devname; + cxl_bus_get_id; + cxl_bus_get_port; ++ cxl_bus_get_ctx; + cxl_port_get_first; + cxl_port_get_next; + cxl_port_get_devname; +@@ -97,6 +98,8 @@ global: + cxl_port_is_endpoint; + cxl_port_get_bus; + cxl_port_get_host; ++ cxl_port_get_bus; ++ cxl_port_hosts_memdev; + cxl_endpoint_get_first; + cxl_endpoint_get_next; + cxl_endpoint_get_devname; +@@ -107,6 +110,8 @@ global: + cxl_endpoint_get_port; + cxl_endpoint_get_host; + cxl_endpoint_get_memdev; ++ cxl_endpoint_get_bus; + cxl_memdev_get_endpoint; + cxl_memdev_is_enabled; ++ cxl_memdev_get_bus; + } LIBCXL_1; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 790ece8..e7b675e 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -39,6 +39,7 @@ int cxl_memdev_get_id(struct cxl_memdev *memdev); + unsigned long long cxl_memdev_get_serial(struct cxl_memdev *memdev); + const char *cxl_memdev_get_devname(struct cxl_memdev *memdev); + const char *cxl_memdev_get_host(struct cxl_memdev *memdev); ++struct cxl_bus *cxl_memdev_get_bus(struct cxl_memdev *memdev); + int cxl_memdev_get_major(struct cxl_memdev *memdev); + int cxl_memdev_get_minor(struct cxl_memdev *memdev); + struct cxl_ctx *cxl_memdev_get_ctx(struct cxl_memdev *memdev); +@@ -68,6 +69,7 @@ const char *cxl_bus_get_provider(struct cxl_bus *bus); + const char *cxl_bus_get_devname(struct cxl_bus *bus); + int cxl_bus_get_id(struct cxl_bus *bus); + struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus); ++struct cxl_ctx *cxl_bus_get_ctx(struct cxl_bus *bus); + + #define cxl_bus_foreach(ctx, bus) \ + for (bus = cxl_bus_get_first(ctx); bus != NULL; \ +@@ -87,6 +89,7 @@ struct cxl_bus *cxl_port_to_bus(struct cxl_port *port); + bool cxl_port_is_endpoint(struct cxl_port *port); + struct cxl_bus *cxl_port_get_bus(struct cxl_port *port); + const char *cxl_port_get_host(struct cxl_port *port); ++bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev); + + #define cxl_port_foreach(parent, port) \ + for (port = cxl_port_get_first(parent); port != NULL; \ +@@ -102,6 +105,7 @@ int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint); + struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint); + struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint); + const char *cxl_endpoint_get_host(struct cxl_endpoint *endpoint); ++struct cxl_bus *cxl_endpoint_get_bus(struct cxl_endpoint *endpoint); + struct cxl_memdev *cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint); + int cxl_memdev_is_enabled(struct cxl_memdev *memdev); + +-- +2.27.0 + diff --git a/SOURCES/0110-cxl-memdev-Use-a-local-logger-for-debug.patch b/SOURCES/0110-cxl-memdev-Use-a-local-logger-for-debug.patch new file mode 100644 index 0000000..1af0f57 --- /dev/null +++ b/SOURCES/0110-cxl-memdev-Use-a-local-logger-for-debug.patch @@ -0,0 +1,173 @@ +From 5e1c1ab5bf6b6257552ad9fa242483ec1c1de006 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:53:55 -0800 +Subject: [PATCH 110/217] cxl/memdev: Use a local logger for debug + +The "fail()" macro skips some of the nicer features of the centralized +logger. Add one to supplement the library logger. + +Link: https://lore.kernel.org/r/164298563547.3021641.16504008034705274247.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/memdev.c | 48 ++++++++++++++++++++++++------------------------ + 1 file changed, 24 insertions(+), 24 deletions(-) + +diff --git a/cxl/memdev.c b/cxl/memdev.c +index b9141be..327c260 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -26,11 +26,7 @@ static struct parameters { + bool verbose; + } param; + +-#define fail(fmt, ...) \ +-do { \ +- fprintf(stderr, "cxl-%s:%s:%d: " fmt, \ +- VERSION, __func__, __LINE__, ##__VA_ARGS__); \ +-} while (0) ++static struct log_ctx ml; + + #define BASE_OPTIONS() \ + OPT_BOOLEAN('v',"verbose", ¶m.verbose, "turn on debug") +@@ -79,7 +75,7 @@ static int action_zero(struct cxl_memdev *memdev, struct action_context *actx) + size = cxl_memdev_get_label_size(memdev); + + if (cxl_memdev_nvdimm_bridge_active(memdev)) { +- fprintf(stderr, ++ log_err(&ml, + "%s: has active nvdimm bridge, abort label write\n", + cxl_memdev_get_devname(memdev)); + return -EBUSY; +@@ -87,7 +83,7 @@ static int action_zero(struct cxl_memdev *memdev, struct action_context *actx) + + rc = cxl_memdev_zero_label(memdev, size, param.offset); + if (rc < 0) +- fprintf(stderr, "%s: label zeroing failed: %s\n", ++ log_err(&ml, "%s: label zeroing failed: %s\n", + cxl_memdev_get_devname(memdev), strerror(-rc)); + + return rc; +@@ -100,7 +96,7 @@ static int action_write(struct cxl_memdev *memdev, struct action_context *actx) + int rc; + + if (cxl_memdev_nvdimm_bridge_active(memdev)) { +- fprintf(stderr, ++ log_err(&ml, + "%s: has active nvdimm bridge, abort label write\n", + cxl_memdev_get_devname(memdev)); + return -EBUSY; +@@ -114,7 +110,7 @@ static int action_write(struct cxl_memdev *memdev, struct action_context *actx) + fseek(actx->f_in, 0L, SEEK_SET); + + if (size > label_size) { +- fprintf(stderr, ++ log_err(&ml, + "File size (%zu) greater than label area size (%zu), aborting\n", + size, label_size); + return -EINVAL; +@@ -133,7 +129,7 @@ static int action_write(struct cxl_memdev *memdev, struct action_context *actx) + + rc = cxl_memdev_write_label(memdev, buf, size, param.offset); + if (rc < 0) +- fprintf(stderr, "%s: label write failed: %s\n", ++ log_err(&ml, "%s: label write failed: %s\n", + cxl_memdev_get_devname(memdev), strerror(-rc)); + + out: +@@ -158,7 +154,7 @@ static int action_read(struct cxl_memdev *memdev, struct action_context *actx) + + rc = cxl_memdev_read_label(memdev, buf, size, param.offset); + if (rc < 0) { +- fprintf(stderr, "%s: label read failed: %s\n", ++ log_err(&ml, "%s: label read failed: %s\n", + cxl_memdev_get_devname(memdev), strerror(-rc)); + goto out; + } +@@ -188,6 +184,7 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + }; + unsigned long id; + ++ log_init(&ml, "cxl memdev", "CXL_MEMDEV_LOG"); + argc = parse_options(argc, argv, options, u, 0); + + if (argc == 0) +@@ -200,8 +197,8 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + } + + if (sscanf(argv[i], "mem%lu", &id) != 1) { +- fprintf(stderr, "'%s' is not a valid memdev name\n", +- argv[i]); ++ log_err(&ml, "'%s' is not a valid memdev name\n", ++ argv[i]); + err++; + } + } +@@ -216,8 +213,8 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + else { + actx.f_out = fopen(param.outfile, "w+"); + if (!actx.f_out) { +- fprintf(stderr, "failed to open: %s: (%s)\n", +- param.outfile, strerror(errno)); ++ log_err(&ml, "failed to open: %s: (%s)\n", ++ param.outfile, strerror(errno)); + rc = -errno; + goto out; + } +@@ -228,15 +225,18 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + } else { + actx.f_in = fopen(param.infile, "r"); + if (!actx.f_in) { +- fprintf(stderr, "failed to open: %s: (%s)\n", +- param.infile, strerror(errno)); ++ log_err(&ml, "failed to open: %s: (%s)\n", param.infile, ++ strerror(errno)); + rc = -errno; + goto out_close_fout; + } + } + +- if (param.verbose) ++ if (param.verbose) { + cxl_set_log_priority(ctx, LOG_DEBUG); ++ ml.log_priority = LOG_DEBUG; ++ } else ++ ml.log_priority = LOG_INFO; + + rc = 0; + err = 0; +@@ -299,8 +299,8 @@ int cmd_write_labels(int argc, const char **argv, struct cxl_ctx *ctx) + int count = memdev_action(argc, argv, ctx, action_write, write_options, + "cxl write-labels [-i ]"); + +- fprintf(stderr, "wrote %d mem%s\n", count >= 0 ? count : 0, +- count > 1 ? "s" : ""); ++ log_info(&ml, "wrote %d mem%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); + return count >= 0 ? 0 : EXIT_FAILURE; + } + +@@ -309,8 +309,8 @@ int cmd_read_labels(int argc, const char **argv, struct cxl_ctx *ctx) + int count = memdev_action(argc, argv, ctx, action_read, read_options, + "cxl read-labels [..] [-o ]"); + +- fprintf(stderr, "read %d mem%s\n", count >= 0 ? count : 0, +- count > 1 ? "s" : ""); ++ log_info(&ml, "read %d mem%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); + return count >= 0 ? 0 : EXIT_FAILURE; + } + +@@ -319,7 +319,7 @@ int cmd_zero_labels(int argc, const char **argv, struct cxl_ctx *ctx) + int count = memdev_action(argc, argv, ctx, action_zero, zero_options, + "cxl zero-labels [..] []"); + +- fprintf(stderr, "zeroed %d mem%s\n", count >= 0 ? count : 0, +- count > 1 ? "s" : ""); ++ log_info(&ml, "zeroed %d mem%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); + return count >= 0 ? 0 : EXIT_FAILURE; + } +-- +2.27.0 + diff --git a/SOURCES/0111-cxl-memdev-Cleanup-memdev-filter.patch b/SOURCES/0111-cxl-memdev-Cleanup-memdev-filter.patch new file mode 100644 index 0000000..94688bd --- /dev/null +++ b/SOURCES/0111-cxl-memdev-Cleanup-memdev-filter.patch @@ -0,0 +1,64 @@ +From 6cab8e0186e73af75f0a15ce87c4db20ef0089df Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:54:01 -0800 +Subject: [PATCH 111/217] cxl/memdev: Cleanup memdev filter + +util_cxl_memdev_filter() already handles the difference between 'mem%d', +'%d', and 'all' for the identifier format. Drop the duplicate / incomplete +format checking. + +If the checking for bad formats was dropped too then this command could +support "0,1,2" syntax in addition to "0 1 2" like 'cxl list'. However, it is +not clear that's worthwhile since 'list' is ok to be imprecise, but memdev +commands need to be stricter. + +Link: https://lore.kernel.org/r/164298564100.3021641.9410483964085163708.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/memdev.c | 18 +++++++----------- + 1 file changed, 7 insertions(+), 11 deletions(-) + +diff --git a/cxl/memdev.c b/cxl/memdev.c +index 327c260..4cca8b8 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -191,16 +191,16 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + usage_with_options(u, options); + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "all") == 0) { +- argv[0] = "all"; + argc = 1; + break; + } ++ if (sscanf(argv[i], "mem%lu", &id) == 1) ++ continue; ++ if (sscanf(argv[i], "%lu", &id) == 1) ++ continue; + +- if (sscanf(argv[i], "mem%lu", &id) != 1) { +- log_err(&ml, "'%s' is not a valid memdev name\n", +- argv[i]); +- err++; +- } ++ log_err(&ml, "'%s' is not a valid memdev name\n", argv[i]); ++ err++; + } + + if (err == argc) { +@@ -243,11 +243,7 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + count = 0; + + for (i = 0; i < argc; i++) { +- if (sscanf(argv[i], "mem%lu", &id) != 1 +- && strcmp(argv[i], "all") != 0) +- continue; +- +- cxl_memdev_foreach (ctx, memdev) { ++ cxl_memdev_foreach(ctx, memdev) { + if (!util_cxl_memdev_filter(memdev, argv[i], NULL)) + continue; + +-- +2.27.0 + diff --git a/SOURCES/0112-cxl-memdev-Add-serial-support-for-memdev-related-com.patch b/SOURCES/0112-cxl-memdev-Add-serial-support-for-memdev-related-com.patch new file mode 100644 index 0000000..4c95292 --- /dev/null +++ b/SOURCES/0112-cxl-memdev-Add-serial-support-for-memdev-related-com.patch @@ -0,0 +1,133 @@ +From c09c507e5a608718ac96af088fdc8cb441b09d0b Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:54:06 -0800 +Subject: [PATCH 112/217] cxl/memdev: Add serial support for memdev-related + commands + +Allow for a "-s, --serial" option to turn the argument list into serial +identifiers. + +Link: https://lore.kernel.org/r/164298564631.3021641.5552442288217413180.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/memdev-option.txt | 5 ++++ + Documentation/cxl/meson.build | 4 ++- + cxl/memdev.c | 45 +++++++++++++++++++++-------- + 3 files changed, 41 insertions(+), 13 deletions(-) + +diff --git a/Documentation/cxl/memdev-option.txt b/Documentation/cxl/memdev-option.txt +index e778582..64348be 100644 +--- a/Documentation/cxl/memdev-option.txt ++++ b/Documentation/cxl/memdev-option.txt +@@ -2,3 +2,8 @@ + A 'memX' device name, or a memdev id number. Restrict the operation to + the specified memdev(s). The keyword 'all' can be specified to indicate + the lack of any restriction. ++ ++-S:: ++--serial:: ++ Rather an a memdev id number, interpret the argument(s) ++ as a list of serial numbers. +diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build +index 64ce13f..0a6346b 100644 +--- a/Documentation/cxl/meson.build ++++ b/Documentation/cxl/meson.build +@@ -19,7 +19,9 @@ else + endif + + filedeps = [ +- '../copyright.txt', ++ '../copyright.txt', ++ 'memdev-option.txt', ++ 'labels-options.txt', + ] + + cxl_manpages = [ +diff --git a/cxl/memdev.c b/cxl/memdev.c +index 4cca8b8..ef5343a 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -24,12 +24,14 @@ static struct parameters { + unsigned len; + unsigned offset; + bool verbose; ++ bool serial; + } param; + + static struct log_ctx ml; + + #define BASE_OPTIONS() \ +-OPT_BOOLEAN('v',"verbose", ¶m.verbose, "turn on debug") ++OPT_BOOLEAN('v',"verbose", ¶m.verbose, "turn on debug"), \ ++OPT_BOOLEAN('S', "serial", ¶m.serial, "user serials numbers to id memdevs") + + #define READ_OPTIONS() \ + OPT_STRING('o', "output", ¶m.outfile, "output-file", \ +@@ -172,8 +174,9 @@ out: + } + + static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, +- int (*action)(struct cxl_memdev *memdev, struct action_context *actx), +- const struct option *options, const char *usage) ++ int (*action)(struct cxl_memdev *memdev, ++ struct action_context *actx), ++ const struct option *options, const char *usage) + { + struct cxl_memdev *memdev, *single = NULL; + struct action_context actx = { 0 }; +@@ -190,16 +193,25 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + if (argc == 0) + usage_with_options(u, options); + for (i = 0; i < argc; i++) { +- if (strcmp(argv[i], "all") == 0) { +- argc = 1; +- break; ++ if (param.serial) { ++ char *end; ++ ++ strtoull(argv[i], &end, 0); ++ if (end[0] == 0) ++ continue; ++ } else { ++ if (strcmp(argv[i], "all") == 0) { ++ argc = 1; ++ break; ++ } ++ if (sscanf(argv[i], "mem%lu", &id) == 1) ++ continue; ++ if (sscanf(argv[i], "%lu", &id) == 1) ++ continue; + } +- if (sscanf(argv[i], "mem%lu", &id) == 1) +- continue; +- if (sscanf(argv[i], "%lu", &id) == 1) +- continue; + +- log_err(&ml, "'%s' is not a valid memdev name\n", argv[i]); ++ log_err(&ml, "'%s' is not a valid memdev %s\n", argv[i], ++ param.serial ? "serial number" : "name"); + err++; + } + +@@ -244,7 +256,16 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + + for (i = 0; i < argc; i++) { + cxl_memdev_foreach(ctx, memdev) { +- if (!util_cxl_memdev_filter(memdev, argv[i], NULL)) ++ const char *memdev_filter = NULL; ++ const char *serial_filter = NULL; ++ ++ if (param.serial) ++ serial_filter = argv[i]; ++ else ++ memdev_filter = argv[i]; ++ ++ if (!util_cxl_memdev_filter(memdev, memdev_filter, ++ serial_filter)) + continue; + + if (action == action_write) { +-- +2.27.0 + diff --git a/SOURCES/0113-cxl-list-Add-numa_node-to-memdev-listings.patch b/SOURCES/0113-cxl-list-Add-numa_node-to-memdev-listings.patch new file mode 100644 index 0000000..682d429 --- /dev/null +++ b/SOURCES/0113-cxl-list-Add-numa_node-to-memdev-listings.patch @@ -0,0 +1,136 @@ +From ca582b2003a2335eafac382e71afdf0a6caaef18 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:54:11 -0800 +Subject: [PATCH 113/217] cxl/list: Add 'numa_node' to memdev listings + +If the kernel exports a valid numa_node, >= 0, include it in memdev objects +listings. + +Link: https://lore.kernel.org/r/164298565156.3021641.14097226245654611710.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/lib/libcxl.txt | 4 ++++ + cxl/json.c | 8 ++++++++ + cxl/lib/libcxl.c | 11 +++++++++++ + cxl/lib/libcxl.sym | 1 + + cxl/lib/private.h | 1 + + cxl/libcxl.h | 1 + + 6 files changed, 26 insertions(+) + +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index b0253d7..de88d19 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -71,6 +71,7 @@ unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev); + const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev); + size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev); + int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev); ++int cxl_memdev_get_numa_node(struct cxl_memdev *memdev); + ---- + + A memdev is given a kernel device name of the form "mem%d" where an id +@@ -89,6 +90,9 @@ The 'pmem_size' and 'ram_size' attributes return the current + provisioning of DPA (Device Physical Address / local capacity) in the + device. + ++cxl_memdev_get_numa_node() returns the affinitized CPU node number if ++available or -1 otherwise. ++ + === MEMDEV: Commands + ---- + struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode); +diff --git a/cxl/json.c b/cxl/json.c +index b809332..51918d6 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -190,6 +190,7 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + const char *devname = cxl_memdev_get_devname(memdev); + struct json_object *jdev, *jobj; + unsigned long long serial; ++ int numa_node; + + jdev = json_object_new_object(); + if (!jdev) +@@ -220,6 +221,13 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + json_object_object_add(jdev, "serial", jobj); + } + ++ numa_node = cxl_memdev_get_numa_node(memdev); ++ if (numa_node >= 0) { ++ jobj = json_object_new_int(numa_node); ++ if (jobj) ++ json_object_object_add(jdev, "numa_node", jobj); ++ } ++ + jobj = json_object_new_string(cxl_memdev_get_host(memdev)); + if (jobj) + json_object_object_add(jdev, "host", jobj); +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 0065f6b..14c7db8 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -348,6 +348,12 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + else + memdev->serial = strtoull(buf, NULL, 0); + ++ sprintf(path, "%s/numa_node", cxlmem_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ memdev->numa_node = -1; ++ else ++ memdev->numa_node = strtol(buf, NULL, 0); ++ + memdev->dev_path = strdup(cxlmem_base); + if (!memdev->dev_path) + goto err_read; +@@ -445,6 +451,11 @@ CXL_EXPORT unsigned long long cxl_memdev_get_serial(struct cxl_memdev *memdev) + return memdev->serial; + } + ++CXL_EXPORT int cxl_memdev_get_numa_node(struct cxl_memdev *memdev) ++{ ++ return memdev->numa_node; ++} ++ + CXL_EXPORT const char *cxl_memdev_get_devname(struct cxl_memdev *memdev) + { + return devpath_to_devname(memdev->dev_path); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 29f3498..b13a2d6 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -77,6 +77,7 @@ local: + LIBCXL_2 { + global: + cxl_memdev_get_serial; ++ cxl_memdev_get_numa_node; + cxl_memdev_get_host; + cxl_bus_get_first; + cxl_bus_get_next; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index b097bdf..c00bb36 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -20,6 +20,7 @@ struct cxl_pmem { + struct cxl_endpoint; + struct cxl_memdev { + int id, major, minor; ++ int numa_node; + void *dev_buf; + size_t buf_len; + char *host; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index e7b675e..be656ed 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -37,6 +37,7 @@ struct cxl_memdev *cxl_memdev_get_first(struct cxl_ctx *ctx); + struct cxl_memdev *cxl_memdev_get_next(struct cxl_memdev *memdev); + int cxl_memdev_get_id(struct cxl_memdev *memdev); + unsigned long long cxl_memdev_get_serial(struct cxl_memdev *memdev); ++int cxl_memdev_get_numa_node(struct cxl_memdev *memdev); + const char *cxl_memdev_get_devname(struct cxl_memdev *memdev); + const char *cxl_memdev_get_host(struct cxl_memdev *memdev); + struct cxl_bus *cxl_memdev_get_bus(struct cxl_memdev *memdev); +-- +2.27.0 + diff --git a/SOURCES/0114-util-Implement-common-bind-unbind-helpers.patch b/SOURCES/0114-util-Implement-common-bind-unbind-helpers.patch new file mode 100644 index 0000000..6b421a2 --- /dev/null +++ b/SOURCES/0114-util-Implement-common-bind-unbind-helpers.patch @@ -0,0 +1,329 @@ +From e31fc778998b4d02ffec68e61869aaeccfd99be8 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:54:17 -0800 +Subject: [PATCH 114/217] util: Implement common bind/unbind helpers + +Refactor ndctl_{bind,unbind}() into util_{bind,unbind}() for libcxl to +reuse. + +daxctl can not join the party for now as it needs to play games with +'new_id'. + +Link: https://lore.kernel.org/r/164298565707.3021641.7763459936156744907.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 103 +++++-------------------------------------- + util/sysfs.c | 76 +++++++++++++++++++++++++++++++ + util/sysfs.h | 8 ++++ + 3 files changed, 96 insertions(+), 91 deletions(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index 1374ad9..98d184b 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -1665,10 +1665,6 @@ static enum ndctl_fwa_result fwa_result_to_result(const char *result) + return NDCTL_FWA_RESULT_INVALID; + } + +-static int ndctl_bind(struct ndctl_ctx *ctx, struct kmod_module *module, +- const char *devname); +-static int ndctl_unbind(struct ndctl_ctx *ctx, const char *devpath); +- + static int populate_dimm_attributes(struct ndctl_dimm *dimm, + const char *dimm_base, + const char *bus_prefix) +@@ -2305,7 +2301,7 @@ NDCTL_EXPORT int ndctl_dimm_disable(struct ndctl_dimm *dimm) + if (!ndctl_dimm_is_enabled(dimm)) + return 0; + +- ndctl_unbind(ctx, dimm->dimm_path); ++ util_unbind(dimm->dimm_path, ctx); + + if (ndctl_dimm_is_enabled(dimm)) { + err(ctx, "%s: failed to disable\n", devname); +@@ -2324,7 +2320,7 @@ NDCTL_EXPORT int ndctl_dimm_enable(struct ndctl_dimm *dimm) + if (ndctl_dimm_is_enabled(dimm)) + return 0; + +- ndctl_bind(ctx, dimm->module, devname); ++ util_bind(devname, dimm->module, "nd", ctx); + + if (!ndctl_dimm_is_enabled(dimm)) { + err(ctx, "%s: failed to enable\n", devname); +@@ -3573,7 +3569,7 @@ NDCTL_EXPORT int ndctl_region_enable(struct ndctl_region *region) + if (ndctl_region_is_enabled(region)) + return 0; + +- ndctl_bind(ctx, region->module, devname); ++ util_bind(devname, region->module, "nd", ctx); + + if (!ndctl_region_is_enabled(region)) { + err(ctx, "%s: failed to enable\n", devname); +@@ -3610,7 +3606,7 @@ static int ndctl_region_disable(struct ndctl_region *region, int cleanup) + if (!ndctl_region_is_enabled(region)) + return 0; + +- ndctl_unbind(ctx, region->region_path); ++ util_unbind(region->region_path, ctx); + + if (ndctl_region_is_enabled(region)) { + err(ctx, "%s: failed to disable\n", devname); +@@ -4373,81 +4369,6 @@ NDCTL_EXPORT struct badblock *ndctl_namespace_get_first_badblock( + return badblocks_iter_first(&ndns->bb_iter, ctx, path); + } + +-static int ndctl_bind(struct ndctl_ctx *ctx, struct kmod_module *module, +- const char *devname) +-{ +- DIR *dir; +- int rc = 0; +- char path[200]; +- struct dirent *de; +- const int len = sizeof(path); +- +- if (!devname) { +- err(ctx, "missing devname\n"); +- return -EINVAL; +- } +- +- if (module) { +- rc = kmod_module_probe_insert_module(module, +- KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, +- NULL); +- if (rc < 0) { +- err(ctx, "%s: insert failure: %d\n", __func__, rc); +- return rc; +- } +- } +- +- if (snprintf(path, len, "/sys/bus/nd/drivers") >= len) { +- err(ctx, "%s: buffer too small!\n", devname); +- return -ENXIO; +- } +- +- dir = opendir(path); +- if (!dir) { +- err(ctx, "%s: opendir(\"%s\") failed\n", devname, path); +- return -ENXIO; +- } +- +- while ((de = readdir(dir)) != NULL) { +- char *drv_path; +- +- if (de->d_ino == 0) +- continue; +- if (de->d_name[0] == '.') +- continue; +- if (asprintf(&drv_path, "%s/%s/bind", path, de->d_name) < 0) { +- err(ctx, "%s: path allocation failure\n", devname); +- continue; +- } +- +- rc = sysfs_write_attr_quiet(ctx, drv_path, devname); +- free(drv_path); +- if (rc == 0) +- break; +- } +- closedir(dir); +- +- if (rc) { +- dbg(ctx, "%s: bind failed\n", devname); +- return -ENXIO; +- } +- return 0; +-} +- +-static int ndctl_unbind(struct ndctl_ctx *ctx, const char *devpath) +-{ +- const char *devname = devpath_to_devname(devpath); +- char path[200]; +- const int len = sizeof(path); +- +- if (snprintf(path, len, "%s/driver/unbind", devpath) >= len) { +- err(ctx, "%s: buffer too small!\n", devname); +- return -ENXIO; +- } +- +- return sysfs_write_attr(ctx, path, devname); +-} +- + static void *add_btt(void *parent, int id, const char *btt_base); + static void *add_pfn(void *parent, int id, const char *pfn_base); + static void *add_dax(void *parent, int id, const char *dax_base); +@@ -4533,7 +4454,7 @@ NDCTL_EXPORT int ndctl_namespace_enable(struct ndctl_namespace *ndns) + if (ndctl_namespace_is_enabled(ndns)) + return 0; + +- rc = ndctl_bind(ctx, ndns->module, devname); ++ rc = util_bind(devname, ndns->module, "nd", ctx); + + /* + * Rescan now as successfully enabling a namespace device leads +@@ -4581,7 +4502,7 @@ NDCTL_EXPORT int ndctl_namespace_disable(struct ndctl_namespace *ndns) + if (!ndctl_namespace_is_enabled(ndns)) + return 0; + +- ndctl_unbind(ctx, ndns->ndns_path); ++ util_unbind(ndns->ndns_path, ctx); + + if (ndctl_namespace_is_enabled(ndns)) { + err(ctx, "%s: failed to disable\n", devname); +@@ -5420,7 +5341,7 @@ NDCTL_EXPORT int ndctl_btt_enable(struct ndctl_btt *btt) + if (ndctl_btt_is_enabled(btt)) + return 0; + +- ndctl_bind(ctx, btt->module, devname); ++ util_bind(devname, btt->module, "nd", ctx); + + if (!ndctl_btt_is_enabled(btt)) { + err(ctx, "%s: failed to enable\n", devname); +@@ -5457,7 +5378,7 @@ NDCTL_EXPORT int ndctl_btt_delete(struct ndctl_btt *btt) + return 0; + } + +- ndctl_unbind(ctx, btt->btt_path); ++ util_unbind(btt->btt_path, ctx); + + rc = ndctl_btt_set_namespace(btt, NULL); + if (rc) { +@@ -5908,7 +5829,7 @@ NDCTL_EXPORT int ndctl_pfn_enable(struct ndctl_pfn *pfn) + if (ndctl_pfn_is_enabled(pfn)) + return 0; + +- ndctl_bind(ctx, pfn->module, devname); ++ util_bind(devname, pfn->module, "nd", ctx); + + if (!ndctl_pfn_is_enabled(pfn)) { + err(ctx, "%s: failed to enable\n", devname); +@@ -5945,7 +5866,7 @@ NDCTL_EXPORT int ndctl_pfn_delete(struct ndctl_pfn *pfn) + return 0; + } + +- ndctl_unbind(ctx, pfn->pfn_path); ++ util_unbind(pfn->pfn_path, ctx); + + rc = ndctl_pfn_set_namespace(pfn, NULL); + if (rc) { +@@ -6101,7 +6022,7 @@ NDCTL_EXPORT int ndctl_dax_enable(struct ndctl_dax *dax) + if (ndctl_dax_is_enabled(dax)) + return 0; + +- ndctl_bind(ctx, pfn->module, devname); ++ util_bind(devname, pfn->module, "nd", ctx); + + if (!ndctl_dax_is_enabled(dax)) { + err(ctx, "%s: failed to enable\n", devname); +@@ -6132,7 +6053,7 @@ NDCTL_EXPORT int ndctl_dax_delete(struct ndctl_dax *dax) + return 0; + } + +- ndctl_unbind(ctx, pfn->pfn_path); ++ util_unbind(pfn->pfn_path, ctx); + + rc = ndctl_dax_set_namespace(dax, NULL); + if (rc) { +diff --git a/util/sysfs.c b/util/sysfs.c +index 23330cb..968683b 100644 +--- a/util/sysfs.c ++++ b/util/sysfs.c +@@ -145,3 +145,79 @@ struct kmod_module *__util_modalias_to_module(struct kmod_ctx *kmod_ctx, + + return mod; + } ++ ++int __util_bind(const char *devname, struct kmod_module *module, ++ const char *bus, struct log_ctx *ctx) ++{ ++ DIR *dir; ++ int rc = 0; ++ char path[200]; ++ struct dirent *de; ++ const int len = sizeof(path); ++ ++ if (!devname) { ++ log_err(ctx, "missing devname\n"); ++ return -EINVAL; ++ } ++ ++ if (module) { ++ rc = kmod_module_probe_insert_module(module, ++ KMOD_PROBE_APPLY_BLACKLIST, ++ NULL, NULL, NULL, NULL); ++ if (rc < 0) { ++ log_err(ctx, "%s: insert failure: %d\n", __func__, rc); ++ return rc; ++ } ++ } ++ ++ if (snprintf(path, len, "/sys/bus/%s/drivers", bus) >= len) { ++ log_err(ctx, "%s: buffer too small!\n", devname); ++ return -ENXIO; ++ } ++ ++ dir = opendir(path); ++ if (!dir) { ++ log_err(ctx, "%s: opendir(\"%s\") failed\n", devname, path); ++ return -ENXIO; ++ } ++ ++ while ((de = readdir(dir)) != NULL) { ++ char *drv_path; ++ ++ if (de->d_ino == 0) ++ continue; ++ if (de->d_name[0] == '.') ++ continue; ++ ++ if (asprintf(&drv_path, "%s/%s/bind", path, de->d_name) < 0) { ++ log_err(ctx, "%s: path allocation failure\n", devname); ++ continue; ++ } ++ ++ rc = __sysfs_write_attr_quiet(ctx, drv_path, devname); ++ free(drv_path); ++ if (rc == 0) ++ break; ++ } ++ closedir(dir); ++ ++ if (rc) { ++ log_dbg(ctx, "%s: bind failed\n", devname); ++ return -ENXIO; ++ } ++ return 0; ++} ++ ++int __util_unbind(const char *devpath, struct log_ctx *ctx) ++{ ++ const char *devname = devpath_to_devname(devpath); ++ char path[200]; ++ const int len = sizeof(path); ++ ++ if (snprintf(path, len, "%s/driver/unbind", devpath) >= len) { ++ log_err(ctx, "%s: buffer too small!\n", devname); ++ return -ENXIO; ++ } ++ ++ return __sysfs_write_attr(ctx, path, devname); ++} +diff --git a/util/sysfs.h b/util/sysfs.h +index bdee4f5..4c95c70 100644 +--- a/util/sysfs.h ++++ b/util/sysfs.h +@@ -35,4 +35,12 @@ struct kmod_module *__util_modalias_to_module(struct kmod_ctx *kmod_ctx, + struct log_ctx *log); + #define util_modalias_to_module(ctx, buf) \ + __util_modalias_to_module((ctx)->kmod_ctx, buf, &(ctx)->ctx) ++ ++int __util_bind(const char *devname, struct kmod_module *module, const char *bus, ++ struct log_ctx *ctx); ++#define util_bind(n, m, b, c) __util_bind(n, m, b, &(c)->ctx) ++ ++int __util_unbind(const char *devpath, struct log_ctx *ctx); ++#define util_unbind(p, c) __util_unbind(p, &(c)->ctx) ++ + #endif /* __UTIL_SYSFS_H__ */ +-- +2.27.0 + diff --git a/SOURCES/0115-cxl-memdev-Enable-disable-support.patch b/SOURCES/0115-cxl-memdev-Enable-disable-support.patch new file mode 100644 index 0000000..965fbb2 --- /dev/null +++ b/SOURCES/0115-cxl-memdev-Enable-disable-support.patch @@ -0,0 +1,379 @@ +From 782694f9aeff6e146cfd00b31822995790546175 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:54:22 -0800 +Subject: [PATCH 115/217] cxl/memdev: Enable / disable support + +Introduce the 'cxl {enable,disable}-memdev' commands. When a memdev is +disabled the ports in the topology may be unregistered. CXL memory regions +require each endpoint in the interleave to attach to the cxl_mem driver +before regions are activated. + +Note that this starts out with the deliberate bug that it has false +positive detection of active memdevs. The fix for that bug requires kernel +support to detect the device's active participation in a region, until then +require all disable attempts to specify the --force override. This way +there are never any releases of cxl-cli that lack disable-memdev safety. + +Link: https://lore.kernel.org/r/164298566245.3021641.12696907310209056878.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-disable-memdev.txt | 37 +++++++++++++++ + Documentation/cxl/cxl-enable-memdev.txt | 34 ++++++++++++++ + Documentation/cxl/lib/libcxl.txt | 23 +++++++++ + Documentation/cxl/meson.build | 2 + + cxl/builtin.h | 2 + + cxl/cxl.c | 2 + + cxl/lib/libcxl.c | 58 +++++++++++++++++++++++ + cxl/lib/libcxl.sym | 2 + + cxl/libcxl.h | 2 + + cxl/memdev.c | 60 +++++++++++++++++++++++- + 10 files changed, 221 insertions(+), 1 deletion(-) + create mode 100644 Documentation/cxl/cxl-disable-memdev.txt + create mode 100644 Documentation/cxl/cxl-enable-memdev.txt + +diff --git a/Documentation/cxl/cxl-disable-memdev.txt b/Documentation/cxl/cxl-disable-memdev.txt +new file mode 100644 +index 0000000..edd5385 +--- /dev/null ++++ b/Documentation/cxl/cxl-disable-memdev.txt +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-disable-memdev(1) ++===================== ++ ++NAME ++---- ++cxl-disable-memdev - deactivate / hot-remove a given CXL memdev ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl disable-memdev' [..] [] ++ ++ ++OPTIONS ++------- ++:: ++include::memdev-option.txt[] ++ ++-f:: ++--force:: ++ DANGEROUS: Override the safety measure that blocks attempts to disable ++ a device if the tool determines the memdev is in active usage. Recall ++ that CXL memory ranges might have been established by platform ++ firmware and disabling an active device is akin to force removing ++ memory from a running system. ++ ++-v:: ++ Turn on verbose debug messages in the library (if libcxl was built with ++ logging and debug enabled). ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-enable-memdev[1] +diff --git a/Documentation/cxl/cxl-enable-memdev.txt b/Documentation/cxl/cxl-enable-memdev.txt +new file mode 100644 +index 0000000..088d5e0 +--- /dev/null ++++ b/Documentation/cxl/cxl-enable-memdev.txt +@@ -0,0 +1,34 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-enable-memdev(1) ++==================== ++ ++NAME ++---- ++cxl-enable-memdev - activate / hot-add a given CXL memdev ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl enable-memdev' [..] [] ++ ++A memdev typically autoenables at initial device discovery. However, if ++it was manually disabled this command can trigger the kernel to activate ++it again. This involves detecting the state of the HDM (Host Managed ++Device Memory) Decoders and validating that CXL.mem is enabled for each ++port in the device's hierarchy. ++ ++OPTIONS ++------- ++:: ++include::memdev-option.txt[] ++ ++-v:: ++ Turn on verbose debug messages in the library (if libcxl was built with ++ logging and debug enabled). ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-disable-memdev[1] +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index de88d19..49edb71 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -93,6 +93,29 @@ device. + cxl_memdev_get_numa_node() returns the affinitized CPU node number if + available or -1 otherwise. + ++=== MEMDEV: Control ++---- ++int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev); ++int cxl_memdev_enable(struct cxl_memdev *memdev); ++---- ++When a memory device is disabled it unregisters its associated endpoints ++and potentially intervening switch ports if there are no other memdevs ++pinning that port active. That means that any existing port objects that ++the library has previously returned are in valid and need to be re-read. ++Callers must be careful to re-retrieve port objects after ++cxl_memdev_disable_invalidate(). Any usage of a previously obtained port ++object after a cxl_memdev_disable_invalidate() call is a use-after-free ++programming error. It follows that after cxl_memdev_enable() new ports ++may appear in the topology that were not previously enumerable. ++ ++NOTE: cxl_memdev_disable_invalidate() will force disable the memdev ++regardless of whether the memory provided by the device is in active use ++by the operating system. Callers take responisbility for assuring that ++it is safe to disable the memory device. Otherwise, this call can be as ++destructive as ripping a DIMM out of a running system. Like all other ++libcxl calls that mutate the system state or divulge security sensitive ++information this call requires root / CAP_SYS_ADMIN. ++ + === MEMDEV: Commands + ---- + struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode); +diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build +index 0a6346b..7618c97 100644 +--- a/Documentation/cxl/meson.build ++++ b/Documentation/cxl/meson.build +@@ -30,6 +30,8 @@ cxl_manpages = [ + 'cxl-read-labels.txt', + 'cxl-write-labels.txt', + 'cxl-zero-labels.txt', ++ 'cxl-enable-memdev.txt', ++ 'cxl-disable-memdev.txt', + ] + + foreach man : cxl_manpages +diff --git a/cxl/builtin.h b/cxl/builtin.h +index 78eca6e..621c85c 100644 +--- a/cxl/builtin.h ++++ b/cxl/builtin.h +@@ -10,4 +10,6 @@ int cmd_read_labels(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_zero_labels(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_init_labels(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_check_labels(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_disable_memdev(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx); + #endif /* _CXL_BUILTIN_H_ */ +diff --git a/cxl/cxl.c b/cxl/cxl.c +index 4b1661d..78d2e9a 100644 +--- a/cxl/cxl.c ++++ b/cxl/cxl.c +@@ -64,6 +64,8 @@ static struct cmd_struct commands[] = { + { "zero-labels", .c_fn = cmd_zero_labels }, + { "read-labels", .c_fn = cmd_read_labels }, + { "write-labels", .c_fn = cmd_write_labels }, ++ { "disable-memdev", .c_fn = cmd_disable_memdev }, ++ { "enable-memdev", .c_fn = cmd_enable_memdev }, + }; + + int main(int argc, const char **argv) +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 14c7db8..2fdaf71 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -500,6 +500,64 @@ CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev + return memdev->firmware_version; + } + ++CXL_EXPORT int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev) ++{ ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ const char *devname = cxl_memdev_get_devname(memdev); ++ struct cxl_port *port, *_p, *bus_port; ++ struct cxl_bus *bus; ++ ++ if (!cxl_memdev_is_enabled(memdev)) ++ return 0; ++ ++ bus = cxl_memdev_get_bus(memdev); ++ if (!bus) { ++ err(ctx, "%s: failed to invalidate\n", devname); ++ return -ENXIO; ++ } ++ ++ util_unbind(memdev->dev_path, ctx); ++ ++ if (cxl_memdev_is_enabled(memdev)) { ++ err(ctx, "%s: failed to disable\n", devname); ++ return -EBUSY; ++ } ++ ++ /* ++ * The state of all ports is now indeterminate, delete them all ++ * and start over. ++ */ ++ bus_port = cxl_bus_get_port(bus); ++ list_for_each_safe(&bus_port->child_ports, port, _p, list) ++ free_port(port, &bus_port->child_ports); ++ bus_port->ports_init = 0; ++ memdev->endpoint = NULL; ++ ++ dbg(ctx, "%s: disabled\n", devname); ++ ++ return 0; ++} ++ ++CXL_EXPORT int cxl_memdev_enable(struct cxl_memdev *memdev) ++{ ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ const char *devname = cxl_memdev_get_devname(memdev); ++ ++ if (cxl_memdev_is_enabled(memdev)) ++ return 0; ++ ++ util_bind(devname, memdev->module, "cxl", ctx); ++ ++ if (!cxl_memdev_is_enabled(memdev)) { ++ err(ctx, "%s: failed to enable\n", devname); ++ return -ENXIO; ++ } ++ ++ dbg(ctx, "%s: enabled\n", devname); ++ ++ return 0; ++} ++ + static struct cxl_endpoint *cxl_port_find_endpoint(struct cxl_port *parent_port, + struct cxl_memdev *memdev) + { +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index b13a2d6..f235e99 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -115,4 +115,6 @@ global: + cxl_memdev_get_endpoint; + cxl_memdev_is_enabled; + cxl_memdev_get_bus; ++ cxl_memdev_disable_invalidate; ++ cxl_memdev_enable; + } LIBCXL_1; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index be656ed..53f68dd 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -48,6 +48,8 @@ unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev); + unsigned long long cxl_memdev_get_ram_size(struct cxl_memdev *memdev); + const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev); + size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev); ++int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev); ++int cxl_memdev_enable(struct cxl_memdev *memdev); + struct cxl_endpoint; + struct cxl_endpoint *cxl_memdev_get_endpoint(struct cxl_memdev *memdev); + int cxl_memdev_nvdimm_bridge_active(struct cxl_memdev *memdev); +diff --git a/cxl/memdev.c b/cxl/memdev.c +index ef5343a..90b33e1 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -25,13 +25,14 @@ static struct parameters { + unsigned offset; + bool verbose; + bool serial; ++ bool force; + } param; + + static struct log_ctx ml; + + #define BASE_OPTIONS() \ + OPT_BOOLEAN('v',"verbose", ¶m.verbose, "turn on debug"), \ +-OPT_BOOLEAN('S', "serial", ¶m.serial, "user serials numbers to id memdevs") ++OPT_BOOLEAN('S', "serial", ¶m.serial, "use serial numbers to id memdevs") + + #define READ_OPTIONS() \ + OPT_STRING('o', "output", ¶m.outfile, "output-file", \ +@@ -46,6 +47,10 @@ OPT_UINTEGER('s', "size", ¶m.len, "number of label bytes to operate"), \ + OPT_UINTEGER('O', "offset", ¶m.offset, \ + "offset into the label area to start operation") + ++#define DISABLE_OPTIONS() \ ++OPT_BOOLEAN('f', "force", ¶m.force, \ ++ "DANGEROUS: override active memdev safety checks") ++ + static const struct option read_options[] = { + BASE_OPTIONS(), + LABEL_OPTIONS(), +@@ -66,6 +71,37 @@ static const struct option zero_options[] = { + OPT_END(), + }; + ++static const struct option disable_options[] = { ++ BASE_OPTIONS(), ++ DISABLE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option enable_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static int action_disable(struct cxl_memdev *memdev, struct action_context *actx) ++{ ++ if (!cxl_memdev_is_enabled(memdev)) ++ return 0; ++ ++ if (!param.force) { ++ /* TODO: actually detect rather than assume active */ ++ log_err(&ml, "%s is part of an active region\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EBUSY; ++ } ++ ++ return cxl_memdev_disable_invalidate(memdev); ++} ++ ++static int action_enable(struct cxl_memdev *memdev, struct action_context *actx) ++{ ++ return cxl_memdev_enable(memdev); ++} ++ + static int action_zero(struct cxl_memdev *memdev, struct action_context *actx) + { + size_t size; +@@ -340,3 +376,25 @@ int cmd_zero_labels(int argc, const char **argv, struct cxl_ctx *ctx) + count > 1 ? "s" : ""); + return count >= 0 ? 0 : EXIT_FAILURE; + } ++ ++int cmd_disable_memdev(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int count = memdev_action( ++ argc, argv, ctx, action_disable, disable_options, ++ "cxl disable-memdev [..] []"); ++ ++ log_info(&ml, "disabled %d mem%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); ++ return count >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int count = memdev_action( ++ argc, argv, ctx, action_enable, enable_options, ++ "cxl enable-memdev [..] []"); ++ ++ log_info(&ml, "enabled %d mem%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); ++ return count >= 0 ? 0 : EXIT_FAILURE; ++} +-- +2.27.0 + diff --git a/SOURCES/0116-cxl-list-Add-decoder-support.patch b/SOURCES/0116-cxl-list-Add-decoder-support.patch new file mode 100644 index 0000000..61d626d --- /dev/null +++ b/SOURCES/0116-cxl-list-Add-decoder-support.patch @@ -0,0 +1,1108 @@ +From 46564977afb733a92526c688fe106e19b139ddfc Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:54:27 -0800 +Subject: [PATCH 116/217] cxl/list: Add decoder support + +Decoder objects exist at each level of a CXL port topology and map a +physical address range a set of interleaved ports at each level of the +hierarchy. Typically end users mostly care about the root-level decoders +which enumerate the potential CXL address space in the system, and the +endpoint decoders that indicate which address ranges a given device +contributes resources. Intermediate switch-level decoders are typically +only useful for debugging decode problems. + +$ cxl list -D -d 3.1 -u +{ + "decoder":"decoder3.1", + "resource":"0x8030000000", + "size":"512.00 MiB (536.87 MB)", + "volatile_capable":true +} + +Link: https://lore.kernel.org/r/164298566760.3021641.3999006903066004615.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .clang-format | 1 + + Documentation/cxl/cxl-list.txt | 18 ++- + Documentation/cxl/lib/libcxl.txt | 70 ++++++++- + cxl/filter.c | 194 +++++++++++++++++++++++-- + cxl/filter.h | 2 + + cxl/json.c | 62 ++++++++ + cxl/json.h | 2 + + cxl/lib/libcxl.c | 239 ++++++++++++++++++++++++++++--- + cxl/lib/libcxl.sym | 14 ++ + cxl/lib/private.h | 31 +++- + cxl/libcxl.h | 28 ++++ + cxl/list.c | 9 +- + util/json.h | 1 + + 13 files changed, 643 insertions(+), 28 deletions(-) + +diff --git a/.clang-format b/.clang-format +index 106bc5e..16e28ac 100644 +--- a/.clang-format ++++ b/.clang-format +@@ -80,6 +80,7 @@ ForEachMacros: + - 'cxl_memdev_foreach' + - 'cxl_bus_foreach' + - 'cxl_port_foreach' ++ - 'cxl_decoder_foreach' + - 'cxl_endpoint_foreach' + - 'daxctl_dev_foreach' + - 'daxctl_mapping_foreach' +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index bac27c7..84872b9 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -5,7 +5,7 @@ cxl-list(1) + + NAME + ---- +-cxl-list - List CXL capable memory devices, and their attributes in json. ++cxl-list - List platform CXL objects, and their attributes, in json. + + SYNOPSIS + -------- +@@ -29,6 +29,10 @@ The potential top-level array names and their nesting properties are: + "endpoints":: nest under ports or buses (if ports are not emitted) + "memdevs":: nest under endpoints or ports (if endpoints are not + emitted) or buses (if endpoints and ports are not emitted) ++"root decoders":: nest under buses ++"port decoders":: nest under ports, or buses (if ports are not emitted) ++"endpoint decoders":: nest under endpoints, or ports (if endpoints are ++ not emitted) or buses (if endpoints and ports are not emitted) + + Filters can by specifed as either a single identidier, a space separated + quoted string, or a comma separated list. When multiple filter +@@ -254,6 +258,18 @@ OPTIONS + Specify CXL endpoint device name(s), or device id(s) to filter + the emitted endpoint(s). + ++-D:: ++--decoders:: ++ Include decoder objects (CXL Memory decode capability instances ++ in buses, ports, and endpoints) in the listing. ++ ++-d:: ++--decoder:: ++ Specify CXL decoder device name(s), device id(s), or decoder type names ++ to filter the emitted decoder(s). The format for a decoder name is ++ "decoder.". The possible decoder type names are ++ "root", "switch", or "endpoint", similar to the port filter syntax. ++ + --debug:: + If the cxl tool was built with debug enabled, turn on debug + messages. +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 49edb71..73af3d0 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -179,6 +179,7 @@ struct cxl_bus *cxl_bus_get_first(struct cxl_ctx *ctx); + struct cxl_bus *cxl_bus_get_next(struct cxl_bus *bus); + struct cxl_ctx *cxl_bus_get_ctx(struct cxl_bus *bus); + struct cxl_bus *cxl_memdev_get_bus(struct cxl_memdev *memdev); ++struct cxl_bus *cxl_port_get_bus(struct cxl_port *port); + struct cxl_bus *cxl_endpoint_get_bus(struct cxl_endpoint *endpoint); + + #define cxl_bus_foreach(ctx, bus) \ +@@ -215,9 +216,9 @@ struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus); + struct cxl_port *cxl_port_get_first(struct cxl_port *parent); + struct cxl_port *cxl_port_get_next(struct cxl_port *port); + struct cxl_port *cxl_port_get_parent(struct cxl_port *port); +-struct cxl_bus *cxl_port_get_bus(struct cxl_port *port); + struct cxl_ctx *cxl_port_get_ctx(struct cxl_port *port); + const char *cxl_port_get_host(struct cxl_port *port); ++struct cxl_port *cxl_decoder_get_port(struct cxl_decoder *decoder); + + #define cxl_port_foreach(parent, port) \ + for (port = cxl_port_get_first(parent); port != NULL; \ +@@ -284,6 +285,73 @@ int cxl_endpoint_get_id(struct cxl_endpoint *endpoint); + int cxl_endpoint_is_enabled(struct cxl_endpoint *endpoint); + ---- + ++DECODERS ++-------- ++Decoder objects are associated with the "HDM Decoder Capability" ++published in Port devices and CXL capable PCIe endpoints. The kernel ++additionally models platform firmware described CXL memory ranges (like ++the ACPI CEDT.CFMWS) as static decoder objects. They route System ++Physical Addresses through a port topology to an endpoint decoder that ++does the final translation from SPA to DPA (system-physical-address to ++device-local-physical-address). ++ ++=== DECODER: Enumeration ++---- ++struct cxl_decoder *cxl_decoder_get_first(struct cxl_port *port); ++struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder); ++struct cxl_ctx *cxl_decoder_get_ctx(struct cxl_decoder *decoder); ++ ++#define cxl_decoder_foreach(port, decoder) \ ++ for (decoder = cxl_decoder_get_first(port); decoder != NULL; \ ++ decoder = cxl_decoder_get_next(decoder)) ++---- ++The definition of a CXL port in libcxl is an object that hosts one or ++more CXL decoder objects. ++ ++=== DECODER: Attributes ++---- ++unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder); ++unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder); ++const char *cxl_decoder_get_devname(struct cxl_decoder *decoder); ++int cxl_decoder_get_id(struct cxl_decoder *decoder); ++ ++enum cxl_decoder_target_type { ++ CXL_DECODER_TTYPE_UNKNOWN, ++ CXL_DECODER_TTYPE_EXPANDER, ++ CXL_DECODER_TTYPE_ACCELERATOR, ++}; ++ ++cxl_decoder_get_target_type(struct cxl_decoder *decoder); ++bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder); ++bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder); ++bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder); ++bool cxl_decoder_is_accelmem_capable(struct cxl_decoder *decoder); ++bool cxl_decoder_is_locked(struct cxl_decoder *decoder); ++---- ++The kernel protects the enumeration of the physical address layout of ++the system. Without CAP_SYS_ADMIN cxl_decoder_get_resource() returns ++ULLONG_MAX to indicate that the address information was not retrievable. ++Otherwise, cxl_decoder_get_resource() returns the currently programmed ++value of the base of the decoder's decode range. A zero-sized decoder ++indicates a disabled decoder. ++ ++Root level decoders only support limited set of memory types in their ++address range. The cxl_decoder_is__capable() helpers identify ++what is supported. Switch level decoders, in contrast are capable of ++routing any memory type, i.e. they just forward along the memory type ++support from their parent port. Endpoint decoders follow the ++capabilities of their host memory device. ++ ++The capabilities of a decoder are not to be confused with their type / ++mode. The type ultimately depends on the endpoint. For example an ++accelerator requires all decoders in its ancestry to be set to ++CXL_DECODER_TTYPE_ACCELERATOR, and conversely plain memory expander ++devices require CXL_DECODER_TTYPE_EXPANDER. ++ ++Platform firmware may setup the CXL decode hierarchy before the OS ++boots, and may additionally require that the OS not change the decode ++settings. This property is indicated by the cxl_decoder_is_locked() API. ++ + include::../../copyright.txt[] + + SEE ALSO +diff --git a/cxl/filter.c b/cxl/filter.c +index 6dc61a1..dc052f6 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -171,6 +171,17 @@ util_cxl_endpoint_filter_by_port(struct cxl_endpoint *endpoint, + return NULL; + } + ++static struct cxl_decoder * ++util_cxl_decoder_filter_by_port(struct cxl_decoder *decoder, const char *ident, ++ enum cxl_port_filter_mode mode) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ ++ if (util_cxl_port_filter(port, ident, mode)) ++ return decoder; ++ return NULL; ++} ++ + static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, + const char *__ident) + { +@@ -233,6 +244,16 @@ static struct cxl_port *util_cxl_port_filter_by_bus(struct cxl_port *port, + return NULL; + } + ++static struct cxl_decoder * ++util_cxl_decoder_filter_by_bus(struct cxl_decoder *decoder, const char *__ident) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ ++ if (!util_cxl_port_filter_by_bus(port, __ident)) ++ return NULL; ++ return decoder; ++} ++ + static struct cxl_memdev * + util_cxl_memdev_serial_filter(struct cxl_memdev *memdev, const char *__serials) + { +@@ -357,6 +378,49 @@ static struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port, + return NULL; + } + ++static struct cxl_decoder *util_cxl_decoder_filter(struct cxl_decoder *decoder, ++ const char *__ident) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ int pid, did; ++ char *ident, *save; ++ const char *name; ++ ++ if (!__ident) ++ return decoder; ++ ++ ident = strdup(__ident); ++ if (!ident) ++ return NULL; ++ ++ for (name = strtok_r(ident, which_sep(__ident), &save); name; ++ name = strtok_r(NULL, which_sep(__ident), &save)) { ++ if (strcmp(name, "all") == 0) ++ break; ++ ++ if (strcmp(name, "root") == 0 && cxl_port_is_root(port)) ++ break; ++ if (strcmp(name, "switch") == 0 && cxl_port_is_switch(port)) ++ break; ++ if (strcmp(name, "endpoint") == 0 && cxl_port_is_endpoint(port)) ++ break; ++ ++ if ((sscanf(name, "%d.%d", &pid, &did) == 2 || ++ sscanf(name, "decoder%d.%d", &pid, &did) == 2) && ++ cxl_port_get_id(port) == pid && ++ cxl_decoder_get_id(decoder) == did) ++ break; ++ ++ if (strcmp(name, cxl_decoder_get_devname(decoder)) == 0) ++ break; ++ } ++ ++ free(ident); ++ if (name) ++ return decoder; ++ return NULL; ++} ++ + static unsigned long params_to_flags(struct cxl_filter_params *param) + { + unsigned long flags = 0; +@@ -440,15 +504,44 @@ static struct json_object *pick_array(struct json_object *child, + return NULL; + } + ++static void walk_decoders(struct cxl_port *port, struct cxl_filter_params *p, ++ struct json_object *jdecoders, unsigned long flags) ++{ ++ struct cxl_decoder *decoder; ++ ++ cxl_decoder_foreach(port, decoder) { ++ struct json_object *jdecoder; ++ ++ if (!p->decoders) ++ continue; ++ if (!util_cxl_decoder_filter(decoder, p->decoder_filter)) ++ continue; ++ if (!util_cxl_decoder_filter_by_bus(decoder, p->bus_filter)) ++ continue; ++ if (!util_cxl_decoder_filter_by_port(decoder, p->port_filter, ++ pf_mode(p))) ++ continue; ++ if (!p->idle && cxl_decoder_get_size(decoder) == 0) ++ continue; ++ jdecoder = util_cxl_decoder_to_json(decoder, flags); ++ if (!decoder) { ++ dbg(p, "decoder object allocation failure\n"); ++ continue; ++ } ++ json_object_array_add(jdecoders, jdecoder); ++ } ++} ++ + static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p, + struct json_object *jeps, struct json_object *jdevs, +- unsigned long flags) ++ struct json_object *jdecoders, unsigned long flags) + { + struct cxl_endpoint *endpoint; + + cxl_endpoint_foreach(port, endpoint) { + struct cxl_port *ep_port = cxl_endpoint_get_port(endpoint); + const char *devname = cxl_endpoint_get_devname(endpoint); ++ struct json_object *jchilddecoders = NULL; + struct json_object *jendpoint = NULL; + struct cxl_memdev *memdev; + +@@ -495,14 +588,31 @@ static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p, + else + json_object_array_add(jdevs, jobj); + } ++ ++ if (p->decoders && p->endpoints) { ++ jchilddecoders = json_object_new_array(); ++ if (!jchilddecoders) { ++ err(p, ++ "%s: failed to enumerate child decoders\n", ++ devname); ++ continue; ++ } ++ } ++ ++ if (!p->decoders) ++ continue; ++ walk_decoders(cxl_endpoint_get_port(endpoint), p, ++ pick_array(jchilddecoders, jdecoders), flags); ++ cond_add_put_array_suffix(jendpoint, "decoders", devname, ++ jchilddecoders); + } + } + +-static void walk_child_ports(struct cxl_port *parent_port, +- struct cxl_filter_params *p, +- struct json_object *jports, +- struct json_object *jeps, +- struct json_object *jdevs, unsigned long flags) ++static void ++walk_child_ports(struct cxl_port *parent_port, struct cxl_filter_params *p, ++ struct json_object *jports, struct json_object *jportdecoders, ++ struct json_object *jeps, struct json_object *jepdecoders, ++ struct json_object *jdevs, unsigned long flags) + { + struct cxl_port *port; + +@@ -512,6 +622,7 @@ static void walk_child_ports(struct cxl_port *parent_port, + struct json_object *jchilddevs = NULL; + struct json_object *jchildports = NULL; + struct json_object *jchildeps = NULL; ++ struct json_object *jchilddecoders = NULL; + + if (!util_cxl_port_filter_by_memdev(port, p->memdev_filter, + p->serial_filter)) +@@ -555,19 +666,37 @@ static void walk_child_ports(struct cxl_port *parent_port, + continue; + } + } ++ ++ if (p->decoders) { ++ jchilddecoders = json_object_new_array(); ++ if (!jchilddecoders) { ++ err(p, ++ "%s: failed to enumerate child decoders\n", ++ devname); ++ continue; ++ } ++ } + } + + walk_children: +- if (p->endpoints || p->memdevs) ++ if (p->endpoints || p->memdevs || p->decoders) + walk_endpoints(port, p, pick_array(jchildeps, jeps), +- pick_array(jchilddevs, jdevs), flags); ++ pick_array(jchilddevs, jdevs), ++ pick_array(jchilddecoders, jepdecoders), ++ flags); + ++ walk_decoders(port, p, ++ pick_array(jchilddecoders, jportdecoders), flags); + walk_child_ports(port, p, pick_array(jchildports, jports), ++ pick_array(jchilddecoders, jportdecoders), + pick_array(jchildeps, jeps), ++ pick_array(jchilddecoders, jepdecoders), + pick_array(jchilddevs, jdevs), flags); + cond_add_put_array_suffix(jport, "ports", devname, jchildports); + cond_add_put_array_suffix(jport, "endpoints", devname, + jchildeps); ++ cond_add_put_array_suffix(jport, "decoders", devname, ++ jchilddecoders); + cond_add_put_array_suffix(jport, "memdevs", devname, + jchilddevs); + } +@@ -578,6 +707,9 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + struct json_object *jdevs = NULL, *jbuses = NULL, *jports = NULL; + struct json_object *jplatform = json_object_new_array(); + unsigned long flags = params_to_flags(p); ++ struct json_object *jportdecoders = NULL; ++ struct json_object *jbusdecoders = NULL; ++ struct json_object *jepdecoders = NULL; + struct json_object *janondevs = NULL; + struct json_object *jeps = NULL; + struct cxl_memdev *memdev; +@@ -609,6 +741,18 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + if (!jdevs) + goto err; + ++ jbusdecoders = json_object_new_array(); ++ if (!jbusdecoders) ++ goto err; ++ ++ jportdecoders = json_object_new_array(); ++ if (!jportdecoders) ++ goto err; ++ ++ jepdecoders = json_object_new_array(); ++ if (!jepdecoders) ++ goto err; ++ + dbg(p, "walk memdevs\n"); + cxl_memdev_foreach(ctx, memdev) { + struct json_object *janondev; +@@ -633,6 +777,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + dbg(p, "walk buses\n"); + cxl_bus_foreach(ctx, bus) { + struct json_object *jbus = NULL; ++ struct json_object *jchilddecoders = NULL; + struct json_object *jchildports = NULL; + struct json_object *jchilddevs = NULL; + struct json_object *jchildeps = NULL; +@@ -681,15 +826,33 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + continue; + } + } ++ if (p->decoders) { ++ jchilddecoders = json_object_new_array(); ++ if (!jchilddecoders) { ++ err(p, ++ "%s: failed to enumerate child decoders\n", ++ devname); ++ continue; ++ } ++ } ++ + } + walk_children: ++ dbg(p, "walk decoders\n"); ++ walk_decoders(port, p, pick_array(jchilddecoders, jbusdecoders), ++ flags); ++ + dbg(p, "walk ports\n"); + walk_child_ports(port, p, pick_array(jchildports, jports), ++ pick_array(jchilddecoders, jportdecoders), + pick_array(jchildeps, jeps), ++ pick_array(jchilddecoders, jepdecoders), + pick_array(jchilddevs, jdevs), flags); + cond_add_put_array_suffix(jbus, "ports", devname, jchildports); + cond_add_put_array_suffix(jbus, "endpoints", devname, + jchildeps); ++ cond_add_put_array_suffix(jbus, "decoders", devname, ++ jchilddecoders); + cond_add_put_array_suffix(jbus, "memdevs", devname, jchilddevs); + } + +@@ -703,12 +866,24 @@ walk_children: + top_level_objs++; + if (json_object_array_length(jdevs)) + top_level_objs++; ++ if (json_object_array_length(jbusdecoders)) ++ top_level_objs++; ++ if (json_object_array_length(jportdecoders)) ++ top_level_objs++; ++ if (json_object_array_length(jepdecoders)) ++ top_level_objs++; + + splice_array(p, janondevs, jplatform, "anon memdevs", top_level_objs > 1); + splice_array(p, jbuses, jplatform, "buses", top_level_objs > 1); + splice_array(p, jports, jplatform, "ports", top_level_objs > 1); + splice_array(p, jeps, jplatform, "endpoints", top_level_objs > 1); + splice_array(p, jdevs, jplatform, "memdevs", top_level_objs > 1); ++ splice_array(p, jbusdecoders, jplatform, "root decoders", ++ top_level_objs > 1); ++ splice_array(p, jportdecoders, jplatform, "port decoders", ++ top_level_objs > 1); ++ splice_array(p, jepdecoders, jplatform, "endpoint decoders", ++ top_level_objs > 1); + + util_display_json_array(stdout, jplatform, flags); + +@@ -719,6 +894,9 @@ err: + json_object_put(jports); + json_object_put(jeps); + json_object_put(jdevs); ++ json_object_put(jbusdecoders); ++ json_object_put(jportdecoders); ++ json_object_put(jepdecoders); + json_object_put(jplatform); + return -ENOMEM; + } +diff --git a/cxl/filter.h b/cxl/filter.h +index bbd341c..5d7bf45 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -12,8 +12,10 @@ struct cxl_filter_params { + const char *bus_filter; + const char *port_filter; + const char *endpoint_filter; ++ const char *decoder_filter; + bool single; + bool endpoints; ++ bool decoders; + bool memdevs; + bool ports; + bool buses; +diff --git a/cxl/json.c b/cxl/json.c +index 51918d6..548bc52 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -262,6 +262,68 @@ struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus, + return jbus; + } + ++struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, ++ unsigned long flags) ++{ ++ const char *devname = cxl_decoder_get_devname(decoder); ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ struct json_object *jdecoder, *jobj; ++ u64 val; ++ ++ jdecoder = json_object_new_object(); ++ if (!jdecoder) ++ return NULL; ++ ++ jobj = json_object_new_string(devname); ++ if (jobj) ++ json_object_object_add(jdecoder, "decoder", jobj); ++ ++ val = cxl_decoder_get_resource(decoder); ++ if (val < ULLONG_MAX) { ++ jobj = util_json_object_hex(val, flags); ++ if (jobj) ++ json_object_object_add(jdecoder, "resource", jobj); ++ } ++ ++ val = cxl_decoder_get_size(decoder); ++ if (val < ULLONG_MAX) { ++ jobj = util_json_object_size(val, flags); ++ if (jobj) ++ json_object_object_add(jdecoder, "size", jobj); ++ } ++ ++ if (val == 0) { ++ jobj = json_object_new_string("disabled"); ++ if (jobj) ++ json_object_object_add(jdecoder, "state", jobj); ++ } ++ ++ if (cxl_port_is_root(port) && cxl_decoder_is_mem_capable(decoder)) { ++ if (cxl_decoder_is_pmem_capable(decoder)) { ++ jobj = json_object_new_boolean(true); ++ if (jobj) ++ json_object_object_add(jdecoder, "pmem_capable", ++ jobj); ++ } ++ if (cxl_decoder_is_volatile_capable(decoder)) { ++ jobj = json_object_new_boolean(true); ++ if (jobj) ++ json_object_object_add( ++ jdecoder, "volatile_capable", jobj); ++ } ++ } ++ ++ if (cxl_port_is_root(port) && ++ cxl_decoder_is_accelmem_capable(decoder)) { ++ jobj = json_object_new_boolean(true); ++ if (jobj) ++ json_object_object_add(jdecoder, "accelmem_capable", ++ jobj); ++ } ++ ++ return jdecoder; ++} ++ + static struct json_object *__util_cxl_port_to_json(struct cxl_port *port, + const char *name_key, + unsigned long flags) +diff --git a/cxl/json.h b/cxl/json.h +index 8f45190..fcca2e6 100644 +--- a/cxl/json.h ++++ b/cxl/json.h +@@ -13,4 +13,6 @@ struct json_object *util_cxl_port_to_json(struct cxl_port *port, + unsigned long flags); + struct json_object *util_cxl_endpoint_to_json(struct cxl_endpoint *endpoint, + unsigned long flags); ++struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, ++ unsigned long flags); + #endif /* __CXL_UTIL_JSON_H__ */ +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 2fdaf71..5e30923 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -63,16 +63,26 @@ static void free_memdev(struct cxl_memdev *memdev, struct list_head *head) + free(memdev->firmware_version); + free(memdev->dev_buf); + free(memdev->dev_path); +- free(memdev->host); ++ free(memdev->host_path); + free(memdev); + } + ++static void free_decoder(struct cxl_decoder *decoder, struct list_head *head) ++{ ++ if (head) ++ list_del_from(head, &decoder->list); ++ free(decoder->dev_buf); ++ free(decoder->dev_path); ++ free(decoder); ++} ++ + static void free_port(struct cxl_port *port, struct list_head *head); + static void free_endpoint(struct cxl_endpoint *endpoint, struct list_head *head); + static void __free_port(struct cxl_port *port, struct list_head *head) + { + struct cxl_port *child, *_c; + struct cxl_endpoint *endpoint, *_e; ++ struct cxl_decoder *decoder, *_d; + + if (head) + list_del_from(head, &port->list); +@@ -80,6 +90,8 @@ static void __free_port(struct cxl_port *port, struct list_head *head) + free_port(child, &port->child_ports); + list_for_each_safe(&port->endpoints, endpoint, _e, port.list) + free_endpoint(endpoint, &port->endpoints); ++ list_for_each_safe(&port->decoders, decoder, _d, list) ++ free_decoder(decoder, &port->decoders); + kmod_module_unref(port->module); + free(port->dev_buf); + free(port->dev_path); +@@ -298,9 +310,9 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + char *path = calloc(1, strlen(cxlmem_base) + 100); + struct cxl_ctx *ctx = parent; + struct cxl_memdev *memdev, *memdev_dup; +- char *host, *rpath = NULL; + char buf[SYSFS_ATTR_SIZE]; + struct stat st; ++ char *host; + + if (!path) + return NULL; +@@ -358,21 +370,13 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + if (!memdev->dev_path) + goto err_read; + +- rpath = realpath(cxlmem_base, NULL); +- if (!rpath) ++ memdev->host_path = realpath(cxlmem_base, NULL); ++ if (!memdev->host_path) + goto err_read; +- host = strrchr(rpath, '/'); +- if (host) { +- host[0] = '\0'; +- host = strrchr(rpath, '/'); +- } ++ host = strrchr(memdev->host_path, '/'); + if (!host) + goto err_read; +- memdev->host = strdup(host + 1); +- if (!memdev->host) +- goto err_read; +- free(rpath); +- rpath = NULL; ++ host[0] = '\0'; + + sprintf(path, "%s/firmware_version", cxlmem_base); + if (sysfs_read_attr(ctx, path, buf) < 0) +@@ -404,8 +408,8 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) + free(memdev->firmware_version); + free(memdev->dev_buf); + free(memdev->dev_path); ++ free(memdev->host_path); + free(memdev); +- free(rpath); + err_dev: + free(path); + return NULL; +@@ -463,7 +467,7 @@ CXL_EXPORT const char *cxl_memdev_get_devname(struct cxl_memdev *memdev) + + CXL_EXPORT const char *cxl_memdev_get_host(struct cxl_memdev *memdev) + { +- return memdev->host; ++ return devpath_to_devname(memdev->host_path); + } + + CXL_EXPORT struct cxl_bus *cxl_memdev_get_bus(struct cxl_memdev *memdev) +@@ -679,9 +683,11 @@ static int cxl_port_init(struct cxl_port *port, struct cxl_port *parent_port, + port->ctx = ctx; + port->type = type; + port->parent = parent_port; ++ port->type = type; + + list_head_init(&port->child_ports); + list_head_init(&port->endpoints); ++ list_head_init(&port->decoders); + + port->dev_path = strdup(cxlport_base); + if (!port->dev_path) +@@ -842,6 +848,207 @@ cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint) + return NULL; + } + ++static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) ++{ ++ const char *devname = devpath_to_devname(cxldecoder_base); ++ char *path = calloc(1, strlen(cxldecoder_base) + 100); ++ struct cxl_decoder *decoder, *decoder_dup; ++ struct cxl_port *port = parent; ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ char buf[SYSFS_ATTR_SIZE]; ++ size_t i; ++ ++ dbg(ctx, "%s: base: \'%s\'\n", devname, cxldecoder_base); ++ ++ if (!path) ++ return NULL; ++ ++ decoder = calloc(1, sizeof(*decoder)); ++ if (!decoder) ++ goto err; ++ ++ decoder->id = id; ++ decoder->ctx = ctx; ++ decoder->port = port; ++ ++ decoder->dev_path = strdup(cxldecoder_base); ++ if (!decoder->dev_path) ++ goto err; ++ ++ decoder->dev_buf = calloc(1, strlen(cxldecoder_base) + 50); ++ if (!decoder->dev_buf) ++ goto err; ++ decoder->buf_len = strlen(cxldecoder_base) + 50; ++ ++ sprintf(path, "%s/start", cxldecoder_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ decoder->start = ULLONG_MAX; ++ else ++ decoder->start = strtoull(buf, NULL, 0); ++ ++ sprintf(path, "%s/size", cxldecoder_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ decoder->size = ULLONG_MAX; ++ else ++ decoder->size = strtoull(buf, NULL, 0); ++ ++ switch (port->type) { ++ case CXL_PORT_SWITCH: ++ case CXL_PORT_ENDPOINT: ++ decoder->pmem_capable = true; ++ decoder->volatile_capable = true; ++ decoder->mem_capable = true; ++ decoder->accelmem_capable = true; ++ sprintf(path, "%s/locked", cxldecoder_base); ++ if (sysfs_read_attr(ctx, path, buf) == 0) ++ decoder->locked = !!strtoul(buf, NULL, 0); ++ sprintf(path, "%s/target_type", cxldecoder_base); ++ if (sysfs_read_attr(ctx, path, buf) == 0) { ++ if (strcmp(buf, "accelerator") == 0) ++ decoder->target_type = ++ CXL_DECODER_TTYPE_ACCELERATOR; ++ if (strcmp(buf, "expander") == 0) ++ decoder->target_type = ++ CXL_DECODER_TTYPE_EXPANDER; ++ } ++ break; ++ case CXL_PORT_ROOT: { ++ struct cxl_decoder_flag { ++ char *name; ++ bool *flag; ++ } flags[] = { ++ { "cap_type2", &decoder->accelmem_capable }, ++ { "cap_type3", &decoder->mem_capable }, ++ { "cap_ram", &decoder->volatile_capable }, ++ { "cap_pmem", &decoder->pmem_capable }, ++ { "locked", &decoder->locked }, ++ }; ++ ++ for (i = 0; i < ARRAY_SIZE(flags); i++) { ++ struct cxl_decoder_flag *flag = &flags[i]; ++ ++ sprintf(path, "%s/%s", cxldecoder_base, flag->name); ++ if (sysfs_read_attr(ctx, path, buf) == 0) ++ *(flag->flag) = !!strtoul(buf, NULL, 0); ++ } ++ break; ++ } ++ } ++ ++ cxl_decoder_foreach(port, decoder_dup) ++ if (decoder_dup->id == decoder->id) { ++ free_decoder(decoder, NULL); ++ return decoder_dup; ++ } ++ ++ list_add(&port->decoders, &decoder->list); ++ ++ return decoder; ++err: ++ free(decoder->dev_path); ++ free(decoder->dev_buf); ++ free(decoder); ++ free(path); ++ return NULL; ++} ++ ++static void cxl_decoders_init(struct cxl_port *port) ++{ ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ char *decoder_fmt; ++ ++ if (port->decoders_init) ++ return; ++ ++ if (asprintf(&decoder_fmt, "decoder%d.", cxl_port_get_id(port)) < 0) { ++ err(ctx, "%s: failed to add decoder(s)\n", ++ cxl_port_get_devname(port)); ++ return; ++ } ++ ++ port->decoders_init = 1; ++ ++ sysfs_device_parse(ctx, port->dev_path, decoder_fmt, port, ++ add_cxl_decoder); ++ ++ free(decoder_fmt); ++} ++ ++CXL_EXPORT struct cxl_decoder *cxl_decoder_get_first(struct cxl_port *port) ++{ ++ cxl_decoders_init(port); ++ ++ return list_top(&port->decoders, struct cxl_decoder, list); ++} ++ ++CXL_EXPORT struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder) ++{ ++ struct cxl_port *port = decoder->port; ++ ++ return list_next(&port->decoders, decoder, list); ++} ++ ++CXL_EXPORT struct cxl_ctx *cxl_decoder_get_ctx(struct cxl_decoder *decoder) ++{ ++ return decoder->ctx; ++} ++ ++CXL_EXPORT int cxl_decoder_get_id(struct cxl_decoder *decoder) ++{ ++ return decoder->id; ++} ++ ++CXL_EXPORT struct cxl_port *cxl_decoder_get_port(struct cxl_decoder *decoder) ++{ ++ return decoder->port; ++} ++ ++CXL_EXPORT unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder) ++{ ++ return decoder->start; ++} ++ ++CXL_EXPORT unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder) ++{ ++ return decoder->size; ++} ++ ++CXL_EXPORT enum cxl_decoder_target_type ++cxl_decoder_get_target_type(struct cxl_decoder *decoder) ++{ ++ return decoder->target_type; ++} ++ ++CXL_EXPORT bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder) ++{ ++ return decoder->pmem_capable; ++} ++ ++CXL_EXPORT bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder) ++{ ++ return decoder->volatile_capable; ++} ++ ++CXL_EXPORT bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder) ++{ ++ return decoder->mem_capable; ++} ++ ++CXL_EXPORT bool cxl_decoder_is_accelmem_capable(struct cxl_decoder *decoder) ++{ ++ return decoder->accelmem_capable; ++} ++ ++CXL_EXPORT bool cxl_decoder_is_locked(struct cxl_decoder *decoder) ++{ ++ return decoder->locked; ++} ++ ++CXL_EXPORT const char *cxl_decoder_get_devname(struct cxl_decoder *decoder) ++{ ++ return devpath_to_devname(decoder->dev_path); ++} ++ + static void *add_cxl_port(void *parent, int id, const char *cxlport_base) + { + const char *devname = devpath_to_devname(cxlport_base); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index f235e99..22babb7 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -117,4 +117,18 @@ global: + cxl_memdev_get_bus; + cxl_memdev_disable_invalidate; + cxl_memdev_enable; ++ cxl_decoder_get_first; ++ cxl_decoder_get_next; ++ cxl_decoder_get_ctx; ++ cxl_decoder_get_id; ++ cxl_decoder_get_port; ++ cxl_decoder_get_resource; ++ cxl_decoder_get_size; ++ cxl_decoder_get_devname; ++ cxl_decoder_get_target_type; ++ cxl_decoder_is_pmem_capable; ++ cxl_decoder_is_volatile_capable; ++ cxl_decoder_is_mem_capable; ++ cxl_decoder_is_accelmem_capable; ++ cxl_decoder_is_locked; + } LIBCXL_1; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index c00bb36..1743a24 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -23,7 +23,7 @@ struct cxl_memdev { + int numa_node; + void *dev_buf; + size_t buf_len; +- char *host; ++ char *host_path; + char *dev_path; + char *firmware_version; + struct cxl_ctx *ctx; +@@ -52,6 +52,7 @@ struct cxl_port { + char *uport; + int ports_init; + int endpoints_init; ++ int decoders_init; + struct cxl_ctx *ctx; + struct cxl_bus *bus; + enum cxl_port_type type; +@@ -60,6 +61,7 @@ struct cxl_port { + struct list_node list; + struct list_head child_ports; + struct list_head endpoints; ++ struct list_head decoders; + }; + + struct cxl_bus { +@@ -71,6 +73,33 @@ struct cxl_endpoint { + struct cxl_memdev *memdev; + }; + ++struct cxl_target { ++ struct list_node list; ++ struct cxl_decoder *decoder; ++ char *dev_path; ++ int id, position; ++}; ++ ++struct cxl_decoder { ++ struct cxl_port *port; ++ struct list_node list; ++ struct cxl_ctx *ctx; ++ u64 start; ++ u64 size; ++ void *dev_buf; ++ size_t buf_len; ++ char *dev_path; ++ int nr_targets; ++ int id; ++ bool pmem_capable; ++ bool volatile_capable; ++ bool mem_capable; ++ bool accelmem_capable; ++ bool locked; ++ enum cxl_decoder_target_type target_type; ++ struct list_head targets; ++}; ++ + enum cxl_cmd_query_status { + CXL_CMD_QUERY_NOT_RUN = 0, + CXL_CMD_QUERY_OK, +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 53f68dd..439ed93 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -98,6 +98,34 @@ bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev); + for (port = cxl_port_get_first(parent); port != NULL; \ + port = cxl_port_get_next(port)) + ++struct cxl_decoder; ++struct cxl_decoder *cxl_decoder_get_first(struct cxl_port *port); ++struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder); ++unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder); ++unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder); ++const char *cxl_decoder_get_devname(struct cxl_decoder *decoder); ++struct cxl_ctx *cxl_decoder_get_ctx(struct cxl_decoder *decoder); ++int cxl_decoder_get_id(struct cxl_decoder *decoder); ++struct cxl_port *cxl_decoder_get_port(struct cxl_decoder *decoder); ++ ++enum cxl_decoder_target_type { ++ CXL_DECODER_TTYPE_UNKNOWN, ++ CXL_DECODER_TTYPE_EXPANDER, ++ CXL_DECODER_TTYPE_ACCELERATOR, ++}; ++ ++enum cxl_decoder_target_type ++cxl_decoder_get_target_type(struct cxl_decoder *decoder); ++bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder); ++bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder); ++bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder); ++bool cxl_decoder_is_accelmem_capable(struct cxl_decoder *decoder); ++bool cxl_decoder_is_locked(struct cxl_decoder *decoder); ++ ++#define cxl_decoder_foreach(port, decoder) \ ++ for (decoder = cxl_decoder_get_first(port); decoder != NULL; \ ++ decoder = cxl_decoder_get_next(decoder)) ++ + struct cxl_endpoint; + struct cxl_endpoint *cxl_endpoint_get_first(struct cxl_port *parent); + struct cxl_endpoint *cxl_endpoint_get_next(struct cxl_endpoint *endpoint); +diff --git a/cxl/list.c b/cxl/list.c +index b15e01c..d70192a 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -36,6 +36,11 @@ static const struct option options[] = { + "filter by CXL endpoint device name(s)"), + OPT_BOOLEAN('E', "endpoints", ¶m.endpoints, + "include CXL endpoint info"), ++ OPT_STRING('d', "decoder", ¶m.decoder_filter, ++ "decoder device name", ++ "filter by CXL decoder device name(s) / class"), ++ OPT_BOOLEAN('D', "decoders", ¶m.decoders, ++ "include CXL decoder info"), + OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), + OPT_BOOLEAN('u', "human", ¶m.human, + "use human friendly number formats "), +@@ -50,7 +55,7 @@ static const struct option options[] = { + static int num_list_flags(void) + { + return !!param.memdevs + !!param.buses + !!param.ports + +- !!param.endpoints; ++ !!param.endpoints + !!param.decoders; + } + + int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) +@@ -82,6 +87,8 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + param.ports = true; + if (param.endpoint_filter) + param.endpoints = true; ++ if (param.decoder_filter) ++ param.decoders = true; + if (num_list_flags() == 0) { + /* + * TODO: We likely want to list regions by default if +diff --git a/util/json.h b/util/json.h +index 061f0d4..e026df1 100644 +--- a/util/json.h ++++ b/util/json.h +@@ -18,6 +18,7 @@ enum util_json_flags { + UTIL_JSON_FIRMWARE = (1 << 8), + UTIL_JSON_DAX_MAPPINGS = (1 << 9), + UTIL_JSON_HEALTH = (1 << 10), ++ UTIL_JSON_TARGETS = (1 << 11), + }; + + void util_display_json_array(FILE *f_out, struct json_object *jarray, +-- +2.27.0 + diff --git a/SOURCES/0117-cxl-list-Extend-decoder-objects-with-target-informat.patch b/SOURCES/0117-cxl-list-Extend-decoder-objects-with-target-informat.patch new file mode 100644 index 0000000..2490b20 --- /dev/null +++ b/SOURCES/0117-cxl-list-Extend-decoder-objects-with-target-informat.patch @@ -0,0 +1,537 @@ +From 1279d1989ef77085d214a193c1458b624039c612 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:54:34 -0800 +Subject: [PATCH 117/217] cxl/list: Extend decoder objects with target + information + +A target combines information about a dport along with its position in the +intereleave order. With targets enumerated decoders can also be filtered be +memory devices by seeing which decoders have a dport in the memory-device's +ancestry. + +$ cxl list -D -d 3.1 -T -u +{ + "decoder":"decoder3.1", + "resource":"0x8030000000", + "size":"512.00 MiB (536.87 MB)", + "volatile_capable":true, + "nr_targets":2, + "targets":[ + { + "target":"cxl_host_bridge.1", + "position":1, + "id":"0x1" + }, + { + "target":"cxl_host_bridge.0", + "position":0, + "id":"0" + } + ] +} + +Link: https://lore.kernel.org/r/164298567435.3021641.3771899644901785666.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .clang-format | 1 + + Documentation/cxl/cxl-list.txt | 8 ++- + Documentation/cxl/lib/libcxl.txt | 58 ++++++++++++++++ + cxl/filter.c | 25 +++++++ + cxl/filter.h | 1 + + cxl/json.c | 46 +++++++++++++ + cxl/lib/libcxl.c | 115 +++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 10 +++ + cxl/libcxl.h | 19 +++++ + cxl/list.c | 2 + + 10 files changed, 283 insertions(+), 2 deletions(-) + +diff --git a/.clang-format b/.clang-format +index 16e28ac..47fb657 100644 +--- a/.clang-format ++++ b/.clang-format +@@ -81,6 +81,7 @@ ForEachMacros: + - 'cxl_bus_foreach' + - 'cxl_port_foreach' + - 'cxl_decoder_foreach' ++ - 'cxl_target_foreach' + - 'cxl_endpoint_foreach' + - 'daxctl_dev_foreach' + - 'daxctl_mapping_foreach' +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 84872b9..20ff2cb 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -44,8 +44,8 @@ would only list objects that are beneath port10 AND map mem0, mem1, OR + mem2. + + Given that many topology queries seek to answer questions relative to a +-given memdev, buses, ports, and endpoints can be filtered by one or more +-memdevs. For example: ++given memdev, buses, ports, endpoints, and decoders can be filtered by ++one or more memdevs. For example: + ---- + # cxl list -P -p switch,endpoint -m mem0 + [ +@@ -270,6 +270,10 @@ OPTIONS + "decoder.". The possible decoder type names are + "root", "switch", or "endpoint", similar to the port filter syntax. + ++-T:: ++--targets:: ++ Extend decoder listings with downstream port target information. ++ + --debug:: + If the cxl tool was built with debug enabled, turn on debug + messages. +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 73af3d0..bd92fef 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -300,6 +300,7 @@ device-local-physical-address). + struct cxl_decoder *cxl_decoder_get_first(struct cxl_port *port); + struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder); + struct cxl_ctx *cxl_decoder_get_ctx(struct cxl_decoder *decoder); ++struct cxl_decoder *cxl_target_get_decoder(struct cxl_target *target); + + #define cxl_decoder_foreach(port, decoder) \ + for (decoder = cxl_decoder_get_first(port); decoder != NULL; \ +@@ -314,6 +315,7 @@ unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder); + const char *cxl_decoder_get_devname(struct cxl_decoder *decoder); + int cxl_decoder_get_id(struct cxl_decoder *decoder); ++int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder); + + enum cxl_decoder_target_type { + CXL_DECODER_TTYPE_UNKNOWN, +@@ -352,6 +354,62 @@ Platform firmware may setup the CXL decode hierarchy before the OS + boots, and may additionally require that the OS not change the decode + settings. This property is indicated by the cxl_decoder_is_locked() API. + ++==== TARGETS ++A root or switch level decoder takes an SPA (system-physical-address) as ++input and routes it to a downstream port. Which downstream port depends ++on the downstream port's position in the interleave. A 'struct ++cxl_target' object represents the properties of a given downstream port ++relative to its interleave configuration. ++ ++===== TARGET: Enumeration ++---- ++struct cxl_target *cxl_decoder_get_target_by_memdev(struct cxl_decoder *decoder, ++ struct cxl_memdev *memdev); ++struct cxl_target * ++cxl_decoder_get_target_by_position(struct cxl_decoder *decoder, int position); ++struct cxl_target *cxl_target_get_first(struct cxl_decoder *decoder); ++struct cxl_target *cxl_target_get_next(struct cxl_target *target); ++ ++#define cxl_target_foreach(decoder, target) \ ++ for (target = cxl_target_get_first(decoder); target != NULL; \ ++ target = cxl_target_get_next(target)) ++---- ++Target objects can only be enumerated if the decoder has been ++configured, for switch decoders. For root decoders they are always ++available since the root decoder target mapping is static. The ++cxl_decoder_get_target_by_memdev() helper walks the topology to validate ++if the given memory device is capable of receiving cycles from this ++upstream decoder. It does not validate if the memory device is currently ++configured to participate in that decode. ++ ++===== TARGET: Attributes ++---- ++int cxl_target_get_position(struct cxl_target *target); ++unsigned long cxl_target_get_id(struct cxl_target *target); ++const char *cxl_target_get_devname(struct cxl_target *target); ++bool cxl_target_maps_memdev(struct cxl_target *target, ++ struct cxl_memdev *memdev); ++---- ++The position of a decoder along with the interleave granularity dictate ++which address in the decoder's resource range map to which port. ++ ++The target id is an identifier that the CXL port uses to reference this ++downstream port. For CXL / PCIe downstream switch ports the id is ++defined by the PCIe Link Capability Port Number field. For root decoders ++the id is specified by platform firmware specific mechanism. For ++ACPI.CXL defined root ports the id comes from the CEDT.CHBS / ACPI0016 ++_UID. ++ ++The device name of a target is the name of the host device for the ++downstream port. For CXL / PCIe downstream ports the devname is ++downstream switch port PCI device. For CXL root ports the devname is a ++platform firmware object for the host bridge like a ACPI0016 device ++instance. ++ ++The cxl_target_maps_memdev() helper is the companion of ++cxl_decoder_get_target_by_memdev() to determine which downstream ports / ++targets are capable of mapping which memdevs. ++ + include::../../copyright.txt[] + + SEE ALSO +diff --git a/cxl/filter.c b/cxl/filter.c +index dc052f6..05ede91 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -421,6 +421,26 @@ static struct cxl_decoder *util_cxl_decoder_filter(struct cxl_decoder *decoder, + return NULL; + } + ++static struct cxl_decoder * ++util_cxl_decoder_filter_by_memdev(struct cxl_decoder *decoder, ++ const char *ident, const char *serial) ++{ ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ struct cxl_memdev *memdev; ++ ++ if (!ident && !serial) ++ return decoder; ++ ++ cxl_memdev_foreach(ctx, memdev) { ++ if (!util_cxl_memdev_filter(memdev, ident, serial)) ++ continue; ++ if (cxl_decoder_get_target_by_memdev(decoder, memdev)) ++ return decoder; ++ } ++ ++ return NULL; ++} ++ + static unsigned long params_to_flags(struct cxl_filter_params *param) + { + unsigned long flags = 0; +@@ -431,6 +451,8 @@ static unsigned long params_to_flags(struct cxl_filter_params *param) + flags |= UTIL_JSON_HUMAN; + if (param->health) + flags |= UTIL_JSON_HEALTH; ++ if (param->targets) ++ flags |= UTIL_JSON_TARGETS; + return flags; + } + +@@ -521,6 +543,9 @@ static void walk_decoders(struct cxl_port *port, struct cxl_filter_params *p, + if (!util_cxl_decoder_filter_by_port(decoder, p->port_filter, + pf_mode(p))) + continue; ++ if (!util_cxl_decoder_filter_by_memdev( ++ decoder, p->memdev_filter, p->serial_filter)) ++ continue; + if (!p->idle && cxl_decoder_get_size(decoder) == 0) + continue; + jdecoder = util_cxl_decoder_to_json(decoder, flags); +diff --git a/cxl/filter.h b/cxl/filter.h +index 5d7bf45..6fd469f 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -16,6 +16,7 @@ struct cxl_filter_params { + bool single; + bool endpoints; + bool decoders; ++ bool targets; + bool memdevs; + bool ports; + bool buses; +diff --git a/cxl/json.c b/cxl/json.c +index 548bc52..3a37909 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -268,6 +268,8 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + const char *devname = cxl_decoder_get_devname(decoder); + struct cxl_port *port = cxl_decoder_get_port(decoder); + struct json_object *jdecoder, *jobj; ++ struct json_object *jtargets; ++ struct cxl_target *target; + u64 val; + + jdecoder = json_object_new_object(); +@@ -321,7 +323,51 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + jobj); + } + ++ /* Endpoints don't have targets, they *are* targets */ ++ if (cxl_port_is_endpoint(port)) ++ return jdecoder; ++ ++ val = cxl_decoder_get_nr_targets(decoder); ++ jobj = json_object_new_int(val); ++ if (jobj) ++ json_object_object_add(jdecoder, "nr_targets", jobj); ++ ++ if (!(flags & UTIL_JSON_TARGETS) || ++ !cxl_decoder_get_nr_targets(decoder)) ++ return jdecoder; ++ ++ jtargets = json_object_new_array(); ++ if (!jtargets) ++ return jdecoder; ++ ++ cxl_target_foreach(decoder, target) { ++ struct json_object *jtarget = json_object_new_object(); ++ ++ if (!jtarget) ++ continue; ++ ++ devname = cxl_target_get_devname(target); ++ jobj = json_object_new_string(devname); ++ if (jobj) ++ json_object_object_add(jtarget, "target", jobj); ++ ++ val = cxl_target_get_position(target); ++ jobj = json_object_new_int(val); ++ if (jobj) ++ json_object_object_add(jtarget, "position", jobj); ++ ++ val = cxl_target_get_id(target); ++ jobj = util_json_object_hex(val, flags); ++ if (jobj) ++ json_object_object_add(jtarget, "id", jobj); ++ ++ json_object_array_add(jtargets, jtarget); ++ } ++ ++ json_object_object_add(jdecoder, "targets", jtargets); ++ + return jdecoder; ++ + } + + static struct json_object *__util_cxl_port_to_json(struct cxl_port *port, +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 5e30923..877f42c 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -67,10 +67,22 @@ static void free_memdev(struct cxl_memdev *memdev, struct list_head *head) + free(memdev); + } + ++static void free_target(struct cxl_target *target, struct list_head *head) ++{ ++ if (head) ++ list_del_from(head, &target->list); ++ free(target->dev_path); ++ free(target); ++} ++ + static void free_decoder(struct cxl_decoder *decoder, struct list_head *head) + { ++ struct cxl_target *target, *_t; ++ + if (head) + list_del_from(head, &decoder->list); ++ list_for_each_safe(&decoder->targets, target, _t, list) ++ free_target(target, &decoder->targets); + free(decoder->dev_buf); + free(decoder->dev_path); + free(decoder); +@@ -856,6 +868,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + struct cxl_port *port = parent; + struct cxl_ctx *ctx = cxl_port_get_ctx(port); + char buf[SYSFS_ATTR_SIZE]; ++ char *target_id, *save; + size_t i; + + dbg(ctx, "%s: base: \'%s\'\n", devname, cxldecoder_base); +@@ -870,6 +883,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + decoder->id = id; + decoder->ctx = ctx; + decoder->port = port; ++ list_head_init(&decoder->targets); + + decoder->dev_path = strdup(cxldecoder_base); + if (!decoder->dev_path) +@@ -935,6 +949,36 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + } + } + ++ sprintf(path, "%s/target_list", cxldecoder_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ buf[0] = '\0'; ++ ++ for (i = 0, target_id = strtok_r(buf, ",", &save); target_id; ++ target_id = strtok_r(NULL, ",", &save), i++) { ++ int did = strtoul(target_id, NULL, 0); ++ struct cxl_target *target = calloc(1, sizeof(*target)); ++ ++ if (!target) ++ break; ++ ++ target->id = did; ++ target->position = i; ++ target->decoder = decoder; ++ sprintf(port->dev_buf, "%s/dport%d", port->dev_path, did); ++ target->dev_path = realpath(port->dev_buf, NULL); ++ if (!target->dev_path) { ++ free(target); ++ break; ++ } ++ dbg(ctx, "%s: target%ld %s\n", devname, i, target->dev_path); ++ list_add(&decoder->targets, &target->list); ++ } ++ ++ if (target_id) ++ err(ctx, "%s: failed to parse target%ld\n", ++ devpath_to_devname(cxldecoder_base), i); ++ decoder->nr_targets = i; ++ + cxl_decoder_foreach(port, decoder_dup) + if (decoder_dup->id == decoder->id) { + free_decoder(decoder, NULL); +@@ -1044,11 +1088,82 @@ CXL_EXPORT bool cxl_decoder_is_locked(struct cxl_decoder *decoder) + return decoder->locked; + } + ++CXL_EXPORT int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder) ++{ ++ return decoder->nr_targets; ++} ++ + CXL_EXPORT const char *cxl_decoder_get_devname(struct cxl_decoder *decoder) + { + return devpath_to_devname(decoder->dev_path); + } + ++CXL_EXPORT struct cxl_target *cxl_target_get_first(struct cxl_decoder *decoder) ++{ ++ return list_top(&decoder->targets, struct cxl_target, list); ++} ++ ++CXL_EXPORT struct cxl_decoder *cxl_target_get_decoder(struct cxl_target *target) ++{ ++ return target->decoder; ++} ++ ++CXL_EXPORT struct cxl_target *cxl_target_get_next(struct cxl_target *target) ++{ ++ struct cxl_decoder *decoder = cxl_target_get_decoder(target); ++ ++ return list_next(&decoder->targets, target, list); ++} ++ ++CXL_EXPORT const char *cxl_target_get_devname(struct cxl_target *target) ++{ ++ return devpath_to_devname(target->dev_path); ++} ++ ++CXL_EXPORT unsigned long cxl_target_get_id(struct cxl_target *target) ++{ ++ return target->id; ++} ++ ++CXL_EXPORT int cxl_target_get_position(struct cxl_target *target) ++{ ++ return target->position; ++} ++ ++CXL_EXPORT bool cxl_target_maps_memdev(struct cxl_target *target, ++ struct cxl_memdev *memdev) ++{ ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ ++ dbg(ctx, "memdev: %s target: %s\n", memdev->host_path, ++ target->dev_path); ++ ++ return !!strstr(memdev->host_path, target->dev_path); ++} ++ ++CXL_EXPORT struct cxl_target * ++cxl_decoder_get_target_by_memdev(struct cxl_decoder *decoder, ++ struct cxl_memdev *memdev) ++{ ++ struct cxl_target *target; ++ ++ cxl_target_foreach(decoder, target) ++ if (cxl_target_maps_memdev(target, memdev)) ++ return target; ++ return NULL; ++} ++ ++CXL_EXPORT struct cxl_target * ++cxl_decoder_get_target_by_position(struct cxl_decoder *decoder, int position) ++{ ++ struct cxl_target *target; ++ ++ cxl_target_foreach(decoder, target) ++ if (target->position == position) ++ return target; ++ return NULL; ++} ++ + static void *add_cxl_port(void *parent, int id, const char *cxlport_base) + { + const char *devname = devpath_to_devname(cxlport_base); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 22babb7..cb33180 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -125,10 +125,20 @@ global: + cxl_decoder_get_resource; + cxl_decoder_get_size; + cxl_decoder_get_devname; ++ cxl_decoder_get_target_by_memdev; ++ cxl_decoder_get_target_by_position; ++ cxl_decoder_get_nr_targets; + cxl_decoder_get_target_type; + cxl_decoder_is_pmem_capable; + cxl_decoder_is_volatile_capable; + cxl_decoder_is_mem_capable; + cxl_decoder_is_accelmem_capable; + cxl_decoder_is_locked; ++ cxl_target_get_first; ++ cxl_target_get_next; ++ cxl_target_get_decoder; ++ cxl_target_get_position; ++ cxl_target_get_id; ++ cxl_target_get_devname; ++ cxl_target_maps_memdev; + } LIBCXL_1; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 439ed93..abda0e5 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -104,6 +104,11 @@ struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder); + const char *cxl_decoder_get_devname(struct cxl_decoder *decoder); ++struct cxl_target *cxl_decoder_get_target_by_memdev(struct cxl_decoder *decoder, ++ struct cxl_memdev *memdev); ++struct cxl_target * ++cxl_decoder_get_target_by_position(struct cxl_decoder *decoder, int position); ++int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder); + struct cxl_ctx *cxl_decoder_get_ctx(struct cxl_decoder *decoder); + int cxl_decoder_get_id(struct cxl_decoder *decoder); + struct cxl_port *cxl_decoder_get_port(struct cxl_decoder *decoder); +@@ -126,6 +131,20 @@ bool cxl_decoder_is_locked(struct cxl_decoder *decoder); + for (decoder = cxl_decoder_get_first(port); decoder != NULL; \ + decoder = cxl_decoder_get_next(decoder)) + ++struct cxl_target; ++struct cxl_target *cxl_target_get_first(struct cxl_decoder *decoder); ++struct cxl_target *cxl_target_get_next(struct cxl_target *target); ++struct cxl_decoder *cxl_target_get_decoder(struct cxl_target *target); ++int cxl_target_get_position(struct cxl_target *target); ++unsigned long cxl_target_get_id(struct cxl_target *target); ++const char *cxl_target_get_devname(struct cxl_target *target); ++bool cxl_target_maps_memdev(struct cxl_target *target, ++ struct cxl_memdev *memdev); ++ ++#define cxl_target_foreach(decoder, target) \ ++ for (target = cxl_target_get_first(decoder); target != NULL; \ ++ target = cxl_target_get_next(target)) ++ + struct cxl_endpoint; + struct cxl_endpoint *cxl_endpoint_get_first(struct cxl_port *parent); + struct cxl_endpoint *cxl_endpoint_get_next(struct cxl_endpoint *endpoint); +diff --git a/cxl/list.c b/cxl/list.c +index d70192a..27c963a 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -41,6 +41,8 @@ static const struct option options[] = { + "filter by CXL decoder device name(s) / class"), + OPT_BOOLEAN('D', "decoders", ¶m.decoders, + "include CXL decoder info"), ++ OPT_BOOLEAN('T', "targets", ¶m.targets, ++ "include CXL target data with decoders"), + OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), + OPT_BOOLEAN('u', "human", ¶m.human, + "use human friendly number formats "), +-- +2.27.0 + diff --git a/SOURCES/0118-cxl-list-Use-physical_node-for-root-port-attachment-.patch b/SOURCES/0118-cxl-list-Use-physical_node-for-root-port-attachment-.patch new file mode 100644 index 0000000..c216a82 --- /dev/null +++ b/SOURCES/0118-cxl-list-Use-physical_node-for-root-port-attachment-.patch @@ -0,0 +1,156 @@ +From 5fb1b8a1630115f3aa3cd6bb7bc9ba5122867f66 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:54:39 -0800 +Subject: [PATCH 118/217] cxl/list: Use 'physical_node' for root port + attachment detection + +Platform firmware objects like ACPI0016 link from /sys/bus/acpi to +/sys/bus/pci via a 'physical_node' attribute. Consider that link when +attempting to link memdevs to root ports. + +Emit it in the the target listing as the 'alias' for the listed target +device. + +Link: https://lore.kernel.org/r/164298567943.3021641.12696733268157328279.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/lib/libcxl.txt | 6 ++++++ + cxl/json.c | 8 ++++++++ + cxl/lib/libcxl.c | 16 +++++++++++++++- + cxl/lib/libcxl.sym | 1 + + cxl/lib/private.h | 1 + + cxl/libcxl.h | 1 + + 6 files changed, 32 insertions(+), 1 deletion(-) + +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index bd92fef..a68a58b 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -389,6 +389,7 @@ unsigned long cxl_target_get_id(struct cxl_target *target); + const char *cxl_target_get_devname(struct cxl_target *target); + bool cxl_target_maps_memdev(struct cxl_target *target, + struct cxl_memdev *memdev); ++const char *cxl_target_get_physical_node(struct cxl_target *target); + ---- + The position of a decoder along with the interleave granularity dictate + which address in the decoder's resource range map to which port. +@@ -410,6 +411,11 @@ The cxl_target_maps_memdev() helper is the companion of + cxl_decoder_get_target_by_memdev() to determine which downstream ports / + targets are capable of mapping which memdevs. + ++Some platform firmware implementations define an alias / companion ++device to represent the root of a PCI device hierarchy. The ++cxl_target_get_physical_node() helper returns the device name of that ++companion object in the PCI hierarchy. ++ + include::../../copyright.txt[] + + SEE ALSO +diff --git a/cxl/json.c b/cxl/json.c +index 3a37909..d81aed8 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -342,6 +342,7 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + + cxl_target_foreach(decoder, target) { + struct json_object *jtarget = json_object_new_object(); ++ const char *phys_node; + + if (!jtarget) + continue; +@@ -351,6 +352,13 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + if (jobj) + json_object_object_add(jtarget, "target", jobj); + ++ phys_node = cxl_target_get_physical_node(target); ++ if (phys_node) { ++ jobj = json_object_new_string(phys_node); ++ if (jobj) ++ json_object_object_add(jtarget, "alias", jobj); ++ } ++ + val = cxl_target_get_position(target); + jobj = json_object_new_int(val); + if (jobj) +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 877f42c..7bf7949 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -72,6 +72,7 @@ static void free_target(struct cxl_target *target, struct list_head *head) + if (head) + list_del_from(head, &target->list); + free(target->dev_path); ++ free(target->phys_path); + free(target); + } + +@@ -970,7 +971,11 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + free(target); + break; + } +- dbg(ctx, "%s: target%ld %s\n", devname, i, target->dev_path); ++ sprintf(port->dev_buf, "%s/dport%d/physical_node", port->dev_path, did); ++ target->phys_path = realpath(port->dev_buf, NULL); ++ dbg(ctx, "%s: target%ld %s phys_path: %s\n", devname, i, ++ target->dev_path, ++ target->phys_path ? target->phys_path : "none"); + list_add(&decoder->targets, &target->list); + } + +@@ -1138,9 +1143,18 @@ CXL_EXPORT bool cxl_target_maps_memdev(struct cxl_target *target, + dbg(ctx, "memdev: %s target: %s\n", memdev->host_path, + target->dev_path); + ++ if (target->phys_path) ++ return !!strstr(memdev->host_path, target->phys_path); + return !!strstr(memdev->host_path, target->dev_path); + } + ++CXL_EXPORT const char *cxl_target_get_physical_node(struct cxl_target *target) ++{ ++ if (!target->phys_path) ++ return NULL; ++ return devpath_to_devname(target->phys_path); ++} ++ + CXL_EXPORT struct cxl_target * + cxl_decoder_get_target_by_memdev(struct cxl_decoder *decoder, + struct cxl_memdev *memdev) +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index cb33180..ce01298 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -141,4 +141,5 @@ global: + cxl_target_get_id; + cxl_target_get_devname; + cxl_target_maps_memdev; ++ cxl_target_get_physical_node; + } LIBCXL_1; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 1743a24..7e7742d 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -77,6 +77,7 @@ struct cxl_target { + struct list_node list; + struct cxl_decoder *decoder; + char *dev_path; ++ char *phys_path; + int id, position; + }; + +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index abda0e5..0e484cc 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -140,6 +140,7 @@ unsigned long cxl_target_get_id(struct cxl_target *target); + const char *cxl_target_get_devname(struct cxl_target *target); + bool cxl_target_maps_memdev(struct cxl_target *target, + struct cxl_memdev *memdev); ++const char *cxl_target_get_physical_node(struct cxl_target *target); + + #define cxl_target_foreach(decoder, target) \ + for (target = cxl_target_get_first(decoder); target != NULL; \ +-- +2.27.0 + diff --git a/SOURCES/0119-cxl-list-Reuse-the-target-option-for-ports.patch b/SOURCES/0119-cxl-list-Reuse-the-target-option-for-ports.patch new file mode 100644 index 0000000..c32c697 --- /dev/null +++ b/SOURCES/0119-cxl-list-Reuse-the-target-option-for-ports.patch @@ -0,0 +1,457 @@ +From d87cee2dd4756f7e067bdadc78a0632dd666cc64 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:54:44 -0800 +Subject: [PATCH 119/217] cxl/list: Reuse the --target option for ports + +It is useful to be able to dump the dport-id to host-device-name. Rather +than introduce a new option, just interpret --target as "list dports" for +port objects. + +$ cxl list -BTu -b ACPI.CXL +{ + "bus":"root0", + "provider":"ACPI.CXL", + "nr_dports":1, + "dports":[ + { + "dport":"ACPI0016:00", + "alias":"pci0000:34", + "id":"0" + } + ] +} + +Link: https://lore.kernel.org/r/164298568481.3021641.4632086646702812643.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .clang-format | 1 + + Documentation/cxl/cxl-list.txt | 18 ++++- + Documentation/cxl/lib/libcxl.txt | 27 ++++++++ + cxl/json.c | 56 +++++++++++++++- + cxl/lib/libcxl.c | 109 ++++++++++++++++++++++++++++++- + cxl/lib/libcxl.sym | 7 ++ + cxl/lib/private.h | 13 ++++ + cxl/libcxl.h | 12 ++++ + cxl/list.c | 2 +- + 9 files changed, 240 insertions(+), 5 deletions(-) + +diff --git a/.clang-format b/.clang-format +index 47fb657..c753487 100644 +--- a/.clang-format ++++ b/.clang-format +@@ -82,6 +82,7 @@ ForEachMacros: + - 'cxl_port_foreach' + - 'cxl_decoder_foreach' + - 'cxl_target_foreach' ++ - 'cxl_dport_foreach' + - 'cxl_endpoint_foreach' + - 'daxctl_dev_foreach' + - 'daxctl_mapping_foreach' +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 20ff2cb..e1299d9 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -272,7 +272,23 @@ OPTIONS + + -T:: + --targets:: +- Extend decoder listings with downstream port target information. ++ Extend decoder listings with downstream port target information, and / ++ or port and bus listings with the downstream port information. ++---- ++# cxl list -BTu -b ACPI.CXL ++{ ++ "bus":"root0", ++ "provider":"ACPI.CXL", ++ "nr_dports":1, ++ "dports":[ ++ { ++ "dport":"ACPI0016:00", ++ "alias":"pci0000:34", ++ "id":"0" ++ } ++ ] ++} ++---- + + --debug:: + If the cxl tool was built with debug enabled, turn on debug +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index a68a58b..2e8570d 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -245,6 +245,7 @@ bool cxl_port_is_root(struct cxl_port *port); + bool cxl_port_is_switch(struct cxl_port *port); + bool cxl_port_is_endpoint(struct cxl_port *port); + bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev); ++int cxl_port_get_nr_dports(struct cxl_port *port); + ---- + The port type is communicated via cxl_port_is_(). An 'enabled' port + is one that has succeeded in discovering the CXL component registers in +@@ -256,6 +257,32 @@ of intervening switch ports, and a terminal endpoint port. + cxl_port_hosts_memdev() returns true if the port's host appears in the + memdev host's device topology ancestry. + ++==== DPORTS ++A CXL dport object represents a CXL / PCIe Switch Downstream Port, or a ++CXL / PCIe host bridge. ++ ++===== DPORT: Enumeration ++---- ++struct cxl_dport *cxl_dport_get_first(struct cxl_port *port); ++struct cxl_dport *cxl_dport_get_next(struct cxl_dport *dport); ++ ++#define cxl_dport_foreach(port, dport) \ ++ for (dport = cxl_dport_get_first(port); dport != NULL; \ ++ dport = cxl_dport_get_next(dport)) ++ ++---- ++ ++===== DPORT: Attributes ++---- ++const char *cxl_dport_get_devname(struct cxl_dport *dport); ++const char *cxl_dport_get_physical_node(struct cxl_dport *dport); ++int cxl_dport_get_id(struct cxl_dport *dport); ++---- ++The id of a dport is the hardware idenfifier used by an upstream port to ++reference a downstream port. The physical node of a dport is only ++available for platform firmware defined downstream ports and alias the ++companion object, like a PCI host bridge, in the PCI device hierarchy. ++ + ENDPOINTS + --------- + CXL endpoint objects encapsulate the set of host-managed device-memory +diff --git a/cxl/json.c b/cxl/json.c +index d81aed8..4fb5eec 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -241,6 +241,58 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + return jdev; + } + ++static struct json_object *util_cxl_dports_to_json(struct json_object *jport, ++ struct cxl_port *port, ++ unsigned long flags) ++{ ++ struct json_object *jobj, *jdports; ++ struct cxl_dport *dport; ++ int val; ++ ++ val = cxl_port_get_nr_dports(port); ++ if (!val || !(flags & UTIL_JSON_TARGETS)) ++ return jport; ++ ++ jobj = json_object_new_int(val); ++ if (jobj) ++ json_object_object_add(jport, "nr_dports", jobj); ++ ++ jdports = json_object_new_array(); ++ if (!jdports) ++ return jport; ++ ++ cxl_dport_foreach(port, dport) { ++ struct json_object *jdport; ++ const char *phys_node; ++ ++ jdport = json_object_new_object(); ++ if (!jdport) ++ continue; ++ ++ jobj = json_object_new_string(cxl_dport_get_devname(dport)); ++ if (jobj) ++ json_object_object_add(jdport, "dport", jobj); ++ ++ phys_node = cxl_dport_get_physical_node(dport); ++ if (phys_node) { ++ jobj = json_object_new_string(phys_node); ++ if (jobj) ++ json_object_object_add(jdport, "alias", jobj); ++ } ++ ++ val = cxl_dport_get_id(dport); ++ jobj = util_json_object_hex(val, flags); ++ if (jobj) ++ json_object_object_add(jdport, "id", jobj); ++ ++ json_object_array_add(jdports, jdport); ++ } ++ ++ json_object_object_add(jport, "dports", jdports); ++ ++ return jport; ++} ++ + struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus, + unsigned long flags) + { +@@ -259,7 +311,7 @@ struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus, + if (jobj) + json_object_object_add(jbus, "provider", jobj); + +- return jbus; ++ return util_cxl_dports_to_json(jbus, cxl_bus_get_port(bus), flags); + } + + struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, +@@ -403,7 +455,7 @@ static struct json_object *__util_cxl_port_to_json(struct cxl_port *port, + json_object_object_add(jport, "state", jobj); + } + +- return jport; ++ return util_cxl_dports_to_json(jport, port, flags); + } + + struct json_object *util_cxl_port_to_json(struct cxl_port *port, +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 7bf7949..d7a3f10 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -89,13 +89,24 @@ static void free_decoder(struct cxl_decoder *decoder, struct list_head *head) + free(decoder); + } + ++static void free_dport(struct cxl_dport *dport, struct list_head *head) ++{ ++ if (head) ++ list_del_from(head, &dport->list); ++ free(dport->dev_buf); ++ free(dport->dev_path); ++ free(dport->phys_path); ++ free(dport); ++} ++ + static void free_port(struct cxl_port *port, struct list_head *head); + static void free_endpoint(struct cxl_endpoint *endpoint, struct list_head *head); + static void __free_port(struct cxl_port *port, struct list_head *head) + { +- struct cxl_port *child, *_c; + struct cxl_endpoint *endpoint, *_e; + struct cxl_decoder *decoder, *_d; ++ struct cxl_dport *dport, *_dp; ++ struct cxl_port *child, *_c; + + if (head) + list_del_from(head, &port->list); +@@ -105,6 +116,8 @@ static void __free_port(struct cxl_port *port, struct list_head *head) + free_endpoint(endpoint, &port->endpoints); + list_for_each_safe(&port->decoders, decoder, _d, list) + free_decoder(decoder, &port->decoders); ++ list_for_each_safe(&port->dports, dport, _dp, list) ++ free_dport(dport , &port->dports); + kmod_module_unref(port->module); + free(port->dev_buf); + free(port->dev_path); +@@ -701,6 +714,7 @@ static int cxl_port_init(struct cxl_port *port, struct cxl_port *parent_port, + list_head_init(&port->child_ports); + list_head_init(&port->endpoints); + list_head_init(&port->decoders); ++ list_head_init(&port->dports); + + port->dev_path = strdup(cxlport_base); + if (!port->dev_path) +@@ -1332,6 +1346,99 @@ CXL_EXPORT struct cxl_bus *cxl_port_to_bus(struct cxl_port *port) + return container_of(port, struct cxl_bus, port); + } + ++static void *add_cxl_dport(void *parent, int id, const char *cxldport_base) ++{ ++ const char *devname = devpath_to_devname(cxldport_base); ++ struct cxl_dport *dport, *dport_dup; ++ struct cxl_port *port = parent; ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ ++ dbg(ctx, "%s: base: \'%s\'\n", devname, cxldport_base); ++ ++ dport = calloc(1, sizeof(*dport)); ++ if (!dport) ++ return NULL; ++ ++ dport->id = id; ++ dport->port = port; ++ ++ dport->dev_path = realpath(cxldport_base, NULL); ++ if (!dport->dev_path) ++ goto err; ++ ++ dport->dev_buf = calloc(1, strlen(cxldport_base) + 50); ++ if (!dport->dev_buf) ++ goto err; ++ dport->buf_len = strlen(cxldport_base) + 50; ++ ++ sprintf(dport->dev_buf, "%s/physical_node", cxldport_base); ++ dport->phys_path = realpath(dport->dev_buf, NULL); ++ ++ cxl_dport_foreach(port, dport_dup) ++ if (dport_dup->id == dport->id) { ++ free_dport(dport, NULL); ++ return dport_dup; ++ } ++ ++ port->nr_dports++; ++ list_add(&port->dports, &dport->list); ++ return dport; ++ ++err: ++ free_dport(dport, NULL); ++ return NULL; ++} ++ ++static void cxl_dports_init(struct cxl_port *port) ++{ ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ ++ if (port->dports_init) ++ return; ++ ++ port->dports_init = 1; ++ ++ sysfs_device_parse(ctx, port->dev_path, "dport", port, add_cxl_dport); ++} ++ ++CXL_EXPORT int cxl_port_get_nr_dports(struct cxl_port *port) ++{ ++ if (!port->dports_init) ++ cxl_dports_init(port); ++ return port->nr_dports; ++} ++ ++CXL_EXPORT struct cxl_dport *cxl_dport_get_first(struct cxl_port *port) ++{ ++ cxl_dports_init(port); ++ ++ return list_top(&port->dports, struct cxl_dport, list); ++} ++ ++CXL_EXPORT struct cxl_dport *cxl_dport_get_next(struct cxl_dport *dport) ++{ ++ struct cxl_port *port = dport->port; ++ ++ return list_next(&port->dports, dport, list); ++} ++ ++CXL_EXPORT const char *cxl_dport_get_devname(struct cxl_dport *dport) ++{ ++ return devpath_to_devname(dport->dev_path); ++} ++ ++CXL_EXPORT const char *cxl_dport_get_physical_node(struct cxl_dport *dport) ++{ ++ if (!dport->phys_path) ++ return NULL; ++ return devpath_to_devname(dport->phys_path); ++} ++ ++CXL_EXPORT int cxl_dport_get_id(struct cxl_dport *dport) ++{ ++ return dport->id; ++} ++ + static void *add_cxl_bus(void *parent, int id, const char *cxlbus_base) + { + const char *devname = devpath_to_devname(cxlbus_base); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index ce01298..0190b13 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -101,6 +101,8 @@ global: + cxl_port_get_host; + cxl_port_get_bus; + cxl_port_hosts_memdev; ++ cxl_port_get_nr_dports; ++ cxl_port_get_next_all; + cxl_endpoint_get_first; + cxl_endpoint_get_next; + cxl_endpoint_get_devname; +@@ -142,4 +144,9 @@ global: + cxl_target_get_devname; + cxl_target_maps_memdev; + cxl_target_get_physical_node; ++ cxl_dport_get_first; ++ cxl_dport_get_next; ++ cxl_dport_get_devname; ++ cxl_dport_get_physical_node; ++ cxl_dport_get_id; + } LIBCXL_1; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 7e7742d..f483c30 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -38,6 +38,16 @@ struct cxl_memdev { + struct cxl_endpoint *endpoint; + }; + ++struct cxl_dport { ++ int id; ++ void *dev_buf; ++ size_t buf_len; ++ char *dev_path; ++ char *phys_path; ++ struct cxl_port *port; ++ struct list_node list; ++}; ++ + enum cxl_port_type { + CXL_PORT_ROOT, + CXL_PORT_SWITCH, +@@ -53,6 +63,8 @@ struct cxl_port { + int ports_init; + int endpoints_init; + int decoders_init; ++ int dports_init; ++ int nr_dports; + struct cxl_ctx *ctx; + struct cxl_bus *bus; + enum cxl_port_type type; +@@ -62,6 +74,7 @@ struct cxl_port { + struct list_head child_ports; + struct list_head endpoints; + struct list_head decoders; ++ struct list_head dports; + }; + + struct cxl_bus { +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 0e484cc..07f4a31 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -93,11 +93,23 @@ bool cxl_port_is_endpoint(struct cxl_port *port); + struct cxl_bus *cxl_port_get_bus(struct cxl_port *port); + const char *cxl_port_get_host(struct cxl_port *port); + bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev); ++int cxl_port_get_nr_dports(struct cxl_port *port); + + #define cxl_port_foreach(parent, port) \ + for (port = cxl_port_get_first(parent); port != NULL; \ + port = cxl_port_get_next(port)) + ++struct cxl_dport; ++struct cxl_dport *cxl_dport_get_first(struct cxl_port *port); ++struct cxl_dport *cxl_dport_get_next(struct cxl_dport *dport); ++const char *cxl_dport_get_devname(struct cxl_dport *dport); ++const char *cxl_dport_get_physical_node(struct cxl_dport *dport); ++int cxl_dport_get_id(struct cxl_dport *dport); ++ ++#define cxl_dport_foreach(port, dport) \ ++ for (dport = cxl_dport_get_first(port); dport != NULL; \ ++ dport = cxl_dport_get_next(dport)) ++ + struct cxl_decoder; + struct cxl_decoder *cxl_decoder_get_first(struct cxl_port *port); + struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder); +diff --git a/cxl/list.c b/cxl/list.c +index 27c963a..de96ff9 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -42,7 +42,7 @@ static const struct option options[] = { + OPT_BOOLEAN('D', "decoders", ¶m.decoders, + "include CXL decoder info"), + OPT_BOOLEAN('T', "targets", ¶m.targets, +- "include CXL target data with decoders"), ++ "include CXL target data with decoders or ports"), + OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), + OPT_BOOLEAN('u', "human", ¶m.human, + "use human friendly number formats "), +-- +2.27.0 + diff --git a/SOURCES/0120-cxl-list-Support-filtering-memdevs-by-decoders.patch b/SOURCES/0120-cxl-list-Support-filtering-memdevs-by-decoders.patch new file mode 100644 index 0000000..58ab116 --- /dev/null +++ b/SOURCES/0120-cxl-list-Support-filtering-memdevs-by-decoders.patch @@ -0,0 +1,205 @@ +From aa022d33418021da81a51bc9656931c54043b10b Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:54:50 -0800 +Subject: [PATCH 120/217] cxl/list: Support filtering memdevs by decoders + +In order to filter memdevs by decoders all the ports in the hierarchy need +to be iterated, so introduce cxl_port_foreach_all() that starts at the bus +and does a depth first iteration of all the descendant ports. + +Link: https://lore.kernel.org/r/164298569017.3021641.15558596583530530035.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .clang-format | 1 + + Documentation/cxl/cxl-list.txt | 13 +++++++++ + Documentation/cxl/lib/libcxl.txt | 11 ++++++++ + cxl/filter.c | 48 ++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.c | 13 +++++++++ + cxl/libcxl.h | 6 ++++ + 6 files changed, 92 insertions(+) + +diff --git a/.clang-format b/.clang-format +index c753487..6aabcb6 100644 +--- a/.clang-format ++++ b/.clang-format +@@ -84,6 +84,7 @@ ForEachMacros: + - 'cxl_target_foreach' + - 'cxl_dport_foreach' + - 'cxl_endpoint_foreach' ++ - 'cxl_port_foreach_all' + - 'daxctl_dev_foreach' + - 'daxctl_mapping_foreach' + - 'daxctl_region_foreach' +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index e1299d9..04e831e 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -61,6 +61,19 @@ one or more memdevs. For example: + } + ] + ---- ++Additionally, when provisioning new interleave configurations it is ++useful to know which memdevs can be referenced by a given decoder like a ++root decoder: ++---- ++# cxl list -Mu -d decoder0.0 ++{ ++ "memdev":"mem0", ++ "pmem_size":"256.00 MiB (268.44 MB)", ++ "ram_size":0, ++ "serial":"0", ++ "host":"0000:35:00.0" ++} ++---- + + The --human option in addition to reformatting some fields to more human + friendly strings also unwraps the array to reduce the number of lines of +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 2e8570d..5ad3027 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -219,10 +219,18 @@ struct cxl_port *cxl_port_get_parent(struct cxl_port *port); + struct cxl_ctx *cxl_port_get_ctx(struct cxl_port *port); + const char *cxl_port_get_host(struct cxl_port *port); + struct cxl_port *cxl_decoder_get_port(struct cxl_decoder *decoder); ++struct cxl_port *cxl_port_get_next_all(struct cxl_port *port, ++ const struct cxl_port *top); + + #define cxl_port_foreach(parent, port) \ + for (port = cxl_port_get_first(parent); port != NULL; \ + port = cxl_port_get_next(port)) ++ ++#define cxl_port_foreach_all(top, port) \ ++ for (port = cxl_port_get_first(top); port != NULL; \ ++ port = cxl_port_get_next_all(port, top)) ++ ++ + ---- + A bus object encapsulates a CXL port object. Use cxl_bus_get_port() to + use generic port APIs on root objects. +@@ -236,6 +244,9 @@ that hierarchy via cxl_port_get_bus(). + The host of a port is the corresponding device name of the PCIe Root + Port, or Switch Upstream Port with CXL capabilities. + ++The cxl_port_foreach_all() helper does a depth first iteration of all ++ports beneath the 'top' port argument. ++ + === PORT: Attributes + ---- + const char *cxl_port_get_devname(struct cxl_port *port); +diff --git a/cxl/filter.c b/cxl/filter.c +index 05ede91..c972545 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -441,6 +441,51 @@ util_cxl_decoder_filter_by_memdev(struct cxl_decoder *decoder, + return NULL; + } + ++static bool __memdev_filter_by_decoder(struct cxl_memdev *memdev, ++ struct cxl_port *port, const char *ident) ++{ ++ struct cxl_decoder *decoder; ++ struct cxl_endpoint *endpoint; ++ ++ cxl_decoder_foreach(port, decoder) { ++ if (!util_cxl_decoder_filter(decoder, ident)) ++ continue; ++ if (cxl_decoder_get_target_by_memdev(decoder, memdev)) ++ return true; ++ } ++ ++ cxl_endpoint_foreach(port, endpoint) ++ if (__memdev_filter_by_decoder( ++ memdev, cxl_endpoint_get_port(endpoint), ident)) ++ return true; ++ return false; ++} ++ ++static struct cxl_memdev * ++util_cxl_memdev_filter_by_decoder(struct cxl_memdev *memdev, const char *ident) ++{ ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ struct cxl_bus *bus; ++ ++ if (!ident) ++ return memdev; ++ ++ cxl_bus_foreach(ctx, bus) { ++ struct cxl_port *port, *top; ++ ++ port = cxl_bus_get_port(bus); ++ if (__memdev_filter_by_decoder(memdev, port, ident)) ++ return memdev; ++ ++ top = port; ++ cxl_port_foreach_all(top, port) ++ if (__memdev_filter_by_decoder(memdev, port, ident)) ++ return memdev; ++ } ++ ++ return NULL; ++} ++ + static unsigned long params_to_flags(struct cxl_filter_params *param) + { + unsigned long flags = 0; +@@ -599,6 +644,9 @@ static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p, + if (!util_cxl_memdev_filter(memdev, p->memdev_filter, + p->serial_filter)) + continue; ++ if (!util_cxl_memdev_filter_by_decoder( ++ memdev, p->decoder_filter)) ++ continue; + if (!p->idle && !cxl_memdev_is_enabled(memdev)) + continue; + jobj = util_cxl_memdev_to_json(memdev, flags); +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index d7a3f10..4ebb8b9 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -1257,6 +1257,19 @@ CXL_EXPORT struct cxl_port *cxl_port_get_next(struct cxl_port *port) + return list_next(&parent_port->child_ports, port, list); + } + ++CXL_EXPORT struct cxl_port *cxl_port_get_next_all(struct cxl_port *port, ++ const struct cxl_port *top) ++{ ++ struct cxl_port *child, *iter = port; ++ ++ child = cxl_port_get_first(iter); ++ if (child) ++ return child; ++ while (!cxl_port_get_next(iter) && iter->parent && iter->parent != top) ++ iter = iter->parent; ++ return cxl_port_get_next(iter); ++} ++ + CXL_EXPORT const char *cxl_port_get_devname(struct cxl_port *port) + { + return devpath_to_devname(port->dev_path); +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 07f4a31..874c381 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -94,11 +94,17 @@ struct cxl_bus *cxl_port_get_bus(struct cxl_port *port); + const char *cxl_port_get_host(struct cxl_port *port); + bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev); + int cxl_port_get_nr_dports(struct cxl_port *port); ++struct cxl_port *cxl_port_get_next_all(struct cxl_port *port, ++ const struct cxl_port *top); + + #define cxl_port_foreach(parent, port) \ + for (port = cxl_port_get_first(parent); port != NULL; \ + port = cxl_port_get_next(port)) + ++#define cxl_port_foreach_all(top, port) \ ++ for (port = cxl_port_get_first(top); port != NULL; \ ++ port = cxl_port_get_next_all(port, top)) ++ + struct cxl_dport; + struct cxl_dport *cxl_dport_get_first(struct cxl_port *port); + struct cxl_dport *cxl_dport_get_next(struct cxl_dport *dport); +-- +2.27.0 + diff --git a/SOURCES/0121-cxl-list-Support-filtering-memdevs-by-ports.patch b/SOURCES/0121-cxl-list-Support-filtering-memdevs-by-ports.patch new file mode 100644 index 0000000..55721ff --- /dev/null +++ b/SOURCES/0121-cxl-list-Support-filtering-memdevs-by-ports.patch @@ -0,0 +1,200 @@ +From 15cae420681c5e8efad2b4cbaf0470960e2eba52 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:54:55 -0800 +Subject: [PATCH 121/217] cxl/list: Support filtering memdevs by ports + +The ability to filter memdevs by decoders falls short when the decoder does +not have its target list programmed. So, introduce a by port filter to show +the potential memdevs that can be targeted by the decoder. + +Link: https://lore.kernel.org/r/164298569568.3021641.888802471376117408.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 3 +- + Documentation/cxl/lib/libcxl.txt | 7 ++++- + cxl/filter.c | 50 ++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.c | 23 +++++++++++++++ + cxl/lib/libcxl.sym | 2 ++ + cxl/libcxl.h | 3 ++ + 6 files changed, 86 insertions(+), 2 deletions(-) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 04e831e..90e6d9f 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -63,7 +63,8 @@ one or more memdevs. For example: + ---- + Additionally, when provisioning new interleave configurations it is + useful to know which memdevs can be referenced by a given decoder like a +-root decoder: ++root decoder, or mapped by a given port if the decoders are not ++configured. + ---- + # cxl list -Mu -d decoder0.0 + { +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 5ad3027..a0fcee9 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -276,11 +276,12 @@ CXL / PCIe host bridge. + ---- + struct cxl_dport *cxl_dport_get_first(struct cxl_port *port); + struct cxl_dport *cxl_dport_get_next(struct cxl_dport *dport); ++struct cxl_dport *cxl_port_get_dport_by_memdev(struct cxl_port *port, ++ struct cxl_memdev *memdev); + + #define cxl_dport_foreach(port, dport) \ + for (dport = cxl_dport_get_first(port); dport != NULL; \ + dport = cxl_dport_get_next(dport)) +- + ---- + + ===== DPORT: Attributes +@@ -288,12 +289,16 @@ struct cxl_dport *cxl_dport_get_next(struct cxl_dport *dport); + const char *cxl_dport_get_devname(struct cxl_dport *dport); + const char *cxl_dport_get_physical_node(struct cxl_dport *dport); + int cxl_dport_get_id(struct cxl_dport *dport); ++bool cxl_dport_maps_memdev(struct cxl_dport *dport, struct cxl_memdev *memdev); + ---- + The id of a dport is the hardware idenfifier used by an upstream port to + reference a downstream port. The physical node of a dport is only + available for platform firmware defined downstream ports and alias the + companion object, like a PCI host bridge, in the PCI device hierarchy. + ++The cxl_dport_maps_memdev() helper checks if a dport is an ancestor of a ++given memdev. ++ + ENDPOINTS + --------- + CXL endpoint objects encapsulate the set of host-managed device-memory +diff --git a/cxl/filter.c b/cxl/filter.c +index c972545..c691edf 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -486,6 +486,53 @@ util_cxl_memdev_filter_by_decoder(struct cxl_memdev *memdev, const char *ident) + return NULL; + } + ++static bool __memdev_filter_by_port(struct cxl_memdev *memdev, ++ struct cxl_port *port, ++ const char *port_ident) ++{ ++ struct cxl_endpoint *endpoint; ++ ++ if (util_cxl_port_filter(port, port_ident, CXL_PF_SINGLE) && ++ cxl_port_get_dport_by_memdev(port, memdev)) ++ return true; ++ ++ cxl_endpoint_foreach(port, endpoint) ++ if (__memdev_filter_by_port(memdev, ++ cxl_endpoint_get_port(endpoint), ++ port_ident)) ++ return true; ++ return false; ++} ++ ++static struct cxl_memdev * ++util_cxl_memdev_filter_by_port(struct cxl_memdev *memdev, const char *bus_ident, ++ const char *port_ident) ++{ ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ struct cxl_bus *bus; ++ ++ if (!bus_ident && !port_ident) ++ return memdev; ++ ++ cxl_bus_foreach(ctx, bus) { ++ struct cxl_port *port, *top; ++ ++ port = cxl_bus_get_port(bus); ++ if (util_cxl_bus_filter(bus, bus_ident)) ++ if (__memdev_filter_by_port(memdev, port, ++ cxl_bus_get_devname(bus))) ++ return memdev; ++ if (__memdev_filter_by_port(memdev, port, port_ident)) ++ return memdev; ++ top = port; ++ cxl_port_foreach_all(top, port) ++ if (__memdev_filter_by_port(memdev, port, port_ident)) ++ return memdev; ++ } ++ ++ return NULL; ++} ++ + static unsigned long params_to_flags(struct cxl_filter_params *param) + { + unsigned long flags = 0; +@@ -647,6 +694,9 @@ static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p, + if (!util_cxl_memdev_filter_by_decoder( + memdev, p->decoder_filter)) + continue; ++ if (!util_cxl_memdev_filter_by_port( ++ memdev, p->bus_filter, p->port_filter)) ++ continue; + if (!p->idle && !cxl_memdev_is_enabled(memdev)) + continue; + jobj = util_cxl_memdev_to_json(memdev, flags); +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 4ebb8b9..dcfc826 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -1452,6 +1452,29 @@ CXL_EXPORT int cxl_dport_get_id(struct cxl_dport *dport) + return dport->id; + } + ++CXL_EXPORT bool cxl_dport_maps_memdev(struct cxl_dport *dport, ++ struct cxl_memdev *memdev) ++{ ++ struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); ++ ++ dbg(ctx, "memdev: %s dport: %s\n", memdev->host_path, dport->dev_path); ++ ++ if (dport->phys_path) ++ return !!strstr(memdev->host_path, dport->phys_path); ++ return !!strstr(memdev->host_path, dport->dev_path); ++} ++ ++CXL_EXPORT struct cxl_dport * ++cxl_port_get_dport_by_memdev(struct cxl_port *port, struct cxl_memdev *memdev) ++{ ++ struct cxl_dport *dport; ++ ++ cxl_dport_foreach(port, dport) ++ if (cxl_dport_maps_memdev(dport, memdev)) ++ return dport; ++ return NULL; ++} ++ + static void *add_cxl_bus(void *parent, int id, const char *cxlbus_base) + { + const char *devname = devpath_to_devname(cxlbus_base); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 0190b13..2c8358e 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -149,4 +149,6 @@ global: + cxl_dport_get_devname; + cxl_dport_get_physical_node; + cxl_dport_get_id; ++ cxl_port_get_dport_by_memdev; ++ cxl_dport_maps_memdev; + } LIBCXL_1; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 874c381..c8d07bb 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -111,6 +111,9 @@ struct cxl_dport *cxl_dport_get_next(struct cxl_dport *dport); + const char *cxl_dport_get_devname(struct cxl_dport *dport); + const char *cxl_dport_get_physical_node(struct cxl_dport *dport); + int cxl_dport_get_id(struct cxl_dport *dport); ++bool cxl_dport_maps_memdev(struct cxl_dport *dport, struct cxl_memdev *memdev); ++struct cxl_dport *cxl_port_get_dport_by_memdev(struct cxl_port *port, ++ struct cxl_memdev *memdev); + + #define cxl_dport_foreach(port, dport) \ + for (dport = cxl_dport_get_first(port); dport != NULL; \ +-- +2.27.0 + diff --git a/SOURCES/0122-cxl-port-Add-disable-enable-port-command.patch b/SOURCES/0122-cxl-port-Add-disable-enable-port-command.patch new file mode 100644 index 0000000..975156d --- /dev/null +++ b/SOURCES/0122-cxl-port-Add-disable-enable-port-command.patch @@ -0,0 +1,734 @@ +From 1cfb7570369ae6bed832bde908435d38fa990f9d Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:55:01 -0800 +Subject: [PATCH 122/217] cxl/port: Add {disable,enable}-port command + +The {disable,enable}-port commands are used for debugging port enumeration +corner cases and testing the kernel CXL device hotplug implementation. + +In addition to unbinding the port from its driver, which also kicks of +unregistration of descendent ports, the disable operation also flushes the +kernels delayed workqueue for memory device removal. + +Link: https://lore.kernel.org/r/164298570117.3021641.14546710754812021284.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-disable-port.txt | 46 +++++ + Documentation/cxl/cxl-enable-port.txt | 43 +++++ + Documentation/cxl/lib/libcxl.txt | 11 ++ + Documentation/cxl/meson.build | 2 + + cxl/builtin.h | 2 + + cxl/cxl.c | 2 + + cxl/filter.c | 21 +- + cxl/filter.h | 13 ++ + cxl/lib/libcxl.c | 95 +++++++++- + cxl/lib/libcxl.sym | 3 + + cxl/libcxl.h | 3 + + cxl/meson.build | 1 + + cxl/port.c | 253 +++++++++++++++++++++++++ + 13 files changed, 471 insertions(+), 24 deletions(-) + create mode 100644 Documentation/cxl/cxl-disable-port.txt + create mode 100644 Documentation/cxl/cxl-enable-port.txt + create mode 100644 cxl/port.c + +diff --git a/Documentation/cxl/cxl-disable-port.txt b/Documentation/cxl/cxl-disable-port.txt +new file mode 100644 +index 0000000..de13c07 +--- /dev/null ++++ b/Documentation/cxl/cxl-disable-port.txt +@@ -0,0 +1,46 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-disable-port(1) ++=================== ++ ++NAME ++---- ++cxl-disable-port - activate / hot-add a given CXL port ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl disable-port' [..] [] ++ ++For test and debug scenarios, disable a CXL port and any memory devices ++dependent on this port being active for CXL.mem operation. ++ ++OPTIONS ++------- ++-e:: ++--endpoint:: ++ Toggle from treating the port arguments as Switch Port identifiers to ++ Endpoint Port identifiers. ++ ++ ++-f:: ++--force:: ++ DANGEROUS: Override the safety measure that blocks attempts to disable a ++ port if the tool determines a descendent memdev is in active usage. ++ Recall that CXL memory ranges might have been established by platform ++ firmware and disabling an active device is akin to force removing memory ++ from a running system. ++ ++ Toggle from treating the port arguments as Switch Port identifiers to ++ Endpoint Port identifiers. ++ ++--debug:: ++ If the cxl tool was built with debug disabled, turn on debug ++ messages. ++ ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-disable-port[1] +diff --git a/Documentation/cxl/cxl-enable-port.txt b/Documentation/cxl/cxl-enable-port.txt +new file mode 100644 +index 0000000..9a37cef +--- /dev/null ++++ b/Documentation/cxl/cxl-enable-port.txt +@@ -0,0 +1,43 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-enable-port(1) ++================== ++ ++NAME ++---- ++cxl-enable-port - activate / hot-add a given CXL port ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl enable-port' [..] [] ++ ++A port typically autoenables at initial device discovery. However, if it ++was manually disabled this command can trigger the kernel to activate it ++again. This involves detecting the state of the HDM (Host Managed Device ++Memory) Decoders and validating that CXL.mem is enabled for each port in ++the device's hierarchy. ++ ++OPTIONS ++------- ++-e:: ++--endpoint:: ++ Toggle from treating the port arguments as Switch Port identifiers to ++ Endpoint Port identifiers. ++ ++-m:: ++--enable-memdevs:: ++ Try to enable descendant memdevs after enabling the port. Recall that a ++ memdev is only enabled after all CXL ports in its device topology ++ ancestry are enabled. ++ ++--debug:: ++ If the cxl tool was built with debug enabled, turn on debug ++ messages. ++ ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-disable-port[1] +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index a0fcee9..27eb29e 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -247,6 +247,16 @@ Port, or Switch Upstream Port with CXL capabilities. + The cxl_port_foreach_all() helper does a depth first iteration of all + ports beneath the 'top' port argument. + ++=== PORT: Control ++--- ++int cxl_port_disable_invalidate(struct cxl_port *port); ++int cxl_port_enable(struct cxl_port *port); ++--- ++cxl_port_disable_invalidate() is a violent operation that disables ++entire sub-tree of CXL Memory Device and Ports, only use it for test / ++debug scenarios, or ensuring that all impacted devices are deactivated ++first. ++ + === PORT: Attributes + ---- + const char *cxl_port_get_devname(struct cxl_port *port); +@@ -315,6 +325,7 @@ struct cxl_port *cxl_endpoint_get_parent(struct cxl_endpoint *endpoint); + struct cxl_port *cxl_endpoint_get_port(struct cxl_endpoint *endpoint); + const char *cxl_endpoint_get_host(struct cxl_endpoint *endpoint); + struct cxl_endpoint *cxl_memdev_get_endpoint(struct cxl_memdev *memdev); ++struct cxl_endpoint *cxl_port_to_endpoint(struct cxl_port *port); + + #define cxl_endpoint_foreach(port, endpoint) \ + for (endpoint = cxl_endpoint_get_first(port); endpoint != NULL; \ +diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build +index 7618c97..96f4666 100644 +--- a/Documentation/cxl/meson.build ++++ b/Documentation/cxl/meson.build +@@ -32,6 +32,8 @@ cxl_manpages = [ + 'cxl-zero-labels.txt', + 'cxl-enable-memdev.txt', + 'cxl-disable-memdev.txt', ++ 'cxl-enable-port.txt', ++ 'cxl-disable-port.txt', + ] + + foreach man : cxl_manpages +diff --git a/cxl/builtin.h b/cxl/builtin.h +index 621c85c..3123d5e 100644 +--- a/cxl/builtin.h ++++ b/cxl/builtin.h +@@ -12,4 +12,6 @@ int cmd_init_labels(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_check_labels(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_disable_memdev(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx); + #endif /* _CXL_BUILTIN_H_ */ +diff --git a/cxl/cxl.c b/cxl/cxl.c +index 78d2e9a..c20c569 100644 +--- a/cxl/cxl.c ++++ b/cxl/cxl.c +@@ -66,6 +66,8 @@ static struct cmd_struct commands[] = { + { "write-labels", .c_fn = cmd_write_labels }, + { "disable-memdev", .c_fn = cmd_disable_memdev }, + { "enable-memdev", .c_fn = cmd_enable_memdev }, ++ { "disable-port", .c_fn = cmd_disable_port }, ++ { "enable-port", .c_fn = cmd_enable_port }, + }; + + int main(int argc, const char **argv) +diff --git a/cxl/filter.c b/cxl/filter.c +index c691edf..f6a23b7 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -47,8 +47,8 @@ bool cxl_filter_has(const char *__filter, const char *needle) + return false; + } + +-static struct cxl_endpoint * +-util_cxl_endpoint_filter(struct cxl_endpoint *endpoint, const char *__ident) ++struct cxl_endpoint *util_cxl_endpoint_filter(struct cxl_endpoint *endpoint, ++ const char *__ident) + { + char *ident, *save; + const char *arg; +@@ -124,11 +124,6 @@ static struct cxl_port *__util_cxl_port_filter(struct cxl_port *port, + return NULL; + } + +-enum cxl_port_filter_mode { +- CXL_PF_SINGLE, +- CXL_PF_ANCESTRY, +-}; +- + static enum cxl_port_filter_mode pf_mode(struct cxl_filter_params *p) + { + if (p->single) +@@ -136,9 +131,8 @@ static enum cxl_port_filter_mode pf_mode(struct cxl_filter_params *p) + return CXL_PF_ANCESTRY; + } + +-static struct cxl_port *util_cxl_port_filter(struct cxl_port *port, +- const char *ident, +- enum cxl_port_filter_mode mode) ++struct cxl_port *util_cxl_port_filter(struct cxl_port *port, const char *ident, ++ enum cxl_port_filter_mode mode) + { + struct cxl_port *iter = port; + +@@ -358,9 +352,9 @@ util_cxl_endpoint_filter_by_memdev(struct cxl_endpoint *endpoint, + return NULL; + } + +-static struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port, +- const char *ident, +- const char *serial) ++struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port, ++ const char *ident, ++ const char *serial) + { + struct cxl_ctx *ctx = cxl_port_get_ctx(port); + struct cxl_memdev *memdev; +@@ -958,7 +952,6 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + continue; + } + } +- + } + walk_children: + dbg(p, "walk decoders\n"); +diff --git a/cxl/filter.h b/cxl/filter.h +index 6fd469f..850df70 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -29,6 +29,19 @@ struct cxl_filter_params { + struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + const char *__ident, + const char *serials); ++struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port, ++ const char *ident, ++ const char *serial); ++ ++enum cxl_port_filter_mode { ++ CXL_PF_SINGLE, ++ CXL_PF_ANCESTRY, ++}; ++ ++struct cxl_port *util_cxl_port_filter(struct cxl_port *port, const char *ident, ++ enum cxl_port_filter_mode mode); ++struct cxl_endpoint *util_cxl_endpoint_filter(struct cxl_endpoint *endpoint, ++ const char *__ident); + int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *param); + bool cxl_filter_has(const char *needle, const char *__filter); + #endif /* _CXL_UTIL_FILTER_H_ */ +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index dcfc826..1a7dccb 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -258,6 +258,11 @@ CXL_EXPORT void cxl_unref(struct cxl_ctx *ctx) + free(ctx); + } + ++static int cxl_flush(struct cxl_ctx *ctx) ++{ ++ return sysfs_write_attr(ctx, "/sys/bus/cxl/flush", "1\n"); ++} ++ + /** + * cxl_set_log_fn - override default log routine + * @ctx: cxl library context +@@ -530,11 +535,31 @@ CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev + return memdev->firmware_version; + } + ++static void bus_invalidate(struct cxl_bus *bus) ++{ ++ struct cxl_ctx *ctx = cxl_bus_get_ctx(bus); ++ struct cxl_port *bus_port, *port, *_p; ++ struct cxl_memdev *memdev; ++ ++ /* ++ * Something happend to cause the state of all ports to be ++ * indeterminate, delete them all and start over. ++ */ ++ cxl_memdev_foreach(ctx, memdev) ++ if (cxl_memdev_get_bus(memdev) == bus) ++ memdev->endpoint = NULL; ++ ++ bus_port = cxl_bus_get_port(bus); ++ list_for_each_safe(&bus_port->child_ports, port, _p, list) ++ free_port(port, &bus_port->child_ports); ++ bus_port->ports_init = 0; ++ cxl_flush(ctx); ++} ++ + CXL_EXPORT int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev) + { + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); + const char *devname = cxl_memdev_get_devname(memdev); +- struct cxl_port *port, *_p, *bus_port; + struct cxl_bus *bus; + + if (!cxl_memdev_is_enabled(memdev)) +@@ -553,15 +578,7 @@ CXL_EXPORT int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev) + return -EBUSY; + } + +- /* +- * The state of all ports is now indeterminate, delete them all +- * and start over. +- */ +- bus_port = cxl_bus_get_port(bus); +- list_for_each_safe(&bus_port->child_ports, port, _p, list) +- free_port(port, &bus_port->child_ports); +- bus_port->ports_init = 0; +- memdev->endpoint = NULL; ++ bus_invalidate(bus); + + dbg(ctx, "%s: disabled\n", devname); + +@@ -1352,6 +1369,57 @@ CXL_EXPORT int cxl_port_is_enabled(struct cxl_port *port) + return is_enabled(path); + } + ++CXL_EXPORT int cxl_port_disable_invalidate(struct cxl_port *port) ++{ ++ const char *devname = cxl_port_get_devname(port); ++ struct cxl_bus *bus = cxl_port_get_bus(port); ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ ++ if (cxl_port_is_root(port)) { ++ err(ctx, "%s: can not be disabled through this interface\n", ++ devname); ++ return -EINVAL; ++ } ++ ++ if (!bus) { ++ err(ctx, "%s: failed to invalidate\n", devname); ++ return -ENXIO; ++ } ++ ++ util_unbind(port->dev_path, ctx); ++ ++ if (cxl_port_is_enabled(port)) { ++ err(ctx, "%s: failed to disable\n", devname); ++ return -EBUSY; ++ } ++ ++ dbg(ctx, "%s: disabled\n", devname); ++ ++ bus_invalidate(bus); ++ ++ return 0; ++} ++ ++CXL_EXPORT int cxl_port_enable(struct cxl_port *port) ++{ ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ const char *devname = cxl_port_get_devname(port); ++ ++ if (cxl_port_is_enabled(port)) ++ return 0; ++ ++ util_bind(devname, port->module, "cxl", ctx); ++ ++ if (!cxl_port_is_enabled(port)) { ++ err(ctx, "%s: failed to enable\n", devname); ++ return -ENXIO; ++ } ++ ++ dbg(ctx, "%s: enabled\n", devname); ++ ++ return 0; ++} ++ + CXL_EXPORT struct cxl_bus *cxl_port_to_bus(struct cxl_port *port) + { + if (!cxl_port_is_root(port)) +@@ -1359,6 +1427,13 @@ CXL_EXPORT struct cxl_bus *cxl_port_to_bus(struct cxl_port *port) + return container_of(port, struct cxl_bus, port); + } + ++CXL_EXPORT struct cxl_endpoint *cxl_port_to_endpoint(struct cxl_port *port) ++{ ++ if (!cxl_port_is_endpoint(port)) ++ return NULL; ++ return container_of(port, struct cxl_endpoint, port); ++} ++ + static void *add_cxl_dport(void *parent, int id, const char *cxldport_base) + { + const char *devname = devpath_to_devname(cxldport_base); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 2c8358e..67c7fd5 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -97,11 +97,14 @@ global: + cxl_port_is_switch; + cxl_port_to_bus; + cxl_port_is_endpoint; ++ cxl_port_to_endpoint; + cxl_port_get_bus; + cxl_port_get_host; + cxl_port_get_bus; + cxl_port_hosts_memdev; + cxl_port_get_nr_dports; ++ cxl_port_disable_invalidate; ++ cxl_port_enable; + cxl_port_get_next_all; + cxl_endpoint_get_first; + cxl_endpoint_get_next; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index c8d07bb..1aac396 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -90,10 +90,13 @@ bool cxl_port_is_root(struct cxl_port *port); + bool cxl_port_is_switch(struct cxl_port *port); + struct cxl_bus *cxl_port_to_bus(struct cxl_port *port); + bool cxl_port_is_endpoint(struct cxl_port *port); ++struct cxl_endpoint *cxl_port_to_endpoint(struct cxl_port *port); + struct cxl_bus *cxl_port_get_bus(struct cxl_port *port); + const char *cxl_port_get_host(struct cxl_port *port); + bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev); + int cxl_port_get_nr_dports(struct cxl_port *port); ++int cxl_port_disable_invalidate(struct cxl_port *port); ++int cxl_port_enable(struct cxl_port *port); + struct cxl_port *cxl_port_get_next_all(struct cxl_port *port, + const struct cxl_port *top); + +diff --git a/cxl/meson.build b/cxl/meson.build +index fc7ee71..87cfea7 100644 +--- a/cxl/meson.build ++++ b/cxl/meson.build +@@ -1,6 +1,7 @@ + cxl_src = [ + 'cxl.c', + 'list.c', ++ 'port.c', + 'memdev.c', + '../util/json.c', + '../util/log.c', +diff --git a/cxl/port.c b/cxl/port.c +new file mode 100644 +index 0000000..46a8f32 +--- /dev/null ++++ b/cxl/port.c +@@ -0,0 +1,253 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2020-2022 Intel Corporation. All rights reserved. */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "filter.h" ++ ++static struct parameters { ++ bool debug; ++ bool force; ++ bool memdevs; ++ bool endpoint; ++} param; ++ ++static struct log_ctx pl; ++ ++#define BASE_OPTIONS() \ ++OPT_BOOLEAN(0, "debug", ¶m.debug, "turn on debug"), \ ++OPT_BOOLEAN('e', "endpoint", ¶m.endpoint, \ ++ "target endpoints instead of switch ports") ++ ++#define ENABLE_OPTIONS() \ ++OPT_BOOLEAN('m', "enable-memdevs", ¶m.memdevs, \ ++ "enable downstream memdev(s)") ++ ++#define DISABLE_OPTIONS() \ ++OPT_BOOLEAN('f', "force", ¶m.force, \ ++ "DANGEROUS: override active memdev safety checks") ++ ++static const struct option disable_options[] = { ++ BASE_OPTIONS(), ++ DISABLE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option enable_options[] = { ++ BASE_OPTIONS(), ++ ENABLE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static int action_disable(struct cxl_port *port) ++{ ++ const char *devname = cxl_port_get_devname(port); ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ struct cxl_memdev *memdev; ++ int active_memdevs = 0; ++ ++ if (!cxl_port_is_enabled(port)) { ++ log_dbg(&pl, "%s already disabled\n", devname); ++ return 0; ++ } ++ ++ if (param.endpoint) { ++ struct cxl_endpoint *endpoint = cxl_port_to_endpoint(port); ++ ++ if (cxl_endpoint_get_memdev(endpoint)) ++ active_memdevs++; ++ } ++ ++ cxl_memdev_foreach(ctx, memdev) { ++ if (!cxl_port_get_dport_by_memdev(port, memdev)) ++ continue; ++ if (cxl_memdev_is_enabled(memdev)) ++ active_memdevs++; ++ } ++ ++ if (active_memdevs && !param.force) { ++ /* ++ * TODO: actually detect rather than assume active just ++ * because the memdev is enabled ++ */ ++ log_err(&pl, ++ "%s hosts %d memdev%s which %s part of an active region\n", ++ devname, active_memdevs, active_memdevs > 1 ? "s" : "", ++ active_memdevs > 1 ? "are" : "is"); ++ log_err(&pl, ++ "See 'cxl list -M -p %s' to see impacted device%s\n", ++ devname, active_memdevs > 1 ? "s" : ""); ++ return -EBUSY; ++ } ++ ++ return cxl_port_disable_invalidate(port); ++} ++ ++static int action_enable(struct cxl_port *port) ++{ ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ struct cxl_memdev *memdev; ++ int rc; ++ ++ rc = cxl_port_enable(port); ++ if (rc || !param.memdevs) ++ return rc; ++ ++ cxl_memdev_foreach(ctx, memdev) ++ if (cxl_port_get_dport_by_memdev(port, memdev)) ++ cxl_memdev_enable(memdev); ++ return 0; ++} ++ ++static struct cxl_port *find_cxl_port(struct cxl_ctx *ctx, const char *ident) ++{ ++ struct cxl_bus *bus; ++ struct cxl_port *port; ++ ++ cxl_bus_foreach(ctx, bus) ++ cxl_port_foreach_all(cxl_bus_get_port(bus), port) ++ if (util_cxl_port_filter(port, ident, CXL_PF_SINGLE)) ++ return port; ++ return NULL; ++} ++ ++static struct cxl_endpoint *find_cxl_endpoint(struct cxl_ctx *ctx, ++ const char *ident) ++{ ++ struct cxl_bus *bus; ++ struct cxl_port *port; ++ struct cxl_endpoint *endpoint; ++ ++ cxl_bus_foreach(ctx, bus) ++ cxl_port_foreach_all(cxl_bus_get_port(bus), port) ++ cxl_endpoint_foreach(port, endpoint) ++ if (util_cxl_endpoint_filter(endpoint, ident)) ++ return endpoint; ++ return NULL; ++} ++ ++ ++ ++static int port_action(int argc, const char **argv, struct cxl_ctx *ctx, ++ int (*action)(struct cxl_port *port), ++ const struct option *options, const char *usage) ++{ ++ int i, rc = 0, count = 0, err = 0; ++ const char * const u[] = { ++ usage, ++ NULL ++ }; ++ unsigned long id; ++ ++ log_init(&pl, "cxl port", "CXL_PORT_LOG"); ++ argc = parse_options(argc, argv, options, u, 0); ++ ++ if (argc == 0) ++ usage_with_options(u, options); ++ for (i = 0; i < argc; i++) { ++ const char *fmt; ++ ++ if (strcmp(argv[i], "all") == 0) { ++ argc = 1; ++ break; ++ } ++ ++ if (param.endpoint) ++ fmt = "endpoint%lu"; ++ else ++ fmt = "port%lu"; ++ ++ if (sscanf(argv[i], fmt, &id) == 1) ++ continue; ++ if (sscanf(argv[i], "%lu", &id) == 1) ++ continue; ++ ++ log_err(&pl, "'%s' is not a valid %s identifer\n", argv[i], ++ param.endpoint ? "endpoint" : "port"); ++ err++; ++ } ++ ++ if (err == argc) { ++ usage_with_options(u, options); ++ return -EINVAL; ++ } ++ ++ if (param.debug) { ++ cxl_set_log_priority(ctx, LOG_DEBUG); ++ pl.log_priority = LOG_DEBUG; ++ } else ++ pl.log_priority = LOG_INFO; ++ ++ rc = 0; ++ err = 0; ++ count = 0; ++ ++ for (i = 0; i < argc; i++) { ++ struct cxl_port *port; ++ ++ if (param.endpoint) { ++ struct cxl_endpoint *endpoint; ++ ++ endpoint = find_cxl_endpoint(ctx, argv[i]); ++ if (!endpoint) { ++ log_dbg(&pl, "endpoint: %s not found\n", ++ argv[i]); ++ continue; ++ } ++ port = cxl_endpoint_get_port(endpoint); ++ } else { ++ port = find_cxl_port(ctx, argv[i]); ++ if (!port) { ++ log_dbg(&pl, "port: %s not found\n", argv[i]); ++ continue; ++ } ++ } ++ ++ log_dbg(&pl, "run action on port: %s\n", ++ cxl_port_get_devname(port)); ++ rc = action(port); ++ if (rc == 0) ++ count++; ++ else if (rc && !err) ++ err = rc; ++ } ++ rc = err; ++ ++ /* ++ * count if some actions succeeded, 0 if none were attempted, ++ * negative error code otherwise. ++ */ ++ if (count > 0) ++ return count; ++ return rc; ++ } ++ ++ int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx) ++ { ++ int count = port_action( ++ argc, argv, ctx, action_disable, disable_options, ++ "cxl disable-port [..] []"); ++ ++ log_info(&pl, "disabled %d port%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); ++ return count >= 0 ? 0 : EXIT_FAILURE; ++ } ++ ++ int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx) ++ { ++ int count = port_action( ++ argc, argv, ctx, action_enable, enable_options, ++ "cxl enable-port [..] []"); ++ ++ log_info(&pl, "enabled %d port%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); ++ return count >= 0 ? 0 : EXIT_FAILURE; ++ } +-- +2.27.0 + diff --git a/SOURCES/0123-cxl-list-Filter-dports-and-targets-by-memdevs.patch b/SOURCES/0123-cxl-list-Filter-dports-and-targets-by-memdevs.patch new file mode 100644 index 0000000..377dccf --- /dev/null +++ b/SOURCES/0123-cxl-list-Filter-dports-and-targets-by-memdevs.patch @@ -0,0 +1,340 @@ +From f9ebf984e7eb93a044f48c4089485051751face8 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Sun, 23 Jan 2022 16:55:06 -0800 +Subject: [PATCH 123/217] cxl/list: Filter dports and targets by memdevs + +Trim dport / target information by the memdev filter. This is useful when +validating connectivity and decoder programming. + +Link: https://lore.kernel.org/r/164298570626.3021641.18034728333613142555.stgit@dwillia2-desk3.amr.corp.intel.com +Reported-by: Ben Widawsky +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/lib/libcxl.txt | 1 + + cxl/filter.c | 51 ++++++++++++++++++++++++++++++++ + cxl/filter.h | 6 ++++ + cxl/json.c | 51 +++++++++++++++++++++----------- + cxl/json.h | 7 +++++ + cxl/lib/libcxl.c | 5 ++++ + cxl/lib/libcxl.sym | 1 + + cxl/libcxl.h | 1 + + 8 files changed, 105 insertions(+), 18 deletions(-) + +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 27eb29e..4392b47 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -221,6 +221,7 @@ const char *cxl_port_get_host(struct cxl_port *port); + struct cxl_port *cxl_decoder_get_port(struct cxl_decoder *decoder); + struct cxl_port *cxl_port_get_next_all(struct cxl_port *port, + const struct cxl_port *top); ++struct cxl_port *cxl_dport_get_port(struct cxl_dport *dport); + + #define cxl_port_foreach(parent, port) \ + for (port = cxl_port_get_first(parent); port != NULL; \ +diff --git a/cxl/filter.c b/cxl/filter.c +index f6a23b7..925bf3a 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -435,6 +435,48 @@ util_cxl_decoder_filter_by_memdev(struct cxl_decoder *decoder, + return NULL; + } + ++struct cxl_target *util_cxl_target_filter_by_memdev(struct cxl_target *target, ++ const char *ident, ++ const char *serial) ++{ ++ struct cxl_decoder *decoder = cxl_target_get_decoder(target); ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ struct cxl_memdev *memdev; ++ ++ if (!ident && !serial) ++ return target; ++ ++ cxl_memdev_foreach(ctx, memdev) { ++ if (!util_cxl_memdev_filter(memdev, ident, serial)) ++ continue; ++ if (cxl_target_maps_memdev(target, memdev)) ++ return target; ++ } ++ ++ return NULL; ++} ++ ++struct cxl_dport *util_cxl_dport_filter_by_memdev(struct cxl_dport *dport, ++ const char *ident, ++ const char *serial) ++{ ++ struct cxl_port *port = cxl_dport_get_port(dport); ++ struct cxl_ctx *ctx = cxl_port_get_ctx(port); ++ struct cxl_memdev *memdev; ++ ++ if (!ident && !serial) ++ return dport; ++ ++ cxl_memdev_foreach (ctx, memdev) { ++ if (!util_cxl_memdev_filter(memdev, ident, serial)) ++ continue; ++ if (cxl_dport_maps_memdev(dport, memdev)) ++ return dport; ++ } ++ ++ return NULL; ++} ++ + static bool __memdev_filter_by_decoder(struct cxl_memdev *memdev, + struct cxl_port *port, const char *ident) + { +@@ -639,6 +681,9 @@ static void walk_decoders(struct cxl_port *port, struct cxl_filter_params *p, + dbg(p, "decoder object allocation failure\n"); + continue; + } ++ util_cxl_targets_append_json(jdecoder, decoder, ++ p->memdev_filter, p->serial_filter, ++ flags); + json_object_array_add(jdecoders, jdecoder); + } + } +@@ -756,6 +801,9 @@ walk_child_ports(struct cxl_port *parent_port, struct cxl_filter_params *p, + err(p, "%s: failed to list\n", devname); + continue; + } ++ util_cxl_dports_append_json(jport, port, ++ p->memdev_filter, ++ p->serial_filter, flags); + json_object_array_add(jports, jport); + jchildports = json_object_new_array(); + if (!jchildports) { +@@ -914,6 +962,9 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + dbg(p, "bus object allocation failure\n"); + continue; + } ++ util_cxl_dports_append_json(jbus, port, ++ p->memdev_filter, ++ p->serial_filter, flags); + json_object_array_add(jbuses, jbus); + if (p->ports) { + jchildports = json_object_new_array(); +diff --git a/cxl/filter.h b/cxl/filter.h +index 850df70..5deabb3 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -42,6 +42,12 @@ struct cxl_port *util_cxl_port_filter(struct cxl_port *port, const char *ident, + enum cxl_port_filter_mode mode); + struct cxl_endpoint *util_cxl_endpoint_filter(struct cxl_endpoint *endpoint, + const char *__ident); ++struct cxl_target *util_cxl_target_filter_by_memdev(struct cxl_target *target, ++ const char *ident, ++ const char *serial); ++struct cxl_dport *util_cxl_dport_filter_by_memdev(struct cxl_dport *dport, ++ const char *ident, ++ const char *serial); + int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *param); + bool cxl_filter_has(const char *needle, const char *__filter); + #endif /* _CXL_UTIL_FILTER_H_ */ +diff --git a/cxl/json.c b/cxl/json.c +index 4fb5eec..f3b536e 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -8,6 +8,7 @@ + #include + #include + ++#include "filter.h" + #include "json.h" + + static struct json_object *util_cxl_memdev_health_to_json( +@@ -241,9 +242,9 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + return jdev; + } + +-static struct json_object *util_cxl_dports_to_json(struct json_object *jport, +- struct cxl_port *port, +- unsigned long flags) ++void util_cxl_dports_append_json(struct json_object *jport, ++ struct cxl_port *port, const char *ident, ++ const char *serial, unsigned long flags) + { + struct json_object *jobj, *jdports; + struct cxl_dport *dport; +@@ -251,7 +252,7 @@ static struct json_object *util_cxl_dports_to_json(struct json_object *jport, + + val = cxl_port_get_nr_dports(port); + if (!val || !(flags & UTIL_JSON_TARGETS)) +- return jport; ++ return; + + jobj = json_object_new_int(val); + if (jobj) +@@ -259,12 +260,15 @@ static struct json_object *util_cxl_dports_to_json(struct json_object *jport, + + jdports = json_object_new_array(); + if (!jdports) +- return jport; ++ return; + + cxl_dport_foreach(port, dport) { + struct json_object *jdport; + const char *phys_node; + ++ if (!util_cxl_dport_filter_by_memdev(dport, ident, serial)) ++ continue; ++ + jdport = json_object_new_object(); + if (!jdport) + continue; +@@ -289,8 +293,6 @@ static struct json_object *util_cxl_dports_to_json(struct json_object *jport, + } + + json_object_object_add(jport, "dports", jdports); +- +- return jport; + } + + struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus, +@@ -311,7 +313,7 @@ struct json_object *util_cxl_bus_to_json(struct cxl_bus *bus, + if (jobj) + json_object_object_add(jbus, "provider", jobj); + +- return util_cxl_dports_to_json(jbus, cxl_bus_get_port(bus), flags); ++ return jbus; + } + + struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, +@@ -320,8 +322,6 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + const char *devname = cxl_decoder_get_devname(decoder); + struct cxl_port *port = cxl_decoder_get_port(decoder); + struct json_object *jdecoder, *jobj; +- struct json_object *jtargets; +- struct cxl_target *target; + u64 val; + + jdecoder = json_object_new_object(); +@@ -375,9 +375,22 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + jobj); + } + ++ return jdecoder; ++} ++ ++void util_cxl_targets_append_json(struct json_object *jdecoder, ++ struct cxl_decoder *decoder, ++ const char *ident, const char *serial, ++ unsigned long flags) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ struct json_object *jobj, *jtargets; ++ struct cxl_target *target; ++ int val; ++ + /* Endpoints don't have targets, they *are* targets */ + if (cxl_port_is_endpoint(port)) +- return jdecoder; ++ return; + + val = cxl_decoder_get_nr_targets(decoder); + jobj = json_object_new_int(val); +@@ -386,16 +399,21 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + + if (!(flags & UTIL_JSON_TARGETS) || + !cxl_decoder_get_nr_targets(decoder)) +- return jdecoder; ++ return; + + jtargets = json_object_new_array(); + if (!jtargets) +- return jdecoder; ++ return; + + cxl_target_foreach(decoder, target) { +- struct json_object *jtarget = json_object_new_object(); ++ struct json_object *jtarget; + const char *phys_node; ++ const char *devname; ++ ++ if (!util_cxl_target_filter_by_memdev(target, ident, serial)) ++ continue; + ++ jtarget = json_object_new_object(); + if (!jtarget) + continue; + +@@ -425,9 +443,6 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + } + + json_object_object_add(jdecoder, "targets", jtargets); +- +- return jdecoder; +- + } + + static struct json_object *__util_cxl_port_to_json(struct cxl_port *port, +@@ -455,7 +470,7 @@ static struct json_object *__util_cxl_port_to_json(struct cxl_port *port, + json_object_object_add(jport, "state", jobj); + } + +- return util_cxl_dports_to_json(jport, port, flags); ++ return jport; + } + + struct json_object *util_cxl_port_to_json(struct cxl_port *port, +diff --git a/cxl/json.h b/cxl/json.h +index fcca2e6..9a5a845 100644 +--- a/cxl/json.h ++++ b/cxl/json.h +@@ -15,4 +15,11 @@ struct json_object *util_cxl_endpoint_to_json(struct cxl_endpoint *endpoint, + unsigned long flags); + struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + unsigned long flags); ++void util_cxl_targets_append_json(struct json_object *jdecoder, ++ struct cxl_decoder *decoder, ++ const char *ident, const char *serial, ++ unsigned long flags); ++void util_cxl_dports_append_json(struct json_object *jport, ++ struct cxl_port *port, const char *ident, ++ const char *serial, unsigned long flags); + #endif /* __CXL_UTIL_JSON_H__ */ +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 1a7dccb..e0b443f 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -1527,6 +1527,11 @@ CXL_EXPORT int cxl_dport_get_id(struct cxl_dport *dport) + return dport->id; + } + ++CXL_EXPORT struct cxl_port *cxl_dport_get_port(struct cxl_dport *dport) ++{ ++ return dport->port; ++} ++ + CXL_EXPORT bool cxl_dport_maps_memdev(struct cxl_dport *dport, + struct cxl_memdev *memdev) + { +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 67c7fd5..e56a2bf 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -152,6 +152,7 @@ global: + cxl_dport_get_devname; + cxl_dport_get_physical_node; + cxl_dport_get_id; ++ cxl_dport_get_port; + cxl_port_get_dport_by_memdev; + cxl_dport_maps_memdev; + } LIBCXL_1; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 1aac396..3b2293b 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -113,6 +113,7 @@ struct cxl_dport *cxl_dport_get_first(struct cxl_port *port); + struct cxl_dport *cxl_dport_get_next(struct cxl_dport *dport); + const char *cxl_dport_get_devname(struct cxl_dport *dport); + const char *cxl_dport_get_physical_node(struct cxl_dport *dport); ++struct cxl_port *cxl_dport_get_port(struct cxl_dport *dport); + int cxl_dport_get_id(struct cxl_dport *dport); + bool cxl_dport_maps_memdev(struct cxl_dport *dport, struct cxl_memdev *memdev); + struct cxl_dport *cxl_port_get_dport_by_memdev(struct cxl_port *port, +-- +2.27.0 + diff --git a/SOURCES/0124-ndctl-test-make-inject-smart.sh-more-tolerant-of-dec.patch b/SOURCES/0124-ndctl-test-make-inject-smart.sh-more-tolerant-of-dec.patch new file mode 100644 index 0000000..9208ab9 --- /dev/null +++ b/SOURCES/0124-ndctl-test-make-inject-smart.sh-more-tolerant-of-dec.patch @@ -0,0 +1,46 @@ +From 4921c0c2040ffbe10facd320f6a718a3d42ad815 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 17 Feb 2022 22:42:29 -0700 +Subject: [PATCH 124/217] ndctl/test: make inject-smart.sh more tolerant of + decimal fields + +Some combinations of json-c/jq/other libraries seem to produce differing +outputs for the final jq-filtered smart fields, in that some have a +decimal "42.0" numeric field, where as in other combinations it is a +simple "42" (still a numeric field, not string). + +This shouldn't matter in practice, but for this contrived test case, we +need to make sure that "42" is treated the same as "42.0" + +Normalize all fields before comparing them to "%0.0f" so that the +comparison doesn't result in superfluous failures. + +Reported-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/inject-smart.sh | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/test/inject-smart.sh b/test/inject-smart.sh +index 8b91360..046322b 100755 +--- a/test/inject-smart.sh ++++ b/test/inject-smart.sh +@@ -105,13 +105,13 @@ get_field() + json="$($NDCTL list -b $bus -d $dimm -H)" + val="$(jq -r ".[].dimms[].health.$smart_listing" <<< $json)" + val="$(translate_val $val)" +- echo $val ++ printf "%0.0f\n" "$val" + } + + verify() + { + local field="$1" +- local val="$2" ++ local val="$(printf "%0.0f\n" "$2")" + + [[ "$val" == "$(get_field $field)" ]] + } +-- +2.27.0 + diff --git a/SOURCES/0125-libndctl-papr-Add-support-for-reporting-shutdown-cou.patch b/SOURCES/0125-libndctl-papr-Add-support-for-reporting-shutdown-cou.patch new file mode 100644 index 0000000..851293a --- /dev/null +++ b/SOURCES/0125-libndctl-papr-Add-support-for-reporting-shutdown-cou.patch @@ -0,0 +1,136 @@ +From aa990008f48f21121474a411d829f24e832c89a2 Mon Sep 17 00:00:00 2001 +From: Vaibhav Jain +Date: Tue, 25 Jan 2022 00:26:05 +0530 +Subject: [PATCH 125/217] libndctl/papr: Add support for reporting + shutdown-count + +Add support for reporting dirty-shutdown-count (DSC) for PAPR based +NVDIMMs. The sysfs attribute exposing this value is located at +nmemX/papr/dirty_shutdown. + +This counter is also returned in payload for PAPR_PDSM_HEALTH as newly +introduced member 'dimm_dsc' in 'struct nd_papr_pdsm_health'. Presence +of 'DSC' is indicated by the PDSM_DIMM_DSC_VALID extension flag. + +The patch implements 'ndctl_dimm_ops.smart_get_shutdown_count' +callback in implemented as papr_smart_get_shutdown_count(). + +Kernel side changes to support reporting DSC have been merged to linux kernel +via patch proposed at [1]. With updated kernel 'ndctl list -DH' reports +following output on PPC64: + +$ sudo ndctl list -DH +[ + { + "dev":"nmem0", + "health":{ + "health_state":"ok", + "life_used_percentage":50, + "shutdown_state":"clean", + "shutdown_count":10 + } + } +] + +[1] http://patchwork.ozlabs.org/project/linuxppc-dev/patch/20210624080621.252038-1-vaibhav@linux.ibm.com + +Link: https://lore.kernel.org/r/20220124185605.1465681-1-vaibhav@linux.ibm.com +Signed-off-by: Vaibhav Jain +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 6 +++++- + ndctl/lib/papr.c | 23 +++++++++++++++++++++++ + ndctl/lib/papr_pdsm.h | 6 ++++++ + 3 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index 47a234c..5979a92 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -1819,8 +1819,12 @@ static int add_papr_dimm(struct ndctl_dimm *dimm, const char *dimm_base) + + /* Allocate monitor mode fd */ + dimm->health_eventfd = open(path, O_RDONLY|O_CLOEXEC); +- rc = 0; ++ /* Get the dirty shutdown counter value */ ++ sprintf(path, "%s/papr/dirty_shutdown", dimm_base); ++ if (sysfs_read_attr(ctx, path, buf) == 0) ++ dimm->dirty_shutdown = strtoll(buf, NULL, 0); + ++ rc = 0; + } else if (strcmp(buf, "nvdimm_test") == 0) { + /* probe via common populate_dimm_attributes() */ + rc = populate_dimm_attributes(dimm, dimm_base, "papr"); +diff --git a/ndctl/lib/papr.c b/ndctl/lib/papr.c +index 43b8412..46cf9c1 100644 +--- a/ndctl/lib/papr.c ++++ b/ndctl/lib/papr.c +@@ -165,6 +165,9 @@ static unsigned int papr_smart_get_flags(struct ndctl_cmd *cmd) + if (health.extension_flags & PDSM_DIMM_HEALTH_RUN_GAUGE_VALID) + flags |= ND_SMART_USED_VALID; + ++ if (health.extension_flags & PDSM_DIMM_DSC_VALID) ++ flags |= ND_SMART_SHUTDOWN_COUNT_VALID; ++ + return flags; + } + +@@ -236,6 +239,25 @@ static unsigned int papr_smart_get_life_used(struct ndctl_cmd *cmd) + (100 - health.dimm_fuel_gauge) : 0; + } + ++static unsigned int papr_smart_get_shutdown_count(struct ndctl_cmd *cmd) ++{ ++ ++ struct nd_papr_pdsm_health health; ++ ++ /* Ignore in case of error or invalid pdsm */ ++ if (!cmd_is_valid(cmd) || ++ to_pdsm(cmd)->cmd_status != 0 || ++ to_pdsm_cmd(cmd) != PAPR_PDSM_HEALTH) ++ return 0; ++ ++ /* get the payload from command */ ++ health = to_payload(cmd)->health; ++ ++ return (health.extension_flags & PDSM_DIMM_DSC_VALID) ? ++ (health.dimm_dsc) : 0; ++ ++} ++ + struct ndctl_dimm_ops * const papr_dimm_ops = &(struct ndctl_dimm_ops) { + .cmd_is_supported = papr_cmd_is_supported, + .smart_get_flags = papr_smart_get_flags, +@@ -245,4 +267,5 @@ struct ndctl_dimm_ops * const papr_dimm_ops = &(struct ndctl_dimm_ops) { + .smart_get_health = papr_smart_get_health, + .smart_get_shutdown_state = papr_smart_get_shutdown_state, + .smart_get_life_used = papr_smart_get_life_used, ++ .smart_get_shutdown_count = papr_smart_get_shutdown_count, + }; +diff --git a/ndctl/lib/papr_pdsm.h b/ndctl/lib/papr_pdsm.h +index 1bac8a7..f45b1e4 100644 +--- a/ndctl/lib/papr_pdsm.h ++++ b/ndctl/lib/papr_pdsm.h +@@ -75,6 +75,9 @@ + /* Indicate that the 'dimm_fuel_gauge' field is valid */ + #define PDSM_DIMM_HEALTH_RUN_GAUGE_VALID 1 + ++/* Indicate that the 'dimm_dsc' field is valid */ ++#define PDSM_DIMM_DSC_VALID 2 ++ + /* + * Struct exchanged between kernel & ndctl in for PAPR_PDSM_HEALTH + * Various flags indicate the health status of the dimm. +@@ -103,6 +106,9 @@ struct nd_papr_pdsm_health { + + /* Extension flag PDSM_DIMM_HEALTH_RUN_GAUGE_VALID */ + __u16 dimm_fuel_gauge; ++ ++ /* Extension flag PDSM_DIMM_DSC_VALID */ ++ __u64 dimm_dsc; + }; + __u8 buf[ND_PDSM_PAYLOAD_MAX_SIZE]; + }; +-- +2.27.0 + diff --git a/SOURCES/0126-libndctl-intel-Indicate-supported-smart-inject-types.patch b/SOURCES/0126-libndctl-intel-Indicate-supported-smart-inject-types.patch new file mode 100644 index 0000000..d22c569 --- /dev/null +++ b/SOURCES/0126-libndctl-intel-Indicate-supported-smart-inject-types.patch @@ -0,0 +1,161 @@ +From edcd9b7e10b3b33a9660e412a2db1beab30936d3 Mon Sep 17 00:00:00 2001 +From: Vaibhav Jain +Date: Tue, 25 Jan 2022 02:07:35 +0530 +Subject: [PATCH 126/217] libndctl, intel: Indicate supported smart-inject + types + +Presently the inject-smart code assumes support for injecting all +smart-errors namely media-temperature, controller-temperature, +spares-remaining, fatal-health and unsafe-shutdown. This assumption +may break in case of other non-Intel NVDIMM types namely PAPR NVDIMMs +which presently only have support for injecting unsafe-shutdown and +fatal health events. + +Trying to inject-smart errors on PAPR NVDIMMs causes problems as +smart_inject() prematurely exits when trying to inject +media-temperature smart-error errors out. + +To fix this issue the patch proposes extending the definition of +dimm_op 'smart_inject_supported' to return bitmap of flags indicating +the type of smart-error injections supported by an NVDIMM. These types +are indicated by the newly introduced defines ND_SMART_INJECT_* . A +dimm-ops provide can return an bitmap composed of these flags back +from its implementation of 'smart_inject_supported' to indicate to +dimm_inject_smart() which type of smart-error injection it +supports. In case of an error the dimm-op is still expected to return +a negative error code back to the caller. + +The patch updates intel_dimm_smart_inject_supported() to return a +bitmap composed of all ND_SMART_INJECT_* flags to indicate support for +all smart-error types. + +Finally the patch also updates smart_inject() to test for specific +ND_START_INJECT_* flags before sending a smart-inject command via +dimm-provider. + +Link: https://lore.kernel.org/r/20220124203735.1490186-1-vaibhav@linux.ibm.com +Reviewed-by: Ira Weiny +Signed-off-by: Vaibhav Jain +Signed-off-by: Vishal Verma +--- + ndctl/inject-smart.c | 31 ++++++++++++++++++++++++------- + ndctl/lib/intel.c | 7 ++++++- + ndctl/libndctl.h | 8 ++++++++ + 3 files changed, 38 insertions(+), 8 deletions(-) + +diff --git a/ndctl/inject-smart.c b/ndctl/inject-smart.c +index 2b9d7e8..bd8c01e 100644 +--- a/ndctl/inject-smart.c ++++ b/ndctl/inject-smart.c +@@ -395,18 +395,26 @@ out: + } \ + } + +-static int smart_inject(struct ndctl_dimm *dimm) ++static int smart_inject(struct ndctl_dimm *dimm, unsigned int inject_types) + { + const char *name = ndctl_dimm_get_devname(dimm); + struct ndctl_cmd *si_cmd = NULL; + int rc = -EOPNOTSUPP; + +- send_inject_val(media_temperature) +- send_inject_val(ctrl_temperature) +- send_inject_val(spares) +- send_inject_bool(fatal) +- send_inject_bool(unsafe_shutdown) ++ if (inject_types & ND_SMART_INJECT_MEDIA_TEMPERATURE) ++ send_inject_val(media_temperature); + ++ if (inject_types & ND_SMART_INJECT_CTRL_TEMPERATURE) ++ send_inject_val(ctrl_temperature); ++ ++ if (inject_types & ND_SMART_INJECT_SPARES_REMAINING) ++ send_inject_val(spares); ++ ++ if (inject_types & ND_SMART_INJECT_HEALTH_STATE) ++ send_inject_bool(fatal); ++ ++ if (inject_types & ND_SMART_INJECT_UNCLEAN_SHUTDOWN) ++ send_inject_bool(unsafe_shutdown); + out: + ndctl_cmd_unref(si_cmd); + return rc; +@@ -417,6 +425,7 @@ static int dimm_inject_smart(struct ndctl_dimm *dimm) + struct json_object *jhealth; + struct json_object *jdimms; + struct json_object *jdimm; ++ unsigned int supported_types; + int rc; + + rc = ndctl_dimm_smart_inject_supported(dimm); +@@ -433,6 +442,14 @@ static int dimm_inject_smart(struct ndctl_dimm *dimm) + error("%s: smart injection not supported by either platform firmware or the kernel.", + ndctl_dimm_get_devname(dimm)); + return rc; ++ default: ++ if (rc < 0) { ++ error("%s: Unknown error %d while checking for smart injection support", ++ ndctl_dimm_get_devname(dimm), rc); ++ return rc; ++ } ++ supported_types = rc; ++ break; + } + + if (sctx.op_mask & (1 << OP_SET)) { +@@ -441,7 +458,7 @@ static int dimm_inject_smart(struct ndctl_dimm *dimm) + goto out; + } + if (sctx.op_mask & (1 << OP_INJECT)) { +- rc = smart_inject(dimm); ++ rc = smart_inject(dimm, supported_types); + if (rc) + goto out; + } +diff --git a/ndctl/lib/intel.c b/ndctl/lib/intel.c +index a3df26e..1314854 100644 +--- a/ndctl/lib/intel.c ++++ b/ndctl/lib/intel.c +@@ -455,7 +455,12 @@ static int intel_dimm_smart_inject_supported(struct ndctl_dimm *dimm) + return -EIO; + } + +- return 0; ++ /* Indicate all smart injection types are supported */ ++ return ND_SMART_INJECT_SPARES_REMAINING | ++ ND_SMART_INJECT_MEDIA_TEMPERATURE | ++ ND_SMART_INJECT_CTRL_TEMPERATURE | ++ ND_SMART_INJECT_HEALTH_STATE | ++ ND_SMART_INJECT_UNCLEAN_SHUTDOWN; + } + + static const char *intel_cmd_desc(int fn) +diff --git a/ndctl/libndctl.h b/ndctl/libndctl.h +index b59026c..4d5cdbf 100644 +--- a/ndctl/libndctl.h ++++ b/ndctl/libndctl.h +@@ -69,6 +69,13 @@ extern "C" { + #define ND_EVENT_HEALTH_STATE (1 << 3) + #define ND_EVENT_UNCLEAN_SHUTDOWN (1 << 4) + ++/* Flags indicating support for various smart injection types */ ++#define ND_SMART_INJECT_SPARES_REMAINING (1 << 0) ++#define ND_SMART_INJECT_MEDIA_TEMPERATURE (1 << 1) ++#define ND_SMART_INJECT_CTRL_TEMPERATURE (1 << 2) ++#define ND_SMART_INJECT_HEALTH_STATE (1 << 3) ++#define ND_SMART_INJECT_UNCLEAN_SHUTDOWN (1 << 4) ++ + size_t ndctl_min_namespace_size(void); + size_t ndctl_sizeof_namespace_index(void); + size_t ndctl_sizeof_namespace_label(void); +@@ -311,6 +318,7 @@ int ndctl_cmd_smart_inject_spares(struct ndctl_cmd *cmd, bool enable, + unsigned int spares); + int ndctl_cmd_smart_inject_fatal(struct ndctl_cmd *cmd, bool enable); + int ndctl_cmd_smart_inject_unsafe_shutdown(struct ndctl_cmd *cmd, bool enable); ++/* Returns a bitmap of ND_SMART_INJECT_* supported */ + int ndctl_dimm_smart_inject_supported(struct ndctl_dimm *dimm); + + struct ndctl_cmd *ndctl_dimm_cmd_new_vendor_specific(struct ndctl_dimm *dimm, +-- +2.27.0 + diff --git a/SOURCES/0127-libndctl-papr-Add-limited-support-for-inject-smart.patch b/SOURCES/0127-libndctl-papr-Add-limited-support-for-inject-smart.patch new file mode 100644 index 0000000..8a379b1 --- /dev/null +++ b/SOURCES/0127-libndctl-papr-Add-limited-support-for-inject-smart.patch @@ -0,0 +1,180 @@ +From 9ef460eb7fd1b1f286955b18db8c18cd1a640e77 Mon Sep 17 00:00:00 2001 +From: Vaibhav Jain +Date: Tue, 25 Jan 2022 02:08:04 +0530 +Subject: [PATCH 127/217] libndctl/papr: Add limited support for inject-smart + +Implements support for ndctl inject-smart command by providing an +implementation of 'smart_inject*' dimm-ops callbacks. Presently only +support for injecting unsafe-shutdown and fatal-health states is +available. + +The patch also introduce various PAPR PDSM structures that are used to +communicate the inject-smart errors to the papr_scm kernel +module. This is done via SMART_INJECT PDSM which sends a payload of +type 'struct nd_papr_pdsm_smart_inject'. + +With the patch following output from ndctl inject-smart command is +expected for PAPR NVDIMMs: + +$ sudo ndctl inject-smart -fU nmem0 +[ + { + "dev":"nmem0", + "flag_failed_flush":true, + "flag_smart_event":true, + "health":{ + "health_state":"fatal", + "shutdown_state":"dirty", + "shutdown_count":0 + } + } +] + +$ sudo ndctl inject-smart -N nmem0 +[ + { + "dev":"nmem0", + "health":{ + "health_state":"ok", + "shutdown_state":"clean", + "shutdown_count":0 + } + } +] + +The patch depends on the kernel PAPR PDSM implementation for +PDSM_SMART_INJECT posted at [1]. + +[1] : https://lore.kernel.org/nvdimm/20220124202204.1488346-1-vaibhav@linux.ibm.com/ + +Link: https://lore.kernel.org/r/20220124203804.1490254-1-vaibhav@linux.ibm.com +Reviewed-by: Ira Weiny +Signed-off-by: Shivaprasad G Bhat +Signed-off-by: Vaibhav Jain +Signed-off-by: Vishal Verma +--- + ndctl/lib/papr.c | 61 +++++++++++++++++++++++++++++++++++++++++++ + ndctl/lib/papr_pdsm.h | 17 ++++++++++++ + 2 files changed, 78 insertions(+) + +diff --git a/ndctl/lib/papr.c b/ndctl/lib/papr.c +index 46cf9c1..7a1d559 100644 +--- a/ndctl/lib/papr.c ++++ b/ndctl/lib/papr.c +@@ -221,6 +221,41 @@ static unsigned int papr_smart_get_shutdown_state(struct ndctl_cmd *cmd) + return health.dimm_bad_shutdown; + } + ++static int papr_smart_inject_supported(struct ndctl_dimm *dimm) ++{ ++ if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) ++ return -EOPNOTSUPP; ++ ++ if (!test_dimm_dsm(dimm, PAPR_PDSM_SMART_INJECT)) ++ return -EIO; ++ ++ return ND_SMART_INJECT_HEALTH_STATE | ND_SMART_INJECT_UNCLEAN_SHUTDOWN; ++} ++ ++static int papr_smart_inject_valid(struct ndctl_cmd *cmd) ++{ ++ if (cmd->type != ND_CMD_CALL || ++ to_pdsm(cmd)->cmd_status != 0 || ++ to_pdsm_cmd(cmd) != PAPR_PDSM_SMART_INJECT) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static struct ndctl_cmd *papr_new_smart_inject(struct ndctl_dimm *dimm) ++{ ++ struct ndctl_cmd *cmd; ++ ++ cmd = allocate_cmd(dimm, PAPR_PDSM_SMART_INJECT, ++ sizeof(struct nd_papr_pdsm_smart_inject)); ++ if (!cmd) ++ return NULL; ++ /* Set the input payload size */ ++ to_ndcmd(cmd)->nd_size_in = ND_PDSM_HDR_SIZE + ++ sizeof(struct nd_papr_pdsm_smart_inject); ++ return cmd; ++} ++ + static unsigned int papr_smart_get_life_used(struct ndctl_cmd *cmd) + { + struct nd_papr_pdsm_health health; +@@ -255,11 +290,37 @@ static unsigned int papr_smart_get_shutdown_count(struct ndctl_cmd *cmd) + + return (health.extension_flags & PDSM_DIMM_DSC_VALID) ? + (health.dimm_dsc) : 0; ++} ++ ++static int papr_cmd_smart_inject_fatal(struct ndctl_cmd *cmd, bool enable) ++{ ++ if (papr_smart_inject_valid(cmd) < 0) ++ return -EINVAL; ++ ++ to_payload(cmd)->inject.flags |= PDSM_SMART_INJECT_HEALTH_FATAL; ++ to_payload(cmd)->inject.fatal_enable = enable; + ++ return 0; ++} ++ ++static int papr_cmd_smart_inject_unsafe_shutdown(struct ndctl_cmd *cmd, ++ bool enable) ++{ ++ if (papr_smart_inject_valid(cmd) < 0) ++ return -EINVAL; ++ ++ to_payload(cmd)->inject.flags |= PDSM_SMART_INJECT_BAD_SHUTDOWN; ++ to_payload(cmd)->inject.unsafe_shutdown_enable = enable; ++ ++ return 0; + } + + struct ndctl_dimm_ops * const papr_dimm_ops = &(struct ndctl_dimm_ops) { + .cmd_is_supported = papr_cmd_is_supported, ++ .new_smart_inject = papr_new_smart_inject, ++ .smart_inject_supported = papr_smart_inject_supported, ++ .smart_inject_fatal = papr_cmd_smart_inject_fatal, ++ .smart_inject_unsafe_shutdown = papr_cmd_smart_inject_unsafe_shutdown, + .smart_get_flags = papr_smart_get_flags, + .get_firmware_status = papr_get_firmware_status, + .xlat_firmware_status = papr_xlat_firmware_status, +diff --git a/ndctl/lib/papr_pdsm.h b/ndctl/lib/papr_pdsm.h +index f45b1e4..20ac20f 100644 +--- a/ndctl/lib/papr_pdsm.h ++++ b/ndctl/lib/papr_pdsm.h +@@ -121,12 +121,29 @@ struct nd_papr_pdsm_health { + enum papr_pdsm { + PAPR_PDSM_MIN = 0x0, + PAPR_PDSM_HEALTH, ++ PAPR_PDSM_SMART_INJECT, + PAPR_PDSM_MAX, + }; ++/* Flags for injecting specific smart errors */ ++#define PDSM_SMART_INJECT_HEALTH_FATAL (1 << 0) ++#define PDSM_SMART_INJECT_BAD_SHUTDOWN (1 << 1) ++ ++struct nd_papr_pdsm_smart_inject { ++ union { ++ struct { ++ /* One or more of PDSM_SMART_INJECT_ */ ++ __u32 flags; ++ __u8 fatal_enable; ++ __u8 unsafe_shutdown_enable; ++ }; ++ __u8 buf[ND_PDSM_PAYLOAD_MAX_SIZE]; ++ }; ++}; + + /* Maximal union that can hold all possible payload types */ + union nd_pdsm_payload { + struct nd_papr_pdsm_health health; ++ struct nd_papr_pdsm_smart_inject inject; + __u8 buf[ND_PDSM_PAYLOAD_MAX_SIZE]; + } __attribute__((packed)); + +-- +2.27.0 + diff --git a/SOURCES/0128-ndtest-ack-shutdown-count-Skip-the-test-on-ndtest.patch b/SOURCES/0128-ndtest-ack-shutdown-count-Skip-the-test-on-ndtest.patch new file mode 100644 index 0000000..fbf92a7 --- /dev/null +++ b/SOURCES/0128-ndtest-ack-shutdown-count-Skip-the-test-on-ndtest.patch @@ -0,0 +1,41 @@ +From 6e85cac1958f920f231b94ff570ac0e434595b7d Mon Sep 17 00:00:00 2001 +From: Shivaprasad G Bhat +Date: Tue, 25 Jan 2022 02:34:25 +0530 +Subject: [PATCH 128/217] ndtest/ack-shutdown-count: Skip the test on ndtest + +The PAPR has non-latched dirty shutdown implementation. +The test is enabling/disabling the LSS latch which is +irrelavent from PAPR pov. Skip the test. + +Link: https://lore.kernel.org/r/20220124210425.1493410-1-vaibhav@linux.ibm.com +Signed-off-by: Shivaprasad G Bhat +Signed-off-by: Vishal Verma +--- + test/ack-shutdown-count-set.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/test/ack-shutdown-count-set.c b/test/ack-shutdown-count-set.c +index a9e95c6..f091a40 100644 +--- a/test/ack-shutdown-count-set.c ++++ b/test/ack-shutdown-count-set.c +@@ -117,6 +117,7 @@ static int test_ack_shutdown_count_set(int loglevel, struct ndctl_test *test, + + int main(int argc, char *argv[]) + { ++ char *test_env = getenv("NDCTL_TEST_FAMILY"); + struct ndctl_test *test = ndctl_test_new(0); + struct ndctl_ctx *ctx; + int rc; +@@ -126,6 +127,9 @@ int main(int argc, char *argv[]) + return EXIT_FAILURE; + } + ++ if (test_env && strcmp(test_env, "PAPR") == 0) ++ return ndctl_test_result(test, 77); ++ + rc = ndctl_new(&ctx); + if (rc) + return ndctl_test_result(test, rc); +-- +2.27.0 + diff --git a/SOURCES/0129-ndctl-libndctl-Update-nvdimm-flags-after-smart-injec.patch b/SOURCES/0129-ndctl-libndctl-Update-nvdimm-flags-after-smart-injec.patch new file mode 100644 index 0000000..9ebc6cc --- /dev/null +++ b/SOURCES/0129-ndctl-libndctl-Update-nvdimm-flags-after-smart-injec.patch @@ -0,0 +1,218 @@ +From c5ccbf29c54b5a3d9cb1c138c06c8d5ac3ee80c2 Mon Sep 17 00:00:00 2001 +From: Vaibhav Jain +Date: Tue, 22 Feb 2022 17:45:19 +0530 +Subject: [PATCH 129/217] ndctl,libndctl: Update nvdimm flags after + smart-inject + +Presently after performing an inject-smart command the nvdimm flags reported are +out of date as shown below where no 'smart_notify' or 'flush_fail' flags were +reported even though they are set after injecting the smart error: + +$ sudo ndctl inject-smart -fU nmem0 +[ + { + "dev":"nmem0", + "health":{ + "health_state":"fatal", + "shutdown_state":"dirty", + "shutdown_count":0 + } + } +] +$ sudo cat /sys/class/nd/ndctl0/device/nmem0/papr/flags +flush_fail smart_notify + +This happens because nvdimm flags are only parsed once during its probe +and not refreshed even after a inject-smart operation makes them out of +date. To fix this the patch forces an update of nvdimm flags via newly +introduced export from libndctl named ndctl_dimm_refresh_flags() thats called +from dimm_inject_smart() after inject-smart command is successfully +submitted. This ensures that correct nvdimm flags are displayed later in that +function. With this implemented correct nvdimm flags are reported after a +inject-smart operation: + +$ sudo ndctl inject-smart -fU nmem0 +[ + { + "dev":"nmem0", + "flag_failed_flush":true, + "flag_smart_event":true, + "health":{ + "health_state":"fatal", + "shutdown_state":"dirty", + "shutdown_count":0 + } + } +] + +The patch refactors populate_dimm_attributes() to move the nvdimm flags +parsing code to the newly introduced ndctl_dimm_refresh_flags() +export. Since reading nvdimm flags requires constructing path using +'bus_prefix' which is only available during add_dimm(), the patch +introduces a new member 'struct ndctl_dimm.bus_prefix' to cache its +value. During ndctl_dimm_refresh_flags() the cached bus_prefix is used to +read the contents of the nvdimm flag file and pass it on to the appropriate +flag parsing function. Finally dimm_inject_smart() is updated to issue call to +ndctl_dimm_refresh_flags() before generating json output of the nvdimm status + +Link: https://lore.kernel.org/r/20220222121519.1674117-1-vaibhav@linux.ibm.com +Signed-off-by: Vaibhav Jain +Signed-off-by: Shivaprasad G Bhat +Signed-off-by: Vishal Verma +--- + ndctl/inject-smart.c | 4 +++ + ndctl/lib/libndctl.c | 55 +++++++++++++++++++++++++++++++----------- + ndctl/lib/libndctl.sym | 4 +++ + ndctl/lib/private.h | 1 + + ndctl/libndctl.h | 1 + + 5 files changed, 51 insertions(+), 14 deletions(-) + +diff -up ndctl-71.1/ndctl/inject-smart.c.orig ndctl-71.1/ndctl/inject-smart.c +--- ndctl-71.1/ndctl/inject-smart.c.orig 2022-10-07 16:40:24.610615979 -0400 ++++ ndctl-71.1/ndctl/inject-smart.c 2022-10-07 16:40:35.031651459 -0400 +@@ -467,6 +467,10 @@ static int dimm_inject_smart(struct ndct + jdimms = json_object_new_array(); + if (!jdimms) + goto out; ++ ++ /* Ensure the dimm flags are upto date before reporting them */ ++ ndctl_dimm_refresh_flags(dimm); ++ + jdimm = util_dimm_to_json(dimm, sctx.flags); + if (!jdimm) + goto out; +diff -up ndctl-71.1/ndctl/lib/libndctl.c.orig ndctl-71.1/ndctl/lib/libndctl.c +--- ndctl-71.1/ndctl/lib/libndctl.c.orig 2022-10-07 16:40:24.608615972 -0400 ++++ ndctl-71.1/ndctl/lib/libndctl.c 2022-10-07 16:40:35.032651462 -0400 +@@ -588,6 +588,7 @@ static void free_dimm(struct ndctl_dimm + free(dimm->unique_id); + free(dimm->dimm_buf); + free(dimm->dimm_path); ++ free(dimm->bus_prefix); + if (dimm->module) + kmod_module_unref(dimm->module); + if (dimm->health_eventfd > -1) +@@ -1645,14 +1646,34 @@ static enum ndctl_fwa_result fwa_result_ + return NDCTL_FWA_RESULT_INVALID; + } + ++NDCTL_EXPORT void ndctl_dimm_refresh_flags(struct ndctl_dimm *dimm) ++{ ++ struct ndctl_ctx *ctx = dimm->bus->ctx; ++ char *path = dimm->dimm_buf; ++ char buf[SYSFS_ATTR_SIZE]; ++ ++ /* Construct path to dimm flags sysfs file */ ++ sprintf(path, "%s/%s/flags", dimm->dimm_path, dimm->bus_prefix); ++ ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ return; ++ ++ /* Reset the flags */ ++ dimm->flags.flags = 0; ++ if (ndctl_bus_has_nfit(dimm->bus)) ++ parse_nfit_mem_flags(dimm, buf); ++ else if (ndctl_bus_is_papr_scm(dimm->bus)) ++ parse_papr_flags(dimm, buf); ++} ++ + static int populate_dimm_attributes(struct ndctl_dimm *dimm, +- const char *dimm_base, +- const char *bus_prefix) ++ const char *dimm_base) + { + int i, rc = -1; + char buf[SYSFS_ATTR_SIZE]; + struct ndctl_ctx *ctx = dimm->bus->ctx; + char *path = calloc(1, strlen(dimm_base) + 100); ++ const char *bus_prefix = dimm->bus_prefix; + + if (!path) + return -ENOMEM; +@@ -1736,16 +1757,10 @@ static int populate_dimm_attributes(stru + } + + sprintf(path, "%s/%s/flags", dimm_base, bus_prefix); +- if (sysfs_read_attr(ctx, path, buf) == 0) { +- if (ndctl_bus_has_nfit(dimm->bus)) +- parse_nfit_mem_flags(dimm, buf); +- else if (ndctl_bus_is_papr_scm(dimm->bus)) { +- dimm->cmd_family = NVDIMM_FAMILY_PAPR; +- parse_papr_flags(dimm, buf); +- } +- } +- + dimm->health_eventfd = open(path, O_RDONLY|O_CLOEXEC); ++ ++ ndctl_dimm_refresh_flags(dimm); ++ + rc = 0; + err_read: + +@@ -1801,8 +1816,9 @@ static int add_papr_dimm(struct ndctl_di + + rc = 0; + } else if (strcmp(buf, "nvdimm_test") == 0) { ++ dimm->cmd_family = NVDIMM_FAMILY_PAPR; + /* probe via common populate_dimm_attributes() */ +- rc = populate_dimm_attributes(dimm, dimm_base, "papr"); ++ rc = populate_dimm_attributes(dimm, dimm_base); + } + out: + free(path); +@@ -1899,9 +1915,20 @@ static void *add_dimm(void *parent, int + dimm->formats = formats; + /* Check if the given dimm supports nfit */ + if (ndctl_bus_has_nfit(bus)) { +- rc = populate_dimm_attributes(dimm, dimm_base, "nfit"); ++ dimm->bus_prefix = strdup("nfit"); ++ if (!dimm->bus_prefix) { ++ rc = -ENOMEM; ++ goto out; ++ } ++ rc = populate_dimm_attributes(dimm, dimm_base); ++ + } else if (ndctl_bus_has_of_node(bus)) { +- rc = add_papr_dimm(dimm, dimm_base); ++ dimm->bus_prefix = strdup("papr"); ++ if (!dimm->bus_prefix) { ++ rc = -ENOMEM; ++ goto out; ++ } ++ rc = add_papr_dimm(dimm, dimm_base); + } + + if (rc == -ENODEV) { +diff -up ndctl-71.1/ndctl/lib/libndctl.sym.orig ndctl-71.1/ndctl/lib/libndctl.sym +--- ndctl-71.1/ndctl/lib/libndctl.sym.orig 2022-10-07 16:40:24.344615073 -0400 ++++ ndctl-71.1/ndctl/lib/libndctl.sym 2022-10-07 16:40:35.032651462 -0400 +@@ -456,3 +456,7 @@ LIBNDCTL_26 { + ndctl_bus_nfit_translate_spa; + ndctl_dimm_sizeof_namespace_index; + } LIBNDCTL_25; ++ ++LIBNDCTL_27 { ++ ndctl_dimm_refresh_flags; ++} LIBNDCTL_26; +diff -up ndctl-71.1/ndctl/lib/private.h.orig ndctl-71.1/ndctl/lib/private.h +--- ndctl-71.1/ndctl/lib/private.h.orig 2022-10-07 16:40:24.455615451 -0400 ++++ ndctl-71.1/ndctl/lib/private.h 2022-10-07 16:40:35.032651462 -0400 +@@ -75,6 +75,7 @@ struct ndctl_dimm { + char *unique_id; + char *dimm_path; + char *dimm_buf; ++ char *bus_prefix; + int health_eventfd; + int buf_len; + int id; +diff -up ndctl-71.1/ndctl/libndctl.h.orig ndctl-71.1/ndctl/libndctl.h +--- ndctl-71.1/ndctl/libndctl.h.orig 2022-10-07 16:40:24.611615982 -0400 ++++ ndctl-71.1/ndctl/libndctl.h 2022-10-07 16:40:35.033651466 -0400 +@@ -221,6 +221,7 @@ int ndctl_dimm_is_active(struct ndctl_di + int ndctl_dimm_is_enabled(struct ndctl_dimm *dimm); + int ndctl_dimm_disable(struct ndctl_dimm *dimm); + int ndctl_dimm_enable(struct ndctl_dimm *dimm); ++void ndctl_dimm_refresh_flags(struct ndctl_dimm *dimm); + + struct ndctl_cmd; + struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus, diff --git a/SOURCES/0132-libcxl-add-GET_PARTITION_INFO-mailbox-command-and-ac.patch b/SOURCES/0132-libcxl-add-GET_PARTITION_INFO-mailbox-command-and-ac.patch new file mode 100644 index 0000000..c0dfe6d --- /dev/null +++ b/SOURCES/0132-libcxl-add-GET_PARTITION_INFO-mailbox-command-and-ac.patch @@ -0,0 +1,202 @@ +From 4f588b964dccf72030b1c432ed5dd8e2856f9d38 Mon Sep 17 00:00:00 2001 +From: Alison Schofield +Date: Tue, 22 Feb 2022 11:56:03 -0800 +Subject: [PATCH 132/217] libcxl: add GET_PARTITION_INFO mailbox command and + accessors + +The CXL PMEM provisioning model depends upon the values reported +in the CXL GET_PARTITION_INFO mailbox command when changing the +partitioning between volatile and persistent capacity. + +Add libcxl APIs to create a new GET_PARTITION_INFO mailbox command, +the command output data structure (privately), and accessor APIs to +return the fields in the partition info output. + +Per the CXL 2.0 specification, devices report partition capacities +as multiples of 256MB. Define and use a capacity multiplier to +convert the raw data into bytes for user consumption. Use byte +format as the norm for all capacity values produced or consumed +using CXL Mailbox commands. + +Link: https://lore.kernel.org/r/6cd7fffe1a95c9a1bc2239cb342067df564401a5.1645558189.git.alison.schofield@intel.com +Reviewed-by: Dan Williams +Signed-off-by: Alison Schofield +Signed-off-by: Vishal Verma +--- + Documentation/cxl/lib/libcxl.txt | 1 + + cxl/lib/libcxl.c | 66 ++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 5 +++ + cxl/lib/private.h | 10 +++++ + cxl/libcxl.h | 5 +++ + util/size.h | 1 + + 6 files changed, 88 insertions(+) + +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 4392b47..a6986ab 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -131,6 +131,7 @@ int cxl_memdev_read_label(struct cxl_memdev *memdev, void *buf, size_t length, + size_t offset); + int cxl_memdev_write_label(struct cxl_memdev *memdev, void *buf, size_t length, + size_t offset); ++struct cxl_cmd *cxl_cmd_new_get_partition(struct cxl_memdev *memdev); + + ---- + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index e0b443f..4557a71 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -1985,6 +1985,11 @@ static int cxl_cmd_validate_status(struct cxl_cmd *cmd, u32 id) + return 0; + } + ++static uint64_t cxl_capacity_to_bytes(leint64_t size) ++{ ++ return le64_to_cpu(size) * CXL_CAPACITY_MULTIPLIER; ++} ++ + /* Helpers for health_info fields (no endian conversion) */ + #define cmd_get_field_u8(cmd, n, N, field) \ + do { \ +@@ -2371,6 +2376,67 @@ CXL_EXPORT ssize_t cxl_cmd_read_label_get_payload(struct cxl_cmd *cmd, + return length; + } + ++CXL_EXPORT struct cxl_cmd *cxl_cmd_new_get_partition(struct cxl_memdev *memdev) ++{ ++ return cxl_cmd_new_generic(memdev, ++ CXL_MEM_COMMAND_ID_GET_PARTITION_INFO); ++} ++ ++static struct cxl_cmd_get_partition * ++cmd_to_get_partition(struct cxl_cmd *cmd) ++{ ++ if (cxl_cmd_validate_status(cmd, CXL_MEM_COMMAND_ID_GET_PARTITION_INFO)) ++ return NULL; ++ ++ if (!cmd) ++ return NULL; ++ return cmd->output_payload; ++} ++ ++CXL_EXPORT unsigned long long ++cxl_cmd_partition_get_active_volatile_size(struct cxl_cmd *cmd) ++{ ++ struct cxl_cmd_get_partition *c; ++ ++ c = cmd_to_get_partition(cmd); ++ if (!c) ++ return ULLONG_MAX; ++ return cxl_capacity_to_bytes(c->active_volatile); ++} ++ ++CXL_EXPORT unsigned long long ++cxl_cmd_partition_get_active_persistent_size(struct cxl_cmd *cmd) ++{ ++ struct cxl_cmd_get_partition *c; ++ ++ c = cmd_to_get_partition(cmd); ++ if (!c) ++ return ULLONG_MAX; ++ return cxl_capacity_to_bytes(c->active_persistent); ++} ++ ++CXL_EXPORT unsigned long long ++cxl_cmd_partition_get_next_volatile_size(struct cxl_cmd *cmd) ++{ ++ struct cxl_cmd_get_partition *c; ++ ++ c = cmd_to_get_partition(cmd); ++ if (!c) ++ return ULLONG_MAX; ++ return cxl_capacity_to_bytes(c->next_volatile); ++} ++ ++CXL_EXPORT unsigned long long ++cxl_cmd_partition_get_next_persistent_size(struct cxl_cmd *cmd) ++{ ++ struct cxl_cmd_get_partition *c; ++ ++ c = cmd_to_get_partition(cmd); ++ if (!c) ++ return ULLONG_MAX; ++ return cxl_capacity_to_bytes(c->next_persistent); ++} ++ + CXL_EXPORT int cxl_cmd_submit(struct cxl_cmd *cmd) + { + struct cxl_memdev *memdev = cmd->memdev; +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index e56a2bf..509e62d 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -155,4 +155,9 @@ global: + cxl_dport_get_port; + cxl_port_get_dport_by_memdev; + cxl_dport_maps_memdev; ++ cxl_cmd_new_get_partition; ++ cxl_cmd_partition_get_active_volatile_size; ++ cxl_cmd_partition_get_active_persistent_size; ++ cxl_cmd_partition_get_next_volatile_size; ++ cxl_cmd_partition_get_next_persistent_size; + } LIBCXL_1; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index f483c30..7f3a562 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + + #define CXL_EXPORT __attribute__ ((visibility("default"))) + +@@ -185,6 +186,15 @@ struct cxl_cmd_get_health_info { + le32 pmem_errors; + } __attribute__((packed)); + ++struct cxl_cmd_get_partition { ++ le64 active_volatile; ++ le64 active_persistent; ++ le64 next_volatile; ++ le64 next_persistent; ++} __attribute__((packed)); ++ ++#define CXL_CAPACITY_MULTIPLIER SZ_256M ++ + /* CXL 2.0 8.2.9.5.3 Byte 0 Health Status */ + #define CXL_CMD_HEALTH_INFO_STATUS_MAINTENANCE_NEEDED_MASK BIT(0) + #define CXL_CMD_HEALTH_INFO_STATUS_PERFORMANCE_DEGRADED_MASK BIT(1) +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 3b2293b..2c0a8d1 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -242,6 +242,11 @@ ssize_t cxl_cmd_read_label_get_payload(struct cxl_cmd *cmd, void *buf, + unsigned int length); + struct cxl_cmd *cxl_cmd_new_write_label(struct cxl_memdev *memdev, + void *buf, unsigned int offset, unsigned int length); ++struct cxl_cmd *cxl_cmd_new_get_partition(struct cxl_memdev *memdev); ++unsigned long long cxl_cmd_partition_get_active_volatile_size(struct cxl_cmd *cmd); ++unsigned long long cxl_cmd_partition_get_active_persistent_size(struct cxl_cmd *cmd); ++unsigned long long cxl_cmd_partition_get_next_volatile_size(struct cxl_cmd *cmd); ++unsigned long long cxl_cmd_partition_get_next_persistent_size(struct cxl_cmd *cmd); + + #ifdef __cplusplus + } /* extern "C" */ +diff --git a/util/size.h b/util/size.h +index a0f3593..e72467f 100644 +--- a/util/size.h ++++ b/util/size.h +@@ -15,6 +15,7 @@ + #define SZ_4M 0x00400000 + #define SZ_16M 0x01000000 + #define SZ_64M 0x04000000 ++#define SZ_256M 0x10000000 + #define SZ_1G 0x40000000 + #define SZ_1T 0x10000000000ULL + +-- +2.27.0 + diff --git a/SOURCES/0133-libcxl-add-accessors-for-capacity-fields-of-the-IDEN.patch b/SOURCES/0133-libcxl-add-accessors-for-capacity-fields-of-the-IDEN.patch new file mode 100644 index 0000000..e63b977 --- /dev/null +++ b/SOURCES/0133-libcxl-add-accessors-for-capacity-fields-of-the-IDEN.patch @@ -0,0 +1,118 @@ +From 50b9d77232d41a33c2109894ae93ddad877c1747 Mon Sep 17 00:00:00 2001 +From: Alison Schofield +Date: Tue, 22 Feb 2022 11:56:04 -0800 +Subject: [PATCH 133/217] libcxl: add accessors for capacity fields of the + IDENTIFY command + +The CXL PMEM provisioning model depends upon the values reported +in the CXL IDENTIFY mailbox command when changing the partitioning +between volatile and persistent capacity. + +Add accessors to the libcxl API to retrieve the total, volatile only, +and persistent only capacities from the IDENTIFY command. + +The fields are specified in multiples of 256MB per the CXL 2.0 spec. +Use the capacity multiplier to convert the raw data into bytes for user +consumption. + +Link: https://lore.kernel.org/r/58dec40b15a68f134466f61421751994735e55c1.1645558189.git.alison.schofield@intel.com +Reviewed-by: Dan Williams +Signed-off-by: Alison Schofield +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 3 +++ + cxl/libcxl.h | 3 +++ + 3 files changed, 50 insertions(+) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 4557a71..9413384 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -2277,6 +2277,17 @@ CXL_EXPORT struct cxl_cmd *cxl_cmd_new_identify(struct cxl_memdev *memdev) + return cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_IDENTIFY); + } + ++static struct cxl_cmd_identify * ++cmd_to_identify(struct cxl_cmd *cmd) ++{ ++ if (cxl_cmd_validate_status(cmd, CXL_MEM_COMMAND_ID_IDENTIFY)) ++ return NULL; ++ ++ if (!cmd) ++ return NULL; ++ return cmd->output_payload; ++} ++ + CXL_EXPORT int cxl_cmd_identify_get_fw_rev(struct cxl_cmd *cmd, char *fw_rev, + int fw_len) + { +@@ -2321,6 +2332,39 @@ CXL_EXPORT unsigned int cxl_cmd_identify_get_label_size(struct cxl_cmd *cmd) + return le32_to_cpu(id->lsa_size); + } + ++CXL_EXPORT unsigned long long ++cxl_cmd_identify_get_total_size(struct cxl_cmd *cmd) ++{ ++ struct cxl_cmd_identify *c; ++ ++ c = cmd_to_identify(cmd); ++ if (!c) ++ return ULLONG_MAX; ++ return cxl_capacity_to_bytes(c->total_capacity); ++} ++ ++CXL_EXPORT unsigned long long ++cxl_cmd_identify_get_volatile_only_size(struct cxl_cmd *cmd) ++{ ++ struct cxl_cmd_identify *c; ++ ++ c = cmd_to_identify(cmd); ++ if (!c) ++ return ULLONG_MAX; ++ return cxl_capacity_to_bytes(c->volatile_capacity); ++} ++ ++CXL_EXPORT unsigned long long ++cxl_cmd_identify_get_persistent_only_size(struct cxl_cmd *cmd) ++{ ++ struct cxl_cmd_identify *c; ++ ++ c = cmd_to_identify(cmd); ++ if (!c) ++ return ULLONG_MAX; ++ return cxl_capacity_to_bytes(c->persistent_capacity); ++} ++ + CXL_EXPORT struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, + int opcode) + { +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 509e62d..5ac6e9b 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -160,4 +160,7 @@ global: + cxl_cmd_partition_get_active_persistent_size; + cxl_cmd_partition_get_next_volatile_size; + cxl_cmd_partition_get_next_persistent_size; ++ cxl_cmd_identify_get_total_size; ++ cxl_cmd_identify_get_volatile_only_size; ++ cxl_cmd_identify_get_persistent_only_size; + } LIBCXL_1; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 2c0a8d1..6e18e84 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -201,6 +201,9 @@ int cxl_cmd_get_mbox_status(struct cxl_cmd *cmd); + int cxl_cmd_get_out_size(struct cxl_cmd *cmd); + struct cxl_cmd *cxl_cmd_new_identify(struct cxl_memdev *memdev); + int cxl_cmd_identify_get_fw_rev(struct cxl_cmd *cmd, char *fw_rev, int fw_len); ++unsigned long long cxl_cmd_identify_get_total_size(struct cxl_cmd *cmd); ++unsigned long long cxl_cmd_identify_get_volatile_only_size(struct cxl_cmd *cmd); ++unsigned long long cxl_cmd_identify_get_persistent_only_size(struct cxl_cmd *cmd); + unsigned long long cxl_cmd_identify_get_partition_align(struct cxl_cmd *cmd); + unsigned int cxl_cmd_identify_get_label_size(struct cxl_cmd *cmd); + struct cxl_cmd *cxl_cmd_new_get_health_info(struct cxl_memdev *memdev); +-- +2.27.0 + diff --git a/SOURCES/0134-libcxl-return-the-partition-alignment-field-in-bytes.patch b/SOURCES/0134-libcxl-return-the-partition-alignment-field-in-bytes.patch new file mode 100644 index 0000000..621186b --- /dev/null +++ b/SOURCES/0134-libcxl-return-the-partition-alignment-field-in-bytes.patch @@ -0,0 +1,53 @@ +From ac0066a6095e9ed0910fc560277ec693d3b507fa Mon Sep 17 00:00:00 2001 +From: Alison Schofield +Date: Tue, 22 Feb 2022 11:56:05 -0800 +Subject: [PATCH 134/217] libcxl: return the partition alignment field in bytes + +Per the CXL specification, the partition alignment field reports +the alignment value in multiples of 256MB. In the libcxl API, values +for all capacity fields are defined to return bytes. + +Update the partition alignment accessor to return bytes so that it +is in sync with other capacity related fields. + +Since this is early in the development cycle, the expectation is that +no third party consumers of this library have come to depend on the +encoded capacity field. If that is not the case, the original format +can be restored, and a new _bytes version introduced. + +Link: https://lore.kernel.org/r/6b937b09b61ddf95e069fd7acfda0c5bbb845be8.1645558189.git.alison.schofield@intel.com +Reviewed-by: Dan Williams +Signed-off-by: Alison Schofield +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 9413384..c05c13c 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -2308,15 +2308,12 @@ CXL_EXPORT int cxl_cmd_identify_get_fw_rev(struct cxl_cmd *cmd, char *fw_rev, + CXL_EXPORT unsigned long long cxl_cmd_identify_get_partition_align( + struct cxl_cmd *cmd) + { +- struct cxl_cmd_identify *id = +- (struct cxl_cmd_identify *)cmd->send_cmd->out.payload; +- +- if (cmd->send_cmd->id != CXL_MEM_COMMAND_ID_IDENTIFY) +- return -EINVAL; +- if (cmd->status < 0) +- return cmd->status; ++ struct cxl_cmd_identify *c; + +- return le64_to_cpu(id->partition_align); ++ c = cmd_to_identify(cmd); ++ if (!c) ++ return ULLONG_MAX; ++ return cxl_capacity_to_bytes(c->partition_align); + } + + CXL_EXPORT unsigned int cxl_cmd_identify_get_label_size(struct cxl_cmd *cmd) +-- +2.27.0 + diff --git a/SOURCES/0135-cxl-add-memdev-partition-information-to-cxl-list.patch b/SOURCES/0135-cxl-add-memdev-partition-information-to-cxl-list.patch new file mode 100644 index 0000000..c8220a3 --- /dev/null +++ b/SOURCES/0135-cxl-add-memdev-partition-information-to-cxl-list.patch @@ -0,0 +1,267 @@ +From 033b94ad7346a82504cffba3d87650b60945c1eb Mon Sep 17 00:00:00 2001 +From: Alison Schofield +Date: Tue, 22 Feb 2022 11:56:06 -0800 +Subject: [PATCH 135/217] cxl: add memdev partition information to cxl-list + +The CXL PMEM provisioning model depends upon the values reported in +both the CXL IDENTIFY and GET_PARTITION_INFO mailbox commands when +changing the partitioning between volatile and persistent capacity. + +Add an option to the 'cxl list' command to display partition information. + +Include the partitioning related fields from the IDENTIFY command: +total, volatile_only, persistent_only, and partition_alignment sizes. +When the partition_alignment size is greater than zero, indicating +partitionable capacity, include the active and next size fields of +GET_PARTITION_INFO. + +Example: + "partition_info":{ + "total_size":273535729664, + "volatile_only_size":0, + "persistent_only_size":0, + "partition_alignment_size":268435456 + "active_volatile_size":273535729664, + "active_persistent_size":0, + "next_volatile_size":0, + "next_persistent_size":0, + } + +Link: https://lore.kernel.org/r/70cc57379d2c49692036b1daa158a122aa19c126.1645558189.git.alison.schofield@intel.com +Reviewed-by: Dan Williams +Signed-off-by: Alison Schofield +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 23 +++++++ + cxl/filter.c | 2 + + cxl/filter.h | 1 + + cxl/json.c | 120 +++++++++++++++++++++++++++++++++ + cxl/list.c | 2 + + util/json.h | 1 + + 6 files changed, 149 insertions(+) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 90e6d9f..f6aba0c 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -196,6 +196,29 @@ OPTIONS + } + ] + ---- ++-I:: ++--partition:: ++ Include partition information in the memdev listing. Example listing: ++---- ++# cxl list -m mem0 -I ++[ ++ { ++ "memdev":"mem0", ++ "pmem_size":0, ++ "ram_size":273535729664, ++ "partition_info":{ ++ "total_size":273535729664, ++ "volatile_only_size":0, ++ "persistent_only_size":0, ++ "partition_alignment_size":268435456 ++ "active_volatile_size":273535729664, ++ "active_persistent_size":0, ++ "next_volatile_size":0, ++ "next_persistent_size":0, ++ } ++ } ++] ++---- + + -B:: + --buses:: +diff --git a/cxl/filter.c b/cxl/filter.c +index 925bf3a..b339642 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -581,6 +581,8 @@ static unsigned long params_to_flags(struct cxl_filter_params *param) + flags |= UTIL_JSON_HEALTH; + if (param->targets) + flags |= UTIL_JSON_TARGETS; ++ if (param->partition) ++ flags |= UTIL_JSON_PARTITION; + return flags; + } + +diff --git a/cxl/filter.h b/cxl/filter.h +index 5deabb3..697b777 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -23,6 +23,7 @@ struct cxl_filter_params { + bool idle; + bool human; + bool health; ++ bool partition; + struct log_ctx ctx; + }; + +diff --git a/cxl/json.c b/cxl/json.c +index f3b536e..fdc6f73 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -185,6 +185,121 @@ err_jobj: + return NULL; + } + ++/* ++ * Present complete view of memdev partition by presenting fields from ++ * both GET_PARTITION_INFO and IDENTIFY mailbox commands. ++ */ ++static struct json_object *util_cxl_memdev_partition_to_json(struct cxl_memdev *memdev, ++ unsigned long flags) ++{ ++ struct json_object *jobj = NULL; ++ struct json_object *jpart; ++ unsigned long long cap; ++ struct cxl_cmd *cmd; ++ int rc; ++ ++ jpart = json_object_new_object(); ++ if (!jpart) ++ return NULL; ++ if (!memdev) ++ goto err_jobj; ++ ++ /* Retrieve partition info in the IDENTIFY mbox cmd */ ++ cmd = cxl_cmd_new_identify(memdev); ++ if (!cmd) ++ goto err_jobj; ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) ++ goto err_identify; ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) ++ goto err_identify; ++ ++ cap = cxl_cmd_identify_get_total_size(cmd); ++ if (cap != ULLONG_MAX) { ++ jobj = util_json_object_size(cap, flags); ++ if (jobj) ++ json_object_object_add(jpart, "total_size", jobj); ++ } ++ cap = cxl_cmd_identify_get_volatile_only_size(cmd); ++ if (cap != ULLONG_MAX) { ++ jobj = util_json_object_size(cap, flags); ++ if (jobj) ++ json_object_object_add(jpart, ++ "volatile_only_size", jobj); ++ } ++ cap = cxl_cmd_identify_get_persistent_only_size(cmd); ++ if (cap != ULLONG_MAX) { ++ jobj = util_json_object_size(cap, flags); ++ if (jobj) ++ json_object_object_add(jpart, ++ "persistent_only_size", jobj); ++ } ++ cap = cxl_cmd_identify_get_partition_align(cmd); ++ jobj = util_json_object_size(cap, flags); ++ if (jobj) ++ json_object_object_add(jpart, "partition_alignment_size", jobj); ++ ++ cxl_cmd_unref(cmd); ++ ++ /* Return now if there is no partition info to get. */ ++ if (!cap) ++ return jpart; ++ ++ /* Retrieve partition info in GET_PARTITION_INFO mbox cmd */ ++ cmd = cxl_cmd_new_get_partition(memdev); ++ if (!cmd) ++ return jpart; ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) ++ goto err_get; ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) ++ goto err_get; ++ ++ cap = cxl_cmd_partition_get_active_volatile_size(cmd); ++ if (cap != ULLONG_MAX) { ++ jobj = util_json_object_size(cap, flags); ++ if (jobj) ++ json_object_object_add(jpart, ++ "active_volatile_size", jobj); ++ } ++ cap = cxl_cmd_partition_get_active_persistent_size(cmd); ++ if (cap != ULLONG_MAX) { ++ jobj = util_json_object_size(cap, flags); ++ if (jobj) ++ json_object_object_add(jpart, ++ "active_persistent_size", jobj); ++ } ++ cap = cxl_cmd_partition_get_next_volatile_size(cmd); ++ if (cap != ULLONG_MAX) { ++ jobj = util_json_object_size(cap, flags); ++ if (jobj) ++ json_object_object_add(jpart, ++ "next_volatile_size", jobj); ++ } ++ cap = cxl_cmd_partition_get_next_persistent_size(cmd); ++ if (cap != ULLONG_MAX) { ++ jobj = util_json_object_size(cap, flags); ++ if (jobj) ++ json_object_object_add(jpart, ++ "next_persistent_size", jobj); ++ } ++ ++err_get: ++ cxl_cmd_unref(cmd); ++ return jpart; ++ ++err_identify: ++ cxl_cmd_unref(cmd); ++ ++err_jobj: ++ json_object_put(jpart); ++ return NULL; ++} ++ + struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + unsigned long flags) + { +@@ -239,6 +354,11 @@ struct json_object *util_cxl_memdev_to_json(struct cxl_memdev *memdev, + json_object_object_add(jdev, "state", jobj); + } + ++ if (flags & UTIL_JSON_PARTITION) { ++ jobj = util_cxl_memdev_partition_to_json(memdev, flags); ++ if (jobj) ++ json_object_object_add(jdev, "partition_info", jobj); ++ } + return jdev; + } + +diff --git a/cxl/list.c b/cxl/list.c +index de96ff9..1e9d441 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -48,6 +48,8 @@ static const struct option options[] = { + "use human friendly number formats "), + OPT_BOOLEAN('H', "health", ¶m.health, + "include memory device health information "), ++ OPT_BOOLEAN('I', "partition", ¶m.partition, ++ "include memory device partition information "), + #ifdef ENABLE_DEBUG + OPT_BOOLEAN(0, "debug", &debug, "debug list walk"), + #endif +diff --git a/util/json.h b/util/json.h +index e026df1..73bb9f0 100644 +--- a/util/json.h ++++ b/util/json.h +@@ -19,6 +19,7 @@ enum util_json_flags { + UTIL_JSON_DAX_MAPPINGS = (1 << 9), + UTIL_JSON_HEALTH = (1 << 10), + UTIL_JSON_TARGETS = (1 << 11), ++ UTIL_JSON_PARTITION = (1 << 12), + }; + + void util_display_json_array(FILE *f_out, struct json_object *jarray, +-- +2.27.0 + diff --git a/SOURCES/0136-libcxl-add-interfaces-for-SET_PARTITION_INFO-mailbox.patch b/SOURCES/0136-libcxl-add-interfaces-for-SET_PARTITION_INFO-mailbox.patch new file mode 100644 index 0000000..fc4253b --- /dev/null +++ b/SOURCES/0136-libcxl-add-interfaces-for-SET_PARTITION_INFO-mailbox.patch @@ -0,0 +1,154 @@ +From b424b4bfd555654fc41996e964bdd0495f585793 Mon Sep 17 00:00:00 2001 +From: Alison Schofield +Date: Tue, 22 Feb 2022 11:56:07 -0800 +Subject: [PATCH 136/217] libcxl: add interfaces for SET_PARTITION_INFO mailbox + command + +The CXL PMEM provisioning model depends upon the CXL mailbox command +SET_PARTITION_INFO to change a device's partitioning between volatile +and persistent capacity. + +Add interfaces to libcxl to allocate and send a SET_PARTITION_INFO +mailbox command as defined in the CXL 2.0 specification. + +Link: https://lore.kernel.org/r/978c1cf78f3dd22f6070e51a241bc63cac9297de.1645558189.git.alison.schofield@intel.com +Reviewed-by: Dan Williams +Signed-off-by: Alison Schofield +Signed-off-by: Vishal Verma +--- + Documentation/cxl/lib/libcxl.txt | 11 +++++++++++ + cxl/lib/libcxl.c | 28 ++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 2 ++ + cxl/lib/private.h | 8 ++++++++ + cxl/libcxl.h | 10 ++++++++++ + 5 files changed, 59 insertions(+) + +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index a6986ab..7b223cb 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -132,6 +132,8 @@ int cxl_memdev_read_label(struct cxl_memdev *memdev, void *buf, size_t length, + int cxl_memdev_write_label(struct cxl_memdev *memdev, void *buf, size_t length, + size_t offset); + struct cxl_cmd *cxl_cmd_new_get_partition(struct cxl_memdev *memdev); ++struct cxl_cmd *cxl_cmd_new_set_partition(struct cxl_memdev *memdev, ++ unsigned long long volatile_size); + + ---- + +@@ -148,6 +150,8 @@ this sub-class of interfaces, there are: + a CXL standard opcode. See the potential command ids in + /usr/include/linux/cxl_mem.h. + ++ * 'cxl_cmd__set_' interfaces that set specific fields in a cxl_cmd ++ + * 'cxl_cmd_submit' which submits the command via ioctl() + + * 'cxl_cmd__get_' interfaces that get specific fields out of the +@@ -167,6 +171,13 @@ cxl_memdev{read,write,zero}_label() are helpers for marshaling multiple + label access commands over an arbitrary extent of the device's label + area. + ++cxl_cmd_partition_set_mode() supports selecting NEXTBOOT or IMMEDIATE ++mode. When CXL_SETPART_IMMEDIATE mode is set, it is the caller’s ++responsibility to avoid immediate changes to partitioning when the ++device is in use. When CXL_SETPART_NEXTBOOT mode is set, the change ++in partitioning shall become the “next” configuration, to become ++active on the next device reset. ++ + BUSES + ----- + The CXL Memory space is CPU and Device coherent. The address ranges that +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index c05c13c..daa2bbc 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -2478,6 +2478,34 @@ cxl_cmd_partition_get_next_persistent_size(struct cxl_cmd *cmd) + return cxl_capacity_to_bytes(c->next_persistent); + } + ++CXL_EXPORT int cxl_cmd_partition_set_mode(struct cxl_cmd *cmd, ++ enum cxl_setpartition_mode mode) ++{ ++ struct cxl_cmd_set_partition *setpart = cmd->input_payload; ++ ++ if (mode == CXL_SETPART_IMMEDIATE) ++ setpart->flags = CXL_CMD_SET_PARTITION_FLAG_IMMEDIATE; ++ else ++ setpart->flags = !CXL_CMD_SET_PARTITION_FLAG_IMMEDIATE; ++ ++ return 0; ++} ++ ++CXL_EXPORT struct cxl_cmd *cxl_cmd_new_set_partition(struct cxl_memdev *memdev, ++ unsigned long long volatile_size) ++{ ++ struct cxl_cmd_set_partition *setpart; ++ struct cxl_cmd *cmd; ++ ++ cmd = cxl_cmd_new_generic(memdev, ++ CXL_MEM_COMMAND_ID_SET_PARTITION_INFO); ++ ++ setpart = cmd->input_payload; ++ setpart->volatile_size = cpu_to_le64(volatile_size) ++ / CXL_CAPACITY_MULTIPLIER; ++ return cmd; ++} ++ + CXL_EXPORT int cxl_cmd_submit(struct cxl_cmd *cmd) + { + struct cxl_memdev *memdev = cmd->memdev; +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 5ac6e9b..aab1112 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -163,4 +163,6 @@ global: + cxl_cmd_identify_get_total_size; + cxl_cmd_identify_get_volatile_only_size; + cxl_cmd_identify_get_persistent_only_size; ++ cxl_cmd_new_set_partition; ++ cxl_cmd_partition_set_mode; + } LIBCXL_1; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 7f3a562..c6d88f7 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -195,6 +195,14 @@ struct cxl_cmd_get_partition { + + #define CXL_CAPACITY_MULTIPLIER SZ_256M + ++struct cxl_cmd_set_partition { ++ le64 volatile_size; ++ u8 flags; ++} __attribute__((packed)); ++ ++/* CXL 2.0 8.2.9.5.2 Set Partition Info */ ++#define CXL_CMD_SET_PARTITION_FLAG_IMMEDIATE BIT(0) ++ + /* CXL 2.0 8.2.9.5.3 Byte 0 Health Status */ + #define CXL_CMD_HEALTH_INFO_STATUS_MAINTENANCE_NEEDED_MASK BIT(0) + #define CXL_CMD_HEALTH_INFO_STATUS_PERFORMANCE_DEGRADED_MASK BIT(1) +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 6e18e84..0063d31 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -250,6 +250,16 @@ unsigned long long cxl_cmd_partition_get_active_volatile_size(struct cxl_cmd *cm + unsigned long long cxl_cmd_partition_get_active_persistent_size(struct cxl_cmd *cmd); + unsigned long long cxl_cmd_partition_get_next_volatile_size(struct cxl_cmd *cmd); + unsigned long long cxl_cmd_partition_get_next_persistent_size(struct cxl_cmd *cmd); ++struct cxl_cmd *cxl_cmd_new_set_partition(struct cxl_memdev *memdev, ++ unsigned long long volatile_size); ++ ++enum cxl_setpartition_mode { ++ CXL_SETPART_NEXTBOOT, ++ CXL_SETPART_IMMEDIATE, ++}; ++ ++int cxl_cmd_partition_set_mode(struct cxl_cmd *cmd, ++ enum cxl_setpartition_mode mode); + + #ifdef __cplusplus + } /* extern "C" */ +-- +2.27.0 + diff --git a/SOURCES/0137-cxl-add-command-cxl-set-partition.patch b/SOURCES/0137-cxl-add-command-cxl-set-partition.patch new file mode 100644 index 0000000..664bd5e --- /dev/null +++ b/SOURCES/0137-cxl-add-command-cxl-set-partition.patch @@ -0,0 +1,400 @@ +From e2d5cbab4a4b530b679172ae7ca59cc506d1d4cc Mon Sep 17 00:00:00 2001 +From: Alison Schofield +Date: Tue, 22 Feb 2022 11:56:08 -0800 +Subject: [PATCH 137/217] cxl: add command 'cxl set-partition' + +CXL devices may support both volatile and persistent memory capacity. +The amount of device capacity set aside for each type is typically +established at the factory, but some devices also allow for dynamic +re-partitioning. Add a command for this purpose. + + usage: cxl set-partition [..] [] + + -v, --verbose turn on debug + -S, --serial use serial numbers to id memdevs + -t, --type 'pmem' or 'volatile' (Default: 'pmem') + -s, --size size in bytes (Default: all available capacity) + -a, --align auto-align --size per device's requirement + +Link: https://lore.kernel.org/r/e7accc7ba93def81b48304cf5fb483345757410d.1645558189.git.alison.schofield@intel.com +Reviewed-by: Dan Williams +Signed-off-by: Alison Schofield +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-set-partition.txt | 68 ++++++++ + Documentation/cxl/meson.build | 1 + + cxl/builtin.h | 1 + + cxl/cxl.c | 1 + + cxl/memdev.c | 206 ++++++++++++++++++++++++ + 5 files changed, 277 insertions(+) + create mode 100644 Documentation/cxl/cxl-set-partition.txt + +diff --git a/Documentation/cxl/cxl-set-partition.txt b/Documentation/cxl/cxl-set-partition.txt +new file mode 100644 +index 0000000..1e548af +--- /dev/null ++++ b/Documentation/cxl/cxl-set-partition.txt +@@ -0,0 +1,68 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-set-partition(1) ++==================== ++ ++NAME ++---- ++cxl-set-partition - set the partitioning between volatile and persistent capacity on a CXL memdev ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl set-partition [ [..] []' ++ ++DESCRIPTION ++----------- ++CXL devices may support both volatile and persistent memory capacity. ++The amount of device capacity set aside for each type is typically ++established at the factory, but some devices also allow for dynamic ++re-partitioning. ++ ++Use this command to partition a device into volatile and persistent ++capacity. The change in partitioning becomes the “next” configuration, ++to become active on the next device reset. ++ ++Use "cxl list -m -I" to examine the partitioning capabilities ++of a device. A partition_alignment_size value of zero means there is ++no available capacity and therefore the partitions cannot be changed. ++ ++Using this command to change the size of the persistent capacity shall ++result in the loss of data stored. ++ ++OPTIONS ++------- ++:: ++include::memdev-option.txt[] ++ ++-t:: ++--type=:: ++ Type of partition, 'pmem' or 'volatile', to modify. ++ Default: 'pmem' ++ ++-s:: ++--size=:: ++ Size of the partition in bytes. Size must align to the ++ devices alignment requirement. Use 'cxl list -m -I' ++ to find 'partition_alignment_size', or, use the --align option. ++ Default: All available capacity is assigned to . ++ ++-a:: ++--align:: ++ Select this option to allow the automatic alignment of --size ++ to meet device alignment requirements. When using this option, ++ specify the minimum --size of the --type partition needed. When ++ this option is omitted, the command fails if --size is not ++ properly aligned. Use 'cxl list -m -I' to examine the ++ partition_alignment_size. ++ ++-v:: ++ Turn on verbose debug messages in the library (if libcxl was built with ++ logging and debug enabled). ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-list[1], ++CXL-2.0 8.2.9.5.2 +diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build +index 96f4666..e927644 100644 +--- a/Documentation/cxl/meson.build ++++ b/Documentation/cxl/meson.build +@@ -34,6 +34,7 @@ cxl_manpages = [ + 'cxl-disable-memdev.txt', + 'cxl-enable-port.txt', + 'cxl-disable-port.txt', ++ 'cxl-set-partition.txt', + ] + + foreach man : cxl_manpages +diff --git a/cxl/builtin.h b/cxl/builtin.h +index 3123d5e..7bbad98 100644 +--- a/cxl/builtin.h ++++ b/cxl/builtin.h +@@ -14,4 +14,5 @@ int cmd_disable_memdev(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx); + #endif /* _CXL_BUILTIN_H_ */ +diff --git a/cxl/cxl.c b/cxl/cxl.c +index c20c569..ab4bbec 100644 +--- a/cxl/cxl.c ++++ b/cxl/cxl.c +@@ -68,6 +68,7 @@ static struct cmd_struct commands[] = { + { "enable-memdev", .c_fn = cmd_enable_memdev }, + { "disable-port", .c_fn = cmd_disable_port }, + { "enable-port", .c_fn = cmd_enable_port }, ++ { "set-partition", .c_fn = cmd_set_partition }, + }; + + int main(int argc, const char **argv) +diff --git a/cxl/memdev.c b/cxl/memdev.c +index 90b33e1..91d914d 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -6,11 +6,14 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include + #include + ++#include "json.h" + #include "filter.h" + + struct action_context { +@@ -26,10 +29,18 @@ static struct parameters { + bool verbose; + bool serial; + bool force; ++ bool align; ++ const char *type; ++ const char *size; + } param; + + static struct log_ctx ml; + ++enum cxl_setpart_type { ++ CXL_SETPART_PMEM, ++ CXL_SETPART_VOLATILE, ++}; ++ + #define BASE_OPTIONS() \ + OPT_BOOLEAN('v',"verbose", ¶m.verbose, "turn on debug"), \ + OPT_BOOLEAN('S', "serial", ¶m.serial, "use serial numbers to id memdevs") +@@ -51,6 +62,14 @@ OPT_UINTEGER('O', "offset", ¶m.offset, \ + OPT_BOOLEAN('f', "force", ¶m.force, \ + "DANGEROUS: override active memdev safety checks") + ++#define SET_PARTITION_OPTIONS() \ ++OPT_STRING('t', "type", ¶m.type, "type", \ ++ "'pmem' or 'volatile' (Default: 'pmem')"), \ ++OPT_STRING('s', "size", ¶m.size, "size", \ ++ "size in bytes (Default: all available capacity)"), \ ++OPT_BOOLEAN('a', "align", ¶m.align, \ ++ "auto-align --size per device's requirement") ++ + static const struct option read_options[] = { + BASE_OPTIONS(), + LABEL_OPTIONS(), +@@ -82,6 +101,12 @@ static const struct option enable_options[] = { + OPT_END(), + }; + ++static const struct option set_partition_options[] = { ++ BASE_OPTIONS(), ++ SET_PARTITION_OPTIONS(), ++ OPT_END(), ++}; ++ + static int action_disable(struct cxl_memdev *memdev, struct action_context *actx) + { + if (!cxl_memdev_is_enabled(memdev)) +@@ -209,6 +234,176 @@ out: + return rc; + } + ++static unsigned long long ++partition_align(const char *devname, enum cxl_setpart_type type, ++ unsigned long long volatile_size, unsigned long long alignment, ++ unsigned long long available) ++{ ++ if (IS_ALIGNED(volatile_size, alignment)) ++ return volatile_size; ++ ++ if (!param.align) { ++ log_err(&ml, "%s: size %lld is not partition aligned %lld\n", ++ devname, volatile_size, alignment); ++ return ULLONG_MAX; ++ } ++ ++ /* Align based on partition type to fulfill users size request */ ++ if (type == CXL_SETPART_PMEM) ++ volatile_size = ALIGN_DOWN(volatile_size, alignment); ++ else ++ volatile_size = ALIGN(volatile_size, alignment); ++ ++ /* Fail if the align pushes size over the available limit. */ ++ if (volatile_size > available) { ++ log_err(&ml, "%s: aligned partition size %lld exceeds available size %lld\n", ++ devname, volatile_size, available); ++ volatile_size = ULLONG_MAX; ++ } ++ ++ return volatile_size; ++} ++ ++static unsigned long long ++param_size_to_volatile_size(const char *devname, enum cxl_setpart_type type, ++ unsigned long long size, unsigned long long available) ++{ ++ /* User omits size option. Apply all available capacity to type. */ ++ if (size == ULLONG_MAX) { ++ if (type == CXL_SETPART_PMEM) ++ return 0; ++ return available; ++ } ++ ++ /* User includes a size option. Apply it to type */ ++ if (size > available) { ++ log_err(&ml, "%s: %lld exceeds available capacity %lld\n", ++ devname, size, available); ++ return ULLONG_MAX; ++ } ++ if (type == CXL_SETPART_PMEM) ++ return available - size; ++ return size; ++} ++ ++/* ++ * Return the volatile_size to use in the CXL set paritition ++ * command, or ULLONG_MAX if unable to validate the partition ++ * request. ++ */ ++static unsigned long long ++validate_partition(struct cxl_memdev *memdev, enum cxl_setpart_type type, ++ unsigned long long size) ++{ ++ unsigned long long total_cap, volatile_only, persistent_only; ++ const char *devname = cxl_memdev_get_devname(memdev); ++ unsigned long long volatile_size = ULLONG_MAX; ++ unsigned long long available, alignment; ++ struct cxl_cmd *cmd; ++ int rc; ++ ++ cmd = cxl_cmd_new_identify(memdev); ++ if (!cmd) ++ return ULLONG_MAX; ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) ++ goto out; ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) ++ goto out; ++ ++ alignment = cxl_cmd_identify_get_partition_align(cmd); ++ if (alignment == 0) { ++ log_err(&ml, "%s: no available capacity\n", devname); ++ goto out; ++ } ++ ++ /* Calculate the actual available capacity */ ++ total_cap = cxl_cmd_identify_get_total_size(cmd); ++ volatile_only = cxl_cmd_identify_get_volatile_only_size(cmd); ++ persistent_only = cxl_cmd_identify_get_persistent_only_size(cmd); ++ available = total_cap - volatile_only - persistent_only; ++ ++ /* Translate the users size request into an aligned volatile_size */ ++ volatile_size = param_size_to_volatile_size(devname, type, size, ++ available); ++ if (volatile_size == ULLONG_MAX) ++ goto out; ++ ++ volatile_size = partition_align(devname, type, volatile_size, alignment, ++ available); ++ ++out: ++ cxl_cmd_unref(cmd); ++ return volatile_size; ++} ++ ++static int action_setpartition(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ const char *devname = cxl_memdev_get_devname(memdev); ++ enum cxl_setpart_type type = CXL_SETPART_PMEM; ++ unsigned long long size = ULLONG_MAX; ++ struct json_object *jmemdev; ++ struct cxl_cmd *cmd; ++ int rc; ++ ++ if (param.type) { ++ if (strcmp(param.type, "pmem") == 0) ++ /* default */; ++ else if (strcmp(param.type, "volatile") == 0) ++ type = CXL_SETPART_VOLATILE; ++ else { ++ log_err(&ml, "invalid type '%s'\n", param.type); ++ return -EINVAL; ++ } ++ } ++ ++ if (param.size) { ++ size = parse_size64(param.size); ++ if (size == ULLONG_MAX) { ++ log_err(&ml, "%s: failed to parse size option '%s'\n", ++ devname, param.size); ++ return -EINVAL; ++ } ++ } ++ ++ size = validate_partition(memdev, type, size); ++ if (size == ULLONG_MAX) ++ return -EINVAL; ++ ++ cmd = cxl_cmd_new_set_partition(memdev, size); ++ if (!cmd) { ++ rc = -ENXIO; ++ goto out_err; ++ } ++ ++ rc = cxl_cmd_submit(cmd); ++ if (rc < 0) { ++ log_err(&ml, "cmd submission failed: %s\n", strerror(-rc)); ++ goto out_cmd; ++ } ++ ++ rc = cxl_cmd_get_mbox_status(cmd); ++ if (rc != 0) { ++ log_err(&ml, "%s: mbox status: %d\n", __func__, rc); ++ rc = -ENXIO; ++ } ++ ++out_cmd: ++ cxl_cmd_unref(cmd); ++out_err: ++ if (rc) ++ log_err(&ml, "%s error: %s\n", devname, strerror(-rc)); ++ ++ jmemdev = util_cxl_memdev_to_json(memdev, UTIL_JSON_PARTITION); ++ if (jmemdev) ++ printf("%s\n", json_object_to_json_string_ext(jmemdev, ++ JSON_C_TO_STRING_PRETTY)); ++ ++ return rc; ++} ++ + static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + int (*action)(struct cxl_memdev *memdev, + struct action_context *actx), +@@ -398,3 +593,14 @@ int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx) + count > 1 ? "s" : ""); + return count >= 0 ? 0 : EXIT_FAILURE; + } ++ ++int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int count = memdev_action(argc, argv, ctx, action_setpartition, ++ set_partition_options, ++ "cxl set-partition [..] []"); ++ log_info(&ml, "set_partition %d mem%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); ++ ++ return count >= 0 ? 0 : EXIT_FAILURE; ++} +-- +2.27.0 + diff --git a/SOURCES/0138-Update-ndctl.spec-to-allow-flatpak-builds.patch b/SOURCES/0138-Update-ndctl.spec-to-allow-flatpak-builds.patch new file mode 100644 index 0000000..c094600 --- /dev/null +++ b/SOURCES/0138-Update-ndctl.spec-to-allow-flatpak-builds.patch @@ -0,0 +1,41 @@ +From cd41a4dc6af9b48045b0aa46669d560faeb134e2 Mon Sep 17 00:00:00 2001 +From: Jay W +Date: Tue, 15 Feb 2022 00:45:00 +0000 +Subject: [PATCH 138/217] Update ndctl.spec to allow flatpak builds + +This will allow ndctl to be build as dependency for some flatpak builds +on fedora systems. +See also: https://docs.fedoraproject.org/en-US/flatpak/troubleshooting/#_uncompressed_manual_pages + +Link: https://github.com/pmem/ndctl/pull/192 +Signed-off-by: Vishal Verma +--- + ndctl.spec.in | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/ndctl.spec.in b/ndctl.spec.in +index 9ca831e..cfcafa2 100644 +--- a/ndctl.spec.in ++++ b/ndctl.spec.in +@@ -38,6 +38,9 @@ subsystem defines a kernel device model and control message interface for + platform NVDIMM resources like those defined by the ACPI 6+ NFIT (NVDIMM + Firmware Interface Table). + ++%if 0%{?flatpak} ++%global _udevrulesdir %{_prefix}/lib/udev/rules.d ++%endif + + %package -n DNAME + Summary: Development files for libndctl +@@ -231,7 +234,7 @@ fi + %{_libdir}/libcxl.so + %{_libdir}/pkgconfig/libcxl.pc + %{_mandir}/man3/cxl* +-%{_mandir}/man3/libcxl.3.gz ++%{_mandir}/man3/libcxl.3* + + + %changelog +-- +2.27.0 + diff --git a/SOURCES/0139-daxctl-provide-safe-versions-of-iteration-API.patch b/SOURCES/0139-daxctl-provide-safe-versions-of-iteration-API.patch new file mode 100644 index 0000000..ca0b342 --- /dev/null +++ b/SOURCES/0139-daxctl-provide-safe-versions-of-iteration-API.patch @@ -0,0 +1,47 @@ +From 3e4a66f0dfb02046f6d3375d637840b6da9c71d1 Mon Sep 17 00:00:00 2001 +From: michalbiesek +Date: Mon, 2 Dec 2019 11:21:03 +0100 +Subject: [PATCH 139/217] daxctl: provide safe versions of iteration API + +Add support for safe iterate through regions and devices + +Link: https://github.com/pmem/ndctl/pull/132 +Signed-off-by: Michal Biesek +Signed-off-by: Vishal Verma +--- + daxctl/libdaxctl.h | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/daxctl/libdaxctl.h b/daxctl/libdaxctl.h +index 6b6c71f..6876037 100644 +--- a/daxctl/libdaxctl.h ++++ b/daxctl/libdaxctl.h +@@ -93,12 +93,25 @@ int daxctl_memory_online_no_movable(struct daxctl_memory *mem); + dev != NULL; \ + dev = daxctl_dev_get_next(dev)) + ++#define daxctl_dev_foreach_safe(region, dev, _dev) \ ++ for (dev = daxctl_dev_get_first(region), \ ++ _dev = dev ? daxctl_dev_get_next(dev) : NULL; \ ++ dev != NULL; \ ++ dev = _dev, \ ++ _dev = _dev ? daxctl_dev_get_next(_dev) : NULL) + + #define daxctl_region_foreach(ctx, region) \ + for (region = daxctl_region_get_first(ctx); \ + region != NULL; \ + region = daxctl_region_get_next(region)) + ++#define daxctl_region_foreach_safe(ctx, region, _region) \ ++ for (region = daxctl_region_get_first(ctx), \ ++ _region = region ? daxctl_region_get_next(region) : NULL; \ ++ region != NULL; \ ++ region = _region, \ ++ _region = _region ? daxctl_region_get_next(_region) : NULL) ++ + struct daxctl_mapping; + struct daxctl_mapping *daxctl_mapping_get_first(struct daxctl_dev *dev); + struct daxctl_mapping *daxctl_mapping_get_next(struct daxctl_mapping *mapping); +-- +2.27.0 + diff --git a/SOURCES/0140-util-size.h-fix-build-for-older-compilers.patch b/SOURCES/0140-util-size.h-fix-build-for-older-compilers.patch new file mode 100644 index 0000000..3ddc3e4 --- /dev/null +++ b/SOURCES/0140-util-size.h-fix-build-for-older-compilers.patch @@ -0,0 +1,216 @@ +From 10653a171bc0ca145236d2c75e5c5422caeb8b55 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Wed, 23 Feb 2022 22:28:05 -0700 +Subject: [PATCH 140/217] util/size.h: fix build for older compilers + +Add a fallback for older compilers that lack __builtin_add_overflow() +and friends. Commit 7aa7c7be6e80 ("util: add the struct_size() helper from the +kernel") which added these helpers from the kernel neglected to copy +over the fallback code. + +Link: https://lore.kernel.org/r/20220224052805.2462449-1-vishal.l.verma@intel.com +Fixes: 7aa7c7be6e80 ("util: add the struct_size() helper from the kernel") +Reported-by: Joao Martins +Reviewed-by: Joao Martins +Signed-off-by: Vishal Verma +--- + util/size.h | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 159 insertions(+), 4 deletions(-) + +diff --git a/util/size.h b/util/size.h +index e72467f..1cb0669 100644 +--- a/util/size.h ++++ b/util/size.h +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + + #define SZ_1K 0x00000400 + #define SZ_4K 0x00001000 +@@ -43,23 +44,177 @@ static inline bool is_power_of_2(unsigned long long v) + * alias for __builtin_add_overflow, but add type checks similar to + * below. + */ +-#define check_add_overflow(a, b, d) (({ \ ++#define is_signed_type(type) (((type)(-1)) < (type)1) ++#define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - is_signed_type(type))) ++#define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T))) ++#define type_min(T) ((T)((T)-type_max(T)-(T)1)) ++ ++#if GCC_VERSION >= 50100 ++#define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1 ++#endif ++ ++#if __clang__ && \ ++ __has_builtin(__builtin_mul_overflow) && \ ++ __has_builtin(__builtin_add_overflow) ++#define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1 ++#endif ++ ++#if COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW ++ ++#define check_add_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + __builtin_add_overflow(__a, __b, __d); \ +-})) ++}) ++ ++#define check_sub_overflow(a, b, d) ({ \ ++ typeof(a) __a = (a); \ ++ typeof(b) __b = (b); \ ++ typeof(d) __d = (d); \ ++ (void) (&__a == &__b); \ ++ (void) (&__a == __d); \ ++ __builtin_sub_overflow(__a, __b, __d); \ ++}) + +-#define check_mul_overflow(a, b, d) (({ \ ++#define check_mul_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + __builtin_mul_overflow(__a, __b, __d); \ +-})) ++}) ++ ++ ++#else /* !COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW */ ++ ++/* Checking for unsigned overflow is relatively easy without causing UB. */ ++#define __unsigned_add_overflow(a, b, d) ({ \ ++ typeof(a) __a = (a); \ ++ typeof(b) __b = (b); \ ++ typeof(d) __d = (d); \ ++ (void) (&__a == &__b); \ ++ (void) (&__a == __d); \ ++ *__d = __a + __b; \ ++ *__d < __a; \ ++}) ++#define __unsigned_sub_overflow(a, b, d) ({ \ ++ typeof(a) __a = (a); \ ++ typeof(b) __b = (b); \ ++ typeof(d) __d = (d); \ ++ (void) (&__a == &__b); \ ++ (void) (&__a == __d); \ ++ *__d = __a - __b; \ ++ __a < __b; \ ++}) ++/* ++ * If one of a or b is a compile-time constant, this avoids a division. ++ */ ++#define __unsigned_mul_overflow(a, b, d) ({ \ ++ typeof(a) __a = (a); \ ++ typeof(b) __b = (b); \ ++ typeof(d) __d = (d); \ ++ (void) (&__a == &__b); \ ++ (void) (&__a == __d); \ ++ *__d = __a * __b; \ ++ __builtin_constant_p(__b) ? \ ++ __b > 0 && __a > type_max(typeof(__a)) / __b : \ ++ __a > 0 && __b > type_max(typeof(__b)) / __a; \ ++}) ++ ++/* ++ * For signed types, detecting overflow is much harder, especially if ++ * we want to avoid UB. But the interface of these macros is such that ++ * we must provide a result in *d, and in fact we must produce the ++ * result promised by gcc's builtins, which is simply the possibly ++ * wrapped-around value. Fortunately, we can just formally do the ++ * operations in the widest relevant unsigned type (u64) and then ++ * truncate the result - gcc is smart enough to generate the same code ++ * with and without the (u64) casts. ++ */ ++ ++/* ++ * Adding two signed integers can overflow only if they have the same ++ * sign, and overflow has happened iff the result has the opposite ++ * sign. ++ */ ++#define __signed_add_overflow(a, b, d) ({ \ ++ typeof(a) __a = (a); \ ++ typeof(b) __b = (b); \ ++ typeof(d) __d = (d); \ ++ (void) (&__a == &__b); \ ++ (void) (&__a == __d); \ ++ *__d = (u64)__a + (u64)__b; \ ++ (((~(__a ^ __b)) & (*__d ^ __a)) \ ++ & type_min(typeof(__a))) != 0; \ ++}) ++ ++/* ++ * Subtraction is similar, except that overflow can now happen only ++ * when the signs are opposite. In this case, overflow has happened if ++ * the result has the opposite sign of a. ++ */ ++#define __signed_sub_overflow(a, b, d) ({ \ ++ typeof(a) __a = (a); \ ++ typeof(b) __b = (b); \ ++ typeof(d) __d = (d); \ ++ (void) (&__a == &__b); \ ++ (void) (&__a == __d); \ ++ *__d = (u64)__a - (u64)__b; \ ++ ((((__a ^ __b)) & (*__d ^ __a)) \ ++ & type_min(typeof(__a))) != 0; \ ++}) ++ ++/* ++ * Signed multiplication is rather hard. gcc always follows C99, so ++ * division is truncated towards 0. This means that we can write the ++ * overflow check like this: ++ * ++ * (a > 0 && (b > MAX/a || b < MIN/a)) || ++ * (a < -1 && (b > MIN/a || b < MAX/a) || ++ * (a == -1 && b == MIN) ++ * ++ * The redundant casts of -1 are to silence an annoying -Wtype-limits ++ * (included in -Wextra) warning: When the type is u8 or u16, the ++ * __b_c_e in check_mul_overflow obviously selects ++ * __unsigned_mul_overflow, but unfortunately gcc still parses this ++ * code and warns about the limited range of __b. ++ */ ++ ++#define __signed_mul_overflow(a, b, d) ({ \ ++ typeof(a) __a = (a); \ ++ typeof(b) __b = (b); \ ++ typeof(d) __d = (d); \ ++ typeof(a) __tmax = type_max(typeof(a)); \ ++ typeof(a) __tmin = type_min(typeof(a)); \ ++ (void) (&__a == &__b); \ ++ (void) (&__a == __d); \ ++ *__d = (u64)__a * (u64)__b; \ ++ (__b > 0 && (__a > __tmax/__b || __a < __tmin/__b)) || \ ++ (__b < (typeof(__b))-1 && (__a > __tmin/__b || __a < __tmax/__b)) || \ ++ (__b == (typeof(__b))-1 && __a == __tmin); \ ++}) ++ ++ ++#define check_add_overflow(a, b, d) \ ++ __builtin_choose_expr(is_signed_type(typeof(a)), \ ++ __signed_add_overflow(a, b, d), \ ++ __unsigned_add_overflow(a, b, d)) ++ ++#define check_sub_overflow(a, b, d) \ ++ __builtin_choose_expr(is_signed_type(typeof(a)), \ ++ __signed_sub_overflow(a, b, d), \ ++ __unsigned_sub_overflow(a, b, d)) ++ ++#define check_mul_overflow(a, b, d) \ ++ __builtin_choose_expr(is_signed_type(typeof(a)), \ ++ __signed_mul_overflow(a, b, d), \ ++ __unsigned_mul_overflow(a, b, d)) ++ ++#endif + + /* + * Compute a*b+c, returning SIZE_MAX on overflow. Internal helper for +-- +2.27.0 + diff --git a/SOURCES/0141-build-Automate-rpmbuild.sh.patch b/SOURCES/0141-build-Automate-rpmbuild.sh.patch new file mode 100644 index 0000000..efba327 --- /dev/null +++ b/SOURCES/0141-build-Automate-rpmbuild.sh.patch @@ -0,0 +1,49 @@ +From 55f36387ee8a88c489863103347ae275b1bc9191 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 23 Feb 2022 18:41:45 -0800 +Subject: [PATCH 141/217] build: Automate rpmbuild.sh + +Prior to the meson conversion rpmbuild.sh with no arguments would find a +pre-created ndctl.spec file relative to the script. Restore that +behavior by looking for the script in the build/ directory, and try to +create it if not there. + +Yes, this fails if someone picks a directory other than build/ for the +output directory, but build/ is conventional. + +Another regression from autotools is the loss of support for building +"dirty" rpms i.e. rpms from git source trees with uncommitted changes. +At least provide a coherent error message for that case. + +Link: https://lore.kernel.org/r/164567050589.2266739.68846452427328787.stgit@dwillia2-desk3.amr.corp.intel.com +Reported-by: Jane Chu +Reported-by: Vishal Verma +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + rpmbuild.sh | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/rpmbuild.sh b/rpmbuild.sh +index b1f4d9e..d9823e5 100755 +--- a/rpmbuild.sh ++++ b/rpmbuild.sh +@@ -4,6 +4,15 @@ spec=${1:-$(dirname $0)/rhel/ndctl.spec)} + + pushd $(dirname $0) >/dev/null + [ ! -d ~/rpmbuild/SOURCES ] && echo "rpmdev tree not found" && exit 1 ++if ./git-version | grep -q dirty; then ++ echo "Uncommitted changes detected, commit or undo them to proceed" ++ git status -uno --short ++ exit 1 ++fi ++if [ ! -f $spec ]; then ++ meson compile -C build rhel/ndctl.spec ++ spec=$(dirname $0)/build/rhel/ndctl.spec ++fi + ./make-git-snapshot.sh + popd > /dev/null + rpmbuild --nocheck -ba $spec +-- +2.27.0 + diff --git a/SOURCES/0142-util-size.h-Fix-build-error-for-GCC-10.patch b/SOURCES/0142-util-size.h-Fix-build-error-for-GCC-10.patch new file mode 100644 index 0000000..61ac801 --- /dev/null +++ b/SOURCES/0142-util-size.h-Fix-build-error-for-GCC-10.patch @@ -0,0 +1,48 @@ +From 3b5fb8b6428dfaab39bab58d67412427f514c1f4 Mon Sep 17 00:00:00 2001 +From: Vaibhav Jain +Date: Sat, 26 Feb 2022 17:09:55 +0530 +Subject: [PATCH 142/217] util/size.h: Fix build error for GCC < 10 + +Building with GCC 8.4.1 results in following build error for 'util/size.c': + +../util/size.h:57:16: error: missing binary operator before token "(" + __has_builtin(__builtin_mul_overflow) && \ + +This is caused due to missing '__has_builtin' preprocessor operator in GCC +versions < 10.0.0. The patch updates the check for CLANG's availability of +__builtin_{mul,add}_overflow to prevent preprocessor from evaluating the +expression "___has_builtin(__builtin_mul_overflow) && +__has_builtin(__builtin_add_overflow)". + +Link: https://lore.kernel.org/r/20220226113955.526036-1-vaibhav@linux.ibm.com +Fixes:10653a171bc0("util/size.h: fix build for older compilers") +Reported-by: Tarun Sahu +Reviewed-by: Joao Martins +Signed-off-by: Vaibhav Jain +Signed-off-by: Vishal Verma +--- + util/size.h | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/util/size.h b/util/size.h +index 1cb0669..02baa77 100644 +--- a/util/size.h ++++ b/util/size.h +@@ -53,11 +53,12 @@ static inline bool is_power_of_2(unsigned long long v) + #define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1 + #endif + +-#if __clang__ && \ +- __has_builtin(__builtin_mul_overflow) && \ ++#if __clang__ ++#if __has_builtin(__builtin_mul_overflow) && \ + __has_builtin(__builtin_add_overflow) + #define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1 + #endif ++#endif + + #if COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW + +-- +2.27.0 + diff --git a/SOURCES/0143-libcxl-Remove-extraneous-NULL-checks-when-validating.patch b/SOURCES/0143-libcxl-Remove-extraneous-NULL-checks-when-validating.patch new file mode 100644 index 0000000..dfbb32a --- /dev/null +++ b/SOURCES/0143-libcxl-Remove-extraneous-NULL-checks-when-validating.patch @@ -0,0 +1,47 @@ +From 4b381a31345930d08ab9adb87087bb765f624506 Mon Sep 17 00:00:00 2001 +From: Alison Schofield +Date: Thu, 3 Mar 2022 15:16:57 -0800 +Subject: [PATCH 143/217] libcxl: Remove extraneous NULL checks when validating + cmd status + +When a cxl_cmd_new_*() function is executed the returned command +pointer is always checked for NULL. Remove extraneous NULL checks +later in the command validation path. + +Coverity pointed these out as 'check_after_deref' issues. + +Link: https://lore.kernel.org/r/20220303231657.1053594-1-alison.schofield@intel.com +Fixes: 4f588b964dcc ("libcxl: add GET_PARTITION_INFO mailbox command and accessors") +Fixes: 50b9d77232d4 ("libcxl: add accessors for capacity fields of the IDENTIFY command") +Reviewed-by: Dan Williams +Signed-off-by: Alison Schofield +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index daa2bbc..f111d86 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -2283,8 +2283,6 @@ cmd_to_identify(struct cxl_cmd *cmd) + if (cxl_cmd_validate_status(cmd, CXL_MEM_COMMAND_ID_IDENTIFY)) + return NULL; + +- if (!cmd) +- return NULL; + return cmd->output_payload; + } + +@@ -2429,8 +2427,6 @@ cmd_to_get_partition(struct cxl_cmd *cmd) + if (cxl_cmd_validate_status(cmd, CXL_MEM_COMMAND_ID_GET_PARTITION_INFO)) + return NULL; + +- if (!cmd) +- return NULL; + return cmd->output_payload; + } + +-- +2.27.0 + diff --git a/SOURCES/0144-libdaxctl-free-resource-allocated-with-asprintf.patch b/SOURCES/0144-libdaxctl-free-resource-allocated-with-asprintf.patch new file mode 100644 index 0000000..9398893 --- /dev/null +++ b/SOURCES/0144-libdaxctl-free-resource-allocated-with-asprintf.patch @@ -0,0 +1,35 @@ +From 057ca6fc2ce63625236bf00e795e0847e6508ed8 Mon Sep 17 00:00:00 2001 +From: Alison Schofield +Date: Thu, 3 Mar 2022 16:01:33 -0800 +Subject: [PATCH 144/217] libdaxctl: free resource allocated with asprintf() + +Static analysis reported this resource leak. + +Link: https://lore.kernel.org/r/20220304000133.1053883-1-alison.schofield@intel.com +Fixes: d07508a0cc3c ("libdaxctl: add daxctl_region_create_dev()") +Signed-off-by: Alison Schofield +Signed-off-by: Vishal Verma +--- + daxctl/lib/libdaxctl.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c +index f173bbb..5703992 100644 +--- a/daxctl/lib/libdaxctl.c ++++ b/daxctl/lib/libdaxctl.c +@@ -624,10 +624,9 @@ DAXCTL_EXPORT int daxctl_region_create_dev(struct daxctl_region *region) + } + + rc = sysfs_write_attr(ctx, path, num_devices); +- if (rc) +- return rc; ++ free(num_devices); + +- return 0; ++ return rc; + } + + DAXCTL_EXPORT int daxctl_region_destroy_dev(struct daxctl_region *region, +-- +2.27.0 + diff --git a/SOURCES/0145-cxl-list-tidy-the-error-path-in-add_cxl_decoder.patch b/SOURCES/0145-cxl-list-tidy-the-error-path-in-add_cxl_decoder.patch new file mode 100644 index 0000000..2de4130 --- /dev/null +++ b/SOURCES/0145-cxl-list-tidy-the-error-path-in-add_cxl_decoder.patch @@ -0,0 +1,51 @@ +From 367593e7b602fd490baf22a26887c09877e75c14 Mon Sep 17 00:00:00 2001 +From: Alison Schofield +Date: Thu, 3 Mar 2022 16:54:23 -0800 +Subject: [PATCH 145/217] cxl/list: tidy the error path in add_cxl_decoder() + +Static analysis reported this NULL pointer dereference during +cleanup on error in add_cxl_decoder(). + +Link: https://lore.kernel.org/r/20220304005423.1054282-1-alison.schofield@intel.com +Fixes: 46564977afb7 ("cxl/list: Add decoder support") +Signed-off-by: Alison Schofield +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index f111d86..1782f42 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -919,11 +919,11 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + + decoder->dev_path = strdup(cxldecoder_base); + if (!decoder->dev_path) +- goto err; ++ goto err_decoder; + + decoder->dev_buf = calloc(1, strlen(cxldecoder_base) + 50); + if (!decoder->dev_buf) +- goto err; ++ goto err_decoder; + decoder->buf_len = strlen(cxldecoder_base) + 50; + + sprintf(path, "%s/start", cxldecoder_base); +@@ -1024,10 +1024,12 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + list_add(&port->decoders, &decoder->list); + + return decoder; +-err: ++ ++err_decoder: + free(decoder->dev_path); + free(decoder->dev_buf); + free(decoder); ++err: + free(path); + return NULL; + } +-- +2.27.0 + diff --git a/SOURCES/0146-cxl-list-always-free-the-path-var-in-add_cxl_decoder.patch b/SOURCES/0146-cxl-list-always-free-the-path-var-in-add_cxl_decoder.patch new file mode 100644 index 0000000..6b1d419 --- /dev/null +++ b/SOURCES/0146-cxl-list-always-free-the-path-var-in-add_cxl_decoder.patch @@ -0,0 +1,38 @@ +From 7564aeeae7bc8c3813bd80676769bd11a1055ca0 Mon Sep 17 00:00:00 2001 +From: Alison Schofield +Date: Thu, 3 Mar 2022 17:36:43 -0800 +Subject: [PATCH 146/217] cxl/list: always free the path var in + add_cxl_decoder() + +Static analysis reported a resource leak where the 'path' variable was +not always freed before returns. + +Link: https://lore.kernel.org/r/20220304013643.1054605-1-alison.schofield@intel.com +Fixes: 46564977afb7 ("cxl/list: Add decoder support") +Signed-off-by: Alison Schofield +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 1782f42..59e1644 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -1018,11 +1018,13 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + cxl_decoder_foreach(port, decoder_dup) + if (decoder_dup->id == decoder->id) { + free_decoder(decoder, NULL); ++ free(path); + return decoder_dup; + } + + list_add(&port->decoders, &decoder->list); + ++ free(path); + return decoder; + + err_decoder: +-- +2.27.0 + diff --git a/SOURCES/0147-scripts-docsurgeon-Fix-document-header-for-section-1.patch b/SOURCES/0147-scripts-docsurgeon-Fix-document-header-for-section-1.patch new file mode 100644 index 0000000..a4395b3 --- /dev/null +++ b/SOURCES/0147-scripts-docsurgeon-Fix-document-header-for-section-1.patch @@ -0,0 +1,40 @@ +From 633205122bc5a54b56ac6d961f9fc4aac917b0fd Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Fri, 4 Mar 2022 13:02:51 -0700 +Subject: [PATCH 147/217] scripts/docsurgeon: Fix document header for section 1 + man pages + +Document header generation for section 1 man pages (cxl-foo commands) was +missing the section number in parenthesis, i.e. it would generate: + + cxl-foo + ======= + +instead of: + + cxl-foo(1) + ========== + +resulting in asciidoc(tor) warnings. + +Signed-off-by: Vishal Verma +--- + scripts/docsurgeon | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/scripts/docsurgeon b/scripts/docsurgeon +index ca0ad78..1421ef7 100755 +--- a/scripts/docsurgeon ++++ b/scripts/docsurgeon +@@ -244,7 +244,7 @@ gen_cli() + + # Start template generation + printf "%s\n" "$copyright_cli" > "$tmp" +- gen_header "$name" >> "$tmp" ++ gen_header "$name($_arg_section)" >> "$tmp" + gen_section_name "$name" >> "$tmp" + gen_section_synopsis_1 "$name" >> "$tmp" + gen_section "DESCRIPTION" >> "$tmp" +-- +2.27.0 + diff --git a/SOURCES/0148-ndctl-release-v73.patch b/SOURCES/0148-ndctl-release-v73.patch new file mode 100644 index 0000000..c301cf0 --- /dev/null +++ b/SOURCES/0148-ndctl-release-v73.patch @@ -0,0 +1,156 @@ +From dd58d43458943d20ff063850670bf54a5242c9c5 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Sun, 6 Mar 2022 23:39:25 -0700 +Subject: [PATCH 148/217] ndctl: release v73 + +This release incorporates functionality up to the 5.17 kernel. + +Highlights include full CXL topology walk and filtering in cxl-list, a +new cxl-cli command to set memdev partitioning info, several unit test +fixes, conversion of the build system to meson, smart error injection +enhancements for 'papr' NVDIMMs, and RPM spec fixes to support config +file migration, and flatpak builds. + +Commands: + cxl-list: full topology walk and filtering + cxl-set-partition: new command to set memdev partitions + cxl{enable,disable}-port: new commands + cxl{enable,disable}-memdev: new commands + +Tests: + Deprecate BLK aperture support + inject-smart.sh: increase tolerance for decimal formatted fields + +APIs: + ndctl_dimm_refresh_flags + cxl_bus_get_ctx + cxl_bus_get_devname + cxl_bus_get_first + cxl_bus_get_id + cxl_bus_get_next + cxl_bus_get_port + cxl_bus_get_provider + cxl_cmd_identify_get_persistent_only_size + cxl_cmd_identify_get_total_size + cxl_cmd_identify_get_volatile_only_size + cxl_cmd_new_get_partition + cxl_cmd_new_set_partition + cxl_cmd_partition_get_active_persistent_size + cxl_cmd_partition_get_active_volatile_size + cxl_cmd_partition_get_next_persistent_size + cxl_cmd_partition_get_next_volatile_size + cxl_cmd_partition_set_mode + cxl_decoder_get_ctx + cxl_decoder_get_devname + cxl_decoder_get_first + cxl_decoder_get_id + cxl_decoder_get_next + cxl_decoder_get_nr_targets + cxl_decoder_get_port + cxl_decoder_get_resource + cxl_decoder_get_size + cxl_decoder_get_target_by_memdev + cxl_decoder_get_target_by_position + cxl_decoder_get_target_type + cxl_decoder_is_accelmem_capable + cxl_decoder_is_locked + cxl_decoder_is_mem_capable + cxl_decoder_is_pmem_capable + cxl_decoder_is_volatile_capable + cxl_dport_get_devname + cxl_dport_get_first + cxl_dport_get_id + cxl_dport_get_next + cxl_dport_get_physical_node + cxl_dport_get_port + cxl_dport_maps_memdev + cxl_endpoint_get_bus + cxl_endpoint_get_ctx + cxl_endpoint_get_devname + cxl_endpoint_get_first + cxl_endpoint_get_host + cxl_endpoint_get_id + cxl_endpoint_get_memdev + cxl_endpoint_get_next + cxl_endpoint_get_parent + cxl_endpoint_get_port + cxl_endpoint_is_enabled + cxl_memdev_disable_invalidate + cxl_memdev_enable + cxl_memdev_get_bus + cxl_memdev_get_endpoint + cxl_memdev_get_host + cxl_memdev_get_numa_node + cxl_memdev_get_serial + cxl_memdev_is_enabled + cxl_port_disable_invalidate + cxl_port_enable + cxl_port_get_bus + cxl_port_get_bus + cxl_port_get_ctx + cxl_port_get_devname + cxl_port_get_dport_by_memdev + cxl_port_get_first + cxl_port_get_host + cxl_port_get_id + cxl_port_get_next + cxl_port_get_next_all + cxl_port_get_nr_dports + cxl_port_get_parent + cxl_port_hosts_memdev + cxl_port_is_enabled + cxl_port_is_endpoint + cxl_port_is_root + cxl_port_is_switch + cxl_port_to_bus + cxl_port_to_endpoint + cxl_target_get_decoder + cxl_target_get_devname + cxl_target_get_first + cxl_target_get_id + cxl_target_get_next + cxl_target_get_physical_node + cxl_target_get_position + cxl_target_maps_memdev +--- + meson.build | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/meson.build b/meson.build +index f25ec6c..42e11aa 100644 +--- a/meson.build ++++ b/meson.build +@@ -1,5 +1,5 @@ + project('ndctl', 'c', +- version : '72', ++ version : '73', + license : [ + 'GPL-2.0', + 'LGPL-2.1', +@@ -270,17 +270,17 @@ config_h = configure_file( + ) + add_project_arguments('-include', 'config.h', language : 'c') + +-LIBNDCTL_CURRENT=25 +-LIBNDCTL_REVISION=1 +-LIBNDCTL_AGE=19 ++LIBNDCTL_CURRENT=26 ++LIBNDCTL_REVISION=0 ++LIBNDCTL_AGE=20 + + LIBDAXCTL_CURRENT=6 +-LIBDAXCTL_REVISION=0 ++LIBDAXCTL_REVISION=1 + LIBDAXCTL_AGE=5 + +-LIBCXL_CURRENT=1 ++LIBCXL_CURRENT=2 + LIBCXL_REVISION=0 +-LIBCXL_AGE=0 ++LIBCXL_AGE=1 + + root_inc = include_directories(['.', 'ndctl', ]) + +-- +2.27.0 + diff --git a/SOURCES/0150-build-Fix-Wall-and-O2-warnings.patch b/SOURCES/0150-build-Fix-Wall-and-O2-warnings.patch new file mode 100644 index 0000000..ee212ab --- /dev/null +++ b/SOURCES/0150-build-Fix-Wall-and-O2-warnings.patch @@ -0,0 +1,49 @@ +From cbe337d6a0ee7d4f0ff9a5c19d14fb5e250df220 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Wed, 9 Mar 2022 15:36:52 -0800 +Subject: [PATCH 150/217] build: Fix '-Wall' and '-O2' warnings + +Stop specifying '-Wall and '-O2' in cc_flags, and rely on the buildtype +and warning_level options. Fixup the '-D_FORTIFY_SOURCE=2' option to +optionally be enabled for optimizated builds rather then forcing -O2. + +Link: https://github.com/pmem/ndctl/issues/195 +Link: https://lore.kernel.org/r/164686901240.2874657.8473455139820858036.stgit@dwillia2-desk3.amr.corp.intel.com +Fixes: 4e5faa1726d2 ("build: Add meson build infrastructure") +Reported-by: Steve Scargall +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + meson.build | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/meson.build b/meson.build +index 17ca65a..4dbb80d 100644 +--- a/meson.build ++++ b/meson.build +@@ -57,7 +57,6 @@ sed -e s,@VERSION@,@0@,g + '''.format(meson.project_version(), prefixdir, libdir, includedir).split() + + cc_flags = [ +- '-Wall', + '-Wchar-subscripts', + '-Wformat-security', + '-Wmissing-declarations', +@@ -70,9 +69,12 @@ cc_flags = [ + '-Wmaybe-uninitialized', + '-Wdeclaration-after-statement', + '-Wunused-result', +- '-D_FORTIFY_SOURCE=2', +- '-O2', + ] ++ ++if get_option('optimization') != '0' ++ cc_flags += [ '-D_FORTIFY_SOURCE=2' ] ++endif ++ + cc = meson.get_compiler('c') + add_project_arguments(cc.get_supported_arguments(cc_flags), language : 'c') + +-- +2.27.0 + diff --git a/SOURCES/0151-build-Fix-test-timeouts.patch b/SOURCES/0151-build-Fix-test-timeouts.patch new file mode 100644 index 0000000..9b5ea02 --- /dev/null +++ b/SOURCES/0151-build-Fix-test-timeouts.patch @@ -0,0 +1,33 @@ +From 900cfd8e062975215fb522ca47cc6239a5269628 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 10 Mar 2022 19:35:19 -0800 +Subject: [PATCH 151/217] build: Fix test timeouts + +Older versions of meson, like the version that ships in CentOS Stream +interpret a timeout of 0 as immediately fail, rather than infinite test +run. Specify a 10 minute timeout by default instead. + +Link: https://lore.kernel.org/r/164696971934.3344888.14976446737826853353.stgit@dwillia2-desk3.amr.corp.intel.com +Fixes: 4e5faa1726d2 ("build: Add meson build infrastructure") +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/meson.build | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/meson.build b/test/meson.build +index 07a5bb6..7ccd451 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -227,7 +227,7 @@ foreach t : tests + mmap, + ], + suite: t[2], +- timeout : 0, ++ timeout : 600, + env : [ + 'NDCTL=@0@'.format(ndctl_tool.full_path()), + 'DAXCTL=@0@'.format(daxctl_tool.full_path()), +-- +2.27.0 + diff --git a/SOURCES/0155-build-Move-utility-helpers-to-libutil.a.patch b/SOURCES/0155-build-Move-utility-helpers-to-libutil.a.patch new file mode 100644 index 0000000..d19ecb1 --- /dev/null +++ b/SOURCES/0155-build-Move-utility-helpers-to-libutil.a.patch @@ -0,0 +1,68 @@ +From 6d0e543f8bf01219b793cf4d7a603d3dfd7844ed Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 28 Apr 2022 15:10:05 -0700 +Subject: [PATCH 155/217] build: Move utility helpers to libutil.a + +Stop listing util/json.c and util/log.c per command, just add them to +the common libutil.a object. + +Link: https://lore.kernel.org/r/165118380572.1676208.16232543117821121022.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/meson.build | 2 -- + daxctl/meson.build | 1 - + ndctl/meson.build | 2 -- + util/meson.build | 2 ++ + 4 files changed, 2 insertions(+), 5 deletions(-) + +diff -up ndctl-71.1/cxl/meson.build.orig ndctl-71.1/cxl/meson.build +--- ndctl-71.1/cxl/meson.build.orig 2022-10-07 16:54:43.451540066 -0400 ++++ ndctl-71.1/cxl/meson.build 2022-10-07 16:55:35.330716698 -0400 +@@ -3,8 +3,6 @@ cxl_src = [ + 'list.c', + 'port.c', + 'memdev.c', +- '../util/json.c', +- '../util/log.c', + 'json.c', + 'filter.c', + ] +diff -up ndctl-71.1/daxctl/meson.build.orig ndctl-71.1/daxctl/meson.build +--- ndctl-71.1/daxctl/meson.build.orig 2022-10-07 16:54:43.333539664 -0400 ++++ ndctl-71.1/daxctl/meson.build 2022-10-07 16:55:35.330716698 -0400 +@@ -4,7 +4,6 @@ daxctl_src = [ + 'list.c', + 'migrate.c', + 'device.c', +- '../util/json.c', + 'json.c', + 'filter.c', + ] +diff -up ndctl-71.1/ndctl/meson.build.orig ndctl-71.1/ndctl/meson.build +--- ndctl-71.1/ndctl/meson.build.orig 2022-10-07 16:54:43.333539664 -0400 ++++ ndctl-71.1/ndctl/meson.build 2022-10-07 16:55:35.330716698 -0400 +@@ -6,11 +6,9 @@ ndctl_src = [ + 'check.c', + 'region.c', + 'dimm.c', +- '../util/log.c', + '../daxctl/filter.c', + 'filter.c', + 'list.c', +- '../util/json.c', + '../daxctl/json.c', + 'json.c', + 'json-smart.c', +diff -up ndctl-71.1/util/meson.build.orig ndctl-71.1/util/meson.build +--- ndctl-71.1/util/meson.build.orig 2022-10-07 16:54:43.334539667 -0400 ++++ ndctl-71.1/util/meson.build 2022-10-07 16:55:35.330716698 -0400 +@@ -2,6 +2,8 @@ util = static_library('util', [ + 'parse-options.c', + 'usage.c', + 'size.c', ++ 'json.c', ++ 'log.c', + 'main.c', + 'help.c', + 'strbuf.c', diff --git a/SOURCES/0156-util-Use-SZ_-size-macros-in-display-size.patch b/SOURCES/0156-util-Use-SZ_-size-macros-in-display-size.patch new file mode 100644 index 0000000..04592cc --- /dev/null +++ b/SOURCES/0156-util-Use-SZ_-size-macros-in-display-size.patch @@ -0,0 +1,60 @@ +From e1c629bb3e01e3df24c189c0ecabde37788139a2 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 28 Apr 2022 15:10:11 -0700 +Subject: [PATCH 156/217] util: Use SZ_ size macros in display size + +In preparation for adding "Terabyte" support, cleanup the "1024" +multiplication with the SZ_* macros. + +Link: https://lore.kernel.org/r/165118381109.1676208.8857362319985041575.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + util/json.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/util/json.c b/util/json.c +index f8cc81f..ebdf8d9 100644 +--- a/util/json.c ++++ b/util/json.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -27,24 +28,24 @@ static int display_size(struct json_object *jobj, struct printbuf *pbuf, + * If prefix == JEDEC, we mean prefixes like kilo,mega,giga etc. + */ + +- if (bytes < 5000*1024) ++ if (bytes < 5000*SZ_1K) + snprintf(buf, sizeof(buf), "%lld", bytes); + else { + /* IEC */ +- if (bytes < 2*1024LL*1024LL*1024LL) { +- long cMiB = (bytes * 200LL / (1LL<<20) +1) /2; ++ if (bytes < 2L*SZ_1G) { ++ long cMiB = (bytes * 200LL / SZ_1M+1) /2; + + c = snprintf(buf, sizeof(buf), "\"%ld.%02ld MiB", + cMiB/100 , cMiB % 100); + } else { +- long cGiB = (bytes * 200LL / (1LL<<30) +1) /2; ++ long cGiB = (bytes * 200LL / SZ_1G+1) /2; + + c = snprintf(buf, sizeof(buf), "\"%ld.%02ld GiB", + cGiB/100 , cGiB % 100); + } + + /* JEDEC */ +- if (bytes < 2*1024LL*1024LL*1024LL) { ++ if (bytes < 2L*SZ_1G) { + long cMB = (bytes / (1000000LL / 200LL) + 1) / 2; + + snprintf(buf + c, sizeof(buf) - c, " (%ld.%02ld MB)\"", +-- +2.27.0 + diff --git a/SOURCES/0157-util-Pretty-print-terabytes.patch b/SOURCES/0157-util-Pretty-print-terabytes.patch new file mode 100644 index 0000000..d00eb34 --- /dev/null +++ b/SOURCES/0157-util-Pretty-print-terabytes.patch @@ -0,0 +1,61 @@ +From e8b5b191a55b7be671abf2c6d5d10db6edd8c1fb Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 28 Apr 2022 15:10:16 -0700 +Subject: [PATCH 157/217] util: Pretty print terabytes + +CXL capacities are such that gigabytes are too small of a unit for +displaying capacities. Add terabyte support to the display_size() +helper. + +Link: https://lore.kernel.org/r/165118381648.1676208.1686584406206186723.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + util/json.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/util/json.c b/util/json.c +index ebdf8d9..1d5c6bc 100644 +--- a/util/json.c ++++ b/util/json.c +@@ -37,11 +37,16 @@ static int display_size(struct json_object *jobj, struct printbuf *pbuf, + + c = snprintf(buf, sizeof(buf), "\"%ld.%02ld MiB", + cMiB/100 , cMiB % 100); +- } else { ++ } else if (bytes < 2*SZ_1T) { + long cGiB = (bytes * 200LL / SZ_1G+1) /2; + + c = snprintf(buf, sizeof(buf), "\"%ld.%02ld GiB", + cGiB/100 , cGiB % 100); ++ } else { ++ long cTiB = (bytes * 200LL / SZ_1T+1) /2; ++ ++ c = snprintf(buf, sizeof(buf), "\"%ld.%02ld TiB", ++ cTiB/100 , cTiB % 100); + } + + /* JEDEC */ +@@ -50,12 +55,18 @@ static int display_size(struct json_object *jobj, struct printbuf *pbuf, + + snprintf(buf + c, sizeof(buf) - c, " (%ld.%02ld MB)\"", + cMB/100, cMB % 100); +- } else { ++ } else if (bytes < 2*SZ_1T) { + long cGB = (bytes / (1000000000LL/200LL) + 1) / 2; + + snprintf(buf + c, sizeof(buf) - c, " (%ld.%02ld GB)\"", + cGB/100 , cGB % 100); ++ } else { ++ long cTB = (bytes / (1000000000000LL/200LL) + 1) / 2; ++ ++ snprintf(buf + c, sizeof(buf) - c, " (%ld.%02ld TB)\"", ++ cTB/100 , cTB % 100); + } ++ + } + + return printbuf_memappend(pbuf, buf, strlen(buf)); +-- +2.27.0 + diff --git a/SOURCES/0158-cxl-port-Fix-disable-port-man-page.patch b/SOURCES/0158-cxl-port-Fix-disable-port-man-page.patch new file mode 100644 index 0000000..6d559b8 --- /dev/null +++ b/SOURCES/0158-cxl-port-Fix-disable-port-man-page.patch @@ -0,0 +1,49 @@ +From 3ead5b8c5464c208cdbf11c0844b519518845e3e Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 28 Apr 2022 15:10:22 -0700 +Subject: [PATCH 158/217] cxl/port: Fix disable-port man page + +The man page was copied from the enable-port. Fix up some enable-port +leftovers, and duplicated --endpoint option description. + +Link: https://lore.kernel.org/r/165118382203.1676208.17234717366569348622.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-disable-port.txt | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/Documentation/cxl/cxl-disable-port.txt b/Documentation/cxl/cxl-disable-port.txt +index de13c07..ac56f20 100644 +--- a/Documentation/cxl/cxl-disable-port.txt ++++ b/Documentation/cxl/cxl-disable-port.txt +@@ -5,7 +5,7 @@ cxl-disable-port(1) + + NAME + ---- +-cxl-disable-port - activate / hot-add a given CXL port ++cxl-disable-port - disable / hot-remove a given CXL port and descendants + + SYNOPSIS + -------- +@@ -22,7 +22,6 @@ OPTIONS + Toggle from treating the port arguments as Switch Port identifiers to + Endpoint Port identifiers. + +- + -f:: + --force:: + DANGEROUS: Override the safety measure that blocks attempts to disable a +@@ -31,9 +30,6 @@ OPTIONS + firmware and disabling an active device is akin to force removing memory + from a running system. + +- Toggle from treating the port arguments as Switch Port identifiers to +- Endpoint Port identifiers. +- + --debug:: + If the cxl tool was built with debug disabled, turn on debug + messages. +-- +2.27.0 + diff --git a/SOURCES/0159-cxl-bus-Add-bus-disable-support.patch b/SOURCES/0159-cxl-bus-Add-bus-disable-support.patch new file mode 100644 index 0000000..48ccaa7 --- /dev/null +++ b/SOURCES/0159-cxl-bus-Add-bus-disable-support.patch @@ -0,0 +1,384 @@ +From 8a35aa8fd3e1db06228329a0ca900ce246ca329e Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 28 Apr 2022 15:10:27 -0700 +Subject: [PATCH 159/217] cxl/bus: Add bus disable support + +Route requests to disable the root back to unbinding the platform firmware +device, ACPI0017 for ACPI.CXL platforms. + +Link: https://lore.kernel.org/r/165118382738.1676208.16851880881648171660.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-disable-bus.txt | 37 ++++++ + Documentation/cxl/lib/libcxl.txt | 12 ++ + Documentation/cxl/meson.build | 1 + + cxl/builtin.h | 1 + + cxl/bus.c | 159 ++++++++++++++++++++++++++ + cxl/cxl.c | 1 + + cxl/filter.c | 3 +- + cxl/filter.h | 1 + + cxl/lib/libcxl.c | 15 +++ + cxl/lib/libcxl.sym | 1 + + cxl/libcxl.h | 1 + + cxl/meson.build | 1 + + 12 files changed, 231 insertions(+), 2 deletions(-) + create mode 100644 Documentation/cxl/cxl-disable-bus.txt + create mode 100644 cxl/bus.c + +diff --git a/Documentation/cxl/cxl-disable-bus.txt b/Documentation/cxl/cxl-disable-bus.txt +new file mode 100644 +index 0000000..65f695c +--- /dev/null ++++ b/Documentation/cxl/cxl-disable-bus.txt +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-disable-bus(1) ++=================== ++ ++NAME ++---- ++cxl-disable-bus - Shutdown an entire tree of CXL devices ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl disable-bus' [..] [] ++ ++For test and debug scenarios, disable a CXL bus and any associated ++memory devices from CXL.mem operations. ++ ++OPTIONS ++------- ++-f:: ++--force:: ++ DANGEROUS: Override the safety measure that blocks attempts to disable a ++ bus if the tool determines a descendent memdev is in active usage. ++ Recall that CXL memory ranges might have been established by platform ++ firmware and disabling an active device is akin to force removing memory ++ from a running system. ++ ++--debug:: ++ If the cxl tool was built with debug disabled, turn on debug ++ messages. ++ ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-disable-port[1] +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 7b223cb..f8f0e66 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -216,6 +216,18 @@ discovery order. The possible provider names are 'ACPI.CXL' and + the kernel device names that are subject to change based on discovery + order. + ++=== BUS: Control ++---- ++int cxl_bus_disable_invalidate(struct cxl_bus *bus); ++---- ++ ++An entire CXL topology can be torn down with this API. Like other ++_invalidate APIs callers must assume that all library objects have been ++freed. This one goes one step further and also frees the @bus argument. ++This may crash the system and is only useful in kernel driver ++development scenarios. ++ ++ + PORTS + ----- + CXL ports track the PCIe hierarchy between a platform firmware CXL root +diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build +index e927644..974a5a4 100644 +--- a/Documentation/cxl/meson.build ++++ b/Documentation/cxl/meson.build +@@ -34,6 +34,7 @@ cxl_manpages = [ + 'cxl-disable-memdev.txt', + 'cxl-enable-port.txt', + 'cxl-disable-port.txt', ++ 'cxl-disable-bus.txt', + 'cxl-set-partition.txt', + ] + +diff --git a/cxl/builtin.h b/cxl/builtin.h +index 7bbad98..a437bc3 100644 +--- a/cxl/builtin.h ++++ b/cxl/builtin.h +@@ -15,4 +15,5 @@ int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_disable_bus(int argc, const char **argv, struct cxl_ctx *ctx); + #endif /* _CXL_BUILTIN_H_ */ +diff --git a/cxl/bus.c b/cxl/bus.c +new file mode 100644 +index 0000000..3321295 +--- /dev/null ++++ b/cxl/bus.c +@@ -0,0 +1,159 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2020-2022 Intel Corporation. All rights reserved. */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "filter.h" ++ ++static struct parameters { ++ bool debug; ++ bool force; ++} param; ++ ++static struct log_ctx bl; ++ ++#define BASE_OPTIONS() \ ++OPT_BOOLEAN(0, "debug", ¶m.debug, "turn on debug") ++ ++#define DISABLE_OPTIONS() \ ++OPT_BOOLEAN('f', "force", ¶m.force, \ ++ "DANGEROUS: override active memdev safety checks") ++ ++static const struct option disable_options[] = { ++ BASE_OPTIONS(), ++ DISABLE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static int action_disable(struct cxl_bus *bus) ++{ ++ const char *devname = cxl_bus_get_devname(bus); ++ struct cxl_ctx *ctx = cxl_bus_get_ctx(bus); ++ struct cxl_memdev *memdev; ++ int active_memdevs = 0; ++ ++ cxl_memdev_foreach(ctx, memdev) ++ if (bus == cxl_memdev_get_bus(memdev)) ++ active_memdevs++; ++ ++ if (active_memdevs && !param.force) { ++ /* ++ * TODO: actually detect rather than assume active just ++ * because the memdev is enabled ++ */ ++ log_err(&bl, ++ "%s hosts %d memdev%s which %s part of an active region\n", ++ devname, active_memdevs, active_memdevs > 1 ? "s" : "", ++ active_memdevs > 1 ? "are" : "is"); ++ log_err(&bl, ++ "See 'cxl list -M -b %s' to see impacted device%s\n", ++ devname, active_memdevs > 1 ? "s" : ""); ++ return -EBUSY; ++ } ++ ++ return cxl_bus_disable_invalidate(bus); ++} ++ ++static struct cxl_bus *find_cxl_bus(struct cxl_ctx *ctx, const char *ident) ++{ ++ struct cxl_bus *bus; ++ ++ cxl_bus_foreach(ctx, bus) ++ if (util_cxl_bus_filter(bus, ident)) ++ return bus; ++ return NULL; ++} ++ ++static int bus_action(int argc, const char **argv, struct cxl_ctx *ctx, ++ int (*action)(struct cxl_bus *bus), ++ const struct option *options, const char *usage) ++{ ++ int i, rc = 0, count = 0, err = 0; ++ const char * const u[] = { ++ usage, ++ NULL ++ }; ++ unsigned long id; ++ ++ log_init(&bl, "cxl bus", "CXL_PORT_LOG"); ++ argc = parse_options(argc, argv, options, u, 0); ++ ++ if (argc == 0) ++ usage_with_options(u, options); ++ for (i = 0; i < argc; i++) { ++ if (strcmp(argv[i], "all") == 0) { ++ argv[0] = "all"; ++ argc = 1; ++ break; ++ } ++ ++ if (sscanf(argv[i], "root%lu", &id) == 1) ++ continue; ++ if (sscanf(argv[i], "%lu", &id) == 1) ++ continue; ++ ++ log_err(&bl, "'%s' is not a valid bus identifer\n", argv[i]); ++ err++; ++ } ++ ++ if (err == argc) { ++ usage_with_options(u, options); ++ return -EINVAL; ++ } ++ ++ if (param.debug) { ++ cxl_set_log_priority(ctx, LOG_DEBUG); ++ bl.log_priority = LOG_DEBUG; ++ } else ++ bl.log_priority = LOG_INFO; ++ ++ rc = 0; ++ err = 0; ++ count = 0; ++ ++ for (i = 0; i < argc; i++) { ++ struct cxl_bus *bus; ++ ++ bus = find_cxl_bus(ctx, argv[i]); ++ if (!bus) { ++ log_dbg(&bl, "bus: %s not found\n", argv[i]); ++ continue; ++ } ++ ++ log_dbg(&bl, "run action on bus: %s\n", ++ cxl_bus_get_devname(bus)); ++ rc = action(bus); ++ if (rc == 0) ++ count++; ++ else if (rc && !err) ++ err = rc; ++ } ++ rc = err; ++ ++ /* ++ * count if some actions succeeded, 0 if none were attempted, ++ * negative error code otherwise. ++ */ ++ if (count > 0) ++ return count; ++ return rc; ++} ++ ++ int cmd_disable_bus(int argc, const char **argv, struct cxl_ctx *ctx) ++ { ++ int count = bus_action( ++ argc, argv, ctx, action_disable, disable_options, ++ "cxl disable-bus [..] []"); ++ ++ log_info(&bl, "disabled %d bus%s\n", count >= 0 ? count : 0, ++ count > 1 ? "s" : ""); ++ return count >= 0 ? 0 : EXIT_FAILURE; ++ } +diff --git a/cxl/cxl.c b/cxl/cxl.c +index ab4bbec..aa4ce61 100644 +--- a/cxl/cxl.c ++++ b/cxl/cxl.c +@@ -69,6 +69,7 @@ static struct cmd_struct commands[] = { + { "disable-port", .c_fn = cmd_disable_port }, + { "enable-port", .c_fn = cmd_enable_port }, + { "set-partition", .c_fn = cmd_set_partition }, ++ { "disable-bus", .c_fn = cmd_disable_bus }, + }; + + int main(int argc, const char **argv) +diff --git a/cxl/filter.c b/cxl/filter.c +index b339642..c6ab9eb 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -176,8 +176,7 @@ util_cxl_decoder_filter_by_port(struct cxl_decoder *decoder, const char *ident, + return NULL; + } + +-static struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, +- const char *__ident) ++struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, const char *__ident) + { + char *ident, *save; + const char *arg; +diff --git a/cxl/filter.h b/cxl/filter.h +index 697b777..9557943 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -41,6 +41,7 @@ enum cxl_port_filter_mode { + + struct cxl_port *util_cxl_port_filter(struct cxl_port *port, const char *ident, + enum cxl_port_filter_mode mode); ++struct cxl_bus *util_cxl_bus_filter(struct cxl_bus *bus, const char *__ident); + struct cxl_endpoint *util_cxl_endpoint_filter(struct cxl_endpoint *endpoint, + const char *__ident); + struct cxl_target *util_cxl_target_filter_by_memdev(struct cxl_target *target, +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 59e1644..0e8dd20 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -556,6 +556,21 @@ static void bus_invalidate(struct cxl_bus *bus) + cxl_flush(ctx); + } + ++CXL_EXPORT int cxl_bus_disable_invalidate(struct cxl_bus *bus) ++{ ++ struct cxl_ctx *ctx = cxl_bus_get_ctx(bus); ++ struct cxl_port *port = cxl_bus_get_port(bus); ++ int rc; ++ ++ rc = util_unbind(port->uport, ctx); ++ if (rc) ++ return rc; ++ ++ free_bus(bus, &ctx->buses); ++ cxl_flush(ctx); ++ return 0; ++} ++ + CXL_EXPORT int cxl_memdev_disable_invalidate(struct cxl_memdev *memdev) + { + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index aab1112..dffcb60 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -86,6 +86,7 @@ global: + cxl_bus_get_id; + cxl_bus_get_port; + cxl_bus_get_ctx; ++ cxl_bus_disable_invalidate; + cxl_port_get_first; + cxl_port_get_next; + cxl_port_get_devname; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 0063d31..0007f4d 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -73,6 +73,7 @@ const char *cxl_bus_get_devname(struct cxl_bus *bus); + int cxl_bus_get_id(struct cxl_bus *bus); + struct cxl_port *cxl_bus_get_port(struct cxl_bus *bus); + struct cxl_ctx *cxl_bus_get_ctx(struct cxl_bus *bus); ++int cxl_bus_disable_invalidate(struct cxl_bus *bus); + + #define cxl_bus_foreach(ctx, bus) \ + for (bus = cxl_bus_get_first(ctx); bus != NULL; \ +diff --git a/cxl/meson.build b/cxl/meson.build +index 671c8e1..d63dcb1 100644 +--- a/cxl/meson.build ++++ b/cxl/meson.build +@@ -2,6 +2,7 @@ cxl_src = [ + 'cxl.c', + 'list.c', + 'port.c', ++ 'bus.c', + 'memdev.c', + 'json.c', + 'filter.c', +-- +2.27.0 + diff --git a/SOURCES/0160-cxl-list-Auto-enable-single-mode-for-port-listings.patch b/SOURCES/0160-cxl-list-Auto-enable-single-mode-for-port-listings.patch new file mode 100644 index 0000000..5afc6bf --- /dev/null +++ b/SOURCES/0160-cxl-list-Auto-enable-single-mode-for-port-listings.patch @@ -0,0 +1,37 @@ +From 2124f62aad2fcc00def36d119cfcdee22a7961e9 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 28 Apr 2022 15:10:32 -0700 +Subject: [PATCH 160/217] cxl/list: Auto-enable 'single' mode for port listings + +The --single parameter instructs the filter code to gate listing of +ancestor ports. However, that behavior can be inferred by attempts to list +a port without the --ports option, i.e. make: + + cxl list -p $port + +...equivalent to: + + cxl list -P -S -p $port + +Link: https://lore.kernel.org/r/165118383246.1676208.2097194779584921177.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/list.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/cxl/list.c b/cxl/list.c +index 1e9d441..940782d 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -104,6 +104,7 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + error("please specify entities to list, e.g. using -m/-M\n"); + usage_with_options(u, options); + } ++ param.single = true; + } + + log_init(¶m.ctx, "cxl list", "CXL_LIST_LOG"); +-- +2.27.0 + diff --git a/SOURCES/0161-cxl-memdev-Fix-bus_invalidate-crash.patch b/SOURCES/0161-cxl-memdev-Fix-bus_invalidate-crash.patch new file mode 100644 index 0000000..be34f7c --- /dev/null +++ b/SOURCES/0161-cxl-memdev-Fix-bus_invalidate-crash.patch @@ -0,0 +1,50 @@ +From 2dbe3b45879ad614968a75392cb1ef9907d8648d Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 28 Apr 2022 15:10:37 -0700 +Subject: [PATCH 161/217] cxl/memdev: Fix bus_invalidate() crash + +bus_invalidate() attempts to limit the invalidation of memdevs to a single +bus scope. However, the ordering of bus_invalidate() leads to a use after +free. Unconditionally invalidate memdevs (disconnect memdevs from their +endpoints) and resotre on next lookup. Otherwise the following command +results in the following backtrace with cxl_test: + + cxl disable-memdev 5,1 --force + +#2 0x00007ffff7fb97d4 in snprintf (__fmt=0x7ffff7fbc3ed "%s/driver", __n=98, + __s=0x574d545619f7bae2 ) + at /usr/include/bits/stdio2.h:71 +#3 cxl_port_is_enabled (port=port@entry=0x422eb0) at ../cxl/lib/libcxl.c:1379 +#4 0x00007ffff7fb99a9 in cxl_port_get_bus (port=0x422eb0) at ../cxl/lib/libcxl.c:1339 +#5 0x00007ffff7fba3d0 in bus_invalidate (bus=bus@entry=0x421740) at ../cxl/lib/libcxl.c:549 +#6 0x00007ffff7fba4e7 in cxl_memdev_disable_invalidate (memdev=0x416fd0) at ../cxl/lib/libcxl.c:596 +#7 0x000000000040624e in memdev_action (argc=, argv=, ctx=0x4152a0, + action=action@entry=0x406b70 , options=options@entry=0x40fca0 , + usage=usage@entry=0x40f4b0 "cxl disable-memdev [..] []") + at ../cxl/memdev.c:506 +#8 0x0000000000406d57 in cmd_disable_memdev (argc=, argv=, + +Link: https://lore.kernel.org/r/165118383756.1676208.5717187278816036969.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 0e8dd20..374b0f1 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -546,8 +546,7 @@ static void bus_invalidate(struct cxl_bus *bus) + * indeterminate, delete them all and start over. + */ + cxl_memdev_foreach(ctx, memdev) +- if (cxl_memdev_get_bus(memdev) == bus) +- memdev->endpoint = NULL; ++ memdev->endpoint = NULL; + + bus_port = cxl_bus_get_port(bus); + list_for_each_safe(&bus_port->child_ports, port, _p, list) +-- +2.27.0 + diff --git a/SOURCES/0162-cxl-list-Add-support-for-filtering-by-host-identifie.patch b/SOURCES/0162-cxl-list-Add-support-for-filtering-by-host-identifie.patch new file mode 100644 index 0000000..41d4ac3 --- /dev/null +++ b/SOURCES/0162-cxl-list-Add-support-for-filtering-by-host-identifie.patch @@ -0,0 +1,73 @@ +From cf0e0586bf45fd7e4ab17ce2b234bf68995daca3 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 28 Apr 2022 15:10:42 -0700 +Subject: [PATCH 162/217] cxl/list: Add support for filtering by host + identifiers + +Accept host device names as valid filters for memdevs, ports, and +endpoints. + +# cxl list -u -m 7 +{ + "memdev":"mem7", + "pmem_size":"256.00 MiB (268.44 MB)", + "ram_size":"256.00 MiB (268.44 MB)", + "serial":"0x6", + "numa_node":0, + "host":"cxl_mem.6" +} + +# cxl list -u -m cxl_mem.6 +{ + "memdev":"mem7", + "pmem_size":"256.00 MiB (268.44 MB)", + "ram_size":"256.00 MiB (268.44 MB)", + "serial":"0x6", + "numa_node":0, + "host":"cxl_mem.6" +} + +Link: https://lore.kernel.org/r/165118384289.1676208.4779565283924668304.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/filter.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/cxl/filter.c b/cxl/filter.c +index c6ab9eb..66fd742 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -73,6 +73,9 @@ struct cxl_endpoint *util_cxl_endpoint_filter(struct cxl_endpoint *endpoint, + + if (strcmp(arg, cxl_endpoint_get_devname(endpoint)) == 0) + break; ++ ++ if (strcmp(arg, cxl_endpoint_get_host(endpoint)) == 0) ++ break; + } + + free(ident); +@@ -116,6 +119,9 @@ static struct cxl_port *__util_cxl_port_filter(struct cxl_port *port, + + if (strcmp(arg, cxl_port_get_devname(port)) == 0) + break; ++ ++ if (strcmp(arg, cxl_port_get_host(port)) == 0) ++ break; + } + + free(ident); +@@ -303,6 +309,9 @@ struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + + if (strcmp(name, cxl_memdev_get_devname(memdev)) == 0) + break; ++ ++ if (strcmp(name, cxl_memdev_get_host(memdev)) == 0) ++ break; + } + + free(ident); +-- +2.27.0 + diff --git a/SOURCES/0163-cxl-port-Relax-port-identifier-validation.patch b/SOURCES/0163-cxl-port-Relax-port-identifier-validation.patch new file mode 100644 index 0000000..7d90528 --- /dev/null +++ b/SOURCES/0163-cxl-port-Relax-port-identifier-validation.patch @@ -0,0 +1,92 @@ +From e32631009a97e4cb72e4afb8eec09f89c3317eb9 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 28 Apr 2022 15:10:48 -0700 +Subject: [PATCH 163/217] cxl/port: Relax port identifier validation + +Now that util_cxl_port_filter() accepts port host identifiers it is no +longer possible to pre-validate that the port arguments follow the "port%d" +format. Instead, let all inputs through and warn if the filter fails to +identify a port. + +Link: https://lore.kernel.org/r/165118384845.1676208.7570620216888371408.stgit@dwillia2-desk3.amr.corp.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/port.c | 30 ++++-------------------------- + 1 file changed, 4 insertions(+), 26 deletions(-) + +diff --git a/cxl/port.c b/cxl/port.c +index 46a8f32..89f3916 100644 +--- a/cxl/port.c ++++ b/cxl/port.c +@@ -145,7 +145,6 @@ static int port_action(int argc, const char **argv, struct cxl_ctx *ctx, + usage, + NULL + }; +- unsigned long id; + + log_init(&pl, "cxl port", "CXL_PORT_LOG"); + argc = parse_options(argc, argv, options, u, 0); +@@ -153,31 +152,10 @@ static int port_action(int argc, const char **argv, struct cxl_ctx *ctx, + if (argc == 0) + usage_with_options(u, options); + for (i = 0; i < argc; i++) { +- const char *fmt; +- + if (strcmp(argv[i], "all") == 0) { + argc = 1; + break; + } +- +- if (param.endpoint) +- fmt = "endpoint%lu"; +- else +- fmt = "port%lu"; +- +- if (sscanf(argv[i], fmt, &id) == 1) +- continue; +- if (sscanf(argv[i], "%lu", &id) == 1) +- continue; +- +- log_err(&pl, "'%s' is not a valid %s identifer\n", argv[i], +- param.endpoint ? "endpoint" : "port"); +- err++; +- } +- +- if (err == argc) { +- usage_with_options(u, options); +- return -EINVAL; + } + + if (param.debug) { +@@ -187,7 +165,6 @@ static int port_action(int argc, const char **argv, struct cxl_ctx *ctx, + pl.log_priority = LOG_INFO; + + rc = 0; +- err = 0; + count = 0; + + for (i = 0; i < argc; i++) { +@@ -198,15 +175,16 @@ static int port_action(int argc, const char **argv, struct cxl_ctx *ctx, + + endpoint = find_cxl_endpoint(ctx, argv[i]); + if (!endpoint) { +- log_dbg(&pl, "endpoint: %s not found\n", +- argv[i]); ++ log_notice(&pl, "endpoint: %s not found\n", ++ argv[i]); + continue; + } + port = cxl_endpoint_get_port(endpoint); + } else { + port = find_cxl_port(ctx, argv[i]); + if (!port) { +- log_dbg(&pl, "port: %s not found\n", argv[i]); ++ log_notice(&pl, "port: %s not found\n", ++ argv[i]); + continue; + } + } +-- +2.27.0 + diff --git a/SOURCES/0165-cxl-test-Add-topology-enumeration-and-hotplug-test.patch b/SOURCES/0165-cxl-test-Add-topology-enumeration-and-hotplug-test.patch new file mode 100644 index 0000000..c45213e --- /dev/null +++ b/SOURCES/0165-cxl-test-Add-topology-enumeration-and-hotplug-test.patch @@ -0,0 +1,236 @@ +From ef85ab79e7a45d19ca329c6e4cad9881a5a904e9 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 28 Apr 2022 15:10:54 -0700 +Subject: [PATCH 165/217] cxl/test: Add topology enumeration and hotplug test + +Test the re-plug of memdevs, switch ports, root ports, and bus objects. + +Link: https://lore.kernel.org/r/165118385401.1676208.9224280236045777443.stgit@dwillia2-desk3.amr.corp.intel.com +Cc: Luis Chamberlain +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/common | 12 ++++ + test/cxl-topology.sh | 166 +++++++++++++++++++++++++++++++++++++++++++ + test/meson.build | 2 + + 3 files changed, 180 insertions(+) + create mode 100644 test/cxl-topology.sh + +diff --git a/test/common b/test/common +index fb48795..65615cc 100644 +--- a/test/common ++++ b/test/common +@@ -27,6 +27,18 @@ if [ -z $DAXCTL ]; then + fi + fi + ++# CXL ++if [ -z $CXL ]; then ++ if [ -f "../cxl/cxl" ] && [ -x "../cxl/cxl" ]; then ++ export CXL=../cxl/cxl ++ elif [ -f "./cxl/cxl" ] && [ -x "./cxl/cxl" ]; then ++ export CXL=./cxl/cxl ++ else ++ echo "Couldn't find a cxl binary" ++ exit 1 ++ fi ++fi ++ + if [ -z $TEST_PATH ]; then + export TEST_PATH=. + fi +diff --git a/test/cxl-topology.sh b/test/cxl-topology.sh +new file mode 100644 +index 0000000..ff11614 +--- /dev/null ++++ b/test/cxl-topology.sh +@@ -0,0 +1,166 @@ ++#!/bin/bash ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (C) 2022 Intel Corporation. All rights reserved. ++ ++. $(dirname $0)/common ++ ++rc=1 ++ ++set -ex ++ ++trap 'err $LINENO' ERR ++ ++check_prereq "jq" ++ ++modprobe -r cxl_test ++modprobe cxl_test ++udevadm settle ++ ++# THEORY OF OPERATION: Validate the hard coded assumptions of the ++# cxl_test.ko module that defines its topology in ++# tools/testing/cxl/test/cxl.c. If that model ever changes then the ++# paired update must be made to this test. ++ ++# collect cxl_test root device id ++json=$($CXL list -b cxl_test) ++count=$(jq "length" <<< $json) ++((count == 1)) || err "$LINENO" ++root=$(jq -r ".[] | .bus" <<< $json) ++ ++ ++# validate 2 host bridges under a root port ++port_sort="sort_by(.port | .[4:] | tonumber)" ++json=$($CXL list -b cxl_test -BP) ++count=$(jq ".[] | .[\"ports:$root\"] | length" <<< $json) ++((count == 2)) || err "$LINENO" ++ ++bridge[0]=$(jq -r ".[] | .[\"ports:$root\"] | $port_sort | .[0].port" <<< $json) ++bridge[1]=$(jq -r ".[] | .[\"ports:$root\"] | $port_sort | .[1].port" <<< $json) ++ ++ ++# validate 2 root ports per host bridge ++json=$($CXL list -b cxl_test -T -p ${bridge[0]}) ++count=$(jq ".[] | .dports | length" <<< $json) ++((count == 2)) || err "$LINENO" ++ ++json=$($CXL list -b cxl_test -T -p ${bridge[1]}) ++count=$(jq ".[] | .dports | length" <<< $json) ++((count == 2)) || err "$LINENO" ++ ++ ++# validate 2 switches per-root port ++json=$($CXL list -b cxl_test -P -p ${bridge[0]}) ++count=$(jq ".[] | .[\"ports:${bridge[0]}\"] | length" <<< $json) ++((count == 2)) || err "$LINENO" ++ ++switch[0]=$(jq -r ".[] | .[\"ports:${bridge[0]}\"] | $port_sort | .[0].host" <<< $json) ++switch[1]=$(jq -r ".[] | .[\"ports:${bridge[0]}\"] | $port_sort | .[1].host" <<< $json) ++ ++json=$($CXL list -b cxl_test -P -p ${bridge[1]}) ++count=$(jq ".[] | .[\"ports:${bridge[1]}\"] | length" <<< $json) ++((count == 2)) || err "$LINENO" ++ ++switch[2]=$(jq -r ".[] | .[\"ports:${bridge[1]}\"] | $port_sort | .[0].host" <<< $json) ++switch[3]=$(jq -r ".[] | .[\"ports:${bridge[1]}\"] | $port_sort | .[1].host" <<< $json) ++ ++ ++# check that all 8 cxl_test memdevs are enabled by default and have a ++# pmem size of 256M ++json=$($CXL list -b cxl_test -M) ++count=$(jq "map(select(.pmem_size == $((256 << 20)))) | length" <<< $json) ++((count == 8)) || err "$LINENO" ++ ++ ++# validate the expected properties of the 4 root decoders ++json=$($CXL list -b cxl_test -D -d root) ++port_id=${root:4} ++port_id_len=${#port_id} ++decoder_sort="sort_by(.decoder | .[$((8+port_id_len)):] | tonumber)" ++count=$(jq "[ $decoder_sort | .[0] | ++ select(.volatile_capable == true) | ++ select(.size == $((256 << 20))) | ++ select(.nr_targets == 1) ] | length" <<< $json) ++((count == 1)) || err "$LINENO" ++ ++count=$(jq "[ $decoder_sort | .[1] | ++ select(.volatile_capable == true) | ++ select(.size == $((512 << 20))) | ++ select(.nr_targets == 2) ] | length" <<< $json) ++((count == 1)) || err "$LINENO" ++ ++count=$(jq "[ $decoder_sort | .[2] | ++ select(.pmem_capable == true) | ++ select(.size == $((256 << 20))) | ++ select(.nr_targets == 1) ] | length" <<< $json) ++((count == 1)) || err "$LINENO" ++ ++count=$(jq "[ $decoder_sort | .[3] | ++ select(.pmem_capable == true) | ++ select(.size == $((512 << 20))) | ++ select(.nr_targets == 2) ] | length" <<< $json) ++((count == 1)) || err "$LINENO" ++ ++# check that switch ports disappear after all of their memdevs have been ++# disabled, and return when the memdevs are enabled. ++for s in ${switch[@]} ++do ++ json=$($CXL list -M -p $s) ++ count=$(jq "length" <<< $json) ++ ((count == 2)) || err "$LINENO" ++ ++ mem[0]=$(jq -r ".[0] | .memdev" <<< $json) ++ mem[1]=$(jq -r ".[1] | .memdev" <<< $json) ++ ++ $CXL disable-memdev ${mem[0]} --force ++ json=$($CXL list -p $s) ++ count=$(jq "length" <<< $json) ++ ((count == 1)) || err "$LINENO" ++ ++ $CXL disable-memdev ${mem[1]} --force ++ json=$($CXL list -p $s) ++ count=$(jq "length" <<< $json) ++ ((count == 0)) || err "$LINENO" ++ ++ $CXL enable-memdev ${mem[0]} ++ $CXL enable-memdev ${mem[1]} ++ ++ json=$($CXL list -p $s) ++ count=$(jq "length" <<< $json) ++ ((count == 1)) || err "$LINENO" ++ ++ $CXL disable-port $s --force ++ json=$($CXL list -p $s) ++ count=$(jq "length" <<< $json) ++ ((count == 0)) || err "$LINENO" ++ ++ $CXL enable-memdev ${mem[0]} ${mem[1]} ++ json=$($CXL list -p $s) ++ count=$(jq "length" <<< $json) ++ ((count == 1)) || err "$LINENO" ++done ++ ++ ++# validate host bridge tear down ++for b in ${bridge[@]} ++do ++ $CXL disable-port $b -f ++ json=$($CXL list -M -i -p $b) ++ count=$(jq "map(select(.state == \"disabled\")) | length" <<< $json) ++ ((count == 4)) || err "$LINENO" ++ ++ $CXL enable-port $b -m ++ json=$($CXL list -M -p $b) ++ count=$(jq "length" <<< $json) ++ ((count == 4)) || err "$LINENO" ++done ++ ++ ++# validate that the bus can be disabled without issue ++$CXL disable-bus $root -f ++ ++ ++# validate no WARN or lockdep report during the run ++log=$(journalctl -r -k --since "-$((SECONDS+1))s") ++grep -q "Call Trace" <<< $log && err "$LINENO" ++ ++modprobe -r cxl_test +diff --git a/test/meson.build b/test/meson.build +index 7ccd451..210dcb0 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -150,6 +150,7 @@ monitor = find_program('monitor.sh') + max_extent = find_program('max_available_extent_ns.sh') + pfn_meta_errors = find_program('pfn-meta-errors.sh') + track_uuid = find_program('track-uuid.sh') ++cxl_topo = find_program('cxl-topology.sh') + + tests = [ + [ 'libndctl', libndctl, 'ndctl' ], +@@ -174,6 +175,7 @@ tests = [ + [ 'max_extent_ns', max_extent, 'ndctl' ], + [ 'pfn-meta-errors.sh', pfn_meta_errors, 'ndctl' ], + [ 'track-uuid.sh', track_uuid, 'ndctl' ], ++ [ 'cxl-topology.sh', cxl_topo, 'cxl' ], + ] + + if get_option('destructive').enabled() +-- +2.27.0 + diff --git a/SOURCES/0167-daxctl-Fix-kernel-option-typo-in-Soft-Reservation-th.patch b/SOURCES/0167-daxctl-Fix-kernel-option-typo-in-Soft-Reservation-th.patch new file mode 100644 index 0000000..459a951 --- /dev/null +++ b/SOURCES/0167-daxctl-Fix-kernel-option-typo-in-Soft-Reservation-th.patch @@ -0,0 +1,32 @@ +From 4bd100a8c97cfd9592be74af2e4699a2ab6d2f34 Mon Sep 17 00:00:00 2001 +From: Michal Suchanek +Date: Thu, 10 Mar 2022 14:30:35 +0100 +Subject: [PATCH 167/217] daxctl: Fix kernel option typo in "Soft Reservation" + theory of operation + +Link: https://lore.kernel.org/r/20220310133035.GA106666@kunlun.suse.cz +Fixes: 8f4e42c ("daxctl: Add "Soft Reservation" theory of operation") +Signed-off-by: Michal Suchanek +Signed-off-by: Vishal Verma +--- + Documentation/daxctl/daxctl-reconfigure-device.txt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/Documentation/daxctl/daxctl-reconfigure-device.txt b/Documentation/daxctl/daxctl-reconfigure-device.txt +index 385c0c5..09691d2 100644 +--- a/Documentation/daxctl/daxctl-reconfigure-device.txt ++++ b/Documentation/daxctl/daxctl-reconfigure-device.txt +@@ -91,8 +91,8 @@ details. + Outside of the NUMA performance details linked above the other method to + detect the presence of "Soft Reserved" memory is to dump /proc/iomem and + look for "Soft Reserved" ranges. If the kernel was not built with +-CONFIG_EFI_SOFTRESERVE, predates the introduction of +-CONFIG_EFI_SOFTRESERVE (v5.5), or was booted with the efi=nosoftreserve ++CONFIG_EFI_SOFT_RESERVE, predates the introduction of ++CONFIG_EFI_SOFT_RESERVE (v5.5), or was booted with the efi=nosoftreserve + command line then device-dax will not attach and the expectation is that + the memory shows up as a memory-only NUMA node. Otherwise the memory + shows up as a device-dax instance and DAXCTL(1) can be used to +-- +2.27.0 + diff --git a/SOURCES/0168-meson-make-modprobedatadir-an-option.patch b/SOURCES/0168-meson-make-modprobedatadir-an-option.patch new file mode 100644 index 0000000..f6d1551 --- /dev/null +++ b/SOURCES/0168-meson-make-modprobedatadir-an-option.patch @@ -0,0 +1,38 @@ +From 524ad09d5eda320b299468324a2f94fc1802ef00 Mon Sep 17 00:00:00 2001 +From: Michal Suchanek +Date: Thu, 10 Mar 2022 14:30:55 +0100 +Subject: [PATCH 168/217] meson: make modprobedatadir an option + +The modprobe.d directory location is hardcoded. + +Link: https://lore.kernel.org/r/20220310133055.GA106731@kunlun.suse.cz +Fixes: 4e5faa1 ("build: Add meson build infrastructure") +Signed-off-by: Michal Suchanek +Signed-off-by: Vishal Verma +--- + contrib/meson.build | 5 ++++- + meson_options.txt | 2 ++ + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff -up ndctl-71.1/contrib/meson.build.orig ndctl-71.1/contrib/meson.build +--- ndctl-71.1/contrib/meson.build.orig 2022-10-07 16:42:24.750025016 -0400 ++++ ndctl-71.1/contrib/meson.build 2022-10-07 16:42:39.374074806 -0400 +@@ -24,5 +24,8 @@ if bashcompletiondir != 'no' + install_data('ndctl', rename : 'cxl', install_dir : bashcompletiondir) + endif + +-modprobedatadir = get_option('sysconfdir') + '/modprobe.d/' ++modprobedatadir = get_option('modprobedatadir') ++if modprobedatadir == '' ++ modprobedatadir = get_option('modprobedatadir') ++endif + install_data('nvdimm-security.conf', install_dir : modprobedatadir) +diff -up ndctl-71.1/meson_options.txt.orig ndctl-71.1/meson_options.txt +--- ndctl-71.1/meson_options.txt.orig 2022-10-07 16:42:39.375074810 -0400 ++++ ndctl-71.1/meson_options.txt 2022-10-07 16:43:10.553180961 -0400 +@@ -23,3 +23,5 @@ option('pkgconfiglibdir', type : 'string + description : 'directory for standard pkg-config files') + option('bashcompletiondir', type : 'string', + description : '''${datadir}/bash-completion/completions''') ++option('modprobedatadir', type : 'string', ++ description : '''${sysconfdir}/modprobe.d/''') diff --git a/SOURCES/0169-namespace-action-Drop-more-zero-namespace-checks.patch b/SOURCES/0169-namespace-action-Drop-more-zero-namespace-checks.patch new file mode 100644 index 0000000..5186339 --- /dev/null +++ b/SOURCES/0169-namespace-action-Drop-more-zero-namespace-checks.patch @@ -0,0 +1,116 @@ +From 2f3851912624e9ede3132cd3749b4f60a348b1d4 Mon Sep 17 00:00:00 2001 +From: Michal Suchanek +Date: Thu, 10 Mar 2022 14:31:06 +0100 +Subject: [PATCH 169/217] namespace-action: Drop more zero namespace checks + +With seed namespaces caught early on with +commit 9bd2994 ("ndctl/namespace: Skip seed namespaces when processing all namespaces.") +commit 07011a3 ("ndctl/namespace: Suppress -ENXIO when processing all namespaces.") +the function-specific checks are no longer needed and can be dropped. + +Reverts commit fb13dfb ("zero_info_block: skip seed devices") +Reverts commit fe626a8 ("ndctl/namespace: Fix disable-namespace accounting relative to seed devices") + +Link: https://lore.kernel.org/r/20220310133106.GA106734@kunlun.suse.cz +Fixes: 80e0d88 ("namespace-action: Drop zero namespace checks.") +Fixes: fb13dfb ("zero_info_block: skip seed devices") +Fixes: fe626a8 ("ndctl/namespace: Fix disable-namespace accounting relative to seed devices") +Signed-off-by: Michal Suchanek +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 7 +------ + ndctl/namespace.c | 11 ++++------- + ndctl/region.c | 2 +- + 3 files changed, 6 insertions(+), 14 deletions(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index ccca8b5..110d8a5 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -4593,7 +4593,6 @@ NDCTL_EXPORT int ndctl_namespace_disable_safe(struct ndctl_namespace *ndns) + const char *bdev = NULL; + int fd, active = 0; + char path[50]; +- unsigned long long size = ndctl_namespace_get_size(ndns); + + if (pfn && ndctl_pfn_is_enabled(pfn)) + bdev = ndctl_pfn_get_block_device(pfn); +@@ -4630,11 +4629,7 @@ NDCTL_EXPORT int ndctl_namespace_disable_safe(struct ndctl_namespace *ndns) + devname); + return -EBUSY; + } else { +- if (size == 0) +- /* No disable necessary due to no capacity allocated */ +- return 1; +- else +- ndctl_namespace_disable_invalidate(ndns); ++ ndctl_namespace_disable_invalidate(ndns); + } + + return 0; +diff --git a/ndctl/namespace.c b/ndctl/namespace.c +index 257b58c..722f13a 100644 +--- a/ndctl/namespace.c ++++ b/ndctl/namespace.c +@@ -1054,9 +1054,6 @@ static int zero_info_block(struct ndctl_namespace *ndns) + void *buf = NULL, *read_buf = NULL; + char path[50]; + +- if (ndctl_namespace_get_size(ndns) == 0) +- return 1; +- + ndctl_namespace_set_raw_mode(ndns, 1); + rc = ndctl_namespace_enable(ndns); + if (rc < 0) { +@@ -1130,7 +1127,7 @@ static int namespace_prep_reconfig(struct ndctl_region *region, + } + + rc = ndctl_namespace_disable_safe(ndns); +- if (rc < 0) ++ if (rc) + return rc; + + ndctl_namespace_set_enforce_mode(ndns, NDCTL_NS_MODE_RAW); +@@ -1426,7 +1423,7 @@ static int dax_clear_badblocks(struct ndctl_dax *dax) + return -ENXIO; + + rc = ndctl_namespace_disable_safe(ndns); +- if (rc < 0) { ++ if (rc) { + error("%s: unable to disable namespace: %s\n", devname, + strerror(-rc)); + return rc; +@@ -1450,7 +1447,7 @@ static int pfn_clear_badblocks(struct ndctl_pfn *pfn) + return -ENXIO; + + rc = ndctl_namespace_disable_safe(ndns); +- if (rc < 0) { ++ if (rc) { + error("%s: unable to disable namespace: %s\n", devname, + strerror(-rc)); + return rc; +@@ -1473,7 +1470,7 @@ static int raw_clear_badblocks(struct ndctl_namespace *ndns) + return -ENXIO; + + rc = ndctl_namespace_disable_safe(ndns); +- if (rc < 0) { ++ if (rc) { + error("%s: unable to disable namespace: %s\n", devname, + strerror(-rc)); + return rc; +diff --git a/ndctl/region.c b/ndctl/region.c +index e499546..33828b0 100644 +--- a/ndctl/region.c ++++ b/ndctl/region.c +@@ -71,7 +71,7 @@ static int region_action(struct ndctl_region *region, enum device_action mode) + case ACTION_DISABLE: + ndctl_namespace_foreach(region, ndns) { + rc = ndctl_namespace_disable_safe(ndns); +- if (rc < 0) ++ if (rc) + return rc; + } + rc = ndctl_region_disable_invalidate(region); +-- +2.27.0 + diff --git a/SOURCES/0170-ndctl-dimm-Flush-invalidated-labels-after-overwrite.patch b/SOURCES/0170-ndctl-dimm-Flush-invalidated-labels-after-overwrite.patch new file mode 100644 index 0000000..5eeba2f --- /dev/null +++ b/SOURCES/0170-ndctl-dimm-Flush-invalidated-labels-after-overwrite.patch @@ -0,0 +1,94 @@ +From 8186ec87dcd1b347ab0ee27ec5e87bda8c9a67e2 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 28 Apr 2022 16:54:48 -0700 +Subject: [PATCH 170/217] ndctl/dimm: Flush invalidated labels after overwrite + +Similar to "ndctl write-labels", after "ndctl sanitize-dimm --overwrite" +the kernel may contain a cached copy of the label area that has been +invalidated by the overwrite. Toggle the enabled state of the dimm-device +to trigger the kernel to release the cached copy. + +Link: https://lore.kernel.org/all/165118817010.1772793.5101398830527716084.stgit@dwillia2-desk3.amr.corp.intel.com/ +Link: https://lore.kernel.org/r/165119008839.1783158.3766085644383173318.stgit@dwillia2-desk3.amr.corp.intel.com +Cc: Jeff Moyer +Cc: Dave Jiang +Acked-by: Jeff Moyer +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + ndctl/dimm.c | 34 ++++++++++++++++++++++------------ + 1 file changed, 22 insertions(+), 12 deletions(-) + +diff --git a/ndctl/dimm.c b/ndctl/dimm.c +index d9718a3..ac7c527 100644 +--- a/ndctl/dimm.c ++++ b/ndctl/dimm.c +@@ -354,6 +354,23 @@ static int rw_bin(FILE *f, struct ndctl_cmd *cmd, ssize_t size, + return 0; + } + ++static int revalidate_labels(struct ndctl_dimm *dimm) ++{ ++ int rc; ++ ++ /* ++ * If the dimm is already disabled the kernel is not holding a cached ++ * copy of the label space. ++ */ ++ if (!ndctl_dimm_is_enabled(dimm)) ++ return 0; ++ ++ rc = ndctl_dimm_disable(dimm); ++ if (rc) ++ return rc; ++ return ndctl_dimm_enable(dimm); ++} ++ + static int action_write(struct ndctl_dimm *dimm, struct action_context *actx) + { + struct ndctl_cmd *cmd_read, *cmd_write; +@@ -377,18 +394,10 @@ static int action_write(struct ndctl_dimm *dimm, struct action_context *actx) + + size = ndctl_cmd_cfg_read_get_size(cmd_read); + rc = rw_bin(actx->f_in, cmd_write, size, param.offset, WRITE); +- +- /* +- * If the dimm is already disabled the kernel is not holding a cached +- * copy of the label space. +- */ +- if (!ndctl_dimm_is_enabled(dimm)) +- goto out; +- +- rc = ndctl_dimm_disable(dimm); + if (rc) + goto out; +- rc = ndctl_dimm_enable(dimm); ++ ++ rc = revalidate_labels(dimm); + + out: + ndctl_cmd_unref(cmd_read); +@@ -1043,7 +1052,7 @@ static int action_security_freeze(struct ndctl_dimm *dimm, + static int action_sanitize_dimm(struct ndctl_dimm *dimm, + struct action_context *actx) + { +- int rc; ++ int rc = 0; + enum ndctl_key_type key_type; + + if (ndctl_dimm_get_security(dimm) < 0) { +@@ -1085,9 +1094,10 @@ static int action_sanitize_dimm(struct ndctl_dimm *dimm, + rc = ndctl_dimm_overwrite_key(dimm); + if (rc < 0) + return rc; ++ rc = revalidate_labels(dimm); + } + +- return 0; ++ return rc; + } + + static int action_wait_overwrite(struct ndctl_dimm *dimm, +-- +2.27.0 + diff --git a/SOURCES/0171-libcxl-fix-a-segfault-when-memdev-pmem-is-absent.patch b/SOURCES/0171-libcxl-fix-a-segfault-when-memdev-pmem-is-absent.patch new file mode 100644 index 0000000..b793e8b --- /dev/null +++ b/SOURCES/0171-libcxl-fix-a-segfault-when-memdev-pmem-is-absent.patch @@ -0,0 +1,45 @@ +From 50e7a021314aa0365c9c85a359a31f26313fe93b Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 2 Jun 2022 09:44:27 -0600 +Subject: [PATCH 171/217] libcxl: fix a segfault when memdev->pmem is absent + +A CXL memdev may not have any persistent capacity, and in this case it +is possible that a 'pmem' object never gets instantiated. Such a +scenario would cause free_pmem () to dereference a NULL pointer and +segfault. + +Fix this by only proceeding in free_pmem() if 'pmem' was valid. + +Link: https://lore.kernel.org/r/20220602154427.462852-1-vishal.l.verma@intel.com +Fixes: cd1aed6cefe8 ("libcxl: add representation for an nvdimm bridge object") +Cc: Dan Williams +Reported-by: Steven Garcia +Tested-by: Steven Garcia +Reviewed-by: Alison Schofield +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 374b0f1..c988ce2 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -49,9 +49,11 @@ struct cxl_ctx { + + static void free_pmem(struct cxl_pmem *pmem) + { +- free(pmem->dev_buf); +- free(pmem->dev_path); +- free(pmem); ++ if (pmem) { ++ free(pmem->dev_buf); ++ free(pmem->dev_path); ++ free(pmem); ++ } + } + + static void free_memdev(struct cxl_memdev *memdev, struct list_head *head) +-- +2.27.0 + diff --git a/SOURCES/0172-ndctl-bus-Handle-missing-scrub-commands-more-gracefu.patch b/SOURCES/0172-ndctl-bus-Handle-missing-scrub-commands-more-gracefu.patch new file mode 100644 index 0000000..7973f8d --- /dev/null +++ b/SOURCES/0172-ndctl-bus-Handle-missing-scrub-commands-more-gracefu.patch @@ -0,0 +1,83 @@ +From 3e17210345482ec9795f1046c766564d3b8a0795 Mon Sep 17 00:00:00 2001 +From: Tarun Sahu +Date: Mon, 2 May 2022 12:34:54 +0530 +Subject: [PATCH 172/217] ndctl/bus: Handle missing scrub commands more + gracefully + +Buses that don't have nfit support return "No such file or directory" +for start-scrub/wait-scrub command. + +Presently, non-nfit support buses do not support start-scrub/ wait-scrub +operation. This patch is to handle these commands more gracefully by +returning" Operation not supported". + +This has been tested on PPC64le lpar with nvdimm that does not support +scrub. + +Previously: + $ ./ndctl start-scrub ndbus0 + error starting scrub: No such file or directory + +Now: + $ ./ndctl start-scrub ndbus0 + error starting scrub: Operation not supported + +- Invalid ndbus + $ sudo ./ndctl start-scrub ndbus5 + error starting scrub: No such device or address + +Link: https://lore.kernel.org/r/20220502070454.179153-1-tsahu@linux.ibm.com +Tested-by: Vaibhav Jain +Reviewed-by: Vaibhav Jain +Signed-off-by: Tarun Sahu +Signed-off-by: Vishal Verma +--- + ndctl/lib/libndctl.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c +index 110d8a5..ad54f06 100644 +--- a/ndctl/lib/libndctl.c ++++ b/ndctl/lib/libndctl.c +@@ -938,10 +938,14 @@ static void *add_bus(void *parent, int id, const char *ctl_base) + if (!bus->wait_probe_path) + goto err_read; + +- sprintf(path, "%s/device/nfit/scrub", ctl_base); +- bus->scrub_path = strdup(path); +- if (!bus->scrub_path) +- goto err_read; ++ if (ndctl_bus_has_nfit(bus)) { ++ sprintf(path, "%s/device/nfit/scrub", ctl_base); ++ bus->scrub_path = strdup(path); ++ if (!bus->scrub_path) ++ goto err_read; ++ } else { ++ bus->scrub_path = NULL; ++ } + + sprintf(path, "%s/device/firmware/activate", ctl_base); + if (sysfs_read_attr(ctx, path, buf) < 0) +@@ -1377,6 +1381,9 @@ NDCTL_EXPORT int ndctl_bus_start_scrub(struct ndctl_bus *bus) + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); + int rc; + ++ if (bus->scrub_path == NULL) ++ return -EOPNOTSUPP; ++ + rc = sysfs_write_attr(ctx, bus->scrub_path, "1\n"); + + /* +@@ -1447,6 +1454,9 @@ NDCTL_EXPORT int ndctl_bus_poll_scrub_completion(struct ndctl_bus *bus, + char in_progress; + int fd = 0, rc; + ++ if (bus->scrub_path == NULL) ++ return -EOPNOTSUPP; ++ + fd = open(bus->scrub_path, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return -errno; +-- +2.27.0 + diff --git a/SOURCES/0173-util-wrapper.c-Fix-gcc-warning-in-xrealloc.patch b/SOURCES/0173-util-wrapper.c-Fix-gcc-warning-in-xrealloc.patch new file mode 100644 index 0000000..98e1e95 --- /dev/null +++ b/SOURCES/0173-util-wrapper.c-Fix-gcc-warning-in-xrealloc.patch @@ -0,0 +1,58 @@ +From bbb2cb56f08d95ecf2c7c047a33cc3dd64eb7fde Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Thu, 16 Jun 2022 13:35:29 -0600 +Subject: [PATCH 173/217] util/wrapper.c: Fix gcc warning in xrealloc() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +A GCC update (12.1.1) now produces a warning in the xrealloc() wrapper +(originally copied from git, and used in strbuf operations): + + ../util/wrapper.c: In function ‘xrealloc’: + ../util/wrapper.c:34:31: warning: pointer ‘ptr’ may be used after ‘realloc’ [-Wuse-after-free] + 34 | ret = realloc(ptr, 1); + | ^~~~~~~~~~~~~~~ + +Pull in an updated definition for xrealloc() from the git project to fix this. + +Link: https://lore.kernel.org/r/20220616193529.56513-1-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + util/wrapper.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/util/wrapper.c b/util/wrapper.c +index 026a54f..6adfde6 100644 +--- a/util/wrapper.c ++++ b/util/wrapper.c +@@ -25,15 +25,15 @@ char *xstrdup(const char *str) + + void *xrealloc(void *ptr, size_t size) + { +- void *ret = realloc(ptr, size); +- if (!ret && !size) +- ret = realloc(ptr, 1); +- if (!ret) { +- ret = realloc(ptr, size); +- if (!ret && !size) +- ret = realloc(ptr, 1); +- if (!ret) +- die("Out of memory, realloc failed"); ++ void *ret; ++ ++ if (!size) { ++ free(ptr); ++ return malloc(1); + } ++ ++ ret = realloc(ptr, size); ++ if (!ret) ++ die("Out of memory, realloc failed"); + return ret; + } +-- +2.27.0 + diff --git a/SOURCES/0174-libcxl-Fix-memory-leakage-in-cxl_port_init.patch b/SOURCES/0174-libcxl-Fix-memory-leakage-in-cxl_port_init.patch new file mode 100644 index 0000000..e572d19 --- /dev/null +++ b/SOURCES/0174-libcxl-Fix-memory-leakage-in-cxl_port_init.patch @@ -0,0 +1,31 @@ +From 29a9e9daa67e2b68bb2433c31fdbc82e86b5f0fe Mon Sep 17 00:00:00 2001 +From: Shivaprasad G Bhat +Date: Mon, 18 Jul 2022 13:53:35 +0530 +Subject: [PATCH 174/217] libcxl: Fix memory leakage in cxl_port_init() + +The local variable 'path' is not freed in cxl_port_init() for success case. +The patch fixes that. + +Link: https://lore.kernel.org/r/165813258358.95191.6678871197554236554.stgit@LAPTOP-TBQTPII8 +Reviewed-by: Dan Williams +Signed-off-by: Shivaprasad G Bhat +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index c988ce2..bf3568d 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -769,6 +769,7 @@ static int cxl_port_init(struct cxl_port *port, struct cxl_port *parent_port, + if (sysfs_read_attr(ctx, path, buf) == 0) + port->module = util_modalias_to_module(ctx, buf); + ++ free(path); + return 0; + err: + free(port->dev_path); +-- +2.27.0 + diff --git a/SOURCES/0175-cxl-list-Reformat-option-list.patch b/SOURCES/0175-cxl-list-Reformat-option-list.patch new file mode 100644 index 0000000..7c87aa8 --- /dev/null +++ b/SOURCES/0175-cxl-list-Reformat-option-list.patch @@ -0,0 +1,47 @@ +From 6a843797767868279a5197699a53154bbed5eb2a Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:01:52 -0700 +Subject: [PATCH 175/217] cxl/list: Reformat option list + +Cleanup some spurious spaces and let clang-format re-layout the options. + +Link: https://lore.kernel.org/r/165781811294.1555691.6271986101970794441.stgit@dwillia2-xfh.jf.intel.com +Reviewed-by: Davidlohr Bueso +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/list.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/cxl/list.c b/cxl/list.c +index 940782d..1b5f583 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -36,8 +36,7 @@ static const struct option options[] = { + "filter by CXL endpoint device name(s)"), + OPT_BOOLEAN('E', "endpoints", ¶m.endpoints, + "include CXL endpoint info"), +- OPT_STRING('d', "decoder", ¶m.decoder_filter, +- "decoder device name", ++ OPT_STRING('d', "decoder", ¶m.decoder_filter, "decoder device name", + "filter by CXL decoder device name(s) / class"), + OPT_BOOLEAN('D', "decoders", ¶m.decoders, + "include CXL decoder info"), +@@ -45,11 +44,11 @@ static const struct option options[] = { + "include CXL target data with decoders or ports"), + OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), + OPT_BOOLEAN('u', "human", ¶m.human, +- "use human friendly number formats "), ++ "use human friendly number formats"), + OPT_BOOLEAN('H', "health", ¶m.health, +- "include memory device health information "), ++ "include memory device health information"), + OPT_BOOLEAN('I', "partition", ¶m.partition, +- "include memory device partition information "), ++ "include memory device partition information"), + #ifdef ENABLE_DEBUG + OPT_BOOLEAN(0, "debug", &debug, "debug list walk"), + #endif +-- +2.27.0 + diff --git a/SOURCES/0176-cxl-list-Emit-endpoint-decoders-filtered-by-memdev.patch b/SOURCES/0176-cxl-list-Emit-endpoint-decoders-filtered-by-memdev.patch new file mode 100644 index 0000000..f41a7a4 --- /dev/null +++ b/SOURCES/0176-cxl-list-Emit-endpoint-decoders-filtered-by-memdev.patch @@ -0,0 +1,46 @@ +From b4262c8e65a4f5aa931b5451535084297968949d Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:01:58 -0700 +Subject: [PATCH 176/217] cxl/list: Emit endpoint decoders filtered by memdev + +For example, dump all the endpoint decoders from memdev 'mem8'. + + cxl list -Di -m 8 -d endpoint + +Link: https://lore.kernel.org/r/165781811836.1555691.1997564050287016121.stgit@dwillia2-xfh.jf.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/filter.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/cxl/filter.c b/cxl/filter.c +index 66fd742..2f88a9d 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -428,7 +428,9 @@ util_cxl_decoder_filter_by_memdev(struct cxl_decoder *decoder, + const char *ident, const char *serial) + { + struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ struct cxl_endpoint *endpoint; + struct cxl_memdev *memdev; ++ struct cxl_port *port; + + if (!ident && !serial) + return decoder; +@@ -438,6 +440,12 @@ util_cxl_decoder_filter_by_memdev(struct cxl_decoder *decoder, + continue; + if (cxl_decoder_get_target_by_memdev(decoder, memdev)) + return decoder; ++ port = cxl_decoder_get_port(decoder); ++ if (!port || !cxl_port_is_endpoint(port)) ++ continue; ++ endpoint = cxl_port_to_endpoint(port); ++ if (cxl_endpoint_get_memdev(endpoint) == memdev) ++ return decoder; + } + + return NULL; +-- +2.27.0 + diff --git a/SOURCES/0177-cxl-list-Hide-0s-in-disabled-decoder-listings.patch b/SOURCES/0177-cxl-list-Hide-0s-in-disabled-decoder-listings.patch new file mode 100644 index 0000000..b2e2690 --- /dev/null +++ b/SOURCES/0177-cxl-list-Hide-0s-in-disabled-decoder-listings.patch @@ -0,0 +1,59 @@ +From f649df38298043f68e73bc730ccb824de045f42d Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:02:04 -0700 +Subject: [PATCH 177/217] cxl/list: Hide 0s in disabled decoder listings + +Trim some redundant information from decoder listings when they are +disabled. + +Link: https://lore.kernel.org/r/165781812427.1555691.5252994293073680408.stgit@dwillia2-xfh.jf.intel.com +Reviewed-by: Davidlohr Bueso +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/json.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/cxl/json.c b/cxl/json.c +index fdc6f73..a213fda 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -442,7 +442,7 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + const char *devname = cxl_decoder_get_devname(decoder); + struct cxl_port *port = cxl_decoder_get_port(decoder); + struct json_object *jdecoder, *jobj; +- u64 val; ++ u64 val, size; + + jdecoder = json_object_new_object(); + if (!jdecoder) +@@ -452,21 +452,21 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + if (jobj) + json_object_object_add(jdecoder, "decoder", jobj); + ++ size = cxl_decoder_get_size(decoder); + val = cxl_decoder_get_resource(decoder); +- if (val < ULLONG_MAX) { ++ if (size && val < ULLONG_MAX) { + jobj = util_json_object_hex(val, flags); + if (jobj) + json_object_object_add(jdecoder, "resource", jobj); + } + +- val = cxl_decoder_get_size(decoder); +- if (val < ULLONG_MAX) { +- jobj = util_json_object_size(val, flags); ++ if (size && size < ULLONG_MAX) { ++ jobj = util_json_object_size(size, flags); + if (jobj) + json_object_object_add(jdecoder, "size", jobj); + } + +- if (val == 0) { ++ if (size == 0) { + jobj = json_object_new_string("disabled"); + if (jobj) + json_object_object_add(jdecoder, "state", jobj); +-- +2.27.0 + diff --git a/SOURCES/0178-cxl-list-Add-DPA-span-to-endpoint-decoder-listings.patch b/SOURCES/0178-cxl-list-Add-DPA-span-to-endpoint-decoder-listings.patch new file mode 100644 index 0000000..cac3194 --- /dev/null +++ b/SOURCES/0178-cxl-list-Add-DPA-span-to-endpoint-decoder-listings.patch @@ -0,0 +1,167 @@ +From bbc0da95d12b3c890cc323a34b48cf489d4b467a Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:02:09 -0700 +Subject: [PATCH 178/217] cxl/list: Add DPA span to endpoint decoder listings + +Optionally include in decoder listings the device local address space for +endpoint decoders with active / allocated capacity. + +Link: https://lore.kernel.org/r/165781812967.1555691.4685129673233918478.stgit@dwillia2-xfh.jf.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/lib/libcxl.txt | 2 ++ + cxl/json.c | 18 +++++++++++++ + cxl/lib/libcxl.c | 43 +++++++++++++++++++++++++++++++- + cxl/lib/libcxl.sym | 6 +++++ + cxl/lib/private.h | 2 ++ + cxl/libcxl.h | 2 ++ + 6 files changed, 72 insertions(+), 1 deletion(-) + +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index f8f0e66..2aef489 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -392,6 +392,8 @@ more CXL decoder objects. + ---- + unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder); ++unsigned long long cxl_decoder_get_dpa_resource(struct cxl_decoder *decoder); ++unsigned long long cxl_decoder_get_dpa_size(struct cxl_decoder *decoder); + const char *cxl_decoder_get_devname(struct cxl_decoder *decoder); + int cxl_decoder_get_id(struct cxl_decoder *decoder); + int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder); +diff --git a/cxl/json.c b/cxl/json.c +index a213fda..3f52d3b 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -472,6 +472,24 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + json_object_object_add(jdecoder, "state", jobj); + } + ++ if (cxl_port_is_endpoint(port)) { ++ size = cxl_decoder_get_dpa_size(decoder); ++ val = cxl_decoder_get_dpa_resource(decoder); ++ if (size && val < ULLONG_MAX) { ++ jobj = util_json_object_hex(val, flags); ++ if (jobj) ++ json_object_object_add(jdecoder, "dpa_resource", ++ jobj); ++ } ++ ++ if (size && size < ULLONG_MAX) { ++ jobj = util_json_object_size(size, flags); ++ if (jobj) ++ json_object_object_add(jdecoder, "dpa_size", ++ jobj); ++ } ++ } ++ + if (cxl_port_is_root(port) && cxl_decoder_is_mem_capable(decoder)) { + if (cxl_decoder_is_pmem_capable(decoder)) { + jobj = json_object_new_boolean(true); +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index bf3568d..6f4d64d 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -956,8 +956,19 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + decoder->size = strtoull(buf, NULL, 0); + + switch (port->type) { +- case CXL_PORT_SWITCH: + case CXL_PORT_ENDPOINT: ++ sprintf(path, "%s/dpa_resource", cxldecoder_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ decoder->dpa_resource = ULLONG_MAX; ++ else ++ decoder->dpa_resource = strtoull(buf, NULL, 0); ++ sprintf(path, "%s/dpa_size", cxldecoder_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ decoder->dpa_size = ULLONG_MAX; ++ else ++ decoder->dpa_size = strtoull(buf, NULL, 0); ++ ++ case CXL_PORT_SWITCH: + decoder->pmem_capable = true; + decoder->volatile_capable = true; + decoder->mem_capable = true; +@@ -1114,6 +1125,36 @@ CXL_EXPORT unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder) + return decoder->size; + } + ++CXL_EXPORT unsigned long long ++cxl_decoder_get_dpa_resource(struct cxl_decoder *decoder) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ ++ if (!cxl_port_is_endpoint(port)) { ++ err(ctx, "%s: not an endpoint decoder\n", ++ cxl_decoder_get_devname(decoder)); ++ return ULLONG_MAX; ++ } ++ ++ return decoder->dpa_resource; ++} ++ ++CXL_EXPORT unsigned long long ++cxl_decoder_get_dpa_size(struct cxl_decoder *decoder) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ ++ if (!cxl_port_is_endpoint(port)) { ++ err(ctx, "%s: not an endpoint decoder\n", ++ cxl_decoder_get_devname(decoder)); ++ return ULLONG_MAX; ++ } ++ ++ return decoder->dpa_size; ++} ++ + CXL_EXPORT enum cxl_decoder_target_type + cxl_decoder_get_target_type(struct cxl_decoder *decoder) + { +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index dffcb60..8e2fc75 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -167,3 +167,9 @@ global: + cxl_cmd_new_set_partition; + cxl_cmd_partition_set_mode; + } LIBCXL_1; ++ ++LIBCXL_3 { ++global: ++ cxl_decoder_get_dpa_resource; ++ cxl_decoder_get_dpa_size; ++} LIBCXL_2; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index c6d88f7..24a2ae6 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -101,6 +101,8 @@ struct cxl_decoder { + struct cxl_ctx *ctx; + u64 start; + u64 size; ++ u64 dpa_resource; ++ u64 dpa_size; + void *dev_buf; + size_t buf_len; + char *dev_path; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 0007f4d..76aebe3 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -129,6 +129,8 @@ struct cxl_decoder *cxl_decoder_get_first(struct cxl_port *port); + struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder); ++unsigned long long cxl_decoder_get_dpa_resource(struct cxl_decoder *decoder); ++unsigned long long cxl_decoder_get_dpa_size(struct cxl_decoder *decoder); + const char *cxl_decoder_get_devname(struct cxl_decoder *decoder); + struct cxl_target *cxl_decoder_get_target_by_memdev(struct cxl_decoder *decoder, + struct cxl_memdev *memdev); +-- +2.27.0 + diff --git a/SOURCES/0179-ccan-list-Import-latest-list-helpers.patch b/SOURCES/0179-ccan-list-Import-latest-list-helpers.patch new file mode 100644 index 0000000..62bc153 --- /dev/null +++ b/SOURCES/0179-ccan-list-Import-latest-list-helpers.patch @@ -0,0 +1,459 @@ +From aaf1059e1c96b8052851030d4db971708e98e4d8 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:02:15 -0700 +Subject: [PATCH 179/217] ccan/list: Import latest list helpers + +Pick up the definition of list_add_{before,after} and other updates from +ccan at commit 52b86922f846 ("ccan/base64: fix GCC warning."). + +Link: https://lore.kernel.org/r/165781813572.1555691.15909358688944168922.stgit@dwillia2-xfh.jf.intel.com +Reported-by: Ira Weiny +Reviewed-by: Ira Weiny +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + ccan/list/list.h | 258 ++++++++++++++++++++++++++++++++++++++------- + ndctl/lib/inject.c | 1 - + util/list.h | 40 ------- + 3 files changed, 222 insertions(+), 77 deletions(-) + delete mode 100644 util/list.h + +diff --git a/ccan/list/list.h b/ccan/list/list.h +index 3ebd1b2..15f5fb7 100644 +--- a/ccan/list/list.h ++++ b/ccan/list/list.h +@@ -95,8 +95,8 @@ struct list_node *list_check_node(const struct list_node *n, + #define list_debug(h, loc) list_check((h), loc) + #define list_debug_node(n, loc) list_check_node((n), loc) + #else +-#define list_debug(h, loc) (h) +-#define list_debug_node(n, loc) (n) ++#define list_debug(h, loc) ((void)loc, h) ++#define list_debug_node(n, loc) ((void)loc, n) + #endif + + /** +@@ -111,7 +111,7 @@ struct list_node *list_check_node(const struct list_node *n, + * Example: + * static struct list_head my_list = LIST_HEAD_INIT(my_list); + */ +-#define LIST_HEAD_INIT(name) { { &name.n, &name.n } } ++#define LIST_HEAD_INIT(name) { { &(name).n, &(name).n } } + + /** + * LIST_HEAD - define and initialize an empty list_head +@@ -145,6 +145,48 @@ static inline void list_head_init(struct list_head *h) + h->n.next = h->n.prev = &h->n; + } + ++/** ++ * list_node_init - initialize a list_node ++ * @n: the list_node to link to itself. ++ * ++ * You don't need to use this normally! But it lets you list_del(@n) ++ * safely. ++ */ ++static inline void list_node_init(struct list_node *n) ++{ ++ n->next = n->prev = n; ++} ++ ++/** ++ * list_add_after - add an entry after an existing node in a linked list ++ * @h: the list_head to add the node to (for debugging) ++ * @p: the existing list_node to add the node after ++ * @n: the new list_node to add to the list. ++ * ++ * The existing list_node must already be a member of the list. ++ * The new list_node does not need to be initialized; it will be overwritten. ++ * ++ * Example: ++ * struct child c1, c2, c3; ++ * LIST_HEAD(h); ++ * ++ * list_add_tail(&h, &c1.list); ++ * list_add_tail(&h, &c3.list); ++ * list_add_after(&h, &c1.list, &c2.list); ++ */ ++#define list_add_after(h, p, n) list_add_after_(h, p, n, LIST_LOC) ++static inline void list_add_after_(struct list_head *h, ++ struct list_node *p, ++ struct list_node *n, ++ const char *abortstr) ++{ ++ n->next = p->next; ++ n->prev = p; ++ p->next->prev = n; ++ p->next = n; ++ (void)list_debug(h, abortstr); ++} ++ + /** + * list_add - add an entry at the start of a linked list. + * @h: the list_head to add the node to +@@ -163,10 +205,34 @@ static inline void list_add_(struct list_head *h, + struct list_node *n, + const char *abortstr) + { +- n->next = h->n.next; +- n->prev = &h->n; +- h->n.next->prev = n; +- h->n.next = n; ++ list_add_after_(h, &h->n, n, abortstr); ++} ++ ++/** ++ * list_add_before - add an entry before an existing node in a linked list ++ * @h: the list_head to add the node to (for debugging) ++ * @p: the existing list_node to add the node before ++ * @n: the new list_node to add to the list. ++ * ++ * The existing list_node must already be a member of the list. ++ * The new list_node does not need to be initialized; it will be overwritten. ++ * ++ * Example: ++ * list_head_init(&h); ++ * list_add_tail(&h, &c1.list); ++ * list_add_tail(&h, &c3.list); ++ * list_add_before(&h, &c3.list, &c2.list); ++ */ ++#define list_add_before(h, p, n) list_add_before_(h, p, n, LIST_LOC) ++static inline void list_add_before_(struct list_head *h, ++ struct list_node *p, ++ struct list_node *n, ++ const char *abortstr) ++{ ++ n->next = p; ++ n->prev = p->prev; ++ p->prev->next = n; ++ p->prev = n; + (void)list_debug(h, abortstr); + } + +@@ -185,11 +251,7 @@ static inline void list_add_tail_(struct list_head *h, + struct list_node *n, + const char *abortstr) + { +- n->next = &h->n; +- n->prev = h->n.prev; +- h->n.prev->next = n; +- h->n.prev = n; +- (void)list_debug(h, abortstr); ++ list_add_before_(h, &h->n, n, abortstr); + } + + /** +@@ -229,6 +291,21 @@ static inline bool list_empty_nodebug(const struct list_head *h) + } + #endif + ++/** ++ * list_empty_nocheck - is a list empty? ++ * @h: the list_head ++ * ++ * If the list is empty, returns true. This doesn't perform any ++ * debug check for list consistency, so it can be called without ++ * locks, racing with the list being modified. This is ok for ++ * checks where an incorrect result is not an issue (optimized ++ * bail out path for example). ++ */ ++static inline bool list_empty_nocheck(const struct list_head *h) ++{ ++ return h->n.next == &h->n; ++} ++ + /** + * list_del - delete an entry from an (unknown) linked list. + * @n: the list_node to delete from the list. +@@ -237,7 +314,7 @@ static inline bool list_empty_nodebug(const struct list_head *h) + * another list, but not deleted again. + * + * See also: +- * list_del_from() ++ * list_del_from(), list_del_init() + * + * Example: + * list_del(&child->list); +@@ -255,6 +332,27 @@ static inline void list_del_(struct list_node *n, const char* abortstr) + #endif + } + ++/** ++ * list_del_init - delete a node, and reset it so it can be deleted again. ++ * @n: the list_node to be deleted. ++ * ++ * list_del(@n) or list_del_init() again after this will be safe, ++ * which can be useful in some cases. ++ * ++ * See also: ++ * list_del_from(), list_del() ++ * ++ * Example: ++ * list_del_init(&child->list); ++ * parent->num_children--; ++ */ ++#define list_del_init(n) list_del_init_(n, LIST_LOC) ++static inline void list_del_init_(struct list_node *n, const char *abortstr) ++{ ++ list_del_(n, abortstr); ++ list_node_init(n); ++} ++ + /** + * list_del_from - delete an entry from a known linked list. + * @h: the list_head the node is in. +@@ -285,6 +383,39 @@ static inline void list_del_from(struct list_head *h, struct list_node *n) + list_del(n); + } + ++/** ++ * list_swap - swap out an entry from an (unknown) linked list for a new one. ++ * @o: the list_node to replace from the list. ++ * @n: the list_node to insert in place of the old one. ++ * ++ * Note that this leaves @o in an undefined state; it can be added to ++ * another list, but not deleted/swapped again. ++ * ++ * See also: ++ * list_del() ++ * ++ * Example: ++ * struct child x1, x2; ++ * LIST_HEAD(xh); ++ * ++ * list_add(&xh, &x1.list); ++ * list_swap(&x1.list, &x2.list); ++ */ ++#define list_swap(o, n) list_swap_(o, n, LIST_LOC) ++static inline void list_swap_(struct list_node *o, ++ struct list_node *n, ++ const char* abortstr) ++{ ++ (void)list_debug_node(o, abortstr); ++ *n = *o; ++ n->next->prev = n; ++ n->prev->next = n; ++#ifdef CCAN_LIST_DEBUG ++ /* Catch use-after-del. */ ++ o->next = o->prev = NULL; ++#endif ++} ++ + /** + * list_entry - convert a list_node back into the structure containing it. + * @n: the list_node +@@ -406,9 +537,29 @@ static inline const void *list_tail_(const struct list_head *h, size_t off) + * printf("Name: %s\n", child->name); + */ + #define list_for_each_rev(h, i, member) \ +- for (i = container_of_var(list_debug(h, LIST_LOC)->n.prev, i, member); \ +- &i->member != &(h)->n; \ +- i = container_of_var(i->member.prev, i, member)) ++ list_for_each_rev_off(h, i, list_off_var_(i, member)) ++ ++/** ++ * list_for_each_rev_safe - iterate through a list backwards, ++ * maybe during deletion ++ * @h: the list_head ++ * @i: the structure containing the list_node ++ * @nxt: the structure containing the list_node ++ * @member: the list_node member of the structure ++ * ++ * This is a convenient wrapper to iterate @i over the entire list backwards. ++ * It's a for loop, so you can break and continue as normal. The extra ++ * variable * @nxt is used to hold the next element, so you can delete @i ++ * from the list. ++ * ++ * Example: ++ * struct child *next; ++ * list_for_each_rev_safe(&parent->children, child, next, list) { ++ * printf("Name: %s\n", child->name); ++ * } ++ */ ++#define list_for_each_rev_safe(h, i, nxt, member) \ ++ list_for_each_rev_safe_off(h, i, nxt, list_off_var_(i, member)) + + /** + * list_for_each_safe - iterate through a list, maybe during deletion +@@ -422,7 +573,6 @@ static inline const void *list_tail_(const struct list_head *h, size_t off) + * @nxt is used to hold the next element, so you can delete @i from the list. + * + * Example: +- * struct child *next; + * list_for_each_safe(&parent->children, child, next, list) { + * list_del(&child->list); + * parent->num_children--; +@@ -537,10 +687,28 @@ static inline void list_prepend_list_(struct list_head *to, + list_head_init(from); + } + ++/* internal macros, do not use directly */ ++#define list_for_each_off_dir_(h, i, off, dir) \ ++ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.dir, \ ++ (off)); \ ++ list_node_from_off_((void *)i, (off)) != &(h)->n; \ ++ i = list_node_to_off_(list_node_from_off_((void *)i, (off))->dir, \ ++ (off))) ++ ++#define list_for_each_safe_off_dir_(h, i, nxt, off, dir) \ ++ for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.dir, \ ++ (off)), \ ++ nxt = list_node_to_off_(list_node_from_off_(i, (off))->dir, \ ++ (off)); \ ++ list_node_from_off_(i, (off)) != &(h)->n; \ ++ i = nxt, \ ++ nxt = list_node_to_off_(list_node_from_off_(i, (off))->dir, \ ++ (off))) ++ + /** + * list_for_each_off - iterate through a list of memory regions. + * @h: the list_head +- * @i: the pointer to a memory region wich contains list node data. ++ * @i: the pointer to a memory region which contains list node data. + * @off: offset(relative to @i) at which list node data resides. + * + * This is a low-level wrapper to iterate @i over the entire list, used to +@@ -548,12 +716,12 @@ static inline void list_prepend_list_(struct list_head *to, + * so you can break and continue as normal. + * + * WARNING! Being the low-level macro that it is, this wrapper doesn't know +- * nor care about the type of @i. The only assumtion made is that @i points ++ * nor care about the type of @i. The only assumption made is that @i points + * to a chunk of memory that at some @offset, relative to @i, contains a +- * properly filled `struct node_list' which in turn contains pointers to +- * memory chunks and it's turtles all the way down. Whith all that in mind ++ * properly filled `struct list_node' which in turn contains pointers to ++ * memory chunks and it's turtles all the way down. With all that in mind + * remember that given the wrong pointer/offset couple this macro will +- * happilly churn all you memory untill SEGFAULT stops it, in other words ++ * happily churn all you memory until SEGFAULT stops it, in other words + * caveat emptor. + * + * It is worth mentioning that one of legitimate use-cases for that wrapper +@@ -567,17 +735,24 @@ static inline void list_prepend_list_(struct list_head *to, + * printf("Name: %s\n", child->name); + */ + #define list_for_each_off(h, i, off) \ +- for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.next, \ +- (off)); \ +- list_node_from_off_((void *)i, (off)) != &(h)->n; \ +- i = list_node_to_off_(list_node_from_off_((void *)i, (off))->next, \ +- (off))) ++ list_for_each_off_dir_((h),(i),(off),next) ++ ++/** ++ * list_for_each_rev_off - iterate through a list of memory regions backwards ++ * @h: the list_head ++ * @i: the pointer to a memory region which contains list node data. ++ * @off: offset(relative to @i) at which list node data resides. ++ * ++ * See list_for_each_off for details ++ */ ++#define list_for_each_rev_off(h, i, off) \ ++ list_for_each_off_dir_((h),(i),(off),prev) + + /** + * list_for_each_safe_off - iterate through a list of memory regions, maybe + * during deletion + * @h: the list_head +- * @i: the pointer to a memory region wich contains list node data. ++ * @i: the pointer to a memory region which contains list node data. + * @nxt: the structure containing the list_node + * @off: offset(relative to @i) at which list node data resides. + * +@@ -590,15 +765,26 @@ static inline void list_prepend_list_(struct list_head *to, + * printf("Name: %s\n", child->name); + */ + #define list_for_each_safe_off(h, i, nxt, off) \ +- for (i = list_node_to_off_(list_debug(h, LIST_LOC)->n.next, \ +- (off)), \ +- nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \ +- (off)); \ +- list_node_from_off_(i, (off)) != &(h)->n; \ +- i = nxt, \ +- nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \ +- (off))) ++ list_for_each_safe_off_dir_((h),(i),(nxt),(off),next) + ++/** ++ * list_for_each_rev_safe_off - iterate backwards through a list of ++ * memory regions, maybe during deletion ++ * @h: the list_head ++ * @i: the pointer to a memory region which contains list node data. ++ * @nxt: the structure containing the list_node ++ * @off: offset(relative to @i) at which list node data resides. ++ * ++ * For details see `list_for_each_rev_off' and `list_for_each_rev_safe' ++ * descriptions. ++ * ++ * Example: ++ * list_for_each_rev_safe_off(&parent->children, child, ++ * next, offsetof(struct child, list)) ++ * printf("Name: %s\n", child->name); ++ */ ++#define list_for_each_rev_safe_off(h, i, nxt, off) \ ++ list_for_each_safe_off_dir_((h),(i),(nxt),(off),prev) + + /* Other -off variants. */ + #define list_entry_off(n, type, off) \ +diff --git a/ndctl/lib/inject.c b/ndctl/lib/inject.c +index d61c02c..3486ffe 100644 +--- a/ndctl/lib/inject.c ++++ b/ndctl/lib/inject.c +@@ -2,7 +2,6 @@ + // Copyright (C) 2014-2020, Intel Corporation. All rights reserved. + #include + #include +-#include + #include + #include + #include +diff --git a/util/list.h b/util/list.h +deleted file mode 100644 +index 1ea9c59..0000000 +--- a/util/list.h ++++ /dev/null +@@ -1,40 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (C) 2015-2020 Intel Corporation. All rights reserved. */ +-#ifndef _NDCTL_LIST_H_ +-#define _NDCTL_LIST_H_ +- +-#include +- +-/** +- * list_add_after - add an entry after the given node in the linked list. +- * @h: the list_head to add the node to +- * @l: the list_node after which to add to +- * @n: the list_node to add to the list. +- * +- * The list_node does not need to be initialized; it will be overwritten. +- * Example: +- * struct child *child = malloc(sizeof(*child)); +- * +- * child->name = "geoffrey"; +- * list_add_after(&parent->children, &child1->list, &child->list); +- * parent->num_children++; +- */ +-#define list_add_after(h, l, n) list_add_after_(h, l, n, LIST_LOC) +-static inline void list_add_after_(struct list_head *h, +- struct list_node *l, +- struct list_node *n, +- const char *abortstr) +-{ +- if (l->next == &h->n) { +- /* l is the last element, this becomes a list_add_tail */ +- list_add_tail(h, n); +- return; +- } +- n->next = l->next; +- n->prev = l; +- l->next->prev = n; +- l->next = n; +- (void)list_debug(h, abortstr); +-} +- +-#endif /* _NDCTL_LIST_H_ */ +-- +2.27.0 + diff --git a/SOURCES/0180-cxl-lib-Maintain-decoders-in-id-order.patch b/SOURCES/0180-cxl-lib-Maintain-decoders-in-id-order.patch new file mode 100644 index 0000000..ae034b9 --- /dev/null +++ b/SOURCES/0180-cxl-lib-Maintain-decoders-in-id-order.patch @@ -0,0 +1,103 @@ +From 06ae7118aaa74aa2139004fa12b4910bc15236de Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:02:21 -0700 +Subject: [PATCH 180/217] cxl/lib: Maintain decoders in id order + +Given that decoder instance order is fundamental to the DPA translation +sequence for endpoint decoders, enforce that cxl_decoder_for_each() returns +decoders in instance order. Otherwise, they show up in readddir() order +which is not predictable. + +Add a list_add_sorted() to generically handle inserting into a sorted list. + +Link: https://lore.kernel.org/r/165781814167.1555691.14895625637451030942.stgit@dwillia2-xfh.jf.intel.com +Reviewed-by: Davidlohr Bueso +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 8 +++++++- + util/list.h | 39 +++++++++++++++++++++++++++++++++++++++ + 2 files changed, 46 insertions(+), 1 deletion(-) + create mode 100644 util/list.h + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 6f4d64d..ea597f6 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -19,6 +19,7 @@ + #include + + #include ++#include + #include + #include + #include +@@ -909,6 +910,11 @@ cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint) + return NULL; + } + ++static int decoder_id_cmp(struct cxl_decoder *d1, struct cxl_decoder *d2) ++{ ++ return d1->id - d2->id; ++} ++ + static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + { + const char *devname = devpath_to_devname(cxldecoder_base); +@@ -1050,7 +1056,7 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + return decoder_dup; + } + +- list_add(&port->decoders, &decoder->list); ++ list_add_sorted(&port->decoders, decoder, list, decoder_id_cmp); + + free(path); + return decoder; +diff --git a/util/list.h b/util/list.h +new file mode 100644 +index 0000000..cb77271 +--- /dev/null ++++ b/util/list.h +@@ -0,0 +1,39 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2022 Intel Corporation. All rights reserved. */ ++#ifndef _NDCTL_LIST_H_ ++#define _NDCTL_LIST_H_ ++ ++#include ++ ++#define list_add_sorted(head, n, node, cmp) \ ++ do { \ ++ struct list_head *__head = (head); \ ++ typeof(n) __iter, __next; \ ++ typeof(n) __new = (n); \ ++ \ ++ if (list_empty(__head)) { \ ++ list_add(__head, &__new->node); \ ++ break; \ ++ } \ ++ \ ++ list_for_each (__head, __iter, node) { \ ++ if (cmp(__new, __iter) < 0) { \ ++ list_add_before(__head, &__iter->node, \ ++ &__new->node); \ ++ break; \ ++ } \ ++ __next = list_next(__head, __iter, node); \ ++ if (!__next) { \ ++ list_add_after(__head, &__iter->node, \ ++ &__new->node); \ ++ break; \ ++ } \ ++ if (cmp(__new, __next) < 0) { \ ++ list_add_before(__head, &__next->node, \ ++ &__new->node); \ ++ break; \ ++ } \ ++ } \ ++ } while (0) ++ ++#endif /* _NDCTL_LIST_H_ */ +-- +2.27.0 + diff --git a/SOURCES/0181-cxl-memdev-Fix-json-for-multi-device-partitioning.patch b/SOURCES/0181-cxl-memdev-Fix-json-for-multi-device-partitioning.patch new file mode 100644 index 0000000..21692ff --- /dev/null +++ b/SOURCES/0181-cxl-memdev-Fix-json-for-multi-device-partitioning.patch @@ -0,0 +1,93 @@ +From 8ed95d22504d7b2b258d1800878e32c162badf8c Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:02:27 -0700 +Subject: [PATCH 181/217] cxl/memdev: Fix json for multi-device partitioning + +In the case when someone partitions several devices at once, collect all +the affected memdevs into a json array. + +With the move to use util_display_json_array() that also requires a set of +flags to be specifiied. Apply the UTIL_JSON_HUMAN flag for all interactive +command result output to bring this command in line with other tools. + +Link: https://lore.kernel.org/r/165781814737.1555691.889129128205037941.stgit@dwillia2-xfh.jf.intel.com +Cc: Alison Schofield +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/memdev.c | 26 +++++++++++++++++++++----- + 1 file changed, 21 insertions(+), 5 deletions(-) + +diff --git a/cxl/memdev.c b/cxl/memdev.c +index 91d914d..9fcd8ae 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -19,6 +19,7 @@ + struct action_context { + FILE *f_out; + FILE *f_in; ++ struct json_object *jdevs; + }; + + static struct parameters { +@@ -339,12 +340,13 @@ out: + } + + static int action_setpartition(struct cxl_memdev *memdev, +- struct action_context *actx) ++ struct action_context *actx) + { + const char *devname = cxl_memdev_get_devname(memdev); + enum cxl_setpart_type type = CXL_SETPART_PMEM; + unsigned long long size = ULLONG_MAX; + struct json_object *jmemdev; ++ unsigned long flags; + struct cxl_cmd *cmd; + int rc; + +@@ -396,10 +398,12 @@ out_err: + if (rc) + log_err(&ml, "%s error: %s\n", devname, strerror(-rc)); + +- jmemdev = util_cxl_memdev_to_json(memdev, UTIL_JSON_PARTITION); +- if (jmemdev) +- printf("%s\n", json_object_to_json_string_ext(jmemdev, +- JSON_C_TO_STRING_PRETTY)); ++ flags = UTIL_JSON_PARTITION; ++ if (actx->f_out == stdout && isatty(1)) ++ flags |= UTIL_JSON_HUMAN; ++ jmemdev = util_cxl_memdev_to_json(memdev, flags); ++ if (actx->jdevs && jmemdev) ++ json_object_array_add(actx->jdevs, jmemdev); + + return rc; + } +@@ -446,6 +450,9 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + err++; + } + ++ if (action == action_setpartition) ++ actx.jdevs = json_object_new_array(); ++ + if (err == argc) { + usage_with_options(u, options); + return -EINVAL; +@@ -528,6 +535,15 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + if (actx.f_in != stdin) + fclose(actx.f_in); + ++ if (actx.jdevs) { ++ unsigned long flags = 0; ++ ++ if (actx.f_out == stdout && isatty(1)) ++ flags |= UTIL_JSON_HUMAN; ++ util_display_json_array(actx.f_out, actx.jdevs, flags); ++ } ++ ++ + out_close_fout: + if (actx.f_out != stdout) + fclose(actx.f_out); +-- +2.27.0 + diff --git a/SOURCES/0182-cxl-list-Emit-mode-for-endpoint-decoder-objects.patch b/SOURCES/0182-cxl-list-Emit-mode-for-endpoint-decoder-objects.patch new file mode 100644 index 0000000..3a37ca9 --- /dev/null +++ b/SOURCES/0182-cxl-list-Emit-mode-for-endpoint-decoder-objects.patch @@ -0,0 +1,181 @@ +From d2a7fc7fb87396eb267cf6c8948468f7e56bea89 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:02:33 -0700 +Subject: [PATCH 182/217] cxl/list: Emit 'mode' for endpoint decoder objects + +The 'mode' property of an endpoint decoder indicates the access +properties of the DPA (device physical address) mapped into HPA (host +physical address) by the decoder. Where the modes are 'none' +(decoder-disabled), 'ram' (voltaile memory), 'pmem' (persistent memory), +and 'mixed' (an unexpected, but possible, case where the decoder +straddles a mode / partition boundary). + +Link: https://lore.kernel.org/r/165781815306.1555691.17541956592287631419.stgit@dwillia2-xfh.jf.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/lib/libcxl.txt | 9 +++++++++ + cxl/json.c | 8 ++++++++ + cxl/lib/libcxl.c | 30 ++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 1 + + cxl/lib/private.h | 1 + + cxl/libcxl.h | 23 +++++++++++++++++++++++ + 6 files changed, 72 insertions(+) + +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 2aef489..90fe338 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -405,6 +405,15 @@ enum cxl_decoder_target_type { + }; + + cxl_decoder_get_target_type(struct cxl_decoder *decoder); ++ ++enum cxl_decoder_mode { ++ CXL_DECODER_MODE_NONE, ++ CXL_DECODER_MODE_MIXED, ++ CXL_DECODER_MODE_PMEM, ++ CXL_DECODER_MODE_RAM, ++}; ++enum cxl_decoder_mode cxl_decoder_get_mode(struct cxl_decoder *decoder); ++ + bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder); + bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder); + bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder); +diff --git a/cxl/json.c b/cxl/json.c +index 3f52d3b..ae9c812 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -473,6 +473,8 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + } + + if (cxl_port_is_endpoint(port)) { ++ enum cxl_decoder_mode mode = cxl_decoder_get_mode(decoder); ++ + size = cxl_decoder_get_dpa_size(decoder); + val = cxl_decoder_get_dpa_resource(decoder); + if (size && val < ULLONG_MAX) { +@@ -488,6 +490,12 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + json_object_object_add(jdecoder, "dpa_size", + jobj); + } ++ ++ if (mode > CXL_DECODER_MODE_NONE) { ++ jobj = json_object_new_string(cxl_decoder_mode_name(mode)); ++ if (jobj) ++ json_object_object_add(jdecoder, "mode", jobj); ++ } + } + + if (cxl_port_is_root(port) && cxl_decoder_is_mem_capable(decoder)) { +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index ea597f6..b802e5d 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -961,6 +961,21 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + else + decoder->size = strtoull(buf, NULL, 0); + ++ sprintf(path, "%s/mode", cxldecoder_base); ++ if (sysfs_read_attr(ctx, path, buf) == 0) { ++ if (strcmp(buf, "ram") == 0) ++ decoder->mode = CXL_DECODER_MODE_RAM; ++ else if (strcmp(buf, "pmem") == 0) ++ decoder->mode = CXL_DECODER_MODE_PMEM; ++ else if (strcmp(buf, "mixed") == 0) ++ decoder->mode = CXL_DECODER_MODE_MIXED; ++ else if (strcmp(buf, "none") == 0) ++ decoder->mode = CXL_DECODER_MODE_NONE; ++ else ++ decoder->mode = CXL_DECODER_MODE_MIXED; ++ } else ++ decoder->mode = CXL_DECODER_MODE_NONE; ++ + switch (port->type) { + case CXL_PORT_ENDPOINT: + sprintf(path, "%s/dpa_resource", cxldecoder_base); +@@ -1161,6 +1176,21 @@ cxl_decoder_get_dpa_size(struct cxl_decoder *decoder) + return decoder->dpa_size; + } + ++CXL_EXPORT enum cxl_decoder_mode ++cxl_decoder_get_mode(struct cxl_decoder *decoder) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ ++ if (!cxl_port_is_endpoint(port)) { ++ err(ctx, "%s: not an endpoint decoder\n", ++ cxl_decoder_get_devname(decoder)); ++ return CXL_DECODER_MODE_NONE; ++ } ++ ++ return decoder->mode; ++} ++ + CXL_EXPORT enum cxl_decoder_target_type + cxl_decoder_get_target_type(struct cxl_decoder *decoder) + { +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 8e2fc75..88c5a7e 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -172,4 +172,5 @@ LIBCXL_3 { + global: + cxl_decoder_get_dpa_resource; + cxl_decoder_get_dpa_size; ++ cxl_decoder_get_mode; + } LIBCXL_2; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 24a2ae6..f6d4573 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -108,6 +108,7 @@ struct cxl_decoder { + char *dev_path; + int nr_targets; + int id; ++ enum cxl_decoder_mode mode; + bool pmem_capable; + bool volatile_capable; + bool mem_capable; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 76aebe3..1436dc4 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -127,10 +127,33 @@ struct cxl_dport *cxl_port_get_dport_by_memdev(struct cxl_port *port, + struct cxl_decoder; + struct cxl_decoder *cxl_decoder_get_first(struct cxl_port *port); + struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder); ++struct cxl_decoder *cxl_decoder_get_last(struct cxl_port *port); ++struct cxl_decoder *cxl_decoder_get_prev(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_dpa_resource(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_dpa_size(struct cxl_decoder *decoder); ++enum cxl_decoder_mode { ++ CXL_DECODER_MODE_NONE, ++ CXL_DECODER_MODE_MIXED, ++ CXL_DECODER_MODE_PMEM, ++ CXL_DECODER_MODE_RAM, ++}; ++static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode) ++{ ++ static const char *names[] = { ++ [CXL_DECODER_MODE_NONE] = "none", ++ [CXL_DECODER_MODE_MIXED] = "mixed", ++ [CXL_DECODER_MODE_PMEM] = "pmem", ++ [CXL_DECODER_MODE_RAM] = "ram", ++ }; ++ ++ if (mode < CXL_DECODER_MODE_NONE || mode > CXL_DECODER_MODE_RAM) ++ mode = CXL_DECODER_MODE_NONE; ++ return names[mode]; ++} ++ ++enum cxl_decoder_mode cxl_decoder_get_mode(struct cxl_decoder *decoder); + const char *cxl_decoder_get_devname(struct cxl_decoder *decoder); + struct cxl_target *cxl_decoder_get_target_by_memdev(struct cxl_decoder *decoder, + struct cxl_memdev *memdev); +-- +2.27.0 + diff --git a/SOURCES/0183-cxl-set-partition-Accept-ram-as-an-alias-for-volatil.patch b/SOURCES/0183-cxl-set-partition-Accept-ram-as-an-alias-for-volatil.patch new file mode 100644 index 0000000..64a5fcc --- /dev/null +++ b/SOURCES/0183-cxl-set-partition-Accept-ram-as-an-alias-for-volatil.patch @@ -0,0 +1,56 @@ +From b1c29cfa125f6da219bfcec732a8290bb124cdce Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:02:38 -0700 +Subject: [PATCH 183/217] cxl/set-partition: Accept 'ram' as an alias for + 'volatile' + +'ram' is a more convenient shorthand for volatile memory. + +Link: https://lore.kernel.org/r/165781815878.1555691.12251226240559355924.stgit@dwillia2-xfh.jf.intel.com +Cc: Alison Schofield +Reviewed-by: Davidlohr Bueso +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-set-partition.txt | 2 +- + cxl/memdev.c | 4 +++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/Documentation/cxl/cxl-set-partition.txt b/Documentation/cxl/cxl-set-partition.txt +index 1e548af..f0126da 100644 +--- a/Documentation/cxl/cxl-set-partition.txt ++++ b/Documentation/cxl/cxl-set-partition.txt +@@ -37,7 +37,7 @@ include::memdev-option.txt[] + + -t:: + --type=:: +- Type of partition, 'pmem' or 'volatile', to modify. ++ Type of partition, 'pmem' or 'ram' (volatile), to modify. + Default: 'pmem' + + -s:: +diff --git a/cxl/memdev.c b/cxl/memdev.c +index 9fcd8ae..1cecad2 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -65,7 +65,7 @@ OPT_BOOLEAN('f', "force", ¶m.force, \ + + #define SET_PARTITION_OPTIONS() \ + OPT_STRING('t', "type", ¶m.type, "type", \ +- "'pmem' or 'volatile' (Default: 'pmem')"), \ ++ "'pmem' or 'ram' (volatile) (Default: 'pmem')"), \ + OPT_STRING('s', "size", ¶m.size, "size", \ + "size in bytes (Default: all available capacity)"), \ + OPT_BOOLEAN('a', "align", ¶m.align, \ +@@ -355,6 +355,8 @@ static int action_setpartition(struct cxl_memdev *memdev, + /* default */; + else if (strcmp(param.type, "volatile") == 0) + type = CXL_SETPART_VOLATILE; ++ else if (strcmp(param.type, "ram") == 0) ++ type = CXL_SETPART_VOLATILE; + else { + log_err(&ml, "invalid type '%s'\n", param.type); + return -EINVAL; +-- +2.27.0 + diff --git a/SOURCES/0184-cxl-memdev-Add-reserve-free-dpa-commands.patch b/SOURCES/0184-cxl-memdev-Add-reserve-free-dpa-commands.patch new file mode 100644 index 0000000..11f218f --- /dev/null +++ b/SOURCES/0184-cxl-memdev-Add-reserve-free-dpa-commands.patch @@ -0,0 +1,763 @@ +From 6624f4fdf7ba43039111c996dfd3982b4fdc43bc Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:02:44 -0700 +Subject: [PATCH 184/217] cxl/memdev: Add {reserve,free}-dpa commands + +Add helper commands for managing allocations of DPA (device physical +address) capacity on a set of CXL memory devices. + +The main convenience this command affords is automatically picking the next +decoder to allocate per-memdev. + +For example, to allocate 256MiB from all endpoints that are covered by a +given root decoder, and collect those resulting endpoint-decoders into an +array: + + readarray -t mem < <(cxl list -M -d $decoder | jq -r ".[].memdev") + readarray -t endpoint < <(cxl reserve-dpa -t pmem ${mem[*]} -s $((256<<20)) | + jq -r ".[] | .decoder.decoder") + +Link: https://lore.kernel.org/r/165781816425.1555691.17958897857798325111.stgit@dwillia2-xfh.jf.intel.com +Signed-off-by: Dan Williams +[vishal: fix typo pointed out by Jonathan] +Signed-off-by: Vishal Verma +--- + .clang-format | 1 + + Documentation/cxl/cxl-free-dpa.txt | 53 +++++ + Documentation/cxl/cxl-reserve-dpa.txt | 67 ++++++ + Documentation/cxl/lib/libcxl.txt | 2 + + Documentation/cxl/meson.build | 2 + + cxl/builtin.h | 2 + + cxl/cxl.c | 2 + + cxl/filter.c | 4 +- + cxl/filter.h | 2 + + cxl/lib/libcxl.c | 86 ++++++++ + cxl/lib/libcxl.sym | 4 + + cxl/libcxl.h | 9 + + cxl/memdev.c | 280 +++++++++++++++++++++++++- + 13 files changed, 511 insertions(+), 3 deletions(-) + create mode 100644 Documentation/cxl/cxl-free-dpa.txt + create mode 100644 Documentation/cxl/cxl-reserve-dpa.txt + +diff --git a/.clang-format b/.clang-format +index 6aabcb6..7254a1b 100644 +--- a/.clang-format ++++ b/.clang-format +@@ -81,6 +81,7 @@ ForEachMacros: + - 'cxl_bus_foreach' + - 'cxl_port_foreach' + - 'cxl_decoder_foreach' ++ - 'cxl_decoder_foreach_reverse' + - 'cxl_target_foreach' + - 'cxl_dport_foreach' + - 'cxl_endpoint_foreach' +diff --git a/Documentation/cxl/cxl-free-dpa.txt b/Documentation/cxl/cxl-free-dpa.txt +new file mode 100644 +index 0000000..73fb048 +--- /dev/null ++++ b/Documentation/cxl/cxl-free-dpa.txt +@@ -0,0 +1,53 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-free-dpa(1) ++=============== ++ ++NAME ++---- ++cxl-free-dpa - release device-physical address space ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl free-dpa' [..] [] ++ ++The CXL region provisioning process proceeds in multiple steps. One of ++the steps is identifying and reserving the DPA span that each member of ++the interleave-set (region) contributes in advance of attaching that ++allocation to a region. For development, test, and debug purposes this ++command is a helper to find the last allocated decoder on a device and ++zero-out / free its DPA allocation. ++ ++OPTIONS ++------- ++:: ++include::memdev-option.txt[] ++ ++-d:: ++--decoder:: ++ Specify the decoder to free. The CXL specification ++ mandates that DPA must be released in the reverse order it was ++ allocated. See linkcxl:cxl-reserve-dpa[1] ++ ++-t:: ++--type:: ++ Constrain the search for "last allocated decoder" to decoders targeting ++ the given partition. ++ ++-f:: ++--force:: ++ The kernel enforces CXL DPA ordering constraints on deallocation events, ++ and the tool anticipates those and fails operations that are expected to ++ fail without sending them to the kernel. For test purposes, continue to ++ attempt "expected to fail" operations to exercise the driver. ++ ++-v:: ++ Turn on verbose debug messages in the library (if libcxl was built with ++ logging and debug enabled). ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-reserve-dpa[1] +diff --git a/Documentation/cxl/cxl-reserve-dpa.txt b/Documentation/cxl/cxl-reserve-dpa.txt +new file mode 100644 +index 0000000..5e79ef2 +--- /dev/null ++++ b/Documentation/cxl/cxl-reserve-dpa.txt +@@ -0,0 +1,67 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-reserve-dpa(1) ++================== ++ ++NAME ++---- ++cxl-reserve-dpa - allocate device-physical address space ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl reserve-dpa' [..] [] ++ ++The CXL region provisioning process proceeds in multiple steps. One of ++the steps is identifying and reserving the DPA span that each member of ++the interleave-set (region) contributes in advance of attaching that ++allocation to a region. For development, test, and debug purposes this ++command is a helper to find the next available decoder on endpoint ++(memdev) and mark a span of DPA as busy. ++ ++OPTIONS ++------- ++:: ++include::memdev-option.txt[] ++ ++-d:: ++--decoder:: ++ Specify the decoder to attempt the allocation. The CXL specification ++ mandates that allocations must be ordered by DPA and decoder instance. ++ I.e. the lowest DPA allocation on the device is covered by decoder0, and ++ the last / highest DPA allocation is covered by the last decoder. This ++ ordering is enforced by the kernel. By default the tool picks the 'next ++ available' decoder. ++ ++-t:: ++--type:: ++ Select the partition for the allocation. CXL devices implement a ++ partition that divdes 'ram' and 'pmem' capacity, where 'pmem' capacity ++ consumes the higher DPA capacity above the partition boundary. The type ++ defaults to 'pmem'. Note that given CXL DPA allocation constraints, once ++ any 'pmem' allocation is established then all remaining 'ram' capacity ++ becomes reserved (skipped). ++ ++-f:: ++--force:: ++ The kernel enforces CXL DPA allocation ordering constraints, and ++ the tool anticipates those and fails operations that are expected to ++ fail without sending them to the kernel. For test purposes, continue to ++ attempt "expected to fail" operations to exercise the driver. ++ ++-s:: ++--size:: ++ Specify the size of the allocation. This option supports the suffixes ++ "k" or "K" for KiB, "m" or "M" for MiB, "g" or "G" for GiB and "t" or ++ "T" for TiB. This defaults to "all available capacity of the specified ++ type". ++ ++-v:: ++ Turn on verbose debug messages in the library (if libcxl was built with ++ logging and debug enabled). ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-free-dpa[1] +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 90fe338..7a38ce4 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -394,6 +394,7 @@ unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_dpa_resource(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_dpa_size(struct cxl_decoder *decoder); ++int cxl_decoder_set_dpa_size(struct cxl_decoder *decoder, unsigned long long size); + const char *cxl_decoder_get_devname(struct cxl_decoder *decoder); + int cxl_decoder_get_id(struct cxl_decoder *decoder); + int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder); +@@ -413,6 +414,7 @@ enum cxl_decoder_mode { + CXL_DECODER_MODE_RAM, + }; + enum cxl_decoder_mode cxl_decoder_get_mode(struct cxl_decoder *decoder); ++int cxl_decoder_set_mode(struct cxl_decoder *decoder, enum cxl_decoder_mode mode); + + bool cxl_decoder_is_pmem_capable(struct cxl_decoder *decoder); + bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder); +diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build +index 974a5a4..d019dfc 100644 +--- a/Documentation/cxl/meson.build ++++ b/Documentation/cxl/meson.build +@@ -36,6 +36,8 @@ cxl_manpages = [ + 'cxl-disable-port.txt', + 'cxl-disable-bus.txt', + 'cxl-set-partition.txt', ++ 'cxl-reserve-dpa.txt', ++ 'cxl-free-dpa.txt', + ] + + foreach man : cxl_manpages +diff --git a/cxl/builtin.h b/cxl/builtin.h +index a437bc3..9e6fc62 100644 +--- a/cxl/builtin.h ++++ b/cxl/builtin.h +@@ -12,6 +12,8 @@ int cmd_init_labels(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_check_labels(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_disable_memdev(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_enable_memdev(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_reserve_dpa(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_free_dpa(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx); +diff --git a/cxl/cxl.c b/cxl/cxl.c +index aa4ce61..ef4cda9 100644 +--- a/cxl/cxl.c ++++ b/cxl/cxl.c +@@ -66,6 +66,8 @@ static struct cmd_struct commands[] = { + { "write-labels", .c_fn = cmd_write_labels }, + { "disable-memdev", .c_fn = cmd_disable_memdev }, + { "enable-memdev", .c_fn = cmd_enable_memdev }, ++ { "reserve-dpa", .c_fn = cmd_reserve_dpa }, ++ { "free-dpa", .c_fn = cmd_free_dpa }, + { "disable-port", .c_fn = cmd_disable_port }, + { "enable-port", .c_fn = cmd_enable_port }, + { "set-partition", .c_fn = cmd_set_partition }, +diff --git a/cxl/filter.c b/cxl/filter.c +index 2f88a9d..e5fab19 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -380,8 +380,8 @@ struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port, + return NULL; + } + +-static struct cxl_decoder *util_cxl_decoder_filter(struct cxl_decoder *decoder, +- const char *__ident) ++struct cxl_decoder *util_cxl_decoder_filter(struct cxl_decoder *decoder, ++ const char *__ident) + { + struct cxl_port *port = cxl_decoder_get_port(decoder); + int pid, did; +diff --git a/cxl/filter.h b/cxl/filter.h +index 9557943..c913daf 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -50,6 +50,8 @@ struct cxl_target *util_cxl_target_filter_by_memdev(struct cxl_target *target, + struct cxl_dport *util_cxl_dport_filter_by_memdev(struct cxl_dport *dport, + const char *ident, + const char *serial); ++struct cxl_decoder *util_cxl_decoder_filter(struct cxl_decoder *decoder, ++ const char *__ident); + int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *param); + bool cxl_filter_has(const char *needle, const char *__filter); + #endif /* _CXL_UTIL_FILTER_H_ */ +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index b802e5d..e52896f 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -1121,6 +1121,20 @@ CXL_EXPORT struct cxl_decoder *cxl_decoder_get_next(struct cxl_decoder *decoder) + return list_next(&port->decoders, decoder, list); + } + ++CXL_EXPORT struct cxl_decoder *cxl_decoder_get_last(struct cxl_port *port) ++{ ++ cxl_decoders_init(port); ++ ++ return list_tail(&port->decoders, struct cxl_decoder, list); ++} ++ ++CXL_EXPORT struct cxl_decoder *cxl_decoder_get_prev(struct cxl_decoder *decoder) ++{ ++ struct cxl_port *port = decoder->port; ++ ++ return list_prev(&port->decoders, decoder, list); ++} ++ + CXL_EXPORT struct cxl_ctx *cxl_decoder_get_ctx(struct cxl_decoder *decoder) + { + return decoder->ctx; +@@ -1176,6 +1190,78 @@ cxl_decoder_get_dpa_size(struct cxl_decoder *decoder) + return decoder->dpa_size; + } + ++CXL_EXPORT int cxl_decoder_set_dpa_size(struct cxl_decoder *decoder, ++ unsigned long long size) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ char *path = decoder->dev_buf; ++ int len = decoder->buf_len, rc; ++ char buf[SYSFS_ATTR_SIZE]; ++ ++ if (!cxl_port_is_endpoint(port)) { ++ err(ctx, "%s: not an endpoint decoder\n", ++ cxl_decoder_get_devname(decoder)); ++ return -EINVAL; ++ } ++ ++ if (snprintf(path, len, "%s/dpa_size", decoder->dev_path) >= len) { ++ err(ctx, "%s: buffer too small!\n", ++ cxl_decoder_get_devname(decoder)); ++ return -ENOMEM; ++ } ++ ++ sprintf(buf, "%#llx\n", size); ++ rc = sysfs_write_attr(ctx, path, buf); ++ if (rc < 0) ++ return rc; ++ ++ decoder->dpa_size = size; ++ return 0; ++} ++ ++CXL_EXPORT int cxl_decoder_set_mode(struct cxl_decoder *decoder, ++ enum cxl_decoder_mode mode) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ char *path = decoder->dev_buf; ++ int len = decoder->buf_len, rc; ++ char buf[SYSFS_ATTR_SIZE]; ++ ++ if (!cxl_port_is_endpoint(port)) { ++ err(ctx, "%s: not an endpoint decoder\n", ++ cxl_decoder_get_devname(decoder)); ++ return -EINVAL; ++ } ++ ++ switch (mode) { ++ case CXL_DECODER_MODE_PMEM: ++ sprintf(buf, "pmem"); ++ break; ++ case CXL_DECODER_MODE_RAM: ++ sprintf(buf, "ram"); ++ break; ++ default: ++ err(ctx, "%s: unsupported mode: %d\n", ++ cxl_decoder_get_devname(decoder), mode); ++ return -EINVAL; ++ } ++ ++ if (snprintf(path, len, "%s/mode", decoder->dev_path) >= len) { ++ err(ctx, "%s: buffer too small!\n", ++ cxl_decoder_get_devname(decoder)); ++ return -ENOMEM; ++ } ++ ++ rc = sysfs_write_attr(ctx, path, buf); ++ if (rc < 0) ++ return rc; ++ ++ decoder->mode = mode; ++ return 0; ++} ++ + CXL_EXPORT enum cxl_decoder_mode + cxl_decoder_get_mode(struct cxl_decoder *decoder) + { +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 88c5a7e..7712de0 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -173,4 +173,8 @@ global: + cxl_decoder_get_dpa_resource; + cxl_decoder_get_dpa_size; + cxl_decoder_get_mode; ++ cxl_decoder_get_last; ++ cxl_decoder_get_prev; ++ cxl_decoder_set_dpa_size; ++ cxl_decoder_set_mode; + } LIBCXL_2; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 1436dc4..33a216e 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -139,6 +139,7 @@ enum cxl_decoder_mode { + CXL_DECODER_MODE_PMEM, + CXL_DECODER_MODE_RAM, + }; ++ + static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode) + { + static const char *names[] = { +@@ -154,6 +155,10 @@ static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode) + } + + enum cxl_decoder_mode cxl_decoder_get_mode(struct cxl_decoder *decoder); ++int cxl_decoder_set_mode(struct cxl_decoder *decoder, ++ enum cxl_decoder_mode mode); ++int cxl_decoder_set_dpa_size(struct cxl_decoder *decoder, ++ unsigned long long size); + const char *cxl_decoder_get_devname(struct cxl_decoder *decoder); + struct cxl_target *cxl_decoder_get_target_by_memdev(struct cxl_decoder *decoder, + struct cxl_memdev *memdev); +@@ -182,6 +187,10 @@ bool cxl_decoder_is_locked(struct cxl_decoder *decoder); + for (decoder = cxl_decoder_get_first(port); decoder != NULL; \ + decoder = cxl_decoder_get_next(decoder)) + ++#define cxl_decoder_foreach_reverse(port, decoder) \ ++ for (decoder = cxl_decoder_get_last(port); decoder != NULL; \ ++ decoder = cxl_decoder_get_prev(decoder)) ++ + struct cxl_target; + struct cxl_target *cxl_target_get_first(struct cxl_decoder *decoder); + struct cxl_target *cxl_target_get_next(struct cxl_target *target); +diff --git a/cxl/memdev.c b/cxl/memdev.c +index 1cecad2..e42f554 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -33,6 +33,7 @@ static struct parameters { + bool align; + const char *type; + const char *size; ++ const char *decoder_filter; + } param; + + static struct log_ctx ml; +@@ -71,6 +72,19 @@ OPT_STRING('s', "size", ¶m.size, "size", \ + OPT_BOOLEAN('a', "align", ¶m.align, \ + "auto-align --size per device's requirement") + ++#define RESERVE_DPA_OPTIONS() \ ++OPT_STRING('s', "size", ¶m.size, "size", \ ++ "size in bytes (Default: all available capacity)") ++ ++#define DPA_OPTIONS() \ ++OPT_STRING('d', "decoder", ¶m.decoder_filter, \ ++ "decoder instance id", \ ++ "override the automatic decoder selection"), \ ++OPT_STRING('t', "type", ¶m.type, "type", \ ++ "'pmem' or 'ram' (volatile) (Default: 'pmem')"), \ ++OPT_BOOLEAN('f', "force", ¶m.force, \ ++ "Attempt 'expected to fail' operations") ++ + static const struct option read_options[] = { + BASE_OPTIONS(), + LABEL_OPTIONS(), +@@ -108,6 +122,242 @@ static const struct option set_partition_options[] = { + OPT_END(), + }; + ++static const struct option reserve_dpa_options[] = { ++ BASE_OPTIONS(), ++ RESERVE_DPA_OPTIONS(), ++ DPA_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option free_dpa_options[] = { ++ BASE_OPTIONS(), ++ DPA_OPTIONS(), ++ OPT_END(), ++}; ++ ++enum reserve_dpa_mode { ++ DPA_ALLOC, ++ DPA_FREE, ++}; ++ ++static int __reserve_dpa(struct cxl_memdev *memdev, ++ enum reserve_dpa_mode alloc_mode, ++ struct action_context *actx) ++{ ++ struct cxl_decoder *decoder, *auto_target = NULL, *target = NULL; ++ struct cxl_endpoint *endpoint = cxl_memdev_get_endpoint(memdev); ++ const char *devname = cxl_memdev_get_devname(memdev); ++ unsigned long long avail_dpa, size; ++ enum cxl_decoder_mode mode; ++ struct cxl_port *port; ++ char buf[256]; ++ int rc; ++ ++ if (param.type) { ++ if (strcmp(param.type, "ram") == 0) ++ mode = CXL_DECODER_MODE_RAM; ++ else if (strcmp(param.type, "volatile") == 0) ++ mode = CXL_DECODER_MODE_RAM; ++ else if (strcmp(param.type, "ram") == 0) ++ mode = CXL_DECODER_MODE_RAM; ++ else if (strcmp(param.type, "pmem") == 0) ++ mode = CXL_DECODER_MODE_PMEM; ++ else { ++ log_err(&ml, "%s: unsupported type: %s\n", devname, ++ param.type); ++ return -EINVAL; ++ } ++ } else ++ mode = CXL_DECODER_MODE_RAM; ++ ++ if (!endpoint) { ++ log_err(&ml, "%s: CXL operation disabled\n", devname); ++ return -ENXIO; ++ } ++ ++ port = cxl_endpoint_get_port(endpoint); ++ ++ if (mode == CXL_DECODER_MODE_RAM) ++ avail_dpa = cxl_memdev_get_ram_size(memdev); ++ else ++ avail_dpa = cxl_memdev_get_pmem_size(memdev); ++ ++ cxl_decoder_foreach(port, decoder) { ++ size = cxl_decoder_get_dpa_size(decoder); ++ if (size == ULLONG_MAX) ++ continue; ++ if (cxl_decoder_get_mode(decoder) != mode) ++ continue; ++ ++ if (size > avail_dpa) { ++ log_err(&ml, "%s: capacity accounting error\n", ++ devname); ++ return -ENXIO; ++ } ++ avail_dpa -= size; ++ } ++ ++ if (!param.size) ++ if (alloc_mode == DPA_ALLOC) { ++ size = avail_dpa; ++ if (!avail_dpa) { ++ log_err(&ml, "%s: no available capacity\n", ++ devname); ++ return -ENOSPC; ++ } ++ } else ++ size = 0; ++ else { ++ size = parse_size64(param.size); ++ if (size == ULLONG_MAX) { ++ log_err(&ml, "%s: failed to parse size option '%s'\n", ++ devname, param.size); ++ return -EINVAL; ++ } ++ if (size > avail_dpa) { ++ log_err(&ml, "%s: '%s' exceeds available capacity\n", ++ devname, param.size); ++ if (!param.force) ++ return -ENOSPC; ++ } ++ } ++ ++ /* ++ * Find next free decoder, assumes cxl_decoder_foreach() is in ++ * hardware instance-id order ++ */ ++ if (alloc_mode == DPA_ALLOC) ++ cxl_decoder_foreach(port, decoder) { ++ /* first 0-dpa_size is our target */ ++ if (cxl_decoder_get_dpa_size(decoder) == 0) { ++ auto_target = decoder; ++ break; ++ } ++ } ++ else ++ cxl_decoder_foreach_reverse(port, decoder) { ++ /* nothing to free? */ ++ if (!cxl_decoder_get_dpa_size(decoder)) ++ continue; ++ /* ++ * Active decoders can't be freed, and by definition all ++ * previous decoders must also be active ++ */ ++ if (cxl_decoder_get_size(decoder)) ++ break; ++ /* first dpa_size > 0 + disabled decoder is our target */ ++ if (cxl_decoder_get_dpa_size(decoder) < ULLONG_MAX) { ++ auto_target = decoder; ++ break; ++ } ++ } ++ ++ if (param.decoder_filter) { ++ unsigned long id; ++ char *end; ++ ++ id = strtoul(param.decoder_filter, &end, 0); ++ /* allow for standalone ordinal decoder ids */ ++ if (*end == '\0') ++ rc = snprintf(buf, sizeof(buf), "decoder%d.%ld", ++ cxl_port_get_id(port), id); ++ else ++ rc = snprintf(buf, sizeof(buf), "%s", ++ param.decoder_filter); ++ ++ if (rc >= (int)sizeof(buf)) { ++ log_err(&ml, "%s: decoder filter '%s' too long\n", ++ devname, param.decoder_filter); ++ return -EINVAL; ++ } ++ ++ if (alloc_mode == DPA_ALLOC) ++ cxl_decoder_foreach(port, decoder) { ++ target = util_cxl_decoder_filter(decoder, buf); ++ if (target) ++ break; ++ } ++ else ++ cxl_decoder_foreach_reverse(port, decoder) { ++ target = util_cxl_decoder_filter(decoder, buf); ++ if (target) ++ break; ++ } ++ ++ if (!target) { ++ log_err(&ml, "%s: no match for decoder: '%s'\n", ++ devname, param.decoder_filter); ++ return -ENXIO; ++ } ++ ++ if (target != auto_target) { ++ log_err(&ml, "%s: %s is out of sequence\n", devname, ++ cxl_decoder_get_devname(target)); ++ if (!param.force) ++ return -EINVAL; ++ } ++ } ++ ++ if (!target) ++ target = auto_target; ++ ++ if (!target) { ++ log_err(&ml, "%s: no suitable decoder found\n", devname); ++ return -ENXIO; ++ } ++ ++ if (cxl_decoder_get_mode(target) != mode) { ++ rc = cxl_decoder_set_dpa_size(target, 0); ++ if (rc) { ++ log_err(&ml, ++ "%s: %s: failed to clear allocation to set mode\n", ++ devname, cxl_decoder_get_devname(target)); ++ return rc; ++ } ++ rc = cxl_decoder_set_mode(target, mode); ++ if (rc) { ++ log_err(&ml, "%s: %s: failed to set %s mode\n", devname, ++ cxl_decoder_get_devname(target), ++ mode == CXL_DECODER_MODE_PMEM ? "pmem" : "ram"); ++ return rc; ++ } ++ } ++ ++ rc = cxl_decoder_set_dpa_size(target, size); ++ if (rc) ++ log_err(&ml, "%s: %s: failed to set dpa allocation\n", devname, ++ cxl_decoder_get_devname(target)); ++ else { ++ struct json_object *jdev, *jdecoder; ++ unsigned long flags = 0; ++ ++ if (actx->f_out == stdout && isatty(1)) ++ flags |= UTIL_JSON_HUMAN; ++ jdev = util_cxl_memdev_to_json(memdev, flags); ++ jdecoder = util_cxl_decoder_to_json(target, flags); ++ if (!jdev || !jdecoder) { ++ json_object_put(jdev); ++ json_object_put(jdecoder); ++ } else { ++ json_object_object_add(jdev, "decoder", jdecoder); ++ json_object_array_add(actx->jdevs, jdev); ++ } ++ } ++ return rc; ++} ++ ++static int action_reserve_dpa(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ return __reserve_dpa(memdev, DPA_ALLOC, actx); ++} ++ ++static int action_free_dpa(struct cxl_memdev *memdev, ++ struct action_context *actx) ++{ ++ return __reserve_dpa(memdev, DPA_FREE, actx); ++} ++ + static int action_disable(struct cxl_memdev *memdev, struct action_context *actx) + { + if (!cxl_memdev_is_enabled(memdev)) +@@ -452,7 +702,8 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + err++; + } + +- if (action == action_setpartition) ++ if (action == action_setpartition || action == action_reserve_dpa || ++ action == action_free_dpa) + actx.jdevs = json_object_new_array(); + + if (err == argc) { +@@ -495,6 +746,8 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + count = 0; + + for (i = 0; i < argc; i++) { ++ bool found = false; ++ + cxl_memdev_foreach(ctx, memdev) { + const char *memdev_filter = NULL; + const char *serial_filter = NULL; +@@ -507,6 +760,7 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + if (!util_cxl_memdev_filter(memdev, memdev_filter, + serial_filter)) + continue; ++ found = true; + + if (action == action_write) { + single = memdev; +@@ -519,6 +773,8 @@ static int memdev_action(int argc, const char **argv, struct cxl_ctx *ctx, + else if (rc && !err) + err = rc; + } ++ if (!found) ++ log_info(&ml, "no memdev matches %s\n", argv[i]); + } + rc = err; + +@@ -622,3 +878,25 @@ int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx) + + return count >= 0 ? 0 : EXIT_FAILURE; + } ++ ++int cmd_reserve_dpa(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int count = memdev_action( ++ argc, argv, ctx, action_reserve_dpa, reserve_dpa_options, ++ "cxl reserve-dpa [..] []"); ++ log_info(&ml, "reservation completed on %d mem device%s\n", ++ count >= 0 ? count : 0, count > 1 ? "s" : ""); ++ ++ return count >= 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_free_dpa(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ int count = memdev_action( ++ argc, argv, ctx, action_free_dpa, free_dpa_options, ++ "cxl free-dpa [..] []"); ++ log_info(&ml, "reservation release completed on %d mem device%s\n", ++ count >= 0 ? count : 0, count > 1 ? "s" : ""); ++ ++ return count >= 0 ? 0 : EXIT_FAILURE; ++} +-- +2.27.0 + diff --git a/SOURCES/0185-cxl-test-Update-CXL-memory-parameters.patch b/SOURCES/0185-cxl-test-Update-CXL-memory-parameters.patch new file mode 100644 index 0000000..c6c6b07 --- /dev/null +++ b/SOURCES/0185-cxl-test-Update-CXL-memory-parameters.patch @@ -0,0 +1,86 @@ +From 74a8134ef2dfa3c22c7e22b0bfd30b6a5cdf678b Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:02:49 -0700 +Subject: [PATCH 185/217] cxl/test: Update CXL memory parameters + +In support of testing CXL region configurations cxl_test changed the size +of its root decoders and endpoints. Use the size of the first root decoder +to determine if this is an updated kernel. + +Link: https://lore.kernel.org/r/165781816971.1555691.18362747345754213762.stgit@dwillia2-xfh.jf.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/cxl-topology.sh | 32 +++++++++++++++++++++----------- + 1 file changed, 21 insertions(+), 11 deletions(-) + +diff --git a/test/cxl-topology.sh b/test/cxl-topology.sh +index ff11614..2583005 100644 +--- a/test/cxl-topology.sh ++++ b/test/cxl-topology.sh +@@ -64,14 +64,9 @@ switch[2]=$(jq -r ".[] | .[\"ports:${bridge[1]}\"] | $port_sort | .[0].host" <<< + switch[3]=$(jq -r ".[] | .[\"ports:${bridge[1]}\"] | $port_sort | .[1].host" <<< $json) + + +-# check that all 8 cxl_test memdevs are enabled by default and have a +-# pmem size of 256M +-json=$($CXL list -b cxl_test -M) +-count=$(jq "map(select(.pmem_size == $((256 << 20)))) | length" <<< $json) +-((count == 8)) || err "$LINENO" +- +- + # validate the expected properties of the 4 root decoders ++# use the size of the first decoder to determine the cxl_test version / ++# properties + json=$($CXL list -b cxl_test -D -d root) + port_id=${root:4} + port_id_len=${#port_id} +@@ -80,26 +75,41 @@ count=$(jq "[ $decoder_sort | .[0] | + select(.volatile_capable == true) | + select(.size == $((256 << 20))) | + select(.nr_targets == 1) ] | length" <<< $json) +-((count == 1)) || err "$LINENO" ++ ++if [ $count -eq 1 ]; then ++ decoder_base_size=$((256 << 20)) ++ pmem_size=$((256 << 20)) ++else ++ decoder_base_size=$((1 << 30)) ++ pmem_size=$((1 << 30)) ++fi + + count=$(jq "[ $decoder_sort | .[1] | + select(.volatile_capable == true) | +- select(.size == $((512 << 20))) | ++ select(.size == $((decoder_base_size * 2))) | + select(.nr_targets == 2) ] | length" <<< $json) + ((count == 1)) || err "$LINENO" + + count=$(jq "[ $decoder_sort | .[2] | + select(.pmem_capable == true) | +- select(.size == $((256 << 20))) | ++ select(.size == $decoder_base_size) | + select(.nr_targets == 1) ] | length" <<< $json) + ((count == 1)) || err "$LINENO" + + count=$(jq "[ $decoder_sort | .[3] | + select(.pmem_capable == true) | +- select(.size == $((512 << 20))) | ++ select(.size == $((decoder_base_size * 2))) | + select(.nr_targets == 2) ] | length" <<< $json) + ((count == 1)) || err "$LINENO" + ++ ++# check that all 8 cxl_test memdevs are enabled by default and have a ++# pmem size of 256M, or 1G ++json=$($CXL list -b cxl_test -M) ++count=$(jq "map(select(.pmem_size == $pmem_size)) | length" <<< $json) ++((count == 8)) || err "$LINENO" ++ ++ + # check that switch ports disappear after all of their memdevs have been + # disabled, and return when the memdevs are enabled. + for s in ${switch[@]} +-- +2.27.0 + diff --git a/SOURCES/0186-cxl-test-Checkout-region-setup-teardown.patch b/SOURCES/0186-cxl-test-Checkout-region-setup-teardown.patch new file mode 100644 index 0000000..a226737 --- /dev/null +++ b/SOURCES/0186-cxl-test-Checkout-region-setup-teardown.patch @@ -0,0 +1,169 @@ +From eef9685245d172a80e9a5dfd830942824e7d40b4 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Thu, 14 Jul 2022 10:02:55 -0700 +Subject: [PATCH 186/217] cxl/test: Checkout region setup/teardown + +Exercise the fundamental region provisioning sysfs mechanisms of discovering +available DPA capacity, allocating DPA to a region, and programming HDM +decoders. + +Link: https://lore.kernel.org/r/165781817516.1555691.3557156570639615515.stgit@dwillia2-xfh.jf.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/cxl-region-sysfs.sh | 122 +++++++++++++++++++++++++++++++++++++++ + test/meson.build | 2 + + 2 files changed, 124 insertions(+) + create mode 100644 test/cxl-region-sysfs.sh + +diff --git a/test/cxl-region-sysfs.sh b/test/cxl-region-sysfs.sh +new file mode 100644 +index 0000000..2582edb +--- /dev/null ++++ b/test/cxl-region-sysfs.sh +@@ -0,0 +1,122 @@ ++#!/bin/bash ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (C) 2022 Intel Corporation. All rights reserved. ++ ++. $(dirname $0)/common ++ ++rc=1 ++ ++set -ex ++ ++trap 'err $LINENO' ERR ++ ++check_prereq "jq" ++ ++modprobe -r cxl_test ++modprobe cxl_test ++udevadm settle ++ ++# THEORY OF OPERATION: Create a x8 interleave across the pmem capacity ++# of the 8 endpoints defined by cxl_test, commit the decoders (which ++# just stubs out the actual hardware programming aspect, but updates the ++# driver state), and then tear it all down again. As with other cxl_test ++# tests if the CXL topology in tools/testing/cxl/test/cxl.c ever changes ++# then the paired update must be made to this test. ++ ++# find the root decoder that spans both test host-bridges and support pmem ++decoder=$($CXL list -b cxl_test -D -d root | jq -r ".[] | ++ select(.pmem_capable == true) | ++ select(.nr_targets == 2) | ++ .decoder") ++ ++# find the memdevs mapped by that decoder ++readarray -t mem < <($CXL list -M -d $decoder | jq -r ".[].memdev") ++ ++# ask cxl reserve-dpa to allocate pmem capacity from each of those memdevs ++readarray -t endpoint < <($CXL reserve-dpa -t pmem ${mem[*]} -s $((256<<20)) | ++ jq -r ".[] | .decoder.decoder") ++ ++# instantiate an empty region ++region=$(cat /sys/bus/cxl/devices/$decoder/create_pmem_region) ++echo $region > /sys/bus/cxl/devices/$decoder/create_pmem_region ++uuidgen > /sys/bus/cxl/devices/$region/uuid ++ ++# setup interleave geometry ++nr_targets=${#endpoint[@]} ++echo $nr_targets > /sys/bus/cxl/devices/$region/interleave_ways ++g=$(cat /sys/bus/cxl/devices/$decoder/interleave_granularity) ++echo $g > /sys/bus/cxl/devices/$region/interleave_granularity ++echo $((nr_targets * (256<<20))) > /sys/bus/cxl/devices/$region/size ++ ++# grab the list of memdevs grouped by host-bridge interleave position ++port_dev0=$($CXL list -T -d $decoder | jq -r ".[] | ++ .targets | .[] | select(.position == 0) | .target") ++port_dev1=$($CXL list -T -d $decoder | jq -r ".[] | ++ .targets | .[] | select(.position == 1) | .target") ++readarray -t mem_sort0 < <($CXL list -M -p $port_dev0 | jq -r ".[] | .memdev") ++readarray -t mem_sort1 < <($CXL list -M -p $port_dev1 | jq -r ".[] | .memdev") ++ ++# TODO: add a cxl list option to list memdevs in valid region provisioning ++# order, hardcode for now. ++mem_sort=() ++mem_sort[0]=${mem_sort0[0]} ++mem_sort[1]=${mem_sort1[0]} ++mem_sort[2]=${mem_sort0[2]} ++mem_sort[3]=${mem_sort1[2]} ++mem_sort[4]=${mem_sort0[1]} ++mem_sort[5]=${mem_sort1[1]} ++mem_sort[6]=${mem_sort0[3]} ++mem_sort[7]=${mem_sort1[3]} ++ ++# TODO: use this alternative memdev ordering to validate a negative test for ++# specifying invalid positions of memdevs ++#mem_sort[2]=${mem_sort0[0]} ++#mem_sort[1]=${mem_sort1[0]} ++#mem_sort[0]=${mem_sort0[2]} ++#mem_sort[3]=${mem_sort1[2]} ++#mem_sort[4]=${mem_sort0[1]} ++#mem_sort[5]=${mem_sort1[1]} ++#mem_sort[6]=${mem_sort0[3]} ++#mem_sort[7]=${mem_sort1[3]} ++ ++# re-generate the list of endpoint decoders in region position programming order ++endpoint=() ++for i in ${mem_sort[@]} ++do ++ readarray -O ${#endpoint[@]} -t endpoint < <($CXL list -Di -d endpoint -m $i | jq -r ".[] | ++ select(.mode == \"pmem\") | .decoder") ++done ++ ++# attach all endpoint decoders to the region ++pos=0 ++for i in ${endpoint[@]} ++do ++ echo $i > /sys/bus/cxl/devices/$region/target$pos ++ pos=$((pos+1)) ++done ++echo "$region added ${#endpoint[@]} targets: ${endpoint[@]}" ++ ++# walk up the topology and commit all decoders ++echo 1 > /sys/bus/cxl/devices/$region/commit ++ ++# walk down the topology and de-commit all decoders ++echo 0 > /sys/bus/cxl/devices/$region/commit ++ ++# remove endpoints from the region ++pos=0 ++for i in ${endpoint[@]} ++do ++ echo "" > /sys/bus/cxl/devices/$region/target$pos ++ pos=$((pos+1)) ++done ++ ++# release DPA capacity ++readarray -t endpoint < <($CXL free-dpa -t pmem ${mem[*]} | ++ jq -r ".[] | .decoder.decoder") ++echo "$region released ${#endpoint[@]} targets: ${endpoint[@]}" ++ ++# validate no WARN or lockdep report during the run ++log=$(journalctl -r -k --since "-$((SECONDS+1))s") ++grep -q "Call Trace" <<< $log && err "$LINENO" ++ ++modprobe -r cxl_test +diff --git a/test/meson.build b/test/meson.build +index 210dcb0..3203d9c 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -151,6 +151,7 @@ max_extent = find_program('max_available_extent_ns.sh') + pfn_meta_errors = find_program('pfn-meta-errors.sh') + track_uuid = find_program('track-uuid.sh') + cxl_topo = find_program('cxl-topology.sh') ++cxl_sysfs = find_program('cxl-region-sysfs.sh') + + tests = [ + [ 'libndctl', libndctl, 'ndctl' ], +@@ -176,6 +177,7 @@ tests = [ + [ 'pfn-meta-errors.sh', pfn_meta_errors, 'ndctl' ], + [ 'track-uuid.sh', track_uuid, 'ndctl' ], + [ 'cxl-topology.sh', cxl_topo, 'cxl' ], ++ [ 'cxl-region-sysfs.sh', cxl_sysfs, 'cxl' ], + ] + + if get_option('destructive').enabled() +-- +2.27.0 + diff --git a/SOURCES/0187-cxl-test-add-a-test-to-read-write-zero-labels.patch b/SOURCES/0187-cxl-test-add-a-test-to-read-write-zero-labels.patch new file mode 100644 index 0000000..f8c2f05 --- /dev/null +++ b/SOURCES/0187-cxl-test-add-a-test-to-read-write-zero-labels.patch @@ -0,0 +1,121 @@ +From f149f539e874415c0ec19b43a2c9bf2c56f3aa80 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Wed, 13 Jul 2022 14:37:58 -0600 +Subject: [PATCH 187/217] cxl/test: add a test to {read,write,zero}-labels + +Add a unit test to test writing, reading, and zeroing LSA aread for +cxl_test based memdevs using ndctl commands, and reading using cxl-cli +commands to exercise that route as much as possible. + +Note that writing using cxl-cli requires a bit more enabling to enable, +as the corresponding nvdimm-bridge object will need to be disabled +first. + +Link: https://lore.kernel.org/r/20220713203758.519892-1-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/cxl-labels.sh | 69 ++++++++++++++++++++++++++++++++++++++++++++++ + test/meson.build | 2 ++ + 2 files changed, 71 insertions(+) + create mode 100644 test/cxl-labels.sh + +diff --git a/test/cxl-labels.sh b/test/cxl-labels.sh +new file mode 100644 +index 0000000..e782e2d +--- /dev/null ++++ b/test/cxl-labels.sh +@@ -0,0 +1,69 @@ ++#!/bin/bash ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (C) 2022 Intel Corporation. All rights reserved. ++ ++. $(dirname $0)/common ++ ++rc=1 ++ ++set -ex ++ ++trap 'err $LINENO' ERR ++ ++check_prereq "jq" ++ ++modprobe -r cxl_test ++modprobe cxl_test ++udevadm settle ++ ++test_label_ops() ++{ ++ nmem="$1" ++ lsa=$(mktemp /tmp/lsa-$nmem.XXXX) ++ lsa_read=$(mktemp /tmp/lsa-read-$nmem.XXXX) ++ ++ # determine LSA size ++ "$NDCTL" read-labels -o "$lsa_read" "$nmem" ++ lsa_size=$(stat -c %s "$lsa_read") ++ ++ dd "if=/dev/urandom" "of=$lsa" "bs=$lsa_size" "count=1" ++ "$NDCTL" write-labels -i "$lsa" "$nmem" ++ "$NDCTL" read-labels -o "$lsa_read" "$nmem" ++ ++ # compare what was written vs read ++ diff "$lsa" "$lsa_read" ++ ++ # zero the LSA and test ++ "$NDCTL" zero-labels "$nmem" ++ dd "if=/dev/zero" "of=$lsa" "bs=$lsa_size" "count=1" ++ "$NDCTL" read-labels -o "$lsa_read" "$nmem" ++ diff "$lsa" "$lsa_read" ++ ++ # cleanup ++ rm "$lsa" "$lsa_read" ++} ++ ++test_label_ops_cxl() ++{ ++ mem="$1" ++ lsa_read=$(mktemp /tmp/lsa-read-$mem.XXXX) ++ ++ "$CXL" read-labels -o "$lsa_read" "$mem" ++ rm "$lsa_read" ++} ++ ++# test reading labels directly through cxl-cli ++readarray -t mems < <("$CXL" list -b cxl_test -Mi | jq -r '.[].memdev') ++ ++for mem in ${mems[@]}; do ++ test_label_ops_cxl "$mem" ++done ++ ++# find nmem devices corresponding to cxl memdevs ++readarray -t nmems < <("$NDCTL" list -b cxl_test -Di | jq -r '.[].dev') ++ ++for nmem in ${nmems[@]}; do ++ test_label_ops "$nmem" ++done ++ ++modprobe -r cxl_test +diff --git a/test/meson.build b/test/meson.build +index 3203d9c..b382f46 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -152,6 +152,7 @@ pfn_meta_errors = find_program('pfn-meta-errors.sh') + track_uuid = find_program('track-uuid.sh') + cxl_topo = find_program('cxl-topology.sh') + cxl_sysfs = find_program('cxl-region-sysfs.sh') ++cxl_labels = find_program('cxl-labels.sh') + + tests = [ + [ 'libndctl', libndctl, 'ndctl' ], +@@ -178,6 +179,7 @@ tests = [ + [ 'track-uuid.sh', track_uuid, 'ndctl' ], + [ 'cxl-topology.sh', cxl_topo, 'cxl' ], + [ 'cxl-region-sysfs.sh', cxl_sysfs, 'cxl' ], ++ [ 'cxl-labels.sh', cxl_labels, 'cxl' ], + ] + + if get_option('destructive').enabled() +-- +2.27.0 + diff --git a/SOURCES/0188-cxl-list-Clarify-B-vs-P-p-root.patch b/SOURCES/0188-cxl-list-Clarify-B-vs-P-p-root.patch new file mode 100644 index 0000000..d2c5f2c --- /dev/null +++ b/SOURCES/0188-cxl-list-Clarify-B-vs-P-p-root.patch @@ -0,0 +1,93 @@ +From db55c5254d932d8d1be1fc082ea7a919def47a5d Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Tue, 26 Jul 2022 18:54:54 -0700 +Subject: [PATCH 188/217] cxl/list: Clarify "-B" vs "-P -p root" + +Matthew notes that 'cxl list' documentation claims that 'cxl list -B' and +'cxl list -P -p root' are equivalent. Which they are not: + +$ cxl list -B +[ + { + "bus":"root0", + "provider":"ACPI.CXL" + } +] + +$ cxl list -P -p root +[ + { + "bus":"root0", + "provider":"ACPI.CXL", + "ports:root0":[ + { + "port":"port1", + "host":"ACPI0016:00" + } + ] + } +] + +Clarify that '--single' needs to be appended to '-P -p root' to make it +equivalent to 'cxl list -B'. This is due to the behavior that listing ports +includes all descendants of a port that matches the filter. In the case of +'-P -p root' that results in all enabled ports. + +$ cxl list -P -p root -S +[ + { + "bus":"root0", + "provider":"ACPI.CXL" + } +] + +Link: https://lore.kernel.org/r/165888675979.3375698.5785835464908538946.stgit@dwillia2-xfh.jf.intel.com +Cc: Adam Manzanares +Reported-by: Matthew Ho +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index f6aba0c..088ea70 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -255,19 +255,27 @@ OPTIONS + --port=:: + Specify CXL Port device name(s), device id(s), and or port type + names to filter the listing. The supported port type names are "root" +- and "switch". Note that since a bus object is also a port, the following +- two syntaxes are equivalent: ++ and "switch". Note that a bus object is also a port, so the ++ following two syntaxes are equivalent: + ---- + # cxl list -B +-# cxl list -P -p root ++# cxl list -P -p root -S + ---- +- Additionally, endpoint objects are also ports so the following commands +- are also equivalent. ++ ...where the '-S/--single' is required since descendant ports are always ++ included in a port listing and '-S/--single' stops after listing the ++ bus. Additionally, endpoint objects are ports so the following commands ++ are equivalent, and no '-S/--single' is required as endpoint ports are ++ terminal: + ---- + # cxl list -E + # cxl list -P -p endpoint + ---- +- By default, only 'switch' ports are listed. ++ By default, only 'switch' ports are listed, i.e. ++---- ++# cxl list -P ++# cxl list -P -p switch ++---- ++ ...are equivalent. + + -S:: + --single:: +-- +2.27.0 + diff --git a/SOURCES/0189-libcxl-add-a-depth-attribute-to-cxl_port.patch b/SOURCES/0189-libcxl-add-a-depth-attribute-to-cxl_port.patch new file mode 100644 index 0000000..c818aad --- /dev/null +++ b/SOURCES/0189-libcxl-add-a-depth-attribute-to-cxl_port.patch @@ -0,0 +1,45 @@ +From 0a43bfdf030b4a84fce562462944a9a44888afaa Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Mon, 15 Aug 2022 13:22:04 -0600 +Subject: [PATCH 189/217] libcxl: add a depth attribute to cxl_port + +Add a depth attribute to the cxl_port structure, that can be used for +calculating its distance from the root port, and will be needed for +interleave granularity calculations during region creation. + +Link: https://lore.kernel.org/r/20220815192214.545800-2-vishal.l.verma@intel.com +Suggested-by: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 1 + + cxl/lib/private.h | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index e52896f..145c6ba 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -744,6 +744,7 @@ static int cxl_port_init(struct cxl_port *port, struct cxl_port *parent_port, + port->type = type; + port->parent = parent_port; + port->type = type; ++ port->depth = parent_port ? parent_port->depth + 1 : 0; + + list_head_init(&port->child_ports); + list_head_init(&port->endpoints); +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index f6d4573..832a815 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -66,6 +66,7 @@ struct cxl_port { + int decoders_init; + int dports_init; + int nr_dports; ++ int depth; + struct cxl_ctx *ctx; + struct cxl_bus *bus; + enum cxl_port_type type; +-- +2.27.0 + diff --git a/SOURCES/0190-cxl-port-Consolidate-the-debug-option-in-cxl-port-ma.patch b/SOURCES/0190-cxl-port-Consolidate-the-debug-option-in-cxl-port-ma.patch new file mode 100644 index 0000000..5b5d585 --- /dev/null +++ b/SOURCES/0190-cxl-port-Consolidate-the-debug-option-in-cxl-port-ma.patch @@ -0,0 +1,82 @@ +From a951e6ddaec82dc0c33fb4e665e20f8fe4ce9caf Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Mon, 15 Aug 2022 13:22:05 -0600 +Subject: [PATCH 190/217] cxl/port: Consolidate the debug option in cxl-port + man pages + +In preparation for additional commands that implement the --debug +option, consolidate the option description from the cxl-port man pages +into an include. + +The port man pages also mentioned the debug option requiring a build +with debug enabled, which wasn't true - so remove that part. + +Link: https://lore.kernel.org/r/20220815192214.545800-3-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-disable-port.txt | 5 +---- + Documentation/cxl/cxl-enable-port.txt | 5 +---- + Documentation/cxl/debug-option.txt | 4 ++++ + Documentation/cxl/meson.build | 1 + + 4 files changed, 7 insertions(+), 8 deletions(-) + create mode 100644 Documentation/cxl/debug-option.txt + +diff --git a/Documentation/cxl/cxl-disable-port.txt b/Documentation/cxl/cxl-disable-port.txt +index ac56f20..7a22efc 100644 +--- a/Documentation/cxl/cxl-disable-port.txt ++++ b/Documentation/cxl/cxl-disable-port.txt +@@ -30,10 +30,7 @@ OPTIONS + firmware and disabling an active device is akin to force removing memory + from a running system. + +---debug:: +- If the cxl tool was built with debug disabled, turn on debug +- messages. +- ++include::debug-option.txt[] + + include::../copyright.txt[] + +diff --git a/Documentation/cxl/cxl-enable-port.txt b/Documentation/cxl/cxl-enable-port.txt +index 9a37cef..50b53d1 100644 +--- a/Documentation/cxl/cxl-enable-port.txt ++++ b/Documentation/cxl/cxl-enable-port.txt +@@ -31,10 +31,7 @@ OPTIONS + memdev is only enabled after all CXL ports in its device topology + ancestry are enabled. + +---debug:: +- If the cxl tool was built with debug enabled, turn on debug +- messages. +- ++include::debug-option.txt[] + + include::../copyright.txt[] + +diff --git a/Documentation/cxl/debug-option.txt b/Documentation/cxl/debug-option.txt +new file mode 100644 +index 0000000..70b922f +--- /dev/null ++++ b/Documentation/cxl/debug-option.txt +@@ -0,0 +1,4 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++--debug:: ++ Turn on additional debug messages including library debug. +diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build +index d019dfc..423be90 100644 +--- a/Documentation/cxl/meson.build ++++ b/Documentation/cxl/meson.build +@@ -22,6 +22,7 @@ filedeps = [ + '../copyright.txt', + 'memdev-option.txt', + 'labels-options.txt', ++ 'debug-option.txt', + ] + + cxl_manpages = [ +-- +2.27.0 + diff --git a/SOURCES/0191-cxl-memdev-refactor-decoder-mode-string-parsing.patch b/SOURCES/0191-cxl-memdev-refactor-decoder-mode-string-parsing.patch new file mode 100644 index 0000000..f49514a --- /dev/null +++ b/SOURCES/0191-cxl-memdev-refactor-decoder-mode-string-parsing.patch @@ -0,0 +1,74 @@ +From 14565442634cfab0aac8823129a175be572fb11e Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Mon, 15 Aug 2022 13:22:06 -0600 +Subject: [PATCH 191/217] cxl/memdev: refactor decoder mode string parsing + +In preparation for create-region to use a similar decoder mode string +to enum operation, break out the mode string parsing into its own inline +helper in libcxl.h, and call it from memdev.c:__reserve_dpa(). + +Link: https://lore.kernel.org/r/20220815192214.545800-4-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/libcxl.h | 13 +++++++++++++ + cxl/memdev.c | 11 ++--------- + 2 files changed, 15 insertions(+), 9 deletions(-) + +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 33a216e..c1f8d14 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -4,6 +4,7 @@ + #define _LIBCXL_H_ + + #include ++#include + #include + #include + +@@ -154,6 +155,18 @@ static inline const char *cxl_decoder_mode_name(enum cxl_decoder_mode mode) + return names[mode]; + } + ++static inline enum cxl_decoder_mode ++cxl_decoder_mode_from_ident(const char *ident) ++{ ++ if (strcmp(ident, "ram") == 0) ++ return CXL_DECODER_MODE_RAM; ++ else if (strcmp(ident, "volatile") == 0) ++ return CXL_DECODER_MODE_RAM; ++ else if (strcmp(ident, "pmem") == 0) ++ return CXL_DECODER_MODE_PMEM; ++ return CXL_DECODER_MODE_NONE; ++} ++ + enum cxl_decoder_mode cxl_decoder_get_mode(struct cxl_decoder *decoder); + int cxl_decoder_set_mode(struct cxl_decoder *decoder, + enum cxl_decoder_mode mode); +diff --git a/cxl/memdev.c b/cxl/memdev.c +index e42f554..0b3ad02 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -154,15 +154,8 @@ static int __reserve_dpa(struct cxl_memdev *memdev, + int rc; + + if (param.type) { +- if (strcmp(param.type, "ram") == 0) +- mode = CXL_DECODER_MODE_RAM; +- else if (strcmp(param.type, "volatile") == 0) +- mode = CXL_DECODER_MODE_RAM; +- else if (strcmp(param.type, "ram") == 0) +- mode = CXL_DECODER_MODE_RAM; +- else if (strcmp(param.type, "pmem") == 0) +- mode = CXL_DECODER_MODE_PMEM; +- else { ++ mode = cxl_decoder_mode_from_ident(param.type); ++ if (mode == CXL_DECODER_MODE_NONE) { + log_err(&ml, "%s: unsupported type: %s\n", devname, + param.type); + return -EINVAL; +-- +2.27.0 + diff --git a/SOURCES/0192-libcxl-Introduce-libcxl-region-and-mapping-objects.patch b/SOURCES/0192-libcxl-Introduce-libcxl-region-and-mapping-objects.patch new file mode 100644 index 0000000..9a4ca60 --- /dev/null +++ b/SOURCES/0192-libcxl-Introduce-libcxl-region-and-mapping-objects.patch @@ -0,0 +1,651 @@ +From d25dc6d7956bc022d7e4c4453416c52368df291d Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Mon, 15 Aug 2022 13:22:07 -0600 +Subject: [PATCH 192/217] libcxl: Introduce libcxl region and mapping objects + +Add a cxl_region object to libcxl that represents a CXL region. CXL +regions are made up of one or more cxl_memdev 'targets'. The +relationship between a target and a region is conveyed with a +cxl_memdev_mapping object. + +CXL regions are childeren of root decoders, and are organized as such. +Mapping objects are childeren of a CXL region. Introduce the two +classes of objects themselves, and common accessors related to them. + +Link: https://lore.kernel.org/r/20220815192214.545800-5-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + .clang-format | 2 + + cxl/lib/libcxl.c | 442 ++++++++++++++++++++++++++++++++++++++++++++- + cxl/lib/libcxl.sym | 20 ++ + cxl/lib/private.h | 35 ++++ + cxl/libcxl.h | 41 +++++ + 5 files changed, 530 insertions(+), 10 deletions(-) + +diff --git a/.clang-format b/.clang-format +index 7254a1b..b6169e1 100644 +--- a/.clang-format ++++ b/.clang-format +@@ -86,6 +86,8 @@ ForEachMacros: + - 'cxl_dport_foreach' + - 'cxl_endpoint_foreach' + - 'cxl_port_foreach_all' ++ - 'cxl_region_foreach' ++ - 'cxl_region_foreach_safe' + - 'daxctl_dev_foreach' + - 'daxctl_mapping_foreach' + - 'daxctl_region_foreach' +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 145c6ba..ad98188 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -79,6 +79,38 @@ static void free_target(struct cxl_target *target, struct list_head *head) + free(target); + } + ++static void free_region(struct cxl_region *region, struct list_head *head) ++{ ++ struct cxl_memdev_mapping *mapping, *_m; ++ ++ list_for_each_safe(®ion->mappings, mapping, _m, list) { ++ list_del_from(®ion->mappings, &mapping->list); ++ free(mapping); ++ } ++ if (head) ++ list_del_from(head, ®ion->list); ++ kmod_module_unref(region->module); ++ free(region->dev_buf); ++ free(region->dev_path); ++ free(region); ++} ++ ++static void free_stale_regions(struct cxl_decoder *decoder) ++{ ++ struct cxl_region *region, *_r; ++ ++ list_for_each_safe(&decoder->stale_regions, region, _r, list) ++ free_region(region, &decoder->stale_regions); ++} ++ ++static void free_regions(struct cxl_decoder *decoder) ++{ ++ struct cxl_region *region, *_r; ++ ++ list_for_each_safe(&decoder->regions, region, _r, list) ++ free_region(region, &decoder->regions); ++} ++ + static void free_decoder(struct cxl_decoder *decoder, struct list_head *head) + { + struct cxl_target *target, *_t; +@@ -87,6 +119,8 @@ static void free_decoder(struct cxl_decoder *decoder, struct list_head *head) + list_del_from(head, &decoder->list); + list_for_each_safe(&decoder->targets, target, _t, list) + free_target(target, &decoder->targets); ++ free_regions(decoder); ++ free_stale_regions(decoder); + free(decoder->dev_buf); + free(decoder->dev_path); + free(decoder); +@@ -304,6 +338,402 @@ CXL_EXPORT void cxl_set_log_priority(struct cxl_ctx *ctx, int priority) + ctx->ctx.log_priority = priority; + } + ++static int is_enabled(const char *drvpath) ++{ ++ struct stat st; ++ ++ if (lstat(drvpath, &st) < 0 || !S_ISLNK(st.st_mode)) ++ return 0; ++ else ++ return 1; ++} ++ ++CXL_EXPORT int cxl_region_is_enabled(struct cxl_region *region) ++{ ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ char *path = region->dev_buf; ++ int len = region->buf_len; ++ ++ if (snprintf(path, len, "%s/driver", region->dev_path) >= len) { ++ err(ctx, "%s: buffer too small!\n", cxl_region_get_devname(region)); ++ return 0; ++ } ++ ++ return is_enabled(path); ++} ++ ++CXL_EXPORT int cxl_region_disable(struct cxl_region *region) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ ++ util_unbind(region->dev_path, ctx); ++ ++ if (cxl_region_is_enabled(region)) { ++ err(ctx, "%s: failed to disable\n", devname); ++ return -EBUSY; ++ } ++ ++ dbg(ctx, "%s: disabled\n", devname); ++ ++ return 0; ++} ++ ++CXL_EXPORT int cxl_region_enable(struct cxl_region *region) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ char *path = region->dev_buf; ++ int len = region->buf_len; ++ char buf[SYSFS_ATTR_SIZE]; ++ u64 resource = ULLONG_MAX; ++ ++ if (cxl_region_is_enabled(region)) ++ return 0; ++ ++ util_bind(devname, region->module, "cxl", ctx); ++ ++ if (!cxl_region_is_enabled(region)) { ++ err(ctx, "%s: failed to enable\n", devname); ++ return -ENXIO; ++ } ++ ++ /* ++ * Currently 'resource' is the only attr that may change after enabling. ++ * Just refresh it here. If there are additional resources that need ++ * to be refreshed here later, split these out into a common helper ++ * for this and add_cxl_region() ++ */ ++ if (snprintf(path, len, "%s/resource", region->dev_path) >= len) { ++ err(ctx, "%s: buffer too small!\n", devname); ++ return 0; ++ } ++ ++ if (sysfs_read_attr(ctx, path, buf) == 0) ++ resource = strtoull(buf, NULL, 0); ++ ++ if (resource < ULLONG_MAX) ++ region->start = resource; ++ ++ dbg(ctx, "%s: enabled\n", devname); ++ ++ return 0; ++} ++ ++static void *add_cxl_region(void *parent, int id, const char *cxlregion_base) ++{ ++ const char *devname = devpath_to_devname(cxlregion_base); ++ char *path = calloc(1, strlen(cxlregion_base) + 100); ++ struct cxl_region *region, *region_dup, *_r; ++ struct cxl_decoder *decoder = parent; ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ char buf[SYSFS_ATTR_SIZE]; ++ u64 resource = ULLONG_MAX; ++ ++ dbg(ctx, "%s: base: \'%s\'\n", devname, cxlregion_base); ++ ++ if (!path) ++ return NULL; ++ ++ region = calloc(1, sizeof(*region)); ++ if (!region) ++ goto err; ++ ++ region->id = id; ++ region->ctx = ctx; ++ region->decoder = decoder; ++ list_head_init(®ion->mappings); ++ ++ region->dev_path = strdup(cxlregion_base); ++ if (!region->dev_path) ++ goto err; ++ ++ region->dev_buf = calloc(1, strlen(cxlregion_base) + 50); ++ if (!region->dev_buf) ++ goto err; ++ region->buf_len = strlen(cxlregion_base) + 50; ++ ++ sprintf(path, "%s/size", cxlregion_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ region->size = ULLONG_MAX; ++ else ++ region->size = strtoull(buf, NULL, 0); ++ ++ sprintf(path, "%s/resource", cxlregion_base); ++ if (sysfs_read_attr(ctx, path, buf) == 0) ++ resource = strtoull(buf, NULL, 0); ++ ++ if (resource < ULLONG_MAX) ++ region->start = resource; ++ ++ sprintf(path, "%s/uuid", cxlregion_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ goto err; ++ if (strlen(buf) && uuid_parse(buf, region->uuid) < 0) { ++ dbg(ctx, "%s:%s\n", path, buf); ++ goto err; ++ } ++ ++ sprintf(path, "%s/interleave_granularity", cxlregion_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ region->interleave_granularity = UINT_MAX; ++ else ++ region->interleave_granularity = strtoul(buf, NULL, 0); ++ ++ sprintf(path, "%s/interleave_ways", cxlregion_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ region->interleave_ways = UINT_MAX; ++ else ++ region->interleave_ways = strtoul(buf, NULL, 0); ++ ++ sprintf(path, "%s/commit", cxlregion_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ region->decode_state = CXL_DECODE_UNKNOWN; ++ else ++ region->decode_state = strtoul(buf, NULL, 0); ++ ++ sprintf(path, "%s/modalias", cxlregion_base); ++ if (sysfs_read_attr(ctx, path, buf) == 0) ++ region->module = util_modalias_to_module(ctx, buf); ++ ++ cxl_region_foreach_safe(decoder, region_dup, _r) ++ if (region_dup->id == region->id) { ++ list_del_from(&decoder->regions, ®ion_dup->list); ++ list_add_tail(&decoder->stale_regions, ++ ®ion_dup->list); ++ break; ++ } ++ ++ list_add(&decoder->regions, ®ion->list); ++ ++ return region; ++err: ++ free(region->dev_path); ++ free(region->dev_buf); ++ free(region); ++ free(path); ++ return NULL; ++} ++ ++static void cxl_regions_init(struct cxl_decoder *decoder) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ ++ if (decoder->regions_init) ++ return; ++ ++ /* Only root port decoders may have child regions */ ++ if (!cxl_port_is_root(port)) ++ return; ++ ++ decoder->regions_init = 1; ++ ++ sysfs_device_parse(ctx, decoder->dev_path, "region", decoder, ++ add_cxl_region); ++} ++ ++CXL_EXPORT struct cxl_region *cxl_region_get_first(struct cxl_decoder *decoder) ++{ ++ cxl_regions_init(decoder); ++ ++ return list_top(&decoder->regions, struct cxl_region, list); ++} ++ ++CXL_EXPORT struct cxl_region *cxl_region_get_next(struct cxl_region *region) ++{ ++ struct cxl_decoder *decoder = region->decoder; ++ ++ return list_next(&decoder->regions, region, list); ++} ++ ++CXL_EXPORT struct cxl_ctx *cxl_region_get_ctx(struct cxl_region *region) ++{ ++ return region->ctx; ++} ++ ++CXL_EXPORT struct cxl_decoder *cxl_region_get_decoder(struct cxl_region *region) ++{ ++ return region->decoder; ++} ++ ++CXL_EXPORT int cxl_region_get_id(struct cxl_region *region) ++{ ++ return region->id; ++} ++ ++CXL_EXPORT const char *cxl_region_get_devname(struct cxl_region *region) ++{ ++ return devpath_to_devname(region->dev_path); ++} ++ ++CXL_EXPORT void cxl_region_get_uuid(struct cxl_region *region, uuid_t uu) ++{ ++ memcpy(uu, region->uuid, sizeof(uuid_t)); ++} ++ ++CXL_EXPORT unsigned long long cxl_region_get_size(struct cxl_region *region) ++{ ++ return region->size; ++} ++ ++CXL_EXPORT unsigned long long cxl_region_get_resource(struct cxl_region *region) ++{ ++ return region->start; ++} ++ ++CXL_EXPORT unsigned int ++cxl_region_get_interleave_ways(struct cxl_region *region) ++{ ++ return region->interleave_ways; ++} ++ ++CXL_EXPORT int cxl_region_decode_is_committed(struct cxl_region *region) ++{ ++ return (region->decode_state == CXL_DECODE_COMMIT) ? 1 : 0; ++} ++ ++CXL_EXPORT unsigned int ++cxl_region_get_interleave_granularity(struct cxl_region *region) ++{ ++ return region->interleave_granularity; ++} ++ ++static struct cxl_decoder *__cxl_port_match_decoder(struct cxl_port *port, ++ const char *ident) ++{ ++ struct cxl_decoder *decoder; ++ ++ cxl_decoder_foreach(port, decoder) ++ if (strcmp(cxl_decoder_get_devname(decoder), ident) == 0) ++ return decoder; ++ ++ return NULL; ++} ++ ++static struct cxl_decoder *cxl_port_find_decoder(struct cxl_port *port, ++ const char *ident) ++{ ++ struct cxl_decoder *decoder; ++ struct cxl_endpoint *ep; ++ ++ /* First, check decoders directly under @port */ ++ decoder = __cxl_port_match_decoder(port, ident); ++ if (decoder) ++ return decoder; ++ ++ /* Next, iterate over the endpoints under @port */ ++ cxl_endpoint_foreach(port, ep) { ++ decoder = __cxl_port_match_decoder(cxl_endpoint_get_port(ep), ++ ident); ++ if (decoder) ++ return decoder; ++ } ++ ++ return NULL; ++} ++ ++static struct cxl_decoder *cxl_decoder_get_by_name(struct cxl_ctx *ctx, ++ const char *ident) ++{ ++ struct cxl_bus *bus; ++ ++ cxl_bus_foreach(ctx, bus) { ++ struct cxl_decoder *decoder; ++ struct cxl_port *port, *top; ++ ++ port = cxl_bus_get_port(bus); ++ decoder = cxl_port_find_decoder(port, ident); ++ if (decoder) ++ return decoder; ++ ++ top = port; ++ cxl_port_foreach_all (top, port) { ++ decoder = cxl_port_find_decoder(port, ident); ++ if (decoder) ++ return decoder; ++ } ++ } ++ ++ return NULL; ++} ++ ++static void cxl_mappings_init(struct cxl_region *region) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ char *mapping_path, buf[SYSFS_ATTR_SIZE]; ++ unsigned int i; ++ ++ if (region->mappings_init) ++ return; ++ region->mappings_init = 1; ++ ++ mapping_path = calloc(1, strlen(region->dev_path) + 100); ++ if (!mapping_path) { ++ err(ctx, "%s: allocation failure\n", devname); ++ return; ++ } ++ ++ for (i = 0; i < region->interleave_ways; i++) { ++ struct cxl_memdev_mapping *mapping; ++ struct cxl_decoder *decoder; ++ ++ sprintf(mapping_path, "%s/target%d", region->dev_path, i); ++ if (sysfs_read_attr(ctx, mapping_path, buf) < 0) { ++ err(ctx, "%s: failed to read target%d\n", devname, i); ++ continue; ++ } ++ ++ decoder = cxl_decoder_get_by_name(ctx, buf); ++ if (!decoder) { ++ err(ctx, "%s target%d: %s lookup failure\n", ++ devname, i, buf); ++ continue; ++ } ++ ++ mapping = calloc(1, sizeof(*mapping)); ++ if (!mapping) { ++ err(ctx, "%s target%d: allocation failure\n", devname, i); ++ continue; ++ } ++ ++ mapping->region = region; ++ mapping->decoder = decoder; ++ mapping->position = i; ++ list_add(®ion->mappings, &mapping->list); ++ } ++ free(mapping_path); ++} ++ ++CXL_EXPORT struct cxl_memdev_mapping * ++cxl_mapping_get_first(struct cxl_region *region) ++{ ++ cxl_mappings_init(region); ++ ++ return list_top(®ion->mappings, struct cxl_memdev_mapping, list); ++} ++ ++CXL_EXPORT struct cxl_memdev_mapping * ++cxl_mapping_get_next(struct cxl_memdev_mapping *mapping) ++{ ++ struct cxl_region *region = mapping->region; ++ ++ return list_next(®ion->mappings, mapping, list); ++} ++ ++CXL_EXPORT struct cxl_decoder * ++cxl_mapping_get_decoder(struct cxl_memdev_mapping *mapping) ++{ ++ return mapping->decoder; ++} ++ ++CXL_EXPORT unsigned int ++cxl_mapping_get_position(struct cxl_memdev_mapping *mapping) ++{ ++ return mapping->position; ++} ++ + static void *add_cxl_pmem(void *parent, int id, const char *br_base) + { + const char *devname = devpath_to_devname(br_base); +@@ -681,16 +1111,6 @@ CXL_EXPORT size_t cxl_memdev_get_label_size(struct cxl_memdev *memdev) + return memdev->lsa_size; + } + +-static int is_enabled(const char *drvpath) +-{ +- struct stat st; +- +- if (lstat(drvpath, &st) < 0 || !S_ISLNK(st.st_mode)) +- return 0; +- else +- return 1; +-} +- + CXL_EXPORT int cxl_memdev_is_enabled(struct cxl_memdev *memdev) + { + struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev); +@@ -940,6 +1360,8 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + decoder->ctx = ctx; + decoder->port = port; + list_head_init(&decoder->targets); ++ list_head_init(&decoder->regions); ++ list_head_init(&decoder->stale_regions); + + decoder->dev_path = strdup(cxldecoder_base); + if (!decoder->dev_path) +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 7712de0..e410298 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -177,4 +177,24 @@ global: + cxl_decoder_get_prev; + cxl_decoder_set_dpa_size; + cxl_decoder_set_mode; ++ cxl_region_get_first; ++ cxl_region_get_next; ++ cxl_region_decode_is_committed; ++ cxl_region_is_enabled; ++ cxl_region_disable; ++ cxl_region_enable; ++ cxl_region_get_ctx; ++ cxl_region_get_decoder; ++ cxl_region_get_id; ++ cxl_region_get_devname; ++ cxl_region_get_uuid; ++ cxl_region_get_size; ++ cxl_region_get_resource; ++ cxl_region_get_interleave_ways; ++ cxl_region_get_interleave_granularity; ++ cxl_mapping_get_first; ++ cxl_mapping_get_next; ++ cxl_mapping_get_decoder; ++ cxl_mapping_get_region; ++ cxl_mapping_get_position; + } LIBCXL_2; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 832a815..5e2fdd5 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -116,7 +116,42 @@ struct cxl_decoder { + bool accelmem_capable; + bool locked; + enum cxl_decoder_target_type target_type; ++ int regions_init; + struct list_head targets; ++ struct list_head regions; ++ struct list_head stale_regions; ++}; ++ ++enum cxl_decode_state { ++ CXL_DECODE_UNKNOWN = -1, ++ CXL_DECODE_RESET = 0, ++ CXL_DECODE_COMMIT, ++}; ++ ++struct cxl_region { ++ struct cxl_decoder *decoder; ++ struct list_node list; ++ int mappings_init; ++ struct cxl_ctx *ctx; ++ void *dev_buf; ++ size_t buf_len; ++ char *dev_path; ++ int id; ++ uuid_t uuid; ++ u64 start; ++ u64 size; ++ unsigned int interleave_ways; ++ unsigned int interleave_granularity; ++ enum cxl_decode_state decode_state; ++ struct kmod_module *module; ++ struct list_head mappings; ++}; ++ ++struct cxl_memdev_mapping { ++ struct cxl_region *region; ++ struct cxl_decoder *decoder; ++ unsigned int position; ++ struct list_node list; + }; + + enum cxl_cmd_query_status { +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index c1f8d14..19d94e4 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -237,6 +237,47 @@ int cxl_memdev_is_enabled(struct cxl_memdev *memdev); + for (endpoint = cxl_endpoint_get_first(port); endpoint != NULL; \ + endpoint = cxl_endpoint_get_next(endpoint)) + ++struct cxl_region; ++struct cxl_region *cxl_region_get_first(struct cxl_decoder *decoder); ++struct cxl_region *cxl_region_get_next(struct cxl_region *region); ++int cxl_region_decode_is_committed(struct cxl_region *region); ++int cxl_region_is_enabled(struct cxl_region *region); ++int cxl_region_disable(struct cxl_region *region); ++int cxl_region_enable(struct cxl_region *region); ++struct cxl_ctx *cxl_region_get_ctx(struct cxl_region *region); ++struct cxl_decoder *cxl_region_get_decoder(struct cxl_region *region); ++int cxl_region_get_id(struct cxl_region *region); ++const char *cxl_region_get_devname(struct cxl_region *region); ++void cxl_region_get_uuid(struct cxl_region *region, uuid_t uu); ++unsigned long long cxl_region_get_size(struct cxl_region *region); ++unsigned long long cxl_region_get_resource(struct cxl_region *region); ++unsigned int cxl_region_get_interleave_ways(struct cxl_region *region); ++unsigned int cxl_region_get_interleave_granularity(struct cxl_region *region); ++ ++#define cxl_region_foreach(decoder, region) \ ++ for (region = cxl_region_get_first(decoder); region != NULL; \ ++ region = cxl_region_get_next(region)) ++ ++#define cxl_region_foreach_safe(decoder, region, _region) \ ++ for (region = cxl_region_get_first(decoder), \ ++ _region = region ? cxl_region_get_next(region) : NULL; \ ++ region != NULL; \ ++ region = _region, \ ++ _region = _region ? cxl_region_get_next(_region) : NULL) ++ ++struct cxl_memdev_mapping; ++struct cxl_memdev_mapping *cxl_mapping_get_first(struct cxl_region *region); ++struct cxl_memdev_mapping * ++cxl_mapping_get_next(struct cxl_memdev_mapping *mapping); ++struct cxl_decoder *cxl_mapping_get_decoder(struct cxl_memdev_mapping *mapping); ++struct cxl_region *cxl_mapping_get_region(struct cxl_memdev_mapping *mapping); ++unsigned int cxl_mapping_get_position(struct cxl_memdev_mapping *mapping); ++ ++#define cxl_mapping_foreach(region, mapping) \ ++ for (mapping = cxl_mapping_get_first(region); \ ++ mapping != NULL; \ ++ mapping = cxl_mapping_get_next(mapping)) ++ + struct cxl_cmd; + const char *cxl_cmd_get_devname(struct cxl_cmd *cmd); + struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode); +-- +2.27.0 + diff --git a/SOURCES/0193-cxl-cli-add-region-listing-support.patch b/SOURCES/0193-cxl-cli-add-region-listing-support.patch new file mode 100644 index 0000000..e2642a2 --- /dev/null +++ b/SOURCES/0193-cxl-cli-add-region-listing-support.patch @@ -0,0 +1,548 @@ +From c0f2b36c481119e06eaec60dda17a42100a81ebc Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Mon, 15 Aug 2022 13:22:08 -0600 +Subject: [PATCH 193/217] cxl-cli: add region listing support + +Add cxl_region -> json and cxl_mapping -> json emitter helpers, and +teach cxl_filter_walk about cxl_regions. With these in place, 'cxl-list' +can now emit json objects for CXL regions. They can be top-level objects +if requested by themselves, or nested under root-decoders, if listed +along with decoders. Allow a plain 'cxl list' command to imply +'--regions'. + +Link: https://lore.kernel.org/r/20220815192214.545800-6-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 13 ++- + cxl/filter.c | 158 +++++++++++++++++++++++++++++++-- + cxl/filter.h | 4 + + cxl/json.c | 114 ++++++++++++++++++++++++ + cxl/json.h | 5 ++ + cxl/list.c | 25 +++--- + 6 files changed, 295 insertions(+), 24 deletions(-) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index 088ea70..b88940a 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -317,8 +317,9 @@ OPTIONS + + -T:: + --targets:: +- Extend decoder listings with downstream port target information, and / +- or port and bus listings with the downstream port information. ++ Extend decoder listings with downstream port target information, port ++ and bus listings with the downstream port information, and / or regions ++ with mapping information. + ---- + # cxl list -BTu -b ACPI.CXL + { +@@ -335,6 +336,14 @@ OPTIONS + } + ---- + ++-R:: ++--regions:: ++ Include region objects in the listing. ++ ++-r:: ++--region:: ++ Specify CXL region device name(s), or device id(s), to filter the listing. ++ + --debug:: + If the cxl tool was built with debug enabled, turn on debug + messages. +diff --git a/cxl/filter.c b/cxl/filter.c +index e5fab19..38ece55 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -585,6 +585,73 @@ util_cxl_memdev_filter_by_port(struct cxl_memdev *memdev, const char *bus_ident, + return NULL; + } + ++static struct cxl_region * ++util_cxl_region_filter_by_bus(struct cxl_region *region, const char *__ident) ++{ ++ struct cxl_decoder *decoder = cxl_region_get_decoder(region); ++ ++ if (!util_cxl_decoder_filter_by_bus(decoder, __ident)) ++ return NULL; ++ return region; ++} ++ ++static struct cxl_region * ++util_cxl_region_filter_by_port(struct cxl_region *region, const char *__ident) ++{ ++ struct cxl_decoder *decoder = cxl_region_get_decoder(region); ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ ++ if (!util_cxl_port_filter(port, __ident ,CXL_PF_ANCESTRY)) ++ return NULL; ++ return region; ++} ++ ++static struct cxl_region * ++util_cxl_region_filter_by_decoder(struct cxl_region *region, ++ const char *__ident) ++{ ++ struct cxl_decoder *decoder = cxl_region_get_decoder(region); ++ ++ if (!util_cxl_decoder_filter(decoder, __ident)) ++ return NULL; ++ return region; ++} ++ ++struct cxl_region *util_cxl_region_filter(struct cxl_region *region, ++ const char *__ident) ++{ ++ char *ident, *save; ++ const char *name; ++ int id; ++ ++ if (!__ident) ++ return region; ++ ++ ident = strdup(__ident); ++ if (!ident) ++ return NULL; ++ ++ for (name = strtok_r(ident, which_sep(__ident), &save); name; ++ name = strtok_r(NULL, which_sep(__ident), &save)) { ++ if (strcmp(name, "all") == 0) ++ break; ++ ++ if ((sscanf(name, "%d", &id) == 1 || ++ sscanf(name, "region%d", &id) == 1) && ++ cxl_region_get_id(region) == id) ++ break; ++ ++ if (strcmp(name, cxl_region_get_devname(region)) == 0) ++ break; ++ } ++ ++ free(ident); ++ if (name) ++ return region; ++ return NULL; ++ ++} ++ + static unsigned long params_to_flags(struct cxl_filter_params *param) + { + unsigned long flags = 0; +@@ -672,26 +739,57 @@ static struct json_object *pick_array(struct json_object *child, + return NULL; + } + ++static void walk_regions(struct cxl_decoder *decoder, ++ struct json_object *jregions, ++ struct cxl_filter_params *p, ++ unsigned long flags) ++{ ++ struct json_object *jregion; ++ struct cxl_region *region; ++ ++ cxl_region_foreach(decoder, region) { ++ if (!util_cxl_region_filter(region, p->region_filter)) ++ continue; ++ if (!util_cxl_region_filter_by_bus(region, p->bus_filter)) ++ continue; ++ if (!util_cxl_region_filter_by_port(region, p->port_filter)) ++ continue; ++ if (!util_cxl_region_filter_by_decoder(region, p->decoder_filter)) ++ continue; ++ if (!p->idle && !cxl_region_is_enabled(region)) ++ continue; ++ jregion = util_cxl_region_to_json(region, flags); ++ if (!jregion) ++ continue; ++ json_object_array_add(jregions, jregion); ++ } ++ ++ return; ++} ++ + static void walk_decoders(struct cxl_port *port, struct cxl_filter_params *p, +- struct json_object *jdecoders, unsigned long flags) ++ struct json_object *jdecoders, ++ struct json_object *jregions, unsigned long flags) + { + struct cxl_decoder *decoder; + + cxl_decoder_foreach(port, decoder) { ++ const char *devname = cxl_decoder_get_devname(decoder); ++ struct json_object *jchildregions = NULL; + struct json_object *jdecoder; + + if (!p->decoders) +- continue; ++ goto walk_children; + if (!util_cxl_decoder_filter(decoder, p->decoder_filter)) +- continue; ++ goto walk_children; + if (!util_cxl_decoder_filter_by_bus(decoder, p->bus_filter)) +- continue; ++ goto walk_children; + if (!util_cxl_decoder_filter_by_port(decoder, p->port_filter, + pf_mode(p))) +- continue; ++ goto walk_children; + if (!util_cxl_decoder_filter_by_memdev( + decoder, p->memdev_filter, p->serial_filter)) +- continue; ++ goto walk_children; + if (!p->idle && cxl_decoder_get_size(decoder) == 0) + continue; + jdecoder = util_cxl_decoder_to_json(decoder, flags); +@@ -702,7 +800,27 @@ static void walk_decoders(struct cxl_port *port, struct cxl_filter_params *p, + util_cxl_targets_append_json(jdecoder, decoder, + p->memdev_filter, p->serial_filter, + flags); ++ ++ if (p->regions) { ++ jchildregions = json_object_new_array(); ++ if (!jchildregions) { ++ err(p, "failed to allocate region object\n"); ++ return; ++ } ++ } ++ + json_object_array_add(jdecoders, jdecoder); ++ ++walk_children: ++ if (!p->regions) ++ continue; ++ if (!cxl_port_is_root(port)) ++ continue; ++ walk_regions(decoder, ++ pick_array(jchildregions, jregions), ++ p, flags); ++ cond_add_put_array_suffix(jdecoder, "regions", devname, ++ jchildregions); + } + } + +@@ -782,7 +900,7 @@ static void walk_endpoints(struct cxl_port *port, struct cxl_filter_params *p, + if (!p->decoders) + continue; + walk_decoders(cxl_endpoint_get_port(endpoint), p, +- pick_array(jchilddecoders, jdecoders), flags); ++ pick_array(jchilddecoders, jdecoders), NULL, flags); + cond_add_put_array_suffix(jendpoint, "decoders", devname, + jchilddecoders); + } +@@ -869,7 +987,8 @@ walk_children: + flags); + + walk_decoders(port, p, +- pick_array(jchilddecoders, jportdecoders), flags); ++ pick_array(jchilddecoders, jportdecoders), NULL, ++ flags); + walk_child_ports(port, p, pick_array(jchildports, jports), + pick_array(jchilddecoders, jportdecoders), + pick_array(jchildeps, jeps), +@@ -894,6 +1013,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + struct json_object *jbusdecoders = NULL; + struct json_object *jepdecoders = NULL; + struct json_object *janondevs = NULL; ++ struct json_object *jregions = NULL; + struct json_object *jeps = NULL; + struct cxl_memdev *memdev; + int top_level_objs = 0; +@@ -936,6 +1056,10 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + if (!jepdecoders) + goto err; + ++ jregions = json_object_new_array(); ++ if (!jregions) ++ goto err; ++ + dbg(p, "walk memdevs\n"); + cxl_memdev_foreach(ctx, memdev) { + struct json_object *janondev; +@@ -964,6 +1088,7 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + struct json_object *jchildports = NULL; + struct json_object *jchilddevs = NULL; + struct json_object *jchildeps = NULL; ++ struct json_object *jchildregions = NULL; + struct cxl_port *port = cxl_bus_get_port(bus); + const char *devname = cxl_bus_get_devname(bus); + +@@ -1021,11 +1146,20 @@ int cxl_filter_walk(struct cxl_ctx *ctx, struct cxl_filter_params *p) + continue; + } + } ++ if (p->regions && !p->decoders) { ++ jchildregions = json_object_new_array(); ++ if (!jchildregions) { ++ err(p, ++ "%s: failed to enumerate child regions\n", ++ devname); ++ continue; ++ } ++ } + } + walk_children: + dbg(p, "walk decoders\n"); + walk_decoders(port, p, pick_array(jchilddecoders, jbusdecoders), +- flags); ++ pick_array(jchildregions, jregions), flags); + + dbg(p, "walk ports\n"); + walk_child_ports(port, p, pick_array(jchildports, jports), +@@ -1038,6 +1172,8 @@ walk_children: + jchildeps); + cond_add_put_array_suffix(jbus, "decoders", devname, + jchilddecoders); ++ cond_add_put_array_suffix(jbus, "regions", devname, ++ jchildregions); + cond_add_put_array_suffix(jbus, "memdevs", devname, jchilddevs); + } + +@@ -1057,6 +1193,8 @@ walk_children: + top_level_objs++; + if (json_object_array_length(jepdecoders)) + top_level_objs++; ++ if (json_object_array_length(jregions)) ++ top_level_objs++; + + splice_array(p, janondevs, jplatform, "anon memdevs", top_level_objs > 1); + splice_array(p, jbuses, jplatform, "buses", top_level_objs > 1); +@@ -1069,6 +1207,7 @@ walk_children: + top_level_objs > 1); + splice_array(p, jepdecoders, jplatform, "endpoint decoders", + top_level_objs > 1); ++ splice_array(p, jregions, jplatform, "regions", top_level_objs > 1); + + util_display_json_array(stdout, jplatform, flags); + +@@ -1082,6 +1221,7 @@ err: + json_object_put(jbusdecoders); + json_object_put(jportdecoders); + json_object_put(jepdecoders); ++ json_object_put(jregions); + json_object_put(jplatform); + return -ENOMEM; + } +diff --git a/cxl/filter.h b/cxl/filter.h +index c913daf..609433c 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -13,9 +13,11 @@ struct cxl_filter_params { + const char *port_filter; + const char *endpoint_filter; + const char *decoder_filter; ++ const char *region_filter; + bool single; + bool endpoints; + bool decoders; ++ bool regions; + bool targets; + bool memdevs; + bool ports; +@@ -33,6 +35,8 @@ struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port, + const char *ident, + const char *serial); ++struct cxl_region *util_cxl_region_filter(struct cxl_region *region, ++ const char *__ident); + + enum cxl_port_filter_mode { + CXL_PF_SINGLE, +diff --git a/cxl/json.c b/cxl/json.c +index ae9c812..70cf286 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -524,6 +524,120 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + return jdecoder; + } + ++void util_cxl_mappings_append_json(struct json_object *jregion, ++ struct cxl_region *region, ++ unsigned long flags) ++{ ++ struct json_object *jobj, *jmappings; ++ struct cxl_memdev_mapping *mapping; ++ unsigned int val, nr_mappings; ++ const char *devname; ++ ++ nr_mappings = cxl_region_get_interleave_ways(region); ++ if (!nr_mappings || (nr_mappings == UINT_MAX)) ++ return; ++ ++ if (!(flags & UTIL_JSON_TARGETS)) ++ return; ++ ++ jmappings = json_object_new_array(); ++ if (!jmappings) ++ return; ++ ++ cxl_mapping_foreach(region, mapping) { ++ struct json_object *jmapping; ++ struct cxl_decoder *decoder; ++ ++ jmapping = json_object_new_object(); ++ if (!jmapping) ++ continue; ++ ++ val = cxl_mapping_get_position(mapping); ++ if (val < UINT_MAX) { ++ jobj = json_object_new_int(val); ++ if (jobj) ++ json_object_object_add(jmapping, "position", ++ jobj); ++ } ++ ++ decoder = cxl_mapping_get_decoder(mapping); ++ if (!decoder) ++ continue; ++ ++ devname = cxl_decoder_get_devname(decoder); ++ jobj = json_object_new_string(devname); ++ if (jobj) ++ json_object_object_add(jmapping, "decoder", jobj); ++ ++ json_object_array_add(jmappings, jmapping); ++ } ++ ++ json_object_object_add(jregion, "mappings", jmappings); ++} ++ ++struct json_object *util_cxl_region_to_json(struct cxl_region *region, ++ unsigned long flags) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct json_object *jregion, *jobj; ++ u64 val; ++ ++ jregion = json_object_new_object(); ++ if (!jregion) ++ return NULL; ++ ++ jobj = json_object_new_string(devname); ++ if (jobj) ++ json_object_object_add(jregion, "region", jobj); ++ ++ val = cxl_region_get_resource(region); ++ if (val < ULLONG_MAX) { ++ jobj = util_json_object_hex(val, flags); ++ if (jobj) ++ json_object_object_add(jregion, "resource", jobj); ++ } ++ ++ val = cxl_region_get_size(region); ++ if (val < ULLONG_MAX) { ++ jobj = util_json_object_size(val, flags); ++ if (jobj) ++ json_object_object_add(jregion, "size", jobj); ++ } ++ ++ val = cxl_region_get_interleave_ways(region); ++ if (val < INT_MAX) { ++ jobj = json_object_new_int(val); ++ if (jobj) ++ json_object_object_add(jregion, ++ "interleave_ways", jobj); ++ } ++ ++ val = cxl_region_get_interleave_granularity(region); ++ if (val < INT_MAX) { ++ jobj = json_object_new_int(val); ++ if (jobj) ++ json_object_object_add(jregion, ++ "interleave_granularity", jobj); ++ } ++ ++ if (cxl_region_decode_is_committed(region)) ++ jobj = json_object_new_string("commit"); ++ else ++ jobj = json_object_new_string("reset"); ++ if (jobj) ++ json_object_object_add(jregion, "decode_state", jobj); ++ ++ if (!cxl_region_is_enabled(region)) { ++ jobj = json_object_new_string("disabled"); ++ if (jobj) ++ json_object_object_add(jregion, "state", jobj); ++ } ++ ++ util_cxl_mappings_append_json(jregion, region, flags); ++ ++ return jregion; ++} ++ + void util_cxl_targets_append_json(struct json_object *jdecoder, + struct cxl_decoder *decoder, + const char *ident, const char *serial, +diff --git a/cxl/json.h b/cxl/json.h +index 9a5a845..eb7572b 100644 +--- a/cxl/json.h ++++ b/cxl/json.h +@@ -15,6 +15,11 @@ struct json_object *util_cxl_endpoint_to_json(struct cxl_endpoint *endpoint, + unsigned long flags); + struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + unsigned long flags); ++struct json_object *util_cxl_region_to_json(struct cxl_region *region, ++ unsigned long flags); ++void util_cxl_mappings_append_json(struct json_object *jregion, ++ struct cxl_region *region, ++ unsigned long flags); + void util_cxl_targets_append_json(struct json_object *jdecoder, + struct cxl_decoder *decoder, + const char *ident, const char *serial, +diff --git a/cxl/list.c b/cxl/list.c +index 1b5f583..88ca9d9 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -41,7 +41,10 @@ static const struct option options[] = { + OPT_BOOLEAN('D', "decoders", ¶m.decoders, + "include CXL decoder info"), + OPT_BOOLEAN('T', "targets", ¶m.targets, +- "include CXL target data with decoders or ports"), ++ "include CXL target data with decoders, ports, or regions"), ++ OPT_STRING('r', "region", ¶m.region_filter, "region name", ++ "filter by CXL region name(s)"), ++ OPT_BOOLEAN('R', "regions", ¶m.regions, "include CXL regions"), + OPT_BOOLEAN('i', "idle", ¶m.idle, "include disabled devices"), + OPT_BOOLEAN('u', "human", ¶m.human, + "use human friendly number formats"), +@@ -58,7 +61,7 @@ static const struct option options[] = { + static int num_list_flags(void) + { + return !!param.memdevs + !!param.buses + !!param.ports + +- !!param.endpoints + !!param.decoders; ++ !!param.endpoints + !!param.decoders + !!param.regions; + } + + int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) +@@ -92,18 +95,14 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + param.endpoints = true; + if (param.decoder_filter) + param.decoders = true; +- if (num_list_flags() == 0) { +- /* +- * TODO: We likely want to list regions by default if +- * nothing was explicitly asked for. But until we have +- * region support, print this error asking for devices +- * explicitly. Once region support is added, this TODO +- * can be removed. +- */ +- error("please specify entities to list, e.g. using -m/-M\n"); +- usage_with_options(u, options); +- } + param.single = true; ++ if (param.region_filter) ++ param.regions = true; ++ } ++ ++ /* List regions by default */ ++ if (num_list_flags() == 0) { ++ param.regions = true; + } + + log_init(¶m.ctx, "cxl list", "CXL_LIST_LOG"); +-- +2.27.0 + diff --git a/SOURCES/0194-libcxl-add-low-level-APIs-for-region-creation.patch b/SOURCES/0194-libcxl-add-low-level-APIs-for-region-creation.patch new file mode 100644 index 0000000..f040988 --- /dev/null +++ b/SOURCES/0194-libcxl-add-low-level-APIs-for-region-creation.patch @@ -0,0 +1,644 @@ +From cafe4b2d4970b0d7f2193abb9cb32f58c03cbe3b Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Mon, 15 Aug 2022 13:22:09 -0600 +Subject: [PATCH 194/217] libcxl: add low level APIs for region creation + +Add libcxl APIs to create a region under a given root decoder, and to +set different attributes for the new region. These allow setting the +size, interleave_ways, interleave_granularity, uuid, and the target +devices for the newly minted cxl_region object. + +Link: https://lore.kernel.org/r/20220815192214.545800-7-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/lib/libcxl.txt | 69 ++++++ + cxl/lib/libcxl.c | 381 ++++++++++++++++++++++++++++++- + cxl/lib/libcxl.sym | 16 ++ + cxl/lib/private.h | 2 + + cxl/libcxl.h | 23 +- + 5 files changed, 488 insertions(+), 3 deletions(-) + +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 7a38ce4..50b0c9c 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -508,6 +508,75 @@ device to represent the root of a PCI device hierarchy. The + cxl_target_get_physical_node() helper returns the device name of that + companion object in the PCI hierarchy. + ++==== REGIONS ++A CXL region is composed of one or more slices of CXL memdevs, with configurable ++interleave settings - both the number of interleave ways, and the interleave ++granularity. In terms of hierarchy, it is the child of a CXL root decoder. A root ++decoder (recall that this corresponds to an ACPI CEDT.CFMWS 'window'), may have ++multiple child regions, but a region is strictly tied to one root decoder. ++ ++The slices that compose a region are called mappings. A mapping is a ++tuple of 'memdev', 'endpoint decoder', and the 'position'. ++ ++===== REGION: Enumeration ++---- ++struct cxl_region *cxl_region_get_first(struct cxl_decoder *decoder); ++struct cxl_region *cxl_region_get_next(struct cxl_region *region); ++ ++#define cxl_region_foreach(decoder, region) \ ++ for (region = cxl_region_get_first(decoder); region != NULL; \ ++ region = cxl_region_get_next(region)) ++ ++#define cxl_region_foreach_safe(decoder, region, _region) \ ++ for (region = cxl_region_get_first(decoder), \ ++ _region = region ? cxl_region_get_next(region) : NULL; \ ++ region != NULL; \ ++ region = _region, \ ++ _region = _region ? cxl_region_get_next(_region) : NULL) ++---- ++ ++===== REGION: Attributes ++---- ++int cxl_region_get_id(struct cxl_region *region); ++const char *cxl_region_get_devname(struct cxl_region *region); ++void cxl_region_get_uuid(struct cxl_region *region, uuid_t uu); ++unsigned long long cxl_region_get_size(struct cxl_region *region); ++unsigned long long cxl_region_get_resource(struct cxl_region *region); ++unsigned int cxl_region_get_interleave_ways(struct cxl_region *region); ++unsigned int cxl_region_get_interleave_granularity(struct cxl_region *region); ++struct cxl_decoder *cxl_region_get_target_decoder(struct cxl_region *region, ++ int position); ++int cxl_region_set_size(struct cxl_region *region, unsigned long long size); ++int cxl_region_set_uuid(struct cxl_region *region, uuid_t uu); ++int cxl_region_set_interleave_ways(struct cxl_region *region, ++ unsigned int ways); ++int cxl_region_set_interleave_granularity(struct cxl_region *region, ++ unsigned int granularity); ++int cxl_region_set_target(struct cxl_region *region, int position, ++ struct cxl_decoder *decoder); ++int cxl_region_clear_target(struct cxl_region *region, int position); ++int cxl_region_clear_all_targets(struct cxl_region *region); ++int cxl_region_decode_commit(struct cxl_region *region); ++int cxl_region_decode_reset(struct cxl_region *region); ++---- ++ ++A region's resource attribute is the Host Physical Address at which the region's ++address space starts. The region's address space is a subset of the parent root ++decoder's address space. ++ ++The interleave ways is the number of component memdevs participating in the ++region. ++ ++The interleave granularity depends on the root decoder's granularity, and must ++follow the interleave math rules defined in the CXL spec. ++ ++Regions have a list of targets 0..N, which are programmed with the name of an ++endpoint decoder under each participating memdev. ++ ++The 'decode_commit' and 'decode_reset' attributes reserve and free DPA space ++on a given memdev by allocating an endpoint decoder, and programming it based ++on the region's interleave geometry. ++ + include::../../copyright.txt[] + + SEE ALSO +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index ad98188..fd2ea4f 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -420,6 +421,40 @@ CXL_EXPORT int cxl_region_enable(struct cxl_region *region) + return 0; + } + ++static int cxl_region_delete_name(struct cxl_decoder *decoder, ++ const char *devname) ++{ ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ char *path = decoder->dev_buf; ++ int rc; ++ ++ sprintf(path, "%s/delete_region", decoder->dev_path); ++ rc = sysfs_write_attr(ctx, path, devname); ++ if (rc != 0) { ++ err(ctx, "error deleting region: %s\n", strerror(-rc)); ++ return rc; ++ } ++ return 0; ++} ++ ++CXL_EXPORT int cxl_region_delete(struct cxl_region *region) ++{ ++ struct cxl_decoder *decoder = cxl_region_get_decoder(region); ++ const char *devname = cxl_region_get_devname(region); ++ int rc; ++ ++ if (cxl_region_is_enabled(region)) ++ return -EBUSY; ++ ++ rc = cxl_region_delete_name(decoder, devname); ++ if (rc != 0) ++ return rc; ++ ++ decoder->regions_init = 0; ++ free_region(region, &decoder->regions); ++ return 0; ++} ++ + static void *add_cxl_region(void *parent, int id, const char *cxlregion_base) + { + const char *devname = devpath_to_devname(cxlregion_base); +@@ -599,6 +634,258 @@ cxl_region_get_interleave_granularity(struct cxl_region *region) + return region->interleave_granularity; + } + ++CXL_EXPORT struct cxl_decoder * ++cxl_region_get_target_decoder(struct cxl_region *region, int position) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ int len = region->buf_len, rc; ++ char *path = region->dev_buf; ++ struct cxl_decoder *decoder; ++ char buf[SYSFS_ATTR_SIZE]; ++ ++ if (snprintf(path, len, "%s/target%d", region->dev_path, position) >= ++ len) { ++ err(ctx, "%s: buffer too small!\n", devname); ++ return NULL; ++ } ++ ++ rc = sysfs_read_attr(ctx, path, buf); ++ if (rc < 0) { ++ err(ctx, "%s: error reading target%d: %s\n", devname, ++ position, strerror(-rc)); ++ return NULL; ++ } ++ ++ decoder = cxl_decoder_get_by_name(ctx, buf); ++ if (!decoder) { ++ err(ctx, "%s: error locating decoder for target%d\n", devname, ++ position); ++ return NULL; ++ } ++ return decoder; ++} ++ ++CXL_EXPORT int cxl_region_set_size(struct cxl_region *region, ++ unsigned long long size) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ int len = region->buf_len, rc; ++ char *path = region->dev_buf; ++ char buf[SYSFS_ATTR_SIZE]; ++ ++ if (size == 0) { ++ dbg(ctx, "%s: cannot use %s to delete a region\n", __func__, ++ devname); ++ return -EINVAL; ++ } ++ ++ if (snprintf(path, len, "%s/size", region->dev_path) >= len) { ++ err(ctx, "%s: buffer too small!\n", devname); ++ return -ENXIO; ++ } ++ ++ sprintf(buf, "%#llx\n", size); ++ rc = sysfs_write_attr(ctx, path, buf); ++ if (rc < 0) ++ return rc; ++ ++ region->size = size; ++ ++ return 0; ++} ++ ++CXL_EXPORT int cxl_region_set_uuid(struct cxl_region *region, uuid_t uu) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ int len = region->buf_len, rc; ++ char *path = region->dev_buf; ++ char uuid[SYSFS_ATTR_SIZE]; ++ ++ if (snprintf(path, len, "%s/uuid", region->dev_path) >= len) { ++ err(ctx, "%s: buffer too small!\n", devname); ++ return -ENXIO; ++ } ++ ++ uuid_unparse(uu, uuid); ++ rc = sysfs_write_attr(ctx, path, uuid); ++ if (rc != 0) ++ return rc; ++ memcpy(region->uuid, uu, sizeof(uuid_t)); ++ return 0; ++} ++ ++CXL_EXPORT int cxl_region_set_interleave_ways(struct cxl_region *region, ++ unsigned int ways) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ int len = region->buf_len, rc; ++ char *path = region->dev_buf; ++ char buf[SYSFS_ATTR_SIZE]; ++ ++ if (snprintf(path, len, "%s/interleave_ways", ++ region->dev_path) >= len) { ++ err(ctx, "%s: buffer too small!\n", devname); ++ return -ENXIO; ++ } ++ ++ sprintf(buf, "%u\n", ways); ++ rc = sysfs_write_attr(ctx, path, buf); ++ if (rc < 0) ++ return rc; ++ ++ region->interleave_ways = ways; ++ ++ return 0; ++} ++ ++CXL_EXPORT int cxl_region_set_interleave_granularity(struct cxl_region *region, ++ unsigned int granularity) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ int len = region->buf_len, rc; ++ char *path = region->dev_buf; ++ char buf[SYSFS_ATTR_SIZE]; ++ ++ if (snprintf(path, len, "%s/interleave_granularity", ++ region->dev_path) >= len) { ++ err(ctx, "%s: buffer too small!\n", devname); ++ return -ENXIO; ++ } ++ ++ sprintf(buf, "%u\n", granularity); ++ rc = sysfs_write_attr(ctx, path, buf); ++ if (rc < 0) ++ return rc; ++ ++ region->interleave_granularity = granularity; ++ ++ return 0; ++} ++ ++static int region_write_target(struct cxl_region *region, int position, ++ struct cxl_decoder *decoder) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ int len = region->buf_len, rc; ++ char *path = region->dev_buf; ++ const char *dec_name = ""; ++ ++ if (decoder) ++ dec_name = cxl_decoder_get_devname(decoder); ++ ++ if (snprintf(path, len, "%s/target%d", region->dev_path, position) >= ++ len) { ++ err(ctx, "%s: buffer too small!\n", devname); ++ return -ENXIO; ++ } ++ ++ rc = sysfs_write_attr(ctx, path, dec_name); ++ if (rc < 0) ++ return rc; ++ ++ return 0; ++} ++ ++CXL_EXPORT int cxl_region_set_target(struct cxl_region *region, int position, ++ struct cxl_decoder *decoder) ++{ ++ if (!decoder) ++ return -ENXIO; ++ ++ return region_write_target(region, position, decoder); ++} ++ ++CXL_EXPORT int cxl_region_clear_target(struct cxl_region *region, int position) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ int rc; ++ ++ if (cxl_region_is_enabled(region)) { ++ err(ctx, "%s: can't clear targets on an active region\n", ++ devname); ++ return -EBUSY; ++ } ++ ++ rc = region_write_target(region, position, NULL); ++ if (rc) { ++ err(ctx, "%s: error clearing target%d: %s\n", ++ devname, position, strerror(-rc)); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++CXL_EXPORT int cxl_region_clear_all_targets(struct cxl_region *region) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ unsigned int ways, i; ++ int rc; ++ ++ if (cxl_region_is_enabled(region)) { ++ err(ctx, "%s: can't clear targets on an active region\n", ++ devname); ++ return -EBUSY; ++ } ++ ++ ways = cxl_region_get_interleave_ways(region); ++ if (ways == 0 || ways == UINT_MAX) ++ return -ENXIO; ++ ++ for (i = 0; i < ways; i++) { ++ rc = region_write_target(region, i, NULL); ++ if (rc) { ++ err(ctx, "%s: error clearing target%d: %s\n", ++ devname, i, strerror(-rc)); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++static int set_region_decode(struct cxl_region *region, ++ enum cxl_decode_state decode_state) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ struct cxl_ctx *ctx = cxl_region_get_ctx(region); ++ int len = region->buf_len, rc; ++ char *path = region->dev_buf; ++ char buf[SYSFS_ATTR_SIZE]; ++ ++ if (snprintf(path, len, "%s/commit", region->dev_path) >= len) { ++ err(ctx, "%s: buffer too small!\n", devname); ++ return -ENXIO; ++ } ++ ++ sprintf(buf, "%d\n", decode_state); ++ rc = sysfs_write_attr(ctx, path, buf); ++ if (rc < 0) ++ return rc; ++ ++ region->decode_state = decode_state; ++ ++ return 0; ++} ++ ++CXL_EXPORT int cxl_region_decode_commit(struct cxl_region *region) ++{ ++ return set_region_decode(region, CXL_DECODE_COMMIT); ++} ++ ++CXL_EXPORT int cxl_region_decode_reset(struct cxl_region *region) ++{ ++ return set_region_decode(region, CXL_DECODE_RESET); ++} ++ + static struct cxl_decoder *__cxl_port_match_decoder(struct cxl_port *port, + const char *ident) + { +@@ -633,8 +920,8 @@ static struct cxl_decoder *cxl_port_find_decoder(struct cxl_port *port, + return NULL; + } + +-static struct cxl_decoder *cxl_decoder_get_by_name(struct cxl_ctx *ctx, +- const char *ident) ++CXL_EXPORT struct cxl_decoder *cxl_decoder_get_by_name(struct cxl_ctx *ctx, ++ const char *ident) + { + struct cxl_bus *bus; + +@@ -1399,6 +1686,18 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + } else + decoder->mode = CXL_DECODER_MODE_NONE; + ++ sprintf(path, "%s/interleave_granularity", cxldecoder_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ decoder->interleave_granularity = UINT_MAX; ++ else ++ decoder->interleave_granularity = strtoul(buf, NULL, 0); ++ ++ sprintf(path, "%s/interleave_ways", cxldecoder_base); ++ if (sysfs_read_attr(ctx, path, buf) < 0) ++ decoder->interleave_ways = UINT_MAX; ++ else ++ decoder->interleave_ways = strtoul(buf, NULL, 0); ++ + switch (port->type) { + case CXL_PORT_ENDPOINT: + sprintf(path, "%s/dpa_resource", cxldecoder_base); +@@ -1731,6 +2030,66 @@ CXL_EXPORT bool cxl_decoder_is_locked(struct cxl_decoder *decoder) + return decoder->locked; + } + ++CXL_EXPORT unsigned int ++cxl_decoder_get_interleave_granularity(struct cxl_decoder *decoder) ++{ ++ return decoder->interleave_granularity; ++} ++ ++CXL_EXPORT unsigned int ++cxl_decoder_get_interleave_ways(struct cxl_decoder *decoder) ++{ ++ return decoder->interleave_ways; ++} ++ ++CXL_EXPORT struct cxl_region * ++cxl_decoder_create_pmem_region(struct cxl_decoder *decoder) ++{ ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ char *path = decoder->dev_buf; ++ char buf[SYSFS_ATTR_SIZE]; ++ struct cxl_region *region; ++ int rc; ++ ++ sprintf(path, "%s/create_pmem_region", decoder->dev_path); ++ rc = sysfs_read_attr(ctx, path, buf); ++ if (rc < 0) { ++ err(ctx, "failed to read new region name: %s\n", ++ strerror(-rc)); ++ return NULL; ++ } ++ ++ rc = sysfs_write_attr(ctx, path, buf); ++ if (rc < 0) { ++ err(ctx, "failed to write new region name: %s\n", ++ strerror(-rc)); ++ return NULL; ++ } ++ ++ /* Force a re-init of regions so that the new one can be discovered */ ++ decoder->regions_init = 0; ++ ++ /* create_region was successful, walk to the new region */ ++ cxl_region_foreach(decoder, region) { ++ const char *devname = cxl_region_get_devname(region); ++ ++ if (strcmp(devname, buf) == 0) ++ goto found; ++ } ++ ++ /* ++ * If walking to the region we just created failed, something has gone ++ * very wrong. Attempt to delete it to avoid leaving a dangling region ++ * id behind. ++ */ ++ err(ctx, "failed to add new region to libcxl\n"); ++ cxl_region_delete_name(decoder, buf); ++ return NULL; ++ ++ found: ++ return region; ++} ++ + CXL_EXPORT int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder) + { + return decoder->nr_targets; +@@ -1741,6 +2100,24 @@ CXL_EXPORT const char *cxl_decoder_get_devname(struct cxl_decoder *decoder) + return devpath_to_devname(decoder->dev_path); + } + ++CXL_EXPORT struct cxl_memdev * ++cxl_decoder_get_memdev(struct cxl_decoder *decoder) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ struct cxl_endpoint *ep; ++ ++ if (!port) ++ return NULL; ++ if (!cxl_port_is_endpoint(port)) ++ return NULL; ++ ++ ep = container_of(port, struct cxl_endpoint, port); ++ if (!ep) ++ return NULL; ++ ++ return cxl_endpoint_get_memdev(ep); ++} ++ + CXL_EXPORT struct cxl_target *cxl_target_get_first(struct cxl_decoder *decoder) + { + return list_top(&decoder->targets, struct cxl_target, list); +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index e410298..cb23a0b 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -140,6 +140,7 @@ global: + cxl_decoder_is_mem_capable; + cxl_decoder_is_accelmem_capable; + cxl_decoder_is_locked; ++ cxl_decoder_create_pmem_region; + cxl_target_get_first; + cxl_target_get_next; + cxl_target_get_decoder; +@@ -183,6 +184,7 @@ global: + cxl_region_is_enabled; + cxl_region_disable; + cxl_region_enable; ++ cxl_region_delete; + cxl_region_get_ctx; + cxl_region_get_decoder; + cxl_region_get_id; +@@ -192,9 +194,23 @@ global: + cxl_region_get_resource; + cxl_region_get_interleave_ways; + cxl_region_get_interleave_granularity; ++ cxl_region_get_target_decoder; ++ cxl_region_set_size; ++ cxl_region_set_uuid; ++ cxl_region_set_interleave_ways; ++ cxl_region_set_interleave_granularity; ++ cxl_region_set_target; ++ cxl_region_clear_target; ++ cxl_region_clear_all_targets; ++ cxl_region_decode_commit; ++ cxl_region_decode_reset; + cxl_mapping_get_first; + cxl_mapping_get_next; + cxl_mapping_get_decoder; + cxl_mapping_get_region; + cxl_mapping_get_position; ++ cxl_decoder_get_by_name; ++ cxl_decoder_get_memdev; ++ cxl_decoder_get_interleave_granularity; ++ cxl_decoder_get_interleave_ways; + } LIBCXL_2; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 5e2fdd5..8bc9620 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -110,6 +110,8 @@ struct cxl_decoder { + int nr_targets; + int id; + enum cxl_decoder_mode mode; ++ unsigned int interleave_ways; ++ unsigned int interleave_granularity; + bool pmem_capable; + bool volatile_capable; + bool mem_capable; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 19d94e4..69d9c09 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -195,7 +195,13 @@ bool cxl_decoder_is_volatile_capable(struct cxl_decoder *decoder); + bool cxl_decoder_is_mem_capable(struct cxl_decoder *decoder); + bool cxl_decoder_is_accelmem_capable(struct cxl_decoder *decoder); + bool cxl_decoder_is_locked(struct cxl_decoder *decoder); +- ++unsigned int ++cxl_decoder_get_interleave_granularity(struct cxl_decoder *decoder); ++unsigned int cxl_decoder_get_interleave_ways(struct cxl_decoder *decoder); ++struct cxl_region *cxl_decoder_create_pmem_region(struct cxl_decoder *decoder); ++struct cxl_decoder *cxl_decoder_get_by_name(struct cxl_ctx *ctx, ++ const char *ident); ++struct cxl_memdev *cxl_decoder_get_memdev(struct cxl_decoder *decoder); + #define cxl_decoder_foreach(port, decoder) \ + for (decoder = cxl_decoder_get_first(port); decoder != NULL; \ + decoder = cxl_decoder_get_next(decoder)) +@@ -244,6 +250,7 @@ int cxl_region_decode_is_committed(struct cxl_region *region); + int cxl_region_is_enabled(struct cxl_region *region); + int cxl_region_disable(struct cxl_region *region); + int cxl_region_enable(struct cxl_region *region); ++int cxl_region_delete(struct cxl_region *region); + struct cxl_ctx *cxl_region_get_ctx(struct cxl_region *region); + struct cxl_decoder *cxl_region_get_decoder(struct cxl_region *region); + int cxl_region_get_id(struct cxl_region *region); +@@ -253,6 +260,20 @@ unsigned long long cxl_region_get_size(struct cxl_region *region); + unsigned long long cxl_region_get_resource(struct cxl_region *region); + unsigned int cxl_region_get_interleave_ways(struct cxl_region *region); + unsigned int cxl_region_get_interleave_granularity(struct cxl_region *region); ++struct cxl_decoder *cxl_region_get_target_decoder(struct cxl_region *region, ++ int position); ++int cxl_region_set_size(struct cxl_region *region, unsigned long long size); ++int cxl_region_set_uuid(struct cxl_region *region, uuid_t uu); ++int cxl_region_set_interleave_ways(struct cxl_region *region, ++ unsigned int ways); ++int cxl_region_set_interleave_granularity(struct cxl_region *region, ++ unsigned int granularity); ++int cxl_region_set_target(struct cxl_region *region, int position, ++ struct cxl_decoder *decoder); ++int cxl_region_clear_target(struct cxl_region *region, int position); ++int cxl_region_clear_all_targets(struct cxl_region *region); ++int cxl_region_decode_commit(struct cxl_region *region); ++int cxl_region_decode_reset(struct cxl_region *region); + + #define cxl_region_foreach(decoder, region) \ + for (region = cxl_region_get_first(decoder); region != NULL; \ +-- +2.27.0 + diff --git a/SOURCES/0195-cxl-add-a-create-region-command.patch b/SOURCES/0195-cxl-add-a-create-region-command.patch new file mode 100644 index 0000000..839722c --- /dev/null +++ b/SOURCES/0195-cxl-add-a-create-region-command.patch @@ -0,0 +1,829 @@ +From 21b089025178442baa7b59823a7fd264b4c075a8 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Mon, 15 Aug 2022 13:22:10 -0600 +Subject: [PATCH 195/217] cxl: add a 'create-region' command + +Add a 'create-region' command to cxl-cli that walks the platform's CXL +hierarchy to find an appropriate root decoder based on any options +provided, and uses libcxl APIs to create a 'region' that is comprehended +by libnvdimm and ndctl. + +Link: https://lore.kernel.org/r/20220815192214.545800-8-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/bus-option.txt | 5 + + Documentation/cxl/cxl-create-region.txt | 112 +++++ + Documentation/cxl/meson.build | 2 + + Documentation/cxl/region-description.txt | 7 + + cxl/builtin.h | 1 + + cxl/cxl.c | 1 + + cxl/filter.h | 4 +- + cxl/json.c | 9 + + cxl/meson.build | 1 + + cxl/region.c | 550 +++++++++++++++++++++++ + 10 files changed, 691 insertions(+), 1 deletion(-) + create mode 100644 Documentation/cxl/bus-option.txt + create mode 100644 Documentation/cxl/cxl-create-region.txt + create mode 100644 Documentation/cxl/region-description.txt + create mode 100644 cxl/region.c + +diff --git a/Documentation/cxl/bus-option.txt b/Documentation/cxl/bus-option.txt +new file mode 100644 +index 0000000..02e2f08 +--- /dev/null ++++ b/Documentation/cxl/bus-option.txt +@@ -0,0 +1,5 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++-b:: ++--bus=:: ++ Restrict the operation to the specified bus. +diff --git a/Documentation/cxl/cxl-create-region.txt b/Documentation/cxl/cxl-create-region.txt +new file mode 100644 +index 0000000..6b740d5 +--- /dev/null ++++ b/Documentation/cxl/cxl-create-region.txt +@@ -0,0 +1,112 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-create-region(1) ++==================== ++ ++NAME ++---- ++cxl-create-region - Assemble a CXL region by setting up attributes of its ++constituent CXL memdevs. ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl create-region []' ++ ++include::region-description.txt[] ++ ++For create-region, a size can optionally be specified, but if not, the maximum ++possible size for each memdev will be used up to the available decode capacity ++in the system for the given memory type. For persistent regions a UUID can ++optionally be specified, but if not, one will be generated. ++ ++If the region-creation operation is successful, a region object will be ++emitted on stdout in JSON format (see examples). If the specified arguments ++cannot be satisfied with a legal configuration, then an appropriate error will ++be emitted on stderr. ++ ++EXAMPLE ++------- ++---- ++#cxl create - region - m - d decoder0 .1 - w 2 - g 1024 mem0 mem1 ++{ ++ "region":"region0", ++ "resource":"0xc90000000", ++ "size":"512.00 MiB (536.87 MB)", ++ "interleave_ways":2, ++ "interleave_granularity":1024, ++ "mappings":[ ++ { ++ "position":1, ++ "decoder":"decoder4.0" ++ }, ++ { ++ "position":0, ++ "decoder":"decoder3.0" ++ } ++ ] ++} ++created 1 region ++---- ++ ++OPTIONS ++------- ++:: ++The CXL targets that should be used to form the region. The number of ++'target' arguments must match the '--ways' option (if provided). The ++targets are memdev names such as 'mem0', 'mem1' etc. ++ ++include::bus-option.txt[] ++ ++-m:: ++--memdevs:: ++ Indicate that the non-option arguments for 'target(s)' refer to memdev ++ names. Currently this is the only option supported, and must be ++ specified. ++ ++-s:: ++--size=:: ++ Specify the total size for the new region. This is optional, and by ++ default, the maximum possible size will be used. The maximum possible ++ size is gated by both the contiguous free HPA space remaining in the ++ root decoder, and the available DPA space in the component memdevs. ++ ++-t:: ++--type=:: ++ Specify the region type - 'pmem' or 'ram'. Defaults to 'pmem'. ++ ++-U:: ++--uuid=:: ++ Specify a UUID for the new region. This shouldn't usually need to be ++ specified, as one will be generated by default. ++ ++-w:: ++--ways=:: ++ The number of interleave ways for the new region's interleave. This ++ should be equal to the number of memdevs specified in --memdevs, if ++ --memdevs is being supplied. If --ways is not specified, it will be ++ determined based on the number of memdev targets provided. ++ ++-g:: ++--granularity=:: ++ The interleave granularity for the new region. Must match the selected ++ root decoder's (if provided) granularity. If the root decoder is ++ interleaved across more than one host-bridge then this value must match ++ that granularity. Otherwise, for non-interleaved decode windows, any ++ granularity can be specified as long as all devices support that setting. ++ ++-d:: ++--decoder=:: ++ The root decoder that the region should be created under. If not ++ supplied, the first cross-host bridge (if available), decoder that ++ supports the largest interleave will be chosen. ++ ++include::human-option.txt[] ++ ++include::debug-option.txt[] ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-list[1], +diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build +index 423be90..340cdee 100644 +--- a/Documentation/cxl/meson.build ++++ b/Documentation/cxl/meson.build +@@ -23,6 +23,7 @@ filedeps = [ + 'memdev-option.txt', + 'labels-options.txt', + 'debug-option.txt', ++ 'region-description.txt', + ] + + cxl_manpages = [ +@@ -39,6 +40,7 @@ cxl_manpages = [ + 'cxl-set-partition.txt', + 'cxl-reserve-dpa.txt', + 'cxl-free-dpa.txt', ++ 'cxl-create-region.txt', + ] + + foreach man : cxl_manpages +diff --git a/Documentation/cxl/region-description.txt b/Documentation/cxl/region-description.txt +new file mode 100644 +index 0000000..d7e3077 +--- /dev/null ++++ b/Documentation/cxl/region-description.txt +@@ -0,0 +1,7 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++DESCRIPTION ++----------- ++A CXL region is composed of one or more slices of CXL memdevs, with configurable ++interleave settings - both the number of interleave ways, and the interleave ++granularity. +diff --git a/cxl/builtin.h b/cxl/builtin.h +index 9e6fc62..843bada 100644 +--- a/cxl/builtin.h ++++ b/cxl/builtin.h +@@ -18,4 +18,5 @@ int cmd_disable_port(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_disable_bus(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_create_region(int argc, const char **argv, struct cxl_ctx *ctx); + #endif /* _CXL_BUILTIN_H_ */ +diff --git a/cxl/cxl.c b/cxl/cxl.c +index ef4cda9..f0afcfe 100644 +--- a/cxl/cxl.c ++++ b/cxl/cxl.c +@@ -72,6 +72,7 @@ static struct cmd_struct commands[] = { + { "enable-port", .c_fn = cmd_enable_port }, + { "set-partition", .c_fn = cmd_set_partition }, + { "disable-bus", .c_fn = cmd_disable_bus }, ++ { "create-region", .c_fn = cmd_create_region }, + }; + + int main(int argc, const char **argv) +diff --git a/cxl/filter.h b/cxl/filter.h +index 609433c..d22d8b1 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -35,8 +35,10 @@ struct cxl_memdev *util_cxl_memdev_filter(struct cxl_memdev *memdev, + struct cxl_port *util_cxl_port_filter_by_memdev(struct cxl_port *port, + const char *ident, + const char *serial); +-struct cxl_region *util_cxl_region_filter(struct cxl_region *region, ++struct cxl_decoder *util_cxl_decoder_filter(struct cxl_decoder *decoder, + const char *__ident); ++struct cxl_region *util_cxl_region_filter(struct cxl_region *region, ++ const char *__ident); + + enum cxl_port_filter_mode { + CXL_PF_SINGLE, +diff --git a/cxl/json.c b/cxl/json.c +index 70cf286..9dc99df 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -547,6 +547,7 @@ void util_cxl_mappings_append_json(struct json_object *jregion, + cxl_mapping_foreach(region, mapping) { + struct json_object *jmapping; + struct cxl_decoder *decoder; ++ struct cxl_memdev *memdev; + + jmapping = json_object_new_object(); + if (!jmapping) +@@ -564,6 +565,14 @@ void util_cxl_mappings_append_json(struct json_object *jregion, + if (!decoder) + continue; + ++ memdev = cxl_decoder_get_memdev(decoder); ++ if (memdev) { ++ devname = cxl_memdev_get_devname(memdev); ++ jobj = json_object_new_string(devname); ++ if (jobj) ++ json_object_object_add(jmapping, "memdev", jobj); ++ } ++ + devname = cxl_decoder_get_devname(decoder); + jobj = json_object_new_string(devname); + if (jobj) +diff --git a/cxl/meson.build b/cxl/meson.build +index d63dcb1..f2474aa 100644 +--- a/cxl/meson.build ++++ b/cxl/meson.build +@@ -3,6 +3,7 @@ cxl_src = [ + 'list.c', + 'port.c', + 'bus.c', ++ 'region.c', + 'memdev.c', + 'json.c', + 'filter.c', +diff --git a/cxl/region.c b/cxl/region.c +new file mode 100644 +index 0000000..2791ac9 +--- /dev/null ++++ b/cxl/region.c +@@ -0,0 +1,550 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2020-2022 Intel Corporation. All rights reserved. */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "filter.h" ++#include "json.h" ++ ++static struct region_params { ++ const char *bus; ++ const char *size; ++ const char *ways; ++ const char *granularity; ++ const char *type; ++ const char *root_decoder; ++ const char *region; ++ bool memdevs; ++ bool force; ++ bool human; ++ bool debug; ++} param; ++ ++struct parsed_params { ++ u64 size; ++ u64 ep_min_size; ++ unsigned int ways; ++ unsigned int granularity; ++ const char **targets; ++ int num_targets; ++ struct cxl_decoder *root_decoder; ++ enum cxl_decoder_mode mode; ++}; ++ ++enum region_actions { ++ ACTION_CREATE, ++}; ++ ++static struct log_ctx rl; ++ ++#define BASE_OPTIONS() \ ++OPT_STRING('b', "bus", ¶m.bus, "bus name", \ ++ "Limit operation to the specified bus"), \ ++OPT_STRING('d', "decoder", ¶m.root_decoder, "root decoder name", \ ++ "Limit to / use the specified root decoder"), \ ++OPT_BOOLEAN(0, "debug", ¶m.debug, "turn on debug") ++ ++#define CREATE_OPTIONS() \ ++OPT_STRING('s', "size", ¶m.size, \ ++ "size in bytes or with a K/M/G etc. suffix", \ ++ "total size desired for the resulting region."), \ ++OPT_STRING('w', "ways", ¶m.ways, \ ++ "number of interleave ways", \ ++ "number of memdevs participating in the regions interleave set"), \ ++OPT_STRING('g', "granularity", \ ++ ¶m.granularity, "interleave granularity", \ ++ "granularity of the interleave set"), \ ++OPT_STRING('t', "type", ¶m.type, \ ++ "region type", "region type - 'pmem' or 'ram'"), \ ++OPT_BOOLEAN('m', "memdevs", ¶m.memdevs, \ ++ "non-option arguments are memdevs"), \ ++OPT_BOOLEAN('u', "human", ¶m.human, "use human friendly number formats") ++ ++static const struct option create_options[] = { ++ BASE_OPTIONS(), ++ CREATE_OPTIONS(), ++ OPT_END(), ++}; ++ ++ ++ ++static int parse_create_options(int argc, const char **argv, ++ struct parsed_params *p) ++{ ++ int i; ++ ++ if (!param.root_decoder) { ++ log_err(&rl, "no root decoder specified\n"); ++ return -EINVAL; ++ } ++ ++ if (param.type) { ++ p->mode = cxl_decoder_mode_from_ident(param.type); ++ if (p->mode == CXL_DECODER_MODE_NONE) { ++ log_err(&rl, "unsupported type: %s\n", param.type); ++ return -EINVAL; ++ } ++ } else { ++ p->mode = CXL_DECODER_MODE_PMEM; ++ } ++ ++ if (param.size) { ++ p->size = parse_size64(param.size); ++ if (p->size == ULLONG_MAX) { ++ log_err(&rl, "Invalid size: %s\n", param.size); ++ return -EINVAL; ++ } ++ } ++ ++ if (param.ways) { ++ unsigned long ways = strtoul(param.ways, NULL, 0); ++ ++ if (ways == ULONG_MAX || (int)ways <= 0) { ++ log_err(&rl, "Invalid interleave ways: %s\n", ++ param.ways); ++ return -EINVAL; ++ } ++ p->ways = ways; ++ } else if (argc) { ++ p->ways = argc; ++ } else { ++ log_err(&rl, ++ "couldn't determine interleave ways from options or arguments\n"); ++ return -EINVAL; ++ } ++ ++ if (param.granularity) { ++ unsigned long granularity = strtoul(param.granularity, NULL, 0); ++ ++ if (granularity == ULONG_MAX || (int)granularity <= 0) { ++ log_err(&rl, "Invalid interleave granularity: %s\n", ++ param.granularity); ++ return -EINVAL; ++ } ++ p->granularity = granularity; ++ } ++ ++ ++ if (argc > (int)p->ways) { ++ for (i = p->ways; i < argc; i++) ++ log_err(&rl, "extra argument: %s\n", p->targets[i]); ++ return -EINVAL; ++ } ++ ++ if (argc < (int)p->ways) { ++ log_err(&rl, ++ "too few target arguments (%d) for interleave ways (%u)\n", ++ argc, p->ways); ++ return -EINVAL; ++ } ++ ++ if (p->size && p->ways) { ++ if (p->size % p->ways) { ++ log_err(&rl, ++ "size (%lu) is not an integral multiple of interleave-ways (%u)\n", ++ p->size, p->ways); ++ return -EINVAL; ++ } ++ } ++ ++ /* ++ * For all practical purposes, -m is the default target type, but ++ * hold off on actively making that decision until a second target ++ * option is available. ++ */ ++ if (!param.memdevs) { ++ log_err(&rl, ++ "must specify option for target object types (-m)\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int parse_region_options(int argc, const char **argv, ++ struct cxl_ctx *ctx, enum region_actions action, ++ const struct option *options, ++ struct parsed_params *p, const char *usage) ++{ ++ const char * const u[] = { ++ usage, ++ NULL ++ }; ++ ++ argc = parse_options(argc, argv, options, u, 0); ++ p->targets = argv; ++ p->num_targets = argc; ++ ++ if (param.debug) { ++ cxl_set_log_priority(ctx, LOG_DEBUG); ++ rl.log_priority = LOG_DEBUG; ++ } else ++ rl.log_priority = LOG_INFO; ++ ++ switch(action) { ++ case ACTION_CREATE: ++ return parse_create_options(argc, argv, p); ++ default: ++ return 0; ++ } ++} ++ ++/** ++ * validate_memdev() - match memdev with the target provided, ++ * and determine its size contribution ++ * @memdev: cxl_memdev being tested for a match against the named target ++ * @target: target memdev ++ * @p: params structure ++ * ++ * This is called for each memdev in the system, and only returns 'true' if ++ * the memdev name matches the target argument being tested. Additionally, ++ * it sets an ep_min_size attribute that always contains the size of the ++ * smallest target in the provided list. This is used during the automatic ++ * size determination later, to ensure that all targets contribute equally ++ * to the region in case of unevenly sized memdevs. ++ */ ++static bool validate_memdev(struct cxl_memdev *memdev, const char *target, ++ struct parsed_params *p) ++{ ++ const char *devname = cxl_memdev_get_devname(memdev); ++ u64 size; ++ ++ if (strcmp(devname, target) != 0) ++ return false; ++ ++ size = cxl_memdev_get_pmem_size(memdev); ++ if (!p->ep_min_size) ++ p->ep_min_size = size; ++ else ++ p->ep_min_size = min(p->ep_min_size, size); ++ ++ return true; ++} ++ ++static int validate_config_memdevs(struct cxl_ctx *ctx, struct parsed_params *p) ++{ ++ unsigned int i, matched = 0; ++ ++ for (i = 0; i < p->ways; i++) { ++ struct cxl_memdev *memdev; ++ ++ cxl_memdev_foreach(ctx, memdev) ++ if (validate_memdev(memdev, p->targets[i], p)) ++ matched++; ++ } ++ if (matched != p->ways) { ++ log_err(&rl, ++ "one or more memdevs not found in CXL topology\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static int validate_decoder(struct cxl_decoder *decoder, ++ struct parsed_params *p) ++{ ++ const char *devname = cxl_decoder_get_devname(decoder); ++ ++ switch(p->mode) { ++ case CXL_DECODER_MODE_RAM: ++ if (!cxl_decoder_is_volatile_capable(decoder)) { ++ log_err(&rl, "%s is not volatile capable\n", devname); ++ return -EINVAL; ++ } ++ break; ++ case CXL_DECODER_MODE_PMEM: ++ if (!cxl_decoder_is_pmem_capable(decoder)) { ++ log_err(&rl, "%s is not pmem capable\n", devname); ++ return -EINVAL; ++ } ++ break; ++ default: ++ log_err(&rl, "unknown type: %s\n", param.type); ++ return -EINVAL; ++ } ++ ++ /* TODO check if the interleave config is possible under this decoder */ ++ ++ return 0; ++} ++ ++static int create_region_validate_config(struct cxl_ctx *ctx, ++ struct parsed_params *p) ++{ ++ struct cxl_bus *bus; ++ int rc; ++ ++ cxl_bus_foreach(ctx, bus) { ++ struct cxl_decoder *decoder; ++ struct cxl_port *port; ++ ++ if (!util_cxl_bus_filter(bus, param.bus)) ++ continue; ++ ++ port = cxl_bus_get_port(bus); ++ if (!cxl_port_is_root(port)) ++ continue; ++ ++ cxl_decoder_foreach (port, decoder) { ++ if (util_cxl_decoder_filter(decoder, ++ param.root_decoder)) { ++ p->root_decoder = decoder; ++ goto found; ++ } ++ } ++ } ++ ++found: ++ if (p->root_decoder == NULL) { ++ log_err(&rl, "%s not found in CXL topology\n", ++ param.root_decoder); ++ return -ENXIO; ++ } ++ ++ rc = validate_decoder(p->root_decoder, p); ++ if (rc) ++ return rc; ++ ++ return validate_config_memdevs(ctx, p); ++} ++ ++static struct cxl_decoder * ++cxl_memdev_target_find_decoder(struct cxl_ctx *ctx, const char *memdev_name) ++{ ++ struct cxl_endpoint *ep = NULL; ++ struct cxl_decoder *decoder; ++ struct cxl_memdev *memdev; ++ struct cxl_port *port; ++ ++ cxl_memdev_foreach(ctx, memdev) { ++ const char *devname = cxl_memdev_get_devname(memdev); ++ ++ if (strcmp(devname, memdev_name) != 0) ++ continue; ++ ++ ep = cxl_memdev_get_endpoint(memdev); ++ } ++ ++ if (!ep) { ++ log_err(&rl, "could not get an endpoint for %s\n", ++ memdev_name); ++ return NULL; ++ } ++ ++ port = cxl_endpoint_get_port(ep); ++ if (!port) { ++ log_err(&rl, "could not get a port for %s\n", ++ memdev_name); ++ return NULL; ++ } ++ ++ cxl_decoder_foreach(port, decoder) ++ if (cxl_decoder_get_size(decoder) == 0) ++ return decoder; ++ ++ log_err(&rl, "could not get a free decoder for %s\n", memdev_name); ++ return NULL; ++} ++ ++#define try(prefix, op, dev, p) \ ++do { \ ++ int __rc = prefix##_##op(dev, p); \ ++ if (__rc) { \ ++ log_err(&rl, "%s: " #op " failed: %s\n", \ ++ prefix##_get_devname(dev), \ ++ strerror(abs(__rc))); \ ++ rc = __rc; \ ++ goto err_delete; \ ++ } \ ++} while (0) ++ ++static int cxl_region_determine_granularity(struct cxl_region *region, ++ struct parsed_params *p) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ unsigned int granularity, ways; ++ ++ /* Default granularity will be the root decoder's granularity */ ++ granularity = cxl_decoder_get_interleave_granularity(p->root_decoder); ++ if (granularity == 0 || granularity == UINT_MAX) { ++ log_err(&rl, "%s: unable to determine root decoder granularity\n", ++ devname); ++ return -ENXIO; ++ } ++ ++ /* If no user-supplied granularity, just use the default */ ++ if (!p->granularity) ++ return granularity; ++ ++ ways = cxl_decoder_get_interleave_ways(p->root_decoder); ++ if (ways == 0 || ways == UINT_MAX) { ++ log_err(&rl, "%s: unable to determine root decoder ways\n", ++ devname); ++ return -ENXIO; ++ } ++ ++ /* For ways == 1, any user-supplied granularity is fine */ ++ if (ways == 1) ++ return p->granularity; ++ ++ /* ++ * For ways > 1, only allow the same granularity as the selected ++ * root decoder ++ */ ++ if (p->granularity == granularity) ++ return granularity; ++ ++ log_err(&rl, ++ "%s: For an x%d root, only root decoder granularity (%d) permitted\n", ++ devname, ways, granularity); ++ return -EINVAL; ++} ++ ++static int create_region(struct cxl_ctx *ctx, int *count, ++ struct parsed_params *p) ++{ ++ unsigned long flags = UTIL_JSON_TARGETS; ++ struct json_object *jregion; ++ unsigned int i, granularity; ++ struct cxl_region *region; ++ const char *devname; ++ uuid_t uuid; ++ u64 size; ++ int rc; ++ ++ rc = create_region_validate_config(ctx, p); ++ if (rc) ++ return rc; ++ ++ if (p->size) { ++ size = p->size; ++ } else if (p->ep_min_size) { ++ size = p->ep_min_size * p->ways; ++ } else { ++ log_err(&rl, "%s: unable to determine region size\n", __func__); ++ return -ENXIO; ++ } ++ ++ if (p->mode == CXL_DECODER_MODE_PMEM) { ++ region = cxl_decoder_create_pmem_region(p->root_decoder); ++ if (!region) { ++ log_err(&rl, "failed to create region under %s\n", ++ param.root_decoder); ++ return -ENXIO; ++ } ++ } else { ++ log_err(&rl, "region type '%s' not supported yet\n", ++ param.type); ++ return -EOPNOTSUPP; ++ } ++ ++ devname = cxl_region_get_devname(region); ++ ++ rc = cxl_region_determine_granularity(region, p); ++ if (rc < 0) ++ goto err_delete; ++ granularity = rc; ++ ++ uuid_generate(uuid); ++ try(cxl_region, set_interleave_granularity, region, granularity); ++ try(cxl_region, set_interleave_ways, region, p->ways); ++ try(cxl_region, set_uuid, region, uuid); ++ try(cxl_region, set_size, region, size); ++ ++ for (i = 0; i < p->ways; i++) { ++ struct cxl_decoder *ep_decoder = NULL; ++ ++ ep_decoder = cxl_memdev_target_find_decoder(ctx, p->targets[i]); ++ if (!ep_decoder) { ++ rc = -ENXIO; ++ goto err_delete; ++ } ++ if (cxl_decoder_get_mode(ep_decoder) != p->mode) { ++ /* ++ * The memdev_target_find_decoder() helper returns a free ++ * decoder whose size has been checked for 0. ++ * Thus it is safe to change the mode here if needed. ++ */ ++ try(cxl_decoder, set_dpa_size, ep_decoder, 0); ++ try(cxl_decoder, set_mode, ep_decoder, p->mode); ++ } ++ try(cxl_decoder, set_dpa_size, ep_decoder, size/p->ways); ++ rc = cxl_region_set_target(region, i, ep_decoder); ++ if (rc) { ++ log_err(&rl, "%s: failed to set target%d to %s\n", ++ devname, i, p->targets[i]); ++ goto err_delete; ++ } ++ } ++ ++ rc = cxl_region_decode_commit(region); ++ if (rc) { ++ log_err(&rl, "%s: failed to commit decode: %s\n", devname, ++ strerror(-rc)); ++ goto err_delete; ++ } ++ ++ rc = cxl_region_enable(region); ++ if (rc) { ++ log_err(&rl, "%s: failed to enable: %s\n", devname, ++ strerror(-rc)); ++ goto err_delete; ++ } ++ *count = 1; ++ ++ if (isatty(1)) ++ flags |= UTIL_JSON_HUMAN; ++ jregion = util_cxl_region_to_json(region, flags); ++ if (jregion) ++ printf("%s\n", json_object_to_json_string_ext(jregion, ++ JSON_C_TO_STRING_PRETTY)); ++ ++ return 0; ++ ++err_delete: ++ cxl_region_delete(region); ++ return rc; ++} ++ ++static int region_action(int argc, const char **argv, struct cxl_ctx *ctx, ++ enum region_actions action, ++ const struct option *options, struct parsed_params *p, ++ int *count, const char *u) ++{ ++ int rc = -ENXIO; ++ ++ log_init(&rl, "cxl region", "CXL_REGION_LOG"); ++ rc = parse_region_options(argc, argv, ctx, action, options, p, u); ++ if (rc) ++ return rc; ++ ++ if (action == ACTION_CREATE) ++ return create_region(ctx, count, p); ++ ++ return rc; ++} ++ ++int cmd_create_region(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ const char *u = "cxl create-region ... []"; ++ struct parsed_params p = { 0 }; ++ int rc, count = 0; ++ ++ rc = region_action(argc, argv, ctx, ACTION_CREATE, create_options, &p, ++ &count, u); ++ log_info(&rl, "created %d region%s\n", count, count == 1 ? "" : "s"); ++ return rc == 0 ? 0 : EXIT_FAILURE; ++} +-- +2.27.0 + diff --git a/SOURCES/0196-cxl-add-commands-to-enable-disable-destroy-region.patch b/SOURCES/0196-cxl-add-commands-to-enable-disable-destroy-region.patch new file mode 100644 index 0000000..279a4ca --- /dev/null +++ b/SOURCES/0196-cxl-add-commands-to-enable-disable-destroy-region.patch @@ -0,0 +1,459 @@ +From 93122376250a8a5cfae635e9729c34dfaa0fd116 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Mon, 15 Aug 2022 13:22:11 -0600 +Subject: [PATCH 196/217] cxl: add commands to {enable,disable,destroy}-region + +With a template from cxl-create-region in place, add its friends: + + cxl enable-region + cxl disable-region + cxl destroy-region + +Link: https://lore.kernel.org/r/20220815192214.545800-9-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-destroy-region.txt | 41 +++++ + Documentation/cxl/cxl-disable-region.txt | 36 +++++ + Documentation/cxl/cxl-enable-region.txt | 36 +++++ + Documentation/cxl/decoder-option.txt | 6 + + Documentation/cxl/meson.build | 4 + + cxl/builtin.h | 3 + + cxl/cxl.c | 3 + + cxl/region.c | 193 ++++++++++++++++++++++- + 8 files changed, 321 insertions(+), 1 deletion(-) + create mode 100644 Documentation/cxl/cxl-destroy-region.txt + create mode 100644 Documentation/cxl/cxl-disable-region.txt + create mode 100644 Documentation/cxl/cxl-enable-region.txt + create mode 100644 Documentation/cxl/decoder-option.txt + +diff --git a/Documentation/cxl/cxl-destroy-region.txt b/Documentation/cxl/cxl-destroy-region.txt +new file mode 100644 +index 0000000..74f4093 +--- /dev/null ++++ b/Documentation/cxl/cxl-destroy-region.txt +@@ -0,0 +1,41 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-destroy-region(1) ++===================== ++ ++NAME ++---- ++cxl-destroy-region - destroy specified region(s). ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl destroy-region []' ++ ++include::region-description.txt[] ++ ++EXAMPLE ++------- ++---- ++# cxl destroy-region all ++destroyed 2 regions ++---- ++ ++OPTIONS ++------- ++include::bus-option.txt[] ++ ++-f:: ++--force:: ++ Force a destroy operation even if the region is active. ++ This will attempt to disable the region first. ++ ++include::decoder-option.txt[] ++ ++include::debug-option.txt[] ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-list[1], linkcxl:cxl-create-region[1] +diff --git a/Documentation/cxl/cxl-disable-region.txt b/Documentation/cxl/cxl-disable-region.txt +new file mode 100644 +index 0000000..6a39aee +--- /dev/null ++++ b/Documentation/cxl/cxl-disable-region.txt +@@ -0,0 +1,36 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-disable-region(1) ++===================== ++ ++NAME ++---- ++cxl-disable-region - disable specified region(s). ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl disable-region []' ++ ++include::region-description.txt[] ++ ++EXAMPLE ++------- ++---- ++# cxl disable-region all ++disabled 2 regions ++---- ++ ++OPTIONS ++------- ++include::bus-option.txt[] ++ ++include::decoder-option.txt[] ++ ++include::debug-option.txt[] ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-list[1], linkcxl:cxl-enable-region[1] +diff --git a/Documentation/cxl/cxl-enable-region.txt b/Documentation/cxl/cxl-enable-region.txt +new file mode 100644 +index 0000000..f6ef00f +--- /dev/null ++++ b/Documentation/cxl/cxl-enable-region.txt +@@ -0,0 +1,36 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++cxl-enable-region(1) ++===================== ++ ++NAME ++---- ++cxl-enable-region - enable specified region(s). ++ ++SYNOPSIS ++-------- ++[verse] ++'cxl enable-region []' ++ ++include::region-description.txt[] ++ ++EXAMPLE ++------- ++---- ++# cxl enable-region all ++enabled 2 regions ++---- ++ ++OPTIONS ++------- ++include::bus-option.txt[] ++ ++include::decoder-option.txt[] ++ ++include::debug-option.txt[] ++ ++include::../copyright.txt[] ++ ++SEE ALSO ++-------- ++linkcxl:cxl-list[1], linkcxl:cxl-disable-region[1] +diff --git a/Documentation/cxl/decoder-option.txt b/Documentation/cxl/decoder-option.txt +new file mode 100644 +index 0000000..e638d6e +--- /dev/null ++++ b/Documentation/cxl/decoder-option.txt +@@ -0,0 +1,6 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++-d:: ++--decoder=:: ++ The root decoder to limit the operation to. Only regions that are ++ children of the specified decoder will be acted upon. +diff --git a/Documentation/cxl/meson.build b/Documentation/cxl/meson.build +index 340cdee..147ea71 100644 +--- a/Documentation/cxl/meson.build ++++ b/Documentation/cxl/meson.build +@@ -24,6 +24,7 @@ filedeps = [ + 'labels-options.txt', + 'debug-option.txt', + 'region-description.txt', ++ 'decoder-option.txt', + ] + + cxl_manpages = [ +@@ -41,6 +42,9 @@ cxl_manpages = [ + 'cxl-reserve-dpa.txt', + 'cxl-free-dpa.txt', + 'cxl-create-region.txt', ++ 'cxl-disable-region.txt', ++ 'cxl-enable-region.txt', ++ 'cxl-destroy-region.txt', + ] + + foreach man : cxl_manpages +diff --git a/cxl/builtin.h b/cxl/builtin.h +index 843bada..b28c221 100644 +--- a/cxl/builtin.h ++++ b/cxl/builtin.h +@@ -19,4 +19,7 @@ int cmd_enable_port(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_set_partition(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_disable_bus(int argc, const char **argv, struct cxl_ctx *ctx); + int cmd_create_region(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_enable_region(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_disable_region(int argc, const char **argv, struct cxl_ctx *ctx); ++int cmd_destroy_region(int argc, const char **argv, struct cxl_ctx *ctx); + #endif /* _CXL_BUILTIN_H_ */ +diff --git a/cxl/cxl.c b/cxl/cxl.c +index f0afcfe..dd1be7a 100644 +--- a/cxl/cxl.c ++++ b/cxl/cxl.c +@@ -73,6 +73,9 @@ static struct cmd_struct commands[] = { + { "set-partition", .c_fn = cmd_set_partition }, + { "disable-bus", .c_fn = cmd_disable_bus }, + { "create-region", .c_fn = cmd_create_region }, ++ { "enable-region", .c_fn = cmd_enable_region }, ++ { "disable-region", .c_fn = cmd_disable_region }, ++ { "destroy-region", .c_fn = cmd_destroy_region }, + }; + + int main(int argc, const char **argv) +diff --git a/cxl/region.c b/cxl/region.c +index 2791ac9..b22d3c8 100644 +--- a/cxl/region.c ++++ b/cxl/region.c +@@ -45,6 +45,9 @@ struct parsed_params { + + enum region_actions { + ACTION_CREATE, ++ ACTION_ENABLE, ++ ACTION_DISABLE, ++ ACTION_DESTROY, + }; + + static struct log_ctx rl; +@@ -78,7 +81,22 @@ static const struct option create_options[] = { + OPT_END(), + }; + ++static const struct option enable_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; + ++static const struct option disable_options[] = { ++ BASE_OPTIONS(), ++ OPT_END(), ++}; ++ ++static const struct option destroy_options[] = { ++ BASE_OPTIONS(), ++ OPT_BOOLEAN('f', "force", ¶m.force, ++ "destroy region even if currently active"), ++ OPT_END(), ++}; + + static int parse_create_options(int argc, const char **argv, + struct parsed_params *p) +@@ -519,12 +537,122 @@ err_delete: + return rc; + } + ++static int destroy_region(struct cxl_region *region) ++{ ++ const char *devname = cxl_region_get_devname(region); ++ unsigned int ways, i; ++ int rc; ++ ++ /* First, unbind/disable the region if needed */ ++ if (cxl_region_is_enabled(region)) { ++ if (param.force) { ++ rc = cxl_region_disable(region); ++ if (rc) { ++ log_err(&rl, "%s: error disabling region: %s\n", ++ devname, strerror(-rc)); ++ return rc; ++ } ++ } else { ++ log_err(&rl, "%s active. Disable it or use --force\n", ++ devname); ++ return -EBUSY; ++ } ++ } ++ ++ /* Reset the region decode in preparation for removal */ ++ rc = cxl_region_decode_reset(region); ++ if (rc) { ++ log_err(&rl, "%s: failed to reset decode: %s\n", devname, ++ strerror(-rc)); ++ return rc; ++ } ++ ++ /* Reset all endpoint decoders and region targets */ ++ ways = cxl_region_get_interleave_ways(region); ++ if (ways == 0 || ways == UINT_MAX) { ++ log_err(&rl, "%s: error getting interleave ways\n", devname); ++ return -ENXIO; ++ } ++ ++ for (i = 0; i < ways; i++) { ++ struct cxl_decoder *ep_decoder; ++ ++ ep_decoder = cxl_region_get_target_decoder(region, i); ++ if (!ep_decoder) ++ return -ENXIO; ++ ++ rc = cxl_region_clear_target(region, i); ++ if (rc) { ++ log_err(&rl, "%s: clearing target%d failed: %s\n", ++ devname, i, strerror(abs(rc))); ++ return rc; ++ } ++ ++ rc = cxl_decoder_set_dpa_size(ep_decoder, 0); ++ if (rc) { ++ log_err(&rl, "%s: set_dpa_size failed: %s\n", ++ cxl_decoder_get_devname(ep_decoder), ++ strerror(abs(rc))); ++ return rc; ++ } ++ } ++ ++ /* Finally, delete the region */ ++ return cxl_region_delete(region); ++} ++ ++static int do_region_xable(struct cxl_region *region, enum region_actions action) ++{ ++ switch (action) { ++ case ACTION_ENABLE: ++ return cxl_region_enable(region); ++ case ACTION_DISABLE: ++ return cxl_region_disable(region); ++ case ACTION_DESTROY: ++ return destroy_region(region); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int decoder_region_action(struct parsed_params *p, ++ struct cxl_decoder *decoder, ++ enum region_actions action, int *count) ++{ ++ struct cxl_region *region, *_r; ++ int rc = 0, err_rc = 0; ++ ++ cxl_region_foreach_safe (decoder, region, _r) { ++ int i, match = 0; ++ ++ for (i = 0; i < p->num_targets; i++) { ++ if (util_cxl_region_filter(region, p->targets[i])) { ++ match = 1; ++ break; ++ } ++ } ++ if (!match) ++ continue; ++ ++ rc = do_region_xable(region, action); ++ if (rc == 0) { ++ *count += 1; ++ } else { ++ log_err(&rl, "%s: failed: %s\n", ++ cxl_region_get_devname(region), strerror(-rc)); ++ err_rc = rc; ++ } ++ } ++ return err_rc ? err_rc : rc; ++} ++ + static int region_action(int argc, const char **argv, struct cxl_ctx *ctx, + enum region_actions action, + const struct option *options, struct parsed_params *p, + int *count, const char *u) + { +- int rc = -ENXIO; ++ int rc = 0, err_rc = 0; ++ struct cxl_bus *bus; + + log_init(&rl, "cxl region", "CXL_REGION_LOG"); + rc = parse_region_options(argc, argv, ctx, action, options, p, u); +@@ -534,6 +662,33 @@ static int region_action(int argc, const char **argv, struct cxl_ctx *ctx, + if (action == ACTION_CREATE) + return create_region(ctx, count, p); + ++ cxl_bus_foreach(ctx, bus) { ++ struct cxl_decoder *decoder; ++ struct cxl_port *port; ++ ++ if (!util_cxl_bus_filter(bus, param.bus)) ++ continue; ++ ++ port = cxl_bus_get_port(bus); ++ if (!cxl_port_is_root(port)) ++ continue; ++ ++ cxl_decoder_foreach (port, decoder) { ++ decoder = util_cxl_decoder_filter(decoder, ++ param.root_decoder); ++ if (!decoder) ++ continue; ++ rc = decoder_region_action(p, decoder, action, count); ++ if (rc) ++ err_rc = rc; ++ } ++ } ++ ++ if (err_rc) { ++ log_err(&rl, "one or more failures, last failure: %s\n", ++ strerror(-err_rc)); ++ return err_rc; ++ } + return rc; + } + +@@ -548,3 +703,39 @@ int cmd_create_region(int argc, const char **argv, struct cxl_ctx *ctx) + log_info(&rl, "created %d region%s\n", count, count == 1 ? "" : "s"); + return rc == 0 ? 0 : EXIT_FAILURE; + } ++ ++int cmd_enable_region(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ const char *u = "cxl enable-region ... []"; ++ struct parsed_params p = { 0 }; ++ int rc, count = 0; ++ ++ rc = region_action(argc, argv, ctx, ACTION_ENABLE, enable_options, &p, ++ &count, u); ++ log_info(&rl, "enabled %d region%s\n", count, count == 1 ? "" : "s"); ++ return rc == 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_disable_region(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ const char *u = "cxl disable-region ... []"; ++ struct parsed_params p = { 0 }; ++ int rc, count = 0; ++ ++ rc = region_action(argc, argv, ctx, ACTION_DISABLE, disable_options, &p, ++ &count, u); ++ log_info(&rl, "disabled %d region%s\n", count, count == 1 ? "" : "s"); ++ return rc == 0 ? 0 : EXIT_FAILURE; ++} ++ ++int cmd_destroy_region(int argc, const char **argv, struct cxl_ctx *ctx) ++{ ++ const char *u = "cxl destroy-region ... []"; ++ struct parsed_params p = { 0 }; ++ int rc, count = 0; ++ ++ rc = region_action(argc, argv, ctx, ACTION_DESTROY, destroy_options, &p, ++ &count, u); ++ log_info(&rl, "destroyed %d region%s\n", count, count == 1 ? "" : "s"); ++ return rc == 0 ? 0 : EXIT_FAILURE; ++} +-- +2.27.0 + diff --git a/SOURCES/0197-cxl-list-make-memdevs-and-regions-the-default-listin.patch b/SOURCES/0197-cxl-list-make-memdevs-and-regions-the-default-listin.patch new file mode 100644 index 0000000..d102e83 --- /dev/null +++ b/SOURCES/0197-cxl-list-make-memdevs-and-regions-the-default-listin.patch @@ -0,0 +1,39 @@ +From fade1a8039446ed1aa8656f49121886c71d221a4 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Mon, 15 Aug 2022 13:22:12 -0600 +Subject: [PATCH 197/217] cxl/list: make memdevs and regions the default + listing + +Instead of only listing regions by default (which can often be empty if +no regions have been configured), change the default listing mode to +both memdevs and regions. This will allow a plain 'cxl-list' to be a +quick health check of whether all the expected memdevs have enumerated +correctly, and see any regions that have been configured. + +Link: https://lore.kernel.org/r/20220815192214.545800-10-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/list.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/cxl/list.c b/cxl/list.c +index 88ca9d9..5f604ec 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -100,9 +100,10 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + param.regions = true; + } + +- /* List regions by default */ ++ /* List regions and memdevs by default */ + if (num_list_flags() == 0) { + param.regions = true; ++ param.memdevs = true; + } + + log_init(¶m.ctx, "cxl list", "CXL_LIST_LOG"); +-- +2.27.0 + diff --git a/SOURCES/0198-test-add-a-cxl-create-region-test.patch b/SOURCES/0198-test-add-a-cxl-create-region-test.patch new file mode 100644 index 0000000..2001fad --- /dev/null +++ b/SOURCES/0198-test-add-a-cxl-create-region-test.patch @@ -0,0 +1,173 @@ +From 11ca099b52ec339b7464946db144e60399ffa344 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Mon, 15 Aug 2022 13:22:13 -0600 +Subject: [PATCH 198/217] test: add a cxl-create-region test + +Add a unit test to exercise the cxl-create-region command with different +combinations of memdevs and decoders, using cxl_test based mocked +devices. + +Link: https://lore.kernel.org/r/20220815192214.545800-11-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/cxl-create-region.sh | 125 ++++++++++++++++++++++++++++++++++++++ + test/meson.build | 2 + + 2 files changed, 127 insertions(+) + create mode 100644 test/cxl-create-region.sh + +diff --git a/test/cxl-create-region.sh b/test/cxl-create-region.sh +new file mode 100644 +index 0000000..66df38f +--- /dev/null ++++ b/test/cxl-create-region.sh +@@ -0,0 +1,125 @@ ++#!/bin/bash ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (C) 2022 Intel Corporation. All rights reserved. ++ ++. $(dirname $0)/common ++ ++rc=1 ++ ++set -ex ++ ++trap 'err $LINENO' ERR ++ ++check_prereq "jq" ++ ++modprobe -r cxl_test ++modprobe cxl_test ++udevadm settle ++ ++destroy_regions() ++{ ++ if [[ "$*" ]]; then ++ $CXL destroy-region -f -b cxl_test "$@" ++ else ++ $CXL destroy-region -f -b cxl_test all ++ fi ++} ++ ++create_x1_region() ++{ ++ mem="$1" ++ ++ # find a pmem capable root decoder for this mem ++ decoder=$($CXL list -b cxl_test -D -d root -m "$mem" | ++ jq -r ".[] | ++ select(.pmem_capable == true) | ++ select(.nr_targets == 1) | ++ .decoder") ++ ++ if [[ ! $decoder ]]; then ++ echo "no suitable decoder found for $mem, skipping" ++ return ++ fi ++ ++ # create region ++ region=$($CXL create-region -d "$decoder" -m "$mem" | jq -r ".region") ++ ++ if [[ ! $region ]]; then ++ echo "create-region failed for $decoder / $mem" ++ err "$LINENO" ++ fi ++ ++ # cycle disable/enable ++ $CXL disable-region --bus=cxl_test "$region" ++ $CXL enable-region --bus=cxl_test "$region" ++ ++ # cycle destroying and creating the same region ++ destroy_regions "$region" ++ region=$($CXL create-region -d "$decoder" -m "$mem" | jq -r ".region") ++ ++ if [[ ! $region ]]; then ++ echo "create-region failed for $decoder / $mem" ++ err "$LINENO" ++ fi ++ destroy_regions "$region" ++} ++ ++create_subregions() ++{ ++ slice=$((256 << 20)) ++ mem="$1" ++ ++ # find a pmem capable root decoder for this mem ++ decoder=$($CXL list -b cxl_test -D -d root -m "$mem" | ++ jq -r ".[] | ++ select(.pmem_capable == true) | ++ select(.nr_targets == 1) | ++ .decoder") ++ ++ if [[ ! $decoder ]]; then ++ echo "no suitable decoder found for $mem, skipping" ++ return ++ fi ++ ++ size="$($CXL list -m "$mem" | jq -r '.[].pmem_size')" ++ if [[ ! $size ]]; then ++ echo "$mem: unable to determine size" ++ err "$LINENO" ++ fi ++ ++ num_regions=$((size / slice)) ++ ++ declare -a regions ++ for (( i = 0; i < num_regions; i++ )); do ++ regions[$i]=$($CXL create-region -d "$decoder" -m "$mem" -s "$slice" | jq -r ".region") ++ if [[ ! ${regions[$i]} ]]; then ++ echo "create sub-region failed for $decoder / $mem" ++ err "$LINENO" ++ fi ++ udevadm settle ++ done ++ ++ echo "created $num_regions subregions:" ++ for (( i = 0; i < num_regions; i++ )); do ++ echo "${regions[$i]}" ++ done ++ ++ for (( i = (num_regions - 1); i >= 0; i-- )); do ++ destroy_regions "${regions[$i]}" ++ done ++} ++ ++# test reading labels directly through cxl-cli ++readarray -t mems < <("$CXL" list -b cxl_test -M | jq -r '.[].memdev') ++ ++for mem in ${mems[@]}; do ++ create_x1_region "$mem" ++done ++ ++# test multiple subregions under the same decoder, using slices of the same memdev ++# to test out back-to-back pmem DPA allocations on memdevs ++for mem in ${mems[@]}; do ++ create_subregions "$mem" ++done ++ ++modprobe -r cxl_test +diff --git a/test/meson.build b/test/meson.build +index b382f46..5953c28 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -153,6 +153,7 @@ track_uuid = find_program('track-uuid.sh') + cxl_topo = find_program('cxl-topology.sh') + cxl_sysfs = find_program('cxl-region-sysfs.sh') + cxl_labels = find_program('cxl-labels.sh') ++cxl_create_region = find_program('cxl-create-region.sh') + + tests = [ + [ 'libndctl', libndctl, 'ndctl' ], +@@ -180,6 +181,7 @@ tests = [ + [ 'cxl-topology.sh', cxl_topo, 'cxl' ], + [ 'cxl-region-sysfs.sh', cxl_sysfs, 'cxl' ], + [ 'cxl-labels.sh', cxl_labels, 'cxl' ], ++ [ 'cxl-create-region.sh', cxl_create_region, 'cxl' ], + ] + + if get_option('destructive').enabled() +-- +2.27.0 + diff --git a/SOURCES/0199-cxl-decoder-add-a-max_available_extent-attribute.patch b/SOURCES/0199-cxl-decoder-add-a-max_available_extent-attribute.patch new file mode 100644 index 0000000..c56487e --- /dev/null +++ b/SOURCES/0199-cxl-decoder-add-a-max_available_extent-attribute.patch @@ -0,0 +1,240 @@ +From bf0c44e79c0db04b0c1eea884022dfbdc011b979 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Mon, 15 Aug 2022 13:22:14 -0600 +Subject: [PATCH 199/217] cxl/decoder: add a max_available_extent attribute + +Add a max_available_extent attribute to cxl_decoder. In order to aid in +its calculation, change the order of regions in the root decoder's list +to be sorted by start HPA of the region. + +Additionally, emit this attribute in decoder listings, and consult it +for available space before creating a new region. + +Link: https://lore.kernel.org/r/20220815192214.545800-12-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/json.c | 8 +++++ + cxl/lib/libcxl.c | 84 +++++++++++++++++++++++++++++++++++++++++++++- + cxl/lib/libcxl.sym | 1 + + cxl/lib/private.h | 1 + + cxl/libcxl.h | 3 ++ + cxl/region.c | 14 +++++++- + 6 files changed, 109 insertions(+), 2 deletions(-) + +diff --git a/cxl/json.c b/cxl/json.c +index 9dc99df..9cec58b 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -499,6 +499,14 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + } + + if (cxl_port_is_root(port) && cxl_decoder_is_mem_capable(decoder)) { ++ size = cxl_decoder_get_max_available_extent(decoder); ++ if (size < ULLONG_MAX) { ++ jobj = util_json_object_size(size, flags); ++ if (jobj) ++ json_object_object_add(jdecoder, ++ "max_available_extent", ++ jobj); ++ } + if (cxl_decoder_is_pmem_capable(decoder)) { + jobj = json_object_new_boolean(true); + if (jobj) +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index fd2ea4f..c7dc2b0 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -455,6 +455,16 @@ CXL_EXPORT int cxl_region_delete(struct cxl_region *region) + return 0; + } + ++static int region_start_cmp(struct cxl_region *r1, struct cxl_region *r2) ++{ ++ if (r1->start == r2->start) ++ return 0; ++ else if (r1->start < r2->start) ++ return -1; ++ else ++ return 1; ++} ++ + static void *add_cxl_region(void *parent, int id, const char *cxlregion_base) + { + const char *devname = devpath_to_devname(cxlregion_base); +@@ -539,7 +549,7 @@ static void *add_cxl_region(void *parent, int id, const char *cxlregion_base) + break; + } + +- list_add(&decoder->regions, ®ion->list); ++ list_add_sorted(&decoder->regions, region, list, region_start_cmp); + + return region; + err: +@@ -1618,6 +1628,70 @@ cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint) + return NULL; + } + ++static bool cxl_region_is_configured(struct cxl_region *region) ++{ ++ return region->size && (region->decode_state != CXL_DECODE_RESET); ++} ++ ++/** ++ * cxl_decoder_calc_max_available_extent() - calculate max available free space ++ * @decoder - the root decoder to calculate the free extents for ++ * ++ * The add_cxl_region() function adds regions to the parent decoder's list ++ * sorted by the region's start HPAs. It can also be assumed that regions have ++ * no overlapped / aliased HPA space. Therefore, calculating each extent is as ++ * simple as walking the region list in order, and subtracting the previous ++ * region's end HPA from the next region's start HPA (and taking into account ++ * the decoder's start and end HPAs as well). ++ */ ++static unsigned long long ++cxl_decoder_calc_max_available_extent(struct cxl_decoder *decoder) ++{ ++ u64 prev_end, decoder_end, cur_extent, max_extent = 0; ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ struct cxl_region *region; ++ ++ if (!cxl_port_is_root(port)) { ++ err(ctx, "%s: not a root decoder\n", ++ cxl_decoder_get_devname(decoder)); ++ return ULLONG_MAX; ++ } ++ ++ /* ++ * Preload prev_end with an imaginary region that ends just before ++ * the decoder's start, so that the extent calculation for the ++ * first region Just Works ++ */ ++ prev_end = decoder->start - 1; ++ ++ cxl_region_foreach(decoder, region) { ++ if (!cxl_region_is_configured(region)) ++ continue; ++ ++ /* ++ * region->start - prev_end would get the difference in ++ * addresses, but a difference of 1 in addresses implies ++ * an extent of 0. Hence the '-1'. ++ */ ++ cur_extent = region->start - prev_end - 1; ++ max_extent = max(max_extent, cur_extent); ++ prev_end = region->start + region->size - 1; ++ } ++ ++ /* ++ * Finally, consider the extent after the last region, up to the end ++ * of the decoder's address space, if any. If there were no regions, ++ * this simply reduces to decoder->size. ++ * Subtracting two addrs gets us a 'size' directly, no need for +/- 1. ++ */ ++ decoder_end = decoder->start + decoder->size - 1; ++ cur_extent = decoder_end - prev_end; ++ max_extent = max(max_extent, cur_extent); ++ ++ return max_extent; ++} ++ + static int decoder_id_cmp(struct cxl_decoder *d1, struct cxl_decoder *d2) + { + return d1->id - d2->id; +@@ -1748,6 +1822,8 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) + if (sysfs_read_attr(ctx, path, buf) == 0) + *(flag->flag) = !!strtoul(buf, NULL, 0); + } ++ decoder->max_available_extent = ++ cxl_decoder_calc_max_available_extent(decoder); + break; + } + } +@@ -1912,6 +1988,12 @@ cxl_decoder_get_dpa_size(struct cxl_decoder *decoder) + return decoder->dpa_size; + } + ++CXL_EXPORT unsigned long long ++cxl_decoder_get_max_available_extent(struct cxl_decoder *decoder) ++{ ++ return decoder->max_available_extent; ++} ++ + CXL_EXPORT int cxl_decoder_set_dpa_size(struct cxl_decoder *decoder, + unsigned long long size) + { +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index cb23a0b..549f88d 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -213,4 +213,5 @@ global: + cxl_decoder_get_memdev; + cxl_decoder_get_interleave_granularity; + cxl_decoder_get_interleave_ways; ++ cxl_decoder_get_max_available_extent; + } LIBCXL_2; +diff --git a/cxl/lib/private.h b/cxl/lib/private.h +index 8bc9620..437eade 100644 +--- a/cxl/lib/private.h ++++ b/cxl/lib/private.h +@@ -104,6 +104,7 @@ struct cxl_decoder { + u64 size; + u64 dpa_resource; + u64 dpa_size; ++ u64 max_available_extent; + void *dev_buf; + size_t buf_len; + char *dev_path; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 69d9c09..61c7fc4 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -134,6 +134,9 @@ unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_dpa_resource(struct cxl_decoder *decoder); + unsigned long long cxl_decoder_get_dpa_size(struct cxl_decoder *decoder); ++unsigned long long ++cxl_decoder_get_max_available_extent(struct cxl_decoder *decoder); ++ + enum cxl_decoder_mode { + CXL_DECODER_MODE_NONE, + CXL_DECODER_MODE_MIXED, +diff --git a/cxl/region.c b/cxl/region.c +index b22d3c8..a30313c 100644 +--- a/cxl/region.c ++++ b/cxl/region.c +@@ -438,9 +438,9 @@ static int create_region(struct cxl_ctx *ctx, int *count, + struct json_object *jregion; + unsigned int i, granularity; + struct cxl_region *region; ++ u64 size, max_extent; + const char *devname; + uuid_t uuid; +- u64 size; + int rc; + + rc = create_region_validate_config(ctx, p); +@@ -455,6 +455,18 @@ static int create_region(struct cxl_ctx *ctx, int *count, + log_err(&rl, "%s: unable to determine region size\n", __func__); + return -ENXIO; + } ++ max_extent = cxl_decoder_get_max_available_extent(p->root_decoder); ++ if (max_extent == ULLONG_MAX) { ++ log_err(&rl, "%s: unable to determine max extent\n", ++ cxl_decoder_get_devname(p->root_decoder)); ++ return -EINVAL; ++ } ++ if (size > max_extent) { ++ log_err(&rl, ++ "%s: region size %#lx exceeds max available space\n", ++ cxl_decoder_get_devname(p->root_decoder), size); ++ return -ENOSPC; ++ } + + if (p->mode == CXL_DECODER_MODE_PMEM) { + region = cxl_decoder_create_pmem_region(p->root_decoder); +-- +2.27.0 + diff --git a/SOURCES/0200-cxl-Add-list-verbose-option-to-the-cxl-command.patch b/SOURCES/0200-cxl-Add-list-verbose-option-to-the-cxl-command.patch new file mode 100644 index 0000000..e13bb11 --- /dev/null +++ b/SOURCES/0200-cxl-Add-list-verbose-option-to-the-cxl-command.patch @@ -0,0 +1,108 @@ +From 8b61e8e75443d79d22bf6e74e6b0e36acdd605c3 Mon Sep 17 00:00:00 2001 +From: Matthew Ho +Date: Fri, 12 Aug 2022 15:15:53 -0700 +Subject: [PATCH 200/217] cxl: Add list verbose option to the cxl command + +This adds the new subcommands cxl list -v, cxl list -vv, and cxl list -vvv. + +cxl list -v is now equivalent to cxl list -RMBDPT, cxl list -vv is +equivalent to cxl list -RMBDPTi, and cxl list -vvv is equivalent to +cxl list -RMBDPTiHI. These additions make it easier to list all of the CXL +devices without having to remember which subcommand must be appended for each +type of device. + +Link: https://lore.kernel.org/r/20220812221553.92278-1-sunfishho12@gmail.com +Reviewed-by: Adam Manzanares +Reviewed-by: Dan Williams +Acked-by: Davidlohr Bueso +Signed-off-by: Matthew Ho +Signed-off-by: Vishal Verma +--- + Documentation/cxl/cxl-list.txt | 18 ++++++++++++++++++ + cxl/filter.h | 1 + + cxl/list.c | 21 +++++++++++++++++++++ + 3 files changed, 40 insertions(+) + +diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt +index b88940a..14a2b4b 100644 +--- a/Documentation/cxl/cxl-list.txt ++++ b/Documentation/cxl/cxl-list.txt +@@ -344,6 +344,24 @@ OPTIONS + --region:: + Specify CXL region device name(s), or device id(s), to filter the listing. + ++-v:: ++--verbose:: ++ Increase verbosity of the output. This can be specified ++ multiple times to be even more verbose on the ++ informational and miscellaneous output, and can be used ++ to override omitted flags for showing specific ++ information. Note that cxl list --verbose --verbose is ++ equivalent to cxl list -vv. ++ - *-v* ++ Enable --memdevs, --regions, --buses, ++ --ports, --decoders, and --targets. ++ - *-vv* ++ Everything *-v* provides, plus include disabled ++ devices with --idle. ++ - *-vvv* ++ Everything *-vv* provides, plus enable ++ --health and --partition. ++ + --debug:: + If the cxl tool was built with debug enabled, turn on debug + messages. +diff --git a/cxl/filter.h b/cxl/filter.h +index d22d8b1..256df49 100644 +--- a/cxl/filter.h ++++ b/cxl/filter.h +@@ -26,6 +26,7 @@ struct cxl_filter_params { + bool human; + bool health; + bool partition; ++ int verbose; + struct log_ctx ctx; + }; + +diff --git a/cxl/list.c b/cxl/list.c +index 5f604ec..8c48fbb 100644 +--- a/cxl/list.c ++++ b/cxl/list.c +@@ -52,6 +52,8 @@ static const struct option options[] = { + "include memory device health information"), + OPT_BOOLEAN('I', "partition", ¶m.partition, + "include memory device partition information"), ++ OPT_INCR('v', "verbose", ¶m.verbose, ++ "increase output detail"), + #ifdef ENABLE_DEBUG + OPT_BOOLEAN(0, "debug", &debug, "debug list walk"), + #endif +@@ -106,6 +108,25 @@ int cmd_list(int argc, const char **argv, struct cxl_ctx *ctx) + param.memdevs = true; + } + ++ switch(param.verbose){ ++ default: ++ case 3: ++ param.health = true; ++ param.partition = true; ++ /* fallthrough */ ++ case 2: ++ param.idle = true; ++ /* fallthrough */ ++ case 1: ++ param.buses = true; ++ param.ports = true; ++ param.decoders = true; ++ param.targets = true; ++ /*fallthrough*/ ++ case 0: ++ break; ++ } ++ + log_init(¶m.ctx, "cxl list", "CXL_LIST_LOG"); + if (debug) { + cxl_set_log_priority(ctx, LOG_DEBUG); +-- +2.27.0 + diff --git a/SOURCES/0201-cxl-test-Validate-endpoint-interleave-geometry.patch b/SOURCES/0201-cxl-test-Validate-endpoint-interleave-geometry.patch new file mode 100644 index 0000000..bd1f867 --- /dev/null +++ b/SOURCES/0201-cxl-test-Validate-endpoint-interleave-geometry.patch @@ -0,0 +1,57 @@ +From 05a60dc4992bc4698ceff8e6737192ed818e7e34 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Fri, 5 Aug 2022 13:37:56 -0700 +Subject: [PATCH 201/217] cxl/test: Validate endpoint interleave geometry + +Check that endpoint interleave geometry settings are updated once the +endpoint decoders are associated with a region. + +Link: https://lore.kernel.org/r/165973187660.1528532.13832323649814892720.stgit@dwillia2-xfh.jf.intel.com +Reported-by: Jonathan Cameron +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/cxl-region-sysfs.sh | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/test/cxl-region-sysfs.sh b/test/cxl-region-sysfs.sh +index 2582edb..110e037 100644 +--- a/test/cxl-region-sysfs.sh ++++ b/test/cxl-region-sysfs.sh +@@ -44,8 +44,8 @@ uuidgen > /sys/bus/cxl/devices/$region/uuid + # setup interleave geometry + nr_targets=${#endpoint[@]} + echo $nr_targets > /sys/bus/cxl/devices/$region/interleave_ways +-g=$(cat /sys/bus/cxl/devices/$decoder/interleave_granularity) +-echo $g > /sys/bus/cxl/devices/$region/interleave_granularity ++r_ig=$(cat /sys/bus/cxl/devices/$decoder/interleave_granularity) ++echo $r_ig > /sys/bus/cxl/devices/$region/interleave_granularity + echo $((nr_targets * (256<<20))) > /sys/bus/cxl/devices/$region/size + + # grab the list of memdevs grouped by host-bridge interleave position +@@ -96,6 +96,22 @@ do + done + echo "$region added ${#endpoint[@]} targets: ${endpoint[@]}" + ++# validate all endpoint decoders have the correct setting ++region_size=$(cat /sys/bus/cxl/devices/$region/size) ++region_base=$(cat /sys/bus/cxl/devices/$region/resource) ++for i in ${endpoint[@]} ++do ++ iw=$(cat /sys/bus/cxl/devices/$i/interleave_ways) ++ ig=$(cat /sys/bus/cxl/devices/$i/interleave_granularity) ++ [ $iw -ne $nr_targets ] && err "$LINENO: decoder: $i iw: $iw targets: $nr_targets" ++ [ $ig -ne $r_ig] && err "$LINENO: decoder: $i ig: $ig root ig: $r_ig" ++ ++ sz=$(cat /sys/bus/cxl/devices/$i/size) ++ res=$(cat /sys/bus/cxl/devices/$i/start) ++ [ $sz -ne $region_size ] && err "$LINENO: decoder: $i sz: $sz region_size: $region_size" ++ [ $res -ne $region_base ] && err "$LINENO: decoder: $i base: $res region_base: $region_base" ++done ++ + # walk up the topology and commit all decoders + echo 1 > /sys/bus/cxl/devices/$region/commit + +-- +2.27.0 + diff --git a/SOURCES/0202-cxl-list-Add-interleave-parameters-to-decoder-listin.patch b/SOURCES/0202-cxl-list-Add-interleave-parameters-to-decoder-listin.patch new file mode 100644 index 0000000..178b93b --- /dev/null +++ b/SOURCES/0202-cxl-list-Add-interleave-parameters-to-decoder-listin.patch @@ -0,0 +1,49 @@ +From 0ee9ca3f6423a2af8ade983f596274f6fcfe66d6 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Fri, 5 Aug 2022 13:38:03 -0700 +Subject: [PATCH 202/217] cxl/list: Add interleave parameters to decoder + listings + +Emit interleave_ways and interleave_granularity in decoder output. + +Link: https://lore.kernel.org/r/165973188300.1528532.222988685552982872.stgit@dwillia2-xfh.jf.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/json.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/cxl/json.c b/cxl/json.c +index 9cec58b..ada1dbe 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -466,6 +466,26 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + json_object_object_add(jdecoder, "size", jobj); + } + ++ val = cxl_decoder_get_interleave_ways(decoder); ++ if (val < UINT_MAX) { ++ jobj = json_object_new_int(val); ++ if (jobj) ++ json_object_object_add(jdecoder, "interleave_ways", ++ jobj); ++ ++ /* granularity is a don't care if not interleaving */ ++ if (val > 1) { ++ val = cxl_decoder_get_interleave_granularity(decoder); ++ if (val < UINT_MAX) { ++ jobj = json_object_new_int(val); ++ if (jobj) ++ json_object_object_add( ++ jdecoder, ++ "interleave_granularity", jobj); ++ } ++ } ++ } ++ + if (size == 0) { + jobj = json_object_new_string("disabled"); + if (jobj) +-- +2.27.0 + diff --git a/SOURCES/0203-cxl-list-Add-region-to-decoder-listings.patch b/SOURCES/0203-cxl-list-Add-region-to-decoder-listings.patch new file mode 100644 index 0000000..40e819a --- /dev/null +++ b/SOURCES/0203-cxl-list-Add-region-to-decoder-listings.patch @@ -0,0 +1,140 @@ +From dba61cf8bb9bb96cde8dcf2c9a2dcc663074698b Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Fri, 5 Aug 2022 13:38:08 -0700 +Subject: [PATCH 203/217] cxl/list: Add region to decoder listings + +While decoders can be matched with regions by physical address, or filtered +by region, it is also useful to get a plain listing of the association. + +Link: https://lore.kernel.org/r/165973188860.1528532.17427805440366364536.stgit@dwillia2-xfh.jf.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/lib/libcxl.txt | 7 +++++++ + cxl/json.c | 8 ++++++++ + cxl/lib/libcxl.c | 34 ++++++++++++++++++++++++++++++++ + cxl/lib/libcxl.sym | 1 + + cxl/libcxl.h | 1 + + 5 files changed, 51 insertions(+) + +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 50b0c9c..6756d2f 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -398,6 +398,7 @@ int cxl_decoder_set_dpa_size(struct cxl_decoder *decoder, unsigned long long siz + const char *cxl_decoder_get_devname(struct cxl_decoder *decoder); + int cxl_decoder_get_id(struct cxl_decoder *decoder); + int cxl_decoder_get_nr_targets(struct cxl_decoder *decoder); ++struct cxl_region *cxl_decoder_get_region(struct cxl_decoder *decoder); + + enum cxl_decoder_target_type { + CXL_DECODER_TTYPE_UNKNOWN, +@@ -446,6 +447,12 @@ Platform firmware may setup the CXL decode hierarchy before the OS + boots, and may additionally require that the OS not change the decode + settings. This property is indicated by the cxl_decoder_is_locked() API. + ++When a decoder is associated with a region cxl_decoder_get_region() ++returns that region object. Note that it is only applicable to switch ++and endpoint decoders as root decoders have a 1:N relationship with ++regions. Use cxl_region_foreach() for the similar functionality for ++root decoders. ++ + ==== TARGETS + A root or switch level decoder takes an SPA (system-physical-address) as + input and routes it to a downstream port. Which downstream port depends +diff --git a/cxl/json.c b/cxl/json.c +index ada1dbe..c3d9299 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -442,6 +442,7 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + const char *devname = cxl_decoder_get_devname(decoder); + struct cxl_port *port = cxl_decoder_get_port(decoder); + struct json_object *jdecoder, *jobj; ++ struct cxl_region *region; + u64 val, size; + + jdecoder = json_object_new_object(); +@@ -486,6 +487,13 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, + } + } + ++ region = cxl_decoder_get_region(decoder); ++ if (region) { ++ jobj = json_object_new_string(cxl_region_get_devname(region)); ++ if (jobj) ++ json_object_object_add(jdecoder, "region", jobj); ++ } ++ + if (size == 0) { + jobj = json_object_new_string("disabled"); + if (jobj) +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index c7dc2b0..ff85b23 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -2124,6 +2124,40 @@ cxl_decoder_get_interleave_ways(struct cxl_decoder *decoder) + return decoder->interleave_ways; + } + ++CXL_EXPORT struct cxl_region * ++cxl_decoder_get_region(struct cxl_decoder *decoder) ++{ ++ struct cxl_port *port = cxl_decoder_get_port(decoder); ++ struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); ++ char *path = decoder->dev_buf; ++ char buf[SYSFS_ATTR_SIZE]; ++ struct cxl_region *region; ++ struct cxl_decoder *iter; ++ int rc; ++ ++ if (cxl_port_is_root(port)) ++ return NULL; ++ ++ sprintf(path, "%s/region", decoder->dev_path); ++ rc = sysfs_read_attr(ctx, path, buf); ++ if (rc < 0) { ++ err(ctx, "failed to read region name: %s\n", strerror(-rc)); ++ return NULL; ++ } ++ ++ if (strcmp(buf, "") == 0) ++ return NULL; ++ ++ while (!cxl_port_is_root(port)) ++ port = cxl_port_get_parent(port); ++ ++ cxl_decoder_foreach(port, iter) ++ cxl_region_foreach(iter, region) ++ if (strcmp(cxl_region_get_devname(region), buf) == 0) ++ return region; ++ return NULL; ++} ++ + CXL_EXPORT struct cxl_region * + cxl_decoder_create_pmem_region(struct cxl_decoder *decoder) + { +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 549f88d..385a8f0 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -214,4 +214,5 @@ global: + cxl_decoder_get_interleave_granularity; + cxl_decoder_get_interleave_ways; + cxl_decoder_get_max_available_extent; ++ cxl_decoder_get_region; + } LIBCXL_2; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 61c7fc4..2498fa1 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -201,6 +201,7 @@ bool cxl_decoder_is_locked(struct cxl_decoder *decoder); + unsigned int + cxl_decoder_get_interleave_granularity(struct cxl_decoder *decoder); + unsigned int cxl_decoder_get_interleave_ways(struct cxl_decoder *decoder); ++struct cxl_region *cxl_decoder_get_region(struct cxl_decoder *decoder); + struct cxl_region *cxl_decoder_create_pmem_region(struct cxl_decoder *decoder); + struct cxl_decoder *cxl_decoder_get_by_name(struct cxl_ctx *ctx, + const char *ident); +-- +2.27.0 + diff --git a/SOURCES/0204-cxl-list-Filter-decoders-by-region.patch b/SOURCES/0204-cxl-list-Filter-decoders-by-region.patch new file mode 100644 index 0000000..021622a --- /dev/null +++ b/SOURCES/0204-cxl-list-Filter-decoders-by-region.patch @@ -0,0 +1,59 @@ +From 81ad0ed44690596a846ac39da0b2fd966973fc21 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Fri, 5 Aug 2022 13:38:14 -0700 +Subject: [PATCH 204/217] cxl/list: Filter decoders by region + +With a region name in hand, it is useful to be able to filter all the +decoders in the topology that are mapping that region. + +Link: https://lore.kernel.org/r/165973189465.1528532.9072953032089147905.stgit@dwillia2-xfh.jf.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/filter.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/cxl/filter.c b/cxl/filter.c +index 38ece55..9a3de8c 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -652,6 +652,26 @@ struct cxl_region *util_cxl_region_filter(struct cxl_region *region, + + } + ++static struct cxl_decoder * ++util_cxl_decoder_filter_by_region(struct cxl_decoder *decoder, ++ const char *__ident) ++{ ++ struct cxl_region *region; ++ ++ if (!__ident) ++ return decoder; ++ ++ region = cxl_decoder_get_region(decoder); ++ if (!region) ++ return NULL; ++ ++ region = util_cxl_region_filter(region, __ident); ++ if (!region) ++ return NULL; ++ ++ return decoder; ++} ++ + static unsigned long params_to_flags(struct cxl_filter_params *param) + { + unsigned long flags = 0; +@@ -790,6 +810,9 @@ static void walk_decoders(struct cxl_port *port, struct cxl_filter_params *p, + if (!util_cxl_decoder_filter_by_memdev( + decoder, p->memdev_filter, p->serial_filter)) + goto walk_children; ++ if (!util_cxl_decoder_filter_by_region(decoder, ++ p->region_filter)) ++ goto walk_children; + if (!p->idle && cxl_decoder_get_size(decoder) == 0) + continue; + jdecoder = util_cxl_decoder_to_json(decoder, flags); +-- +2.27.0 + diff --git a/SOURCES/0205-cxl-list-Add-depth-to-port-listings.patch b/SOURCES/0205-cxl-list-Add-depth-to-port-listings.patch new file mode 100644 index 0000000..69f483b --- /dev/null +++ b/SOURCES/0205-cxl-list-Add-depth-to-port-listings.patch @@ -0,0 +1,90 @@ +From 2ceddb91d3a0d70a59242b3a9dab401be6e5c825 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Fri, 5 Aug 2022 13:38:20 -0700 +Subject: [PATCH 205/217] cxl/list: Add 'depth' to port listings + +Simplify the task of determining how deep a port is in the hierarchy by +just emitting what libcxl already counted. This is useful for validating +interleave math. + +Link: https://lore.kernel.org/r/165973190022.1528532.6351628365510289908.stgit@dwillia2-xfh.jf.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + Documentation/cxl/lib/libcxl.txt | 1 + + cxl/json.c | 4 ++++ + cxl/lib/libcxl.c | 5 +++++ + cxl/lib/libcxl.sym | 1 + + cxl/libcxl.h | 1 + + 5 files changed, 12 insertions(+) + +diff --git a/Documentation/cxl/lib/libcxl.txt b/Documentation/cxl/lib/libcxl.txt +index 6756d2f..fd2962a 100644 +--- a/Documentation/cxl/lib/libcxl.txt ++++ b/Documentation/cxl/lib/libcxl.txt +@@ -290,6 +290,7 @@ int cxl_port_is_enabled(struct cxl_port *port); + bool cxl_port_is_root(struct cxl_port *port); + bool cxl_port_is_switch(struct cxl_port *port); + bool cxl_port_is_endpoint(struct cxl_port *port); ++int cxl_port_get_depth(struct cxl_port *port); + bool cxl_port_hosts_memdev(struct cxl_port *port, struct cxl_memdev *memdev); + int cxl_port_get_nr_dports(struct cxl_port *port); + ---- +diff --git a/cxl/json.c b/cxl/json.c +index c3d9299..63c1751 100644 +--- a/cxl/json.c ++++ b/cxl/json.c +@@ -769,6 +769,10 @@ static struct json_object *__util_cxl_port_to_json(struct cxl_port *port, + if (jobj) + json_object_object_add(jport, "host", jobj); + ++ jobj = json_object_new_int(cxl_port_get_depth(port)); ++ if (jobj) ++ json_object_object_add(jport, "depth", jobj); ++ + if (!cxl_port_is_enabled(port)) { + jobj = json_object_new_string("disabled"); + if (jobj) +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index ff85b23..021d59f 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -2417,6 +2417,11 @@ CXL_EXPORT bool cxl_port_is_endpoint(struct cxl_port *port) + return port->type == CXL_PORT_ENDPOINT; + } + ++CXL_EXPORT int cxl_port_get_depth(struct cxl_port *port) ++{ ++ return port->depth; ++} ++ + CXL_EXPORT struct cxl_bus *cxl_port_get_bus(struct cxl_port *port) + { + struct cxl_bus *bus; +diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym +index 385a8f0..8bb91e0 100644 +--- a/cxl/lib/libcxl.sym ++++ b/cxl/lib/libcxl.sym +@@ -96,6 +96,7 @@ global: + cxl_port_get_parent; + cxl_port_is_root; + cxl_port_is_switch; ++ cxl_port_get_depth; + cxl_port_to_bus; + cxl_port_is_endpoint; + cxl_port_to_endpoint; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 2498fa1..9fe4e99 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -90,6 +90,7 @@ int cxl_port_is_enabled(struct cxl_port *port); + struct cxl_port *cxl_port_get_parent(struct cxl_port *port); + bool cxl_port_is_root(struct cxl_port *port); + bool cxl_port_is_switch(struct cxl_port *port); ++int cxl_port_get_depth(struct cxl_port *port); + struct cxl_bus *cxl_port_to_bus(struct cxl_port *port); + bool cxl_port_is_endpoint(struct cxl_port *port); + struct cxl_endpoint *cxl_port_to_endpoint(struct cxl_port *port); +-- +2.27.0 + diff --git a/SOURCES/0206-cxl-test-Validate-switch-port-settings-in-cxl-region.patch b/SOURCES/0206-cxl-test-Validate-switch-port-settings-in-cxl-region.patch new file mode 100644 index 0000000..e8ceadf --- /dev/null +++ b/SOURCES/0206-cxl-test-Validate-switch-port-settings-in-cxl-region.patch @@ -0,0 +1,63 @@ +From bcc9897b38be91df38f81fb3105d2351f7941377 Mon Sep 17 00:00:00 2001 +From: Dan Williams +Date: Fri, 5 Aug 2022 13:38:26 -0700 +Subject: [PATCH 206/217] cxl/test: Validate switch port settings in + cxl-region-sysfs.sh + +A recent kernel fix to add the missing update of endpoint decoder HPA range +settings regressed switch decoder HPA range settings. Add validation for +switch port settings to avoid regressions like that going forward. + +Link: https://lore.kernel.org/r/165973190625.1528532.12244196912617964754.stgit@dwillia2-xfh.jf.intel.com +Signed-off-by: Dan Williams +Signed-off-by: Vishal Verma +--- + test/cxl-region-sysfs.sh | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/test/cxl-region-sysfs.sh b/test/cxl-region-sysfs.sh +index 110e037..ae0f556 100644 +--- a/test/cxl-region-sysfs.sh ++++ b/test/cxl-region-sysfs.sh +@@ -112,6 +112,38 @@ do + [ $res -ne $region_base ] && err "$LINENO: decoder: $i base: $res region_base: $region_base" + done + ++# validate all switch decoders have the correct settings ++nr_switches=$((nr_targets/2)) ++nr_host_bridges=$((nr_switches/2)) ++nr_switch_decoders=$((nr_switches + nr_host_bridges)) ++ ++json=$($CXL list -D -r $region -d switch) ++readarray -t switch_decoders < <(echo $json | jq -r ".[].decoder") ++ ++[ ${#switch_decoders[@]} -ne $nr_switch_decoders ] && err \ ++"$LINENO: expected $nr_switch_decoders got ${#switch_decoders[@]} switch decoders" ++ ++for i in ${switch_decoders[@]} ++do ++ decoder=$(echo $json | jq -r ".[] | select(.decoder == \"$i\")") ++ id=${i#decoder} ++ port_id=${id%.*} ++ depth=$($CXL list -p $port_id -S | jq -r ".[].depth") ++ iw=$(echo $decoder | jq -r ".interleave_ways") ++ ig=$(echo $decoder | jq -r ".interleave_granularity") ++ ++ [ $iw -ne 2 ] && err "$LINENO: decoder: $i iw: $iw targets: 2" ++ [ $ig -ne $((r_ig << depth)) ] && err \ ++ "$LINENO: decoder: $i ig: $ig switch_ig: $((r_ig << depth))" ++ ++ res=$(echo $decoder | jq -r ".resource") ++ sz=$(echo $decoder | jq -r ".size") ++ [ $sz -ne $region_size ] && err \ ++ "$LINENO: decoder: $i sz: $sz region_size: $region_size" ++ [ $res -ne $region_base ] && err \ ++ "$LINENO: decoder: $i base: $res region_base: $region_base" ++done ++ + # walk up the topology and commit all decoders + echo 1 > /sys/bus/cxl/devices/$region/commit + +-- +2.27.0 + diff --git a/SOURCES/0207-meson-fix-modprobedatadir-default-value.patch b/SOURCES/0207-meson-fix-modprobedatadir-default-value.patch new file mode 100644 index 0000000..64cf175 --- /dev/null +++ b/SOURCES/0207-meson-fix-modprobedatadir-default-value.patch @@ -0,0 +1,34 @@ +From ed56d6053c532f43a8bbd109a87e754a43def845 Mon Sep 17 00:00:00 2001 +From: Miguel Bernal Marin +Date: Thu, 11 Aug 2022 19:36:53 -0500 +Subject: [PATCH 207/217] meson: fix modprobedatadir default value + +The modprobedatadir is now set as a meson option, but without a +default value. + +Set the default value if modprobedatadir is not set. + +Link: https://lore.kernel.org/r/20220812003653.53992-1-miguel.bernal.marin@linux.intel.com +Fixes: 524ad09d5eda ("meson: make modprobedatadir an option") +Reviewed-by: Dan Williams +Signed-off-by: Miguel Bernal Marin +Signed-off-by: Vishal Verma +--- + contrib/meson.build | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/contrib/meson.build b/contrib/meson.build +index ad63a50..48aa7c0 100644 +--- a/contrib/meson.build ++++ b/contrib/meson.build +@@ -26,6 +26,6 @@ endif + + modprobedatadir = get_option('modprobedatadir') + if modprobedatadir == '' +- modprobedatadir = get_option('modprobedatadir') ++ modprobedatadir = sysconfdir + '/modprobe.d/' + endif + install_data('nvdimm-security.conf', install_dir : modprobedatadir) +-- +2.27.0 + diff --git a/SOURCES/0208-ndctl-move-developer-scripts-from-contrib-to-scripts.patch b/SOURCES/0208-ndctl-move-developer-scripts-from-contrib-to-scripts.patch new file mode 100644 index 0000000..757548b --- /dev/null +++ b/SOURCES/0208-ndctl-move-developer-scripts-from-contrib-to-scripts.patch @@ -0,0 +1,49 @@ +From 6f8695fdda4aa8889d2f335e785efb9bfeaef6c5 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Wed, 15 Jun 2022 16:48:09 -0600 +Subject: [PATCH 208/217] ndctl: move developer scripts from contrib/ to + scripts/ + +Allow for scripts/ to be the defacto location for scripts and tooling +that may be useful for developers of ndctl, but isn't distributed or +installed. Move such scripts currently in contrib/ to scripts/. + +Link: https://lore.kernel.org/r/20220615224813.523053-2-vishal.l.verma@intel.com +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + {contrib => scripts}/daxctl-qemu-hmat-setup | 0 + {contrib => scripts}/do_abidiff | 0 + {contrib => scripts}/prepare-release.sh | 2 +- + 3 files changed, 1 insertion(+), 1 deletion(-) + rename {contrib => scripts}/daxctl-qemu-hmat-setup (100%) + rename {contrib => scripts}/do_abidiff (100%) + rename {contrib => scripts}/prepare-release.sh (99%) + +diff --git a/contrib/daxctl-qemu-hmat-setup b/scripts/daxctl-qemu-hmat-setup +similarity index 100% +rename from contrib/daxctl-qemu-hmat-setup +rename to scripts/daxctl-qemu-hmat-setup +diff --git a/contrib/do_abidiff b/scripts/do_abidiff +similarity index 100% +rename from contrib/do_abidiff +rename to scripts/do_abidiff +diff --git a/contrib/prepare-release.sh b/scripts/prepare-release.sh +similarity index 99% +rename from contrib/prepare-release.sh +rename to scripts/prepare-release.sh +index fb5cfe3..97ab964 100755 +--- a/contrib/prepare-release.sh ++++ b/scripts/prepare-release.sh +@@ -186,7 +186,7 @@ check_libtool_vers "libdaxctl" + gen_lists ${last_ref}..HEAD~1 + + # For ABI diff purposes, use the latest fixes tag +-contrib/do_abidiff ${last_fix}..HEAD ++scripts/do_abidiff ${last_fix}..HEAD + + # once everything passes, update the git-version + sed -i -e "s/DEF_VER=[0-9]\+.*/DEF_VER=${next_ref#v}/" git-version +-- +2.27.0 + diff --git a/SOURCES/0209-ndctl-remove-obsolete-m4-directory.patch b/SOURCES/0209-ndctl-remove-obsolete-m4-directory.patch new file mode 100644 index 0000000..a2be5f8 --- /dev/null +++ b/SOURCES/0209-ndctl-remove-obsolete-m4-directory.patch @@ -0,0 +1,31 @@ +From 426fa7f6c0ad7db9c6b18ef9b2247be224e0ea01 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Wed, 15 Jun 2022 16:48:10 -0600 +Subject: [PATCH 209/217] ndctl: remove obsolete m4 directory + +With the conversion to meson, the m4 directory, which may have held +symlinks to libtool.m4 and friends, is no longer needed. Remove it. + +Link: https://lore.kernel.org/r/20220615224813.523053-3-vishal.l.verma@intel.com +Cc: Dan Williams +Signed-off-by: Vishal Verma +--- + m4/.gitignore | 6 ------ + 1 file changed, 6 deletions(-) + delete mode 100644 m4/.gitignore + +diff --git a/m4/.gitignore b/m4/.gitignore +deleted file mode 100644 +index 8bab51c..0000000 +--- a/m4/.gitignore ++++ /dev/null +@@ -1,6 +0,0 @@ +-libtool.m4 +-ltoptions.m4 +-ltsugar.m4 +-ltversion.m4 +-lt~obsolete.m4 +- +-- +2.27.0 + diff --git a/SOURCES/0210-ndctl-update-.gitignore.patch b/SOURCES/0210-ndctl-update-.gitignore.patch new file mode 100644 index 0000000..d2d81d4 --- /dev/null +++ b/SOURCES/0210-ndctl-update-.gitignore.patch @@ -0,0 +1,32 @@ +From fcc3f6fa2989fbd2ce210c282fabf0bf3821d834 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Wed, 15 Jun 2022 16:48:11 -0600 +Subject: [PATCH 210/217] ndctl: update .gitignore + +Add a few files and dirs to .gitignore: + - cscope.* (since we already ignore 'tags') + - release/ (created by scripts/prepare_release) + - scripts/docsurgeon_parser.sh (parser generated by argbash) + +Link: https://lore.kernel.org/r/20220615224813.523053-4-vishal.l.verma@intel.com +Signed-off-by: Vishal Verma +--- + .gitignore | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/.gitignore b/.gitignore +index aa0ce8e..eeb275f 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -1,5 +1,8 @@ + build/ ++release/ + rhel/ndctl.spec + sles/ndctl.spec + *.swp + tags ++cscope.* ++scripts/docsurgeon_parser.sh +-- +2.27.0 + diff --git a/SOURCES/0211-scripts-fix-contrib-do_abidiff-for-updated-fedpkg.patch b/SOURCES/0211-scripts-fix-contrib-do_abidiff-for-updated-fedpkg.patch new file mode 100644 index 0000000..b29688b --- /dev/null +++ b/SOURCES/0211-scripts-fix-contrib-do_abidiff-for-updated-fedpkg.patch @@ -0,0 +1,30 @@ +From 411651adc693e55a521ba312e1e64876519b0379 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Wed, 15 Jun 2022 16:48:12 -0600 +Subject: [PATCH 211/217] scripts: fix contrib/do_abidiff for updated fedpkg + +A recent fedpkg update wants --name instead of --module-name. + +Link: https://lore.kernel.org/r/20220615224813.523053-5-vishal.l.verma@intel.com +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + scripts/do_abidiff | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/scripts/do_abidiff b/scripts/do_abidiff +index 0bd7a16..e8c3a65 100755 +--- a/scripts/do_abidiff ++++ b/scripts/do_abidiff +@@ -29,7 +29,7 @@ build_rpm() + version="$(./git-version)" + release="f$(basename $(readlink -f /etc/mock/default.cfg) | cut -d- -f2)" + git archive --format=tar --prefix="ndctl-${version}/" HEAD | gzip > ndctl-${version}.tar.gz +- fedpkg --release $release --module-name ndctl mockbuild ++ fedpkg --release $release --name=ndctl mockbuild + [ "$?" -eq 0 ] || err "error building $ref" + mkdir -p release/rel_${ref}/ + cp results_ndctl/*/*/*.x86_64.rpm release/rel_${ref}/ +-- +2.27.0 + diff --git a/SOURCES/0212-scripts-update-release-helper-scripts-for-meson-and-.patch b/SOURCES/0212-scripts-update-release-helper-scripts-for-meson-and-.patch new file mode 100644 index 0000000..86805c8 --- /dev/null +++ b/SOURCES/0212-scripts-update-release-helper-scripts-for-meson-and-.patch @@ -0,0 +1,66 @@ +From 9ce0fa95778a76d2334848e2629d111c4d636515 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Wed, 15 Jun 2022 16:48:13 -0600 +Subject: [PATCH 212/217] scripts: update release helper scripts for meson and + cxl + +The prepare-release.sh and do_abidiff scripts perform sanity checking +for library versioning and also guard against accidental ABI breakage +by comparing the current release with the previous using 'abipkgdiff' +from libabigail. Teach the scripts about libcxl, so that it too can +participate in the above checks. + +Additionally, move the checks over to the new meson regime. This does +break any checking for the older autotools based build, but that should +be okay. + +Link: https://lore.kernel.org/r/20220615224813.523053-6-vishal.l.verma@intel.com +Cc: Dan Williams +Signed-off-by: Vishal Verma +--- + scripts/do_abidiff | 3 ++- + scripts/prepare-release.sh | 3 ++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/scripts/do_abidiff b/scripts/do_abidiff +index e8c3a65..ec3e344 100755 +--- a/scripts/do_abidiff ++++ b/scripts/do_abidiff +@@ -53,7 +53,7 @@ do_diff() + local old_lib="$(find . -regex "./release/rel_${old}/${pkg}-libs-[0-9]+.*" | head -1)" + local new_lib="$(find . -regex "./release/rel_${new}/${pkg}-libs-[0-9]+.*" | head -1)" + +- [ -n "$pkg" ] || err "specify a package for diff (ndctl, daxctl)" ++ [ -n "$pkg" ] || err "specify a package for diff (ndctl, daxctl, cxl)" + [ -n "$old_base" ] || err "$pkg: old_base empty, possible build failure" + [ -n "$new_base" ] || err "$pkg: new_base empty, possible build failure" + +@@ -75,3 +75,4 @@ build_rpm $old > release/buildlog_$old 2>&1 + build_rpm $new > release/buildlog_$new 2>&1 + do_diff ndctl + do_diff daxctl ++do_diff cxl +diff --git a/scripts/prepare-release.sh b/scripts/prepare-release.sh +index 97ab964..8901b50 100755 +--- a/scripts/prepare-release.sh ++++ b/scripts/prepare-release.sh +@@ -100,7 +100,7 @@ gen_lists() + } + + # Check libtool versions in Makefile.am.in +-# $1: lib name (currently libndctl or libdaxctl) ++# $1: lib name (currently libndctl, libdaxctl, or libcxl) + check_libtool_vers() + { + local lib="$1" +@@ -181,6 +181,7 @@ next_fix=$(next_fix "$last_fix") + + check_libtool_vers "libndctl" + check_libtool_vers "libdaxctl" ++check_libtool_vers "libcxl" + + # HEAD~1 because HEAD would be the release commit + gen_lists ${last_ref}..HEAD~1 +-- +2.27.0 + diff --git a/SOURCES/0213-meson.build-be-specific-for-library-path.patch b/SOURCES/0213-meson.build-be-specific-for-library-path.patch new file mode 100644 index 0000000..4be2083 --- /dev/null +++ b/SOURCES/0213-meson.build-be-specific-for-library-path.patch @@ -0,0 +1,48 @@ +From 9a993ce24fdd5de45774b65211570dd514cdf61d Mon Sep 17 00:00:00 2001 +From: Luis Chamberlain +Date: Wed, 17 Aug 2022 18:23:04 -0700 +Subject: [PATCH 213/217] meson.build: be specific for library path + +If you run the typical configure script on a typical linux software +project say with ./configure --prefix=/usr/ then the libdir defaults +to /usr/lib/ however this is not true with meson. + +With meson the current libdir path follows the one set by the prefix, +and so with the current setup with prefix forced by default to /usr/ +we end up with libdir set to /usr/ as well and so libraries built +and installed also placed into /usr/ as well, not /usr/lib/ as we +would typically expect. + +So you if you use today's defaults you end up with the libraries placed +into /usr/ and then a simple error such as: + +cxl: error while loading shared libraries: libcxl.so.1: cannot open shared object file: No such file or directory + +Folks may have overlooked this as their old library is still usable. + +Fix this by forcing the default library path to /usr/lib, and so +requiring users to set both prefix and libdir if they want to +customize both. + +Link: https://lore.kernel.org/r/Yv2UeCIcA00lJC5j@bombadil.infradead.org +Signed-off-by: Luis Chamberlain +Signed-off-by: Vishal Verma +--- + meson.build | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/meson.build b/meson.build +index aecf461..802b38c 100644 +--- a/meson.build ++++ b/meson.build +@@ -9,6 +9,7 @@ project('ndctl', 'c', + default_options : [ + 'c_std=gnu99', + 'prefix=/usr', ++ 'libdir=/usr/lib', + 'sysconfdir=/etc', + 'localstatedir=/var', + ], +-- +2.27.0 + diff --git a/SOURCES/0214-cxl-region-fix-a-dereferecnce-after-NULL-check.patch b/SOURCES/0214-cxl-region-fix-a-dereferecnce-after-NULL-check.patch new file mode 100644 index 0000000..a0ca7a7 --- /dev/null +++ b/SOURCES/0214-cxl-region-fix-a-dereferecnce-after-NULL-check.patch @@ -0,0 +1,39 @@ +From cc2766399882001880a899e723c7bd968e60f100 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Tue, 23 Aug 2022 01:45:25 -0600 +Subject: [PATCH 214/217] cxl/region: fix a dereferecnce after NULL check + +A NULL check in region_action() implies that 'decoder' might be NULL, but +later we dereference it during cxl_decoder_foreach(). The NULL check is +valid because it was the filter result being checked, however, while +doing this, the original 'decoder' variable was being clobbered. + +Check the filter results independently of the original decoder variable. + +Link: https://lore.kernel.org/r/20220823074527.404435-2-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/region.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/cxl/region.c b/cxl/region.c +index a30313c..334fcc2 100644 +--- a/cxl/region.c ++++ b/cxl/region.c +@@ -686,9 +686,8 @@ static int region_action(int argc, const char **argv, struct cxl_ctx *ctx, + continue; + + cxl_decoder_foreach (port, decoder) { +- decoder = util_cxl_decoder_filter(decoder, +- param.root_decoder); +- if (!decoder) ++ if (!util_cxl_decoder_filter(decoder, ++ param.root_decoder)) + continue; + rc = decoder_region_action(p, decoder, action, count); + if (rc) +-- +2.27.0 + diff --git a/SOURCES/0215-libcxl-fox-a-resource-leak-and-a-forward-NULL-check.patch b/SOURCES/0215-libcxl-fox-a-resource-leak-and-a-forward-NULL-check.patch new file mode 100644 index 0000000..ac370ce --- /dev/null +++ b/SOURCES/0215-libcxl-fox-a-resource-leak-and-a-forward-NULL-check.patch @@ -0,0 +1,50 @@ +From 4750c7f50050195bbd427da69037645916a59b24 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Tue, 23 Aug 2022 01:45:26 -0600 +Subject: [PATCH 215/217] libcxl: fox a resource leak and a forward NULL check + +Static analysis reports a couple of issues in add_cxl_region(). Firstly, +'path' wasn't freed in the success case, only in the error case. +Secondly, the error handling after 'calloc()'ing the region object +erroneously jumped to the error path which tried to free the region object. + +Add a new error label to just free 'path' and return for this exit case. + +Link: https://lore.kernel.org/r/20220823074527.404435-3-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/lib/libcxl.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 021d59f..e8c5d44 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -482,7 +482,7 @@ static void *add_cxl_region(void *parent, int id, const char *cxlregion_base) + + region = calloc(1, sizeof(*region)); + if (!region) +- goto err; ++ goto err_path; + + region->id = id; + region->ctx = ctx; +@@ -551,11 +551,13 @@ static void *add_cxl_region(void *parent, int id, const char *cxlregion_base) + + list_add_sorted(&decoder->regions, region, list, region_start_cmp); + ++ free(path); + return region; + err: + free(region->dev_path); + free(region->dev_buf); + free(region); ++err_path: + free(path); + return NULL; + } +-- +2.27.0 + diff --git a/SOURCES/0216-cxl-filter-Fix-an-uninitialized-pointer-dereference.patch b/SOURCES/0216-cxl-filter-Fix-an-uninitialized-pointer-dereference.patch new file mode 100644 index 0000000..44ed9a6 --- /dev/null +++ b/SOURCES/0216-cxl-filter-Fix-an-uninitialized-pointer-dereference.patch @@ -0,0 +1,33 @@ +From 934d2b34b616566163d80f5844b371ecf88d646a Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Tue, 23 Aug 2022 01:45:27 -0600 +Subject: [PATCH 216/217] cxl/filter: Fix an uninitialized pointer dereference + +Static analysis points out that there was a chance that 'jdecoder' could +be used while uninitialized in walk_decoders(). Initialize it to NULL to +avoid this. + +Link: https://lore.kernel.org/r/20220823074527.404435-4-vishal.l.verma@intel.com +Cc: Dan Williams +Reviewed-by: Dan Williams +Signed-off-by: Vishal Verma +--- + cxl/filter.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/cxl/filter.c b/cxl/filter.c +index 9a3de8c..56c6599 100644 +--- a/cxl/filter.c ++++ b/cxl/filter.c +@@ -796,7 +796,7 @@ static void walk_decoders(struct cxl_port *port, struct cxl_filter_params *p, + cxl_decoder_foreach(port, decoder) { + const char *devname = cxl_decoder_get_devname(decoder); + struct json_object *jchildregions = NULL; +- struct json_object *jdecoder; ++ struct json_object *jdecoder = NULL; + + if (!p->decoders) + goto walk_children; +-- +2.27.0 + diff --git a/SOURCES/0217-ndctl-release-v74.patch b/SOURCES/0217-ndctl-release-v74.patch new file mode 100644 index 0000000..bdec84c --- /dev/null +++ b/SOURCES/0217-ndctl-release-v74.patch @@ -0,0 +1,133 @@ +From c9c9db39354ea0c3f737378186318e9b7908e3a7 Mon Sep 17 00:00:00 2001 +From: Vishal Verma +Date: Tue, 23 Aug 2022 18:44:35 -0600 +Subject: [PATCH 217/217] ndctl: release v74 + +This release incorporates functionality up to the 6.0 kernel. + +Highlights include CXL region management, enhancements to cxl-list, +cxl_test based unit tests for topology enumeration, and region and +label operations, misc build fixes, iniparser include resolution, +fixes in config parsing for ndctl-monitor, and misc documentation +and unit test updates. + +Commands: + cxl-create-region: new command (also {enable,disable,destroy}-region) + cxl-list: add -v / -vv etc. options for verbosity control + cxl-list: new filtering options, endpoint decoder DPA listings + cxl-list: add decoder interleave settings, and mode + cxl-list: add port depth + cxl-list: Auto-enable 'single' mode for port listings + cxl-set-partition: Accept 'ram' as an alias for 'volatile' + cxl-disable-bus: new command + cxl-{reserve,free}-dpa: new commands + ndctl-xable-namespace: zero namespace fixes + ndctl-monitor: fix config parsing + daxctl-reconfigure-device: fix systemd escaping for policy config + +Tests: + cxl-topology.sh: new test for CXL topology enumeration + cxl-region-sysfs.sh: new test for the low-level CXL region ABI + cxl-create-region.sh: new test for region management + cxl-labels.sh: new test for label management commands + +APIs: + cxl_bus_disable_invalidate + cxl_decoder_create_pmem_region + cxl_decoder_get_by_name + cxl_decoder_get_dpa_resource + cxl_decoder_get_dpa_size + cxl_decoder_get_interleave_granularity + cxl_decoder_get_interleave_ways + cxl_decoder_get_last + cxl_decoder_get_max_available_extent + cxl_decoder_get_memdev + cxl_decoder_get_mode + cxl_decoder_get_prev + cxl_decoder_get_region + cxl_decoder_set_dpa_size + cxl_decoder_set_mode + cxl_mapping_get_decoder + cxl_mapping_get_first + cxl_mapping_get_next + cxl_mapping_get_position + cxl_mapping_get_region + cxl_port_get_depth + cxl_region_clear_all_targets + cxl_region_clear_target + cxl_region_decode_commit + cxl_region_decode_is_committed + cxl_region_decode_reset + cxl_region_delete + cxl_region_disable + cxl_region_enable + cxl_region_get_ctx + cxl_region_get_decoder + cxl_region_get_devname + cxl_region_get_first + cxl_region_get_id + cxl_region_get_interleave_granularity + cxl_region_get_interleave_ways + cxl_region_get_next + cxl_region_get_resource + cxl_region_get_size + cxl_region_get_target_decoder + cxl_region_get_uuid + cxl_region_is_enabled + cxl_region_set_interleave_granularity + cxl_region_set_interleave_ways + cxl_region_set_size + cxl_region_set_target + cxl_region_set_uuid +--- + git-version | 2 +- + meson.build | 8 ++++---- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/git-version b/git-version +index bdea9a9..7d76fa1 100755 +--- a/git-version ++++ b/git-version +@@ -19,7 +19,7 @@ dirty() { + fi + } + +-DEF_VER=72 ++DEF_VER=74 + + LF=' + ' +diff --git a/meson.build b/meson.build +index 802b38c..20a646d 100644 +--- a/meson.build ++++ b/meson.build +@@ -1,5 +1,5 @@ + project('ndctl', 'c', +- version : '73', ++ version : '74', + license : [ + 'GPL-2.0', + 'LGPL-2.1', +@@ -292,16 +292,16 @@ config_h = configure_file( + add_project_arguments('-include', 'config.h', language : 'c') + + LIBNDCTL_CURRENT=26 +-LIBNDCTL_REVISION=0 ++LIBNDCTL_REVISION=1 + LIBNDCTL_AGE=20 + + LIBDAXCTL_CURRENT=6 + LIBDAXCTL_REVISION=1 + LIBDAXCTL_AGE=5 + +-LIBCXL_CURRENT=2 ++LIBCXL_CURRENT=3 + LIBCXL_REVISION=0 +-LIBCXL_AGE=1 ++LIBCXL_AGE=2 + + root_inc = include_directories(['.', 'ndctl', ]) + +-- +2.27.0 + diff --git a/SOURCES/elake-diff-bafc29a0-df411ebe.patch b/SOURCES/elake-diff-bafc29a0-df411ebe.patch new file mode 100644 index 0000000..4180718 --- /dev/null +++ b/SOURCES/elake-diff-bafc29a0-df411ebe.patch @@ -0,0 +1,273 @@ +diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c +index 8d39d2c..ba265ed 100644 +--- a/cxl/lib/libcxl.c ++++ b/cxl/lib/libcxl.c +@@ -1483,7 +1483,7 @@ struct cel_entry { + __le16 effect; + } __attribute__((packed)); + +-CXL_EXPORT int cxl_memdev_get_cel_log(struct cxl_memdev *memdev) ++CXL_EXPORT int cxl_memdev_get_cel_log(struct cxl_memdev *memdev, const char* uuid) + { + struct cxl_cmd *cmd; + struct cxl_mbox_get_log *get_log_input; +@@ -1491,6 +1491,12 @@ CXL_EXPORT int cxl_memdev_get_cel_log(struct cxl_memdev *memdev) + int no_cel_entries; + int rc = 0; + ++ if (!uuid) { ++ fprintf(stderr, "%s: Please specify log uuid argument\n", ++ cxl_memdev_get_devname(memdev)); ++ return -EINVAL; ++ } ++ + cmd = cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_GET_LOG); + if (!cmd) { + fprintf(stderr, "%s: cxl_memdev_get_cel_log returned Null output\n", +@@ -1499,7 +1505,7 @@ CXL_EXPORT int cxl_memdev_get_cel_log(struct cxl_memdev *memdev) + } + + get_log_input = (void *) cmd->send_cmd->in.payload; +- uuid_parse(CEL_UUID, get_log_input->uuid); ++ uuid_parse(uuid, get_log_input->uuid); + get_log_input->offset = 0; + get_log_input->length = cmd->memdev->payload_max; + +@@ -2021,7 +2027,9 @@ struct cxl_dram_event_record { + u8 row[3]; + __le16 column; + u8 correction_mask[0x20]; +- u8 reserved[0x17]; ++ u8 component_identifier[0x10]; ++ u8 sub_channel; ++ u8 reserved[0x6]; + } __attribute__((packed)); + + struct cxl_memory_module_record { +@@ -2160,6 +2168,16 @@ CXL_EXPORT int cxl_memdev_get_event_records(struct cxl_memdev *memdev, u8 event_ + fprintf(stdout, "%*srow: 0x%02x%02x%02x\n", indent+2, "", dram_event->row[0], + dram_event->row[1], dram_event->row[2]); + fprintf(stdout, "%*scolumn: 0x%x\n", indent+2, "", le16_to_cpu(dram_event->column)); ++ for (int i=0; i < 4; i++) { ++ fprintf(stdout, "%*scorrection mask[%d]: 0x", indent+2, "", i); ++ for (int j=0; j < 8; j++) { ++ fprintf(stdout, "%02x", dram_event->correction_mask[i*j+j]); ++ } ++ fprintf(stdout, "\n"); ++ } ++ fprintf(stdout, "%*scomponent identifier: 0x%02x%02x%02x\n", indent+2, "", ++ dram_event->component_identifier[0], dram_event->component_identifier[1], ++ dram_event->component_identifier[2]); + } + } + +@@ -6272,14 +6290,12 @@ out: + return 0; + } + +- + #define CXL_MEM_COMMAND_ID_HEALTH_COUNTERS_GET CXL_MEM_COMMAND_ID_RAW + #define CXL_MEM_COMMAND_ID_HEALTH_COUNTERS_GET_OPCODE 52737 + #define CXL_MEM_COMMAND_ID_HEALTH_COUNTERS_GET_PAYLOAD_OUT_SIZE 40 + +- + struct cxl_mbox_health_counters_get_out { +- __le32 temperature_threshold_exceeded; ++ __le32 critical_over_temperature_exceeded; + __le32 power_on_events; + __le32 power_on_hours; + __le32 cxl_mem_link_crc_errors; +@@ -6289,6 +6305,12 @@ struct cxl_mbox_health_counters_get_out { + __le32 num_ddr_double_ecc_errors; + __le32 link_recovery_events; + __le32 time_in_throttled; ++ __le32 over_temperature_warning_level_exceeded; ++ __le32 critical_under_temperature_exceeded; ++ __le32 under_temperature_warning_level_exceeded; ++ __le32 rx_retry_request; ++ __le32 rcmd_qs0_hi_threshold_detect; ++ __le32 rcmd_qs1_hi_threshold_detect; + } __attribute__((packed)); + + CXL_EXPORT int cxl_memdev_health_counters_get(struct cxl_memdev *memdev) +@@ -6327,16 +6349,22 @@ CXL_EXPORT int cxl_memdev_health_counters_get(struct cxl_memdev *memdev) + + health_counters_get_out = (void *)cmd->send_cmd->out.payload; + fprintf(stdout, "============================= get health counters ==============================\n"); +- fprintf(stdout, "Number of times temperature has exceeded threshold: %d\n", le32_to_cpu(health_counters_get_out->temperature_threshold_exceeded)); +- fprintf(stdout, "Number of Power On events: %d\n", le32_to_cpu(health_counters_get_out->power_on_events)); +- fprintf(stdout, "Number of Power On hours: %d\n", le32_to_cpu(health_counters_get_out->power_on_hours)); +- fprintf(stdout, "Number of CXL.mem Link CRC errors: %d\n", le32_to_cpu(health_counters_get_out->cxl_mem_link_crc_errors)); +- fprintf(stdout, "Number of CXL.io Link LCRC errors: %d\n", le32_to_cpu(health_counters_get_out->cxl_io_link_lcrc_errors)); +- fprintf(stdout, "Number of CXL.io Link ECRC errors: %d\n", le32_to_cpu(health_counters_get_out->cxl_io_link_ecrc_errors)); +- fprintf(stdout, "Number of DDR single ECC errors: %d\n", le32_to_cpu(health_counters_get_out->num_ddr_single_ecc_errors)); +- fprintf(stdout, "Number of DDR double ECC errors: %d\n", le32_to_cpu(health_counters_get_out->num_ddr_double_ecc_errors)); +- fprintf(stdout, "Number of Link recovery events: %d\n", le32_to_cpu(health_counters_get_out->link_recovery_events)); +- fprintf(stdout, "Amount of time spent in throttled state (in seconds): %d\n", le32_to_cpu(health_counters_get_out->time_in_throttled)); ++ fprintf(stdout, "0: CRITICAL_OVER_TEMPERATURE_EXCEEDED = %d\n", le32_to_cpu(health_counters_get_out->critical_over_temperature_exceeded)); ++ fprintf(stdout, "1: OVER_TEMPERATURE_WARNING_LEVEL_EXCEEDED = %d\n", le32_to_cpu(health_counters_get_out->over_temperature_warning_level_exceeded)); ++ fprintf(stdout, "2: CRITICAL_UNDER_TEMPERATURE_EXCEEDED = %d\n", le32_to_cpu(health_counters_get_out->critical_under_temperature_exceeded)); ++ fprintf(stdout, "3: UNDER_TEMPERATURE_WARNING_LEVEL_EXCEEDED = %d\n", le32_to_cpu(health_counters_get_out->under_temperature_warning_level_exceeded)); ++ fprintf(stdout, "4: POWER_ON_EVENTS = %d\n", le32_to_cpu(health_counters_get_out->power_on_events)); ++ fprintf(stdout, "5: POWER_ON_HOURS = %d\n", le32_to_cpu(health_counters_get_out->power_on_hours)); ++ fprintf(stdout, "6: CXL_MEM_LINK_CRC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->cxl_mem_link_crc_errors)); ++ fprintf(stdout, "7: CXL_IO_LINK_LCRC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->cxl_io_link_lcrc_errors)); ++ fprintf(stdout, "8: CXL_IO_LINK_ECRC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->cxl_io_link_ecrc_errors)); ++ fprintf(stdout, "9: NUM_DDR_SINGLE_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_single_ecc_errors)); ++ fprintf(stdout, "10: NUM_DDR_DOUBLE_ECC_ERRORS = %d\n", le32_to_cpu(health_counters_get_out->num_ddr_double_ecc_errors)); ++ fprintf(stdout, "11: LINK_RECOVERY_EVENTS = %d\n", le32_to_cpu(health_counters_get_out->link_recovery_events)); ++ fprintf(stdout, "12: TIME_IN_THROTTLED = %d\n", le32_to_cpu(health_counters_get_out->time_in_throttled)); ++ fprintf(stdout, "13: RX_RETRY_REQUEST = %d\n", le32_to_cpu(health_counters_get_out->rx_retry_request)); ++ fprintf(stdout, "14: RCMD_QS0_HI_THRESHOLD_DETECT = %d\n", le32_to_cpu(health_counters_get_out->rcmd_qs0_hi_threshold_detect)); ++ fprintf(stdout, "15: RCMD_QS1_HI_THRESHOLD_DETECT = %d\n", le32_to_cpu(health_counters_get_out->rcmd_qs1_hi_threshold_detect)); + + out: + cxl_cmd_unref(cmd); +@@ -9964,6 +9992,31 @@ out: + return rc; + } + ++struct cxl_dimm_slot_info_out { ++ u8 num_dimm_slots; ++ u8 rsvd[3]; ++ u8 slot0_spd_i2c_addr; ++ u8 slot0_channel_id; ++ u8 slot0_dimm_silk_screen; ++ u8 slot0_dimm_present; ++ u8 rsvd1[12]; ++ u8 slot1_spd_i2c_addr; ++ u8 slot1_channel_id; ++ u8 slot1_dimm_silk_screen; ++ u8 slot1_dimm_present; ++ u8 rsvd2[12]; ++ u8 slot2_spd_i2c_addr; ++ u8 slot2_channel_id; ++ u8 slot2_dimm_silk_screen; ++ u8 slot2_dimm_present; ++ u8 rsvd3[12]; ++ u8 slot3_spd_i2c_addr; ++ u8 slot3_channel_id; ++ u8 slot3_dimm_silk_screen; ++ u8 slot3_dimm_present; ++ u8 rsvd4[12]; ++} __attribute__((packed)); ++ + #define CXL_MEM_COMMAND_ID_DIMM_SLOT_INFO CXL_MEM_COMMAND_ID_RAW + #define CXL_MEM_COMMAND_ID_DIMM_SLOT_INFO_OPCODE 0xC520 + #define CXL_MEM_COMMAND_ID_DIMM_SLOT_INFO_PAYLOAD_IN_SIZE 0 +@@ -9973,10 +10026,12 @@ CXL_EXPORT int cxl_memdev_dimm_slot_info(struct cxl_memdev *memdev) + struct cxl_cmd *cmd; + struct cxl_mem_query_commands *query; + struct cxl_command_info *cinfo; +- u8 *dimm_slot_info; ++ struct cxl_dimm_slot_info_out *dimm_slot_info; ++ u8 *dimm_slots; + int rc = 0; + int offset = 0; +- ++ int indent = 2; ++ char silk_screen_char; + + cmd = cxl_cmd_new_raw(memdev, CXL_MEM_COMMAND_ID_DIMM_SLOT_INFO_OPCODE); + if (!cmd) { +@@ -10018,20 +10073,52 @@ CXL_EXPORT int cxl_memdev_dimm_slot_info(struct cxl_memdev *memdev) + return -EINVAL; + } + +- dimm_slot_info = (u8*)cmd->send_cmd->out.payload; ++ dimm_slot_info = (void *)cmd->send_cmd->out.payload; ++ dimm_slots = (u8*)cmd->send_cmd->out.payload; + fprintf(stdout, "=========================== DIMM SLOT INFO ============================\n"); + fprintf(stdout, "Output Payload:\n"); + for(int i=0; isend_cmd->out.size; i++){ + if (i % 16 == 0) + { +- fprintf(stdout, "\n%04x %02x ", i+offset, dimm_slot_info[i]); ++ fprintf(stdout, "\n%04x %02x ", i+offset, dimm_slots[i]); + } + else + { +- fprintf(stdout, "%02x ", dimm_slot_info[i]); ++ fprintf(stdout, "%02x ", dimm_slots[i]); + } + } +- fprintf(stdout, "\n"); ++ fprintf(stdout, "\n\n"); ++ ++ // Decoding slot info data. ++ fprintf(stdout, "\n\n====== DIMM SLOTS INFO DECODE ============\n"); ++ ++ fprintf(stdout, "Number of DIMM Slots: %d\n", dimm_slot_info->num_dimm_slots); ++ fprintf(stdout, "DIMM SPD Index: 0\n"); ++ fprintf(stdout, "%*sDIMM Present: 0x%x\n", indent+2, "", dimm_slot_info->slot0_dimm_present); ++ silk_screen_char = dimm_slot_info->slot0_dimm_silk_screen; ++ fprintf(stdout, "%*sDIMM Silk Screen: %c\n", indent+2, "", silk_screen_char); ++ fprintf(stdout, "%*sChannel ID: 0x%x\n", indent+2, "", dimm_slot_info->slot0_channel_id); ++ fprintf(stdout, "%*sI2C Address: 0x%x\n", indent+2, "", dimm_slot_info->slot0_spd_i2c_addr); ++ fprintf(stdout, "DIMM SPD Index: 1\n"); ++ fprintf(stdout, "%*sDIMM Present: 0x%x\n", indent+2, "", dimm_slot_info->slot1_dimm_present); ++ silk_screen_char = dimm_slot_info->slot1_dimm_silk_screen; ++ fprintf(stdout, "%*sDIMM Silk Screen: %c\n", indent+2, "", silk_screen_char); ++ fprintf(stdout, "%*sChannel ID: 0x%x\n", indent+2, "", dimm_slot_info->slot1_channel_id); ++ fprintf(stdout, "%*sI2C Address: 0x%x\n", indent+2, "", dimm_slot_info->slot1_spd_i2c_addr); ++ fprintf(stdout, "DIMM SPD Index: 2\n"); ++ fprintf(stdout, "%*sDIMM Present: 0x%x\n", indent+2, "", dimm_slot_info->slot2_dimm_present); ++ silk_screen_char = dimm_slot_info->slot2_dimm_silk_screen; ++ fprintf(stdout, "%*sDIMM Silk Screen: %c\n", indent+2, "", silk_screen_char); ++ fprintf(stdout, "%*sChannel ID: 0x%x\n", indent+2, "", dimm_slot_info->slot2_channel_id); ++ fprintf(stdout, "%*sI2C Address: 0x%x\n", indent+2, "", dimm_slot_info->slot2_spd_i2c_addr); ++ fprintf(stdout, "DIMM SPD Index: 3\n"); ++ fprintf(stdout, "%*sDIMM Present: 0x%x\n", indent+2, "", dimm_slot_info->slot3_dimm_present); ++ silk_screen_char = dimm_slot_info->slot3_dimm_silk_screen; ++ fprintf(stdout, "%*sDIMM Silk Screen: %c\n", indent+2, "", silk_screen_char); ++ fprintf(stdout, "%*sChannel ID: 0x%x\n", indent+2, "", dimm_slot_info->slot3_channel_id); ++ fprintf(stdout, "%*sI2C Address: 0x%x\n", indent+2, "", dimm_slot_info->slot3_spd_i2c_addr); ++ ++ fprintf(stdout, "\n\n"); + out: + cxl_cmd_unref(cmd); + return rc; +diff --git a/cxl/libcxl.h b/cxl/libcxl.h +index 0c24579..6583af5 100644 +--- a/cxl/libcxl.h ++++ b/cxl/libcxl.h +@@ -63,7 +63,7 @@ int cxl_memdev_transfer_fw(struct cxl_memdev *memdev, u8 action, + int cxl_memdev_activate_fw(struct cxl_memdev *memdev, u8 action, + u8 slot); + int cxl_memdev_get_supported_logs(struct cxl_memdev *memdev); +-int cxl_memdev_get_cel_log(struct cxl_memdev *memdev); ++int cxl_memdev_get_cel_log(struct cxl_memdev *memdev, const char *uuid); + int cxl_memdev_get_event_interrupt_policy(struct cxl_memdev *memdev); + int cxl_memdev_set_event_interrupt_policy(struct cxl_memdev *memdev, u32 int_policy); + int cxl_memdev_get_timestamp(struct cxl_memdev *memdev); +diff --git a/cxl/memdev.c b/cxl/memdev.c +index 64ba7e0..fba1f75 100644 +--- a/cxl/memdev.c ++++ b/cxl/memdev.c +@@ -84,8 +84,17 @@ static const struct option cmd_get_supported_logs_options[] = { + OPT_END(), + }; + ++static struct _log_uuid { ++ const char* uuid; ++} log_uuid; ++ ++#define LOG_UUID_OPTIONS() \ ++OPT_STRING('l', "log_uuid", &log_uuid.uuid, "log-uuid", \ ++ "CEL Log UUID") ++ + static const struct option cmd_get_cel_log_options[] = { + BASE_OPTIONS(), ++ LOG_UUID_OPTIONS(), + OPT_END(), + }; + +@@ -2247,7 +2256,7 @@ static int action_cmd_get_cel_log(struct cxl_memdev *memdev, struct action_conte + return -EBUSY; + } + +- return cxl_memdev_get_cel_log(memdev); ++ return cxl_memdev_get_cel_log(memdev, log_uuid.uuid); + } + + static int action_cmd_get_supported_logs(struct cxl_memdev *memdev, struct action_context *actx) diff --git a/SPECS/ndctl.spec b/SPECS/ndctl.spec index 6fbaa01..3843277 100644 --- a/SPECS/ndctl.spec +++ b/SPECS/ndctl.spec @@ -1,9 +1,8 @@ Name: ndctl Version: 71.1 -Release: 4.2%{?dist} +Release: 8.1%{?dist} Summary: Manage "libnvdimm" subsystem devices (Non-volatile Memory) License: GPLv2 -Group: System Environment/Base Url: https://github.com/pmem/ndctl Source0: https://github.com/pmem/%{name}/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz @@ -13,33 +12,215 @@ Source0: https://github.com/pmem/%{name}/archive/v%{version}.tar.gz#/%{name}-%{v Patch0: elake-diff-ea014c0-3ff031d.patch # Normal patch generated from `git format-patch` Patch1: elake-c5d4db-7fcbe3.patch +# Another `git diff` patch +Patch2: elake-diff-bafc29a0-df411ebe.patch %else -Patch0: modprobe-link-user-keyring-before-loadkeys.patch -Patch1: fb13dfb-zero_info_block-skip-seed-devices.patch -Patch2: daef3a3-libndctl-Unify-adding-dimms-for-papr-and-nfit-families.patch -Patch3: f081f30-papr-Add-support-to-parse-save_fail-flag-for-dimm.patch -Patch4: fe831b5-Use-page-size-as-alignment-value.patch -Patch5: e086106-libndctl-papr-Fix-probe-for-papr-scm-compatible-nvdimms.patch -Patch6: c521093-ndctl-scrub-Stop-translating-return-values.patch -Patch7: 4e646fa-ndctl-scrub-Reread-scrub-engine-status-at-start.patch -Patch8: 9bd2994-ndctl-namespace-Skip-seed-namespaces-when-processing-all-namespaces.patch -Patch9: 07011a3-ndctl-namespace-Suppress-ENXIO-when-processing-all-namespaces.patch -Patch10: 80e0d88-namespace-action-Drop-zero-namespace-checks.patch -Patch11: aa99000-libndctl-papr-Add-support-for-reporting-shutdown-count.patch -Patch12: edcd9b7-libndctl-intel-Indicate-supported-smart-inject-types.patch -Patch13: 9ef460e-libndctl-papr-Add-limited-support-for-inject-smart.patch -Patch14: 6e85cac-ndtest-ack-shutdown-count-Skip-the-test-on-ndtest.patch +Patch0: 0003-ndctl-test-Fix-btt-expect-table-compile-warning.patch +Patch1: 0004-ndctl-test-Cleanup-unnecessary-out-label.patch +Patch2: 0005-ndctl-test-Fix-device-dax-mremap-test.patch +Patch3: 0006-ndctl-test-Exercise-soft_offline_page-corner-cases.patch +Patch4: 0007-msft-Add-xlat_firmware_status-for-JEDEC-Byte-Address.patch +Patch5: 0008-ndctl-namespace-Fix-disable-namespace-accounting-rel.patch +Patch6: 0009-zero_info_block-skip-seed-devices.patch +Patch7: 0010-ndctl-update-.gitignore.patch +Patch8: 0011-ndctl-test-add-checking-the-presence-of-jq-command-a.patch +Patch9: 0012-Expose-ndctl_bus_nfit_translate_spa-as-a-public-func.patch +Patch10: 0013-test-libndctl-Use-ndctl_region_set_ro-to-change-disk.patch +Patch11: 0014-daxctl-fail-reconfigure-device-based-on-kernel-onlin.patch +Patch12: 0015-libdaxctl-add-an-API-to-check-if-a-device-is-active.patch +Patch13: 0016-libndctl-check-for-active-system-ram-before-disablin.patch +Patch14: 0017-daxctl-emit-counts-of-total-and-online-memblocks.patch +Patch15: 0018-libndctl-Unify-adding-dimms-for-papr-and-nfit-famili.patch +Patch16: 0019-test-Don-t-skip-tests-if-nfit-modules-are-missing.patch +Patch17: 0020-papr-Add-support-to-parse-save_fail-flag-for-dimm.patch +Patch18: 0021-Use-page-size-as-alignment-value.patch +Patch19: 0022-libndctl-Remove-redundant-checks-and-assignments.patch +Patch20: 0023-ndctl-Update-nvdimm-mailing-list-address.patch +Patch21: 0024-libndctl-papr-Fix-probe-for-papr-scm-compatible-nvdi.patch +Patch22: 0025-ndctl-scrub-Stop-translating-return-values.patch +Patch23: 0026-ndctl-scrub-Reread-scrub-engine-status-at-start.patch +Patch24: 0027-ndctl-dimm-Fix-label-index-block-calculations.patch +Patch25: 0028-ndctl-namespace-Skip-seed-namespaces-when-processing.patch +Patch26: 0029-ndctl-namespace-Suppress-ENXIO-when-processing-all-n.patch +Patch27: 0030-namespace-action-Drop-zero-namespace-checks.patch +Patch28: 0031-ndctl-add-.clang-format.patch +Patch29: 0032-cxl-add-a-cxl-utility-and-libcxl-library.patch +Patch30: 0033-cxl-add-a-local-copy-of-the-cxl_mem-UAPI-header.patch +Patch31: 0034-util-add-the-struct_size-helper-from-the-kernel.patch +Patch32: 0035-libcxl-add-support-for-command-query-and-submission.patch +Patch33: 0036-libcxl-add-support-for-the-Identify-Device-command.patch +Patch34: 0037-libcxl-add-GET_HEALTH_INFO-mailbox-command-and-acces.patch +Patch35: 0038-libcxl-add-support-for-the-GET_LSA-command.patch +Patch36: 0039-libcxl-add-label_size-to-cxl_memdev-and-an-API-to-re.patch +Patch37: 0040-libcxl-add-representation-for-an-nvdimm-bridge-objec.patch +Patch38: 0041-libcxl-add-interfaces-for-label-operations.patch +Patch39: 0042-cxl-add-commands-to-read-write-and-zero-labels.patch +Patch40: 0043-Documentation-cxl-add-library-API-documentation.patch +Patch41: 0044-ndctl-Add-CXL-packages-to-the-RPM-spec.patch +Patch42: 0045-cxl-cli-add-bash-completion.patch +Patch43: 0046-cxl-add-health-information-to-cxl-list.patch +Patch44: 0047-ndctl-install-bash-completion-symlinks.patch +Patch45: 0048-scripts-Add-a-man-page-template-generator.patch +Patch46: 0049-daxctl-Add-Soft-Reservation-theory-of-operation.patch +Patch47: 0061-libcxl-fix-potential-NULL-dereference-in-cxl_memdev_.patch +Patch48: 0064-ndctl-release-v72.patch +Patch49: 0067-ndctl-add-repology-graphic-to-README.md.patch +Patch50: 0068-Documentation-ndctl-fix-self-reference-of-ndctl-disa.patch +Patch51: 0069-ndctl-docs-Clarify-update-firwmware-activation-overf.patch +Patch52: 0070-ndctl-test-Prepare-for-BLK-aperture-support-removal.patch +Patch53: 0071-ndctl-test-Move-reset-to-function-in-common.patch +Patch54: 0072-ndctl-test-Initialize-the-label-area-by-default.patch +Patch55: 0073-ndctl-test-Skip-BLK-flags-checks.patch +Patch56: 0074-ndctl-test-Move-sector-mode-to-a-different-region.patch +Patch57: 0075-ndctl-Deprecate-BLK-aperture-support.patch +Patch58: 0076-ndctl-test-Fix-support-for-missing-dax_pmem_compat-m.patch +Patch59: 0077-util-Distribute-filter-and-json-helpers-to-per-tool-.patch +Patch60: 0078-Documentation-Drop-attrs.adoc-include.patch +Patch61: 0079-build-Drop-unnecessary-tool-config.h-includes.patch +Patch62: 0080-test-Prepare-out-of-line-builds.patch +Patch63: 0081-ndctl-Drop-executable-bit-for-bash-completion-script.patch +Patch64: 0082-build-Add-meson-build-infrastructure.patch +Patch65: 0083-build-Add-meson-rpmbuild-support.patch +Patch66: 0084-ndctl-Jettison-autotools.patch +Patch67: 0085-ndctl-build-Default-asciidoctor-to-enabled.patch +Patch68: 0086-ndctl-update-README.md-for-meson-build.patch +Patch69: 0087-test-Add-suite-identifiers-to-tests.patch +Patch70: 0088-ndctl-Rename-util_filter-to-ndctl_filter.patch +Patch71: 0089-build-Add-tags.patch +Patch72: 0090-json-Add-support-for-json_object_new_uint64.patch +Patch73: 0091-cxl-json-Cleanup-object-leak-false-positive.patch +Patch74: 0092-cxl-list-Support-multiple-memdev-device-name-filter-.patch +Patch75: 0093-cxl-list-Support-comma-separated-lists.patch +Patch76: 0094-cxl-list-Introduce-cxl_filter_walk.patch +Patch77: 0095-cxl-list-Emit-device-serial-numbers.patch +Patch78: 0096-cxl-list-Add-filter-by-serial-support.patch +Patch79: 0097-cxl-lib-Rename-nvdimm-bridge-to-pmem.patch +Patch80: 0098-cxl-list-Cleanup-options-definitions.patch +Patch81: 0099-Documentation-Enhance-libcxl-memdev-API-documentatio.patch +Patch82: 0100-cxl-list-Add-bus-objects.patch +Patch83: 0101-util-json-Warn-on-stderr-about-empty-list-results.patch +Patch84: 0102-util-sysfs-Uplevel-modalias-lookup-helper-to-util.patch +Patch85: 0103-cxl-list-Add-port-enumeration.patch +Patch86: 0104-cxl-list-Add-debug-option.patch +Patch87: 0105-cxl-list-Add-endpoints.patch +Patch88: 0106-cxl-list-Add-host-entries-for-port-like-objects.patch +Patch89: 0107-cxl-list-Add-host-entries-for-memdevs.patch +Patch90: 0108-cxl-list-Move-enabled-memdevs-underneath-their-endpo.patch +Patch91: 0109-cxl-list-Filter-memdev-by-ancestry.patch +Patch92: 0110-cxl-memdev-Use-a-local-logger-for-debug.patch +Patch93: 0111-cxl-memdev-Cleanup-memdev-filter.patch +Patch94: 0112-cxl-memdev-Add-serial-support-for-memdev-related-com.patch +Patch95: 0113-cxl-list-Add-numa_node-to-memdev-listings.patch +Patch96: 0114-util-Implement-common-bind-unbind-helpers.patch +Patch97: 0115-cxl-memdev-Enable-disable-support.patch +Patch98: 0116-cxl-list-Add-decoder-support.patch +Patch99: 0117-cxl-list-Extend-decoder-objects-with-target-informat.patch +Patch100: 0118-cxl-list-Use-physical_node-for-root-port-attachment-.patch +Patch101: 0119-cxl-list-Reuse-the-target-option-for-ports.patch +Patch102: 0120-cxl-list-Support-filtering-memdevs-by-decoders.patch +Patch103: 0121-cxl-list-Support-filtering-memdevs-by-ports.patch +Patch104: 0122-cxl-port-Add-disable-enable-port-command.patch +Patch105: 0123-cxl-list-Filter-dports-and-targets-by-memdevs.patch +Patch106: 0124-ndctl-test-make-inject-smart.sh-more-tolerant-of-dec.patch +Patch107: 0125-libndctl-papr-Add-support-for-reporting-shutdown-cou.patch +Patch108: 0126-libndctl-intel-Indicate-supported-smart-inject-types.patch +Patch109: 0127-libndctl-papr-Add-limited-support-for-inject-smart.patch +Patch110: 0128-ndtest-ack-shutdown-count-Skip-the-test-on-ndtest.patch +Patch111: 0129-ndctl-libndctl-Update-nvdimm-flags-after-smart-injec.patch +Patch112: 0132-libcxl-add-GET_PARTITION_INFO-mailbox-command-and-ac.patch +Patch113: 0133-libcxl-add-accessors-for-capacity-fields-of-the-IDEN.patch +Patch114: 0134-libcxl-return-the-partition-alignment-field-in-bytes.patch +Patch115: 0135-cxl-add-memdev-partition-information-to-cxl-list.patch +Patch116: 0136-libcxl-add-interfaces-for-SET_PARTITION_INFO-mailbox.patch +Patch117: 0137-cxl-add-command-cxl-set-partition.patch +Patch118: 0138-Update-ndctl.spec-to-allow-flatpak-builds.patch +Patch119: 0139-daxctl-provide-safe-versions-of-iteration-API.patch +Patch120: 0140-util-size.h-fix-build-for-older-compilers.patch +Patch121: 0141-build-Automate-rpmbuild.sh.patch +Patch122: 0142-util-size.h-Fix-build-error-for-GCC-10.patch +Patch123: 0143-libcxl-Remove-extraneous-NULL-checks-when-validating.patch +Patch124: 0144-libdaxctl-free-resource-allocated-with-asprintf.patch +Patch125: 0145-cxl-list-tidy-the-error-path-in-add_cxl_decoder.patch +Patch126: 0146-cxl-list-always-free-the-path-var-in-add_cxl_decoder.patch +Patch127: 0147-scripts-docsurgeon-Fix-document-header-for-section-1.patch +Patch128: 0148-ndctl-release-v73.patch +Patch130: 0150-build-Fix-Wall-and-O2-warnings.patch +Patch131: 0151-build-Fix-test-timeouts.patch +Patch132: 0155-build-Move-utility-helpers-to-libutil.a.patch +Patch133: 0156-util-Use-SZ_-size-macros-in-display-size.patch +Patch134: 0157-util-Pretty-print-terabytes.patch +Patch135: 0158-cxl-port-Fix-disable-port-man-page.patch +Patch136: 0159-cxl-bus-Add-bus-disable-support.patch +Patch137: 0160-cxl-list-Auto-enable-single-mode-for-port-listings.patch +Patch138: 0161-cxl-memdev-Fix-bus_invalidate-crash.patch +Patch139: 0162-cxl-list-Add-support-for-filtering-by-host-identifie.patch +Patch140: 0163-cxl-port-Relax-port-identifier-validation.patch +Patch142: 0165-cxl-test-Add-topology-enumeration-and-hotplug-test.patch +Patch143: 0167-daxctl-Fix-kernel-option-typo-in-Soft-Reservation-th.patch +Patch144: 0168-meson-make-modprobedatadir-an-option.patch +Patch145: 0169-namespace-action-Drop-more-zero-namespace-checks.patch +Patch146: 0170-ndctl-dimm-Flush-invalidated-labels-after-overwrite.patch +Patch147: 0171-libcxl-fix-a-segfault-when-memdev-pmem-is-absent.patch +Patch148: 0172-ndctl-bus-Handle-missing-scrub-commands-more-gracefu.patch +Patch149: 0173-util-wrapper.c-Fix-gcc-warning-in-xrealloc.patch +Patch150: 0174-libcxl-Fix-memory-leakage-in-cxl_port_init.patch +Patch151: 0175-cxl-list-Reformat-option-list.patch +Patch152: 0176-cxl-list-Emit-endpoint-decoders-filtered-by-memdev.patch +Patch153: 0177-cxl-list-Hide-0s-in-disabled-decoder-listings.patch +Patch154: 0178-cxl-list-Add-DPA-span-to-endpoint-decoder-listings.patch +Patch155: 0179-ccan-list-Import-latest-list-helpers.patch +Patch156: 0180-cxl-lib-Maintain-decoders-in-id-order.patch +Patch157: 0181-cxl-memdev-Fix-json-for-multi-device-partitioning.patch +Patch158: 0182-cxl-list-Emit-mode-for-endpoint-decoder-objects.patch +Patch159: 0183-cxl-set-partition-Accept-ram-as-an-alias-for-volatil.patch +Patch160: 0184-cxl-memdev-Add-reserve-free-dpa-commands.patch +Patch161: 0185-cxl-test-Update-CXL-memory-parameters.patch +Patch162: 0186-cxl-test-Checkout-region-setup-teardown.patch +Patch163: 0187-cxl-test-add-a-test-to-read-write-zero-labels.patch +Patch164: 0188-cxl-list-Clarify-B-vs-P-p-root.patch +Patch165: 0189-libcxl-add-a-depth-attribute-to-cxl_port.patch +Patch166: 0190-cxl-port-Consolidate-the-debug-option-in-cxl-port-ma.patch +Patch167: 0191-cxl-memdev-refactor-decoder-mode-string-parsing.patch +Patch168: 0192-libcxl-Introduce-libcxl-region-and-mapping-objects.patch +Patch169: 0193-cxl-cli-add-region-listing-support.patch +Patch170: 0194-libcxl-add-low-level-APIs-for-region-creation.patch +Patch171: 0195-cxl-add-a-create-region-command.patch +Patch172: 0196-cxl-add-commands-to-enable-disable-destroy-region.patch +Patch173: 0197-cxl-list-make-memdevs-and-regions-the-default-listin.patch +Patch174: 0198-test-add-a-cxl-create-region-test.patch +Patch175: 0199-cxl-decoder-add-a-max_available_extent-attribute.patch +Patch176: 0200-cxl-Add-list-verbose-option-to-the-cxl-command.patch +Patch177: 0201-cxl-test-Validate-endpoint-interleave-geometry.patch +Patch178: 0202-cxl-list-Add-interleave-parameters-to-decoder-listin.patch +Patch179: 0203-cxl-list-Add-region-to-decoder-listings.patch +Patch180: 0204-cxl-list-Filter-decoders-by-region.patch +Patch181: 0205-cxl-list-Add-depth-to-port-listings.patch +Patch182: 0206-cxl-test-Validate-switch-port-settings-in-cxl-region.patch +Patch183: 0207-meson-fix-modprobedatadir-default-value.patch +Patch184: 0208-ndctl-move-developer-scripts-from-contrib-to-scripts.patch +Patch185: 0209-ndctl-remove-obsolete-m4-directory.patch +Patch186: 0210-ndctl-update-.gitignore.patch +Patch187: 0211-scripts-fix-contrib-do_abidiff-for-updated-fedpkg.patch +Patch188: 0212-scripts-update-release-helper-scripts-for-meson-and-.patch +Patch189: 0213-meson.build-be-specific-for-library-path.patch +Patch190: 0214-cxl-region-fix-a-dereferecnce-after-NULL-check.patch +Patch191: 0215-libcxl-fox-a-resource-leak-and-a-forward-NULL-check.patch +Patch192: 0216-cxl-filter-Fix-an-uninitialized-pointer-dereference.patch +Patch193: 0217-ndctl-release-v74.patch %endif Requires: ndctl-libs%{?_isa} = %{version}-%{release} Requires: daxctl-libs%{?_isa} = %{version}-%{release} %if 0%{?facebook} -Requires: cxl-libs%{?_isa} = %{version}-%{release} +Requires: cxl-libs%{?_isa} = %{version}-%{release} %endif +BuildRequires: make BuildRequires: autoconf BuildRequires: asciidoc +%define asciidoc -Dasciidoctor=disabled BuildRequires: xmlto -BuildRequires: automake +BuildRequires: meson +BuildRequires: ninja-build BuildRequires: libtool BuildRequires: pkgconfig BuildRequires: pkgconfig(libkmod) @@ -60,7 +241,6 @@ Firmware Interface Table). %package -n ndctl-devel Summary: Development files for libndctl License: LGPLv2 -Group: Development/Libraries Requires: ndctl-libs%{?_isa} = %{version}-%{release} %description -n ndctl-devel @@ -70,7 +250,6 @@ developing applications that use %{name}. %package -n daxctl Summary: Manage Device-DAX instances License: GPLv2 -Group: System Environment/Base Requires: daxctl-libs%{?_isa} = %{version}-%{release} %description -n daxctl @@ -79,7 +258,17 @@ the Linux kernel Device-DAX facility. This facility enables DAX mappings of performance / feature differentiated memory without need of a filesystem. -%if 0%{?facebook} +%package -n daxctl-devel +Summary: Development files for libdaxctl +License: LGPLv2 +Requires: daxctl-libs%{?_isa} = %{version}-%{release} + +%description -n daxctl-devel +The %{name}-devel package contains libraries and header files for +developing applications that use %{name}, a library for enumerating +"Device DAX" devices. Device DAX is a facility for establishing DAX +mappings of performance / feature-differentiated memory. + %package -n cxl-cli Summary: Manage CXL devices License: GPLv2 @@ -97,25 +286,10 @@ Requires: cxl-libs%{?_isa} = %{version}-%{release} %description -n cxl-devel This package contains libraries and header files for developing applications that use libcxl, a library for enumerating and communicating with CXL devices. -%endif - -%package -n daxctl-devel -Summary: Development files for libdaxctl -License: LGPLv2 -Group: Development/Libraries -Requires: daxctl-libs%{?_isa} = %{version}-%{release} - -%description -n daxctl-devel -The %{name}-devel package contains libraries and header files for -developing applications that use %{name}, a library for enumerating -"Device DAX" devices. Device DAX is a facility for establishing DAX -mappings of performance / feature-differentiated memory. - %package -n ndctl-libs Summary: Management library for "libnvdimm" subsystem devices (Non-volatile Memory) License: LGPLv2 -Group: System Environment/Libraries Requires: daxctl-libs%{?_isa} = %{version}-%{release} @@ -125,38 +299,44 @@ Libraries for %{name}. %package -n daxctl-libs Summary: Management library for "Device DAX" devices License: LGPLv2 -Group: System Environment/Libraries %description -n daxctl-libs Device DAX is a facility for establishing DAX mappings of performance / feature-differentiated memory. daxctl-libs provides an enumeration / control API for these devices. -%if 0%{?facebook} %package -n cxl-libs Summary: Management library for CXL devices License: LGPLv2 %description -n cxl-libs libcxl is a library for enumerating and communicating with CXL devices. -%endif %prep %autosetup -p1 ndctl-%{version} %build +%if 0%{?facebook} echo %{version} > version ./autogen.sh %configure --disable-static --disable-silent-rules --disable-asciidoctor make %{?_smp_mflags} +%else +%meson %{?asciidoc} -Dversion-tag=%{version} +%meson_build +%endif %install +%if 0%{?facebook} %make_install find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %check make check +%else +%meson_install +%endif %ldconfig_scriptlets -n ndctl-libs @@ -185,11 +365,12 @@ make check %{_mandir}/man1/daxctl* %{_datadir}/daxctl/daxctl.conf -%if 0%{?facebook} %files -n cxl-cli %license LICENSES/preferred/GPL-2.0 LICENSES/other/MIT LICENSES/other/CC0-1.0 %{_bindir}/cxl %{_mandir}/man1/cxl* +%if 0%{?facebook} < 1 +%{bashcompdir}/cxl %endif %files -n ndctl-libs @@ -202,12 +383,10 @@ make check %license LICENSES/preferred/LGPL-2.1 LICENSES/other/MIT LICENSES/other/CC0-1.0 %{_libdir}/libdaxctl.so.* -%if 0%{?facebook} %files -n cxl-libs %doc README.md %license LICENSES/preferred/LGPL-2.1 LICENSES/other/MIT LICENSES/other/CC0-1.0 %{_libdir}/libcxl.so.* -%endif %files -n ndctl-devel %license LICENSES/preferred/LGPL-2.1 @@ -221,18 +400,20 @@ make check %{_libdir}/libdaxctl.so %{_libdir}/pkgconfig/libdaxctl.pc -%if 0%{?facebook} %files -n cxl-devel %license LICENSES/preferred/LGPL-2.1 %{_includedir}/cxl/ %{_libdir}/libcxl.so %{_libdir}/pkgconfig/libcxl.pc %{_mandir}/man3/cxl* -%{_mandir}/man3/libcxl.3.gz -%endif +%{_mandir}/man3/libcxl.3* %changelog +* Fri Aug 04 2023 Anita Zhang - 71.1-8.1 +- Sync 71.1-8 from c9 branch. +- Patch bafc29a0..df411ebe from elake/ndctl fork for hs+fb + * Thu Feb 23 2023 Anita Zhang - 71.1-4.2 - Patch SPD decoding and dimm_slot_info CCI command support @@ -240,47 +421,113 @@ make check - Patch 71.1 with diff between ea014c0..3ff031d from elake/ndctl fork for hs+fb - Sync changes from rhel/ndctl.spec (autogenerated by the GitHub Makefile) -* Tue Jun 14 2022 Jeff Moyer - 71.1-4.el8 -- Pull in fixes from upstream v72 and v73 (Jeff Moyer) - - Fix enable-namespace all reporting errors incorrectly - - Add support for inject-smart on papr scm -- Related: bz#2090190 bz#1986185 bz#2040074 +* Thu Oct 13 2022 Jeff Moyer - 71.1-8 +- Backport changes up to v74, excluding the config file changes. (Jeff Moyer) + This includes support for the CXL commands, and adds the following + packages: cxl-cli, cxl-devel, cxl-libs +- Resolves: rhbz#2132167 -* Mon Nov 29 2021 Bryan Gurney - 71.1-3.el8 +* Tue Jun 14 2022 Bryan Gurney - 71.1-7 +- Pull in fixes from upstream v72 and v73 (Jeff Moyer) +- Fix enable-namespace all reporting errors incorrectly +- Add support for inject-smart on papr scm +- Related: rhbz#2040075 +- Related: rhbz#1873851 +- Related: rhbz#1880578 +- Related: rhbz#1922538 +- Related: rhbz#2087707 + +* Wed Dec 1 2021 Bryan Gurney - 71.1-6 +- Add gating test +- Related: rhbz#2028152 + +* Mon Nov 29 2021 Bryan Gurney - 71.1-5 - Rebuild with latest json-c version -- Related: bz#2021816 - -* Thu Feb 11 2021 Jeff Moyer - 71.1-2.el8 -- Get rid of confusing message when deleting all namespaces -- Related: bz#1782182 - -* Fri Feb 5 2021 Jeff Moyer - 71.1-1.el8 -- Update to v71.1 to pull in ppc support. -- Related: bz#1782182 - -* Fri Nov 1 2019 Jeff Moyer - 67-2.el8 -- Fix up botched change to nvdimm-security.conf (Jeff Moyer) -- Related: bz#1724531 - -* Mon Oct 28 2019 Jeff Moyer - 67-1.el8 -- Rebase to v67. This brings in the following features: - - support for the 'security frozen' sysfs attribute - - support for using pmem as system ram - - various cleanup and bug fixes -- Fix load-keys failure in initramfs (Jeff Moyer) -- Resolves: bz#1724531 bz#1730673 bz#1741164 bz#1741165 bz#1749888 bz#1749889 - -* Mon Jun 3 2019 Jeff Moyer - 65-1.el8 -- Rebase to v65. -- Resolves: bz#1665407 bz#1634349 - -* Tue Oct 09 2018 Jeff Moyer - 62-1 -- rebase to v62 -- Resolves: bz#1567756 bz#1497651 bz#1610650 bz#1511774 bz#1570548 +- Related: rhbz#2023317 + +* Mon Aug 09 2021 Mohan Boddu - 71.1-4 +- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags + Related: rhbz#1991688 + +* Fri Apr 16 2021 Mohan Boddu - 71.1-3 +- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 + +* Tue Jan 26 2021 Fedora Release Engineering - 71.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Tue Dec 22 2020 Vishal Verma - 71.1-1 +- release v71.1 + +* Sat Dec 19 2020 Vishal Verma - 71-1 +- release v71 + +* Sat Oct 10 2020 Vishal Verma - 70.1-1 +- release v70.1 + +* Tue Oct 06 2020 Vishal Verma - 70-1 +- release v70 + +* Tue Jul 28 2020 Fedora Release Engineering - 69-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Thu Jul 23 2020 Vishal Verma - 69-1 +- release v69 + +* Tue Apr 21 2020 Björn Esser - 68-2 +- Rebuild (json-c) + +* Tue Mar 24 2020 Vishal Verma - 68-1 +- release v68 + +* Fri Jan 31 2020 Vishal Verma - 67-3 +- Add fix for GCC10 builds + +* Wed Jan 29 2020 Fedora Release Engineering - 67-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Mon Oct 28 2019 Vishal Verma - 67-1 +- release v67 + +* Wed Aug 07 2019 Vishal Verma - 66-1 +- release v66 + +* Thu Jul 25 2019 Fedora Release Engineering - 65-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Sat May 11 2019 Vishal Verma - 65-1 +- release v65 + +* Wed Feb 06 2019 Vishal Verma - 64.1-1 +- release v64.1 + +* Mon Feb 04 2019 Vishal Verma - 64-1 +- release v64 + +* Fri Feb 01 2019 Fedora Release Engineering - 63-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Fri Oct 05 2018 Vishal Verma - 63-1 +- release v63 +- remove ndctl-udev and related files + +* Tue Aug 14 2018 Vishal Verma - 62-1 +- release v62 +- Add files for udev and ndctl-monitor + +* Fri Jul 13 2018 Fedora Release Engineering - 61.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Wed Jul 11 2018 Vishal Verma - 61.2-1 +- release v61.2 + +* Tue Jun 26 2018 Vishal Verma - 61.1-1 +- release v61.1 + +* Tue Jun 26 2018 Vishal Verma - 61-1 +- new version + +* Thu May 17 2018 Dan Williams - 60.3-1 +- release v60.3 * Mon Apr 23 2018 Dan Williams - 60.1-1 - release v60.1