From: Ben Widawsky <ben.widawsky@intel.com>
To: Vishal Verma <vishal.l.verma@intel.com>
Cc: linux-cxl@vger.kernel.org, linux-nvdimm@lists.01.org
Subject: Re: [ndctl PATCH v2 01/13] cxl: add a cxl utility and libcxl library
Date: Mon, 22 Feb 2021 13:36:15 -0800 [thread overview]
Message-ID: <20210222213615.lwjansmxclewb3xo@intel.com> (raw)
In-Reply-To: <20210219020331.725687-2-vishal.l.verma@intel.com>
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.widawsky@intel.com>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@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", ¶m.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-leave@lists.01.org
next prev parent reply other threads:[~2021-02-22 21:36 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-02-19 2:03 [ndctl PATCH v2 00/13] Initial CXL support Vishal Verma
2021-02-19 2:03 ` [ndctl PATCH v2 01/13] cxl: add a cxl utility and libcxl library Vishal Verma
2021-02-22 21:36 ` Ben Widawsky [this message]
2021-02-23 19:23 ` Verma, Vishal L
2021-02-19 2:03 ` [ndctl PATCH v2 02/13] cxl: add a local copy of the cxl_mem UAPI header Vishal Verma
2021-02-22 21:41 ` Ben Widawsky
2021-02-19 2:03 ` [ndctl PATCH v2 03/13] libcxl: add support for command query and submission Vishal Verma
2021-02-22 21:55 ` Ben Widawsky
2021-02-19 2:03 ` [ndctl PATCH v2 04/13] libcxl: add support for the 'Identify Device' command Vishal Verma
2021-02-22 22:02 ` Ben Widawsky
2021-02-19 2:03 ` [ndctl PATCH v2 05/13] test: rename 'ndctl_test' to 'test_ctx' Vishal Verma
2021-02-19 2:03 ` [ndctl PATCH v2 06/13] test: rename 'ndctl_test_*' helpers to 'test_*' Vishal Verma
2021-02-19 2:03 ` [ndctl PATCH v2 07/13] test: introduce a libcxl unit test Vishal Verma
2021-02-22 22:15 ` Ben Widawsky
2021-02-19 2:03 ` [ndctl PATCH v2 08/13] libcxl: add GET_HEALTH_INFO mailbox command and accessors Vishal Verma
2021-02-19 2:03 ` [ndctl PATCH v2 09/13] libcxl: add support for the 'GET_LSA' command Vishal Verma
2021-02-19 2:03 ` [ndctl PATCH v2 10/13] util/hexdump: Add a util helper to print a buffer in hex Vishal Verma
2021-02-19 2:03 ` [ndctl PATCH v2 11/13] test/libcxl: add a test for {set, get}_lsa commands Vishal Verma
2021-02-19 2:03 ` [ndctl PATCH v2 12/13] Documentation/cxl: add library API documentation Vishal Verma
2021-02-19 2:03 ` [ndctl PATCH v2 13/13] test/libcxl: introduce a command size fuzzing test Vishal Verma
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210222213615.lwjansmxclewb3xo@intel.com \
--to=ben.widawsky@intel.com \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-nvdimm@lists.01.org \
--cc=vishal.l.verma@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).