Commit f0e6a232 authored by Dave Jiang's avatar Dave Jiang
Browse files

cxl: Add Get Supported Features command for kernel usage



CXL spec r3.2 8.2.9.6.1 Get Supported Features (Opcode 0500h)
The command retrieve the list of supported device-specific features
(identified by UUID) and general information about each Feature.

The driver will retrieve the Feature entries in order to make checks and
provide information for the Get Feature and Set Feature command. One of
the main piece of information retrieved are the effects a Set Feature
command would have for a particular feature. The retrieved Feature
entries are stored in the cxl_mailbox context.

The setup of Features is initiated via devm_cxl_setup_features() during the
pci probe function before the cxl_memdev is enumerated.

Reviewed-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: default avatarDan Williams <dan.j.williams@intel.com>
Reviewed-by: default avatarLi Ming <ming.li@zohomail.com>
Reviewed-by: default avatarDavidlohr Bueso <dave@stgolabs.net>
Tested-by: default avatarShiju Jose <shiju.jose@huawei.com>
Link: https://patch.msgid.link/20250220194438.2281088-3-dave.jiang@intel.com


Signed-off-by: default avatarDave Jiang <dave.jiang@intel.com>
parent cbbca60a
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -102,6 +102,17 @@ config CXL_MEM

	  If unsure say 'm'.

config CXL_FEATURES
	bool "CXL: Features"
	depends on CXL_PCI
	help
	  Enable support for CXL Features. A CXL device that includes a mailbox
	  supports commands that allows listing, getting, and setting of
	  optionally defined features such as memory sparing or post package
	  sparing. Vendors may define custom features for the device.

	  If unsure say 'n'

config CXL_PORT
	default CXL_BUS
	tristate
+1 −0
Original line number Diff line number Diff line
@@ -16,3 +16,4 @@ cxl_core-y += pmu.o
cxl_core-y += cdat.o
cxl_core-$(CONFIG_TRACING) += trace.o
cxl_core-$(CONFIG_CXL_REGION) += region.o
cxl_core-$(CONFIG_CXL_FEATURES) += features.o
+175 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */
#include <linux/device.h>
#include <cxl/mailbox.h>
#include <cxl/features.h>
#include "cxl.h"
#include "cxlmem.h"

inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds)
{
	return cxlds->cxlfs;
}
EXPORT_SYMBOL_NS_GPL(to_cxlfs, "CXL");

static int cxl_get_supported_features_count(struct cxl_mailbox *cxl_mbox)
{
	struct cxl_mbox_get_sup_feats_out mbox_out;
	struct cxl_mbox_get_sup_feats_in mbox_in;
	struct cxl_mbox_cmd mbox_cmd;
	int rc;

	memset(&mbox_in, 0, sizeof(mbox_in));
	mbox_in.count = cpu_to_le32(sizeof(mbox_out));
	memset(&mbox_out, 0, sizeof(mbox_out));
	mbox_cmd = (struct cxl_mbox_cmd) {
		.opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES,
		.size_in = sizeof(mbox_in),
		.payload_in = &mbox_in,
		.size_out = sizeof(mbox_out),
		.payload_out = &mbox_out,
		.min_out = sizeof(mbox_out),
	};
	rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
	if (rc < 0)
		return rc;

	return le16_to_cpu(mbox_out.supported_feats);
}

static struct cxl_feat_entries *
get_supported_features(struct cxl_features_state *cxlfs)
{
	int remain_feats, max_size, max_feats, start, rc, hdr_size;
	struct cxl_mailbox *cxl_mbox = &cxlfs->cxlds->cxl_mbox;
	int feat_size = sizeof(struct cxl_feat_entry);
	struct cxl_mbox_get_sup_feats_in mbox_in;
	struct cxl_feat_entry *entry;
	struct cxl_mbox_cmd mbox_cmd;
	int count;

	count = cxl_get_supported_features_count(cxl_mbox);
	if (count <= 0)
		return NULL;

	struct cxl_feat_entries *entries __free(kvfree) =
		kvmalloc(struct_size(entries, ent, count), GFP_KERNEL);
	if (!entries)
		return NULL;

	struct cxl_mbox_get_sup_feats_out *mbox_out __free(kvfree) =
		kvmalloc(cxl_mbox->payload_size, GFP_KERNEL);
	if (!mbox_out)
		return NULL;

	hdr_size = struct_size(mbox_out, ents, 0);
	max_size = cxl_mbox->payload_size - hdr_size;
	/* max feat entries that can fit in mailbox max payload size */
	max_feats = max_size / feat_size;
	entry = entries->ent;

	start = 0;
	remain_feats = count;
	do {
		int retrieved, alloc_size, copy_feats;
		int num_entries;

		if (remain_feats > max_feats) {
			alloc_size = struct_size(mbox_out, ents, max_feats);
			remain_feats = remain_feats - max_feats;
			copy_feats = max_feats;
		} else {
			alloc_size = struct_size(mbox_out, ents, remain_feats);
			copy_feats = remain_feats;
			remain_feats = 0;
		}

		memset(&mbox_in, 0, sizeof(mbox_in));
		mbox_in.count = cpu_to_le32(alloc_size);
		mbox_in.start_idx = cpu_to_le16(start);
		memset(mbox_out, 0, alloc_size);
		mbox_cmd = (struct cxl_mbox_cmd) {
			.opcode = CXL_MBOX_OP_GET_SUPPORTED_FEATURES,
			.size_in = sizeof(mbox_in),
			.payload_in = &mbox_in,
			.size_out = alloc_size,
			.payload_out = mbox_out,
			.min_out = hdr_size,
		};
		rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
		if (rc < 0)
			return NULL;

		if (mbox_cmd.size_out <= hdr_size)
			return NULL;

		/*
		 * Make sure retrieved out buffer is multiple of feature
		 * entries.
		 */
		retrieved = mbox_cmd.size_out - hdr_size;
		if (retrieved % feat_size)
			return NULL;

		num_entries = le16_to_cpu(mbox_out->num_entries);
		/*
		 * If the reported output entries * defined entry size !=
		 * retrieved output bytes, then the output package is incorrect.
		 */
		if (num_entries * feat_size != retrieved)
			return NULL;

		memcpy(entry, mbox_out->ents, retrieved);
		entry += num_entries;
		/*
		 * If the number of output entries is less than expected, add the
		 * remaining entries to the next batch.
		 */
		remain_feats += copy_feats - num_entries;
		start += num_entries;
	} while (remain_feats);

	entries->num_features = count;

	return no_free_ptr(entries);
}

static void free_cxlfs(void *_cxlfs)
{
	struct cxl_features_state *cxlfs = _cxlfs;
	struct cxl_dev_state *cxlds = cxlfs->cxlds;

	cxlds->cxlfs = NULL;
	kvfree(cxlfs->entries);
	kfree(cxlfs);
}

/**
 * devm_cxl_setup_features() - Allocate and initialize features context
 * @cxlds: CXL device context
 *
 * Return 0 on success or -errno on failure.
 */
int devm_cxl_setup_features(struct cxl_dev_state *cxlds)
{
	struct cxl_mailbox *cxl_mbox = &cxlds->cxl_mbox;

	if (cxl_mbox->feat_cap < CXL_FEATURES_RO)
		return -ENODEV;

	struct cxl_features_state *cxlfs __free(kfree) =
		kzalloc(sizeof(*cxlfs), GFP_KERNEL);
	if (!cxlfs)
		return -ENOMEM;

	cxlfs->cxlds = cxlds;

	cxlfs->entries = get_supported_features(cxlfs);
	if (!cxlfs->entries)
		return -ENOMEM;

	cxlds->cxlfs = cxlfs;

	return devm_add_action_or_reset(cxlds->dev, free_cxlfs, no_free_ptr(cxlfs));
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_setup_features, "CXL");
+4 −0
Original line number Diff line number Diff line
@@ -392,6 +392,7 @@ struct cxl_dpa_perf {
 * @serial: PCIe Device Serial Number
 * @type: Generic Memory Class device or Vendor Specific Memory device
 * @cxl_mbox: CXL mailbox context
 * @cxlfs: CXL features context
 */
struct cxl_dev_state {
	struct device *dev;
@@ -407,6 +408,9 @@ struct cxl_dev_state {
	u64 serial;
	enum cxl_devtype type;
	struct cxl_mailbox cxl_mbox;
#ifdef CONFIG_CXL_FEATURES
	struct cxl_features_state *cxlfs;
#endif
};

static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox)
+4 −0
Original line number Diff line number Diff line
@@ -997,6 +997,10 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
	if (rc)
		return rc;

	rc = devm_cxl_setup_features(cxlds);
	if (rc)
		dev_dbg(&pdev->dev, "No CXL Features discovered\n");

	cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);
	if (IS_ERR(cxlmd))
		return PTR_ERR(cxlmd);
Loading