From: Ira Weiny <[email protected]>

Dynamic Capacity Devices (DCD) support extent change notifications
through the event log mechanism.  The interrupt mailbox commands were
extended in CXL 3.1 to support these notifications.  Firmware can't
configure DCD events to be FW controlled but can retain control of
memory events.

Configure DCD event log interrupts on devices supporting dynamic
capacity.  Disable DCD if interrupts are not supported.

Care is taken to preserve the interrupt policy set by the FW if FW first
has been selected by the BIOS.

Accept the 4-byte CXL 2.0 reply on GET Event Interrupt Policy by setting
min_out to CXL_EVENT_INT_POLICY_BASE_SIZE; pre-CXL 3.1 firmware omits
dcd_settings and would otherwise fail the size check.

Based on an original patch by Navneet Singh.

Signed-off-by: Ira Weiny <[email protected]>
Signed-off-by: Anisa Su <[email protected]>

---
Changes:
[anisa: rebase]
[anisa: accept 4-byte CXL 2.0 GET reply via min_out]
[anisa: drop Reviewed-by tags now that the patch carries new changes]
---
 drivers/cxl/cxlmem.h |  2 ++
 drivers/cxl/pci.c    | 75 ++++++++++++++++++++++++++++++++++++--------
 2 files changed, 64 insertions(+), 13 deletions(-)

diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index 10175ca3b7ee..65c009b02da6 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -218,7 +218,9 @@ struct cxl_event_interrupt_policy {
        u8 warn_settings;
        u8 failure_settings;
        u8 fatal_settings;
+       u8 dcd_settings;
 } __packed;
+#define CXL_EVENT_INT_POLICY_BASE_SIZE 4 /* info, warn, failure, fatal */
 
 /**
  * struct cxl_event_state - Event log driver state
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 8d12c684d670..83617439bbd3 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -557,6 +557,8 @@ static int cxl_event_get_int_policy(struct cxl_memdev_state 
*mds,
                .opcode = CXL_MBOX_OP_GET_EVT_INT_POLICY,
                .payload_out = policy,
                .size_out = sizeof(*policy),
+               /* CXL 2.0 firmware omits dcd_settings; accept the shorter 
reply */
+               .min_out = CXL_EVENT_INT_POLICY_BASE_SIZE,
        };
        int rc;
 
@@ -569,23 +571,34 @@ static int cxl_event_get_int_policy(struct 
cxl_memdev_state *mds,
 }
 
 static int cxl_event_config_msgnums(struct cxl_memdev_state *mds,
-                                   struct cxl_event_interrupt_policy *policy)
+                                   struct cxl_event_interrupt_policy *policy,
+                                   bool native_cxl)
 {
        struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
+       size_t size_in = CXL_EVENT_INT_POLICY_BASE_SIZE;
        struct cxl_mbox_cmd mbox_cmd;
        int rc;
 
-       *policy = (struct cxl_event_interrupt_policy) {
-               .info_settings = CXL_INT_MSI_MSIX,
-               .warn_settings = CXL_INT_MSI_MSIX,
-               .failure_settings = CXL_INT_MSI_MSIX,
-               .fatal_settings = CXL_INT_MSI_MSIX,
-       };
+       /* memory event policy is left if FW has control */
+       if (native_cxl) {
+               *policy = (struct cxl_event_interrupt_policy) {
+                       .info_settings = CXL_INT_MSI_MSIX,
+                       .warn_settings = CXL_INT_MSI_MSIX,
+                       .failure_settings = CXL_INT_MSI_MSIX,
+                       .fatal_settings = CXL_INT_MSI_MSIX,
+                       .dcd_settings = 0,
+               };
+       }
+
+       if (cxl_dcd_supported(mds)) {
+               policy->dcd_settings = CXL_INT_MSI_MSIX;
+               size_in += sizeof(policy->dcd_settings);
+       }
 
        mbox_cmd = (struct cxl_mbox_cmd) {
                .opcode = CXL_MBOX_OP_SET_EVT_INT_POLICY,
                .payload_in = policy,
-               .size_in = sizeof(*policy),
+               .size_in = size_in,
        };
 
        rc = cxl_internal_send_cmd(cxl_mbox, &mbox_cmd);
@@ -632,6 +645,30 @@ static int cxl_event_irqsetup(struct cxl_memdev_state *mds,
        return 0;
 }
 
+static int cxl_irqsetup(struct cxl_memdev_state *mds,
+                       struct cxl_event_interrupt_policy *policy,
+                       bool native_cxl)
+{
+       struct cxl_dev_state *cxlds = &mds->cxlds;
+       int rc;
+
+       if (native_cxl) {
+               rc = cxl_event_irqsetup(mds, policy);
+               if (rc)
+                       return rc;
+       }
+
+       if (cxl_dcd_supported(mds)) {
+               rc = cxl_event_req_irq(cxlds, policy->dcd_settings);
+               if (rc) {
+                       dev_err(cxlds->dev, "Failed to get interrupt for DCD 
event log\n");
+                       cxl_disable_dcd(mds);
+               }
+       }
+
+       return 0;
+}
+
 static bool cxl_event_int_is_fw(u8 setting)
 {
        u8 mode = FIELD_GET(CXLDEV_EVENT_INT_MODE_MASK, setting);
@@ -657,18 +694,26 @@ static bool cxl_event_validate_mem_policy(struct 
cxl_memdev_state *mds,
 static int cxl_event_config(struct pci_host_bridge *host_bridge,
                            struct cxl_memdev_state *mds, bool irq_avail)
 {
-       struct cxl_event_interrupt_policy policy;
+       struct cxl_event_interrupt_policy policy = { 0 };
+       bool native_cxl = host_bridge->native_cxl_error;
        int rc;
 
        /*
         * When BIOS maintains CXL error reporting control, it will process
         * event records.  Only one agent can do so.
+        *
+        * If BIOS has control of events and DCD is not supported skip event
+        * configuration.
         */
-       if (!host_bridge->native_cxl_error)
+       if (!native_cxl && !cxl_dcd_supported(mds))
                return 0;
 
        if (!irq_avail) {
                dev_info(mds->cxlds.dev, "No interrupt support, disable event 
processing.\n");
+               if (cxl_dcd_supported(mds)) {
+                       dev_info(mds->cxlds.dev, "DCD requires interrupts, 
disable DCD\n");
+                       cxl_disable_dcd(mds);
+               }
                return 0;
        }
 
@@ -676,10 +721,10 @@ static int cxl_event_config(struct pci_host_bridge 
*host_bridge,
        if (rc)
                return rc;
 
-       if (!cxl_event_validate_mem_policy(mds, &policy))
+       if (native_cxl && !cxl_event_validate_mem_policy(mds, &policy))
                return -EBUSY;
 
-       rc = cxl_event_config_msgnums(mds, &policy);
+       rc = cxl_event_config_msgnums(mds, &policy, native_cxl);
        if (rc)
                return rc;
 
@@ -687,12 +732,16 @@ static int cxl_event_config(struct pci_host_bridge 
*host_bridge,
        if (rc)
                return rc;
 
-       rc = cxl_event_irqsetup(mds, &policy);
+       rc = cxl_irqsetup(mds, &policy, native_cxl);
        if (rc)
                return rc;
 
        cxl_mem_get_event_records(mds, CXLDEV_EVENT_STATUS_ALL);
 
+       dev_dbg(mds->cxlds.dev, "Event config : %s DCD %s\n",
+               native_cxl ? "OS" : "BIOS",
+               cxl_dcd_supported(mds) ? "supported" : "not supported");
+
        return 0;
 }
 
-- 
2.43.0


Reply via email to