anitazha / rpms / ndctl

Forked from rpms/ndctl a year ago
Clone

Blame SOURCES/0035-libcxl-add-support-for-command-query-and-submission.patch

e0018b
From 96afebd1b32ff839129f3bc0ba323ab5f04674ea Mon Sep 17 00:00:00 2001
e0018b
From: Vishal Verma <vishal.l.verma@intel.com>
e0018b
Date: Thu, 7 Oct 2021 02:21:27 -0600
e0018b
Subject: [PATCH 035/217] libcxl: add support for command query and submission
e0018b
e0018b
Add a set of APIs around 'cxl_cmd' for querying the kernel for supported
e0018b
commands, allocating and validating command structures against the
e0018b
supported set, and submitting the commands.
e0018b
e0018b
'Query Commands' and 'Send Command' are implemented as IOCTLs in the
e0018b
kernel. 'Query Commands' returns information about each supported
e0018b
command, such as flags governing its use, or input and output payload
e0018b
sizes. This information is used to validate command support, as well as
e0018b
set up input and output buffers for command submission.
e0018b
e0018b
Cc: Ben Widawsky <ben.widawsky@intel.com>
e0018b
Cc: Dan Williams <dan.j.williams@intel.com>
e0018b
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
e0018b
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
e0018b
---
e0018b
 cxl/lib/libcxl.c   | 390 +++++++++++++++++++++++++++++++++++++++++++++
e0018b
 cxl/lib/libcxl.sym |   9 ++
e0018b
 cxl/lib/private.h  |  33 ++++
e0018b
 cxl/libcxl.h       |  11 ++
e0018b
 4 files changed, 443 insertions(+)
e0018b
e0018b
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
e0018b
index c15e987..727d599 100644
e0018b
--- a/cxl/lib/libcxl.c
e0018b
+++ b/cxl/lib/libcxl.c
e0018b
@@ -9,14 +9,17 @@
e0018b
 #include <unistd.h>
e0018b
 #include <sys/stat.h>
e0018b
 #include <sys/types.h>
e0018b
+#include <sys/ioctl.h>
e0018b
 #include <sys/sysmacros.h>
e0018b
 #include <uuid/uuid.h>
e0018b
 #include <ccan/list/list.h>
e0018b
 #include <ccan/array_size/array_size.h>
e0018b
 
e0018b
 #include <util/log.h>
e0018b
+#include <util/size.h>
e0018b
 #include <util/sysfs.h>
e0018b
 #include <util/bitmap.h>
e0018b
+#include <cxl/cxl_mem.h>
e0018b
 #include <cxl/libcxl.h>
e0018b
 #include "private.h"
e0018b
 
e0018b
@@ -343,3 +346,390 @@ CXL_EXPORT const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev
e0018b
 {
e0018b
 	return memdev->firmware_version;
e0018b
 }
e0018b
+
e0018b
+CXL_EXPORT void cxl_cmd_unref(struct cxl_cmd *cmd)
e0018b
+{
e0018b
+	if (!cmd)
e0018b
+		return;
e0018b
+	if (--cmd->refcount == 0) {
e0018b
+		free(cmd->query_cmd);
e0018b
+		free(cmd->send_cmd);
e0018b
+		free(cmd->input_payload);
e0018b
+		free(cmd->output_payload);
e0018b
+		free(cmd);
e0018b
+	}
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT void cxl_cmd_ref(struct cxl_cmd *cmd)
e0018b
+{
e0018b
+	cmd->refcount++;
e0018b
+}
e0018b
+
e0018b
+static int cxl_cmd_alloc_query(struct cxl_cmd *cmd, int num_cmds)
e0018b
+{
e0018b
+	size_t size;
e0018b
+
e0018b
+	if (!cmd)
e0018b
+		return -EINVAL;
e0018b
+
e0018b
+	if (cmd->query_cmd != NULL)
e0018b
+		free(cmd->query_cmd);
e0018b
+
e0018b
+	size = struct_size(cmd->query_cmd, commands, num_cmds);
e0018b
+	if (size == SIZE_MAX)
e0018b
+		return -EOVERFLOW;
e0018b
+
e0018b
+	cmd->query_cmd = calloc(1, size);
e0018b
+	if (!cmd->query_cmd)
e0018b
+		return -ENOMEM;
e0018b
+
e0018b
+	cmd->query_cmd->n_commands = num_cmds;
e0018b
+
e0018b
+	return 0;
e0018b
+}
e0018b
+
e0018b
+static struct cxl_cmd *cxl_cmd_new(struct cxl_memdev *memdev)
e0018b
+{
e0018b
+	struct cxl_cmd *cmd;
e0018b
+	size_t size;
e0018b
+
e0018b
+	size = sizeof(*cmd);
e0018b
+	cmd = calloc(1, size);
e0018b
+	if (!cmd)
e0018b
+		return NULL;
e0018b
+
e0018b
+	cxl_cmd_ref(cmd);
e0018b
+	cmd->memdev = memdev;
e0018b
+
e0018b
+	return cmd;
e0018b
+}
e0018b
+
e0018b
+static int __do_cmd(struct cxl_cmd *cmd, int ioctl_cmd, int fd)
e0018b
+{
e0018b
+	void *cmd_buf;
e0018b
+	int rc;
e0018b
+
e0018b
+	switch (ioctl_cmd) {
e0018b
+	case CXL_MEM_QUERY_COMMANDS:
e0018b
+		cmd_buf = cmd->query_cmd;
e0018b
+		break;
e0018b
+	case CXL_MEM_SEND_COMMAND:
e0018b
+		cmd_buf = cmd->send_cmd;
e0018b
+		break;
e0018b
+	default:
e0018b
+		return -EINVAL;
e0018b
+	}
e0018b
+
e0018b
+	rc = ioctl(fd, ioctl_cmd, cmd_buf);
e0018b
+	if (rc < 0)
e0018b
+		rc = -errno;
e0018b
+
e0018b
+	return rc;
e0018b
+}
e0018b
+
e0018b
+static int do_cmd(struct cxl_cmd *cmd, int ioctl_cmd)
e0018b
+{
e0018b
+	char *path;
e0018b
+	struct stat st;
e0018b
+	unsigned int major, minor;
e0018b
+	int rc = 0, fd;
e0018b
+	struct cxl_memdev *memdev = cmd->memdev;
e0018b
+	struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
e0018b
+	const char *devname = cxl_memdev_get_devname(memdev);
e0018b
+
e0018b
+	major = cxl_memdev_get_major(memdev);
e0018b
+	minor = cxl_memdev_get_minor(memdev);
e0018b
+
e0018b
+	if (asprintf(&path, "/dev/cxl/%s", devname) < 0)
e0018b
+		return -ENOMEM;
e0018b
+
e0018b
+	fd = open(path, O_RDWR);
e0018b
+	if (fd < 0) {
e0018b
+		err(ctx, "failed to open %s: %s\n", path, strerror(errno));
e0018b
+		rc = -errno;
e0018b
+		goto out;
e0018b
+	}
e0018b
+
e0018b
+	if (fstat(fd, &st) >= 0 && S_ISCHR(st.st_mode)
e0018b
+			&& major(st.st_rdev) == major
e0018b
+			&& minor(st.st_rdev) == minor) {
e0018b
+		rc = __do_cmd(cmd, ioctl_cmd, fd);
e0018b
+	} else {
e0018b
+		err(ctx, "failed to validate %s as a CXL memdev node\n", path);
e0018b
+		rc = -ENXIO;
e0018b
+	}
e0018b
+	close(fd);
e0018b
+out:
e0018b
+	free(path);
e0018b
+	return rc;
e0018b
+}
e0018b
+
e0018b
+static int alloc_do_query(struct cxl_cmd *cmd, int num_cmds)
e0018b
+{
e0018b
+	struct cxl_ctx *ctx = cxl_memdev_get_ctx(cmd->memdev);
e0018b
+	int rc;
e0018b
+
e0018b
+	rc = cxl_cmd_alloc_query(cmd, num_cmds);
e0018b
+	if (rc)
e0018b
+		return rc;
e0018b
+
e0018b
+	rc = do_cmd(cmd, CXL_MEM_QUERY_COMMANDS);
e0018b
+	if (rc < 0)
e0018b
+		err(ctx, "%s: query commands failed: %s\n",
e0018b
+			cxl_memdev_get_devname(cmd->memdev),
e0018b
+			strerror(-rc));
e0018b
+	return rc;
e0018b
+}
e0018b
+
e0018b
+static int cxl_cmd_do_query(struct cxl_cmd *cmd)
e0018b
+{
e0018b
+	struct cxl_memdev *memdev = cmd->memdev;
e0018b
+	struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
e0018b
+	const char *devname = cxl_memdev_get_devname(memdev);
e0018b
+	int rc, n_commands;
e0018b
+
e0018b
+	switch (cmd->query_status) {
e0018b
+	case CXL_CMD_QUERY_OK:
e0018b
+		return 0;
e0018b
+	case CXL_CMD_QUERY_UNSUPPORTED:
e0018b
+		return -EOPNOTSUPP;
e0018b
+	case CXL_CMD_QUERY_NOT_RUN:
e0018b
+		break;
e0018b
+	default:
e0018b
+		err(ctx, "%s: Unknown query_status %d\n",
e0018b
+			devname, cmd->query_status);
e0018b
+		return -EINVAL;
e0018b
+	}
e0018b
+
e0018b
+	rc = alloc_do_query(cmd, 0);
e0018b
+	if (rc)
e0018b
+		return rc;
e0018b
+
e0018b
+	n_commands = cmd->query_cmd->n_commands;
e0018b
+	dbg(ctx, "%s: supports %d commands\n", devname, n_commands);
e0018b
+
e0018b
+	return alloc_do_query(cmd, n_commands);
e0018b
+}
e0018b
+
e0018b
+static int cxl_cmd_validate(struct cxl_cmd *cmd, u32 cmd_id)
e0018b
+{
e0018b
+	struct cxl_memdev *memdev = cmd->memdev;
e0018b
+	struct cxl_mem_query_commands *query = cmd->query_cmd;
e0018b
+	const char *devname = cxl_memdev_get_devname(memdev);
e0018b
+	struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
e0018b
+	u32 i;
e0018b
+
e0018b
+	for (i = 0; i < query->n_commands; i++) {
e0018b
+		struct cxl_command_info *cinfo = &query->commands[i];
e0018b
+		const char *cmd_name = cxl_command_names[cinfo->id].name;
e0018b
+
e0018b
+		if (cinfo->id != cmd_id)
e0018b
+			continue;
e0018b
+
e0018b
+		dbg(ctx, "%s: %s: in: %d, out %d, flags: %#08x\n",
e0018b
+			devname, cmd_name, cinfo->size_in,
e0018b
+			cinfo->size_out, cinfo->flags);
e0018b
+
e0018b
+		cmd->query_idx = i;
e0018b
+		cmd->query_status = CXL_CMD_QUERY_OK;
e0018b
+		return 0;
e0018b
+	}
e0018b
+	cmd->query_status = CXL_CMD_QUERY_UNSUPPORTED;
e0018b
+	return -EOPNOTSUPP;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT int cxl_cmd_set_input_payload(struct cxl_cmd *cmd, void *buf,
e0018b
+		int size)
e0018b
+{
e0018b
+	struct cxl_memdev *memdev = cmd->memdev;
e0018b
+
e0018b
+	if (size > memdev->payload_max || size < 0)
e0018b
+		return -EINVAL;
e0018b
+
e0018b
+	if (!buf) {
e0018b
+
e0018b
+		/* If the user didn't supply a buffer, allocate it */
e0018b
+		cmd->input_payload = calloc(1, size);
e0018b
+		if (!cmd->input_payload)
e0018b
+			return -ENOMEM;
e0018b
+		cmd->send_cmd->in.payload = (u64)cmd->input_payload;
e0018b
+	} else {
e0018b
+		/*
e0018b
+		 * Use user-buffer as is. If an automatic allocation was
e0018b
+		 * previously made (based on a fixed size from query),
e0018b
+		 * it will get freed during unref.
e0018b
+		 */
e0018b
+		cmd->send_cmd->in.payload = (u64)buf;
e0018b
+	}
e0018b
+	cmd->send_cmd->in.size = size;
e0018b
+
e0018b
+	return 0;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT int cxl_cmd_set_output_payload(struct cxl_cmd *cmd, void *buf,
e0018b
+		int size)
e0018b
+{
e0018b
+	struct cxl_memdev *memdev = cmd->memdev;
e0018b
+
e0018b
+	if (size > memdev->payload_max || size < 0)
e0018b
+		return -EINVAL;
e0018b
+
e0018b
+	if (!buf) {
e0018b
+
e0018b
+		/* If the user didn't supply a buffer, allocate it */
e0018b
+		cmd->output_payload = calloc(1, size);
e0018b
+		if (!cmd->output_payload)
e0018b
+			return -ENOMEM;
e0018b
+		cmd->send_cmd->out.payload = (u64)cmd->output_payload;
e0018b
+	} else {
e0018b
+		/*
e0018b
+		 * Use user-buffer as is. If an automatic allocation was
e0018b
+		 * previously made (based on a fixed size from query),
e0018b
+		 * it will get freed during unref.
e0018b
+		 */
e0018b
+		cmd->send_cmd->out.payload = (u64)buf;
e0018b
+	}
e0018b
+	cmd->send_cmd->out.size = size;
e0018b
+
e0018b
+	return 0;
e0018b
+}
e0018b
+
e0018b
+static int cxl_cmd_alloc_send(struct cxl_cmd *cmd, u32 cmd_id)
e0018b
+{
e0018b
+	struct cxl_mem_query_commands *query = cmd->query_cmd;
e0018b
+	struct cxl_command_info *cinfo = &query->commands[cmd->query_idx];
e0018b
+	size_t size;
e0018b
+
e0018b
+	if (!query)
e0018b
+		return -EINVAL;
e0018b
+
e0018b
+	size = sizeof(struct cxl_send_command);
e0018b
+	cmd->send_cmd = calloc(1, size);
e0018b
+	if (!cmd->send_cmd)
e0018b
+		return -ENOMEM;
e0018b
+
e0018b
+	if (cinfo->id != cmd_id)
e0018b
+		return -EINVAL;
e0018b
+
e0018b
+	cmd->send_cmd->id = cmd_id;
e0018b
+
e0018b
+	if (cinfo->size_in > 0) {
e0018b
+		cmd->input_payload = calloc(1, cinfo->size_in);
e0018b
+		if (!cmd->input_payload)
e0018b
+			return -ENOMEM;
e0018b
+		cmd->send_cmd->in.payload = (u64)cmd->input_payload;
e0018b
+		cmd->send_cmd->in.size = cinfo->size_in;
e0018b
+	}
e0018b
+	if (cinfo->size_out > 0) {
e0018b
+		cmd->output_payload = calloc(1, cinfo->size_out);
e0018b
+		if (!cmd->output_payload)
e0018b
+			return -ENOMEM;
e0018b
+		cmd->send_cmd->out.payload = (u64)cmd->output_payload;
e0018b
+		cmd->send_cmd->out.size = cinfo->size_out;
e0018b
+	}
e0018b
+
e0018b
+	return 0;
e0018b
+}
e0018b
+
e0018b
+static struct cxl_cmd *cxl_cmd_new_generic(struct cxl_memdev *memdev,
e0018b
+		u32 cmd_id)
e0018b
+{
e0018b
+	const char *devname = cxl_memdev_get_devname(memdev);
e0018b
+	struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
e0018b
+	struct cxl_cmd *cmd;
e0018b
+	int rc;
e0018b
+
e0018b
+	cmd = cxl_cmd_new(memdev);
e0018b
+	if (!cmd)
e0018b
+		return NULL;
e0018b
+
e0018b
+	rc = cxl_cmd_do_query(cmd);
e0018b
+	if (rc) {
e0018b
+		err(ctx, "%s: query returned: %s\n", devname, strerror(-rc));
e0018b
+		goto fail;
e0018b
+	}
e0018b
+
e0018b
+	rc = cxl_cmd_validate(cmd, cmd_id);
e0018b
+	if (rc) {
e0018b
+		errno = -rc;
e0018b
+		goto fail;
e0018b
+	}
e0018b
+
e0018b
+	rc = cxl_cmd_alloc_send(cmd, cmd_id);
e0018b
+	if (rc) {
e0018b
+		errno = -rc;
e0018b
+		goto fail;
e0018b
+	}
e0018b
+
e0018b
+	cmd->status = 1;
e0018b
+	return cmd;
e0018b
+
e0018b
+fail:
e0018b
+	cxl_cmd_unref(cmd);
e0018b
+	return NULL;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT const char *cxl_cmd_get_devname(struct cxl_cmd *cmd)
e0018b
+{
e0018b
+	return cxl_memdev_get_devname(cmd->memdev);
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev,
e0018b
+		int opcode)
e0018b
+{
e0018b
+	struct cxl_cmd *cmd;
e0018b
+
e0018b
+	/* opcode '0' is reserved */
e0018b
+	if (opcode <= 0) {
e0018b
+		errno = EINVAL;
e0018b
+		return NULL;
e0018b
+	}
e0018b
+
e0018b
+	cmd = cxl_cmd_new_generic(memdev, CXL_MEM_COMMAND_ID_RAW);
e0018b
+	if (!cmd)
e0018b
+		return NULL;
e0018b
+
e0018b
+	cmd->send_cmd->raw.opcode = opcode;
e0018b
+	return cmd;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT int cxl_cmd_submit(struct cxl_cmd *cmd)
e0018b
+{
e0018b
+	struct cxl_memdev *memdev = cmd->memdev;
e0018b
+	const char *devname = cxl_memdev_get_devname(memdev);
e0018b
+	struct cxl_ctx *ctx = cxl_memdev_get_ctx(memdev);
e0018b
+	int rc;
e0018b
+
e0018b
+	switch (cmd->query_status) {
e0018b
+	case CXL_CMD_QUERY_OK:
e0018b
+		break;
e0018b
+	case CXL_CMD_QUERY_UNSUPPORTED:
e0018b
+		return -EOPNOTSUPP;
e0018b
+	case CXL_CMD_QUERY_NOT_RUN:
e0018b
+		return -EINVAL;
e0018b
+	default:
e0018b
+		err(ctx, "%s: Unknown query_status %d\n",
e0018b
+			devname, cmd->query_status);
e0018b
+		return -EINVAL;
e0018b
+	}
e0018b
+
e0018b
+	dbg(ctx, "%s: submitting SEND cmd: in: %d, out: %d\n", devname,
e0018b
+		cmd->send_cmd->in.size, cmd->send_cmd->out.size);
e0018b
+	rc = do_cmd(cmd, CXL_MEM_SEND_COMMAND);
e0018b
+	cmd->status = cmd->send_cmd->retval;
e0018b
+	dbg(ctx, "%s: got SEND cmd: in: %d, out: %d, retval: %d, status: %d\n",
e0018b
+		devname, cmd->send_cmd->in.size, cmd->send_cmd->out.size,
e0018b
+		rc, cmd->status);
e0018b
+
e0018b
+	return rc;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT int cxl_cmd_get_mbox_status(struct cxl_cmd *cmd)
e0018b
+{
e0018b
+	return cmd->status;
e0018b
+}
e0018b
+
e0018b
+CXL_EXPORT int cxl_cmd_get_out_size(struct cxl_cmd *cmd)
e0018b
+{
e0018b
+	return cmd->send_cmd->out.size;
e0018b
+}
e0018b
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
e0018b
index 2616e5c..3900f90 100644
e0018b
--- a/cxl/lib/libcxl.sym
e0018b
+++ b/cxl/lib/libcxl.sym
e0018b
@@ -20,6 +20,15 @@ global:
e0018b
 	cxl_memdev_get_pmem_size;
e0018b
 	cxl_memdev_get_ram_size;
e0018b
 	cxl_memdev_get_firmware_verison;
e0018b
+	cxl_cmd_get_devname;
e0018b
+	cxl_cmd_new_raw;
e0018b
+	cxl_cmd_set_input_payload;
e0018b
+	cxl_cmd_set_output_payload;
e0018b
+	cxl_cmd_ref;
e0018b
+	cxl_cmd_unref;
e0018b
+	cxl_cmd_submit;
e0018b
+	cxl_cmd_get_mbox_status;
e0018b
+	cxl_cmd_get_out_size;
e0018b
 local:
e0018b
         *;
e0018b
 };
e0018b
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
e0018b
index fc88fa1..87ca17e 100644
e0018b
--- a/cxl/lib/private.h
e0018b
+++ b/cxl/lib/private.h
e0018b
@@ -4,6 +4,9 @@
e0018b
 #define _LIBCXL_PRIVATE_H_
e0018b
 
e0018b
 #include <libkmod.h>
e0018b
+#include <cxl/cxl_mem.h>
e0018b
+#include <ccan/endian/endian.h>
e0018b
+#include <ccan/short_types/short_types.h>
e0018b
 
e0018b
 #define CXL_EXPORT __attribute__ ((visibility("default")))
e0018b
 
e0018b
@@ -21,6 +24,36 @@ struct cxl_memdev {
e0018b
 	struct kmod_module *module;
e0018b
 };
e0018b
 
e0018b
+enum cxl_cmd_query_status {
e0018b
+	CXL_CMD_QUERY_NOT_RUN = 0,
e0018b
+	CXL_CMD_QUERY_OK,
e0018b
+	CXL_CMD_QUERY_UNSUPPORTED,
e0018b
+};
e0018b
+
e0018b
+/**
e0018b
+ * struct cxl_cmd - CXL memdev command
e0018b
+ * @memdev: the memory device to which the command is being sent
e0018b
+ * @query_cmd: structure for the Linux 'Query commands' ioctl
e0018b
+ * @send_cmd: structure for the Linux 'Send command' ioctl
e0018b
+ * @input_payload: buffer for input payload managed by libcxl
e0018b
+ * @output_payload: buffer for output payload managed by libcxl
e0018b
+ * @refcount: reference for passing command buffer around
e0018b
+ * @query_status: status from query_commands
e0018b
+ * @query_idx: index of 'this' command in the query_commands array
e0018b
+ * @status: command return status from the device
e0018b
+ */
e0018b
+struct cxl_cmd {
e0018b
+	struct cxl_memdev *memdev;
e0018b
+	struct cxl_mem_query_commands *query_cmd;
e0018b
+	struct cxl_send_command *send_cmd;
e0018b
+	void *input_payload;
e0018b
+	void *output_payload;
e0018b
+	int refcount;
e0018b
+	int query_status;
e0018b
+	int query_idx;
e0018b
+	int status;
e0018b
+};
e0018b
+
e0018b
 static inline int check_kmod(struct kmod_ctx *kmod_ctx)
e0018b
 {
e0018b
 	return kmod_ctx ? 0 : -ENXIO;
e0018b
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
e0018b
index fd06790..6e87b80 100644
e0018b
--- a/cxl/libcxl.h
e0018b
+++ b/cxl/libcxl.h
e0018b
@@ -48,6 +48,17 @@ const char *cxl_memdev_get_firmware_verison(struct cxl_memdev *memdev);
e0018b
              memdev != NULL; \
e0018b
              memdev = cxl_memdev_get_next(memdev))
e0018b
 
e0018b
+struct cxl_cmd;
e0018b
+const char *cxl_cmd_get_devname(struct cxl_cmd *cmd);
e0018b
+struct cxl_cmd *cxl_cmd_new_raw(struct cxl_memdev *memdev, int opcode);
e0018b
+int cxl_cmd_set_input_payload(struct cxl_cmd *cmd, void *in, int size);
e0018b
+int cxl_cmd_set_output_payload(struct cxl_cmd *cmd, void *out, int size);
e0018b
+void cxl_cmd_ref(struct cxl_cmd *cmd);
e0018b
+void cxl_cmd_unref(struct cxl_cmd *cmd);
e0018b
+int cxl_cmd_submit(struct cxl_cmd *cmd);
e0018b
+int cxl_cmd_get_mbox_status(struct cxl_cmd *cmd);
e0018b
+int cxl_cmd_get_out_size(struct cxl_cmd *cmd);
e0018b
+
e0018b
 #ifdef __cplusplus
e0018b
 } /* extern "C" */
e0018b
 #endif
e0018b
-- 
e0018b
2.27.0
e0018b