On 21-02-18 19:03:19, Vishal Verma wrote:
> 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 <ben.widaw...@intel.com>
> Cc: Dan Williams <dan.j.willi...@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.ve...@intel.com>

A couple of really minor things below

> ---
>  Documentation/cxl/cxl-list.txt       |  65 +++++
>  Documentation/cxl/cxl.txt            |  34 +++
>  Documentation/cxl/human-option.txt   |   8 +
>  Documentation/cxl/verbose-option.txt |   5 +
>  configure.ac                         |   3 +
>  Makefile.am                          |   8 +-
>  Makefile.am.in                       |   4 +
>  cxl/lib/private.h                    |  29 +++
>  cxl/lib/libcxl.c                     | 345 +++++++++++++++++++++++++++
>  cxl/builtin.h                        |   8 +
>  cxl/libcxl.h                         |  55 +++++
>  util/filter.h                        |   2 +
>  util/json.h                          |   3 +
>  util/main.h                          |   3 +
>  cxl/cxl.c                            |  95 ++++++++
>  cxl/list.c                           | 113 +++++++++
>  util/filter.c                        |  20 ++
>  util/json.c                          |  26 ++
>  .gitignore                           |   2 +
>  Documentation/cxl/Makefile.am        |  58 +++++
>  cxl/Makefile.am                      |  21 ++
>  cxl/lib/Makefile.am                  |  32 +++
>  cxl/lib/libcxl.pc.in                 |  11 +
>  cxl/lib/libcxl.sym                   |  29 +++
>  24 files changed, 976 insertions(+), 3 deletions(-)
>  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/lib/private.h
>  create mode 100644 cxl/lib/libcxl.c
>  create mode 100644 cxl/builtin.h
>  create mode 100644 cxl/libcxl.h
>  create mode 100644 cxl/cxl.c
>  create mode 100644 cxl/list.c
>  create mode 100644 Documentation/cxl/Makefile.am
>  create mode 100644 cxl/Makefile.am
>  create mode 100644 cxl/lib/Makefile.am
>  create mode 100644 cxl/lib/libcxl.pc.in
>  create mode 100644 cxl/lib/libcxl.sym
> 
> diff --git a/Documentation/cxl/cxl-list.txt b/Documentation/cxl/cxl-list.txt
> new file mode 100644
> index 0000000..107b388
> --- /dev/null
> +++ b/Documentation/cxl/cxl-list.txt
> @@ -0,0 +1,65 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +cxl-list(1)
> +===========
> +
> +NAME
> +----
> +cxl-list - CXL capable host bridges, switches, devices, and their attributes
> +in json.
> +
> +SYNOPSIS
> +--------
> +[verse]
> +'cxl list' [<options>]
> +
> +Walk the CXL capable device hierarchy in the system and list all device
> +instances along with some of their major attributes.

This doesn't seem to match the above. Here it's just devices and above you talk
about bridges and switches as well.

> +
> +Options can be specified to limit the output to specific devices.
> +By default, 'cxl list' with no options is equivalent to:
> +[verse]
> +cxl list --devices
> +
> +EXAMPLE
> +-------
> +----
> +# cxl list --devices
> +{
> +  "memdev":"mem0",
> +  "pmem_size":268435456,
> +  "ram_size":0,
> +}
> +----
> +
> +OPTIONS
> +-------
> +-d::
> +--dev=::
> +     Specify a cxl device name to filter the listing. For example:
> +----
> +# cxl list --dev=mem0
> +{
> +  "memdev":"mem0",
> +  "pmem_size":268435456,
> +  "ram_size":0,
> +}
> +----
> +
> +-D::
> +--devices::
> +     Include all CXL 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..e99e61b
> --- /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 devices
> +
> +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/configure.ac b/configure.ac
> index 5ec8d2f..7f5e6f0 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/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/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 <libkmod.h>
> +
> +#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/lib/libcxl.c b/cxl/lib/libcxl.c
> new file mode 100644
> index 0000000..d34e7d0
> --- /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 <stdio.h>
> +#include <errno.h>
> +#include <limits.h>
> +#include <libgen.h>
> +#include <stdlib.h>
> +#include <dirent.h>
> +#include <unistd.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <sys/sysmacros.h>
> +#include <uuid/uuid.h>
> +#include <ccan/list/list.h>
> +#include <ccan/array_size/array_size.h>
> +
> +#include <util/log.h>
> +#include <util/sysfs.h>
> +#include <util/bitmap.h>
> +#include <cxl/libcxl.h>
> +#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", __func__, 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);

For strtoull usage and below - it certainly doesn't matter much but maybe using
10 for base would better since sysfs is ABI and therefore anything other than
base 10 is incorrect.

> +
> +     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/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/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 <stdarg.h>
> +#include <unistd.h>
> +
> +#ifdef HAVE_UUID
> +#include <uuid/uuid.h>
> +#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/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.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);
>       };
>  };
>  
> diff --git a/cxl/cxl.c b/cxl/cxl.c
> new file mode 100644
> index 0000000..ed062ef
> --- /dev/null
> +++ b/cxl/cxl.c
> @@ -0,0 +1,95 @@
> +// 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 <stdio.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <cxl/libcxl.h>
> +#include <util/parse-options.h>
> +#include <ccan/array_size/array_size.h>
> +
> +#include <util/strbuf.h>
> +#include <util/util.h>
> +#include <util/main.h>
> +#include <cxl/builtin.h>
> +
> +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,
> +     };

Move NULL to newline.

> +     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/list.c b/cxl/list.c
> new file mode 100644
> index 0000000..7a4f34b
> --- /dev/null
> +++ b/cxl/list.c
> @@ -0,0 +1,113 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (C) 2020-2021 Intel Corporation. All rights reserved. */
> +#include <stdio.h>
> +#include <errno.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <limits.h>
> +#include <util/json.h>
> +#include <util/filter.h>
> +#include <json-c/json.h>
> +#include <cxl/libcxl.h>
> +#include <util/parse-options.h>
> +#include <ccan/array_size/array_size.h>
> +
> +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('d', "memdev", &param.memdev, "memory device name",
> +                        "filter by CXL memory device name"),
> +             OPT_BOOLEAN('D', "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 [<options>]",
> +             NULL
> +     };
> +     struct json_object *jdevs = NULL;
> +     unsigned long list_flags;
> +     struct cxl_memdev *memdev;
> +     int i;
> +
> +        argc = parse_options(argc, argv, options, u, 0);

Tab.

/me looks for .clang-format

> +     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)
> +             list.memdevs = true;
> +
> +     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 <util/filter.h>
>  #include <ndctl/libndctl.h>
>  #include <daxctl/libdaxctl.h>
> +#include <cxl/libcxl.h>
>  
>  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/json.c b/util/json.c
> index ca0167b..a855571 100644
> --- a/util/json.c
> +++ b/util/json.c
> @@ -9,6 +9,7 @@
>  #include <json-c/printbuf.h>
>  #include <ndctl/libndctl.h>
>  #include <daxctl/libdaxctl.h>
> +#include <cxl/libcxl.h>
>  #include <ccan/array_size/array_size.h>
>  #include <ccan/short_types/short_types.h>
>  #include <ndctl.h>
> @@ -1429,3 +1430,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/.gitignore b/.gitignore
> index 3ef9ff7..de43823 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -15,8 +15,10 @@ Makefile.in
>  *.1
>  Documentation/daxctl/asciidoc.conf
>  Documentation/ndctl/asciidoc.conf
> +Documentation/cxl/asciidoc.conf
>  Documentation/daxctl/asciidoctor-extensions.rb
>  Documentation/ndctl/asciidoctor-extensions.rb
> +Documentation/cxl/asciidoctor-extensions.rb
>  .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/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/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.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..0f6ecad
> --- /dev/null
> +++ b/cxl/lib/libcxl.sym
> @@ -0,0 +1,29 @@
> +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;
> +local:
> +        *;
> +};
> +
> +LIBCXL_2 {
> +global:
> +     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;
> +} LIBCXL_1;
> -- 
> 2.29.2
> 
_______________________________________________
Linux-nvdimm mailing list -- linux-nvdimm@lists.01.org
To unsubscribe send an email to linux-nvdimm-le...@lists.01.org

Reply via email to