* [NDCTL PATCH v4 0/3] ndctl: Add support and test for CXL Features support
@ 2025-02-18 22:59 Dave Jiang
2025-02-18 22:59 ` [NDCTL PATCH v4 1/3] cxl: Add cxl_bus_get_by_provider() Dave Jiang
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: Dave Jiang @ 2025-02-18 22:59 UTC (permalink / raw)
To: linux-cxl; +Cc: alison.schofield
v3:
- Update test to use opcode instead of command id.
v2:
- Drop features device enumeration
- Add discovery of char device under memdev
The series provides support of libcxl enumerating FWCTL character device
under the cxl_memdev device. It discovers the char device major
and minor numbers for the CXL features device in order to allow issuing
of ioctls to the device.
A unit test is added to locate cxl_memdev exported by the cxl_test
kernel module and issue all the supported ioctls to the associated
FWCTL char device to verify that all the ioctl paths are working as expected.
Kernel series: https://lore.kernel.org/linux-cxl/20250218225721.2682235-1-dave.jiang@intel.com/T/#t
Dave Jiang (3):
cxl: Add cxl_bus_get_by_provider()
cxl: Enumerate major/minor of FWCTL char device
cxl/test: Add test for cxl features device
cxl/lib/libcxl.c | 85 ++++++++++++++++
cxl/lib/libcxl.sym | 7 ++
cxl/lib/private.h | 1 +
cxl/libcxl.h | 4 +
test/cxl-features.sh | 31 ++++++
test/fwctl.c | 383 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
test/meson.build | 45 +++++++++
7 files changed, 556 insertions(+)
^ permalink raw reply [flat|nested] 11+ messages in thread* [NDCTL PATCH v4 1/3] cxl: Add cxl_bus_get_by_provider() 2025-02-18 22:59 [NDCTL PATCH v4 0/3] ndctl: Add support and test for CXL Features support Dave Jiang @ 2025-02-18 22:59 ` Dave Jiang 2025-04-09 0:42 ` Alison Schofield 2025-02-18 22:59 ` [NDCTL PATCH v4 2/3] cxl: Enumerate major/minor of FWCTL char device Dave Jiang 2025-02-18 22:59 ` [NDCTL PATCH v4 3/3] cxl/test: Add test for cxl features device Dave Jiang 2 siblings, 1 reply; 11+ messages in thread From: Dave Jiang @ 2025-02-18 22:59 UTC (permalink / raw) To: linux-cxl; +Cc: alison.schofield Add helper function cxl_bus_get_by_provider() in order to support unit test that will utilize the API call. Signed-off-by: Dave Jiang <dave.jiang@intel.com> --- cxl/lib/libcxl.c | 11 +++++++++++ cxl/lib/libcxl.sym | 5 +++++ cxl/libcxl.h | 2 ++ 3 files changed, 18 insertions(+) diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c index 63aa4ef3acdc..bab7343e8a4a 100644 --- a/cxl/lib/libcxl.c +++ b/cxl/lib/libcxl.c @@ -3358,6 +3358,17 @@ CXL_EXPORT struct cxl_ctx *cxl_bus_get_ctx(struct cxl_bus *bus) return cxl_port_get_ctx(&bus->port); } +CXL_EXPORT struct cxl_bus *cxl_bus_get_by_provider(struct cxl_ctx *ctx, + const char *provider) +{ + struct cxl_bus *bus; + + cxl_bus_foreach(ctx, bus) + if (strcmp(provider, cxl_bus_get_provider(bus)) == 0) + return bus; + return NULL; +} + 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 0c155a40ad47..1fc33cc6e1a4 100644 --- a/cxl/lib/libcxl.sym +++ b/cxl/lib/libcxl.sym @@ -288,3 +288,8 @@ global: cxl_memdev_trigger_poison_list; cxl_region_trigger_poison_list; } LIBCXL_7; + +LIBCXL_9 { +global: + cxl_bus_get_by_provider; +} LIBECXL_8; diff --git a/cxl/libcxl.h b/cxl/libcxl.h index 0a5fd0e13cc2..3b309968a808 100644 --- a/cxl/libcxl.h +++ b/cxl/libcxl.h @@ -122,6 +122,8 @@ 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); +struct cxl_bus *cxl_bus_get_by_provider(struct cxl_ctx *ctx, + const char *provider); #define cxl_bus_foreach(ctx, bus) \ for (bus = cxl_bus_get_first(ctx); bus != NULL; \ -- 2.48.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [NDCTL PATCH v4 1/3] cxl: Add cxl_bus_get_by_provider() 2025-02-18 22:59 ` [NDCTL PATCH v4 1/3] cxl: Add cxl_bus_get_by_provider() Dave Jiang @ 2025-04-09 0:42 ` Alison Schofield 0 siblings, 0 replies; 11+ messages in thread From: Alison Schofield @ 2025-04-09 0:42 UTC (permalink / raw) To: Dave Jiang; +Cc: linux-cxl, nvdimm On Tue, Feb 18, 2025 at 03:59:54PM -0700, Dave Jiang wrote: > Add helper function cxl_bus_get_by_provider() in order to support unit > test that will utilize the API call. > > Signed-off-by: Dave Jiang <dave.jiang@intel.com> > --- > cxl/lib/libcxl.c | 11 +++++++++++ > cxl/lib/libcxl.sym | 5 +++++ > cxl/libcxl.h | 2 ++ > 3 files changed, 18 insertions(+) Please describe the new library interface in "Documentation/cxl/lib/libcxl.txt" Add To: nvdimm@lists.linux.dev snip ^ permalink raw reply [flat|nested] 11+ messages in thread
* [NDCTL PATCH v4 2/3] cxl: Enumerate major/minor of FWCTL char device 2025-02-18 22:59 [NDCTL PATCH v4 0/3] ndctl: Add support and test for CXL Features support Dave Jiang 2025-02-18 22:59 ` [NDCTL PATCH v4 1/3] cxl: Add cxl_bus_get_by_provider() Dave Jiang @ 2025-02-18 22:59 ` Dave Jiang 2025-04-09 19:56 ` Alison Schofield 2025-04-09 23:39 ` Dan Williams 2025-02-18 22:59 ` [NDCTL PATCH v4 3/3] cxl/test: Add test for cxl features device Dave Jiang 2 siblings, 2 replies; 11+ messages in thread From: Dave Jiang @ 2025-02-18 22:59 UTC (permalink / raw) To: linux-cxl; +Cc: alison.schofield Add major/minor discovery for the FWCTL character device that is associated with supprting CXL Features under the cxl_memdev. Add libcxl API functions to retrieve the major and minor of the FWCTL character device. Signed-off-by: Dave Jiang <dave.jiang@intel.com> --- cxl/lib/libcxl.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++ cxl/lib/libcxl.sym | 2 ++ cxl/lib/private.h | 1 + cxl/libcxl.h | 2 ++ 4 files changed, 79 insertions(+) diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c index bab7343e8a4a..566870acb30a 100644 --- a/cxl/lib/libcxl.c +++ b/cxl/lib/libcxl.c @@ -1253,6 +1253,66 @@ static int add_cxl_memdev_fwl(struct cxl_memdev *memdev, return -ENOMEM; } +static const char fwctl_prefix[] = "fwctl"; +static int get_feature_chardev(const char *base, char *chardev_path) +{ + char *path = calloc(1, strlen(base) + 100); + struct dirent *entry; + int rc = 0; + DIR *dir; + + if (!path) + return -ENOMEM; + + sprintf(path, "%s/fwctl/", base); + dir = opendir(path); + if (!dir) { + rc = -errno; + goto err; + } + + while ((entry = readdir(dir)) != NULL) + if (strncmp(entry->d_name, fwctl_prefix, strlen(fwctl_prefix)) == 0) + break; + + if (!entry) { + rc = -ENOENT; + goto read_err; + } + + sprintf(chardev_path, "/dev/fwctl/%s", entry->d_name); + +read_err: + closedir(dir); +err: + free(path); + return rc; +} + +static int memdev_get_fwctl_chardev(struct cxl_memdev *memdev, + const char *cxlmem_base) +{ + char *path = calloc(1, strlen(cxlmem_base) + 100); + struct stat st; + int rc; + + rc = get_feature_chardev(cxlmem_base, path); + if (rc) + goto out; + + if (stat(path, &st) < 0) { + rc = -errno; + goto out; + } + + memdev->fwctl_major = major(st.st_rdev); + memdev->fwctl_minor = minor(st.st_rdev); + +out: + free(path); + return rc; +} + static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) { const char *devname = devpath_to_devname(cxlmem_base); @@ -1279,6 +1339,10 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) memdev->major = major(st.st_rdev); memdev->minor = minor(st.st_rdev); + /* If this fails, no Features support. */ + if (memdev_get_fwctl_chardev(memdev, cxlmem_base)) + dbg(ctx, "%s: no Features support.\n", devname); + sprintf(path, "%s/pmem/size", cxlmem_base); if (sysfs_read_attr(ctx, path, buf) == 0) memdev->pmem_size = strtoull(buf, NULL, 0); @@ -1515,6 +1579,16 @@ CXL_EXPORT int cxl_memdev_get_minor(struct cxl_memdev *memdev) return memdev->minor; } +CXL_EXPORT int cxl_memdev_get_fwctl_major(struct cxl_memdev *memdev) +{ + return memdev->fwctl_major; +} + +CXL_EXPORT int cxl_memdev_get_fwctl_minor(struct cxl_memdev *memdev) +{ + return memdev->fwctl_minor; +} + CXL_EXPORT unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev) { return memdev->pmem_size; diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym index 1fc33cc6e1a4..b2b51a72673c 100644 --- a/cxl/lib/libcxl.sym +++ b/cxl/lib/libcxl.sym @@ -292,4 +292,6 @@ global: LIBCXL_9 { global: cxl_bus_get_by_provider; + cxl_memdev_get_fwctl_major; + cxl_memdev_get_fwctl_minor; } LIBECXL_8; diff --git a/cxl/lib/private.h b/cxl/lib/private.h index b6cd910e9335..676bf1573487 100644 --- a/cxl/lib/private.h +++ b/cxl/lib/private.h @@ -37,6 +37,7 @@ enum cxl_fwl_loading { struct cxl_endpoint; struct cxl_memdev { int id, major, minor; + int fwctl_major, fwctl_minor; int numa_node; void *dev_buf; size_t buf_len; diff --git a/cxl/libcxl.h b/cxl/libcxl.h index 3b309968a808..26aa906740af 100644 --- a/cxl/libcxl.h +++ b/cxl/libcxl.h @@ -69,6 +69,8 @@ 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); +int cxl_memdev_get_fwctl_major(struct cxl_memdev *memdev); +int cxl_memdev_get_fwctl_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); -- 2.48.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [NDCTL PATCH v4 2/3] cxl: Enumerate major/minor of FWCTL char device 2025-02-18 22:59 ` [NDCTL PATCH v4 2/3] cxl: Enumerate major/minor of FWCTL char device Dave Jiang @ 2025-04-09 19:56 ` Alison Schofield 2025-04-09 22:40 ` Dave Jiang 2025-04-09 23:39 ` Dan Williams 1 sibling, 1 reply; 11+ messages in thread From: Alison Schofield @ 2025-04-09 19:56 UTC (permalink / raw) To: Dave Jiang; +Cc: linux-cxl On Tue, Feb 18, 2025 at 03:59:55PM -0700, Dave Jiang wrote: > Add major/minor discovery for the FWCTL character device that is associated > with supprting CXL Features under the cxl_memdev. Add libcxl API functions > to retrieve the major and minor of the FWCTL character device. > > Signed-off-by: Dave Jiang <dave.jiang@intel.com> > --- > cxl/lib/libcxl.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++ > cxl/lib/libcxl.sym | 2 ++ > cxl/lib/private.h | 1 + > cxl/libcxl.h | 2 ++ > 4 files changed, 79 insertions(+) Please describe the new library interface in "Documentation/cxl/lib/libcxl.txt" Add To: nvdimm@lists.linux.dev > > diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c > index bab7343e8a4a..566870acb30a 100644 > --- a/cxl/lib/libcxl.c > +++ b/cxl/lib/libcxl.c > @@ -1253,6 +1253,66 @@ static int add_cxl_memdev_fwl(struct cxl_memdev *memdev, > return -ENOMEM; > } > Can we define like this: #define CXL_PATH_MAX 512 and do this: char path[CXL_PATH_MAX]; in next 2 funcs? > +static const char fwctl_prefix[] = "fwctl"; > +static int get_feature_chardev(const char *base, char *chardev_path) > +{ > + char *path = calloc(1, strlen(base) + 100); > + struct dirent *entry; > + int rc = 0; > + DIR *dir; > + > + if (!path) > + return -ENOMEM; > + > + sprintf(path, "%s/fwctl/", base); > + dir = opendir(path); > + if (!dir) { > + rc = -errno; > + goto err; > + } > + > + while ((entry = readdir(dir)) != NULL) > + if (strncmp(entry->d_name, fwctl_prefix, strlen(fwctl_prefix)) == 0) > + break; Can we exit this while loop w entry not NULL, yet not a match? Maybe a found flag or store a match separately. > + > + if (!entry) { > + rc = -ENOENT; > + goto read_err; > + } > + > + sprintf(chardev_path, "/dev/fwctl/%s", entry->d_name); > + > +read_err: > + closedir(dir); > +err: > + free(path); > + return rc; > +} > + > +static int memdev_get_fwctl_chardev(struct cxl_memdev *memdev, > + const char *cxlmem_base) > +{ > + char *path = calloc(1, strlen(cxlmem_base) + 100); > + struct stat st; > + int rc; > + Do we need to init memdev->fwctl... to something to avoid garbage? > + rc = get_feature_chardev(cxlmem_base, path); > + if (rc) > + goto out; > + > + if (stat(path, &st) < 0) { > + rc = -errno; > + goto out; > + } > + > + memdev->fwctl_major = major(st.st_rdev); > + memdev->fwctl_minor = minor(st.st_rdev); > + > +out: > + free(path); > + return rc; > +} > + > static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) > { > const char *devname = devpath_to_devname(cxlmem_base); > @@ -1279,6 +1339,10 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) > memdev->major = major(st.st_rdev); > memdev->minor = minor(st.st_rdev); > > + /* If this fails, no Features support. */ > + if (memdev_get_fwctl_chardev(memdev, cxlmem_base)) > + dbg(ctx, "%s: no Features support.\n", devname); > + > sprintf(path, "%s/pmem/size", cxlmem_base); > if (sysfs_read_attr(ctx, path, buf) == 0) > memdev->pmem_size = strtoull(buf, NULL, 0); > @@ -1515,6 +1579,16 @@ CXL_EXPORT int cxl_memdev_get_minor(struct cxl_memdev *memdev) > return memdev->minor; > } > > +CXL_EXPORT int cxl_memdev_get_fwctl_major(struct cxl_memdev *memdev) > +{ > + return memdev->fwctl_major; > +} > + > +CXL_EXPORT int cxl_memdev_get_fwctl_minor(struct cxl_memdev *memdev) > +{ > + return memdev->fwctl_minor; > +} > + > CXL_EXPORT unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev) > { > return memdev->pmem_size; > diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym > index 1fc33cc6e1a4..b2b51a72673c 100644 > --- a/cxl/lib/libcxl.sym > +++ b/cxl/lib/libcxl.sym > @@ -292,4 +292,6 @@ global: > LIBCXL_9 { > global: > cxl_bus_get_by_provider; > + cxl_memdev_get_fwctl_major; > + cxl_memdev_get_fwctl_minor; > } LIBECXL_8; > diff --git a/cxl/lib/private.h b/cxl/lib/private.h > index b6cd910e9335..676bf1573487 100644 > --- a/cxl/lib/private.h > +++ b/cxl/lib/private.h > @@ -37,6 +37,7 @@ enum cxl_fwl_loading { > struct cxl_endpoint; > struct cxl_memdev { > int id, major, minor; > + int fwctl_major, fwctl_minor; > int numa_node; > void *dev_buf; > size_t buf_len; > diff --git a/cxl/libcxl.h b/cxl/libcxl.h > index 3b309968a808..26aa906740af 100644 > --- a/cxl/libcxl.h > +++ b/cxl/libcxl.h > @@ -69,6 +69,8 @@ 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); > +int cxl_memdev_get_fwctl_major(struct cxl_memdev *memdev); > +int cxl_memdev_get_fwctl_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); > -- > 2.48.1 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [NDCTL PATCH v4 2/3] cxl: Enumerate major/minor of FWCTL char device 2025-04-09 19:56 ` Alison Schofield @ 2025-04-09 22:40 ` Dave Jiang 0 siblings, 0 replies; 11+ messages in thread From: Dave Jiang @ 2025-04-09 22:40 UTC (permalink / raw) To: Alison Schofield; +Cc: linux-cxl On 4/9/25 12:56 PM, Alison Schofield wrote: > On Tue, Feb 18, 2025 at 03:59:55PM -0700, Dave Jiang wrote: >> Add major/minor discovery for the FWCTL character device that is associated >> with supprting CXL Features under the cxl_memdev. Add libcxl API functions >> to retrieve the major and minor of the FWCTL character device. >> >> Signed-off-by: Dave Jiang <dave.jiang@intel.com> >> --- >> cxl/lib/libcxl.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++ >> cxl/lib/libcxl.sym | 2 ++ >> cxl/lib/private.h | 1 + >> cxl/libcxl.h | 2 ++ >> 4 files changed, 79 insertions(+) > > Please describe the new library interface in > "Documentation/cxl/lib/libcxl.txt" > > Add To: nvdimm@lists.linux.dev > >> >> diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c >> index bab7343e8a4a..566870acb30a 100644 >> --- a/cxl/lib/libcxl.c >> +++ b/cxl/lib/libcxl.c >> @@ -1253,6 +1253,66 @@ static int add_cxl_memdev_fwl(struct cxl_memdev *memdev, >> return -ENOMEM; >> } >> > > > Can we define like this: > #define CXL_PATH_MAX 512 > > and do this: > char path[CXL_PATH_MAX]; > > in next 2 funcs? > >> +static const char fwctl_prefix[] = "fwctl"; >> +static int get_feature_chardev(const char *base, char *chardev_path) >> +{ >> + char *path = calloc(1, strlen(base) + 100); >> + struct dirent *entry; >> + int rc = 0; >> + DIR *dir; >> + >> + if (!path) >> + return -ENOMEM; >> + >> + sprintf(path, "%s/fwctl/", base); >> + dir = opendir(path); >> + if (!dir) { >> + rc = -errno; >> + goto err; >> + } >> + >> + while ((entry = readdir(dir)) != NULL) >> + if (strncmp(entry->d_name, fwctl_prefix, strlen(fwctl_prefix)) == 0) >> + break; > > Can we exit this while loop w entry not NULL, yet not a match? > Maybe a found flag or store a match separately. > >> + >> + if (!entry) { >> + rc = -ENOENT; >> + goto read_err; >> + } >> + >> + sprintf(chardev_path, "/dev/fwctl/%s", entry->d_name); >> + >> +read_err: >> + closedir(dir); >> +err: >> + free(path); >> + return rc; >> +} >> + >> +static int memdev_get_fwctl_chardev(struct cxl_memdev *memdev, >> + const char *cxlmem_base) >> +{ >> + char *path = calloc(1, strlen(cxlmem_base) + 100); >> + struct stat st; >> + int rc; >> + > > Do we need to init memdev->fwctl... to something to avoid garbage? I think 0.0 is probably sufficient as incorrect chardev for user. We don't init memdev->major/minor either. DJ > > >> + rc = get_feature_chardev(cxlmem_base, path); >> + if (rc) >> + goto out; >> + >> + if (stat(path, &st) < 0) { >> + rc = -errno; >> + goto out; >> + } >> + >> + memdev->fwctl_major = major(st.st_rdev); >> + memdev->fwctl_minor = minor(st.st_rdev); >> + >> +out: >> + free(path); >> + return rc; >> +} >> + >> static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) >> { >> const char *devname = devpath_to_devname(cxlmem_base); >> @@ -1279,6 +1339,10 @@ static void *add_cxl_memdev(void *parent, int id, const char *cxlmem_base) >> memdev->major = major(st.st_rdev); >> memdev->minor = minor(st.st_rdev); >> >> + /* If this fails, no Features support. */ >> + if (memdev_get_fwctl_chardev(memdev, cxlmem_base)) >> + dbg(ctx, "%s: no Features support.\n", devname); >> + >> sprintf(path, "%s/pmem/size", cxlmem_base); >> if (sysfs_read_attr(ctx, path, buf) == 0) >> memdev->pmem_size = strtoull(buf, NULL, 0); >> @@ -1515,6 +1579,16 @@ CXL_EXPORT int cxl_memdev_get_minor(struct cxl_memdev *memdev) >> return memdev->minor; >> } >> >> +CXL_EXPORT int cxl_memdev_get_fwctl_major(struct cxl_memdev *memdev) >> +{ >> + return memdev->fwctl_major; >> +} >> + >> +CXL_EXPORT int cxl_memdev_get_fwctl_minor(struct cxl_memdev *memdev) >> +{ >> + return memdev->fwctl_minor; >> +} >> + >> CXL_EXPORT unsigned long long cxl_memdev_get_pmem_size(struct cxl_memdev *memdev) >> { >> return memdev->pmem_size; >> diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym >> index 1fc33cc6e1a4..b2b51a72673c 100644 >> --- a/cxl/lib/libcxl.sym >> +++ b/cxl/lib/libcxl.sym >> @@ -292,4 +292,6 @@ global: >> LIBCXL_9 { >> global: >> cxl_bus_get_by_provider; >> + cxl_memdev_get_fwctl_major; >> + cxl_memdev_get_fwctl_minor; >> } LIBECXL_8; >> diff --git a/cxl/lib/private.h b/cxl/lib/private.h >> index b6cd910e9335..676bf1573487 100644 >> --- a/cxl/lib/private.h >> +++ b/cxl/lib/private.h >> @@ -37,6 +37,7 @@ enum cxl_fwl_loading { >> struct cxl_endpoint; >> struct cxl_memdev { >> int id, major, minor; >> + int fwctl_major, fwctl_minor; >> int numa_node; >> void *dev_buf; >> size_t buf_len; >> diff --git a/cxl/libcxl.h b/cxl/libcxl.h >> index 3b309968a808..26aa906740af 100644 >> --- a/cxl/libcxl.h >> +++ b/cxl/libcxl.h >> @@ -69,6 +69,8 @@ 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); >> +int cxl_memdev_get_fwctl_major(struct cxl_memdev *memdev); >> +int cxl_memdev_get_fwctl_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); >> -- >> 2.48.1 >> ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [NDCTL PATCH v4 2/3] cxl: Enumerate major/minor of FWCTL char device 2025-02-18 22:59 ` [NDCTL PATCH v4 2/3] cxl: Enumerate major/minor of FWCTL char device Dave Jiang 2025-04-09 19:56 ` Alison Schofield @ 2025-04-09 23:39 ` Dan Williams 1 sibling, 0 replies; 11+ messages in thread From: Dan Williams @ 2025-04-09 23:39 UTC (permalink / raw) To: Dave Jiang, linux-cxl; +Cc: alison.schofield Dave Jiang wrote: > Add major/minor discovery for the FWCTL character device that is associated > with supprting CXL Features under the cxl_memdev. Add libcxl API functions > to retrieve the major and minor of the FWCTL character device. > > Signed-off-by: Dave Jiang <dave.jiang@intel.com> [..] > diff --git a/cxl/lib/private.h b/cxl/lib/private.h > index b6cd910e9335..676bf1573487 100644 > --- a/cxl/lib/private.h > +++ b/cxl/lib/private.h > @@ -37,6 +37,7 @@ enum cxl_fwl_loading { > struct cxl_endpoint; > struct cxl_memdev { > int id, major, minor; > + int fwctl_major, fwctl_minor; Unlike daxctl_dev_get_{major,minor}() which need a valid daxctl_dev, there is no guarantee that a memdev has a fwctl capability. So I would prefer that the helper functions are: int cxl_fwctl_get_major(const struct cxl_fwctl *) int cxl_fwctl_get_minor(const struct cxl_fwctl *) struct cxl_fwctl *cxl_memdev_get_fwctl(struct cxl_memdev *) ...where obtaining that 'struct cxl_fwctl' gives you a chance to return NULL and prevent non-sensical cxl_memdev_get_fwctl_major() against a memdev that does not have an active fwctl portal. It also lets you build other cxl_fwctl helper functionality around that object. ^ permalink raw reply [flat|nested] 11+ messages in thread
* [NDCTL PATCH v4 3/3] cxl/test: Add test for cxl features device 2025-02-18 22:59 [NDCTL PATCH v4 0/3] ndctl: Add support and test for CXL Features support Dave Jiang 2025-02-18 22:59 ` [NDCTL PATCH v4 1/3] cxl: Add cxl_bus_get_by_provider() Dave Jiang 2025-02-18 22:59 ` [NDCTL PATCH v4 2/3] cxl: Enumerate major/minor of FWCTL char device Dave Jiang @ 2025-02-18 22:59 ` Dave Jiang 2025-04-09 20:40 ` Alison Schofield 2 siblings, 1 reply; 11+ messages in thread From: Dave Jiang @ 2025-02-18 22:59 UTC (permalink / raw) To: linux-cxl; +Cc: alison.schofield Add a unit test to verify the features ioctl commands. Test support added for locating a features device, retrieve and verify the supported features commands, retrieve specific feature command data, retrieve test feature data, and write and verify test feature data. Signed-off-by: Dave Jiang <dave.jiang@intel.com> --- v4: - Adjust for kernel changes of input/out data structures - Setup test script to error out if not -ENODEV - Remove kernel 6.15 check --- test/cxl-features.sh | 31 ++++ test/fwctl.c | 383 +++++++++++++++++++++++++++++++++++++++++++ test/meson.build | 45 +++++ 3 files changed, 459 insertions(+) create mode 100755 test/cxl-features.sh create mode 100644 test/fwctl.c diff --git a/test/cxl-features.sh b/test/cxl-features.sh new file mode 100755 index 000000000000..8e44a5bcc3e5 --- /dev/null +++ b/test/cxl-features.sh @@ -0,0 +1,31 @@ +#!/bin/bash -Ex +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2025 Intel Corporation. All rights reserved. + +rc=77 +# 237 is -ENODEV +ERR_NODEV=237 + +. $(dirname $0)/common +FWCTL="$TEST_PATH"/fwctl + +trap 'err $LINENO' ERR + +modprobe cxl_test + +test -x "$FWCTL" || do_skip "no fwctl" +# disable trap +trap - $(compgen -A signal) +"$FWCTL" +rc=$? + +echo "error: $rc" +if [ "$rc" -eq "$ERR_NODEV" ]; then + do_skip "no fwctl char dev" +elif [ "$rc" -ne 0 ]; then + echo "fail: $LINENO" && exit 1 +fi + +trap 'err $LINENO' ERR + +_cxl_cleanup diff --git a/test/fwctl.c b/test/fwctl.c new file mode 100644 index 000000000000..ca39e30f6dca --- /dev/null +++ b/test/fwctl.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2024-2025 Intel Corporation. All rights reserved. +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <endian.h> +#include <stdint.h> +#include <stdlib.h> +#include <syslog.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <cxl/libcxl.h> +#include <cxl/features.h> +#include <fwctl/fwctl.h> +#include <fwctl/cxl.h> +#include <linux/uuid.h> +#include <uuid/uuid.h> +#include <util/bitmap.h> + +static const char provider[] = "cxl_test"; + +UUID_DEFINE(test_uuid, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, + 0xff, 0xff, + 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +); + +#define CXL_MBOX_OPCODE_GET_SUPPORTED_FEATURES 0x0500 +#define CXL_MBOX_OPCODE_GET_FEATURE 0x0501 +#define CXL_MBOX_OPCODE_SET_FEATURE 0x0502 + +#define GET_FEAT_SIZE 4 +#define SET_FEAT_SIZE 4 +#define EFFECTS_MASK (BIT(0) | BIT(9)) + +#define MAX_TEST_FEATURES 1 +#define DEFAULT_TEST_DATA 0xdeadbeef +#define DEFAULT_TEST_DATA2 0xabcdabcd + +struct test_feature { + uuid_t uuid; + size_t get_size; + size_t set_size; +}; + +static int send_command(int fd, struct fwctl_rpc *rpc, struct fwctl_rpc_cxl_out *out) +{ + if (ioctl(fd, FWCTL_RPC, rpc) == -1) { + fprintf(stderr, "RPC ioctl error: %s\n", strerror(errno)); + return -errno; + } + + if (out->retval) { + fprintf(stderr, "operation returned failure: %d\n", out->retval); + return -ENXIO; + } + + return 0; +} + +static int cxl_fwctl_rpc_get_test_feature(int fd, struct test_feature *feat_ctx, + const uint32_t expected_data) +{ + struct cxl_mbox_get_feat_in *feat_in; + struct fwctl_rpc_cxl_out *out; + struct fwctl_rpc rpc = {0}; + struct fwctl_rpc_cxl *in; + size_t out_size, in_size; + uint32_t val; + void *data; + int rc; + + in_size = sizeof(*in) + sizeof(*feat_in); + rc = posix_memalign((void **)&in, 16, in_size); + if (rc) + return -ENOMEM; + memset(in, 0, in_size); + feat_in = &in->get_feat_in; + + uuid_copy(feat_in->uuid, feat_ctx->uuid); + feat_in->count = feat_ctx->get_size; + + out_size = sizeof(*out) + feat_ctx->get_size; + rc = posix_memalign((void **)&out, 16, out_size); + if (rc) + goto free_in; + memset(out, 0, out_size); + + in->opcode = CXL_MBOX_OPCODE_GET_FEATURE; + in->op_size = sizeof(*feat_in); + + rpc.size = sizeof(rpc); + rpc.scope = FWCTL_RPC_CONFIGURATION; + rpc.in_len = in_size; + rpc.out_len = out_size; + rpc.in = (uint64_t)(uint64_t *)in; + rpc.out = (uint64_t)(uint64_t *)out; + + rc = send_command(fd, &rpc, out); + if (rc) + goto free_all; + + data = out->payload; + val = le32toh(*(__le32 *)data); + if (memcmp(&val, &expected_data, sizeof(val)) != 0) { + rc = -ENXIO; + goto free_all; + } + +free_all: + free(out); +free_in: + free(in); + return rc; +} + +static int cxl_fwctl_rpc_set_test_feature(int fd, struct test_feature *feat_ctx) +{ + struct cxl_mbox_set_feat_in *feat_in; + struct fwctl_rpc_cxl_out *out; + struct fwctl_rpc_cxl *in; + struct fwctl_rpc rpc = {0}; + size_t in_size, out_size; + uint32_t val; + void *data; + int rc; + + in_size = sizeof(*in) + sizeof(*feat_in) + sizeof(val); + rc = posix_memalign((void **)&in, 16, in_size); + if (rc) + return -ENOMEM; + memset(in, 0, in_size); + feat_in = &in->set_feat_in; + + out_size = sizeof(*out) + sizeof(val); + rc = posix_memalign((void **)&out, 16, out_size); + if (rc) + goto free_in; + + uuid_copy(feat_in->uuid, feat_ctx->uuid); + data = feat_in->feat_data; + val = DEFAULT_TEST_DATA2; + *(uint32_t *)data = htole32(val); + feat_in->flags = CXL_SET_FEAT_FLAG_FULL_DATA_TRANSFER; + + in->opcode = CXL_MBOX_OPCODE_SET_FEATURE; + in->op_size = sizeof(*feat_in) + sizeof(val); + + rpc.size = sizeof(rpc); + rpc.scope = FWCTL_RPC_DEBUG_WRITE_FULL; + rpc.in_len = in_size; + rpc.out_len = out_size; + rpc.in = (uint64_t)(uint64_t *)in; + rpc.out = (uint64_t)(uint64_t *)out; + + rc = send_command(fd, &rpc, out); + if (rc) + goto free_all; + + rc = cxl_fwctl_rpc_get_test_feature(fd, feat_ctx, DEFAULT_TEST_DATA2); + if (rc) { + fprintf(stderr, "Failed ioctl to get feature verify: %d\n", rc); + goto free_all; + } + +free_all: + free(out); +free_in: + free(in); + return rc; +} + +static int cxl_fwctl_rpc_get_supported_features(int fd, struct test_feature *feat_ctx) +{ + struct cxl_mbox_get_sup_feats_out *feat_out; + struct cxl_mbox_get_sup_feats_in *feat_in; + struct fwctl_rpc_cxl_out *out; + struct fwctl_rpc_cxl *in; + struct cxl_feat_entry *entry; + struct fwctl_rpc rpc = {0}; + size_t out_size, in_size; + int feats, rc; + + in_size = sizeof(*in) + sizeof(*feat_in); + rc = posix_memalign((void **)&in, 16, in_size); + if (rc) + return rc; + memset(in, 0, in_size); + /* First query, to get number of features w/o per feature data */ + in->opcode = CXL_MBOX_OPCODE_GET_SUPPORTED_FEATURES; + in->op_size = sizeof(*feat_in); + + out_size = sizeof(*out) + sizeof(*feat_out); + rc = posix_memalign((void **)&out, 16, out_size); + if (rc) + goto free_in; + + /* No need to fill in feat_in first go as we are passing in all 0's */ + + rpc.size = sizeof(rpc); + rpc.scope = FWCTL_RPC_CONFIGURATION; + rpc.in_len = in_size; + rpc.out_len = out_size; + rpc.in = (uint64_t)(uint64_t *)in; + rpc.out = (uint64_t)(uint64_t *)out; + + rc = send_command(fd, &rpc, out); + if (rc) + goto free_all; + + feat_out = &out->get_sup_feats_out; + feats = le16toh(feat_out->supported_feats); + if (feats != MAX_TEST_FEATURES) { + fprintf(stderr, "Test device has greater than %d test features.\n", + MAX_TEST_FEATURES); + rc = -ENXIO; + goto free_all; + } + + free(in); + free(out); + + /* Going second round to retrieve each feature details */ + in_size += feats * sizeof(*entry); + rc = posix_memalign((void **)&in, 16, in_size); + if (rc) + return rc; + memset(in, 0, in_size); + in->opcode = CXL_MBOX_OPCODE_GET_SUPPORTED_FEATURES; + in->op_size = sizeof(*feat_in); + + out_size += feats * sizeof(*entry); + rpc.out_len = out_size; + rc = posix_memalign((void **)&out, 16, out_size); + if (rc) + goto free_in; + memset(out, 0, out_size); + + feat_in = &in->get_sup_feats_in; + + rpc.out = (uint64_t)(uint64_t *)out; + rpc.out_len = out_size; + rpc.in = (uint64_t)(uint64_t *)in; + rpc.in_len = in_size; + feat_in->count = htole32(feats * sizeof(*entry)); + + rc = send_command(fd, &rpc, out); + if (rc) + goto free_all; + + feat_out = &out->get_sup_feats_out; + feats = le16toh(feat_out->supported_feats); + if (feats != MAX_TEST_FEATURES) { + fprintf(stderr, "Test device has greater than %u test features.\n", + MAX_TEST_FEATURES); + rc = -ENXIO; + goto free_all; + } + + if (le16toh(feat_out->num_entries) != MAX_TEST_FEATURES) { + fprintf(stderr, "Test device did not return expected entries. %u\n", + le16toh(feat_out->num_entries)); + rc = -ENXIO; + goto free_all; + } + + entry = &feat_out->ents[0]; + if (uuid_compare(test_uuid, entry->uuid) != 0) { + fprintf(stderr, "Test device did not export expected test feature.\n"); + rc = -ENXIO; + goto free_all; + } + + if (le16toh(entry->get_feat_size) != GET_FEAT_SIZE || + le16toh(entry->set_feat_size) != SET_FEAT_SIZE) { + fprintf(stderr, "Test device feature in/out size incorrect.\n"); + rc = -ENXIO; + goto free_all; + } + + if (le16toh(entry->effects) != EFFECTS_MASK) { + fprintf(stderr, "Test device set effects incorrect\n"); + rc = -ENXIO; + goto free_all; + } + + uuid_copy(feat_ctx->uuid, entry->uuid); + feat_ctx->get_size = le16toh(entry->get_feat_size); + feat_ctx->set_size = le16toh(entry->set_feat_size); + +free_all: + free(out); +free_in: + free(in); + return rc; +} + +static int test_fwctl_features(struct cxl_memdev *memdev) +{ + struct test_feature feat_ctx; + unsigned int major, minor; + int fd, rc; + char path[256]; + + major = cxl_memdev_get_fwctl_major(memdev); + minor = cxl_memdev_get_fwctl_minor(memdev); + + if (!major && !minor) + return -ENODEV; + + sprintf(path, "/dev/char/%d:%d", major, minor); + + fd = open(path, O_RDONLY, 0644); + if (!fd) { + fprintf(stderr, "Failed to open: %d\n", -errno); + return -errno; + } + + rc = cxl_fwctl_rpc_get_supported_features(fd, &feat_ctx); + if (rc) { + fprintf(stderr, "Failed ioctl to get supported features: %d\n", rc); + goto out; + } + + rc = cxl_fwctl_rpc_get_test_feature(fd, &feat_ctx, DEFAULT_TEST_DATA); + if (rc) { + fprintf(stderr, "Failed ioctl to get feature: %d\n", rc); + goto out; + } + + rc = cxl_fwctl_rpc_set_test_feature(fd, &feat_ctx); + if (rc) { + fprintf(stderr, "Failed ioctl to set feature: %d\n", rc); + goto out; + } + +out: + close(fd); + return rc; +} + +static int test_fwctl(struct cxl_ctx *ctx, struct cxl_bus *bus) +{ + struct cxl_memdev *memdev; + + cxl_memdev_foreach(ctx, memdev) { + if (cxl_memdev_get_bus(memdev) != bus) + continue; + return test_fwctl_features(memdev); + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct cxl_ctx *ctx; + struct cxl_bus *bus; + int rc; + + rc = cxl_new(&ctx); + if (rc < 0) + return rc; + + cxl_set_log_priority(ctx, LOG_DEBUG); + + bus = cxl_bus_get_by_provider(ctx, provider); + if (!bus) { + fprintf(stderr, "%s: unable to find bus (%s)\n", + argv[0], provider); + rc = -EINVAL; + goto out; + } + + rc = test_fwctl(ctx, bus); + +out: + cxl_unref(ctx); + return rc; +} diff --git a/test/meson.build b/test/meson.build index d871e28e17ce..476f4ba6c97c 100644 --- a/test/meson.build +++ b/test/meson.build @@ -17,6 +17,13 @@ ndctl_deps = libndctl_deps + [ versiondep, ] +libcxl_deps = [ + cxl_dep, + ndctl_dep, + uuid, + kmod, +] + libndctl = executable('libndctl', testcore + [ 'libndctl.c'], dependencies : libndctl_deps, include_directories : root_inc, @@ -130,6 +137,33 @@ revoke_devmem = executable('revoke_devmem', testcore + [ include_directories : root_inc, ) +fs = import('fs') + +feature_hdrs = [ + '/usr/include/cxl/features.h', + '/usr/include/fwctl/cxl.h', + '/usr/include/fwctl/fwctl.h', +] + +feat_hdrs_exist = true +foreach file : feature_hdrs + if not fs.exists(file) + feat_hdrs_exist = false + break + endif +endforeach + +if feat_hdrs_exist + fwctl = executable('fwctl', [ + 'fwctl.c', + ], + include_directories : root_inc, + dependencies : libcxl_deps, + ) +else + fwctl = [] +endif + mmap = executable('mmap', 'mmap.c',) create = find_program('create.sh') @@ -162,6 +196,10 @@ cxl_destroy_region = find_program('cxl-destroy-region.sh') cxl_qos_class = find_program('cxl-qos-class.sh') cxl_poison = find_program('cxl-poison.sh') +if feat_hdrs_exist + cxl_features = find_program('cxl-features.sh') +endif + tests = [ [ 'libndctl', libndctl, 'ndctl' ], [ 'dsm-fail', dsm_fail, 'ndctl' ], @@ -196,6 +234,12 @@ tests = [ [ 'cxl-poison.sh', cxl_poison, 'cxl' ], ] +if feat_hdrs_exist + tests += [ + [ 'cxl-features.sh', cxl_features, 'cxl' ], + ] +endif + if get_option('destructive').enabled() sub_section = find_program('sub-section.sh') dax_ext4 = find_program('dax-ext4.sh') @@ -249,6 +293,7 @@ foreach t : tests daxdev_errors, dax_dev, mmap, + fwctl, ], suite: t[2], timeout : 600, -- 2.48.1 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [NDCTL PATCH v4 3/3] cxl/test: Add test for cxl features device 2025-02-18 22:59 ` [NDCTL PATCH v4 3/3] cxl/test: Add test for cxl features device Dave Jiang @ 2025-04-09 20:40 ` Alison Schofield 2025-04-09 23:10 ` Dave Jiang 2025-04-10 1:05 ` Dave Jiang 0 siblings, 2 replies; 11+ messages in thread From: Alison Schofield @ 2025-04-09 20:40 UTC (permalink / raw) To: Dave Jiang; +Cc: linux-cxl, nvdimm On Tue, Feb 18, 2025 at 03:59:56PM -0700, Dave Jiang wrote: > Add a unit test to verify the features ioctl commands. Test support added > for locating a features device, retrieve and verify the supported features > commands, retrieve specific feature command data, retrieve test feature > data, and write and verify test feature data. > Let's revisit the naming - If the script is cxl-feature.sh then would the C program make sense as feature-control.c or ??? > Signed-off-by: Dave Jiang <dave.jiang@intel.com> > --- > v4: > - Adjust for kernel changes of input/out data structures > - Setup test script to error out if not -ENODEV > - Remove kernel 6.15 check > --- > test/cxl-features.sh | 31 ++++ > test/fwctl.c | 383 +++++++++++++++++++++++++++++++++++++++++++ > test/meson.build | 45 +++++ > 3 files changed, 459 insertions(+) > create mode 100755 test/cxl-features.sh > create mode 100644 test/fwctl.c > > diff --git a/test/cxl-features.sh b/test/cxl-features.sh snip > diff --git a/test/fwctl.c b/test/fwctl.c > new file mode 100644 > index 000000000000..ca39e30f6dca > --- /dev/null > +++ b/test/fwctl.c > @@ -0,0 +1,383 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// Copyright (C) 2024-2025 Intel Corporation. All rights reserved. > +#include <errno.h> > +#include <fcntl.h> > +#include <stdio.h> > +#include <endian.h> > +#include <stdint.h> > +#include <stdlib.h> > +#include <syslog.h> > +#include <string.h> > +#include <unistd.h> > +#include <sys/ioctl.h> > +#include <cxl/libcxl.h> > +#include <cxl/features.h> > +#include <fwctl/fwctl.h> > +#include <fwctl/cxl.h> > +#include <linux/uuid.h> > +#include <uuid/uuid.h> > +#include <util/bitmap.h> Not clear bitmap.h is needed? > + > +static const char provider[] = "cxl_test"; > + > +UUID_DEFINE(test_uuid, > + 0xff, 0xff, 0xff, 0xff, > + 0xff, 0xff, > + 0xff, 0xff, > + 0xff, 0xff, > + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff > +); > + > +#define CXL_MBOX_OPCODE_GET_SUPPORTED_FEATURES 0x0500 > +#define CXL_MBOX_OPCODE_GET_FEATURE 0x0501 > +#define CXL_MBOX_OPCODE_SET_FEATURE 0x0502 > + > +#define GET_FEAT_SIZE 4 > +#define SET_FEAT_SIZE 4 > +#define EFFECTS_MASK (BIT(0) | BIT(9)) > + > +#define MAX_TEST_FEATURES 1 > +#define DEFAULT_TEST_DATA 0xdeadbeef > +#define DEFAULT_TEST_DATA2 0xabcdabcd > + > +struct test_feature { > + uuid_t uuid; > + size_t get_size; > + size_t set_size; > +}; > + > +static int send_command(int fd, struct fwctl_rpc *rpc, struct fwctl_rpc_cxl_out *out) > +{ > + if (ioctl(fd, FWCTL_RPC, rpc) == -1) { > + fprintf(stderr, "RPC ioctl error: %s\n", strerror(errno)); > + return -errno; > + } > + > + if (out->retval) { > + fprintf(stderr, "operation returned failure: %d\n", out->retval); > + return -ENXIO; > + } > + > + return 0; > +} Above the send_command() is factored out and reused. How about doing similar with the ioctl setup - ie a setup_and_send_command() that setups up the ioctl and calls send_command(). That removes redundancy in *get_feature, *set_feature, *get_supported. > + > +static int cxl_fwctl_rpc_get_test_feature(int fd, struct test_feature *feat_ctx, > + const uint32_t expected_data) > +{ > + struct cxl_mbox_get_feat_in *feat_in; > + struct fwctl_rpc_cxl_out *out; > + struct fwctl_rpc rpc = {0}; > + struct fwctl_rpc_cxl *in; > + size_t out_size, in_size; > + uint32_t val; > + void *data; > + int rc; > + > + in_size = sizeof(*in) + sizeof(*feat_in); > + rc = posix_memalign((void **)&in, 16, in_size); > + if (rc) > + return -ENOMEM; > + memset(in, 0, in_size); How about de-duplicating the repeated posix_memalign() + memset() pattern into one helper func like alloc_aligned_memory() - including the memset on success. > + feat_in = &in->get_feat_in; > + > + uuid_copy(feat_in->uuid, feat_ctx->uuid); > + feat_in->count = feat_ctx->get_size; > + > + out_size = sizeof(*out) + feat_ctx->get_size; > + rc = posix_memalign((void **)&out, 16, out_size); > + if (rc) > + goto free_in; > + memset(out, 0, out_size); > + > + in->opcode = CXL_MBOX_OPCODE_GET_FEATURE; > + in->op_size = sizeof(*feat_in); > + > + rpc.size = sizeof(rpc); > + rpc.scope = FWCTL_RPC_CONFIGURATION; > + rpc.in_len = in_size; > + rpc.out_len = out_size; > + rpc.in = (uint64_t)(uint64_t *)in; > + rpc.out = (uint64_t)(uint64_t *)out; > + > + rc = send_command(fd, &rpc, out); > + if (rc) > + goto free_all; > + > + data = out->payload; > + val = le32toh(*(__le32 *)data); > + if (memcmp(&val, &expected_data, sizeof(val)) != 0) { > + rc = -ENXIO; > + goto free_all; > + } > + > +free_all: > + free(out); > +free_in: > + free(in); > + return rc; > +} > + snip > +static int test_fwctl_features(struct cxl_memdev *memdev) > +{ > + struct test_feature feat_ctx; > + unsigned int major, minor; > + int fd, rc; > + char path[256]; > + > + major = cxl_memdev_get_fwctl_major(memdev); > + minor = cxl_memdev_get_fwctl_minor(memdev); > + > + if (!major && !minor) > + return -ENODEV; > + > + sprintf(path, "/dev/char/%d:%d", major, minor); > + > + fd = open(path, O_RDONLY, 0644); > + if (!fd) { > + fprintf(stderr, "Failed to open: %d\n", -errno); > + return -errno; > + } Needs to be "if (fd < 0)" as open() returns -1 on failure. snip ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [NDCTL PATCH v4 3/3] cxl/test: Add test for cxl features device 2025-04-09 20:40 ` Alison Schofield @ 2025-04-09 23:10 ` Dave Jiang 2025-04-10 1:05 ` Dave Jiang 1 sibling, 0 replies; 11+ messages in thread From: Dave Jiang @ 2025-04-09 23:10 UTC (permalink / raw) To: Alison Schofield; +Cc: linux-cxl, nvdimm On 4/9/25 1:40 PM, Alison Schofield wrote: > On Tue, Feb 18, 2025 at 03:59:56PM -0700, Dave Jiang wrote: >> Add a unit test to verify the features ioctl commands. Test support added >> for locating a features device, retrieve and verify the supported features >> commands, retrieve specific feature command data, retrieve test feature >> data, and write and verify test feature data. >> > > Let's revisit the naming - > > If the script is cxl-feature.sh then would the C program make sense as > feature-control.c or ??? I don't have strong opinions on this. I can change it to feature-control.c. DJ > >> Signed-off-by: Dave Jiang <dave.jiang@intel.com> >> --- >> v4: >> - Adjust for kernel changes of input/out data structures >> - Setup test script to error out if not -ENODEV >> - Remove kernel 6.15 check >> --- >> test/cxl-features.sh | 31 ++++ >> test/fwctl.c | 383 +++++++++++++++++++++++++++++++++++++++++++ >> test/meson.build | 45 +++++ >> 3 files changed, 459 insertions(+) >> create mode 100755 test/cxl-features.sh >> create mode 100644 test/fwctl.c >> >> diff --git a/test/cxl-features.sh b/test/cxl-features.sh > > snip > >> diff --git a/test/fwctl.c b/test/fwctl.c >> new file mode 100644 >> index 000000000000..ca39e30f6dca >> --- /dev/null >> +++ b/test/fwctl.c >> @@ -0,0 +1,383 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +// Copyright (C) 2024-2025 Intel Corporation. All rights reserved. >> +#include <errno.h> >> +#include <fcntl.h> >> +#include <stdio.h> >> +#include <endian.h> >> +#include <stdint.h> >> +#include <stdlib.h> >> +#include <syslog.h> >> +#include <string.h> >> +#include <unistd.h> >> +#include <sys/ioctl.h> >> +#include <cxl/libcxl.h> >> +#include <cxl/features.h> >> +#include <fwctl/fwctl.h> >> +#include <fwctl/cxl.h> >> +#include <linux/uuid.h> >> +#include <uuid/uuid.h> >> +#include <util/bitmap.h> > > Not clear bitmap.h is needed? > >> + >> +static const char provider[] = "cxl_test"; >> + >> +UUID_DEFINE(test_uuid, >> + 0xff, 0xff, 0xff, 0xff, >> + 0xff, 0xff, >> + 0xff, 0xff, >> + 0xff, 0xff, >> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff >> +); >> + >> +#define CXL_MBOX_OPCODE_GET_SUPPORTED_FEATURES 0x0500 >> +#define CXL_MBOX_OPCODE_GET_FEATURE 0x0501 >> +#define CXL_MBOX_OPCODE_SET_FEATURE 0x0502 >> + >> +#define GET_FEAT_SIZE 4 >> +#define SET_FEAT_SIZE 4 >> +#define EFFECTS_MASK (BIT(0) | BIT(9)) >> + >> +#define MAX_TEST_FEATURES 1 >> +#define DEFAULT_TEST_DATA 0xdeadbeef >> +#define DEFAULT_TEST_DATA2 0xabcdabcd >> + >> +struct test_feature { >> + uuid_t uuid; >> + size_t get_size; >> + size_t set_size; >> +}; >> + >> +static int send_command(int fd, struct fwctl_rpc *rpc, struct fwctl_rpc_cxl_out *out) >> +{ >> + if (ioctl(fd, FWCTL_RPC, rpc) == -1) { >> + fprintf(stderr, "RPC ioctl error: %s\n", strerror(errno)); >> + return -errno; >> + } >> + >> + if (out->retval) { >> + fprintf(stderr, "operation returned failure: %d\n", out->retval); >> + return -ENXIO; >> + } >> + >> + return 0; >> +} > > Above the send_command() is factored out and reused. How about doing similar with > the ioctl setup - ie a setup_and_send_command() that setups up the ioctl and calls > send_command(). That removes redundancy in *get_feature, *set_feature, *get_supported. > > >> + >> +static int cxl_fwctl_rpc_get_test_feature(int fd, struct test_feature *feat_ctx, >> + const uint32_t expected_data) >> +{ >> + struct cxl_mbox_get_feat_in *feat_in; >> + struct fwctl_rpc_cxl_out *out; >> + struct fwctl_rpc rpc = {0}; >> + struct fwctl_rpc_cxl *in; >> + size_t out_size, in_size; >> + uint32_t val; >> + void *data; >> + int rc; >> + >> + in_size = sizeof(*in) + sizeof(*feat_in); >> + rc = posix_memalign((void **)&in, 16, in_size); >> + if (rc) >> + return -ENOMEM; >> + memset(in, 0, in_size); > > How about de-duplicating the repeated posix_memalign() + memset() pattern into > one helper func like alloc_aligned_memory() - including the memset on success. > > >> + feat_in = &in->get_feat_in; >> + >> + uuid_copy(feat_in->uuid, feat_ctx->uuid); >> + feat_in->count = feat_ctx->get_size; >> + >> + out_size = sizeof(*out) + feat_ctx->get_size; >> + rc = posix_memalign((void **)&out, 16, out_size); >> + if (rc) >> + goto free_in; >> + memset(out, 0, out_size); >> + >> + in->opcode = CXL_MBOX_OPCODE_GET_FEATURE; >> + in->op_size = sizeof(*feat_in); >> + >> + rpc.size = sizeof(rpc); >> + rpc.scope = FWCTL_RPC_CONFIGURATION; >> + rpc.in_len = in_size; >> + rpc.out_len = out_size; >> + rpc.in = (uint64_t)(uint64_t *)in; >> + rpc.out = (uint64_t)(uint64_t *)out; >> + >> + rc = send_command(fd, &rpc, out); >> + if (rc) >> + goto free_all; >> + >> + data = out->payload; >> + val = le32toh(*(__le32 *)data); >> + if (memcmp(&val, &expected_data, sizeof(val)) != 0) { >> + rc = -ENXIO; >> + goto free_all; >> + } >> + >> +free_all: >> + free(out); >> +free_in: >> + free(in); >> + return rc; >> +} >> + > snip > >> +static int test_fwctl_features(struct cxl_memdev *memdev) >> +{ >> + struct test_feature feat_ctx; >> + unsigned int major, minor; >> + int fd, rc; >> + char path[256]; >> + >> + major = cxl_memdev_get_fwctl_major(memdev); >> + minor = cxl_memdev_get_fwctl_minor(memdev); >> + >> + if (!major && !minor) >> + return -ENODEV; >> + >> + sprintf(path, "/dev/char/%d:%d", major, minor); >> + >> + fd = open(path, O_RDONLY, 0644); >> + if (!fd) { >> + fprintf(stderr, "Failed to open: %d\n", -errno); >> + return -errno; >> + } > > Needs to be "if (fd < 0)" as open() returns -1 on failure. > > snip ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [NDCTL PATCH v4 3/3] cxl/test: Add test for cxl features device 2025-04-09 20:40 ` Alison Schofield 2025-04-09 23:10 ` Dave Jiang @ 2025-04-10 1:05 ` Dave Jiang 1 sibling, 0 replies; 11+ messages in thread From: Dave Jiang @ 2025-04-10 1:05 UTC (permalink / raw) To: Alison Schofield; +Cc: linux-cxl, nvdimm On 4/9/25 1:40 PM, Alison Schofield wrote: > On Tue, Feb 18, 2025 at 03:59:56PM -0700, Dave Jiang wrote: >> Add a unit test to verify the features ioctl commands. Test support added >> for locating a features device, retrieve and verify the supported features >> commands, retrieve specific feature command data, retrieve test feature >> data, and write and verify test feature data. >> > > Let's revisit the naming - > > If the script is cxl-feature.sh then would the C program make sense as > feature-control.c or ??? > >> Signed-off-by: Dave Jiang <dave.jiang@intel.com> >> --- >> v4: >> - Adjust for kernel changes of input/out data structures >> - Setup test script to error out if not -ENODEV >> - Remove kernel 6.15 check >> --- >> test/cxl-features.sh | 31 ++++ >> test/fwctl.c | 383 +++++++++++++++++++++++++++++++++++++++++++ >> test/meson.build | 45 +++++ >> 3 files changed, 459 insertions(+) >> create mode 100755 test/cxl-features.sh >> create mode 100644 test/fwctl.c >> >> diff --git a/test/cxl-features.sh b/test/cxl-features.sh > > snip > >> diff --git a/test/fwctl.c b/test/fwctl.c >> new file mode 100644 >> index 000000000000..ca39e30f6dca >> --- /dev/null >> +++ b/test/fwctl.c >> @@ -0,0 +1,383 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +// Copyright (C) 2024-2025 Intel Corporation. All rights reserved. >> +#include <errno.h> >> +#include <fcntl.h> >> +#include <stdio.h> >> +#include <endian.h> >> +#include <stdint.h> >> +#include <stdlib.h> >> +#include <syslog.h> >> +#include <string.h> >> +#include <unistd.h> >> +#include <sys/ioctl.h> >> +#include <cxl/libcxl.h> >> +#include <cxl/features.h> >> +#include <fwctl/fwctl.h> >> +#include <fwctl/cxl.h> >> +#include <linux/uuid.h> >> +#include <uuid/uuid.h> >> +#include <util/bitmap.h> > > Not clear bitmap.h is needed? Yes. Needed for using BIT() for EFFECTS_MASK. DJ > >> + >> +static const char provider[] = "cxl_test"; >> + >> +UUID_DEFINE(test_uuid, >> + 0xff, 0xff, 0xff, 0xff, >> + 0xff, 0xff, >> + 0xff, 0xff, >> + 0xff, 0xff, >> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff >> +); >> + >> +#define CXL_MBOX_OPCODE_GET_SUPPORTED_FEATURES 0x0500 >> +#define CXL_MBOX_OPCODE_GET_FEATURE 0x0501 >> +#define CXL_MBOX_OPCODE_SET_FEATURE 0x0502 >> + >> +#define GET_FEAT_SIZE 4 >> +#define SET_FEAT_SIZE 4 >> +#define EFFECTS_MASK (BIT(0) | BIT(9)) >> + >> +#define MAX_TEST_FEATURES 1 >> +#define DEFAULT_TEST_DATA 0xdeadbeef >> +#define DEFAULT_TEST_DATA2 0xabcdabcd >> + >> +struct test_feature { >> + uuid_t uuid; >> + size_t get_size; >> + size_t set_size; >> +}; >> + >> +static int send_command(int fd, struct fwctl_rpc *rpc, struct fwctl_rpc_cxl_out *out) >> +{ >> + if (ioctl(fd, FWCTL_RPC, rpc) == -1) { >> + fprintf(stderr, "RPC ioctl error: %s\n", strerror(errno)); >> + return -errno; >> + } >> + >> + if (out->retval) { >> + fprintf(stderr, "operation returned failure: %d\n", out->retval); >> + return -ENXIO; >> + } >> + >> + return 0; >> +} > > Above the send_command() is factored out and reused. How about doing similar with > the ioctl setup - ie a setup_and_send_command() that setups up the ioctl and calls > send_command(). That removes redundancy in *get_feature, *set_feature, *get_supported. > > >> + >> +static int cxl_fwctl_rpc_get_test_feature(int fd, struct test_feature *feat_ctx, >> + const uint32_t expected_data) >> +{ >> + struct cxl_mbox_get_feat_in *feat_in; >> + struct fwctl_rpc_cxl_out *out; >> + struct fwctl_rpc rpc = {0}; >> + struct fwctl_rpc_cxl *in; >> + size_t out_size, in_size; >> + uint32_t val; >> + void *data; >> + int rc; >> + >> + in_size = sizeof(*in) + sizeof(*feat_in); >> + rc = posix_memalign((void **)&in, 16, in_size); >> + if (rc) >> + return -ENOMEM; >> + memset(in, 0, in_size); > > How about de-duplicating the repeated posix_memalign() + memset() pattern into > one helper func like alloc_aligned_memory() - including the memset on success. > > >> + feat_in = &in->get_feat_in; >> + >> + uuid_copy(feat_in->uuid, feat_ctx->uuid); >> + feat_in->count = feat_ctx->get_size; >> + >> + out_size = sizeof(*out) + feat_ctx->get_size; >> + rc = posix_memalign((void **)&out, 16, out_size); >> + if (rc) >> + goto free_in; >> + memset(out, 0, out_size); >> + >> + in->opcode = CXL_MBOX_OPCODE_GET_FEATURE; >> + in->op_size = sizeof(*feat_in); >> + >> + rpc.size = sizeof(rpc); >> + rpc.scope = FWCTL_RPC_CONFIGURATION; >> + rpc.in_len = in_size; >> + rpc.out_len = out_size; >> + rpc.in = (uint64_t)(uint64_t *)in; >> + rpc.out = (uint64_t)(uint64_t *)out; >> + >> + rc = send_command(fd, &rpc, out); >> + if (rc) >> + goto free_all; >> + >> + data = out->payload; >> + val = le32toh(*(__le32 *)data); >> + if (memcmp(&val, &expected_data, sizeof(val)) != 0) { >> + rc = -ENXIO; >> + goto free_all; >> + } >> + >> +free_all: >> + free(out); >> +free_in: >> + free(in); >> + return rc; >> +} >> + > snip > >> +static int test_fwctl_features(struct cxl_memdev *memdev) >> +{ >> + struct test_feature feat_ctx; >> + unsigned int major, minor; >> + int fd, rc; >> + char path[256]; >> + >> + major = cxl_memdev_get_fwctl_major(memdev); >> + minor = cxl_memdev_get_fwctl_minor(memdev); >> + >> + if (!major && !minor) >> + return -ENODEV; >> + >> + sprintf(path, "/dev/char/%d:%d", major, minor); >> + >> + fd = open(path, O_RDONLY, 0644); >> + if (!fd) { >> + fprintf(stderr, "Failed to open: %d\n", -errno); >> + return -errno; >> + } > > Needs to be "if (fd < 0)" as open() returns -1 on failure. > > snip ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-04-10 1:05 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-02-18 22:59 [NDCTL PATCH v4 0/3] ndctl: Add support and test for CXL Features support Dave Jiang 2025-02-18 22:59 ` [NDCTL PATCH v4 1/3] cxl: Add cxl_bus_get_by_provider() Dave Jiang 2025-04-09 0:42 ` Alison Schofield 2025-02-18 22:59 ` [NDCTL PATCH v4 2/3] cxl: Enumerate major/minor of FWCTL char device Dave Jiang 2025-04-09 19:56 ` Alison Schofield 2025-04-09 22:40 ` Dave Jiang 2025-04-09 23:39 ` Dan Williams 2025-02-18 22:59 ` [NDCTL PATCH v4 3/3] cxl/test: Add test for cxl features device Dave Jiang 2025-04-09 20:40 ` Alison Schofield 2025-04-09 23:10 ` Dave Jiang 2025-04-10 1:05 ` Dave Jiang
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox