]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
cxl: Add Get Supported Features command for kernel usage
authorDave Jiang <dave.jiang@intel.com>
Thu, 20 Feb 2025 19:42:40 +0000 (12:42 -0700)
committerDave Jiang <dave.jiang@intel.com>
Wed, 26 Feb 2025 15:51:27 +0000 (08:51 -0700)
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: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
Tested-by: Shiju Jose <shiju.jose@huawei.com>
Link: https://patch.msgid.link/20250220194438.2281088-3-dave.jiang@intel.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
drivers/cxl/Kconfig
drivers/cxl/core/Makefile
drivers/cxl/core/features.c [new file with mode: 0644]
drivers/cxl/cxlmem.h
drivers/cxl/pci.c
include/cxl/features.h
tools/testing/cxl/Kbuild
tools/testing/cxl/test/mem.c

index 876469e23f7a7f0d53da9f33ac81498e2754cd0a..ad2e796e4ac63fd4529f8daf3caf9474addb416f 100644 (file)
@@ -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
index 9259bcc6773c804ccace2478c9f6f09267b48c9d..b0bfbd9eac9be87af0ceb4c96718c54cd00926b1 100644 (file)
@@ -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
diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c
new file mode 100644 (file)
index 0000000..f2898a5
--- /dev/null
@@ -0,0 +1,175 @@
+// 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");
index 55c55685cb39f9f7b2da2df1ba166f3124a191d9..dd2b7060d501c5462e3de1219e1e11be72b7aef6 100644 (file)
@@ -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)
index a96e54c6259e1241af93dac21225b5a943dc4eae..3e666ec515808756e709018bcd9f80bb6357e3f6 100644 (file)
@@ -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);
index 357d3acf8429dedf500fd77da9a8b6f30737a241..5b3485eb0f7749044c375fc9ca1d4a0eb999ed0a 100644 (file)
@@ -3,6 +3,8 @@
 #ifndef __CXL_FEATURES_H__
 #define __CXL_FEATURES_H__
 
+#include <linux/uuid.h>
+
 /* Feature commands capability supported by a device */
 enum cxl_features_capability {
        CXL_FEATURES_NONE = 0,
@@ -10,4 +12,89 @@ enum cxl_features_capability {
        CXL_FEATURES_RW,
 };
 
+/* Get Supported Features (0x500h) CXL r3.2 8.2.9.6.1 */
+struct cxl_mbox_get_sup_feats_in {
+       __le32 count;
+       __le16 start_idx;
+       u8 reserved[2];
+} __packed;
+
+/* CXL spec r3.2 Table 8-87 command effects */
+#define CXL_CMD_CONFIG_CHANGE_COLD_RESET       BIT(0)
+#define CXL_CMD_CONFIG_CHANGE_IMMEDIATE                BIT(1)
+#define CXL_CMD_DATA_CHANGE_IMMEDIATE          BIT(2)
+#define CXL_CMD_POLICY_CHANGE_IMMEDIATE                BIT(3)
+#define CXL_CMD_LOG_CHANGE_IMMEDIATE           BIT(4)
+#define CXL_CMD_SECURITY_STATE_CHANGE          BIT(5)
+#define CXL_CMD_BACKGROUND                     BIT(6)
+#define CXL_CMD_BGCMD_ABORT_SUPPORTED          BIT(7)
+#define CXL_CMD_EFFECTS_VALID                  BIT(9)
+#define CXL_CMD_CONFIG_CHANGE_CONV_RESET       BIT(10)
+#define CXL_CMD_CONFIG_CHANGE_CXL_RESET                BIT(11)
+
+/*
+ * CXL spec r3.2 Table 8-109
+ * Get Supported Features Supported Feature Entry
+ */
+struct cxl_feat_entry {
+       uuid_t uuid;
+       __le16 id;
+       __le16 get_feat_size;
+       __le16 set_feat_size;
+       __le32 flags;
+       u8 get_feat_ver;
+       u8 set_feat_ver;
+       __le16 effects;
+       u8 reserved[18];
+} __packed;
+
+/* @flags field for 'struct cxl_feat_entry' */
+#define CXL_FEATURE_F_CHANGEABLE               BIT(0)
+#define CXL_FEATURE_F_PERSIST_FW_UPDATE                BIT(4)
+#define CXL_FEATURE_F_DEFAULT_SEL              BIT(5)
+#define CXL_FEATURE_F_SAVED_SEL                        BIT(6)
+
+/*
+ * CXL spec r3.2 Table 8-108
+ * Get supported Features Output Payload
+ */
+struct cxl_mbox_get_sup_feats_out {
+       __struct_group(cxl_mbox_get_sup_feats_out_hdr, hdr, /* no attrs */,
+               __le16 num_entries;
+               __le16 supported_feats;
+               __u8 reserved[4];
+       );
+       struct cxl_feat_entry ents[] __counted_by_le(num_entries);
+} __packed;
+
+/**
+ * struct cxl_features_state - The Features state for the device
+ * @cxlds: Pointer to CXL device state
+ * @entries: CXl feature entry context
+ *     @num_features: total Features supported by the device
+ *     @ent: Flex array of Feature detail entries from the device
+ */
+struct cxl_features_state {
+       struct cxl_dev_state *cxlds;
+       struct cxl_feat_entries {
+               int num_features;
+               struct cxl_feat_entry ent[] __counted_by(num_features);
+       } *entries;
+};
+
+#ifdef CONFIG_CXL_FEATURES
+inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds);
+int devm_cxl_setup_features(struct cxl_dev_state *cxlds);
+#else
+static inline struct cxl_features_state *to_cxlfs(struct cxl_dev_state *cxlds)
+{
+       return NULL;
+}
+
+static inline int devm_cxl_setup_features(struct cxl_dev_state *cxlds)
+{
+       return -EOPNOTSUPP;
+}
+#endif
+
 #endif
index b1256fee3567fc7743812ee14bc46e09b7c8ba9b..0a6572ab6f37222692e82f76e2ead609cd777e0a 100644 (file)
@@ -63,6 +63,7 @@ cxl_core-y += $(CXL_CORE_SRC)/pmu.o
 cxl_core-y += $(CXL_CORE_SRC)/cdat.o
 cxl_core-$(CONFIG_TRACING) += $(CXL_CORE_SRC)/trace.o
 cxl_core-$(CONFIG_CXL_REGION) += $(CXL_CORE_SRC)/region.o
+cxl_core-$(CONFIG_CXL_FEATURES) += $(CXL_CORE_SRC)/features.o
 cxl_core-y += config_check.o
 cxl_core-y += cxl_core_test.o
 cxl_core-y += cxl_core_exports.o
index 8d731bd639882012df96c17c1e1de99301285c9d..e9494cd446ef521ddf0c80585bd84619802a4f50 100644 (file)
@@ -1558,6 +1558,10 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
        if (rc)
                return rc;
 
+       rc = devm_cxl_setup_features(cxlds);
+       if (rc)
+               dev_dbg(dev, "No CXL Features discovered\n");
+
        cxl_mock_add_event_logs(&mdata->mes);
 
        cxlmd = devm_cxl_add_memdev(&pdev->dev, cxlds);