Skip to content

Commit a49aa81

Browse files
Davidlohr Buesodjbw
authored andcommitted
cxl/mem: Wire up event interrupts
Currently the only CXL features targeted for irq support require their message numbers to be within the first 16 entries. The device may however support less than 16 entries depending on the support it provides. Attempt to allocate these 16 irq vectors. If the device supports less then the PCI infrastructure will allocate that number. Upon successful allocation, users can plug in their respective isr at any point thereafter. CXL device events are signaled via interrupts. Each event log may have a different interrupt message number. These message numbers are reported in the Get Event Interrupt Policy mailbox command. Add interrupt support for event logs. Interrupts are allocated as shared interrupts. Therefore, all or some event logs can share the same message number. In addition all logs are queried on any interrupt in order of the most to least severe based on the status register. Finally place all event configuration logic into cxl_event_config(). Previously the logic was a simple 'read all' on start up. But interrupts must be configured prior to any reads to ensure no events are missed. A single event configuration function results in a cleaner over all implementation. Cc: Bjorn Helgaas <[email protected]> Cc: Jonathan Cameron <[email protected]> Co-developed-by: Ira Weiny <[email protected]> Signed-off-by: Davidlohr Bueso <[email protected]> Reviewed-by: Jonathan Cameron <[email protected]> Signed-off-by: Ira Weiny <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Dan Williams <[email protected]>
1 parent 6ebe28f commit a49aa81

File tree

4 files changed

+240
-10
lines changed

4 files changed

+240
-10
lines changed

drivers/cxl/cxl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw)
168168
CXLDEV_EVENT_STATUS_FAIL | \
169169
CXLDEV_EVENT_STATUS_FATAL)
170170

171+
/* CXL rev 3.0 section 8.2.9.2.4; Table 8-52 */
172+
#define CXLDEV_EVENT_INT_MODE_MASK GENMASK(1, 0)
173+
#define CXLDEV_EVENT_INT_MSGNUM_MASK GENMASK(7, 4)
174+
171175
/* CXL 2.0 8.2.8.4 Mailbox Registers */
172176
#define CXLDEV_MBOX_CAPS_OFFSET 0x00
173177
#define CXLDEV_MBOX_CAP_PAYLOAD_SIZE_MASK GENMASK(4, 0)

drivers/cxl/cxlmem.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,23 @@ struct cxl_endpoint_dvsec_info {
194194
struct range dvsec_range[2];
195195
};
196196

197+
/**
198+
* Event Interrupt Policy
199+
*
200+
* CXL rev 3.0 section 8.2.9.2.4; Table 8-52
201+
*/
202+
enum cxl_event_int_mode {
203+
CXL_INT_NONE = 0x00,
204+
CXL_INT_MSI_MSIX = 0x01,
205+
CXL_INT_FW = 0x02
206+
};
207+
struct cxl_event_interrupt_policy {
208+
u8 info_settings;
209+
u8 warn_settings;
210+
u8 failure_settings;
211+
u8 fatal_settings;
212+
} __packed;
213+
197214
/**
198215
* struct cxl_event_state - Event log driver state
199216
*
@@ -288,6 +305,8 @@ enum cxl_opcode {
288305
CXL_MBOX_OP_RAW = CXL_MBOX_OP_INVALID,
289306
CXL_MBOX_OP_GET_EVENT_RECORD = 0x0100,
290307
CXL_MBOX_OP_CLEAR_EVENT_RECORD = 0x0101,
308+
CXL_MBOX_OP_GET_EVT_INT_POLICY = 0x0102,
309+
CXL_MBOX_OP_SET_EVT_INT_POLICY = 0x0103,
291310
CXL_MBOX_OP_GET_FW_INFO = 0x0200,
292311
CXL_MBOX_OP_ACTIVATE_FW = 0x0202,
293312
CXL_MBOX_OP_GET_SUPPORTED_LOGS = 0x0400,

drivers/cxl/cxlpci.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@
5353
#define CXL_DVSEC_REG_LOCATOR_BLOCK_ID_MASK GENMASK(15, 8)
5454
#define CXL_DVSEC_REG_LOCATOR_BLOCK_OFF_LOW_MASK GENMASK(31, 16)
5555

56+
/*
57+
* NOTE: Currently all the functions which are enabled for CXL require their
58+
* vectors to be in the first 16. Use this as the default max.
59+
*/
60+
#define CXL_PCI_DEFAULT_MAX_VECTORS 16
61+
5662
/* Register Block Identifier (RBI) */
5763
enum cxl_regloc_type {
5864
CXL_REGLOC_RBI_EMPTY = 0,

drivers/cxl/pci.c

Lines changed: 211 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,209 @@ static int cxl_mem_alloc_event_buf(struct cxl_dev_state *cxlds)
438438
return devm_add_action_or_reset(cxlds->dev, free_event_buf, buf);
439439
}
440440

441+
static int cxl_alloc_irq_vectors(struct pci_dev *pdev)
442+
{
443+
int nvecs;
444+
445+
/*
446+
* Per CXL 3.0 3.1.1 CXL.io Endpoint a function on a CXL device must
447+
* not generate INTx messages if that function participates in
448+
* CXL.cache or CXL.mem.
449+
*
450+
* Additionally pci_alloc_irq_vectors() handles calling
451+
* pci_free_irq_vectors() automatically despite not being called
452+
* pcim_*. See pci_setup_msi_context().
453+
*/
454+
nvecs = pci_alloc_irq_vectors(pdev, 1, CXL_PCI_DEFAULT_MAX_VECTORS,
455+
PCI_IRQ_MSIX | PCI_IRQ_MSI);
456+
if (nvecs < 1) {
457+
dev_dbg(&pdev->dev, "Failed to alloc irq vectors: %d\n", nvecs);
458+
return -ENXIO;
459+
}
460+
return 0;
461+
}
462+
463+
struct cxl_dev_id {
464+
struct cxl_dev_state *cxlds;
465+
};
466+
467+
static irqreturn_t cxl_event_thread(int irq, void *id)
468+
{
469+
struct cxl_dev_id *dev_id = id;
470+
struct cxl_dev_state *cxlds = dev_id->cxlds;
471+
u32 status;
472+
473+
do {
474+
/*
475+
* CXL 3.0 8.2.8.3.1: The lower 32 bits are the status;
476+
* ignore the reserved upper 32 bits
477+
*/
478+
status = readl(cxlds->regs.status + CXLDEV_DEV_EVENT_STATUS_OFFSET);
479+
/* Ignore logs unknown to the driver */
480+
status &= CXLDEV_EVENT_STATUS_ALL;
481+
if (!status)
482+
break;
483+
cxl_mem_get_event_records(cxlds, status);
484+
cond_resched();
485+
} while (status);
486+
487+
return IRQ_HANDLED;
488+
}
489+
490+
static int cxl_event_req_irq(struct cxl_dev_state *cxlds, u8 setting)
491+
{
492+
struct device *dev = cxlds->dev;
493+
struct pci_dev *pdev = to_pci_dev(dev);
494+
struct cxl_dev_id *dev_id;
495+
int irq;
496+
497+
if (FIELD_GET(CXLDEV_EVENT_INT_MODE_MASK, setting) != CXL_INT_MSI_MSIX)
498+
return -ENXIO;
499+
500+
/* dev_id must be globally unique and must contain the cxlds */
501+
dev_id = devm_kzalloc(dev, sizeof(*dev_id), GFP_KERNEL);
502+
if (!dev_id)
503+
return -ENOMEM;
504+
dev_id->cxlds = cxlds;
505+
506+
irq = pci_irq_vector(pdev,
507+
FIELD_GET(CXLDEV_EVENT_INT_MSGNUM_MASK, setting));
508+
if (irq < 0)
509+
return irq;
510+
511+
return devm_request_threaded_irq(dev, irq, NULL, cxl_event_thread,
512+
IRQF_SHARED, NULL, dev_id);
513+
}
514+
515+
static int cxl_event_get_int_policy(struct cxl_dev_state *cxlds,
516+
struct cxl_event_interrupt_policy *policy)
517+
{
518+
struct cxl_mbox_cmd mbox_cmd = {
519+
.opcode = CXL_MBOX_OP_GET_EVT_INT_POLICY,
520+
.payload_out = policy,
521+
.size_out = sizeof(*policy),
522+
};
523+
int rc;
524+
525+
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
526+
if (rc < 0)
527+
dev_err(cxlds->dev, "Failed to get event interrupt policy : %d",
528+
rc);
529+
530+
return rc;
531+
}
532+
533+
static int cxl_event_config_msgnums(struct cxl_dev_state *cxlds,
534+
struct cxl_event_interrupt_policy *policy)
535+
{
536+
struct cxl_mbox_cmd mbox_cmd;
537+
int rc;
538+
539+
*policy = (struct cxl_event_interrupt_policy) {
540+
.info_settings = CXL_INT_MSI_MSIX,
541+
.warn_settings = CXL_INT_MSI_MSIX,
542+
.failure_settings = CXL_INT_MSI_MSIX,
543+
.fatal_settings = CXL_INT_MSI_MSIX,
544+
};
545+
546+
mbox_cmd = (struct cxl_mbox_cmd) {
547+
.opcode = CXL_MBOX_OP_SET_EVT_INT_POLICY,
548+
.payload_in = policy,
549+
.size_in = sizeof(*policy),
550+
};
551+
552+
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
553+
if (rc < 0) {
554+
dev_err(cxlds->dev, "Failed to set event interrupt policy : %d",
555+
rc);
556+
return rc;
557+
}
558+
559+
/* Retrieve final interrupt settings */
560+
return cxl_event_get_int_policy(cxlds, policy);
561+
}
562+
563+
static int cxl_event_irqsetup(struct cxl_dev_state *cxlds)
564+
{
565+
struct cxl_event_interrupt_policy policy;
566+
int rc;
567+
568+
rc = cxl_event_config_msgnums(cxlds, &policy);
569+
if (rc)
570+
return rc;
571+
572+
rc = cxl_event_req_irq(cxlds, policy.info_settings);
573+
if (rc) {
574+
dev_err(cxlds->dev, "Failed to get interrupt for event Info log\n");
575+
return rc;
576+
}
577+
578+
rc = cxl_event_req_irq(cxlds, policy.warn_settings);
579+
if (rc) {
580+
dev_err(cxlds->dev, "Failed to get interrupt for event Warn log\n");
581+
return rc;
582+
}
583+
584+
rc = cxl_event_req_irq(cxlds, policy.failure_settings);
585+
if (rc) {
586+
dev_err(cxlds->dev, "Failed to get interrupt for event Failure log\n");
587+
return rc;
588+
}
589+
590+
rc = cxl_event_req_irq(cxlds, policy.fatal_settings);
591+
if (rc) {
592+
dev_err(cxlds->dev, "Failed to get interrupt for event Fatal log\n");
593+
return rc;
594+
}
595+
596+
return 0;
597+
}
598+
599+
static bool cxl_event_int_is_fw(u8 setting)
600+
{
601+
u8 mode = FIELD_GET(CXLDEV_EVENT_INT_MODE_MASK, setting);
602+
603+
return mode == CXL_INT_FW;
604+
}
605+
606+
static int cxl_event_config(struct pci_host_bridge *host_bridge,
607+
struct cxl_dev_state *cxlds)
608+
{
609+
struct cxl_event_interrupt_policy policy;
610+
int rc;
611+
612+
/*
613+
* When BIOS maintains CXL error reporting control, it will process
614+
* event records. Only one agent can do so.
615+
*/
616+
if (!host_bridge->native_cxl_error)
617+
return 0;
618+
619+
rc = cxl_mem_alloc_event_buf(cxlds);
620+
if (rc)
621+
return rc;
622+
623+
rc = cxl_event_get_int_policy(cxlds, &policy);
624+
if (rc)
625+
return rc;
626+
627+
if (cxl_event_int_is_fw(policy.info_settings) ||
628+
cxl_event_int_is_fw(policy.warn_settings) ||
629+
cxl_event_int_is_fw(policy.failure_settings) ||
630+
cxl_event_int_is_fw(policy.fatal_settings)) {
631+
dev_err(cxlds->dev, "FW still in control of Event Logs despite _OSC settings\n");
632+
return -EBUSY;
633+
}
634+
635+
rc = cxl_event_irqsetup(cxlds);
636+
if (rc)
637+
return rc;
638+
639+
cxl_mem_get_event_records(cxlds, CXLDEV_EVENT_STATUS_ALL);
640+
641+
return 0;
642+
}
643+
441644
static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
442645
{
443646
struct pci_host_bridge *host_bridge = pci_find_host_bridge(pdev->bus);
@@ -456,6 +659,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
456659
rc = pcim_enable_device(pdev);
457660
if (rc)
458661
return rc;
662+
pci_set_master(pdev);
459663

460664
cxlds = cxl_dev_state_create(&pdev->dev);
461665
if (IS_ERR(cxlds))
@@ -512,20 +716,17 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
512716
if (rc)
513717
return rc;
514718

719+
rc = cxl_alloc_irq_vectors(pdev);
720+
if (rc)
721+
return rc;
722+
515723
cxlmd = devm_cxl_add_memdev(cxlds);
516724
if (IS_ERR(cxlmd))
517725
return PTR_ERR(cxlmd);
518726

519-
/*
520-
* When BIOS maintains CXL error reporting control, it will process
521-
* event records. Only one agent can do so.
522-
*/
523-
if (host_bridge->native_cxl_error) {
524-
rc = cxl_mem_alloc_event_buf(cxlds);
525-
if (rc)
526-
return rc;
527-
cxl_mem_get_event_records(cxlds, CXLDEV_EVENT_STATUS_ALL);
528-
}
727+
rc = cxl_event_config(host_bridge, cxlds);
728+
if (rc)
729+
return rc;
529730

530731
if (cxlds->regs.ras) {
531732
pci_enable_pcie_error_reporting(pdev);

0 commit comments

Comments
 (0)